Reorganise into faster (R2) and finer (R3)

This commit is contained in:
Chris Cannam
2022-05-19 13:34:51 +01:00
parent e9264ae909
commit e9ad04e2b4
66 changed files with 89 additions and 127 deletions

View File

@@ -0,0 +1,72 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band Library
An audio time-stretching and pitch-shifting library.
Copyright 2007-2022 Particular Programs Ltd.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
Alternatively, if you have a valid commercial licence for the
Rubber Band Library obtained by agreement with the copyright
holders, you may redistribute and/or modify it under the terms
described in that licence.
If you wish to distribute code using the Rubber Band Library
under terms other than those of the GNU General Public License,
you must obtain a valid commercial licence before doing so.
*/
#include "AudioCurveCalculator.h"
#include <iostream>
namespace RubberBand
{
static const int MaxPerceivedFreq = 16000;
AudioCurveCalculator::AudioCurveCalculator(Parameters parameters) :
m_sampleRate(parameters.sampleRate),
m_fftSize(parameters.fftSize)
{
recalculateLastPerceivedBin();
}
AudioCurveCalculator::~AudioCurveCalculator()
{
}
void
AudioCurveCalculator::setSampleRate(int newRate)
{
m_sampleRate = newRate;
recalculateLastPerceivedBin();
}
void
AudioCurveCalculator::setFftSize(int newSize)
{
m_fftSize = newSize;
recalculateLastPerceivedBin();
}
void
AudioCurveCalculator::recalculateLastPerceivedBin()
{
if (m_sampleRate == 0) {
m_lastPerceivedBin = 0;
return;
}
m_lastPerceivedBin = ((MaxPerceivedFreq * m_fftSize) / m_sampleRate);
if (m_lastPerceivedBin > m_fftSize/2) {
m_lastPerceivedBin = m_fftSize/2;
}
}
}

View File

@@ -0,0 +1,134 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band Library
An audio time-stretching and pitch-shifting library.
Copyright 2007-2022 Particular Programs Ltd.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
Alternatively, if you have a valid commercial licence for the
Rubber Band Library obtained by agreement with the copyright
holders, you may redistribute and/or modify it under the terms
described in that licence.
If you wish to distribute code using the Rubber Band Library
under terms other than those of the GNU General Public License,
you must obtain a valid commercial licence before doing so.
*/
#ifndef RUBBERBAND_AUDIO_CURVE_CALCULATOR_H
#define RUBBERBAND_AUDIO_CURVE_CALCULATOR_H
#include <sys/types.h>
#include "../common/sysutils.h"
namespace RubberBand
{
/**
* AudioCurveCalculator turns a sequence of audio "columns" --
* short-time spectrum magnitude blocks -- into a sequence of numbers
* representing some quality of the input such as power or likelihood
* of an onset occurring.
*
* These are typically low-level building-blocks: AudioCurveCalculator
* is a simple causal interface in which each input column corresponds
* to exactly one output value which is returned immediately. They
* have far less power (because of the causal interface and
* magnitude-only input) and flexibility (because of the limited
* return types) than for example the Vamp plugin interface.
*
* AudioCurveCalculator implementations typically remember the history
* of their processing data, and the caller must call reset() before
* resynchronising to an unrelated piece of input audio.
*/
class AudioCurveCalculator
{
public:
struct Parameters {
Parameters(int _sampleRate, int _fftSize) :
sampleRate(_sampleRate),
fftSize(_fftSize)
{ }
int sampleRate;
int fftSize;
};
AudioCurveCalculator(Parameters parameters);
virtual ~AudioCurveCalculator();
int getSampleRate() const { return m_sampleRate; }
int getFftSize() const { return m_fftSize; }
virtual void setSampleRate(int newRate);
virtual void setFftSize(int newSize);
Parameters getParameters() const {
return Parameters(m_sampleRate, m_fftSize);
}
void setParameters(Parameters p) {
setSampleRate(p.sampleRate);
setFftSize(p.fftSize);
}
// You may not mix calls to the various process functions on a
// given instance
/**
* Process the given magnitude spectrum block and return the curve
* value for it. The mag input contains (fftSize/2 + 1) values
* corresponding to the magnitudes of the complex FFT output bins
* for a windowed input of size fftSize. The hop (expressed in
* time-domain audio samples) from the previous to the current
* input block is given by increment.
*/
virtual float processFloat(const float *R__ mag, int increment) = 0;
/**
* Process the given magnitude spectrum block and return the curve
* value for it. The mag input contains (fftSize/2 + 1) values
* corresponding to the magnitudes of the complex FFT output bins
* for a windowed input of size fftSize. The hop (expressed in
* time-domain audio samples) from the previous to the current
* input block is given by increment.
*/
virtual double processDouble(const double *R__ mag, int increment) = 0;
/**
* Obtain a confidence for the curve value (if applicable). A
* value of 1.0 indicates perfect confidence in the curve
* calculation, 0.0 indicates none.
*/
virtual double getConfidence() const { return 1.0; }
/**
* Reset the calculator, forgetting the history of the audio input
* so far.
*/
virtual void reset() = 0;
/**
* If the output of this calculator has a known unit, return it as
* text. For example, "Hz" or "V".
*/
virtual const char *getUnit() const { return ""; }
protected:
int m_sampleRate;
int m_fftSize;
int m_lastPerceivedBin;
void recalculateLastPerceivedBin();
};
}
#endif

View File

@@ -0,0 +1,167 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band Library
An audio time-stretching and pitch-shifting library.
Copyright 2007-2022 Particular Programs Ltd.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
Alternatively, if you have a valid commercial licence for the
Rubber Band Library obtained by agreement with the copyright
holders, you may redistribute and/or modify it under the terms
described in that licence.
If you wish to distribute code using the Rubber Band Library
under terms other than those of the GNU General Public License,
you must obtain a valid commercial licence before doing so.
*/
#include "CompoundAudioCurve.h"
#include "../common/MovingMedian.h"
#include <iostream>
namespace RubberBand
{
CompoundAudioCurve::CompoundAudioCurve(Parameters parameters) :
AudioCurveCalculator(parameters),
m_percussive(parameters),
m_hf(parameters),
m_hfFilter(new MovingMedian<double>(19, 85)),
m_hfDerivFilter(new MovingMedian<double>(19, 90)),
m_type(CompoundDetector),
m_lastHf(0.0),
m_lastResult(0.0),
m_risingCount(0)
{
}
CompoundAudioCurve::~CompoundAudioCurve()
{
delete m_hfFilter;
delete m_hfDerivFilter;
}
void
CompoundAudioCurve::setType(Type type)
{
m_type = type;
}
void
CompoundAudioCurve::reset()
{
m_percussive.reset();
m_hf.reset();
m_hfFilter->reset();
m_hfDerivFilter->reset();
m_lastHf = 0.0;
m_lastResult = 0.0;
}
void
CompoundAudioCurve::setFftSize(int newSize)
{
m_percussive.setFftSize(newSize);
m_hf.setFftSize(newSize);
m_fftSize = newSize;
m_lastHf = 0.0;
m_lastResult = 0.0;
}
float
CompoundAudioCurve::processFloat(const float *R__ mag, int increment)
{
float percussive = 0.f;
float hf = 0.f;
switch (m_type) {
case PercussiveDetector:
percussive = m_percussive.processFloat(mag, increment);
break;
case CompoundDetector:
percussive = m_percussive.processFloat(mag, increment);
hf = m_hf.processFloat(mag, increment);
break;
case SoftDetector:
hf = m_hf.processFloat(mag, increment);
break;
}
return processFiltering(percussive, hf);
}
double
CompoundAudioCurve::processDouble(const double *R__ mag, int increment)
{
double percussive = 0.0;
double hf = 0.0;
switch (m_type) {
case PercussiveDetector:
percussive = m_percussive.processDouble(mag, increment);
break;
case CompoundDetector:
percussive = m_percussive.processDouble(mag, increment);
hf = m_hf.processDouble(mag, increment);
break;
case SoftDetector:
hf = m_hf.processDouble(mag, increment);
break;
}
return processFiltering(percussive, hf);
}
double
CompoundAudioCurve::processFiltering(double percussive, double hf)
{
if (m_type == PercussiveDetector) {
return percussive;
}
double rv = 0.f;
double hfDeriv = hf - m_lastHf;
m_hfFilter->push(hf);
m_hfDerivFilter->push(hfDeriv);
double hfFiltered = m_hfFilter->get();
double hfDerivFiltered = m_hfDerivFilter->get();
m_lastHf = hf;
double result = 0.f;
double hfExcess = hf - hfFiltered;
if (hfExcess > 0.0) {
result = hfDeriv - hfDerivFiltered;
}
if (result < m_lastResult) {
if (m_risingCount > 3 && m_lastResult > 0) rv = 0.5;
m_risingCount = 0;
} else {
m_risingCount ++;
}
if (m_type == CompoundDetector) {
if (percussive > 0.35 && percussive > rv) {
rv = percussive;
}
}
m_lastResult = result;
return rv;
}
}

View File

