- Improved SoundTouch::flush() so that it produces exactly accurate number of output samples.

- Changed 'float' variables into 'double' for more precise calculation of input-vs-output samples.
This commit is contained in:
oparviai 2015-07-26 14:45:48 +00:00
parent da748228b9
commit c9507ff7f1
10 changed files with 131 additions and 84 deletions

View File

@ -151,17 +151,24 @@ private:
class TDStretch *pTDStretch; class TDStretch *pTDStretch;
/// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters. /// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters.
float virtualRate; double virtualRate;
/// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters. /// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters.
float virtualTempo; double virtualTempo;
/// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters. /// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters.
float virtualPitch; double virtualPitch;
/// Flag: Has sample rate been set? /// Flag: Has sample rate been set?
bool bSrateSet; bool bSrateSet;
/// Accumulator for how many samples in total will be expected as output vs. samples put in,
/// considering current processing settings.
double samplesExpectedOut;
/// Accumulator for how many samples in total have been read out from the processing so far
long samplesOutput;
/// Calculates effective rate & tempo valuescfrom 'virtualRate', 'virtualTempo' and /// Calculates effective rate & tempo valuescfrom 'virtualRate', 'virtualTempo' and
/// 'virtualPitch' parameters. /// 'virtualPitch' parameters.
void calcEffectiveRateAndTempo(); void calcEffectiveRateAndTempo();
@ -171,10 +178,10 @@ protected :
uint channels; uint channels;
/// Effective 'rate' value calculated from 'virtualRate', 'virtualTempo' and 'virtualPitch' /// Effective 'rate' value calculated from 'virtualRate', 'virtualTempo' and 'virtualPitch'
float rate; double rate;
/// Effective 'tempo' value calculated from 'virtualRate', 'virtualTempo' and 'virtualPitch' /// Effective 'tempo' value calculated from 'virtualRate', 'virtualTempo' and 'virtualPitch'
float tempo; double tempo;
public: public:
SoundTouch(); SoundTouch();
@ -188,32 +195,32 @@ public:
/// Sets new rate control value. Normal rate = 1.0, smaller values /// Sets new rate control value. Normal rate = 1.0, smaller values
/// represent slower rate, larger faster rates. /// represent slower rate, larger faster rates.
void setRate(float newRate); void setRate(double newRate);
/// Sets new tempo control value. Normal tempo = 1.0, smaller values /// Sets new tempo control value. Normal tempo = 1.0, smaller values
/// represent slower tempo, larger faster tempo. /// represent slower tempo, larger faster tempo.
void setTempo(float newTempo); void setTempo(double newTempo);
/// Sets new rate control value as a difference in percents compared /// Sets new rate control value as a difference in percents compared
/// to the original rate (-50 .. +100 %) /// to the original rate (-50 .. +100 %)
void setRateChange(float newRate); void setRateChange(double newRate);
/// Sets new tempo control value as a difference in percents compared /// Sets new tempo control value as a difference in percents compared
/// to the original tempo (-50 .. +100 %) /// to the original tempo (-50 .. +100 %)
void setTempoChange(float newTempo); void setTempoChange(double newTempo);
/// Sets new pitch control value. Original pitch = 1.0, smaller values /// Sets new pitch control value. Original pitch = 1.0, smaller values
/// represent lower pitches, larger values higher pitch. /// represent lower pitches, larger values higher pitch.
void setPitch(float newPitch); void setPitch(double newPitch);
/// Sets pitch change in octaves compared to the original pitch /// Sets pitch change in octaves compared to the original pitch
/// (-1.00 .. +1.00) /// (-1.00 .. +1.00)
void setPitchOctaves(float newPitch); void setPitchOctaves(double newPitch);
/// Sets pitch change in semi-tones compared to the original pitch /// Sets pitch change in semi-tones compared to the original pitch
/// (-12 .. +12) /// (-12 .. +12)
void setPitchSemiTones(int newPitch); void setPitchSemiTones(int newPitch);
void setPitchSemiTones(float newPitch); void setPitchSemiTones(double newPitch);
/// Sets the number of channels, 1 = mono, 2 = stereo /// Sets the number of channels, 1 = mono, 2 = stereo
void setChannels(uint numChannels); void setChannels(uint numChannels);
@ -240,6 +247,23 @@ public:
///< contains data for both channels. ///< contains data for both channels.
); );
/// Output samples from beginning of the sample buffer. Copies requested samples to
/// output buffer and removes them from the sample buffer. If there are less than
/// 'numsample' samples in the buffer, returns all that available.
///
/// \return Number of samples returned.
virtual uint receiveSamples(SAMPLETYPE *output, ///< Buffer where to copy output samples.
uint maxSamples ///< How many samples to receive at max.
);
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
/// sample buffer without copying them anywhere.
///
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
/// with 'ptrBegin' function.
virtual uint receiveSamples(uint maxSamples ///< Remove this many samples from the beginning of pipe.
);
/// Clears all the samples in the object's output and internal processing /// Clears all the samples in the object's output and internal processing
/// buffers. /// buffers.
virtual void clear(); virtual void clear();

