mirror of
https://github.com/citra-emu/citra.git
synced 2024-11-25 07:20:15 +00:00
parent
69effbcb6e
commit
28f64f98f7
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -7,6 +7,3 @@
|
||||
[submodule "nihstro"]
|
||||
path = externals/nihstro
|
||||
url = https://github.com/neobrain/nihstro.git
|
||||
[submodule "rubberband"]
|
||||
path = externals/rubberband/rubberband
|
||||
url = https://github.com/breakfastquay/rubberband.git
|
||||
|
@ -148,7 +148,6 @@ if (ENABLE_SDL2)
|
||||
download_bundled_external("sdl2/" ${SDL2_VER} SDL2_PREFIX)
|
||||
endif()
|
||||
|
||||
set(SDL2_FOUND YES)
|
||||
set(SDL2_INCLUDE_DIR "${SDL2_PREFIX}/include" CACHE PATH "Path to SDL2 headers")
|
||||
set(SDL2_LIBRARY "${SDL2_PREFIX}/lib/x64/SDL2.lib" CACHE PATH "Path to SDL2 library")
|
||||
set(SDL2_DLL_DIR "${SDL2_PREFIX}/lib/x64/" CACHE PATH "Path to SDL2.dll")
|
||||
@ -241,15 +240,11 @@ if (MSVC)
|
||||
add_subdirectory(externals/getopt)
|
||||
endif()
|
||||
|
||||
# process subdirectories
|
||||
if(ENABLE_QT)
|
||||
include_directories(externals/qhexedit)
|
||||
add_subdirectory(externals/qhexedit)
|
||||
endif()
|
||||
|
||||
add_subdirectory(externals/soundtouch)
|
||||
add_subdirectory(externals/rubberband)
|
||||
|
||||
# process subdirectories
|
||||
add_subdirectory(src)
|
||||
|
||||
# Install freedesktop.org metadata files, following those specifications:
|
||||
|
2
externals/boost
vendored
2
externals/boost
vendored
@ -1 +1 @@
|
||||
Subproject commit 2dcb9d979665b6aabb1635c617973e02914e60ec
|
||||
Subproject commit d81b9269900ae183d0dc98403eea4c971590a807
|
79
externals/rubberband/CMakeLists.txt
vendored
79
externals/rubberband/CMakeLists.txt
vendored
@ -1,79 +0,0 @@
|
||||
set(SRCS
|
||||
rubberband/src/audiocurves/CompoundAudioCurve.cpp
|
||||
rubberband/src/audiocurves/ConstantAudioCurve.cpp
|
||||
rubberband/src/audiocurves/HighFrequencyAudioCurve.cpp
|
||||
rubberband/src/audiocurves/PercussiveAudioCurve.cpp
|
||||
rubberband/src/audiocurves/SilentAudioCurve.cpp
|
||||
rubberband/src/audiocurves/SpectralDifferenceAudioCurve.cpp
|
||||
rubberband/src/base/Profiler.cpp
|
||||
rubberband/src/dsp/AudioCurveCalculator.cpp
|
||||
rubberband/src/dsp/FFT.cpp
|
||||
rubberband/src/dsp/Resampler.cpp
|
||||
rubberband/src/kissfft/kiss_fft.c
|
||||
rubberband/src/kissfft/kiss_fftr.c
|
||||
rubberband/src/RubberBandStretcher.cpp
|
||||
rubberband/src/speex/resample.c
|
||||
rubberband/src/StretchCalculator.cpp
|
||||
rubberband/src/StretcherChannelData.cpp
|
||||
rubberband/src/StretcherImpl.cpp
|
||||
rubberband/src/StretcherProcess.cpp
|
||||
rubberband/src/system/Allocators.cpp
|
||||
rubberband/src/system/sysutils.cpp
|
||||
rubberband/src/system/Thread.cpp
|
||||
rubberband/src/system/VectorOpsComplex.cpp
|
||||
)
|
||||
|
||||
SET(HEADERS
|
||||
rubberband/src/audiocurves/CompoundAudioCurve.h
|
||||
rubberband/src/audiocurves/ConstantAudioCurve.h
|
||||
rubberband/src/audiocurves/HighFrequencyAudioCurve.h
|
||||
rubberband/src/audiocurves/PercussiveAudioCurve.h
|
||||
rubberband/src/audiocurves/SilentAudioCurve.h
|
||||
rubberband/src/audiocurves/SpectralDifferenceAudioCurve.h
|
||||
rubberband/src/base/Profiler.h
|
||||
rubberband/src/base/RingBuffer.h
|
||||
rubberband/src/base/Scavenger.h
|
||||
rubberband/src/dsp/AudioCurveCalculator.h
|
||||
rubberband/src/dsp/FFT.h
|
||||
rubberband/src/dsp/MovingMedian.h
|
||||
rubberband/src/dsp/Resampler.h
|
||||
rubberband/src/dsp/SampleFilter.h
|
||||
rubberband/src/dsp/SincWindow.h
|
||||
rubberband/src/dsp/Window.h
|
||||
rubberband/src/float_cast/float_cast.h
|
||||
rubberband/src/kissfft/kiss_fft.h
|
||||
rubberband/src/kissfft/kiss_fftr.h
|
||||
rubberband/src/kissfft/_kiss_fft_guts.h
|
||||
rubberband/src/pommier/neon_mathfun.h
|
||||
rubberband/src/pommier/sse_mathfun.h
|
||||
rubberband/src/speex/speex_resampler.h
|
||||
rubberband/src/StretchCalculator.h
|
||||
rubberband/src/StretcherChannelData.h
|
||||
rubberband/src/StretcherImpl.h
|
||||
rubberband/src/system/Allocators.h
|
||||
rubberband/src/system/sysutils.h
|
||||
rubberband/src/system/Thread.h
|
||||
rubberband/src/system/VectorOps.h
|
||||
rubberband/src/system/VectorOpsComplex.h
|
||||
rubberband/rubberband/rubberband-c.h
|
||||
rubberband/rubberband/RubberBandStretcher.h
|
||||
)
|
||||
|
||||
add_library(rubberband STATIC ${SRCS} ${HEADERS})
|
||||
|
||||
target_include_directories(rubberband PRIVATE rubberband/src)
|
||||
target_include_directories(rubberband PRIVATE rubberband)
|
||||
|
||||
set_property(TARGET rubberband APPEND PROPERTY COMPILE_DEFINITIONS USE_SPEEX)
|
||||
if(APPLE)
|
||||
set_property(TARGET rubberband APPEND PROPERTY COMPILE_DEFINITIONS HAVE_VDSP)
|
||||
set_property(TARGET rubberband APPEND PROPERTY COMPILE_DEFINITIONS USE_PTHREADS)
|
||||
target_link_libraries(rubberband "-framework Accelerate")
|
||||
elseif(MSVC)
|
||||
set_property(TARGET rubberband APPEND PROPERTY COMPILE_DEFINITIONS USE_KISSFFT)
|
||||
set_property(TARGET rubberband APPEND PROPERTY COMPILE_DEFINITIONS __MSVC__)
|
||||
set_property(TARGET rubberband APPEND PROPERTY COMPILE_DEFINITIONS WIN32)
|
||||
else()
|
||||
set_property(TARGET rubberband APPEND PROPERTY COMPILE_DEFINITIONS USE_KISSFFT)
|
||||
set_property(TARGET rubberband APPEND PROPERTY COMPILE_DEFINITIONS USE_PTHREADS)
|
||||
endif()
|
1
externals/rubberband/rubberband
vendored
1
externals/rubberband/rubberband
vendored
@ -1 +0,0 @@
|
||||
Subproject commit c93a18535ffea1ca7b18eb41c34064b77f8419e3
|
236
externals/soundtouch/AAFilter.cpp
vendored
236
externals/soundtouch/AAFilter.cpp
vendored
@ -1,236 +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
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2014-01-05 23:40:22 +0200 (Sun, 05 Jan 2014) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: AAFilter.cpp 177 2014-01-05 21:40:22Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// 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.141592655357989
|
||||
#define TWOPI (2 * PI)
|
||||
|
||||
// define this to save AA filter coefficients to a file
|
||||
// #define _DEBUG_SAVE_AAFILTER_COEFFICIENTS 1
|
||||
|
||||
#ifdef _DEBUG_SAVE_AAFILTER_COEFFICIENTS
|
||||
#include <stdio.h>
|
||||
|
||||
static void _DEBUG_SAVE_AAFIR_COEFFS(SAMPLETYPE *coeffs, int len)
|
||||
{
|
||||
FILE *fptr = fopen("aa_filter_coeffs.txt", "wt");
|
||||
if (fptr == NULL) return;
|
||||
|
||||
for (int i = 0; i < len; i ++)
|
||||
{
|
||||
double temp = coeffs[i];
|
||||
fprintf(fptr, "%lf\n", temp);
|
||||
}
|
||||
fclose(fptr);
|
||||
}
|
||||
|
||||
#else
|
||||
#define _DEBUG_SAVE_AAFIR_COEFFS(x, y)
|
||||
#endif
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* Implementation of the class 'AAFilter'
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
AAFilter::AAFilter(uint len)
|
||||
{
|
||||
pFIR = FIRFilter::newInstance();
|
||||
cutoffFreq = 0.5;
|
||||
setLength(len);
|
||||
}
|
||||
|
||||
|
||||
|
||||
AAFilter::~AAFilter()
|
||||
{
|
||||
delete pFIR;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets new anti-alias filter cut-off edge frequency, scaled to
|
||||
// sampling frequency (nyquist frequency = 0.5).
|
||||
// The filter will cut frequencies higher than the given frequency.
|
||||
void AAFilter::setCutoffFreq(double newCutoffFreq)
|
||||
{
|
||||
cutoffFreq = newCutoffFreq;
|
||||
calculateCoeffs();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets number of FIR filter taps
|
||||
void AAFilter::setLength(uint newLength)
|
||||
{
|
||||
length = newLength;
|
||||
calculateCoeffs();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Calculates coefficients for a low-pass FIR filter using Hamming window
|
||||
void AAFilter::calculateCoeffs()
|
||||
{
|
||||
uint i;
|
||||
double cntTemp, temp, tempCoeff,h, w;
|
||||
double wc;
|
||||
double scaleCoeff, sum;
|
||||
double *work;
|
||||
SAMPLETYPE *coeffs;
|
||||
|
||||
assert(length >= 2);
|
||||
assert(length % 4 == 0);
|
||||
assert(cutoffFreq >= 0);
|
||||
assert(cutoffFreq <= 0.5);
|
||||
|
||||
work = new double[length];
|
||||
coeffs = new SAMPLETYPE[length];
|
||||
|
||||
wc = 2.0 * PI * cutoffFreq;
|
||||
tempCoeff = TWOPI / (double)length;
|
||||
|
||||
sum = 0;
|
||||
for (i = 0; i < length; i ++)
|
||||
{
|
||||
cntTemp = (double)i - (double)(length / 2);
|
||||
|
||||
temp = cntTemp * wc;
|
||||
if (temp != 0)
|
||||
{
|
||||
h = sin(temp) / temp; // sinc function
|
||||
}
|
||||
else
|
||||
{
|
||||
h = 1.0;
|
||||
}
|
||||
w = 0.54 + 0.46 * cos(tempCoeff * cntTemp); // hamming window
|
||||
|
||||
temp = w * h;
|
||||
work[i] = temp;
|
||||
|
||||
// calc net sum of coefficients
|
||||
sum += temp;
|
||||
}
|
||||
|
||||
// ensure the sum of coefficients is larger than zero
|
||||
assert(sum > 0);
|
||||
|
||||
// ensure we've really designed a lowpass filter...
|
||||
assert(work[length/2] > 0);
|
||||
assert(work[length/2 + 1] > -1e-6);
|
||||
assert(work[length/2 - 1] > -1e-6);
|
||||
|
||||
// Calculate a scaling coefficient in such a way that the result can be
|
||||
// divided by 16384
|
||||
scaleCoeff = 16384.0f / sum;
|
||||
|
||||
for (i = 0; i < length; i ++)
|
||||
{
|
||||
temp = work[i] * scaleCoeff;
|
||||
//#if SOUNDTOUCH_INTEGER_SAMPLES
|
||||
// scale & round to nearest integer
|
||||
temp += (temp >= 0) ? 0.5 : -0.5;
|
||||
// ensure no overfloods
|
||||
assert(temp >= -32768 && temp <= 32767);
|
||||
//#endif
|
||||
coeffs[i] = (SAMPLETYPE)temp;
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
100
externals/soundtouch/AAFilter.h
vendored
100
externals/soundtouch/AAFilter.h
vendored
@ -1,100 +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
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2014-01-07 21:41:23 +0200 (Tue, 07 Jan 2014) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: AAFilter.h 187 2014-01-07 19:41:23Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// 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
|
371
externals/soundtouch/BPMDetect.cpp
vendored
371
externals/soundtouch/BPMDetect.cpp
vendored
@ -1,371 +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
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2015-02-21 23:24:29 +0200 (Sat, 21 Feb 2015) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: BPMDetect.cpp 202 2015-02-21 21:24:29Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// 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 "FIFOSampleBuffer.h"
|
||||
#include "PeakFinder.h"
|
||||
#include "BPMDetect.h"
|
||||
|
||||
using namespace soundtouch;
|
||||
|
||||
#define INPUT_BLOCK_SAMPLES 2048
|
||||
#define DECIMATED_BLOCK_SAMPLES 256
|
||||
|
||||
/// decay constant for calculating RMS volume sliding average approximation
|
||||
/// (time constant is about 10 sec)
|
||||
const float avgdecay = 0.99986f;
|
||||
|
||||
/// Normalization coefficient for calculating RMS sliding average approximation.
|
||||
const float avgnorm = (1 - avgdecay);
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Enable following define to create bpm analysis file:
|
||||
|
||||
// #define _CREATE_BPM_DEBUG_FILE
|
||||
|
||||
#ifdef _CREATE_BPM_DEBUG_FILE
|
||||
|
||||
#define DEBUGFILE_NAME "c:\\temp\\soundtouch-bpm-debug.txt"
|
||||
|
||||
static void _SaveDebugData(const float *data, int minpos, int maxpos, double coeff)
|
||||
{
|
||||
FILE *fptr = fopen(DEBUGFILE_NAME, "wt");
|
||||
int i;
|
||||
|
||||
if (fptr)
|
||||
{
|
||||
printf("\n\nWriting BPM debug data into file " DEBUGFILE_NAME "\n\n");
|
||||
for (i = minpos; i < maxpos; i ++)
|
||||
{
|
||||
fprintf(fptr, "%d\t%.1lf\t%f\n", i, coeff / (double)i, data[i]);
|
||||
}
|
||||
fclose(fptr);
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define _SaveDebugData(a,b,c,d)
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
BPMDetect::BPMDetect(int numChannels, int aSampleRate)
|
||||
{
|
||||
this->sampleRate = aSampleRate;
|
||||
this->channels = numChannels;
|
||||
|
||||
decimateSum = 0;
|
||||
decimateCount = 0;
|
||||
|
||||
envelopeAccu = 0;
|
||||
|
||||
// Initialize RMS volume accumulator to RMS level of 1500 (out of 32768) that's
|
||||
// safe initial RMS signal level value for song data. This value is then adapted
|
||||
// to the actual level during processing.
|
||||
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||
// integer samples
|
||||
RMSVolumeAccu = (1500 * 1500) / avgnorm;
|
||||
#else
|
||||
// float samples, scaled to range [-1..+1[
|
||||
RMSVolumeAccu = (0.045f * 0.045f) / avgnorm;
|
||||
#endif
|
||||
|
||||
// choose decimation factor so that result is approx. 1000 Hz
|
||||
decimateBy = sampleRate / 1000;
|
||||
assert(decimateBy > 0);
|
||||
assert(INPUT_BLOCK_SAMPLES < decimateBy * DECIMATED_BLOCK_SAMPLES);
|
||||
|
||||
// Calculate window length & starting item according to desired min & max bpms
|
||||
windowLen = (60 * sampleRate) / (decimateBy * MIN_BPM);
|
||||
windowStart = (60 * sampleRate) / (decimateBy * MAX_BPM);
|
||||
|
||||
assert(windowLen > windowStart);
|
||||
|
||||
// allocate new working objects
|
||||
xcorr = new float[windowLen];
|
||||
memset(xcorr, 0, windowLen * sizeof(float));
|
||||
|
||||
// allocate processing buffer
|
||||
buffer = new FIFOSampleBuffer();
|
||||
// we do processing in mono mode
|
||||
buffer->setChannels(1);
|
||||
buffer->clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
BPMDetect::~BPMDetect()
|
||||
{
|
||||
delete[] xcorr;
|
||||
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));
|
||||
|
||||
pBuffer = buffer->ptrBegin();
|
||||
#pragma omp parallel for
|
||||
for (offs = windowStart; offs < windowLen; offs ++)
|
||||
{
|
||||
LONG_SAMPLETYPE sum;
|
||||
int i;
|
||||
|
||||
sum = 0;
|
||||
for (i = 0; i < process_samples; i ++)
|
||||
{
|
||||
sum += pBuffer[i] * pBuffer[i + offs]; // scaling the sub-result shouldn't be necessary
|
||||
}
|
||||
// xcorr[offs] *= xcorr_decay; // decay 'xcorr' here with suitable coefficients
|
||||
// if it's desired that the system adapts automatically to
|
||||
// various bpms, e.g. in processing continouos music stream.
|
||||
// The 'xcorr_decay' should be a value that's smaller than but
|
||||
// close to one, and should also depend on 'process_samples' value.
|
||||
|
||||
xcorr[offs] += (float)sum;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Calculates envelope of the sample data
|
||||
void BPMDetect::calcEnvelope(SAMPLETYPE *samples, int numsamples)
|
||||
{
|
||||
const static double decay = 0.7f; // decay constant for smoothing the envelope
|
||||
const static double norm = (1 - decay);
|
||||
|
||||
int i;
|
||||
LONG_SAMPLETYPE out;
|
||||
double val;
|
||||
|
||||
for (i = 0; i < numsamples; i ++)
|
||||
{
|
||||
// calc average RMS volume
|
||||
RMSVolumeAccu *= avgdecay;
|
||||
val = (float)fabs((float)samples[i]);
|
||||
RMSVolumeAccu += val * val;
|
||||
|
||||
// cut amplitudes that are below cutoff ~2 times RMS volume
|
||||
// (we're interested in peak values, not the silent moments)
|
||||
if (val < 0.5 * sqrt(RMSVolumeAccu * avgnorm))
|
||||
{
|
||||
val = 0;
|
||||
}
|
||||
|
||||
// smooth amplitude envelope
|
||||
envelopeAccu *= decay;
|
||||
envelopeAccu += val;
|
||||
out = (LONG_SAMPLETYPE)(envelopeAccu * norm);
|
||||
|
||||
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||
// cut peaks (shouldn't be necessary though)
|
||||
if (out > 32767) out = 32767;
|
||||
#endif // SOUNDTOUCH_INTEGER_SAMPLES
|
||||
samples[i] = (SAMPLETYPE)out;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void BPMDetect::inputSamples(const SAMPLETYPE *samples, int numSamples)
|
||||
{
|
||||
SAMPLETYPE decimated[DECIMATED_BLOCK_SAMPLES];
|
||||
|
||||
// iterate so that max INPUT_BLOCK_SAMPLES processed per iteration
|
||||
while (numSamples > 0)
|
||||
{
|
||||
int block;
|
||||
int decSamples;
|
||||
|
||||
block = (numSamples > INPUT_BLOCK_SAMPLES) ? INPUT_BLOCK_SAMPLES : numSamples;
|
||||
|
||||
// decimate. note that converts to mono at the same time
|
||||
decSamples = decimate(decimated, samples, block);
|
||||
samples += block * channels;
|
||||
numSamples -= block;
|
||||
|
||||
// envelope new samples and add them to buffer
|
||||
calcEnvelope(decimated, decSamples);
|
||||
buffer->putSamples(decimated, decSamples);
|
||||
}
|
||||
|
||||
// when the buffer has enought samples for processing...
|
||||
if ((int)buffer->numSamples() > windowLen)
|
||||
{
|
||||
int processLength;
|
||||
|
||||
// how many samples are processed
|
||||
processLength = (int)buffer->numSamples() - windowLen;
|
||||
|
||||
// ... calculate autocorrelations for oldest samples...
|
||||
updateXCorr(processLength);
|
||||
// ... and remove them from the buffer
|
||||
buffer->receiveSamples(processLength);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void BPMDetect::removeBias()
|
||||
{
|
||||
int i;
|
||||
float minval = 1e12f; // arbitrary large number
|
||||
|
||||
for (i = windowStart; i < windowLen; i ++)
|
||||
{
|
||||
if (xcorr[i] < minval)
|
||||
{
|
||||
minval = xcorr[i];
|
||||
}
|
||||
}
|
||||
|
||||
for (i = windowStart; i < windowLen; i ++)
|
||||
{
|
||||
xcorr[i] -= minval;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
float BPMDetect::getBpm()
|
||||
{
|
||||
double peakPos;
|
||||
double coeff;
|
||||
PeakFinder peakFinder;
|
||||
|
||||
coeff = 60.0 * ((double)sampleRate / (double)decimateBy);
|
||||
|
||||
// save bpm debug analysis data if debug data enabled
|
||||
_SaveDebugData(xcorr, windowStart, windowLen, coeff);
|
||||
|
||||
// remove bias from xcorr data
|
||||
removeBias();
|
||||
|
||||
// find peak position
|
||||
peakPos = peakFinder.detectPeak(xcorr, windowStart, windowLen);
|
||||
|
||||
assert(decimateBy != 0);
|
||||
if (peakPos < 1e-9) return 0.0; // detection failed.
|
||||
|
||||
// calculate BPM
|
||||
return (float) (coeff / peakPos);
|
||||
}
|
164
externals/soundtouch/BPMDetect.h
vendored
164
externals/soundtouch/BPMDetect.h
vendored
@ -1,164 +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
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2012-08-30 22:53:44 +0300 (Thu, 30 Aug 2012) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: BPMDetect.h 150 2012-08-30 19:53:44Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// 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 "STTypes.h"
|
||||
#include "FIFOSampleBuffer.h"
|
||||
|
||||
namespace soundtouch
|
||||
{
|
||||
|
||||
/// Minimum allowed BPM rate. Used to restrict accepted result above a reasonable limit.
|
||||
#define MIN_BPM 29
|
||||
|
||||
/// Maximum allowed BPM rate. Used to restrict accepted result below a reasonable limit.
|
||||
#define MAX_BPM 200
|
||||
|
||||
|
||||
/// Class for calculating BPM rate for audio data.
|
||||
class BPMDetect
|
||||
{
|
||||
protected:
|
||||
/// Auto-correlation accumulator bins.
|
||||
float *xcorr;
|
||||
|
||||
/// Amplitude envelope sliding average approximation level accumulator
|
||||
double envelopeAccu;
|
||||
|
||||
/// RMS volume sliding average approximation level accumulator
|
||||
double RMSVolumeAccu;
|
||||
|
||||
/// Sample average counter.
|
||||
int decimateCount;
|
||||
|
||||
/// Sample average accumulator for FIFO-like decimation.
|
||||
soundtouch::LONG_SAMPLETYPE decimateSum;
|
||||
|
||||
/// Decimate sound by this coefficient to reach approx. 500 Hz.
|
||||
int decimateBy;
|
||||
|
||||
/// Auto-correlation window length
|
||||
int windowLen;
|
||||
|
||||
/// Number of channels (1 = mono, 2 = stereo)
|
||||
int channels;
|
||||
|
||||
/// sample rate
|
||||
int sampleRate;
|
||||
|
||||
/// Beginning of auto-correlation window: Autocorrelation isn't being updated for
|
||||
/// the first these many correlation bins.
|
||||
int windowStart;
|
||||
|
||||
/// FIFO-buffer for decimated processing samples.
|
||||
soundtouch::FIFOSampleBuffer *buffer;
|
||||
|
||||
/// Updates auto-correlation function for given number of decimated samples that
|
||||
/// are read from the internal 'buffer' pipe (samples aren't removed from the pipe
|
||||
/// though).
|
||||
void updateXCorr(int process_samples /// How many samples are processed.
|
||||
);
|
||||
|
||||
/// Decimates samples to approx. 500 Hz.
|
||||
///
|
||||
/// \return Number of output samples.
|
||||
int decimate(soundtouch::SAMPLETYPE *dest, ///< Destination buffer
|
||||
const soundtouch::SAMPLETYPE *src, ///< Source sample buffer
|
||||
int numsamples ///< Number of source samples.
|
||||
);
|
||||
|
||||
/// Calculates amplitude envelope for the buffer of samples.
|
||||
/// Result is output to 'samples'.
|
||||
void calcEnvelope(soundtouch::SAMPLETYPE *samples, ///< Pointer to input/output data buffer
|
||||
int numsamples ///< Number of samples in buffer
|
||||
);
|
||||
|
||||
/// remove constant bias from xcorr data
|
||||
void removeBias();
|
||||
|
||||
public:
|
||||
/// Constructor.
|
||||
BPMDetect(int numChannels, ///< Number of channels in sample data.
|
||||
int sampleRate ///< Sample rate in Hz.
|
||||
);
|
||||
|
||||
/// Destructor.
|
||||
virtual ~BPMDetect();
|
||||
|
||||
/// Inputs a block of samples for analyzing: Envelopes the samples and then
|
||||
/// updates the autocorrelation estimation. When whole song data has been input
|
||||
/// in smaller blocks using this function, read the resulting bpm with 'getBpm'
|
||||
/// function.
|
||||
///
|
||||
/// Notice that data in 'samples' array can be disrupted in processing.
|
||||
void inputSamples(const soundtouch::SAMPLETYPE *samples, ///< Pointer to input/working data buffer
|
||||
int numSamples ///< Number of samples in buffer
|
||||
);
|
||||
|
||||
|
||||
/// Analyzes the results and returns the BPM rate. Use this function to read result
|
||||
/// after whole song data has been input to the class by consecutive calls of
|
||||
/// 'inputSamples' function.
|
||||
///
|
||||
/// \return Beats-per-minute rate, or zero if detection failed.
|
||||
float getBpm();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // _BPMDetect_H_
|
18
externals/soundtouch/CMakeLists.txt
vendored
18
externals/soundtouch/CMakeLists.txt
vendored
@ -1,18 +0,0 @@
|
||||
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})
|
274
externals/soundtouch/FIFOSampleBuffer.cpp
vendored
274
externals/soundtouch/FIFOSampleBuffer.cpp
vendored
@ -1,274 +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
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2012-11-08 20:53:01 +0200 (Thu, 08 Nov 2012) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: FIFOSampleBuffer.cpp 160 2012-11-08 18:53:01Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// 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 = NULL;
|
||||
bufferUnaligned = NULL;
|
||||
samplesInBuffer = 0;
|
||||
bufferPos = 0;
|
||||
channels = (uint)numChannels;
|
||||
ensureCapacity(32); // allocate initial capacity
|
||||
}
|
||||
|
||||
|
||||
// destructor
|
||||
FIFOSampleBuffer::~FIFOSampleBuffer()
|
||||
{
|
||||
delete[] bufferUnaligned;
|
||||
bufferUnaligned = NULL;
|
||||
buffer = NULL;
|
||||
}
|
||||
|
||||
|
||||
// Sets number of channels, 1 = mono, 2 = stereo
|
||||
void FIFOSampleBuffer::setChannels(int numChannels)
|
||||
{
|
||||
uint usedBytes;
|
||||
|
||||
assert(numChannels > 0);
|
||||
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
|
||||
// succesfully 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 enought 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 == NULL)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
184
externals/soundtouch/FIFOSampleBuffer.h
vendored
184
externals/soundtouch/FIFOSampleBuffer.h
vendored
@ -1,184 +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
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2014-01-05 23:40:22 +0200 (Sun, 05 Jan 2014) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: FIFOSampleBuffer.h 177 2014-01-05 21:40:22Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// 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();
|
||||
|
||||
/// 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();
|
||||
|
||||
/// 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 succesfully 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.
|
||||
);
|
||||
|
||||
/// 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.
|
||||
);
|
||||
|
||||
/// 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.
|
||||
);
|
||||
|
||||
/// Returns number of samples currently available.
|
||||
virtual uint numSamples() const;
|
||||
|
||||
/// 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;
|
||||
|
||||
/// Clears all the samples.
|
||||
virtual void clear();
|
||||
|
||||
/// allow trimming (downwards) amount of samples in pipeline.
|
||||
/// Returns adjusted amount of samples
|
||||
uint adjustAmountOfSamples(uint numSamples);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
234
externals/soundtouch/FIFOSamplePipe.h
vendored
234
externals/soundtouch/FIFOSamplePipe.h
vendored
@ -1,234 +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
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2012-06-13 22:29:53 +0300 (Wed, 13 Jun 2012) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: FIFOSamplePipe.h 143 2012-06-13 19:29:53Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// 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
|
||||
{
|
||||
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 == NULL);
|
||||
assert(pOutput != NULL);
|
||||
output = pOutput;
|
||||
}
|
||||
|
||||
|
||||
/// Constructor. Doesn't define output pipe; it has to be set be
|
||||
/// 'setOutPipe' function.
|
||||
FIFOProcessor()
|
||||
{
|
||||
output = NULL;
|
||||
}
|
||||
|
||||
|
||||
/// Constructor. Configures output pipe.
|
||||
FIFOProcessor(FIFOSamplePipe *pOutput ///< Output pipe.
|
||||
)
|
||||
{
|
||||
output = pOutput;
|
||||
}
|
||||
|
||||
|
||||
/// Destructor.
|
||||
virtual ~FIFOProcessor()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/// Returns a pointer to the beginning of the output samples.
|
||||
/// This function is provided for accessing the output samples directly.
|
||||
/// Please be careful for not to corrupt the book-keeping!
|
||||
///
|
||||
/// 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()
|
||||
{
|
||||
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.
|
||||
)
|
||||
{
|
||||
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.
|
||||
)
|
||||
{
|
||||
return output->receiveSamples(maxSamples);
|
||||
}
|
||||
|
||||
|
||||
/// Returns number of samples currently available.
|
||||
virtual uint numSamples() const
|
||||
{
|
||||
return output->numSamples();
|
||||
}
|
||||
|
||||
|
||||
/// Returns nonzero if there aren't any samples available for outputting.
|
||||
virtual int isEmpty() const
|
||||
{
|
||||
return output->isEmpty();
|
||||
}
|
||||
|
||||
/// allow trimming (downwards) amount of samples in pipeline.
|
||||
/// Returns adjusted amount of samples
|
||||
virtual uint adjustAmountOfSamples(uint numSamples)
|
||||
{
|
||||
return output->adjustAmountOfSamples(numSamples);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
328
externals/soundtouch/FIRFilter.cpp
vendored
328
externals/soundtouch/FIRFilter.cpp
vendored
@ -1,328 +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
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2015-02-21 23:24:29 +0200 (Sat, 21 Feb 2015) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: FIRFilter.cpp 202 2015-02-21 21:24:29Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// 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 = NULL;
|
||||
}
|
||||
|
||||
|
||||
FIRFilter::~FIRFilter()
|
||||
{
|
||||
delete[] filterCoeffs;
|
||||
}
|
||||
|
||||
// Usual C-version of the filter routine for stereo sound
|
||||
uint FIRFilter::evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const
|
||||
{
|
||||
int j, end;
|
||||
#ifdef SOUNDTOUCH_FLOAT_SAMPLES
|
||||
// when using floating point samples, use a scaler instead of a divider
|
||||
// because division is much slower operation than multiplying.
|
||||
double dScaler = 1.0 / (double)resultDivider;
|
||||
#endif
|
||||
|
||||
assert(length != 0);
|
||||
assert(src != NULL);
|
||||
assert(dest != NULL);
|
||||
assert(filterCoeffs != NULL);
|
||||
|
||||
end = 2 * (numSamples - length);
|
||||
|
||||
#pragma omp parallel for
|
||||
for (j = 0; j < end; j += 2)
|
||||
{
|
||||
const SAMPLETYPE *ptr;
|
||||
LONG_SAMPLETYPE suml, sumr;
|
||||
uint i;
|
||||
|
||||
suml = sumr = 0;
|
||||
ptr = src + j;
|
||||
|
||||
for (i = 0; i < length; i += 4)
|
||||
{
|
||||
// loop is unrolled by factor of 4 here for efficiency
|
||||
suml += ptr[2 * i + 0] * filterCoeffs[i + 0] +
|
||||
ptr[2 * i + 2] * filterCoeffs[i + 1] +
|
||||
ptr[2 * i + 4] * filterCoeffs[i + 2] +
|
||||
ptr[2 * i + 6] * filterCoeffs[i + 3];
|
||||
sumr += ptr[2 * i + 1] * filterCoeffs[i + 0] +
|
||||
ptr[2 * i + 3] * filterCoeffs[i + 1] +
|
||||
ptr[2 * i + 5] * filterCoeffs[i + 2] +
|
||||
ptr[2 * i + 7] * filterCoeffs[i + 3];
|
||||
}
|
||||
|
||||
#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;
|
||||
#else
|
||||
suml *= dScaler;
|
||||
sumr *= dScaler;
|
||||
#endif // SOUNDTOUCH_INTEGER_SAMPLES
|
||||
dest[j] = (SAMPLETYPE)suml;
|
||||
dest[j + 1] = (SAMPLETYPE)sumr;
|
||||
}
|
||||
return numSamples - length;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Usual C-version of the filter routine for mono sound
|
||||
uint FIRFilter::evaluateFilterMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const
|
||||
{
|
||||
int j, end;
|
||||
#ifdef SOUNDTOUCH_FLOAT_SAMPLES
|
||||
// when using floating point samples, use a scaler instead of a divider
|
||||
// because division is much slower operation than multiplying.
|
||||
double dScaler = 1.0 / (double)resultDivider;
|
||||
#endif
|
||||
|
||||
assert(length != 0);
|
||||
|
||||
end = numSamples - length;
|
||||
#pragma omp parallel for
|
||||
for (j = 0; j < end; j ++)
|
||||
{
|
||||
const SAMPLETYPE *pSrc = src + j;
|
||||
LONG_SAMPLETYPE sum;
|
||||
uint i;
|
||||
|
||||
sum = 0;
|
||||
for (i = 0; i < length; i += 4)
|
||||
{
|
||||
// loop is unrolled by factor of 4 here for efficiency
|
||||
sum += pSrc[i + 0] * filterCoeffs[i + 0] +
|
||||
pSrc[i + 1] * filterCoeffs[i + 1] +
|
||||
pSrc[i + 2] * filterCoeffs[i + 2] +
|
||||
pSrc[i + 3] * filterCoeffs[i + 3];
|
||||
}
|
||||
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||
sum >>= resultDivFactor;
|
||||
// saturate to 16 bit integer limits
|
||||
sum = (sum < -32768) ? -32768 : (sum > 32767) ? 32767 : sum;
|
||||
#else
|
||||
sum *= dScaler;
|
||||
#endif // SOUNDTOUCH_INTEGER_SAMPLES
|
||||
dest[j] = (SAMPLETYPE)sum;
|
||||
}
|
||||
return end;
|
||||
}
|
||||
|
||||
|
||||
uint FIRFilter::evaluateFilterMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels)
|
||||
{
|
||||
int j, end;
|
||||
|
||||
#ifdef SOUNDTOUCH_FLOAT_SAMPLES
|
||||
// when using floating point samples, use a scaler instead of a divider
|
||||
// because division is much slower operation than multiplying.
|
||||
double dScaler = 1.0 / (double)resultDivider;
|
||||
#endif
|
||||
|
||||
assert(length != 0);
|
||||
assert(src != NULL);
|
||||
assert(dest != NULL);
|
||||
assert(filterCoeffs != NULL);
|
||||
assert(numChannels < 16);
|
||||
|
||||
end = numChannels * (numSamples - length);
|
||||
|
||||
#pragma omp parallel for
|
||||
for (j = 0; j < end; j += numChannels)
|
||||
{
|
||||
const SAMPLETYPE *ptr;
|
||||
LONG_SAMPLETYPE sums[16];
|
||||
uint c, i;
|
||||
|
||||
for (c = 0; c < numChannels; c ++)
|
||||
{
|
||||
sums[c] = 0;
|
||||
}
|
||||
|
||||
ptr = src + j;
|
||||
|
||||
for (i = 0; i < length; 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;
|
||||
#else
|
||||
sums[c] *= dScaler;
|
||||
#endif // SOUNDTOUCH_INTEGER_SAMPLES
|
||||
dest[j+c] = (SAMPLETYPE)sums[c];
|
||||
}
|
||||
}
|
||||
return numSamples - length;
|
||||
}
|
||||
|
||||
|
||||
// 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");
|
||||
|
||||
lengthDiv8 = newLength / 8;
|
||||
length = lengthDiv8 * 8;
|
||||
assert(length == newLength);
|
||||
|
||||
resultDivFactor = uResultDivFactor;
|
||||
resultDivider = (SAMPLETYPE)::pow(2.0, (int)resultDivFactor);
|
||||
|
||||
delete[] filterCoeffs;
|
||||
filterCoeffs = new SAMPLETYPE[length];
|
||||
memcpy(filterCoeffs, coeffs, length * sizeof(SAMPLETYPE));
|
||||
}
|
||||
|
||||
|
||||
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 s)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
}
|
146
externals/soundtouch/FIRFilter.h
vendored
146
externals/soundtouch/FIRFilter.h
vendored
@ -1,146 +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
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2015-02-21 23:24:29 +0200 (Sat, 21 Feb 2015) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: FIRFilter.h 202 2015-02-21 21:24:29Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// 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;
|
||||
|
||||
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;
|
||||
public:
|
||||
FIRFilterMMX();
|
||||
~FIRFilterMMX();
|
||||
|
||||
virtual void setCoefficients(const short *coeffs, uint newLength, uint uResultDivFactor);
|
||||
};
|
||||
|
||||
#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;
|
||||
public:
|
||||
FIRFilterSSE();
|
||||
~FIRFilterSSE();
|
||||
|
||||
virtual void setCoefficients(const float *coeffs, uint newLength, uint uResultDivFactor);
|
||||
};
|
||||
|
||||
#endif // SOUNDTOUCH_ALLOW_SSE
|
||||
|
||||
}
|
||||
|
||||
#endif // FIRFilter_H
|
200
externals/soundtouch/InterpolateCubic.cpp
vendored
200
externals/soundtouch/InterpolateCubic.cpp
vendored
@ -1,200 +0,0 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Cubic interpolation routine.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $Id: InterpolateCubic.cpp 179 2014-01-06 18:41:42Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <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;
|
||||
}
|
67
externals/soundtouch/InterpolateCubic.h
vendored
67
externals/soundtouch/InterpolateCubic.h
vendored
@ -1,67 +0,0 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Cubic interpolation routine.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $Id: InterpolateCubic.h 225 2015-07-26 14:45:48Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _InterpolateCubic_H_
|
||||
#define _InterpolateCubic_H_
|
||||
|
||||
#include "RateTransposer.h"
|
||||
#include "STTypes.h"
|
||||
|
||||
namespace soundtouch
|
||||
{
|
||||
|
||||
class InterpolateCubic : public TransposerBase
|
||||
{
|
||||
protected:
|
||||
virtual void resetRegisters();
|
||||
virtual int transposeMono(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
int &srcSamples);
|
||||
virtual int transposeStereo(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
int &srcSamples);
|
||||
virtual int transposeMulti(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
int &srcSamples);
|
||||
|
||||
double fract;
|
||||
|
||||
public:
|
||||
InterpolateCubic();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
300
externals/soundtouch/InterpolateLinear.cpp
vendored
300
externals/soundtouch/InterpolateLinear.cpp
vendored
@ -1,300 +0,0 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Linear interpolation algorithm.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $Id: InterpolateLinear.cpp 225 2015-07-26 14:45:48Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <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 = (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;
|
||||
}
|
92
externals/soundtouch/InterpolateLinear.h
vendored
92
externals/soundtouch/InterpolateLinear.h
vendored
@ -1,92 +0,0 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Linear interpolation routine.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $Id: InterpolateLinear.h 225 2015-07-26 14:45:48Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _InterpolateLinear_H_
|
||||
#define _InterpolateLinear_H_
|
||||
|
||||
#include "RateTransposer.h"
|
||||
#include "STTypes.h"
|
||||
|
||||
namespace soundtouch
|
||||
{
|
||||
|
||||
/// Linear transposer class that uses integer arithmetics
|
||||
class InterpolateLinearInteger : public TransposerBase
|
||||
{
|
||||
protected:
|
||||
int iFract;
|
||||
int iRate;
|
||||
|
||||
virtual void resetRegisters();
|
||||
|
||||
virtual int transposeMono(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
int &srcSamples);
|
||||
virtual int transposeStereo(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
int &srcSamples);
|
||||
virtual int transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples);
|
||||
public:
|
||||
InterpolateLinearInteger();
|
||||
|
||||
/// Sets new target rate. Normal rate = 1.0, smaller values represent slower
|
||||
/// rate, larger faster rates.
|
||||
virtual void setRate(double newRate);
|
||||
};
|
||||
|
||||
|
||||
/// Linear transposer class that uses floating point arithmetics
|
||||
class InterpolateLinearFloat : public TransposerBase
|
||||
{
|
||||
protected:
|
||||
double fract;
|
||||
|
||||
virtual void resetRegisters();
|
||||
|
||||
virtual int transposeMono(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
int &srcSamples);
|
||||
virtual int transposeStereo(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
int &srcSamples);
|
||||
virtual int transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples);
|
||||
|
||||
public:
|
||||
InterpolateLinearFloat();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
185
externals/soundtouch/InterpolateShannon.cpp
vendored
185
externals/soundtouch/InterpolateShannon.cpp
vendored
@ -1,185 +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
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $Id: InterpolateShannon.cpp 195 2014-04-06 15:57:21Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <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 *pdest,
|
||||
const SAMPLETYPE *psrc,
|
||||
int &srcSamples)
|
||||
{
|
||||
// not implemented
|
||||
assert(false);
|
||||
return 0;
|
||||
}
|
72
externals/soundtouch/InterpolateShannon.h
vendored
72
externals/soundtouch/InterpolateShannon.h
vendored
@ -1,72 +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
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $Id: InterpolateShannon.h 225 2015-07-26 14:45:48Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _InterpolateShannon_H_
|
||||
#define _InterpolateShannon_H_
|
||||
|
||||
#include "RateTransposer.h"
|
||||
#include "STTypes.h"
|
||||
|
||||
namespace soundtouch
|
||||
{
|
||||
|
||||
class InterpolateShannon : public TransposerBase
|
||||
{
|
||||
protected:
|
||||
void resetRegisters();
|
||||
int transposeMono(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
int &srcSamples);
|
||||
int transposeStereo(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
int &srcSamples);
|
||||
int transposeMulti(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
int &srcSamples);
|
||||
|
||||
double fract;
|
||||
|
||||
public:
|
||||
InterpolateShannon();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
286
externals/soundtouch/PeakFinder.cpp
vendored
286
externals/soundtouch/PeakFinder.cpp
vendored
@ -1,286 +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
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2015-05-18 18:22:02 +0300 (Mon, 18 May 2015) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: PeakFinder.cpp 213 2015-05-18 15:22:02Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// 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 < 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 = 3; i < 10; i ++)
|
||||
{
|
||||
double peaktmp, harmonic;
|
||||
int i1,i2;
|
||||
|
||||
harmonic = (double)i * 0.5;
|
||||
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;
|
||||
}
|
97
externals/soundtouch/PeakFinder.h
vendored
97
externals/soundtouch/PeakFinder.h
vendored
@ -1,97 +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
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2011-12-30 22:33:46 +0200 (Fri, 30 Dec 2011) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: PeakFinder.h 132 2011-12-30 20:33:46Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// 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 beloging to the peak.
|
||||
int lastPos ///< Index of last vector item beloging 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_
|
302
externals/soundtouch/RateTransposer.cpp
vendored
302
externals/soundtouch/RateTransposer.cpp
vendored
@ -1,302 +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
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2015-07-26 17:45:48 +0300 (Sun, 26 Jul 2015) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: RateTransposer.cpp 225 2015-07-26 14:45:48Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// 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 = true;
|
||||
|
||||
// Instantiates the anti-alias filter
|
||||
pAAFilter = new AAFilter(64);
|
||||
pTransposer = TransposerBase::newInstance();
|
||||
}
|
||||
|
||||
|
||||
|
||||
RateTransposer::~RateTransposer()
|
||||
{
|
||||
delete pAAFilter;
|
||||
delete pTransposer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Enables/disables the anti-alias filter. Zero to disable, nonzero to enable
|
||||
void RateTransposer::enableAAFilter(bool newMode)
|
||||
{
|
||||
bUseAAFilter = newMode;
|
||||
}
|
||||
|
||||
|
||||
/// 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)
|
||||
{
|
||||
uint count;
|
||||
|
||||
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)
|
||||
{
|
||||
count = 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)
|
||||
{
|
||||
assert(nChannels > 0);
|
||||
|
||||
if (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();
|
||||
}
|
||||
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 arithmetics 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 NULL;
|
||||
}
|
||||
#endif
|
||||
}
|
179
externals/soundtouch/RateTransposer.h
vendored
179
externals/soundtouch/RateTransposer.h
vendored
@ -1,179 +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
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2015-07-26 17:45:48 +0300 (Sun, 26 Jul 2015) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: RateTransposer.h 225 2015-07-26 14:45:48Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// 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 void resetRegisters() = 0;
|
||||
|
||||
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);
|
||||
|
||||
// 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();
|
||||
|
||||
/// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
||||
/// depending on if we're to use integer or floating point arithmetics.
|
||||
// static void *operator new(size_t s);
|
||||
|
||||
/// Use this function instead of "new" operator to create a new instance of this class.
|
||||
/// This function automatically chooses a correct implementation, depending on if
|
||||
/// integer ot floating point arithmetics are to be used.
|
||||
// static RateTransposer *newInstance();
|
||||
|
||||
/// Returns the output buffer object
|
||||
FIFOSamplePipe *getOutput() { return &outputBuffer; };
|
||||
|
||||
/// Returns the store buffer object
|
||||
// FIFOSamplePipe *getStore() { return &storeBuffer; };
|
||||
|
||||
/// Return anti-alias filter object
|
||||
AAFilter *getAAFilter();
|
||||
|
||||
/// 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);
|
||||
|
||||
/// Clears all the samples in the object
|
||||
void clear();
|
||||
|
||||
/// Returns nonzero if there aren't any samples available for outputting.
|
||||
int isEmpty() const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
185
externals/soundtouch/STTypes.h
vendored
185
externals/soundtouch/STTypes.h
vendored
@ -1,185 +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
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2015-05-18 18:25:07 +0300 (Mon, 18 May 2015) $
|
||||
// File revision : $Revision: 3 $
|
||||
//
|
||||
// $Id: STTypes.h 215 2015-05-18 15:25:07Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// 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
|
||||
|
||||
typedef unsigned int uint;
|
||||
typedef unsigned long ulong;
|
||||
|
||||
// Patch for MinGW: on Win64 long is 32-bit
|
||||
#ifdef _WIN64
|
||||
typedef unsigned long long ulongptr;
|
||||
#else
|
||||
typedef ulong ulongptr;
|
||||
#endif
|
||||
|
||||
|
||||
// Helper macro for aligning pointer up to next 16-byte boundary
|
||||
#define SOUNDTOUCH_ALIGN_POINTER_16(x) ( ( (ulongptr)(x) + 15 ) & ~(ulongptr)15 )
|
||||
|
||||
|
||||
#if (defined(__GNUC__) && !defined(ANDROID))
|
||||
// In GCC, include soundtouch_config.h made by config scritps.
|
||||
// Skip this in Android compilation that uses GCC but without configure scripts.
|
||||
//#include "soundtouch_config.h"
|
||||
#endif
|
||||
|
||||
|
||||
namespace soundtouch
|
||||
{
|
||||
/// 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__))
|
||||
// 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 take minor shortcuts
|
||||
// for improved performance. Undefine to require faithfully similar SIMD
|
||||
// calculations as in normal C implementation.
|
||||
#define SOUNDTOUCH_ALLOW_NONEXACT_SIMD_OPTIMIZATION 1
|
||||
|
||||
|
||||
#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
|
||||
#define SOUNDTOUCH_ALLOW_MMX 1
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
// floating point samples
|
||||
typedef float SAMPLETYPE;
|
||||
// data type for sample accumulation: Use double to utilize full precision.
|
||||
typedef double LONG_SAMPLETYPE;
|
||||
|
||||
#ifdef SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS
|
||||
// Allow SSE optimizations
|
||||
#define SOUNDTOUCH_ALLOW_SSE 1
|
||||
#endif
|
||||
|
||||
#endif // SOUNDTOUCH_INTEGER_SAMPLES
|
||||
|
||||
};
|
||||
|
||||
// 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
|
526
externals/soundtouch/SoundTouch.cpp
vendored
526
externals/soundtouch/SoundTouch.cpp
vendored
@ -1,526 +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
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2015-07-26 17:45:48 +0300 (Sun, 26 Jul 2015) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: SoundTouch.cpp 225 2015-07-26 14:45:48Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// 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 (numChannels != 1 && numChannels != 2)
|
||||
{
|
||||
//ST_THROW_RT_ERROR("Illegal number of channels");
|
||||
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)
|
||||
{
|
||||
bSrateSet = true;
|
||||
// set sample rate, leave other tempo changer parameters as they are.
|
||||
pTDStretch->setParameters((int)srate);
|
||||
}
|
||||
|
||||
|
||||
// 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");
|
||||
}
|
||||
|
||||
// Transpose the rate of the new samples if necessary
|
||||
/* Bypass the nominal setting - can introduce a click in sound when tempo/pitch control crosses the nominal value...
|
||||
if (rate == 1.0f)
|
||||
{
|
||||
// The rate value is same as the original, simply evaluate the tempo changer.
|
||||
assert(output == pTDStretch);
|
||||
if (pRateTransposer->isEmpty() == 0)
|
||||
{
|
||||
// yet flush the last samples in the pitch transposer buffer
|
||||
// (may happen if 'rate' changes from a non-zero value to zero)
|
||||
pTDStretch->moveSamples(*pRateTransposer);
|
||||
}
|
||||
pTDStretch->putSamples(samples, nSamples);
|
||||
}
|
||||
*/
|
||||
|
||||
// accumulate how many samples are expected out from processing, given the current
|
||||
// processing setting
|
||||
samplesExpectedOut += (double)nSamples / ((double)rate * (double)tempo);
|
||||
|
||||
#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);
|
||||
|
||||
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
|
||||
// pRateTransposer->clearInput();
|
||||
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(NULL, &temp, NULL, NULL);
|
||||
return temp;
|
||||
|
||||
case SETTING_SEEKWINDOW_MS:
|
||||
pTDStretch->getParameters(NULL, NULL, &temp, NULL);
|
||||
return temp;
|
||||
|
||||
case SETTING_OVERLAP_MS:
|
||||
pTDStretch->getParameters(NULL, NULL, NULL, &temp);
|
||||
return temp;
|
||||
|
||||
case SETTING_NOMINAL_INPUT_SEQUENCE :
|
||||
return pTDStretch->getInputSampleReq();
|
||||
|
||||
case SETTING_NOMINAL_OUTPUT_SEQUENCE :
|
||||
return pTDStretch->getOutputBatchSize();
|
||||
|
||||
default :
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Clears all the samples in the object's output and internal processing
|
||||
// buffers.
|
||||
void SoundTouch::clear()
|
||||
{
|
||||
samplesExpectedOut = 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;
|
||||
}
|
301
externals/soundtouch/SoundTouch.h
vendored
301
externals/soundtouch/SoundTouch.h
vendored
@ -1,301 +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
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2015-09-20 10:38:32 +0300 (Sun, 20 Sep 2015) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: SoundTouch.h 230 2015-09-20 07:38:32Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// 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 "1.9.2"
|
||||
|
||||
/// SoundTouch library version id
|
||||
#define SOUNDTOUCH_VERSION_ID (10902)
|
||||
|
||||
//
|
||||
// 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 nominal average processing sequence
|
||||
/// size in samples. This value tells approcimate value how many input samples
|
||||
/// SoundTouch needs to gather before it does DSP processing run for the sample batch.
|
||||
///
|
||||
/// Notices:
|
||||
/// - This is read-only parameter, i.e. setSetting ignores this parameter
|
||||
/// - Returned value is approximate average value, exact processing batch
|
||||
/// size may wary from time to time
|
||||
/// - This parameter value is not constant but may change depending on
|
||||
/// 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
|
||||
/// - Returned value is approximate average value, exact processing batch
|
||||
/// size may wary from time to time
|
||||
/// - This parameter value is not constant but may change depending on
|
||||
/// tempo/pitch/rate/samplerate settings.
|
||||
#define SETTING_NOMINAL_OUTPUT_SEQUENCE 7
|
||||
|
||||
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();
|
||||
|
||||
/// 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);
|
||||
|
||||
/// 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.
|
||||
);
|
||||
|
||||
/// 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.
|
||||
);
|
||||
|
||||
/// 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.
|
||||
);
|
||||
|
||||
/// Clears all the samples in the object's output and internal processing
|
||||
/// buffers.
|
||||
virtual void clear();
|
||||
|
||||
/// Changes a setting controlling the processing system behaviour. See the
|
||||
/// 'SETTING_...' defines for available setting ID's.
|
||||
///
|
||||
/// \return 'true' if the setting was succesfully changed
|
||||
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;
|
||||
|
||||
|
||||
/// 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
|
75
externals/soundtouch/SoundTouch.vcxproj
vendored
75
externals/soundtouch/SoundTouch.vcxproj
vendored
@ -1,75 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{EC082900-B4D8-42E9-9663-77F02F6936AE}</ProjectGuid>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="..\..\Source\VSProps\Base.props" />
|
||||
<Import Project="..\..\Source\VSProps\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>
|
1078
externals/soundtouch/TDStretch.cpp
vendored
1078
externals/soundtouch/TDStretch.cpp
vendored
File diff suppressed because it is too large
Load Diff
281
externals/soundtouch/TDStretch.h
vendored
281
externals/soundtouch/TDStretch.h
vendored
@ -1,281 +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
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2015-08-09 00:00:15 +0300 (Sun, 09 Aug 2015) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: TDStretch.h 226 2015-08-08 21:00:15Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// 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;
|
||||
|
||||
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();
|
||||
|
||||
/// 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();
|
||||
|
||||
/// 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 NULL, 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
|
||||
);
|
||||
|
||||
/// 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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
// 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);
|
||||
double calcCrossCorrAccumulate(const short *mixingPos, const short *compare, double &norm);
|
||||
virtual void overlapStereo(short *output, const short *input) const;
|
||||
virtual void clearCrossCorrState();
|
||||
};
|
||||
#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);
|
||||
double calcCrossCorrAccumulate(const float *mixingPos, const float *compare, double &norm);
|
||||
};
|
||||
|
||||
#endif /// SOUNDTOUCH_ALLOW_SSE
|
||||
|
||||
}
|
||||
#endif /// TDStretch_H
|
62
externals/soundtouch/cpu_detect.h
vendored
62
externals/soundtouch/cpu_detect.h
vendored
@ -1,62 +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
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2008-02-10 18:26:55 +0200 (Sun, 10 Feb 2008) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: cpu_detect.h 11 2008-02-10 16:26:55Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// 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
|
||||
|
||||
/// 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_
|
138
externals/soundtouch/cpu_detect_x86.cpp
vendored
138
externals/soundtouch/cpu_detect_x86.cpp
vendored
@ -1,138 +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
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2014-01-07 20:24:28 +0200 (Tue, 07 Jan 2014) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: cpu_detect_x86.cpp 183 2014-01-07 18:24:28Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// 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
|
||||
}
|
395
externals/soundtouch/mmx_optimized.cpp
vendored
395
externals/soundtouch/mmx_optimized.cpp
vendored
@ -1,395 +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
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2015-08-09 00:00:15 +0300 (Sun, 09 Aug 2015) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: mmx_optimized.cpp 226 2015-08-08 21:00:15Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// 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)
|
||||
{
|
||||
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 = NULL;
|
||||
filterCoeffsUnalign = NULL;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#endif // SOUNDTOUCH_ALLOW_MMX
|
372
externals/soundtouch/sse_optimized.cpp
vendored
372
externals/soundtouch/sse_optimized.cpp
vendored
@ -1,372 +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
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2015-08-09 00:00:15 +0300 (Sun, 09 Aug 2015) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: sse_optimized.cpp 226 2015-08-08 21:00:15Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// 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 SOUNDTOUCH_ALLOW_NONEXACT_SIMD_OPTIMIZATION
|
||||
// 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 = NULL;
|
||||
filterCoeffsUnalign = NULL;
|
||||
}
|
||||
|
||||
|
||||
FIRFilterSSE::~FIRFilterSSE()
|
||||
{
|
||||
delete[] filterCoeffsUnalign;
|
||||
filterCoeffsAlign = NULL;
|
||||
filterCoeffsUnalign = NULL;
|
||||
}
|
||||
|
||||
|
||||
// (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 != NULL);
|
||||
assert(dest != NULL);
|
||||
assert((length % 8) == 0);
|
||||
assert(filterCoeffsAlign != NULL);
|
||||
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
|
@ -2,14 +2,8 @@ set(SRCS
|
||||
audio_core.cpp
|
||||
codec.cpp
|
||||
hle/dsp.cpp
|
||||
hle/effects.cpp
|
||||
hle/filter.cpp
|
||||
hle/final.cpp
|
||||
hle/pipe.cpp
|
||||
hle/source.cpp
|
||||
interpolate.cpp
|
||||
null_sink.cpp
|
||||
time_stretch.cpp
|
||||
)
|
||||
|
||||
set(HEADERS
|
||||
@ -17,31 +11,11 @@ set(HEADERS
|
||||
codec.h
|
||||
hle/common.h
|
||||
hle/dsp.h
|
||||
hle/effects.h
|
||||
hle/filter.h
|
||||
hle/final.h
|
||||
hle/pipe.h
|
||||
hle/source.h
|
||||
interpolate.h
|
||||
null_sink.h
|
||||
sink.h
|
||||
time_stretch.h
|
||||
)
|
||||
|
||||
if(SDL2_FOUND)
|
||||
set(SRCS ${SRCS} sdl2_sink.cpp)
|
||||
set(HEADERS ${HEADERS} sdl2_sink.h)
|
||||
include_directories(${SDL2_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
include_directories(../../externals/soundtouch)
|
||||
include_directories(../../externals/rubberband/rubberband/rubberband)
|
||||
|
||||
create_directory_groups(${SRCS} ${HEADERS})
|
||||
|
||||
add_library(audio_core STATIC ${SRCS} ${HEADERS})
|
||||
target_link_libraries(audio_core SoundTouch rubberband)
|
||||
|
||||
if(SDL2_FOUND)
|
||||
target_link_libraries(audio_core ${SDL2_LIBRARY})
|
||||
endif()
|
||||
add_library(audio_core STATIC ${SRCS} ${HEADERS})
|
@ -4,7 +4,6 @@
|
||||
|
||||
#include "audio_core/audio_core.h"
|
||||
#include "audio_core/hle/dsp.h"
|
||||
#include "audio_core/sdl2_sink.h"
|
||||
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/kernel/vm_manager.h"
|
||||
|
@ -4,17 +4,15 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
namespace Kernel {
|
||||
class VMManager;
|
||||
}
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
constexpr size_t num_sources = 24;
|
||||
constexpr size_t samples_per_frame = 160; ///< Samples per audio frame at native sample rate
|
||||
constexpr unsigned native_sample_rate = 32728; ///< 32kHz
|
||||
constexpr int num_sources = 24;
|
||||
constexpr int samples_per_frame = 160; ///< Samples per audio frame at native sample rate
|
||||
constexpr int native_sample_rate = 32728; ///< 32kHz
|
||||
|
||||
/// Initialise Audio Core
|
||||
void Init();
|
||||
|
@ -77,7 +77,9 @@ StereoBuffer16 DecodeADPCM(const u8* const data, const size_t sample_count, cons
|
||||
}
|
||||
|
||||
static s16 SignExtendS8(u8 x) {
|
||||
return s16(u16(x) << 8);
|
||||
// The data is actually signed PCM8.
|
||||
// We sign extend this to signed PCM16.
|
||||
return static_cast<s16>(static_cast<s8>(x));
|
||||
}
|
||||
|
||||
StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data, const size_t sample_count) {
|
||||
|
@ -2,23 +2,8 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
|
||||
#include "audio_core/audio_core.h"
|
||||
#include "audio_core/hle/effects.h"
|
||||
#include "audio_core/hle/dsp.h"
|
||||
#include "audio_core/hle/final.h"
|
||||
#include "audio_core/hle/pipe.h"
|
||||
#include "audio_core/hle/source.h"
|
||||
#include "audio_core/sink.h"
|
||||
#include "audio_core/time_stretch.h"
|
||||
|
||||
#include "common/microprofile.h"
|
||||
#include "common/profiler.h"
|
||||
#include "common/thread.h"
|
||||
|
||||
#include "core/hle/service/dsp_dsp.h"
|
||||
|
||||
namespace DSP {
|
||||
namespace HLE {
|
||||
@ -26,104 +11,14 @@ namespace HLE {
|
||||
SharedMemory g_region0;
|
||||
SharedMemory g_region1;
|
||||
|
||||
static void ThreadFunc();
|
||||
static Common::Barrier ThreadFunc_barrier(2);
|
||||
static std::atomic<bool> ThreadFunc_quit = true;
|
||||
|
||||
void Init() {
|
||||
ResetPipes();
|
||||
SourceInit();
|
||||
EffectsInit();
|
||||
FinalInit();
|
||||
TimeStretch::Init();
|
||||
|
||||
ThreadFunc_quit = false;
|
||||
std::thread thread(ThreadFunc);
|
||||
thread.detach();
|
||||
|
||||
// Lioncash won't like this. Don't do it.
|
||||
std::memset(&g_region0, 0, sizeof(SharedMemory));
|
||||
std::memset(&g_region1, 0, sizeof(SharedMemory));
|
||||
DSP::HLE::ResetPipes();
|
||||
}
|
||||
|
||||
void Shutdown() {
|
||||
TimeStretch::Shutdown();
|
||||
|
||||
if (!ThreadFunc_quit) {
|
||||
ThreadFunc_quit = true;
|
||||
ThreadFunc_barrier.Sync();
|
||||
}
|
||||
}
|
||||
|
||||
static bool next_region_is_ready = true;
|
||||
|
||||
unsigned num_frames = 500;
|
||||
double time_for_a_frame = 0.005;
|
||||
|
||||
static Common::Profiling::TimingCategory profile_tick("DSP::Tick");
|
||||
static Common::Profiling::TimingCategory profile_work("DSP::Work");
|
||||
MICROPROFILE_DEFINE(DSP_Tick, "DSP", "Tick", MP_RGB(204, 204, 0));
|
||||
MICROPROFILE_DEFINE(DSP_Work, "DSP", "Work", MP_RGB(153, 153, 0));
|
||||
|
||||
static void DoWork() {
|
||||
Common::Profiling::ScopeTimer timer_work(profile_work);
|
||||
MICROPROFILE_SCOPE(DSP_Work);
|
||||
|
||||
auto& region = CurrentRegion();
|
||||
|
||||
for (int i = 0; i < AudioCore::num_sources; i++) {
|
||||
auto& config = region.source_configurations.config[i];
|
||||
auto& coeffs = region.adpcm_coefficients.coeff[i];
|
||||
auto& status = region.source_statuses.status[i];
|
||||
|
||||
SourceUpdate(i, config, coeffs, status);
|
||||
}
|
||||
|
||||
EffectsUpdate(region.dsp_configuration, region.intermediate_mix_samples);
|
||||
|
||||
FinalUpdate(region.dsp_configuration, region.dsp_status, region.final_samples);
|
||||
|
||||
StereoFrame16 samples = FinalFrame();
|
||||
|
||||
#if 0
|
||||
std::vector<s16> output;
|
||||
output.reserve(AudioCore::samples_per_frame * 2);
|
||||
for (int i = 0; i < AudioCore::samples_per_frame; i++) {
|
||||
output.push_back(samples[0][i]);
|
||||
output.push_back(samples[1][i]);
|
||||
}
|
||||
AudioCore::sink->EnqueueSamples(output);
|
||||
#else
|
||||
TimeStretch::Tick(AudioCore::sink->SamplesInQueue());
|
||||
TimeStretch::AddSamples(samples);
|
||||
TimeStretch::OutputSamples([&](const std::vector<s16>& output) {
|
||||
if (AudioCore::sink->SamplesInQueue() < 16000) {
|
||||
AudioCore::sink->EnqueueSamples(output);
|
||||
}
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
static void ThreadFunc() {
|
||||
while (true) {
|
||||
ThreadFunc_barrier.Sync();
|
||||
if (ThreadFunc_quit) {
|
||||
break;
|
||||
}
|
||||
|
||||
DoWork();
|
||||
}
|
||||
}
|
||||
|
||||
bool Tick() {
|
||||
Common::Profiling::ScopeTimer timer_tick(profile_tick);
|
||||
MICROPROFILE_SCOPE(DSP_Tick);
|
||||
|
||||
if (GetDspState() != DspState::On || !DSP_DSP::SemaphoreSignalled())
|
||||
return false;
|
||||
|
||||
ThreadFunc_barrier.Sync();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -313,10 +313,10 @@ ASSERT_DSP_STRUCT(SourceConfiguration::Configuration::Buffer, 20);
|
||||
struct SourceStatus {
|
||||
struct Status {
|
||||
u8 is_enabled; ///< Is this channel enabled? (Doesn't have to be playing anything.)
|
||||
u8 current_buffer_id_dirty; ///< Non-zero when current_buffer_id changes
|
||||
u8 previous_buffer_id_dirty; ///< Non-zero when previous_buffer_id changes
|
||||
u16_le sync; ///< Is set by the DSP to the value of SourceConfiguration::sync
|
||||
u32_dsp buffer_position; ///< Number of samples into the current buffer
|
||||
u16_le current_buffer_id; ///< Updated when a buffer finishes playing
|
||||
u16_le previous_buffer_id; ///< Updated when a buffer finishes playing
|
||||
INSERT_PADDING_DSPWORDS(1);
|
||||
};
|
||||
|
||||
@ -330,13 +330,6 @@ struct DspConfiguration {
|
||||
union {
|
||||
u32_le dirty_raw;
|
||||
|
||||
BitField<0, 1, u32_le> unknown10_dirty;
|
||||
BitField<1, 1, u32_le> unknown11_dirty;
|
||||
BitField<2, 1, u32_le> unknown12_dirty;
|
||||
BitField<3, 1, u32_le> unknown13_dirty;
|
||||
BitField<4, 1, u32_le> unknown14_dirty;
|
||||
BitField<5, 1, u32_le> unknown15_dirty;
|
||||
|
||||
BitField<8, 1, u32_le> mixer1_enabled_dirty;
|
||||
BitField<9, 1, u32_le> mixer2_enabled_dirty;
|
||||
BitField<10, 1, u32_le> delay_effect_0_dirty;
|
||||
@ -344,7 +337,6 @@ struct DspConfiguration {
|
||||
BitField<12, 1, u32_le> reverb_effect_0_dirty;
|
||||
BitField<13, 1, u32_le> reverb_effect_1_dirty;
|
||||
|
||||
BitField<15, 1, u32_le> unknown17_dirty;
|
||||
BitField<16, 1, u32_le> volume_0_dirty;
|
||||
|
||||
BitField<24, 1, u32_le> volume_1_dirty;
|
||||
@ -352,16 +344,12 @@ struct DspConfiguration {
|
||||
BitField<26, 1, u32_le> output_format_dirty;
|
||||
BitField<27, 1, u32_le> limiter_enabled_dirty;
|
||||
BitField<28, 1, u32_le> headphones_connected_dirty;
|
||||
|
||||
BitField<30, 1, u32_le> unknown16_dirty;
|
||||
BitField<31, 1, u32_le> unknown18_dirty;
|
||||
};
|
||||
|
||||
/// The DSP has three intermediate audio mixers. This controls the volume level (0.0-1.0) for each at the final mixer
|
||||
float_le volume[3];
|
||||
|
||||
u16 unknown17;
|
||||
INSERT_PADDING_DSPWORDS(2);
|
||||
INSERT_PADDING_DSPWORDS(3);
|
||||
|
||||
enum class OutputFormat : u16_le {
|
||||
Mono = 0,
|
||||
@ -373,10 +361,7 @@ struct DspConfiguration {
|
||||
|
||||
u16_le limiter_enabled; ///< Not sure of the exact gain equation for the limiter.
|
||||
u16_le headphones_connected; ///< Application updates the DSP on headphone status.
|
||||
INSERT_PADDING_DSPWORDS(1); ///< TODO: Surround sound related
|
||||
u16 unknown16;
|
||||
u16 unknown15;
|
||||
u16 unknown18;
|
||||
INSERT_PADDING_DSPWORDS(4); ///< TODO: Surround sound related
|
||||
INSERT_PADDING_DSPWORDS(2); ///< TODO: Intermediate mixer 1/2 related
|
||||
u16_le mixer1_enabled;
|
||||
u16_le mixer2_enabled;
|
||||
@ -494,29 +479,25 @@ struct SharedMemory {
|
||||
AdpcmCoefficients adpcm_coefficients;
|
||||
|
||||
struct {
|
||||
u16 unknown[256];
|
||||
INSERT_PADDING_DSPWORDS(0x100);
|
||||
} unknown10;
|
||||
|
||||
struct {
|
||||
u16 unknown[192];
|
||||
INSERT_PADDING_DSPWORDS(0xC0);
|
||||
} unknown11;
|
||||
|
||||
struct {
|
||||
u16 unknown[384];
|
||||
INSERT_PADDING_DSPWORDS(0x180);
|
||||
} unknown12;
|
||||
|
||||
struct {
|
||||
// biq
|
||||
u32_dsp unknown[5];
|
||||
INSERT_PADDING_DSPWORDS(0xA);
|
||||
} unknown13;
|
||||
|
||||
struct {
|
||||
// biq
|
||||
u32_dsp unknown[5];
|
||||
INSERT_PADDING_DSPWORDS(0x13A3);
|
||||
} unknown14;
|
||||
|
||||
INSERT_PADDING_DSPWORDS(0x1399);
|
||||
|
||||
u16_le frame_counter;
|
||||
};
|
||||
ASSERT_DSP_STRUCT(SharedMemory, 0x8000);
|
||||
|
@ -1,44 +0,0 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "audio_core/audio_core.h"
|
||||
#include "audio_core/hle/common.h"
|
||||
#include "audio_core/hle/effects.h"
|
||||
#include "audio_core/hle/source.h"
|
||||
|
||||
namespace DSP {
|
||||
namespace HLE {
|
||||
|
||||
struct State {
|
||||
QuadFrame32 current_frame;
|
||||
};
|
||||
|
||||
static std::array<State, 3> state;
|
||||
|
||||
void EffectsInit() {
|
||||
state = {};
|
||||
}
|
||||
|
||||
void EffectsUpdate(const DspConfiguration& config, IntermediateMixSamples& samples) {
|
||||
state[0].current_frame.fill({});
|
||||
state[1].current_frame.fill({});
|
||||
state[2].current_frame.fill({});
|
||||
|
||||
for (size_t source_id = 0; source_id < AudioCore::num_sources; source_id++) {
|
||||
SourceFrameMixInto(state[0].current_frame, source_id, 0);
|
||||
SourceFrameMixInto(state[1].current_frame, source_id, 1);
|
||||
SourceFrameMixInto(state[2].current_frame, source_id, 2);
|
||||
}
|
||||
|
||||
// TODO: Delay
|
||||
// TODO: Reverb
|
||||
|
||||
}
|
||||
|
||||
const QuadFrame32& IntermediateMixFrame(int mix_id) {
|
||||
return state[mix_id].current_frame;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "audio_core/hle/common.h"
|
||||
#include "audio_core/hle/dsp.h"
|
||||
|
||||
namespace DSP {
|
||||
namespace HLE {
|
||||
|
||||
void EffectsInit();
|
||||
|
||||
void EffectsUpdate(const DspConfiguration& config, IntermediateMixSamples& samples);
|
||||
|
||||
const QuadFrame32& IntermediateMixFrame(int mix_id);
|
||||
|
||||
}
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "audio_core/hle/common.h"
|
||||
#include "audio_core/hle/effects.h"
|
||||
#include "audio_core/hle/final.h"
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/math_util.h"
|
||||
|
||||
namespace DSP {
|
||||
namespace HLE {
|
||||
|
||||
struct State {
|
||||
StereoFrame16 current_frame;
|
||||
std::array<float, 3> volumes = {1.0, 0.0, 0.0};
|
||||
};
|
||||
|
||||
static State state;
|
||||
|
||||
void FinalInit() {
|
||||
state = {};
|
||||
}
|
||||
|
||||
void FinalUpdate(const DspConfiguration& config, DspStatus& status, FinalMixSamples& samples) {
|
||||
// TODO: Final processing
|
||||
|
||||
bool clipping = false;
|
||||
|
||||
std::array<QuadFrame32, 3> mix;
|
||||
for (int k = 0; k < 3; k++) {
|
||||
mix[k] = IntermediateMixFrame(k);
|
||||
}
|
||||
|
||||
for (int i = 0; i < AudioCore::samples_per_frame; i++) {
|
||||
for (int j = 0; j < 2; j++) {
|
||||
s32 value = 0;
|
||||
for (int k = 0; k < 3; k++) {
|
||||
value += 0.2 * state.volumes[0] * mix[k][i][j + 0];
|
||||
value += 0.2 * state.volumes[0] * mix[k][i][j + 2];
|
||||
}
|
||||
|
||||
if (value > 0x8000 || value < -0x7FFF) {
|
||||
clipping = true;
|
||||
}
|
||||
|
||||
state.current_frame[i][j] = static_cast<s16>(MathUtil::Clamp(value, -0x7FFF, 0x8000));
|
||||
}
|
||||
}
|
||||
|
||||
if (clipping) {
|
||||
LOG_ERROR(Audio_DSP, "AUDIO IS CLIPPING");
|
||||
}
|
||||
}
|
||||
|
||||
const StereoFrame16& FinalFrame() {
|
||||
return state.current_frame;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "audio_core/hle/dsp.h"
|
||||
|
||||
namespace DSP {
|
||||
namespace HLE {
|
||||
|
||||
void FinalInit();
|
||||
|
||||
void FinalUpdate(const DspConfiguration& config, DspStatus& status, FinalMixSamples& samples);
|
||||
|
||||
const StereoFrame16& FinalFrame();
|
||||
|
||||
}
|
||||
}
|
@ -17,7 +17,7 @@ namespace HLE {
|
||||
|
||||
static DspState dsp_state = DspState::Off;
|
||||
|
||||
static std::array<std::vector<u8>, DspPipe_MAX> pipe_data;
|
||||
static std::array<std::vector<u8>, static_cast<size_t>(DspPipe::DspPipe_MAX)> pipe_data;
|
||||
|
||||
void ResetPipes() {
|
||||
for (auto& data : pipe_data) {
|
||||
|
@ -23,8 +23,6 @@ enum class DspPipe {
|
||||
DspPipe_MAX
|
||||
};
|
||||
|
||||
constexpr size_t DspPipe_MAX = static_cast<size_t>(DspPipe::DspPipe_MAX);
|
||||
|
||||
/**
|
||||
* Read a DSP pipe.
|
||||
* @param pipe_number The Pipe ID
|
||||
|
@ -1,334 +0,0 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <queue>
|
||||
#include <vector>
|
||||
|
||||
#include "audio_core/codec.h"
|
||||
#include "audio_core/hle/common.h"
|
||||
#include "audio_core/hle/source.h"
|
||||
#include "audio_core/interpolate.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace DSP {
|
||||
namespace HLE {
|
||||
|
||||
using MonoOrStereo = SourceConfiguration::Configuration::MonoOrStereo;
|
||||
using Format = SourceConfiguration::Configuration::Format;
|
||||
using DspBuffer = SourceConfiguration::Configuration::Buffer;
|
||||
|
||||
struct Buffer {
|
||||
PAddr physical_address;
|
||||
u32 length;
|
||||
u8 adpcm_ps;
|
||||
u16 adpcm_yn[2];
|
||||
bool adpcm_dirty;
|
||||
bool is_looping;
|
||||
u16 buffer_id;
|
||||
|
||||
MonoOrStereo mono_or_stereo;
|
||||
Format format;
|
||||
|
||||
bool from_queue;
|
||||
|
||||
bool operator < (const Buffer& other) const {
|
||||
// We want things with lower id to appear first, unless we have wraparound.
|
||||
// priority_queue puts a before b when b < a.
|
||||
if (this->buffer_id < 10 && other.buffer_id > 65520) return true;
|
||||
if (other.buffer_id < 10 && this->buffer_id > 65520) return false;
|
||||
return this->buffer_id > other.buffer_id;
|
||||
}
|
||||
};
|
||||
|
||||
struct State {
|
||||
size_t source_id;
|
||||
|
||||
bool enabled = false;
|
||||
float rate_multiplier = 1.0;
|
||||
u16 sync = 0;
|
||||
std::array<std::array<float, 4>, 3> gains = {};
|
||||
MonoOrStereo mono_or_stereo = MonoOrStereo::Mono;
|
||||
Format format = Format::PCM16;
|
||||
|
||||
std::array<s16, 16> adpcm_coeffs = {};
|
||||
Codec::ADPCMState adpcm_state = {0, 0};
|
||||
|
||||
AudioInterp::State interp_state = {};
|
||||
|
||||
bool do_not_trigger_update = true;
|
||||
bool buffer_update = false;
|
||||
u32 current_buffer_id = 0;
|
||||
u32 previous_buffer_id = 0;
|
||||
|
||||
std::priority_queue<Buffer> queue = {};
|
||||
u32 current_sample_number = 0;
|
||||
u32 next_sample_number = 0;
|
||||
Codec::StereoBuffer16 current_buffer = {};
|
||||
QuadFrame32 current_frame = {};
|
||||
};
|
||||
|
||||
static void ParseConfig(State& s, SourceConfiguration::Configuration& config, const s16_le adpcm_coeffs[16]) {
|
||||
if (!config.dirty_raw) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.reset_flag) {
|
||||
config.reset_flag.Assign(0);
|
||||
size_t id = s.source_id;
|
||||
s = {};
|
||||
s.source_id = id;
|
||||
LOG_DEBUG(Audio_DSP, "source_id=%zu reset", s.source_id);
|
||||
}
|
||||
|
||||
if (config.enable_dirty) {
|
||||
config.enable_dirty.Assign(0);
|
||||
s.enabled = config.enable != 0;
|
||||
LOG_TRACE(Audio_DSP, "source_id=%zu enable=%d", s.source_id, s.enabled);
|
||||
}
|
||||
|
||||
if (config.sync_dirty) {
|
||||
config.sync_dirty.Assign(0);
|
||||
s.sync = config.sync;
|
||||
LOG_DEBUG(Audio_DSP, "source_id=%zu sync=%u", s.source_id, s.sync);
|
||||
}
|
||||
|
||||
if (config.rate_multiplier_dirty) {
|
||||
config.rate_multiplier_dirty.Assign(0);
|
||||
s.rate_multiplier = config.rate_multiplier;
|
||||
LOG_TRACE(Audio_DSP, "source_id=%zu rate=%f", s.source_id, s.rate_multiplier);
|
||||
}
|
||||
|
||||
if (config.adpcm_coefficients_dirty) {
|
||||
config.adpcm_coefficients_dirty.Assign(0);
|
||||
std::copy(adpcm_coeffs, adpcm_coeffs + s.adpcm_coeffs.size(), s.adpcm_coeffs.begin());
|
||||
LOG_TRACE(Audio_DSP, "source_id=%zu adpcm update", s.source_id);
|
||||
}
|
||||
|
||||
if (config.gain_0_dirty) {
|
||||
config.gain_0_dirty.Assign(0);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
s.gains[0][i] = config.gain[0][i];
|
||||
LOG_TRACE(Audio_DSP, "source_id=%zu gains[0][%i] = %f", s.source_id, i, s.gains[0][i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (config.gain_1_dirty) {
|
||||
config.gain_1_dirty.Assign(0);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
s.gains[1][i] = config.gain[1][i];
|
||||
LOG_TRACE(Audio_DSP, "source_id=%zu gains[1][%i] = %f", s.source_id, i, s.gains[1][i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (config.gain_2_dirty) {
|
||||
config.gain_2_dirty.Assign(0);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
s.gains[2][i] = config.gain[2][i];
|
||||
LOG_TRACE(Audio_DSP, "source_id=%zu gains[2][%i] = %f", s.source_id, i, s.gains[2][i]);
|
||||
}
|
||||
}
|
||||
|
||||
// if (config.unknown_flag) {
|
||||
//config.unknown_flag = 0;
|
||||
// LOG_WARNING(Audio_DSP, "(STUB) unknown_flag is set!!!");
|
||||
// }
|
||||
|
||||
if (config.format_dirty || config.embedded_buffer_dirty) {
|
||||
config.format_dirty.Assign(0);
|
||||
s.format = config.format;
|
||||
LOG_DEBUG(Audio_DSP, "source_id=%zu format=%u", s.source_id, s.format);
|
||||
}
|
||||
|
||||
if (config.mono_or_stereo_dirty || config.embedded_buffer_dirty) {
|
||||
config.mono_or_stereo_dirty.Assign(0);
|
||||
s.mono_or_stereo = config.mono_or_stereo;
|
||||
LOG_DEBUG(Audio_DSP, "source_id=%zu mono_or_stereo=%u", s.source_id, s.mono_or_stereo);
|
||||
}
|
||||
|
||||
if (config.buffer_queue_dirty) {
|
||||
config.buffer_queue_dirty.Assign(0);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (config.buffers_dirty & (1 << i)) {
|
||||
const auto& b = config.buffers[i];
|
||||
s.queue.emplace(Buffer{
|
||||
b.physical_address,
|
||||
b.length,
|
||||
(u8)b.adpcm_ps,
|
||||
{ b.adpcm_yn[0], b.adpcm_yn[1] },
|
||||
b.adpcm_dirty != 0,
|
||||
b.is_looping != 0,
|
||||
b.buffer_id,
|
||||
s.mono_or_stereo,
|
||||
s.format,
|
||||
true
|
||||
});
|
||||
LOG_TRACE(Audio_DSP, "enqueueing queued %i addr=0x%08x len=%u id=%u", i, b.physical_address, b.length, b.buffer_id);
|
||||
}
|
||||
}
|
||||
config.buffers_dirty = 0;
|
||||
}
|
||||
|
||||
if (config.embedded_buffer_dirty) {
|
||||
config.embedded_buffer_dirty.Assign(0);
|
||||
s.queue.emplace(Buffer {
|
||||
config.physical_address,
|
||||
config.length,
|
||||
(u8)config.adpcm_ps,
|
||||
{ config.adpcm_yn[0], config.adpcm_yn[1] },
|
||||
config.adpcm_dirty.ToBool(),
|
||||
config.is_looping.ToBool(),
|
||||
config.buffer_id,
|
||||
s.mono_or_stereo,
|
||||
s.format,
|
||||
false
|
||||
});
|
||||
LOG_TRACE(Audio_DSP, "enqueueing embedded addr=0x%08x len=%u id=%u", config.physical_address, config.length, config.buffer_id);
|
||||
}
|
||||
|
||||
if (config.interpolation_dirty) {
|
||||
config.interpolation_dirty.Assign(0);
|
||||
//config.interpolation_mode
|
||||
LOG_DEBUG(Audio_DSP, "source_id=%zu interpolation_mode=%u ", s.source_id, config.interpolation_mode);
|
||||
}
|
||||
|
||||
if (config.dirty_raw) {
|
||||
LOG_WARNING(Audio_DSP, "source_id=%zu remaining_dirty=%x", s.source_id, config.dirty_raw);
|
||||
}
|
||||
|
||||
config.dirty_raw = 0;
|
||||
}
|
||||
|
||||
static bool DequeueBuffer(State& s) {
|
||||
if (!s.current_buffer.empty())
|
||||
return true;
|
||||
if (s.queue.empty())
|
||||
return false;
|
||||
|
||||
const Buffer buf = s.queue.top();
|
||||
s.queue.pop();
|
||||
|
||||
const u8* const memory = Memory::GetPhysicalPointer(buf.physical_address);
|
||||
ASSERT(memory);
|
||||
|
||||
if (buf.adpcm_dirty) {
|
||||
s.adpcm_state.yn1 = buf.adpcm_yn[0];
|
||||
s.adpcm_state.yn2 = buf.adpcm_yn[1];
|
||||
}
|
||||
|
||||
if (buf.is_looping) {
|
||||
LOG_ERROR(Audio_DSP, "Looped buffers are unimplemented at the moment");
|
||||
}
|
||||
|
||||
const unsigned num_channels = buf.mono_or_stereo == MonoOrStereo::Stereo ? 2 : 1;
|
||||
switch (buf.format) {
|
||||
case Format::PCM8:
|
||||
s.current_buffer = Codec::DecodePCM8(num_channels, memory, buf.length);
|
||||
break;
|
||||
case Format::PCM16:
|
||||
s.current_buffer = Codec::DecodePCM16(num_channels, memory, buf.length);
|
||||
break;
|
||||
case Format::ADPCM:
|
||||
ASSERT(num_channels == 1);
|
||||
s.current_buffer = Codec::DecodeADPCM(memory, buf.length, s.adpcm_coeffs, s.adpcm_state);
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED();
|
||||
break;
|
||||
}
|
||||
|
||||
s.current_sample_number = s.next_sample_number = 0;
|
||||
s.current_buffer_id = buf.buffer_id;
|
||||
s.buffer_update = buf.from_queue;
|
||||
|
||||
LOG_TRACE(Audio_DSP, "source_id=%u buffer_id=%u from_queue=%d", s.source_id, buf.buffer_id, buf.from_queue);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ResampleBuffer(State& s) {
|
||||
s.current_frame.fill({});
|
||||
|
||||
s.current_sample_number = s.next_sample_number;
|
||||
while (true) {
|
||||
if (!DequeueBuffer(s))
|
||||
break;
|
||||
|
||||
auto result = AudioInterp::None(s.interp_state, s.current_frame, s.current_buffer, s.rate_multiplier);
|
||||
s.next_sample_number += std::get<0>(result);
|
||||
if (!std::get<1>(result))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void AdvanceFrame(State& s) {
|
||||
ResampleBuffer(s);
|
||||
|
||||
if (s.current_sample_number == s.next_sample_number) {
|
||||
s.enabled = false;
|
||||
}
|
||||
|
||||
// TODO: Filters
|
||||
}
|
||||
|
||||
static void UpdateStatus(State& s, SourceStatus::Status& status) {
|
||||
// Applications depend on the correct emulation of
|
||||
// previous_buffer_id_dirty and previous_buffer_id to synchronise
|
||||
// audio with video.
|
||||
status.is_enabled = s.enabled;
|
||||
status.current_buffer_id_dirty = s.buffer_update ? 1 : 0;
|
||||
s.buffer_update = false;
|
||||
status.current_buffer_id = s.current_buffer_id;
|
||||
status.buffer_position = s.current_sample_number;
|
||||
status.sync = s.sync;
|
||||
}
|
||||
|
||||
std::array<State, AudioCore::num_sources> state = {};
|
||||
|
||||
void SourceInit() {
|
||||
state = {};
|
||||
for (size_t i = 0; i < state.size(); i++) {
|
||||
state[i] = {};
|
||||
state[i].source_id = i;
|
||||
}
|
||||
}
|
||||
|
||||
void SourceUpdate(int source_id, SourceConfiguration::Configuration& config, const s16_le adpcm_coeffs[16], SourceStatus::Status& status) {
|
||||
ASSERT(source_id >= 0 && source_id < AudioCore::num_sources);
|
||||
|
||||
ParseConfig(state[source_id], config, adpcm_coeffs);
|
||||
AdvanceFrame(state[source_id]);
|
||||
UpdateStatus(state[source_id], status);
|
||||
}
|
||||
|
||||
const QuadFrame32& SourceFrame(int source_id) {
|
||||
ASSERT(source_id >= 0 && source_id < AudioCore::num_sources);
|
||||
ASSERT(state[source_id].enabled);
|
||||
|
||||
return state[source_id].current_frame;
|
||||
}
|
||||
|
||||
void SourceFrameMixInto(QuadFrame32& dest, int source_id, int intermediate_mix_id) {
|
||||
ASSERT(source_id >= 0 && source_id < AudioCore::num_sources);
|
||||
ASSERT(intermediate_mix_id >= 0 && intermediate_mix_id < 3);
|
||||
|
||||
const State& s = state[source_id];
|
||||
|
||||
if (!s.enabled)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < dest.size(); i++) {
|
||||
for (int channel = 0; channel < 4; channel++) {
|
||||
dest[i][channel] += s.gains[intermediate_mix_id][channel] * s.current_frame[i][channel];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "audio_core/audio_core.h"
|
||||
#include "audio_core/hle/common.h"
|
||||
#include "audio_core/hle/dsp.h"
|
||||
|
||||
namespace DSP {
|
||||
namespace HLE {
|
||||
|
||||
/// Initialise this DSP module
|
||||
void SourceInit();
|
||||
|
||||
/**
|
||||
* Perform processing for this DSP module.
|
||||
* This module performs:
|
||||
* - Buffer management
|
||||
* - Decoding of buffers
|
||||
* - Buffer resampling and interpolation
|
||||
* - Per-source filtering (SimpleFilter, BiquadFilter)
|
||||
* - Per-source gain
|
||||
*/
|
||||
void SourceUpdate(int source_id, SourceConfiguration::Configuration& config, const s16_le adpcm_coeffs[16], SourceStatus::Status& status);
|
||||
|
||||
/// Output of this DSP module.
|
||||
const QuadFrame32& SourceFrame(int source_id);
|
||||
|
||||
/// Mix current frame from source_id into buffer based on gain coefficients for intermediate_mix_id.
|
||||
void SourceFrameMixInto(QuadFrame32& dest, int source_id, int intermediate_mix_id);
|
||||
|
||||
}
|
||||
}
|
@ -1,237 +0,0 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <cmath>
|
||||
|
||||
#include "audio_core/interpolate.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/math_util.h"
|
||||
|
||||
namespace AudioInterp {
|
||||
|
||||
/// Kaiser window with alpha=2.4, N=11
|
||||
static const std::array<double, required_history/2> kaiser_window {{
|
||||
0.327949,
|
||||
0.521302,
|
||||
0.707379,
|
||||
0.861996,
|
||||
0.964250
|
||||
}};
|
||||
|
||||
struct BiquadLpf {
|
||||
/// Calculate coefficients required for a biquad filter to behave as a low-pass filter.
|
||||
void Init(double freq) {
|
||||
const double w0 = 2 * M_PI * freq;
|
||||
const double Q = 0.707;
|
||||
const double a = sin(w0) / (2.0*Q);
|
||||
double a0;
|
||||
|
||||
b0 = 0.5 * (1.0 - cos(w0));
|
||||
b1 = (1.0 - cos(w0));
|
||||
b2 = 0.5 * (1.0 - cos(w0));
|
||||
a0 = 1.0 + a;
|
||||
a1 = -2.0 * cos(w0);
|
||||
a2 = 1.0 - a;
|
||||
|
||||
// Normalize;
|
||||
b0 /= a0;
|
||||
b1 /= a0;
|
||||
b2 /= a0;
|
||||
a1 /= a0;
|
||||
a2 /= a0;
|
||||
}
|
||||
inline s32 Process(s32 x) {
|
||||
double xn0 = x;
|
||||
double yn0 = b0 * xn0 + b1 * xn1 + b2 * xn2 - a1 * yn1 - a2 * yn2;
|
||||
|
||||
// Advance state
|
||||
xn2 = xn1;
|
||||
xn1 = xn0;
|
||||
yn2 = yn1;
|
||||
yn1 = yn0;
|
||||
|
||||
return (s32)yn0;
|
||||
}
|
||||
private:
|
||||
double a1, a2;
|
||||
double b0, b1, b2;
|
||||
double xn1 = 0.0, xn2 = 0.0, yn1 = 0.0, yn2 = 0.0;
|
||||
};
|
||||
|
||||
double sinc(double x) {
|
||||
DEBUG_ASSERT(x != 0);
|
||||
return sin(x) / x;
|
||||
}
|
||||
|
||||
std::tuple<size_t, bool> KaiserSinc(State& state, DSP::HLE::QuadFrame32& output, std::array<std::vector<s16>, 2>& input, const float rate_change) {
|
||||
ASSERT(input[0].size() == input[1].size());
|
||||
ASSERT(input[0].size() > required_history);
|
||||
|
||||
size_t position = 0;
|
||||
double& position_fractional = state.position_fractional;
|
||||
|
||||
for (int j = 0; j < 2; j++) {
|
||||
input[j].insert(input[j].begin(), state.history[j].begin(), state.history[j].end());
|
||||
}
|
||||
|
||||
std::array<BiquadLpf, 2> lpf;
|
||||
const double lpf_cutoff = std::min(0.5 * rate_change, 0.5 / rate_change);
|
||||
lpf[0].Init(lpf_cutoff);
|
||||
lpf[1].Init(lpf_cutoff);
|
||||
|
||||
auto step = [&](size_t i) -> s32 {
|
||||
auto& in = input[i];
|
||||
s32 sample = 0;
|
||||
sample += kaiser_window[0] * sinc(-5.0 - position_fractional) * in[position + 0];
|
||||
sample += kaiser_window[1] * sinc(-4.0 - position_fractional) * in[position + 1];
|
||||
sample += kaiser_window[2] * sinc(-3.0 - position_fractional) * in[position + 2];
|
||||
sample += kaiser_window[3] * sinc(-2.0 - position_fractional) * in[position + 3];
|
||||
sample += kaiser_window[4] * sinc(-1.0 - position_fractional) * in[position + 4];
|
||||
sample += in[position + 5];
|
||||
sample += kaiser_window[4] * sinc(+1.0 - position_fractional) * in[position + 6];
|
||||
sample += kaiser_window[3] * sinc(+2.0 - position_fractional) * in[position + 7];
|
||||
sample += kaiser_window[2] * sinc(+3.0 - position_fractional) * in[position + 8];
|
||||
sample += kaiser_window[1] * sinc(+4.0 - position_fractional) * in[position + 9];
|
||||
sample += kaiser_window[0] * sinc(+5.0 - position_fractional) * in[position + 10];
|
||||
//sample = lpf[i].Process(sample);
|
||||
return sample;
|
||||
};
|
||||
|
||||
const size_t position_stop = input[0].size() - required_history;
|
||||
while (state.output_position < output[0].size() && position < position_stop) {
|
||||
s32 sample0 = step(0);
|
||||
s32 sample1 = step(1);
|
||||
|
||||
output[0][state.output_position] = sample0;
|
||||
output[1][state.output_position] = sample0;
|
||||
output[2][state.output_position] = sample1;
|
||||
output[3][state.output_position] = sample1;
|
||||
|
||||
position_fractional += rate_change;
|
||||
position += (size_t)position_fractional;
|
||||
position_fractional -= (size_t)position_fractional;
|
||||
|
||||
state.output_position++;
|
||||
}
|
||||
|
||||
bool continue_feeding_me = true;
|
||||
if (state.output_position >= output[0].size()) {
|
||||
state.output_position = 0;
|
||||
continue_feeding_me = false;
|
||||
}
|
||||
|
||||
for (int j = 0; j < 2; j++) {
|
||||
std::copy(input[j].begin() + position,
|
||||
input[j].begin() + position + required_history,
|
||||
state.history[j].begin());
|
||||
if (position + required_history >= input[j].size()) {
|
||||
input[j].clear();
|
||||
} else {
|
||||
input[j].erase(input[j].begin(),
|
||||
input[j].begin() + position + required_history);
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT(input[0].size() == input[1].size());
|
||||
|
||||
return std::make_tuple(position, continue_feeding_me);
|
||||
}
|
||||
|
||||
std::tuple<size_t, bool> Linear(State& state, DSP::HLE::QuadFrame32& output, std::array<std::vector<s16>, 2>& input, const float rate_change) {
|
||||
ASSERT(input[0].size() == input[1].size());
|
||||
while (input[0].size() < 2) {
|
||||
input[0].emplace_back(0);
|
||||
input[1].emplace_back(0);
|
||||
}
|
||||
|
||||
size_t position = 0;
|
||||
double& position_fractional = state.position_fractional;
|
||||
|
||||
auto step = [&](size_t i) -> s32 {
|
||||
auto& in = input[i];
|
||||
s32 sample = 0;
|
||||
sample = position_fractional * in[position + 0] + (1.0 - position_fractional) * in[position + 1];
|
||||
return sample;
|
||||
};
|
||||
|
||||
const size_t position_stop = input.size() - 1;
|
||||
while (state.output_position < output.size() && position < position_stop) {
|
||||
s32 sample0 = step(0);
|
||||
s32 sample1 = step(1);
|
||||
|
||||
output[state.output_position][0] = sample0;
|
||||
output[state.output_position][1] = sample0;
|
||||
output[state.output_position][2] = sample1;
|
||||
output[state.output_position][3] = sample1;
|
||||
|
||||
position_fractional += rate_change;
|
||||
position += (size_t)position_fractional;
|
||||
position_fractional -= (size_t)position_fractional;
|
||||
|
||||
state.output_position++;
|
||||
}
|
||||
|
||||
bool continue_feeding_me = true;
|
||||
if (state.output_position >= output[0].size()) {
|
||||
state.output_position = 0;
|
||||
continue_feeding_me = false;
|
||||
}
|
||||
|
||||
for (int j = 0; j < 2; j++) {
|
||||
if (position >= input[j].size()) {
|
||||
input[j].clear();
|
||||
} else {
|
||||
input[j].erase(input[j].begin(),
|
||||
input[j].begin() + position);
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT(input[0].size() == input[1].size());
|
||||
|
||||
return std::make_tuple(position, continue_feeding_me);
|
||||
}
|
||||
|
||||
std::tuple<size_t, bool> None(State& state, DSP::HLE::QuadFrame32& output, std::vector<std::array<s16, 2>>& input, const float rate_change) {
|
||||
size_t position = 0;
|
||||
double& position_fractional = state.position_fractional;
|
||||
|
||||
auto step = [&](size_t i) -> s32 {
|
||||
return input[position][i];
|
||||
};
|
||||
|
||||
const size_t position_stop = input.size();
|
||||
while (state.output_position < output.size() && position < position_stop) {
|
||||
s32 sample0 = step(0);
|
||||
s32 sample1 = step(1);
|
||||
|
||||
output[state.output_position][0] = sample0;
|
||||
output[state.output_position][1] = sample0;
|
||||
output[state.output_position][2] = sample1;
|
||||
output[state.output_position][3] = sample1;
|
||||
|
||||
position_fractional += rate_change;
|
||||
position += (size_t)position_fractional;
|
||||
position_fractional -= (size_t)position_fractional;
|
||||
|
||||
state.output_position++;
|
||||
}
|
||||
|
||||
bool continue_feeding_me = true;
|
||||
if (state.output_position >= output.size()) {
|
||||
state.output_position = 0;
|
||||
continue_feeding_me = false;
|
||||
}
|
||||
|
||||
if (position >= input.size()) {
|
||||
input.clear();
|
||||
} else {
|
||||
input.erase(input.begin(), input.begin() + position);
|
||||
}
|
||||
|
||||
return std::make_tuple(position, continue_feeding_me);
|
||||
}
|
||||
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
#include "audio_core/hle/common.h"
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioInterp {
|
||||
|
||||
constexpr size_t required_history = 11;
|
||||
|
||||
struct State {
|
||||
double position_fractional = 0;
|
||||
size_t output_position = 0;
|
||||
std::array<std::array<s16, required_history>, 2> history = {};
|
||||
};
|
||||
|
||||
std::tuple<size_t, bool> KaiserSinc(State& state, DSP::HLE::QuadFrame32& output, std::array<std::vector<s16>, 2>& input, const float rate_change);
|
||||
|
||||
std::tuple<size_t, bool> Linear(State& state, DSP::HLE::QuadFrame32& output, std::array<std::vector<s16>, 2>& input, const float rate_change);
|
||||
|
||||
std::tuple<size_t, bool> None(State& state, DSP::HLE::QuadFrame32& output, std::vector<std::array<s16, 2>>& input, const float rate_change);
|
||||
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
@ -1,9 +0,0 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
}
|
@ -1,118 +0,0 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include "audio_core/audio_core.h"
|
||||
#include "audio_core/sdl2_sink.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
std::unique_ptr<Sink> sink(new SDL2Sink());
|
||||
|
||||
SDL2Sink::SDL2Sink() {
|
||||
if (SDL_Init(SDL_INIT_AUDIO) < 0) {
|
||||
LOG_CRITICAL(Audio_SDL2, "SDL_Init(SDL_INIT_AUDIO) failed");
|
||||
exit(-2);
|
||||
}
|
||||
|
||||
SDL_AudioSpec desired_audiospec;
|
||||
SDL_zero(desired_audiospec);
|
||||
desired_audiospec.format = AUDIO_S16;
|
||||
desired_audiospec.channels = 2;
|
||||
desired_audiospec.freq = AudioCore::native_sample_rate;
|
||||
desired_audiospec.samples = 4096;
|
||||
desired_audiospec.userdata = this;
|
||||
desired_audiospec.callback = &SDL2Sink::Callback; // We're going to use SDL_QueueAudio
|
||||
|
||||
SDL_AudioSpec obtained_audiospec;
|
||||
SDL_zero(obtained_audiospec);
|
||||
|
||||
audio_device_id = SDL_OpenAudioDevice(nullptr, /*iscapture=*/false, &desired_audiospec, &obtained_audiospec, 0);
|
||||
if (audio_device_id < 0) {
|
||||
LOG_CRITICAL(Audio_SDL2, "SDL_OpenAudioDevice failed");
|
||||
exit(-2);
|
||||
}
|
||||
|
||||
sample_rate = obtained_audiospec.freq;
|
||||
|
||||
SDL_PauseAudioDevice(audio_device_id, 0);
|
||||
}
|
||||
|
||||
SDL2Sink::~SDL2Sink() {
|
||||
}
|
||||
|
||||
/// The native rate of this sink. The sink expects to be fed samples that respect this. (Units: samples/sec)
|
||||
unsigned SDL2Sink::GetNativeSampleRate() const {
|
||||
return sample_rate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Feed stereo samples to sink.
|
||||
* @param samples Samples in interleaved stereo PCM16 format. Size of vector must be multiple of two.
|
||||
*/
|
||||
void SDL2Sink::EnqueueSamples(const std::vector<s16>& samples) {
|
||||
ASSERT(samples.size() % 2 == 0);
|
||||
SDL_LockAudioDevice(audio_device_id);
|
||||
queue.emplace_back(samples);
|
||||
SDL_UnlockAudioDevice(audio_device_id);
|
||||
}
|
||||
|
||||
/// Samples enqueued that have not been played yet.
|
||||
size_t SDL2Sink::SamplesInQueue() const {
|
||||
const size_t queue_size = RealQueueSize() + dequeue_consumed;
|
||||
|
||||
if (dequeue_consumed == 0)
|
||||
return queue_size;
|
||||
|
||||
const std::chrono::duration<double> duration = std::chrono::steady_clock::now() - dequeue_time;
|
||||
const size_t estimated_samples_consumed = sample_rate * duration.count();
|
||||
|
||||
if (estimated_samples_consumed > queue_size)
|
||||
return 0;
|
||||
|
||||
return queue_size - estimated_samples_consumed;
|
||||
}
|
||||
|
||||
size_t SDL2Sink::RealQueueSize() const {
|
||||
size_t total_size = 0;
|
||||
SDL_LockAudioDevice(audio_device_id);
|
||||
for (const auto& buf : queue) {
|
||||
total_size += buf.size() / 2;
|
||||
}
|
||||
SDL_UnlockAudioDevice(audio_device_id);
|
||||
return total_size;
|
||||
}
|
||||
|
||||
void SDL2Sink::Callback(void* sink_, u8* buffer, int buffer_size) {
|
||||
SDL2Sink* sink = reinterpret_cast<SDL2Sink*>(sink_);
|
||||
buffer_size /= sizeof(s16); // Convert to number of half-samples.
|
||||
|
||||
sink->dequeue_time = std::chrono::steady_clock::now();
|
||||
sink->dequeue_consumed = buffer_size / 2;
|
||||
|
||||
while (buffer_size > 0 && !sink->queue.empty()) {
|
||||
if (sink->queue.front().size() <= buffer_size) {
|
||||
memcpy(buffer, sink->queue.front().data(), sink->queue.front().size() * sizeof(s16));
|
||||
buffer += sink->queue.front().size() * sizeof(s16);
|
||||
buffer_size -= sink->queue.front().size();
|
||||
sink->queue.pop_front();
|
||||
} else {
|
||||
memcpy(buffer, sink->queue.front().data(), buffer_size * sizeof(s16));
|
||||
buffer += buffer_size * sizeof(s16);
|
||||
sink->queue.front().erase(sink->queue.front().begin(), sink->queue.front().begin() + buffer_size);
|
||||
buffer_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer_size > 0) {
|
||||
sink->dequeue_consumed -= buffer_size / 2;
|
||||
memset(buffer, 0, buffer_size * sizeof(s16));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
|
||||
#include "audio_core/sink.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
class SDL2Sink final : public Sink {
|
||||
public:
|
||||
SDL2Sink();
|
||||
~SDL2Sink() override;
|
||||
|
||||
/// The native rate of this sink. The sink expects to be fed samples that respect this. (Units: samples/sec)
|
||||
unsigned GetNativeSampleRate() const override;
|
||||
|
||||
/**
|
||||
* Feed stereo samples to sink.
|
||||
* @param samples Samples in interleaved stereo PCM16 format. Size of vector must be multiple of two.
|
||||
*/
|
||||
void EnqueueSamples(const std::vector<s16>& samples) override;
|
||||
|
||||
/// Samples enqueued that have not been played yet.
|
||||
size_t SamplesInQueue() const override;
|
||||
|
||||
private:
|
||||
using SDL_AudioDeviceID = u32;
|
||||
unsigned sample_rate;
|
||||
SDL_AudioDeviceID audio_device_id;
|
||||
|
||||
std::list<std::vector<s16>> queue;
|
||||
size_t RealQueueSize() const;
|
||||
static void Callback(void* sink, u8* buffer, int buffer_size);
|
||||
|
||||
std::chrono::steady_clock::time_point dequeue_time;
|
||||
size_t dequeue_consumed = 0;
|
||||
};
|
||||
|
||||
}
|
@ -4,7 +4,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
@ -32,6 +31,4 @@ public:
|
||||
virtual std::size_t SamplesInQueue() const = 0;
|
||||
};
|
||||
|
||||
extern std::unique_ptr<Sink> sink;
|
||||
|
||||
} // namespace
|
||||
|
@ -1,83 +0,0 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <functional>
|
||||
#include <list>
|
||||
#include <numeric>
|
||||
#include <vector>
|
||||
|
||||
#include <SoundTouch.h>
|
||||
|
||||
#include "audio_core/audio_core.h"
|
||||
#include "audio_core/sink.h"
|
||||
#include "audio_core/time_stretch.h"
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/math_util.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
namespace TimeStretch {
|
||||
|
||||
static soundtouch::SoundTouch soundtouch;
|
||||
|
||||
using steady_clock = std::chrono::steady_clock;
|
||||
|
||||
steady_clock::time_point frame_timer = steady_clock::now();
|
||||
double smooth_ratio = 1.0;
|
||||
void Tick(unsigned samples_in_queue) {
|
||||
const steady_clock::time_point now = steady_clock::now();
|
||||
const std::chrono::duration<double> duration = now - frame_timer;
|
||||
frame_timer = now;
|
||||
|
||||
constexpr double native_frame_time = (double)AudioCore::samples_per_frame / (double)AudioCore::native_sample_rate;
|
||||
const double actual_frame_time = duration.count();
|
||||
|
||||
double ratio = actual_frame_time / native_frame_time;
|
||||
ratio = MathUtil::Clamp<double>(ratio, 0.01, 100.0);
|
||||
|
||||
// TODO: Uhh was just reading this and this seems super wonky double-check your logic wtf are you thinking.
|
||||
if (samples_in_queue < 4096) {
|
||||
ratio = ratio > 1.0 ? ratio * ratio : 1.0;
|
||||
ratio = MathUtil::Clamp<double>(ratio, 0.01, 100.0);
|
||||
} else if (AudioCore::sink->SamplesInQueue() > 16000) {
|
||||
ratio = ratio > 1.0 ? sqrt(ratio) : 0.01;
|
||||
ratio = MathUtil::Clamp<double>(ratio, 0.01, 100.0);
|
||||
}
|
||||
|
||||
smooth_ratio = 0.993 * smooth_ratio + 0.007 * ratio;
|
||||
smooth_ratio = MathUtil::Clamp<double>(smooth_ratio, 0.01, 100.0);
|
||||
|
||||
//printf("%f, %f\n", ratio, smooth_ratio);
|
||||
|
||||
soundtouch.setTempo(1.0 / smooth_ratio);
|
||||
}
|
||||
|
||||
void Init() {
|
||||
soundtouch.setTempo(1.0);
|
||||
soundtouch.setChannels(2);
|
||||
}
|
||||
|
||||
void Shutdown() {
|
||||
soundtouch.setTempo(1.0);
|
||||
}
|
||||
|
||||
void AddSamples(const std::array<std::array<s16, 2>, AudioCore::samples_per_frame>& samples) {
|
||||
// FIXME: lol don't do this c-style cast
|
||||
soundtouch.putSamples((s16*)samples.data(), AudioCore::samples_per_frame);
|
||||
}
|
||||
|
||||
void OutputSamples(std::function<void(const std::vector<s16>&)> fn) {
|
||||
size_t available = soundtouch.numSamples();
|
||||
|
||||
std::vector<s16> output(available * 2);
|
||||
|
||||
soundtouch.receiveSamples(output.data(), available);
|
||||
|
||||
fn(output);
|
||||
}
|
||||
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace TimeStretch {
|
||||
|
||||
void Init();
|
||||
void Shutdown();
|
||||
|
||||
void Tick(unsigned samples_in_queue);
|
||||
void AddSamples(const std::array<std::array<s16, 2>, AudioCore::samples_per_frame>& samples);
|
||||
void OutputSamples(std::function<void(const std::vector<s16>&)> fn);
|
||||
|
||||
}
|
@ -117,8 +117,6 @@ if (Qt5_FOUND AND MSVC)
|
||||
)
|
||||
windows_copy_files(citra-qt ${Qt5_PLATFORMS_DIR} ${PLATFORMS} qwindows$<$<CONFIG:Debug>:d>.*)
|
||||
|
||||
windows_copy_files(citra-qt ${SDL2_DLL_DIR} ${DLL_DEST} SDL2.dll)
|
||||
|
||||
unset(Qt5_DLL_DIR)
|
||||
unset(Qt5_PLATFORMS_DIR)
|
||||
unset(DLL_DEST)
|
||||
|
@ -65,7 +65,6 @@ namespace Log {
|
||||
SUB(Render, OpenGL) \
|
||||
CLS(Audio) \
|
||||
SUB(Audio, DSP) \
|
||||
SUB(Audio, SDL2) \
|
||||
CLS(Loader)
|
||||
|
||||
// GetClassName is a macro defined by Windows.h, grrr...
|
||||
|
@ -80,7 +80,6 @@ enum class Class : ClassType {
|
||||
Render_OpenGL, ///< OpenGL backend
|
||||
Audio, ///< Emulator audio output
|
||||
Audio_DSP, ///< The HLE implementation of the DSP
|
||||
Audio_SDL2, ///< SDL2 frontend for audio output
|
||||
Loader, ///< ROM loader
|
||||
|
||||
Count ///< Total number of logging classes
|
||||
|
@ -20,55 +20,29 @@ namespace DSP_DSP {
|
||||
static u32 read_pipe_count;
|
||||
static Kernel::SharedPtr<Kernel::Event> semaphore_event;
|
||||
|
||||
enum class InterruptType {
|
||||
Zero = 0, // Unknown purpose. Channel is always zero.
|
||||
One = 1, // Unknown purpose. Channel is always zero.
|
||||
Pipe = 2, // Related to a pipe
|
||||
MAX
|
||||
};
|
||||
constexpr size_t InterruptType_MAX = static_cast<size_t>(InterruptType::MAX);
|
||||
|
||||
/// Map of (interrupt number, channel number) to Kernel::Events. See: RegisterInterruptEvents
|
||||
static std::array<std::unordered_map<u32, Kernel::SharedPtr<Kernel::Event>>, InterruptType_MAX> interrupt_events;
|
||||
constexpr size_t max_number_of_interrupt_events = 6;
|
||||
|
||||
size_t GetNumberOfRegisteredEvents() {
|
||||
size_t number = 0;
|
||||
for (const auto& events : interrupt_events) {
|
||||
number += events.size();
|
||||
struct PairHash {
|
||||
template <typename T, typename U>
|
||||
std::size_t operator()(const std::pair<T, U> &x) const {
|
||||
// TODO(yuriks): Replace with better hash combining function.
|
||||
return std::hash<T>()(x.first) ^ std::hash<U>()(x.second);
|
||||
}
|
||||
return number;
|
||||
}
|
||||
};
|
||||
|
||||
/// Map of (audio interrupt number, channel number) to Kernel::Events. See: RegisterInterruptEvents
|
||||
static std::unordered_map<std::pair<u32, u32>, Kernel::SharedPtr<Kernel::Event>, PairHash> interrupt_events;
|
||||
|
||||
// DSP Interrupts:
|
||||
// Interrupt (2, 2) occurs every frame tick. Userland programs normally have a thread that's waiting
|
||||
// Interrupt #2 occurs every frame tick. Userland programs normally have a thread that's waiting
|
||||
// for an interrupt event. Immediately after this interrupt event, userland normally updates the
|
||||
// state in the next region and increments the relevant frame counter by two.
|
||||
void SignalAllInterrupts() {
|
||||
// HACK: The other interrupts have currently unknown purpose, we trigger them each tick in any case.
|
||||
for (auto& events : interrupt_events)
|
||||
for (auto& event : events)
|
||||
event.second->Signal();
|
||||
for (auto& interrupt_event : interrupt_events)
|
||||
interrupt_event.second->Signal();
|
||||
}
|
||||
|
||||
void SignalInterrupt(u32 interrupt, u32 channel) {
|
||||
ASSERT(interrupt < interrupt_events.size());
|
||||
if (interrupt == 0 || interrupt == 1)
|
||||
ASSERT(channel == 0);
|
||||
|
||||
auto& events = interrupt_events[interrupt];
|
||||
if (events.find(channel) != events.end()) {
|
||||
events[channel]->Signal();
|
||||
}
|
||||
}
|
||||
|
||||
bool SemaphoreSignalled() {
|
||||
if (semaphore_event->signaled) {
|
||||
semaphore_event->Clear();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
interrupt_events[std::make_pair(interrupt, channel)]->Signal();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -84,7 +58,6 @@ static void ConvertProcessAddressFromDspDram(Service::Interface* self) {
|
||||
|
||||
u32 addr = cmd_buff[1];
|
||||
|
||||
cmd_buff[0] = 0xC0080;
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
cmd_buff[2] = (addr << 1) + (Memory::DSP_RAM_VADDR + 0x40000);
|
||||
|
||||
@ -140,11 +113,9 @@ static void LoadComponent(Service::Interface* self) {
|
||||
static void GetSemaphoreEventHandle(Service::Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
|
||||
cmd_buff[0] = 0x160042;
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
cmd_buff[3] = Kernel::g_handle_table.Create(semaphore_event).MoveFrom(); // Event handle
|
||||
|
||||
|
||||
LOG_WARNING(Service_DSP, "(STUBBED) called");
|
||||
}
|
||||
|
||||
@ -186,47 +157,23 @@ static void FlushDataCache(Service::Interface* self) {
|
||||
static void RegisterInterruptEvents(Service::Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
|
||||
u32 type_num = cmd_buff[1];
|
||||
u32 interrupt = cmd_buff[1];
|
||||
u32 channel = cmd_buff[2];
|
||||
u32 event_handle = cmd_buff[4];
|
||||
|
||||
if (cmd_buff[3] != 0) {
|
||||
cmd_buff[0] = 0x40;
|
||||
cmd_buff[1] = 0xD9001830;
|
||||
return;
|
||||
}
|
||||
|
||||
InterruptType type = static_cast<InterruptType>(type_num);
|
||||
if (type == InterruptType::Zero || type == InterruptType::One) {
|
||||
channel = 0;
|
||||
} else if (type == InterruptType::Pipe) {
|
||||
if (channel >= DSP::HLE::DspPipe_MAX) {
|
||||
LOG_ERROR(Service_DSP, "Invalid (type, channel) combination (%u, %u)", type, channel);
|
||||
}
|
||||
} else {
|
||||
// I suspect that interrupt values greater than two are invalid.
|
||||
LOG_ERROR(Service_DSP, "Unimplemented (type, channel) combination (%u, %u)", type, channel);
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
cmd_buff[0] = 0x150040;
|
||||
if (event_handle) {
|
||||
auto evt = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[4]);
|
||||
if (evt) {
|
||||
if (GetNumberOfRegisteredEvents() < max_number_of_interrupt_events) {
|
||||
interrupt_events[type_num][channel] = evt;
|
||||
LOG_INFO(Service_DSP, "Registered type=%u, channel=%u, event_handle=0x%08X", type, channel, event_handle);
|
||||
} else {
|
||||
cmd_buff[1] = 0xC860A7FF;
|
||||
LOG_ERROR(Service_DSP, "Ran out of space to register interrupts");
|
||||
}
|
||||
interrupt_events[std::make_pair(interrupt, channel)] = evt;
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||
LOG_INFO(Service_DSP, "Registered interrupt=%u, channel=%u, event_handle=0x%08X", interrupt, channel, event_handle);
|
||||
} else {
|
||||
LOG_CRITICAL(Service_DSP, "Invalid event handle! type=%u, channel=%u, event_handle=0x%08X", type, channel, event_handle);
|
||||
LOG_CRITICAL(Service_DSP, "Invalid event handle! interrupt=%u, channel=%u, event_handle=0x%08X", interrupt, channel, event_handle);
|
||||
ASSERT(false); // This should really be handled at a IPC translation layer.
|
||||
}
|
||||
} else {
|
||||
interrupt_events[type_num].erase(channel);
|
||||
LOG_INFO(Service_DSP, "Unregistered type=%u, channel=%u, event_handle=0x%08X", type, channel, event_handle);
|
||||
interrupt_events.erase(std::make_pair(interrupt, channel));
|
||||
LOG_INFO(Service_DSP, "Unregistered interrupt=%u, channel=%u, event_handle=0x%08X", interrupt, channel, event_handle);
|
||||
}
|
||||
}
|
||||
|
||||
@ -240,13 +187,8 @@ static void RegisterInterruptEvents(Service::Interface* self) {
|
||||
static void SetSemaphore(Service::Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
|
||||
cmd_buff[0] = 0x70040;
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
|
||||
// Observed Behaviour: Waits for DSP_PSEM to be clear then sets DSP_PSEM.
|
||||
|
||||
SignalAllInterrupts(); // This is a HACK
|
||||
|
||||
LOG_WARNING(Service_DSP, "(STUBBED) called");
|
||||
}
|
||||
|
||||
@ -268,13 +210,7 @@ static void WriteProcessPipe(Service::Interface* self) {
|
||||
u32 size = cmd_buff[2];
|
||||
u32 buffer = cmd_buff[4];
|
||||
|
||||
if (IPC::StaticBufferDesc(size, 1) != cmd_buff[3]) {
|
||||
LOG_ERROR(Service_DSP, "IPC static buffer descriptor failed validation (0x%X). pipe=%u, size=0x%X, buffer=0x%08X", cmd_buff[3], pipe, size, buffer);
|
||||
cmd_buff[0] = 0x40;
|
||||
cmd_buff[1] = 0xD9001830;
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT_MSG(IPC::StaticBufferDesc(size, 1) == cmd_buff[3], "IPC static buffer descriptor failed validation (0x%X). pipe=%u, size=0x%X, buffer=0x%08X", cmd_buff[3], pipe, size, buffer);
|
||||
ASSERT_MSG(Memory::GetPointer(buffer) != nullptr, "Invalid Buffer: pipe=%u, size=0x%X, buffer=0x%08X", pipe, size, buffer);
|
||||
|
||||
std::vector<u8> message(size);
|
||||
@ -285,7 +221,6 @@ static void WriteProcessPipe(Service::Interface* self) {
|
||||
|
||||
DSP::HLE::PipeWrite(pipe, message);
|
||||
|
||||
cmd_buff[0] = 0xD0040;
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
|
||||
LOG_DEBUG(Service_DSP, "pipe=%u, size=0x%X, buffer=0x%08X", pipe, size, buffer);
|
||||
@ -315,14 +250,16 @@ static void ReadPipeIfPossible(Service::Interface* self) {
|
||||
|
||||
ASSERT_MSG(Memory::GetPointer(addr) != nullptr, "Invalid addr: pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe, unknown, size, addr);
|
||||
|
||||
cmd_buff[0] = 0x100082;
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
if (DSP::HLE::GetPipeReadableSize(pipe) >= size) {
|
||||
std::vector<u8> response = DSP::HLE::PipeRead(pipe, size);
|
||||
|
||||
std::vector<u8> response = DSP::HLE::PipeRead(pipe, size);
|
||||
Memory::WriteBlock(addr, response.data(), response.size());
|
||||
|
||||
Memory::WriteBlock(addr, response.data(), response.size());
|
||||
|
||||
cmd_buff[2] = static_cast<u32>(response.size());
|
||||
cmd_buff[2] = static_cast<u32>(response.size());
|
||||
} else {
|
||||
cmd_buff[2] = 0; // Return no data
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_DSP, "pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X, return cmd_buff[2]=0x%08X", pipe, unknown, size, addr, cmd_buff[2]);
|
||||
}
|
||||
@ -396,7 +333,6 @@ static void SetSemaphoreMask(Service::Interface* self) {
|
||||
|
||||
u32 mask = cmd_buff[1];
|
||||
|
||||
cmd_buff[0] = 0x170040;
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
|
||||
LOG_WARNING(Service_DSP, "(STUBBED) called mask=0x%08X", mask);
|
||||
@ -414,11 +350,10 @@ static void SetSemaphoreMask(Service::Interface* self) {
|
||||
static void GetHeadphoneStatus(Service::Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
|
||||
cmd_buff[0] = 0x1F0080;
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
cmd_buff[2] = 0; // Not using headphones?
|
||||
|
||||
LOG_TRACE(Service_DSP, "called");
|
||||
LOG_WARNING(Service_DSP, "(STUBBED) called");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -441,7 +376,6 @@ static void RecvData(Service::Interface* self) {
|
||||
|
||||
// Application reads this after requesting DSP shutdown, to verify the DSP has indeed shutdown or slept.
|
||||
|
||||
cmd_buff[0] = 0x10080;
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||
switch (DSP::HLE::GetDspState()) {
|
||||
case DSP::HLE::DspState::On:
|
||||
@ -477,7 +411,6 @@ static void RecvDataIsReady(Service::Interface* self) {
|
||||
|
||||
ASSERT_MSG(register_number == 0, "Unknown register_number %u", register_number);
|
||||
|
||||
cmd_buff[0] = 0x20080;
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||
cmd_buff[2] = 1; // Ready to read
|
||||
|
||||
@ -532,8 +465,7 @@ Interface::Interface() {
|
||||
|
||||
Interface::~Interface() {
|
||||
semaphore_event = nullptr;
|
||||
for (auto& events : interrupt_events)
|
||||
events.clear();
|
||||
interrupt_events.clear();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -34,7 +34,4 @@ void SignalAllInterrupts();
|
||||
*/
|
||||
void SignalInterrupt(u32 interrupt_id, u32 channel_id);
|
||||
|
||||
/// Returns true it the application signalled the semaphore, then clears the semaphore.
|
||||
bool SemaphoreSignalled();
|
||||
|
||||
} // namespace
|
||||
|
@ -440,7 +440,8 @@ static void DebugHandler(GLenum source, GLenum type, GLuint id, GLenum severity,
|
||||
level = Log::Level::Debug;
|
||||
break;
|
||||
}
|
||||
//LOG_GENERIC(Log::Class::Render_OpenGL, level, "%s %s %d: %s", GetSource(source), GetType(type), id, message);
|
||||
LOG_GENERIC(Log::Class::Render_OpenGL, level, "%s %s %d: %s",
|
||||
GetSource(source), GetType(type), id, message);
|
||||
}
|
||||
|
||||
/// Initialize the renderer
|
||||
|
Loading…
Reference in New Issue
Block a user