Update SoundTouch to 2.3.2 commit 1eda9c0b01039f29d230a46cda9f2290bbd1f62b
This commit is contained in:
parent
7de01597c6
commit
4e3a366b2d
|
@ -12,13 +12,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2014-01-05 23:40:22 +0200 (Sun, 05 Jan 2014) $
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id: AAFilter.cpp 177 2014-01-05 21:40:22Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
@ -49,7 +42,7 @@
|
||||||
|
|
||||||
using namespace soundtouch;
|
using namespace soundtouch;
|
||||||
|
|
||||||
#define PI 3.141592655357989
|
#define PI 3.14159265358979323846
|
||||||
#define TWOPI (2 * PI)
|
#define TWOPI (2 * PI)
|
||||||
|
|
||||||
// define this to save AA filter coefficients to a file
|
// define this to save AA filter coefficients to a file
|
||||||
|
@ -61,7 +54,7 @@ using namespace soundtouch;
|
||||||
static void _DEBUG_SAVE_AAFIR_COEFFS(SAMPLETYPE *coeffs, int len)
|
static void _DEBUG_SAVE_AAFIR_COEFFS(SAMPLETYPE *coeffs, int len)
|
||||||
{
|
{
|
||||||
FILE *fptr = fopen("aa_filter_coeffs.txt", "wt");
|
FILE *fptr = fopen("aa_filter_coeffs.txt", "wt");
|
||||||
if (fptr == NULL) return;
|
if (fptr == nullptr) return;
|
||||||
|
|
||||||
for (int i = 0; i < len; i ++)
|
for (int i = 0; i < len; i ++)
|
||||||
{
|
{
|
||||||
|
@ -75,7 +68,6 @@ using namespace soundtouch;
|
||||||
#define _DEBUG_SAVE_AAFIR_COEFFS(x, y)
|
#define _DEBUG_SAVE_AAFIR_COEFFS(x, y)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
*
|
*
|
||||||
* Implementation of the class 'AAFilter'
|
* Implementation of the class 'AAFilter'
|
||||||
|
@ -90,14 +82,12 @@ AAFilter::AAFilter(uint len)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
AAFilter::~AAFilter()
|
AAFilter::~AAFilter()
|
||||||
{
|
{
|
||||||
delete pFIR;
|
delete pFIR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Sets new anti-alias filter cut-off edge frequency, scaled to
|
// Sets new anti-alias filter cut-off edge frequency, scaled to
|
||||||
// sampling frequency (nyquist frequency = 0.5).
|
// sampling frequency (nyquist frequency = 0.5).
|
||||||
// The filter will cut frequencies higher than the given frequency.
|
// The filter will cut frequencies higher than the given frequency.
|
||||||
|
@ -108,7 +98,6 @@ void AAFilter::setCutoffFreq(double newCutoffFreq)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Sets number of FIR filter taps
|
// Sets number of FIR filter taps
|
||||||
void AAFilter::setLength(uint newLength)
|
void AAFilter::setLength(uint newLength)
|
||||||
{
|
{
|
||||||
|
@ -117,7 +106,6 @@ void AAFilter::setLength(uint newLength)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Calculates coefficients for a low-pass FIR filter using Hamming window
|
// Calculates coefficients for a low-pass FIR filter using Hamming window
|
||||||
void AAFilter::calculateCoeffs()
|
void AAFilter::calculateCoeffs()
|
||||||
{
|
{
|
||||||
|
@ -177,12 +165,10 @@ void AAFilter::calculateCoeffs()
|
||||||
for (i = 0; i < length; i ++)
|
for (i = 0; i < length; i ++)
|
||||||
{
|
{
|
||||||
temp = work[i] * scaleCoeff;
|
temp = work[i] * scaleCoeff;
|
||||||
//#if SOUNDTOUCH_INTEGER_SAMPLES
|
|
||||||
// scale & round to nearest integer
|
// scale & round to nearest integer
|
||||||
temp += (temp >= 0) ? 0.5 : -0.5;
|
temp += (temp >= 0) ? 0.5 : -0.5;
|
||||||
// ensure no overfloods
|
// ensure no overfloods
|
||||||
assert(temp >= -32768 && temp <= 32767);
|
assert(temp >= -32768 && temp <= 32767);
|
||||||
//#endif
|
|
||||||
coeffs[i] = (SAMPLETYPE)temp;
|
coeffs[i] = (SAMPLETYPE)temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,13 +13,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2014-01-07 21:41:23 +0200 (Tue, 07 Jan 2014) $
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id: AAFilter.h 187 2014-01-07 19:41:23Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
|
|
@ -26,13 +26,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2015-02-21 23:24:29 +0200 (Sat, 21 Feb 2015) $
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id: BPMDetect.cpp 202 2015-02-21 21:24:29Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
@ -54,26 +47,45 @@
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#define _USE_MATH_DEFINES
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <cfloat>
|
||||||
#include "FIFOSampleBuffer.h"
|
#include "FIFOSampleBuffer.h"
|
||||||
#include "PeakFinder.h"
|
#include "PeakFinder.h"
|
||||||
#include "BPMDetect.h"
|
#include "BPMDetect.h"
|
||||||
|
|
||||||
using namespace soundtouch;
|
using namespace soundtouch;
|
||||||
|
|
||||||
#define INPUT_BLOCK_SAMPLES 2048
|
// algorithm input sample block size
|
||||||
#define DECIMATED_BLOCK_SAMPLES 256
|
static const int INPUT_BLOCK_SIZE = 2048;
|
||||||
|
|
||||||
/// decay constant for calculating RMS volume sliding average approximation
|
// decimated sample block size
|
||||||
/// (time constant is about 10 sec)
|
static const int DECIMATED_BLOCK_SIZE = 256;
|
||||||
const float avgdecay = 0.99986f;
|
|
||||||
|
|
||||||
/// Normalization coefficient for calculating RMS sliding average approximation.
|
/// Target sample rate after decimation
|
||||||
const float avgnorm = (1 - avgdecay);
|
static const int TARGET_SRATE = 1000;
|
||||||
|
|
||||||
|
/// XCorr update sequence size, update in about 200msec chunks
|
||||||
|
static const int XCORR_UPDATE_SEQUENCE = (int)(TARGET_SRATE / 5);
|
||||||
|
|
||||||
|
/// Moving average N size
|
||||||
|
static const int MOVING_AVERAGE_N = 15;
|
||||||
|
|
||||||
|
/// XCorr decay time constant, decay to half in 30 seconds
|
||||||
|
/// If it's desired to have the system adapt quicker to beat rate
|
||||||
|
/// changes within a continuing music stream, then the
|
||||||
|
/// 'xcorr_decay_time_constant' value can be reduced, yet that
|
||||||
|
/// can increase possibility of glitches in bpm detection.
|
||||||
|
static const double XCORR_DECAY_TIME_CONSTANT = 30.0;
|
||||||
|
|
||||||
|
/// Data overlap factor for beat detection algorithm
|
||||||
|
static const int OVERLAP_FACTOR = 4;
|
||||||
|
|
||||||
|
static const double TWOPI = (2 * M_PI);
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@ -83,16 +95,14 @@ const float avgnorm = (1 - avgdecay);
|
||||||
|
|
||||||
#ifdef _CREATE_BPM_DEBUG_FILE
|
#ifdef _CREATE_BPM_DEBUG_FILE
|
||||||
|
|
||||||
#define DEBUGFILE_NAME "c:\\temp\\soundtouch-bpm-debug.txt"
|
static void _SaveDebugData(const char *name, const float *data, int minpos, int maxpos, double coeff)
|
||||||
|
|
||||||
static void _SaveDebugData(const float *data, int minpos, int maxpos, double coeff)
|
|
||||||
{
|
{
|
||||||
FILE *fptr = fopen(DEBUGFILE_NAME, "wt");
|
FILE *fptr = fopen(name, "wt");
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (fptr)
|
if (fptr)
|
||||||
{
|
{
|
||||||
printf("\n\nWriting BPM debug data into file " DEBUGFILE_NAME "\n\n");
|
printf("\nWriting BPM debug data into file %s\n", name);
|
||||||
for (i = minpos; i < maxpos; i ++)
|
for (i = minpos; i < maxpos; i ++)
|
||||||
{
|
{
|
||||||
fprintf(fptr, "%d\t%.1lf\t%f\n", i, coeff / (double)i, data[i]);
|
fprintf(fptr, "%d\t%.1lf\t%f\n", i, coeff / (double)i, data[i]);
|
||||||
|
@ -100,42 +110,90 @@ const float avgnorm = (1 - avgdecay);
|
||||||
fclose(fptr);
|
fclose(fptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _SaveDebugBeatPos(const char *name, const std::vector<BEAT> &beats)
|
||||||
|
{
|
||||||
|
printf("\nWriting beat detections data into file %s\n", name);
|
||||||
|
|
||||||
|
FILE *fptr = fopen(name, "wt");
|
||||||
|
if (fptr)
|
||||||
|
{
|
||||||
|
for (uint i = 0; i < beats.size(); i++)
|
||||||
|
{
|
||||||
|
BEAT b = beats[i];
|
||||||
|
fprintf(fptr, "%lf\t%lf\n", b.pos, b.strength);
|
||||||
|
}
|
||||||
|
fclose(fptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
#define _SaveDebugData(a,b,c,d)
|
#define _SaveDebugData(name, a,b,c,d)
|
||||||
|
#define _SaveDebugBeatPos(name, b)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Hamming window
|
||||||
|
void hamming(float *w, int N)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < N; i++)
|
||||||
|
{
|
||||||
|
w[i] = (float)(0.54 - 0.46 * cos(TWOPI * i / (N - 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// IIR2_filter - 2nd order IIR filter
|
||||||
|
|
||||||
|
IIR2_filter::IIR2_filter(const double *lpf_coeffs)
|
||||||
|
{
|
||||||
|
memcpy(coeffs, lpf_coeffs, 5 * sizeof(double));
|
||||||
|
memset(prev, 0, sizeof(prev));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
float IIR2_filter::update(float x)
|
||||||
|
{
|
||||||
|
prev[0] = x;
|
||||||
|
double y = x * coeffs[0];
|
||||||
|
|
||||||
|
for (int i = 4; i >= 1; i--)
|
||||||
|
{
|
||||||
|
y += coeffs[i] * prev[i];
|
||||||
|
prev[i] = prev[i - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
prev[3] = y;
|
||||||
|
return (float)y;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// IIR low-pass filter coefficients, calculated with matlab/octave cheby2(2,40,0.05)
|
||||||
|
const double _LPF_coeffs[5] = { 0.00996655391939, -0.01944529148401, 0.00996655391939, 1.96867605796247, -0.96916387431724 };
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
BPMDetect::BPMDetect(int numChannels, int aSampleRate) :
|
||||||
BPMDetect::BPMDetect(int numChannels, int aSampleRate)
|
beat_lpf(_LPF_coeffs)
|
||||||
{
|
{
|
||||||
|
beats.reserve(250); // initial reservation to prevent frequent reallocation
|
||||||
|
|
||||||
this->sampleRate = aSampleRate;
|
this->sampleRate = aSampleRate;
|
||||||
this->channels = numChannels;
|
this->channels = numChannels;
|
||||||
|
|
||||||
decimateSum = 0;
|
decimateSum = 0;
|
||||||
decimateCount = 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
|
// choose decimation factor so that result is approx. 1000 Hz
|
||||||
decimateBy = sampleRate / 1000;
|
decimateBy = sampleRate / TARGET_SRATE;
|
||||||
assert(decimateBy > 0);
|
if ((decimateBy <= 0) || (decimateBy * DECIMATED_BLOCK_SIZE < INPUT_BLOCK_SIZE))
|
||||||
assert(INPUT_BLOCK_SAMPLES < decimateBy * DECIMATED_BLOCK_SAMPLES);
|
{
|
||||||
|
ST_THROW_RT_ERROR("Too small samplerate");
|
||||||
|
}
|
||||||
|
|
||||||
// Calculate window length & starting item according to desired min & max bpms
|
// Calculate window length & starting item according to desired min & max bpms
|
||||||
windowLen = (60 * sampleRate) / (decimateBy * MIN_BPM);
|
windowLen = (60 * sampleRate) / (decimateBy * MIN_BPM);
|
||||||
windowStart = (60 * sampleRate) / (decimateBy * MAX_BPM);
|
windowStart = (60 * sampleRate) / (decimateBy * MAX_BPM_RANGE);
|
||||||
|
|
||||||
assert(windowLen > windowStart);
|
assert(windowLen > windowStart);
|
||||||
|
|
||||||
|
@ -143,23 +201,38 @@ BPMDetect::BPMDetect(int numChannels, int aSampleRate)
|
||||||
xcorr = new float[windowLen];
|
xcorr = new float[windowLen];
|
||||||
memset(xcorr, 0, windowLen * sizeof(float));
|
memset(xcorr, 0, windowLen * sizeof(float));
|
||||||
|
|
||||||
|
pos = 0;
|
||||||
|
peakPos = 0;
|
||||||
|
peakVal = 0;
|
||||||
|
init_scaler = 1;
|
||||||
|
beatcorr_ringbuffpos = 0;
|
||||||
|
beatcorr_ringbuff = new float[windowLen];
|
||||||
|
memset(beatcorr_ringbuff, 0, windowLen * sizeof(float));
|
||||||
|
|
||||||
// allocate processing buffer
|
// allocate processing buffer
|
||||||
buffer = new FIFOSampleBuffer();
|
buffer = new FIFOSampleBuffer();
|
||||||
// we do processing in mono mode
|
// we do processing in mono mode
|
||||||
buffer->setChannels(1);
|
buffer->setChannels(1);
|
||||||
buffer->clear();
|
buffer->clear();
|
||||||
}
|
|
||||||
|
|
||||||
|
// calculate hamming windows
|
||||||
|
hamw = new float[XCORR_UPDATE_SEQUENCE];
|
||||||
|
hamming(hamw, XCORR_UPDATE_SEQUENCE);
|
||||||
|
hamw2 = new float[XCORR_UPDATE_SEQUENCE / 2];
|
||||||
|
hamming(hamw2, XCORR_UPDATE_SEQUENCE / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
BPMDetect::~BPMDetect()
|
BPMDetect::~BPMDetect()
|
||||||
{
|
{
|
||||||
delete[] xcorr;
|
delete[] xcorr;
|
||||||
|
delete[] beatcorr_ringbuff;
|
||||||
|
delete[] hamw;
|
||||||
|
delete[] hamw2;
|
||||||
delete buffer;
|
delete buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// convert to mono, low-pass filter & decimate to about 500 Hz.
|
/// convert to mono, low-pass filter & decimate to about 500 Hz.
|
||||||
/// return number of outputted samples.
|
/// return number of outputted samples.
|
||||||
///
|
///
|
||||||
|
@ -216,7 +289,6 @@ int BPMDetect::decimate(SAMPLETYPE *dest, const SAMPLETYPE *src, int numsamples)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Calculates autocorrelation function of the sample history buffer
|
// Calculates autocorrelation function of the sample history buffer
|
||||||
void BPMDetect::updateXCorr(int process_samples)
|
void BPMDetect::updateXCorr(int process_samples)
|
||||||
{
|
{
|
||||||
|
@ -224,72 +296,122 @@ void BPMDetect::updateXCorr(int process_samples)
|
||||||
SAMPLETYPE *pBuffer;
|
SAMPLETYPE *pBuffer;
|
||||||
|
|
||||||
assert(buffer->numSamples() >= (uint)(process_samples + windowLen));
|
assert(buffer->numSamples() >= (uint)(process_samples + windowLen));
|
||||||
|
assert(process_samples == XCORR_UPDATE_SEQUENCE);
|
||||||
|
|
||||||
pBuffer = buffer->ptrBegin();
|
pBuffer = buffer->ptrBegin();
|
||||||
|
|
||||||
|
// calculate decay factor for xcorr filtering
|
||||||
|
float xcorr_decay = (float)pow(0.5, 1.0 / (XCORR_DECAY_TIME_CONSTANT * TARGET_SRATE / process_samples));
|
||||||
|
|
||||||
|
// prescale pbuffer
|
||||||
|
float tmp[XCORR_UPDATE_SEQUENCE];
|
||||||
|
for (int i = 0; i < process_samples; i++)
|
||||||
|
{
|
||||||
|
tmp[i] = hamw[i] * hamw[i] * pBuffer[i];
|
||||||
|
}
|
||||||
|
|
||||||
#pragma omp parallel for
|
#pragma omp parallel for
|
||||||
for (offs = windowStart; offs < windowLen; offs ++)
|
for (offs = windowStart; offs < windowLen; offs ++)
|
||||||
{
|
{
|
||||||
LONG_SAMPLETYPE sum;
|
float sum;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
sum = 0;
|
sum = 0;
|
||||||
for (i = 0; i < process_samples; i ++)
|
for (i = 0; i < process_samples; i ++)
|
||||||
{
|
{
|
||||||
sum += pBuffer[i] * pBuffer[i + offs]; // scaling the sub-result shouldn't be necessary
|
sum += tmp[i] * pBuffer[i + offs]; // scaling the sub-result shouldn't be necessary
|
||||||
}
|
}
|
||||||
// xcorr[offs] *= xcorr_decay; // decay 'xcorr' here with suitable coefficients
|
xcorr[offs] *= xcorr_decay; // decay 'xcorr' here with suitable time constant.
|
||||||
// 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;
|
xcorr[offs] += (float)fabs(sum);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Calculates envelope of the sample data
|
// Detect individual beat positions
|
||||||
void BPMDetect::calcEnvelope(SAMPLETYPE *samples, int numsamples)
|
void BPMDetect::updateBeatPos(int process_samples)
|
||||||
{
|
{
|
||||||
const static double decay = 0.7f; // decay constant for smoothing the envelope
|
SAMPLETYPE *pBuffer;
|
||||||
const static double norm = (1 - decay);
|
|
||||||
|
|
||||||
int i;
|
assert(buffer->numSamples() >= (uint)(process_samples + windowLen));
|
||||||
LONG_SAMPLETYPE out;
|
|
||||||
double val;
|
|
||||||
|
|
||||||
for (i = 0; i < numsamples; i ++)
|
pBuffer = buffer->ptrBegin();
|
||||||
|
assert(process_samples == XCORR_UPDATE_SEQUENCE / 2);
|
||||||
|
|
||||||
|
// static double thr = 0.0003;
|
||||||
|
double posScale = (double)this->decimateBy / (double)this->sampleRate;
|
||||||
|
int resetDur = (int)(0.12 / posScale + 0.5);
|
||||||
|
|
||||||
|
// prescale pbuffer
|
||||||
|
float tmp[XCORR_UPDATE_SEQUENCE / 2];
|
||||||
|
for (int i = 0; i < process_samples; i++)
|
||||||
{
|
{
|
||||||
// calc average RMS volume
|
tmp[i] = hamw2[i] * hamw2[i] * pBuffer[i];
|
||||||
RMSVolumeAccu *= avgdecay;
|
}
|
||||||
val = (float)fabs((float)samples[i]);
|
|
||||||
RMSVolumeAccu += val * val;
|
|
||||||
|
|
||||||
// cut amplitudes that are below cutoff ~2 times RMS volume
|
#pragma omp parallel for
|
||||||
// (we're interested in peak values, not the silent moments)
|
for (int offs = windowStart; offs < windowLen; offs++)
|
||||||
if (val < 0.5 * sqrt(RMSVolumeAccu * avgnorm))
|
|
||||||
{
|
{
|
||||||
val = 0;
|
float sum = 0;
|
||||||
|
for (int i = 0; i < process_samples; i++)
|
||||||
|
{
|
||||||
|
sum += tmp[i] * pBuffer[offs + i];
|
||||||
|
}
|
||||||
|
beatcorr_ringbuff[(beatcorr_ringbuffpos + offs) % windowLen] += (float)((sum > 0) ? sum : 0); // accumulate only positive correlations
|
||||||
}
|
}
|
||||||
|
|
||||||
// smooth amplitude envelope
|
int skipstep = XCORR_UPDATE_SEQUENCE / OVERLAP_FACTOR;
|
||||||
envelopeAccu *= decay;
|
|
||||||
envelopeAccu += val;
|
|
||||||
out = (LONG_SAMPLETYPE)(envelopeAccu * norm);
|
|
||||||
|
|
||||||
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
// compensate empty buffer at beginning by scaling coefficient
|
||||||
// cut peaks (shouldn't be necessary though)
|
float scale = (float)windowLen / (float)(skipstep * init_scaler);
|
||||||
if (out > 32767) out = 32767;
|
if (scale > 1.0f)
|
||||||
#endif // SOUNDTOUCH_INTEGER_SAMPLES
|
{
|
||||||
samples[i] = (SAMPLETYPE)out;
|
init_scaler++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
scale = 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// detect beats
|
||||||
|
for (int i = 0; i < skipstep; i++)
|
||||||
|
{
|
||||||
|
float sum = beatcorr_ringbuff[beatcorr_ringbuffpos];
|
||||||
|
sum -= beat_lpf.update(sum);
|
||||||
|
|
||||||
|
if (sum > peakVal)
|
||||||
|
{
|
||||||
|
// found new local largest value
|
||||||
|
peakVal = sum;
|
||||||
|
peakPos = pos;
|
||||||
|
}
|
||||||
|
if (pos > peakPos + resetDur)
|
||||||
|
{
|
||||||
|
// largest value not updated for 200msec => accept as beat
|
||||||
|
peakPos += skipstep;
|
||||||
|
if (peakVal > 0)
|
||||||
|
{
|
||||||
|
// add detected beat to end of "beats" vector
|
||||||
|
BEAT temp = { (float)(peakPos * posScale), (float)(peakVal * scale) };
|
||||||
|
beats.push_back(temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
peakVal = 0;
|
||||||
|
peakPos = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
beatcorr_ringbuff[beatcorr_ringbuffpos] = 0;
|
||||||
|
pos++;
|
||||||
|
beatcorr_ringbuffpos = (beatcorr_ringbuffpos + 1) % windowLen;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define max(x,y) ((x) > (y) ? (x) : (y))
|
||||||
|
|
||||||
void BPMDetect::inputSamples(const SAMPLETYPE *samples, int numSamples)
|
void BPMDetect::inputSamples(const SAMPLETYPE *samples, int numSamples)
|
||||||
{
|
{
|
||||||
SAMPLETYPE decimated[DECIMATED_BLOCK_SAMPLES];
|
SAMPLETYPE decimated[DECIMATED_BLOCK_SIZE];
|
||||||
|
|
||||||
// iterate so that max INPUT_BLOCK_SAMPLES processed per iteration
|
// iterate so that max INPUT_BLOCK_SAMPLES processed per iteration
|
||||||
while (numSamples > 0)
|
while (numSamples > 0)
|
||||||
|
@ -297,48 +419,70 @@ void BPMDetect::inputSamples(const SAMPLETYPE *samples, int numSamples)
|
||||||
int block;
|
int block;
|
||||||
int decSamples;
|
int decSamples;
|
||||||
|
|
||||||
block = (numSamples > INPUT_BLOCK_SAMPLES) ? INPUT_BLOCK_SAMPLES : numSamples;
|
block = (numSamples > INPUT_BLOCK_SIZE) ? INPUT_BLOCK_SIZE : numSamples;
|
||||||
|
|
||||||
// decimate. note that converts to mono at the same time
|
// decimate. note that converts to mono at the same time
|
||||||
decSamples = decimate(decimated, samples, block);
|
decSamples = decimate(decimated, samples, block);
|
||||||
samples += block * channels;
|
samples += block * channels;
|
||||||
numSamples -= block;
|
numSamples -= block;
|
||||||
|
|
||||||
// envelope new samples and add them to buffer
|
|
||||||
calcEnvelope(decimated, decSamples);
|
|
||||||
buffer->putSamples(decimated, decSamples);
|
buffer->putSamples(decimated, decSamples);
|
||||||
}
|
}
|
||||||
|
|
||||||
// when the buffer has enought samples for processing...
|
// when the buffer has enough samples for processing...
|
||||||
if ((int)buffer->numSamples() > windowLen)
|
int req = max(windowLen + XCORR_UPDATE_SEQUENCE, 2 * XCORR_UPDATE_SEQUENCE);
|
||||||
|
while ((int)buffer->numSamples() >= req)
|
||||||
{
|
{
|
||||||
int processLength;
|
// ... update autocorrelations...
|
||||||
|
updateXCorr(XCORR_UPDATE_SEQUENCE);
|
||||||
// how many samples are processed
|
// ...update beat position calculation...
|
||||||
processLength = (int)buffer->numSamples() - windowLen;
|
updateBeatPos(XCORR_UPDATE_SEQUENCE / 2);
|
||||||
|
// ... and remove proceessed samples from the buffer
|
||||||
// ... calculate autocorrelations for oldest samples...
|
int n = XCORR_UPDATE_SEQUENCE / OVERLAP_FACTOR;
|
||||||
updateXCorr(processLength);
|
buffer->receiveSamples(n);
|
||||||
// ... and remove them from the buffer
|
|
||||||
buffer->receiveSamples(processLength);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void BPMDetect::removeBias()
|
void BPMDetect::removeBias()
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
float minval = 1e12f; // arbitrary large number
|
|
||||||
|
|
||||||
|
// Remove linear bias: calculate linear regression coefficient
|
||||||
|
// 1. calc mean of 'xcorr' and 'i'
|
||||||
|
double mean_i = 0;
|
||||||
|
double mean_x = 0;
|
||||||
for (i = windowStart; i < windowLen; i++)
|
for (i = windowStart; i < windowLen; i++)
|
||||||
{
|
{
|
||||||
|
mean_x += xcorr[i];
|
||||||
|
}
|
||||||
|
mean_x /= (windowLen - windowStart);
|
||||||
|
mean_i = 0.5 * (windowLen - 1 + windowStart);
|
||||||
|
|
||||||
|
// 2. calculate linear regression coefficient
|
||||||
|
double b = 0;
|
||||||
|
double div = 0;
|
||||||
|
for (i = windowStart; i < windowLen; i++)
|
||||||
|
{
|
||||||
|
double xt = xcorr[i] - mean_x;
|
||||||
|
double xi = i - mean_i;
|
||||||
|
b += xt * xi;
|
||||||
|
div += xi * xi;
|
||||||
|
}
|
||||||
|
b /= div;
|
||||||
|
|
||||||
|
// subtract linear regression and resolve min. value bias
|
||||||
|
float minval = FLT_MAX; // arbitrary large number
|
||||||
|
for (i = windowStart; i < windowLen; i ++)
|
||||||
|
{
|
||||||
|
xcorr[i] -= (float)(b * i);
|
||||||
if (xcorr[i] < minval)
|
if (xcorr[i] < minval)
|
||||||
{
|
{
|
||||||
minval = xcorr[i];
|
minval = xcorr[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// subtract min.value
|
||||||
for (i = windowStart; i < windowLen; i ++)
|
for (i = windowStart; i < windowLen; i ++)
|
||||||
{
|
{
|
||||||
xcorr[i] -= minval;
|
xcorr[i] -= minval;
|
||||||
|
@ -346,26 +490,82 @@ void BPMDetect::removeBias()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Calculate N-point moving average for "source" values
|
||||||
|
void MAFilter(float *dest, const float *source, int start, int end, int N)
|
||||||
|
{
|
||||||
|
for (int i = start; i < end; i++)
|
||||||
|
{
|
||||||
|
int i1 = i - N / 2;
|
||||||
|
int i2 = i + N / 2 + 1;
|
||||||
|
if (i1 < start) i1 = start;
|
||||||
|
if (i2 > end) i2 = end;
|
||||||
|
|
||||||
|
double sum = 0;
|
||||||
|
for (int j = i1; j < i2; j ++)
|
||||||
|
{
|
||||||
|
sum += source[j];
|
||||||
|
}
|
||||||
|
dest[i] = (float)(sum / (i2 - i1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
float BPMDetect::getBpm()
|
float BPMDetect::getBpm()
|
||||||
{
|
{
|
||||||
double peakPos;
|
double peakPos;
|
||||||
double coeff;
|
double coeff;
|
||||||
PeakFinder peakFinder;
|
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
|
// remove bias from xcorr data
|
||||||
removeBias();
|
removeBias();
|
||||||
|
|
||||||
|
coeff = 60.0 * ((double)sampleRate / (double)decimateBy);
|
||||||
|
|
||||||
|
// save bpm debug data if debug data writing enabled
|
||||||
|
_SaveDebugData("soundtouch-bpm-xcorr.txt", xcorr, windowStart, windowLen, coeff);
|
||||||
|
|
||||||
|
// Smoothen by N-point moving-average
|
||||||
|
float *data = new float[windowLen];
|
||||||
|
memset(data, 0, sizeof(float) * windowLen);
|
||||||
|
MAFilter(data, xcorr, windowStart, windowLen, MOVING_AVERAGE_N);
|
||||||
|
|
||||||
// find peak position
|
// find peak position
|
||||||
peakPos = peakFinder.detectPeak(xcorr, windowStart, windowLen);
|
peakPos = peakFinder.detectPeak(data, windowStart, windowLen);
|
||||||
|
|
||||||
|
// save bpm debug data if debug data writing enabled
|
||||||
|
_SaveDebugData("soundtouch-bpm-smoothed.txt", data, windowStart, windowLen, coeff);
|
||||||
|
|
||||||
|
delete[] data;
|
||||||
|
|
||||||
assert(decimateBy != 0);
|
assert(decimateBy != 0);
|
||||||
if (peakPos < 1e-9) return 0.0; // detection failed.
|
if (peakPos < 1e-9) return 0.0; // detection failed.
|
||||||
|
|
||||||
|
_SaveDebugBeatPos("soundtouch-detected-beats.txt", beats);
|
||||||
|
|
||||||
// calculate BPM
|
// calculate BPM
|
||||||
return (float) (coeff / peakPos);
|
float bpm = (float)(coeff / peakPos);
|
||||||
|
return (bpm >= MIN_BPM && bpm <= MAX_BPM_VALID) ? bpm : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Get beat position arrays. Note: The array includes also really low beat detection values
|
||||||
|
/// in absence of clear strong beats. Consumer may wish to filter low values away.
|
||||||
|
/// - "pos" receive array of beat positions
|
||||||
|
/// - "values" receive array of beat detection strengths
|
||||||
|
/// - max_num indicates max.size of "pos" and "values" array.
|
||||||
|
///
|
||||||
|
/// You can query a suitable array sized by calling this with nullptr in "pos" & "values".
|
||||||
|
///
|
||||||
|
/// \return number of beats in the arrays.
|
||||||
|
int BPMDetect::getBeats(float *pos, float *values, int max_num)
|
||||||
|
{
|
||||||
|
int num = (int)beats.size();
|
||||||
|
if ((!pos) || (!values)) return num; // pos or values nullptr, return just size
|
||||||
|
|
||||||
|
for (int i = 0; (i < num) && (i < max_num); i++)
|
||||||
|
{
|
||||||
|
pos[i] = beats[i].pos;
|
||||||
|
values[i] = beats[i].strength;
|
||||||
|
}
|
||||||
|
return num;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,13 +26,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2012-08-30 22:53:44 +0300 (Thu, 30 Aug 2012) $
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id: BPMDetect.h 150 2012-08-30 19:53:44Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
@ -57,6 +50,7 @@
|
||||||
#ifndef _BPMDetect_H_
|
#ifndef _BPMDetect_H_
|
||||||
#define _BPMDetect_H_
|
#define _BPMDetect_H_
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
#include "STTypes.h"
|
#include "STTypes.h"
|
||||||
#include "FIFOSampleBuffer.h"
|
#include "FIFOSampleBuffer.h"
|
||||||
|
|
||||||
|
@ -64,10 +58,32 @@ namespace soundtouch
|
||||||
{
|
{
|
||||||
|
|
||||||
/// Minimum allowed BPM rate. Used to restrict accepted result above a reasonable limit.
|
/// Minimum allowed BPM rate. Used to restrict accepted result above a reasonable limit.
|
||||||
#define MIN_BPM 29
|
#define MIN_BPM 45
|
||||||
|
|
||||||
/// Maximum allowed BPM rate. Used to restrict accepted result below a reasonable limit.
|
/// Maximum allowed BPM rate range. Used for calculating algorithm parametrs
|
||||||
#define MAX_BPM 200
|
#define MAX_BPM_RANGE 200
|
||||||
|
|
||||||
|
/// Maximum allowed BPM rate range. Used to restrict accepted result below a reasonable limit.
|
||||||
|
#define MAX_BPM_VALID 190
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
float pos;
|
||||||
|
float strength;
|
||||||
|
} BEAT;
|
||||||
|
|
||||||
|
|
||||||
|
class IIR2_filter
|
||||||
|
{
|
||||||
|
double coeffs[5];
|
||||||
|
double prev[5];
|
||||||
|
|
||||||
|
public:
|
||||||
|
IIR2_filter(const double *lpf_coeffs);
|
||||||
|
float update(float x);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/// Class for calculating BPM rate for audio data.
|
/// Class for calculating BPM rate for audio data.
|
||||||
|
@ -77,12 +93,6 @@ protected:
|
||||||
/// Auto-correlation accumulator bins.
|
/// Auto-correlation accumulator bins.
|
||||||
float *xcorr;
|
float *xcorr;
|
||||||
|
|
||||||
/// Amplitude envelope sliding average approximation level accumulator
|
|
||||||
double envelopeAccu;
|
|
||||||
|
|
||||||
/// RMS volume sliding average approximation level accumulator
|
|
||||||
double RMSVolumeAccu;
|
|
||||||
|
|
||||||
/// Sample average counter.
|
/// Sample average counter.
|
||||||
int decimateCount;
|
int decimateCount;
|
||||||
|
|
||||||
|
@ -105,9 +115,28 @@ protected:
|
||||||
/// the first these many correlation bins.
|
/// the first these many correlation bins.
|
||||||
int windowStart;
|
int windowStart;
|
||||||
|
|
||||||
|
/// window functions for data preconditioning
|
||||||
|
float *hamw;
|
||||||
|
float *hamw2;
|
||||||
|
|
||||||
|
// beat detection variables
|
||||||
|
int pos;
|
||||||
|
int peakPos;
|
||||||
|
int beatcorr_ringbuffpos;
|
||||||
|
int init_scaler;
|
||||||
|
float peakVal;
|
||||||
|
float *beatcorr_ringbuff;
|
||||||
|
|
||||||
/// FIFO-buffer for decimated processing samples.
|
/// FIFO-buffer for decimated processing samples.
|
||||||
soundtouch::FIFOSampleBuffer *buffer;
|
soundtouch::FIFOSampleBuffer *buffer;
|
||||||
|
|
||||||
|
/// Collection of detected beat positions
|
||||||
|
//BeatCollection beats;
|
||||||
|
std::vector<BEAT> beats;
|
||||||
|
|
||||||
|
// 2nd order low-pass-filter
|
||||||
|
IIR2_filter beat_lpf;
|
||||||
|
|
||||||
/// Updates auto-correlation function for given number of decimated samples that
|
/// 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
|
/// are read from the internal 'buffer' pipe (samples aren't removed from the pipe
|
||||||
/// though).
|
/// though).
|
||||||
|
@ -131,6 +160,10 @@ protected:
|
||||||
/// remove constant bias from xcorr data
|
/// remove constant bias from xcorr data
|
||||||
void removeBias();
|
void removeBias();
|
||||||
|
|
||||||
|
// Detect individual beat positions
|
||||||
|
void updateBeatPos(int process_samples);
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// Constructor.
|
/// Constructor.
|
||||||
BPMDetect(int numChannels, ///< Number of channels in sample data.
|
BPMDetect(int numChannels, ///< Number of channels in sample data.
|
||||||
|
@ -150,15 +183,23 @@ public:
|
||||||
int numSamples ///< Number of samples in buffer
|
int numSamples ///< Number of samples in buffer
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
/// Analyzes the results and returns the BPM rate. Use this function to read result
|
/// 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
|
/// after whole song data has been input to the class by consecutive calls of
|
||||||
/// 'inputSamples' function.
|
/// 'inputSamples' function.
|
||||||
///
|
///
|
||||||
/// \return Beats-per-minute rate, or zero if detection failed.
|
/// \return Beats-per-minute rate, or zero if detection failed.
|
||||||
float getBpm();
|
float getBpm();
|
||||||
|
|
||||||
|
/// Get beat position arrays. Note: The array includes also really low beat detection values
|
||||||
|
/// in absence of clear strong beats. Consumer may wish to filter low values away.
|
||||||
|
/// - "pos" receive array of beat positions
|
||||||
|
/// - "values" receive array of beat detection strengths
|
||||||
|
/// - max_num indicates max.size of "pos" and "values" array.
|
||||||
|
///
|
||||||
|
/// You can query a suitable array sized by calling this with nullptr in "pos" & "values".
|
||||||
|
///
|
||||||
|
/// \return number of beats in the arrays.
|
||||||
|
int getBeats(float *pos, float *strength, int max_num);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // _BPMDetect_H_
|
#endif // _BPMDetect_H_
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
# OSX meeds to know
|
||||||
|
check_and_add_flag(CXX11 -std=c++11)
|
||||||
|
|
||||||
set(SRCS
|
set(SRCS
|
||||||
AAFilter.cpp
|
AAFilter.cpp
|
||||||
BPMDetect.cpp
|
BPMDetect.cpp
|
||||||
|
@ -17,4 +20,3 @@ set(SRCS
|
||||||
|
|
||||||
add_library(SoundTouch STATIC ${SRCS})
|
add_library(SoundTouch STATIC ${SRCS})
|
||||||
dolphin_disable_warnings_msvc(SoundTouch)
|
dolphin_disable_warnings_msvc(SoundTouch)
|
||||||
add_definitions(-w)
|
|
||||||
|
|
|
@ -15,13 +15,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2012-11-08 20:53:01 +0200 (Thu, 08 Nov 2012) $
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id: FIFOSampleBuffer.cpp 160 2012-11-08 18:53:01Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
@ -57,8 +50,8 @@ FIFOSampleBuffer::FIFOSampleBuffer(int numChannels)
|
||||||
{
|
{
|
||||||
assert(numChannels > 0);
|
assert(numChannels > 0);
|
||||||
sizeInBytes = 0; // reasonable initial value
|
sizeInBytes = 0; // reasonable initial value
|
||||||
buffer = NULL;
|
buffer = nullptr;
|
||||||
bufferUnaligned = NULL;
|
bufferUnaligned = nullptr;
|
||||||
samplesInBuffer = 0;
|
samplesInBuffer = 0;
|
||||||
bufferPos = 0;
|
bufferPos = 0;
|
||||||
channels = (uint)numChannels;
|
channels = (uint)numChannels;
|
||||||
|
@ -70,8 +63,8 @@ FIFOSampleBuffer::FIFOSampleBuffer(int numChannels)
|
||||||
FIFOSampleBuffer::~FIFOSampleBuffer()
|
FIFOSampleBuffer::~FIFOSampleBuffer()
|
||||||
{
|
{
|
||||||
delete[] bufferUnaligned;
|
delete[] bufferUnaligned;
|
||||||
bufferUnaligned = NULL;
|
bufferUnaligned = nullptr;
|
||||||
buffer = NULL;
|
buffer = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -80,7 +73,8 @@ void FIFOSampleBuffer::setChannels(int numChannels)
|
||||||
{
|
{
|
||||||
uint usedBytes;
|
uint usedBytes;
|
||||||
|
|
||||||
assert(numChannels > 0);
|
if (!verifyNumberOfChannels(numChannels)) return;
|
||||||
|
|
||||||
usedBytes = channels * samplesInBuffer;
|
usedBytes = channels * samplesInBuffer;
|
||||||
channels = (uint)numChannels;
|
channels = (uint)numChannels;
|
||||||
samplesInBuffer = usedBytes / channels;
|
samplesInBuffer = usedBytes / channels;
|
||||||
|
@ -131,7 +125,7 @@ void FIFOSampleBuffer::putSamples(uint nSamples)
|
||||||
//
|
//
|
||||||
// Parameter 'slackCapacity' tells the function how much free capacity (in
|
// Parameter 'slackCapacity' tells the function how much free capacity (in
|
||||||
// terms of samples) there _at least_ should be, in order to the caller to
|
// terms of samples) there _at least_ should be, in order to the caller to
|
||||||
// succesfully insert all the required samples to the buffer. When necessary,
|
// successfully insert all the required samples to the buffer. When necessary,
|
||||||
// the function grows the buffer size to comply with this requirement.
|
// the function grows the buffer size to comply with this requirement.
|
||||||
//
|
//
|
||||||
// When using this function as means for inserting new samples, also remember
|
// When using this function as means for inserting new samples, also remember
|
||||||
|
@ -158,7 +152,7 @@ SAMPLETYPE *FIFOSampleBuffer::ptrBegin()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Ensures that the buffer has enought capacity, i.e. space for _at least_
|
// Ensures that the buffer has enough capacity, i.e. space for _at least_
|
||||||
// 'capacityRequirement' number of samples. The buffer is grown in steps of
|
// 'capacityRequirement' number of samples. The buffer is grown in steps of
|
||||||
// 4 kilobytes to eliminate the need for frequently growing up the buffer,
|
// 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.
|
// as well as to round the buffer size up to the virtual memory page size.
|
||||||
|
@ -172,7 +166,7 @@ void FIFOSampleBuffer::ensureCapacity(uint capacityRequirement)
|
||||||
sizeInBytes = (capacityRequirement * channels * sizeof(SAMPLETYPE) + 4095) & (uint)-4096;
|
sizeInBytes = (capacityRequirement * channels * sizeof(SAMPLETYPE) + 4095) & (uint)-4096;
|
||||||
assert(sizeInBytes % 2 == 0);
|
assert(sizeInBytes % 2 == 0);
|
||||||
tempUnaligned = new SAMPLETYPE[sizeInBytes / sizeof(SAMPLETYPE) + 16 / sizeof(SAMPLETYPE)];
|
tempUnaligned = new SAMPLETYPE[sizeInBytes / sizeof(SAMPLETYPE) + 16 / sizeof(SAMPLETYPE)];
|
||||||
if (tempUnaligned == NULL)
|
if (tempUnaligned == nullptr)
|
||||||
{
|
{
|
||||||
ST_THROW_RT_ERROR("Couldn't allocate memory!\n");
|
ST_THROW_RT_ERROR("Couldn't allocate memory!\n");
|
||||||
}
|
}
|
||||||
|
@ -272,3 +266,10 @@ uint FIFOSampleBuffer::adjustAmountOfSamples(uint numSamples)
|
||||||
return samplesInBuffer;
|
return samplesInBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Add silence to end of buffer
|
||||||
|
void FIFOSampleBuffer::addSilent(uint nSamples)
|
||||||
|
{
|
||||||
|
memset(ptrEnd(nSamples), 0, sizeof(SAMPLETYPE) * nSamples * channels);
|
||||||
|
samplesInBuffer += nSamples;
|
||||||
|
}
|
||||||
|
|
|
@ -15,13 +15,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2014-01-05 23:40:22 +0200 (Sun, 05 Jan 2014) $
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id: FIFOSampleBuffer.h 177 2014-01-05 21:40:22Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
@ -98,7 +91,7 @@ public:
|
||||||
);
|
);
|
||||||
|
|
||||||
/// destructor
|
/// destructor
|
||||||
~FIFOSampleBuffer();
|
~FIFOSampleBuffer() override;
|
||||||
|
|
||||||
/// Returns a pointer to the beginning of the output samples.
|
/// Returns a pointer to the beginning of the output samples.
|
||||||
/// This function is provided for accessing the output samples directly.
|
/// This function is provided for accessing the output samples directly.
|
||||||
|
@ -107,7 +100,7 @@ public:
|
||||||
/// When using this function to output samples, also remember to 'remove' the
|
/// When using this function to output samples, also remember to 'remove' the
|
||||||
/// output samples from the buffer by calling the
|
/// output samples from the buffer by calling the
|
||||||
/// 'receiveSamples(numSamples)' function
|
/// 'receiveSamples(numSamples)' function
|
||||||
virtual SAMPLETYPE *ptrBegin();
|
virtual SAMPLETYPE *ptrBegin() override;
|
||||||
|
|
||||||
/// Returns a pointer to the end of the used part of the sample buffer (i.e.
|
/// 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
|
/// where the new samples are to be inserted). This function may be used for
|
||||||
|
@ -119,7 +112,7 @@ public:
|
||||||
/// 'putSamples(numSamples)' function.
|
/// 'putSamples(numSamples)' function.
|
||||||
SAMPLETYPE *ptrEnd(
|
SAMPLETYPE *ptrEnd(
|
||||||
uint slackCapacity ///< How much free capacity (in samples) there _at least_
|
uint slackCapacity ///< How much free capacity (in samples) there _at least_
|
||||||
///< should be so that the caller can succesfully insert the
|
///< should be so that the caller can successfully insert the
|
||||||
///< desired samples to the buffer. If necessary, the function
|
///< desired samples to the buffer. If necessary, the function
|
||||||
///< grows the buffer size to comply with this requirement.
|
///< grows the buffer size to comply with this requirement.
|
||||||
);
|
);
|
||||||
|
@ -128,7 +121,7 @@ public:
|
||||||
/// the sample buffer.
|
/// the sample buffer.
|
||||||
virtual void putSamples(const SAMPLETYPE *samples, ///< Pointer to samples.
|
virtual void putSamples(const SAMPLETYPE *samples, ///< Pointer to samples.
|
||||||
uint numSamples ///< Number of samples to insert.
|
uint numSamples ///< Number of samples to insert.
|
||||||
);
|
) override;
|
||||||
|
|
||||||
/// Adjusts the book-keeping to increase number of samples in the buffer without
|
/// Adjusts the book-keeping to increase number of samples in the buffer without
|
||||||
/// copying any actual samples.
|
/// copying any actual samples.
|
||||||
|
@ -146,7 +139,7 @@ public:
|
||||||
/// \return Number of samples returned.
|
/// \return Number of samples returned.
|
||||||
virtual uint receiveSamples(SAMPLETYPE *output, ///< Buffer where to copy output samples.
|
virtual uint receiveSamples(SAMPLETYPE *output, ///< Buffer where to copy output samples.
|
||||||
uint maxSamples ///< How many samples to receive at max.
|
uint maxSamples ///< How many samples to receive at max.
|
||||||
);
|
) override;
|
||||||
|
|
||||||
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
||||||
/// sample buffer without copying them anywhere.
|
/// sample buffer without copying them anywhere.
|
||||||
|
@ -154,10 +147,10 @@ public:
|
||||||
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
|
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
|
||||||
/// with 'ptrBegin' function.
|
/// with 'ptrBegin' function.
|
||||||
virtual uint receiveSamples(uint maxSamples ///< Remove this many samples from the beginning of pipe.
|
virtual uint receiveSamples(uint maxSamples ///< Remove this many samples from the beginning of pipe.
|
||||||
);
|
) override;
|
||||||
|
|
||||||
/// Returns number of samples currently available.
|
/// Returns number of samples currently available.
|
||||||
virtual uint numSamples() const;
|
virtual uint numSamples() const override;
|
||||||
|
|
||||||
/// Sets number of channels, 1 = mono, 2 = stereo.
|
/// Sets number of channels, 1 = mono, 2 = stereo.
|
||||||
void setChannels(int numChannels);
|
void setChannels(int numChannels);
|
||||||
|
@ -169,14 +162,17 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns nonzero if there aren't any samples available for outputting.
|
/// Returns nonzero if there aren't any samples available for outputting.
|
||||||
virtual int isEmpty() const;
|
virtual int isEmpty() const override;
|
||||||
|
|
||||||
/// Clears all the samples.
|
/// Clears all the samples.
|
||||||
virtual void clear();
|
virtual void clear() override;
|
||||||
|
|
||||||
/// allow trimming (downwards) amount of samples in pipeline.
|
/// allow trimming (downwards) amount of samples in pipeline.
|
||||||
/// Returns adjusted amount of samples
|
/// Returns adjusted amount of samples
|
||||||
uint adjustAmountOfSamples(uint numSamples);
|
uint adjustAmountOfSamples(uint numSamples) override;
|
||||||
|
|
||||||
|
/// Add silence to end of buffer
|
||||||
|
void addSilent(uint nSamples);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,13 +17,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2012-06-13 22:29:53 +0300 (Wed, 13 Jun 2012) $
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id: FIFOSamplePipe.h 143 2012-06-13 19:29:53Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
@ -58,6 +51,18 @@ namespace soundtouch
|
||||||
/// Abstract base class for FIFO (first-in-first-out) sample processing classes.
|
/// Abstract base class for FIFO (first-in-first-out) sample processing classes.
|
||||||
class FIFOSamplePipe
|
class FIFOSamplePipe
|
||||||
{
|
{
|
||||||
|
protected:
|
||||||
|
|
||||||
|
bool verifyNumberOfChannels(int nChannels) const
|
||||||
|
{
|
||||||
|
if ((nChannels > 0) && (nChannels <= SOUNDTOUCH_MAX_CHANNELS))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
ST_THROW_RT_ERROR("Error: Illegal number of channels");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// virtual default destructor
|
// virtual default destructor
|
||||||
virtual ~FIFOSamplePipe() {}
|
virtual ~FIFOSamplePipe() {}
|
||||||
|
@ -122,7 +127,6 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// Base-class for sound processing routines working in FIFO principle. With this base
|
/// 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,
|
/// 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
|
/// so that samples that are fed into beginning of the pipe automatically go through
|
||||||
|
@ -140,20 +144,18 @@ protected:
|
||||||
/// Sets output pipe.
|
/// Sets output pipe.
|
||||||
void setOutPipe(FIFOSamplePipe *pOutput)
|
void setOutPipe(FIFOSamplePipe *pOutput)
|
||||||
{
|
{
|
||||||
assert(output == NULL);
|
assert(output == nullptr);
|
||||||
assert(pOutput != NULL);
|
assert(pOutput != nullptr);
|
||||||
output = pOutput;
|
output = pOutput;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Constructor. Doesn't define output pipe; it has to be set be
|
/// Constructor. Doesn't define output pipe; it has to be set be
|
||||||
/// 'setOutPipe' function.
|
/// 'setOutPipe' function.
|
||||||
FIFOProcessor()
|
FIFOProcessor()
|
||||||
{
|
{
|
||||||
output = NULL;
|
output = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Constructor. Configures output pipe.
|
/// Constructor. Configures output pipe.
|
||||||
FIFOProcessor(FIFOSamplePipe *pOutput ///< Output pipe.
|
FIFOProcessor(FIFOSamplePipe *pOutput ///< Output pipe.
|
||||||
)
|
)
|
||||||
|
@ -161,13 +163,11 @@ protected:
|
||||||
output = pOutput;
|
output = pOutput;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Destructor.
|
/// Destructor.
|
||||||
virtual ~FIFOProcessor()
|
virtual ~FIFOProcessor() override
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Returns a pointer to the beginning of the output samples.
|
/// Returns a pointer to the beginning of the output samples.
|
||||||
/// This function is provided for accessing the output samples directly.
|
/// This function is provided for accessing the output samples directly.
|
||||||
/// Please be careful for not to corrupt the book-keeping!
|
/// Please be careful for not to corrupt the book-keeping!
|
||||||
|
@ -175,7 +175,7 @@ protected:
|
||||||
/// When using this function to output samples, also remember to 'remove' the
|
/// When using this function to output samples, also remember to 'remove' the
|
||||||
/// output samples from the buffer by calling the
|
/// output samples from the buffer by calling the
|
||||||
/// 'receiveSamples(numSamples)' function
|
/// 'receiveSamples(numSamples)' function
|
||||||
virtual SAMPLETYPE *ptrBegin()
|
virtual SAMPLETYPE *ptrBegin() override
|
||||||
{
|
{
|
||||||
return output->ptrBegin();
|
return output->ptrBegin();
|
||||||
}
|
}
|
||||||
|
@ -189,44 +189,40 @@ public:
|
||||||
/// \return Number of samples returned.
|
/// \return Number of samples returned.
|
||||||
virtual uint receiveSamples(SAMPLETYPE *outBuffer, ///< Buffer where to copy output samples.
|
virtual uint receiveSamples(SAMPLETYPE *outBuffer, ///< Buffer where to copy output samples.
|
||||||
uint maxSamples ///< How many samples to receive at max.
|
uint maxSamples ///< How many samples to receive at max.
|
||||||
)
|
) override
|
||||||
{
|
{
|
||||||
return output->receiveSamples(outBuffer, maxSamples);
|
return output->receiveSamples(outBuffer, maxSamples);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
||||||
/// sample buffer without copying them anywhere.
|
/// sample buffer without copying them anywhere.
|
||||||
///
|
///
|
||||||
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
|
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
|
||||||
/// with 'ptrBegin' function.
|
/// with 'ptrBegin' function.
|
||||||
virtual uint receiveSamples(uint maxSamples ///< Remove this many samples from the beginning of pipe.
|
virtual uint receiveSamples(uint maxSamples ///< Remove this many samples from the beginning of pipe.
|
||||||
)
|
) override
|
||||||
{
|
{
|
||||||
return output->receiveSamples(maxSamples);
|
return output->receiveSamples(maxSamples);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Returns number of samples currently available.
|
/// Returns number of samples currently available.
|
||||||
virtual uint numSamples() const
|
virtual uint numSamples() const override
|
||||||
{
|
{
|
||||||
return output->numSamples();
|
return output->numSamples();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Returns nonzero if there aren't any samples available for outputting.
|
/// Returns nonzero if there aren't any samples available for outputting.
|
||||||
virtual int isEmpty() const
|
virtual int isEmpty() const override
|
||||||
{
|
{
|
||||||
return output->isEmpty();
|
return output->isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// allow trimming (downwards) amount of samples in pipeline.
|
/// allow trimming (downwards) amount of samples in pipeline.
|
||||||
/// Returns adjusted amount of samples
|
/// Returns adjusted amount of samples
|
||||||
virtual uint adjustAmountOfSamples(uint numSamples)
|
virtual uint adjustAmountOfSamples(uint numSamples) override
|
||||||
{
|
{
|
||||||
return output->adjustAmountOfSamples(numSamples);
|
return output->adjustAmountOfSamples(numSamples);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,22 +2,21 @@
|
||||||
///
|
///
|
||||||
/// General FIR digital filter routines with MMX optimization.
|
/// General FIR digital filter routines with MMX optimization.
|
||||||
///
|
///
|
||||||
/// Note : MMX optimized functions reside in a separate, platform-specific file,
|
/// Notes : MMX optimized functions reside in a separate, platform-specific file,
|
||||||
/// e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'
|
/// e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'
|
||||||
///
|
///
|
||||||
|
/// This source file contains OpenMP optimizations that allow speeding up the
|
||||||
|
/// corss-correlation algorithm by executing it in several threads / CPU cores
|
||||||
|
/// in parallel. See the following article link for more detailed discussion
|
||||||
|
/// about SoundTouch OpenMP optimizations:
|
||||||
|
/// http://www.softwarecoven.com/parallel-computing-in-embedded-mobile-devices
|
||||||
|
///
|
||||||
/// Author : Copyright (c) Olli Parviainen
|
/// Author : Copyright (c) Olli Parviainen
|
||||||
/// Author e-mail : oparviai 'at' iki.fi
|
/// Author e-mail : oparviai 'at' iki.fi
|
||||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
/// 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 :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
@ -60,53 +59,43 @@ FIRFilter::FIRFilter()
|
||||||
resultDivider = 0;
|
resultDivider = 0;
|
||||||
length = 0;
|
length = 0;
|
||||||
lengthDiv8 = 0;
|
lengthDiv8 = 0;
|
||||||
filterCoeffs = NULL;
|
filterCoeffs = nullptr;
|
||||||
|
filterCoeffsStereo = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
FIRFilter::~FIRFilter()
|
FIRFilter::~FIRFilter()
|
||||||
{
|
{
|
||||||
delete[] filterCoeffs;
|
delete[] filterCoeffs;
|
||||||
|
delete[] filterCoeffsStereo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Usual C-version of the filter routine for stereo sound
|
// Usual C-version of the filter routine for stereo sound
|
||||||
uint FIRFilter::evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const
|
uint FIRFilter::evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const
|
||||||
{
|
{
|
||||||
int j, end;
|
int j, end;
|
||||||
#ifdef SOUNDTOUCH_FLOAT_SAMPLES
|
// hint compiler autovectorization that loop length is divisible by 8
|
||||||
// when using floating point samples, use a scaler instead of a divider
|
uint ilength = length & -8;
|
||||||
// because division is much slower operation than multiplying.
|
|
||||||
double dScaler = 1.0 / (double)resultDivider;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
assert(length != 0);
|
assert((length != 0) && (length == ilength) && (src != nullptr) && (dest != nullptr) && (filterCoeffs != nullptr));
|
||||||
assert(src != NULL);
|
assert(numSamples > ilength);
|
||||||
assert(dest != NULL);
|
|
||||||
assert(filterCoeffs != NULL);
|
|
||||||
|
|
||||||
end = 2 * (numSamples - length);
|
end = 2 * (numSamples - ilength);
|
||||||
|
|
||||||
#pragma omp parallel for
|
#pragma omp parallel for
|
||||||
for (j = 0; j < end; j += 2)
|
for (j = 0; j < end; j += 2)
|
||||||
{
|
{
|
||||||
const SAMPLETYPE *ptr;
|
const SAMPLETYPE *ptr;
|
||||||
LONG_SAMPLETYPE suml, sumr;
|
LONG_SAMPLETYPE suml, sumr;
|
||||||
uint i;
|
|
||||||
|
|
||||||
suml = sumr = 0;
|
suml = sumr = 0;
|
||||||
ptr = src + j;
|
ptr = src + j;
|
||||||
|
|
||||||
for (i = 0; i < length; i += 4)
|
for (uint i = 0; i < ilength; i ++)
|
||||||
{
|
{
|
||||||
// loop is unrolled by factor of 4 here for efficiency
|
suml += ptr[2 * i] * filterCoeffsStereo[2 * i];
|
||||||
suml += ptr[2 * i + 0] * filterCoeffs[i + 0] +
|
sumr += ptr[2 * i + 1] * filterCoeffsStereo[2 * i + 1];
|
||||||
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
|
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
|
@ -116,54 +105,41 @@ uint FIRFilter::evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, ui
|
||||||
suml = (suml < -32768) ? -32768 : (suml > 32767) ? 32767 : suml;
|
suml = (suml < -32768) ? -32768 : (suml > 32767) ? 32767 : suml;
|
||||||
// saturate to 16 bit integer limits
|
// saturate to 16 bit integer limits
|
||||||
sumr = (sumr < -32768) ? -32768 : (sumr > 32767) ? 32767 : sumr;
|
sumr = (sumr < -32768) ? -32768 : (sumr > 32767) ? 32767 : sumr;
|
||||||
#else
|
|
||||||
suml *= dScaler;
|
|
||||||
sumr *= dScaler;
|
|
||||||
#endif // SOUNDTOUCH_INTEGER_SAMPLES
|
#endif // SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
dest[j] = (SAMPLETYPE)suml;
|
dest[j] = (SAMPLETYPE)suml;
|
||||||
dest[j + 1] = (SAMPLETYPE)sumr;
|
dest[j + 1] = (SAMPLETYPE)sumr;
|
||||||
}
|
}
|
||||||
return numSamples - length;
|
return numSamples - ilength;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Usual C-version of the filter routine for mono sound
|
// Usual C-version of the filter routine for mono sound
|
||||||
uint FIRFilter::evaluateFilterMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const
|
uint FIRFilter::evaluateFilterMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const
|
||||||
{
|
{
|
||||||
int j, end;
|
int j, end;
|
||||||
#ifdef SOUNDTOUCH_FLOAT_SAMPLES
|
|
||||||
// when using floating point samples, use a scaler instead of a divider
|
|
||||||
// because division is much slower operation than multiplying.
|
|
||||||
double dScaler = 1.0 / (double)resultDivider;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
assert(length != 0);
|
// hint compiler autovectorization that loop length is divisible by 8
|
||||||
|
int ilength = length & -8;
|
||||||
|
|
||||||
end = numSamples - length;
|
assert(ilength != 0);
|
||||||
|
|
||||||
|
end = numSamples - ilength;
|
||||||
#pragma omp parallel for
|
#pragma omp parallel for
|
||||||
for (j = 0; j < end; j ++)
|
for (j = 0; j < end; j ++)
|
||||||
{
|
{
|
||||||
const SAMPLETYPE *pSrc = src + j;
|
const SAMPLETYPE *pSrc = src + j;
|
||||||
LONG_SAMPLETYPE sum;
|
LONG_SAMPLETYPE sum;
|
||||||
uint i;
|
int i;
|
||||||
|
|
||||||
sum = 0;
|
sum = 0;
|
||||||
for (i = 0; i < length; i += 4)
|
for (i = 0; i < ilength; i ++)
|
||||||
{
|
{
|
||||||
// loop is unrolled by factor of 4 here for efficiency
|
sum += pSrc[i] * filterCoeffs[i];
|
||||||
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
|
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
sum >>= resultDivFactor;
|
sum >>= resultDivFactor;
|
||||||
// saturate to 16 bit integer limits
|
// saturate to 16 bit integer limits
|
||||||
sum = (sum < -32768) ? -32768 : (sum > 32767) ? 32767 : sum;
|
sum = (sum < -32768) ? -32768 : (sum > 32767) ? 32767 : sum;
|
||||||
#else
|
|
||||||
sum *= dScaler;
|
|
||||||
#endif // SOUNDTOUCH_INTEGER_SAMPLES
|
#endif // SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
dest[j] = (SAMPLETYPE)sum;
|
dest[j] = (SAMPLETYPE)sum;
|
||||||
}
|
}
|
||||||
|
@ -175,26 +151,24 @@ uint FIRFilter::evaluateFilterMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, uin
|
||||||
{
|
{
|
||||||
int j, end;
|
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(length != 0);
|
||||||
assert(src != NULL);
|
assert(src != nullptr);
|
||||||
assert(dest != NULL);
|
assert(dest != nullptr);
|
||||||
assert(filterCoeffs != NULL);
|
assert(filterCoeffs != nullptr);
|
||||||
assert(numChannels < 16);
|
assert(numChannels < 16);
|
||||||
|
|
||||||
end = numChannels * (numSamples - length);
|
// hint compiler autovectorization that loop length is divisible by 8
|
||||||
|
int ilength = length & -8;
|
||||||
|
|
||||||
|
end = numChannels * (numSamples - ilength);
|
||||||
|
|
||||||
#pragma omp parallel for
|
#pragma omp parallel for
|
||||||
for (j = 0; j < end; j += numChannels)
|
for (j = 0; j < end; j += numChannels)
|
||||||
{
|
{
|
||||||
const SAMPLETYPE *ptr;
|
const SAMPLETYPE *ptr;
|
||||||
LONG_SAMPLETYPE sums[16];
|
LONG_SAMPLETYPE sums[16];
|
||||||
uint c, i;
|
uint c;
|
||||||
|
int i;
|
||||||
|
|
||||||
for (c = 0; c < numChannels; c ++)
|
for (c = 0; c < numChannels; c ++)
|
||||||
{
|
{
|
||||||
|
@ -203,7 +177,7 @@ uint FIRFilter::evaluateFilterMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, uin
|
||||||
|
|
||||||
ptr = src + j;
|
ptr = src + j;
|
||||||
|
|
||||||
for (i = 0; i < length; i ++)
|
for (i = 0; i < ilength; i ++)
|
||||||
{
|
{
|
||||||
SAMPLETYPE coef=filterCoeffs[i];
|
SAMPLETYPE coef=filterCoeffs[i];
|
||||||
for (c = 0; c < numChannels; c ++)
|
for (c = 0; c < numChannels; c ++)
|
||||||
|
@ -217,13 +191,11 @@ uint FIRFilter::evaluateFilterMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, uin
|
||||||
{
|
{
|
||||||
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
sums[c] >>= resultDivFactor;
|
sums[c] >>= resultDivFactor;
|
||||||
#else
|
|
||||||
sums[c] *= dScaler;
|
|
||||||
#endif // SOUNDTOUCH_INTEGER_SAMPLES
|
#endif // SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
dest[j+c] = (SAMPLETYPE)sums[c];
|
dest[j+c] = (SAMPLETYPE)sums[c];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return numSamples - length;
|
return numSamples - ilength;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -235,6 +207,13 @@ void FIRFilter::setCoefficients(const SAMPLETYPE *coeffs, uint newLength, uint u
|
||||||
assert(newLength > 0);
|
assert(newLength > 0);
|
||||||
if (newLength % 8) ST_THROW_RT_ERROR("FIR filter length not divisible by 8");
|
if (newLength % 8) ST_THROW_RT_ERROR("FIR filter length not divisible by 8");
|
||||||
|
|
||||||
|
#ifdef SOUNDTOUCH_FLOAT_SAMPLES
|
||||||
|
// scale coefficients already here if using floating samples
|
||||||
|
double scale = 1.0 / resultDivider;
|
||||||
|
#else
|
||||||
|
short scale = 1;
|
||||||
|
#endif
|
||||||
|
|
||||||
lengthDiv8 = newLength / 8;
|
lengthDiv8 = newLength / 8;
|
||||||
length = lengthDiv8 * 8;
|
length = lengthDiv8 * 8;
|
||||||
assert(length == newLength);
|
assert(length == newLength);
|
||||||
|
@ -244,7 +223,16 @@ void FIRFilter::setCoefficients(const SAMPLETYPE *coeffs, uint newLength, uint u
|
||||||
|
|
||||||
delete[] filterCoeffs;
|
delete[] filterCoeffs;
|
||||||
filterCoeffs = new SAMPLETYPE[length];
|
filterCoeffs = new SAMPLETYPE[length];
|
||||||
memcpy(filterCoeffs, coeffs, length * sizeof(SAMPLETYPE));
|
delete[] filterCoeffsStereo;
|
||||||
|
filterCoeffsStereo = new SAMPLETYPE[length*2];
|
||||||
|
for (uint i = 0; i < length; i ++)
|
||||||
|
{
|
||||||
|
filterCoeffs[i] = (SAMPLETYPE)(coeffs[i] * scale);
|
||||||
|
// create also stereo set of filter coefficients: this allows compiler
|
||||||
|
// to autovectorize filter evaluation much more efficiently
|
||||||
|
filterCoeffsStereo[2 * i] = (SAMPLETYPE)(coeffs[i] * scale);
|
||||||
|
filterCoeffsStereo[2 * i + 1] = (SAMPLETYPE)(coeffs[i] * scale);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -254,7 +242,6 @@ uint FIRFilter::getLength() const
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Applies the filter to the given sequence of samples.
|
// Applies the filter to the given sequence of samples.
|
||||||
//
|
//
|
||||||
// Note : The amount of outputted samples is by value of 'filter_length'
|
// Note : The amount of outputted samples is by value of 'filter_length'
|
||||||
|
@ -284,10 +271,9 @@ uint FIRFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSample
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
||||||
// depending on if we've a MMX-capable CPU available or not.
|
// depending on if we've a MMX-capable CPU available or not.
|
||||||
void * FIRFilter::operator new(size_t s)
|
void * FIRFilter::operator new(size_t)
|
||||||
{
|
{
|
||||||
// Notice! don't use "new FIRFilter" directly, use "newInstance" to create a new instance instead!
|
// 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!");
|
ST_THROW_RT_ERROR("Error in FIRFilter::new: Don't use 'new FIRFilter', use 'newInstance' member instead!");
|
||||||
|
|
|
@ -11,13 +11,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2015-02-21 23:24:29 +0200 (Sat, 21 Feb 2015) $
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id: FIRFilter.h 202 2015-02-21 21:24:29Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
@ -64,6 +57,7 @@ protected:
|
||||||
|
|
||||||
// Memory for filter coefficients
|
// Memory for filter coefficients
|
||||||
SAMPLETYPE *filterCoeffs;
|
SAMPLETYPE *filterCoeffs;
|
||||||
|
SAMPLETYPE *filterCoeffsStereo;
|
||||||
|
|
||||||
virtual uint evaluateFilterStereo(SAMPLETYPE *dest,
|
virtual uint evaluateFilterStereo(SAMPLETYPE *dest,
|
||||||
const SAMPLETYPE *src,
|
const SAMPLETYPE *src,
|
||||||
|
@ -112,12 +106,12 @@ public:
|
||||||
short *filterCoeffsUnalign;
|
short *filterCoeffsUnalign;
|
||||||
short *filterCoeffsAlign;
|
short *filterCoeffsAlign;
|
||||||
|
|
||||||
virtual uint evaluateFilterStereo(short *dest, const short *src, uint numSamples) const;
|
virtual uint evaluateFilterStereo(short *dest, const short *src, uint numSamples) const override;
|
||||||
public:
|
public:
|
||||||
FIRFilterMMX();
|
FIRFilterMMX();
|
||||||
~FIRFilterMMX();
|
~FIRFilterMMX();
|
||||||
|
|
||||||
virtual void setCoefficients(const short *coeffs, uint newLength, uint uResultDivFactor);
|
virtual void setCoefficients(const short *coeffs, uint newLength, uint uResultDivFactor) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SOUNDTOUCH_ALLOW_MMX
|
#endif // SOUNDTOUCH_ALLOW_MMX
|
||||||
|
@ -131,12 +125,12 @@ public:
|
||||||
float *filterCoeffsUnalign;
|
float *filterCoeffsUnalign;
|
||||||
float *filterCoeffsAlign;
|
float *filterCoeffsAlign;
|
||||||
|
|
||||||
virtual uint evaluateFilterStereo(float *dest, const float *src, uint numSamples) const;
|
virtual uint evaluateFilterStereo(float *dest, const float *src, uint numSamples) const override;
|
||||||
public:
|
public:
|
||||||
FIRFilterSSE();
|
FIRFilterSSE();
|
||||||
~FIRFilterSSE();
|
~FIRFilterSSE();
|
||||||
|
|
||||||
virtual void setCoefficients(const float *coeffs, uint newLength, uint uResultDivFactor);
|
virtual void setCoefficients(const float *coeffs, uint newLength, uint uResultDivFactor) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SOUNDTOUCH_ALLOW_SSE
|
#endif // SOUNDTOUCH_ALLOW_SSE
|
||||||
|
|
|
@ -8,10 +8,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// $Id: InterpolateCubic.cpp 179 2014-01-06 18:41:42Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
|
|
@ -8,10 +8,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// $Id: InterpolateCubic.h 225 2015-07-26 14:45:48Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
@ -45,21 +41,27 @@ namespace soundtouch
|
||||||
class InterpolateCubic : public TransposerBase
|
class InterpolateCubic : public TransposerBase
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
virtual void resetRegisters();
|
|
||||||
virtual int transposeMono(SAMPLETYPE *dest,
|
virtual int transposeMono(SAMPLETYPE *dest,
|
||||||
const SAMPLETYPE *src,
|
const SAMPLETYPE *src,
|
||||||
int &srcSamples);
|
int &srcSamples) override;
|
||||||
virtual int transposeStereo(SAMPLETYPE *dest,
|
virtual int transposeStereo(SAMPLETYPE *dest,
|
||||||
const SAMPLETYPE *src,
|
const SAMPLETYPE *src,
|
||||||
int &srcSamples);
|
int &srcSamples) override;
|
||||||
virtual int transposeMulti(SAMPLETYPE *dest,
|
virtual int transposeMulti(SAMPLETYPE *dest,
|
||||||
const SAMPLETYPE *src,
|
const SAMPLETYPE *src,
|
||||||
int &srcSamples);
|
int &srcSamples) override;
|
||||||
|
|
||||||
double fract;
|
double fract;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
InterpolateCubic();
|
InterpolateCubic();
|
||||||
|
|
||||||
|
virtual void resetRegisters() override;
|
||||||
|
|
||||||
|
virtual int getLatency() const override
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,10 +8,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// $Id: InterpolateLinear.cpp 225 2015-07-26 14:45:48Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
@ -146,7 +142,7 @@ int InterpolateLinearInteger::transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE
|
||||||
LONG_SAMPLETYPE temp, vol1;
|
LONG_SAMPLETYPE temp, vol1;
|
||||||
|
|
||||||
assert(iFract < SCALE);
|
assert(iFract < SCALE);
|
||||||
vol1 = (SCALE - iFract);
|
vol1 = (LONG_SAMPLETYPE)(SCALE - iFract);
|
||||||
for (int c = 0; c < numChannels; c ++)
|
for (int c = 0; c < numChannels; c ++)
|
||||||
{
|
{
|
||||||
temp = vol1 * src[c] + iFract * src[c + numChannels];
|
temp = vol1 * src[c] + iFract * src[c + numChannels];
|
||||||
|
|
|
@ -8,10 +8,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// $Id: InterpolateLinear.h 225 2015-07-26 14:45:48Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
@ -42,39 +38,42 @@
|
||||||
namespace soundtouch
|
namespace soundtouch
|
||||||
{
|
{
|
||||||
|
|
||||||
/// Linear transposer class that uses integer arithmetics
|
/// Linear transposer class that uses integer arithmetic
|
||||||
class InterpolateLinearInteger : public TransposerBase
|
class InterpolateLinearInteger : public TransposerBase
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
int iFract;
|
int iFract;
|
||||||
int iRate;
|
int iRate;
|
||||||
|
|
||||||
virtual void resetRegisters();
|
|
||||||
|
|
||||||
virtual int transposeMono(SAMPLETYPE *dest,
|
virtual int transposeMono(SAMPLETYPE *dest,
|
||||||
const SAMPLETYPE *src,
|
const SAMPLETYPE *src,
|
||||||
int &srcSamples);
|
int &srcSamples) override;
|
||||||
virtual int transposeStereo(SAMPLETYPE *dest,
|
virtual int transposeStereo(SAMPLETYPE *dest,
|
||||||
const SAMPLETYPE *src,
|
const SAMPLETYPE *src,
|
||||||
int &srcSamples);
|
int &srcSamples) override;
|
||||||
virtual int transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples);
|
virtual int transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples) override;
|
||||||
public:
|
public:
|
||||||
InterpolateLinearInteger();
|
InterpolateLinearInteger();
|
||||||
|
|
||||||
/// Sets new target rate. Normal rate = 1.0, smaller values represent slower
|
/// Sets new target rate. Normal rate = 1.0, smaller values represent slower
|
||||||
/// rate, larger faster rates.
|
/// rate, larger faster rates.
|
||||||
virtual void setRate(double newRate);
|
virtual void setRate(double newRate) override;
|
||||||
|
|
||||||
|
virtual void resetRegisters() override;
|
||||||
|
|
||||||
|
virtual int getLatency() const override
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/// Linear transposer class that uses floating point arithmetics
|
/// Linear transposer class that uses floating point arithmetic
|
||||||
class InterpolateLinearFloat : public TransposerBase
|
class InterpolateLinearFloat : public TransposerBase
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
double fract;
|
double fract;
|
||||||
|
|
||||||
virtual void resetRegisters();
|
|
||||||
|
|
||||||
virtual int transposeMono(SAMPLETYPE *dest,
|
virtual int transposeMono(SAMPLETYPE *dest,
|
||||||
const SAMPLETYPE *src,
|
const SAMPLETYPE *src,
|
||||||
int &srcSamples);
|
int &srcSamples);
|
||||||
|
@ -85,6 +84,13 @@ protected:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
InterpolateLinearFloat();
|
InterpolateLinearFloat();
|
||||||
|
|
||||||
|
virtual void resetRegisters();
|
||||||
|
|
||||||
|
int getLatency() const
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,10 +13,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// $Id: InterpolateShannon.cpp 195 2014-04-06 15:57:21Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
@ -175,9 +171,9 @@ int InterpolateShannon::transposeStereo(SAMPLETYPE *pdest,
|
||||||
|
|
||||||
/// Transpose stereo audio. Returns number of produced output samples, and
|
/// Transpose stereo audio. Returns number of produced output samples, and
|
||||||
/// updates "srcSamples" to amount of consumed source samples
|
/// updates "srcSamples" to amount of consumed source samples
|
||||||
int InterpolateShannon::transposeMulti(SAMPLETYPE *pdest,
|
int InterpolateShannon::transposeMulti(SAMPLETYPE *,
|
||||||
const SAMPLETYPE *psrc,
|
const SAMPLETYPE *,
|
||||||
int &srcSamples)
|
int &)
|
||||||
{
|
{
|
||||||
// not implemented
|
// not implemented
|
||||||
assert(false);
|
assert(false);
|
||||||
|
|
|
@ -13,10 +13,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// $Id: InterpolateShannon.h 225 2015-07-26 14:45:48Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
@ -50,21 +46,27 @@ namespace soundtouch
|
||||||
class InterpolateShannon : public TransposerBase
|
class InterpolateShannon : public TransposerBase
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
void resetRegisters();
|
|
||||||
int transposeMono(SAMPLETYPE *dest,
|
int transposeMono(SAMPLETYPE *dest,
|
||||||
const SAMPLETYPE *src,
|
const SAMPLETYPE *src,
|
||||||
int &srcSamples);
|
int &srcSamples) override;
|
||||||
int transposeStereo(SAMPLETYPE *dest,
|
int transposeStereo(SAMPLETYPE *dest,
|
||||||
const SAMPLETYPE *src,
|
const SAMPLETYPE *src,
|
||||||
int &srcSamples);
|
int &srcSamples) override;
|
||||||
int transposeMulti(SAMPLETYPE *dest,
|
int transposeMulti(SAMPLETYPE *dest,
|
||||||
const SAMPLETYPE *src,
|
const SAMPLETYPE *src,
|
||||||
int &srcSamples);
|
int &srcSamples) override;
|
||||||
|
|
||||||
double fract;
|
double fract;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
InterpolateShannon();
|
InterpolateShannon();
|
||||||
|
|
||||||
|
void resetRegisters() override;
|
||||||
|
|
||||||
|
virtual int getLatency() const override
|
||||||
|
{
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,13 +11,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2015-05-18 18:22:02 +0300 (Mon, 18 May 2015) $
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id: PeakFinder.cpp 213 2015-05-18 15:22:02Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
@ -149,7 +142,7 @@ int PeakFinder::findCrossingLevel(const float *data, float level, int peakpos, i
|
||||||
peaklevel = data[peakpos];
|
peaklevel = data[peakpos];
|
||||||
assert(peaklevel >= level);
|
assert(peaklevel >= level);
|
||||||
pos = peakpos;
|
pos = peakpos;
|
||||||
while ((pos >= minPos) && (pos < maxPos))
|
while ((pos >= minPos) && (pos + direction < maxPos))
|
||||||
{
|
{
|
||||||
if (data[pos + direction] < level) return pos; // crossing found
|
if (data[pos + direction] < level) return pos; // crossing found
|
||||||
pos += direction;
|
pos += direction;
|
||||||
|
@ -178,7 +171,6 @@ double PeakFinder::calcMassCenter(const float *data, int firstPos, int lastPos)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// get exact center of peak near given position by calculating local mass of center
|
/// get exact center of peak near given position by calculating local mass of center
|
||||||
double PeakFinder::getPeakCenter(const float *data, int peakpos) const
|
double PeakFinder::getPeakCenter(const float *data, int peakpos) const
|
||||||
{
|
{
|
||||||
|
@ -218,7 +210,6 @@ double PeakFinder::getPeakCenter(const float *data, int peakpos) const
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
double PeakFinder::detectPeak(const float *data, int aminPos, int amaxPos)
|
double PeakFinder::detectPeak(const float *data, int aminPos, int amaxPos)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -249,12 +240,12 @@ double PeakFinder::detectPeak(const float *data, int aminPos, int amaxPos)
|
||||||
// - sometimes the highest peak can be Nth harmonic of the true base peak yet
|
// - sometimes the highest peak can be Nth harmonic of the true base peak yet
|
||||||
// just a slightly higher than the true base
|
// just a slightly higher than the true base
|
||||||
|
|
||||||
for (i = 3; i < 10; i ++)
|
for (i = 1; i < 3; i ++)
|
||||||
{
|
{
|
||||||
double peaktmp, harmonic;
|
double peaktmp, harmonic;
|
||||||
int i1,i2;
|
int i1,i2;
|
||||||
|
|
||||||
harmonic = (double)i * 0.5;
|
harmonic = (double)pow(2.0, i);
|
||||||
peakpos = (int)(highPeak / harmonic + 0.5f);
|
peakpos = (int)(highPeak / harmonic + 0.5f);
|
||||||
if (peakpos < minPos) break;
|
if (peakpos < minPos) break;
|
||||||
peakpos = findTop(data, peakpos); // seek true local maximum index
|
peakpos = findTop(data, peakpos); // seek true local maximum index
|
||||||
|
|
|
@ -9,13 +9,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2011-12-30 22:33:46 +0200 (Fri, 30 Dec 2011) $
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id: PeakFinder.h 132 2011-12-30 20:33:46Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
@ -51,8 +44,8 @@ protected:
|
||||||
|
|
||||||
/// Calculates the mass center between given vector items.
|
/// Calculates the mass center between given vector items.
|
||||||
double calcMassCenter(const float *data, ///< Data vector.
|
double calcMassCenter(const float *data, ///< Data vector.
|
||||||
int firstPos, ///< Index of first vector item beloging to the peak.
|
int firstPos, ///< Index of first vector item belonging to the peak.
|
||||||
int lastPos ///< Index of last vector item beloging to the peak.
|
int lastPos ///< Index of last vector item belonging to the peak.
|
||||||
) const;
|
) const;
|
||||||
|
|
||||||
/// Finds the data vector index where the monotoniously decreasing signal crosses the
|
/// Finds the data vector index where the monotoniously decreasing signal crosses the
|
||||||
|
|
|
@ -10,13 +10,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2015-07-26 17:45:48 +0300 (Sun, 26 Jul 2015) $
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id: RateTransposer.cpp 225 2015-07-26 14:45:48Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
@ -57,15 +50,21 @@ TransposerBase::ALGORITHM TransposerBase::algorithm = TransposerBase::CUBIC;
|
||||||
// Constructor
|
// Constructor
|
||||||
RateTransposer::RateTransposer() : FIFOProcessor(&outputBuffer)
|
RateTransposer::RateTransposer() : FIFOProcessor(&outputBuffer)
|
||||||
{
|
{
|
||||||
bUseAAFilter = true;
|
bUseAAFilter =
|
||||||
|
#ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
|
||||||
|
true;
|
||||||
|
#else
|
||||||
|
// Disable Anti-alias filter if desirable to avoid click at rate change zero value crossover
|
||||||
|
false;
|
||||||
|
#endif
|
||||||
|
|
||||||
// Instantiates the anti-alias filter
|
// Instantiates the anti-alias filter
|
||||||
pAAFilter = new AAFilter(64);
|
pAAFilter = new AAFilter(64);
|
||||||
pTransposer = TransposerBase::newInstance();
|
pTransposer = TransposerBase::newInstance();
|
||||||
|
clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
RateTransposer::~RateTransposer()
|
RateTransposer::~RateTransposer()
|
||||||
{
|
{
|
||||||
delete pAAFilter;
|
delete pAAFilter;
|
||||||
|
@ -73,11 +72,14 @@ RateTransposer::~RateTransposer()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// Enables/disables the anti-alias filter. Zero to disable, nonzero to enable
|
/// Enables/disables the anti-alias filter. Zero to disable, nonzero to enable
|
||||||
void RateTransposer::enableAAFilter(bool newMode)
|
void RateTransposer::enableAAFilter(bool newMode)
|
||||||
{
|
{
|
||||||
|
#ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
|
||||||
|
// Disable Anti-alias filter if desirable to avoid click at rate change zero value crossover
|
||||||
bUseAAFilter = newMode;
|
bUseAAFilter = newMode;
|
||||||
|
clear();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -94,7 +96,6 @@ AAFilter *RateTransposer::getAAFilter()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Sets new target iRate. Normal iRate = 1.0, smaller values represent slower
|
// Sets new target iRate. Normal iRate = 1.0, smaller values represent slower
|
||||||
// iRate, larger faster iRates.
|
// iRate, larger faster iRates.
|
||||||
void RateTransposer::setRate(double newRate)
|
void RateTransposer::setRate(double newRate)
|
||||||
|
@ -130,8 +131,6 @@ void RateTransposer::putSamples(const SAMPLETYPE *samples, uint nSamples)
|
||||||
// the 'set_returnBuffer_size' function.
|
// the 'set_returnBuffer_size' function.
|
||||||
void RateTransposer::processSamples(const SAMPLETYPE *src, uint nSamples)
|
void RateTransposer::processSamples(const SAMPLETYPE *src, uint nSamples)
|
||||||
{
|
{
|
||||||
uint count;
|
|
||||||
|
|
||||||
if (nSamples == 0) return;
|
if (nSamples == 0) return;
|
||||||
|
|
||||||
// Store samples to input buffer
|
// Store samples to input buffer
|
||||||
|
@ -141,7 +140,7 @@ void RateTransposer::processSamples(const SAMPLETYPE *src, uint nSamples)
|
||||||
// the filter
|
// the filter
|
||||||
if (bUseAAFilter == false)
|
if (bUseAAFilter == false)
|
||||||
{
|
{
|
||||||
count = pTransposer->transpose(outputBuffer, inputBuffer);
|
(void)pTransposer->transpose(outputBuffer, inputBuffer);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,11 +176,10 @@ void RateTransposer::processSamples(const SAMPLETYPE *src, uint nSamples)
|
||||||
// Sets the number of channels, 1 = mono, 2 = stereo
|
// Sets the number of channels, 1 = mono, 2 = stereo
|
||||||
void RateTransposer::setChannels(int nChannels)
|
void RateTransposer::setChannels(int nChannels)
|
||||||
{
|
{
|
||||||
assert(nChannels > 0);
|
if (!verifyNumberOfChannels(nChannels) ||
|
||||||
|
(pTransposer->numChannels == nChannels)) return;
|
||||||
|
|
||||||
if (pTransposer->numChannels == nChannels) return;
|
|
||||||
pTransposer->setChannels(nChannels);
|
pTransposer->setChannels(nChannels);
|
||||||
|
|
||||||
inputBuffer.setChannels(nChannels);
|
inputBuffer.setChannels(nChannels);
|
||||||
midBuffer.setChannels(nChannels);
|
midBuffer.setChannels(nChannels);
|
||||||
outputBuffer.setChannels(nChannels);
|
outputBuffer.setChannels(nChannels);
|
||||||
|
@ -194,6 +192,11 @@ void RateTransposer::clear()
|
||||||
outputBuffer.clear();
|
outputBuffer.clear();
|
||||||
midBuffer.clear();
|
midBuffer.clear();
|
||||||
inputBuffer.clear();
|
inputBuffer.clear();
|
||||||
|
pTransposer->resetRegisters();
|
||||||
|
|
||||||
|
// prefill buffer to avoid losing first samples at beginning of stream
|
||||||
|
int prefill = getLatency();
|
||||||
|
inputBuffer.addSilent(prefill);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -208,6 +211,14 @@ int RateTransposer::isEmpty() const
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Return approximate initial input-output latency
|
||||||
|
int RateTransposer::getLatency() const
|
||||||
|
{
|
||||||
|
return pTransposer->getLatency() +
|
||||||
|
((bUseAAFilter) ? (pAAFilter->getLength() / 2) : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// TransposerBase - Base class for interpolation
|
// TransposerBase - Base class for interpolation
|
||||||
|
@ -280,7 +291,7 @@ void TransposerBase::setRate(double newRate)
|
||||||
TransposerBase *TransposerBase::newInstance()
|
TransposerBase *TransposerBase::newInstance()
|
||||||
{
|
{
|
||||||
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
// Notice: For integer arithmetics support only linear algorithm (due to simplest calculus)
|
// Notice: For integer arithmetic support only linear algorithm (due to simplest calculus)
|
||||||
return ::new InterpolateLinearInteger;
|
return ::new InterpolateLinearInteger;
|
||||||
#else
|
#else
|
||||||
switch (algorithm)
|
switch (algorithm)
|
||||||
|
@ -296,7 +307,7 @@ TransposerBase *TransposerBase::newInstance()
|
||||||
|
|
||||||
default:
|
default:
|
||||||
assert(false);
|
assert(false);
|
||||||
return NULL;
|
return nullptr;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,13 +14,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2015-07-26 17:45:48 +0300 (Sun, 26 Jul 2015) $
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id: RateTransposer.h 225 2015-07-26 14:45:48Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
@ -66,8 +59,6 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void resetRegisters() = 0;
|
|
||||||
|
|
||||||
virtual int transposeMono(SAMPLETYPE *dest,
|
virtual int transposeMono(SAMPLETYPE *dest,
|
||||||
const SAMPLETYPE *src,
|
const SAMPLETYPE *src,
|
||||||
int &srcSamples) = 0;
|
int &srcSamples) = 0;
|
||||||
|
@ -90,6 +81,9 @@ public:
|
||||||
virtual int transpose(FIFOSampleBuffer &dest, FIFOSampleBuffer &src);
|
virtual int transpose(FIFOSampleBuffer &dest, FIFOSampleBuffer &src);
|
||||||
virtual void setRate(double newRate);
|
virtual void setRate(double newRate);
|
||||||
virtual void setChannels(int channels);
|
virtual void setChannels(int channels);
|
||||||
|
virtual int getLatency() const = 0;
|
||||||
|
|
||||||
|
virtual void resetRegisters() = 0;
|
||||||
|
|
||||||
// static factory function
|
// static factory function
|
||||||
static TransposerBase *newInstance();
|
static TransposerBase *newInstance();
|
||||||
|
@ -130,23 +124,11 @@ protected:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RateTransposer();
|
RateTransposer();
|
||||||
virtual ~RateTransposer();
|
virtual ~RateTransposer() override;
|
||||||
|
|
||||||
/// 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
|
/// Returns the output buffer object
|
||||||
FIFOSamplePipe *getOutput() { return &outputBuffer; };
|
FIFOSamplePipe *getOutput() { return &outputBuffer; };
|
||||||
|
|
||||||
/// Returns the store buffer object
|
|
||||||
// FIFOSamplePipe *getStore() { return &storeBuffer; };
|
|
||||||
|
|
||||||
/// Return anti-alias filter object
|
/// Return anti-alias filter object
|
||||||
AAFilter *getAAFilter();
|
AAFilter *getAAFilter();
|
||||||
|
|
||||||
|
@ -165,13 +147,16 @@ public:
|
||||||
|
|
||||||
/// Adds 'numSamples' pcs of samples from the 'samples' memory position into
|
/// Adds 'numSamples' pcs of samples from the 'samples' memory position into
|
||||||
/// the input of the object.
|
/// the input of the object.
|
||||||
void putSamples(const SAMPLETYPE *samples, uint numSamples);
|
void putSamples(const SAMPLETYPE *samples, uint numSamples) override;
|
||||||
|
|
||||||
/// Clears all the samples in the object
|
/// Clears all the samples in the object
|
||||||
void clear();
|
void clear() override;
|
||||||
|
|
||||||
/// Returns nonzero if there aren't any samples available for outputting.
|
/// Returns nonzero if there aren't any samples available for outputting.
|
||||||
int isEmpty() const;
|
int isEmpty() const override;
|
||||||
|
|
||||||
|
/// Return approximate initial input-output latency
|
||||||
|
int getLatency() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,13 +8,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2015-05-18 18:25:07 +0300 (Mon, 18 May 2015) $
|
|
||||||
// File revision : $Revision: 3 $
|
|
||||||
//
|
|
||||||
// $Id: STTypes.h 215 2015-05-18 15:25:07Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
@ -39,15 +32,6 @@
|
||||||
#ifndef STTypes_H
|
#ifndef STTypes_H
|
||||||
#define STTypes_H
|
#define STTypes_H
|
||||||
|
|
||||||
#if (defined(__GNUC__) && !defined(ANDROID))
|
|
||||||
// In GCC, include soundtouch_config.h made by config scritps.
|
|
||||||
// Skip this in Android compilation that uses GCC but without configure scripts.
|
|
||||||
//#include "soundtouch_config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
namespace soundtouch
|
|
||||||
{
|
|
||||||
typedef unsigned int uint;
|
typedef unsigned int uint;
|
||||||
typedef unsigned long ulong;
|
typedef unsigned long ulong;
|
||||||
|
|
||||||
|
@ -62,10 +46,23 @@ namespace soundtouch
|
||||||
// Helper macro for aligning pointer up to next 16-byte boundary
|
// Helper macro for aligning pointer up to next 16-byte boundary
|
||||||
#define SOUNDTOUCH_ALIGN_POINTER_16(x) ( ( (ulongptr)(x) + 15 ) & ~(ulongptr)15 )
|
#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
|
||||||
|
{
|
||||||
|
/// Max allowed number of channels
|
||||||
|
#define SOUNDTOUCH_MAX_CHANNELS 16
|
||||||
|
|
||||||
/// Activate these undef's to overrule the possible sampletype
|
/// Activate these undef's to overrule the possible sampletype
|
||||||
/// setting inherited from some other header file:
|
/// setting inherited from some other header file:
|
||||||
#undef SOUNDTOUCH_INTEGER_SAMPLES
|
//#undef SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
#undef SOUNDTOUCH_FLOAT_SAMPLES
|
//#undef SOUNDTOUCH_FLOAT_SAMPLES
|
||||||
|
|
||||||
/// If following flag is defined, always uses multichannel processing
|
/// If following flag is defined, always uses multichannel processing
|
||||||
/// routines also for mono and stero sound. This is for routine testing
|
/// routines also for mono and stero sound. This is for routine testing
|
||||||
|
@ -74,7 +71,7 @@ namespace soundtouch
|
||||||
/// runtime performance so recommendation is to keep this off.
|
/// runtime performance so recommendation is to keep this off.
|
||||||
// #define USE_MULTICH_ALWAYS
|
// #define USE_MULTICH_ALWAYS
|
||||||
|
|
||||||
#if (defined(__SOFTFP__))
|
#if (defined(__SOFTFP__) && defined(ANDROID))
|
||||||
// For Android compilation: Force use of Integer samples in case that
|
// For Android compilation: Force use of Integer samples in case that
|
||||||
// compilation uses soft-floating point emulation - soft-fp is way too slow
|
// compilation uses soft-floating point emulation - soft-fp is way too slow
|
||||||
#undef SOUNDTOUCH_FLOAT_SAMPLES
|
#undef SOUNDTOUCH_FLOAT_SAMPLES
|
||||||
|
@ -97,8 +94,8 @@ namespace soundtouch
|
||||||
/// However, if you still prefer to select the sample format here
|
/// However, if you still prefer to select the sample format here
|
||||||
/// also in GNU environment, then please #undef the INTEGER_SAMPLE
|
/// also in GNU environment, then please #undef the INTEGER_SAMPLE
|
||||||
/// and FLOAT_SAMPLE defines first as in comments above.
|
/// and FLOAT_SAMPLE defines first as in comments above.
|
||||||
#define SOUNDTOUCH_INTEGER_SAMPLES 1 //< 16bit integer samples
|
//#define SOUNDTOUCH_INTEGER_SAMPLES 1 //< 16bit integer samples
|
||||||
//#define SOUNDTOUCH_FLOAT_SAMPLES 1 //< 32bit float samples
|
#define SOUNDTOUCH_FLOAT_SAMPLES 1 //< 32bit float samples
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -109,7 +106,7 @@ namespace soundtouch
|
||||||
/// routines compiled for whatever reason, you may disable these optimizations
|
/// routines compiled for whatever reason, you may disable these optimizations
|
||||||
/// to make the library compile.
|
/// to make the library compile.
|
||||||
|
|
||||||
//#define SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS 1
|
#define SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS 1
|
||||||
|
|
||||||
/// In GNU environment, allow the user to override this setting by
|
/// In GNU environment, allow the user to override this setting by
|
||||||
/// giving the following switch to the configure script:
|
/// giving the following switch to the configure script:
|
||||||
|
@ -124,10 +121,10 @@ namespace soundtouch
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// If defined, allows the SIMD-optimized routines to take minor shortcuts
|
// If defined, allows the SIMD-optimized routines to skip unevenly aligned
|
||||||
// for improved performance. Undefine to require faithfully similar SIMD
|
// memory offsets that can cause performance penalty in some SIMD implementations.
|
||||||
// calculations as in normal C implementation.
|
// Causes slight compromise in sound quality.
|
||||||
#define SOUNDTOUCH_ALLOW_NONEXACT_SIMD_OPTIMIZATION 1
|
// #define SOUNDTOUCH_ALLOW_NONEXACT_SIMD_OPTIMIZATION 1
|
||||||
|
|
||||||
|
|
||||||
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
|
@ -142,16 +139,19 @@ namespace soundtouch
|
||||||
#endif // SOUNDTOUCH_FLOAT_SAMPLES
|
#endif // SOUNDTOUCH_FLOAT_SAMPLES
|
||||||
|
|
||||||
#ifdef SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS
|
#ifdef SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS
|
||||||
// Allow MMX optimizations
|
// Allow MMX optimizations (not available in X64 mode)
|
||||||
|
#if (!_M_X64)
|
||||||
#define SOUNDTOUCH_ALLOW_MMX 1
|
#define SOUNDTOUCH_ALLOW_MMX 1
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
// floating point samples
|
// floating point samples
|
||||||
typedef float SAMPLETYPE;
|
typedef float SAMPLETYPE;
|
||||||
// data type for sample accumulation: Use double to utilize full precision.
|
// data type for sample accumulation: Use float also here to enable
|
||||||
typedef double LONG_SAMPLETYPE;
|
// efficient autovectorization
|
||||||
|
typedef float LONG_SAMPLETYPE;
|
||||||
|
|
||||||
#ifdef SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS
|
#ifdef SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS
|
||||||
// Allow SSE optimizations
|
// Allow SSE optimizations
|
||||||
|
@ -160,10 +160,16 @@ namespace soundtouch
|
||||||
|
|
||||||
#endif // SOUNDTOUCH_INTEGER_SAMPLES
|
#endif // SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
|
|
||||||
};
|
#if ((SOUNDTOUCH_ALLOW_SSE) || (__SSE__) || (SOUNDTOUCH_USE_NEON))
|
||||||
|
#if SOUNDTOUCH_ALLOW_NONEXACT_SIMD_OPTIMIZATION
|
||||||
|
#define ST_SIMD_AVOID_UNALIGNED
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// define ST_NO_EXCEPTION_HANDLING switch to disable throwing std exceptions:
|
// define ST_NO_EXCEPTION_HANDLING switch to disable throwing std exceptions:
|
||||||
#define ST_NO_EXCEPTION_HANDLING 1
|
// #define ST_NO_EXCEPTION_HANDLING 1
|
||||||
#ifdef ST_NO_EXCEPTION_HANDLING
|
#ifdef ST_NO_EXCEPTION_HANDLING
|
||||||
// Exceptions disabled. Throw asserts instead if enabled.
|
// Exceptions disabled. Throw asserts instead if enabled.
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
|
@ -41,13 +41,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2015-07-26 17:45:48 +0300 (Sun, 26 Jul 2015) $
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id: SoundTouch.cpp 225 2015-07-26 14:45:48Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
@ -118,7 +111,6 @@ SoundTouch::SoundTouch()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
SoundTouch::~SoundTouch()
|
SoundTouch::~SoundTouch()
|
||||||
{
|
{
|
||||||
delete pRateTransposer;
|
delete pRateTransposer;
|
||||||
|
@ -126,7 +118,6 @@ SoundTouch::~SoundTouch()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// Get SoundTouch library version string
|
/// Get SoundTouch library version string
|
||||||
const char *SoundTouch::getVersionString()
|
const char *SoundTouch::getVersionString()
|
||||||
{
|
{
|
||||||
|
@ -146,18 +137,14 @@ uint SoundTouch::getVersionId()
|
||||||
// Sets the number of channels, 1 = mono, 2 = stereo
|
// Sets the number of channels, 1 = mono, 2 = stereo
|
||||||
void SoundTouch::setChannels(uint numChannels)
|
void SoundTouch::setChannels(uint numChannels)
|
||||||
{
|
{
|
||||||
/*if (numChannels != 1 && numChannels != 2)
|
if (!verifyNumberOfChannels(numChannels)) return;
|
||||||
{
|
|
||||||
//ST_THROW_RT_ERROR("Illegal number of channels");
|
|
||||||
return;
|
|
||||||
}*/
|
|
||||||
channels = numChannels;
|
channels = numChannels;
|
||||||
pRateTransposer->setChannels((int)numChannels);
|
pRateTransposer->setChannels((int)numChannels);
|
||||||
pTDStretch->setChannels((int)numChannels);
|
pTDStretch->setChannels((int)numChannels);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Sets new rate control value. Normal rate = 1.0, smaller values
|
// Sets new rate control value. Normal rate = 1.0, smaller values
|
||||||
// represent slower rate, larger faster rates.
|
// represent slower rate, larger faster rates.
|
||||||
void SoundTouch::setRate(double newRate)
|
void SoundTouch::setRate(double newRate)
|
||||||
|
@ -167,7 +154,6 @@ void SoundTouch::setRate(double newRate)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Sets new rate control value as a difference in percents compared
|
// Sets new rate control value as a difference in percents compared
|
||||||
// to the original rate (-50 .. +100 %)
|
// to the original rate (-50 .. +100 %)
|
||||||
void SoundTouch::setRateChange(double newRate)
|
void SoundTouch::setRateChange(double newRate)
|
||||||
|
@ -177,7 +163,6 @@ void SoundTouch::setRateChange(double newRate)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Sets new tempo control value. Normal tempo = 1.0, smaller values
|
// Sets new tempo control value. Normal tempo = 1.0, smaller values
|
||||||
// represent slower tempo, larger faster tempo.
|
// represent slower tempo, larger faster tempo.
|
||||||
void SoundTouch::setTempo(double newTempo)
|
void SoundTouch::setTempo(double newTempo)
|
||||||
|
@ -187,7 +172,6 @@ void SoundTouch::setTempo(double newTempo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Sets new tempo control value as a difference in percents compared
|
// Sets new tempo control value as a difference in percents compared
|
||||||
// to the original tempo (-50 .. +100 %)
|
// to the original tempo (-50 .. +100 %)
|
||||||
void SoundTouch::setTempoChange(double newTempo)
|
void SoundTouch::setTempoChange(double newTempo)
|
||||||
|
@ -197,7 +181,6 @@ void SoundTouch::setTempoChange(double newTempo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Sets new pitch control value. Original pitch = 1.0, smaller values
|
// Sets new pitch control value. Original pitch = 1.0, smaller values
|
||||||
// represent lower pitches, larger values higher pitch.
|
// represent lower pitches, larger values higher pitch.
|
||||||
void SoundTouch::setPitch(double newPitch)
|
void SoundTouch::setPitch(double newPitch)
|
||||||
|
@ -207,7 +190,6 @@ void SoundTouch::setPitch(double newPitch)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Sets pitch change in octaves compared to the original pitch
|
// Sets pitch change in octaves compared to the original pitch
|
||||||
// (-1.00 .. +1.00)
|
// (-1.00 .. +1.00)
|
||||||
void SoundTouch::setPitchOctaves(double newPitch)
|
void SoundTouch::setPitchOctaves(double newPitch)
|
||||||
|
@ -217,7 +199,6 @@ void SoundTouch::setPitchOctaves(double newPitch)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Sets pitch change in semi-tones compared to the original pitch
|
// Sets pitch change in semi-tones compared to the original pitch
|
||||||
// (-12 .. +12)
|
// (-12 .. +12)
|
||||||
void SoundTouch::setPitchSemiTones(int newPitch)
|
void SoundTouch::setPitchSemiTones(int newPitch)
|
||||||
|
@ -226,7 +207,6 @@ void SoundTouch::setPitchSemiTones(int newPitch)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void SoundTouch::setPitchSemiTones(double newPitch)
|
void SoundTouch::setPitchSemiTones(double newPitch)
|
||||||
{
|
{
|
||||||
setPitchOctaves(newPitch / 12.0);
|
setPitchOctaves(newPitch / 12.0);
|
||||||
|
@ -286,9 +266,9 @@ void SoundTouch::calcEffectiveRateAndTempo()
|
||||||
// Sets sample rate.
|
// Sets sample rate.
|
||||||
void SoundTouch::setSampleRate(uint srate)
|
void SoundTouch::setSampleRate(uint srate)
|
||||||
{
|
{
|
||||||
bSrateSet = true;
|
|
||||||
// set sample rate, leave other tempo changer parameters as they are.
|
// set sample rate, leave other tempo changer parameters as they are.
|
||||||
pTDStretch->setParameters((int)srate);
|
pTDStretch->setParameters((int)srate);
|
||||||
|
bSrateSet = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -305,22 +285,6 @@ void SoundTouch::putSamples(const SAMPLETYPE *samples, uint nSamples)
|
||||||
ST_THROW_RT_ERROR("SoundTouch : Number of channels not defined");
|
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
|
// accumulate how many samples are expected out from processing, given the current
|
||||||
// processing setting
|
// processing setting
|
||||||
samplesExpectedOut += (double)nSamples / ((double)rate * (double)tempo);
|
samplesExpectedOut += (double)nSamples / ((double)rate * (double)tempo);
|
||||||
|
@ -359,6 +323,7 @@ void SoundTouch::flush()
|
||||||
|
|
||||||
// how many samples are still expected to output
|
// how many samples are still expected to output
|
||||||
numStillExpected = (int)((long)(samplesExpectedOut + 0.5) - samplesOutput);
|
numStillExpected = (int)((long)(samplesExpectedOut + 0.5) - samplesOutput);
|
||||||
|
if (numStillExpected < 0) numStillExpected = 0;
|
||||||
|
|
||||||
memset(buff, 0, 128 * channels * sizeof(SAMPLETYPE));
|
memset(buff, 0, 128 * channels * sizeof(SAMPLETYPE));
|
||||||
// "Push" the last active samples out from the processing pipeline by
|
// "Push" the last active samples out from the processing pipeline by
|
||||||
|
@ -375,7 +340,6 @@ void SoundTouch::flush()
|
||||||
delete[] buff;
|
delete[] buff;
|
||||||
|
|
||||||
// Clear input buffers
|
// Clear input buffers
|
||||||
// pRateTransposer->clearInput();
|
|
||||||
pTDStretch->clearInput();
|
pTDStretch->clearInput();
|
||||||
// yet leave the output intouched as that's where the
|
// yet leave the output intouched as that's where the
|
||||||
// flushed samples are!
|
// flushed samples are!
|
||||||
|
@ -449,22 +413,62 @@ int SoundTouch::getSetting(int settingId) const
|
||||||
return (uint)pTDStretch->isQuickSeekEnabled();
|
return (uint)pTDStretch->isQuickSeekEnabled();
|
||||||
|
|
||||||
case SETTING_SEQUENCE_MS:
|
case SETTING_SEQUENCE_MS:
|
||||||
pTDStretch->getParameters(NULL, &temp, NULL, NULL);
|
pTDStretch->getParameters(nullptr, &temp, nullptr, nullptr);
|
||||||
return temp;
|
return temp;
|
||||||
|
|
||||||
case SETTING_SEEKWINDOW_MS:
|
case SETTING_SEEKWINDOW_MS:
|
||||||
pTDStretch->getParameters(NULL, NULL, &temp, NULL);
|
pTDStretch->getParameters(nullptr, nullptr, &temp, nullptr);
|
||||||
return temp;
|
return temp;
|
||||||
|
|
||||||
case SETTING_OVERLAP_MS:
|
case SETTING_OVERLAP_MS:
|
||||||
pTDStretch->getParameters(NULL, NULL, NULL, &temp);
|
pTDStretch->getParameters(nullptr, nullptr, nullptr, &temp);
|
||||||
return temp;
|
return temp;
|
||||||
|
|
||||||
case SETTING_NOMINAL_INPUT_SEQUENCE :
|
case SETTING_NOMINAL_INPUT_SEQUENCE :
|
||||||
return pTDStretch->getInputSampleReq();
|
{
|
||||||
|
int size = pTDStretch->getInputSampleReq();
|
||||||
|
|
||||||
|
#ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
|
||||||
|
if (rate <= 1.0)
|
||||||
|
{
|
||||||
|
// transposing done before timestretch, which impacts latency
|
||||||
|
return (int)(size * rate + 0.5);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
case SETTING_NOMINAL_OUTPUT_SEQUENCE :
|
case SETTING_NOMINAL_OUTPUT_SEQUENCE :
|
||||||
return pTDStretch->getOutputBatchSize();
|
{
|
||||||
|
int size = pTDStretch->getOutputBatchSize();
|
||||||
|
|
||||||
|
if (rate > 1.0)
|
||||||
|
{
|
||||||
|
// transposing done after timestretch, which impacts latency
|
||||||
|
return (int)(size / rate + 0.5);
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SETTING_INITIAL_LATENCY:
|
||||||
|
{
|
||||||
|
double latency = pTDStretch->getLatency();
|
||||||
|
int latency_tr = pRateTransposer->getLatency();
|
||||||
|
|
||||||
|
#ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
|
||||||
|
if (rate <= 1.0)
|
||||||
|
{
|
||||||
|
// transposing done before timestretch, which impacts latency
|
||||||
|
latency = (latency + latency_tr) * rate;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
latency += (double)latency_tr / rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int)(latency + 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
default :
|
default :
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -477,12 +481,12 @@ int SoundTouch::getSetting(int settingId) const
|
||||||
void SoundTouch::clear()
|
void SoundTouch::clear()
|
||||||
{
|
{
|
||||||
samplesExpectedOut = 0;
|
samplesExpectedOut = 0;
|
||||||
|
samplesOutput = 0;
|
||||||
pRateTransposer->clear();
|
pRateTransposer->clear();
|
||||||
pTDStretch->clear();
|
pTDStretch->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// Returns number of samples currently unprocessed.
|
/// Returns number of samples currently unprocessed.
|
||||||
uint SoundTouch::numUnprocessedSamples() const
|
uint SoundTouch::numUnprocessedSamples() const
|
||||||
{
|
{
|
||||||
|
@ -499,7 +503,6 @@ uint SoundTouch::numUnprocessedSamples() const
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
||||||
/// output buffer and removes them from the sample buffer. If there are less than
|
/// output buffer and removes them from the sample buffer. If there are less than
|
||||||
/// 'numsample' samples in the buffer, returns all that available.
|
/// 'numsample' samples in the buffer, returns all that available.
|
||||||
|
@ -524,3 +527,12 @@ uint SoundTouch::receiveSamples(uint maxSamples)
|
||||||
samplesOutput += (long)ret;
|
samplesOutput += (long)ret;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Get ratio between input and output audio durations, useful for calculating
|
||||||
|
/// processed output duration: if you'll process a stream of N samples, then
|
||||||
|
/// you can expect to get out N * getInputOutputSampleRatio() samples.
|
||||||
|
double SoundTouch::getInputOutputSampleRatio()
|
||||||
|
{
|
||||||
|
return 1.0 / (tempo * rate);
|
||||||
|
}
|
||||||
|
|
|
@ -41,13 +41,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2015-09-20 10:38:32 +0300 (Sun, 20 Sep 2015) $
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id: SoundTouch.h 230 2015-09-20 07:38:32Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
@ -79,10 +72,10 @@ namespace soundtouch
|
||||||
{
|
{
|
||||||
|
|
||||||
/// Soundtouch library version string
|
/// Soundtouch library version string
|
||||||
#define SOUNDTOUCH_VERSION "1.9.2"
|
#define SOUNDTOUCH_VERSION "2.3.2"
|
||||||
|
|
||||||
/// SoundTouch library version id
|
/// SoundTouch library version id
|
||||||
#define SOUNDTOUCH_VERSION_ID (10902)
|
#define SOUNDTOUCH_VERSION_ID (20302)
|
||||||
|
|
||||||
//
|
//
|
||||||
// Available setting IDs for the 'setSetting' & 'get_setting' functions:
|
// Available setting IDs for the 'setSetting' & 'get_setting' functions:
|
||||||
|
@ -116,15 +109,17 @@ namespace soundtouch
|
||||||
#define SETTING_OVERLAP_MS 5
|
#define SETTING_OVERLAP_MS 5
|
||||||
|
|
||||||
|
|
||||||
/// Call "getSetting" with this ID to query nominal average processing sequence
|
/// Call "getSetting" with this ID to query processing sequence size in samples.
|
||||||
/// size in samples. This value tells approcimate value how many input samples
|
/// This value gives approximate value of how many input samples you'll need to
|
||||||
/// SoundTouch needs to gather before it does DSP processing run for the sample batch.
|
/// feed into SoundTouch after initial buffering to get out a new batch of
|
||||||
|
/// output samples.
|
||||||
|
///
|
||||||
|
/// This value does not include initial buffering at beginning of a new processing
|
||||||
|
/// stream, use SETTING_INITIAL_LATENCY to get the initial buffering size.
|
||||||
///
|
///
|
||||||
/// Notices:
|
/// Notices:
|
||||||
/// - This is read-only parameter, i.e. setSetting ignores this parameter
|
/// - This is read-only parameter, i.e. setSetting ignores this parameter
|
||||||
/// - Returned value is approximate average value, exact processing batch
|
/// - This parameter value is not constant but change depending on
|
||||||
/// size may wary from time to time
|
|
||||||
/// - This parameter value is not constant but may change depending on
|
|
||||||
/// tempo/pitch/rate/samplerate settings.
|
/// tempo/pitch/rate/samplerate settings.
|
||||||
#define SETTING_NOMINAL_INPUT_SEQUENCE 6
|
#define SETTING_NOMINAL_INPUT_SEQUENCE 6
|
||||||
|
|
||||||
|
@ -135,12 +130,41 @@ namespace soundtouch
|
||||||
///
|
///
|
||||||
/// Notices:
|
/// Notices:
|
||||||
/// - This is read-only parameter, i.e. setSetting ignores this parameter
|
/// - This is read-only parameter, i.e. setSetting ignores this parameter
|
||||||
/// - Returned value is approximate average value, exact processing batch
|
/// - This parameter value is not constant but change depending on
|
||||||
/// size may wary from time to time
|
|
||||||
/// - This parameter value is not constant but may change depending on
|
|
||||||
/// tempo/pitch/rate/samplerate settings.
|
/// tempo/pitch/rate/samplerate settings.
|
||||||
#define SETTING_NOMINAL_OUTPUT_SEQUENCE 7
|
#define SETTING_NOMINAL_OUTPUT_SEQUENCE 7
|
||||||
|
|
||||||
|
|
||||||
|
/// Call "getSetting" with this ID to query initial processing latency, i.e.
|
||||||
|
/// approx. how many samples you'll need to enter to SoundTouch pipeline before
|
||||||
|
/// you can expect to get first batch of ready output samples out.
|
||||||
|
///
|
||||||
|
/// After the first output batch, you can then expect to get approx.
|
||||||
|
/// SETTING_NOMINAL_OUTPUT_SEQUENCE ready samples out for every
|
||||||
|
/// SETTING_NOMINAL_INPUT_SEQUENCE samples that you enter into SoundTouch.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// processing with parameter -tempo=5
|
||||||
|
/// => initial latency = 5509 samples
|
||||||
|
/// input sequence = 4167 samples
|
||||||
|
/// output sequence = 3969 samples
|
||||||
|
///
|
||||||
|
/// Accordingly, you can expect to feed in approx. 5509 samples at beginning of
|
||||||
|
/// the stream, and then you'll get out the first 3969 samples. After that, for
|
||||||
|
/// every approx. 4167 samples that you'll put in, you'll receive again approx.
|
||||||
|
/// 3969 samples out.
|
||||||
|
///
|
||||||
|
/// This also means that average latency during stream processing is
|
||||||
|
/// INITIAL_LATENCY-OUTPUT_SEQUENCE/2, in the above example case 5509-3969/2
|
||||||
|
/// = 3524 samples
|
||||||
|
///
|
||||||
|
/// Notices:
|
||||||
|
/// - This is read-only parameter, i.e. setSetting ignores this parameter
|
||||||
|
/// - This parameter value is not constant but change depending on
|
||||||
|
/// tempo/pitch/rate/samplerate settings.
|
||||||
|
#define SETTING_INITIAL_LATENCY 8
|
||||||
|
|
||||||
|
|
||||||
class SoundTouch : public FIFOProcessor
|
class SoundTouch : public FIFOProcessor
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
@ -185,7 +209,7 @@ protected :
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SoundTouch();
|
SoundTouch();
|
||||||
virtual ~SoundTouch();
|
virtual ~SoundTouch() override;
|
||||||
|
|
||||||
/// Get SoundTouch library version string
|
/// Get SoundTouch library version string
|
||||||
static const char *getVersionString();
|
static const char *getVersionString();
|
||||||
|
@ -228,6 +252,24 @@ public:
|
||||||
/// Sets sample rate.
|
/// Sets sample rate.
|
||||||
void setSampleRate(uint srate);
|
void setSampleRate(uint srate);
|
||||||
|
|
||||||
|
/// Get ratio between input and output audio durations, useful for calculating
|
||||||
|
/// processed output duration: if you'll process a stream of N samples, then
|
||||||
|
/// you can expect to get out N * getInputOutputSampleRatio() samples.
|
||||||
|
///
|
||||||
|
/// This ratio will give accurate target duration ratio for a full audio track,
|
||||||
|
/// given that the the whole track is processed with same processing parameters.
|
||||||
|
///
|
||||||
|
/// If this ratio is applied to calculate intermediate offsets inside a processing
|
||||||
|
/// stream, then this ratio is approximate and can deviate +- some tens of milliseconds
|
||||||
|
/// from ideal offset, yet by end of the audio stream the duration ratio will become
|
||||||
|
/// exact.
|
||||||
|
///
|
||||||
|
/// Example: if processing with parameters "-tempo=15 -pitch=-3", the function
|
||||||
|
/// will return value 0.8695652... Now, if processing an audio stream whose duration
|
||||||
|
/// is exactly one million audio samples, then you can expect the processed
|
||||||
|
/// output duration be 0.869565 * 1000000 = 869565 samples.
|
||||||
|
double getInputOutputSampleRatio();
|
||||||
|
|
||||||
/// Flushes the last samples from the processing pipeline to the output.
|
/// Flushes the last samples from the processing pipeline to the output.
|
||||||
/// Clears also the internal processing buffers.
|
/// Clears also the internal processing buffers.
|
||||||
//
|
//
|
||||||
|
@ -245,7 +287,7 @@ public:
|
||||||
uint numSamples ///< Number of samples in buffer. Notice
|
uint numSamples ///< Number of samples in buffer. Notice
|
||||||
///< that in case of stereo-sound a single sample
|
///< that in case of stereo-sound a single sample
|
||||||
///< contains data for both channels.
|
///< contains data for both channels.
|
||||||
);
|
) override;
|
||||||
|
|
||||||
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
||||||
/// output buffer and removes them from the sample buffer. If there are less than
|
/// output buffer and removes them from the sample buffer. If there are less than
|
||||||
|
@ -254,7 +296,7 @@ public:
|
||||||
/// \return Number of samples returned.
|
/// \return Number of samples returned.
|
||||||
virtual uint receiveSamples(SAMPLETYPE *output, ///< Buffer where to copy output samples.
|
virtual uint receiveSamples(SAMPLETYPE *output, ///< Buffer where to copy output samples.
|
||||||
uint maxSamples ///< How many samples to receive at max.
|
uint maxSamples ///< How many samples to receive at max.
|
||||||
);
|
) override;
|
||||||
|
|
||||||
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
||||||
/// sample buffer without copying them anywhere.
|
/// sample buffer without copying them anywhere.
|
||||||
|
@ -262,16 +304,16 @@ public:
|
||||||
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
|
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
|
||||||
/// with 'ptrBegin' function.
|
/// with 'ptrBegin' function.
|
||||||
virtual uint receiveSamples(uint maxSamples ///< Remove this many samples from the beginning of pipe.
|
virtual uint receiveSamples(uint maxSamples ///< Remove this many samples from the beginning of pipe.
|
||||||
);
|
) override;
|
||||||
|
|
||||||
/// Clears all the samples in the object's output and internal processing
|
/// Clears all the samples in the object's output and internal processing
|
||||||
/// buffers.
|
/// buffers.
|
||||||
virtual void clear();
|
virtual void clear() override;
|
||||||
|
|
||||||
/// Changes a setting controlling the processing system behaviour. See the
|
/// Changes a setting controlling the processing system behaviour. See the
|
||||||
/// 'SETTING_...' defines for available setting ID's.
|
/// 'SETTING_...' defines for available setting ID's.
|
||||||
///
|
///
|
||||||
/// \return 'true' if the setting was succesfully changed
|
/// \return 'true' if the setting was successfully changed
|
||||||
bool setSetting(int settingId, ///< Setting ID number. see SETTING_... defines.
|
bool setSetting(int settingId, ///< Setting ID number. see SETTING_... defines.
|
||||||
int value ///< New setting value.
|
int value ///< New setting value.
|
||||||
);
|
);
|
||||||
|
@ -286,6 +328,11 @@ public:
|
||||||
/// Returns number of samples currently unprocessed.
|
/// Returns number of samples currently unprocessed.
|
||||||
virtual uint numUnprocessedSamples() const;
|
virtual uint numUnprocessedSamples() const;
|
||||||
|
|
||||||
|
/// Return number of channels
|
||||||
|
uint numChannels() const
|
||||||
|
{
|
||||||
|
return channels;
|
||||||
|
}
|
||||||
|
|
||||||
/// Other handy functions that are implemented in the ancestor classes (see
|
/// Other handy functions that are implemented in the ancestor classes (see
|
||||||
/// classes 'FIFOProcessor' and 'FIFOSamplePipe')
|
/// classes 'FIFOProcessor' and 'FIFOSamplePipe')
|
||||||
|
|
|
@ -1,11 +1,17 @@
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
///
|
///
|
||||||
/// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo
|
/// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo
|
||||||
/// while maintaining the original pitch by using a time domain WSOLA-like
|
/// while maintaining the original pitch by using a time domain WSOLA-like
|
||||||
/// method with several performance-increasing tweaks.
|
/// method with several performance-increasing tweaks.
|
||||||
///
|
///
|
||||||
/// Note : MMX optimized functions reside in a separate, platform-specific
|
/// Notes : MMX optimized functions reside in a separate, platform-specific
|
||||||
/// file, e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'
|
/// file, e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'.
|
||||||
|
///
|
||||||
|
/// This source file contains OpenMP optimizations that allow speeding up the
|
||||||
|
/// corss-correlation algorithm by executing it in several threads / CPU cores
|
||||||
|
/// in parallel. See the following article link for more detailed discussion
|
||||||
|
/// about SoundTouch OpenMP optimizations:
|
||||||
|
/// http://www.softwarecoven.com/parallel-computing-in-embedded-mobile-devices
|
||||||
///
|
///
|
||||||
/// Author : Copyright (c) Olli Parviainen
|
/// Author : Copyright (c) Olli Parviainen
|
||||||
/// Author e-mail : oparviai 'at' iki.fi
|
/// Author e-mail : oparviai 'at' iki.fi
|
||||||
|
@ -13,13 +19,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2015-08-09 00:00:15 +0300 (Sun, 09 Aug 2015) $
|
|
||||||
// File revision : $Revision: 1.12 $
|
|
||||||
//
|
|
||||||
// $Id: TDStretch.cpp 226 2015-08-08 21:00:15Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
@ -55,26 +54,6 @@ using namespace soundtouch;
|
||||||
|
|
||||||
#define max(x, y) (((x) > (y)) ? (x) : (y))
|
#define max(x, y) (((x) > (y)) ? (x) : (y))
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************
|
|
||||||
*
|
|
||||||
* Constant definitions
|
|
||||||
*
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
// Table for the hierarchical mixing position seeking algorithm
|
|
||||||
const short _scanOffsets[5][24]={
|
|
||||||
{ 124, 186, 248, 310, 372, 434, 496, 558, 620, 682, 744, 806,
|
|
||||||
868, 930, 992, 1054, 1116, 1178, 1240, 1302, 1364, 1426, 1488, 0},
|
|
||||||
{-100, -75, -50, -25, 25, 50, 75, 100, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
|
||||||
{ -20, -15, -10, -5, 5, 10, 15, 20, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
|
||||||
{ -4, -3, -2, -1, 1, 2, 3, 4, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
|
||||||
{ 121, 114, 97, 114, 98, 105, 108, 32, 104, 99, 117, 111,
|
|
||||||
116, 100, 110, 117, 111, 115, 0, 0, 0, 0, 0, 0}};
|
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
*
|
*
|
||||||
* Implementation of the class 'TDStretch'
|
* Implementation of the class 'TDStretch'
|
||||||
|
@ -87,18 +66,13 @@ TDStretch::TDStretch() : FIFOProcessor(&outputBuffer)
|
||||||
bQuickSeek = false;
|
bQuickSeek = false;
|
||||||
channels = 2;
|
channels = 2;
|
||||||
|
|
||||||
pMidBuffer = NULL;
|
pMidBuffer = nullptr;
|
||||||
pMidBufferUnaligned = NULL;
|
pMidBufferUnaligned = nullptr;
|
||||||
overlapLength = 0;
|
overlapLength = 0;
|
||||||
|
|
||||||
bAutoSeqSetting = true;
|
bAutoSeqSetting = true;
|
||||||
bAutoSeekSetting = true;
|
bAutoSeekSetting = true;
|
||||||
|
|
||||||
maxnorm = 0;
|
|
||||||
maxnormf = 1e8;
|
|
||||||
|
|
||||||
skipFract = 0;
|
|
||||||
|
|
||||||
tempo = 1.0f;
|
tempo = 1.0f;
|
||||||
setParameters(44100, DEFAULT_SEQUENCE_MS, DEFAULT_SEEKWINDOW_MS, DEFAULT_OVERLAP_MS);
|
setParameters(44100, DEFAULT_SEQUENCE_MS, DEFAULT_SEEKWINDOW_MS, DEFAULT_OVERLAP_MS);
|
||||||
setTempo(1.0f);
|
setTempo(1.0f);
|
||||||
|
@ -128,7 +102,12 @@ void TDStretch::setParameters(int aSampleRate, int aSequenceMS,
|
||||||
int aSeekWindowMS, int aOverlapMS)
|
int aSeekWindowMS, int aOverlapMS)
|
||||||
{
|
{
|
||||||
// accept only positive parameter values - if zero or negative, use old values instead
|
// accept only positive parameter values - if zero or negative, use old values instead
|
||||||
if (aSampleRate > 0) this->sampleRate = aSampleRate;
|
if (aSampleRate > 0)
|
||||||
|
{
|
||||||
|
if (aSampleRate > 192000) ST_THROW_RT_ERROR("Error: Excessive samplerate");
|
||||||
|
this->sampleRate = aSampleRate;
|
||||||
|
}
|
||||||
|
|
||||||
if (aOverlapMS > 0) this->overlapMs = aOverlapMS;
|
if (aOverlapMS > 0) this->overlapMs = aOverlapMS;
|
||||||
|
|
||||||
if (aSequenceMS > 0)
|
if (aSequenceMS > 0)
|
||||||
|
@ -164,7 +143,7 @@ void TDStretch::setParameters(int aSampleRate, int aSequenceMS,
|
||||||
|
|
||||||
|
|
||||||
/// Get routine control parameters, see setParameters() function.
|
/// Get routine control parameters, see setParameters() function.
|
||||||
/// Any of the parameters to this function can be NULL, in such case corresponding parameter
|
/// Any of the parameters to this function can be nullptr, in such case corresponding parameter
|
||||||
/// value isn't returned.
|
/// value isn't returned.
|
||||||
void TDStretch::getParameters(int *pSampleRate, int *pSequenceMs, int *pSeekWindowMs, int *pOverlapMs) const
|
void TDStretch::getParameters(int *pSampleRate, int *pSequenceMs, int *pSeekWindowMs, int *pOverlapMs) const
|
||||||
{
|
{
|
||||||
|
@ -219,6 +198,10 @@ void TDStretch::clearInput()
|
||||||
{
|
{
|
||||||
inputBuffer.clear();
|
inputBuffer.clear();
|
||||||
clearMidBuffer();
|
clearMidBuffer();
|
||||||
|
isBeginning = true;
|
||||||
|
maxnorm = 0;
|
||||||
|
maxnormf = 1e8;
|
||||||
|
skipFract = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -297,21 +280,23 @@ int TDStretch::seekBestOverlapPositionFull(const SAMPLETYPE *refPos)
|
||||||
int i;
|
int i;
|
||||||
double norm;
|
double norm;
|
||||||
|
|
||||||
bestCorr = FLT_MIN;
|
bestCorr = -FLT_MAX;
|
||||||
bestOffs = 0;
|
bestOffs = 0;
|
||||||
|
|
||||||
// Scans for the best correlation value by testing each possible position
|
// Scans for the best correlation value by testing each possible position
|
||||||
// over the permitted range.
|
// over the permitted range.
|
||||||
bestCorr = calcCrossCorr(refPos, pMidBuffer, norm);
|
bestCorr = calcCrossCorr(refPos, pMidBuffer, norm);
|
||||||
|
bestCorr = (bestCorr + 0.1) * 0.75;
|
||||||
|
|
||||||
#pragma omp parallel for
|
#pragma omp parallel for
|
||||||
for (i = 1; i < seekLength; i ++)
|
for (i = 1; i < seekLength; i ++)
|
||||||
{
|
{
|
||||||
double corr;
|
double corr;
|
||||||
// Calculates correlation value for the mixing position corresponding to 'i'
|
// Calculates correlation value for the mixing position corresponding to 'i'
|
||||||
#ifdef _OPENMP
|
#if defined(_OPENMP) || defined(ST_SIMD_AVOID_UNALIGNED)
|
||||||
// in parallel OpenMP mode, can't use norm accumulator version as parallel executor won't
|
// in parallel OpenMP mode, can't use norm accumulator version as parallel executor won't
|
||||||
// iterate the loop in sequential order
|
// iterate the loop in sequential order
|
||||||
|
// in SIMD mode, avoid accumulator version to allow avoiding unaligned positions
|
||||||
corr = calcCrossCorr(refPos + channels * i, pMidBuffer, norm);
|
corr = calcCrossCorr(refPos + channels * i, pMidBuffer, norm);
|
||||||
#else
|
#else
|
||||||
// In non-parallel version call "calcCrossCorrAccumulate" that is otherwise same
|
// In non-parallel version call "calcCrossCorrAccumulate" that is otherwise same
|
||||||
|
@ -354,7 +339,7 @@ int TDStretch::seekBestOverlapPositionFull(const SAMPLETYPE *refPos)
|
||||||
// with improved precision
|
// with improved precision
|
||||||
//
|
//
|
||||||
// Based on testing:
|
// Based on testing:
|
||||||
// - This algorithm gives on average 99% as good match as the full algorith
|
// - This algorithm gives on average 99% as good match as the full algorithm
|
||||||
// - this quick seek algorithm finds the best match on ~90% of cases
|
// - this quick seek algorithm finds the best match on ~90% of cases
|
||||||
// - on those 10% of cases when this algorithm doesn't find best match,
|
// - on those 10% of cases when this algorithm doesn't find best match,
|
||||||
// it still finds on average ~90% match vs. the best possible match
|
// it still finds on average ~90% match vs. the best possible match
|
||||||
|
@ -373,12 +358,10 @@ int TDStretch::seekBestOverlapPositionQuick(const SAMPLETYPE *refPos)
|
||||||
|
|
||||||
// note: 'float' types used in this function in case that the platform would need to use software-fp
|
// note: 'float' types used in this function in case that the platform would need to use software-fp
|
||||||
|
|
||||||
bestCorr = FLT_MIN;
|
bestCorr =
|
||||||
bestOffs = SCANWIND;
|
bestCorr2 = -FLT_MAX;
|
||||||
bestCorr2 = FLT_MIN;
|
bestOffs =
|
||||||
bestOffs2 = 0;
|
bestOffs2 = SCANWIND;
|
||||||
|
|
||||||
int best = 0;
|
|
||||||
|
|
||||||
// Scans for the best correlation value by testing each possible position
|
// Scans for the best correlation value by testing each possible position
|
||||||
// over the permitted range. Look for two best matches on the first pass to
|
// over the permitted range. Look for two best matches on the first pass to
|
||||||
|
@ -436,7 +419,6 @@ int TDStretch::seekBestOverlapPositionQuick(const SAMPLETYPE *refPos)
|
||||||
{
|
{
|
||||||
bestCorr = corr;
|
bestCorr = corr;
|
||||||
bestOffs = i;
|
bestOffs = i;
|
||||||
best = 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -458,7 +440,6 @@ int TDStretch::seekBestOverlapPositionQuick(const SAMPLETYPE *refPos)
|
||||||
{
|
{
|
||||||
bestCorr = corr;
|
bestCorr = corr;
|
||||||
bestOffs = i;
|
bestOffs = i;
|
||||||
best = 2;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -515,18 +496,18 @@ void TDStretch::clearCrossCorrState()
|
||||||
void TDStretch::calcSeqParameters()
|
void TDStretch::calcSeqParameters()
|
||||||
{
|
{
|
||||||
// Adjust tempo param according to tempo, so that variating processing sequence length is used
|
// Adjust tempo param according to tempo, so that variating processing sequence length is used
|
||||||
// at varius tempo settings, between the given low...top limits
|
// at various tempo settings, between the given low...top limits
|
||||||
#define AUTOSEQ_TEMPO_LOW 0.5 // auto setting low tempo range (-50%)
|
#define AUTOSEQ_TEMPO_LOW 0.5 // auto setting low tempo range (-50%)
|
||||||
#define AUTOSEQ_TEMPO_TOP 2.0 // auto setting top tempo range (+100%)
|
#define AUTOSEQ_TEMPO_TOP 2.0 // auto setting top tempo range (+100%)
|
||||||
|
|
||||||
// sequence-ms setting values at above low & top tempo
|
// sequence-ms setting values at above low & top tempo
|
||||||
#define AUTOSEQ_AT_MIN 125.0
|
#define AUTOSEQ_AT_MIN 90.0
|
||||||
#define AUTOSEQ_AT_MAX 50.0
|
#define AUTOSEQ_AT_MAX 40.0
|
||||||
#define AUTOSEQ_K ((AUTOSEQ_AT_MAX - AUTOSEQ_AT_MIN) / (AUTOSEQ_TEMPO_TOP - AUTOSEQ_TEMPO_LOW))
|
#define AUTOSEQ_K ((AUTOSEQ_AT_MAX - AUTOSEQ_AT_MIN) / (AUTOSEQ_TEMPO_TOP - AUTOSEQ_TEMPO_LOW))
|
||||||
#define AUTOSEQ_C (AUTOSEQ_AT_MIN - (AUTOSEQ_K) * (AUTOSEQ_TEMPO_LOW))
|
#define AUTOSEQ_C (AUTOSEQ_AT_MIN - (AUTOSEQ_K) * (AUTOSEQ_TEMPO_LOW))
|
||||||
|
|
||||||
// seek-window-ms setting values at above low & top tempoq
|
// seek-window-ms setting values at above low & top tempoq
|
||||||
#define AUTOSEEK_AT_MIN 25.0
|
#define AUTOSEEK_AT_MIN 20.0
|
||||||
#define AUTOSEEK_AT_MAX 15.0
|
#define AUTOSEEK_AT_MAX 15.0
|
||||||
#define AUTOSEEK_K ((AUTOSEEK_AT_MAX - AUTOSEEK_AT_MIN) / (AUTOSEQ_TEMPO_TOP - AUTOSEQ_TEMPO_LOW))
|
#define AUTOSEEK_K ((AUTOSEEK_AT_MAX - AUTOSEEK_AT_MIN) / (AUTOSEQ_TEMPO_TOP - AUTOSEQ_TEMPO_LOW))
|
||||||
#define AUTOSEEK_C (AUTOSEEK_AT_MIN - (AUTOSEEK_K) * (AUTOSEQ_TEMPO_LOW))
|
#define AUTOSEEK_C (AUTOSEEK_AT_MIN - (AUTOSEEK_K) * (AUTOSEQ_TEMPO_LOW))
|
||||||
|
@ -586,9 +567,8 @@ void TDStretch::setTempo(double newTempo)
|
||||||
// Sets the number of channels, 1 = mono, 2 = stereo
|
// Sets the number of channels, 1 = mono, 2 = stereo
|
||||||
void TDStretch::setChannels(int numChannels)
|
void TDStretch::setChannels(int numChannels)
|
||||||
{
|
{
|
||||||
assert(numChannels > 0);
|
if (!verifyNumberOfChannels(numChannels) ||
|
||||||
if (channels == numChannels) return;
|
(channels == numChannels)) return;
|
||||||
// assert(numChannels == 1 || numChannels == 2);
|
|
||||||
|
|
||||||
channels = numChannels;
|
channels = numChannels;
|
||||||
inputBuffer.setChannels(channels);
|
inputBuffer.setChannels(channels);
|
||||||
|
@ -637,7 +617,8 @@ void TDStretch::processNominalTempo()
|
||||||
// the result into 'outputBuffer'
|
// the result into 'outputBuffer'
|
||||||
void TDStretch::processSamples()
|
void TDStretch::processSamples()
|
||||||
{
|
{
|
||||||
int ovlSkip, offset;
|
int ovlSkip;
|
||||||
|
int offset = 0;
|
||||||
int temp;
|
int temp;
|
||||||
|
|
||||||
/* Removed this small optimization - can introduce a click to sound when tempo setting
|
/* Removed this small optimization - can introduce a click to sound when tempo setting
|
||||||
|
@ -654,8 +635,10 @@ void TDStretch::processSamples()
|
||||||
// to form a processing frame.
|
// to form a processing frame.
|
||||||
while ((int)inputBuffer.numSamples() >= sampleReq)
|
while ((int)inputBuffer.numSamples() >= sampleReq)
|
||||||
{
|
{
|
||||||
// If tempo differs from the normal ('SCALE'), scan for the best overlapping
|
if (isBeginning == false)
|
||||||
// position
|
{
|
||||||
|
// apart from the very beginning of the track,
|
||||||
|
// scan for the best overlapping position & do overlap-add
|
||||||
offset = seekBestOverlapPosition(inputBuffer.ptrBegin());
|
offset = seekBestOverlapPosition(inputBuffer.ptrBegin());
|
||||||
|
|
||||||
// Mix the samples in the 'inputBuffer' at position of 'offset' with the
|
// Mix the samples in the 'inputBuffer' at position of 'offset' with the
|
||||||
|
@ -664,25 +647,50 @@ void TDStretch::processSamples()
|
||||||
// (that's in 'midBuffer')
|
// (that's in 'midBuffer')
|
||||||
overlap(outputBuffer.ptrEnd((uint)overlapLength), inputBuffer.ptrBegin(), (uint)offset);
|
overlap(outputBuffer.ptrEnd((uint)overlapLength), inputBuffer.ptrBegin(), (uint)offset);
|
||||||
outputBuffer.putSamples((uint)overlapLength);
|
outputBuffer.putSamples((uint)overlapLength);
|
||||||
|
offset += overlapLength;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Adjust processing offset at beginning of track by not perform initial overlapping
|
||||||
|
// and compensating that in the 'input buffer skip' calculation
|
||||||
|
isBeginning = false;
|
||||||
|
int skip = (int)(tempo * overlapLength + 0.5 * seekLength + 0.5);
|
||||||
|
|
||||||
|
#ifdef ST_SIMD_AVOID_UNALIGNED
|
||||||
|
// in SIMD mode, round the skip amount to value corresponding to aligned memory address
|
||||||
|
if (channels == 1)
|
||||||
|
{
|
||||||
|
skip &= -4;
|
||||||
|
}
|
||||||
|
else if (channels == 2)
|
||||||
|
{
|
||||||
|
skip &= -2;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
skipFract -= skip;
|
||||||
|
if (skipFract <= -nominalSkip)
|
||||||
|
{
|
||||||
|
skipFract = -nominalSkip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ... then copy sequence samples from 'inputBuffer' to output:
|
// ... then copy sequence samples from 'inputBuffer' to output:
|
||||||
|
|
||||||
// length of sequence
|
|
||||||
temp = (seekWindowLength - 2 * overlapLength);
|
|
||||||
|
|
||||||
// crosscheck that we don't have buffer overflow...
|
// crosscheck that we don't have buffer overflow...
|
||||||
if ((int)inputBuffer.numSamples() < (offset + temp + overlapLength * 2))
|
if ((int)inputBuffer.numSamples() < (offset + seekWindowLength - overlapLength))
|
||||||
{
|
{
|
||||||
continue; // just in case, shouldn't really happen
|
continue; // just in case, shouldn't really happen
|
||||||
}
|
}
|
||||||
|
|
||||||
outputBuffer.putSamples(inputBuffer.ptrBegin() + channels * (offset + overlapLength), (uint)temp);
|
// length of sequence
|
||||||
|
temp = (seekWindowLength - 2 * overlapLength);
|
||||||
|
outputBuffer.putSamples(inputBuffer.ptrBegin() + channels * offset, (uint)temp);
|
||||||
|
|
||||||
// Copies the end of the current sequence from 'inputBuffer' to
|
// Copies the end of the current sequence from 'inputBuffer' to
|
||||||
// 'midBuffer' for being mixed with the beginning of the next
|
// 'midBuffer' for being mixed with the beginning of the next
|
||||||
// processing sequence and so on
|
// processing sequence and so on
|
||||||
assert((offset + temp + overlapLength * 2) <= (int)inputBuffer.numSamples());
|
assert((offset + temp + overlapLength) <= (int)inputBuffer.numSamples());
|
||||||
memcpy(pMidBuffer, inputBuffer.ptrBegin() + channels * (offset + temp + overlapLength),
|
memcpy(pMidBuffer, inputBuffer.ptrBegin() + channels * (offset + temp),
|
||||||
channels * sizeof(SAMPLETYPE) * overlapLength);
|
channels * sizeof(SAMPLETYPE) * overlapLength);
|
||||||
|
|
||||||
// Remove the processed samples from the input buffer. Update
|
// Remove the processed samples from the input buffer. Update
|
||||||
|
@ -732,7 +740,7 @@ void TDStretch::acceptNewOverlapLength(int newOverlapLength)
|
||||||
|
|
||||||
// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
// 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.
|
// depending on if we've a MMX/SSE/etc-capable CPU available or not.
|
||||||
void * TDStretch::operator new(size_t s)
|
void * TDStretch::operator new(size_t)
|
||||||
{
|
{
|
||||||
// Notice! don't use "new TDStretch" directly, use "newInstance" to create a new instance instead!
|
// Notice! don't use "new TDStretch" directly, use "newInstance" to create a new instance instead!
|
||||||
ST_THROW_RT_ERROR("Error in TDStretch::new: Don't use 'new TDStretch' directly, use 'newInstance' member instead!");
|
ST_THROW_RT_ERROR("Error in TDStretch::new: Don't use 'new TDStretch' directly, use 'newInstance' member instead!");
|
||||||
|
@ -776,7 +784,7 @@ TDStretch * TDStretch::newInstance()
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Integer arithmetics specific algorithm implementations.
|
// Integer arithmetic specific algorithm implementations.
|
||||||
//
|
//
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@ -802,21 +810,19 @@ void TDStretch::overlapStereo(short *poutput, const short *input) const
|
||||||
|
|
||||||
// Overlaps samples in 'midBuffer' with the samples in 'input'. The 'Multi'
|
// Overlaps samples in 'midBuffer' with the samples in 'input'. The 'Multi'
|
||||||
// version of the routine.
|
// version of the routine.
|
||||||
void TDStretch::overlapMulti(SAMPLETYPE *poutput, const SAMPLETYPE *input) const
|
void TDStretch::overlapMulti(short *poutput, const short *input) const
|
||||||
{
|
{
|
||||||
SAMPLETYPE m1=(SAMPLETYPE)0;
|
short m1;
|
||||||
SAMPLETYPE m2;
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
for (m2 = (SAMPLETYPE)overlapLength; m2; m2 --)
|
for (m1 = 0; m1 < overlapLength; m1 ++)
|
||||||
{
|
{
|
||||||
|
short m2 = (short)(overlapLength - m1);
|
||||||
for (int c = 0; c < channels; c ++)
|
for (int c = 0; c < channels; c ++)
|
||||||
{
|
{
|
||||||
poutput[i] = (input[i] * m1 + pMidBuffer[i] * m2) / overlapLength;
|
poutput[i] = (input[i] * m1 + pMidBuffer[i] * m2) / overlapLength;
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
m1++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -861,26 +867,34 @@ double TDStretch::calcCrossCorr(const short *mixingPos, const short *compare, do
|
||||||
unsigned long lnorm;
|
unsigned long lnorm;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
#ifdef ST_SIMD_AVOID_UNALIGNED
|
||||||
|
// in SIMD mode skip 'mixingPos' positions that aren't aligned to 16-byte boundary
|
||||||
|
if (((ulongptr)mixingPos) & 15) return -1e50;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// hint compiler autovectorization that loop length is divisible by 8
|
||||||
|
int ilength = (channels * overlapLength) & -8;
|
||||||
|
|
||||||
corr = lnorm = 0;
|
corr = lnorm = 0;
|
||||||
// Same routine for stereo and mono. For stereo, unroll loop for better
|
// Same routine for stereo and mono
|
||||||
// efficiency and gives slightly better resolution against rounding.
|
for (i = 0; i < ilength; i += 2)
|
||||||
// For mono it same routine, just unrolls loop by factor of 4
|
|
||||||
for (i = 0; i < channels * overlapLength; i += 4)
|
|
||||||
{
|
{
|
||||||
corr += (mixingPos[i] * compare[i] +
|
corr += (mixingPos[i] * compare[i] +
|
||||||
mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBitsNorm; // notice: do intermediate division here to avoid integer overflow
|
mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBitsNorm;
|
||||||
corr += (mixingPos[i + 2] * compare[i + 2] +
|
|
||||||
mixingPos[i + 3] * compare[i + 3]) >> overlapDividerBitsNorm;
|
|
||||||
lnorm += (mixingPos[i] * mixingPos[i] +
|
lnorm += (mixingPos[i] * mixingPos[i] +
|
||||||
mixingPos[i + 1] * mixingPos[i + 1]) >> overlapDividerBitsNorm; // notice: do intermediate division here to avoid integer overflow
|
mixingPos[i + 1] * mixingPos[i + 1]) >> overlapDividerBitsNorm;
|
||||||
lnorm += (mixingPos[i + 2] * mixingPos[i + 2] +
|
// do intermediate scalings to avoid integer overflow
|
||||||
mixingPos[i + 3] * mixingPos[i + 3]) >> overlapDividerBitsNorm;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lnorm > maxnorm)
|
||||||
|
{
|
||||||
|
// modify 'maxnorm' inside critical section to avoid multi-access conflict if in OpenMP mode
|
||||||
|
#pragma omp critical
|
||||||
if (lnorm > maxnorm)
|
if (lnorm > maxnorm)
|
||||||
{
|
{
|
||||||
maxnorm = lnorm;
|
maxnorm = lnorm;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
||||||
// done using floating point operation
|
// done using floating point operation
|
||||||
norm = (double)lnorm;
|
norm = (double)lnorm;
|
||||||
|
@ -892,9 +906,12 @@ double TDStretch::calcCrossCorr(const short *mixingPos, const short *compare, do
|
||||||
double TDStretch::calcCrossCorrAccumulate(const short *mixingPos, const short *compare, double &norm)
|
double TDStretch::calcCrossCorrAccumulate(const short *mixingPos, const short *compare, double &norm)
|
||||||
{
|
{
|
||||||
long corr;
|
long corr;
|
||||||
unsigned long lnorm;
|
long lnorm;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
// hint compiler autovectorization that loop length is divisible by 8
|
||||||
|
int ilength = (channels * overlapLength) & -8;
|
||||||
|
|
||||||
// cancel first normalizer tap from previous round
|
// cancel first normalizer tap from previous round
|
||||||
lnorm = 0;
|
lnorm = 0;
|
||||||
for (i = 1; i <= channels; i ++)
|
for (i = 1; i <= channels; i ++)
|
||||||
|
@ -903,15 +920,11 @@ double TDStretch::calcCrossCorrAccumulate(const short *mixingPos, const short *c
|
||||||
}
|
}
|
||||||
|
|
||||||
corr = 0;
|
corr = 0;
|
||||||
// Same routine for stereo and mono. For stereo, unroll loop for better
|
// Same routine for stereo and mono.
|
||||||
// efficiency and gives slightly better resolution against rounding.
|
for (i = 0; i < ilength; i += 2)
|
||||||
// For mono it same routine, just unrolls loop by factor of 4
|
|
||||||
for (i = 0; i < channels * overlapLength; i += 4)
|
|
||||||
{
|
{
|
||||||
corr += (mixingPos[i] * compare[i] +
|
corr += (mixingPos[i] * compare[i] +
|
||||||
mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBitsNorm; // notice: do intermediate division here to avoid integer overflow
|
mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBitsNorm;
|
||||||
corr += (mixingPos[i + 2] * compare[i + 2] +
|
|
||||||
mixingPos[i + 3] * compare[i + 3]) >> overlapDividerBitsNorm;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// update normalizer with last samples of this round
|
// update normalizer with last samples of this round
|
||||||
|
@ -936,7 +949,7 @@ double TDStretch::calcCrossCorrAccumulate(const short *mixingPos, const short *c
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Floating point arithmetics specific algorithm implementations.
|
// Floating point arithmetic specific algorithm implementations.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifdef SOUNDTOUCH_FLOAT_SAMPLES
|
#ifdef SOUNDTOUCH_FLOAT_SAMPLES
|
||||||
|
@ -1012,27 +1025,24 @@ void TDStretch::calculateOverlapLength(int overlapInMsec)
|
||||||
/// Calculate cross-correlation
|
/// Calculate cross-correlation
|
||||||
double TDStretch::calcCrossCorr(const float *mixingPos, const float *compare, double &anorm)
|
double TDStretch::calcCrossCorr(const float *mixingPos, const float *compare, double &anorm)
|
||||||
{
|
{
|
||||||
double corr;
|
float corr;
|
||||||
double norm;
|
float norm;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
#ifdef ST_SIMD_AVOID_UNALIGNED
|
||||||
|
// in SIMD mode skip 'mixingPos' positions that aren't aligned to 16-byte boundary
|
||||||
|
if (((ulongptr)mixingPos) & 15) return -1e50;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// hint compiler autovectorization that loop length is divisible by 8
|
||||||
|
int ilength = (channels * overlapLength) & -8;
|
||||||
|
|
||||||
corr = norm = 0;
|
corr = norm = 0;
|
||||||
// Same routine for stereo and mono. For Stereo, unroll by factor of 2.
|
// Same routine for stereo and mono
|
||||||
// For mono it's same routine yet unrollsd by factor of 4.
|
for (i = 0; i < ilength; i ++)
|
||||||
for (i = 0; i < channels * overlapLength; i += 4)
|
|
||||||
{
|
{
|
||||||
corr += mixingPos[i] * compare[i] +
|
corr += mixingPos[i] * compare[i];
|
||||||
mixingPos[i + 1] * compare[i + 1];
|
norm += mixingPos[i] * mixingPos[i];
|
||||||
|
|
||||||
norm += mixingPos[i] * mixingPos[i] +
|
|
||||||
mixingPos[i + 1] * mixingPos[i + 1];
|
|
||||||
|
|
||||||
// unroll the loop for better CPU efficiency:
|
|
||||||
corr += mixingPos[i + 2] * compare[i + 2] +
|
|
||||||
mixingPos[i + 3] * compare[i + 3];
|
|
||||||
|
|
||||||
norm += mixingPos[i + 2] * mixingPos[i + 2] +
|
|
||||||
mixingPos[i + 3] * mixingPos[i + 3];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
anorm = norm;
|
anorm = norm;
|
||||||
|
@ -1043,7 +1053,7 @@ double TDStretch::calcCrossCorr(const float *mixingPos, const float *compare, do
|
||||||
/// Update cross-correlation by accumulating "norm" coefficient by previously calculated value
|
/// Update cross-correlation by accumulating "norm" coefficient by previously calculated value
|
||||||
double TDStretch::calcCrossCorrAccumulate(const float *mixingPos, const float *compare, double &norm)
|
double TDStretch::calcCrossCorrAccumulate(const float *mixingPos, const float *compare, double &norm)
|
||||||
{
|
{
|
||||||
double corr;
|
float corr;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
corr = 0;
|
corr = 0;
|
||||||
|
@ -1054,14 +1064,13 @@ double TDStretch::calcCrossCorrAccumulate(const float *mixingPos, const float *c
|
||||||
norm -= mixingPos[-i] * mixingPos[-i];
|
norm -= mixingPos[-i] * mixingPos[-i];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Same routine for stereo and mono. For Stereo, unroll by factor of 2.
|
// hint compiler autovectorization that loop length is divisible by 8
|
||||||
// For mono it's same routine yet unrollsd by factor of 4.
|
int ilength = (channels * overlapLength) & -8;
|
||||||
for (i = 0; i < channels * overlapLength; i += 4)
|
|
||||||
|
// Same routine for stereo and mono
|
||||||
|
for (i = 0; i < ilength; i ++)
|
||||||
{
|
{
|
||||||
corr += mixingPos[i] * compare[i] +
|
corr += mixingPos[i] * compare[i];
|
||||||
mixingPos[i + 1] * compare[i + 1] +
|
|
||||||
mixingPos[i + 2] * compare[i + 2] +
|
|
||||||
mixingPos[i + 3] * compare[i + 3];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// update normalizer with last samples of this round
|
// update normalizer with last samples of this round
|
||||||
|
|
|
@ -13,13 +13,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2015-08-09 00:00:15 +0300 (Sun, 09 Aug 2015) $
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id: TDStretch.h 226 2015-08-08 21:00:15Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
@ -134,6 +127,7 @@ protected:
|
||||||
bool bQuickSeek;
|
bool bQuickSeek;
|
||||||
bool bAutoSeqSetting;
|
bool bAutoSeqSetting;
|
||||||
bool bAutoSeekSetting;
|
bool bAutoSeekSetting;
|
||||||
|
bool isBeginning;
|
||||||
|
|
||||||
SAMPLETYPE *pMidBuffer;
|
SAMPLETYPE *pMidBuffer;
|
||||||
SAMPLETYPE *pMidBufferUnaligned;
|
SAMPLETYPE *pMidBufferUnaligned;
|
||||||
|
@ -163,7 +157,6 @@ protected:
|
||||||
void calcSeqParameters();
|
void calcSeqParameters();
|
||||||
void adaptNormalizer();
|
void adaptNormalizer();
|
||||||
|
|
||||||
|
|
||||||
/// Changes the tempo of the given sound samples.
|
/// Changes the tempo of the given sound samples.
|
||||||
/// Returns amount of samples returned in the "output" buffer.
|
/// 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 maximum amount of samples that can be returned at a time is set by
|
||||||
|
@ -172,7 +165,7 @@ protected:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TDStretch();
|
TDStretch();
|
||||||
virtual ~TDStretch();
|
virtual ~TDStretch() override;
|
||||||
|
|
||||||
/// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
/// 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.
|
/// depending on if we've a MMX/SSE/etc-capable CPU available or not.
|
||||||
|
@ -194,7 +187,7 @@ public:
|
||||||
void setTempo(double newTempo);
|
void setTempo(double newTempo);
|
||||||
|
|
||||||
/// Returns nonzero if there aren't any samples available for outputting.
|
/// Returns nonzero if there aren't any samples available for outputting.
|
||||||
virtual void clear();
|
virtual void clear() override;
|
||||||
|
|
||||||
/// Clears the input buffer
|
/// Clears the input buffer
|
||||||
void clearInput();
|
void clearInput();
|
||||||
|
@ -224,7 +217,7 @@ public:
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Get routine control parameters, see setParameters() function.
|
/// Get routine control parameters, see setParameters() function.
|
||||||
/// Any of the parameters to this function can be NULL, in such case corresponding parameter
|
/// Any of the parameters to this function can be nullptr, in such case corresponding parameter
|
||||||
/// value isn't returned.
|
/// value isn't returned.
|
||||||
void getParameters(int *pSampleRate, int *pSequenceMs, int *pSeekWindowMs, int *pOverlapMs) const;
|
void getParameters(int *pSampleRate, int *pSequenceMs, int *pSeekWindowMs, int *pOverlapMs) const;
|
||||||
|
|
||||||
|
@ -234,7 +227,7 @@ public:
|
||||||
const SAMPLETYPE *samples, ///< Input sample data
|
const SAMPLETYPE *samples, ///< Input sample data
|
||||||
uint numSamples ///< Number of samples in 'samples' so that one sample
|
uint numSamples ///< Number of samples in 'samples' so that one sample
|
||||||
///< contains both channels if stereo
|
///< contains both channels if stereo
|
||||||
);
|
) override;
|
||||||
|
|
||||||
/// return nominal input sample requirement for triggering a processing batch
|
/// return nominal input sample requirement for triggering a processing batch
|
||||||
int getInputSampleReq() const
|
int getInputSampleReq() const
|
||||||
|
@ -247,8 +240,13 @@ public:
|
||||||
{
|
{
|
||||||
return seekWindowLength - overlapLength;
|
return seekWindowLength - overlapLength;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
|
/// return approximate initial input-output latency
|
||||||
|
int getLatency() const
|
||||||
|
{
|
||||||
|
return sampleReq;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// Implementation-specific class declarations:
|
// Implementation-specific class declarations:
|
||||||
|
@ -258,10 +256,10 @@ public:
|
||||||
class TDStretchMMX : public TDStretch
|
class TDStretchMMX : public TDStretch
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
double calcCrossCorr(const short *mixingPos, const short *compare, double &norm);
|
double calcCrossCorr(const short *mixingPos, const short *compare, double &norm) override;
|
||||||
double calcCrossCorrAccumulate(const short *mixingPos, const short *compare, double &norm);
|
double calcCrossCorrAccumulate(const short *mixingPos, const short *compare, double &norm) override;
|
||||||
virtual void overlapStereo(short *output, const short *input) const;
|
virtual void overlapStereo(short *output, const short *input) const override;
|
||||||
virtual void clearCrossCorrState();
|
virtual void clearCrossCorrState() override;
|
||||||
};
|
};
|
||||||
#endif /// SOUNDTOUCH_ALLOW_MMX
|
#endif /// SOUNDTOUCH_ALLOW_MMX
|
||||||
|
|
||||||
|
@ -271,8 +269,8 @@ public:
|
||||||
class TDStretchSSE : public TDStretch
|
class TDStretchSSE : public TDStretch
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
double calcCrossCorr(const float *mixingPos, const float *compare, double &norm);
|
double calcCrossCorr(const float *mixingPos, const float *compare, double &norm) override;
|
||||||
double calcCrossCorrAccumulate(const float *mixingPos, const float *compare, double &norm);
|
double calcCrossCorrAccumulate(const float *mixingPos, const float *compare, double &norm) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /// SOUNDTOUCH_ALLOW_SSE
|
#endif /// SOUNDTOUCH_ALLOW_SSE
|
||||||
|
|
|
@ -12,13 +12,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2008-02-10 18:26:55 +0200 (Sun, 10 Feb 2008) $
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id: cpu_detect.h 11 2008-02-10 16:26:55Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
@ -51,8 +44,6 @@
|
||||||
#define SUPPORT_SSE 0x0008
|
#define SUPPORT_SSE 0x0008
|
||||||
#define SUPPORT_SSE2 0x0010
|
#define SUPPORT_SSE2 0x0010
|
||||||
|
|
||||||
using namespace soundtouch;
|
|
||||||
|
|
||||||
/// Checks which instruction set extensions are supported by the CPU.
|
/// Checks which instruction set extensions are supported by the CPU.
|
||||||
///
|
///
|
||||||
/// \return A bitmask of supported extensions, see SUPPORT_... defines.
|
/// \return A bitmask of supported extensions, see SUPPORT_... defines.
|
||||||
|
|
|
@ -11,13 +11,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2014-01-07 20:24:28 +0200 (Tue, 07 Jan 2014) $
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id: cpu_detect_x86.cpp 183 2014-01-07 18:24:28Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
@ -75,7 +68,6 @@ void disableExtensions(uint dwDisableMask)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// Checks which instruction set extensions are supported by the CPU.
|
/// Checks which instruction set extensions are supported by the CPU.
|
||||||
uint detectCPUextensions(void)
|
uint detectCPUextensions(void)
|
||||||
{
|
{
|
||||||
|
|
|
@ -20,13 +20,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2015-08-09 00:00:15 +0300 (Sun, 09 Aug 2015) $
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id: mmx_optimized.cpp 226 2015-08-08 21:00:15Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
@ -123,10 +116,15 @@ double TDStretchMMX::calcCrossCorr(const short *pV1, const short *pV2, double &d
|
||||||
// Clear MMS state
|
// Clear MMS state
|
||||||
_m_empty();
|
_m_empty();
|
||||||
|
|
||||||
|
if (norm > (long)maxnorm)
|
||||||
|
{
|
||||||
|
// modify 'maxnorm' inside critical section to avoid multi-access conflict if in OpenMP mode
|
||||||
|
#pragma omp critical
|
||||||
if (norm > (long)maxnorm)
|
if (norm > (long)maxnorm)
|
||||||
{
|
{
|
||||||
maxnorm = norm;
|
maxnorm = norm;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
||||||
// done using floating point operation
|
// done using floating point operation
|
||||||
|
@ -219,7 +217,6 @@ void TDStretchMMX::clearCrossCorrState()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// MMX-optimized version of the function overlapStereo
|
// MMX-optimized version of the function overlapStereo
|
||||||
void TDStretchMMX::overlapStereo(short *output, const short *input) const
|
void TDStretchMMX::overlapStereo(short *output, const short *input) const
|
||||||
{
|
{
|
||||||
|
@ -297,8 +294,8 @@ void TDStretchMMX::overlapStereo(short *output, const short *input) const
|
||||||
|
|
||||||
FIRFilterMMX::FIRFilterMMX() : FIRFilter()
|
FIRFilterMMX::FIRFilterMMX() : FIRFilter()
|
||||||
{
|
{
|
||||||
filterCoeffsAlign = NULL;
|
filterCoeffsAlign = nullptr;
|
||||||
filterCoeffsUnalign = NULL;
|
filterCoeffsUnalign = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -335,7 +332,6 @@ void FIRFilterMMX::setCoefficients(const short *coeffs, uint newLength, uint uRe
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// mmx-optimized version of the filter routine for stereo sound
|
// mmx-optimized version of the filter routine for stereo sound
|
||||||
uint FIRFilterMMX::evaluateFilterStereo(short *dest, const short *src, uint numSamples) const
|
uint FIRFilterMMX::evaluateFilterStereo(short *dest, const short *src, uint numSamples) const
|
||||||
{
|
{
|
||||||
|
@ -392,4 +388,9 @@ uint FIRFilterMMX::evaluateFilterStereo(short *dest, const short *src, uint numS
|
||||||
return (numSamples & 0xfffffffe) - length;
|
return (numSamples & 0xfffffffe) - length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
// workaround to not complain about empty module
|
||||||
|
bool _dontcomplain_mmx_empty;
|
||||||
|
|
||||||
#endif // SOUNDTOUCH_ALLOW_MMX
|
#endif // SOUNDTOUCH_ALLOW_MMX
|
||||||
|
|
|
@ -23,13 +23,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2015-08-09 00:00:15 +0300 (Sun, 09 Aug 2015) $
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id: sse_optimized.cpp 226 2015-08-08 21:00:15Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
@ -87,7 +80,7 @@ double TDStretchSSE::calcCrossCorr(const float *pV1, const float *pV2, double &a
|
||||||
// Compile-time define SOUNDTOUCH_ALLOW_NONEXACT_SIMD_OPTIMIZATION is provided
|
// Compile-time define SOUNDTOUCH_ALLOW_NONEXACT_SIMD_OPTIMIZATION is provided
|
||||||
// for choosing if this little cheating is allowed.
|
// for choosing if this little cheating is allowed.
|
||||||
|
|
||||||
#ifdef SOUNDTOUCH_ALLOW_NONEXACT_SIMD_OPTIMIZATION
|
#ifdef ST_SIMD_AVOID_UNALIGNED
|
||||||
// Little cheating allowed, return valid correlation only for
|
// Little cheating allowed, return valid correlation only for
|
||||||
// aligned locations, meaning every second round for stereo sound.
|
// aligned locations, meaning every second round for stereo sound.
|
||||||
|
|
||||||
|
@ -202,16 +195,16 @@ double TDStretchSSE::calcCrossCorrAccumulate(const float *pV1, const float *pV2,
|
||||||
|
|
||||||
FIRFilterSSE::FIRFilterSSE() : FIRFilter()
|
FIRFilterSSE::FIRFilterSSE() : FIRFilter()
|
||||||
{
|
{
|
||||||
filterCoeffsAlign = NULL;
|
filterCoeffsAlign = nullptr;
|
||||||
filterCoeffsUnalign = NULL;
|
filterCoeffsUnalign = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
FIRFilterSSE::~FIRFilterSSE()
|
FIRFilterSSE::~FIRFilterSSE()
|
||||||
{
|
{
|
||||||
delete[] filterCoeffsUnalign;
|
delete[] filterCoeffsUnalign;
|
||||||
filterCoeffsAlign = NULL;
|
filterCoeffsAlign = nullptr;
|
||||||
filterCoeffsUnalign = NULL;
|
filterCoeffsUnalign = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -252,10 +245,10 @@ uint FIRFilterSSE::evaluateFilterStereo(float *dest, const float *source, uint n
|
||||||
|
|
||||||
if (count < 2) return 0;
|
if (count < 2) return 0;
|
||||||
|
|
||||||
assert(source != NULL);
|
assert(source != nullptr);
|
||||||
assert(dest != NULL);
|
assert(dest != nullptr);
|
||||||
assert((length % 8) == 0);
|
assert((length % 8) == 0);
|
||||||
assert(filterCoeffsAlign != NULL);
|
assert(filterCoeffsAlign != nullptr);
|
||||||
assert(((ulongptr)filterCoeffsAlign) % 16 == 0);
|
assert(((ulongptr)filterCoeffsAlign) % 16 == 0);
|
||||||
|
|
||||||
// filter is evaluated for two stereo samples with each iteration, thus use of 'j += 2'
|
// filter is evaluated for two stereo samples with each iteration, thus use of 'j += 2'
|
||||||
|
|
Loading…
Reference in New Issue