View File

@ -56,7 +56,7 @@ protected:
const SAMPLETYPE *src, const SAMPLETYPE *src,
int &srcSamples); int &srcSamples);
float fract; double fract;
public: public:
InterpolateCubic(); InterpolateCubic();

View File

@ -170,9 +170,9 @@ int InterpolateLinearInteger::transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE
// Sets new target iRate. Normal iRate = 1.0, smaller values represent slower // Sets new target iRate. Normal iRate = 1.0, smaller values represent slower
// iRate, larger faster iRates. // iRate, larger faster iRates.
void InterpolateLinearInteger::setRate(float newRate) void InterpolateLinearInteger::setRate(double newRate)
{ {
iRate = (int)(newRate * SCALE + 0.5f); iRate = (int)(newRate * SCALE + 0.5);
TransposerBase::setRate(newRate); TransposerBase::setRate(newRate);
} }
@ -190,7 +190,7 @@ InterpolateLinearFloat::InterpolateLinearFloat() : TransposerBase()
// Notice: use local function calling syntax for sake of clarity, // Notice: use local function calling syntax for sake of clarity,
// to indicate the fact that C++ constructor can't call virtual functions. // to indicate the fact that C++ constructor can't call virtual functions.
resetRegisters(); resetRegisters();
setRate(1.0f); setRate(1.0);
} }
@ -275,12 +275,13 @@ int InterpolateLinearFloat::transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE *s
i = 0; i = 0;
while (srcCount < srcSampleEnd) while (srcCount < srcSampleEnd)
{ {
float temp, vol1; float temp, vol1, fract_float;
vol1 = (1.0f- fract); vol1 = (float)(1.0 - fract);
fract_float = (float)fract;
for (int c = 0; c < numChannels; c ++) for (int c = 0; c < numChannels; c ++)
{ {
temp = vol1 * src[c] + fract * src[c + numChannels]; temp = vol1 * src[c] + fract_float * src[c + numChannels];
*dest = (SAMPLETYPE)temp; *dest = (SAMPLETYPE)temp;
dest ++; dest ++;
} }

View File

@ -63,7 +63,7 @@ public:
/// Sets new target rate. Normal rate = 1.0, smaller values represent slower /// Sets new target rate. Normal rate = 1.0, smaller values represent slower
/// rate, larger faster rates. /// rate, larger faster rates.
virtual void setRate(float newRate); virtual void setRate(double newRate);
}; };
@ -71,7 +71,7 @@ public:
class InterpolateLinearFloat : public TransposerBase class InterpolateLinearFloat : public TransposerBase
{ {
protected: protected:
float fract; double fract;
virtual void resetRegisters(); virtual void resetRegisters();

View File

@ -61,7 +61,7 @@ protected:
const SAMPLETYPE *src, const SAMPLETYPE *src,
int &srcSamples); int &srcSamples);
float fract; double fract;
public: public:
InterpolateShannon(); InterpolateShannon();

View File

