diff --git a/include/SoundTouch.h b/include/SoundTouch.h index 208cfeb..482d312 100644 --- a/include/SoundTouch.h +++ b/include/SoundTouch.h @@ -116,30 +116,61 @@ namespace soundtouch #define SETTING_OVERLAP_MS 5 -/// Call "getSetting" with this ID to query nominal average processing sequence -/// size in samples. This value tells approcimate value how many input samples -/// SoundTouch needs to gather before it does DSP processing run for the sample batch. +/// Call "getSetting" with this ID to query processing sequence size in samples. +/// This value gives approximate value of how many input samples you'll need to +/// feed into SoundTouch after initial buffering to get out a new batch of +/// output samples. +/// +/// This value does not include initial buffering at beginning of a new processing +/// stream, use SETTING_INITIAL_LATENCY to get the initial buffering size. /// /// Notices: /// - This is read-only parameter, i.e. setSetting ignores this parameter -/// - Returned value is approximate average value, exact processing batch -/// size may wary from time to time -/// - This parameter value is not constant but may change depending on +/// - This parameter value is not constant but change depending on /// tempo/pitch/rate/samplerate settings. -#define SETTING_NOMINAL_INPUT_SEQUENCE 6 +#define SETTING_NOMINAL_INPUT_SEQUENCE 6 /// Call "getSetting" with this ID to query nominal average processing output /// size in samples. This value tells approcimate value how many output samples /// SoundTouch outputs once it does DSP processing run for a batch of input samples. -/// +/// /// Notices: /// - This is read-only parameter, i.e. setSetting ignores this parameter -/// - Returned value is approximate average value, exact processing batch -/// size may wary from time to time -/// - This parameter value is not constant but may change depending on +/// - This parameter value is not constant but change depending on /// tempo/pitch/rate/samplerate settings. -#define SETTING_NOMINAL_OUTPUT_SEQUENCE 7 +#define SETTING_NOMINAL_OUTPUT_SEQUENCE 7 + + +/// Call "getSetting" with this ID to query initial processing latency, i.e. +/// approx. how many samples you'll need to enter to SoundTouch pipeline before +/// you can expect to get first batch of ready output samples out. +/// +/// After the first output batch, you can then expect to get approx. +/// SETTING_NOMINAL_OUTPUT_SEQUENCE ready samples out for every +/// SETTING_NOMINAL_INPUT_SEQUENCE samples that you enter into SoundTouch. +/// +/// Example: +/// processing with parameter -tempo=5 +/// => initial latency = 5509 samples +/// input sequence = 4167 samples +/// output sequence = 3969 samples +/// +/// Accordingly, you can expect to feed in approx. 5509 samples at beginning of +/// the stream, and then you'll get out the first 3969 samples. After that, for +/// every approx. 4167 samples that you'll put in, you'll receive again approx. +/// 3969 samples out. +/// +/// This also means that average latency during stream processing is +/// INITIAL_LATENCY-OUTPUT_SEQUENCE/2, in the above example case 5509-3969/2 +/// = 3524 samples +/// +/// Notices: +/// - This is read-only parameter, i.e. setSetting ignores this parameter +/// - This parameter value is not constant but change depending on +/// tempo/pitch/rate/samplerate settings. +#define SETTING_INITIAL_LATENCY 8 + class SoundTouch : public FIFOProcessor { diff --git a/source/SoundTouch/RateTransposer.cpp b/source/SoundTouch/RateTransposer.cpp index 33d19a1..1b61eae 100644 --- a/source/SoundTouch/RateTransposer.cpp +++ b/source/SoundTouch/RateTransposer.cpp @@ -208,6 +208,13 @@ int RateTransposer::isEmpty() const } +/// Return approximate initial input-output latency +int RateTransposer::getLatency() const +{ + return (bUseAAFilter) ? pAAFilter->getLength() : 0; +} + + ////////////////////////////////////////////////////////////////////////////// // // TransposerBase - Base class for interpolation diff --git a/source/SoundTouch/SoundTouch.cpp b/source/SoundTouch/SoundTouch.cpp index 5a42fcb..b942b80 100644 --- a/source/SoundTouch/SoundTouch.cpp +++ b/source/SoundTouch/SoundTouch.cpp @@ -447,7 +447,7 @@ int SoundTouch::getSetting(int settingId) const return pRateTransposer->getAAFilter()->getLength(); case SETTING_USE_QUICKSEEK : - return (uint) pTDStretch->isQuickSeekEnabled(); + return (uint)pTDStretch->isQuickSeekEnabled(); case SETTING_SEQUENCE_MS: pTDStretch->getParameters(NULL, &temp, NULL, NULL); @@ -462,10 +462,51 @@ int SoundTouch::getSetting(int settingId) const return temp; case SETTING_NOMINAL_INPUT_SEQUENCE : - return pTDStretch->getInputSampleReq(); + { + int size = pTDStretch->getInputSampleReq(); + +#ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER + if (rate <= 1.0) + { + // transposing done before timestretch, which impacts latency + return (int)(size * rate + 0.5); + } +#endif + return size; + } + case SETTING_NOMINAL_OUTPUT_SEQUENCE : - return pTDStretch->getOutputBatchSize(); + { + int size = pTDStretch->getOutputBatchSize(); + + if (rate > 1.0) + { + // transposing done after timestretch, which impacts latency + return (int)(size / rate + 0.5); + } + return size; + } + + case SETTING_INITIAL_LATENCY: + { + double latency = pTDStretch->getLatency(); + int latency_tr = pRateTransposer->getLatency(); + +#ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER + if (rate <= 1.0) + { + // transposing done before timestretch, which impacts latency + latency = (latency + latency_tr) * rate; + } + else +#endif + { + latency += (double)latency_tr / rate; + } + + return (int)(latency + 0.5); + } default : return 0; diff --git a/source/SoundTouch/TDStretch.cpp b/source/SoundTouch/TDStretch.cpp index 6005eaf..bac49b7 100644 --- a/source/SoundTouch/TDStretch.cpp +++ b/source/SoundTouch/TDStretch.cpp @@ -656,7 +656,7 @@ void TDStretch::processSamples() // to form a processing frame. while ((int)inputBuffer.numSamples() >= sampleReq) { - // If tempo differs from the normal ('SCALE'), scan for the best overlapping + // If tempo differs from the normal ('SCALE'), scan for the best overlapping // position offset = seekBestOverlapPosition(inputBuffer.ptrBegin()); @@ -673,7 +673,7 @@ void TDStretch::processSamples() temp = (seekWindowLength - 2 * overlapLength); // crosscheck that we don't have buffer overflow... - if ((int)inputBuffer.numSamples() < (offset + temp + overlapLength * 2)) + if ((int)inputBuffer.numSamples() < (offset + seekWindowLength)) { continue; // just in case, shouldn't really happen } diff --git a/source/SoundTouch/TDStretch.h b/source/SoundTouch/TDStretch.h index 046481b..c6b5f97 100644 --- a/source/SoundTouch/TDStretch.h +++ b/source/SoundTouch/TDStretch.h @@ -247,6 +247,13 @@ public: { return seekWindowLength - overlapLength; } + + + /// return approximate initial input-output latency + int getLatency() const + { + return sampleReq; + } };