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:
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
{ }
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user