@@ -0,0 +1,73 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band Library
An audio time-stretching and pitch-shifting library.
Copyright 2007-2022 Particular Programs Ltd.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
Alternatively, if you have a valid commercial licence for the
Rubber Band Library obtained by agreement with the copyright
holders, you may redistribute and/or modify it under the terms
described in that licence.
If you wish to distribute code using the Rubber Band Library
under terms other than those of the GNU General Public License,
you must obtain a valid commercial licence before doing so.
*/
#ifndef RUBBERBAND_COMPOUND_AUDIO_CURVE_H
#define RUBBERBAND_COMPOUND_AUDIO_CURVE_H
#include "PercussiveAudioCurve.h"
#include "HighFrequencyAudioCurve.h"
#include "../common/SampleFilter.h"
namespace RubberBand
{
class CompoundAudioCurve : public AudioCurveCalculator
{
public:
CompoundAudioCurve(Parameters parameters);
virtual ~CompoundAudioCurve();
enum Type {
PercussiveDetector,
CompoundDetector,
SoftDetector
};
virtual void setType(Type); // default is CompoundDetector
virtual void setFftSize(int newSize);
virtual float processFloat(const float *R__ mag, int increment);
virtual double processDouble(const double *R__ mag, int increment);
virtual void reset();
protected:
PercussiveAudioCurve m_percussive;
HighFrequencyAudioCurve m_hf;
SampleFilter<double> *m_hfFilter;
SampleFilter<double> *m_hfDerivFilter;
Type m_type;
double m_lastHf;
double m_lastResult;
int m_risingCount;
double processFiltering(double percussive, double hf);
};
}
#endif

View File

@@ -0,0 +1,73 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band Library
An audio time-stretching and pitch-shifting library.
Copyright 2007-2022 Particular Programs Ltd.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
Alternatively, if you have a valid commercial licence for the
Rubber Band Library obtained by agreement with the copyright
holders, you may redistribute and/or modify it under the terms
described in that licence.
If you wish to distribute code using the Rubber Band Library
under terms other than those of the GNU General Public License,
you must obtain a valid commercial licence before doing so.
*/
#include "HighFrequencyAudioCurve.h"
namespace RubberBand
{
HighFrequencyAudioCurve::HighFrequencyAudioCurve(Parameters parameters) :
AudioCurveCalculator(parameters)
{
}
HighFrequencyAudioCurve::~HighFrequencyAudioCurve()
{
}
void
HighFrequencyAudioCurve::reset()
{
}
float
HighFrequencyAudioCurve::processFloat(const float *R__ mag, int)
{
float result = 0.0;
const int sz = m_lastPerceivedBin;
for (int n = 0; n <= sz; ++n) {
result = result + mag[n] * n;
}
return result;
}
double
HighFrequencyAudioCurve::processDouble(const double *R__ mag, int)
{
double result = 0.0;
const int sz = m_lastPerceivedBin;
for (int n = 0; n <= sz; ++n) {
result = result + mag[n] * n;
}
return result;
}
}

View File

@@ -0,0 +1,47 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band Library
An audio time-stretching and pitch-shifting library.
Copyright 2007-2022 Particular Programs Ltd.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
Alternatively, if you have a valid commercial licence for the
Rubber Band Library obtained by agreement with the copyright
holders, you may redistribute and/or modify it under the terms
described in that licence.
If you wish to distribute code using the Rubber Band Library
under terms other than those of the GNU General Public License,
you must obtain a valid commercial licence before doing so.
*/
#ifndef RUBBERBAND_HIGHFREQUENCY_AUDIO_CURVE_H
#define RUBBERBAND_HIGHFREQUENCY_AUDIO_CURVE_H
#include "AudioCurveCalculator.h"
namespace RubberBand
{
class HighFrequencyAudioCurve : public AudioCurveCalculator
{
public:
HighFrequencyAudioCurve(Parameters parameters);
virtual ~HighFrequencyAudioCurve();
virtual float processFloat(const float *R__ mag, int increment);
virtual double processDouble(const double *R__ mag, int increment);
virtual void reset();
virtual const char *getUnit() const { return "Vbin"; }
};
}
#endif

View File

@@ -0,0 +1,114 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band Library
An audio time-stretching and pitch-shifting library.
Copyright 2007-2022 Particular Programs Ltd.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
Alternatively, if you have a valid commercial licence for the
Rubber Band Library obtained by agreement with the copyright
holders, you may redistribute and/or modify it under the terms
described in that licence.
If you wish to distribute code using the Rubber Band Library
under terms other than those of the GNU General Public License,
you must obtain a valid commercial licence before doing so.
*/
#include "PercussiveAudioCurve.h"
#include "../common/Allocators.h"
#include "../common/VectorOps.h"
#include <cmath>
#include <iostream>
namespace RubberBand
{
PercussiveAudioCurve::PercussiveAudioCurve(Parameters parameters) :
AudioCurveCalculator(parameters)
{
m_prevMag = allocate_and_zero<double>(m_fftSize/2 + 1);
}
PercussiveAudioCurve::~PercussiveAudioCurve()
{
deallocate(m_prevMag);
}
void
PercussiveAudioCurve::reset()
{
v_zero(m_prevMag, m_fftSize/2 + 1);
}
void
PercussiveAudioCurve::setFftSize(int newSize)
{
m_prevMag = reallocate(m_prevMag, m_fftSize/2 + 1, newSize/2 + 1);
AudioCurveCalculator::setFftSize(newSize);
reset();
}
float
PercussiveAudioCurve::processFloat(const float *R__ mag, int)
{
static float threshold = powf(10.f, 0.15f); // 3dB rise in square of magnitude
static float zeroThresh = powf(10.f, -8);
int count = 0;
int nonZeroCount = 0;
const int sz = m_lastPerceivedBin;
for (int n = 1; n <= sz; ++n) {
float v = 0.f;
if (m_prevMag[n] > zeroThresh) v = mag[n] / m_prevMag[n];
else if (mag[n] > zeroThresh) v = threshold;
bool above = (v >= threshold);
if (above) ++count;
if (mag[n] > zeroThresh) ++nonZeroCount;
}
v_convert(m_prevMag, mag, sz + 1);
if (nonZeroCount == 0) return 0;
else return float(count) / float(nonZeroCount);
}
double
PercussiveAudioCurve::processDouble(const double *R__ mag, int)
{
static double threshold = pow(10., 0.15); // 3dB rise in square of magnitude
static double zeroThresh = pow(10., -8);
int count = 0;
int nonZeroCount = 0;
const int sz = m_lastPerceivedBin;
for (int n = 1; n <= sz; ++n) {
double v = 0.0;
if (m_prevMag[n] > zeroThresh) v = mag[n] / m_prevMag[n];
else if (mag[n] > zeroThresh) v = threshold;
bool above = (v >= threshold);
if (above) ++count;
if (mag[n] > zeroThresh) ++nonZeroCount;
}
v_copy(m_prevMag, mag, sz + 1);
if (nonZeroCount == 0) return 0;
else return double(count) / double(nonZeroCount);
}
}

View File

@@ -0,0 +1,54 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band Library
An audio time-stretching and pitch-shifting library.
Copyright 2007-2022 Particular Programs Ltd.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
Alternatively, if you have a valid commercial licence for the
Rubber Band Library obtained by agreement with the copyright
holders, you may redistribute and/or modify it under the terms
described in that licence.
If you wish to distribute code using the Rubber Band Library
under terms other than those of the GNU General Public License,
you must obtain a valid commercial licence before doing so.
*/
#ifndef RUBBERBAND_PERCUSSIVE_AUDIO_CURVE_H
#define RUBBERBAND_PERCUSSIVE_AUDIO_CURVE_H
#include "AudioCurveCalculator.h"
namespace RubberBand
{
class PercussiveAudioCurve : public AudioCurveCalculator
{
public:
PercussiveAudioCurve(Parameters parameters);
virtual ~PercussiveAudioCurve();
virtual void setFftSize(int newSize);
virtual float processFloat(const float *R__ mag, int increment);
virtual double processDouble(const double *R__ mag, int increment);
virtual void reset();
virtual const char *getUnit() const { return "bin/total"; }
protected:
double *R__ m_prevMag;
};
}
#endif

View File

@@ -0,0 +1,73 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band Library
An audio time-stretching and pitch-shifting library.
Copyright 2007-2022 Particular Programs Ltd.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
Alternatively, if you have a valid commercial licence for the
Rubber Band Library obtained by agreement with the copyright
holders, you may redistribute and/or modify it under the terms
described in that licence.
If you wish to distribute code using the Rubber Band Library
under terms other than those of the GNU General Public License,
you must obtain a valid commercial licence before doing so.
*/
#include "SilentAudioCurve.h"
#include <cmath>
namespace RubberBand
{
SilentAudioCurve::SilentAudioCurve(Parameters parameters) :
AudioCurveCalculator(parameters)
{
}
SilentAudioCurve::~SilentAudioCurve()
{
}
void
SilentAudioCurve::reset()
{
}
float
SilentAudioCurve::processFloat(const float *R__ mag, int)
{
const int hs = m_lastPerceivedBin;
static float threshold = powf(10.f, -6);
for (int i = 0; i <= hs; ++i) {
if (mag[i] > threshold) return 0.f;
}
return 1.f;
}
double
SilentAudioCurve::processDouble(const double *R__ mag, int)
{
const int hs = m_lastPerceivedBin;
static double threshold = pow(10.0, -6);
for (int i = 0; i <= hs; ++i) {
if (mag[i] > threshold) return 0.f;
}
return 1.f;
}
}

