From aad372842600887029adc894b0d85728ea37b3a5 Mon Sep 17 00:00:00 2001 From: Mystro256 Date: Sun, 5 Apr 2020 10:56:07 -0400 Subject: [PATCH 2/5] soundtounch: update to 2.1.2 Note that soundtouch_config.h is a custom file. --- Externals/soundtouch/AAFilter.cpp | 16 +- Externals/soundtouch/AAFilter.h | 7 - Externals/soundtouch/BPMDetect.cpp | 400 +++++++++---- Externals/soundtouch/BPMDetect.h | 241 ++++---- Externals/soundtouch/FIFOSampleBuffer.cpp | 15 +- Externals/soundtouch/FIFOSampleBuffer.h | 9 +- Externals/soundtouch/FIFOSamplePipe.h | 28 +- Externals/soundtouch/FIRFilter.cpp | 20 +- Externals/soundtouch/FIRFilter.h | 7 - Externals/soundtouch/InterpolateCubic.cpp | 396 +++++++------ Externals/soundtouch/InterpolateCubic.h | 130 +++-- Externals/soundtouch/InterpolateLinear.cpp | 596 ++++++++++---------- Externals/soundtouch/InterpolateLinear.h | 180 +++--- Externals/soundtouch/InterpolateShannon.cpp | 366 ++++++------ Externals/soundtouch/InterpolateShannon.h | 140 +++-- Externals/soundtouch/PeakFinder.cpp | 19 +- Externals/soundtouch/PeakFinder.h | 11 +- Externals/soundtouch/RateTransposer.cpp | 35 +- Externals/soundtouch/RateTransposer.h | 22 +- Externals/soundtouch/STTypes.h | 32 +- Externals/soundtouch/SoundTouch.cpp | 154 ++--- Externals/soundtouch/SoundTouch.h | 91 ++- Externals/soundtouch/SoundTouch.vcxproj | 3 +- Externals/soundtouch/TDStretch.cpp | 131 +++-- Externals/soundtouch/TDStretch.h | 16 +- Externals/soundtouch/cpu_detect.h | 7 - Externals/soundtouch/cpu_detect_x86.cpp | 8 - Externals/soundtouch/mmx_optimized.cpp | 21 +- Externals/soundtouch/soundtouch_config.h | 2 + Externals/soundtouch/sse_optimized.cpp | 7 - 30 files changed, 1661 insertions(+), 1449 deletions(-) create mode 100644 Externals/soundtouch/soundtouch_config.h diff --git a/Externals/soundtouch/AAFilter.cpp b/Externals/soundtouch/AAFilter.cpp index c69f356f68..76a3da65d4 100644 --- a/Externals/soundtouch/AAFilter.cpp +++ b/Externals/soundtouch/AAFilter.cpp @@ -12,13 +12,6 @@ /// //////////////////////////////////////////////////////////////////////////////// // -// Last changed : $Date: 2014-01-05 23:40:22 +0200 (Sun, 05 Jan 2014) $ -// File revision : $Revision: 4 $ -// -// $Id: AAFilter.cpp 177 2014-01-05 21:40:22Z oparviai $ -// -//////////////////////////////////////////////////////////////////////////////// -// // License : // // SoundTouch audio processing library @@ -49,7 +42,7 @@ using namespace soundtouch; -#define PI 3.141592655357989 +#define PI 3.14159265358979323846 #define TWOPI (2 * PI) // define this to save AA filter coefficients to a file @@ -75,7 +68,6 @@ using namespace soundtouch; #define _DEBUG_SAVE_AAFIR_COEFFS(x, y) #endif - /***************************************************************************** * * Implementation of the class 'AAFilter' @@ -90,14 +82,12 @@ AAFilter::AAFilter(uint len) } - AAFilter::~AAFilter() { delete pFIR; } - // Sets new anti-alias filter cut-off edge frequency, scaled to // sampling frequency (nyquist frequency = 0.5). // The filter will cut frequencies higher than the given frequency. @@ -108,7 +98,6 @@ void AAFilter::setCutoffFreq(double newCutoffFreq) } - // Sets number of FIR filter taps void AAFilter::setLength(uint newLength) { @@ -117,7 +106,6 @@ void AAFilter::setLength(uint newLength) } - // Calculates coefficients for a low-pass FIR filter using Hamming window void AAFilter::calculateCoeffs() { @@ -177,12 +165,10 @@ void AAFilter::calculateCoeffs() for (i = 0; i < length; i ++) { temp = work[i] * scaleCoeff; -//#if SOUNDTOUCH_INTEGER_SAMPLES // scale & round to nearest integer temp += (temp >= 0) ? 0.5 : -0.5; // ensure no overfloods assert(temp >= -32768 && temp <= 32767); -//#endif coeffs[i] = (SAMPLETYPE)temp; } diff --git a/Externals/soundtouch/AAFilter.h b/Externals/soundtouch/AAFilter.h index 33e96948ec..8e5697f796 100644 --- a/Externals/soundtouch/AAFilter.h +++ b/Externals/soundtouch/AAFilter.h @@ -13,13 +13,6 @@ /// //////////////////////////////////////////////////////////////////////////////// // -// Last changed : $Date: 2014-01-07 21:41:23 +0200 (Tue, 07 Jan 2014) $ -// File revision : $Revision: 4 $ -// -// $Id: AAFilter.h 187 2014-01-07 19:41:23Z oparviai $ -// -//////////////////////////////////////////////////////////////////////////////// -// // License : // // SoundTouch audio processing library diff --git a/Externals/soundtouch/BPMDetect.cpp b/Externals/soundtouch/BPMDetect.cpp index 39dae83761..5ad19269dd 100644 --- a/Externals/soundtouch/BPMDetect.cpp +++ b/Externals/soundtouch/BPMDetect.cpp @@ -26,13 +26,6 @@ /// //////////////////////////////////////////////////////////////////////////////// // -// Last changed : $Date: 2015-02-21 23:24:29 +0200 (Sat, 21 Feb 2015) $ -// File revision : $Revision: 4 $ -// -// $Id: BPMDetect.cpp 202 2015-02-21 21:24:29Z oparviai $ -// -//////////////////////////////////////////////////////////////////////////////// -// // License : // // SoundTouch audio processing library @@ -54,45 +47,62 @@ // //////////////////////////////////////////////////////////////////////////////// +#define _USE_MATH_DEFINES + #include #include #include #include +#include #include "FIFOSampleBuffer.h" #include "PeakFinder.h" #include "BPMDetect.h" using namespace soundtouch; -#define INPUT_BLOCK_SAMPLES 2048 -#define DECIMATED_BLOCK_SAMPLES 256 +// algorithm input sample block size +static const int INPUT_BLOCK_SIZE = 2048; + +// decimated sample block size +static const int DECIMATED_BLOCK_SIZE = 256; + +/// Target sample rate after decimation +static const int TARGET_SRATE = 1000; + +/// XCorr update sequence size, update in about 200msec chunks +static const int XCORR_UPDATE_SEQUENCE = (int)(TARGET_SRATE / 5); -/// decay constant for calculating RMS volume sliding average approximation -/// (time constant is about 10 sec) -const float avgdecay = 0.99986f; +/// Moving average N size +static const int MOVING_AVERAGE_N = 15; -/// Normalization coefficient for calculating RMS sliding average approximation. -const float avgnorm = (1 - avgdecay); +/// XCorr decay time constant, decay to half in 30 seconds +/// If it's desired to have the system adapt quicker to beat rate +/// changes within a continuing music stream, then the +/// 'xcorr_decay_time_constant' value can be reduced, yet that +/// can increase possibility of glitches in bpm detection. +static const double XCORR_DECAY_TIME_CONSTANT = 30.0; +/// Data overlap factor for beat detection algorithm +static const int OVERLAP_FACTOR = 4; + +static const double TWOPI = (2 * M_PI); //////////////////////////////////////////////////////////////////////////////// // Enable following define to create bpm analysis file: -// #define _CREATE_BPM_DEBUG_FILE +//#define _CREATE_BPM_DEBUG_FILE #ifdef _CREATE_BPM_DEBUG_FILE - #define DEBUGFILE_NAME "c:\\temp\\soundtouch-bpm-debug.txt" - - static void _SaveDebugData(const float *data, int minpos, int maxpos, double coeff) + static void _SaveDebugData(const char *name, const float *data, int minpos, int maxpos, double coeff) { - FILE *fptr = fopen(DEBUGFILE_NAME, "wt"); + FILE *fptr = fopen(name, "wt"); int i; if (fptr) { - printf("\n\nWriting BPM debug data into file " DEBUGFILE_NAME "\n\n"); + printf("\nWriting BPM debug data into file %s\n", name); for (i = minpos; i < maxpos; i ++) { fprintf(fptr, "%d\t%.1lf\t%f\n", i, coeff / (double)i, data[i]); @@ -100,42 +110,90 @@ const float avgnorm = (1 - avgdecay); fclose(fptr); } } + + void _SaveDebugBeatPos(const char *name, const std::vector &beats) + { + printf("\nWriting beat detections data into file %s\n", name); + + FILE *fptr = fopen(name, "wt"); + if (fptr) + { + for (uint i = 0; i < beats.size(); i++) + { + BEAT b = beats[i]; + fprintf(fptr, "%lf\t%lf\n", b.pos, b.strength); + } + fclose(fptr); + } + } #else - #define _SaveDebugData(a,b,c,d) + #define _SaveDebugData(name, a,b,c,d) + #define _SaveDebugBeatPos(name, b) #endif +// Hamming window +void hamming(float *w, int N) +{ + for (int i = 0; i < N; i++) + { + w[i] = (float)(0.54 - 0.46 * cos(TWOPI * i / (N - 1))); + } + +} + //////////////////////////////////////////////////////////////////////////////// +// +// IIR2_filter - 2nd order IIR filter + +IIR2_filter::IIR2_filter(const double *lpf_coeffs) +{ + memcpy(coeffs, lpf_coeffs, 5 * sizeof(double)); + memset(prev, 0, sizeof(prev)); +} + +float IIR2_filter::update(float x) +{ + prev[0] = x; + double y = x * coeffs[0]; + + for (int i = 4; i >= 1; i--) + { + y += coeffs[i] * prev[i]; + prev[i] = prev[i - 1]; + } + + prev[3] = y; + return (float)y; +} + + +// IIR low-pass filter coefficients, calculated with matlab/octave cheby2(2,40,0.05) +const double _LPF_coeffs[5] = { 0.00996655391939, -0.01944529148401, 0.00996655391939, 1.96867605796247, -0.96916387431724 }; -BPMDetect::BPMDetect(int numChannels, int aSampleRate) +//////////////////////////////////////////////////////////////////////////////// + +BPMDetect::BPMDetect(int numChannels, int aSampleRate) : + beat_lpf(_LPF_coeffs) { + beats.reserve(250); // initial reservation to prevent frequent reallocation + this->sampleRate = aSampleRate; this->channels = numChannels; decimateSum = 0; decimateCount = 0; - envelopeAccu = 0; - - // Initialize RMS volume accumulator to RMS level of 1500 (out of 32768) that's - // safe initial RMS signal level value for song data. This value is then adapted - // to the actual level during processing. -#ifdef SOUNDTOUCH_INTEGER_SAMPLES - // integer samples - RMSVolumeAccu = (1500 * 1500) / avgnorm; -#else - // float samples, scaled to range [-1..+1[ - RMSVolumeAccu = (0.045f * 0.045f) / avgnorm; -#endif - // choose decimation factor so that result is approx. 1000 Hz - decimateBy = sampleRate / 1000; - assert(decimateBy > 0); - assert(INPUT_BLOCK_SAMPLES < decimateBy * DECIMATED_BLOCK_SAMPLES); + decimateBy = sampleRate / TARGET_SRATE; + if ((decimateBy <= 0) || (decimateBy * DECIMATED_BLOCK_SIZE < INPUT_BLOCK_SIZE)) + { + ST_THROW_RT_ERROR("Too small samplerate"); + } // Calculate window length & starting item according to desired min & max bpms windowLen = (60 * sampleRate) / (decimateBy * MIN_BPM); - windowStart = (60 * sampleRate) / (decimateBy * MAX_BPM); + windowStart = (60 * sampleRate) / (decimateBy * MAX_BPM_RANGE); assert(windowLen > windowStart); @@ -143,23 +201,38 @@ BPMDetect::BPMDetect(int numChannels, int aSampleRate) xcorr = new float[windowLen]; memset(xcorr, 0, windowLen * sizeof(float)); + pos = 0; + peakPos = 0; + peakVal = 0; + init_scaler = 1; + beatcorr_ringbuffpos = 0; + beatcorr_ringbuff = new float[windowLen]; + memset(beatcorr_ringbuff, 0, windowLen * sizeof(float)); + // allocate processing buffer buffer = new FIFOSampleBuffer(); // we do processing in mono mode buffer->setChannels(1); buffer->clear(); -} + // calculate hamming windows + hamw = new float[XCORR_UPDATE_SEQUENCE]; + hamming(hamw, XCORR_UPDATE_SEQUENCE); + hamw2 = new float[XCORR_UPDATE_SEQUENCE / 2]; + hamming(hamw2, XCORR_UPDATE_SEQUENCE / 2); +} BPMDetect::~BPMDetect() { delete[] xcorr; + delete[] beatcorr_ringbuff; + delete[] hamw; + delete[] hamw2; delete buffer; } - /// convert to mono, low-pass filter & decimate to about 500 Hz. /// return number of outputted samples. /// @@ -216,7 +289,6 @@ int BPMDetect::decimate(SAMPLETYPE *dest, const SAMPLETYPE *src, int numsamples) } - // Calculates autocorrelation function of the sample history buffer void BPMDetect::updateXCorr(int process_samples) { @@ -224,72 +296,124 @@ void BPMDetect::updateXCorr(int process_samples) SAMPLETYPE *pBuffer; assert(buffer->numSamples() >= (uint)(process_samples + windowLen)); + assert(process_samples == XCORR_UPDATE_SEQUENCE); pBuffer = buffer->ptrBegin(); + + // calculate decay factor for xcorr filtering + float xcorr_decay = (float)pow(0.5, 1.0 / (XCORR_DECAY_TIME_CONSTANT * TARGET_SRATE / process_samples)); + + // prescale pbuffer + float tmp[XCORR_UPDATE_SEQUENCE]; + for (int i = 0; i < process_samples; i++) + { + tmp[i] = hamw[i] * hamw[i] * pBuffer[i]; + } + #pragma omp parallel for for (offs = windowStart; offs < windowLen; offs ++) { - LONG_SAMPLETYPE sum; + float sum; int i; sum = 0; for (i = 0; i < process_samples; i ++) { - sum += pBuffer[i] * pBuffer[i + offs]; // scaling the sub-result shouldn't be necessary + sum += tmp[i] * pBuffer[i + offs]; // scaling the sub-result shouldn't be necessary } -// xcorr[offs] *= xcorr_decay; // decay 'xcorr' here with suitable coefficients - // if it's desired that the system adapts automatically to - // various bpms, e.g. in processing continouos music stream. - // The 'xcorr_decay' should be a value that's smaller than but - // close to one, and should also depend on 'process_samples' value. + xcorr[offs] *= xcorr_decay; // decay 'xcorr' here with suitable time constant. - xcorr[offs] += (float)sum; + xcorr[offs] += (float)fabs(sum); } } -// Calculates envelope of the sample data -void BPMDetect::calcEnvelope(SAMPLETYPE *samples, int numsamples) +// Detect individual beat positions +void BPMDetect::updateBeatPos(int process_samples) { - const static double decay = 0.7f; // decay constant for smoothing the envelope - const static double norm = (1 - decay); + SAMPLETYPE *pBuffer; - int i; - LONG_SAMPLETYPE out; - double val; + assert(buffer->numSamples() >= (uint)(process_samples + windowLen)); + + pBuffer = buffer->ptrBegin(); + assert(process_samples == XCORR_UPDATE_SEQUENCE / 2); + + // static double thr = 0.0003; + double posScale = (double)this->decimateBy / (double)this->sampleRate; + int resetDur = (int)(0.12 / posScale + 0.5); + + // prescale pbuffer + float tmp[XCORR_UPDATE_SEQUENCE / 2]; + for (int i = 0; i < process_samples; i++) + { + tmp[i] = hamw2[i] * hamw2[i] * pBuffer[i]; + } - for (i = 0; i < numsamples; i ++) + #pragma omp parallel for + for (int offs = windowStart; offs < windowLen; offs++) { - // calc average RMS volume - RMSVolumeAccu *= avgdecay; - val = (float)fabs((float)samples[i]); - RMSVolumeAccu += val * val; - - // cut amplitudes that are below cutoff ~2 times RMS volume - // (we're interested in peak values, not the silent moments) - if (val < 0.5 * sqrt(RMSVolumeAccu * avgnorm)) + float sum = 0; + for (int i = 0; i < process_samples; i++) { - val = 0; + sum += tmp[i] * pBuffer[offs + i]; } + beatcorr_ringbuff[(beatcorr_ringbuffpos + offs) % windowLen] += (float)((sum > 0) ? sum : 0); // accumulate only positive correlations + } - // smooth amplitude envelope - envelopeAccu *= decay; - envelopeAccu += val; - out = (LONG_SAMPLETYPE)(envelopeAccu * norm); + int skipstep = XCORR_UPDATE_SEQUENCE / OVERLAP_FACTOR; -#ifdef SOUNDTOUCH_INTEGER_SAMPLES - // cut peaks (shouldn't be necessary though) - if (out > 32767) out = 32767; -#endif // SOUNDTOUCH_INTEGER_SAMPLES - samples[i] = (SAMPLETYPE)out; + // compensate empty buffer at beginning by scaling coefficient + float scale = (float)windowLen / (float)(skipstep * init_scaler); + if (scale > 1.0f) + { + init_scaler++; + } + else + { + scale = 1.0f; + } + + // detect beats + for (int i = 0; i < skipstep; i++) + { + LONG_SAMPLETYPE max = 0; + + float sum = beatcorr_ringbuff[beatcorr_ringbuffpos]; + sum -= beat_lpf.update(sum); + + if (sum > peakVal) + { + // found new local largest value + peakVal = sum; + peakPos = pos; + } + if (pos > peakPos + resetDur) + { + // largest value not updated for 200msec => accept as beat + peakPos += skipstep; + if (peakVal > 0) + { + // add detected beat to end of "beats" vector + BEAT temp = { (float)(peakPos * posScale), (float)(peakVal * scale) }; + beats.push_back(temp); + } + + peakVal = 0; + peakPos = pos; + } + + beatcorr_ringbuff[beatcorr_ringbuffpos] = 0; + pos++; + beatcorr_ringbuffpos = (beatcorr_ringbuffpos + 1) % windowLen; } } +#define max(x,y) ((x) > (y) ? (x) : (y)) void BPMDetect::inputSamples(const SAMPLETYPE *samples, int numSamples) { - SAMPLETYPE decimated[DECIMATED_BLOCK_SAMPLES]; + SAMPLETYPE decimated[DECIMATED_BLOCK_SIZE]; // iterate so that max INPUT_BLOCK_SAMPLES processed per iteration while (numSamples > 0) @@ -297,48 +421,70 @@ void BPMDetect::inputSamples(const SAMPLETYPE *samples, int numSamples) int block; int decSamples; - block = (numSamples > INPUT_BLOCK_SAMPLES) ? INPUT_BLOCK_SAMPLES : numSamples; + block = (numSamples > INPUT_BLOCK_SIZE) ? INPUT_BLOCK_SIZE : numSamples; // decimate. note that converts to mono at the same time decSamples = decimate(decimated, samples, block); samples += block * channels; numSamples -= block; - // envelope new samples and add them to buffer - calcEnvelope(decimated, decSamples); buffer->putSamples(decimated, decSamples); } - // when the buffer has enought samples for processing... - if ((int)buffer->numSamples() > windowLen) + // when the buffer has enough samples for processing... + int req = max(windowLen + XCORR_UPDATE_SEQUENCE, 2 * XCORR_UPDATE_SEQUENCE); + while ((int)buffer->numSamples() >= req) { - int processLength; - - // how many samples are processed - processLength = (int)buffer->numSamples() - windowLen; - - // ... calculate autocorrelations for oldest samples... - updateXCorr(processLength); - // ... and remove them from the buffer - buffer->receiveSamples(processLength); + // ... update autocorrelations... + updateXCorr(XCORR_UPDATE_SEQUENCE); + // ...update beat position calculation... + updateBeatPos(XCORR_UPDATE_SEQUENCE / 2); + // ... and remove proceessed samples from the buffer + int n = XCORR_UPDATE_SEQUENCE / OVERLAP_FACTOR; + buffer->receiveSamples(n); } } - void BPMDetect::removeBias() { int i; - float minval = 1e12f; // arbitrary large number + // Remove linear bias: calculate linear regression coefficient + // 1. calc mean of 'xcorr' and 'i' + double mean_i = 0; + double mean_x = 0; + for (i = windowStart; i < windowLen; i++) + { + mean_x += xcorr[i]; + } + mean_x /= (windowLen - windowStart); + mean_i = 0.5 * (windowLen - 1 + windowStart); + + // 2. calculate linear regression coefficient + double b = 0; + double div = 0; + for (i = windowStart; i < windowLen; i++) + { + double xt = xcorr[i] - mean_x; + double xi = i - mean_i; + b += xt * xi; + div += xi * xi; + } + b /= div; + + // subtract linear regression and resolve min. value bias + float minval = FLT_MAX; // arbitrary large number for (i = windowStart; i < windowLen; i ++) { + xcorr[i] -= (float)(b * i); if (xcorr[i] < minval) { minval = xcorr[i]; } } + // subtract min.value for (i = windowStart; i < windowLen; i ++) { xcorr[i] -= minval; @@ -346,26 +492,82 @@ void BPMDetect::removeBias() } +// Calculate N-point moving average for "source" values +void MAFilter(float *dest, const float *source, int start, int end, int N) +{ + for (int i = start; i < end; i++) + { + int i1 = i - N / 2; + int i2 = i + N / 2 + 1; + if (i1 < start) i1 = start; + if (i2 > end) i2 = end; + + double sum = 0; + for (int j = i1; j < i2; j ++) + { + sum += source[j]; + } + dest[i] = (float)(sum / (i2 - i1)); + } +} + + float BPMDetect::getBpm() { double peakPos; double coeff; PeakFinder peakFinder; + // remove bias from xcorr data + removeBias(); + coeff = 60.0 * ((double)sampleRate / (double)decimateBy); - // save bpm debug analysis data if debug data enabled - _SaveDebugData(xcorr, windowStart, windowLen, coeff); + // save bpm debug data if debug data writing enabled + _SaveDebugData("soundtouch-bpm-xcorr.txt", xcorr, windowStart, windowLen, coeff); - // remove bias from xcorr data - removeBias(); + // Smoothen by N-point moving-average + float *data = new float[windowLen]; + memset(data, 0, sizeof(float) * windowLen); + MAFilter(data, xcorr, windowStart, windowLen, MOVING_AVERAGE_N); // find peak position - peakPos = peakFinder.detectPeak(xcorr, windowStart, windowLen); + peakPos = peakFinder.detectPeak(data, windowStart, windowLen); + + // save bpm debug data if debug data writing enabled + _SaveDebugData("soundtouch-bpm-smoothed.txt", data, windowStart, windowLen, coeff); + + delete[] data; assert(decimateBy != 0); if (peakPos < 1e-9) return 0.0; // detection failed. + _SaveDebugBeatPos("soundtouch-detected-beats.txt", beats); + // calculate BPM - return (float) (coeff / peakPos); + float bpm = (float)(coeff / peakPos); + return (bpm >= MIN_BPM && bpm <= MAX_BPM_VALID) ? bpm : 0; +} + + +/// Get beat position arrays. Note: The array includes also really low beat detection values +/// in absence of clear strong beats. Consumer may wish to filter low values away. +/// - "pos" receive array of beat positions +/// - "values" receive array of beat detection strengths +/// - max_num indicates max.size of "pos" and "values" array. +/// +/// You can query a suitable array sized by calling this with NULL in "pos" & "values". +/// +/// \return number of beats in the arrays. +int BPMDetect::getBeats(float *pos, float *values, int max_num) +{ + int num = beats.size(); + if ((!pos) || (!values)) return num; // pos or values NULL, return just size + + for (int i = 0; (i < num) && (i < max_num); i++) + { + pos[i] = beats[i].pos; + values[i] = beats[i].strength; + } + return num; } diff --git a/Externals/soundtouch/BPMDetect.h b/Externals/soundtouch/BPMDetect.h index 69d98143a7..8ece78448d 100644 --- a/Externals/soundtouch/BPMDetect.h +++ b/Externals/soundtouch/BPMDetect.h @@ -26,13 +26,6 @@ /// //////////////////////////////////////////////////////////////////////////////// // -// Last changed : $Date: 2012-08-30 22:53:44 +0300 (Thu, 30 Aug 2012) $ -// File revision : $Revision: 4 $ -// -// $Id: BPMDetect.h 150 2012-08-30 19:53:44Z oparviai $ -// -//////////////////////////////////////////////////////////////////////////////// -// // License : // // SoundTouch audio processing library @@ -57,108 +50,156 @@ #ifndef _BPMDetect_H_ #define _BPMDetect_H_ +#include #include "STTypes.h" #include "FIFOSampleBuffer.h" namespace soundtouch { -/// Minimum allowed BPM rate. Used to restrict accepted result above a reasonable limit. -#define MIN_BPM 29 + /// Minimum allowed BPM rate. Used to restrict accepted result above a reasonable limit. + #define MIN_BPM 45 -/// Maximum allowed BPM rate. Used to restrict accepted result below a reasonable limit. -#define MAX_BPM 200 + /// Maximum allowed BPM rate range. Used for calculating algorithm parametrs + #define MAX_BPM_RANGE 200 + /// Maximum allowed BPM rate range. Used to restrict accepted result below a reasonable limit. + #define MAX_BPM_VALID 190 -/// Class for calculating BPM rate for audio data. -class BPMDetect -{ -protected: - /// Auto-correlation accumulator bins. - float *xcorr; - - /// Amplitude envelope sliding average approximation level accumulator - double envelopeAccu; - - /// RMS volume sliding average approximation level accumulator - double RMSVolumeAccu; - - /// Sample average counter. - int decimateCount; - - /// Sample average accumulator for FIFO-like decimation. - soundtouch::LONG_SAMPLETYPE decimateSum; - - /// Decimate sound by this coefficient to reach approx. 500 Hz. - int decimateBy; - - /// Auto-correlation window length - int windowLen; - - /// Number of channels (1 = mono, 2 = stereo) - int channels; - - /// sample rate - int sampleRate; - - /// Beginning of auto-correlation window: Autocorrelation isn't being updated for - /// the first these many correlation bins. - int windowStart; - - /// FIFO-buffer for decimated processing samples. - soundtouch::FIFOSampleBuffer *buffer; - - /// Updates auto-correlation function for given number of decimated samples that - /// are read from the internal 'buffer' pipe (samples aren't removed from the pipe - /// though). - void updateXCorr(int process_samples /// How many samples are processed. - ); - - /// Decimates samples to approx. 500 Hz. - /// - /// \return Number of output samples. - int decimate(soundtouch::SAMPLETYPE *dest, ///< Destination buffer - const soundtouch::SAMPLETYPE *src, ///< Source sample buffer - int numsamples ///< Number of source samples. - ); - - /// Calculates amplitude envelope for the buffer of samples. - /// Result is output to 'samples'. - void calcEnvelope(soundtouch::SAMPLETYPE *samples, ///< Pointer to input/output data buffer - int numsamples ///< Number of samples in buffer - ); - - /// remove constant bias from xcorr data - void removeBias(); - -public: - /// Constructor. - BPMDetect(int numChannels, ///< Number of channels in sample data. - int sampleRate ///< Sample rate in Hz. - ); - - /// Destructor. - virtual ~BPMDetect(); - - /// Inputs a block of samples for analyzing: Envelopes the samples and then - /// updates the autocorrelation estimation. When whole song data has been input - /// in smaller blocks using this function, read the resulting bpm with 'getBpm' - /// function. - /// - /// Notice that data in 'samples' array can be disrupted in processing. - void inputSamples(const soundtouch::SAMPLETYPE *samples, ///< Pointer to input/working data buffer - int numSamples ///< Number of samples in buffer - ); - - - /// Analyzes the results and returns the BPM rate. Use this function to read result - /// after whole song data has been input to the class by consecutive calls of - /// 'inputSamples' function. - /// - /// \return Beats-per-minute rate, or zero if detection failed. - float getBpm(); -}; +//////////////////////////////////////////////////////////////////////////////// -} + typedef struct + { + float pos; + float strength; + } BEAT; + + class IIR2_filter + { + double coeffs[5]; + double prev[5]; + + public: + IIR2_filter(const double *lpf_coeffs); + float update(float x); + }; + + + /// Class for calculating BPM rate for audio data. + class BPMDetect + { + protected: + /// Auto-correlation accumulator bins. + float *xcorr; + + /// Sample average counter. + int decimateCount; + + /// Sample average accumulator for FIFO-like decimation. + soundtouch::LONG_SAMPLETYPE decimateSum; + + /// Decimate sound by this coefficient to reach approx. 500 Hz. + int decimateBy; + + /// Auto-correlation window length + int windowLen; + + /// Number of channels (1 = mono, 2 = stereo) + int channels; + + /// sample rate + int sampleRate; + + /// Beginning of auto-correlation window: Autocorrelation isn't being updated for + /// the first these many correlation bins. + int windowStart; + + /// window functions for data preconditioning + float *hamw; + float *hamw2; + + // beat detection variables + int pos; + int peakPos; + int beatcorr_ringbuffpos; + int init_scaler; + float peakVal; + float *beatcorr_ringbuff; + + /// FIFO-buffer for decimated processing samples. + soundtouch::FIFOSampleBuffer *buffer; + + /// Collection of detected beat positions + //BeatCollection beats; + std::vector beats; + + // 2nd order low-pass-filter + IIR2_filter beat_lpf; + + /// Updates auto-correlation function for given number of decimated samples that + /// are read from the internal 'buffer' pipe (samples aren't removed from the pipe + /// though). + void updateXCorr(int process_samples /// How many samples are processed. + ); + + /// Decimates samples to approx. 500 Hz. + /// + /// \return Number of output samples. + int decimate(soundtouch::SAMPLETYPE *dest, ///< Destination buffer + const soundtouch::SAMPLETYPE *src, ///< Source sample buffer + int numsamples ///< Number of source samples. + ); + + /// Calculates amplitude envelope for the buffer of samples. + /// Result is output to 'samples'. + void calcEnvelope(soundtouch::SAMPLETYPE *samples, ///< Pointer to input/output data buffer + int numsamples ///< Number of samples in buffer + ); + + /// remove constant bias from xcorr data + void removeBias(); + + // Detect individual beat positions + void updateBeatPos(int process_samples); + + + public: + /// Constructor. + BPMDetect(int numChannels, ///< Number of channels in sample data. + int sampleRate ///< Sample rate in Hz. + ); + + /// Destructor. + virtual ~BPMDetect(); + + /// Inputs a block of samples for analyzing: Envelopes the samples and then + /// updates the autocorrelation estimation. When whole song data has been input + /// in smaller blocks using this function, read the resulting bpm with 'getBpm' + /// function. + /// + /// Notice that data in 'samples' array can be disrupted in processing. + void inputSamples(const soundtouch::SAMPLETYPE *samples, ///< Pointer to input/working data buffer + int numSamples ///< Number of samples in buffer + ); + + /// Analyzes the results and returns the BPM rate. Use this function to read result + /// after whole song data has been input to the class by consecutive calls of + /// 'inputSamples' function. + /// + /// \return Beats-per-minute rate, or zero if detection failed. + float getBpm(); + + /// Get beat position arrays. Note: The array includes also really low beat detection values + /// in absence of clear strong beats. Consumer may wish to filter low values away. + /// - "pos" receive array of beat positions + /// - "values" receive array of beat detection strengths + /// - max_num indicates max.size of "pos" and "values" array. + /// + /// You can query a suitable array sized by calling this with NULL in "pos" & "values". + /// + /// \return number of beats in the arrays. + int getBeats(float *pos, float *strength, int max_num); + }; +} #endif // _BPMDetect_H_ diff --git a/Externals/soundtouch/FIFOSampleBuffer.cpp b/Externals/soundtouch/FIFOSampleBuffer.cpp index 5f5ec4b7db..8341163a60 100644 --- a/Externals/soundtouch/FIFOSampleBuffer.cpp +++ b/Externals/soundtouch/FIFOSampleBuffer.cpp @@ -15,13 +15,6 @@ /// //////////////////////////////////////////////////////////////////////////////// // -// Last changed : $Date: 2012-11-08 20:53:01 +0200 (Thu, 08 Nov 2012) $ -// File revision : $Revision: 4 $ -// -// $Id: FIFOSampleBuffer.cpp 160 2012-11-08 18:53:01Z oparviai $ -// -//////////////////////////////////////////////////////////////////////////////// -// // License : // // SoundTouch audio processing library @@ -80,7 +73,8 @@ void FIFOSampleBuffer::setChannels(int numChannels) { uint usedBytes; - assert(numChannels > 0); + if (!verifyNumberOfChannels(numChannels)) return; + usedBytes = channels * samplesInBuffer; channels = (uint)numChannels; samplesInBuffer = usedBytes / channels; @@ -131,7 +125,7 @@ void FIFOSampleBuffer::putSamples(uint nSamples) // // Parameter 'slackCapacity' tells the function how much free capacity (in // terms of samples) there _at least_ should be, in order to the caller to -// succesfully insert all the required samples to the buffer. When necessary, +// successfully insert all the required samples to the buffer. When necessary, // the function grows the buffer size to comply with this requirement. // // When using this function as means for inserting new samples, also remember @@ -158,7 +152,7 @@ SAMPLETYPE *FIFOSampleBuffer::ptrBegin() } -// Ensures that the buffer has enought capacity, i.e. space for _at least_ +// Ensures that the buffer has enough capacity, i.e. space for _at least_ // 'capacityRequirement' number of samples. The buffer is grown in steps of // 4 kilobytes to eliminate the need for frequently growing up the buffer, // as well as to round the buffer size up to the virtual memory page size. @@ -271,4 +265,3 @@ uint FIFOSampleBuffer::adjustAmountOfSamples(uint numSamples) } return samplesInBuffer; } - diff --git a/Externals/soundtouch/FIFOSampleBuffer.h b/Externals/soundtouch/FIFOSampleBuffer.h index 6f33df3daa..de298dd82b 100644 --- a/Externals/soundtouch/FIFOSampleBuffer.h +++ b/Externals/soundtouch/FIFOSampleBuffer.h @@ -15,13 +15,6 @@ /// //////////////////////////////////////////////////////////////////////////////// // -// Last changed : $Date: 2014-01-05 23:40:22 +0200 (Sun, 05 Jan 2014) $ -// File revision : $Revision: 4 $ -// -// $Id: FIFOSampleBuffer.h 177 2014-01-05 21:40:22Z oparviai $ -// -//////////////////////////////////////////////////////////////////////////////// -// // License : // // SoundTouch audio processing library @@ -119,7 +112,7 @@ public: /// 'putSamples(numSamples)' function. SAMPLETYPE *ptrEnd( uint slackCapacity ///< How much free capacity (in samples) there _at least_ - ///< should be so that the caller can succesfully insert the + ///< should be so that the caller can successfully insert the ///< desired samples to the buffer. If necessary, the function ///< grows the buffer size to comply with this requirement. ); diff --git a/Externals/soundtouch/FIFOSamplePipe.h b/Externals/soundtouch/FIFOSamplePipe.h index 6e3105970b..38ef31a5c2 100644 --- a/Externals/soundtouch/FIFOSamplePipe.h +++ b/Externals/soundtouch/FIFOSamplePipe.h @@ -17,13 +17,6 @@ /// //////////////////////////////////////////////////////////////////////////////// // -// Last changed : $Date: 2012-06-13 22:29:53 +0300 (Wed, 13 Jun 2012) $ -// File revision : $Revision: 4 $ -// -// $Id: FIFOSamplePipe.h 143 2012-06-13 19:29:53Z oparviai $ -// -//////////////////////////////////////////////////////////////////////////////// -// // License : // // SoundTouch audio processing library @@ -58,6 +51,18 @@ namespace soundtouch /// Abstract base class for FIFO (first-in-first-out) sample processing classes. class FIFOSamplePipe { +protected: + + bool verifyNumberOfChannels(int nChannels) const + { + if ((nChannels > 0) && (nChannels <= SOUNDTOUCH_MAX_CHANNELS)) + { + return true; + } + ST_THROW_RT_ERROR("Error: Illegal number of channels"); + return false; + } + public: // virtual default destructor virtual ~FIFOSamplePipe() {} @@ -122,7 +127,6 @@ public: }; - /// Base-class for sound processing routines working in FIFO principle. With this base /// class it's easy to implement sound processing stages that can be chained together, /// so that samples that are fed into beginning of the pipe automatically go through @@ -145,7 +149,6 @@ protected: output = pOutput; } - /// Constructor. Doesn't define output pipe; it has to be set be /// 'setOutPipe' function. FIFOProcessor() @@ -153,7 +156,6 @@ protected: output = NULL; } - /// Constructor. Configures output pipe. FIFOProcessor(FIFOSamplePipe *pOutput ///< Output pipe. ) @@ -161,13 +163,11 @@ protected: output = pOutput; } - /// Destructor. virtual ~FIFOProcessor() { } - /// Returns a pointer to the beginning of the output samples. /// This function is provided for accessing the output samples directly. /// Please be careful for not to corrupt the book-keeping! @@ -194,7 +194,6 @@ public: return output->receiveSamples(outBuffer, maxSamples); } - /// Adjusts book-keeping so that given number of samples are removed from beginning of the /// sample buffer without copying them anywhere. /// @@ -206,14 +205,12 @@ public: return output->receiveSamples(maxSamples); } - /// Returns number of samples currently available. virtual uint numSamples() const { return output->numSamples(); } - /// Returns nonzero if there aren't any samples available for outputting. virtual int isEmpty() const { @@ -226,7 +223,6 @@ public: { return output->adjustAmountOfSamples(numSamples); } - }; } diff --git a/Externals/soundtouch/FIRFilter.cpp b/Externals/soundtouch/FIRFilter.cpp index e56969b053..218e50ef5a 100644 --- a/Externals/soundtouch/FIRFilter.cpp +++ b/Externals/soundtouch/FIRFilter.cpp @@ -2,22 +2,21 @@ /// /// General FIR digital filter routines with MMX optimization. /// -/// Note : MMX optimized functions reside in a separate, platform-specific file, +/// Notes : MMX optimized functions reside in a separate, platform-specific file, /// e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp' /// +/// This source file contains OpenMP optimizations that allow speeding up the +/// corss-correlation algorithm by executing it in several threads / CPU cores +/// in parallel. See the following article link for more detailed discussion +/// about SoundTouch OpenMP optimizations: +/// http://www.softwarecoven.com/parallel-computing-in-embedded-mobile-devices +/// /// Author : Copyright (c) Olli Parviainen /// Author e-mail : oparviai 'at' iki.fi /// SoundTouch WWW: http://www.surina.net/soundtouch /// //////////////////////////////////////////////////////////////////////////////// // -// Last changed : $Date: 2015-02-21 23:24:29 +0200 (Sat, 21 Feb 2015) $ -// File revision : $Revision: 4 $ -// -// $Id: FIRFilter.cpp 202 2015-02-21 21:24:29Z oparviai $ -// -//////////////////////////////////////////////////////////////////////////////// -// // License : // // SoundTouch audio processing library @@ -69,6 +68,7 @@ FIRFilter::~FIRFilter() delete[] filterCoeffs; } + // Usual C-version of the filter routine for stereo sound uint FIRFilter::evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const { @@ -127,8 +127,6 @@ uint FIRFilter::evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, ui } - - // Usual C-version of the filter routine for mono sound uint FIRFilter::evaluateFilterMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const { @@ -254,7 +252,6 @@ uint FIRFilter::getLength() const } - // Applies the filter to the given sequence of samples. // // Note : The amount of outputted samples is by value of 'filter_length' @@ -284,7 +281,6 @@ uint FIRFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSample } - // Operator 'new' is overloaded so that it automatically creates a suitable instance // depending on if we've a MMX-capable CPU available or not. void * FIRFilter::operator new(size_t s) diff --git a/Externals/soundtouch/FIRFilter.h b/Externals/soundtouch/FIRFilter.h index 6b14238ce8..297b0f81ec 100644 --- a/Externals/soundtouch/FIRFilter.h +++ b/Externals/soundtouch/FIRFilter.h @@ -11,13 +11,6 @@ /// //////////////////////////////////////////////////////////////////////////////// // -// Last changed : $Date: 2015-02-21 23:24:29 +0200 (Sat, 21 Feb 2015) $ -// File revision : $Revision: 4 $ -// -// $Id: FIRFilter.h 202 2015-02-21 21:24:29Z oparviai $ -// -//////////////////////////////////////////////////////////////////////////////// -// // License : // // SoundTouch audio processing library diff --git a/Externals/soundtouch/InterpolateCubic.cpp b/Externals/soundtouch/InterpolateCubic.cpp index 8aa7374c74..fe49684817 100644 --- a/Externals/soundtouch/InterpolateCubic.cpp +++ b/Externals/soundtouch/InterpolateCubic.cpp @@ -1,200 +1,196 @@ -//////////////////////////////////////////////////////////////////////////////// -/// -/// Cubic interpolation routine. -/// -/// Author : Copyright (c) Olli Parviainen -/// Author e-mail : oparviai 'at' iki.fi -/// SoundTouch WWW: http://www.surina.net/soundtouch -/// -//////////////////////////////////////////////////////////////////////////////// -// -// $Id: InterpolateCubic.cpp 179 2014-01-06 18:41:42Z oparviai $ -// -//////////////////////////////////////////////////////////////////////////////// -// -// License : -// -// SoundTouch audio processing library -// Copyright (c) Olli Parviainen -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// -//////////////////////////////////////////////////////////////////////////////// - -#include -#include -#include "InterpolateCubic.h" -#include "STTypes.h" - -using namespace soundtouch; - -// cubic interpolation coefficients -static const float _coeffs[]= -{ -0.5f, 1.0f, -0.5f, 0.0f, - 1.5f, -2.5f, 0.0f, 1.0f, - -1.5f, 2.0f, 0.5f, 0.0f, - 0.5f, -0.5f, 0.0f, 0.0f}; - - -InterpolateCubic::InterpolateCubic() -{ - fract = 0; -} - - -void InterpolateCubic::resetRegisters() -{ - fract = 0; -} - - -/// Transpose mono audio. Returns number of produced output samples, and -/// updates "srcSamples" to amount of consumed source samples -int InterpolateCubic::transposeMono(SAMPLETYPE *pdest, - const SAMPLETYPE *psrc, - int &srcSamples) -{ - int i; - int srcSampleEnd = srcSamples - 4; - int srcCount = 0; - - i = 0; - while (srcCount < srcSampleEnd) - { - float out; - const float x3 = 1.0f; - const float x2 = (float)fract; // x - const float x1 = x2*x2; // x^2 - const float x0 = x1*x2; // x^3 - float y0, y1, y2, y3; - - assert(fract < 1.0); - - y0 = _coeffs[0] * x0 + _coeffs[1] * x1 + _coeffs[2] * x2 + _coeffs[3] * x3; - y1 = _coeffs[4] * x0 + _coeffs[5] * x1 + _coeffs[6] * x2 + _coeffs[7] * x3; - y2 = _coeffs[8] * x0 + _coeffs[9] * x1 + _coeffs[10] * x2 + _coeffs[11] * x3; - y3 = _coeffs[12] * x0 + _coeffs[13] * x1 + _coeffs[14] * x2 + _coeffs[15] * x3; - - out = y0 * psrc[0] + y1 * psrc[1] + y2 * psrc[2] + y3 * psrc[3]; - - pdest[i] = (SAMPLETYPE)out; - i ++; - - // update position fraction - fract += rate; - // update whole positions - int whole = (int)fract; - fract -= whole; - psrc += whole; - srcCount += whole; - } - srcSamples = srcCount; - return i; -} - - -/// Transpose stereo audio. Returns number of produced output samples, and -/// updates "srcSamples" to amount of consumed source samples -int InterpolateCubic::transposeStereo(SAMPLETYPE *pdest, - const SAMPLETYPE *psrc, - int &srcSamples) -{ - int i; - int srcSampleEnd = srcSamples - 4; - int srcCount = 0; - - i = 0; - while (srcCount < srcSampleEnd) - { - const float x3 = 1.0f; - const float x2 = (float)fract; // x - const float x1 = x2*x2; // x^2 - const float x0 = x1*x2; // x^3 - float y0, y1, y2, y3; - float out0, out1; - - assert(fract < 1.0); - - y0 = _coeffs[0] * x0 + _coeffs[1] * x1 + _coeffs[2] * x2 + _coeffs[3] * x3; - y1 = _coeffs[4] * x0 + _coeffs[5] * x1 + _coeffs[6] * x2 + _coeffs[7] * x3; - y2 = _coeffs[8] * x0 + _coeffs[9] * x1 + _coeffs[10] * x2 + _coeffs[11] * x3; - y3 = _coeffs[12] * x0 + _coeffs[13] * x1 + _coeffs[14] * x2 + _coeffs[15] * x3; - - out0 = y0 * psrc[0] + y1 * psrc[2] + y2 * psrc[4] + y3 * psrc[6]; - out1 = y0 * psrc[1] + y1 * psrc[3] + y2 * psrc[5] + y3 * psrc[7]; - - pdest[2*i] = (SAMPLETYPE)out0; - pdest[2*i+1] = (SAMPLETYPE)out1; - i ++; - - // update position fraction - fract += rate; - // update whole positions - int whole = (int)fract; - fract -= whole; - psrc += 2*whole; - srcCount += whole; - } - srcSamples = srcCount; - return i; -} - - -/// Transpose multi-channel audio. Returns number of produced output samples, and -/// updates "srcSamples" to amount of consumed source samples -int InterpolateCubic::transposeMulti(SAMPLETYPE *pdest, - const SAMPLETYPE *psrc, - int &srcSamples) -{ - int i; - int srcSampleEnd = srcSamples - 4; - int srcCount = 0; - - i = 0; - while (srcCount < srcSampleEnd) - { - const float x3 = 1.0f; - const float x2 = (float)fract; // x - const float x1 = x2*x2; // x^2 - const float x0 = x1*x2; // x^3 - float y0, y1, y2, y3; - - assert(fract < 1.0); - - y0 = _coeffs[0] * x0 + _coeffs[1] * x1 + _coeffs[2] * x2 + _coeffs[3] * x3; - y1 = _coeffs[4] * x0 + _coeffs[5] * x1 + _coeffs[6] * x2 + _coeffs[7] * x3; - y2 = _coeffs[8] * x0 + _coeffs[9] * x1 + _coeffs[10] * x2 + _coeffs[11] * x3; - y3 = _coeffs[12] * x0 + _coeffs[13] * x1 + _coeffs[14] * x2 + _coeffs[15] * x3; - - for (int c = 0; c < numChannels; c ++) - { - float out; - out = y0 * psrc[c] + y1 * psrc[c + numChannels] + y2 * psrc[c + 2 * numChannels] + y3 * psrc[c + 3 * numChannels]; - pdest[0] = (SAMPLETYPE)out; - pdest ++; - } - i ++; - - // update position fraction - fract += rate; - // update whole positions - int whole = (int)fract; - fract -= whole; - psrc += numChannels*whole; - srcCount += whole; - } - srcSamples = srcCount; - return i; -} +//////////////////////////////////////////////////////////////////////////////// +/// +/// Cubic interpolation routine. +/// +/// Author : Copyright (c) Olli Parviainen +/// Author e-mail : oparviai 'at' iki.fi +/// SoundTouch WWW: http://www.surina.net/soundtouch +/// +//////////////////////////////////////////////////////////////////////////////// +// +// License : +// +// SoundTouch audio processing library +// Copyright (c) Olli Parviainen +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +//////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include "InterpolateCubic.h" +#include "STTypes.h" + +using namespace soundtouch; + +// cubic interpolation coefficients +static const float _coeffs[]= +{ -0.5f, 1.0f, -0.5f, 0.0f, + 1.5f, -2.5f, 0.0f, 1.0f, + -1.5f, 2.0f, 0.5f, 0.0f, + 0.5f, -0.5f, 0.0f, 0.0f}; + + +InterpolateCubic::InterpolateCubic() +{ + fract = 0; +} + + +void InterpolateCubic::resetRegisters() +{ + fract = 0; +} + + +/// Transpose mono audio. Returns number of produced output samples, and +/// updates "srcSamples" to amount of consumed source samples +int InterpolateCubic::transposeMono(SAMPLETYPE *pdest, + const SAMPLETYPE *psrc, + int &srcSamples) +{ + int i; + int srcSampleEnd = srcSamples - 4; + int srcCount = 0; + + i = 0; + while (srcCount < srcSampleEnd) + { + float out; + const float x3 = 1.0f; + const float x2 = (float)fract; // x + const float x1 = x2*x2; // x^2 + const float x0 = x1*x2; // x^3 + float y0, y1, y2, y3; + + assert(fract < 1.0); + + y0 = _coeffs[0] * x0 + _coeffs[1] * x1 + _coeffs[2] * x2 + _coeffs[3] * x3; + y1 = _coeffs[4] * x0 + _coeffs[5] * x1 + _coeffs[6] * x2 + _coeffs[7] * x3; + y2 = _coeffs[8] * x0 + _coeffs[9] * x1 + _coeffs[10] * x2 + _coeffs[11] * x3; + y3 = _coeffs[12] * x0 + _coeffs[13] * x1 + _coeffs[14] * x2 + _coeffs[15] * x3; + + out = y0 * psrc[0] + y1 * psrc[1] + y2 * psrc[2] + y3 * psrc[3]; + + pdest[i] = (SAMPLETYPE)out; + i ++; + + // update position fraction + fract += rate; + // update whole positions + int whole = (int)fract; + fract -= whole; + psrc += whole; + srcCount += whole; + } + srcSamples = srcCount; + return i; +} + + +/// Transpose stereo audio. Returns number of produced output samples, and +/// updates "srcSamples" to amount of consumed source samples +int InterpolateCubic::transposeStereo(SAMPLETYPE *pdest, + const SAMPLETYPE *psrc, + int &srcSamples) +{ + int i; + int srcSampleEnd = srcSamples - 4; + int srcCount = 0; + + i = 0; + while (srcCount < srcSampleEnd) + { + const float x3 = 1.0f; + const float x2 = (float)fract; // x + const float x1 = x2*x2; // x^2 + const float x0 = x1*x2; // x^3 + float y0, y1, y2, y3; + float out0, out1; + + assert(fract < 1.0); + + y0 = _coeffs[0] * x0 + _coeffs[1] * x1 + _coeffs[2] * x2 + _coeffs[3] * x3; + y1 = _coeffs[4] * x0 + _coeffs[5] * x1 + _coeffs[6] * x2 + _coeffs[7] * x3; + y2 = _coeffs[8] * x0 + _coeffs[9] * x1 + _coeffs[10] * x2 + _coeffs[11] * x3; + y3 = _coeffs[12] * x0 + _coeffs[13] * x1 + _coeffs[14] * x2 + _coeffs[15] * x3; + + out0 = y0 * psrc[0] + y1 * psrc[2] + y2 * psrc[4] + y3 * psrc[6]; + out1 = y0 * psrc[1] + y1 * psrc[3] + y2 * psrc[5] + y3 * psrc[7]; + + pdest[2*i] = (SAMPLETYPE)out0; + pdest[2*i+1] = (SAMPLETYPE)out1; + i ++; + + // update position fraction + fract += rate; + // update whole positions + int whole = (int)fract; + fract -= whole; + psrc += 2*whole; + srcCount += whole; + } + srcSamples = srcCount; + return i; +} + + +/// Transpose multi-channel audio. Returns number of produced output samples, and +/// updates "srcSamples" to amount of consumed source samples +int InterpolateCubic::transposeMulti(SAMPLETYPE *pdest, + const SAMPLETYPE *psrc, + int &srcSamples) +{ + int i; + int srcSampleEnd = srcSamples - 4; + int srcCount = 0; + + i = 0; + while (srcCount < srcSampleEnd) + { + const float x3 = 1.0f; + const float x2 = (float)fract; // x + const float x1 = x2*x2; // x^2 + const float x0 = x1*x2; // x^3 + float y0, y1, y2, y3; + + assert(fract < 1.0); + + y0 = _coeffs[0] * x0 + _coeffs[1] * x1 + _coeffs[2] * x2 + _coeffs[3] * x3; + y1 = _coeffs[4] * x0 + _coeffs[5] * x1 + _coeffs[6] * x2 + _coeffs[7] * x3; + y2 = _coeffs[8] * x0 + _coeffs[9] * x1 + _coeffs[10] * x2 + _coeffs[11] * x3; + y3 = _coeffs[12] * x0 + _coeffs[13] * x1 + _coeffs[14] * x2 + _coeffs[15] * x3; + + for (int c = 0; c < numChannels; c ++) + { + float out; + out = y0 * psrc[c] + y1 * psrc[c + numChannels] + y2 * psrc[c + 2 * numChannels] + y3 * psrc[c + 3 * numChannels]; + pdest[0] = (SAMPLETYPE)out; + pdest ++; + } + i ++; + + // update position fraction + fract += rate; + // update whole positions + int whole = (int)fract; + fract -= whole; + psrc += numChannels*whole; + srcCount += whole; + } + srcSamples = srcCount; + return i; +} diff --git a/Externals/soundtouch/InterpolateCubic.h b/Externals/soundtouch/InterpolateCubic.h index f98e701d7b..457821033f 100644 --- a/Externals/soundtouch/InterpolateCubic.h +++ b/Externals/soundtouch/InterpolateCubic.h @@ -1,67 +1,63 @@ -//////////////////////////////////////////////////////////////////////////////// -/// -/// Cubic interpolation routine. -/// -/// Author : Copyright (c) Olli Parviainen -/// Author e-mail : oparviai 'at' iki.fi -/// SoundTouch WWW: http://www.surina.net/soundtouch -/// -//////////////////////////////////////////////////////////////////////////////// -// -// $Id: InterpolateCubic.h 225 2015-07-26 14:45:48Z oparviai $ -// -//////////////////////////////////////////////////////////////////////////////// -// -// License : -// -// SoundTouch audio processing library -// Copyright (c) Olli Parviainen -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// -//////////////////////////////////////////////////////////////////////////////// - -#ifndef _InterpolateCubic_H_ -#define _InterpolateCubic_H_ - -#include "RateTransposer.h" -#include "STTypes.h" - -namespace soundtouch -{ - -class InterpolateCubic : public TransposerBase -{ -protected: - virtual void resetRegisters(); - virtual int transposeMono(SAMPLETYPE *dest, - const SAMPLETYPE *src, - int &srcSamples); - virtual int transposeStereo(SAMPLETYPE *dest, - const SAMPLETYPE *src, - int &srcSamples); - virtual int transposeMulti(SAMPLETYPE *dest, - const SAMPLETYPE *src, - int &srcSamples); - - double fract; - -public: - InterpolateCubic(); -}; - -} - -#endif +//////////////////////////////////////////////////////////////////////////////// +/// +/// Cubic interpolation routine. +/// +/// Author : Copyright (c) Olli Parviainen +/// Author e-mail : oparviai 'at' iki.fi +/// SoundTouch WWW: http://www.surina.net/soundtouch +/// +//////////////////////////////////////////////////////////////////////////////// +// +// License : +// +// SoundTouch audio processing library +// Copyright (c) Olli Parviainen +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef _InterpolateCubic_H_ +#define _InterpolateCubic_H_ + +#include "RateTransposer.h" +#include "STTypes.h" + +namespace soundtouch +{ + +class InterpolateCubic : public TransposerBase +{ +protected: + virtual void resetRegisters(); + virtual int transposeMono(SAMPLETYPE *dest, + const SAMPLETYPE *src, + int &srcSamples); + virtual int transposeStereo(SAMPLETYPE *dest, + const SAMPLETYPE *src, + int &srcSamples); + virtual int transposeMulti(SAMPLETYPE *dest, + const SAMPLETYPE *src, + int &srcSamples); + + double fract; + +public: + InterpolateCubic(); +}; + +} + +#endif diff --git a/Externals/soundtouch/InterpolateLinear.cpp b/Externals/soundtouch/InterpolateLinear.cpp index 8119813857..c3aa1994cf 100644 --- a/Externals/soundtouch/InterpolateLinear.cpp +++ b/Externals/soundtouch/InterpolateLinear.cpp @@ -1,300 +1,296 @@ -//////////////////////////////////////////////////////////////////////////////// -/// -/// Linear interpolation algorithm. -/// -/// Author : Copyright (c) Olli Parviainen -/// Author e-mail : oparviai 'at' iki.fi -/// SoundTouch WWW: http://www.surina.net/soundtouch -/// -//////////////////////////////////////////////////////////////////////////////// -// -// $Id: InterpolateLinear.cpp 225 2015-07-26 14:45:48Z oparviai $ -// -//////////////////////////////////////////////////////////////////////////////// -// -// License : -// -// SoundTouch audio processing library -// Copyright (c) Olli Parviainen -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// -//////////////////////////////////////////////////////////////////////////////// - -#include -#include -#include "InterpolateLinear.h" - -using namespace soundtouch; - -////////////////////////////////////////////////////////////////////////////// -// -// InterpolateLinearInteger - integer arithmetic implementation -// - -/// fixed-point interpolation routine precision -#define SCALE 65536 - - -// Constructor -InterpolateLinearInteger::InterpolateLinearInteger() : TransposerBase() -{ - // Notice: use local function calling syntax for sake of clarity, - // to indicate the fact that C++ constructor can't call virtual functions. - resetRegisters(); - setRate(1.0f); -} - - -void InterpolateLinearInteger::resetRegisters() -{ - iFract = 0; -} - - -// Transposes the sample rate of the given samples using linear interpolation. -// 'Mono' version of the routine. Returns the number of samples returned in -// the "dest" buffer -int InterpolateLinearInteger::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples) -{ - int i; - int srcSampleEnd = srcSamples - 1; - int srcCount = 0; - - i = 0; - while (srcCount < srcSampleEnd) - { - LONG_SAMPLETYPE temp; - - assert(iFract < SCALE); - - temp = (SCALE - iFract) * src[0] + iFract * src[1]; - dest[i] = (SAMPLETYPE)(temp / SCALE); - i++; - - iFract += iRate; - - int iWhole = iFract / SCALE; - iFract -= iWhole * SCALE; - srcCount += iWhole; - src += iWhole; - } - srcSamples = srcCount; - - return i; -} - - -// Transposes the sample rate of the given samples using linear interpolation. -// 'Stereo' version of the routine. Returns the number of samples returned in -// the "dest" buffer -int InterpolateLinearInteger::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples) -{ - int i; - int srcSampleEnd = srcSamples - 1; - int srcCount = 0; - - i = 0; - while (srcCount < srcSampleEnd) - { - LONG_SAMPLETYPE temp0; - LONG_SAMPLETYPE temp1; - - assert(iFract < SCALE); - - temp0 = (SCALE - iFract) * src[0] + iFract * src[2]; - temp1 = (SCALE - iFract) * src[1] + iFract * src[3]; - dest[0] = (SAMPLETYPE)(temp0 / SCALE); - dest[1] = (SAMPLETYPE)(temp1 / SCALE); - dest += 2; - i++; - - iFract += iRate; - - int iWhole = iFract / SCALE; - iFract -= iWhole * SCALE; - srcCount += iWhole; - src += 2*iWhole; - } - srcSamples = srcCount; - - return i; -} - - -int InterpolateLinearInteger::transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples) -{ - int i; - int srcSampleEnd = srcSamples - 1; - int srcCount = 0; - - i = 0; - while (srcCount < srcSampleEnd) - { - LONG_SAMPLETYPE temp, vol1; - - assert(iFract < SCALE); - vol1 = (SCALE - iFract); - for (int c = 0; c < numChannels; c ++) - { - temp = vol1 * src[c] + iFract * src[c + numChannels]; - dest[0] = (SAMPLETYPE)(temp / SCALE); - dest ++; - } - i++; - - iFract += iRate; - - int iWhole = iFract / SCALE; - iFract -= iWhole * SCALE; - srcCount += iWhole; - src += iWhole * numChannels; - } - srcSamples = srcCount; - - return i; -} - - -// Sets new target iRate. Normal iRate = 1.0, smaller values represent slower -// iRate, larger faster iRates. -void InterpolateLinearInteger::setRate(double newRate) -{ - iRate = (int)(newRate * SCALE + 0.5); - TransposerBase::setRate(newRate); -} - - -////////////////////////////////////////////////////////////////////////////// -// -// InterpolateLinearFloat - floating point arithmetic implementation -// -////////////////////////////////////////////////////////////////////////////// - - -// Constructor -InterpolateLinearFloat::InterpolateLinearFloat() : TransposerBase() -{ - // Notice: use local function calling syntax for sake of clarity, - // to indicate the fact that C++ constructor can't call virtual functions. - resetRegisters(); - setRate(1.0); -} - - -void InterpolateLinearFloat::resetRegisters() -{ - fract = 0; -} - - -// Transposes the sample rate of the given samples using linear interpolation. -// 'Mono' version of the routine. Returns the number of samples returned in -// the "dest" buffer -int InterpolateLinearFloat::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples) -{ - int i; - int srcSampleEnd = srcSamples - 1; - int srcCount = 0; - - i = 0; - while (srcCount < srcSampleEnd) - { - double out; - assert(fract < 1.0); - - out = (1.0 - fract) * src[0] + fract * src[1]; - dest[i] = (SAMPLETYPE)out; - i ++; - - // update position fraction - fract += rate; - // update whole positions - int whole = (int)fract; - fract -= whole; - src += whole; - srcCount += whole; - } - srcSamples = srcCount; - return i; -} - - -// Transposes the sample rate of the given samples using linear interpolation. -// 'Mono' version of the routine. Returns the number of samples returned in -// the "dest" buffer -int InterpolateLinearFloat::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples) -{ - int i; - int srcSampleEnd = srcSamples - 1; - int srcCount = 0; - - i = 0; - while (srcCount < srcSampleEnd) - { - double out0, out1; - assert(fract < 1.0); - - out0 = (1.0 - fract) * src[0] + fract * src[2]; - out1 = (1.0 - fract) * src[1] + fract * src[3]; - dest[2*i] = (SAMPLETYPE)out0; - dest[2*i+1] = (SAMPLETYPE)out1; - i ++; - - // update position fraction - fract += rate; - // update whole positions - int whole = (int)fract; - fract -= whole; - src += 2*whole; - srcCount += whole; - } - srcSamples = srcCount; - return i; -} - - -int InterpolateLinearFloat::transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples) -{ - int i; - int srcSampleEnd = srcSamples - 1; - int srcCount = 0; - - i = 0; - while (srcCount < srcSampleEnd) - { - float temp, vol1, fract_float; - - vol1 = (float)(1.0 - fract); - fract_float = (float)fract; - for (int c = 0; c < numChannels; c ++) - { - temp = vol1 * src[c] + fract_float * src[c + numChannels]; - *dest = (SAMPLETYPE)temp; - dest ++; - } - i++; - - fract += rate; - - int iWhole = (int)fract; - fract -= iWhole; - srcCount += iWhole; - src += iWhole * numChannels; - } - srcSamples = srcCount; - - return i; -} +//////////////////////////////////////////////////////////////////////////////// +/// +/// Linear interpolation algorithm. +/// +/// Author : Copyright (c) Olli Parviainen +/// Author e-mail : oparviai 'at' iki.fi +/// SoundTouch WWW: http://www.surina.net/soundtouch +/// +//////////////////////////////////////////////////////////////////////////////// +// +// License : +// +// SoundTouch audio processing library +// Copyright (c) Olli Parviainen +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +//////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include "InterpolateLinear.h" + +using namespace soundtouch; + +////////////////////////////////////////////////////////////////////////////// +// +// InterpolateLinearInteger - integer arithmetic implementation +// + +/// fixed-point interpolation routine precision +#define SCALE 65536 + + +// Constructor +InterpolateLinearInteger::InterpolateLinearInteger() : TransposerBase() +{ + // Notice: use local function calling syntax for sake of clarity, + // to indicate the fact that C++ constructor can't call virtual functions. + resetRegisters(); + setRate(1.0f); +} + + +void InterpolateLinearInteger::resetRegisters() +{ + iFract = 0; +} + + +// Transposes the sample rate of the given samples using linear interpolation. +// 'Mono' version of the routine. Returns the number of samples returned in +// the "dest" buffer +int InterpolateLinearInteger::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples) +{ + int i; + int srcSampleEnd = srcSamples - 1; + int srcCount = 0; + + i = 0; + while (srcCount < srcSampleEnd) + { + LONG_SAMPLETYPE temp; + + assert(iFract < SCALE); + + temp = (SCALE - iFract) * src[0] + iFract * src[1]; + dest[i] = (SAMPLETYPE)(temp / SCALE); + i++; + + iFract += iRate; + + int iWhole = iFract / SCALE; + iFract -= iWhole * SCALE; + srcCount += iWhole; + src += iWhole; + } + srcSamples = srcCount; + + return i; +} + + +// Transposes the sample rate of the given samples using linear interpolation. +// 'Stereo' version of the routine. Returns the number of samples returned in +// the "dest" buffer +int InterpolateLinearInteger::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples) +{ + int i; + int srcSampleEnd = srcSamples - 1; + int srcCount = 0; + + i = 0; + while (srcCount < srcSampleEnd) + { + LONG_SAMPLETYPE temp0; + LONG_SAMPLETYPE temp1; + + assert(iFract < SCALE); + + temp0 = (SCALE - iFract) * src[0] + iFract * src[2]; + temp1 = (SCALE - iFract) * src[1] + iFract * src[3]; + dest[0] = (SAMPLETYPE)(temp0 / SCALE); + dest[1] = (SAMPLETYPE)(temp1 / SCALE); + dest += 2; + i++; + + iFract += iRate; + + int iWhole = iFract / SCALE; + iFract -= iWhole * SCALE; + srcCount += iWhole; + src += 2*iWhole; + } + srcSamples = srcCount; + + return i; +} + + +int InterpolateLinearInteger::transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples) +{ + int i; + int srcSampleEnd = srcSamples - 1; + int srcCount = 0; + + i = 0; + while (srcCount < srcSampleEnd) + { + LONG_SAMPLETYPE temp, vol1; + + assert(iFract < SCALE); + vol1 = (SCALE - iFract); + for (int c = 0; c < numChannels; c ++) + { + temp = vol1 * src[c] + iFract * src[c + numChannels]; + dest[0] = (SAMPLETYPE)(temp / SCALE); + dest ++; + } + i++; + + iFract += iRate; + + int iWhole = iFract / SCALE; + iFract -= iWhole * SCALE; + srcCount += iWhole; + src += iWhole * numChannels; + } + srcSamples = srcCount; + + return i; +} + + +// Sets new target iRate. Normal iRate = 1.0, smaller values represent slower +// iRate, larger faster iRates. +void InterpolateLinearInteger::setRate(double newRate) +{ + iRate = (int)(newRate * SCALE + 0.5); + TransposerBase::setRate(newRate); +} + + +////////////////////////////////////////////////////////////////////////////// +// +// InterpolateLinearFloat - floating point arithmetic implementation +// +////////////////////////////////////////////////////////////////////////////// + + +// Constructor +InterpolateLinearFloat::InterpolateLinearFloat() : TransposerBase() +{ + // Notice: use local function calling syntax for sake of clarity, + // to indicate the fact that C++ constructor can't call virtual functions. + resetRegisters(); + setRate(1.0); +} + + +void InterpolateLinearFloat::resetRegisters() +{ + fract = 0; +} + + +// Transposes the sample rate of the given samples using linear interpolation. +// 'Mono' version of the routine. Returns the number of samples returned in +// the "dest" buffer +int InterpolateLinearFloat::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples) +{ + int i; + int srcSampleEnd = srcSamples - 1; + int srcCount = 0; + + i = 0; + while (srcCount < srcSampleEnd) + { + double out; + assert(fract < 1.0); + + out = (1.0 - fract) * src[0] + fract * src[1]; + dest[i] = (SAMPLETYPE)out; + i ++; + + // update position fraction + fract += rate; + // update whole positions + int whole = (int)fract; + fract -= whole; + src += whole; + srcCount += whole; + } + srcSamples = srcCount; + return i; +} + + +// Transposes the sample rate of the given samples using linear interpolation. +// 'Mono' version of the routine. Returns the number of samples returned in +// the "dest" buffer +int InterpolateLinearFloat::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples) +{ + int i; + int srcSampleEnd = srcSamples - 1; + int srcCount = 0; + + i = 0; + while (srcCount < srcSampleEnd) + { + double out0, out1; + assert(fract < 1.0); + + out0 = (1.0 - fract) * src[0] + fract * src[2]; + out1 = (1.0 - fract) * src[1] + fract * src[3]; + dest[2*i] = (SAMPLETYPE)out0; + dest[2*i+1] = (SAMPLETYPE)out1; + i ++; + + // update position fraction + fract += rate; + // update whole positions + int whole = (int)fract; + fract -= whole; + src += 2*whole; + srcCount += whole; + } + srcSamples = srcCount; + return i; +} + + +int InterpolateLinearFloat::transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples) +{ + int i; + int srcSampleEnd = srcSamples - 1; + int srcCount = 0; + + i = 0; + while (srcCount < srcSampleEnd) + { + float temp, vol1, fract_float; + + vol1 = (float)(1.0 - fract); + fract_float = (float)fract; + for (int c = 0; c < numChannels; c ++) + { + temp = vol1 * src[c] + fract_float * src[c + numChannels]; + *dest = (SAMPLETYPE)temp; + dest ++; + } + i++; + + fract += rate; + + int iWhole = (int)fract; + fract -= iWhole; + srcCount += iWhole; + src += iWhole * numChannels; + } + srcSamples = srcCount; + + return i; +} diff --git a/Externals/soundtouch/InterpolateLinear.h b/Externals/soundtouch/InterpolateLinear.h index 6a7e11d18b..faa2e2c5a5 100644 --- a/Externals/soundtouch/InterpolateLinear.h +++ b/Externals/soundtouch/InterpolateLinear.h @@ -1,92 +1,88 @@ -//////////////////////////////////////////////////////////////////////////////// -/// -/// Linear interpolation routine. -/// -/// Author : Copyright (c) Olli Parviainen -/// Author e-mail : oparviai 'at' iki.fi -/// SoundTouch WWW: http://www.surina.net/soundtouch -/// -//////////////////////////////////////////////////////////////////////////////// -// -// $Id: InterpolateLinear.h 225 2015-07-26 14:45:48Z oparviai $ -// -//////////////////////////////////////////////////////////////////////////////// -// -// License : -// -// SoundTouch audio processing library -// Copyright (c) Olli Parviainen -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// -//////////////////////////////////////////////////////////////////////////////// - -#ifndef _InterpolateLinear_H_ -#define _InterpolateLinear_H_ - -#include "RateTransposer.h" -#include "STTypes.h" - -namespace soundtouch -{ - -/// Linear transposer class that uses integer arithmetics -class InterpolateLinearInteger : public TransposerBase -{ -protected: - int iFract; - int iRate; - - virtual void resetRegisters(); - - virtual int transposeMono(SAMPLETYPE *dest, - const SAMPLETYPE *src, - int &srcSamples); - virtual int transposeStereo(SAMPLETYPE *dest, - const SAMPLETYPE *src, - int &srcSamples); - virtual int transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples); -public: - InterpolateLinearInteger(); - - /// Sets new target rate. Normal rate = 1.0, smaller values represent slower - /// rate, larger faster rates. - virtual void setRate(double newRate); -}; - - -/// Linear transposer class that uses floating point arithmetics -class InterpolateLinearFloat : public TransposerBase -{ -protected: - double fract; - - virtual void resetRegisters(); - - virtual int transposeMono(SAMPLETYPE *dest, - const SAMPLETYPE *src, - int &srcSamples); - virtual int transposeStereo(SAMPLETYPE *dest, - const SAMPLETYPE *src, - int &srcSamples); - virtual int transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples); - -public: - InterpolateLinearFloat(); -}; - -} - -#endif +//////////////////////////////////////////////////////////////////////////////// +/// +/// Linear interpolation routine. +/// +/// Author : Copyright (c) Olli Parviainen +/// Author e-mail : oparviai 'at' iki.fi +/// SoundTouch WWW: http://www.surina.net/soundtouch +/// +//////////////////////////////////////////////////////////////////////////////// +// +// License : +// +// SoundTouch audio processing library +// Copyright (c) Olli Parviainen +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef _InterpolateLinear_H_ +#define _InterpolateLinear_H_ + +#include "RateTransposer.h" +#include "STTypes.h" + +namespace soundtouch +{ + +/// Linear transposer class that uses integer arithmetic +class InterpolateLinearInteger : public TransposerBase +{ +protected: + int iFract; + int iRate; + + virtual void resetRegisters(); + + virtual int transposeMono(SAMPLETYPE *dest, + const SAMPLETYPE *src, + int &srcSamples); + virtual int transposeStereo(SAMPLETYPE *dest, + const SAMPLETYPE *src, + int &srcSamples); + virtual int transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples); +public: + InterpolateLinearInteger(); + + /// Sets new target rate. Normal rate = 1.0, smaller values represent slower + /// rate, larger faster rates. + virtual void setRate(double newRate); +}; + + +/// Linear transposer class that uses floating point arithmetic +class InterpolateLinearFloat : public TransposerBase +{ +protected: + double fract; + + virtual void resetRegisters(); + + virtual int transposeMono(SAMPLETYPE *dest, + const SAMPLETYPE *src, + int &srcSamples); + virtual int transposeStereo(SAMPLETYPE *dest, + const SAMPLETYPE *src, + int &srcSamples); + virtual int transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples); + +public: + InterpolateLinearFloat(); +}; + +} + +#endif diff --git a/Externals/soundtouch/InterpolateShannon.cpp b/Externals/soundtouch/InterpolateShannon.cpp index 1085fd14cb..1d69a2e884 100644 --- a/Externals/soundtouch/InterpolateShannon.cpp +++ b/Externals/soundtouch/InterpolateShannon.cpp @@ -1,185 +1,181 @@ -//////////////////////////////////////////////////////////////////////////////// -/// -/// Sample interpolation routine using 8-tap band-limited Shannon interpolation -/// with kaiser window. -/// -/// Notice. This algorithm is remarkably much heavier than linear or cubic -/// interpolation, and not remarkably better than cubic algorithm. Thus mostly -/// for experimental purposes -/// -/// Author : Copyright (c) Olli Parviainen -/// Author e-mail : oparviai 'at' iki.fi -/// SoundTouch WWW: http://www.surina.net/soundtouch -/// -//////////////////////////////////////////////////////////////////////////////// -// -// $Id: InterpolateShannon.cpp 195 2014-04-06 15:57:21Z oparviai $ -// -//////////////////////////////////////////////////////////////////////////////// -// -// License : -// -// SoundTouch audio processing library -// Copyright (c) Olli Parviainen -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// -//////////////////////////////////////////////////////////////////////////////// - -#include -#include "InterpolateShannon.h" -#include "STTypes.h" - -using namespace soundtouch; - - -/// Kaiser window with beta = 2.0 -/// Values scaled down by 5% to avoid overflows -static const double _kaiser8[8] = -{ - 0.41778693317814, - 0.64888025049173, - 0.83508562409944, - 0.93887857733412, - 0.93887857733412, - 0.83508562409944, - 0.64888025049173, - 0.41778693317814 -}; - - -InterpolateShannon::InterpolateShannon() -{ - fract = 0; -} - - -void InterpolateShannon::resetRegisters() -{ - fract = 0; -} - - -#define PI 3.1415926536 -#define sinc(x) (sin(PI * (x)) / (PI * (x))) - -/// Transpose mono audio. Returns number of produced output samples, and -/// updates "srcSamples" to amount of consumed source samples -int InterpolateShannon::transposeMono(SAMPLETYPE *pdest, - const SAMPLETYPE *psrc, - int &srcSamples) -{ - int i; - int srcSampleEnd = srcSamples - 8; - int srcCount = 0; - - i = 0; - while (srcCount < srcSampleEnd) - { - double out; - assert(fract < 1.0); - - out = psrc[0] * sinc(-3.0 - fract) * _kaiser8[0]; - out += psrc[1] * sinc(-2.0 - fract) * _kaiser8[1]; - out += psrc[2] * sinc(-1.0 - fract) * _kaiser8[2]; - if (fract < 1e-6) - { - out += psrc[3] * _kaiser8[3]; // sinc(0) = 1 - } - else - { - out += psrc[3] * sinc(- fract) * _kaiser8[3]; - } - out += psrc[4] * sinc( 1.0 - fract) * _kaiser8[4]; - out += psrc[5] * sinc( 2.0 - fract) * _kaiser8[5]; - out += psrc[6] * sinc( 3.0 - fract) * _kaiser8[6]; - out += psrc[7] * sinc( 4.0 - fract) * _kaiser8[7]; - - pdest[i] = (SAMPLETYPE)out; - i ++; - - // update position fraction - fract += rate; - // update whole positions - int whole = (int)fract; - fract -= whole; - psrc += whole; - srcCount += whole; - } - srcSamples = srcCount; - return i; -} - - -/// Transpose stereo audio. Returns number of produced output samples, and -/// updates "srcSamples" to amount of consumed source samples -int InterpolateShannon::transposeStereo(SAMPLETYPE *pdest, - const SAMPLETYPE *psrc, - int &srcSamples) -{ - int i; - int srcSampleEnd = srcSamples - 8; - int srcCount = 0; - - i = 0; - while (srcCount < srcSampleEnd) - { - double out0, out1, w; - assert(fract < 1.0); - - w = sinc(-3.0 - fract) * _kaiser8[0]; - out0 = psrc[0] * w; out1 = psrc[1] * w; - w = sinc(-2.0 - fract) * _kaiser8[1]; - out0 += psrc[2] * w; out1 += psrc[3] * w; - w = sinc(-1.0 - fract) * _kaiser8[2]; - out0 += psrc[4] * w; out1 += psrc[5] * w; - w = _kaiser8[3] * ((fract < 1e-5) ? 1.0 : sinc(- fract)); // sinc(0) = 1 - out0 += psrc[6] * w; out1 += psrc[7] * w; - w = sinc( 1.0 - fract) * _kaiser8[4]; - out0 += psrc[8] * w; out1 += psrc[9] * w; - w = sinc( 2.0 - fract) * _kaiser8[5]; - out0 += psrc[10] * w; out1 += psrc[11] * w; - w = sinc( 3.0 - fract) * _kaiser8[6]; - out0 += psrc[12] * w; out1 += psrc[13] * w; - w = sinc( 4.0 - fract) * _kaiser8[7]; - out0 += psrc[14] * w; out1 += psrc[15] * w; - - pdest[2*i] = (SAMPLETYPE)out0; - pdest[2*i+1] = (SAMPLETYPE)out1; - i ++; - - // update position fraction - fract += rate; - // update whole positions - int whole = (int)fract; - fract -= whole; - psrc += 2*whole; - srcCount += whole; - } - srcSamples = srcCount; - return i; -} - - -/// Transpose stereo audio. Returns number of produced output samples, and -/// updates "srcSamples" to amount of consumed source samples -int InterpolateShannon::transposeMulti(SAMPLETYPE *pdest, - const SAMPLETYPE *psrc, - int &srcSamples) -{ - // not implemented - assert(false); - return 0; -} +//////////////////////////////////////////////////////////////////////////////// +/// +/// Sample interpolation routine using 8-tap band-limited Shannon interpolation +/// with kaiser window. +/// +/// Notice. This algorithm is remarkably much heavier than linear or cubic +/// interpolation, and not remarkably better than cubic algorithm. Thus mostly +/// for experimental purposes +/// +/// Author : Copyright (c) Olli Parviainen +/// Author e-mail : oparviai 'at' iki.fi +/// SoundTouch WWW: http://www.surina.net/soundtouch +/// +//////////////////////////////////////////////////////////////////////////////// +// +// License : +// +// SoundTouch audio processing library +// Copyright (c) Olli Parviainen +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +//////////////////////////////////////////////////////////////////////////////// + +#include +#include "InterpolateShannon.h" +#include "STTypes.h" + +using namespace soundtouch; + + +/// Kaiser window with beta = 2.0 +/// Values scaled down by 5% to avoid overflows +static const double _kaiser8[8] = +{ + 0.41778693317814, + 0.64888025049173, + 0.83508562409944, + 0.93887857733412, + 0.93887857733412, + 0.83508562409944, + 0.64888025049173, + 0.41778693317814 +}; + + +InterpolateShannon::InterpolateShannon() +{ + fract = 0; +} + + +void InterpolateShannon::resetRegisters() +{ + fract = 0; +} + + +#define PI 3.1415926536 +#define sinc(x) (sin(PI * (x)) / (PI * (x))) + +/// Transpose mono audio. Returns number of produced output samples, and +/// updates "srcSamples" to amount of consumed source samples +int InterpolateShannon::transposeMono(SAMPLETYPE *pdest, + const SAMPLETYPE *psrc, + int &srcSamples) +{ + int i; + int srcSampleEnd = srcSamples - 8; + int srcCount = 0; + + i = 0; + while (srcCount < srcSampleEnd) + { + double out; + assert(fract < 1.0); + + out = psrc[0] * sinc(-3.0 - fract) * _kaiser8[0]; + out += psrc[1] * sinc(-2.0 - fract) * _kaiser8[1]; + out += psrc[2] * sinc(-1.0 - fract) * _kaiser8[2]; + if (fract < 1e-6) + { + out += psrc[3] * _kaiser8[3]; // sinc(0) = 1 + } + else + { + out += psrc[3] * sinc(- fract) * _kaiser8[3]; + } + out += psrc[4] * sinc( 1.0 - fract) * _kaiser8[4]; + out += psrc[5] * sinc( 2.0 - fract) * _kaiser8[5]; + out += psrc[6] * sinc( 3.0 - fract) * _kaiser8[6]; + out += psrc[7] * sinc( 4.0 - fract) * _kaiser8[7]; + + pdest[i] = (SAMPLETYPE)out; + i ++; + + // update position fraction + fract += rate; + // update whole positions + int whole = (int)fract; + fract -= whole; + psrc += whole; + srcCount += whole; + } + srcSamples = srcCount; + return i; +} + + +/// Transpose stereo audio. Returns number of produced output samples, and +/// updates "srcSamples" to amount of consumed source samples +int InterpolateShannon::transposeStereo(SAMPLETYPE *pdest, + const SAMPLETYPE *psrc, + int &srcSamples) +{ + int i; + int srcSampleEnd = srcSamples - 8; + int srcCount = 0; + + i = 0; + while (srcCount < srcSampleEnd) + { + double out0, out1, w; + assert(fract < 1.0); + + w = sinc(-3.0 - fract) * _kaiser8[0]; + out0 = psrc[0] * w; out1 = psrc[1] * w; + w = sinc(-2.0 - fract) * _kaiser8[1]; + out0 += psrc[2] * w; out1 += psrc[3] * w; + w = sinc(-1.0 - fract) * _kaiser8[2]; + out0 += psrc[4] * w; out1 += psrc[5] * w; + w = _kaiser8[3] * ((fract < 1e-5) ? 1.0 : sinc(- fract)); // sinc(0) = 1 + out0 += psrc[6] * w; out1 += psrc[7] * w; + w = sinc( 1.0 - fract) * _kaiser8[4]; + out0 += psrc[8] * w; out1 += psrc[9] * w; + w = sinc( 2.0 - fract) * _kaiser8[5]; + out0 += psrc[10] * w; out1 += psrc[11] * w; + w = sinc( 3.0 - fract) * _kaiser8[6]; + out0 += psrc[12] * w; out1 += psrc[13] * w; + w = sinc( 4.0 - fract) * _kaiser8[7]; + out0 += psrc[14] * w; out1 += psrc[15] * w; + + pdest[2*i] = (SAMPLETYPE)out0; + pdest[2*i+1] = (SAMPLETYPE)out1; + i ++; + + // update position fraction + fract += rate; + // update whole positions + int whole = (int)fract; + fract -= whole; + psrc += 2*whole; + srcCount += whole; + } + srcSamples = srcCount; + return i; +} + + +/// Transpose stereo audio. Returns number of produced output samples, and +/// updates "srcSamples" to amount of consumed source samples +int InterpolateShannon::transposeMulti(SAMPLETYPE *pdest, + const SAMPLETYPE *psrc, + int &srcSamples) +{ + // not implemented + assert(false); + return 0; +} diff --git a/Externals/soundtouch/InterpolateShannon.h b/Externals/soundtouch/InterpolateShannon.h index 54703d9808..c621cf1e1d 100644 --- a/Externals/soundtouch/InterpolateShannon.h +++ b/Externals/soundtouch/InterpolateShannon.h @@ -1,72 +1,68 @@ -//////////////////////////////////////////////////////////////////////////////// -/// -/// Sample interpolation routine using 8-tap band-limited Shannon interpolation -/// with kaiser window. -/// -/// Notice. This algorithm is remarkably much heavier than linear or cubic -/// interpolation, and not remarkably better than cubic algorithm. Thus mostly -/// for experimental purposes -/// -/// Author : Copyright (c) Olli Parviainen -/// Author e-mail : oparviai 'at' iki.fi -/// SoundTouch WWW: http://www.surina.net/soundtouch -/// -//////////////////////////////////////////////////////////////////////////////// -// -// $Id: InterpolateShannon.h 225 2015-07-26 14:45:48Z oparviai $ -// -//////////////////////////////////////////////////////////////////////////////// -// -// License : -// -// SoundTouch audio processing library -// Copyright (c) Olli Parviainen -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// -//////////////////////////////////////////////////////////////////////////////// - -#ifndef _InterpolateShannon_H_ -#define _InterpolateShannon_H_ - -#include "RateTransposer.h" -#include "STTypes.h" - -namespace soundtouch -{ - -class InterpolateShannon : public TransposerBase -{ -protected: - void resetRegisters(); - int transposeMono(SAMPLETYPE *dest, - const SAMPLETYPE *src, - int &srcSamples); - int transposeStereo(SAMPLETYPE *dest, - const SAMPLETYPE *src, - int &srcSamples); - int transposeMulti(SAMPLETYPE *dest, - const SAMPLETYPE *src, - int &srcSamples); - - double fract; - -public: - InterpolateShannon(); -}; - -} - -#endif +//////////////////////////////////////////////////////////////////////////////// +/// +/// Sample interpolation routine using 8-tap band-limited Shannon interpolation +/// with kaiser window. +/// +/// Notice. This algorithm is remarkably much heavier than linear or cubic +/// interpolation, and not remarkably better than cubic algorithm. Thus mostly +/// for experimental purposes +/// +/// Author : Copyright (c) Olli Parviainen +/// Author e-mail : oparviai 'at' iki.fi +/// SoundTouch WWW: http://www.surina.net/soundtouch +/// +//////////////////////////////////////////////////////////////////////////////// +// +// License : +// +// SoundTouch audio processing library +// Copyright (c) Olli Parviainen +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef _InterpolateShannon_H_ +#define _InterpolateShannon_H_ + +#include "RateTransposer.h" +#include "STTypes.h" + +namespace soundtouch +{ + +class InterpolateShannon : public TransposerBase +{ +protected: + void resetRegisters(); + int transposeMono(SAMPLETYPE *dest, + const SAMPLETYPE *src, + int &srcSamples); + int transposeStereo(SAMPLETYPE *dest, + const SAMPLETYPE *src, + int &srcSamples); + int transposeMulti(SAMPLETYPE *dest, + const SAMPLETYPE *src, + int &srcSamples); + + double fract; + +public: + InterpolateShannon(); +}; + +} + +#endif diff --git a/Externals/soundtouch/PeakFinder.cpp b/Externals/soundtouch/PeakFinder.cpp index 111cc88bbd..34db39bc23 100644 --- a/Externals/soundtouch/PeakFinder.cpp +++ b/Externals/soundtouch/PeakFinder.cpp @@ -11,13 +11,6 @@ /// //////////////////////////////////////////////////////////////////////////////// // -// Last changed : $Date: 2015-05-18 18:22:02 +0300 (Mon, 18 May 2015) $ -// File revision : $Revision: 4 $ -// -// $Id: PeakFinder.cpp 213 2015-05-18 15:22:02Z oparviai $ -// -//////////////////////////////////////////////////////////////////////////////// -// // License : // // SoundTouch audio processing library @@ -64,7 +57,7 @@ int PeakFinder::findTop(const float *data, int peakpos) const refvalue = data[peakpos]; - // seek within ±10 points + // seek within �10 points start = peakpos - 10; if (start < minPos) start = minPos; end = peakpos + 10; @@ -149,7 +142,7 @@ int PeakFinder::findCrossingLevel(const float *data, float level, int peakpos, i peaklevel = data[peakpos]; assert(peaklevel >= level); pos = peakpos; - while ((pos >= minPos) && (pos < maxPos)) + while ((pos >= minPos) && (pos + direction < maxPos)) { if (data[pos + direction] < level) return pos; // crossing found pos += direction; @@ -178,7 +171,6 @@ double PeakFinder::calcMassCenter(const float *data, int firstPos, int lastPos) } - /// get exact center of peak near given position by calculating local mass of center double PeakFinder::getPeakCenter(const float *data, int peakpos) const { @@ -218,7 +210,6 @@ double PeakFinder::getPeakCenter(const float *data, int peakpos) const } - double PeakFinder::detectPeak(const float *data, int aminPos, int amaxPos) { @@ -249,12 +240,12 @@ double PeakFinder::detectPeak(const float *data, int aminPos, int amaxPos) // - sometimes the highest peak can be Nth harmonic of the true base peak yet // just a slightly higher than the true base - for (i = 3; i < 10; i ++) + for (i = 1; i < 3; i ++) { double peaktmp, harmonic; int i1,i2; - harmonic = (double)i * 0.5; + harmonic = (double)pow(2.0, i); peakpos = (int)(highPeak / harmonic + 0.5f); if (peakpos < minPos) break; peakpos = findTop(data, peakpos); // seek true local maximum index @@ -265,7 +256,7 @@ double PeakFinder::detectPeak(const float *data, int aminPos, int amaxPos) // accept harmonic peak if // (a) it is found - // (b) is within ±4% of the expected harmonic interval + // (b) is within �4% of the expected harmonic interval // (c) has at least half x-corr value of the max. peak double diff = harmonic * peaktmp / highPeak; diff --git a/Externals/soundtouch/PeakFinder.h b/Externals/soundtouch/PeakFinder.h index 594f230882..9fe66adac5 100644 --- a/Externals/soundtouch/PeakFinder.h +++ b/Externals/soundtouch/PeakFinder.h @@ -9,13 +9,6 @@ /// //////////////////////////////////////////////////////////////////////////////// // -// Last changed : $Date: 2011-12-30 22:33:46 +0200 (Fri, 30 Dec 2011) $ -// File revision : $Revision: 4 $ -// -// $Id: PeakFinder.h 132 2011-12-30 20:33:46Z oparviai $ -// -//////////////////////////////////////////////////////////////////////////////// -// // License : // // SoundTouch audio processing library @@ -51,8 +44,8 @@ protected: /// Calculates the mass center between given vector items. double calcMassCenter(const float *data, ///< Data vector. - int firstPos, ///< Index of first vector item beloging to the peak. - int lastPos ///< Index of last vector item beloging to the peak. + int firstPos, ///< Index of first vector item belonging to the peak. + int lastPos ///< Index of last vector item belonging to the peak. ) const; /// Finds the data vector index where the monotoniously decreasing signal crosses the diff --git a/Externals/soundtouch/RateTransposer.cpp b/Externals/soundtouch/RateTransposer.cpp index 95b9437a24..2efaf0424d 100644 --- a/Externals/soundtouch/RateTransposer.cpp +++ b/Externals/soundtouch/RateTransposer.cpp @@ -10,13 +10,6 @@ /// //////////////////////////////////////////////////////////////////////////////// // -// Last changed : $Date: 2015-07-26 17:45:48 +0300 (Sun, 26 Jul 2015) $ -// File revision : $Revision: 4 $ -// -// $Id: RateTransposer.cpp 225 2015-07-26 14:45:48Z oparviai $ -// -//////////////////////////////////////////////////////////////////////////////// -// // License : // // SoundTouch audio processing library @@ -57,7 +50,13 @@ TransposerBase::ALGORITHM TransposerBase::algorithm = TransposerBase::CUBIC; // Constructor RateTransposer::RateTransposer() : FIFOProcessor(&outputBuffer) { - bUseAAFilter = true; + bUseAAFilter = +#ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER + true; +#else + // Disable Anti-alias filter if desirable to avoid click at rate change zero value crossover + false; +#endif // Instantiates the anti-alias filter pAAFilter = new AAFilter(64); @@ -65,7 +64,6 @@ RateTransposer::RateTransposer() : FIFOProcessor(&outputBuffer) } - RateTransposer::~RateTransposer() { delete pAAFilter; @@ -73,11 +71,13 @@ RateTransposer::~RateTransposer() } - /// Enables/disables the anti-alias filter. Zero to disable, nonzero to enable void RateTransposer::enableAAFilter(bool newMode) { +#ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER + // Disable Anti-alias filter if desirable to avoid click at rate change zero value crossover bUseAAFilter = newMode; +#endif } @@ -94,7 +94,6 @@ AAFilter *RateTransposer::getAAFilter() } - // Sets new target iRate. Normal iRate = 1.0, smaller values represent slower // iRate, larger faster iRates. void RateTransposer::setRate(double newRate) @@ -177,11 +176,10 @@ void RateTransposer::processSamples(const SAMPLETYPE *src, uint nSamples) // Sets the number of channels, 1 = mono, 2 = stereo void RateTransposer::setChannels(int nChannels) { - assert(nChannels > 0); + if (!verifyNumberOfChannels(nChannels) || + (pTransposer->numChannels == nChannels)) return; - if (pTransposer->numChannels == nChannels) return; pTransposer->setChannels(nChannels); - inputBuffer.setChannels(nChannels); midBuffer.setChannels(nChannels); outputBuffer.setChannels(nChannels); @@ -208,6 +206,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 @@ -280,7 +285,7 @@ void TransposerBase::setRate(double newRate) TransposerBase *TransposerBase::newInstance() { #ifdef SOUNDTOUCH_INTEGER_SAMPLES - // Notice: For integer arithmetics support only linear algorithm (due to simplest calculus) + // Notice: For integer arithmetic support only linear algorithm (due to simplest calculus) return ::new InterpolateLinearInteger; #else switch (algorithm) diff --git a/Externals/soundtouch/RateTransposer.h b/Externals/soundtouch/RateTransposer.h index e9cedfac9c..52b7441b89 100644 --- a/Externals/soundtouch/RateTransposer.h +++ b/Externals/soundtouch/RateTransposer.h @@ -14,13 +14,6 @@ /// //////////////////////////////////////////////////////////////////////////////// // -// Last changed : $Date: 2015-07-26 17:45:48 +0300 (Sun, 26 Jul 2015) $ -// File revision : $Revision: 4 $ -// -// $Id: RateTransposer.h 225 2015-07-26 14:45:48Z oparviai $ -// -//////////////////////////////////////////////////////////////////////////////// -// // License : // // SoundTouch audio processing library @@ -132,21 +125,9 @@ public: RateTransposer(); virtual ~RateTransposer(); - /// Operator 'new' is overloaded so that it automatically creates a suitable instance - /// depending on if we're to use integer or floating point arithmetics. -// static void *operator new(size_t s); - - /// Use this function instead of "new" operator to create a new instance of this class. - /// This function automatically chooses a correct implementation, depending on if - /// integer ot floating point arithmetics are to be used. -// static RateTransposer *newInstance(); - /// Returns the output buffer object FIFOSamplePipe *getOutput() { return &outputBuffer; }; - /// Returns the store buffer object -// FIFOSamplePipe *getStore() { return &storeBuffer; }; - /// Return anti-alias filter object AAFilter *getAAFilter(); @@ -172,6 +153,9 @@ public: /// Returns nonzero if there aren't any samples available for outputting. int isEmpty() const; + + /// Return approximate initial input-output latency + int getLatency() const; }; } diff --git a/Externals/soundtouch/STTypes.h b/Externals/soundtouch/STTypes.h index a07bc3dcea..862505e769 100644 --- a/Externals/soundtouch/STTypes.h +++ b/Externals/soundtouch/STTypes.h @@ -8,13 +8,6 @@ /// //////////////////////////////////////////////////////////////////////////////// // -// Last changed : $Date: 2015-05-18 18:25:07 +0300 (Mon, 18 May 2015) $ -// File revision : $Revision: 3 $ -// -// $Id: STTypes.h 215 2015-05-18 15:25:07Z oparviai $ -// -//////////////////////////////////////////////////////////////////////////////// -// // License : // // SoundTouch audio processing library @@ -57,16 +50,19 @@ typedef unsigned long ulong; #if (defined(__GNUC__) && !defined(ANDROID)) // In GCC, include soundtouch_config.h made by config scritps. // Skip this in Android compilation that uses GCC but without configure scripts. - //#include "soundtouch_config.h" + #include "soundtouch_config.h" #endif namespace soundtouch { + /// Max allowed number of channels + #define SOUNDTOUCH_MAX_CHANNELS 16 + /// Activate these undef's to overrule the possible sampletype /// setting inherited from some other header file: - #undef SOUNDTOUCH_INTEGER_SAMPLES - #undef SOUNDTOUCH_FLOAT_SAMPLES + //#undef SOUNDTOUCH_INTEGER_SAMPLES + //#undef SOUNDTOUCH_FLOAT_SAMPLES /// If following flag is defined, always uses multichannel processing /// routines also for mono and stero sound. This is for routine testing @@ -75,7 +71,7 @@ namespace soundtouch /// runtime performance so recommendation is to keep this off. // #define USE_MULTICH_ALWAYS - #if (defined(__SOFTFP__)) + #if (defined(__SOFTFP__) && defined(ANDROID)) // For Android compilation: Force use of Integer samples in case that // compilation uses soft-floating point emulation - soft-fp is way too slow #undef SOUNDTOUCH_FLOAT_SAMPLES @@ -98,8 +94,8 @@ namespace soundtouch /// However, if you still prefer to select the sample format here /// also in GNU environment, then please #undef the INTEGER_SAMPLE /// and FLOAT_SAMPLE defines first as in comments above. - #define SOUNDTOUCH_INTEGER_SAMPLES 1 //< 16bit integer samples - //#define SOUNDTOUCH_FLOAT_SAMPLES 1 //< 32bit float samples + //#define SOUNDTOUCH_INTEGER_SAMPLES 1 //< 16bit integer samples + #define SOUNDTOUCH_FLOAT_SAMPLES 1 //< 32bit float samples #endif @@ -110,7 +106,7 @@ namespace soundtouch /// routines compiled for whatever reason, you may disable these optimizations /// to make the library compile. - //#define SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS 1 + #define SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS 1 /// In GNU environment, allow the user to override this setting by /// giving the following switch to the configure script: @@ -143,8 +139,10 @@ namespace soundtouch #endif // SOUNDTOUCH_FLOAT_SAMPLES #ifdef SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS - // Allow MMX optimizations - #define SOUNDTOUCH_ALLOW_MMX 1 + // Allow MMX optimizations (not available in X64 mode) + #if (!_M_X64) + #define SOUNDTOUCH_ALLOW_MMX 1 + #endif #endif #else @@ -164,7 +162,7 @@ namespace soundtouch }; // define ST_NO_EXCEPTION_HANDLING switch to disable throwing std exceptions: - #define ST_NO_EXCEPTION_HANDLING 1 +// #define ST_NO_EXCEPTION_HANDLING 1 #ifdef ST_NO_EXCEPTION_HANDLING // Exceptions disabled. Throw asserts instead if enabled. #include diff --git a/Externals/soundtouch/SoundTouch.cpp b/Externals/soundtouch/SoundTouch.cpp index edccce238d..1618884cf5 100644 --- a/Externals/soundtouch/SoundTouch.cpp +++ b/Externals/soundtouch/SoundTouch.cpp @@ -41,13 +41,6 @@ /// //////////////////////////////////////////////////////////////////////////////// // -// Last changed : $Date: 2015-07-26 17:45:48 +0300 (Sun, 26 Jul 2015) $ -// File revision : $Revision: 4 $ -// -// $Id: SoundTouch.cpp 225 2015-07-26 14:45:48Z oparviai $ -// -//////////////////////////////////////////////////////////////////////////////// -// // License : // // SoundTouch audio processing library @@ -110,15 +103,14 @@ SoundTouch::SoundTouch() calcEffectiveRateAndTempo(); - samplesExpectedOut = 0; - samplesOutput = 0; + samplesExpectedOut = 0; + samplesOutput = 0; channels = 0; bSrateSet = false; } - SoundTouch::~SoundTouch() { delete pRateTransposer; @@ -126,7 +118,6 @@ SoundTouch::~SoundTouch() } - /// Get SoundTouch library version string const char *SoundTouch::getVersionString() { @@ -146,18 +137,14 @@ uint SoundTouch::getVersionId() // Sets the number of channels, 1 = mono, 2 = stereo void SoundTouch::setChannels(uint numChannels) { - /*if (numChannels != 1 && numChannels != 2) - { - //ST_THROW_RT_ERROR("Illegal number of channels"); - return; - }*/ + if (!verifyNumberOfChannels(numChannels)) return; + channels = numChannels; pRateTransposer->setChannels((int)numChannels); pTDStretch->setChannels((int)numChannels); } - // Sets new rate control value. Normal rate = 1.0, smaller values // represent slower rate, larger faster rates. void SoundTouch::setRate(double newRate) @@ -167,7 +154,6 @@ void SoundTouch::setRate(double newRate) } - // Sets new rate control value as a difference in percents compared // to the original rate (-50 .. +100 %) void SoundTouch::setRateChange(double newRate) @@ -177,7 +163,6 @@ void SoundTouch::setRateChange(double newRate) } - // Sets new tempo control value. Normal tempo = 1.0, smaller values // represent slower tempo, larger faster tempo. void SoundTouch::setTempo(double newTempo) @@ -187,7 +172,6 @@ void SoundTouch::setTempo(double newTempo) } - // Sets new tempo control value as a difference in percents compared // to the original tempo (-50 .. +100 %) void SoundTouch::setTempoChange(double newTempo) @@ -197,7 +181,6 @@ void SoundTouch::setTempoChange(double newTempo) } - // Sets new pitch control value. Original pitch = 1.0, smaller values // represent lower pitches, larger values higher pitch. void SoundTouch::setPitch(double newPitch) @@ -207,7 +190,6 @@ void SoundTouch::setPitch(double newPitch) } - // Sets pitch change in octaves compared to the original pitch // (-1.00 .. +1.00) void SoundTouch::setPitchOctaves(double newPitch) @@ -217,7 +199,6 @@ void SoundTouch::setPitchOctaves(double newPitch) } - // Sets pitch change in semi-tones compared to the original pitch // (-12 .. +12) void SoundTouch::setPitchSemiTones(int newPitch) @@ -226,7 +207,6 @@ void SoundTouch::setPitchSemiTones(int newPitch) } - void SoundTouch::setPitchSemiTones(double newPitch) { setPitchOctaves(newPitch / 12.0); @@ -240,11 +220,11 @@ void SoundTouch::calcEffectiveRateAndTempo() double oldTempo = tempo; double oldRate = rate; - tempo = virtualTempo / virtualPitch; - rate = virtualPitch * virtualRate; + tempo = virtualTempo / virtualPitch; + rate = virtualPitch * virtualRate; if (!TEST_FLOAT_EQUAL(rate,oldRate)) pRateTransposer->setRate(rate); - if (!TEST_FLOAT_EQUAL(tempo, oldTempo)) pTDStretch->setTempo(tempo); + if (!TEST_FLOAT_EQUAL(tempo, oldTempo)) pTDStretch->setTempo(tempo); #ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER if (rate <= 1.0f) @@ -286,9 +266,9 @@ void SoundTouch::calcEffectiveRateAndTempo() // Sets sample rate. void SoundTouch::setSampleRate(uint srate) { - bSrateSet = true; // set sample rate, leave other tempo changer parameters as they are. pTDStretch->setParameters((int)srate); + bSrateSet = true; } @@ -305,25 +285,9 @@ void SoundTouch::putSamples(const SAMPLETYPE *samples, uint nSamples) ST_THROW_RT_ERROR("SoundTouch : Number of channels not defined"); } - // Transpose the rate of the new samples if necessary - /* Bypass the nominal setting - can introduce a click in sound when tempo/pitch control crosses the nominal value... - if (rate == 1.0f) - { - // The rate value is same as the original, simply evaluate the tempo changer. - assert(output == pTDStretch); - if (pRateTransposer->isEmpty() == 0) - { - // yet flush the last samples in the pitch transposer buffer - // (may happen if 'rate' changes from a non-zero value to zero) - pTDStretch->moveSamples(*pRateTransposer); - } - 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); + // 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 if (rate <= 1.0f) @@ -354,28 +318,28 @@ void SoundTouch::putSamples(const SAMPLETYPE *samples, uint nSamples) void SoundTouch::flush() { int i; - int numStillExpected; + int numStillExpected; SAMPLETYPE *buff = new SAMPLETYPE[128 * channels]; - // how many samples are still expected to output - numStillExpected = (int)((long)(samplesExpectedOut + 0.5) - samplesOutput); + // how many samples are still expected to output + numStillExpected = (int)((long)(samplesExpectedOut + 0.5) - samplesOutput); + if (numStillExpected < 0) numStillExpected = 0; memset(buff, 0, 128 * channels * sizeof(SAMPLETYPE)); // "Push" the last active samples out from the processing pipeline by // feeding blank samples into the processing pipeline until new, // processed samples appear in the output (not however, more than // 24ksamples in any case) - for (i = 0; (numStillExpected > (int)numSamples()) && (i < 200); i ++) - { - putSamples(buff, 128); - } + for (i = 0; (numStillExpected > (int)numSamples()) && (i < 200); i ++) + { + putSamples(buff, 128); + } - adjustAmountOfSamples(numStillExpected); + adjustAmountOfSamples(numStillExpected); delete[] buff; // Clear input buffers - // pRateTransposer->clearInput(); pTDStretch->clearInput(); // yet leave the output intouched as that's where the // flushed samples are! @@ -446,7 +410,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); @@ -460,13 +424,53 @@ int SoundTouch::getSetting(int settingId) const pTDStretch->getParameters(NULL, NULL, NULL, &temp); return temp; - case SETTING_NOMINAL_INPUT_SEQUENCE : - return pTDStretch->getInputSampleReq(); + case SETTING_NOMINAL_INPUT_SEQUENCE : + { + 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 : + { + 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; + } - case SETTING_NOMINAL_OUTPUT_SEQUENCE : - return pTDStretch->getOutputBatchSize(); + return (int)(latency + 0.5); + } - default : + default : return 0; } } @@ -476,13 +480,13 @@ int SoundTouch::getSetting(int settingId) const // buffers. void SoundTouch::clear() { - samplesExpectedOut = 0; + samplesExpectedOut = 0; + samplesOutput = 0; pRateTransposer->clear(); pTDStretch->clear(); } - /// Returns number of samples currently unprocessed. uint SoundTouch::numUnprocessedSamples() const { @@ -499,7 +503,6 @@ uint SoundTouch::numUnprocessedSamples() const } - /// 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. @@ -507,9 +510,9 @@ uint SoundTouch::numUnprocessedSamples() const /// \return Number of samples returned. uint SoundTouch::receiveSamples(SAMPLETYPE *output, uint maxSamples) { - uint ret = FIFOProcessor::receiveSamples(output, maxSamples); - samplesOutput += (long)ret; - return ret; + uint ret = FIFOProcessor::receiveSamples(output, maxSamples); + samplesOutput += (long)ret; + return ret; } @@ -520,7 +523,16 @@ uint SoundTouch::receiveSamples(SAMPLETYPE *output, uint maxSamples) /// with 'ptrBegin' function. uint SoundTouch::receiveSamples(uint maxSamples) { - uint ret = FIFOProcessor::receiveSamples(maxSamples); - samplesOutput += (long)ret; - return ret; + uint ret = FIFOProcessor::receiveSamples(maxSamples); + samplesOutput += (long)ret; + return ret; +} + + +/// Get ratio between input and output audio durations, useful for calculating +/// processed output duration: if you'll process a stream of N samples, then +/// you can expect to get out N * getInputOutputSampleRatio() samples. +double SoundTouch::getInputOutputSampleRatio() +{ + return 1.0 / (tempo * rate); } diff --git a/Externals/soundtouch/SoundTouch.h b/Externals/soundtouch/SoundTouch.h index 24e8716e04..e963ddfde8 100644 --- a/Externals/soundtouch/SoundTouch.h +++ b/Externals/soundtouch/SoundTouch.h @@ -41,13 +41,6 @@ /// //////////////////////////////////////////////////////////////////////////////// // -// Last changed : $Date: 2015-09-20 10:38:32 +0300 (Sun, 20 Sep 2015) $ -// File revision : $Revision: 4 $ -// -// $Id: SoundTouch.h 230 2015-09-20 07:38:32Z oparviai $ -// -//////////////////////////////////////////////////////////////////////////////// -// // License : // // SoundTouch audio processing library @@ -79,10 +72,10 @@ namespace soundtouch { /// Soundtouch library version string -#define SOUNDTOUCH_VERSION "1.9.2" +#define SOUNDTOUCH_VERSION "2.1.3" /// SoundTouch library version id -#define SOUNDTOUCH_VERSION_ID (10902) +#define SOUNDTOUCH_VERSION_ID (20103) // // Available setting IDs for the 'setSetting' & 'get_setting' functions: @@ -116,30 +109,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 { @@ -228,6 +252,24 @@ public: /// Sets sample rate. void setSampleRate(uint srate); + /// Get ratio between input and output audio durations, useful for calculating + /// processed output duration: if you'll process a stream of N samples, then + /// you can expect to get out N * getInputOutputSampleRatio() samples. + /// + /// This ratio will give accurate target duration ratio for a full audio track, + /// given that the the whole track is processed with same processing parameters. + /// + /// If this ratio is applied to calculate intermediate offsets inside a processing + /// stream, then this ratio is approximate and can deviate +- some tens of milliseconds + /// from ideal offset, yet by end of the audio stream the duration ratio will become + /// exact. + /// + /// Example: if processing with parameters "-tempo=15 -pitch=-3", the function + /// will return value 0.8695652... Now, if processing an audio stream whose duration + /// is exactly one million audio samples, then you can expect the processed + /// output duration be 0.869565 * 1000000 = 869565 samples. + double getInputOutputSampleRatio(); + /// Flushes the last samples from the processing pipeline to the output. /// Clears also the internal processing buffers. // @@ -271,7 +313,7 @@ public: /// Changes a setting controlling the processing system behaviour. See the /// 'SETTING_...' defines for available setting ID's. /// - /// \return 'true' if the setting was succesfully changed + /// \return 'true' if the setting was successfully changed bool setSetting(int settingId, ///< Setting ID number. see SETTING_... defines. int value ///< New setting value. ); @@ -286,6 +328,11 @@ public: /// Returns number of samples currently unprocessed. virtual uint numUnprocessedSamples() const; + /// Return number of channels + uint numChannels() const + { + return channels; + } /// Other handy functions that are implemented in the ancestor classes (see /// classes 'FIFOProcessor' and 'FIFOSamplePipe') diff --git a/Externals/soundtouch/SoundTouch.vcxproj b/Externals/soundtouch/SoundTouch.vcxproj index c29234fada..8ea3563cdf 100644 --- a/Externals/soundtouch/SoundTouch.vcxproj +++ b/Externals/soundtouch/SoundTouch.vcxproj @@ -44,6 +44,7 @@ + @@ -53,4 +54,4 @@ - \ No newline at end of file + diff --git a/Externals/soundtouch/TDStretch.cpp b/Externals/soundtouch/TDStretch.cpp index bb473a9f63..ce49310900 100644 --- a/Externals/soundtouch/TDStretch.cpp +++ b/Externals/soundtouch/TDStretch.cpp @@ -4,8 +4,14 @@ /// while maintaining the original pitch by using a time domain WSOLA-like /// method with several performance-increasing tweaks. /// -/// Note : MMX optimized functions reside in a separate, platform-specific -/// file, e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp' +/// Notes : MMX optimized functions reside in a separate, platform-specific +/// file, e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'. +/// +/// This source file contains OpenMP optimizations that allow speeding up the +/// corss-correlation algorithm by executing it in several threads / CPU cores +/// in parallel. See the following article link for more detailed discussion +/// about SoundTouch OpenMP optimizations: +/// http://www.softwarecoven.com/parallel-computing-in-embedded-mobile-devices /// /// Author : Copyright (c) Olli Parviainen /// Author e-mail : oparviai 'at' iki.fi @@ -13,13 +19,6 @@ /// //////////////////////////////////////////////////////////////////////////////// // -// Last changed : $Date: 2015-08-09 00:00:15 +0300 (Sun, 09 Aug 2015) $ -// File revision : $Revision: 1.12 $ -// -// $Id: TDStretch.cpp 226 2015-08-08 21:00:15Z oparviai $ -// -//////////////////////////////////////////////////////////////////////////////// -// // License : // // SoundTouch audio processing library @@ -128,8 +127,13 @@ void TDStretch::setParameters(int aSampleRate, int aSequenceMS, int aSeekWindowMS, int aOverlapMS) { // accept only positive parameter values - if zero or negative, use old values instead - if (aSampleRate > 0) this->sampleRate = aSampleRate; - if (aOverlapMS > 0) this->overlapMs = aOverlapMS; + if (aSampleRate > 0) + { + if (aSampleRate > 192000) ST_THROW_RT_ERROR("Error: Excessive samplerate"); + this->sampleRate = aSampleRate; + } + + if (aOverlapMS > 0) this->overlapMs = aOverlapMS; if (aSequenceMS > 0) { @@ -219,6 +223,7 @@ void TDStretch::clearInput() { inputBuffer.clear(); clearMidBuffer(); + isBeginning = true; } @@ -297,12 +302,13 @@ int TDStretch::seekBestOverlapPositionFull(const SAMPLETYPE *refPos) int i; double norm; - bestCorr = FLT_MIN; + bestCorr = -FLT_MAX; bestOffs = 0; // Scans for the best correlation value by testing each possible position // over the permitted range. bestCorr = calcCrossCorr(refPos, pMidBuffer, norm); + bestCorr = (bestCorr + 0.1) * 0.75; #pragma omp parallel for for (i = 1; i < seekLength; i ++) @@ -354,7 +360,7 @@ int TDStretch::seekBestOverlapPositionFull(const SAMPLETYPE *refPos) // with improved precision // // Based on testing: -// - This algorithm gives on average 99% as good match as the full algorith +// - This algorithm gives on average 99% as good match as the full algorithm // - this quick seek algorithm finds the best match on ~90% of cases // - on those 10% of cases when this algorithm doesn't find best match, // it still finds on average ~90% match vs. the best possible match @@ -373,12 +379,10 @@ int TDStretch::seekBestOverlapPositionQuick(const SAMPLETYPE *refPos) // note: 'float' types used in this function in case that the platform would need to use software-fp - bestCorr = FLT_MIN; - bestOffs = SCANWIND; - bestCorr2 = FLT_MIN; - bestOffs2 = 0; - - int best = 0; + bestCorr = + bestCorr2 = -FLT_MAX; + bestOffs = + bestOffs2 = SCANWIND; // Scans for the best correlation value by testing each possible position // over the permitted range. Look for two best matches on the first pass to @@ -436,7 +440,6 @@ int TDStretch::seekBestOverlapPositionQuick(const SAMPLETYPE *refPos) { bestCorr = corr; bestOffs = i; - best = 1; } } @@ -458,7 +461,6 @@ int TDStretch::seekBestOverlapPositionQuick(const SAMPLETYPE *refPos) { bestCorr = corr; bestOffs = i; - best = 2; } } @@ -515,18 +517,18 @@ void TDStretch::clearCrossCorrState() void TDStretch::calcSeqParameters() { // Adjust tempo param according to tempo, so that variating processing sequence length is used - // at varius tempo settings, between the given low...top limits + // at various tempo settings, between the given low...top limits #define AUTOSEQ_TEMPO_LOW 0.5 // auto setting low tempo range (-50%) #define AUTOSEQ_TEMPO_TOP 2.0 // auto setting top tempo range (+100%) // sequence-ms setting values at above low & top tempo - #define AUTOSEQ_AT_MIN 125.0 - #define AUTOSEQ_AT_MAX 50.0 + #define AUTOSEQ_AT_MIN 90.0 + #define AUTOSEQ_AT_MAX 40.0 #define AUTOSEQ_K ((AUTOSEQ_AT_MAX - AUTOSEQ_AT_MIN) / (AUTOSEQ_TEMPO_TOP - AUTOSEQ_TEMPO_LOW)) #define AUTOSEQ_C (AUTOSEQ_AT_MIN - (AUTOSEQ_K) * (AUTOSEQ_TEMPO_LOW)) // seek-window-ms setting values at above low & top tempoq - #define AUTOSEEK_AT_MIN 25.0 + #define AUTOSEEK_AT_MIN 20.0 #define AUTOSEEK_AT_MAX 15.0 #define AUTOSEEK_K ((AUTOSEEK_AT_MAX - AUTOSEEK_AT_MIN) / (AUTOSEQ_TEMPO_TOP - AUTOSEQ_TEMPO_LOW)) #define AUTOSEEK_C (AUTOSEEK_AT_MIN - (AUTOSEEK_K) * (AUTOSEQ_TEMPO_LOW)) @@ -586,9 +588,8 @@ void TDStretch::setTempo(double newTempo) // Sets the number of channels, 1 = mono, 2 = stereo void TDStretch::setChannels(int numChannels) { - assert(numChannels > 0); - if (channels == numChannels) return; -// assert(numChannels == 1 || numChannels == 2); + if (!verifyNumberOfChannels(numChannels) || + (channels == numChannels)) return; channels = numChannels; inputBuffer.setChannels(channels); @@ -637,7 +638,8 @@ void TDStretch::processNominalTempo() // the result into 'outputBuffer' void TDStretch::processSamples() { - int ovlSkip, offset; + int ovlSkip; + int offset = 0; int temp; /* Removed this small optimization - can introduce a click to sound when tempo setting @@ -654,35 +656,61 @@ 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 - // position - offset = seekBestOverlapPosition(inputBuffer.ptrBegin()); - - // Mix the samples in the 'inputBuffer' at position of 'offset' with the - // samples in 'midBuffer' using sliding overlapping - // ... first partially overlap with the end of the previous sequence - // (that's in 'midBuffer') - overlap(outputBuffer.ptrEnd((uint)overlapLength), inputBuffer.ptrBegin(), (uint)offset); - outputBuffer.putSamples((uint)overlapLength); + if (isBeginning == false) + { + // apart from the very beginning of the track, + // scan for the best overlapping position & do overlap-add + offset = seekBestOverlapPosition(inputBuffer.ptrBegin()); + + // Mix the samples in the 'inputBuffer' at position of 'offset' with the + // samples in 'midBuffer' using sliding overlapping + // ... first partially overlap with the end of the previous sequence + // (that's in 'midBuffer') + overlap(outputBuffer.ptrEnd((uint)overlapLength), inputBuffer.ptrBegin(), (uint)offset); + outputBuffer.putSamples((uint)overlapLength); + offset += overlapLength; + } + else + { + // Adjust processing offset at beginning of track by not perform initial overlapping + // and compensating that in the 'input buffer skip' calculation + isBeginning = false; + int skip = (int)(tempo * overlapLength + 0.5); + + #ifdef SOUNDTOUCH_ALLOW_NONEXACT_SIMD_OPTIMIZATION + #ifdef SOUNDTOUCH_ALLOW_SSE + // if SSE mode, round the skip amount to value corresponding to aligned memory address + if (channels == 1) + { + skip &= -4; + } + else if (channels == 2) + { + skip &= -2; + } + #endif + #endif + skipFract -= skip; + assert(nominalSkip >= -skipFract); + } // ... then copy sequence samples from 'inputBuffer' to output: - // length of sequence - 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 - overlapLength)) { continue; // just in case, shouldn't really happen } - outputBuffer.putSamples(inputBuffer.ptrBegin() + channels * (offset + overlapLength), (uint)temp); + // length of sequence + temp = (seekWindowLength - 2 * overlapLength); + outputBuffer.putSamples(inputBuffer.ptrBegin() + channels * offset, (uint)temp); // Copies the end of the current sequence from 'inputBuffer' to // 'midBuffer' for being mixed with the beginning of the next // processing sequence and so on - assert((offset + temp + overlapLength * 2) <= (int)inputBuffer.numSamples()); - memcpy(pMidBuffer, inputBuffer.ptrBegin() + channels * (offset + temp + overlapLength), + assert((offset + temp + overlapLength) <= (int)inputBuffer.numSamples()); + memcpy(pMidBuffer, inputBuffer.ptrBegin() + channels * (offset + temp), channels * sizeof(SAMPLETYPE) * overlapLength); // Remove the processed samples from the input buffer. Update @@ -776,7 +804,7 @@ TDStretch * TDStretch::newInstance() ////////////////////////////////////////////////////////////////////////////// // -// Integer arithmetics specific algorithm implementations. +// Integer arithmetic specific algorithm implementations. // ////////////////////////////////////////////////////////////////////////////// @@ -879,7 +907,12 @@ double TDStretch::calcCrossCorr(const short *mixingPos, const short *compare, do if (lnorm > maxnorm) { - maxnorm = lnorm; + // modify 'maxnorm' inside critical section to avoid multi-access conflict if in OpenMP mode + #pragma omp critical + if (lnorm > maxnorm) + { + maxnorm = lnorm; + } } // Normalize result by dividing by sqrt(norm) - this step is easiest // done using floating point operation @@ -936,7 +969,7 @@ double TDStretch::calcCrossCorrAccumulate(const short *mixingPos, const short *c ////////////////////////////////////////////////////////////////////////////// // -// Floating point arithmetics specific algorithm implementations. +// Floating point arithmetic specific algorithm implementations. // #ifdef SOUNDTOUCH_FLOAT_SAMPLES diff --git a/Externals/soundtouch/TDStretch.h b/Externals/soundtouch/TDStretch.h index 213304c982..4118f9f474 100644 --- a/Externals/soundtouch/TDStretch.h +++ b/Externals/soundtouch/TDStretch.h @@ -13,13 +13,6 @@ /// //////////////////////////////////////////////////////////////////////////////// // -// Last changed : $Date: 2015-08-09 00:00:15 +0300 (Sun, 09 Aug 2015) $ -// File revision : $Revision: 4 $ -// -// $Id: TDStretch.h 226 2015-08-08 21:00:15Z oparviai $ -// -//////////////////////////////////////////////////////////////////////////////// -// // License : // // SoundTouch audio processing library @@ -134,6 +127,7 @@ protected: bool bQuickSeek; bool bAutoSeqSetting; bool bAutoSeekSetting; + bool isBeginning; SAMPLETYPE *pMidBuffer; SAMPLETYPE *pMidBufferUnaligned; @@ -163,7 +157,6 @@ protected: void calcSeqParameters(); void adaptNormalizer(); - /// Changes the tempo of the given sound samples. /// Returns amount of samples returned in the "output" buffer. /// The maximum amount of samples that can be returned at a time is set by @@ -247,8 +240,13 @@ public: { return seekWindowLength - overlapLength; } -}; + /// return approximate initial input-output latency + int getLatency() const + { + return sampleReq; + } +}; // Implementation-specific class declarations: diff --git a/Externals/soundtouch/cpu_detect.h b/Externals/soundtouch/cpu_detect.h index 025781dae1..0cdc22356f 100644 --- a/Externals/soundtouch/cpu_detect.h +++ b/Externals/soundtouch/cpu_detect.h @@ -12,13 +12,6 @@ /// //////////////////////////////////////////////////////////////////////////////// // -// Last changed : $Date: 2008-02-10 18:26:55 +0200 (Sun, 10 Feb 2008) $ -// File revision : $Revision: 4 $ -// -// $Id: cpu_detect.h 11 2008-02-10 16:26:55Z oparviai $ -// -//////////////////////////////////////////////////////////////////////////////// -// // License : // // SoundTouch audio processing library diff --git a/Externals/soundtouch/cpu_detect_x86.cpp b/Externals/soundtouch/cpu_detect_x86.cpp index 5ef0246216..b1286106eb 100644 --- a/Externals/soundtouch/cpu_detect_x86.cpp +++ b/Externals/soundtouch/cpu_detect_x86.cpp @@ -11,13 +11,6 @@ /// //////////////////////////////////////////////////////////////////////////////// // -// Last changed : $Date: 2014-01-07 20:24:28 +0200 (Tue, 07 Jan 2014) $ -// File revision : $Revision: 4 $ -// -// $Id: cpu_detect_x86.cpp 183 2014-01-07 18:24:28Z oparviai $ -// -//////////////////////////////////////////////////////////////////////////////// -// // License : // // SoundTouch audio processing library @@ -75,7 +68,6 @@ void disableExtensions(uint dwDisableMask) } - /// Checks which instruction set extensions are supported by the CPU. uint detectCPUextensions(void) { diff --git a/Externals/soundtouch/mmx_optimized.cpp b/Externals/soundtouch/mmx_optimized.cpp index 8ad2811b9a..741ba4f22e 100644 --- a/Externals/soundtouch/mmx_optimized.cpp +++ b/Externals/soundtouch/mmx_optimized.cpp @@ -20,13 +20,6 @@ /// //////////////////////////////////////////////////////////////////////////////// // -// Last changed : $Date: 2015-08-09 00:00:15 +0300 (Sun, 09 Aug 2015) $ -// File revision : $Revision: 4 $ -// -// $Id: mmx_optimized.cpp 226 2015-08-08 21:00:15Z oparviai $ -// -//////////////////////////////////////////////////////////////////////////////// -// // License : // // SoundTouch audio processing library @@ -125,7 +118,12 @@ double TDStretchMMX::calcCrossCorr(const short *pV1, const short *pV2, double &d if (norm > (long)maxnorm) { - maxnorm = norm; + // modify 'maxnorm' inside critical section to avoid multi-access conflict if in OpenMP mode + #pragma omp critical + if (norm > (long)maxnorm) + { + maxnorm = norm; + } } // Normalize result by dividing by sqrt(norm) - this step is easiest @@ -219,7 +217,6 @@ void TDStretchMMX::clearCrossCorrState() } - // MMX-optimized version of the function overlapStereo void TDStretchMMX::overlapStereo(short *output, const short *input) const { @@ -335,7 +332,6 @@ void FIRFilterMMX::setCoefficients(const short *coeffs, uint newLength, uint uRe } - // mmx-optimized version of the filter routine for stereo sound uint FIRFilterMMX::evaluateFilterStereo(short *dest, const short *src, uint numSamples) const { @@ -392,4 +388,9 @@ uint FIRFilterMMX::evaluateFilterStereo(short *dest, const short *src, uint numS return (numSamples & 0xfffffffe) - length; } +#else + +// workaround to not complain about empty module +bool _dontcomplain_mmx_empty; + #endif // SOUNDTOUCH_ALLOW_MMX diff --git a/Externals/soundtouch/soundtouch_config.h b/Externals/soundtouch/soundtouch_config.h new file mode 100644 index 0000000000..950e3353e2 --- /dev/null +++ b/Externals/soundtouch/soundtouch_config.h @@ -0,0 +1,2 @@ +#define SOUNDTOUCH_INTEGER_SAMPLES 1 //< 16bit integer samples +//#define SOUNDTOUCH_FLOAT_SAMPLES 1 //< 32bit float samples diff --git a/Externals/soundtouch/sse_optimized.cpp b/Externals/soundtouch/sse_optimized.cpp index 490d0d2080..0dc637015f 100644 --- a/Externals/soundtouch/sse_optimized.cpp +++ b/Externals/soundtouch/sse_optimized.cpp @@ -23,13 +23,6 @@ /// //////////////////////////////////////////////////////////////////////////////// // -// Last changed : $Date: 2015-08-09 00:00:15 +0300 (Sun, 09 Aug 2015) $ -// File revision : $Revision: 4 $ -// -// $Id: sse_optimized.cpp 226 2015-08-08 21:00:15Z oparviai $ -// -//////////////////////////////////////////////////////////////////////////////// -// // License : // // SoundTouch audio processing library -- 2.26.2