1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-11-05 08:33:53 +01:00

TimeScale effect improvements, updating to sbsms 2, not including GUI changes (except removing obsolete transient sharpening option). See Bug 485.

This commit is contained in:
clayton.otey
2012-04-16 18:26:48 +00:00
parent aa0738a81a
commit 87fb427603
37 changed files with 12087 additions and 9726 deletions

View File

@@ -6,7 +6,7 @@ SBSMSEffect.cpp
Clayton Otey
This abstract class contains all of the common code for an
This class contains all of the common code for an
effect that uses SBSMS to do its processing (TimeScale)
**********************************************************************/
@@ -22,79 +22,90 @@ effect that uses SBSMS to do its processing (TimeScale)
#include "../Project.h"
#include "TimeWarper.h"
class resampleBuf
enum {
SBSMSOutBlockSize = 512
};
class ResampleBuf
{
public:
resampleBuf()
ResampleBuf()
{
processed = 0;
buf = NULL;
leftBuffer = NULL;
rightBuffer = NULL;
quality = NULL;
iface = NULL;
sbsms = NULL;
sbsmser = NULL;
outBuf = NULL;
outputLeftBuffer = NULL;
outputRightBuffer = NULL;
resampler = NULL;
SBSMSBuf = NULL;
outputLeftTrack = NULL;
outputRightTrack = NULL;
resampler = NULL;
}
~resampleBuf()
~ResampleBuf()
{
if(buf) free(buf);
if(leftBuffer) free(leftBuffer);
if(rightBuffer) free(rightBuffer);
if(sbsmser) sbsms_destroy(sbsmser);
if(outBuf) free(outBuf);
if(outputLeftBuffer) free(outputLeftBuffer);
if(outputRightBuffer) free(outputRightBuffer);
if(SBSMSBuf) free(SBSMSBuf);
if(outputLeftTrack) delete outputLeftTrack;
if(outputRightTrack) delete outputRightTrack;
if(quality) delete quality;
if(sbsms) delete sbsms;
if(iface) delete iface;
if(resampler) delete resampler;
}
bool bPitch;
audio *buf;
double ratio;
sampleCount block;
sampleCount processed;
sampleCount blockSize;
sampleCount SBSMSBlockSize;
sampleCount offset;
sampleCount end;
float *leftBuffer;
float *rightBuffer;
WaveTrack *leftTrack;
WaveTrack *rightTrack;
SBSMS *sbsms;
SBSMSInterface *iface;
audio *SBSMSBuf;
// Not required by callbacks, but makes for easier cleanup
sbsms *sbsmser;
audio *outBuf;
float *outputLeftBuffer;
float *outputRightBuffer;
Resampler *resampler;
SBSMSQuality *quality;
WaveTrack *outputLeftTrack;
WaveTrack *outputRightTrack;
};
class SBSMSEffectInterface : public SBSMSInterfaceSliding {
public:
SBSMSEffectInterface(Resampler *resampler,
Slide *rateSlide, Slide *pitchSlide,
bool bReferenceInput,
long samples, long preSamples,
SBSMSQuality *quality)
: SBSMSInterfaceSliding(rateSlide,pitchSlide,bReferenceInput,samples,preSamples,quality)
{
this->resampler = resampler;
}
virtual ~SBSMSEffectInterface() {}
long samples(audio *buf, long n) {
return resampler->read(buf, n);
}
protected:
Resampler *resampler;
};
long samplesCB(audio *chdata, long numFrames, void *userData)
{
sbsmsInfo *si = (sbsmsInfo*) userData;
long n_read = si->rs->read(chdata, numFrames);
return n_read;
}
void EffectSBSMS :: setParameters(double rateStart, double rateEnd, double pitchStart, double pitchEnd, bool bPreAnalyze)
long resampleCB(void *cb_data, SBSMSFrame *data)
{
this->rateStart = rateStart;
this->rateEnd = rateEnd;
this->pitchStart = pitchStart;
this->pitchEnd = pitchEnd;
this->bPreAnalyze = bPreAnalyze;
}
bool EffectSBSMS :: bInit = FALSE;
long resampleCB(void *cb_data, sbsms_resample_frame *data)
{
resampleBuf *r = (resampleBuf*) cb_data;
ResampleBuf *r = (ResampleBuf*) cb_data;
long blockSize = r->leftTrack->GetBestBlockSize(r->offset);
@@ -112,29 +123,75 @@ long resampleCB(void *cb_data, sbsms_resample_frame *data)
r->buf[i][1] = r->rightBuffer[i];
}
r->offset += blockSize;
data->in = r->buf;
data->buf = r->buf;
data->size = blockSize;
data->ratio0 = r->ratio;
data->ratio1 = r->ratio;
if(r->bPitch) {
float t0 = (float)(r->processed) / r->iface->getSamplesToInput();
float t1 = (float)(r->processed + blockSize) / r->iface->getSamplesToInput();
data->ratio0 = r->iface->getStretch(t0);
data->ratio1 = r->iface->getStretch(t1);
} else {
data->ratio0 = r->ratio;
data->ratio1 = r->ratio;
}
r->processed += blockSize;
r->offset += blockSize;
return blockSize;
}
long postResampleCB(void *cb_data, SBSMSFrame *data)
{
ResampleBuf *r = (ResampleBuf*) cb_data;
long sampleCount = r->sbsms->read(r->iface, r->SBSMSBuf, r->SBSMSBlockSize);
data->buf = r->SBSMSBuf;
data->size = sampleCount;
data->ratio0 = 1.0 / r->ratio;
data->ratio1 = 1.0 / r->ratio;
return sampleCount;
}
void EffectSBSMS :: setParameters(double rateStart, double rateEnd, double pitchStart, double pitchEnd,
SlideType rateSlideType, SlideType pitchSlideType,
bool bLinkRatePitch, bool bRateReferenceInput, bool bPitchReferenceInput)
{
this->rateStart = rateStart;
this->rateEnd = rateEnd;
this->pitchStart = pitchStart;
this->pitchEnd = pitchEnd;
this->bLinkRatePitch = bLinkRatePitch;
this->rateSlideType = rateSlideType;
this->pitchSlideType = pitchSlideType;
this->bRateReferenceInput = bRateReferenceInput;
this->bPitchReferenceInput = bPitchReferenceInput;
}
TimeWarper *createTimeWarper(double t0, double t1, double duration,
double rateStart, double rateEnd, SlideType rateSlideType)
{
TimeWarper *warper = NULL;
if (rateStart == rateEnd || rateSlideType == SlideConstant) {
warper = new LinearTimeWarper(t0, t0, t1, t0+duration);
} else if(rateSlideType == SlideLinearInputRate) {
warper = new LinearInputRateTimeWarper(t0, t1, rateStart, rateEnd);
} else if(rateSlideType == SlideLinearOutputRate) {
warper = new LinearOutputRateTimeWarper(t0, t1, rateStart, rateEnd);
} else if(rateSlideType == SlideLinearInputStretch) {
warper = new LinearInputStretchTimeWarper(t0, t1, rateStart, rateEnd);
} else if(rateSlideType == SlideLinearOutputStretch) {
warper = new LinearOutputStretchTimeWarper(t0, t1, rateStart, rateEnd);
} else if(rateSlideType == SlideGeometricInput) {
warper = new GeometricInputTimeWarper(t0, t1, rateStart, rateEnd);
} else if(rateSlideType == SlideGeometricOutput) {
warper = new GeometricOutputTimeWarper(t0, t1, rateStart, rateEnd);
}
return warper;
}
// Labels inside the affected region are moved to match the audio; labels after
// it are shifted along appropriately.
bool EffectSBSMS::ProcessLabelTrack(Track *t)
{
TimeWarper *warper = NULL;
if (rateStart == rateEnd)
{
warper = new LinearTimeWarper(mT0, mT0,
mT1, mT0+(mT1-mT0)*mTotalStretch);
} else
{
warper = new LogarithmicTimeWarper(mT0, mT1,
rateStart, rateEnd);
}
TimeWarper *warper = createTimeWarper(mT0,mT1,(mT1-mT0)*mTotalStretch,rateStart,rateEnd,rateSlideType);
SetTimeWarper(new RegionTimeWarper(mT0, mT1, warper));
LabelTrack *lt = (LabelTrack*)t;
if (lt == NULL) return false;
@@ -144,16 +201,10 @@ bool EffectSBSMS::ProcessLabelTrack(Track *t)
bool EffectSBSMS::Process()
{
if(!bInit) {
sbsms_init(8192);
bInit = TRUE;
}
bool bGoodResult = true;
//Iterate over each track
// Track::All is needed because this effect needs to introduce
// silence in the group tracks to keep sync-lock.
//Track::All is needed because this effect needs to introduce silence in the group tracks to keep sync
this->CopyInputTracks(Track::All); // Set up mOutputTracks.
TrackListIterator iter(mOutputTracks);
Track* t;
@@ -161,13 +212,11 @@ bool EffectSBSMS::Process()
double maxDuration = 0.0;
if(rateStart == rateEnd)
mTotalStretch = 1.0/rateStart;
else
mTotalStretch = 1.0/(rateEnd-rateStart)*log(rateEnd/rateStart);
// Must sync if selection length will change
bool mustSync = (mTotalStretch != 1.0);
bool mustSync = (rateStart != rateEnd);
Slide rateSlide(rateSlideType,rateStart,rateEnd);
Slide pitchSlide(pitchSlideType,pitchStart,pitchEnd);
mTotalStretch = rateSlide.getTotalStretch();
t = iter.First();
while (t != NULL) {
@@ -218,131 +267,116 @@ bool EffectSBSMS::Process()
mCurTrackNum++; // Increment for rightTrack, too.
}
sampleCount trackStart = leftTrack->TimeToLongSamples(leftTrack->GetStartTime());
sampleCount trackEnd = leftTrack->TimeToLongSamples(leftTrack->GetEndTime());
// SBSMS has a fixed sample rate - we just convert to its sample rate and then convert back
float srIn = leftTrack->GetRate();
// mchinen: srSBMS doesn't do the right thing when it was set to fixed 44100. This seems to fix it.
float srSBSMS = leftTrack->GetRate();
float srTrack = leftTrack->GetRate();
float srProcess = bLinkRatePitch?srTrack:44100.0;
// the resampler needs a callback to supply its samples
resampleBuf rb;
ResampleBuf rb;
sampleCount maxBlockSize = leftTrack->GetMaxBlockSize();
rb.block = maxBlockSize;
rb.buf = (audio*)calloc(rb.block,sizeof(audio));
rb.blockSize = maxBlockSize;
rb.buf = (audio*)calloc(rb.blockSize,sizeof(audio));
rb.leftTrack = leftTrack;
rb.rightTrack = rightTrack?rightTrack:leftTrack;
rb.leftBuffer = (float*)calloc(maxBlockSize,sizeof(float));
rb.rightBuffer = (float*)calloc(maxBlockSize,sizeof(float));
rb.offset = start;
rb.end = trackEnd;
rb.ratio = srSBSMS/srIn;
rb.resampler = new Resampler(resampleCB, &rb);
// Samples in selection
sampleCount samplesIn = end-start;
// Samples for SBSMS to process after resampling
sampleCount samplesToProcess = (sampleCount) ((real)samplesIn*(srSBSMS/srIn));
sampleCount samplesToProcess = (sampleCount) ((float)samplesIn*(srProcess/srTrack));
SlideType outSlideType;
SBSMSResampleCB outResampleCB;
sampleCount processPresamples = 0;
sampleCount trackPresamples = 0;
if(bLinkRatePitch) {
rb.bPitch = true;
outSlideType = rateSlideType;
outResampleCB = resampleCB;
rb.offset = start;
rb.end = end;
rb.iface = new SBSMSInterfaceSliding(&rateSlide,&pitchSlide,
bPitchReferenceInput,
samplesToProcess,0,
NULL);
} else {
rb.bPitch = false;
outSlideType = (srProcess==srTrack?SlideIdentity:SlideConstant);
outResampleCB = postResampleCB;
rb.ratio = srProcess/srTrack;
rb.quality = new SBSMSQuality(&SBSMSQualityStandard);
rb.resampler = new Resampler(resampleCB, &rb, srProcess==srTrack?SlideIdentity:SlideConstant);
rb.sbsms = new SBSMS(rightTrack?2:1,rb.quality,true);
rb.SBSMSBlockSize = rb.sbsms->getInputFrameSize();
rb.SBSMSBuf = (audio*)calloc(rb.SBSMSBlockSize,sizeof(audio));
processPresamples = wxMin(rb.quality->getMaxPresamples(),
(long)((float)(start-trackStart)*(srProcess/srTrack)));
trackPresamples = wxMin(start-trackStart,
(long)((float)(processPresamples)*(srTrack/srProcess)));
rb.offset = start - trackPresamples;
rb.end = trackEnd;
rb.iface = new SBSMSEffectInterface(rb.resampler,
&rateSlide,&pitchSlide,
bPitchReferenceInput,
samplesToProcess,processPresamples,
rb.quality);
}
Resampler resampler(outResampleCB,&rb,outSlideType);
audio outBuf[SBSMSOutBlockSize];
float outBufLeft[2*SBSMSOutBlockSize];
float outBufRight[2*SBSMSOutBlockSize];
// Samples in output after SBSMS
sampleCount samplesToOutput = rb.iface->getSamplesToOutput();
// Samples in output after resampling back
sampleCount samplesToGenerate = (sampleCount) ((real)samplesToProcess * mTotalStretch);
sampleCount samplesOut = (sampleCount) ((real)samplesIn * mTotalStretch);
sampleCount samplesOut = (sampleCount) ((float)samplesToOutput * (srTrack/srProcess));
// Duration in track time
double duration = (mCurT1-mCurT0) * mTotalStretch;
if(duration > maxDuration)
maxDuration = duration;
TimeWarper *warper = NULL;
if (rateStart == rateEnd)
{
warper = new LinearTimeWarper(mCurT0, mCurT0,
mCurT1, mCurT0+maxDuration);
} else
{
warper = new LogarithmicTimeWarper(mCurT0, mCurT1,
rateStart, rateEnd);
}
TimeWarper *warper = createTimeWarper(mCurT0,mCurT1,maxDuration,rateStart,rateEnd,rateSlideType);
SetTimeWarper(warper);
sbsmsInfo si;
si.rs = rb.resampler;
si.samplesToProcess = samplesToProcess;
si.samplesToGenerate = samplesToGenerate;
si.rate0 = rateStart;
si.rate1 = rateEnd;
si.pitch0 = pitchStart;
si.pitch1 = pitchEnd;
sbsms_quality quality = sbsms_quality_fast;
rb.sbsmser = sbsms_create(&samplesCB,&rateCBLinear,&pitchCBLinear,rightTrack?2:1,&quality,bPreAnalyze,true);
rb.outputLeftTrack = mFactory->NewWaveTrack(leftTrack->GetSampleFormat(),
leftTrack->GetRate());
if(rightTrack)
rb.outputRightTrack = mFactory->NewWaveTrack(rightTrack->GetSampleFormat(),
rightTrack->GetRate());
sampleCount blockSize = quality.maxoutframesize;
rb.outBuf = (audio*)calloc(blockSize,sizeof(audio));
rb.outputLeftBuffer = (float*)calloc(blockSize*2,sizeof(float));
if(rightTrack)
rb.outputRightBuffer = (float*)calloc(blockSize*2,sizeof(float));
long pos = 0;
long outputCount = -1;
// pre analysis
real fracPre = 0.0f;
if(bPreAnalyze) {
fracPre = 0.05f;
resampleBuf rbPre;
rbPre.block = maxBlockSize;
rbPre.buf = (audio*)calloc(rb.block,sizeof(audio));
rbPre.leftTrack = leftTrack;
rbPre.rightTrack = rightTrack?rightTrack:leftTrack;
rbPre.leftBuffer = (float*)calloc(maxBlockSize,sizeof(float));
rbPre.rightBuffer = (float*)calloc(maxBlockSize,sizeof(float));
rbPre.offset = start;
rbPre.end = end;
rbPre.ratio = srSBSMS/srIn;
rbPre.resampler = new Resampler(resampleCB, &rbPre);
si.rs = rbPre.resampler;
long pos = 0;
long lastPos = 0;
long ret = 0;
while(lastPos<samplesToProcess) {
ret = sbsms_pre_analyze(&samplesCB,&si,rb.sbsmser);
lastPos = pos;
pos += ret;
real completion = (real)lastPos/(real)samplesToProcess;
if (TrackProgress(0,fracPre*completion))
return false;
}
sbsms_pre_analyze_complete(rb.sbsmser);
sbsms_reset(rb.sbsmser);
si.rs = rb.resampler;
}
// process
while(pos<samplesOut && outputCount) {
outputCount = sbsms_read_frame(rb.outBuf, &si, rb.sbsmser, NULL, NULL);
if(pos+outputCount>samplesOut) {
outputCount = samplesOut - pos;
long frames;
if(pos+SBSMSOutBlockSize>samplesOut) {
frames = samplesOut - pos;
} else {
frames = SBSMSOutBlockSize;
}
outputCount = resampler.read(outBuf,frames);
for(int i = 0; i < outputCount; i++) {
rb.outputLeftBuffer[i] = rb.outBuf[i][0];
outBufLeft[i] = outBuf[i][0];
if(rightTrack)
rb.outputRightBuffer[i] = rb.outBuf[i][1];
outBufRight[i] = outBuf[i][1];
}
pos += outputCount;
rb.outputLeftTrack->Append((samplePtr)rb.outputLeftBuffer, floatSample, outputCount);
rb.outputLeftTrack->Append((samplePtr)outBufLeft, floatSample, outputCount);
if(rightTrack)
rb.outputRightTrack->Append((samplePtr)rb.outputRightBuffer, floatSample, outputCount);
rb.outputRightTrack->Append((samplePtr)outBufRight, floatSample, outputCount);
double frac = (double)pos/(double)samplesOut;
int nWhichTrack = mCurTrackNum;
@@ -356,7 +390,7 @@ bool EffectSBSMS::Process()
frac *= 2.0; // Show twice as far for each track, because we're doing 2 at once.
}
}
if (TrackProgress(nWhichTrack, fracPre + (1.0-fracPre)*frac))
if (TrackProgress(nWhichTrack, frac))
return false;
}
rb.outputLeftTrack->Flush();

View File

@@ -21,22 +21,23 @@
#include "sbsms.h"
using namespace _sbsms_;
class WaveTrack;
class EffectSBSMS : public Effect {
public:
static bool bInit;
virtual bool Process();
void setParameters(double rateStart, double rateEnd, double pitchStart, double pitchEnd, bool bPreAnalyze);
void setParameters(double rateStart, double rateEnd, double pitchStart, double pitchEnd,
SlideType rateSlideType, SlideType pitchSlideType,
bool bLinkRatePitch, bool bRateReferenceInput, bool bPitchReferenceInput);
private:
bool ProcessLabelTrack(Track *track);
double rateStart, rateEnd, pitchStart, pitchEnd;
bool bPreAnalyze;
bool bLinkRatePitch, bRateReferenceInput, bPitchReferenceInput;
SlideType rateSlideType;
SlideType pitchSlideType;
int mCurTrackNum;
double mCurT0;
double mCurT1;
real mTotalStretch;
float mTotalStretch;
};
#endif

View File

@@ -23,6 +23,7 @@
#if USE_SBSMS
#include "TimeScale.h"
#include "sbsms.h"
#include "../ShuttleGui.h"
@@ -103,12 +104,6 @@ bool EffectTimeScale::TransferParameters( Shuttle & shuttle )
return true;
}
inline double InvertedPercentChangeToRatio(double percentChange)
{
//mchinen hack: invert the ratio so it works with the sbsms bug which requires the reciprocal number.
return 1.0/(1.0 + percentChange / 100.0);
}
inline double PercentChangeToRatio(double percentChange)
{
return 1.0 + percentChange / 100.0;
@@ -121,19 +116,16 @@ inline double HalfStepsToPercentChange(double halfSteps)
inline double PercentChangeToHalfSteps(double percentChange)
{
// mchinen: hack: take the negative of this so the correct value is displayed
// (see the InvertedPercentChangeToRatio hack for why this is needed)
return 17.312340490667560888319096172023 * log(PercentChangeToRatio(percentChange));
}
bool EffectTimeScale::Process()
{
// The pitch part of sbsms is backwards, so use an inverted function
double pitchStart = InvertedPercentChangeToRatio(m_PitchPercentChangeStart);
double pitchEnd = InvertedPercentChangeToRatio(m_PitchPercentChangeEnd);
double pitchStart = PercentChangeToRatio(m_PitchPercentChangeStart);
double pitchEnd = PercentChangeToRatio(m_PitchPercentChangeEnd);
double rateStart = PercentChangeToRatio(m_RatePercentChangeStart);
double rateEnd = PercentChangeToRatio(m_RatePercentChangeEnd);
this->EffectSBSMS::setParameters(rateStart,rateEnd,pitchStart,pitchEnd,m_PreAnalyze);
this->EffectSBSMS::setParameters(rateStart,rateEnd,pitchStart,pitchEnd,SlideLinearOutputRate,SlideLinearOutputRate,false,false,false);
return this->EffectSBSMS::Process();
}
@@ -298,23 +290,8 @@ void TimeScaleDialog::PopulateOrExchange(ShuttleGui & S)
}
S.EndStatic();
S.EndMultiColumn();
S.StartStatic(_("Options"));
{
S.StartHorizontalLay(wxEXPAND);
{
S.SetStyle(wxSL_HORIZONTAL);
/* i18n-hint: Transients are sounds like the onset of cymbals or drums.
They can get 'blurred' by sound stretching. This checkbox option
may make them sharper again. */
m_pCheckBox_PreAnalyze = S.Id(ID_CHECKBOX_PREANALYZE)
.AddCheckBox(_("Dynamic Transient Sharpening"), wxT("false"));
}
S.EndHorizontalLay();
}
S.EndStatic();
return;
return;
}
bool TimeScaleDialog::TransferDataToWindow()