View File

@@ -0,0 +1,46 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band Library
An audio time-stretching and pitch-shifting library.
Copyright 2007-2022 Particular Programs Ltd.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
Alternatively, if you have a valid commercial licence for the
Rubber Band Library obtained by agreement with the copyright
holders, you may redistribute and/or modify it under the terms
described in that licence.
If you wish to distribute code using the Rubber Band Library
under terms other than those of the GNU General Public License,
you must obtain a valid commercial licence before doing so.
*/
#ifndef RUBBERBAND_SILENT_AUDIO_CURVE_H
#define RUBBERBAND_SILENT_AUDIO_CURVE_H
#include "AudioCurveCalculator.h"
namespace RubberBand
{
class SilentAudioCurve : public AudioCurveCalculator
{
public:
SilentAudioCurve(Parameters parameters);
virtual ~SilentAudioCurve();
virtual float processFloat(const float *R__ mag, int increment);
virtual double processDouble(const double *R__ mag, int increment);
virtual void reset();
virtual const char *getUnit() const { return "bool"; }
};
}
#endif

155
src/faster/SincWindow.h Normal file
View File

@@ -0,0 +1,155 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band Library
An audio time-stretching and pitch-shifting library.
Copyright 2007-2022 Particular Programs Ltd.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
Alternatively, if you have a valid commercial licence for the
Rubber Band Library obtained by agreement with the copyright
holders, you may redistribute and/or modify it under the terms
described in that licence.
If you wish to distribute code using the Rubber Band Library
under terms other than those of the GNU General Public License,
you must obtain a valid commercial licence before doing so.
*/
#ifndef RUBBERBAND_SINC_WINDOW_H
#define RUBBERBAND_SINC_WINDOW_H
#include <cmath>
#include <iostream>
#include <cstdlib>
#include <map>
#include "../common/sysutils.h"
#include "../common/VectorOps.h"
#include "../common/Allocators.h"
namespace RubberBand {
template <typename T>
class SincWindow
{
public:
/**
* Construct a sinc windower which produces a window of size n
* containing the values of sinc(x) with x=0 at index n/2, such
* that the distance from -pi to pi (the point at which the sinc
* function first crosses zero, for negative and positive
* arguments respectively) is p samples.
*/
SincWindow(int n, int p) : m_size(n), m_p(p), m_cache(0) {
encache();
}
SincWindow(const SincWindow &w) : m_size(w.m_size), m_p(w.m_p), m_cache(0) {
encache();
}
SincWindow &operator=(const SincWindow &w) {
if (&w == this) return *this;
m_size = w.m_size;
m_p = w.m_p;
m_cache = 0;
encache();
return *this;
}
virtual ~SincWindow() {
deallocate(m_cache);
}
/**
* Regenerate the sinc window with the same size, but a new scale
* (the p value is interpreted as for the argument of the same
* name to the constructor). If p is unchanged from the previous
* value, do nothing (quickly).
*/
inline void rewrite(int p) {
if (m_p == p) return;
m_p = p;
encache();
}
inline void cut(T *const R__ dst) const {
v_multiply(dst, m_cache, m_size);
}
inline void cut(const T *const R__ src, T *const R__ dst) const {
v_multiply(dst, src, m_cache, m_size);
}
inline void add(T *const R__ dst, T scale) const {
v_add_with_gain(dst, m_cache, scale, m_size);
}
inline T getArea() const { return m_area; }
inline T getValue(int i) const { return m_cache[i]; }
inline int getSize() const { return m_size; }
inline int getP() const { return m_p; }
/**
* Write a sinc window of size n with scale p (the p value is
* interpreted as for the argument of the same name to the
* constructor).
*/
static
void write(T *const R__ dst, const int n, const int p) {
const int half = n/2;
writeHalf(dst, n, p);
int target = half - 1;
for (int i = half + 1; i < n; ++i) {
dst[target--] = dst[i];
}
const T twopi = T(2. * M_PI);
T arg = T(half) * twopi / p;
dst[0] = sin(arg) / arg;
}
protected:
int m_size;
int m_p;
T *R__ m_cache;
T m_area;
/**
* Write the positive half (i.e. n/2 to n-1) of a sinc window of
* size n with scale p (the p value is interpreted as for the
* argument of the same name to the constructor). The negative
* half (indices 0 to n/2-1) of dst is left unchanged.
*/
static
void writeHalf(T *const R__ dst, const int n, const int p) {
const int half = n/2;
const T twopi = T(2. * M_PI);
dst[half] = T(1.0);
for (int i = 1; i < half; ++i) {
T arg = T(i) * twopi / p;
dst[half+i] = sin(arg) / arg;
}
}
void encache() {
if (!m_cache) {
m_cache = allocate<T>(m_size);
}
write(m_cache, m_size, m_p);
m_area = 0;
for (int i = 0; i < m_size; ++i) {
m_area += m_cache[i];
}
m_area /= m_size;
}
};
}
#endif

View File

