diff options
| author | JMC47 <JMC4789@gmail.com> | 2025-03-14 03:57:09 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-03-14 03:57:09 -0400 |
| commit | 4d3f247cc37cec8ab7bf42e2a158f045f8559bb6 (patch) | |
| tree | 7395da269bfc52f61e412c62c6009f30a5d414dd | |
| parent | e82f03b825ed4ec35539d93512877cdd2a02294b (diff) | |
| parent | f09ba10daae4c44fc9404aa2cc48076f6edec9a4 (diff) | |
Merge pull request #13352 from Sam-Belliveau/granular_synthesis
AudioCommon: Granule Synthesis Audio System
51 files changed, 384 insertions, 7925 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 40dd391a19..f2fcabd64f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -696,11 +696,6 @@ if (APPLE OR WIN32) include_directories(Externals/ed25519) endif() -# Using static soundtouch from Externals -# Unable to use system soundtouch library: We require shorts, not floats. -add_subdirectory(Externals/soundtouch) -include_directories(Externals/soundtouch) - if(ENABLE_CUBEB) dolphin_find_optional_system_library(CUBEB Externals/cubeb) add_definitions(-DHAVE_CUBEB) diff --git a/Externals/licenses.md b/Externals/licenses.md index 088aaffc13..5a62c93c3b 100644 --- a/Externals/licenses.md +++ b/Externals/licenses.md @@ -68,8 +68,6 @@ Dolphin includes or links code of the following third-party software projects: [zlib license](http://hg.libsdl.org/SDL/file/tip/COPYING.txt) - [SFML](http://www.sfml-dev.org/): [zlib license](http://www.sfml-dev.org/license.php) -- [SoundTouch](http://www.surina.net/soundtouch/): - [LGPLv2+](http://www.surina.net/soundtouch/license.html) - [TAP-Windows](https://openvpn.net/): header only - [Windows Implementation Libraries](https://github.com/microsoft/wil): diff --git a/Externals/soundtouch/AAFilter.cpp b/Externals/soundtouch/AAFilter.cpp deleted file mode 100644 index 89c54cc265..0000000000 --- a/Externals/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 == nullptr) 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/Externals/soundtouch/AAFilter.h b/Externals/soundtouch/AAFilter.h deleted file mode 100644 index 8e5697f796..0000000000 --- a/Externals/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/Externals/soundtouch/BPMDetect.cpp b/Externals/soundtouch/BPMDetect.cpp deleted file mode 100644 index e09815f595..0000000000 --- a/Externals/soundtouch/BPMDetect.cpp +++ /dev/null @@ -1,570 +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
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#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;
-
-#define PI 3.14159265358979323846
-#define TWOPI (2 * 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 ++)
- {
- float 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);
-
- // 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++)
- {
- float 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++)
- {
- 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()
-{
- int i;
-
- // 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;
- }
-}
-
-
-// Calculate N-point moving average for "source" values
-void MAFilter(float *dest, const float *source, int start, int end, int N)
-{
- for (int i = start; i < end; i++)
- {
- int i1 = i - N / 2;
- int i2 = i + N / 2 + 1;
- if (i1 < start) i1 = start;
- if (i2 > end) i2 = end;
-
- double sum = 0;
- for (int j = i1; j < i2; j ++)
- {
- sum += source[j];
- }
- dest[i] = (float)(sum / (i2 - i1));
- }
-}
-
-
-float BPMDetect::getBpm()
-{
- double peakPos;
- double coeff;
- PeakFinder peakFinder;
-
- // remove bias from xcorr data
- removeBias();
-
- coeff = 60.0 * ((double)sampleRate / (double)decimateBy);
-
- // save bpm debug 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(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
- 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 deleted file mode 100644 index 763266f4ac..0000000000 --- a/Externals/soundtouch/BPMDetect.h +++ /dev/null @@ -1,205 +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.
-/// - Input 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
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#ifndef _BPMDetect_H_
-#define _BPMDetect_H_
-
-#include <vector>
-#include "STTypes.h"
-#include "FIFOSampleBuffer.h"
-
-namespace soundtouch
-{
-
- /// Minimum allowed BPM rate. Used to restrict accepted result above a reasonable limit.
- #define MIN_BPM 45
-
- /// 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 IIR2_filter
- {
- double coeffs[5];
- double prev[5];
-
- public:
- IIR2_filter(const double *lpf_coeffs);
- float update(float x);
- };
-
-
- /// Class for calculating BPM rate for audio data.
- class BPMDetect
- {
- protected:
- /// Auto-correlation accumulator bins.
- float *xcorr;
-
- /// Sample average counter.
- int decimateCount;
-
- /// Sample average accumulator for FIFO-like decimation.
- soundtouch::LONG_SAMPLETYPE decimateSum;
-
- /// Decimate sound by this coefficient to reach approx. 500 Hz.
- int decimateBy;
-
- /// Auto-correlation window length
- int windowLen;
-
- /// Number of channels (1 = mono, 2 = stereo)
- int channels;
-
- /// sample rate
- int sampleRate;
-
- /// Beginning of auto-correlation window: Autocorrelation isn't being updated for
- /// the first these many correlation bins.
- int windowStart;
-
- /// window functions for data preconditioning
- float *hamw;
- float *hamw2;
-
- // beat detection variables
- int pos;
- int peakPos;
- int beatcorr_ringbuffpos;
- int init_scaler;
- float peakVal;
- float *beatcorr_ringbuff;
-
- /// FIFO-buffer for decimated processing samples.
- soundtouch::FIFOSampleBuffer *buffer;
-
- /// Collection of detected beat positions
- //BeatCollection beats;
- std::vector<BEAT> 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 deleted file mode 100644 index 6372095ba8..0000000000 --- a/Externals/soundtouch/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -# OSX meeds to know -check_and_add_flag(CXX11 -std=c++11) - -set(SRCS - AAFilter.cpp - BPMDetect.cpp - cpu_detect_x86.cpp - FIFOSampleBuffer.cpp - FIRFilter.cpp - InterpolateCubic.cpp - InterpolateLinear.cpp - InterpolateShannon.cpp - mmx_optimized.cpp - PeakFinder.cpp - RateTransposer.cpp - SoundTouch.cpp - sse_optimized.cpp - TDStretch.cpp - ) - -add_library(SoundTouch STATIC ${SRCS}) -dolphin_disable_warnings(SoundTouch) diff --git a/Externals/soundtouch/FIFOSampleBuffer.cpp b/Externals/soundtouch/FIFOSampleBuffer.cpp deleted file mode 100644 index 420e0d7998..0000000000 --- a/Externals/soundtouch/FIFOSampleBuffer.cpp +++ /dev/null @@ -1,275 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////
-///
-/// A buffer class for temporarily storaging sound samples, operates as a
-/// first-in-first-out pipe.
-///
-/// Samples are added to the end of the sample buffer with the 'putSamples'
-/// function, and are received from the beginning of the buffer by calling
-/// the 'receiveSamples' function. The class automatically removes the
-/// outputted samples from the buffer, as well as grows the buffer size
-/// whenever necessary.
-///
-/// 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 <stdlib.h>
-#include <memory.h>
-#include <string.h>
-#include <assert.h>
-
-#include "FIFOSampleBuffer.h"
-
-using namespace soundtouch;
-
-// Constructor
-FIFOSampleBuffer::FIFOSampleBuffer(int numChannels)
-{
- assert(numChannels > 0);
- sizeInBytes = 0; // reasonable initial value
- buffer = nullptr;
- bufferUnaligned = nullptr;
- samplesInBuffer = 0;
- bufferPos = 0;
- channels = (uint)numChannels;
- ensureCapacity(32); // allocate initial capacity
-}
-
-
-// destructor
-FIFOSampleBuffer::~FIFOSampleBuffer()
-{
- delete[] bufferUnaligned;
- bufferUnaligned = nullptr;
- buffer = nullptr;
-}
-
-
-// Sets number of channels, 1 = mono, 2 = stereo
-void FIFOSampleBuffer::setChannels(int numChannels)
-{
- uint usedBytes;
-
- if (!verifyNumberOfChannels(numChannels)) return;
-
- usedBytes = channels * samplesInBuffer;
- channels = (uint)numChannels;
- samplesInBuffer = usedBytes / channels;
-}
-
-
-// if output location pointer 'bufferPos' isn't zero, 'rewinds' the buffer and
-// zeroes this pointer by copying samples from the 'bufferPos' pointer
-// location on to the beginning of the buffer.
-void FIFOSampleBuffer::rewind()
-{
- if (buffer && bufferPos)
- {
- memmove(buffer, ptrBegin(), sizeof(SAMPLETYPE) * channels * samplesInBuffer);
- bufferPos = 0;
- }
-}
-
-
-// Adds 'numSamples' pcs of samples from the 'samples' memory position to
-// the sample buffer.
-void FIFOSampleBuffer::putSamples(const SAMPLETYPE *samples, uint nSamples)
-{
- memcpy(ptrEnd(nSamples), samples, sizeof(SAMPLETYPE) * nSamples * channels);
- samplesInBuffer += nSamples;
-}
-
-
-// Increases the number of samples in the buffer without copying any actual
-// samples.
-//
-// This function is used to update the number of samples in the sample buffer
-// when accessing the buffer directly with 'ptrEnd' function. Please be
-// careful though!
-void FIFOSampleBuffer::putSamples(uint nSamples)
-{
- uint req;
-
- req = samplesInBuffer + nSamples;
- ensureCapacity(req);
- samplesInBuffer += nSamples;
-}
-
-
-// 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
-// inserting new samples into the sample buffer directly. Please be careful!
-//
-// Parameter 'slackCapacity' tells the function how much free capacity (in
-// terms of samples) there _at least_ should be, in order to the caller to
-// 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
-// to increase the sample count afterwards, by calling the
-// 'putSamples(numSamples)' function.
-SAMPLETYPE *FIFOSampleBuffer::ptrEnd(uint slackCapacity)
-{
- ensureCapacity(samplesInBuffer + slackCapacity);
- return buffer + samplesInBuffer * channels;
-}
-
-
-// Returns a pointer to the beginning of the currently non-outputted samples.
-// This function is provided for accessing the output samples directly.
-// Please be careful!
-//
-// When using this function to output samples, also remember to 'remove' the
-// outputted samples from the buffer by calling the
-// 'receiveSamples(numSamples)' function
-SAMPLETYPE *FIFOSampleBuffer::ptrBegin()
-{
- assert(buffer);
- return buffer + bufferPos * channels;
-}
-
-
-// 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.
-void FIFOSampleBuffer::ensureCapacity(uint capacityRequirement)
-{
- SAMPLETYPE *tempUnaligned, *temp;
-
- if (capacityRequirement > getCapacity())
- {
- // enlarge the buffer in 4kbyte steps (round up to next 4k boundary)
- sizeInBytes = (capacityRequirement * channels * sizeof(SAMPLETYPE) + 4095) & (uint)-4096;
- assert(sizeInBytes % 2 == 0);
- tempUnaligned = new SAMPLETYPE[sizeInBytes / sizeof(SAMPLETYPE) + 16 / sizeof(SAMPLETYPE)];
- if (tempUnaligned == nullptr)
- {
- ST_THROW_RT_ERROR("Couldn't allocate memory!\n");
- }
- // Align the buffer to begin at 16byte cache line boundary for optimal performance
- temp = (SAMPLETYPE *)SOUNDTOUCH_ALIGN_POINTER_16(tempUnaligned);
- if (samplesInBuffer)
- {
- memcpy(temp, ptrBegin(), samplesInBuffer * channels * sizeof(SAMPLETYPE));
- }
- delete[] bufferUnaligned;
- buffer = temp;
- bufferUnaligned = tempUnaligned;
- bufferPos = 0;
- }
- else
- {
- // simply rewind the buffer (if necessary)
- rewind();
- }
-}
-
-
-// Returns the current buffer capacity in terms of samples
-uint FIFOSampleBuffer::getCapacity() const
-{
- return sizeInBytes / (channels * sizeof(SAMPLETYPE));
-}
-
-
-// Returns the number of samples currently in the buffer
-uint FIFOSampleBuffer::numSamples() const
-{
- return samplesInBuffer;
-}
-
-
-// Output samples from beginning of the sample buffer. Copies demanded number
-// of samples to output and removes them from the sample buffer. If there
-// are less than 'numsample' samples in the buffer, returns all available.
-//
-// Returns number of samples copied.
-uint FIFOSampleBuffer::receiveSamples(SAMPLETYPE *output, uint maxSamples)
-{
- uint num;
-
- num = (maxSamples > samplesInBuffer) ? samplesInBuffer : maxSamples;
-
- memcpy(output, ptrBegin(), channels * sizeof(SAMPLETYPE) * num);
- return receiveSamples(num);
-}
-
-
-// Removes samples from the beginning of the sample buffer without copying them
-// anywhere. Used to reduce the number of samples in the buffer, when accessing
-// the sample buffer with the 'ptrBegin' function.
-uint FIFOSampleBuffer::receiveSamples(uint maxSamples)
-{
- if (maxSamples >= samplesInBuffer)
- {
- uint temp;
-
- temp = samplesInBuffer;
- samplesInBuffer = 0;
- return temp;
- }
-
- samplesInBuffer -= maxSamples;
- bufferPos += maxSamples;
-
- return maxSamples;
-}
-
-
-// Returns nonzero if the sample buffer is empty
-int FIFOSampleBuffer::isEmpty() const
-{
- return (samplesInBuffer == 0) ? 1 : 0;
-}
-
-
-// Clears the sample buffer
-void FIFOSampleBuffer::clear()
-{
- samplesInBuffer = 0;
- bufferPos = 0;
-}
-
-
-/// allow trimming (downwards) amount of samples in pipeline.
-/// Returns adjusted amount of samples
-uint FIFOSampleBuffer::adjustAmountOfSamples(uint numSamples)
-{
- if (numSamples < samplesInBuffer)
- {
- samplesInBuffer = 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 deleted file mode 100644 index 6c5785a6e2..0000000000 --- a/Externals/soundtouch/FIFOSampleBuffer.h +++ /dev/null @@ -1,180 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////
-///
-/// A buffer class for temporarily storaging sound samples, operates as a
-/// first-in-first-out pipe.
-///
-/// Samples are added to the end of the sample buffer with the 'putSamples'
-/// function, and are received from the beginning of the buffer by calling
-/// the 'receiveSamples' function. The class automatically removes the
-/// output samples from the buffer as well as grows the storage size
-/// whenever necessary.
-///
-/// 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 FIFOSampleBuffer_H
-#define FIFOSampleBuffer_H
-
-#include "FIFOSamplePipe.h"
-
-namespace soundtouch
-{
-
-/// Sample buffer working in FIFO (first-in-first-out) principle. The class takes
-/// care of storage size adjustment and data moving during input/output operations.
-///
-/// Notice that in case of stereo audio, one sample is considered to consist of
-/// both channel data.
-class FIFOSampleBuffer : public FIFOSamplePipe
-{
-private:
- /// Sample buffer.
- SAMPLETYPE *buffer;
-
- // Raw unaligned buffer memory. 'buffer' is made aligned by pointing it to first
- // 16-byte aligned location of this buffer
- SAMPLETYPE *bufferUnaligned;
-
- /// Sample buffer size in bytes
- uint sizeInBytes;
-
- /// How many samples are currently in buffer.
- uint samplesInBuffer;
-
- /// Channels, 1=mono, 2=stereo.
- uint channels;
-
- /// Current position pointer to the buffer. This pointer is increased when samples are
- /// removed from the pipe so that it's necessary to actually rewind buffer (move data)
- /// only new data when is put to the pipe.
- uint bufferPos;
-
- /// Rewind the buffer by moving data from position pointed by 'bufferPos' to real
- /// beginning of the buffer.
- void rewind();
-
- /// Ensures that the buffer has capacity for at least this many samples.
- void ensureCapacity(uint capacityRequirement);
-
- /// Returns current capacity.
- uint getCapacity() const;
-
-public:
-
- /// Constructor
- FIFOSampleBuffer(int numChannels = 2 ///< Number of channels, 1=mono, 2=stereo.
- ///< Default is stereo.
- );
-
- /// destructor
- ~FIFOSampleBuffer() 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!
- ///
- /// 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() 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
- /// inserting new samples into the sample buffer directly. Please be careful
- /// not corrupt the book-keeping!
- ///
- /// When using this function as means for inserting new samples, also remember
- /// to increase the sample count afterwards, by calling the
- /// 'putSamples(numSamples)' function.
- SAMPLETYPE *ptrEnd(
- uint slackCapacity ///< How much free capacity (in samples) there _at least_
- ///< 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.
- );
-
- /// Adds 'numSamples' pcs of samples from the 'samples' memory position to
- /// 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.
- ///
- /// This function is used to update the number of samples in the sample buffer
- /// when accessing the buffer directly with 'ptrEnd' function. Please be
- /// careful though!
- virtual void putSamples(uint numSamples ///< Number of samples been inserted.
- );
-
- /// 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.
- ///
- /// 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 override;
-
- /// Sets number of channels, 1 = mono, 2 = stereo.
- void setChannels(int numChannels);
-
- /// Get number of channels
- int getChannels()
- {
- return channels;
- }
-
- /// Returns nonzero if there aren't any samples available for outputting.
- virtual int isEmpty() const override;
-
- /// Clears all the samples.
- virtual void clear() override;
-
- /// allow trimming (downwards) amount of samples in pipeline.
- /// Returns adjusted amount of samples
- uint adjustAmountOfSamples(uint numSamples) override;
-
- /// Add silence to end of buffer
- void addSilent(uint nSamples);
-};
-
-}
-
-#endif
diff --git a/Externals/soundtouch/FIFOSamplePipe.h b/Externals/soundtouch/FIFOSamplePipe.h deleted file mode 100644 index 2ce5178a43..0000000000 --- a/Externals/soundtouch/FIFOSamplePipe.h +++ /dev/null @@ -1,230 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////
-///
-/// 'FIFOSamplePipe' : An abstract base class for classes that manipulate sound
-/// samples by operating like a first-in-first-out pipe: New samples are fed
-/// into one end of the pipe with the 'putSamples' function, and the processed
-/// samples are received from the other end with the 'receiveSamples' function.
-///
-/// 'FIFOProcessor' : A base class for classes the do signal processing with
-/// the samples while operating like a first-in-first-out pipe. When samples
-/// are input with the 'putSamples' function, the class processes them
-/// and moves the processed samples to the given 'output' pipe object, which
-/// may be either another processing stage, or a fifo sample buffer object.
-///
-/// 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 FIFOSamplePipe_H
-#define FIFOSamplePipe_H
-
-#include <assert.h>
-#include <stdlib.h>
-#include "STTypes.h"
-
-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() {}
-
-
- /// 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!
- ///
- /// 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() = 0;
-
- /// Adds 'numSamples' pcs of samples from the 'samples' memory position to
- /// the sample buffer.
- virtual void putSamples(const SAMPLETYPE *samples, ///< Pointer to samples.
- uint numSamples ///< Number of samples to insert.
- ) = 0;
-
-
- // Moves samples from the 'other' pipe instance to this instance.
- void moveSamples(FIFOSamplePipe &other ///< Other pipe instance where from the receive the data.
- )
- {
- int oNumSamples = other.numSamples();
-
- putSamples(other.ptrBegin(), oNumSamples);
- other.receiveSamples(oNumSamples);
- };
-
- /// 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.
- ) = 0;
-
- /// 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.
- ) = 0;
-
- /// Returns number of samples currently available.
- virtual uint numSamples() const = 0;
-
- // Returns nonzero if there aren't any samples available for outputting.
- virtual int isEmpty() const = 0;
-
- /// Clears all the samples.
- virtual void clear() = 0;
-
- /// allow trimming (downwards) amount of samples in pipeline.
- /// Returns adjusted amount of samples
- virtual uint adjustAmountOfSamples(uint numSamples) = 0;
-
-};
-
-
-/// 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
-/// all the processing stages.
-///
-/// When samples are input to this class, they're first processed and then put to
-/// the FIFO pipe that's defined as output of this class. This output pipe can be
-/// either other processing stage or a FIFO sample buffer.
-class FIFOProcessor :public FIFOSamplePipe
-{
-protected:
- /// Internal pipe where processed samples are put.
- FIFOSamplePipe *output;
-
- /// Sets output pipe.
- void setOutPipe(FIFOSamplePipe *pOutput)
- {
- assert(output == nullptr);
- assert(pOutput != nullptr);
- output = pOutput;
- }
-
- /// Constructor. Doesn't define output pipe; it has to be set be
- /// 'setOutPipe' function.
- FIFOProcessor()
- {
- output = nullptr;
- }
-
- /// Constructor. Configures output pipe.
- FIFOProcessor(FIFOSamplePipe *pOutput ///< Output pipe.
- )
- {
- output = pOutput;
- }
-
- /// Destructor.
- 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!
- ///
- /// 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() override
- {
- return output->ptrBegin();
- }
-
-public:
-
- /// 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 *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 override
- {
- return output->numSamples();
- }
-
- /// Returns nonzero if there aren't any samples available for outputting.
- 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) override
- {
- return output->adjustAmountOfSamples(numSamples);
- }
-};
-
-}
-
-#endif
diff --git a/Externals/soundtouch/FIRFilter.cpp b/Externals/soundtouch/FIRFilter.cpp deleted file mode 100644 index 473ca07a75..0000000000 --- a/Externals/soundtouch/FIRFilter.cpp +++ /dev/null @@ -1,314 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////
-///
-/// General FIR digital filter routines with MMX optimization.
-///
-/// 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
-///
-////////////////////////////////////////////////////////////////////////////////
-//
-// 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 "FIRFilter.h"
-#include "cpu_detect.h"
-
-using namespace soundtouch;
-
-/*****************************************************************************
- *
- * Implementation of the class 'FIRFilter'
- *
- *****************************************************************************/
-
-FIRFilter::FIRFilter()
-{
- resultDivFactor = 0;
- resultDivider = 0;
- length = 0;
- lengthDiv8 = 0;
- 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;
- // hint compiler autovectorization that loop length is divisible by 8
- uint ilength = length & -8;
-
- assert((length != 0) && (length == ilength) && (src != nullptr) && (dest != nullptr) && (filterCoeffs != nullptr));
- assert(numSamples > ilength);
-
- end = 2 * (numSamples - ilength);
-
- #pragma omp parallel for
- for (j = 0; j < end; j += 2)
- {
- const SAMPLETYPE *ptr;
- LONG_SAMPLETYPE suml, sumr;
-
- suml = sumr = 0;
- ptr = src + j;
-
- for (uint i = 0; i < ilength; i ++)
- {
- suml += ptr[2 * i] * filterCoeffsStereo[2 * i];
- sumr += ptr[2 * i + 1] * filterCoeffsStereo[2 * i + 1];
- }
-
-#ifdef SOUNDTOUCH_INTEGER_SAMPLES
- suml >>= resultDivFactor;
- sumr >>= resultDivFactor;
- // saturate to 16 bit integer limits
- suml = (suml < -32768) ? -32768 : (suml > 32767) ? 32767 : suml;
- // saturate to 16 bit integer limits
- sumr = (sumr < -32768) ? -32768 : (sumr > 32767) ? 32767 : sumr;
-#endif // SOUNDTOUCH_INTEGER_SAMPLES
- dest[j] = (SAMPLETYPE)suml;
- dest[j + 1] = (SAMPLETYPE)sumr;
- }
- 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;
-
- // hint compiler autovectorization that loop length is divisible by 8
- int ilength = length & -8;
-
- assert(ilength != 0);
-
- end = numSamples - ilength;
- #pragma omp parallel for
- for (j = 0; j < end; j ++)
- {
- const SAMPLETYPE *pSrc = src + j;
- LONG_SAMPLETYPE sum;
- int i;
-
- sum = 0;
- for (i = 0; i < ilength; i ++)
- {
- 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;
-#endif // SOUNDTOUCH_INTEGER_SAMPLES
- dest[j] = (SAMPLETYPE)sum;
- }
- return end;
-}
-
-
-uint FIRFilter::evaluateFilterMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels)
-{
- int j, end;
-
- assert(length != 0);
- assert(src != nullptr);
- assert(dest != nullptr);
- assert(filterCoeffs != nullptr);
- assert(numChannels < 16);
-
- // 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;
- int i;
-
- for (c = 0; c < numChannels; c ++)
- {
- sums[c] = 0;
- }
-
- ptr = src + j;
-
- for (i = 0; i < ilength; i ++)
- {
- SAMPLETYPE coef=filterCoeffs[i];
- for (c = 0; c < numChannels; c ++)
- {
- sums[c] += ptr[0] * coef;
- ptr ++;
- }
- }
-
- for (c = 0; c < numChannels; c ++)
- {
-#ifdef SOUNDTOUCH_INTEGER_SAMPLES
- sums[c] >>= resultDivFactor;
-#endif // SOUNDTOUCH_INTEGER_SAMPLES
- dest[j+c] = (SAMPLETYPE)sums[c];
- }
- }
- return numSamples - ilength;
-}
-
-
-// Set filter coeffiecients and length.
-//
-// Throws an exception if filter length isn't divisible by 8
-void FIRFilter::setCoefficients(const SAMPLETYPE *coeffs, uint newLength, uint uResultDivFactor)
-{
- 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);
-
- resultDivFactor = uResultDivFactor;
- resultDivider = (SAMPLETYPE)::pow(2.0, (int)resultDivFactor);
-
- delete[] filterCoeffs;
- filterCoeffs = new SAMPLETYPE[length];
- 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);
- }
-}
-
-
-uint FIRFilter::getLength() const
-{
- return length;
-}
-
-
-// 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 FIRFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels)
-{
- assert(length > 0);
- assert(lengthDiv8 * 8 == length);
-
- if (numSamples < length) return 0;
-
-#ifndef USE_MULTICH_ALWAYS
- if (numChannels == 1)
- {
- return evaluateFilterMono(dest, src, numSamples);
- }
- else if (numChannels == 2)
- {
- return evaluateFilterStereo(dest, src, numSamples);
- }
- else
-#endif // USE_MULTICH_ALWAYS
- {
- assert(numChannels > 0);
- return evaluateFilterMulti(dest, src, numSamples, numChannels);
- }
-}
-
-
-// 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)
-{
- // 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!");
- return newInstance();
-}
-
-
-FIRFilter * FIRFilter::newInstance()
-{
- uint uExtensions;
-
- uExtensions = detectCPUextensions();
-
- // Check if MMX/SSE instruction set extensions supported by CPU
-
-#ifdef SOUNDTOUCH_ALLOW_MMX
- // MMX routines available only with integer sample types
- if (uExtensions & SUPPORT_MMX)
- {
- return ::new FIRFilterMMX;
- }
- else
-#endif // SOUNDTOUCH_ALLOW_MMX
-
-#ifdef SOUNDTOUCH_ALLOW_SSE
- if (uExtensions & SUPPORT_SSE)
- {
- // SSE support
- return ::new FIRFilterSSE;
- }
- else
-#endif // SOUNDTOUCH_ALLOW_SSE
-
- {
- // ISA optimizations not supported, use plain C version
- return ::new FIRFilter;
- }
-}
diff --git a/Externals/soundtouch/FIRFilter.h b/Externals/soundtouch/FIRFilter.h deleted file mode 100644 index 90b730fcb5..0000000000 --- a/Externals/soundtouch/FIRFilter.h +++ /dev/null @@ -1,140 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////
-///
-/// General FIR digital filter routines with MMX optimization.
-///
-/// Note : MMX optimized functions reside in a separate, platform-specific file,
-/// e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'
-///
-/// 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 FIRFilter_H
-#define FIRFilter_H
-
-#include <stddef.h>
-#include "STTypes.h"
-
-namespace soundtouch
-{
-
-class FIRFilter
-{
-protected:
- // Number of FIR filter taps
- uint length;
- // Number of FIR filter taps divided by 8
- uint lengthDiv8;
-
- // Result divider factor in 2^k format
- uint resultDivFactor;
-
- // Result divider value.
- SAMPLETYPE resultDivider;
-
- // Memory for filter coefficients
- SAMPLETYPE *filterCoeffs;
- SAMPLETYPE *filterCoeffsStereo;
-
- virtual uint evaluateFilterStereo(SAMPLETYPE *dest,
- const SAMPLETYPE *src,
- uint numSamples) const;
- virtual uint evaluateFilterMono(SAMPLETYPE *dest,
- const SAMPLETYPE *src,
- uint numSamples) const;
- virtual uint evaluateFilterMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels);
-
-public:
- FIRFilter();
- virtual ~FIRFilter();
-
- /// Operator 'new' is overloaded so that it automatically creates a suitable instance
- /// depending on if we've a MMX-capable CPU available or not.
- static void * operator new(size_t s);
-
- static FIRFilter *newInstance();
-
- /// 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.
- ///
- /// \return Number of samples copied to 'dest'.
- uint evaluate(SAMPLETYPE *dest,
- const SAMPLETYPE *src,
- uint numSamples,
- uint numChannels);
-
- uint getLength() const;
-
- virtual void setCoefficients(const SAMPLETYPE *coeffs,
- uint newLength,
- uint uResultDivFactor);
-};
-
-
-// Optional subclasses that implement CPU-specific optimizations:
-
-#ifdef SOUNDTOUCH_ALLOW_MMX
-
-/// Class that implements MMX optimized functions exclusive for 16bit integer samples type.
- class FIRFilterMMX : public FIRFilter
- {
- protected:
- short *filterCoeffsUnalign;
- short *filterCoeffsAlign;
-
- 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) override;
- };
-
-#endif // SOUNDTOUCH_ALLOW_MMX
-
-
-#ifdef SOUNDTOUCH_ALLOW_SSE
- /// Class that implements SSE optimized functions exclusive for floating point samples type.
- class FIRFilterSSE : public FIRFilter
- {
- protected:
- float *filterCoeffsUnalign;
- float *filterCoeffsAlign;
-
- 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) override;
- };
-
-#endif // SOUNDTOUCH_ALLOW_SSE
-
-}
-
-#endif // FIRFilter_H
diff --git a/Externals/soundtouch/InterpolateCubic.cpp b/Externals/soundtouch/InterpolateCubic.cpp deleted file mode 100644 index fe49684817..0000000000 --- a/Externals/soundtouch/InterpolateCubic.cpp +++ /dev/null @@ -1,196 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////
-///
-/// 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 <stddef.h>
-#include <math.h>
-#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 deleted file mode 100644 index 64faf646bb..0000000000 --- a/Externals/soundtouch/InterpolateCubic.h +++ /dev/null @@ -1,69 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////
-///
-/// 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 deleted file mode 100644 index a11a493a98..0000000000 --- a/Externals/soundtouch/InterpolateLinear.cpp +++ /dev/null @@ -1,296 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////
-///
-/// 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 <assert.h>
-#include <stdlib.h>
-#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 deleted file mode 100644 index 8034d76ff1..0000000000 --- a/Externals/soundtouch/InterpolateLinear.h +++ /dev/null @@ -1,98 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////
-///
-/// 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 deleted file mode 100644 index 8d9761336d..0000000000 --- a/Externals/soundtouch/InterpolateShannon.cpp +++ /dev/null @@ -1,181 +0,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 <math.h>
-#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 deleted file mode 100644 index e0c6fa5d73..0000000000 --- a/Externals/soundtouch/InterpolateShannon.h +++ /dev/null @@ -1,74 +0,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
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#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 deleted file mode 100644 index 5423c859aa..0000000000 --- a/Externals/soundtouch/PeakFinder.cpp +++ /dev/null @@ -1,277 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////
-///
-/// Peak detection routine.
-///
-/// The routine detects highest value on an array of values and calculates the
-/// precise peak location as a mass-center of the 'hump' around the peak value.
-///
-/// 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 <math.h>
-#include <assert.h>
-
-#include "PeakFinder.h"
-
-using namespace soundtouch;
-
-#define max(x, y) (((x) > (y)) ? (x) : (y))
-
-
-PeakFinder::PeakFinder()
-{
- minPos = maxPos = 0;
-}
-
-
-// Finds real 'top' of a peak hump from neighnourhood of the given 'peakpos'.
-int PeakFinder::findTop(const float *data, int peakpos) const
-{
- int i;
- int start, end;
- float refvalue;
-
- refvalue = data[peakpos];
-
- // seek within ±10 points
- start = peakpos - 10;
- if (start < minPos) start = minPos;
- end = peakpos + 10;
- if (end > maxPos) end = maxPos;
-
- for (i = start; i <= end; i ++)
- {
- if (data[i] > refvalue)
- {
- peakpos = i;
- refvalue = data[i];
- }
- }
-
- // failure if max value is at edges of seek range => it's not peak, it's at slope.
- if ((peakpos == start) || (peakpos == end)) return 0;
-
- return peakpos;
-}
-
-
-// Finds 'ground level' of a peak hump by starting from 'peakpos' and proceeding
-// to direction defined by 'direction' until next 'hump' after minimum value will
-// begin
-int PeakFinder::findGround(const float *data, int peakpos, int direction) const
-{
- int lowpos;
- int pos;
- int climb_count;
- float refvalue;
- float delta;
-
- climb_count = 0;
- refvalue = data[peakpos];
- lowpos = peakpos;
-
- pos = peakpos;
-
- while ((pos > minPos+1) && (pos < maxPos-1))
- {
- int prevpos;
-
- prevpos = pos;
- pos += direction;
-
- // calculate derivate
- delta = data[pos] - data[prevpos];
- if (delta <= 0)
- {
- // going downhill, ok
- if (climb_count)
- {
- climb_count --; // decrease climb count
- }
-
- // check if new minimum found
- if (data[pos] < refvalue)
- {
- // new minimum found
- lowpos = pos;
- refvalue = data[pos];
- }
- }
- else
- {
- // going uphill, increase climbing counter
- climb_count ++;
- if (climb_count > 5) break; // we've been climbing too long => it's next uphill => quit
- }
- }
- return lowpos;
-}
-
-
-// Find offset where the value crosses the given level, when starting from 'peakpos' and
-// proceeds to direction defined in 'direction'
-int PeakFinder::findCrossingLevel(const float *data, float level, int peakpos, int direction) const
-{
- float peaklevel;
- int pos;
-
- peaklevel = data[peakpos];
- assert(peaklevel >= level);
- pos = peakpos;
- while ((pos >= minPos) && (pos + direction < maxPos))
- {
- if (data[pos + direction] < level) return pos; // crossing found
- pos += direction;
- }
- return -1; // not found
-}
-
-
-// Calculates the center of mass location of 'data' array items between 'firstPos' and 'lastPos'
-double PeakFinder::calcMassCenter(const float *data, int firstPos, int lastPos) const
-{
- int i;
- float sum;
- float wsum;
-
- sum = 0;
- wsum = 0;
- for (i = firstPos; i <= lastPos; i ++)
- {
- sum += (float)i * data[i];
- wsum += data[i];
- }
-
- if (wsum < 1e-6) return 0;
- return sum / wsum;
-}
-
-
-/// get exact center of peak near given position by calculating local mass of center
-double PeakFinder::getPeakCenter(const float *data, int peakpos) const
-{
- float peakLevel; // peak level
- int crosspos1, crosspos2; // position where the peak 'hump' crosses cutting level
- float cutLevel; // cutting value
- float groundLevel; // ground level of the peak
- int gp1, gp2; // bottom positions of the peak 'hump'
-
- // find ground positions.
- gp1 = findGround(data, peakpos, -1);
- gp2 = findGround(data, peakpos, 1);
-
- peakLevel = data[peakpos];
-
- if (gp1 == gp2)
- {
- // avoid rounding errors when all are equal
- assert(gp1 == peakpos);
- cutLevel = groundLevel = peakLevel;
- } else {
- // get average of the ground levels
- groundLevel = 0.5f * (data[gp1] + data[gp2]);
-
- // calculate 70%-level of the peak
- cutLevel = 0.70f * peakLevel + 0.30f * groundLevel;
- }
-
- // find mid-level crossings
- crosspos1 = findCrossingLevel(data, cutLevel, peakpos, -1);
- crosspos2 = findCrossingLevel(data, cutLevel, peakpos, 1);
-
- if ((crosspos1 < 0) || (crosspos2 < 0)) return 0; // no crossing, no peak..
-
- // calculate mass center of the peak surroundings
- return calcMassCenter(data, crosspos1, crosspos2);
-}
-
-
-double PeakFinder::detectPeak(const float *data, int aminPos, int amaxPos)
-{
-
- int i;
- int peakpos; // position of peak level
- double highPeak, peak;
-
- this->minPos = aminPos;
- this->maxPos = amaxPos;
-
- // find absolute peak
- peakpos = minPos;
- peak = data[minPos];
- for (i = minPos + 1; i < maxPos; i ++)
- {
- if (data[i] > peak)
- {
- peak = data[i];
- peakpos = i;
- }
- }
-
- // Calculate exact location of the highest peak mass center
- highPeak = getPeakCenter(data, peakpos);
- peak = highPeak;
-
- // Now check if the highest peak were in fact harmonic of the true base beat peak
- // - sometimes the highest peak can be Nth harmonic of the true base peak yet
- // just a slightly higher than the true base
-
- for (i = 1; i < 3; i ++)
- {
- double peaktmp, harmonic;
- int i1,i2;
-
- 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
- if (peakpos == 0) continue; // no local max here
-
- // calculate mass-center of possible harmonic peak
- peaktmp = getPeakCenter(data, peakpos);
-
- // accept harmonic peak if
- // (a) it is found
- // (b) is within ±4% of the expected harmonic interval
- // (c) has at least half x-corr value of the max. peak
-
- double diff = harmonic * peaktmp / highPeak;
- if ((diff < 0.96) || (diff > 1.04)) continue; // peak too afar from expected
-
- // now compare to highest detected peak
- i1 = (int)(highPeak + 0.5);
- i2 = (int)(peaktmp + 0.5);
- if (data[i2] >= 0.4*data[i1])
- {
- // The harmonic is at least half as high primary peak,
- // thus use the harmonic peak instead
- peak = peaktmp;
- }
- }
-
- return peak;
-}
diff --git a/Externals/soundtouch/PeakFinder.h b/Externals/soundtouch/PeakFinder.h deleted file mode 100644 index 9fe66adac5..0000000000 --- a/Externals/soundtouch/PeakFinder.h +++ /dev/null @@ -1,90 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////
-///
-/// The routine detects highest value on an array of values and calculates the
-/// precise peak location as a mass-center of the 'hump' around the peak value.
-///
-/// 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 _PeakFinder_H_
-#define _PeakFinder_H_
-
-namespace soundtouch
-{
-
-class PeakFinder
-{
-protected:
- /// Min, max allowed peak positions within the data vector
- int minPos, maxPos;
-
- /// Calculates the mass center between given vector items.
- double calcMassCenter(const float *data, ///< Data vector.
- 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
- /// given level.
- int findCrossingLevel(const float *data, ///< Data vector.
- float level, ///< Goal crossing level.
- int peakpos, ///< Peak position index within the data vector.
- int direction /// Direction where to proceed from the peak: 1 = right, -1 = left.
- ) const;
-
- // Finds real 'top' of a peak hump from neighnourhood of the given 'peakpos'.
- int findTop(const float *data, int peakpos) const;
-
-
- /// Finds the 'ground' level, i.e. smallest level between two neighbouring peaks, to right-
- /// or left-hand side of the given peak position.
- int findGround(const float *data, /// Data vector.
- int peakpos, /// Peak position index within the data vector.
- int direction /// Direction where to proceed from the peak: 1 = right, -1 = left.
- ) const;
-
- /// get exact center of peak near given position by calculating local mass of center
- double getPeakCenter(const float *data, int peakpos) const;
-
-public:
- /// Constructor.
- PeakFinder();
-
- /// Detect exact peak position of the data vector by finding the largest peak 'hump'
- /// and calculating the mass-center location of the peak hump.
- ///
- /// \return The location of the largest base harmonic peak hump.
- double detectPeak(const float *data, /// Data vector to be analyzed. The data vector has
- /// to be at least 'maxPos' items long.
- int minPos, ///< Min allowed peak location within the vector data.
- int maxPos ///< Max allowed peak location within the vector data.
- );
-};
-
-}
-
-#endif // _PeakFinder_H_
diff --git a/Externals/soundtouch/RateTransposer.cpp b/Externals/soundtouch/RateTransposer.cpp deleted file mode 100644 index a48ea86569..0000000000 --- a/Externals/soundtouch/RateTransposer.cpp +++ /dev/null @@ -1,313 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////
-///
-/// Sample rate transposer. Changes sample rate by using linear interpolation
-/// together with anti-alias filtering (first order interpolation with anti-
-/// alias filtering should be quite adequate for this application)
-///
-/// 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 <stdlib.h>
-#include <stdio.h>
-#include "RateTransposer.h"
-#include "InterpolateLinear.h"
-#include "InterpolateCubic.h"
-#include "InterpolateShannon.h"
-#include "AAFilter.h"
-
-using namespace soundtouch;
-
-// Define default interpolation algorithm here
-TransposerBase::ALGORITHM TransposerBase::algorithm = TransposerBase::CUBIC;
-
-
-// Constructor
-RateTransposer::RateTransposer() : FIFOProcessor(&outputBuffer)
-{
- 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;
- delete pTransposer;
-}
-
-
-/// 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
-}
-
-
-/// Returns nonzero if anti-alias filter is enabled.
-bool RateTransposer::isAAFilterEnabled() const
-{
- return bUseAAFilter;
-}
-
-
-AAFilter *RateTransposer::getAAFilter()
-{
- return pAAFilter;
-}
-
-
-// Sets new target iRate. Normal iRate = 1.0, smaller values represent slower
-// iRate, larger faster iRates.
-void RateTransposer::setRate(double newRate)
-{
- double fCutoff;
-
- pTransposer->setRate(newRate);
-
- // design a new anti-alias filter
- if (newRate > 1.0)
- {
- fCutoff = 0.5 / newRate;
- }
- else
- {
- fCutoff = 0.5 * newRate;
- }
- pAAFilter->setCutoffFreq(fCutoff);
-}
-
-
-// Adds 'nSamples' pcs of samples from the 'samples' memory position into
-// the input of the object.
-void RateTransposer::putSamples(const SAMPLETYPE *samples, uint nSamples)
-{
- processSamples(samples, nSamples);
-}
-
-
-// Transposes sample rate by applying anti-alias filter to prevent folding.
-// Returns amount of samples returned in the "dest" buffer.
-// The maximum amount of samples that can be returned at a time is set by
-// the 'set_returnBuffer_size' function.
-void RateTransposer::processSamples(const SAMPLETYPE *src, uint nSamples)
-{
- if (nSamples == 0) return;
-
- // Store samples to input buffer
- inputBuffer.putSamples(src, nSamples);
-
- // If anti-alias filter is turned off, simply transpose without applying
- // the filter
- if (bUseAAFilter == false)
- {
- (void)pTransposer->transpose(outputBuffer, inputBuffer);
- return;
- }
-
- assert(pAAFilter);
-
- // Transpose with anti-alias filter
- if (pTransposer->rate < 1.0f)
- {
- // If the parameter 'Rate' value is smaller than 1, first transpose
- // the samples and then apply the anti-alias filter to remove aliasing.
-
- // Transpose the samples, store the result to end of "midBuffer"
- pTransposer->transpose(midBuffer, inputBuffer);
-
- // Apply the anti-alias filter for transposed samples in midBuffer
- pAAFilter->evaluate(outputBuffer, midBuffer);
- }
- else
- {
- // If the parameter 'Rate' value is larger than 1, first apply the
- // anti-alias filter to remove high frequencies (prevent them from folding
- // over the lover frequencies), then transpose.
-
- // Apply the anti-alias filter for samples in inputBuffer
- pAAFilter->evaluate(midBuffer, inputBuffer);
-
- // Transpose the AA-filtered samples in "midBuffer"
- pTransposer->transpose(outputBuffer, midBuffer);
- }
-}
-
-
-// Sets the number of channels, 1 = mono, 2 = stereo
-void RateTransposer::setChannels(int nChannels)
-{
- if (!verifyNumberOfChannels(nChannels) ||
- (pTransposer->numChannels == nChannels)) return;
-
- pTransposer->setChannels(nChannels);
- inputBuffer.setChannels(nChannels);
- midBuffer.setChannels(nChannels);
- outputBuffer.setChannels(nChannels);
-}
-
-
-// Clears all the samples in the object
-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);
-}
-
-
-// Returns nonzero if there aren't any samples available for outputting.
-int RateTransposer::isEmpty() const
-{
- int res;
-
- res = FIFOProcessor::isEmpty();
- if (res == 0) return 0;
- return inputBuffer.isEmpty();
-}
-
-
-/// Return approximate initial input-output latency
-int RateTransposer::getLatency() const
-{
- return pTransposer->getLatency() +
- ((bUseAAFilter) ? (pAAFilter->getLength() / 2) : 0);
-}
-
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// TransposerBase - Base class for interpolation
-//
-
-// static function to set interpolation algorithm
-void TransposerBase::setAlgorithm(TransposerBase::ALGORITHM a)
-{
- TransposerBase::algorithm = a;
-}
-
-
-// Transposes the sample rate of the given samples using linear interpolation.
-// Returns the number of samples returned in the "dest" buffer
-int TransposerBase::transpose(FIFOSampleBuffer &dest, FIFOSampleBuffer &src)
-{
- int numSrcSamples = src.numSamples();
- int sizeDemand = (int)((double)numSrcSamples / rate) + 8;
- int numOutput;
- SAMPLETYPE *psrc = src.ptrBegin();
- SAMPLETYPE *pdest = dest.ptrEnd(sizeDemand);
-
-#ifndef USE_MULTICH_ALWAYS
- if (numChannels == 1)
- {
- numOutput = transposeMono(pdest, psrc, numSrcSamples);
- }
- else if (numChannels == 2)
- {
- numOutput = transposeStereo(pdest, psrc, numSrcSamples);
- }
- else
-#endif // USE_MULTICH_ALWAYS
- {
- assert(numChannels > 0);
- numOutput = transposeMulti(pdest, psrc, numSrcSamples);
- }
- dest.putSamples(numOutput);
- src.receiveSamples(numSrcSamples);
- return numOutput;
-}
-
-
-TransposerBase::TransposerBase()
-{
- numChannels = 0;
- rate = 1.0f;
-}
-
-
-TransposerBase::~TransposerBase()
-{
-}
-
-
-void TransposerBase::setChannels(int channels)
-{
- numChannels = channels;
- resetRegisters();
-}
-
-
-void TransposerBase::setRate(double newRate)
-{
- rate = newRate;
-}
-
-
-// static factory function
-TransposerBase *TransposerBase::newInstance()
-{
-#ifdef SOUNDTOUCH_INTEGER_SAMPLES
- // Notice: For integer arithmetic support only linear algorithm (due to simplest calculus)
- return ::new InterpolateLinearInteger;
-#else
- switch (algorithm)
- {
- case LINEAR:
- return new InterpolateLinearFloat;
-
- case CUBIC:
- return new InterpolateCubic;
-
- case SHANNON:
- return new InterpolateShannon;
-
- default:
- assert(false);
- return nullptr;
- }
-#endif
-}
diff --git a/Externals/soundtouch/RateTransposer.h b/Externals/soundtouch/RateTransposer.h deleted file mode 100644 index 411f4e897d..0000000000 --- a/Externals/soundtouch/RateTransposer.h +++ /dev/null @@ -1,164 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////
-///
-/// Sample rate transposer. Changes sample rate by using linear interpolation
-/// together with anti-alias filtering (first order interpolation with anti-
-/// alias filtering should be quite adequate for this application).
-///
-/// Use either of the derived classes of 'RateTransposerInteger' or
-/// 'RateTransposerFloat' for corresponding integer/floating point tranposing
-/// algorithm implementation.
-///
-/// 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 RateTransposer_H
-#define RateTransposer_H
-
-#include <stddef.h>
-#include "AAFilter.h"
-#include "FIFOSamplePipe.h"
-#include "FIFOSampleBuffer.h"
-
-#include "STTypes.h"
-
-namespace soundtouch
-{
-
-/// Abstract base class for transposer implementations (linear, advanced vs integer, float etc)
-class TransposerBase
-{
-public:
- enum ALGORITHM {
- LINEAR = 0,
- CUBIC,
- SHANNON
- };
-
-protected:
- virtual int transposeMono(SAMPLETYPE *dest,
- const SAMPLETYPE *src,
- int &srcSamples) = 0;
- virtual int transposeStereo(SAMPLETYPE *dest,
- const SAMPLETYPE *src,
- int &srcSamples) = 0;
- virtual int transposeMulti(SAMPLETYPE *dest,
- const SAMPLETYPE *src,
- int &srcSamples) = 0;
-
- static ALGORITHM algorithm;
-
-public:
- double rate;
- int numChannels;
-
- TransposerBase();
- virtual ~TransposerBase();
-
- 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();
-
- // static function to set interpolation algorithm
- static void setAlgorithm(ALGORITHM a);
-};
-
-
-/// A common linear samplerate transposer class.
-///
-class RateTransposer : public FIFOProcessor
-{
-protected:
- /// Anti-alias filter object
- AAFilter *pAAFilter;
- TransposerBase *pTransposer;
-
- /// Buffer for collecting samples to feed the anti-alias filter between
- /// two batches
- FIFOSampleBuffer inputBuffer;
-
- /// Buffer for keeping samples between transposing & anti-alias filter
- FIFOSampleBuffer midBuffer;
-
- /// Output sample buffer
- FIFOSampleBuffer outputBuffer;
-
- bool bUseAAFilter;
-
-
- /// Transposes sample rate by applying anti-alias filter to prevent folding.
- /// Returns amount of samples returned in the "dest" buffer.
- /// The maximum amount of samples that can be returned at a time is set by
- /// the 'set_returnBuffer_size' function.
- void processSamples(const SAMPLETYPE *src,
- uint numSamples);
-
-public:
- RateTransposer();
- virtual ~RateTransposer() override;
-
- /// Returns the output buffer object
- FIFOSamplePipe *getOutput() { return &outputBuffer; };
-
- /// Return anti-alias filter object
- AAFilter *getAAFilter();
-
- /// Enables/disables the anti-alias filter. Zero to disable, nonzero to enable
- void enableAAFilter(bool newMode);
-
- /// Returns nonzero if anti-alias filter is enabled.
- bool isAAFilterEnabled() const;
-
- /// Sets new target rate. Normal rate = 1.0, smaller values represent slower
- /// rate, larger faster rates.
- virtual void setRate(double newRate);
-
- /// Sets the number of channels, 1 = mono, 2 = stereo
- void setChannels(int channels);
-
- /// Adds 'numSamples' pcs of samples from the 'samples' memory position into
- /// the input of the object.
- void putSamples(const SAMPLETYPE *samples, uint numSamples) override;
-
- /// Clears all the samples in the object
- void clear() override;
-
- /// Returns nonzero if there aren't any samples available for outputting.
- int isEmpty() const override;
-
- /// Return approximate initial input-output latency
- int getLatency() const;
-};
-
-}
-
-#endif
diff --git a/Externals/soundtouch/STTypes.h b/Externals/soundtouch/STTypes.h deleted file mode 100644 index 2141d6b0ab..0000000000 --- a/Externals/soundtouch/STTypes.h +++ /dev/null @@ -1,189 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////
-///
-/// Common type definitions for SoundTouch audio processing library.
-///
-/// 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 STTypes_H
-#define STTypes_H
-
-#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"
-#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
-
- /// If following flag is defined, always uses multichannel processing
- /// routines also for mono and stero sound. This is for routine testing
- /// purposes; output should be same with either routines, yet disabling
- /// the dedicated mono/stereo processing routines will result in slower
- /// runtime performance so recommendation is to keep this off.
- // #define USE_MULTICH_ALWAYS
-
- #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
- #define SOUNDTOUCH_INTEGER_SAMPLES 1
- #endif
-
- #if !(SOUNDTOUCH_INTEGER_SAMPLES || SOUNDTOUCH_FLOAT_SAMPLES)
-
- /// Choose either 32bit floating point or 16bit integer sampletype
- /// by choosing one of the following defines, unless this selection
- /// has already been done in some other file.
- ////
- /// Notes:
- /// - In Windows environment, choose the sample format with the
- /// following defines.
- /// - In GNU environment, the floating point samples are used by
- /// default, but integer samples can be chosen by giving the
- /// following switch to the configure script:
- /// ./configure --enable-integer-samples
- /// 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
-
- #endif
-
- #if (_M_IX86 || __i386__ || __x86_64__ || _M_X64)
- /// Define this to allow X86-specific assembler/intrinsic optimizations.
- /// Notice that library contains also usual C++ versions of each of these
- /// these routines, so if you're having difficulties getting the optimized
- /// routines compiled for whatever reason, you may disable these optimizations
- /// to make the library compile.
-
- //#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:
- /// ./configure --disable-x86-optimizations
- /// ./configure --enable-x86-optimizations=no
- #ifdef SOUNDTOUCH_DISABLE_X86_OPTIMIZATIONS
- #undef SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS
- #endif
- #else
- /// Always disable optimizations when not using a x86 systems.
- #undef SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS
-
- #endif
-
- // 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
- // 16bit integer sample type
- typedef short SAMPLETYPE;
- // data type for sample accumulation: Use 32bit integer to prevent overflows
- typedef long LONG_SAMPLETYPE;
-
- #ifdef SOUNDTOUCH_FLOAT_SAMPLES
- // check that only one sample type is defined
- #error "conflicting sample types defined"
- #endif // SOUNDTOUCH_FLOAT_SAMPLES
-
- #ifdef SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS
- // 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 float also here to enable
- // efficient autovectorization
- typedef float LONG_SAMPLETYPE;
-
- #ifdef SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS
- // Allow SSE optimizations
- #define SOUNDTOUCH_ALLOW_SSE 1
- #endif
-
- #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
-#ifdef ST_NO_EXCEPTION_HANDLING
- // Exceptions disabled. Throw asserts instead if enabled.
- #include <assert.h>
- #define ST_THROW_RT_ERROR(x) {assert((const char *)x);}
-#else
- // use c++ standard exceptions
- #include <stdexcept>
- #include <string>
- #define ST_THROW_RT_ERROR(x) {throw std::runtime_error(x);}
-#endif
-
-// When this #define is active, eliminates a clicking sound when the "rate" or "pitch"
-// parameter setting crosses from value <1 to >=1 or vice versa during processing.
-// Default is off as such crossover is untypical case and involves a slight sound
-// quality compromise.
-//#define SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER 1
-
-#endif
diff --git a/Externals/soundtouch/SoundTouch.cpp b/Externals/soundtouch/SoundTouch.cpp deleted file mode 100644 index cb161f0d4f..0000000000 --- a/Externals/soundtouch/SoundTouch.cpp +++ /dev/null @@ -1,538 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////
-///
-/// 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
-/// desired tempo/pitch/rate settings with the corresponding functions.
-///
-/// - 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
-/// 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
-/// 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
-/// '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
-/// relationship with the amount of previously input samples.
-///
-/// - The tempo/pitch/rate control parameters can be altered during processing.
-/// Please notice though that they aren't currently protected by semaphores,
-/// so in multi-thread application external semaphore protection may be
-/// 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' (change pitch but maintain tempo) is produced by a combination of
-/// combining the two other controls.
-///
-/// 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 <assert.h>
-#include <stdlib.h>
-#include <memory.h>
-#include <math.h>
-#include <stdio.h>
-
-#include "SoundTouch.h"
-#include "TDStretch.h"
-#include "RateTransposer.h"
-#include "cpu_detect.h"
-
-using namespace soundtouch;
-
-/// test if two floating point numbers are equal
-#define TEST_FLOAT_EQUAL(a, b) (fabs(a - b) < 1e-10)
-
-
-/// Print library version string for autoconf
-extern "C" void soundtouch_ac_test()
-{
- printf("SoundTouch Version: %s\n",SOUNDTOUCH_VERSION);
-}
-
-
-SoundTouch::SoundTouch()
-{
- // Initialize rate transposer and tempo changer instances
-
- pRateTransposer = new RateTransposer();
- pTDStretch = TDStretch::newInstance();
-
- setOutPipe(pTDStretch);
-
- rate = tempo = 0;
-
- virtualPitch =
- virtualRate =
- virtualTempo = 1.0;
-
- calcEffectiveRateAndTempo();
-
- samplesExpectedOut = 0;
- samplesOutput = 0;
-
- channels = 0;
- bSrateSet = false;
-}
-
-
-SoundTouch::~SoundTouch()
-{
- delete pRateTransposer;
- delete pTDStretch;
-}
-
-
-/// Get SoundTouch library version string
-const char *SoundTouch::getVersionString()
-{
- static const char *_version = SOUNDTOUCH_VERSION;
-
- return _version;
-}
-
-
-/// Get SoundTouch library version Id
-uint SoundTouch::getVersionId()
-{
- return SOUNDTOUCH_VERSION_ID;
-}
-
-
-// Sets the number of channels, 1 = mono, 2 = stereo
-void SoundTouch::setChannels(uint numChannels)
-{
- 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)
-{
- virtualRate = newRate;
- calcEffectiveRateAndTempo();
-}
-
-
-// Sets new rate control value as a difference in percents compared
-// to the original rate (-50 .. +100 %)
-void SoundTouch::setRateChange(double newRate)
-{
- virtualRate = 1.0 + 0.01 * newRate;
- calcEffectiveRateAndTempo();
-}
-
-
-// Sets new tempo control value. Normal tempo = 1.0, smaller values
-// represent slower tempo, larger faster tempo.
-void SoundTouch::setTempo(double newTempo)
-{
- virtualTempo = newTempo;
- calcEffectiveRateAndTempo();
-}
-
-
-// Sets new tempo control value as a difference in percents compared
-// to the original tempo (-50 .. +100 %)
-void SoundTouch::setTempoChange(double newTempo)
-{
- virtualTempo = 1.0 + 0.01 * newTempo;
- calcEffectiveRateAndTempo();
-}
-
-
-// Sets new pitch control value. Original pitch = 1.0, smaller values
-// represent lower pitches, larger values higher pitch.
-void SoundTouch::setPitch(double newPitch)
-{
- virtualPitch = newPitch;
- calcEffectiveRateAndTempo();
-}
-
-
-// Sets pitch change in octaves compared to the original pitch
-// (-1.00 .. +1.00)
-void SoundTouch::setPitchOctaves(double newPitch)
-{
- virtualPitch = exp(0.69314718056 * newPitch);
- calcEffectiveRateAndTempo();
-}
-
-
-// Sets pitch change in semi-tones compared to the original pitch
-// (-12 .. +12)
-void SoundTouch::setPitchSemiTones(int newPitch)
-{
- setPitchOctaves((double)newPitch / 12.0);
-}
-
-
-void SoundTouch::setPitchSemiTones(double newPitch)
-{
- setPitchOctaves(newPitch / 12.0);
-}
-
-
-// Calculates 'effective' rate and tempo values from the
-// nominal control values.
-void SoundTouch::calcEffectiveRateAndTempo()
-{
- double oldTempo = tempo;
- double oldRate = rate;
-
- tempo = virtualTempo / virtualPitch;
- rate = virtualPitch * virtualRate;
-
- if (!TEST_FLOAT_EQUAL(rate,oldRate)) pRateTransposer->setRate(rate);
- if (!TEST_FLOAT_EQUAL(tempo, oldTempo)) pTDStretch->setTempo(tempo);
-
-#ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
- if (rate <= 1.0f)
- {
- if (output != pTDStretch)
- {
- FIFOSamplePipe *tempoOut;
-
- assert(output == pRateTransposer);
- // move samples in the current output buffer to the output of pTDStretch
- tempoOut = pTDStretch->getOutput();
- tempoOut->moveSamples(*output);
- // move samples in pitch transposer's store buffer to tempo changer's input
- // deprecated : pTDStretch->moveSamples(*pRateTransposer->getStore());
-
- output = pTDStretch;
- }
- }
- else
-#endif
- {
- if (output != pRateTransposer)
- {
- FIFOSamplePipe *transOut;
-
- assert(output == pTDStretch);
- // move samples in the current output buffer to the output of pRateTransposer
- transOut = pRateTransposer->getOutput();
- transOut->moveSamples(*output);
- // move samples in tempo changer's input to pitch transposer's input
- pRateTransposer->moveSamples(*pTDStretch->getInput());
-
- output = pRateTransposer;
- }
- }
-}
-
-
-// Sets sample rate.
-void SoundTouch::setSampleRate(uint srate)
-{
- // set sample rate, leave other tempo changer parameters as they are.
- pTDStretch->setParameters((int)srate);
- bSrateSet = true;
-}
-
-
-// Adds 'numSamples' pcs of samples from the 'samples' memory position into
-// the input of the object.
-void SoundTouch::putSamples(const SAMPLETYPE *samples, uint nSamples)
-{
- if (bSrateSet == false)
- {
- ST_THROW_RT_ERROR("SoundTouch : Sample rate not defined");
- }
- else if (channels == 0)
- {
- ST_THROW_RT_ERROR("SoundTouch : Number of channels not defined");
- }
-
- // 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)
- {
- // transpose the rate down, output the transposed sound to tempo changer buffer
- assert(output == pTDStretch);
- pRateTransposer->putSamples(samples, nSamples);
- pTDStretch->moveSamples(*pRateTransposer);
- }
- else
-#endif
- {
- // evaluate the tempo changer, then transpose the rate up,
- assert(output == pRateTransposer);
- pTDStretch->putSamples(samples, nSamples);
- pRateTransposer->moveSamples(*pTDStretch);
- }
-}
-
-
-// Flushes the last samples from the processing pipeline to the output.
-// Clears also the internal processing buffers.
-//
-// Note: This function is meant for extracting the last samples of a sound
-// stream. This function may introduce additional blank samples in the end
-// of the sound stream, and thus it's not recommended to call this function
-// in the middle of a sound stream.
-void SoundTouch::flush()
-{
- int i;
- int numStillExpected;
- SAMPLETYPE *buff = new SAMPLETYPE[128 * channels];
-
- // 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);
- }
-
- adjustAmountOfSamples(numStillExpected);
-
- delete[] buff;
-
- // Clear input buffers
- pTDStretch->clearInput();
- // yet leave the output intouched as that's where the
- // flushed samples are!
-}
-
-
-// Changes a setting controlling the processing system behaviour. See the
-// 'SETTING_...' defines for available setting ID's.
-bool SoundTouch::setSetting(int settingId, int value)
-{
- int sampleRate, sequenceMs, seekWindowMs, overlapMs;
-
- // read current tdstretch routine parameters
- pTDStretch->getParameters(&sampleRate, &sequenceMs, &seekWindowMs, &overlapMs);
-
- switch (settingId)
- {
- case SETTING_USE_AA_FILTER :
- // enables / disabless anti-alias filter
- pRateTransposer->enableAAFilter((value != 0) ? true : false);
- return true;
-
- case SETTING_AA_FILTER_LENGTH :
- // sets anti-alias filter length
- pRateTransposer->getAAFilter()->setLength(value);
- return true;
-
- case SETTING_USE_QUICKSEEK :
- // enables / disables tempo routine quick seeking algorithm
- pTDStretch->enableQuickSeek((value != 0) ? true : false);
- return true;
-
- case SETTING_SEQUENCE_MS:
- // change time-stretch sequence duration parameter
- pTDStretch->setParameters(sampleRate, value, seekWindowMs, overlapMs);
- return true;
-
- case SETTING_SEEKWINDOW_MS:
- // change time-stretch seek window length parameter
- pTDStretch->setParameters(sampleRate, sequenceMs, value, overlapMs);
- return true;
-
- case SETTING_OVERLAP_MS:
- // change time-stretch overlap length parameter
- pTDStretch->setParameters(sampleRate, sequenceMs, seekWindowMs, value);
- return true;
-
- default :
- return false;
- }
-}
-
-
-// Reads a setting controlling the processing system behaviour. See the
-// 'SETTING_...' defines for available setting ID's.
-//
-// Returns the setting value.
-int SoundTouch::getSetting(int settingId) const
-{
- int temp;
-
- switch (settingId)
- {
- case SETTING_USE_AA_FILTER :
- return (uint)pRateTransposer->isAAFilterEnabled();
-
- case SETTING_AA_FILTER_LENGTH :
- return pRateTransposer->getAAFilter()->getLength();
-
- case SETTING_USE_QUICKSEEK :
- return (uint)pTDStretch->isQuickSeekEnabled();
-
- case SETTING_SEQUENCE_MS:
- pTDStretch->getParameters(nullptr, &temp, nullptr, nullptr);
- return temp;
-
- case SETTING_SEEKWINDOW_MS:
- pTDStretch->getParameters(nullptr, nullptr, &temp, nullptr);
- return temp;
-
- case SETTING_OVERLAP_MS:
- pTDStretch->getParameters(nullptr, nullptr, nullptr, &temp);
- return temp;
-
- case SETTING_NOMINAL_INPUT_SEQUENCE :
- {
- int size = pTDStretch->getInputSampleReq();
-
-#ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
- if (rate <= 1.0)
- {
- // transposing done before timestretch, which impacts latency
- return (int)(size * rate + 0.5);
- }
-#endif
- return size;
- }
-
- case SETTING_NOMINAL_OUTPUT_SEQUENCE :
- {
- int size = pTDStretch->getOutputBatchSize();
-
- if (rate > 1.0)
- {
- // transposing done after timestretch, which impacts latency
- return (int)(size / rate + 0.5);
- }
- return size;
- }
-
- case SETTING_INITIAL_LATENCY:
- {
- double latency = pTDStretch->getLatency();
- int latency_tr = pRateTransposer->getLatency();
-
-#ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
- if (rate <= 1.0)
- {
- // transposing done before timestretch, which impacts latency
- latency = (latency + latency_tr) * rate;
- }
- else
-#endif
- {
- latency += (double)latency_tr / rate;
- }
-
- return (int)(latency + 0.5);
- }
-
- default :
- return 0;
- }
-}
-
-
-// Clears all the samples in the object's output and internal processing
-// buffers.
-void SoundTouch::clear()
-{
- samplesExpectedOut = 0;
- samplesOutput = 0;
- pRateTransposer->clear();
- pTDStretch->clear();
-}
-
-
-/// Returns number of samples currently unprocessed.
-uint SoundTouch::numUnprocessedSamples() const
-{
- FIFOSamplePipe * psp;
- if (pTDStretch)
- {
- psp = pTDStretch->getInput();
- if (psp)
- {
- return psp->numSamples();
- }
- }
- return 0;
-}
-
-
-/// Output samples from beginning of the sample buffer. Copies requested samples to
-/// output buffer and removes them from the sample buffer. If there are less than
-/// 'numsample' samples in the buffer, returns all that available.
-///
-/// \return Number of samples returned.
-uint SoundTouch::receiveSamples(SAMPLETYPE *output, uint maxSamples)
-{
- uint ret = FIFOProcessor::receiveSamples(output, maxSamples);
- samplesOutput += (long)ret;
- return ret;
-}
-
-
-/// Adjusts book-keeping so that given number of samples are removed from beginning of the
-/// sample buffer without copying them anywhere.
-///
-/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
-/// with 'ptrBegin' function.
-uint SoundTouch::receiveSamples(uint maxSamples)
-{
- uint ret = FIFOProcessor::receiveSamples(maxSamples);
- samplesOutput += (long)ret;
- return ret;
-}
-
-
-/// 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 deleted file mode 100644 index 0c22cb51cf..0000000000 --- a/Externals/soundtouch/SoundTouch.h +++ /dev/null @@ -1,348 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////
-///
-/// 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
-/// desired tempo/pitch/rate settings with the corresponding functions.
-///
-/// - 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
-/// 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
-/// 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
-/// '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
-/// relationship with the amount of previously input samples.
-///
-/// - The tempo/pitch/rate control parameters can be altered during processing.
-/// Please notice though that they aren't currently protected by semaphores,
-/// so in multi-thread application external semaphore protection may be
-/// 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' (change pitch but maintain tempo) is produced by a combination of
-/// combining the two other controls.
-///
-/// 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 SoundTouch_H
-#define SoundTouch_H
-
-#include "FIFOSamplePipe.h"
-#include "STTypes.h"
-
-namespace soundtouch
-{
-
-/// Soundtouch library version string
-#define SOUNDTOUCH_VERSION "2.3.2"
-
-/// SoundTouch library version id
-#define SOUNDTOUCH_VERSION_ID (20302)
-
-//
-// Available setting IDs for the 'setSetting' & 'get_setting' functions:
-
-/// Enable/disable anti-alias filter in pitch transposer (0 = disable)
-#define SETTING_USE_AA_FILTER 0
-
-/// Pitch transposer anti-alias filter length (8 .. 128 taps, default = 32)
-#define SETTING_AA_FILTER_LENGTH 1
-
-/// Enable/disable quick seeking algorithm in tempo changer routine
-/// (enabling quick seeking lowers CPU utilization but causes a minor sound
-/// 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.
-/// 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.
-/// 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.
-/// See "STTypes.h" or README for more information.
-#define SETTING_OVERLAP_MS 5
-
-
-/// Call "getSetting" with this ID to query processing sequence size in samples.
-/// This value gives approximate value of how many input samples you'll need to
-/// feed into SoundTouch after initial buffering to get out a new batch of
-/// output samples.
-///
-/// This value does not include initial buffering at beginning of a new processing
-/// stream, use SETTING_INITIAL_LATENCY to get the initial buffering size.
-///
-/// Notices:
-/// - This is read-only parameter, i.e. setSetting ignores this parameter
-/// - This parameter value is not constant but change depending on
-/// tempo/pitch/rate/samplerate settings.
-#define SETTING_NOMINAL_INPUT_SEQUENCE 6
-
-
-/// Call "getSetting" with this ID to query nominal average processing output
-/// size in samples. This value tells approcimate value how many output samples
-/// SoundTouch outputs once it does DSP processing run for a batch of input samples.
-///
-/// Notices:
-/// - This is read-only parameter, i.e. setSetting ignores this parameter
-/// - This parameter value is not constant but change depending on
-/// tempo/pitch/rate/samplerate settings.
-#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
-{
-private:
- /// Rate transposer class instance
- class RateTransposer *pRateTransposer;
-
- /// Time-stretch class instance
- class TDStretch *pTDStretch;
-
- /// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters.
- double virtualRate;
-
- /// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters.
- double virtualTempo;
-
- /// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters.
- double virtualPitch;
-
- /// Flag: Has sample rate been set?
- bool bSrateSet;
-
- /// Accumulator for how many samples in total will be expected as output vs. samples put in,
- /// considering current processing settings.
- double samplesExpectedOut;
-
- /// Accumulator for how many samples in total have been read out from the processing so far
- long samplesOutput;
-
- /// Calculates effective rate & tempo valuescfrom 'virtualRate', 'virtualTempo' and
- /// 'virtualPitch' parameters.
- void calcEffectiveRateAndTempo();
-
-protected :
- /// Number of channels
- uint channels;
-
- /// Effective 'rate' value calculated from 'virtualRate', 'virtualTempo' and 'virtualPitch'
- double rate;
-
- /// Effective 'tempo' value calculated from 'virtualRate', 'virtualTempo' and 'virtualPitch'
- double tempo;
-
-public:
- SoundTouch();
- virtual ~SoundTouch() override;
-
- /// Get SoundTouch library version string
- static const char *getVersionString();
-
- /// Get SoundTouch library version Id
- static uint getVersionId();
-
- /// Sets new rate control value. Normal rate = 1.0, smaller values
- /// represent slower rate, larger faster rates.
- void setRate(double newRate);
-
- /// Sets new tempo control value. Normal tempo = 1.0, smaller values
- /// represent slower tempo, larger faster tempo.
- void setTempo(double newTempo);
-
- /// Sets new rate control value as a difference in percents compared
- /// to the original rate (-50 .. +100 %)
- void setRateChange(double newRate);
-
- /// Sets new tempo control value as a difference in percents compared
- /// to the original tempo (-50 .. +100 %)
- void setTempoChange(double newTempo);
-
- /// Sets new pitch control value. Original pitch = 1.0, smaller values
- /// represent lower pitches, larger values higher pitch.
- void setPitch(double newPitch);
-
- /// Sets pitch change in octaves compared to the original pitch
- /// (-1.00 .. +1.00)
- void setPitchOctaves(double newPitch);
-
- /// Sets pitch change in semi-tones compared to the original pitch
- /// (-12 .. +12)
- void setPitchSemiTones(int newPitch);
- void setPitchSemiTones(double newPitch);
-
- /// Sets the number of channels, 1 = mono, 2 = stereo
- void setChannels(uint numChannels);
-
- /// 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.
- //
- /// Note: This function is meant for extracting the last samples of a sound
- /// stream. This function may introduce additional blank samples in the end
- /// of the sound stream, and thus it's not recommended to call this function
- /// in the middle of a sound stream.
- void flush();
-
- /// Adds 'numSamples' pcs of samples from the 'samples' memory position into
- /// the input of the object. Notice that sample rate _has_to_ be set before
- /// calling this function, otherwise throws a runtime_error exception.
- virtual void putSamples(
- const SAMPLETYPE *samples, ///< Pointer to sample buffer.
- 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
- /// '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.
- ///
- /// 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() override;
-
- /// Changes a setting controlling the processing system behaviour. See the
- /// 'SETTING_...' defines for available setting ID's.
- ///
- /// \return 'true' if the setting was successfully changed
- bool setSetting(int settingId, ///< Setting ID number. see SETTING_... defines.
- int value ///< New setting value.
- );
-
- /// Reads a setting controlling the processing system behaviour. See the
- /// 'SETTING_...' defines for available setting ID's.
- ///
- /// \return the setting value.
- int getSetting(int settingId ///< Setting ID number, see SETTING_... defines.
- ) const;
-
- /// 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
- /// function 'receiveSamples()'
- /// - isEmpty() : Returns nonzero if there aren't any 'ready' samples.
- /// - clear() : Clears all samples from ready/processing buffers.
-};
-
-}
-#endif
diff --git a/Externals/soundtouch/SoundTouch.vcxproj b/Externals/soundtouch/SoundTouch.vcxproj deleted file mode 100644 index 6b978e8aec..0000000000 --- a/Externals/soundtouch/SoundTouch.vcxproj +++ /dev/null @@ -1,56 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project> - <Import Project="..\..\Source\VSProps\Base.Macros.props" /> - <Import Project="$(VSPropsDir)Base.Targets.props" /> - <PropertyGroup Label="Globals"> - <ProjectGuid>{EC082900-B4D8-42E9-9663-77F02F6936AE}</ProjectGuid> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> - <Import Project="$(VSPropsDir)Configuration.StaticLibrary.props" /> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> - <ImportGroup Label="ExtensionSettings" /> - <ImportGroup Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - <Import Project="$(VSPropsDir)Base.props" /> - <Import Project="$(VSPropsDir)ClDisableAllWarnings.props" /> - </ImportGroup> - <PropertyGroup Label="UserMacros" /> - <ItemGroup> - <ClCompile Include="AAFilter.cpp" /> - <ClCompile Include="BPMDetect.cpp" /> - <ClCompile Include="cpu_detect_x86.cpp" /> - <ClCompile Include="FIFOSampleBuffer.cpp" /> - <ClCompile Include="FIRFilter.cpp" /> - <ClCompile Include="InterpolateCubic.cpp" /> - <ClCompile Include="InterpolateLinear.cpp" /> - <ClCompile Include="InterpolateShannon.cpp" /> - <ClCompile Include="mmx_optimized.cpp" /> - <ClCompile Include="PeakFinder.cpp" /> - <ClCompile Include="RateTransposer.cpp" /> - <ClCompile Include="SoundTouch.cpp" /> - <ClCompile Include="sse_optimized.cpp" /> - <ClCompile Include="TDStretch.cpp" /> - </ItemGroup> - <ItemGroup> - <ClInclude Include="AAFilter.h" /> - <ClInclude Include="BPMDetect.h" /> - <ClInclude Include="cpu_detect.h" /> - <ClInclude Include="FIFOSampleBuffer.h" /> - <ClInclude Include="FIFOSamplePipe.h" /> - <ClInclude Include="FIRFilter.h" /> - <ClInclude Include="InterpolateCubic.h" /> - <ClInclude Include="InterpolateLinear.h" /> - <ClInclude Include="InterpolateShannon.h" /> - <ClInclude Include="PeakFinder.h" /> - <ClInclude Include="RateTransposer.h" /> - <ClInclude Include="SoundTouch.h" /> - <ClInclude Include="STTypes.h" /> - <ClInclude Include="TDStretch.h" /> - </ItemGroup> - <ItemGroup> - <Text Include="CMakeLists.txt" /> - </ItemGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> - <ImportGroup Label="ExtensionTargets"> - </ImportGroup> -</Project> diff --git a/Externals/soundtouch/TDStretch.cpp b/Externals/soundtouch/TDStretch.cpp deleted file mode 100644 index 157f0e4d6e..0000000000 --- a/Externals/soundtouch/TDStretch.cpp +++ /dev/null @@ -1,1087 +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.
-///
-/// 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
-///
-////////////////////////////////////////////////////////////////////////////////
-//
-// 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 <string.h>
-#include <limits.h>
-#include <assert.h>
-#include <math.h>
-#include <float.h>
-
-#include "STTypes.h"
-#include "cpu_detect.h"
-#include "TDStretch.h"
-
-using namespace soundtouch;
-
-#define max(x, y) (((x) > (y)) ? (x) : (y))
-
-/*****************************************************************************
- *
- * Implementation of the class 'TDStretch'
- *
- *****************************************************************************/
-
-
-TDStretch::TDStretch() : FIFOProcessor(&outputBuffer)
-{
- bQuickSeek = false;
- channels = 2;
-
- pMidBuffer = nullptr;
- pMidBufferUnaligned = nullptr;
- overlapLength = 0;
-
- bAutoSeqSetting = true;
- bAutoSeekSetting = true;
-
- tempo = 1.0f;
- setParameters(44100, DEFAULT_SEQUENCE_MS, DEFAULT_SEEKWINDOW_MS, DEFAULT_OVERLAP_MS);
- setTempo(1.0f);
-
- clear();
-}
-
-
-
-TDStretch::~TDStretch()
-{
- delete[] pMidBufferUnaligned;
-}
-
-
-
-// Sets routine control parameters. These control are certain time constants
-// defining how the sound is stretched to the desired duration.
-//
-// 'sampleRate' = sample rate of the sound
-// 'sequenceMS' = one processing sequence length in milliseconds (default = 82 ms)
-// 'seekwindowMS' = seeking window length for scanning the best overlapping
-// position (default = 28 ms)
-// 'overlapMS' = overlapping length (default = 12 ms)
-
-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)
- {
- if (aSampleRate > 192000) ST_THROW_RT_ERROR("Error: Excessive samplerate");
- this->sampleRate = aSampleRate;
- }
-
- if (aOverlapMS > 0) this->overlapMs = aOverlapMS;
-
- if (aSequenceMS > 0)
- {
- this->sequenceMs = aSequenceMS;
- bAutoSeqSetting = false;
- }
- else if (aSequenceMS == 0)
- {
- // if zero, use automatic setting
- bAutoSeqSetting = true;
- }
-
- if (aSeekWindowMS > 0)
- {
- this->seekWindowMs = aSeekWindowMS;
- bAutoSeekSetting = false;
- }
- else if (aSeekWindowMS == 0)
- {
- // if zero, use automatic setting
- bAutoSeekSetting = true;
- }
-
- calcSeqParameters();
-
- calculateOverlapLength(overlapMs);
-
- // set tempo to recalculate 'sampleReq'
- setTempo(tempo);
-}
-
-
-
-/// Get routine control parameters, see setParameters() function.
-/// 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
-{
- if (pSampleRate)
- {
- *pSampleRate = sampleRate;
- }
-
- if (pSequenceMs)
- {
- *pSequenceMs = (bAutoSeqSetting) ? (USE_AUTO_SEQUENCE_LEN) : sequenceMs;
- }
-
- if (pSeekWindowMs)
- {
- *pSeekWindowMs = (bAutoSeekSetting) ? (USE_AUTO_SEEKWINDOW_LEN) : seekWindowMs;
- }
-
- if (pOverlapMs)
- {
- *pOverlapMs = overlapMs;
- }
-}
-
-
-// Overlaps samples in 'midBuffer' with the samples in 'pInput'
-void TDStretch::overlapMono(SAMPLETYPE *pOutput, const SAMPLETYPE *pInput) const
-{
- int i;
- SAMPLETYPE m1, m2;
-
- m1 = (SAMPLETYPE)0;
- m2 = (SAMPLETYPE)overlapLength;
-
- for (i = 0; i < overlapLength ; i ++)
- {
- pOutput[i] = (pInput[i] * m1 + pMidBuffer[i] * m2 ) / overlapLength;
- m1 += 1;
- m2 -= 1;
- }
-}
-
-
-
-void TDStretch::clearMidBuffer()
-{
- memset(pMidBuffer, 0, channels * sizeof(SAMPLETYPE) * overlapLength);
-}
-
-
-void TDStretch::clearInput()
-{
- inputBuffer.clear();
- clearMidBuffer();
- isBeginning = true;
- maxnorm = 0;
- maxnormf = 1e8;
- skipFract = 0;
-}
-
-
-// Clears the sample buffers
-void TDStretch::clear()
-{
- outputBuffer.clear();
- clearInput();
-}
-
-
-
-// Enables/disables the quick position seeking algorithm. Zero to disable, nonzero
-// to enable
-void TDStretch::enableQuickSeek(bool enable)
-{
- bQuickSeek = enable;
-}
-
-
-// Returns nonzero if the quick seeking algorithm is enabled.
-bool TDStretch::isQuickSeekEnabled() const
-{
- return bQuickSeek;
-}
-
-
-// Seeks for the optimal overlap-mixing position.
-int TDStretch::seekBestOverlapPosition(const SAMPLETYPE *refPos)
-{
- if (bQuickSeek)
- {
- return seekBestOverlapPositionQuick(refPos);
- }
- else
- {
- return seekBestOverlapPositionFull(refPos);
- }
-}
-
-
-// Overlaps samples in 'midBuffer' with the samples in 'pInputBuffer' at position
-// of 'ovlPos'.
-inline void TDStretch::overlap(SAMPLETYPE *pOutput, const SAMPLETYPE *pInput, uint ovlPos) const
-{
-#ifndef USE_MULTICH_ALWAYS
- if (channels == 1)
- {
- // mono sound.
- overlapMono(pOutput, pInput + ovlPos);
- }
- else if (channels == 2)
- {
- // stereo sound
- overlapStereo(pOutput, pInput + 2 * ovlPos);
- }
- else
-#endif // USE_MULTICH_ALWAYS
- {
- assert(channels > 0);
- overlapMulti(pOutput, pInput + channels * ovlPos);
- }
-}
-
-
-// Seeks for the optimal overlap-mixing position. The 'stereo' version of the
-// routine
-//
-// The best position is determined as the position where the two overlapped
-// sample sequences are 'most alike', in terms of the highest cross-correlation
-// value over the overlapping period
-int TDStretch::seekBestOverlapPositionFull(const SAMPLETYPE *refPos)
-{
- int bestOffs;
- double bestCorr;
- int i;
- double norm;
-
- 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 ++)
- {
- double corr;
- // Calculates correlation value for the mixing position corresponding to 'i'
-#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
- // as "calcCrossCorr", but saves time by reusing & updating previously stored
- // "norm" value
- corr = calcCrossCorrAccumulate(refPos + channels * i, pMidBuffer, norm);
-#endif
- // heuristic rule to slightly favour values close to mid of the range
- double tmp = (double)(2 * i - seekLength) / (double)seekLength;
- corr = ((corr + 0.1) * (1.0 - 0.25 * tmp * tmp));
-
- // Checks for the highest correlation value
- if (corr > bestCorr)
- {
- // For optimal performance, enter critical section only in case that best value found.
- // in such case repeat 'if' condition as it's possible that parallel execution may have
- // updated the bestCorr value in the mean time
- #pragma omp critical
- if (corr > bestCorr)
- {
- bestCorr = corr;
- bestOffs = i;
- }
- }
- }
-
-#ifdef SOUNDTOUCH_INTEGER_SAMPLES
- adaptNormalizer();
-#endif
-
- // clear cross correlation routine state if necessary (is so e.g. in MMX routines).
- clearCrossCorrState();
-
- return bestOffs;
-}
-
-
-// Quick seek algorithm for improved runtime-performance: First roughly scans through the
-// correlation area, and then scan surroundings of two best preliminary correlation candidates
-// with improved precision
-//
-// Based on testing:
-// - 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
-int TDStretch::seekBestOverlapPositionQuick(const SAMPLETYPE *refPos)
-{
-#define _MIN(a, b) (((a) < (b)) ? (a) : (b))
-#define SCANSTEP 16
-#define SCANWIND 8
-
- int bestOffs;
- int i;
- int bestOffs2;
- float bestCorr, corr;
- float bestCorr2;
- double norm;
-
- // note: 'float' types used in this function in case that the platform would need to use software-fp
-
- 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
- // increase possibility of ideal match.
- //
- // Begin from "SCANSTEP" instead of SCANWIND to make the calculation
- // catch the 'middlepoint' of seekLength vector as that's the a-priori
- // expected best match position
- //
- // Roughly:
- // - 15% of cases find best result directly on the first round,
- // - 75% cases find better match on 2nd round around the best match from 1st round
- // - 10% cases find better match on 2nd round around the 2nd-best-match from 1st round
- for (i = SCANSTEP; i < seekLength - SCANWIND - 1; i += SCANSTEP)
- {
- // Calculates correlation value for the mixing position corresponding
- // to 'i'
- corr = (float)calcCrossCorr(refPos + channels*i, pMidBuffer, norm);
- // heuristic rule to slightly favour values close to mid of the seek range
- float tmp = (float)(2 * i - seekLength - 1) / (float)seekLength;
- corr = ((corr + 0.1f) * (1.0f - 0.25f * tmp * tmp));
-
- // Checks for the highest correlation value
- if (corr > bestCorr)
- {
- // found new best match. keep the previous best as 2nd best match
- bestCorr2 = bestCorr;
- bestOffs2 = bestOffs;
- bestCorr = corr;
- bestOffs = i;
- }
- else if (corr > bestCorr2)
- {
- // not new best, but still new 2nd best match
- bestCorr2 = corr;
- bestOffs2 = i;
- }
- }
-
- // Scans surroundings of the found best match with small stepping
- int end = _MIN(bestOffs + SCANWIND + 1, seekLength);
- for (i = bestOffs - SCANWIND; i < end; i++)
- {
- if (i == bestOffs) continue; // this offset already calculated, thus skip
-
- // Calculates correlation value for the mixing position corresponding
- // to 'i'
- corr = (float)calcCrossCorr(refPos + channels*i, pMidBuffer, norm);
- // heuristic rule to slightly favour values close to mid of the range
- float tmp = (float)(2 * i - seekLength - 1) / (float)seekLength;
- corr = ((corr + 0.1f) * (1.0f - 0.25f * tmp * tmp));
-
- // Checks for the highest correlation value
- if (corr > bestCorr)
- {
- bestCorr = corr;
- bestOffs = i;
- }
- }
-
- // Scans surroundings of the 2nd best match with small stepping
- end = _MIN(bestOffs2 + SCANWIND + 1, seekLength);
- for (i = bestOffs2 - SCANWIND; i < end; i++)
- {
- if (i == bestOffs2) continue; // this offset already calculated, thus skip
-
- // Calculates correlation value for the mixing position corresponding
- // to 'i'
- corr = (float)calcCrossCorr(refPos + channels*i, pMidBuffer, norm);
- // heuristic rule to slightly favour values close to mid of the range
- float tmp = (float)(2 * i - seekLength - 1) / (float)seekLength;
- corr = ((corr + 0.1f) * (1.0f - 0.25f * tmp * tmp));
-
- // Checks for the highest correlation value
- if (corr > bestCorr)
- {
- bestCorr = corr;
- bestOffs = i;
- }
- }
-
- // clear cross correlation routine state if necessary (is so e.g. in MMX routines).
- clearCrossCorrState();
-
-#ifdef SOUNDTOUCH_INTEGER_SAMPLES
- adaptNormalizer();
-#endif
-
- return bestOffs;
-}
-
-
-
-
-/// For integer algorithm: adapt normalization factor divider with music so that
-/// it'll not be pessimistically restrictive that can degrade quality on quieter sections
-/// yet won't cause integer overflows either
-void TDStretch::adaptNormalizer()
-{
- // Do not adapt normalizer over too silent sequences to avoid averaging filter depleting to
- // too low values during pauses in music
- if ((maxnorm > 1000) || (maxnormf > 40000000))
- {
- //norm averaging filter
- maxnormf = 0.9f * maxnormf + 0.1f * (float)maxnorm;
-
- if ((maxnorm > 800000000) && (overlapDividerBitsNorm < 16))
- {
- // large values, so increase divider
- overlapDividerBitsNorm++;
- if (maxnorm > 1600000000) overlapDividerBitsNorm++; // extra large value => extra increase
- }
- else if ((maxnormf < 1000000) && (overlapDividerBitsNorm > 0))
- {
- // extra small values, decrease divider
- overlapDividerBitsNorm--;
- }
- }
-
- maxnorm = 0;
-}
-
-
-/// clear cross correlation routine state if necessary
-void TDStretch::clearCrossCorrState()
-{
- // default implementation is empty.
-}
-
-
-/// Calculates processing sequence length according to tempo setting
-void TDStretch::calcSeqParameters()
-{
- // Adjust tempo param according to tempo, so that variating processing sequence length is used
- // 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 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 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))
-
- #define CHECK_LIMITS(x, mi, ma) (((x) < (mi)) ? (mi) : (((x) > (ma)) ? (ma) : (x)))
-
- double seq, seek;
-
- if (bAutoSeqSetting)
- {
- seq = AUTOSEQ_C + AUTOSEQ_K * tempo;
- seq = CHECK_LIMITS(seq, AUTOSEQ_AT_MAX, AUTOSEQ_AT_MIN);
- sequenceMs = (int)(seq + 0.5);
- }
-
- if (bAutoSeekSetting)
- {
- seek = AUTOSEEK_C + AUTOSEEK_K * tempo;
- seek = CHECK_LIMITS(seek, AUTOSEEK_AT_MAX, AUTOSEEK_AT_MIN);
- seekWindowMs = (int)(seek + 0.5);
- }
-
- // Update seek window lengths
- seekWindowLength = (sampleRate * sequenceMs) / 1000;
- if (seekWindowLength < 2 * overlapLength)
- {
- seekWindowLength = 2 * overlapLength;
- }
- seekLength = (sampleRate * seekWindowMs) / 1000;
-}
-
-
-
-// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower
-// tempo, larger faster tempo.
-void TDStretch::setTempo(double newTempo)
-{
- int intskip;
-
- tempo = newTempo;
-
- // Calculate new sequence duration
- calcSeqParameters();
-
- // Calculate ideal skip length (according to tempo value)
- nominalSkip = tempo * (seekWindowLength - overlapLength);
- intskip = (int)(nominalSkip + 0.5);
-
- // Calculate how many samples are needed in the 'inputBuffer' to
- // process another batch of samples
- //sampleReq = max(intskip + overlapLength, seekWindowLength) + seekLength / 2;
- sampleReq = max(intskip + overlapLength, seekWindowLength) + seekLength;
-}
-
-
-
-// Sets the number of channels, 1 = mono, 2 = stereo
-void TDStretch::setChannels(int numChannels)
-{
- if (!verifyNumberOfChannels(numChannels) ||
- (channels == numChannels)) return;
-
- channels = numChannels;
- inputBuffer.setChannels(channels);
- outputBuffer.setChannels(channels);
-
- // re-init overlap/buffer
- overlapLength=0;
- setParameters(sampleRate);
-}
-
-
-// nominal tempo, no need for processing, just pass the samples through
-// to outputBuffer
-/*
-void TDStretch::processNominalTempo()
-{
- assert(tempo == 1.0f);
-
- if (bMidBufferDirty)
- {
- // If there are samples in pMidBuffer waiting for overlapping,
- // do a single sliding overlapping with them in order to prevent a
- // clicking distortion in the output sound
- if (inputBuffer.numSamples() < overlapLength)
- {
- // wait until we've got overlapLength input samples
- return;
- }
- // Mix the samples in the beginning of 'inputBuffer' with the
- // samples in 'midBuffer' using sliding overlapping
- overlap(outputBuffer.ptrEnd(overlapLength), inputBuffer.ptrBegin(), 0);
- outputBuffer.putSamples(overlapLength);
- inputBuffer.receiveSamples(overlapLength);
- clearMidBuffer();
- // now we've caught the nominal sample flow and may switch to
- // bypass mode
- }
-
- // Simply bypass samples from input to output
- outputBuffer.moveSamples(inputBuffer);
-}
-*/
-
-
-// Processes as many processing frames of the samples 'inputBuffer', store
-// the result into 'outputBuffer'
-void TDStretch::processSamples()
-{
- int ovlSkip;
- int offset = 0;
- int temp;
-
- /* Removed this small optimization - can introduce a click to sound when tempo setting
- crosses the nominal value
- if (tempo == 1.0f)
- {
- // tempo not changed from the original, so bypass the processing
- processNominalTempo();
- return;
- }
- */
-
- // Process samples as long as there are enough samples in 'inputBuffer'
- // to form a processing frame.
- while ((int)inputBuffer.numSamples() >= sampleReq)
- {
- if (isBeginning == false)
- {
- // apart from the very beginning of the track,
- // scan for the best overlapping position & do overlap-add
- offset = seekBestOverlapPosition(inputBuffer.ptrBegin());
-
- // Mix the samples in the 'inputBuffer' at position of 'offset' with the
- // samples in 'midBuffer' using sliding overlapping
- // ... first partially overlap with the end of the previous sequence
- // (that's in 'midBuffer')
- overlap(outputBuffer.ptrEnd((uint)overlapLength), inputBuffer.ptrBegin(), (uint)offset);
- outputBuffer.putSamples((uint)overlapLength);
- offset += overlapLength;
- }
- else
- {
- // Adjust processing offset at beginning of track by not perform initial overlapping
- // and compensating that in the 'input buffer skip' calculation
- isBeginning = false;
- int skip = (int)(tempo * overlapLength + 0.5 * 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:
-
- // crosscheck that we don't have buffer overflow...
- if ((int)inputBuffer.numSamples() < (offset + seekWindowLength - overlapLength))
- {
- continue; // just in case, shouldn't really happen
- }
-
- // 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) <= (int)inputBuffer.numSamples());
- memcpy(pMidBuffer, inputBuffer.ptrBegin() + channels * (offset + temp),
- channels * sizeof(SAMPLETYPE) * overlapLength);
-
- // Remove the processed samples from the input buffer. Update
- // the difference between integer & nominal skip step to 'skipFract'
- // in order to prevent the error from accumulating over time.
- skipFract += nominalSkip; // real skip size
- ovlSkip = (int)skipFract; // rounded to integer skip
- skipFract -= ovlSkip; // maintain the fraction part, i.e. real vs. integer skip
- inputBuffer.receiveSamples((uint)ovlSkip);
- }
-}
-
-
-// Adds 'numsamples' pcs of samples from the 'samples' memory position into
-// the input of the object.
-void TDStretch::putSamples(const SAMPLETYPE *samples, uint nSamples)
-{
- // Add the samples into the input buffer
- inputBuffer.putSamples(samples, nSamples);
- // Process the samples in input buffer
- processSamples();
-}
-
-
-
-/// Set new overlap length parameter & reallocate RefMidBuffer if necessary.
-void TDStretch::acceptNewOverlapLength(int newOverlapLength)
-{
- int prevOvl;
-
- assert(newOverlapLength >= 0);
- prevOvl = overlapLength;
- overlapLength = newOverlapLength;
-
- if (overlapLength > prevOvl)
- {
- delete[] pMidBufferUnaligned;
-
- pMidBufferUnaligned = new SAMPLETYPE[overlapLength * channels + 16 / sizeof(SAMPLETYPE)];
- // ensure that 'pMidBuffer' is aligned to 16 byte boundary for efficiency
- pMidBuffer = (SAMPLETYPE *)SOUNDTOUCH_ALIGN_POINTER_16(pMidBufferUnaligned);
-
- clearMidBuffer();
- }
-}
-
-
-// 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)
-{
- // 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!");
- return newInstance();
-}
-
-
-TDStretch * TDStretch::newInstance()
-{
- uint uExtensions;
-
- uExtensions = detectCPUextensions();
-
- // Check if MMX/SSE instruction set extensions supported by CPU
-
-#ifdef SOUNDTOUCH_ALLOW_MMX
- // MMX routines available only with integer sample types
- if (uExtensions & SUPPORT_MMX)
- {
- return ::new TDStretchMMX;
- }
- else
-#endif // SOUNDTOUCH_ALLOW_MMX
-
-
-#ifdef SOUNDTOUCH_ALLOW_SSE
- if (uExtensions & SUPPORT_SSE)
- {
- // SSE support
- return ::new TDStretchSSE;
- }
- else
-#endif // SOUNDTOUCH_ALLOW_SSE
-
- {
- // ISA optimizations not supported, use plain C version
- return ::new TDStretch;
- }
-}
-
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// Integer arithmetic specific algorithm implementations.
-//
-//////////////////////////////////////////////////////////////////////////////
-
-#ifdef SOUNDTOUCH_INTEGER_SAMPLES
-
-// Overlaps samples in 'midBuffer' with the samples in 'input'. The 'Stereo'
-// version of the routine.
-void TDStretch::overlapStereo(short *poutput, const short *input) const
-{
- int i;
- short temp;
- int cnt2;
-
- for (i = 0; i < overlapLength ; i ++)
- {
- temp = (short)(overlapLength - i);
- cnt2 = 2 * i;
- poutput[cnt2] = (input[cnt2] * i + pMidBuffer[cnt2] * temp ) / overlapLength;
- poutput[cnt2 + 1] = (input[cnt2 + 1] * i + pMidBuffer[cnt2 + 1] * temp ) / overlapLength;
- }
-}
-
-
-// Overlaps samples in 'midBuffer' with the samples in 'input'. The 'Multi'
-// version of the routine.
-void TDStretch::overlapMulti(short *poutput, const short *input) const
-{
- short m1;
- int i = 0;
-
- 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++;
- }
- }
-}
-
-// Calculates the x having the closest 2^x value for the given value
-static int _getClosest2Power(double value)
-{
- return (int)(log(value) / log(2.0) + 0.5);
-}
-
-
-/// Calculates overlap period length in samples.
-/// Integer version rounds overlap length to closest power of 2
-/// for a divide scaling operation.
-void TDStretch::calculateOverlapLength(int aoverlapMs)
-{
- int newOvl;
-
- assert(aoverlapMs >= 0);
-
- // calculate overlap length so that it's power of 2 - thus it's easy to do
- // integer division by right-shifting. Term "-1" at end is to account for
- // the extra most significatnt bit left unused in result by signed multiplication
- overlapDividerBitsPure = _getClosest2Power((sampleRate * aoverlapMs) / 1000.0) - 1;
- if (overlapDividerBitsPure > 9) overlapDividerBitsPure = 9;
- if (overlapDividerBitsPure < 3) overlapDividerBitsPure = 3;
- newOvl = (int)pow(2.0, (int)overlapDividerBitsPure + 1); // +1 => account for -1 above
-
- acceptNewOverlapLength(newOvl);
-
- overlapDividerBitsNorm = overlapDividerBitsPure;
-
- // calculate sloping divider so that crosscorrelation operation won't
- // overflow 32-bit register. Max. sum of the crosscorrelation sum without
- // divider would be 2^30*(N^3-N)/3, where N = overlap length
- slopingDivider = (newOvl * newOvl - 1) / 3;
-}
-
-
-double TDStretch::calcCrossCorr(const short *mixingPos, const short *compare, double &norm)
-{
- long corr;
- 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 (i = 0; i < ilength; i += 2)
- {
- corr += (mixingPos[i] * compare[i] +
- mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBitsNorm;
- lnorm += (mixingPos[i] * mixingPos[i] +
- mixingPos[i + 1] * mixingPos[i + 1]) >> overlapDividerBitsNorm;
- // do intermediate scalings to avoid integer overflow
- }
-
- if (lnorm > maxnorm)
- {
- // 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
- norm = (double)lnorm;
- return (double)corr / sqrt((norm < 1e-9) ? 1.0 : norm);
-}
-
-
-/// Update cross-correlation by accumulating "norm" coefficient by previously calculated value
-double TDStretch::calcCrossCorrAccumulate(const short *mixingPos, const short *compare, double &norm)
-{
- long corr;
- 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 ++)
- {
- lnorm -= (mixingPos[-i] * mixingPos[-i]) >> overlapDividerBitsNorm;
- }
-
- corr = 0;
- // 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;
- }
-
- // update normalizer with last samples of this round
- for (int j = 0; j < channels; j ++)
- {
- i --;
- lnorm += (mixingPos[i] * mixingPos[i]) >> overlapDividerBitsNorm;
- }
-
- norm += (double)lnorm;
- if (norm > maxnorm)
- {
- maxnorm = (unsigned long)norm;
- }
-
- // Normalize result by dividing by sqrt(norm) - this step is easiest
- // done using floating point operation
- return (double)corr / sqrt((norm < 1e-9) ? 1.0 : norm);
-}
-
-#endif // SOUNDTOUCH_INTEGER_SAMPLES
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// Floating point arithmetic specific algorithm implementations.
-//
-
-#ifdef SOUNDTOUCH_FLOAT_SAMPLES
-
-// Overlaps samples in 'midBuffer' with the samples in 'pInput'
-void TDStretch::overlapStereo(float *pOutput, const float *pInput) const
-{
- int i;
- float fScale;
- float f1;
- float f2;
-
- fScale = 1.0f / (float)overlapLength;
-
- f1 = 0;
- f2 = 1.0f;
-
- for (i = 0; i < 2 * (int)overlapLength ; i += 2)
- {
- pOutput[i + 0] = pInput[i + 0] * f1 + pMidBuffer[i + 0] * f2;
- pOutput[i + 1] = pInput[i + 1] * f1 + pMidBuffer[i + 1] * f2;
-
- f1 += fScale;
- f2 -= fScale;
- }
-}
-
-
-// Overlaps samples in 'midBuffer' with the samples in 'input'.
-void TDStretch::overlapMulti(float *pOutput, const float *pInput) const
-{
- int i;
- float fScale;
- float f1;
- float f2;
-
- fScale = 1.0f / (float)overlapLength;
-
- f1 = 0;
- f2 = 1.0f;
-
- i=0;
- for (int i2 = 0; i2 < overlapLength; i2 ++)
- {
- // note: Could optimize this slightly by taking into account that always channels > 2
- for (int c = 0; c < channels; c ++)
- {
- pOutput[i] = pInput[i] * f1 + pMidBuffer[i] * f2;
- i++;
- }
- f1 += fScale;
- f2 -= fScale;
- }
-}
-
-
-/// Calculates overlapInMsec period length in samples.
-void TDStretch::calculateOverlapLength(int overlapInMsec)
-{
- int newOvl;
-
- assert(overlapInMsec >= 0);
- newOvl = (sampleRate * overlapInMsec) / 1000;
- if (newOvl < 16) newOvl = 16;
-
- // must be divisible by 8
- newOvl -= newOvl % 8;
-
- acceptNewOverlapLength(newOvl);
-}
-
-
-/// Calculate cross-correlation
-double TDStretch::calcCrossCorr(const float *mixingPos, const float *compare, double &anorm)
-{
- 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 (i = 0; i < ilength; i ++)
- {
- corr += mixingPos[i] * compare[i];
- norm += mixingPos[i] * mixingPos[i];
- }
-
- anorm = norm;
- return corr / sqrt((norm < 1e-9 ? 1.0 : norm));
-}
-
-
-/// Update cross-correlation by accumulating "norm" coefficient by previously calculated value
-double TDStretch::calcCrossCorrAccumulate(const float *mixingPos, const float *compare, double &norm)
-{
- float corr;
- int i;
-
- corr = 0;
-
- // cancel first normalizer tap from previous round
- for (i = 1; i <= channels; i ++)
- {
- norm -= mixingPos[-i] * mixingPos[-i];
- }
-
- // 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];
- }
-
- // update normalizer with last samples of this round
- for (int j = 0; j < channels; j ++)
- {
- i --;
- norm += mixingPos[i] * mixingPos[i];
- }
-
- return corr / sqrt((norm < 1e-9 ? 1.0 : norm));
-}
-
-
-#endif // SOUNDTOUCH_FLOAT_SAMPLES
diff --git a/Externals/soundtouch/TDStretch.h b/Externals/soundtouch/TDStretch.h deleted file mode 100644 index 6fddc2379b..0000000000 --- a/Externals/soundtouch/TDStretch.h +++ /dev/null @@ -1,279 +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.
-///
-/// Note : MMX/SSE optimized functions reside in separate, platform-specific files
-/// 'mmx_optimized.cpp' and 'sse_optimized.cpp'
-///
-/// 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 TDStretch_H
-#define TDStretch_H
-
-#include <stddef.h>
-#include "STTypes.h"
-#include "RateTransposer.h"
-#include "FIFOSamplePipe.h"
-
-namespace soundtouch
-{
-
-/// Default values for sound processing parameters:
-/// Notice that the default parameters are tuned for contemporary popular music
-/// processing. For speech processing applications these parameters suit better:
-/// #define DEFAULT_SEQUENCE_MS 40
-/// #define DEFAULT_SEEKWINDOW_MS 15
-/// #define DEFAULT_OVERLAP_MS 8
-///
-
-/// Default length of a single processing sequence, in milliseconds. This determines to how
-/// long sequences the original sound is chopped in the time-stretch algorithm.
-///
-/// The larger this value is, the lesser sequences are used in processing. In principle
-/// a bigger value sounds better when slowing down tempo, but worse when increasing tempo
-/// and vice versa.
-///
-/// Increasing this value reduces computational burden & vice versa.
-//#define DEFAULT_SEQUENCE_MS 40
-#define DEFAULT_SEQUENCE_MS USE_AUTO_SEQUENCE_LEN
-
-/// Giving this value for the sequence length sets automatic parameter value
-/// according to tempo setting (recommended)
-#define USE_AUTO_SEQUENCE_LEN 0
-
-/// Seeking window default 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.
-///
-/// The bigger this window setting is, the higher the possibility to find a better mixing
-/// position will become, but at the same time large values may cause a "drifting" artifact
-/// because consequent sequences will be taken at more uneven intervals.
-///
-/// If there's a disturbing artifact that sounds as if a constant frequency was drifting
-/// around, try reducing this setting.
-///
-/// Increasing this value increases computational burden & vice versa.
-//#define DEFAULT_SEEKWINDOW_MS 15
-#define DEFAULT_SEEKWINDOW_MS USE_AUTO_SEEKWINDOW_LEN
-
-/// Giving this value for the seek window length sets automatic parameter value
-/// according to tempo setting (recommended)
-#define USE_AUTO_SEEKWINDOW_LEN 0
-
-/// 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.
-///
-/// This shouldn't be that critical parameter. If you reduce the DEFAULT_SEQUENCE_MS setting
-/// by a large amount, you might wish to try a smaller value on this.
-///
-/// Increasing this value increases computational burden & vice versa.
-#define DEFAULT_OVERLAP_MS 8
-
-
-/// Class that does the time-stretch (tempo change) effect for the processed
-/// sound.
-class TDStretch : public FIFOProcessor
-{
-protected:
- int channels;
- int sampleReq;
-
- int overlapLength;
- int seekLength;
- int seekWindowLength;
- int overlapDividerBitsNorm;
- int overlapDividerBitsPure;
- int slopingDivider;
- int sampleRate;
- int sequenceMs;
- int seekWindowMs;
- int overlapMs;
-
- unsigned long maxnorm;
- float maxnormf;
-
- double tempo;
- double nominalSkip;
- double skipFract;
-
- bool bQuickSeek;
- bool bAutoSeqSetting;
- bool bAutoSeekSetting;
- bool isBeginning;
-
- SAMPLETYPE *pMidBuffer;
- SAMPLETYPE *pMidBufferUnaligned;
-
- FIFOSampleBuffer outputBuffer;
- FIFOSampleBuffer inputBuffer;
-
- void acceptNewOverlapLength(int newOverlapLength);
-
- virtual void clearCrossCorrState();
- void calculateOverlapLength(int overlapMs);
-
- virtual double calcCrossCorr(const SAMPLETYPE *mixingPos, const SAMPLETYPE *compare, double &norm);
- virtual double calcCrossCorrAccumulate(const SAMPLETYPE *mixingPos, const SAMPLETYPE *compare, double &norm);
-
- virtual int seekBestOverlapPositionFull(const SAMPLETYPE *refPos);
- virtual int seekBestOverlapPositionQuick(const SAMPLETYPE *refPos);
- virtual int seekBestOverlapPosition(const SAMPLETYPE *refPos);
-
- virtual void overlapStereo(SAMPLETYPE *output, const SAMPLETYPE *input) const;
- virtual void overlapMono(SAMPLETYPE *output, const SAMPLETYPE *input) const;
- virtual void overlapMulti(SAMPLETYPE *output, const SAMPLETYPE *input) const;
-
- void clearMidBuffer();
- void overlap(SAMPLETYPE *output, const SAMPLETYPE *input, uint ovlPos) const;
-
- 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
- /// the 'set_returnBuffer_size' function.
- void processSamples();
-
-public:
- 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.
- 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 feature set depending on if the CPU
- /// supports MMX/SSE/etc extensions.
- static TDStretch *newInstance();
-
- /// Returns the output buffer object
- FIFOSamplePipe *getOutput() { return &outputBuffer; };
-
- /// Returns the input buffer object
- FIFOSamplePipe *getInput() { return &inputBuffer; };
-
- /// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower
- /// tempo, larger faster tempo.
- void setTempo(double newTempo);
-
- /// Returns nonzero if there aren't any samples available for outputting.
- virtual void clear() override;
-
- /// Clears the input buffer
- void clearInput();
-
- /// Sets the number of channels, 1 = mono, 2 = stereo
- void setChannels(int numChannels);
-
- /// Enables/disables the quick position seeking algorithm. Zero to disable,
- /// nonzero to enable
- void enableQuickSeek(bool enable);
-
- /// Returns nonzero if the quick seeking algorithm is enabled.
- bool isQuickSeekEnabled() const;
-
- /// Sets routine control parameters. These control are certain time constants
- /// defining how the sound is stretched to the desired duration.
- //
- /// 'sampleRate' = sample rate of the sound
- /// 'sequenceMS' = one processing sequence length in milliseconds
- /// 'seekwindowMS' = seeking window length for scanning the best overlapping
- /// position
- /// 'overlapMS' = overlapping length
- void setParameters(int sampleRate, ///< Samplerate of sound being processed (Hz)
- int sequenceMS = -1, ///< Single processing sequence length (ms)
- int seekwindowMS = -1, ///< Offset seeking window length (ms)
- int overlapMS = -1 ///< Sequence overlapping length (ms)
- );
-
- /// Get routine control parameters, see setParameters() function.
- /// 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;
-
- /// Adds 'numsamples' pcs of samples from the 'samples' memory position into
- /// the input of the object.
- virtual void putSamples(
- 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
- {
- return (int)(nominalSkip + 0.5);
- }
-
- /// return nominal output sample amount when running a processing batch
- int getOutputBatchSize() const
- {
- return seekWindowLength - overlapLength;
- }
-
- /// return approximate initial input-output latency
- int getLatency() const
- {
- return sampleReq;
- }
-};
-
-
-// Implementation-specific class declarations:
-
-#ifdef SOUNDTOUCH_ALLOW_MMX
- /// Class that implements MMX optimized routines for 16bit integer samples type.
- class TDStretchMMX : public TDStretch
- {
- protected:
- 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
-
-
-#ifdef SOUNDTOUCH_ALLOW_SSE
- /// Class that implements SSE optimized routines for floating point samples type.
- class TDStretchSSE : public TDStretch
- {
- protected:
- 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
-
-}
-#endif /// TDStretch_H
diff --git a/Externals/soundtouch/cpu_detect.h b/Externals/soundtouch/cpu_detect.h deleted file mode 100644 index 145712225f..0000000000 --- a/Externals/soundtouch/cpu_detect.h +++ /dev/null @@ -1,57 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////
-///
-/// A header file for detecting the Intel MMX instructions set extension.
-///
-/// Please see 'mmx_win.cpp', 'mmx_cpp.cpp' and 'mmx_non_x86.cpp' for the
-/// routine implementations for x86 Windows, x86 gnu version and non-x86
-/// platforms, respectively.
-///
-/// 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 _CPU_DETECT_H_
-#define _CPU_DETECT_H_
-
-#include "STTypes.h"
-
-#define SUPPORT_MMX 0x0001
-#define SUPPORT_3DNOW 0x0002
-#define SUPPORT_ALTIVEC 0x0004
-#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.
-uint detectCPUextensions(void);
-
-/// Disables given set of instruction extensions. See SUPPORT_... defines.
-void disableExtensions(uint wDisableMask);
-
-#endif // _CPU_DETECT_H_
diff --git a/Externals/soundtouch/cpu_detect_x86.cpp b/Externals/soundtouch/cpu_detect_x86.cpp deleted file mode 100644 index b1286106eb..0000000000 --- a/Externals/soundtouch/cpu_detect_x86.cpp +++ /dev/null @@ -1,130 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////
-///
-/// Generic version of the x86 CPU extension detection routine.
-///
-/// This file is for GNU & other non-Windows compilers, see 'cpu_detect_x86_win.cpp'
-/// for the Microsoft compiler version.
-///
-/// 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 "cpu_detect.h"
-#include "STTypes.h"
-
-
-#if defined(SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS)
-
- #if defined(__GNUC__) && defined(__i386__)
- // gcc
- #include "cpuid.h"
- #elif defined(_M_IX86)
- // windows non-gcc
- #include <intrin.h>
- #endif
-
- #define bit_MMX (1 << 23)
- #define bit_SSE (1 << 25)
- #define bit_SSE2 (1 << 26)
-#endif
-
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// processor instructions extension detection routines
-//
-//////////////////////////////////////////////////////////////////////////////
-
-// Flag variable indicating whick ISA extensions are disabled (for debugging)
-static uint _dwDisabledISA = 0x00; // 0xffffffff; //<- use this to disable all extensions
-
-// Disables given set of instruction extensions. See SUPPORT_... defines.
-void disableExtensions(uint dwDisableMask)
-{
- _dwDisabledISA = dwDisableMask;
-}
-
-
-/// Checks which instruction set extensions are supported by the CPU.
-uint detectCPUextensions(void)
-{
-/// If building for a 64bit system (no Itanium) and the user wants optimizations.
-/// Return the OR of SUPPORT_{MMX,SSE,SSE2}. 11001 or 0x19.
-/// Keep the _dwDisabledISA test (2 more operations, could be eliminated).
-#if ((defined(__GNUC__) && defined(__x86_64__)) \
- || defined(_M_X64)) \
- && defined(SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS)
- return 0x19 & ~_dwDisabledISA;
-
-/// If building for a 32bit system and the user wants optimizations.
-/// Keep the _dwDisabledISA test (2 more operations, could be eliminated).
-#elif ((defined(__GNUC__) && defined(__i386__)) \
- || defined(_M_IX86)) \
- && defined(SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS)
-
- if (_dwDisabledISA == 0xffffffff) return 0;
-
- uint res = 0;
-
-#if defined(__GNUC__)
- // GCC version of cpuid. Requires GCC 4.3.0 or later for __cpuid intrinsic support.
- uint eax, ebx, ecx, edx; // unsigned int is the standard type. uint is defined by the compiler and not guaranteed to be portable.
-
- // Check if no cpuid support.
- if (!__get_cpuid (1, &eax, &ebx, &ecx, &edx)) return 0; // always disable extensions.
-
- if (edx & bit_MMX) res = res | SUPPORT_MMX;
- if (edx & bit_SSE) res = res | SUPPORT_SSE;
- if (edx & bit_SSE2) res = res | SUPPORT_SSE2;
-
-#else
- // Window / VS version of cpuid. Notice that Visual Studio 2005 or later required
- // for __cpuid intrinsic support.
- int reg[4] = {-1};
-
- // Check if no cpuid support.
- __cpuid(reg,0);
- if ((unsigned int)reg[0] == 0) return 0; // always disable extensions.
-
- __cpuid(reg,1);
- if ((unsigned int)reg[3] & bit_MMX) res = res | SUPPORT_MMX;
- if ((unsigned int)reg[3] & bit_SSE) res = res | SUPPORT_SSE;
- if ((unsigned int)reg[3] & bit_SSE2) res = res | SUPPORT_SSE2;
-
-#endif
-
- return res & ~_dwDisabledISA;
-
-#else
-
-/// One of these is true:
-/// 1) We don't want optimizations.
-/// 2) Using an unsupported compiler.
-/// 3) Running on a non-x86 platform.
- return 0;
-
-#endif
-}
diff --git a/Externals/soundtouch/exports.props b/Externals/soundtouch/exports.props deleted file mode 100644 index 7e7c34d735..0000000000 --- a/Externals/soundtouch/exports.props +++ /dev/null @@ -1,13 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project> - <ItemDefinitionGroup> - <ClCompile> - <AdditionalIncludeDirectories>$(ExternalsDir)soundtouch;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - </ClCompile> - </ItemDefinitionGroup> - <ItemGroup> - <ProjectReference Include="$(ExternalsDir)soundtouch\SoundTouch.vcxproj"> - <Project>{ec082900-b4d8-42e9-9663-77f02f6936ae}</Project> - </ProjectReference> - </ItemGroup> -</Project> diff --git a/Externals/soundtouch/mmx_optimized.cpp b/Externals/soundtouch/mmx_optimized.cpp deleted file mode 100644 index 0a2949cd35..0000000000 --- a/Externals/soundtouch/mmx_optimized.cpp +++ /dev/null @@ -1,396 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////
-///
-/// MMX optimized routines. All MMX optimized functions have been gathered into
-/// this single source code file, regardless to their class or original source
-/// code file, in order to ease porting the library to other compiler and
-/// processor platforms.
-///
-/// The MMX-optimizations are programmed using MMX compiler intrinsics that
-/// are supported both by Microsoft Visual C++ and GCC compilers, so this file
-/// should compile with both toolsets.
-///
-/// NOTICE: If using Visual Studio 6.0, you'll need to install the "Visual C++
-/// 6.0 processor pack" update to support compiler intrinsic syntax. The update
-/// is available for download at Microsoft Developers Network, see here:
-/// http://msdn.microsoft.com/en-us/vstudio/aa718349.aspx
-///
-/// 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 "STTypes.h"
-
-#ifdef SOUNDTOUCH_ALLOW_MMX
-// MMX routines available only with integer sample type
-
-using namespace soundtouch;
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// implementation of MMX optimized functions of class 'TDStretchMMX'
-//
-//////////////////////////////////////////////////////////////////////////////
-
-#include "TDStretch.h"
-#include <mmintrin.h>
-#include <limits.h>
-#include <math.h>
-
-
-// Calculates cross correlation of two buffers
-double TDStretchMMX::calcCrossCorr(const short *pV1, const short *pV2, double &dnorm)
-{
- const __m64 *pVec1, *pVec2;
- __m64 shifter;
- __m64 accu, normaccu;
- long corr, norm;
- int i;
-
- pVec1 = (__m64*)pV1;
- pVec2 = (__m64*)pV2;
-
- shifter = _m_from_int(overlapDividerBitsNorm);
- normaccu = accu = _mm_setzero_si64();
-
- // Process 4 parallel sets of 2 * stereo samples or 4 * mono samples
- // during each round for improved CPU-level parallellization.
- for (i = 0; i < channels * overlapLength / 16; i ++)
- {
- __m64 temp, temp2;
-
- // dictionary of instructions:
- // _m_pmaddwd : 4*16bit multiply-add, resulting two 32bits = [a0*b0+a1*b1 ; a2*b2+a3*b3]
- // _mm_add_pi32 : 2*32bit add
- // _m_psrad : 32bit right-shift
-
- temp = _mm_add_pi32(_mm_sra_pi32(_mm_madd_pi16(pVec1[0], pVec2[0]), shifter),
- _mm_sra_pi32(_mm_madd_pi16(pVec1[1], pVec2[1]), shifter));
- temp2 = _mm_add_pi32(_mm_sra_pi32(_mm_madd_pi16(pVec1[0], pVec1[0]), shifter),
- _mm_sra_pi32(_mm_madd_pi16(pVec1[1], pVec1[1]), shifter));
- accu = _mm_add_pi32(accu, temp);
- normaccu = _mm_add_pi32(normaccu, temp2);
-
- temp = _mm_add_pi32(_mm_sra_pi32(_mm_madd_pi16(pVec1[2], pVec2[2]), shifter),
- _mm_sra_pi32(_mm_madd_pi16(pVec1[3], pVec2[3]), shifter));
- temp2 = _mm_add_pi32(_mm_sra_pi32(_mm_madd_pi16(pVec1[2], pVec1[2]), shifter),
- _mm_sra_pi32(_mm_madd_pi16(pVec1[3], pVec1[3]), shifter));
- accu = _mm_add_pi32(accu, temp);
- normaccu = _mm_add_pi32(normaccu, temp2);
-
- pVec1 += 4;
- pVec2 += 4;
- }
-
- // copy hi-dword of mm0 to lo-dword of mm1, then sum mmo+mm1
- // and finally store the result into the variable "corr"
-
- accu = _mm_add_pi32(accu, _mm_srli_si64(accu, 32));
- corr = _m_to_int(accu);
-
- normaccu = _mm_add_pi32(normaccu, _mm_srli_si64(normaccu, 32));
- norm = _m_to_int(normaccu);
-
- // Clear MMS state
- _m_empty();
-
- if (norm > (long)maxnorm)
- {
- // 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
- // done using floating point operation
- dnorm = (double)norm;
-
- return (double)corr / sqrt(dnorm < 1e-9 ? 1.0 : dnorm);
- // Note: Warning about the missing EMMS instruction is harmless
- // as it'll be called elsewhere.
-}
-
-
-/// Update cross-correlation by accumulating "norm" coefficient by previously calculated value
-double TDStretchMMX::calcCrossCorrAccumulate(const short *pV1, const short *pV2, double &dnorm)
-{
- const __m64 *pVec1, *pVec2;
- __m64 shifter;
- __m64 accu;
- long corr, lnorm;
- int i;
-
- // cancel first normalizer tap from previous round
- lnorm = 0;
- for (i = 1; i <= channels; i ++)
- {
- lnorm -= (pV1[-i] * pV1[-i]) >> overlapDividerBitsNorm;
- }
-
- pVec1 = (__m64*)pV1;
- pVec2 = (__m64*)pV2;
-
- shifter = _m_from_int(overlapDividerBitsNorm);
- accu = _mm_setzero_si64();
-
- // Process 4 parallel sets of 2 * stereo samples or 4 * mono samples
- // during each round for improved CPU-level parallellization.
- for (i = 0; i < channels * overlapLength / 16; i ++)
- {
- __m64 temp;
-
- // dictionary of instructions:
- // _m_pmaddwd : 4*16bit multiply-add, resulting two 32bits = [a0*b0+a1*b1 ; a2*b2+a3*b3]
- // _mm_add_pi32 : 2*32bit add
- // _m_psrad : 32bit right-shift
-
- temp = _mm_add_pi32(_mm_sra_pi32(_mm_madd_pi16(pVec1[0], pVec2[0]), shifter),
- _mm_sra_pi32(_mm_madd_pi16(pVec1[1], pVec2[1]), shifter));
- accu = _mm_add_pi32(accu, temp);
-
- temp = _mm_add_pi32(_mm_sra_pi32(_mm_madd_pi16(pVec1[2], pVec2[2]), shifter),
- _mm_sra_pi32(_mm_madd_pi16(pVec1[3], pVec2[3]), shifter));
- accu = _mm_add_pi32(accu, temp);
-
- pVec1 += 4;
- pVec2 += 4;
- }
-
- // copy hi-dword of mm0 to lo-dword of mm1, then sum mmo+mm1
- // and finally store the result into the variable "corr"
-
- accu = _mm_add_pi32(accu, _mm_srli_si64(accu, 32));
- corr = _m_to_int(accu);
-
- // Clear MMS state
- _m_empty();
-
- // update normalizer with last samples of this round
- pV1 = (short *)pVec1;
- for (int j = 1; j <= channels; j ++)
- {
- lnorm += (pV1[-j] * pV1[-j]) >> overlapDividerBitsNorm;
- }
- dnorm += (double)lnorm;
-
- if (lnorm > (long)maxnorm)
- {
- maxnorm = lnorm;
- }
-
- // Normalize result by dividing by sqrt(norm) - this step is easiest
- // done using floating point operation
- return (double)corr / sqrt((dnorm < 1e-9) ? 1.0 : dnorm);
-}
-
-
-void TDStretchMMX::clearCrossCorrState()
-{
- // Clear MMS state
- _m_empty();
- //_asm EMMS;
-}
-
-
-// MMX-optimized version of the function overlapStereo
-void TDStretchMMX::overlapStereo(short *output, const short *input) const
-{
- const __m64 *pVinput, *pVMidBuf;
- __m64 *pVdest;
- __m64 mix1, mix2, adder, shifter;
- int i;
-
- pVinput = (const __m64*)input;
- pVMidBuf = (const __m64*)pMidBuffer;
- pVdest = (__m64*)output;
-
- // mix1 = mixer values for 1st stereo sample
- // mix1 = mixer values for 2nd stereo sample
- // adder = adder for updating mixer values after each round
-
- mix1 = _mm_set_pi16(0, overlapLength, 0, overlapLength);
- adder = _mm_set_pi16(1, -1, 1, -1);
- mix2 = _mm_add_pi16(mix1, adder);
- adder = _mm_add_pi16(adder, adder);
-
- // Overlaplength-division by shifter. "+1" is to account for "-1" deduced in
- // overlapDividerBits calculation earlier.
- shifter = _m_from_int(overlapDividerBitsPure + 1);
-
- for (i = 0; i < overlapLength / 4; i ++)
- {
- __m64 temp1, temp2;
-
- // load & shuffle data so that input & mixbuffer data samples are paired
- temp1 = _mm_unpacklo_pi16(pVMidBuf[0], pVinput[0]); // = i0l m0l i0r m0r
- temp2 = _mm_unpackhi_pi16(pVMidBuf[0], pVinput[0]); // = i1l m1l i1r m1r
-
- // temp = (temp .* mix) >> shifter
- temp1 = _mm_sra_pi32(_mm_madd_pi16(temp1, mix1), shifter);
- temp2 = _mm_sra_pi32(_mm_madd_pi16(temp2, mix2), shifter);
- pVdest[0] = _mm_packs_pi32(temp1, temp2); // pack 2*2*32bit => 4*16bit
-
- // update mix += adder
- mix1 = _mm_add_pi16(mix1, adder);
- mix2 = _mm_add_pi16(mix2, adder);
-
- // --- second round begins here ---
-
- // load & shuffle data so that input & mixbuffer data samples are paired
- temp1 = _mm_unpacklo_pi16(pVMidBuf[1], pVinput[1]); // = i2l m2l i2r m2r
- temp2 = _mm_unpackhi_pi16(pVMidBuf[1], pVinput[1]); // = i3l m3l i3r m3r
-
- // temp = (temp .* mix) >> shifter
- temp1 = _mm_sra_pi32(_mm_madd_pi16(temp1, mix1), shifter);
- temp2 = _mm_sra_pi32(_mm_madd_pi16(temp2, mix2), shifter);
- pVdest[1] = _mm_packs_pi32(temp1, temp2); // pack 2*2*32bit => 4*16bit
-
- // update mix += adder
- mix1 = _mm_add_pi16(mix1, adder);
- mix2 = _mm_add_pi16(mix2, adder);
-
- pVinput += 2;
- pVMidBuf += 2;
- pVdest += 2;
- }
-
- _m_empty(); // clear MMS state
-}
-
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// implementation of MMX optimized functions of class 'FIRFilter'
-//
-//////////////////////////////////////////////////////////////////////////////
-
-#include "FIRFilter.h"
-
-
-FIRFilterMMX::FIRFilterMMX() : FIRFilter()
-{
- filterCoeffsAlign = nullptr;
- filterCoeffsUnalign = nullptr;
-}
-
-
-FIRFilterMMX::~FIRFilterMMX()
-{
- delete[] filterCoeffsUnalign;
-}
-
-
-// (overloaded) Calculates filter coefficients for MMX routine
-void FIRFilterMMX::setCoefficients(const short *coeffs, uint newLength, uint uResultDivFactor)
-{
- uint i;
- FIRFilter::setCoefficients(coeffs, newLength, uResultDivFactor);
-
- // Ensure that filter coeffs array is aligned to 16-byte boundary
- delete[] filterCoeffsUnalign;
- filterCoeffsUnalign = new short[2 * newLength + 8];
- filterCoeffsAlign = (short *)SOUNDTOUCH_ALIGN_POINTER_16(filterCoeffsUnalign);
-
- // rearrange the filter coefficients for mmx routines
- for (i = 0;i < length; i += 4)
- {
- filterCoeffsAlign[2 * i + 0] = coeffs[i + 0];
- filterCoeffsAlign[2 * i + 1] = coeffs[i + 2];
- filterCoeffsAlign[2 * i + 2] = coeffs[i + 0];
- filterCoeffsAlign[2 * i + 3] = coeffs[i + 2];
-
- filterCoeffsAlign[2 * i + 4] = coeffs[i + 1];
- filterCoeffsAlign[2 * i + 5] = coeffs[i + 3];
- filterCoeffsAlign[2 * i + 6] = coeffs[i + 1];
- filterCoeffsAlign[2 * i + 7] = coeffs[i + 3];
- }
-}
-
-
-// mmx-optimized version of the filter routine for stereo sound
-uint FIRFilterMMX::evaluateFilterStereo(short *dest, const short *src, uint numSamples) const
-{
- // Create stack copies of the needed member variables for asm routines :
- uint i, j;
- __m64 *pVdest = (__m64*)dest;
-
- if (length < 2) return 0;
-
- for (i = 0; i < (numSamples - length) / 2; i ++)
- {
- __m64 accu1;
- __m64 accu2;
- const __m64 *pVsrc = (const __m64*)src;
- const __m64 *pVfilter = (const __m64*)filterCoeffsAlign;
-
- accu1 = accu2 = _mm_setzero_si64();
- for (j = 0; j < lengthDiv8 * 2; j ++)
- {
- __m64 temp1, temp2;
-
- temp1 = _mm_unpacklo_pi16(pVsrc[0], pVsrc[1]); // = l2 l0 r2 r0
- temp2 = _mm_unpackhi_pi16(pVsrc[0], pVsrc[1]); // = l3 l1 r3 r1
-
- accu1 = _mm_add_pi32(accu1, _mm_madd_pi16(temp1, pVfilter[0])); // += l2*f2+l0*f0 r2*f2+r0*f0
- accu1 = _mm_add_pi32(accu1, _mm_madd_pi16(temp2, pVfilter[1])); // += l3*f3+l1*f1 r3*f3+r1*f1
-
- temp1 = _mm_unpacklo_pi16(pVsrc[1], pVsrc[2]); // = l4 l2 r4 r2
-
- accu2 = _mm_add_pi32(accu2, _mm_madd_pi16(temp2, pVfilter[0])); // += l3*f2+l1*f0 r3*f2+r1*f0
- accu2 = _mm_add_pi32(accu2, _mm_madd_pi16(temp1, pVfilter[1])); // += l4*f3+l2*f1 r4*f3+r2*f1
-
- // accu1 += l2*f2+l0*f0 r2*f2+r0*f0
- // += l3*f3+l1*f1 r3*f3+r1*f1
-
- // accu2 += l3*f2+l1*f0 r3*f2+r1*f0
- // l4*f3+l2*f1 r4*f3+r2*f1
-
- pVfilter += 2;
- pVsrc += 2;
- }
- // accu >>= resultDivFactor
- accu1 = _mm_srai_pi32(accu1, resultDivFactor);
- accu2 = _mm_srai_pi32(accu2, resultDivFactor);
-
- // pack 2*2*32bits => 4*16 bits
- pVdest[0] = _mm_packs_pi32(accu1, accu2);
- src += 4;
- pVdest ++;
- }
-
- _m_empty(); // clear emms state
-
- 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 deleted file mode 100644 index ebcc441db1..0000000000 --- a/Externals/soundtouch/sse_optimized.cpp +++ /dev/null @@ -1,365 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////
-///
-/// SSE optimized routines for Pentium-III, Athlon-XP and later CPUs. All SSE
-/// optimized functions have been gathered into this single source
-/// code file, regardless to their class or original source code file, in order
-/// to ease porting the library to other compiler and processor platforms.
-///
-/// The SSE-optimizations are programmed using SSE compiler intrinsics that
-/// are supported both by Microsoft Visual C++ and GCC compilers, so this file
-/// should compile with both toolsets.
-///
-/// NOTICE: If using Visual Studio 6.0, you'll need to install the "Visual C++
-/// 6.0 processor pack" update to support SSE instruction set. The update is
-/// available for download at Microsoft Developers Network, see here:
-/// http://msdn.microsoft.com/en-us/vstudio/aa718349.aspx
-///
-/// If the above URL is expired or removed, go to "http://msdn.microsoft.com" and
-/// perform a search with keywords "processor pack".
-///
-/// 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 "cpu_detect.h"
-#include "STTypes.h"
-
-using namespace soundtouch;
-
-#ifdef SOUNDTOUCH_ALLOW_SSE
-
-// SSE routines available only with float sample type
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// implementation of SSE optimized functions of class 'TDStretchSSE'
-//
-//////////////////////////////////////////////////////////////////////////////
-
-#include "TDStretch.h"
-#include <xmmintrin.h>
-#include <math.h>
-
-// Calculates cross correlation of two buffers
-double TDStretchSSE::calcCrossCorr(const float *pV1, const float *pV2, double &anorm)
-{
- int i;
- const float *pVec1;
- const __m128 *pVec2;
- __m128 vSum, vNorm;
-
- // Note. It means a major slow-down if the routine needs to tolerate
- // unaligned __m128 memory accesses. It's way faster if we can skip
- // unaligned slots and use _mm_load_ps instruction instead of _mm_loadu_ps.
- // This can mean up to ~ 10-fold difference (incl. part of which is
- // due to skipping every second round for stereo sound though).
- //
- // Compile-time define SOUNDTOUCH_ALLOW_NONEXACT_SIMD_OPTIMIZATION is provided
- // for choosing if this little cheating is allowed.
-
-#ifdef ST_SIMD_AVOID_UNALIGNED
- // Little cheating allowed, return valid correlation only for
- // aligned locations, meaning every second round for stereo sound.
-
- #define _MM_LOAD _mm_load_ps
-
- if (((ulongptr)pV1) & 15) return -1e50; // skip unaligned locations
-
-#else
- // No cheating allowed, use unaligned load & take the resulting
- // performance hit.
- #define _MM_LOAD _mm_loadu_ps
-#endif
-
- // ensure overlapLength is divisible by 8
- assert((overlapLength % 8) == 0);
-
- // Calculates the cross-correlation value between 'pV1' and 'pV2' vectors
- // Note: pV2 _must_ be aligned to 16-bit boundary, pV1 need not.
- pVec1 = (const float*)pV1;
- pVec2 = (const __m128*)pV2;
- vSum = vNorm = _mm_setzero_ps();
-
- // Unroll the loop by factor of 4 * 4 operations. Use same routine for
- // stereo & mono, for mono it just means twice the amount of unrolling.
- for (i = 0; i < channels * overlapLength / 16; i ++)
- {
- __m128 vTemp;
- // vSum += pV1[0..3] * pV2[0..3]
- vTemp = _MM_LOAD(pVec1);
- vSum = _mm_add_ps(vSum, _mm_mul_ps(vTemp ,pVec2[0]));
- vNorm = _mm_add_ps(vNorm, _mm_mul_ps(vTemp ,vTemp));
-
- // vSum += pV1[4..7] * pV2[4..7]
- vTemp = _MM_LOAD(pVec1 + 4);
- vSum = _mm_add_ps(vSum, _mm_mul_ps(vTemp, pVec2[1]));
- vNorm = _mm_add_ps(vNorm, _mm_mul_ps(vTemp ,vTemp));
-
- // vSum += pV1[8..11] * pV2[8..11]
- vTemp = _MM_LOAD(pVec1 + 8);
- vSum = _mm_add_ps(vSum, _mm_mul_ps(vTemp, pVec2[2]));
- vNorm = _mm_add_ps(vNorm, _mm_mul_ps(vTemp ,vTemp));
-
- // vSum += pV1[12..15] * pV2[12..15]
- vTemp = _MM_LOAD(pVec1 + 12);
- vSum = _mm_add_ps(vSum, _mm_mul_ps(vTemp, pVec2[3]));
- vNorm = _mm_add_ps(vNorm, _mm_mul_ps(vTemp ,vTemp));
-
- pVec1 += 16;
- pVec2 += 4;
- }
-
- // return value = vSum[0] + vSum[1] + vSum[2] + vSum[3]
- float *pvNorm = (float*)&vNorm;
- float norm = (pvNorm[0] + pvNorm[1] + pvNorm[2] + pvNorm[3]);
- anorm = norm;
-
- float *pvSum = (float*)&vSum;
- return (double)(pvSum[0] + pvSum[1] + pvSum[2] + pvSum[3]) / sqrt(norm < 1e-9 ? 1.0 : norm);
-
- /* This is approximately corresponding routine in C-language yet without normalization:
- double corr, norm;
- uint i;
-
- // Calculates the cross-correlation value between 'pV1' and 'pV2' vectors
- corr = norm = 0.0;
- for (i = 0; i < channels * overlapLength / 16; i ++)
- {
- corr += pV1[0] * pV2[0] +
- pV1[1] * pV2[1] +
- pV1[2] * pV2[2] +
- pV1[3] * pV2[3] +
- pV1[4] * pV2[4] +
- pV1[5] * pV2[5] +
- pV1[6] * pV2[6] +
- pV1[7] * pV2[7] +
- pV1[8] * pV2[8] +
- pV1[9] * pV2[9] +
- pV1[10] * pV2[10] +
- pV1[11] * pV2[11] +
- pV1[12] * pV2[12] +
- pV1[13] * pV2[13] +
- pV1[14] * pV2[14] +
- pV1[15] * pV2[15];
-
- for (j = 0; j < 15; j ++) norm += pV1[j] * pV1[j];
-
- pV1 += 16;
- pV2 += 16;
- }
- return corr / sqrt(norm);
- */
-}
-
-
-
-double TDStretchSSE::calcCrossCorrAccumulate(const float *pV1, const float *pV2, double &norm)
-{
- // call usual calcCrossCorr function because SSE does not show big benefit of
- // accumulating "norm" value, and also the "norm" rolling algorithm would get
- // complicated due to SSE-specific alignment-vs-nonexact correlation rules.
- return calcCrossCorr(pV1, pV2, norm);
-}
-
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// implementation of SSE optimized functions of class 'FIRFilter'
-//
-//////////////////////////////////////////////////////////////////////////////
-
-#include "FIRFilter.h"
-
-FIRFilterSSE::FIRFilterSSE() : FIRFilter()
-{
- filterCoeffsAlign = nullptr;
- filterCoeffsUnalign = nullptr;
-}
-
-
-FIRFilterSSE::~FIRFilterSSE()
-{
- delete[] filterCoeffsUnalign;
- filterCoeffsAlign = nullptr;
- filterCoeffsUnalign = nullptr;
-}
-
-
-// (overloaded) Calculates filter coefficients for SSE routine
-void FIRFilterSSE::setCoefficients(const float *coeffs, uint newLength, uint uResultDivFactor)
-{
- uint i;
- float fDivider;
-
- FIRFilter::setCoefficients(coeffs, newLength, uResultDivFactor);
-
- // Scale the filter coefficients so that it won't be necessary to scale the filtering result
- // also rearrange coefficients suitably for SSE
- // Ensure that filter coeffs array is aligned to 16-byte boundary
- delete[] filterCoeffsUnalign;
- filterCoeffsUnalign = new float[2 * newLength + 4];
- filterCoeffsAlign = (float *)SOUNDTOUCH_ALIGN_POINTER_16(filterCoeffsUnalign);
-
- fDivider = (float)resultDivider;
-
- // rearrange the filter coefficients for mmx routines
- for (i = 0; i < newLength; i ++)
- {
- filterCoeffsAlign[2 * i + 0] =
- filterCoeffsAlign[2 * i + 1] = coeffs[i + 0] / fDivider;
- }
-}
-
-
-
-// SSE-optimized version of the filter routine for stereo sound
-uint FIRFilterSSE::evaluateFilterStereo(float *dest, const float *source, uint numSamples) const
-{
- int count = (int)((numSamples - length) & (uint)-2);
- int j;
-
- assert(count % 2 == 0);
-
- if (count < 2) return 0;
-
- assert(source != nullptr);
- assert(dest != nullptr);
- assert((length % 8) == 0);
- assert(filterCoeffsAlign != nullptr);
- assert(((ulongptr)filterCoeffsAlign) % 16 == 0);
-
- // filter is evaluated for two stereo samples with each iteration, thus use of 'j += 2'
- #pragma omp parallel for
- for (j = 0; j < count; j += 2)
- {
- const float *pSrc;
- float *pDest;
- const __m128 *pFil;
- __m128 sum1, sum2;
- uint i;
-
- pSrc = (const float*)source + j * 2; // source audio data
- pDest = dest + j * 2; // destination audio data
- pFil = (const __m128*)filterCoeffsAlign; // filter coefficients. NOTE: Assumes coefficients
- // are aligned to 16-byte boundary
- sum1 = sum2 = _mm_setzero_ps();
-
- for (i = 0; i < length / 8; i ++)
- {
- // Unroll loop for efficiency & calculate filter for 2*2 stereo samples
- // at each pass
-
- // sum1 is accu for 2*2 filtered stereo sound data at the primary sound data offset
- // sum2 is accu for 2*2 filtered stereo sound data for the next sound sample offset.
-
- sum1 = _mm_add_ps(sum1, _mm_mul_ps(_mm_loadu_ps(pSrc) , pFil[0]));
- sum2 = _mm_add_ps(sum2, _mm_mul_ps(_mm_loadu_ps(pSrc + 2), pFil[0]));
-
- sum1 = _mm_add_ps(sum1, _mm_mul_ps(_mm_loadu_ps(pSrc + 4), pFil[1]));
- sum2 = _mm_add_ps(sum2, _mm_mul_ps(_mm_loadu_ps(pSrc + 6), pFil[1]));
-
- sum1 = _mm_add_ps(sum1, _mm_mul_ps(_mm_loadu_ps(pSrc + 8) , pFil[2]));
- sum2 = _mm_add_ps(sum2, _mm_mul_ps(_mm_loadu_ps(pSrc + 10), pFil[2]));
-
- sum1 = _mm_add_ps(sum1, _mm_mul_ps(_mm_loadu_ps(pSrc + 12), pFil[3]));
- sum2 = _mm_add_ps(sum2, _mm_mul_ps(_mm_loadu_ps(pSrc + 14), pFil[3]));
-
- pSrc += 16;
- pFil += 4;
- }
-
- // Now sum1 and sum2 both have a filtered 2-channel sample each, but we still need
- // to sum the two hi- and lo-floats of these registers together.
-
- // post-shuffle & add the filtered values and store to dest.
- _mm_storeu_ps(pDest, _mm_add_ps(
- _mm_shuffle_ps(sum1, sum2, _MM_SHUFFLE(1,0,3,2)), // s2_1 s2_0 s1_3 s1_2
- _mm_shuffle_ps(sum1, sum2, _MM_SHUFFLE(3,2,1,0)) // s2_3 s2_2 s1_1 s1_0
- ));
- }
-
- // Ideas for further improvement:
- // 1. If it could be guaranteed that 'source' were always aligned to 16-byte
- // boundary, a faster aligned '_mm_load_ps' instruction could be used.
- // 2. If it could be guaranteed that 'dest' were always aligned to 16-byte
- // boundary, a faster '_mm_store_ps' instruction could be used.
-
- return (uint)count;
-
- /* original routine in C-language. please notice the C-version has differently
- organized coefficients though.
- double suml1, suml2;
- double sumr1, sumr2;
- uint i, j;
-
- for (j = 0; j < count; j += 2)
- {
- const float *ptr;
- const float *pFil;
-
- suml1 = sumr1 = 0.0;
- suml2 = sumr2 = 0.0;
- ptr = src;
- pFil = filterCoeffs;
- for (i = 0; i < lengthLocal; i ++)
- {
- // unroll loop for efficiency.
-
- suml1 += ptr[0] * pFil[0] +
- ptr[2] * pFil[2] +
- ptr[4] * pFil[4] +
- ptr[6] * pFil[6];
-
- sumr1 += ptr[1] * pFil[1] +
- ptr[3] * pFil[3] +
- ptr[5] * pFil[5] +
- ptr[7] * pFil[7];
-
- suml2 += ptr[8] * pFil[0] +
- ptr[10] * pFil[2] +
- ptr[12] * pFil[4] +
- ptr[14] * pFil[6];
-
- sumr2 += ptr[9] * pFil[1] +
- ptr[11] * pFil[3] +
- ptr[13] * pFil[5] +
- ptr[15] * pFil[7];
-
- ptr += 16;
- pFil += 8;
- }
- dest[0] = (float)suml1;
- dest[1] = (float)sumr1;
- dest[2] = (float)suml2;
- dest[3] = (float)sumr2;
-
- src += 4;
- dest += 4;
- }
- */
-}
-
-#endif // SOUNDTOUCH_ALLOW_SSE
diff --git a/Installer/dolphin-emu.spec b/Installer/dolphin-emu.spec index 99563547de..6e4e16c70d 100644 --- a/Installer/dolphin-emu.spec +++ b/Installer/dolphin-emu.spec @@ -60,7 +60,6 @@ BuildRequires: openal-soft-devel BuildRequires: mbedtls-devel BuildRequires: SDL2-devel BuildRequires: SFML-devel -BuildRequires: soundtouch-devel %endif %if 0%{?suse_version} @@ -69,7 +68,6 @@ BuildRequires: libSDL2-devel BuildRequires: lzo-devel BuildRequires: openal-devel BuildRequires: sfml-devel -BuildRequires: soundtouch-devel BuildRequires: update-desktop-files %endif diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/BooleanSetting.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/BooleanSetting.kt index 156d6ccd4c..16ee12e307 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/BooleanSetting.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/BooleanSetting.kt @@ -30,7 +30,7 @@ enum class BooleanSetting( "OverrideRegionSettings", false ), - MAIN_AUDIO_STRETCH(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "AudioStretch", false), + MAIN_AUDIO_FILL_GAPS(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "AudioFillGaps", true), MAIN_BBA_XLINK_CHAT_OSD( Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.kt index 2af2202599..eb18edc0b7 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.kt @@ -533,9 +533,9 @@ class SettingsFragmentPresenter( sl.add( SwitchSetting( context, - BooleanSetting.MAIN_AUDIO_STRETCH, - R.string.audio_stretch, - R.string.audio_stretch_description + BooleanSetting.MAIN_AUDIO_FILL_GAPS, + R.string.audio_fill_gaps, + R.string.audio_fill_gaps_description ) ) sl.add( diff --git a/Source/Android/app/src/main/res/values/strings.xml b/Source/Android/app/src/main/res/values/strings.xml index 0dbee76452..e10a781114 100644 --- a/Source/Android/app/src/main/res/values/strings.xml +++ b/Source/Android/app/src/main/res/values/strings.xml @@ -132,7 +132,8 @@ <!-- SerialPort1 Subsetting Fragment --> <string name="serialport1_submenu">Serial Port 1</string> - <string name="xlink_kai_guide_header">For setup instructions, <a href="https://www.teamxlink.co.uk/wiki/Dolphin">refer to this page.</a></string> + <string name="xlink_kai_guide_header">For setup instructions, <a href="https://www.teamxlink.co.uk/wiki/Dolphin">refer to this page.</a> + </string> <string name="xlink_kai_bba_ip">XLink Kai IP Address/hostname</string> <string name="xlink_kai_bba_ip_description">IP address or hostname of device running the XLink Kai client</string> <string name="bba_tapserver_destination">Tapserver destination</string> @@ -181,8 +182,8 @@ <!-- Audio Settings --> <string name="audio_submenu">Audio</string> <string name="dsp_emulation_engine">DSP Emulation Engine</string> - <string name="audio_stretch">Audio Stretching</string> - <string name="audio_stretch_description">Stretches audio to reduce stuttering. Increases latency.</string> + <string name="audio_fill_gaps">Fill Audio Gaps</string> + <string name="audio_fill_gaps_description">Repeat existing audio during lag spikes to prevent stuttering. If unsure, leave this checked.</string> <string name="audio_volume">Audio Volume</string> <!-- Path Settings --> @@ -877,9 +878,15 @@ It can efficiently compress both junk data and encrypted Wii data. <string name="about_branch">Branch:\n%s</string> <string name="about_revision">Revision:\n%s</string> <string name="about_dolphin_description">Dolphin is a free and open-source GameCube and Wii emulator.\n\nThis software should not be used to play games you do not legally own.</string> - <string name="about_website"><a href="https://dolphin-emu.org/">Website</a></string> - <string name="about_github"><a href="https://github.com/dolphin-emu/dolphin">GitHub</a></string> - <string name="about_support"><a href="https://forums.dolphin-emu.org/">Support</a></string> + <string name="about_website"> + <a href="https://dolphin-emu.org/">Website</a> + </string> + <string name="about_github"> + <a href="https://github.com/dolphin-emu/dolphin">GitHub</a> + </string> + <string name="about_support"> + <a href="https://forums.dolphin-emu.org/">Support</a> + </string> <string name="about_copyright_warning">\u00A9 2003–2024+ Dolphin Team. \u201cGameCube\u201d and \u201cWii\u201d are trademarks of Nintendo. Dolphin is not affiliated with Nintendo in any way.</string> <string name="system_driver">System driver</string> <string name="system_driver_desc">The GPU driver that is part of the OS.</string> diff --git a/Source/Core/AudioCommon/AudioStretcher.cpp b/Source/Core/AudioCommon/AudioStretcher.cpp deleted file mode 100644 index a5e54b8163..0000000000 --- a/Source/Core/AudioCommon/AudioStretcher.cpp +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2017 Dolphin Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "AudioCommon/AudioStretcher.h" - -#include <algorithm> -#include <cmath> -#include <cstddef> - -#include "Common/Logging/Log.h" -#include "Core/Config/MainSettings.h" - -namespace AudioCommon -{ -AudioStretcher::AudioStretcher(unsigned int sample_rate) : m_sample_rate(sample_rate) -{ - m_sound_touch.setChannels(2); - m_sound_touch.setSampleRate(sample_rate); - m_sound_touch.setPitch(1.0); - m_sound_touch.setTempo(1.0); -} - -void AudioStretcher::Clear() -{ - m_sound_touch.clear(); -} - -void AudioStretcher::ProcessSamples(const short* in, unsigned int num_in, unsigned int num_out) -{ - const double time_delta = static_cast<double>(num_out) / m_sample_rate; // seconds - - // We were given actual_samples number of samples, and num_samples were requested from us. - double current_ratio = static_cast<double>(num_in) / static_cast<double>(num_out); - - const double max_latency = Config::Get(Config::MAIN_AUDIO_STRETCH_LATENCY); - const double max_backlog = m_sample_rate * max_latency / 1000.0 / m_stretch_ratio; - const double backlog_fullness = m_sound_touch.numSamples() / max_backlog; - if (backlog_fullness > 5.0) - { - // Too many samples in backlog: Don't push anymore on - num_in = 0; - } - - // We ideally want the backlog to be about 50% full. - // This gives some headroom both ways to prevent underflow and overflow. - // We tweak current_ratio to encourage this. - constexpr double tweak_time_scale = 0.5; // seconds - current_ratio *= 1.0 + 2.0 * (backlog_fullness - 0.5) * (time_delta / tweak_time_scale); - - // This low-pass filter smoothes out variance in the calculated stretch ratio. - // The time-scale determines how responsive this filter is. - constexpr double lpf_time_scale = 1.0; // seconds - const double lpf_gain = 1.0 - std::exp(-time_delta / lpf_time_scale); - m_stretch_ratio += lpf_gain * (current_ratio - m_stretch_ratio); - - // Place a lower limit of 10% speed. When a game boots up, there will be - // many silence samples. These do not need to be timestretched. - m_stretch_ratio = std::max(m_stretch_ratio, 0.1); - m_sound_touch.setTempo(m_stretch_ratio); - - DEBUG_LOG_FMT(AUDIO, "Audio stretching: samples:{}/{} ratio:{} backlog:{} gain: {}", num_in, - num_out, m_stretch_ratio, backlog_fullness, lpf_gain); - - m_sound_touch.putSamples(in, num_in); -} - -void AudioStretcher::GetStretchedSamples(short* out, unsigned int num_out) -{ - const size_t samples_received = m_sound_touch.receiveSamples(out, num_out); - - if (samples_received != 0) - { - m_last_stretched_sample[0] = out[samples_received * 2 - 2]; - m_last_stretched_sample[1] = out[samples_received * 2 - 1]; - } - - // Perform padding if we've run out of samples. - for (size_t i = samples_received; i < num_out; i++) - { - out[i * 2 + 0] = m_last_stretched_sample[0]; - out[i * 2 + 1] = m_last_stretched_sample[1]; - } -} - -} // namespace AudioCommon diff --git a/Source/Core/AudioCommon/AudioStretcher.h b/Source/Core/AudioCommon/AudioStretcher.h deleted file mode 100644 index 9bc67ad893..0000000000 --- a/Source/Core/AudioCommon/AudioStretcher.h +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2017 Dolphin Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <array> - -#include <SoundTouch.h> - -namespace AudioCommon -{ -class AudioStretcher -{ -public: - explicit AudioStretcher(unsigned int sample_rate); - void ProcessSamples(const short* in, unsigned int num_in, unsigned int num_out); - void GetStretchedSamples(short* out, unsigned int num_out); - void Clear(); - -private: - unsigned int m_sample_rate; - std::array<short, 2> m_last_stretched_sample = {}; - soundtouch::SoundTouch m_sound_touch; - double m_stretch_ratio = 1.0; -}; - -} // namespace AudioCommon diff --git a/Source/Core/AudioCommon/CMakeLists.txt b/Source/Core/AudioCommon/CMakeLists.txt index d08a87247b..04ad967234 100644 --- a/Source/Core/AudioCommon/CMakeLists.txt +++ b/Source/Core/AudioCommon/CMakeLists.txt @@ -1,8 +1,6 @@ add_library(audiocommon AudioCommon.cpp AudioCommon.h - AudioStretcher.cpp - AudioStretcher.h CubebStream.h Enums.h Mixer.cpp @@ -90,7 +88,6 @@ PUBLIC common PRIVATE - SoundTouch FreeSurround) if(ENABLE_CUBEB) diff --git a/Source/Core/AudioCommon/Mixer.cpp b/Source/Core/AudioCommon/Mixer.cpp index 18d899e3cd..e44197a429 100644 --- a/Source/Core/AudioCommon/Mixer.cpp +++ b/Source/Core/AudioCommon/Mixer.cpp @@ -13,8 +13,8 @@ #include "Common/Logging/Log.h" #include "Common/Swap.h" #include "Core/Config/MainSettings.h" -#include "Core/ConfigManager.h" -#include "VideoCommon/PerformanceMetrics.h" +#include "Core/Core.h" +#include "Core/System.h" static u32 DPL2QualityToFrameBlockSize(AudioCommon::DPL2Quality quality) { @@ -31,8 +31,8 @@ static u32 DPL2QualityToFrameBlockSize(AudioCommon::DPL2Quality quality) } } -Mixer::Mixer(unsigned int BackendSampleRate) - : m_sampleRate(BackendSampleRate), m_stretcher(BackendSampleRate), +Mixer::Mixer(u32 BackendSampleRate) + : m_output_sample_rate(BackendSampleRate), m_surround_decoder(BackendSampleRate, DPL2QualityToFrameBlockSize(Config::Get(Config::MAIN_DPL2_QUALITY))) { @@ -58,177 +58,80 @@ void Mixer::DoState(PointerWrap& p) } // Executed from sound stream thread -unsigned int Mixer::MixerFifo::Mix(short* samples, unsigned int numSamples, - bool consider_framelimit, float emulationspeed, - int timing_variance) -{ - unsigned int currentSample = 0; - - // Cache access in non-volatile variable - // This is the only function changing the read value, so it's safe to - // cache it locally although it's written here. - // The writing pointer will be modified outside, but it will only increase, - // so we will just ignore new written data while interpolating. - // Without this cache, the compiler wouldn't be allowed to optimize the - // interpolation loop. - u32 indexR = m_indexR.load(); - u32 indexW = m_indexW.load(); - - // render numleft sample pairs to samples[] - // advance indexR with sample position - // remember fractional offset - - float aid_sample_rate = - FIXED_SAMPLE_RATE_DIVIDEND / static_cast<float>(m_input_sample_rate_divisor); - if (consider_framelimit && emulationspeed > 0.0f) - { - float numLeft = static_cast<float>(((indexW - indexR) & INDEX_MASK) / 2); - - u32 low_watermark = (FIXED_SAMPLE_RATE_DIVIDEND * timing_variance) / - (static_cast<u64>(m_input_sample_rate_divisor) * 1000); - low_watermark = std::min(low_watermark, MAX_SAMPLES / 2); +void Mixer::MixerFifo::Mix(s16* samples, std::size_t num_samples) +{ + constexpr u32 half = 0x80000000; - m_numLeftI = (numLeft + m_numLeftI * (CONTROL_AVG - 1)) / CONTROL_AVG; - float offset = (m_numLeftI - low_watermark) * CONTROL_FACTOR; - if (offset > MAX_FREQ_SHIFT) - offset = MAX_FREQ_SHIFT; - if (offset < -MAX_FREQ_SHIFT) - offset = -MAX_FREQ_SHIFT; + const u64 out_sample_rate = m_mixer->m_output_sample_rate; + u64 in_sample_rate = FIXED_SAMPLE_RATE_DIVIDEND / m_input_sample_rate_divisor; - aid_sample_rate = (aid_sample_rate + offset) * emulationspeed; - } + const float emulation_speed = m_mixer->m_config_emulation_speed; + if (0 < emulation_speed && emulation_speed != 1.0) + in_sample_rate = static_cast<u64>(std::llround(in_sample_rate * emulation_speed)); - const u32 ratio = (u32)(65536.0f * aid_sample_rate / (float)m_mixer->m_sampleRate); + const u32 index_jump = (in_sample_rate << GRANULE_BUFFER_FRAC_BITS) / (out_sample_rate); - s32 lvolume = m_LVolume.load(); - s32 rvolume = m_RVolume.load(); + const StereoPair volume{m_LVolume.load() / 256.0f, m_RVolume.load() / 256.0f}; - const auto read_buffer = [this](auto index) { - return m_little_endian ? m_buffer[index] : Common::swap16(m_buffer[index]); - }; - - // TODO: consider a higher-quality resampling algorithm. - for (; currentSample < numSamples * 2 && ((indexW - indexR) & INDEX_MASK) > 2; currentSample += 2) + while (num_samples-- > 0) { - u32 indexR2 = indexR + 2; // next sample - - s16 l1 = read_buffer(indexR & INDEX_MASK); // current - s16 l2 = read_buffer(indexR2 & INDEX_MASK); // next - int sampleL = ((l1 << 16) + (l2 - l1) * (u16)m_frac) >> 16; - sampleL = (sampleL * lvolume) >> 8; - sampleL += samples[currentSample + 1]; - samples[currentSample + 1] = std::clamp(sampleL, -32767, 32767); - - s16 r1 = read_buffer((indexR + 1) & INDEX_MASK); // current - s16 r2 = read_buffer((indexR2 + 1) & INDEX_MASK); // next - int sampleR = ((r1 << 16) + (r2 - r1) * (u16)m_frac) >> 16; - sampleR = (sampleR * rvolume) >> 8; - sampleR += samples[currentSample]; - samples[currentSample] = std::clamp(sampleR, -32767, 32767); - - m_frac += ratio; - indexR += 2 * (u16)(m_frac >> 16); - m_frac &= 0xffff; - } + StereoPair sample = Granule::InterpStereoPair(m_front, m_back, m_current_index); + sample *= volume; - // Actual number of samples written to the buffer without padding. - unsigned int actual_sample_count = currentSample / 2; + sample.l += samples[0] + m_quantization_error.l; + samples[0] = ToShort(std::lround(sample.l)); + m_quantization_error.l = std::clamp(sample.l - samples[0], -1.0f, 1.0f); - // Padding - short s[2]; - s[0] = read_buffer((indexR - 1) & INDEX_MASK); - s[1] = read_buffer((indexR - 2) & INDEX_MASK); - s[0] = (s[0] * rvolume) >> 8; - s[1] = (s[1] * lvolume) >> 8; - for (; currentSample < numSamples * 2; currentSample += 2) - { - int sampleR = std::clamp(s[0] + samples[currentSample + 0], -32767, 32767); - int sampleL = std::clamp(s[1] + samples[currentSample + 1], -32767, 32767); + sample.r += samples[1] + m_quantization_error.r; + samples[1] = ToShort(std::lround(sample.r)); + m_quantization_error.r = std::clamp(sample.r - samples[1], -1.0f, 1.0f); - samples[currentSample + 0] = sampleR; - samples[currentSample + 1] = sampleL; - } + samples += 2; - // Flush cached variable - m_indexR.store(indexR); + m_current_index += index_jump; + if (m_current_index < half) + { + m_front = m_back; + Dequeue(&m_back); - return actual_sample_count; + m_current_index += half; + } + } } -unsigned int Mixer::Mix(short* samples, unsigned int num_samples) +std::size_t Mixer::Mix(s16* samples, std::size_t num_samples) { if (!samples) return 0; - memset(samples, 0, num_samples * 2 * sizeof(short)); - - // TODO: Determine how emulation speed will be used in audio - // const float emulation_speed = g_perf_metrics.GetSpeed(); - const float emulation_speed = m_config_emulation_speed; - const int timing_variance = m_config_timing_variance; - if (m_config_audio_stretch) - { - unsigned int available_samples = - std::min(m_dma_mixer.AvailableSamples(), m_streaming_mixer.AvailableSamples()); - - ASSERT_MSG(AUDIO, available_samples <= MAX_SAMPLES, - "Audio stretching would overflow m_scratch_buffer: min({}, {}) -> {} > {} ({})", - m_dma_mixer.AvailableSamples(), m_streaming_mixer.AvailableSamples(), - available_samples, MAX_SAMPLES, num_samples); - - m_scratch_buffer.fill(0); - - m_dma_mixer.Mix(m_scratch_buffer.data(), available_samples, false, emulation_speed, - timing_variance); - m_streaming_mixer.Mix(m_scratch_buffer.data(), available_samples, false, emulation_speed, - timing_variance); - m_wiimote_speaker_mixer.Mix(m_scratch_buffer.data(), available_samples, false, emulation_speed, - timing_variance); - m_skylander_portal_mixer.Mix(m_scratch_buffer.data(), available_samples, false, emulation_speed, - timing_variance); - for (auto& mixer : m_gba_mixers) - { - mixer.Mix(m_scratch_buffer.data(), available_samples, false, emulation_speed, - timing_variance); - } + memset(samples, 0, num_samples * 2 * sizeof(s16)); - if (!m_is_stretching) - { - m_stretcher.Clear(); - m_is_stretching = true; - } - m_stretcher.ProcessSamples(m_scratch_buffer.data(), available_samples, num_samples); - m_stretcher.GetStretchedSamples(samples, num_samples); - } - else - { - m_dma_mixer.Mix(samples, num_samples, true, emulation_speed, timing_variance); - m_streaming_mixer.Mix(samples, num_samples, true, emulation_speed, timing_variance); - m_wiimote_speaker_mixer.Mix(samples, num_samples, true, emulation_speed, timing_variance); - m_skylander_portal_mixer.Mix(samples, num_samples, true, emulation_speed, timing_variance); - for (auto& mixer : m_gba_mixers) - mixer.Mix(samples, num_samples, true, emulation_speed, timing_variance); - m_is_stretching = false; - } + m_dma_mixer.Mix(samples, num_samples); + m_streaming_mixer.Mix(samples, num_samples); + m_wiimote_speaker_mixer.Mix(samples, num_samples); + m_skylander_portal_mixer.Mix(samples, num_samples); + for (auto& mixer : m_gba_mixers) + mixer.Mix(samples, num_samples); return num_samples; } -unsigned int Mixer::MixSurround(float* samples, unsigned int num_samples) +std::size_t Mixer::MixSurround(float* samples, std::size_t num_samples) { if (!num_samples) return 0; memset(samples, 0, num_samples * SURROUND_CHANNELS * sizeof(float)); - size_t needed_frames = m_surround_decoder.QueryFramesNeededForSurroundOutput(num_samples); + std::size_t needed_frames = m_surround_decoder.QueryFramesNeededForSurroundOutput(num_samples); - // Mix() may also use m_scratch_buffer internally, but is safe because it alternates reads - // and writes. - ASSERT_MSG(AUDIO, needed_frames <= MAX_SAMPLES, + constexpr std::size_t max_samples = 0x8000; + ASSERT_MSG(AUDIO, needed_frames <= max_samples, "needed_frames would overflow m_scratch_buffer: {} -> {} > {}", num_samples, - needed_frames, MAX_SAMPLES); - size_t available_frames = Mix(m_scratch_buffer.data(), static_cast<u32>(needed_frames)); + needed_frames, max_samples); + + std::array<s16, max_samples> buffer; + std::size_t available_frames = Mix(buffer.data(), static_cast<std::size_t>(needed_frames)); if (available_frames != needed_frames) { ERROR_LOG_FMT(AUDIO, @@ -237,71 +140,58 @@ unsigned int Mixer::MixSurround(float* samples, unsigned int num_samples) return 0; } - m_surround_decoder.PutFrames(m_scratch_buffer.data(), needed_frames); + m_surround_decoder.PutFrames(buffer.data(), needed_frames); m_surround_decoder.ReceiveFrames(samples, num_samples); return num_samples; } -void Mixer::MixerFifo::PushSamples(const short* samples, unsigned int num_samples) +void Mixer::MixerFifo::PushSamples(const s16* samples, std::size_t num_samples) { - // Cache access in non-volatile variable - // indexR isn't allowed to cache in the audio throttling loop as it - // needs to get updates to not deadlock. - u32 indexW = m_indexW.load(); + while (num_samples-- > 0) + { + const s16 l = m_little_endian ? samples[1] : Common::swap16(samples[1]); + const s16 r = m_little_endian ? samples[0] : Common::swap16(samples[0]); - // Check if we have enough free space - // indexW == m_indexR results in empty buffer, so indexR must always be smaller than indexW - if (num_samples * 2 + ((indexW - m_indexR.load()) & INDEX_MASK) >= MAX_SAMPLES * 2) - return; + m_buffer[m_buffer_index] = StereoPair(l, r); + m_buffer_index = (m_buffer_index + 1) & GRANULE_BUFFER_MASK; + samples += 2; - // AyuanX: Actual re-sampling work has been moved to sound thread - // to alleviate the workload on main thread - // and we simply store raw data here to make fast mem copy - int over_bytes = num_samples * 4 - (MAX_SAMPLES * 2 - (indexW & INDEX_MASK)) * sizeof(short); - if (over_bytes > 0) - { - memcpy(&m_buffer[indexW & INDEX_MASK], samples, num_samples * 4 - over_bytes); - memcpy(&m_buffer[0], samples + (num_samples * 4 - over_bytes) / sizeof(short), over_bytes); - } - else - { - memcpy(&m_buffer[indexW & INDEX_MASK], samples, num_samples * 4); + if (m_buffer_index == 0 || m_buffer_index == m_buffer.size() / 2) + Enqueue(Granule(m_buffer, m_buffer_index)); } - - m_indexW.fetch_add(num_samples * 2); } -void Mixer::PushSamples(const short* samples, unsigned int num_samples) +void Mixer::PushSamples(const s16* samples, std::size_t num_samples) { m_dma_mixer.PushSamples(samples, num_samples); if (m_log_dsp_audio) { - int sample_rate_divisor = m_dma_mixer.GetInputSampleRateDivisor(); + const s32 sample_rate_divisor = m_dma_mixer.GetInputSampleRateDivisor(); auto volume = m_dma_mixer.GetVolume(); - m_wave_writer_dsp.AddStereoSamplesBE(samples, num_samples, sample_rate_divisor, volume.first, - volume.second); + m_wave_writer_dsp.AddStereoSamplesBE(samples, static_cast<u32>(num_samples), + sample_rate_divisor, volume.first, volume.second); } } -void Mixer::PushStreamingSamples(const short* samples, unsigned int num_samples) +void Mixer::PushStreamingSamples(const s16* samples, std::size_t num_samples) { m_streaming_mixer.PushSamples(samples, num_samples); if (m_log_dtk_audio) { - int sample_rate_divisor = m_streaming_mixer.GetInputSampleRateDivisor(); + const s32 sample_rate_divisor = m_streaming_mixer.GetInputSampleRateDivisor(); auto volume = m_streaming_mixer.GetVolume(); - m_wave_writer_dtk.AddStereoSamplesBE(samples, num_samples, sample_rate_divisor, volume.first, - volume.second); + m_wave_writer_dtk.AddStereoSamplesBE(samples, static_cast<u32>(num_samples), + sample_rate_divisor, volume.first, volume.second); } } -void Mixer::PushWiimoteSpeakerSamples(const short* samples, unsigned int num_samples, - unsigned int sample_rate_divisor) +void Mixer::PushWiimoteSpeakerSamples(const s16* samples, std::size_t num_samples, + u32 sample_rate_divisor) { // Max 20 bytes/speaker report, may be 4-bit ADPCM so multiply by 2 - static constexpr u32 MAX_SPEAKER_SAMPLES = 20 * 2; - std::array<short, MAX_SPEAKER_SAMPLES * 2> samples_stereo; + static constexpr std::size_t MAX_SPEAKER_SAMPLES = 20 * 2; + std::array<s16, MAX_SPEAKER_SAMPLES * 2> samples_stereo; ASSERT_MSG(AUDIO, num_samples <= MAX_SPEAKER_SAMPLES, "num_samples would overflow samples_stereo: {} > {}", num_samples, @@ -310,7 +200,7 @@ void Mixer::PushWiimoteSpeakerSamples(const short* samples, unsigned int num_sam { m_wiimote_speaker_mixer.SetInputSampleRateDivisor(sample_rate_divisor); - for (unsigned int i = 0; i < num_samples; ++i) + for (std::size_t i = 0; i < num_samples; ++i) { samples_stereo[i * 2] = samples[i]; samples_stereo[i * 2 + 1] = samples[i]; @@ -320,12 +210,12 @@ void Mixer::PushWiimoteSpeakerSamples(const short* samples, unsigned int num_sam } } -void Mixer::PushSkylanderPortalSamples(const u8* samples, unsigned int num_samples) +void Mixer::PushSkylanderPortalSamples(const u8* samples, std::size_t num_samples) { // Skylander samples are always supplied as 64 bytes, 32 x 16 bit samples // The portal speaker is 1 channel, so duplicate and play as stereo audio - static constexpr u32 MAX_PORTAL_SPEAKER_SAMPLES = 32; - std::array<short, MAX_PORTAL_SPEAKER_SAMPLES * 2> samples_stereo; + static constexpr std::size_t MAX_PORTAL_SPEAKER_SAMPLES = 32; + std::array<s16, MAX_PORTAL_SPEAKER_SAMPLES * 2> samples_stereo; ASSERT_MSG(AUDIO, num_samples <= MAX_PORTAL_SPEAKER_SAMPLES, "num_samples is not less or equal to 32: {} > {}", num_samples, @@ -333,7 +223,7 @@ void Mixer::PushSkylanderPortalSamples(const u8* samples, unsigned int num_sampl if (num_samples <= MAX_PORTAL_SPEAKER_SAMPLES) { - for (unsigned int i = 0; i < num_samples; ++i) + for (std::size_t i = 0; i < num_samples; ++i) { s16 sample = static_cast<u16>(samples[i * 2 + 1]) << 8 | static_cast<u16>(samples[i * 2]); samples_stereo[i * 2] = sample; @@ -344,37 +234,38 @@ void Mixer::PushSkylanderPortalSamples(const u8* samples, unsigned int num_sampl } } -void Mixer::PushGBASamples(int device_number, const short* samples, unsigned int num_samples) +void Mixer::PushGBASamples(std::size_t device_number, const s16* samples, std::size_t num_samples) { m_gba_mixers[device_number].PushSamples(samples, num_samples); } -void Mixer::SetDMAInputSampleRateDivisor(unsigned int rate_divisor) +void Mixer::SetDMAInputSampleRateDivisor(u32 rate_divisor) { m_dma_mixer.SetInputSampleRateDivisor(rate_divisor); } -void Mixer::SetStreamInputSampleRateDivisor(unsigned int rate_divisor) +void Mixer::SetStreamInputSampleRateDivisor(u32 rate_divisor) { m_streaming_mixer.SetInputSampleRateDivisor(rate_divisor); } -void Mixer::SetGBAInputSampleRateDivisors(int device_number, unsigned int rate_divisor) +void Mixer::SetGBAInputSampleRateDivisors(std::size_t device_number, u32 rate_divisor) { m_gba_mixers[device_number].SetInputSampleRateDivisor(rate_divisor); } -void Mixer::SetStreamingVolume(unsigned int lvolume, unsigned int rvolume) +void Mixer::SetStreamingVolume(u32 lvolume, u32 rvolume) { - m_streaming_mixer.SetVolume(lvolume, rvolume); + m_streaming_mixer.SetVolume(std::clamp<u32>(lvolume, 0x00, 0xff), + std::clamp<u32>(rvolume, 0x00, 0xff)); } -void Mixer::SetWiimoteSpeakerVolume(unsigned int lvolume, unsigned int rvolume) +void Mixer::SetWiimoteSpeakerVolume(u32 lvolume, u32 rvolume) { m_wiimote_speaker_mixer.SetVolume(lvolume, rvolume); } -void Mixer::SetGBAVolume(int device_number, unsigned int lvolume, unsigned int rvolume) +void Mixer::SetGBAVolume(std::size_t device_number, u32 lvolume, u32 rvolume) { m_gba_mixers[device_number].SetVolume(lvolume, rvolume); } @@ -456,8 +347,7 @@ void Mixer::StopLogDSPAudio() void Mixer::RefreshConfig() { m_config_emulation_speed = Config::Get(Config::MAIN_EMULATION_SPEED); - m_config_timing_variance = Config::Get(Config::MAIN_TIMING_VARIANCE); - m_config_audio_stretch = Config::Get(Config::MAIN_AUDIO_STRETCH); + m_audio_fill_gaps = Config::Get(Config::MAIN_AUDIO_FILL_GAPS); } void Mixer::MixerFifo::DoState(PointerWrap& p) @@ -467,17 +357,17 @@ void Mixer::MixerFifo::DoState(PointerWrap& p) p.Do(m_RVolume); } -void Mixer::MixerFifo::SetInputSampleRateDivisor(unsigned int rate_divisor) +void Mixer::MixerFifo::SetInputSampleRateDivisor(u32 rate_divisor) { m_input_sample_rate_divisor = rate_divisor; } -unsigned int Mixer::MixerFifo::GetInputSampleRateDivisor() const +u32 Mixer::MixerFifo::GetInputSampleRateDivisor() const { return m_input_sample_rate_divisor; } -void Mixer::MixerFifo::SetVolume(unsigned int lvolume, unsigned int rvolume) +void Mixer::MixerFifo::SetVolume(u32 lvolume, u32 rvolume) { m_LVolume.store(lvolume + (lvolume >> 7)); m_RVolume.store(rvolume + (rvolume >> 7)); @@ -488,11 +378,161 @@ std::pair<s32, s32> Mixer::MixerFifo::GetVolume() const return std::make_pair(m_LVolume.load(), m_RVolume.load()); } -unsigned int Mixer::MixerFifo::AvailableSamples() const +void Mixer::MixerFifo::Enqueue(const Granule& granule) +{ + const std::size_t head = m_queue_head.load(std::memory_order_relaxed); + + std::size_t next_head = (head + 1) % GRANULE_QUEUE_SIZE; + if (next_head == m_queue_tail.load(std::memory_order_acquire)) + next_head = (head + GRANULE_QUEUE_SIZE / 2) % GRANULE_QUEUE_SIZE; + + m_queue[head] = granule; + m_queue_head.store(next_head, std::memory_order_release); + + m_queue_looping.store(false, std::memory_order_relaxed); +} + +void Mixer::MixerFifo::Dequeue(Granule* granule) +{ + // import numpy as np + // import scipy.signal as signal + // window = np.cumsum(signal.windows.dpss(32, 10))[::-1] + // window /= window.max() + // elements = ", ".join([f"{x:.10f}f" for x in window]) + // print(f'constexpr std::array<StereoPair, {len(window)}> FADE_WINDOW = {{ {elements} }};') + constexpr std::array<float, 32> FADE_WINDOW = { + 1.0000000000f, 0.9999999932f, 0.9999998472f, 0.9999982765f, 0.9999870876f, 0.9999278274f, + 0.9996794215f, 0.9988227502f, 0.9963278433f, 0.9900772448f, 0.9764215513f, 0.9501402658f, + 0.9052392639f, 0.8367449916f, 0.7430540364f, 0.6277889467f, 0.5000000000f, 0.3722110533f, + 0.2569459636f, 0.1632550084f, 0.0947607361f, 0.0498597342f, 0.0235784487f, 0.0099227552f, + 0.0036721567f, 0.0011772498f, 0.0003205785f, 0.0000721726f, 0.0000129124f, 0.0000017235f, + 0.0000001528f, 0.0000000068f}; + + const std::size_t tail = m_queue_tail.load(std::memory_order_relaxed); + std::size_t next_tail = (tail + 1) % GRANULE_QUEUE_SIZE; + + if (next_tail == m_queue_head.load(std::memory_order_acquire)) + { + // Only fill gaps when running to prevent stutter on pause. + const bool is_running = Core::GetState(Core::System::GetInstance()) == Core::State::Running; + if (m_mixer->m_audio_fill_gaps && is_running) + { + next_tail = (tail + GRANULE_QUEUE_SIZE / 2) % GRANULE_QUEUE_SIZE; + m_queue_looping.store(true, std::memory_order_relaxed); + } + else + { + *granule = Granule(); + return; + } + } + + if (m_queue_looping.load(std::memory_order_relaxed)) + m_queue_fade_index = std::min(m_queue_fade_index + 1, FADE_WINDOW.size() - 1); + else + m_queue_fade_index = 0; + + *granule = m_queue[tail]; + *granule *= StereoPair(FADE_WINDOW[m_queue_fade_index]); + + m_queue_tail.store(next_tail, std::memory_order_release); +} + +// Implementation of Granule's constructor +constexpr Mixer::MixerFifo::Granule::Granule(const GranuleBuffer& input, + const std::size_t start_index) +{ + // import numpy as np + // import scipy.signal as signal + // window = np.convolve(np.ones(128), signal.windows.dpss(128 + 1, 4)) + // window /= (window[:len(window) // 2] + window[len(window) // 2:]).max() + // elements = ", ".join([f"{x:.10f}f" for x in window]) + // print(f'constexpr std::array<StereoPair, GRANULE_BUFFER_SIZE> GRANULE_WINDOW = {{ {elements} + // }};') + constexpr std::array<float, GRANULE_BUFFER_SIZE> GRANULE_WINDOW = { + 0.0000016272f, 0.0000050749f, 0.0000113187f, 0.0000216492f, 0.0000377350f, 0.0000616906f, + 0.0000961509f, 0.0001443499f, 0.0002102045f, 0.0002984010f, 0.0004144844f, 0.0005649486f, + 0.0007573262f, 0.0010002765f, 0.0013036694f, 0.0016786636f, 0.0021377783f, 0.0026949534f, + 0.0033656000f, 0.0041666352f, 0.0051165029f, 0.0062351752f, 0.0075441359f, 0.0090663409f, + 0.0108261579f, 0.0128492811f, 0.0151626215f, 0.0177941726f, 0.0207728499f, 0.0241283062f, + 0.0278907219f, 0.0320905724f, 0.0367583739f, 0.0419244083f, 0.0476184323f, 0.0538693708f, + 0.0607049996f, 0.0681516192f, 0.0762337261f, 0.0849736833f, 0.0943913952f, 0.1045039915f, + 0.1153255250f, 0.1268666867f, 0.1391345431f, 0.1521323012f, 0.1658591025f, 0.1803098534f, + 0.1954750915f, 0.2113408944f, 0.2278888303f, 0.2450959552f, 0.2629348550f, 0.2813737361f, + 0.3003765625f, 0.3199032396f, 0.3399098438f, 0.3603488941f, 0.3811696664f, 0.4023185434f, + 0.4237393998f, 0.4453740162f, 0.4671625177f, 0.4890438330f, 0.5109561670f, 0.5328374823f, + 0.5546259838f, 0.5762606002f, 0.5976814566f, 0.6188303336f, 0.6396511059f, 0.6600901562f, + 0.6800967604f, 0.6996234375f, 0.7186262639f, 0.7370651450f, 0.7549040448f, 0.7721111697f, + 0.7886591056f, 0.8045249085f, 0.8196901466f, 0.8341408975f, 0.8478676988f, 0.8608654569f, + 0.8731333133f, 0.8846744750f, 0.8954960085f, 0.9056086048f, 0.9150263167f, 0.9237662739f, + 0.9318483808f, 0.9392950004f, 0.9461306292f, 0.9523815677f, 0.9580755917f, 0.9632416261f, + 0.9679094276f, 0.9721092781f, 0.9758716938f, 0.9792271501f, 0.9822058274f, 0.9848373785f, + 0.9871507189f, 0.9891738421f, 0.9909336591f, 0.9924558641f, 0.9937648248f, 0.9948834971f, + 0.9958333648f, 0.9966344000f, 0.9973050466f, 0.9978622217f, 0.9983213364f, 0.9986963306f, + 0.9989997235f, 0.9992426738f, 0.9994350514f, 0.9995855156f, 0.9997015990f, 0.9997897955f, + 0.9998556501f, 0.9999038491f, 0.9999383094f, 0.9999622650f, 0.9999783508f, 0.9999886813f, + 0.9999949251f, 0.9999983728f, 0.9999983728f, 0.9999949251f, 0.9999886813f, 0.9999783508f, + 0.9999622650f, 0.9999383094f, 0.9999038491f, 0.9998556501f, 0.9997897955f, 0.9997015990f, + 0.9995855156f, 0.9994350514f, 0.9992426738f, 0.9989997235f, 0.9986963306f, 0.9983213364f, + 0.9978622217f, 0.9973050466f, 0.9966344000f, 0.9958333648f, 0.9948834971f, 0.9937648248f, + 0.9924558641f, 0.9909336591f, 0.9891738421f, 0.9871507189f, 0.9848373785f, 0.9822058274f, + 0.9792271501f, 0.9758716938f, 0.9721092781f, 0.9679094276f, 0.9632416261f, 0.9580755917f, + 0.9523815677f, 0.9461306292f, 0.9392950004f, 0.9318483808f, 0.9237662739f, 0.9150263167f, + 0.9056086048f, 0.8954960085f, 0.8846744750f, 0.8731333133f, 0.8608654569f, 0.8478676988f, + 0.8341408975f, 0.8196901466f, 0.8045249085f, 0.7886591056f, 0.7721111697f, 0.7549040448f, + 0.7370651450f, 0.7186262639f, 0.6996234375f, 0.6800967604f, 0.6600901562f, 0.6396511059f, + 0.6188303336f, 0.5976814566f, 0.5762606002f, 0.5546259838f, 0.5328374823f, 0.5109561670f, + 0.4890438330f, 0.4671625177f, 0.4453740162f, 0.4237393998f, 0.4023185434f, 0.3811696664f, + 0.3603488941f, 0.3399098438f, 0.3199032396f, 0.3003765625f, 0.2813737361f, 0.2629348550f, + 0.2450959552f, 0.2278888303f, 0.2113408944f, 0.1954750915f, 0.1803098534f, 0.1658591025f, + 0.1521323012f, 0.1391345431f, 0.1268666867f, 0.1153255250f, 0.1045039915f, 0.0943913952f, + 0.0849736833f, 0.0762337261f, 0.0681516192f, 0.0607049996f, 0.0538693708f, 0.0476184323f, + 0.0419244083f, 0.0367583739f, 0.0320905724f, 0.0278907219f, 0.0241283062f, 0.0207728499f, + 0.0177941726f, 0.0151626215f, 0.0128492811f, 0.0108261579f, 0.0090663409f, 0.0075441359f, + 0.0062351752f, 0.0051165029f, 0.0041666352f, 0.0033656000f, 0.0026949534f, 0.0021377783f, + 0.0016786636f, 0.0013036694f, 0.0010002765f, 0.0007573262f, 0.0005649486f, 0.0004144844f, + 0.0002984010f, 0.0002102045f, 0.0001443499f, 0.0000961509f, 0.0000616906f, 0.0000377350f, + 0.0000216492f, 0.0000113187f, 0.0000050749f, 0.0000016272f}; + + const auto input_middle = input.end() - start_index; + std::ranges::rotate_copy(input, input_middle, m_buffer.begin()); + + for (std::size_t i = 0; i < m_buffer.size(); ++i) + m_buffer[i] *= StereoPair(GRANULE_WINDOW[i]); +} + +Mixer::MixerFifo::StereoPair Mixer::MixerFifo::Granule::InterpStereoPair(const Granule& prev, + const Granule& next, + const u32 frac) { - unsigned int samples_in_fifo = ((m_indexW.load() - m_indexR.load()) & INDEX_MASK) / 2; - if (samples_in_fifo <= 1) - return 0; // Mixer::MixerFifo::Mix always keeps one sample in the buffer. - return (samples_in_fifo - 1) * static_cast<u64>(m_mixer->m_sampleRate) * - m_input_sample_rate_divisor / FIXED_SAMPLE_RATE_DIVIDEND; + const std::size_t prev_index = frac >> Mixer::MixerFifo::GRANULE_BUFFER_FRAC_BITS; + const std::size_t next_index = prev_index - (GRANULE_BUFFER_SIZE / 2); + + const u32 frac_t = frac & ((1 << GRANULE_BUFFER_FRAC_BITS) - 1); + const float t1 = frac_t / static_cast<float>(1 << GRANULE_BUFFER_FRAC_BITS); + const float t2 = t1 * t1; + const float t3 = t2 * t1; + + // The Granules are pre-windowed, so we can just add them together + StereoPair s0 = prev.m_buffer[(prev_index - 2) & GRANULE_BUFFER_MASK] + + next.m_buffer[(next_index - 2) & GRANULE_BUFFER_MASK]; + StereoPair s1 = prev.m_buffer[(prev_index - 1) & GRANULE_BUFFER_MASK] + + next.m_buffer[(next_index - 1) & GRANULE_BUFFER_MASK]; + StereoPair s2 = prev.m_buffer[(prev_index + 0) & GRANULE_BUFFER_MASK] + + next.m_buffer[(next_index + 0) & GRANULE_BUFFER_MASK]; + StereoPair s3 = prev.m_buffer[(prev_index + 1) & GRANULE_BUFFER_MASK] + + next.m_buffer[(next_index + 1) & GRANULE_BUFFER_MASK]; + StereoPair s4 = prev.m_buffer[(prev_index + 2) & GRANULE_BUFFER_MASK] + + next.m_buffer[(next_index + 2) & GRANULE_BUFFER_MASK]; + StereoPair s5 = prev.m_buffer[(prev_index + 3) & GRANULE_BUFFER_MASK] + + next.m_buffer[(next_index + 3) & GRANULE_BUFFER_MASK]; + + s0 *= StereoPair{(+0.0f + 1.0f * t1 - 2.0f * t2 + 1.0f * t3) / 12.0f}; + s1 *= StereoPair{(+0.0f - 8.0f * t1 + 15.0f * t2 - 7.0f * t3) / 12.0f}; + s2 *= StereoPair{(+3.0f + 0.0f * t1 - 7.0f * t2 + 4.0f * t3) / 3.0f}; + s3 *= StereoPair{(+0.0f + 2.0f * t1 + 5.0f * t2 - 4.0f * t3) / 3.0f}; + s4 *= StereoPair{(+0.0f - 1.0f * t1 - 6.0f * t2 + 7.0f * t3) / 12.0f}; + s5 *= StereoPair{(+0.0f + 0.0f * t1 + 1.0f * t2 - 1.0f * t3) / 12.0f}; + + return s0 + s1 + s2 + s3 + s4 + s5; } diff --git a/Source/Core/AudioCommon/Mixer.h b/Source/Core/AudioCommon/Mixer.h index 5a71f5e8af..9ddec6dba4 100644 --- a/Source/Core/AudioCommon/Mixer.h +++ b/Source/Core/AudioCommon/Mixer.h @@ -3,10 +3,12 @@ #pragma once +#include <algorithm> #include <array> #include <atomic> +#include <bit> +#include <cmath> -#include "AudioCommon/AudioStretcher.h" #include "AudioCommon/SurroundDecoder.h" #include "AudioCommon/WaveFile.h" #include "Common/CommonTypes.h" @@ -17,32 +19,32 @@ class PointerWrap; class Mixer final { public: - explicit Mixer(unsigned int BackendSampleRate); + explicit Mixer(u32 BackendSampleRate); ~Mixer(); void DoState(PointerWrap& p); // Called from audio threads - unsigned int Mix(short* samples, unsigned int numSamples); - unsigned int MixSurround(float* samples, unsigned int num_samples); + std::size_t Mix(s16* samples, std::size_t numSamples); + std::size_t MixSurround(float* samples, std::size_t num_samples); // Called from main thread - void PushSamples(const short* samples, unsigned int num_samples); - void PushStreamingSamples(const short* samples, unsigned int num_samples); - void PushWiimoteSpeakerSamples(const short* samples, unsigned int num_samples, - unsigned int sample_rate_divisor); - void PushSkylanderPortalSamples(const u8* samples, unsigned int num_samples); - void PushGBASamples(int device_number, const short* samples, unsigned int num_samples); + void PushSamples(const s16* samples, std::size_t num_samples); + void PushStreamingSamples(const s16* samples, std::size_t num_samples); + void PushWiimoteSpeakerSamples(const s16* samples, std::size_t num_samples, + u32 sample_rate_divisor); + void PushSkylanderPortalSamples(const u8* samples, std::size_t num_samples); + void PushGBASamples(std::size_t device_number, const s16* samples, std::size_t num_samples); - unsigned int GetSampleRate() const { return m_sampleRate; } + u32 GetSampleRate() const { return m_output_sample_rate; } - void SetDMAInputSampleRateDivisor(unsigned int rate_divisor); - void SetStreamInputSampleRateDivisor(unsigned int rate_divisor); - void SetGBAInputSampleRateDivisors(int device_number, unsigned int rate_divisor); + void SetDMAInputSampleRateDivisor(u32 rate_divisor); + void SetStreamInputSampleRateDivisor(u32 rate_divisor); + void SetGBAInputSampleRateDivisors(std::size_t device_number, u32 rate_divisor); - void SetStreamingVolume(unsigned int lvolume, unsigned int rvolume); - void SetWiimoteSpeakerVolume(unsigned int lvolume, unsigned int rvolume); - void SetGBAVolume(int device_number, unsigned int lvolume, unsigned int rvolume); + void SetStreamingVolume(u32 lvolume, u32 rvolume); + void SetWiimoteSpeakerVolume(u32 lvolume, u32 rvolume); + void SetGBAVolume(std::size_t device_number, u32 lvolume, u32 rvolume); void StartLogDTKAudio(const std::string& filename); void StopLogDTKAudio(); @@ -54,44 +56,105 @@ public: static constexpr u64 FIXED_SAMPLE_RATE_DIVIDEND = 54000000 * 2; private: - static constexpr u32 MAX_SAMPLES = 1024 * 4; // 128 ms - static constexpr u32 INDEX_MASK = MAX_SAMPLES * 2 - 1; - static constexpr int MAX_FREQ_SHIFT = 200; // Per 32000 Hz - static constexpr float CONTROL_FACTOR = 0.2f; - static constexpr u32 CONTROL_AVG = 32; // In freq_shift per FIFO size offset - - const unsigned int SURROUND_CHANNELS = 6; + const std::size_t SURROUND_CHANNELS = 6; class MixerFifo final { + static constexpr std::size_t GRANULE_QUEUE_SIZE = 20; + + template <typename T> + static s16 ToShort(const T x) + { + return static_cast<s16>(std::clamp<T>(x, static_cast<T>(std::numeric_limits<s16>::min()), + static_cast<T>(std::numeric_limits<s16>::max()))); + } + struct StereoPair final + { + float l = 0.f; + float r = 0.f; + + constexpr StereoPair() = default; + constexpr explicit StereoPair(float mono) : l(mono), r(mono) {} + constexpr StereoPair(float left, float right) : l(left), r(right) {} + constexpr StereoPair(s16 left, s16 right) : l(left), r(right) {} + + StereoPair operator+(const StereoPair& other) const + { + return StereoPair(l + other.l, r + other.r); + } + + StereoPair& operator*=(const StereoPair& other) + { + l *= other.l; + r *= other.r; + return *this; + } + }; + + static constexpr std::size_t GRANULE_BUFFER_SIZE = 256; + static constexpr std::size_t GRANULE_BUFFER_MASK = GRANULE_BUFFER_SIZE - 1; + static constexpr std::size_t GRANULE_BUFFER_BITS = std::countr_one(GRANULE_BUFFER_MASK); + static constexpr std::size_t GRANULE_BUFFER_FRAC_BITS = 32 - GRANULE_BUFFER_BITS; + + using GranuleBuffer = std::array<StereoPair, GRANULE_BUFFER_SIZE>; + class Granule final + { + public: + constexpr Granule() = default; + constexpr Granule(const GranuleBuffer& input, std::size_t start_index); + + static StereoPair InterpStereoPair(const Granule& front, const Granule& back, u32 frac); + + Granule& operator*=(const StereoPair& x) + { + for (auto& sample : m_buffer) + sample *= x; + return *this; + } + + private: + GranuleBuffer m_buffer{}; + }; + public: - MixerFifo(Mixer* mixer, unsigned sample_rate_divisor, bool little_endian) + MixerFifo(Mixer* mixer, u32 sample_rate_divisor, bool little_endian) : m_mixer(mixer), m_input_sample_rate_divisor(sample_rate_divisor), m_little_endian(little_endian) { } void DoState(PointerWrap& p); - void PushSamples(const short* samples, unsigned int num_samples); - unsigned int Mix(short* samples, unsigned int numSamples, bool consider_framelimit, - float emulationspeed, int timing_variance); - void SetInputSampleRateDivisor(unsigned int rate_divisor); - unsigned int GetInputSampleRateDivisor() const; - void SetVolume(unsigned int lvolume, unsigned int rvolume); + void PushSamples(const s16* samples, std::size_t num_samples); + void Mix(s16* samples, std::size_t num_samples); + void SetInputSampleRateDivisor(u32 rate_divisor); + u32 GetInputSampleRateDivisor() const; + void SetVolume(u32 lvolume, u32 rvolume); std::pair<s32, s32> GetVolume() const; - unsigned int AvailableSamples() const; private: Mixer* m_mixer; - unsigned m_input_sample_rate_divisor; + u32 m_input_sample_rate_divisor; bool m_little_endian; - std::array<short, MAX_SAMPLES * 2> m_buffer{}; - std::atomic<u32> m_indexW{0}; - std::atomic<u32> m_indexR{0}; + + std::size_t m_buffer_index = 0; + GranuleBuffer m_buffer{}; + + u32 m_current_index = 0; + Granule m_front, m_back; + + std::array<Granule, GRANULE_QUEUE_SIZE> m_queue; + std::atomic<std::size_t> m_queue_head{0}; + std::atomic<std::size_t> m_queue_tail{0}; + std::atomic<bool> m_queue_looping{false}; + std::size_t m_queue_fade_index = 0; + + void Enqueue(const Granule& granule); + void Dequeue(Granule* granule); + // Volume ranges from 0-256 std::atomic<s32> m_LVolume{256}; std::atomic<s32> m_RVolume{256}; - float m_numLeftI = 0.0f; - u32 m_frac = 0; + + StereoPair m_quantization_error; }; void RefreshConfig(); @@ -104,12 +167,9 @@ private: MixerFifo{this, FIXED_SAMPLE_RATE_DIVIDEND / 48000, true}, MixerFifo{this, FIXED_SAMPLE_RATE_DIVIDEND / 48000, true}, MixerFifo{this, FIXED_SAMPLE_RATE_DIVIDEND / 48000, true}}; - unsigned int m_sampleRate; + u32 m_output_sample_rate; - bool m_is_stretching = false; - AudioCommon::AudioStretcher m_stretcher; AudioCommon::SurroundDecoder m_surround_decoder; - std::array<short, MAX_SAMPLES * 2> m_scratch_buffer{}; WaveFileWriter m_wave_writer_dtk; WaveFileWriter m_wave_writer_dsp; @@ -118,8 +178,7 @@ private: bool m_log_dsp_audio = false; float m_config_emulation_speed; - int m_config_timing_variance; - bool m_config_audio_stretch; + bool m_audio_fill_gaps = true; Config::ConfigChangedCallbackID m_config_changed_callback_id; }; diff --git a/Source/Core/AudioCommon/OpenALStream.cpp b/Source/Core/AudioCommon/OpenALStream.cpp index cdd44501ca..31ae9b047e 100644 --- a/Source/Core/AudioCommon/OpenALStream.cpp +++ b/Source/Core/AudioCommon/OpenALStream.cpp @@ -293,7 +293,7 @@ void OpenALStream::SoundLoop() if (use_surround) { std::array<float, OAL_MAX_FRAMES * SURROUND_CHANNELS> dpl2; - u32 rendered_frames = m_mixer->MixSurround(dpl2.data(), min_frames); + u32 rendered_frames = static_cast<u32>(m_mixer->MixSurround(dpl2.data(), min_frames)); if (rendered_frames < min_frames) continue; @@ -351,7 +351,7 @@ void OpenALStream::SoundLoop() } else { - u32 rendered_frames = m_mixer->Mix(m_realtime_buffer.data(), min_frames); + u32 rendered_frames = static_cast<u32>(m_mixer->Mix(m_realtime_buffer.data(), min_frames)); if (!rendered_frames) continue; diff --git a/Source/Core/Core/Config/MainSettings.cpp b/Source/Core/Core/Config/MainSettings.cpp index 4159042b2f..6ee56e461a 100644 --- a/Source/Core/Core/Config/MainSettings.cpp +++ b/Source/Core/Core/Config/MainSettings.cpp @@ -56,8 +56,7 @@ const Info<bool> MAIN_DPL2_DECODER{{System::Main, "Core", "DPL2Decoder"}, false} const Info<AudioCommon::DPL2Quality> MAIN_DPL2_QUALITY{{System::Main, "Core", "DPL2Quality"}, AudioCommon::GetDefaultDPL2Quality()}; const Info<int> MAIN_AUDIO_LATENCY{{System::Main, "Core", "AudioLatency"}, 20}; -const Info<bool> MAIN_AUDIO_STRETCH{{System::Main, "Core", "AudioStretch"}, false}; -const Info<int> MAIN_AUDIO_STRETCH_LATENCY{{System::Main, "Core", "AudioStretchMaxLatency"}, 80}; +const Info<bool> MAIN_AUDIO_FILL_GAPS{{System::Main, "Core", "AudioFillGaps"}, true}; const Info<std::string> MAIN_MEMCARD_A_PATH{{System::Main, "Core", "MemcardAPath"}, ""}; const Info<std::string> MAIN_MEMCARD_B_PATH{{System::Main, "Core", "MemcardBPath"}, ""}; const Info<std::string>& GetInfoForMemcardPath(ExpansionInterface::Slot slot) diff --git a/Source/Core/Core/Config/MainSettings.h b/Source/Core/Core/Config/MainSettings.h index 8764a5d91c..748ffac440 100644 --- a/Source/Core/Core/Config/MainSettings.h +++ b/Source/Core/Core/Config/MainSettings.h @@ -72,8 +72,7 @@ extern const Info<bool> MAIN_OVERRIDE_REGION_SETTINGS; extern const Info<bool> MAIN_DPL2_DECODER; extern const Info<AudioCommon::DPL2Quality> MAIN_DPL2_QUALITY; extern const Info<int> MAIN_AUDIO_LATENCY; -extern const Info<bool> MAIN_AUDIO_STRETCH; -extern const Info<int> MAIN_AUDIO_STRETCH_LATENCY; +extern const Info<bool> MAIN_AUDIO_FILL_GAPS; extern const Info<std::string> MAIN_MEMCARD_A_PATH; extern const Info<std::string> MAIN_MEMCARD_B_PATH; const Info<std::string>& GetInfoForMemcardPath(ExpansionInterface::Slot slot); diff --git a/Source/Core/DolphinLib.props b/Source/Core/DolphinLib.props index 2c9437c01c..c8b3b0ed4d 100644 --- a/Source/Core/DolphinLib.props +++ b/Source/Core/DolphinLib.props @@ -2,7 +2,6 @@ <Project> <ItemGroup> <ClInclude Include="AudioCommon\AudioCommon.h" /> - <ClInclude Include="AudioCommon\AudioStretcher.h" /> <ClInclude Include="AudioCommon\CubebStream.h" /> <ClInclude Include="AudioCommon\CubebUtils.h" /> <ClInclude Include="AudioCommon\Enums.h" /> @@ -769,7 +768,6 @@ </ItemGroup> <ItemGroup> <ClCompile Include="AudioCommon\AudioCommon.cpp" /> - <ClCompile Include="AudioCommon\AudioStretcher.cpp" /> <ClCompile Include="AudioCommon\CubebStream.cpp" /> <ClCompile Include="AudioCommon\CubebUtils.cpp" /> <ClCompile Include="AudioCommon\Mixer.cpp" /> diff --git a/Source/Core/DolphinLib.vcxproj b/Source/Core/DolphinLib.vcxproj index 781bb082e6..0d4925c47e 100644 --- a/Source/Core/DolphinLib.vcxproj +++ b/Source/Core/DolphinLib.vcxproj @@ -56,7 +56,6 @@ <Import Project="$(ExternalsDir)rcheevos\exports.props" /> <Import Project="$(ExternalsDir)SDL\exports.props" /> <Import Project="$(ExternalsDir)SFML\exports.props" /> - <Import Project="$(ExternalsDir)soundtouch\exports.props" /> <Import Project="$(ExternalsDir)spirv_cross\exports.props" /> <Import Project="$(ExternalsDir)tinygltf\exports.props" /> <Import Project="$(ExternalsDir)xxhash\exports.props" /> diff --git a/Source/Core/DolphinQt/DolphinQt.vcxproj b/Source/Core/DolphinQt/DolphinQt.vcxproj index edcdc2516a..41349a4cee 100644 --- a/Source/Core/DolphinQt/DolphinQt.vcxproj +++ b/Source/Core/DolphinQt/DolphinQt.vcxproj @@ -482,7 +482,6 @@ <Import Project="$(ExternalsDir)picojson\exports.props" /> <Import Project="$(ExternalsDir)rcheevos\exports.props" /> <Import Project="$(ExternalsDir)SFML\exports.props" /> - <Import Project="$(ExternalsDir)soundtouch\exports.props" /> <Import Project="$(ExternalsDir)xxhash\exports.props" /> <Import Project="$(ExternalsDir)zstd\exports.props" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> diff --git a/Source/Core/DolphinQt/Settings/AudioPane.cpp b/Source/Core/DolphinQt/Settings/AudioPane.cpp index c61245375a..b457fe382a 100644 --- a/Source/Core/DolphinQt/Settings/AudioPane.cpp +++ b/Source/Core/DolphinQt/Settings/AudioPane.cpp @@ -25,6 +25,7 @@ #include "Core/Core.h" #include "Core/System.h" +#include "DolphinQt/Config/ConfigControls/ConfigBool.h" #include "DolphinQt/Config/SettingsWindow.h" #include "DolphinQt/Settings.h" @@ -137,43 +138,29 @@ void AudioPane::CreateWidgets() backend_layout->addRow(dolby_quality_layout); backend_layout->addRow(m_dolby_quality_latency_label); - auto* stretching_box = new QGroupBox(tr("Audio Stretching Settings")); - auto* stretching_layout = new QGridLayout; - m_stretching_enable = new QCheckBox(tr("Enable Audio Stretching")); - m_stretching_buffer_slider = new QSlider(Qt::Horizontal); - m_stretching_buffer_indicator = new QLabel(); - m_stretching_buffer_label = new QLabel(tr("Buffer Size:")); - stretching_box->setLayout(stretching_layout); - - m_stretching_buffer_slider->setMinimum(5); - m_stretching_buffer_slider->setMaximum(300); - - m_stretching_enable->setToolTip(tr("Enables stretching of the audio to match emulation speed.")); - m_stretching_buffer_slider->setToolTip(tr("Size of stretch buffer in milliseconds. " - "Values too low may cause audio crackling.")); - - stretching_layout->addWidget(m_stretching_enable, 0, 0, 1, -1); - stretching_layout->addWidget(m_stretching_buffer_label, 1, 0); - stretching_layout->addWidget(m_stretching_buffer_slider, 1, 1); - stretching_layout->addWidget(m_stretching_buffer_indicator, 1, 2); - dsp_box->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); auto* misc_box = new QGroupBox(tr("Miscellaneous Settings")); auto* misc_layout = new QGridLayout; misc_box->setLayout(misc_layout); - m_speed_up_mute_enable = new QCheckBox(tr("Mute When Disabling Speed Limit")); - m_speed_up_mute_enable->setToolTip( + m_audio_fill_gaps = new ConfigBool(tr("Fill Audio Gaps"), Config::MAIN_AUDIO_FILL_GAPS); + m_audio_fill_gaps->SetDescription( + tr("Repeat existing audio during lag spikes to prevent stuttering." + "<br><br><dolphin_emphasis>If unsure, leave this checked.</dolphin_emphasis>")); + + m_speed_up_mute_enable = new ConfigBool(tr("Mute When Disabling Speed Limit"), + Config::MAIN_AUDIO_MUTE_ON_DISABLED_SPEED_LIMIT); + m_speed_up_mute_enable->SetDescription( tr("Mutes the audio when overriding the emulation speed limit (default hotkey: Tab).")); - misc_layout->addWidget(m_speed_up_mute_enable, 0, 0, 1, 1); + misc_layout->addWidget(m_audio_fill_gaps, 0, 0, 1, 1); + misc_layout->addWidget(m_speed_up_mute_enable, 1, 0, 1, 1); auto* const main_vbox_layout = new QVBoxLayout; main_vbox_layout->addWidget(dsp_box); main_vbox_layout->addWidget(backend_box); - main_vbox_layout->addWidget(stretching_box); main_vbox_layout->addWidget(misc_box); m_main_layout = new QHBoxLayout; @@ -192,14 +179,11 @@ void AudioPane::ConnectWidgets() { connect(m_latency_spin, &QSpinBox::valueChanged, this, &AudioPane::SaveSettings); } - connect(m_stretching_buffer_slider, &QSlider::valueChanged, this, &AudioPane::SaveSettings); connect(m_dolby_pro_logic, &QCheckBox::toggled, this, &AudioPane::SaveSettings); connect(m_dolby_quality_slider, &QSlider::valueChanged, this, &AudioPane::SaveSettings); - connect(m_stretching_enable, &QCheckBox::toggled, this, &AudioPane::SaveSettings); connect(m_dsp_hle, &QRadioButton::toggled, this, &AudioPane::SaveSettings); connect(m_dsp_lle, &QRadioButton::toggled, this, &AudioPane::SaveSettings); connect(m_dsp_interpreter, &QRadioButton::toggled, this, &AudioPane::SaveSettings); - connect(m_speed_up_mute_enable, &QCheckBox::toggled, this, &AudioPane::SaveSettings); #ifdef _WIN32 connect(m_wasapi_device_combo, &QComboBox::currentIndexChanged, this, &AudioPane::SaveSettings); @@ -255,17 +239,6 @@ void AudioPane::LoadSettings() if (m_latency_control_supported) m_latency_spin->setValue(Config::Get(Config::MAIN_AUDIO_LATENCY)); - // Stretch - m_stretching_enable->setChecked(Config::Get(Config::MAIN_AUDIO_STRETCH)); - m_stretching_buffer_label->setEnabled(m_stretching_enable->isChecked()); - m_stretching_buffer_slider->setValue(Config::Get(Config::MAIN_AUDIO_STRETCH_LATENCY)); - m_stretching_buffer_slider->setEnabled(m_stretching_enable->isChecked()); - m_stretching_buffer_indicator->setEnabled(m_stretching_enable->isChecked()); - m_stretching_buffer_indicator->setText(tr("%1 ms").arg(m_stretching_buffer_slider->value())); - - // Misc - m_speed_up_mute_enable->setChecked(Config::Get(Config::MAIN_AUDIO_MUTE_ON_DISABLED_SPEED_LIMIT)); - #ifdef _WIN32 if (Config::Get(Config::MAIN_WASAPI_DEVICE) == "default") { @@ -326,16 +299,8 @@ void AudioPane::SaveSettings() if (m_latency_control_supported) Config::SetBaseOrCurrent(Config::MAIN_AUDIO_LATENCY, m_latency_spin->value()); - // Stretch - Config::SetBaseOrCurrent(Config::MAIN_AUDIO_STRETCH, m_stretching_enable->isChecked()); - Config::SetBaseOrCurrent(Config::MAIN_AUDIO_STRETCH_LATENCY, m_stretching_buffer_slider->value()); - m_stretching_buffer_label->setEnabled(m_stretching_enable->isChecked()); - m_stretching_buffer_slider->setEnabled(m_stretching_enable->isChecked()); - m_stretching_buffer_indicator->setEnabled(m_stretching_enable->isChecked()); - m_stretching_buffer_indicator->setText( - tr("%1 ms").arg(Config::Get(Config::MAIN_AUDIO_STRETCH_LATENCY))); - // Misc + Config::SetBaseOrCurrent(Config::MAIN_AUDIO_FILL_GAPS, m_audio_fill_gaps->isChecked()); Config::SetBaseOrCurrent(Config::MAIN_AUDIO_MUTE_ON_DISABLED_SPEED_LIMIT, m_speed_up_mute_enable->isChecked()); diff --git a/Source/Core/DolphinQt/Settings/AudioPane.h b/Source/Core/DolphinQt/Settings/AudioPane.h index 493b067939..825865eafe 100644 --- a/Source/Core/DolphinQt/Settings/AudioPane.h +++ b/Source/Core/DolphinQt/Settings/AudioPane.h @@ -18,6 +18,7 @@ class QRadioButton; class QSlider; class QSpinBox; class SettingsWindow; +class ConfigBool; class AudioPane final : public QWidget { @@ -71,12 +72,7 @@ private: QComboBox* m_wasapi_device_combo; #endif - // Audio Stretching - QCheckBox* m_stretching_enable; - QLabel* m_stretching_buffer_label; - QSlider* m_stretching_buffer_slider; - QLabel* m_stretching_buffer_indicator; - // Misc Settings - QCheckBox* m_speed_up_mute_enable; + ConfigBool* m_audio_fill_gaps; + ConfigBool* m_speed_up_mute_enable; }; diff --git a/Source/dolphin-emu.sln b/Source/dolphin-emu.sln index ca61c67d7e..e11ef45992 100644 --- a/Source/dolphin-emu.sln +++ b/Source/dolphin-emu.sln @@ -33,8 +33,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xxhash", "..\Externals\xxha EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib-ng", "..\Externals\zlib-ng\zlib-ng.vcxproj", "{F6EA7144-8D64-4EBB-A13E-76DFBD911EAE}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SoundTouch", "..\Externals\soundtouch\SoundTouch.vcxproj", "{EC082900-B4D8-42E9-9663-77F02F6936AE}" -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mbedTLS", "..\Externals\mbedtls\mbedTLS.vcxproj", "{BDB6578B-0691-4E80-A46C-DF21639FD3B8}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SCMRevGen", "Core\Common\SCMRevGen.vcxproj", "{41279555-F94F-4EBC-99DE-AF863C10C5C4}" @@ -209,14 +207,6 @@ Global {F6EA7144-8D64-4EBB-A13E-76DFBD911EAE}.Release|ARM64.Build.0 = Release|ARM64 {F6EA7144-8D64-4EBB-A13E-76DFBD911EAE}.Release|x64.ActiveCfg = Release|x64 {F6EA7144-8D64-4EBB-A13E-76DFBD911EAE}.Release|x64.Build.0 = Release|x64 - {EC082900-B4D8-42E9-9663-77F02F6936AE}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {EC082900-B4D8-42E9-9663-77F02F6936AE}.Debug|ARM64.Build.0 = Debug|ARM64 - {EC082900-B4D8-42E9-9663-77F02F6936AE}.Debug|x64.ActiveCfg = Debug|x64 - {EC082900-B4D8-42E9-9663-77F02F6936AE}.Debug|x64.Build.0 = Debug|x64 - {EC082900-B4D8-42E9-9663-77F02F6936AE}.Release|ARM64.ActiveCfg = Release|ARM64 - {EC082900-B4D8-42E9-9663-77F02F6936AE}.Release|ARM64.Build.0 = Release|ARM64 - {EC082900-B4D8-42E9-9663-77F02F6936AE}.Release|x64.ActiveCfg = Release|x64 - {EC082900-B4D8-42E9-9663-77F02F6936AE}.Release|x64.Build.0 = Release|x64 {BDB6578B-0691-4E80-A46C-DF21639FD3B8}.Debug|ARM64.ActiveCfg = Debug|ARM64 {BDB6578B-0691-4E80-A46C-DF21639FD3B8}.Debug|ARM64.Build.0 = Debug|ARM64 {BDB6578B-0691-4E80-A46C-DF21639FD3B8}.Debug|x64.ActiveCfg = Debug|x64 @@ -460,7 +450,6 @@ Global {31643FDB-1BB8-4965-9DE7-000FC88D35AE} = {87ADDFF9-5768-4DA2-A33B-2477593D6677} {677EA016-1182-440C-9345-DC88D1E98C0C} = {87ADDFF9-5768-4DA2-A33B-2477593D6677} {F6EA7144-8D64-4EBB-A13E-76DFBD911EAE} = {87ADDFF9-5768-4DA2-A33B-2477593D6677} - {EC082900-B4D8-42E9-9663-77F02F6936AE} = {87ADDFF9-5768-4DA2-A33B-2477593D6677} {BDB6578B-0691-4E80-A46C-DF21639FD3B8} = {87ADDFF9-5768-4DA2-A33B-2477593D6677} {93D73454-2512-424E-9CDA-4BB357FE13DD} = {87ADDFF9-5768-4DA2-A33B-2477593D6677} {349EE8F9-7D25-4909-AAF5-FF3FADE72187} = {87ADDFF9-5768-4DA2-A33B-2477593D6677} |
