diff --git a/Externals/soundtouch/AAFilter.cpp b/Externals/soundtouch/AAFilter.cpp index c69f356f68..89c54cc265 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 @@ -61,7 +54,7 @@ using namespace soundtouch; static void _DEBUG_SAVE_AAFIR_COEFFS(SAMPLETYPE *coeffs, int len) { FILE *fptr = fopen("aa_filter_coeffs.txt", "wt"); - if (fptr == NULL) return; + if (fptr == nullptr) return; for (int i = 0; i < len; i ++) { @@ -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..4c11a2e9e4 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; -/// decay constant for calculating RMS volume sliding average approximation -/// (time constant is about 10 sec) -const float avgdecay = 0.99986f; +// decimated sample block size +static const int DECIMATED_BLOCK_SIZE = 256; -/// Normalization coefficient for calculating RMS sliding average approximation. -const float avgnorm = (1 - avgdecay); +/// 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); + +/// Moving average N size +static const int MOVING_AVERAGE_N = 15; + +/// 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,122 @@ 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)); - for (i = 0; i < numsamples; i ++) + 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++) { - // calc average RMS volume - RMSVolumeAccu *= avgdecay; - val = (float)fabs((float)samples[i]); - RMSVolumeAccu += val * val; + tmp[i] = hamw2[i] * hamw2[i] * pBuffer[i]; + } - // 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)) + #pragma omp parallel for + for (int offs = windowStart; offs < windowLen; offs++) + { + 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 + } + + int skipstep = XCORR_UPDATE_SEQUENCE / OVERLAP_FACTOR; + + // 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++) + { + 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; } - // smooth amplitude envelope - envelopeAccu *= decay; - envelopeAccu += val; - out = (LONG_SAMPLETYPE)(envelopeAccu * norm); - -#ifdef SOUNDTOUCH_INTEGER_SAMPLES - // cut peaks (shouldn't be necessary though) - if (out > 32767) out = 32767; -#endif // SOUNDTOUCH_INTEGER_SAMPLES - samples[i] = (SAMPLETYPE)out; + 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 +419,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 +490,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; - coeff = 60.0 * ((double)sampleRate / (double)decimateBy); - - // save bpm debug analysis data if debug data enabled - _SaveDebugData(xcorr, windowStart, windowLen, coeff); - // remove bias from xcorr data removeBias(); + coeff = 60.0 * ((double)sampleRate / (double)decimateBy); + + // save bpm debug data if debug data writing enabled + _SaveDebugData("soundtouch-bpm-xcorr.txt", xcorr, windowStart, windowLen, coeff); + + // 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 nullptr in "pos" & "values". +/// +/// \return number of beats in the arrays. +int BPMDetect::getBeats(float *pos, float *values, int max_num) +{ + int num = (int)beats.size(); + if ((!pos) || (!values)) return num; // pos or values nullptr, 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..763266f4ac 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 + +//////////////////////////////////////////////////////////////////////////////// + + typedef struct + { + float pos; + float strength; + } BEAT; -/// 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; + class IIR2_filter + { + double coeffs[5]; + double prev[5]; - /// 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 - ); + public: + IIR2_filter(const double *lpf_coeffs); + float update(float x); + }; - /// 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(); -}; + /// 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 nullptr 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/CMakeLists.txt b/Externals/soundtouch/CMakeLists.txt index eefc3a8bba..46b5a2a965 100644 --- a/Externals/soundtouch/CMakeLists.txt +++ b/Externals/soundtouch/CMakeLists.txt @@ -1,3 +1,6 @@ +# OSX meeds to know +check_and_add_flag(CXX11 -std=c++11) + set(SRCS AAFilter.cpp BPMDetect.cpp @@ -17,4 +20,3 @@ set(SRCS add_library(SoundTouch STATIC ${SRCS}) dolphin_disable_warnings_msvc(SoundTouch) -add_definitions(-w) diff --git a/Externals/soundtouch/FIFOSampleBuffer.cpp b/Externals/soundtouch/FIFOSampleBuffer.cpp index 5f5ec4b7db..420e0d7998 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 @@ -57,8 +50,8 @@ FIFOSampleBuffer::FIFOSampleBuffer(int numChannels) { assert(numChannels > 0); sizeInBytes = 0; // reasonable initial value - buffer = NULL; - bufferUnaligned = NULL; + buffer = nullptr; + bufferUnaligned = nullptr; samplesInBuffer = 0; bufferPos = 0; channels = (uint)numChannels; @@ -70,8 +63,8 @@ FIFOSampleBuffer::FIFOSampleBuffer(int numChannels) FIFOSampleBuffer::~FIFOSampleBuffer() { delete[] bufferUnaligned; - bufferUnaligned = NULL; - buffer = NULL; + bufferUnaligned = nullptr; + buffer = nullptr; } @@ -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. @@ -172,7 +166,7 @@ void FIFOSampleBuffer::ensureCapacity(uint capacityRequirement) sizeInBytes = (capacityRequirement * channels * sizeof(SAMPLETYPE) + 4095) & (uint)-4096; assert(sizeInBytes % 2 == 0); tempUnaligned = new SAMPLETYPE[sizeInBytes / sizeof(SAMPLETYPE) + 16 / sizeof(SAMPLETYPE)]; - if (tempUnaligned == NULL) + if (tempUnaligned == nullptr) { ST_THROW_RT_ERROR("Couldn't allocate memory!\n"); } @@ -272,3 +266,10 @@ uint FIFOSampleBuffer::adjustAmountOfSamples(uint numSamples) return samplesInBuffer; } + +/// Add silence to end of buffer +void FIFOSampleBuffer::addSilent(uint nSamples) +{ + memset(ptrEnd(nSamples), 0, sizeof(SAMPLETYPE) * nSamples * channels); + samplesInBuffer += nSamples; +} diff --git a/Externals/soundtouch/FIFOSampleBuffer.h b/Externals/soundtouch/FIFOSampleBuffer.h index 6f33df3daa..6c5785a6e2 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 @@ -98,7 +91,7 @@ public: ); /// destructor - ~FIFOSampleBuffer(); + ~FIFOSampleBuffer() override; /// Returns a pointer to the beginning of the output samples. /// This function is provided for accessing the output samples directly. @@ -107,7 +100,7 @@ public: /// When using this function to output samples, also remember to 'remove' the /// output samples from the buffer by calling the /// 'receiveSamples(numSamples)' function - virtual SAMPLETYPE *ptrBegin(); + virtual SAMPLETYPE *ptrBegin() override; /// Returns a pointer to the end of the used part of the sample buffer (i.e. /// where the new samples are to be inserted). This function may be used for @@ -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. ); @@ -128,7 +121,7 @@ public: /// the sample buffer. virtual void putSamples(const SAMPLETYPE *samples, ///< Pointer to samples. uint numSamples ///< Number of samples to insert. - ); + ) override; /// Adjusts the book-keeping to increase number of samples in the buffer without /// copying any actual samples. @@ -146,7 +139,7 @@ public: /// \return Number of samples returned. virtual uint receiveSamples(SAMPLETYPE *output, ///< Buffer where to copy output samples. uint maxSamples ///< How many samples to receive at max. - ); + ) override; /// Adjusts book-keeping so that given number of samples are removed from beginning of the /// sample buffer without copying them anywhere. @@ -154,10 +147,10 @@ public: /// Used to reduce the number of samples in the buffer when accessing the sample buffer directly /// with 'ptrBegin' function. virtual uint receiveSamples(uint maxSamples ///< Remove this many samples from the beginning of pipe. - ); + ) override; /// Returns number of samples currently available. - virtual uint numSamples() const; + virtual uint numSamples() const override; /// Sets number of channels, 1 = mono, 2 = stereo. void setChannels(int numChannels); @@ -169,14 +162,17 @@ public: } /// Returns nonzero if there aren't any samples available for outputting. - virtual int isEmpty() const; + virtual int isEmpty() const override; /// Clears all the samples. - virtual void clear(); + virtual void clear() override; /// allow trimming (downwards) amount of samples in pipeline. /// Returns adjusted amount of samples - uint adjustAmountOfSamples(uint numSamples); + uint adjustAmountOfSamples(uint numSamples) override; + + /// Add silence to end of buffer + void addSilent(uint nSamples); }; } diff --git a/Externals/soundtouch/FIFOSamplePipe.h b/Externals/soundtouch/FIFOSamplePipe.h index 6e3105970b..2ce5178a43 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 @@ -140,20 +144,18 @@ protected: /// Sets output pipe. void setOutPipe(FIFOSamplePipe *pOutput) { - assert(output == NULL); - assert(pOutput != NULL); + assert(output == nullptr); + assert(pOutput != nullptr); output = pOutput; } - /// Constructor. Doesn't define output pipe; it has to be set be /// 'setOutPipe' function. FIFOProcessor() { - output = NULL; + output = nullptr; } - /// Constructor. Configures output pipe. FIFOProcessor(FIFOSamplePipe *pOutput ///< Output pipe. ) @@ -161,13 +163,11 @@ protected: output = pOutput; } - /// Destructor. - virtual ~FIFOProcessor() + virtual ~FIFOProcessor() override { } - /// 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! @@ -175,7 +175,7 @@ protected: /// When using this function to output samples, also remember to 'remove' the /// output samples from the buffer by calling the /// 'receiveSamples(numSamples)' function - virtual SAMPLETYPE *ptrBegin() + virtual SAMPLETYPE *ptrBegin() override { return output->ptrBegin(); } @@ -189,44 +189,40 @@ public: /// \return Number of samples returned. virtual uint receiveSamples(SAMPLETYPE *outBuffer, ///< Buffer where to copy output samples. uint maxSamples ///< How many samples to receive at max. - ) + ) override { 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. /// /// Used to reduce the number of samples in the buffer when accessing the sample buffer directly /// with 'ptrBegin' function. virtual uint receiveSamples(uint maxSamples ///< Remove this many samples from the beginning of pipe. - ) + ) override { return output->receiveSamples(maxSamples); } - /// Returns number of samples currently available. - virtual uint numSamples() const + virtual uint numSamples() const override { return output->numSamples(); } - /// Returns nonzero if there aren't any samples available for outputting. - virtual int isEmpty() const + virtual int isEmpty() const override { return output->isEmpty(); } /// allow trimming (downwards) amount of samples in pipeline. /// Returns adjusted amount of samples - virtual uint adjustAmountOfSamples(uint numSamples) + virtual uint adjustAmountOfSamples(uint numSamples) override { return output->adjustAmountOfSamples(numSamples); } - }; } diff --git a/Externals/soundtouch/FIRFilter.cpp b/Externals/soundtouch/FIRFilter.cpp index e56969b053..473ca07a75 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 @@ -60,53 +59,43 @@ FIRFilter::FIRFilter() resultDivider = 0; length = 0; lengthDiv8 = 0; - filterCoeffs = NULL; + filterCoeffs = nullptr; + filterCoeffsStereo = nullptr; } FIRFilter::~FIRFilter() { delete[] filterCoeffs; + delete[] filterCoeffsStereo; } + // Usual C-version of the filter routine for stereo sound uint FIRFilter::evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const { int j, end; -#ifdef SOUNDTOUCH_FLOAT_SAMPLES - // when using floating point samples, use a scaler instead of a divider - // because division is much slower operation than multiplying. - double dScaler = 1.0 / (double)resultDivider; -#endif + // hint compiler autovectorization that loop length is divisible by 8 + uint ilength = length & -8; - assert(length != 0); - assert(src != NULL); - assert(dest != NULL); - assert(filterCoeffs != NULL); + assert((length != 0) && (length == ilength) && (src != nullptr) && (dest != nullptr) && (filterCoeffs != nullptr)); + assert(numSamples > ilength); - end = 2 * (numSamples - length); + end = 2 * (numSamples - ilength); #pragma omp parallel for for (j = 0; j < end; j += 2) { const SAMPLETYPE *ptr; LONG_SAMPLETYPE suml, sumr; - uint i; suml = sumr = 0; ptr = src + j; - for (i = 0; i < length; i += 4) + for (uint i = 0; i < ilength; i ++) { - // loop is unrolled by factor of 4 here for efficiency - suml += ptr[2 * i + 0] * filterCoeffs[i + 0] + - ptr[2 * i + 2] * filterCoeffs[i + 1] + - ptr[2 * i + 4] * filterCoeffs[i + 2] + - ptr[2 * i + 6] * filterCoeffs[i + 3]; - sumr += ptr[2 * i + 1] * filterCoeffs[i + 0] + - ptr[2 * i + 3] * filterCoeffs[i + 1] + - ptr[2 * i + 5] * filterCoeffs[i + 2] + - ptr[2 * i + 7] * filterCoeffs[i + 3]; + suml += ptr[2 * i] * filterCoeffsStereo[2 * i]; + sumr += ptr[2 * i + 1] * filterCoeffsStereo[2 * i + 1]; } #ifdef SOUNDTOUCH_INTEGER_SAMPLES @@ -116,54 +105,41 @@ uint FIRFilter::evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, ui suml = (suml < -32768) ? -32768 : (suml > 32767) ? 32767 : suml; // saturate to 16 bit integer limits sumr = (sumr < -32768) ? -32768 : (sumr > 32767) ? 32767 : sumr; -#else - suml *= dScaler; - sumr *= dScaler; #endif // SOUNDTOUCH_INTEGER_SAMPLES dest[j] = (SAMPLETYPE)suml; dest[j + 1] = (SAMPLETYPE)sumr; } - return numSamples - length; + return numSamples - ilength; } - - // Usual C-version of the filter routine for mono sound uint FIRFilter::evaluateFilterMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const { int j, end; -#ifdef SOUNDTOUCH_FLOAT_SAMPLES - // when using floating point samples, use a scaler instead of a divider - // because division is much slower operation than multiplying. - double dScaler = 1.0 / (double)resultDivider; -#endif - assert(length != 0); + // hint compiler autovectorization that loop length is divisible by 8 + int ilength = length & -8; - end = numSamples - length; + assert(ilength != 0); + + end = numSamples - ilength; #pragma omp parallel for - for (j = 0; j < end; j ++) + for (j = 0; j < end; j ++) { const SAMPLETYPE *pSrc = src + j; LONG_SAMPLETYPE sum; - uint i; + int i; sum = 0; - for (i = 0; i < length; i += 4) + for (i = 0; i < ilength; i ++) { - // loop is unrolled by factor of 4 here for efficiency - sum += pSrc[i + 0] * filterCoeffs[i + 0] + - pSrc[i + 1] * filterCoeffs[i + 1] + - pSrc[i + 2] * filterCoeffs[i + 2] + - pSrc[i + 3] * filterCoeffs[i + 3]; + sum += pSrc[i] * filterCoeffs[i]; } #ifdef SOUNDTOUCH_INTEGER_SAMPLES sum >>= resultDivFactor; // saturate to 16 bit integer limits sum = (sum < -32768) ? -32768 : (sum > 32767) ? 32767 : sum; -#else - sum *= dScaler; #endif // SOUNDTOUCH_INTEGER_SAMPLES dest[j] = (SAMPLETYPE)sum; } @@ -175,26 +151,24 @@ uint FIRFilter::evaluateFilterMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, uin { int j, end; -#ifdef SOUNDTOUCH_FLOAT_SAMPLES - // when using floating point samples, use a scaler instead of a divider - // because division is much slower operation than multiplying. - double dScaler = 1.0 / (double)resultDivider; -#endif - assert(length != 0); - assert(src != NULL); - assert(dest != NULL); - assert(filterCoeffs != NULL); + assert(src != nullptr); + assert(dest != nullptr); + assert(filterCoeffs != nullptr); assert(numChannels < 16); - end = numChannels * (numSamples - length); + // hint compiler autovectorization that loop length is divisible by 8 + int ilength = length & -8; + + end = numChannels * (numSamples - ilength); #pragma omp parallel for for (j = 0; j < end; j += numChannels) { const SAMPLETYPE *ptr; LONG_SAMPLETYPE sums[16]; - uint c, i; + uint c; + int i; for (c = 0; c < numChannels; c ++) { @@ -203,7 +177,7 @@ uint FIRFilter::evaluateFilterMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, uin ptr = src + j; - for (i = 0; i < length; i ++) + for (i = 0; i < ilength; i ++) { SAMPLETYPE coef=filterCoeffs[i]; for (c = 0; c < numChannels; c ++) @@ -217,13 +191,11 @@ uint FIRFilter::evaluateFilterMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, uin { #ifdef SOUNDTOUCH_INTEGER_SAMPLES sums[c] >>= resultDivFactor; -#else - sums[c] *= dScaler; #endif // SOUNDTOUCH_INTEGER_SAMPLES dest[j+c] = (SAMPLETYPE)sums[c]; } } - return numSamples - length; + return numSamples - ilength; } @@ -235,6 +207,13 @@ void FIRFilter::setCoefficients(const SAMPLETYPE *coeffs, uint newLength, uint u assert(newLength > 0); if (newLength % 8) ST_THROW_RT_ERROR("FIR filter length not divisible by 8"); + #ifdef SOUNDTOUCH_FLOAT_SAMPLES + // scale coefficients already here if using floating samples + double scale = 1.0 / resultDivider; + #else + short scale = 1; + #endif + lengthDiv8 = newLength / 8; length = lengthDiv8 * 8; assert(length == newLength); @@ -244,7 +223,16 @@ void FIRFilter::setCoefficients(const SAMPLETYPE *coeffs, uint newLength, uint u delete[] filterCoeffs; filterCoeffs = new SAMPLETYPE[length]; - memcpy(filterCoeffs, coeffs, length * sizeof(SAMPLETYPE)); + delete[] filterCoeffsStereo; + filterCoeffsStereo = new SAMPLETYPE[length*2]; + for (uint i = 0; i < length; i ++) + { + filterCoeffs[i] = (SAMPLETYPE)(coeffs[i] * scale); + // create also stereo set of filter coefficients: this allows compiler + // to autovectorize filter evaluation much more efficiently + filterCoeffsStereo[2 * i] = (SAMPLETYPE)(coeffs[i] * scale); + filterCoeffsStereo[2 * i + 1] = (SAMPLETYPE)(coeffs[i] * scale); + } } @@ -254,7 +242,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,10 +271,9 @@ 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) +void * FIRFilter::operator new(size_t) { // Notice! don't use "new FIRFilter" directly, use "newInstance" to create a new instance instead! ST_THROW_RT_ERROR("Error in FIRFilter::new: Don't use 'new FIRFilter', use 'newInstance' member instead!"); diff --git a/Externals/soundtouch/FIRFilter.h b/Externals/soundtouch/FIRFilter.h index 6b14238ce8..90b730fcb5 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 @@ -64,6 +57,7 @@ protected: // Memory for filter coefficients SAMPLETYPE *filterCoeffs; + SAMPLETYPE *filterCoeffsStereo; virtual uint evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, @@ -112,12 +106,12 @@ public: short *filterCoeffsUnalign; short *filterCoeffsAlign; - virtual uint evaluateFilterStereo(short *dest, const short *src, uint numSamples) const; + virtual uint evaluateFilterStereo(short *dest, const short *src, uint numSamples) const override; public: FIRFilterMMX(); ~FIRFilterMMX(); - virtual void setCoefficients(const short *coeffs, uint newLength, uint uResultDivFactor); + virtual void setCoefficients(const short *coeffs, uint newLength, uint uResultDivFactor) override; }; #endif // SOUNDTOUCH_ALLOW_MMX @@ -131,12 +125,12 @@ public: float *filterCoeffsUnalign; float *filterCoeffsAlign; - virtual uint evaluateFilterStereo(float *dest, const float *src, uint numSamples) const; + virtual uint evaluateFilterStereo(float *dest, const float *src, uint numSamples) const override; public: FIRFilterSSE(); ~FIRFilterSSE(); - virtual void setCoefficients(const float *coeffs, uint newLength, uint uResultDivFactor); + virtual void setCoefficients(const float *coeffs, uint newLength, uint uResultDivFactor) override; }; #endif // SOUNDTOUCH_ALLOW_SSE 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..64faf646bb 100644 --- a/Externals/soundtouch/InterpolateCubic.h +++ b/Externals/soundtouch/InterpolateCubic.h @@ -1,67 +1,69 @@ -//////////////////////////////////////////////////////////////////////////////// -/// -/// 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 int transposeMono(SAMPLETYPE *dest, + const SAMPLETYPE *src, + int &srcSamples) override; + virtual int transposeStereo(SAMPLETYPE *dest, + const SAMPLETYPE *src, + int &srcSamples) override; + virtual int transposeMulti(SAMPLETYPE *dest, + const SAMPLETYPE *src, + int &srcSamples) override; + + double fract; + +public: + InterpolateCubic(); + + virtual void resetRegisters() override; + + virtual int getLatency() const override + { + return 1; + } +}; + +} + +#endif diff --git a/Externals/soundtouch/InterpolateLinear.cpp b/Externals/soundtouch/InterpolateLinear.cpp index 8119813857..a11a493a98 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 = (LONG_SAMPLETYPE)(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..8034d76ff1 100644 --- a/Externals/soundtouch/InterpolateLinear.h +++ b/Externals/soundtouch/InterpolateLinear.h @@ -1,92 +1,98 @@ -//////////////////////////////////////////////////////////////////////////////// -/// -/// 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 int transposeMono(SAMPLETYPE *dest, + const SAMPLETYPE *src, + int &srcSamples) override; + virtual int transposeStereo(SAMPLETYPE *dest, + const SAMPLETYPE *src, + int &srcSamples) override; + virtual int transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples) override; +public: + InterpolateLinearInteger(); + + /// Sets new target rate. Normal rate = 1.0, smaller values represent slower + /// rate, larger faster rates. + virtual void setRate(double newRate) override; + + virtual void resetRegisters() override; + + virtual int getLatency() const override + { + return 0; + } +}; + + +/// Linear transposer class that uses floating point arithmetic +class InterpolateLinearFloat : public TransposerBase +{ +protected: + double fract; + + 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(); + + virtual void resetRegisters(); + + int getLatency() const + { + return 0; + } +}; + +} + +#endif diff --git a/Externals/soundtouch/InterpolateShannon.cpp b/Externals/soundtouch/InterpolateShannon.cpp index 1085fd14cb..8d9761336d 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 *, + const SAMPLETYPE *, + int &) +{ + // not implemented + assert(false); + return 0; +} diff --git a/Externals/soundtouch/InterpolateShannon.h b/Externals/soundtouch/InterpolateShannon.h index 54703d9808..e0c6fa5d73 100644 --- a/Externals/soundtouch/InterpolateShannon.h +++ b/Externals/soundtouch/InterpolateShannon.h @@ -1,72 +1,74 @@ -//////////////////////////////////////////////////////////////////////////////// -/// -/// 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: + int transposeMono(SAMPLETYPE *dest, + const SAMPLETYPE *src, + int &srcSamples) override; + int transposeStereo(SAMPLETYPE *dest, + const SAMPLETYPE *src, + int &srcSamples) override; + int transposeMulti(SAMPLETYPE *dest, + const SAMPLETYPE *src, + int &srcSamples) override; + + double fract; + +public: + InterpolateShannon(); + + void resetRegisters() override; + + virtual int getLatency() const override + { + return 3; + } +}; + +} + +#endif diff --git a/Externals/soundtouch/PeakFinder.cpp b/Externals/soundtouch/PeakFinder.cpp index b1e2205c3f..5423c859aa 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 @@ -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 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..a48ea86569 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,15 +50,21 @@ 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); pTransposer = TransposerBase::newInstance(); + clear(); } - RateTransposer::~RateTransposer() { delete pAAFilter; @@ -73,11 +72,14 @@ 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; + clear(); +#endif } @@ -94,7 +96,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) @@ -130,8 +131,6 @@ void RateTransposer::putSamples(const SAMPLETYPE *samples, uint nSamples) // the 'set_returnBuffer_size' function. void RateTransposer::processSamples(const SAMPLETYPE *src, uint nSamples) { - uint count; - if (nSamples == 0) return; // Store samples to input buffer @@ -141,7 +140,7 @@ void RateTransposer::processSamples(const SAMPLETYPE *src, uint nSamples) // the filter if (bUseAAFilter == false) { - count = pTransposer->transpose(outputBuffer, inputBuffer); + (void)pTransposer->transpose(outputBuffer, inputBuffer); return; } @@ -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); @@ -194,6 +192,11 @@ void RateTransposer::clear() outputBuffer.clear(); midBuffer.clear(); inputBuffer.clear(); + pTransposer->resetRegisters(); + + // prefill buffer to avoid losing first samples at beginning of stream + int prefill = getLatency(); + inputBuffer.addSilent(prefill); } @@ -208,6 +211,14 @@ int RateTransposer::isEmpty() const } +/// Return approximate initial input-output latency +int RateTransposer::getLatency() const +{ + return pTransposer->getLatency() + + ((bUseAAFilter) ? (pAAFilter->getLength() / 2) : 0); +} + + ////////////////////////////////////////////////////////////////////////////// // // TransposerBase - Base class for interpolation @@ -280,7 +291,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) @@ -296,7 +307,7 @@ TransposerBase *TransposerBase::newInstance() default: assert(false); - return NULL; + return nullptr; } #endif } diff --git a/Externals/soundtouch/RateTransposer.h b/Externals/soundtouch/RateTransposer.h index e9cedfac9c..411f4e897d 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 @@ -66,8 +59,6 @@ public: }; protected: - virtual void resetRegisters() = 0; - virtual int transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples) = 0; @@ -90,6 +81,9 @@ public: virtual int transpose(FIFOSampleBuffer &dest, FIFOSampleBuffer &src); virtual void setRate(double newRate); virtual void setChannels(int channels); + virtual int getLatency() const = 0; + + virtual void resetRegisters() = 0; // static factory function static TransposerBase *newInstance(); @@ -130,23 +124,11 @@ protected: 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(); + virtual ~RateTransposer() override; /// 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(); @@ -165,13 +147,16 @@ public: /// Adds 'numSamples' pcs of samples from the 'samples' memory position into /// the input of the object. - void putSamples(const SAMPLETYPE *samples, uint numSamples); + void putSamples(const SAMPLETYPE *samples, uint numSamples) override; /// Clears all the samples in the object - void clear(); + void clear() override; /// Returns nonzero if there aren't any samples available for outputting. - int isEmpty() const; + int isEmpty() const override; + + /// Return approximate initial input-output latency + int getLatency() const; }; } diff --git a/Externals/soundtouch/STTypes.h b/Externals/soundtouch/STTypes.h index 375a0a3789..71dd0ebd11 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 @@ -39,33 +32,37 @@ #ifndef STTypes_H #define STTypes_H +typedef unsigned int uint; +typedef unsigned long ulong; + +// Patch for MinGW: on Win64 long is 32-bit +#ifdef _WIN64 + typedef unsigned long long ulongptr; +#else + typedef ulong ulongptr; +#endif + + +// Helper macro for aligning pointer up to next 16-byte boundary +#define SOUNDTOUCH_ALIGN_POINTER_16(x) ( ( (ulongptr)(x) + 15 ) & ~(ulongptr)15 ) + + #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 { - typedef unsigned int uint; - typedef unsigned long ulong; - - // Patch for MinGW: on Win64 long is 32-bit - #ifdef _WIN64 - typedef unsigned long long ulongptr; - #else - typedef ulong ulongptr; - #endif - - - // Helper macro for aligning pointer up to next 16-byte boundary - #define SOUNDTOUCH_ALIGN_POINTER_16(x) ( ( (ulongptr)(x) + 15 ) & ~(ulongptr)15 ) + /// 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 @@ -74,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 @@ -97,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 @@ -109,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: @@ -124,10 +121,10 @@ namespace soundtouch #endif - // If defined, allows the SIMD-optimized routines to take minor shortcuts - // for improved performance. Undefine to require faithfully similar SIMD - // calculations as in normal C implementation. - #define SOUNDTOUCH_ALLOW_NONEXACT_SIMD_OPTIMIZATION 1 + // If defined, allows the SIMD-optimized routines to skip unevenly aligned + // memory offsets that can cause performance penalty in some SIMD implementations. + // Causes slight compromise in sound quality. + // #define SOUNDTOUCH_ALLOW_NONEXACT_SIMD_OPTIMIZATION 1 #ifdef SOUNDTOUCH_INTEGER_SAMPLES @@ -142,16 +139,19 @@ 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 // floating point samples typedef float SAMPLETYPE; - // data type for sample accumulation: Use double to utilize full precision. - typedef double LONG_SAMPLETYPE; + // data type for sample accumulation: Use float also here to enable + // efficient autovectorization + typedef float LONG_SAMPLETYPE; #ifdef SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS // Allow SSE optimizations @@ -160,10 +160,16 @@ namespace soundtouch #endif // SOUNDTOUCH_INTEGER_SAMPLES -}; + #if ((SOUNDTOUCH_ALLOW_SSE) || (__SSE__) || (SOUNDTOUCH_USE_NEON)) + #if SOUNDTOUCH_ALLOW_NONEXACT_SIMD_OPTIMIZATION + #define ST_SIMD_AVOID_UNALIGNED + #endif + #endif + +} // 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..cb161f0d4f 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,27 +410,67 @@ 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); + pTDStretch->getParameters(nullptr, &temp, nullptr, nullptr); return temp; case SETTING_SEEKWINDOW_MS: - pTDStretch->getParameters(NULL, NULL, &temp, NULL); + pTDStretch->getParameters(nullptr, nullptr, &temp, nullptr); return temp; case SETTING_OVERLAP_MS: - pTDStretch->getParameters(NULL, NULL, NULL, &temp); + pTDStretch->getParameters(nullptr, nullptr, nullptr, &temp); return temp; - case SETTING_NOMINAL_INPUT_SEQUENCE : - return pTDStretch->getInputSampleReq(); + case SETTING_NOMINAL_INPUT_SEQUENCE : + { + int size = pTDStretch->getInputSampleReq(); - case SETTING_NOMINAL_OUTPUT_SEQUENCE : - return pTDStretch->getOutputBatchSize(); +#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; + } - default : + 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; + } + + return (int)(latency + 0.5); + } + + 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..0c22cb51cf 100644 --- a/Externals/soundtouch/SoundTouch.h +++ b/Externals/soundtouch/SoundTouch.h @@ -1,27 +1,27 @@ ////////////////////////////////////////////////////////////////////////////// /// -/// SoundTouch - main class for tempo/pitch/rate adjusting routines. +/// SoundTouch - main class for tempo/pitch/rate adjusting routines. /// /// Notes: -/// - Initialize the SoundTouch object instance by setting up the sound stream -/// parameters with functions 'setSampleRate' and 'setChannels', then set +/// - Initialize the SoundTouch object instance by setting up the sound stream +/// parameters with functions 'setSampleRate' and 'setChannels', then set /// desired tempo/pitch/rate settings with the corresponding functions. /// -/// - The SoundTouch class behaves like a first-in-first-out pipeline: The +/// - The SoundTouch class behaves like a first-in-first-out pipeline: The /// samples that are to be processed are fed into one of the pipe by calling -/// function 'putSamples', while the ready processed samples can be read +/// function 'putSamples', while the ready processed samples can be read /// from the other end of the pipeline with function 'receiveSamples'. -/// -/// - The SoundTouch processing classes require certain sized 'batches' of -/// samples in order to process the sound. For this reason the classes buffer -/// incoming samples until there are enough of samples available for +/// +/// - The SoundTouch processing classes require certain sized 'batches' of +/// samples in order to process the sound. For this reason the classes buffer +/// incoming samples until there are enough of samples available for /// processing, then they carry out the processing step and consequently /// make the processed samples available for outputting. -/// -/// - For the above reason, the processing routines introduce a certain +/// +/// - For the above reason, the processing routines introduce a certain /// 'latency' between the input and output, so that the samples input to -/// SoundTouch may not be immediately available in the output, and neither -/// the amount of outputtable samples may not immediately be in direct +/// SoundTouch may not be immediately available in the output, and neither +/// the amount of outputtable samples may not immediately be in direct /// relationship with the amount of previously input samples. /// /// - The tempo/pitch/rate control parameters can be altered during processing. @@ -30,8 +30,8 @@ /// required. /// /// - This class utilizes classes 'TDStretch' for tempo change (without modifying -/// pitch) and 'RateTransposer' for changing the playback rate (that is, both -/// tempo and pitch in the same ratio) of the sound. The third available control +/// pitch) and 'RateTransposer' for changing the playback rate (that is, both +/// tempo and pitch in the same ratio) of the sound. The third available control /// 'pitch' (change pitch but maintain tempo) is produced by a combination of /// combining the two other controls. /// @@ -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.3.2" /// SoundTouch library version id -#define SOUNDTOUCH_VERSION_ID (10902) +#define SOUNDTOUCH_VERSION_ID (20302) // // Available setting IDs for the 'setSetting' & 'get_setting' functions: @@ -98,48 +91,79 @@ namespace soundtouch /// quality compromising) #define SETTING_USE_QUICKSEEK 2 -/// Time-stretch algorithm single processing sequence length in milliseconds. This determines -/// to how long sequences the original sound is chopped in the time-stretch algorithm. +/// Time-stretch algorithm single processing sequence length in milliseconds. This determines +/// to how long sequences the original sound is chopped in the time-stretch algorithm. /// See "STTypes.h" or README for more information. #define SETTING_SEQUENCE_MS 3 -/// Time-stretch algorithm seeking window length in milliseconds for algorithm that finds the -/// best possible overlapping location. This determines from how wide window the algorithm -/// may look for an optimal joining location when mixing the sound sequences back together. +/// Time-stretch algorithm seeking window length in milliseconds for algorithm that finds the +/// best possible overlapping location. This determines from how wide window the algorithm +/// may look for an optimal joining location when mixing the sound sequences back together. /// See "STTypes.h" or README for more information. #define SETTING_SEEKWINDOW_MS 4 -/// Time-stretch algorithm overlap length in milliseconds. When the chopped sound sequences -/// are mixed back together, to form a continuous sound stream, this parameter defines over -/// how long period the two consecutive sequences are let to overlap each other. +/// Time-stretch algorithm overlap length in milliseconds. When the chopped sound sequences +/// are mixed back together, to form a continuous sound stream, this parameter defines over +/// how long period the two consecutive sequences are let to overlap each other. /// See "STTypes.h" or README for more information. #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. /// -/// Notices: +/// 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 +/// 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: +/// +/// 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 { @@ -169,7 +193,7 @@ private: /// Accumulator for how many samples in total have been read out from the processing so far long samplesOutput; - /// Calculates effective rate & tempo valuescfrom 'virtualRate', 'virtualTempo' and + /// Calculates effective rate & tempo valuescfrom 'virtualRate', 'virtualTempo' and /// 'virtualPitch' parameters. void calcEffectiveRateAndTempo(); @@ -185,7 +209,7 @@ protected : public: SoundTouch(); - virtual ~SoundTouch(); + virtual ~SoundTouch() override; /// Get SoundTouch library version string static const char *getVersionString(); @@ -213,7 +237,7 @@ public: /// represent lower pitches, larger values higher pitch. void setPitch(double newPitch); - /// Sets pitch change in octaves compared to the original pitch + /// Sets pitch change in octaves compared to the original pitch /// (-1.00 .. +1.00) void setPitchOctaves(double newPitch); @@ -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. // @@ -245,33 +287,33 @@ public: uint numSamples ///< Number of samples in buffer. Notice ///< that in case of stereo-sound a single sample ///< contains data for both channels. - ); + ) override; - /// 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 + /// Output samples from beginning of the sample buffer. Copies requested samples to + /// output buffer and removes them from the sample buffer. If there are less than /// 'numsample' samples in the buffer, returns all that available. /// /// \return Number of samples returned. virtual uint receiveSamples(SAMPLETYPE *output, ///< Buffer where to copy output samples. uint maxSamples ///< How many samples to receive at max. - ); + ) override; - /// Adjusts book-keeping so that given number of samples are removed from beginning of the - /// sample buffer without copying them anywhere. + /// Adjusts book-keeping so that given number of samples are removed from beginning of the + /// sample buffer without copying them anywhere. /// /// Used to reduce the number of samples in the buffer when accessing the sample buffer directly /// with 'ptrBegin' function. virtual uint receiveSamples(uint maxSamples ///< Remove this many samples from the beginning of pipe. - ); + ) override; /// Clears all the samples in the object's output and internal processing /// buffers. - virtual void clear(); + virtual void clear() override; /// 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,12 +328,17 @@ 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') /// /// - receiveSamples() : Use this function to receive 'ready' processed samples from SoundTouch. - /// - numSamples() : Get number of 'ready' samples that can be received with + /// - numSamples() : Get number of 'ready' samples that can be received with /// function 'receiveSamples()' /// - isEmpty() : Returns nonzero if there aren't any 'ready' samples. /// - clear() : Clears all samples from ready/processing buffers. diff --git a/Externals/soundtouch/SoundTouch.vcxproj b/Externals/soundtouch/SoundTouch.vcxproj index cd4da6e480..6b978e8aec 100644 --- a/Externals/soundtouch/SoundTouch.vcxproj +++ b/Externals/soundtouch/SoundTouch.vcxproj @@ -53,4 +53,4 @@ - \ No newline at end of file + diff --git a/Externals/soundtouch/TDStretch.cpp b/Externals/soundtouch/TDStretch.cpp index bb473a9f63..157f0e4d6e 100644 --- a/Externals/soundtouch/TDStretch.cpp +++ b/Externals/soundtouch/TDStretch.cpp @@ -1,11 +1,17 @@ -//////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// /// /// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo /// 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 @@ -55,26 +54,6 @@ using namespace soundtouch; #define max(x, y) (((x) > (y)) ? (x) : (y)) - -/***************************************************************************** - * - * Constant definitions - * - *****************************************************************************/ - -// Table for the hierarchical mixing position seeking algorithm -const short _scanOffsets[5][24]={ - { 124, 186, 248, 310, 372, 434, 496, 558, 620, 682, 744, 806, - 868, 930, 992, 1054, 1116, 1178, 1240, 1302, 1364, 1426, 1488, 0}, - {-100, -75, -50, -25, 25, 50, 75, 100, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - { -20, -15, -10, -5, 5, 10, 15, 20, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - { -4, -3, -2, -1, 1, 2, 3, 4, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - { 121, 114, 97, 114, 98, 105, 108, 32, 104, 99, 117, 111, - 116, 100, 110, 117, 111, 115, 0, 0, 0, 0, 0, 0}}; - /***************************************************************************** * * Implementation of the class 'TDStretch' @@ -87,18 +66,13 @@ TDStretch::TDStretch() : FIFOProcessor(&outputBuffer) bQuickSeek = false; channels = 2; - pMidBuffer = NULL; - pMidBufferUnaligned = NULL; + pMidBuffer = nullptr; + pMidBufferUnaligned = nullptr; overlapLength = 0; bAutoSeqSetting = true; bAutoSeekSetting = true; - maxnorm = 0; - maxnormf = 1e8; - - skipFract = 0; - tempo = 1.0f; setParameters(44100, DEFAULT_SEQUENCE_MS, DEFAULT_SEEKWINDOW_MS, DEFAULT_OVERLAP_MS); setTempo(1.0f); @@ -128,8 +102,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) { @@ -164,7 +143,7 @@ void TDStretch::setParameters(int aSampleRate, int aSequenceMS, /// Get routine control parameters, see setParameters() function. -/// Any of the parameters to this function can be NULL, in such case corresponding parameter +/// Any of the parameters to this function can be nullptr, in such case corresponding parameter /// value isn't returned. void TDStretch::getParameters(int *pSampleRate, int *pSequenceMs, int *pSeekWindowMs, int *pOverlapMs) const { @@ -199,7 +178,7 @@ void TDStretch::overlapMono(SAMPLETYPE *pOutput, const SAMPLETYPE *pInput) const m1 = (SAMPLETYPE)0; m2 = (SAMPLETYPE)overlapLength; - for (i = 0; i < overlapLength ; i ++) + for (i = 0; i < overlapLength ; i ++) { pOutput[i] = (pInput[i] * m1 + pMidBuffer[i] * m2 ) / overlapLength; m1 += 1; @@ -219,6 +198,10 @@ void TDStretch::clearInput() { inputBuffer.clear(); clearMidBuffer(); + isBeginning = true; + maxnorm = 0; + maxnormf = 1e8; + skipFract = 0; } @@ -297,21 +280,23 @@ 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 ++) + for (i = 1; i < seekLength; i ++) { double corr; // Calculates correlation value for the mixing position corresponding to 'i' -#ifdef _OPENMP +#if defined(_OPENMP) || defined(ST_SIMD_AVOID_UNALIGNED) // in parallel OpenMP mode, can't use norm accumulator version as parallel executor won't // iterate the loop in sequential order + // in SIMD mode, avoid accumulator version to allow avoiding unaligned positions corr = calcCrossCorr(refPos + channels * i, pMidBuffer, norm); #else // In non-parallel version call "calcCrossCorrAccumulate" that is otherwise same @@ -354,7 +339,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 +358,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 +419,6 @@ int TDStretch::seekBestOverlapPositionQuick(const SAMPLETYPE *refPos) { bestCorr = corr; bestOffs = i; - best = 1; } } @@ -458,7 +440,6 @@ int TDStretch::seekBestOverlapPositionQuick(const SAMPLETYPE *refPos) { bestCorr = corr; bestOffs = i; - best = 2; } } @@ -515,18 +496,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 +567,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 +617,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 +635,62 @@ 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()); + 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); + // 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 * seekLength + 0.5); + + #ifdef ST_SIMD_AVOID_UNALIGNED + // in SIMD mode, round the skip amount to value corresponding to aligned memory address + if (channels == 1) + { + skip &= -4; + } + else if (channels == 2) + { + skip &= -2; + } + #endif + skipFract -= skip; + if (skipFract <= -nominalSkip) + { + skipFract = -nominalSkip; + } + } // ... 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 @@ -732,7 +740,7 @@ void TDStretch::acceptNewOverlapLength(int newOverlapLength) // Operator 'new' is overloaded so that it automatically creates a suitable instance // depending on if we've a MMX/SSE/etc-capable CPU available or not. -void * TDStretch::operator new(size_t s) +void * TDStretch::operator new(size_t) { // Notice! don't use "new TDStretch" directly, use "newInstance" to create a new instance instead! ST_THROW_RT_ERROR("Error in TDStretch::new: Don't use 'new TDStretch' directly, use 'newInstance' member instead!"); @@ -776,7 +784,7 @@ TDStretch * TDStretch::newInstance() ////////////////////////////////////////////////////////////////////////////// // -// Integer arithmetics specific algorithm implementations. +// Integer arithmetic specific algorithm implementations. // ////////////////////////////////////////////////////////////////////////////// @@ -790,7 +798,7 @@ void TDStretch::overlapStereo(short *poutput, const short *input) const short temp; int cnt2; - for (i = 0; i < overlapLength ; i ++) + for (i = 0; i < overlapLength ; i ++) { temp = (short)(overlapLength - i); cnt2 = 2 * i; @@ -802,21 +810,19 @@ void TDStretch::overlapStereo(short *poutput, const short *input) const // Overlaps samples in 'midBuffer' with the samples in 'input'. The 'Multi' // version of the routine. -void TDStretch::overlapMulti(SAMPLETYPE *poutput, const SAMPLETYPE *input) const +void TDStretch::overlapMulti(short *poutput, const short *input) const { - SAMPLETYPE m1=(SAMPLETYPE)0; - SAMPLETYPE m2; - int i=0; + short m1; + int i = 0; - for (m2 = (SAMPLETYPE)overlapLength; m2; m2 --) + for (m1 = 0; m1 < overlapLength; m1 ++) { + short m2 = (short)(overlapLength - m1); for (int c = 0; c < channels; c ++) { poutput[i] = (input[i] * m1 + pMidBuffer[i] * m2) / overlapLength; i++; } - - m1++; } } @@ -861,25 +867,33 @@ double TDStretch::calcCrossCorr(const short *mixingPos, const short *compare, do unsigned long lnorm; int i; + #ifdef ST_SIMD_AVOID_UNALIGNED + // in SIMD mode skip 'mixingPos' positions that aren't aligned to 16-byte boundary + if (((ulongptr)mixingPos) & 15) return -1e50; + #endif + + // hint compiler autovectorization that loop length is divisible by 8 + int ilength = (channels * overlapLength) & -8; + corr = lnorm = 0; - // Same routine for stereo and mono. For stereo, unroll loop for better - // efficiency and gives slightly better resolution against rounding. - // For mono it same routine, just unrolls loop by factor of 4 - for (i = 0; i < channels * overlapLength; i += 4) + // Same routine for stereo and mono + for (i = 0; i < ilength; i += 2) { corr += (mixingPos[i] * compare[i] + - mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBitsNorm; // notice: do intermediate division here to avoid integer overflow - corr += (mixingPos[i + 2] * compare[i + 2] + - mixingPos[i + 3] * compare[i + 3]) >> overlapDividerBitsNorm; + mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBitsNorm; lnorm += (mixingPos[i] * mixingPos[i] + - mixingPos[i + 1] * mixingPos[i + 1]) >> overlapDividerBitsNorm; // notice: do intermediate division here to avoid integer overflow - lnorm += (mixingPos[i + 2] * mixingPos[i + 2] + - mixingPos[i + 3] * mixingPos[i + 3]) >> overlapDividerBitsNorm; + mixingPos[i + 1] * mixingPos[i + 1]) >> overlapDividerBitsNorm; + // do intermediate scalings to avoid integer overflow } 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 @@ -892,9 +906,12 @@ double TDStretch::calcCrossCorr(const short *mixingPos, const short *compare, do double TDStretch::calcCrossCorrAccumulate(const short *mixingPos, const short *compare, double &norm) { long corr; - unsigned long lnorm; + long lnorm; int i; + // hint compiler autovectorization that loop length is divisible by 8 + int ilength = (channels * overlapLength) & -8; + // cancel first normalizer tap from previous round lnorm = 0; for (i = 1; i <= channels; i ++) @@ -903,15 +920,11 @@ double TDStretch::calcCrossCorrAccumulate(const short *mixingPos, const short *c } corr = 0; - // Same routine for stereo and mono. For stereo, unroll loop for better - // efficiency and gives slightly better resolution against rounding. - // For mono it same routine, just unrolls loop by factor of 4 - for (i = 0; i < channels * overlapLength; i += 4) + // Same routine for stereo and mono. + for (i = 0; i < ilength; i += 2) { corr += (mixingPos[i] * compare[i] + - mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBitsNorm; // notice: do intermediate division here to avoid integer overflow - corr += (mixingPos[i + 2] * compare[i + 2] + - mixingPos[i + 3] * compare[i + 3]) >> overlapDividerBitsNorm; + mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBitsNorm; } // update normalizer with last samples of this round @@ -936,7 +949,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 @@ -1012,27 +1025,24 @@ void TDStretch::calculateOverlapLength(int overlapInMsec) /// Calculate cross-correlation double TDStretch::calcCrossCorr(const float *mixingPos, const float *compare, double &anorm) { - double corr; - double norm; + float corr; + float norm; int i; + #ifdef ST_SIMD_AVOID_UNALIGNED + // in SIMD mode skip 'mixingPos' positions that aren't aligned to 16-byte boundary + if (((ulongptr)mixingPos) & 15) return -1e50; + #endif + + // hint compiler autovectorization that loop length is divisible by 8 + int ilength = (channels * overlapLength) & -8; + corr = norm = 0; - // Same routine for stereo and mono. For Stereo, unroll by factor of 2. - // For mono it's same routine yet unrollsd by factor of 4. - for (i = 0; i < channels * overlapLength; i += 4) + // Same routine for stereo and mono + for (i = 0; i < ilength; i ++) { - corr += mixingPos[i] * compare[i] + - mixingPos[i + 1] * compare[i + 1]; - - norm += mixingPos[i] * mixingPos[i] + - mixingPos[i + 1] * mixingPos[i + 1]; - - // unroll the loop for better CPU efficiency: - corr += mixingPos[i + 2] * compare[i + 2] + - mixingPos[i + 3] * compare[i + 3]; - - norm += mixingPos[i + 2] * mixingPos[i + 2] + - mixingPos[i + 3] * mixingPos[i + 3]; + corr += mixingPos[i] * compare[i]; + norm += mixingPos[i] * mixingPos[i]; } anorm = norm; @@ -1043,7 +1053,7 @@ double TDStretch::calcCrossCorr(const float *mixingPos, const float *compare, do /// Update cross-correlation by accumulating "norm" coefficient by previously calculated value double TDStretch::calcCrossCorrAccumulate(const float *mixingPos, const float *compare, double &norm) { - double corr; + float corr; int i; corr = 0; @@ -1054,14 +1064,13 @@ double TDStretch::calcCrossCorrAccumulate(const float *mixingPos, const float *c norm -= mixingPos[-i] * mixingPos[-i]; } - // Same routine for stereo and mono. For Stereo, unroll by factor of 2. - // For mono it's same routine yet unrollsd by factor of 4. - for (i = 0; i < channels * overlapLength; i += 4) + // hint compiler autovectorization that loop length is divisible by 8 + int ilength = (channels * overlapLength) & -8; + + // Same routine for stereo and mono + for (i = 0; i < ilength; i ++) { - corr += mixingPos[i] * compare[i] + - mixingPos[i + 1] * compare[i + 1] + - mixingPos[i + 2] * compare[i + 2] + - mixingPos[i + 3] * compare[i + 3]; + corr += mixingPos[i] * compare[i]; } // update normalizer with last samples of this round diff --git a/Externals/soundtouch/TDStretch.h b/Externals/soundtouch/TDStretch.h index 213304c982..6fddc2379b 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 @@ -172,7 +165,7 @@ protected: public: TDStretch(); - virtual ~TDStretch(); + virtual ~TDStretch() override; /// Operator 'new' is overloaded so that it automatically creates a suitable instance /// depending on if we've a MMX/SSE/etc-capable CPU available or not. @@ -194,7 +187,7 @@ public: void setTempo(double newTempo); /// Returns nonzero if there aren't any samples available for outputting. - virtual void clear(); + virtual void clear() override; /// Clears the input buffer void clearInput(); @@ -224,7 +217,7 @@ public: ); /// Get routine control parameters, see setParameters() function. - /// Any of the parameters to this function can be NULL, in such case corresponding parameter + /// Any of the parameters to this function can be nullptr, in such case corresponding parameter /// value isn't returned. void getParameters(int *pSampleRate, int *pSequenceMs, int *pSeekWindowMs, int *pOverlapMs) const; @@ -234,7 +227,7 @@ public: const SAMPLETYPE *samples, ///< Input sample data uint numSamples ///< Number of samples in 'samples' so that one sample ///< contains both channels if stereo - ); + ) override; /// return nominal input sample requirement for triggering a processing batch int getInputSampleReq() const @@ -247,8 +240,13 @@ public: { return seekWindowLength - overlapLength; } -}; + /// return approximate initial input-output latency + int getLatency() const + { + return sampleReq; + } +}; // Implementation-specific class declarations: @@ -258,10 +256,10 @@ public: class TDStretchMMX : public TDStretch { protected: - double calcCrossCorr(const short *mixingPos, const short *compare, double &norm); - double calcCrossCorrAccumulate(const short *mixingPos, const short *compare, double &norm); - virtual void overlapStereo(short *output, const short *input) const; - virtual void clearCrossCorrState(); + double calcCrossCorr(const short *mixingPos, const short *compare, double &norm) override; + double calcCrossCorrAccumulate(const short *mixingPos, const short *compare, double &norm) override; + virtual void overlapStereo(short *output, const short *input) const override; + virtual void clearCrossCorrState() override; }; #endif /// SOUNDTOUCH_ALLOW_MMX @@ -271,8 +269,8 @@ public: class TDStretchSSE : public TDStretch { protected: - double calcCrossCorr(const float *mixingPos, const float *compare, double &norm); - double calcCrossCorrAccumulate(const float *mixingPos, const float *compare, double &norm); + double calcCrossCorr(const float *mixingPos, const float *compare, double &norm) override; + double calcCrossCorrAccumulate(const float *mixingPos, const float *compare, double &norm) override; }; #endif /// SOUNDTOUCH_ALLOW_SSE diff --git a/Externals/soundtouch/cpu_detect.h b/Externals/soundtouch/cpu_detect.h index 79d5127204..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 @@ -51,8 +44,6 @@ #define SUPPORT_SSE 0x0008 #define SUPPORT_SSE2 0x0010 -using namespace soundtouch; - /// Checks which instruction set extensions are supported by the CPU. /// /// \return A bitmask of supported extensions, see SUPPORT_... defines. 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..0a2949cd35 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 { @@ -297,8 +294,8 @@ void TDStretchMMX::overlapStereo(short *output, const short *input) const FIRFilterMMX::FIRFilterMMX() : FIRFilter() { - filterCoeffsAlign = NULL; - filterCoeffsUnalign = NULL; + filterCoeffsAlign = nullptr; + filterCoeffsUnalign = nullptr; } @@ -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/sse_optimized.cpp b/Externals/soundtouch/sse_optimized.cpp index 490d0d2080..ebcc441db1 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 @@ -87,7 +80,7 @@ double TDStretchSSE::calcCrossCorr(const float *pV1, const float *pV2, double &a // Compile-time define SOUNDTOUCH_ALLOW_NONEXACT_SIMD_OPTIMIZATION is provided // for choosing if this little cheating is allowed. -#ifdef SOUNDTOUCH_ALLOW_NONEXACT_SIMD_OPTIMIZATION +#ifdef ST_SIMD_AVOID_UNALIGNED // Little cheating allowed, return valid correlation only for // aligned locations, meaning every second round for stereo sound. @@ -202,16 +195,16 @@ double TDStretchSSE::calcCrossCorrAccumulate(const float *pV1, const float *pV2, FIRFilterSSE::FIRFilterSSE() : FIRFilter() { - filterCoeffsAlign = NULL; - filterCoeffsUnalign = NULL; + filterCoeffsAlign = nullptr; + filterCoeffsUnalign = nullptr; } FIRFilterSSE::~FIRFilterSSE() { delete[] filterCoeffsUnalign; - filterCoeffsAlign = NULL; - filterCoeffsUnalign = NULL; + filterCoeffsAlign = nullptr; + filterCoeffsUnalign = nullptr; } @@ -252,10 +245,10 @@ uint FIRFilterSSE::evaluateFilterStereo(float *dest, const float *source, uint n if (count < 2) return 0; - assert(source != NULL); - assert(dest != NULL); + assert(source != nullptr); + assert(dest != nullptr); assert((length % 8) == 0); - assert(filterCoeffsAlign != NULL); + assert(filterCoeffsAlign != nullptr); assert(((ulongptr)filterCoeffsAlign) % 16 == 0); // filter is evaluated for two stereo samples with each iteration, thus use of 'j += 2'