@ -97,20 +97,20 @@ AAFilter *RateTransposer::getAAFilter()
// Sets new target iRate. Normal iRate = 1.0, smaller values represent slower // Sets new target iRate. Normal iRate = 1.0, smaller values represent slower
// iRate, larger faster iRates. // iRate, larger faster iRates.
void RateTransposer::setRate(float newRate) void RateTransposer::setRate(double newRate)
{ {
double fCutoff; double fCutoff;
pTransposer->setRate(newRate); pTransposer->setRate(newRate);
// design a new anti-alias filter // design a new anti-alias filter
if (newRate > 1.0f) if (newRate > 1.0)
{ {
fCutoff = 0.5f / newRate; fCutoff = 0.5 / newRate;
} }
else else
{ {
fCutoff = 0.5f * newRate; fCutoff = 0.5 * newRate;
} }
pAAFilter->setCutoffFreq(fCutoff); pAAFilter->setCutoffFreq(fCutoff);
} }
@ -225,7 +225,7 @@ void TransposerBase::setAlgorithm(TransposerBase::ALGORITHM a)
int TransposerBase::transpose(FIFOSampleBuffer &dest, FIFOSampleBuffer &src) int TransposerBase::transpose(FIFOSampleBuffer &dest, FIFOSampleBuffer &src)
{ {
int numSrcSamples = src.numSamples(); int numSrcSamples = src.numSamples();
int sizeDemand = (int)((float)numSrcSamples / rate) + 8; int sizeDemand = (int)((double)numSrcSamples / rate) + 8;
int numOutput; int numOutput;
SAMPLETYPE *psrc = src.ptrBegin(); SAMPLETYPE *psrc = src.ptrBegin();
SAMPLETYPE *pdest = dest.ptrEnd(sizeDemand); SAMPLETYPE *pdest = dest.ptrEnd(sizeDemand);
@ -270,7 +270,7 @@ void TransposerBase::setChannels(int channels)
} }
void TransposerBase::setRate(float newRate) void TransposerBase::setRate(double newRate)
{ {
rate = newRate; rate = newRate;
} }

View File

@ -81,14 +81,14 @@ protected:
static ALGORITHM algorithm; static ALGORITHM algorithm;
public: public:
float rate; double rate;
int numChannels; int numChannels;
TransposerBase(); TransposerBase();
virtual ~TransposerBase(); virtual ~TransposerBase();
virtual int transpose(FIFOSampleBuffer &dest, FIFOSampleBuffer &src); virtual int transpose(FIFOSampleBuffer &dest, FIFOSampleBuffer &src);
virtual void setRate(float newRate); virtual void setRate(double newRate);
virtual void setChannels(int channels); virtual void setChannels(int channels);
// static factory function // static factory function
@ -158,7 +158,7 @@ public:
/// Sets new target rate. Normal rate = 1.0, smaller values represent slower /// Sets new target rate. Normal rate = 1.0, smaller values represent slower
/// rate, larger faster rates. /// rate, larger faster rates.
virtual void setRate(float newRate); virtual void setRate(double newRate);
/// Sets the number of channels, 1 = mono, 2 = stereo /// Sets the number of channels, 1 = mono, 2 = stereo
void setChannels(int channels); void setChannels(int channels);

View File