@@ -0,0 +1,865 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band Library
An audio time-stretching and pitch-shifting library.
Copyright 2007-2022 Particular Programs Ltd.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
Alternatively, if you have a valid commercial licence for the
Rubber Band Library obtained by agreement with the copyright
holders, you may redistribute and/or modify it under the terms
described in that licence.
If you wish to distribute code using the Rubber Band Library
under terms other than those of the GNU General Public License,
you must obtain a valid commercial licence before doing so.
*/
#include "StretchCalculator.h"
#include <math.h>
#include <iostream>
#include <deque>
#include <set>
#include <cassert>
#include <algorithm>
#include "../common/sysutils.h"
namespace RubberBand
{
StretchCalculator::StretchCalculator(size_t sampleRate,
size_t inputIncrement,
bool useHardPeaks) :
m_sampleRate(sampleRate),
m_increment(inputIncrement),
m_prevDf(0),
m_prevRatio(1.0),
m_prevTimeRatio(1.0),
m_transientAmnesty(0),
m_debugLevel(0),
m_useHardPeaks(useHardPeaks),
m_inFrameCounter(0),
m_frameCheckpoint(0, 0),
m_outFrameCounter(0)
{
// std::cerr << "StretchCalculator::StretchCalculator: useHardPeaks = " << useHardPeaks << std::endl;
}
StretchCalculator::~StretchCalculator()
{
}
void
StretchCalculator::setKeyFrameMap(const std::map<size_t, size_t> &mapping)
{
m_keyFrameMap = mapping;
// Ensure we always have a 0 -> 0 mapping. If there's nothing in
// the map at all, don't need to worry about this (empty map is
// handled separately anyway)
if (!m_keyFrameMap.empty()) {
if (m_keyFrameMap.find(0) == m_keyFrameMap.end()) {
m_keyFrameMap[0] = 0;
}
}
}
std::vector<int>
StretchCalculator::calculate(double ratio, size_t inputDuration,
const std::vector<float> &phaseResetDf)
{
m_peaks = findPeaks(phaseResetDf);
size_t totalCount = phaseResetDf.size();
size_t outputDuration = lrint(inputDuration * ratio);
if (m_debugLevel > 0) {
std::cerr << "StretchCalculator::calculate(): inputDuration " << inputDuration << ", ratio " << ratio << ", outputDuration " << outputDuration;
}
outputDuration = lrint((phaseResetDf.size() * m_increment) * ratio);
if (m_debugLevel > 0) {
std::cerr << " (rounded up to " << outputDuration << ")";
std::cerr << ", df size " << phaseResetDf.size() << ", increment "
<< m_increment << std::endl;
}
std::vector<Peak> peaks; // peak position (in chunks) and hardness
std::vector<size_t> targets; // targets for mapping peaks (in samples)
mapPeaks(peaks, targets, outputDuration, totalCount);
if (m_debugLevel > 1) {
std::cerr << "have " << peaks.size() << " fixed positions" << std::endl;
}
size_t totalInput = 0, totalOutput = 0;
std::vector<int> increments;
for (size_t i = 0; i <= peaks.size(); ++i) {
size_t regionStart, regionStartChunk, regionEnd, regionEndChunk;
bool phaseReset = false;
if (i == 0) {
regionStartChunk = 0;
regionStart = 0;
} else {
regionStartChunk = peaks[i-1].chunk;
regionStart = targets[i-1];
phaseReset = peaks[i-1].hard;
}
if (i == peaks.size()) {
// std::cerr << "note: i (=" << i << ") == peaks.size(); regionEndChunk " << regionEndChunk << " -> " << totalCount << ", regionEnd " << regionEnd << " -> " << outputDuration << std::endl;
regionEndChunk = totalCount;
regionEnd = outputDuration;
} else {
regionEndChunk = peaks[i].chunk;
regionEnd = targets[i];
}
if (regionStartChunk > totalCount) regionStartChunk = totalCount;
if (regionStart > outputDuration) regionStart = outputDuration;
if (regionEndChunk > totalCount) regionEndChunk = totalCount;
if (regionEnd > outputDuration) regionEnd = outputDuration;
if (regionEndChunk < regionStartChunk) regionEndChunk = regionStartChunk;
if (regionEnd < regionStart) regionEnd = regionStart;
size_t regionDuration = regionEnd - regionStart;
size_t nchunks = regionEndChunk - regionStartChunk;
if (nchunks == 0) {
//!!!
break;
}
double per = double(regionDuration) / double(nchunks);
double acc = 0.0;
size_t nremaining = nchunks;
size_t totalForRegion = 0;
if (phaseReset) {
size_t incr;
if (nchunks > 1) {
incr = m_increment;
if (incr > regionDuration) {
incr = regionDuration;
}
} else {
incr = regionDuration;
}
increments.push_back(- int64_t(incr));
per = double(regionDuration - incr) / double(nchunks - 1);
acc += incr;
totalForRegion += incr;
totalInput += m_increment;
nremaining = nremaining - 1;
}
if (nremaining > 0) {
for (size_t j = 0; j+1 < nremaining; ++j) {
acc += per;
size_t incr = size_t(round(acc - totalForRegion));
increments.push_back(incr);
totalForRegion += incr;
totalInput += m_increment;
}
if (regionDuration > totalForRegion) {
size_t final = regionDuration - totalForRegion;
increments.push_back(final);
totalForRegion += final;
totalInput += m_increment;
}
}
totalOutput += totalForRegion;
}
if (m_debugLevel > 0) {
std::cerr << "total input increment = " << totalInput << " (= " << totalInput / m_increment << " chunks), output = " << totalOutput << ", ratio = " << double(totalOutput)/double(totalInput) << ", ideal output " << size_t(ceil(totalInput * ratio)) << std::endl;
}
return increments;
}
void
StretchCalculator::mapPeaks(std::vector<Peak> &peaks,
std::vector<size_t> &targets,
size_t outputDuration,
size_t totalCount)
{
// outputDuration is in audio samples; totalCount is in chunks
if (m_keyFrameMap.empty()) {
// "normal" behaviour -- fixed points are strictly in
// proportion
peaks = m_peaks;
for (size_t i = 0; i < peaks.size(); ++i) {
targets.push_back
(lrint((double(peaks[i].chunk) * outputDuration) / totalCount));
}
return;
}
// We have been given a set of source -> target sample frames in
// m_keyFrameMap. We want to ensure that (to the nearest chunk) these
// are followed exactly, and any fixed points that we calculated
// ourselves are interpolated in linear proportion in between.
size_t peakidx = 0;
std::map<size_t, size_t>::const_iterator mi = m_keyFrameMap.begin();
// NB we know for certain we have a mapping from 0 -> 0 (or at
// least, some mapping for source sample 0) because that is
// enforced in setKeyFrameMap above. However, we aren't guaranteed
// to have a mapping for the total duration -- we will usually
// need to assume it maps to the normal duration * ratio sample
while (mi != m_keyFrameMap.end()) {
// std::cerr << "mi->first is " << mi->first << ", second is " << mi->second <<std::endl;
// The map we've been given is from sample to sample, but
// we can only map from chunk to sample. We should perhaps
// adjust the target sample to compensate for the discrepancy
// between the chunk position and the exact requested source
// sample. But we aren't doing that yet.
size_t sourceStartChunk = mi->first / m_increment;
size_t sourceEndChunk = totalCount;
size_t targetStartSample = mi->second;
size_t targetEndSample = outputDuration;
++mi;
if (mi != m_keyFrameMap.end()) {
sourceEndChunk = mi->first / m_increment;
targetEndSample = mi->second;
}
if (sourceStartChunk >= totalCount ||
sourceStartChunk >= sourceEndChunk ||
targetStartSample >= outputDuration ||
targetStartSample >= targetEndSample) {
std::cerr << "NOTE: ignoring mapping from chunk " << sourceStartChunk << " to sample " << targetStartSample << "\n(source or target chunk exceeds total count, or end is not later than start)" << std::endl;
continue;
}
// one peak and target for the mapping, then one for each of
// the computed peaks that appear before the following mapping
Peak p;
p.chunk = sourceStartChunk;
p.hard = false; // mappings are in time only, not phase reset points
peaks.push_back(p);
targets.push_back(targetStartSample);
if (m_debugLevel > 1) {
std::cerr << "mapped chunk " << sourceStartChunk << " (frame " << sourceStartChunk * m_increment << ") -> " << targetStartSample << std::endl;
}
while (peakidx < m_peaks.size()) {
size_t pchunk = m_peaks[peakidx].chunk;
if (pchunk < sourceStartChunk) {
// shouldn't happen, should have been dealt with
// already -- but no harm in ignoring it explicitly
++peakidx;
continue;
}
if (pchunk == sourceStartChunk) {
// convert that last peak to a hard one, after all
peaks[peaks.size()-1].hard = true;
++peakidx;
continue;
}
if (pchunk >= sourceEndChunk) {
// leave the rest for after the next mapping
break;
}
p.chunk = pchunk;
p.hard = m_peaks[peakidx].hard;
double proportion =
double(pchunk - sourceStartChunk) /
double(sourceEndChunk - sourceStartChunk);
size_t target =
targetStartSample +
lrint(proportion *
(targetEndSample - targetStartSample));
if (target <= targets[targets.size()-1] + m_increment) {
// peaks will become too close together afterwards, ignore
++peakidx;
continue;
}
if (m_debugLevel > 1) {
std::cerr << " peak chunk " << pchunk << " (frame " << pchunk * m_increment << ") -> " << target << std::endl;
}
peaks.push_back(p);
targets.push_back(target);
++peakidx;
}
}
}
int64_t
StretchCalculator::expectedOutFrame(int64_t inFrame, double timeRatio)
{
int64_t checkpointedAt = m_frameCheckpoint.first;
int64_t checkpointed = m_frameCheckpoint.second;
return int64_t(round(checkpointed + (inFrame - checkpointedAt) * timeRatio));
}
int
StretchCalculator::calculateSingle(double timeRatio,
double effectivePitchRatio,
float df,
size_t inIncrement,
size_t analysisWindowSize,
size_t synthesisWindowSize)
{
double ratio = timeRatio / effectivePitchRatio;
int increment = int(inIncrement);
if (increment == 0) increment = m_increment;
int outIncrement = lrint(increment * ratio); // the normal case
bool isTransient = false;
// We want to ensure, as close as possible, that the phase reset
// points appear at the right audio frame numbers. To this end we
// track the incoming frame number, its corresponding expected
// output frame number, and the actual output frame number
// projected based on the ratios provided.
//
// There are two subtleties:
//
// (1) on a ratio change, we need to checkpoint the expected
// output frame number reached so far and start counting again
// with the new ratio. We could do this with a reset to zero, but
// it's easier to reason about absolute input/output frame
// matches, so for the moment at least we're doing this by
// explicitly checkpointing the current numbers (hence the use of
// the above expectedOutFrame() function which refers to the
// last checkpointed values).
//
// (2) in the case of a pitch shift in a configuration where
// resampling occurs after stretching, all of our output
// increments will be effectively modified by resampling after we
// return. This is why we separate out timeRatio and
// effectivePitchRatio arguments - the former is the ratio that
// has already been applied and the latter is the ratio that will
// be applied by any subsequent resampling step (which will be 1.0
// / pitchScale if resampling is happening after stretching). So
// the overall ratio is timeRatio / effectivePitchRatio.
bool ratioChanged = (ratio != m_prevRatio);
if (ratioChanged) {
// Reset our frame counters from the ratio change.
// m_outFrameCounter tracks the frames counted at output from
// this function, which normally precedes resampling - hence
// the use of timeRatio rather than ratio here
if (m_debugLevel > 1) {
std::cerr << "StretchCalculator: ratio changed from " << m_prevRatio << " to " << ratio << std::endl;
}
int64_t toCheckpoint = expectedOutFrame
(m_inFrameCounter, m_prevTimeRatio);
m_frameCheckpoint =
std::pair<int64_t, int64_t>(m_inFrameCounter, toCheckpoint);
}
m_prevRatio = ratio;
m_prevTimeRatio = timeRatio;
if (m_debugLevel > 2) {
std::cerr << "StretchCalculator::calculateSingle: timeRatio = "
<< timeRatio << ", effectivePitchRatio = "
<< effectivePitchRatio << " (that's 1.0 / "
<< (1.0 / effectivePitchRatio)
<< "), ratio = " << ratio << ", df = " << df
<< ", inIncrement = " << inIncrement
<< ", default outIncrement = " << outIncrement
<< ", analysisWindowSize = " << analysisWindowSize
<< ", synthesisWindowSize = " << synthesisWindowSize
<< std::endl;
std::cerr << "inFrameCounter = " << m_inFrameCounter
<< ", outFrameCounter = " << m_outFrameCounter
<< std::endl;
std::cerr << "The next sample out is input sample " << m_inFrameCounter << std::endl;
}
int64_t intended = expectedOutFrame
(m_inFrameCounter + analysisWindowSize/4, timeRatio);
int64_t projected = int64_t
(round(m_outFrameCounter + (synthesisWindowSize/4 * effectivePitchRatio)));
int64_t divergence = projected - intended;
if (m_debugLevel > 2) {
std::cerr << "for current frame + quarter frame: intended " << intended << ", projected " << projected << ", divergence " << divergence << std::endl;
}
// In principle, the threshold depends on chunk size: larger chunk
// sizes need higher thresholds. Since chunk size depends on
// ratio, I suppose we could in theory calculate the threshold
// from the ratio directly. For the moment we're happy if it
// works well in common situations.
float transientThreshold = 0.35f;
// if (ratio > 1) transientThreshold = 0.25f;
if (m_useHardPeaks && df > m_prevDf * 1.1f && df > transientThreshold) {
if (divergence > 1000 || divergence < -1000) {
if (m_debugLevel > 1) {
std::cerr << "StretchCalculator::calculateSingle: transient, but we're not permitting it because the divergence (" << divergence << ") is too great" << std::endl;
}
} else {
isTransient = true;
}
}
if (m_debugLevel > 2) {
std::cerr << "df = " << df << ", prevDf = " << m_prevDf
<< ", thresh = " << transientThreshold << std::endl;
}
m_prevDf = df;
if (m_transientAmnesty > 0) {
if (isTransient) {
if (m_debugLevel > 1) {
std::cerr << "StretchCalculator::calculateSingle: transient, but we have an amnesty (df " << df << ", threshold " << transientThreshold << ")" << std::endl;
}
isTransient = false;
}
--m_transientAmnesty;
}
if (isTransient) {
if (m_debugLevel > 1) {
std::cerr << "StretchCalculator::calculateSingle: transient at (df " << df << ", threshold " << transientThreshold << ")" << std::endl;
}
// as in offline mode, 0.05 sec approx min between transients
m_transientAmnesty =
lrint(ceil(double(m_sampleRate) / (20 * double(increment))));
outIncrement = increment;
} else {
double recovery = 0.0;
if (divergence > 1000 || divergence < -1000) {
recovery = divergence / ((m_sampleRate / 10.0) / increment);
} else if (divergence > 100 || divergence < -100) {
recovery = divergence / ((m_sampleRate / 20.0) / increment);
} else {
recovery = divergence / 4.0;
}
int incr = lrint(outIncrement - recovery);
if (m_debugLevel > 2 || (m_debugLevel > 1 && divergence != 0)) {
std::cerr << "divergence = " << divergence << ", recovery = " << recovery << ", incr = " << incr << ", ";
}
int minIncr = lrint(increment * ratio * 0.3);
int maxIncr = lrint(increment * ratio * 2);
if (incr < minIncr) {
incr = minIncr;
} else if (incr > maxIncr) {
incr = maxIncr;
}
if (m_debugLevel > 2 || (m_debugLevel > 1 && divergence != 0)) {
std::cerr << "clamped into [" << minIncr << ", " << maxIncr
<< "] becomes " << incr << std::endl;
}
if (incr < 0) {
std::cerr << "WARNING: internal error: incr < 0 in calculateSingle"
<< std::endl;
outIncrement = 0;
} else {
outIncrement = incr;
}
}
if (m_debugLevel > 1) {
std::cerr << "StretchCalculator::calculateSingle: returning isTransient = "
<< isTransient << ", outIncrement = " << outIncrement
<< std::endl;
}
m_inFrameCounter += inIncrement;
m_outFrameCounter += outIncrement * effectivePitchRatio;
if (isTransient) {
return -outIncrement;
} else {
return outIncrement;
}
}
void
StretchCalculator::reset()
{
m_prevDf = 0;
m_prevRatio = 1.0;
m_prevTimeRatio = 1.0;
m_inFrameCounter = 0;
m_frameCheckpoint = std::pair<int64_t, int64_t>(0, 0);
m_outFrameCounter = 0.0;
m_transientAmnesty = 0;
m_keyFrameMap.clear();
}
std::vector<StretchCalculator::Peak>
StretchCalculator::findPeaks(const std::vector<float> &rawDf)
{
std::vector<float> df = smoothDF(rawDf);
// We distinguish between "soft" and "hard" peaks. A soft peak is
// simply the result of peak-picking on the smoothed onset
// detection function, and it represents any (strong-ish) onset.
// We aim to ensure always that soft peaks are placed at the
// correct position in time. A hard peak is where there is a very
// rapid rise in detection function, and it presumably represents
// a more broadband, noisy transient. For these we perform a
// phase reset (if in the appropriate mode), and we locate the
// reset at the first point where we notice enough of a rapid
// rise, rather than necessarily at the peak itself, in order to
// preserve the shape of the transient.
std::set<size_t> hardPeakCandidates;
std::set<size_t> softPeakCandidates;
if (m_useHardPeaks) {
// 0.05 sec approx min between hard peaks
size_t hardPeakAmnesty = lrint(ceil(double(m_sampleRate) /
(20 * double(m_increment))));
size_t prevHardPeak = 0;
if (m_debugLevel > 1) {
std::cerr << "hardPeakAmnesty = " << hardPeakAmnesty << std::endl;
}
for (size_t i = 1; i + 1 < df.size(); ++i) {
if (df[i] < 0.1) continue;
if (df[i] <= df[i-1] * 1.1) continue;
if (df[i] < 0.22) continue;
if (!hardPeakCandidates.empty() &&
i < prevHardPeak + hardPeakAmnesty) {
continue;
}
bool hard = (df[i] > 0.4);
if (hard && (m_debugLevel > 1)) {
std::cerr << "hard peak at " << i << ": " << df[i]
<< " > absolute " << 0.4
<< std::endl;
}
if (!hard) {
hard = (df[i] > df[i-1] * 1.4);
if (hard && (m_debugLevel > 1)) {
std::cerr << "hard peak at " << i << ": " << df[i]
<< " > prev " << df[i-1] << " * 1.4"
<< std::endl;
}
}
if (!hard && i > 1) {
hard = (df[i] > df[i-1] * 1.2 &&
df[i-1] > df[i-2] * 1.2);
if (hard && (m_debugLevel > 1)) {
std::cerr << "hard peak at " << i << ": " << df[i]
<< " > prev " << df[i-1] << " * 1.2 and "
<< df[i-1] << " > prev " << df[i-2] << " * 1.2"
<< std::endl;
}
}
if (!hard && i > 2) {
// have already established that df[i] > df[i-1] * 1.1
hard = (df[i] > 0.3 &&
df[i-1] > df[i-2] * 1.1 &&
df[i-2] > df[i-3] * 1.1);
if (hard && (m_debugLevel > 1)) {
std::cerr << "hard peak at " << i << ": " << df[i]
<< " > prev " << df[i-1] << " * 1.1 and "
<< df[i-1] << " > prev " << df[i-2] << " * 1.1 and "
<< df[i-2] << " > prev " << df[i-3] << " * 1.1"
<< std::endl;
}
}
if (!hard) continue;
// (df[i+1] > df[i] && df[i+1] > df[i-1] * 1.8) ||
// df[i] > 0.4) {
size_t peakLocation = i;
if (i + 1 < rawDf.size() &&
rawDf[i + 1] > rawDf[i] * 1.4) {
++peakLocation;
if (m_debugLevel > 1) {
std::cerr << "pushing hard peak forward to " << peakLocation << ": " << df[peakLocation] << " > " << df[peakLocation-1] << " * " << 1.4 << std::endl;
}
}
hardPeakCandidates.insert(peakLocation);
prevHardPeak = peakLocation;
}
}
size_t medianmaxsize = lrint(ceil(double(m_sampleRate) /
double(m_increment))); // 1 sec ish
if (m_debugLevel > 1) {
std::cerr << "mediansize = " << medianmaxsize << std::endl;
}
if (medianmaxsize < 7) {
medianmaxsize = 7;
if (m_debugLevel > 1) {
std::cerr << "adjusted mediansize = " << medianmaxsize << std::endl;
}
}
int minspacing = lrint(ceil(double(m_sampleRate) /
(20 * double(m_increment)))); // 0.05 sec ish
std::deque<float> medianwin;
std::vector<float> sorted;
int softPeakAmnesty = 0;
for (size_t i = 0; i < medianmaxsize/2; ++i) {
medianwin.push_back(0);
}
for (size_t i = 0; i < medianmaxsize/2 && i < df.size(); ++i) {
medianwin.push_back(df[i]);
}
size_t lastSoftPeak = 0;
for (size_t i = 0; i < df.size(); ++i) {
size_t mediansize = medianmaxsize;
if (medianwin.size() < mediansize) {
mediansize = medianwin.size();
}
size_t middle = medianmaxsize / 2;
if (middle >= mediansize) middle = mediansize-1;
size_t nextDf = i + mediansize - middle;
if (mediansize < 2) {
if (mediansize > medianmaxsize) { // absurd, but never mind that
medianwin.pop_front();
}
if (nextDf < df.size()) {
medianwin.push_back(df[nextDf]);
} else {
medianwin.push_back(0);
}
continue;
}
if (m_debugLevel > 2) {
// std::cerr << "have " << mediansize << " in median buffer" << std::endl;
}
sorted.clear();
for (size_t j = 0; j < mediansize; ++j) {
sorted.push_back(medianwin[j]);
}
std::sort(sorted.begin(), sorted.end());
size_t n = 90; // percentile above which we pick peaks
size_t index = (sorted.size() * n) / 100;
if (index >= sorted.size()) index = sorted.size()-1;
if (index == sorted.size()-1 && index > 0) --index;
float thresh = sorted[index];
// if (m_debugLevel > 2) {
// std::cerr << "medianwin[" << middle << "] = " << medianwin[middle] << ", thresh = " << thresh << std::endl;
// if (medianwin[middle] == 0.f) {
// std::cerr << "contents: ";
// for (size_t j = 0; j < medianwin.size(); ++j) {
// std::cerr << medianwin[j] << " ";
// }
// std::cerr << std::endl;
// }
// }
if (medianwin[middle] > thresh &&
medianwin[middle] > medianwin[middle-1] &&
medianwin[middle] > medianwin[middle+1] &&
softPeakAmnesty == 0) {
size_t maxindex = middle;
float maxval = medianwin[middle];
for (size_t j = middle+1; j < mediansize; ++j) {
if (medianwin[j] > maxval) {
maxval = medianwin[j];
maxindex = j;
} else if (medianwin[j] < medianwin[middle]) {
break;
}
}
size_t peak = i + maxindex - middle;
// std::cerr << "i = " << i << ", maxindex = " << maxindex << ", middle = " << middle << ", so peak at " << peak << std::endl;
if (softPeakCandidates.empty() || lastSoftPeak != peak) {
if (m_debugLevel > 1) {
std::cerr << "soft peak at " << peak << " ("
<< peak * m_increment << "): "
<< medianwin[middle] << " > "
<< thresh << " and "
<< medianwin[middle]
<< " > " << medianwin[middle-1] << " and "
<< medianwin[middle]
<< " > " << medianwin[middle+1]
<< std::endl;
}
if (peak >= df.size()) {
if (m_debugLevel > 2) {
std::cerr << "peak is beyond end" << std::endl;
}
} else {
softPeakCandidates.insert(peak);
lastSoftPeak = peak;
}
}
softPeakAmnesty = minspacing + maxindex - middle;
if (m_debugLevel > 2) {
std::cerr << "amnesty = " << softPeakAmnesty << std::endl;
}
} else if (softPeakAmnesty > 0) --softPeakAmnesty;
if (mediansize >= medianmaxsize) {
medianwin.pop_front();
}
if (nextDf < df.size()) {
medianwin.push_back(df[nextDf]);
} else {
medianwin.push_back(0);
}
}
std::vector<Peak> peaks;
while (!hardPeakCandidates.empty() || !softPeakCandidates.empty()) {
bool haveHardPeak = !hardPeakCandidates.empty();
bool haveSoftPeak = !softPeakCandidates.empty();
size_t hardPeak = (haveHardPeak ? *hardPeakCandidates.begin() : 0);
size_t softPeak = (haveSoftPeak ? *softPeakCandidates.begin() : 0);
Peak peak;
peak.hard = false;
peak.chunk = softPeak;
bool ignore = false;
if (haveHardPeak &&
(!haveSoftPeak || hardPeak <= softPeak)) {
if (m_debugLevel > 2) {
std::cerr << "Hard peak: " << hardPeak << std::endl;
}
peak.hard = true;
peak.chunk = hardPeak;
hardPeakCandidates.erase(hardPeakCandidates.begin());
} else {
if (m_debugLevel > 2) {
std::cerr << "Soft peak: " << softPeak << std::endl;
}
if (!peaks.empty() &&
peaks[peaks.size()-1].hard &&
peaks[peaks.size()-1].chunk + 3 >= softPeak) {
if (m_debugLevel > 2) {
std::cerr << "(ignoring, as we just had a hard peak)"
<< std::endl;
}
ignore = true;
}
}
if (haveSoftPeak && peak.chunk == softPeak) {
softPeakCandidates.erase(softPeakCandidates.begin());
}
if (!ignore) {
peaks.push_back(peak);
}
}
return peaks;
}
std::vector<float>
StretchCalculator::smoothDF(const std::vector<float> &df)
{
std::vector<float> smoothedDF;
for (size_t i = 0; i < df.size(); ++i) {
// three-value moving mean window for simple smoothing
float total = 0.f, count = 0;
if (i > 0) { total += df[i-1]; ++count; }
total += df[i]; ++count;
if (i+1 < df.size()) { total += df[i+1]; ++count; }
float mean = total / count;
smoothedDF.push_back(mean);
}
return smoothedDF;
}
}

