mirror of
https://github.com/cookiengineer/audacity
synced 2026-02-05 19:21:59 +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,225 +6,244 @@
|
||||
#include "real.h"
|
||||
#include "utils.h"
|
||||
#include <algorithm>
|
||||
#include "buffer.h"
|
||||
using namespace std;
|
||||
|
||||
namespace _sbsms_ {
|
||||
|
||||
#define SBSMS_RESAMPLE_CHUNK_SIZE 8192L
|
||||
enum { resampleChunkSize = 8192 };
|
||||
|
||||
class ResamplerImp {
|
||||
public:
|
||||
ResamplerImp(SBSMSResampleCB func, void *data, SlideType slideType = SlideConstant);
|
||||
~ResamplerImp();
|
||||
inline long read(audio *audioOut, long frames);
|
||||
inline void writingComplete();
|
||||
inline void reset();
|
||||
inline void init();
|
||||
inline long samplesInOutput();
|
||||
protected:
|
||||
SBSMSFrame frame;
|
||||
SampleCountType startAbs;
|
||||
SampleCountType midAbs;
|
||||
float midAbsf;
|
||||
SampleCountType endAbs;
|
||||
SampleCountType writePosAbs;
|
||||
bool bInput;
|
||||
SampleBuf *out;
|
||||
SBSMSResampleCB cb;
|
||||
void *data;
|
||||
long inOffset;
|
||||
SlideType slideType;
|
||||
Slide *slide;
|
||||
bool bWritingComplete;
|
||||
};
|
||||
|
||||
Resampler :: Resampler(sbsms_resample_cb cb, void *data)
|
||||
Resampler :: Resampler(SBSMSResampleCB cb, void *data, SlideType slideType)
|
||||
{
|
||||
imp = new ResamplerImp(cb,data,slideType);
|
||||
}
|
||||
|
||||
ResamplerImp :: ResamplerImp(SBSMSResampleCB cb, void *data, SlideType slideType)
|
||||
{
|
||||
init();
|
||||
this->cb = cb;
|
||||
this->data = data;
|
||||
bPull = true;
|
||||
bInput = true;
|
||||
this->slideType = slideType;
|
||||
frame.size = 0;
|
||||
}
|
||||
|
||||
Resampler :: Resampler(SampleBuf *in, real ratio)
|
||||
{
|
||||
init();
|
||||
bPull = false;
|
||||
this->in = in;
|
||||
bInput = true;
|
||||
frame.ratio0 = ratio;
|
||||
frame.ratio1 = ratio;
|
||||
}
|
||||
|
||||
void Resampler :: init()
|
||||
void ResamplerImp :: init()
|
||||
{
|
||||
inOffset = 0;
|
||||
startAbs = 0;
|
||||
midAbs = 0;
|
||||
endAbs = 0;
|
||||
writePosAbs = 0;
|
||||
midAbsf = 0.0;
|
||||
midAbsf = 0.0f;
|
||||
out = new SampleBuf(0);
|
||||
slide = NULL;
|
||||
bWritingComplete = false;
|
||||
sincZeros = SBSMS_SINC_SAMPLES;
|
||||
}
|
||||
|
||||
void Resampler :: reset()
|
||||
void Resampler :: reset() { imp->reset(); }
|
||||
void ResamplerImp :: reset()
|
||||
{
|
||||
if(slide) delete slide;
|
||||
delete out;
|
||||
init();
|
||||
frame.size = 0;
|
||||
bInput = true;
|
||||
}
|
||||
|
||||
long Resampler :: read(audio *audioOut, long samples)
|
||||
Resampler :: ~Resampler() { delete imp; }
|
||||
ResamplerImp :: ~ResamplerImp()
|
||||
{
|
||||
if(!bPull) {
|
||||
frame.in = in->getReadBuf();
|
||||
frame.size = in->n_readable();
|
||||
if(frame.size) bInput = true;
|
||||
}
|
||||
if(slide) delete slide;
|
||||
delete out;
|
||||
}
|
||||
|
||||
long nRead = out->n_readable();
|
||||
void updateSlide(Slide *slide, float *f, float *scale, int *maxDist, float *ratio)
|
||||
{
|
||||
float stretch = slide->getStretch();
|
||||
slide->step();
|
||||
if(stretch <= 1.0f) {
|
||||
*f = resampleSincRes;
|
||||
*scale = stretch;
|
||||
*maxDist = lrintf(resampleSincSamples);
|
||||
} else {
|
||||
*f = resampleSincRes / stretch;
|
||||
*scale = 1.0f;
|
||||
*maxDist = lrintf(resampleSincSamples * stretch);
|
||||
}
|
||||
*ratio = stretch;
|
||||
}
|
||||
|
||||
long Resampler :: read(audio *audioOut, long samples) { return imp->read(audioOut,samples); }
|
||||
long ResamplerImp :: read(audio *audioOut, long samples)
|
||||
{
|
||||
long nRead = out->nReadable();
|
||||
while(nRead < samples && bInput) {
|
||||
if(bInput && inOffset == frame.size) {
|
||||
if(!bPull) {
|
||||
in->advance(frame.size);
|
||||
bInput = false;
|
||||
cb(data,&frame);
|
||||
if(frame.size) {
|
||||
if(slide) delete slide;
|
||||
slide = new Slide(slideType,1.0f/frame.ratio0,1.0f/frame.ratio1,frame.size);
|
||||
} else {
|
||||
cb(data,&frame);
|
||||
if(!frame.size)
|
||||
bWritingComplete = true;
|
||||
bWritingComplete = true;
|
||||
}
|
||||
if(bWritingComplete) {
|
||||
bInput = false;
|
||||
out->grow(midAbs - writePosAbs);
|
||||
out->writePos += midAbs - writePosAbs;
|
||||
out->delay = 0;
|
||||
bInput = false;
|
||||
long n = (long)(midAbs - writePosAbs);
|
||||
out->grow(n);
|
||||
out->writePos += n;
|
||||
}
|
||||
inOffset = 0;
|
||||
}
|
||||
if(frame.size) {
|
||||
real dratio = 1.0f/(real)frame.size*(frame.ratio1-frame.ratio0);
|
||||
|
||||
real ratio = frame.ratio0 + (real)inOffset*dratio;
|
||||
real ratiorec = ratio<1.0f?1.0f:1.0f/ratio;
|
||||
real f = ratiorec*SBSMS_SINC_RES;
|
||||
int fi = round2int(f-0.5f);
|
||||
real ff = f - fi;
|
||||
if(ff<0.0f) {
|
||||
ff += 1.0f;
|
||||
fi--;
|
||||
}
|
||||
real scale = ratio<1.0f?ratio:1.0f;
|
||||
int maxDist = round2int(sincZeros*ratio-0.5f);
|
||||
// absolute start position
|
||||
startAbs = max((long)0,midAbs-maxDist);
|
||||
// samples to advance
|
||||
long advance = max((long)0,startAbs - maxDist - writePosAbs);
|
||||
writePosAbs += advance;
|
||||
// absolute end position
|
||||
endAbs = midAbs + maxDist;
|
||||
// starting position in output
|
||||
int start = startAbs - writePosAbs;
|
||||
assert(start>=0);
|
||||
// zero point in output
|
||||
int mid = midAbs - writePosAbs;
|
||||
// ending position in output
|
||||
int end = endAbs - writePosAbs;
|
||||
// provide extra delay for variable rate ratio
|
||||
out->delay = maxDist<<1;
|
||||
out->writePos += advance;
|
||||
|
||||
if(fabs(ratio-1.0f) < 1e-6f && fabs(dratio) < 1e-8f ) {
|
||||
// how far ahead to write
|
||||
int nAhead = mid+frame.size;
|
||||
out->N = nAhead;
|
||||
out->grow(nAhead);
|
||||
long nWrite = min(SBSMS_RESAMPLE_CHUNK_SIZE,frame.size-inOffset);
|
||||
for(int j=0;j<nWrite;j++) {
|
||||
out->buf[out->writePos+mid+j][0] += frame.in[j+inOffset][0];
|
||||
out->buf[out->writePos+mid+j][1] += frame.in[j+inOffset][1];
|
||||
}
|
||||
inOffset += nWrite;
|
||||
midAbsf += ratio*nWrite;
|
||||
int nWritten = round2int(midAbsf);
|
||||
midAbsf -= nWritten;
|
||||
midAbs += nWritten;
|
||||
if(slideType == SlideIdentity) {
|
||||
out->write(frame.buf,frame.size);
|
||||
inOffset = frame.size;
|
||||
} else {
|
||||
long nWrite = min(SBSMS_RESAMPLE_CHUNK_SIZE,frame.size-inOffset);
|
||||
audio *i = &(frame.in[inOffset]);
|
||||
for(int j=0;j<nWrite;j++) {
|
||||
// how far ahead to write
|
||||
int nAhead = end;
|
||||
out->N = nAhead;
|
||||
out->grow(nAhead);
|
||||
audio *o = &(out->buf[out->writePos+start]);
|
||||
real d = (start-mid-midAbsf)*f;
|
||||
int di = round2int(d-0.5f);
|
||||
real df = d-di;
|
||||
if(df<0.0f) {
|
||||
df += 1.0f;
|
||||
di--;
|
||||
}
|
||||
real i0 = (*i)[0];
|
||||
real i1 = (*i)[1];
|
||||
for(int k=start;k<end;k++) {
|
||||
int k0 = (di<0)?-di:di;
|
||||
int k1 = (di<0)?k0-1:k0+1;
|
||||
real sinc;
|
||||
if(k1>=SBSMS_SINC_SIZE) {
|
||||
if(k0>=SBSMS_SINC_SIZE) {
|
||||
sinc = 0.0f;
|
||||
} else {
|
||||
sinc = scale*sincTable[k0];
|
||||
}
|
||||
} else if(k0>=SBSMS_SINC_SIZE) {
|
||||
sinc = scale*sincTable[k1];
|
||||
} else {
|
||||
sinc = scale*((1.0f-df)*sincTable[k0] + df*sincTable[k1]);
|
||||
}
|
||||
(*o)[0] += i0 * sinc;
|
||||
(*o)[1] += i1 * sinc;
|
||||
di += fi;
|
||||
df += ff;
|
||||
if(!(df<1.0f)) {
|
||||
df -= 1.0f;
|
||||
di++;
|
||||
}
|
||||
o++;
|
||||
}
|
||||
i++;
|
||||
midAbsf += ratio;
|
||||
|
||||
if(dratio != 0.0f) {
|
||||
ratio += dratio;
|
||||
ratiorec = ratio<1.0f?1.0f:1.0f/ratio;
|
||||
f = ratiorec*SBSMS_SINC_RES;
|
||||
fi = round2int(f-0.5f);
|
||||
ff = f - fi;
|
||||
if(ff<0.0f) {
|
||||
ff += 1.0f;
|
||||
fi--;
|
||||
}
|
||||
scale = ratio<1.0f?ratio:1.0f;
|
||||
maxDist = round2int(sincZeros*ratio-0.5f);
|
||||
}
|
||||
|
||||
int nWritten = round2int(midAbsf);
|
||||
midAbsf -= nWritten;
|
||||
midAbs += nWritten;
|
||||
startAbs = max((long)0,midAbs-maxDist);
|
||||
endAbs = midAbs + maxDist;
|
||||
start = startAbs - writePosAbs;
|
||||
mid = midAbs - writePosAbs;
|
||||
end = endAbs - writePosAbs;
|
||||
}
|
||||
inOffset += nWrite;
|
||||
bool bNoSinc = false;
|
||||
if(fabs(frame.ratio0-1.0f) < 1e-6f &&
|
||||
fabs((float)(frame.ratio1 - frame.ratio0)/(float)frame.size) < 1e-9f) {
|
||||
bNoSinc = true;
|
||||
}
|
||||
float f;
|
||||
float scale;
|
||||
int maxDist;
|
||||
float ratio;
|
||||
updateSlide(slide,&f,&scale,&maxDist,&ratio);
|
||||
int fi = lrintf(f);
|
||||
float ff = f - fi;
|
||||
if(ff<0.0f) {
|
||||
ff += 1.0f;
|
||||
fi--;
|
||||
}
|
||||
startAbs = max((SampleCountType)0,midAbs-maxDist);
|
||||
long advance = max(0L,(long)(startAbs - maxDist - writePosAbs));
|
||||
writePosAbs += advance;
|
||||
endAbs = midAbs + maxDist;
|
||||
long start = (long)(startAbs - writePosAbs);
|
||||
long mid = (long)(midAbs - writePosAbs);
|
||||
long end = (long)(endAbs - writePosAbs);
|
||||
out->writePos += advance;
|
||||
if(bNoSinc) {
|
||||
int nAhead = mid+frame.size;
|
||||
out->N = nAhead;
|
||||
out->grow(nAhead);
|
||||
long nWrite = min((long)resampleChunkSize,frame.size-inOffset);
|
||||
for(int j=0;j<nWrite;j++) {
|
||||
out->buf[out->writePos+mid+j][0] += frame.buf[j+inOffset][0];
|
||||
out->buf[out->writePos+mid+j][1] += frame.buf[j+inOffset][1];
|
||||
}
|
||||
inOffset += nWrite;
|
||||
midAbsf += nWrite;
|
||||
int nWritten = lrintf(midAbsf);
|
||||
midAbsf -= nWritten;
|
||||
midAbs += nWritten;
|
||||
} else {
|
||||
long nWrite = min((long)resampleChunkSize,frame.size-inOffset);
|
||||
audio *i = &(frame.buf[inOffset]);
|
||||
for(int j=0;j<nWrite;j++) {
|
||||
int nAhead = end;
|
||||
out->N = nAhead;
|
||||
out->grow(nAhead);
|
||||
audio *o = &(out->buf[out->writePos+start]);
|
||||
float d = (start-mid-midAbsf)*f;
|
||||
int di = lrintf(d);
|
||||
float df = d-di;
|
||||
if(df<0.0f) {
|
||||
df += 1.0f;
|
||||
di--;
|
||||
}
|
||||
float i0 = (*i)[0];
|
||||
float i1 = (*i)[1];
|
||||
for(int k=start;k<end;k++) {
|
||||
int k0 = (di<0)?-di:di;
|
||||
int k1 = (di<0)?k0-1:k0+1;
|
||||
float sinc;
|
||||
if(k1>=resampleSincSize) {
|
||||
if(k0>=resampleSincSize) {
|
||||
sinc = 0.0f;
|
||||
} else {
|
||||
sinc = scale*sincTable[k0];
|
||||
}
|
||||
} else if(k0>=resampleSincSize) {
|
||||
sinc = scale*sincTable[k1];
|
||||
} else {
|
||||
sinc = scale*((1.0f-df)*sincTable[k0] + df*sincTable[k1]);
|
||||
}
|
||||
(*o)[0] += i0 * sinc;
|
||||
(*o)[1] += i1 * sinc;
|
||||
di += fi;
|
||||
df += ff;
|
||||
if(!(df<1.0f)) {
|
||||
df -= 1.0f;
|
||||
di++;
|
||||
}
|
||||
o++;
|
||||
}
|
||||
i++;
|
||||
updateSlide(slide,&f,&scale,&maxDist,&ratio);
|
||||
fi = lrintf(f);
|
||||
ff = f - fi;
|
||||
if(ff<0.0f) {
|
||||
ff += 1.0f;
|
||||
fi--;
|
||||
}
|
||||
midAbsf += ratio;
|
||||
int nWritten = lrintf(midAbsf);
|
||||
midAbsf -= nWritten;
|
||||
midAbs += nWritten;
|
||||
startAbs = max((SampleCountType)0,midAbs-maxDist);
|
||||
endAbs = midAbs + maxDist;
|
||||
start = (long)(startAbs - writePosAbs);
|
||||
mid = (long)(midAbs - writePosAbs);
|
||||
end = (long)(endAbs - writePosAbs);
|
||||
}
|
||||
inOffset += nWrite;
|
||||
}
|
||||
}
|
||||
nRead = out->nReadable();
|
||||
}
|
||||
nRead = out->n_readable();
|
||||
}
|
||||
|
||||
nRead = min(samples,out->n_readable());
|
||||
if(nRead) {
|
||||
out->read(audioOut,nRead);
|
||||
out->advance(nRead);
|
||||
}
|
||||
|
||||
return nRead;
|
||||
out->read(audioOut,samples);
|
||||
return samples;
|
||||
}
|
||||
|
||||
long Resampler :: samplesInOutput()
|
||||
long Resampler :: samplesInOutput() { return imp->samplesInOutput(); }
|
||||
long ResamplerImp :: samplesInOutput()
|
||||
{
|
||||
long samplesFromBuffer = round2int(0.5f*(frame.ratio0+frame.ratio1)*(frame.size-inOffset));
|
||||
return out->writePos + midAbs - writePosAbs - out->readPos + samplesFromBuffer;
|
||||
long samplesFromBuffer = lrintf(0.5f*(frame.ratio0+frame.ratio1)*(frame.size-inOffset));
|
||||
return (long)(out->writePos + (midAbs - writePosAbs) - out->readPos + samplesFromBuffer);
|
||||
}
|
||||
|
||||
void Resampler :: writingComplete()
|
||||
void ResamplerImp :: writingComplete()
|
||||
{
|
||||
bWritingComplete = true;
|
||||
}
|
||||
|
||||
Resampler :: ~Resampler()
|
||||
{
|
||||
delete out;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user