@ -110,6 +110,9 @@ SoundTouch::SoundTouch()
calcEffectiveRateAndTempo(); calcEffectiveRateAndTempo();
samplesExpectedOut = 0;
samplesOutput = 0;
channels = 0; channels = 0;
bSrateSet = false; bSrateSet = false;
} }
@ -157,7 +160,7 @@ void SoundTouch::setChannels(uint numChannels)
// Sets new rate control value. Normal rate = 1.0, smaller values // Sets new rate control value. Normal rate = 1.0, smaller values
// represent slower rate, larger faster rates. // represent slower rate, larger faster rates.
void SoundTouch::setRate(float newRate) void SoundTouch::setRate(double newRate)
{ {
virtualRate = newRate; virtualRate = newRate;
calcEffectiveRateAndTempo(); calcEffectiveRateAndTempo();
@ -167,9 +170,9 @@ void SoundTouch::setRate(float newRate)
// Sets new rate control value as a difference in percents compared // Sets new rate control value as a difference in percents compared
// to the original rate (-50 .. +100 %) // to the original rate (-50 .. +100 %)
void SoundTouch::setRateChange(float newRate) void SoundTouch::setRateChange(double newRate)
{ {
virtualRate = 1.0f + 0.01f * newRate; virtualRate = 1.0 + 0.01 * newRate;
calcEffectiveRateAndTempo(); calcEffectiveRateAndTempo();
} }
@ -177,7 +180,7 @@ void SoundTouch::setRateChange(float newRate)
// Sets new tempo control value. Normal tempo = 1.0, smaller values // Sets new tempo control value. Normal tempo = 1.0, smaller values
// represent slower tempo, larger faster tempo. // represent slower tempo, larger faster tempo.
void SoundTouch::setTempo(float newTempo) void SoundTouch::setTempo(double newTempo)
{ {
virtualTempo = newTempo; virtualTempo = newTempo;
calcEffectiveRateAndTempo(); calcEffectiveRateAndTempo();
@ -187,9 +190,9 @@ void SoundTouch::setTempo(float newTempo)
// Sets new tempo control value as a difference in percents compared // Sets new tempo control value as a difference in percents compared
// to the original tempo (-50 .. +100 %) // to the original tempo (-50 .. +100 %)
void SoundTouch::setTempoChange(float newTempo) void SoundTouch::setTempoChange(double newTempo)
{ {
virtualTempo = 1.0f + 0.01f * newTempo; virtualTempo = 1.0 + 0.01 * newTempo;
calcEffectiveRateAndTempo(); calcEffectiveRateAndTempo();
} }
@ -197,7 +200,7 @@ void SoundTouch::setTempoChange(float newTempo)
// Sets new pitch control value. Original pitch = 1.0, smaller values // Sets new pitch control value. Original pitch = 1.0, smaller values
// represent lower pitches, larger values higher pitch. // represent lower pitches, larger values higher pitch.
void SoundTouch::setPitch(float newPitch) void SoundTouch::setPitch(double newPitch)
{ {
virtualPitch = newPitch; virtualPitch = newPitch;
calcEffectiveRateAndTempo(); calcEffectiveRateAndTempo();
@ -207,9 +210,9 @@ void SoundTouch::setPitch(float newPitch)
// Sets pitch change in octaves compared to the original pitch // Sets pitch change in octaves compared to the original pitch
// (-1.00 .. +1.00) // (-1.00 .. +1.00)
void SoundTouch::setPitchOctaves(float newPitch) void SoundTouch::setPitchOctaves(double newPitch)
{ {
virtualPitch = (float)exp(0.69314718056f * newPitch); virtualPitch = exp(0.69314718056 * newPitch);
calcEffectiveRateAndTempo(); calcEffectiveRateAndTempo();
} }
@ -219,14 +222,14 @@ void SoundTouch::setPitchOctaves(float newPitch)
// (-12 .. +12) // (-12 .. +12)
void SoundTouch::setPitchSemiTones(int newPitch) void SoundTouch::setPitchSemiTones(int newPitch)
{ {
setPitchOctaves((float)newPitch / 12.0f); setPitchOctaves((double)newPitch / 12.0);
} }
void SoundTouch::setPitchSemiTones(float newPitch) void SoundTouch::setPitchSemiTones(double newPitch)
{ {
setPitchOctaves(newPitch / 12.0f); setPitchOctaves(newPitch / 12.0);
} }
@ -234,8 +237,8 @@ void SoundTouch::setPitchSemiTones(float newPitch)
// nominal control values. // nominal control values.
void SoundTouch::calcEffectiveRateAndTempo() void SoundTouch::calcEffectiveRateAndTempo()
{ {
float oldTempo = tempo; double oldTempo = tempo;
float oldRate = rate; double oldRate = rate;
tempo = virtualTempo / virtualPitch; tempo = virtualTempo / virtualPitch;
rate = virtualPitch * virtualRate; rate = virtualPitch * virtualRate;
@ -317,8 +320,13 @@ void SoundTouch::putSamples(const SAMPLETYPE *samples, uint nSamples)
pTDStretch->putSamples(samples, nSamples); pTDStretch->putSamples(samples, nSamples);
} }
*/ */
// accumulate how many samples are expected out from processing, given the current
// processing setting
samplesExpectedOut += (double)nSamples / ((double)rate * (double)tempo);
#ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER #ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
else if (rate <= 1.0f) if (rate <= 1.0f)
{ {
// transpose the rate down, output the transposed sound to tempo changer buffer // transpose the rate down, output the transposed sound to tempo changer buffer
assert(output == pTDStretch); assert(output == pTDStretch);
@ -346,44 +354,30 @@ void SoundTouch::putSamples(const SAMPLETYPE *samples, uint nSamples)
void SoundTouch::flush() void SoundTouch::flush()
{ {
int i; int i;
int nUnprocessed; int numStillExpected;
int nOut; SAMPLETYPE *buff = new SAMPLETYPE[128 * channels];
SAMPLETYPE *buff = new SAMPLETYPE[64 * channels];
// check how many samples still await processing, and scale // how many samples are still expected to output
// that by tempo & rate to get expected output sample count numStillExpected = (int)((long)(samplesExpectedOut + 0.5) - samplesOutput);
nUnprocessed = numUnprocessedSamples();
nUnprocessed = (int)((double)nUnprocessed / (tempo * rate) + 0.5);
nOut = numSamples(); // ready samples currently in buffer ... memset(buff, 0, 128 * channels * sizeof(SAMPLETYPE));
nOut += nUnprocessed; // ... and how many we expect there to be in the end
memset(buff, 0, 64 * channels * sizeof(SAMPLETYPE));
// "Push" the last active samples out from the processing pipeline by // "Push" the last active samples out from the processing pipeline by
// feeding blank samples into the processing pipeline until new, // feeding blank samples into the processing pipeline until new,
// processed samples appear in the output (not however, more than // processed samples appear in the output (not however, more than
// 8ksamples in any case) // 24ksamples in any case)
for (i = 0; i < 128; i ++) for (i = 0; (numStillExpected > (int)numSamples()) && (i < 200); i ++)
{ {
putSamples(buff, 64); putSamples(buff, 128);
if ((int)numSamples() >= nOut) }
{
// Enough new samples have appeared into the output!
// As samples come from processing with bigger chunks, now truncate it
// back to maximum "nOut" samples to improve duration accuracy
adjustAmountOfSamples(nOut);
// finish adjustAmountOfSamples(numStillExpected);
break;
}
}
delete[] buff; delete[] buff;
// Clear working buffers // Clear input buffers
pRateTransposer->clear(); // pRateTransposer->clearInput();
pTDStretch->clearInput(); pTDStretch->clearInput();
// yet leave the 'tempoChanger' output intouched as that's where the // yet leave the output intouched as that's where the
// flushed samples are! // flushed samples are!
} }
@ -482,6 +476,7 @@ int SoundTouch::getSetting(int settingId) const
// buffers. // buffers.
void SoundTouch::clear() void SoundTouch::clear()
{ {
samplesExpectedOut = 0;
pRateTransposer->clear(); pRateTransposer->clear();
pTDStretch->clear(); pTDStretch->clear();
} }
@ -502,3 +497,30 @@ uint SoundTouch::numUnprocessedSamples() const
} }
return 0; return 0;
} }
/// Output samples from beginning of the sample buffer. Copies requested samples to
/// output buffer and removes them from the sample buffer. If there are less than
/// 'numsample' samples in the buffer, returns all that available.
///
/// \return Number of samples returned.
uint SoundTouch::receiveSamples(SAMPLETYPE *output, uint maxSamples)
{
uint ret = FIFOProcessor::receiveSamples(output, maxSamples);
samplesOutput += (long)ret;
return ret;
}
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
/// sample buffer without copying them anywhere.
///
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
/// with 'ptrBegin' function.
uint SoundTouch::receiveSamples(uint maxSamples)
{
uint ret = FIFOProcessor::receiveSamples(maxSamples);
samplesOutput += (long)ret;
return ret;
}

View File

@ -459,7 +459,7 @@ void TDStretch::calcSeqParameters()
// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower // Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower
// tempo, larger faster tempo. // tempo, larger faster tempo.
void TDStretch::setTempo(float newTempo) void TDStretch::setTempo(double newTempo)
{ {
int intskip; int intskip;
@ -470,7 +470,7 @@ void TDStretch::setTempo(float newTempo)
// Calculate ideal skip length (according to tempo value) // Calculate ideal skip length (according to tempo value)
nominalSkip = tempo * (seekWindowLength - overlapLength); nominalSkip = tempo * (seekWindowLength - overlapLength);
intskip = (int)(nominalSkip + 0.5f); intskip = (int)(nominalSkip + 0.5);
// Calculate how many samples are needed in the 'inputBuffer' to // Calculate how many samples are needed in the 'inputBuffer' to
// process another batch of samples // process another batch of samples

View File

@ -112,7 +112,7 @@ class TDStretch : public FIFOProcessor
protected: protected:
int channels; int channels;
int sampleReq; int sampleReq;
float tempo; double tempo;
SAMPLETYPE *pMidBuffer; SAMPLETYPE *pMidBuffer;
SAMPLETYPE *pMidBufferUnaligned; SAMPLETYPE *pMidBufferUnaligned;
@ -121,8 +121,8 @@ protected:
int seekWindowLength; int seekWindowLength;
int overlapDividerBits; int overlapDividerBits;
int slopingDivider; int slopingDivider;
float nominalSkip; double nominalSkip;
float skipFract; double skipFract;
FIFOSampleBuffer outputBuffer; FIFOSampleBuffer outputBuffer;
FIFOSampleBuffer inputBuffer; FIFOSampleBuffer inputBuffer;
bool bQuickSeek; bool bQuickSeek;
@ -182,7 +182,7 @@ public:
/// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower /// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower
/// tempo, larger faster tempo. /// tempo, larger faster tempo.
void setTempo(float newTempo); void setTempo(double newTempo);
/// Returns nonzero if there aren't any samples available for outputting. /// Returns nonzero if there aren't any samples available for outputting.
virtual void clear(); virtual void clear();