View File

@@ -0,0 +1,115 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band Library
An audio time-stretching and pitch-shifting library.
Copyright 2007-2022 Particular Programs Ltd.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
Alternatively, if you have a valid commercial licence for the
Rubber Band Library obtained by agreement with the copyright
holders, you may redistribute and/or modify it under the terms
described in that licence.
If you wish to distribute code using the Rubber Band Library
under terms other than those of the GNU General Public License,
you must obtain a valid commercial licence before doing so.
*/
#ifndef RUBBERBAND_STRETCH_CALCULATOR_H
#define RUBBERBAND_STRETCH_CALCULATOR_H
#include <sys/types.h>
#include <vector>
#include <map>
#include <cstdint>
namespace RubberBand
{
class StretchCalculator
{
public:
StretchCalculator(size_t sampleRate, size_t inputIncrement, bool useHardPeaks);
virtual ~StretchCalculator();
/**
* Provide a set of mappings from "before" to "after" sample
* numbers so as to enforce a particular stretch profile. This
* must be called before calculate(). The argument is a map from
* audio sample frame number in the source material to the
* corresponding sample frame number in the stretched output.
*/
void setKeyFrameMap(const std::map<size_t, size_t> &mapping);
/**
* Calculate phase increments for a region of audio, given the
* overall target stretch ratio, input duration in audio samples,
* and the audio curves to use for identifying phase lock points.
*/
std::vector<int> calculate(double ratio, size_t inputDuration,
const std::vector<float> &lockAudioCurve);
/**
* Calculate the phase increment for a single audio block, given
* the overall target stretch ratio and the block's value on the
* phase-lock audio curve. State is retained between calls in the
* StretchCalculator object; call reset() to reset it. This uses
* a less sophisticated method than the offline calculate().
*
* If increment is non-zero, use it for the input increment for
* this block in preference to m_increment.
*/
int calculateSingle(double timeRatio,
double effectivePitchRatio,
float curveValue,
size_t increment,
size_t analysisWindowSize,
size_t synthesisWindowSize);
void setUseHardPeaks(bool use) { m_useHardPeaks = use; }
void reset();
void setDebugLevel(int level) { m_debugLevel = level; }
struct Peak {
size_t chunk;
bool hard;
};
std::vector<Peak> getLastCalculatedPeaks() const { return m_peaks; }
std::vector<float> smoothDF(const std::vector<float> &df);
protected:
std::vector<Peak> findPeaks(const std::vector<float> &audioCurve);
void mapPeaks(std::vector<Peak> &peaks, std::vector<size_t> &targets,
size_t outputDuration, size_t totalCount);
size_t m_sampleRate;
size_t m_increment;
float m_prevDf;
double m_prevRatio;
double m_prevTimeRatio;
int m_transientAmnesty; // only in RT mode; handled differently offline
int m_debugLevel;
bool m_useHardPeaks;
int64_t m_inFrameCounter;
std::pair<int64_t, int64_t> m_frameCheckpoint;
int64_t expectedOutFrame(int64_t inFrame, double timeRatio);
double m_outFrameCounter;
std::map<size_t, size_t> m_keyFrameMap;
std::vector<Peak> m_peaks;
};
}
#endif