View File

@@ -10,7 +10,8 @@
\file TimeWarper.cpp
\brief Contains definitions for IdentityTimeWarper, ShiftTimeWarper,
LinearTimeWarper, LogarithmicTimeWarper classes
LinearTimeWarper, LogarithmicTimeWarper, QuadraticTimeWarper,
Geometric TimeWarper classes
*//*******************************************************************/
@@ -33,14 +34,14 @@ double LinearTimeWarper::Warp(double originalTime) const
return originalTime*mScale + mShift;
}
double LogarithmicTimeWarper::Warp(double originalTime) const
double LinearInputRateTimeWarper::Warp(double originalTime) const
{
double rate = mRateWarper.Warp(originalTime);
return mTStart + mScale*log(rate/mRStart);
}
LogarithmicTimeWarper::LogarithmicTimeWarper(double tStart, double tEnd,
double rStart, double rEnd)
LinearInputRateTimeWarper::LinearInputRateTimeWarper(double tStart, double tEnd,
double rStart, double rEnd)
: mRateWarper(tStart, rStart, tEnd, rEnd), mRStart(rStart),
mTStart(tStart), mScale((tEnd-tStart)/(rEnd-rStart))
{
@@ -48,6 +49,91 @@ LogarithmicTimeWarper::LogarithmicTimeWarper(double tStart, double tEnd,
wxASSERT(tStart < tEnd);
}
double LinearOutputRateTimeWarper::Warp(double originalTime) const
{
double scaledTime = mTimeWarper.Warp(originalTime);
return mTStart + mScale*(sqrt(mC1 + scaledTime * mC2) - mRStart);
}
LinearOutputRateTimeWarper::LinearOutputRateTimeWarper(double tStart, double tEnd,
double rStart, double rEnd)
: mTimeWarper(tStart, 0.0, tEnd, 1.0),
mRStart(rStart), mTStart(tStart),
mScale(2.0*(tEnd-tStart)/(rEnd*rEnd-rStart*rStart)),
mC1(rStart*rStart), mC2(rEnd*rEnd-rStart*rStart)
{
wxASSERT(rStart != rEnd);
wxASSERT(rStart > 0.0);
wxASSERT(rEnd > 0.0);
wxASSERT(tStart < tEnd);
}
double LinearInputStretchTimeWarper::Warp(double originalTime) const
{
double scaledTime = mTimeWarper.Warp(originalTime);
return mTStart + mC1 * scaledTime * (1.0 + mC2 * scaledTime);
}
LinearInputStretchTimeWarper::LinearInputStretchTimeWarper(double tStart, double tEnd,
double rStart, double rEnd)
: mTimeWarper(tStart, 0.0, tEnd, 1.0), mTStart(tStart),
mC1((tEnd-tStart)/rStart), mC2(0.5*(rStart/rEnd - 1.0))
{
wxASSERT(rStart > 0.0);
wxASSERT(rEnd > 0.0);
wxASSERT(tStart < tEnd);
}
double LinearOutputStretchTimeWarper::Warp(double originalTime) const
{
double scaledTime = mTimeWarper.Warp(originalTime);
return mTStart + mC1 * (pow(mC2, scaledTime) - 1.0);
}
LinearOutputStretchTimeWarper::LinearOutputStretchTimeWarper(double tStart, double tEnd,
double rStart, double rEnd)
: mTimeWarper(tStart, 0.0, tEnd, 1.0), mTStart(tStart),
mC1((tEnd-tStart)/(rStart*log(rStart/rEnd))), mC2(rStart/rEnd)
{
wxASSERT(rStart != rEnd);
wxASSERT(rStart > 0.0);
wxASSERT(rEnd > 0.0);
wxASSERT(tStart < tEnd);
}
double GeometricInputTimeWarper::Warp(double originalTime) const
{
double scaledTime = mTimeWarper.Warp(originalTime);
return mTStart + mScale*(pow(mRatio,scaledTime) - 1.0);
}
GeometricInputTimeWarper::GeometricInputTimeWarper(double tStart, double tEnd,
double rStart, double rEnd)
: mTimeWarper(tStart, 0.0, tEnd, 1.0), mTStart(tStart),
mScale((tEnd-tStart)/(log(rStart/rEnd)*rStart)), mRatio(rStart/rEnd)
{
wxASSERT(rStart != rEnd);
wxASSERT(rStart > 0.0);
wxASSERT(rEnd > 0.0);
wxASSERT(tStart < tEnd);
}
double GeometricOutputTimeWarper::Warp(double originalTime) const
{
double scaledTime = mTimeWarper.Warp(originalTime);
return mTStart + mScale*log(mC0 * scaledTime + 1.0);
}
GeometricOutputTimeWarper::GeometricOutputTimeWarper(double tStart, double tEnd,
double rStart, double rEnd)
: mTimeWarper(tStart, 0.0, tEnd, 1.0), mTStart(tStart),
mScale((tEnd-tStart)/(rEnd-rStart)), mC0((rEnd-rStart)/rStart)
{
wxASSERT(rStart > 0.0);
wxASSERT(rEnd > 0.0);
wxASSERT(tStart < tEnd);
}
StepTimeWarper::StepTimeWarper(double tStep, double offset)
: mTStep(tStep), mOffset(offset)
{ }

View File

@@ -10,7 +10,9 @@
\file TimeWarper.h
\brief Contains declarations for TimeWarper, IdentityTimeWarper,
ShiftTimeWarper, LinearTimeWarper, LogarithmicTimeWarper classes
ShiftTimeWarper, LinearTimeWarper, LinearInputRateSlideTimeWarper,
LinearOutputRateSlideTimeWarper, LinearInputInverseRateTimeWarper,
GeometricInputRateTimeWarper, GeometricOutputRateTimeWarper classes
\class TimeWarper
\brief Transforms one point in time to another point. For example, a time
@@ -26,8 +28,20 @@ split points in the input.
\class LinearTimeWarper
\brief Linear scaling, initialised by giving two points on the line
\class LogarithmicTimeWarper
\brief TimeScale - rate varies linearly, so time changes logarithmically.
\class LinearInputRateTimeWarper
\brief TimeScale - rate varies linearly with input
\class LinearOutputRateTimeWarper
\brief TimeScale - rate varies linearly with output
\class LinearInputInverseRateTimeWarper
\brief TimeScale - inverse rate varies linearly with input
\class GeometricInputRateTimeWarper
\brief TimeScale - rate varies geometrically with input
\class GeometricOutputRateTimeWarper
\brief TimeScale - rate varies geometrically with output
\class StepTimeWarper
\brief Like identity but with a jump
@@ -82,7 +96,7 @@ public:
virtual double Warp(double originalTime) const;
};
class LogarithmicTimeWarper : public TimeWarper
class LinearInputRateTimeWarper : public TimeWarper
{
private:
LinearTimeWarper mRateWarper;
@@ -90,8 +104,75 @@ private:
double mTStart;
double mScale;
public:
LogarithmicTimeWarper(double tStart, double tEnd,
double rStart, double rEnd);
LinearInputRateTimeWarper(double tStart, double tEnd,
double rStart, double rEnd);
virtual double Warp(double originalTime) const;
};
class LinearOutputRateTimeWarper : public TimeWarper
{
private:
LinearTimeWarper mTimeWarper;
double mRStart;
double mTStart;
double mScale;
double mC1;
double mC2;
public:
LinearOutputRateTimeWarper(double tStart, double tEnd,
double rStart, double rEnd);
virtual double Warp(double originalTime) const;
};
class LinearInputStretchTimeWarper : public TimeWarper
{
private:
LinearTimeWarper mTimeWarper;
double mTStart;
double mC1;
double mC2;
public:
LinearInputStretchTimeWarper(double tStart, double tEnd,
double rStart, double rEnd);
virtual double Warp(double originalTime) const;
};
class LinearOutputStretchTimeWarper : public TimeWarper
{
private:
LinearTimeWarper mTimeWarper;
double mTStart;
double mC1;
double mC2;
public:
LinearOutputStretchTimeWarper(double tStart, double tEnd,
double rStart, double rEnd);
virtual double Warp(double originalTime) const;
};
class GeometricInputTimeWarper : public TimeWarper
{
private:
LinearTimeWarper mTimeWarper;
double mTStart;
double mScale;
double mRatio;
public:
GeometricInputTimeWarper(double tStart, double tEnd,
double rStart, double rEnd);
virtual double Warp(double originalTime) const;
};
class GeometricOutputTimeWarper : public TimeWarper
{
private:
LinearTimeWarper mTimeWarper;
double mTStart;
double mScale;
double mC0;
public:
GeometricOutputTimeWarper(double tStart, double tEnd,
double rStart, double rEnd);
virtual double Warp(double originalTime) const;
};