summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Schürmann <daschuer@mixxx.org>2021-09-30 11:02:57 +0200
committerGitHub <noreply@github.com>2021-09-30 11:02:57 +0200
commitc8c8eb30ab949eb606888f0b1864ada1959c67db (patch)
treed92177ba72c1ed26c7e1c57a8c1a2c9ab0ad9b99
parent19e952b6151b53e54aee3c8177b5517d66321e8a (diff)
parente06ddba339f7354868d243802e560048d7631fb5 (diff)
Merge pull request #4201 from Be-ing/devendor_soundtouch
Devendor SoundTouch
-rw-r--r--CMakeLists.txt28
-rw-r--r--lib/soundtouch/AAFilter.cpp222
-rw-r--r--lib/soundtouch/AAFilter.h93
-rw-r--r--lib/soundtouch/BPMDetect.cpp574
-rw-r--r--lib/soundtouch/BPMDetect.h205
-rw-r--r--lib/soundtouch/COPYING.TXT458
-rw-r--r--lib/soundtouch/FIFOSampleBuffer.cpp267
-rw-r--r--lib/soundtouch/FIFOSampleBuffer.h177
-rw-r--r--lib/soundtouch/FIFOSamplePipe.h230
-rw-r--r--lib/soundtouch/FIRFilter.cpp324
-rw-r--r--lib/soundtouch/FIRFilter.h139
-rw-r--r--lib/soundtouch/InterpolateCubic.cpp196
-rw-r--r--lib/soundtouch/InterpolateCubic.h63
-rw-r--r--lib/soundtouch/InterpolateLinear.cpp296
-rw-r--r--lib/soundtouch/InterpolateLinear.h88
-rw-r--r--lib/soundtouch/InterpolateShannon.cpp181
-rw-r--r--lib/soundtouch/InterpolateShannon.h68
-rw-r--r--lib/soundtouch/PeakFinder.cpp277
-rw-r--r--lib/soundtouch/PeakFinder.h90
-rw-r--r--lib/soundtouch/README.html923
-rw-r--r--lib/soundtouch/RateTransposer.cpp307
-rw-r--r--lib/soundtouch/RateTransposer.h163
-rw-r--r--lib/soundtouch/STTypes.h183
-rw-r--r--lib/soundtouch/SoundTouch.cpp538
-rw-r--r--lib/soundtouch/SoundTouch.h348
-rw-r--r--lib/soundtouch/TDStretch.cpp1111
-rw-r--r--lib/soundtouch/TDStretch.h279
-rw-r--r--lib/soundtouch/cpu_detect.h55
-rw-r--r--lib/soundtouch/cpu_detect_x86.cpp130
-rw-r--r--lib/soundtouch/mmx_optimized.cpp396
-rw-r--r--lib/soundtouch/readme.md52
-rw-r--r--lib/soundtouch/soundtouch_config.h3
-rw-r--r--lib/soundtouch/sse_optimized.cpp365
33 files changed, 2 insertions, 8827 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0b9de0c53c..cca36a1045 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2342,32 +2342,8 @@ if(SndFile_SUPPORTS_SET_COMPRESSION_LEVEL)
endif()
# SoundTouch
-find_package(SoundTouch)
-default_option(SoundTouch_STATIC "Link libSoundTouch statically" "NOT SoundTouch_FOUND OR SoundTouch_VERSION VERSION_LESS 2.1.2")
-if(SoundTouch_STATIC)
- message(STATUS "Preparing internal libSoundTouch")
- add_library(SoundTouch STATIC EXCLUDE_FROM_ALL
- lib/soundtouch/AAFilter.cpp
- lib/soundtouch/BPMDetect.cpp
- lib/soundtouch/FIFOSampleBuffer.cpp
- lib/soundtouch/FIRFilter.cpp
- lib/soundtouch/InterpolateCubic.cpp
- lib/soundtouch/InterpolateLinear.cpp
- lib/soundtouch/InterpolateShannon.cpp
- lib/soundtouch/PeakFinder.cpp
- lib/soundtouch/RateTransposer.cpp
- lib/soundtouch/SoundTouch.cpp
- lib/soundtouch/TDStretch.cpp
- lib/soundtouch/cpu_detect_x86.cpp
- lib/soundtouch/mmx_optimized.cpp
- lib/soundtouch/sse_optimized.cpp
- )
- target_include_directories(SoundTouch SYSTEM PUBLIC lib)
- target_link_libraries(mixxx-lib PRIVATE SoundTouch)
-else()
- message(STATUS "Linking libSoundTouch dynamically")
- target_link_libraries(mixxx-lib PRIVATE SoundTouch::SoundTouch)
-endif()
+find_package(SoundTouch 2.1.2 REQUIRED)
+target_link_libraries(mixxx-lib PRIVATE SoundTouch::SoundTouch)
# TagLib
find_package(TagLib 1.11 REQUIRED)
diff --git a/lib/soundtouch/AAFilter.cpp b/lib/soundtouch/AAFilter.cpp
deleted file mode 100644
index 76a3da65d4..0000000000
--- a/lib/soundtouch/AAFilter.cpp
+++ /dev/null
@@ -1,222 +0,0 @@
-////////////////////////////////////////////////////////////////////////////////
-///
-/// FIR low-pass (anti-alias) filter with filter coefficient design routine and
-/// MMX optimization.
-///
-/// Anti-alias filter is used to prevent folding of high frequencies when
-/// transposing the sample rate with interpolation.
-///
-/// 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 <memory.h>
-#include <assert.h>
-#include <math.h>
-#include <stdlib.h>
-#include "AAFilter.h"
-#include "FIRFilter.h"
-
-using namespace soundtouch;
-
-#define PI 3.14159265358979323846
-#define TWOPI (2 * PI)
-
-// define this to save AA filter coefficients to a file
-// #define _DEBUG_SAVE_AAFILTER_COEFFICIENTS 1
-
-#ifdef _DEBUG_SAVE_AAFILTER_COEFFICIENTS
- #include <stdio.h>
-
- static void _DEBUG_SAVE_AAFIR_COEFFS(SAMPLETYPE *coeffs, int len)
- {
- FILE *fptr = fopen("aa_filter_coeffs.txt", "wt");
- if (fptr == NULL) return;
-
- for (int i = 0; i < len; i ++)
- {
- double temp = coeffs[i];
- fprintf(fptr, "%lf\n", temp);
- }
- fclose(fptr);
- }
-
-#else
- #define _DEBUG_SAVE_AAFIR_COEFFS(x, y)
-#endif
-
-/*****************************************************************************
- *
- * Implementation of the class 'AAFilter'
- *
- *****************************************************************************/
-
-AAFilter::AAFilter(uint len)
-{
- pFIR = FIRFilter::newInstance();
- cutoffFreq = 0.5;
- setLength(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.
-void AAFilter::setCutoffFreq(double newCutoffFreq)
-{
- cutoffFreq = newCutoffFreq;
- calculateCoeffs();
-}
-
-
-// Sets number of FIR filter taps
-void AAFilter::setLength(uint newLength)
-{
- length = newLength;
- calculateCoeffs();
-}
-
-
-// Calculates coefficients for a low-pass FIR filter using Hamming window
-void AAFilter::calculateCoeffs()
-{
- uint i;
- double cntTemp, temp, tempCoeff,h, w;
- double wc;
- double scaleCoeff, sum;
- double *work;
- SAMPLETYPE *coeffs;
-
- assert(length >= 2);
- assert(length % 4 == 0);
- assert(cutoffFreq >= 0);
- assert(cutoffFreq <= 0.5);
-
- work = new double[length];
- coeffs = new SAMPLETYPE[length];
-
- wc = 2.0 * PI * cutoffFreq;
- tempCoeff = TWOPI / (double)length;
-
- sum = 0;
- for (i = 0; i < length; i ++)
- {
- cntTemp = (double)i - (double)(length / 2);
-
- temp = cntTemp * wc;
- if (temp != 0)
- {
- h = sin(temp) / temp; // sinc function
- }
- else
- {
- h = 1.0;
- }
- w = 0.54 + 0.46 * cos(tempCoeff * cntTemp); // hamming window
-
- temp = w * h;
- work[i] = temp;
-
- // calc net sum of coefficients
- sum += temp;
- }
-
- // ensure the sum of coefficients is larger than zero
- assert(sum > 0);
-
- // ensure we've really designed a lowpass filter...
- assert(work[length/2] > 0);
- assert(work[length/2 + 1] > -1e-6);
- assert(work[length/2 - 1] > -1e-6);
-
- // Calculate a scaling coefficient in such a way that the result can be
- // divided by 16384
- scaleCoeff = 16384.0f / sum;
-
- for (i = 0; i < length; i ++)
- {
- temp = work[i] * scaleCoeff;
- // scale & round to nearest integer
- temp += (temp >= 0) ? 0.5 : -0.5;
- // ensure no overfloods
- assert(temp >= -32768 && temp <= 32767);
- coeffs[i] = (SAMPLETYPE)temp;
- }
-
- // Set coefficients. Use divide factor 14 => divide result by 2^14 = 16384
- pFIR->setCoefficients(coeffs, length, 14);
-
- _DEBUG_SAVE_AAFIR_COEFFS(coeffs, length);
-
- delete[] work;
- delete[] coeffs;
-}
-
-
-// Applies the filter to the given sequence of samples.
-// Note : The amount of outputted samples is by value of 'filter length'
-// smaller than the amount of input samples.
-uint AAFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels) const
-{
- return pFIR->evaluate(dest, src, numSamples, numChannels);
-}
-
-
-/// Applies the filter to the given src & dest pipes, so that processed amount of
-/// samples get removed from src, and produced amount added to dest
-/// Note : The amount of outputted samples is by value of 'filter length'
-/// smaller than the amount of input samples.
-uint AAFilter::evaluate(FIFOSampleBuffer &dest, FIFOSampleBuffer &src) const
-{
- SAMPLETYPE *pdest;
- const SAMPLETYPE *psrc;
- uint numSrcSamples;
- uint result;
- int numChannels = src.getChannels();
-
- assert(numChannels == dest.getChannels());
-
- numSrcSamples = src.numSamples();
- psrc = src.ptrBegin();
- pdest = dest.ptrEnd(numSrcSamples);
- result = pFIR->evaluate(pdest, psrc, numSrcSamples, numChannels);
- src.receiveSamples(result);
- dest.putSamples(result);
-
- return result;
-}
-
-
-uint AAFilter::getLength() const
-{
- return pFIR->getLength();
-}
diff --git a/lib/soundtouch/AAFilter.h b/lib/soundtouch/AAFilter.h
deleted file mode 100644
index 8e5697f796..0000000000
--- a/lib/soundtouch/AAFilter.h
+++ /dev/null
@@ -1,93 +0,0 @@
-////////////////////////////////////////////////////////////////////////////////
-///
-/// 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.
-///
-/// Anti-alias filter is used to prevent folding of high frequencies when
-/// transposing the sample rate with interpolation.
-///
-/// 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 AAFilter_H
-#define AAFilter_H
-
-#include "STTypes.h"
-#include "FIFOSampleBuffer.h"
-
-namespace soundtouch
-{
-
-class AAFilter
-{
-protected:
- class FIRFilter *pFIR;
-
- /// Low-pass filter cut-off frequency, negative = invalid
- double cutoffFreq;
-
- /// num of filter taps
- uint length;
-
- /// Calculate the FIR coefficients realizing the given cutoff-frequency
- void calculateCoeffs();
-public:
- AAFilter(uint length);
-
- ~AAFilter();
-
- /// Sets new anti-alias filter cut-off edge frequency, scaled to sampling
- /// frequency (nyquist frequency = 0.5). The filter will cut off the
- /// frequencies than that.
- void setCutoffFreq(double newCutoffFreq);
-
- /// Sets number of FIR filter taps, i.e. ~filter complexity
- void setLength(uint newLength);
-
- uint getLength() const;
-
- /// Applies the filter to the given sequence of samples.
- /// Note : The amount of outputted samples is by value of 'filter length'
- /// smaller than the amount of input samples.
- uint evaluate(SAMPLETYPE *dest,
- const SAMPLETYPE *src,
- uint numSamples,
- uint numChannels) const;
-
- /// Applies the filter to the given src & dest pipes, so that processed amount of
- /// samples get removed from src, and produced amount added to dest
- /// Note : The amount of outputted samples is by value of 'filter length'
- /// smaller than the amount of input samples.
- uint evaluate(FIFOSampleBuffer &dest,
- FIFOSampleBuffer &src) const;
-
-};
-
-}
-
-#endif
diff --git a/lib/soundtouch/BPMDetect.cpp b/lib/soundtouch/BPMDetect.cpp
deleted file mode 100644
index 3ecda49f61..0000000000
--- a/lib/soundtouch/BPMDetect.cpp
+++ /dev/null
@@ -1,574 +0,0 @@
-////////////////////////////////////////////////////////////////////////////////
-///
-/// Beats-per-minute (BPM) detection routine.
-///
-/// The beat detection algorithm works as follows:
-/// - Use function 'inputSamples' to input a chunks of samples to the class for
-/// analysis. It's a good idea to enter a large sound file or stream in smallish
-/// chunks of around few kilosamples in order not to extinguish too much RAM memory.
-/// - Inputted sound data is decimated to approx 500 Hz to reduce calculation burden,
-/// which is basically ok as low (bass) frequencies mostly determine the beat rate.
-/// Simple averaging is used for anti-alias filtering because the resulting signal
-/// quality isn't of that high importance.
-/// - Decimated sound data is enveloped, i.e. the amplitude shape is detected by
-/// taking absolute value that's smoothed by sliding average. Signal levels that
-/// are below a couple of times the general RMS amplitude level are cut away to
-/// leave only notable peaks there.
-/// - Repeating sound patterns (e.g. beats) are detected by calculating short-term
-/// autocorrelation function of the enveloped signal.
-/// - After whole sound data file has been analyzed as above, the bpm level is
-/// detected by function 'getBpm' that finds the highest peak of the autocorrelation
-/// function, calculates it's precise location and converts this reading to bpm's.
-///
-/// 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
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#define _USE_MATH_DEFINES
-
-#include <math.h>
-#include <assert.h>
-#include <string.h>
-#include <stdio.h>
-#include <cfloat>
-#include "FIFOSampleBuffer.h"
-#include "PeakFinder.h"
-#include "BPMDetect.h"
-
-using namespace soundtouch;
-
-// algorithm input sample block size
-static const int INPUT_BLOCK_SIZE = 2048;
-
-// decimated sample block size
-static const int DECIMATED_BLOCK_SIZE = 256;
-
-/// Target sample rate after decimation
-static const int TARGET_SRATE = 1000;
-
-/// XCorr update sequence size, update in about 200msec chunks
-static const int XCORR_UPDATE_SEQUENCE = (int)(TARGET_SRATE / 5);
-
-/// 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
-
-#ifdef _CREATE_BPM_DEBUG_FILE
-
- static void _SaveDebugData(const char *name, const float *data, int minpos, int maxpos, double coeff)
- {
- FILE *fptr = fopen(name, "wt");
- int i;
-
- if (fptr)
- {
- 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]);
- }
- fclose(fptr);
- }
- }
-
- void _SaveDebugBeatPos(const char *name, const std::vector<BEAT> &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(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) :
- beat_lpf(_LPF_coeffs)
-{
- beats.reserve(250); // initial reservation to prevent frequent reallocation
-
- this->sampleRate = aSampleRate;
- this->channels = numChannels;
-
- decimateSum = 0;
- decimateCount = 0;
-
- // choose decimation factor so that result is approx. 1000 Hz
- 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_RANGE);
-
- assert(windowLen > windowStart);
-
- // allocate new working objects
- 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.
-///
-/// Decimation is used to remove the unnecessary frequencies and thus to reduce
-/// the amount of data needed to be processed as calculating autocorrelation
-/// function is a very-very heavy operation.
-///
-/// Anti-alias filtering is done simply by averaging the samples. This is really a
-/// poor-man's anti-alias filtering, but it's not so critical in this kind of application
-/// (it'd also be difficult to design a high-quality filter with steep cut-off at very
-/// narrow band)
-int BPMDetect::decimate(SAMPLETYPE *dest, const SAMPLETYPE *src, int numsamples)
-{
- int count, outcount;
- LONG_SAMPLETYPE out;
-
- assert(channels > 0);
- assert(decimateBy > 0);
- outcount = 0;
- for (count = 0; count < numsamples; count ++)
- {
- int j;
-
- // convert to mono and accumulate
- for (j = 0; j < channels; j ++)
- {
- decimateSum += src[j];
- }
- src += j;
-
- decimateCount ++;
- if (decimateCount >= decimateBy)
- {
- // Store every Nth sample only
- out = (LONG_SAMPLETYPE)(decimateSum / (decimateBy * channels));
- decimateSum = 0;
- decimateCount = 0;
-#ifdef SOUNDTOUCH_INTEGER_SAMPLES
- // check ranges for sure (shouldn't actually be necessary)
- if (out > 32767)
- {
- out = 32767;
- }
- else if (out < -32768)
- {
- out = -32768;
- }
-#endif // SOUNDTOUCH_INTEGER_SAMPLES
- dest[outcount] = (SAMPLETYPE)out;
- outcount ++;
- }
- }
- return outcount;
-}
-
-
-// Calculates autocorrelation function of the sample history buffer
-void BPMDetect::updateXCorr(int process_samples)
-{
- int offs;
- 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 ++)
- {
- double sum;
- int i;
-
- sum = 0;
- for (i = 0; i < process_samples; i ++)
- {
- sum += tmp[i] * pBuffer[i + offs]; // scaling the sub-result shouldn't be necessary
- }
- xcorr[offs] *= xcorr_decay; // decay 'xcorr' here with suitable time constant.
-
- xcorr[offs] += (float)fabs(sum);
- }
-}
-
-
-// Detect individual beat positions
-void BPMDetect::updateBeatPos(int process_samples)
-{
- SAMPLETYPE *pBuffer;
-
- assert(buffer->numSamples() >= (uint)(process_samples + windowLen));
-
- pBuffer = buffer->ptrBegin();
- assert(process_samples == XCORR_UPDATE_SEQUENCE / 2);
-
- // static double thr = 0.0003;
- double posScale = (double)this->decimateBy / (double)this->sampleRate;
- int resetDur = (int)(0.12 / posScale + 0.5);
- double corrScale = 1.0 / (double)(windowLen - windowStart);
-
- // prescale pbuffer
- float tmp[XCORR_UPDATE_SEQUENCE / 2];
- for (int i = 0; i < process_samples; i++)
- {
- tmp[i] = hamw2[i] * hamw2[i] * pBuffer[i];
- }
-
- #pragma omp parallel for
- for (int offs = windowStart; offs < windowLen; offs++)
- {
- double sum = 0;
- for (int i = 0; i < process_samples; i++)
- {
- 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++)
- {
- LONG_SAMPLETYPE max = 0;
-
- float sum = beatcorr_ringbuff[beatcorr_ringbuffpos];
- sum -= beat_lpf.update(sum);
-
- if (sum > peakVal)
- {
- // found new local largest value
- peakVal = sum;
- peakPos = pos;
- }
- if (pos > peakPos + resetDur)
- {
- // largest value not updated for 200msec => accept as beat
- peakPos += skipstep;
- if (peakVal > 0)
- {
- // add detected beat to end of "beats" vector
- BEAT temp = { (float)(peakPos * posScale), (float)(peakVal * scale) };
- beats.push_back(temp);
- }
-
- peakVal = 0;
- peakPos = pos;
- }
-
- beatcorr_ringbuff[beatcorr_ringbuffpos] = 0;
- pos++;
- beatcorr_ringbuffpos = (beatcorr_ringbuffpos + 1) % windowLen;
- }
-}
-
-
-#define max(x,y) ((x) > (y) ? (x) : (y))
-
-void BPMDetect::inputSamples(const SAMPLETYPE *samples, int numSamples)
-{
- SAMPLETYPE decimated[DECIMATED_BLOCK_SIZE];
-
- // iterate so that max INPUT_BLOCK_SAMPLES processed per iteration
- while (numSamples > 0)
- {
- int block;
- int decSamples;
-
- 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;
-
- buffer->putSamples(decimated, decSamples);
- }
-
- // when the buffer has enough samples for processing...
- int req = max(windowLen + XCORR_UPDATE_SEQUENCE, 2 * XCORR_UPDATE_SEQUENCE);
- while ((int)buffer->numSamples() >= req)
- {
- // ... 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()
-{