View File

@@ -0,0 +1,291 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band Library
An audio time-stretching and pitch-shifting library.
Copyright 2007-2022 Particular Programs Ltd.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
Alternatively, if you have a valid commercial licence for the
Rubber Band Library obtained by agreement with the copyright
holders, you may redistribute and/or modify it under the terms
described in that licence.
If you wish to distribute code using the Rubber Band Library
under terms other than those of the GNU General Public License,
you must obtain a valid commercial licence before doing so.
*/
#include "StretcherChannelData.h"
#include "../common/Resampler.h"
#include "../common/Allocators.h"
#include <algorithm>
namespace RubberBand
{
RubberBandStretcher::Impl::ChannelData::ChannelData(size_t windowSize,
size_t fftSize,
size_t outbufSize)
{
std::set<size_t> s;
construct(s, windowSize, fftSize, outbufSize);
}
RubberBandStretcher::Impl::ChannelData::ChannelData(const std::set<size_t> &sizes,
size_t initialWindowSize,
size_t initialFftSize,
size_t outbufSize)
{
construct(sizes, initialWindowSize, initialFftSize, outbufSize);
}
void
RubberBandStretcher::Impl::ChannelData::construct(const std::set<size_t> &sizes,
size_t initialWindowSize,
size_t initialFftSize,
size_t outbufSize)
{
size_t maxSize = initialWindowSize * 2;
if (initialFftSize > maxSize) maxSize = initialFftSize;
// std::cerr << "ChannelData::construct: initialWindowSize = " << initialWindowSize << ", initialFftSize = " << initialFftSize << ", outbufSize = " << outbufSize << std::endl;
// std::set is ordered by value
std::set<size_t>::const_iterator i = sizes.end();
if (i != sizes.begin()) {
--i;
if (*i > maxSize) maxSize = *i;
}
// max possible size of the real "half" of freq data
size_t realSize = maxSize / 2 + 1;
// std::cerr << "ChannelData::construct([" << sizes.size() << "], " << maxSize << ", " << realSize << ", " << outbufSize << ")" << std::endl;
if (outbufSize < maxSize) outbufSize = maxSize;
inbuf = new RingBuffer<float>(maxSize);
outbuf = new RingBuffer<float>(outbufSize);
mag = allocate_and_zero<process_t>(realSize);
phase = allocate_and_zero<process_t>(realSize);
prevPhase = allocate_and_zero<process_t>(realSize);
prevError = allocate_and_zero<process_t>(realSize);
unwrappedPhase = allocate_and_zero<process_t>(realSize);
envelope = allocate_and_zero<process_t>(realSize);
fltbuf = allocate_and_zero<float>(maxSize);
dblbuf = allocate_and_zero<process_t>(maxSize);
accumulator = allocate_and_zero<float>(maxSize);
windowAccumulator = allocate_and_zero<float>(maxSize);
ms = allocate_and_zero<float>(maxSize);
interpolator = allocate_and_zero<float>(maxSize);
interpolatorScale = 0;
for (std::set<size_t>::const_iterator i = sizes.begin();
i != sizes.end(); ++i) {
ffts[*i] = new FFT(*i);
if (sizeof(process_t) == sizeof(double)) {
ffts[*i]->initDouble();
} else {
ffts[*i]->initFloat();
}
}
fft = ffts[initialFftSize];
resampler = 0;
resamplebuf = 0;
resamplebufSize = 0;
reset();
// Avoid dividing opening sample (which will be discarded anyway) by zero
windowAccumulator[0] = 1.f;
}
void
RubberBandStretcher::Impl::ChannelData::setSizes(size_t windowSize,
size_t fftSize)
{
// std::cerr << "ChannelData::setSizes: windowSize = " << windowSize << ", fftSize = " << fftSize << std::endl;
size_t maxSize = 2 * std::max(windowSize, fftSize);
size_t realSize = maxSize / 2 + 1;
size_t oldMax = inbuf->getSize();
size_t oldReal = oldMax / 2 + 1;
if (oldMax >= maxSize) {
// no need to reallocate buffers, just reselect fft
//!!! we can't actually do this without locking against the
//process thread, can we? we need to zero the mag/phase
//buffers without interference
if (ffts.find(fftSize) == ffts.end()) {
//!!! this also requires a lock, but it shouldn't occur in
//RT mode with proper initialisation
ffts[fftSize] = new FFT(fftSize);
if (sizeof(process_t) == sizeof(double)) {
ffts[fftSize]->initDouble();
} else {
ffts[fftSize]->initFloat();
}
}
fft = ffts[fftSize];
v_zero(fltbuf, maxSize);
v_zero(dblbuf, maxSize);
v_zero(mag, realSize);
v_zero(phase, realSize);
v_zero(prevPhase, realSize);
v_zero(prevError, realSize);
v_zero(unwrappedPhase, realSize);
return;
}
//!!! at this point we need a lock in case a different client
//thread is calling process() -- we need this lock even if we
//aren't running in threaded mode ourselves -- if we're in RT
//mode, then the process call should trylock and fail if the lock
//is unavailable (since this should never normally be the case in
//general use in RT mode)
RingBuffer<float> *newbuf = inbuf->resized(maxSize);
delete inbuf;
inbuf = newbuf;
// We don't want to preserve data in these arrays
mag = reallocate_and_zero(mag, oldReal, realSize);
phase = reallocate_and_zero(phase, oldReal, realSize);
prevPhase = reallocate_and_zero(prevPhase, oldReal, realSize);
prevError = reallocate_and_zero(prevError, oldReal, realSize);
unwrappedPhase = reallocate_and_zero(unwrappedPhase, oldReal, realSize);
envelope = reallocate_and_zero(envelope, oldReal, realSize);
fltbuf = reallocate_and_zero(fltbuf, oldMax, maxSize);
dblbuf = reallocate_and_zero(dblbuf, oldMax, maxSize);
ms = reallocate_and_zero(ms, oldMax, maxSize);
interpolator = reallocate_and_zero(interpolator, oldMax, maxSize);
// But we do want to preserve data in these
accumulator = reallocate_and_zero_extension
(accumulator, oldMax, maxSize);
windowAccumulator = reallocate_and_zero_extension
(windowAccumulator, oldMax, maxSize);
interpolatorScale = 0;
//!!! and resampler?
if (ffts.find(fftSize) == ffts.end()) {
ffts[fftSize] = new FFT(fftSize);
if (sizeof(process_t) == sizeof(double)) {
ffts[fftSize]->initDouble();
} else {
ffts[fftSize]->initFloat();
}
}
fft = ffts[fftSize];
}
void
RubberBandStretcher::Impl::ChannelData::setOutbufSize(size_t outbufSize)
{
size_t oldSize = outbuf->getSize();
// std::cerr << "ChannelData::setOutbufSize(" << outbufSize << ") [from " << oldSize << "]" << std::endl;
if (oldSize < outbufSize) {
//!!! at this point we need a lock in case a different client
//thread is calling process()
RingBuffer<float> *newbuf = outbuf->resized(outbufSize);
delete outbuf;
outbuf = newbuf;
}
}
void
RubberBandStretcher::Impl::ChannelData::setResampleBufSize(size_t sz)
{
resamplebuf = reallocate_and_zero<float>(resamplebuf, resamplebufSize, sz);
resamplebufSize = sz;
}
RubberBandStretcher::Impl::ChannelData::~ChannelData()
{
delete resampler;
deallocate(resamplebuf);
delete inbuf;
delete outbuf;
deallocate(mag);
deallocate(phase);
deallocate(prevPhase);
deallocate(prevError);
deallocate(unwrappedPhase);
deallocate(envelope);
deallocate(interpolator);
deallocate(ms);
deallocate(accumulator);
deallocate(windowAccumulator);
deallocate(fltbuf);
deallocate(dblbuf);
for (std::map<size_t, FFT *>::iterator i = ffts.begin();
i != ffts.end(); ++i) {
delete i->second;
}
}
void
RubberBandStretcher::Impl::ChannelData::reset()
{
inbuf->reset();
outbuf->reset();
if (resampler) resampler->reset();
size_t size = inbuf->getSize();
for (size_t i = 0; i < size; ++i) {
accumulator[i] = 0.f;
windowAccumulator[i] = 0.f;
}
// Avoid dividing opening sample (which will be discarded anyway) by zero
windowAccumulator[0] = 1.f;
accumulatorFill = 0;
prevIncrement = 0;
chunkCount = 0;
inCount = 0;
inputSize = -1;
outCount = 0;
interpolatorScale = 0;
unchanged = true;
draining = false;
outputComplete = false;
}
}

View File

@@ -0,0 +1,147 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band Library
An audio time-stretching and pitch-shifting library.
Copyright 2007-2022 Particular Programs Ltd.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
Alternatively, if you have a valid commercial licence for the
Rubber Band Library obtained by agreement with the copyright
holders, you may redistribute and/or modify it under the terms
described in that licence.
If you wish to distribute code using the Rubber Band Library
under terms other than those of the GNU General Public License,
you must obtain a valid commercial licence before doing so.
*/
#ifndef RUBBERBAND_STRETCHERCHANNELDATA_H
#define RUBBERBAND_STRETCHERCHANNELDATA_H
#include "StretcherImpl.h"
#include <set>
#include <atomic>
namespace RubberBand
{
class Resampler;
class RubberBandStretcher::Impl::ChannelData
{
public:
/**
* Construct a ChannelData structure.
*
* The sizes passed in here are for the time-domain analysis
* window and FFT calculation, and most of the buffer sizes also
* depend on them. In practice they are always powers of two, the
* window and FFT sizes are either equal or generally in a 2:1
* relationship either way, and except for very extreme stretches
* the FFT size is either 1024, 2048 or 4096.
*
* The outbuf size depends on other factors as well, including
* the pitch scale factor and any maximum processing block
* size specified by the user of the code.
*/
ChannelData(size_t windowSize,
size_t fftSize,
size_t outbufSize);
/**
* Construct a ChannelData structure that can process at different
* FFT sizes without requiring reallocation when the size changes.
* The sizes can subsequently be changed with a call to setSizes.
* Reallocation will only be necessary if setSizes is called with
* values not equal to any of those passed in to the constructor.
*
* The outbufSize should be the maximum possible outbufSize to
* avoid reallocation, which will happen if setOutbufSize is
* called subsequently.
*/
ChannelData(const std::set<size_t> &sizes,
size_t initialWindowSize,
size_t initialFftSize,
size_t outbufSize);
~ChannelData();
/**
* Reset buffers
*/
void reset();
/**
* Set the FFT, analysis window, and buffer sizes. If this
* ChannelData was constructed with a set of sizes and the given
* window and FFT sizes here were among them, no reallocation will
* be required.
*/
void setSizes(size_t windowSize, size_t fftSizes);
/**
* Set the outbufSize for the channel data. Reallocation will
* occur.
*/
void setOutbufSize(size_t outbufSize);
/**
* Set the resampler buffer size. Default if not called is no
* buffer allocated at all.
*/
void setResampleBufSize(size_t resamplebufSize);
RingBuffer<float> *inbuf;
RingBuffer<float> *outbuf;
process_t *mag;
process_t *phase;
process_t *prevPhase;
process_t *prevError;
process_t *unwrappedPhase;
float *accumulator;
size_t accumulatorFill;
float *windowAccumulator;
float *ms; // only used when mid-side processing
float *interpolator; // only used when time-domain smoothing is on
int interpolatorScale;
float *fltbuf;
process_t *dblbuf; // owned by FFT object, only used for time domain FFT i/o
process_t *envelope; // for cepstral formant shift
bool unchanged;
size_t prevIncrement; // only used in RT mode
size_t chunkCount;
size_t inCount;
std::atomic<int64_t> inputSize; // set only after known (when data ended); -1 previously
size_t outCount;
std::atomic<bool> draining;
std::atomic<bool> outputComplete;
FFT *fft;
std::map<size_t, FFT *> ffts;
Resampler *resampler;
float *resamplebuf;
size_t resamplebufSize;
private:
void construct(const std::set<size_t> &sizes,
size_t initialWindowSize, size_t initialFftSize,
size_t outbufSize);
};
}
#endif

1341
src/faster/StretcherImpl.cpp Normal file

File diff suppressed because it is too large Load Diff

266
src/faster/StretcherImpl.h Normal file
View File

@@ -0,0 +1,266 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band Library
An audio time-stretching and pitch-shifting library.
Copyright 2007-2022 Particular Programs Ltd.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
Alternatively, if you have a valid commercial licence for the
Rubber Band Library obtained by agreement with the copyright
holders, you may redistribute and/or modify it under the terms
described in that licence.
If you wish to distribute code using the Rubber Band Library
under terms other than those of the GNU General Public License,
you must obtain a valid commercial licence before doing so.
*/
#ifndef RUBBERBAND_STRETCHERIMPL_H
#define RUBBERBAND_STRETCHERIMPL_H
#include "../rubberband/RubberBandStretcher.h"
#include "../common/Window.h"
#include "../common/FFT.h"
#include "../common/RingBuffer.h"
#include "../common/Scavenger.h"
#include "../common/Thread.h"
#include "../common/sysutils.h"
#include "SincWindow.h"
#include "CompoundAudioCurve.h"
#include <set>
#include <algorithm>
namespace RubberBand
{
#ifdef PROCESS_SAMPLE_TYPE
typedef PROCESS_SAMPLE_TYPE process_t;
#else
typedef double process_t;
#endif
class AudioCurveCalculator;
class StretchCalculator;
class RubberBandStretcher::Impl
{
public:
Impl(size_t sampleRate, size_t channels, Options options,
double initialTimeRatio, double initialPitchScale);
~Impl();
void reset();
void setTimeRatio(double ratio);
void setPitchScale(double scale);
double getTimeRatio() const;
double getPitchScale() const;
size_t getLatency() const;
void setTransientsOption(Options);
void setDetectorOption(Options);
void setPhaseOption(Options);
void setFormantOption(Options);
void setPitchOption(Options);
void setExpectedInputDuration(size_t samples);
void setMaxProcessSize(size_t samples);
void setKeyFrameMap(const std::map<size_t, size_t> &);
size_t getSamplesRequired() const;
void study(const float *const *input, size_t samples, bool final);
void process(const float *const *input, size_t samples, bool final);
int available() const;
size_t retrieve(float *const *output, size_t samples) const;
float getFrequencyCutoff(int n) const;
void setFrequencyCutoff(int n, float f);
size_t getInputIncrement() const {
return m_increment;
}
std::vector<int> getOutputIncrements() const;
std::vector<float> getPhaseResetCurve() const;
std::vector<int> getExactTimePoints() const;
size_t getChannelCount() const {
return m_channels;
}
void calculateStretch();
void setDebugLevel(int level);
static void setDefaultDebugLevel(int level) { m_defaultDebugLevel = level; }
protected:
size_t m_sampleRate;
size_t m_channels;
void prepareChannelMS(size_t channel, const float *const *inputs,
size_t offset, size_t samples, float *prepared);
size_t consumeChannel(size_t channel, const float *const *inputs,
size_t offset, size_t samples, bool final);
void processChunks(size_t channel, bool &any, bool &last);
bool processOneChunk(); // across all channels, for real time use
bool processChunkForChannel(size_t channel, size_t phaseIncrement,
size_t shiftIncrement, bool phaseReset);
bool testInbufReadSpace(size_t channel);
void calculateIncrements(size_t &phaseIncrement,
size_t &shiftIncrement, bool &phaseReset);
bool getIncrements(size_t channel, size_t &phaseIncrement,
size_t &shiftIncrement, bool &phaseReset);
void analyseChunk(size_t channel);
void modifyChunk(size_t channel, size_t outputIncrement, bool phaseReset);
void formantShiftChunk(size_t channel);
void synthesiseChunk(size_t channel, size_t shiftIncrement);
void writeChunk(size_t channel, size_t shiftIncrement, bool last);
void calculateSizes();
void configure();
void reconfigure();
double getEffectiveRatio() const;
size_t roundUp(size_t value); // to next power of two
template <typename T, typename S>
void cutShiftAndFold(T *target, int targetSize,
S *src, // destructive to src
Window<float> *window) {
window->cut(src);
const int windowSize = window->getSize();
const int hs = targetSize / 2;
if (windowSize == targetSize) {
v_convert(target, src + hs, hs);
v_convert(target + hs, src, hs);
} else {
v_zero(target, targetSize);
int j = targetSize - windowSize/2;
while (j < 0) j += targetSize;
for (int i = 0; i < windowSize; ++i) {
target[j] += src[i];
if (++j == targetSize) j = 0;
}
}
}
bool resampleBeforeStretching() const;
double m_timeRatio;
double m_pitchScale;
// n.b. either m_fftSize is an integer multiple of m_windowSize,
// or vice versa
size_t m_fftSize;
size_t m_aWindowSize; //!!! or use m_awindow->getSize() throughout?
size_t m_sWindowSize; //!!! or use m_swindow->getSize() throughout?
size_t m_increment;
size_t m_outbufSize;
size_t m_maxProcessSize;
size_t m_expectedInputDuration;
#ifndef NO_THREADING
bool m_threaded;
#endif
bool m_realtime;
Options m_options;
int m_debugLevel;
enum ProcessMode {
JustCreated,
Studying,
Processing,
Finished
};
ProcessMode m_mode;
std::map<size_t, Window<float> *> m_windows;
std::map<size_t, SincWindow<float> *> m_sincs;
Window<float> *m_awindow;
SincWindow<float> *m_afilter;
Window<float> *m_swindow;
FFT *m_studyFFT;
#ifndef NO_THREADING
Condition m_spaceAvailable;
class ProcessThread : public Thread
{
public:
ProcessThread(Impl *s, size_t c);
void run();
void signalDataAvailable();
void abandon();
private:
Impl *m_s;
size_t m_channel;
Condition m_dataAvailable;
bool m_abandoning;
};
mutable Mutex m_threadSetMutex;
typedef std::set<ProcessThread *> ThreadSet;
ThreadSet m_threadSet;
#if defined(HAVE_IPP) && !defined(NO_THREADING) && !defined(USE_BQRESAMPLER) && !defined(USE_SPEEX) && !defined(HAVE_LIBSAMPLERATE)
// Exasperatingly, the IPP polyphase resampler does not appear to
// be thread-safe as advertised -- a good reason to prefer any of
// the alternatives
#define STRETCHER_IMPL_RESAMPLER_MUTEX_REQUIRED 1
Mutex m_resamplerMutex;
#endif
#endif // ! NO_THREADING
size_t m_inputDuration;
CompoundAudioCurve::Type m_detectorType;
std::vector<float> m_phaseResetDf;
std::vector<bool> m_silence;
int m_silentHistory;
class ChannelData;
std::vector<ChannelData *> m_channelData;
std::vector<int> m_outputIncrements;
mutable RingBuffer<int> m_lastProcessOutputIncrements;
mutable RingBuffer<float> m_lastProcessPhaseResetDf;
Scavenger<RingBuffer<float> > m_emergencyScavenger;
CompoundAudioCurve *m_phaseResetAudioCurve;
AudioCurveCalculator *m_silentAudioCurve;
StretchCalculator *m_stretchCalculator;
float m_freq0;
float m_freq1;
float m_freq2;
size_t m_baseFftSize;
float m_rateMultiple;
void writeOutput(RingBuffer<float> &to, float *from,
size_t qty, size_t &outCount, size_t theoreticalOut);
static int m_defaultDebugLevel;
static const size_t m_defaultIncrement;
static const size_t m_defaultFftSize;
};
}
#endif

File diff suppressed because it is too large Load Diff