Implement resampling
This commit is contained in:
@@ -29,6 +29,69 @@
|
|||||||
|
|
||||||
namespace RubberBand {
|
namespace RubberBand {
|
||||||
|
|
||||||
|
R3StretcherImpl::R3StretcherImpl(Parameters parameters,
|
||||||
|
double initialTimeRatio,
|
||||||
|
double initialPitchScale) :
|
||||||
|
m_parameters(parameters),
|
||||||
|
m_timeRatio(initialTimeRatio),
|
||||||
|
m_pitchScale(initialPitchScale),
|
||||||
|
m_guide(Guide::Parameters(m_parameters.sampleRate, parameters.logger)),
|
||||||
|
m_guideConfiguration(m_guide.getConfiguration()),
|
||||||
|
m_channelAssembly(m_parameters.channels),
|
||||||
|
m_troughPicker(m_guideConfiguration.classificationFftSize / 2 + 1),
|
||||||
|
m_inhop(1),
|
||||||
|
m_prevOuthop(1),
|
||||||
|
m_draining(false)
|
||||||
|
{
|
||||||
|
BinSegmenter::Parameters segmenterParameters
|
||||||
|
(m_guideConfiguration.classificationFftSize,
|
||||||
|
m_parameters.sampleRate);
|
||||||
|
BinClassifier::Parameters classifierParameters
|
||||||
|
(m_guideConfiguration.classificationFftSize / 2 + 1,
|
||||||
|
9, 1, 10, 2.0, 2.0, 1.0e-7);
|
||||||
|
|
||||||
|
int inRingBufferSize = m_guideConfiguration.longestFftSize * 2;
|
||||||
|
int outRingBufferSize = m_guideConfiguration.longestFftSize * 16;
|
||||||
|
|
||||||
|
for (int c = 0; c < m_parameters.channels; ++c) {
|
||||||
|
m_channelData.push_back(std::make_shared<ChannelData>
|
||||||
|
(segmenterParameters,
|
||||||
|
classifierParameters,
|
||||||
|
m_guideConfiguration.longestFftSize,
|
||||||
|
inRingBufferSize,
|
||||||
|
outRingBufferSize));
|
||||||
|
for (auto band: m_guideConfiguration.fftBandLimits) {
|
||||||
|
int fftSize = band.fftSize;
|
||||||
|
m_channelData[c]->scales[fftSize] =
|
||||||
|
std::make_shared<ChannelScaleData>
|
||||||
|
(fftSize, m_guideConfiguration.longestFftSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto band: m_guideConfiguration.fftBandLimits) {
|
||||||
|
int fftSize = band.fftSize;
|
||||||
|
GuidedPhaseAdvance::Parameters guidedParameters
|
||||||
|
(fftSize, m_parameters.sampleRate, m_parameters.channels,
|
||||||
|
m_parameters.logger);
|
||||||
|
m_scaleData[fftSize] = std::make_shared<ScaleData>(guidedParameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_calculator = std::unique_ptr<StretchCalculator>
|
||||||
|
(new StretchCalculator(int(round(m_parameters.sampleRate)), //!!! which is a double...
|
||||||
|
1, false)); // no fixed inputIncrement
|
||||||
|
|
||||||
|
Resampler::Parameters resamplerParameters;
|
||||||
|
resamplerParameters.quality = Resampler::FastestTolerable;
|
||||||
|
resamplerParameters.dynamism = Resampler::RatioOftenChanging;
|
||||||
|
resamplerParameters.ratioChange = Resampler::SmoothRatioChange;
|
||||||
|
resamplerParameters.initialSampleRate = m_parameters.sampleRate;
|
||||||
|
resamplerParameters.maxBufferSize = m_guideConfiguration.longestFftSize; //!!!???
|
||||||
|
m_resampler = std::unique_ptr<Resampler>
|
||||||
|
(new Resampler(resamplerParameters, m_parameters.channels));
|
||||||
|
|
||||||
|
calculateHop();
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
R3StretcherImpl::setTimeRatio(double ratio)
|
R3StretcherImpl::setTimeRatio(double ratio)
|
||||||
{
|
{
|
||||||
@@ -180,6 +243,7 @@ R3StretcherImpl::consume()
|
|||||||
{
|
{
|
||||||
double ratio = getEffectiveRatio();
|
double ratio = getEffectiveRatio();
|
||||||
int longest = m_guideConfiguration.longestFftSize;
|
int longest = m_guideConfiguration.longestFftSize;
|
||||||
|
int channels = m_parameters.channels;
|
||||||
|
|
||||||
m_calculator->setDebugLevel(3);
|
m_calculator->setDebugLevel(3);
|
||||||
|
|
||||||
@@ -213,7 +277,7 @@ R3StretcherImpl::consume()
|
|||||||
|
|
||||||
// Analysis
|
// Analysis
|
||||||
|
|
||||||
for (int c = 0; c < m_parameters.channels; ++c) {
|
for (int c = 0; c < channels; ++c) {
|
||||||
analyseChannel(c, m_prevOuthop);
|
analyseChannel(c, m_prevOuthop);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,7 +285,7 @@ R3StretcherImpl::consume()
|
|||||||
|
|
||||||
for (auto &it : m_channelData[0]->scales) {
|
for (auto &it : m_channelData[0]->scales) {
|
||||||
int fftSize = it.first;
|
int fftSize = it.first;
|
||||||
for (int c = 0; c < m_parameters.channels; ++c) {
|
for (int c = 0; c < channels; ++c) {
|
||||||
auto &cd = m_channelData.at(c);
|
auto &cd = m_channelData.at(c);
|
||||||
auto &scale = cd->scales.at(fftSize);
|
auto &scale = cd->scales.at(fftSize);
|
||||||
m_channelAssembly.mag[c] = scale->mag.data();
|
m_channelAssembly.mag[c] = scale->mag.data();
|
||||||
@@ -241,9 +305,46 @@ R3StretcherImpl::consume()
|
|||||||
|
|
||||||
// Resynthesis
|
// Resynthesis
|
||||||
|
|
||||||
for (int c = 0; c < m_parameters.channels; ++c) {
|
for (int c = 0; c < channels; ++c) {
|
||||||
synthesiseChannel(c, outhop);
|
synthesiseChannel(c, outhop);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resample
|
||||||
|
|
||||||
|
int resampledCount = 0;
|
||||||
|
if (m_resampler) {
|
||||||
|
for (int c = 0; c < channels; ++c) {
|
||||||
|
auto &cd = m_channelData.at(c);
|
||||||
|
m_channelAssembly.mixdown[c] = cd->mixdown.data();
|
||||||
|
m_channelAssembly.resampled[c] = cd->resampled.data();
|
||||||
|
}
|
||||||
|
resampledCount = m_resampler->resample
|
||||||
|
(m_channelAssembly.resampled.data(),
|
||||||
|
m_channelData[0]->resampled.size(),
|
||||||
|
m_channelAssembly.mixdown.data(),
|
||||||
|
outhop,
|
||||||
|
1.0 / m_pitchScale,
|
||||||
|
m_draining && readSpace < longest);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit
|
||||||
|
|
||||||
|
for (int c = 0; c < channels; ++c) {
|
||||||
|
auto &cd = m_channelData.at(c);
|
||||||
|
if (m_resampler) {
|
||||||
|
cd->outbuf->write(cd->resampled.data(), resampledCount);
|
||||||
|
} else {
|
||||||
|
cd->outbuf->write(cd->mixdown.data(), outhop);
|
||||||
|
}
|
||||||
|
|
||||||
|
int readSpace = cd->inbuf->getReadSpace();
|
||||||
|
if (readSpace < m_inhop) {
|
||||||
|
// This should happen only when draining
|
||||||
|
cd->inbuf->skip(readSpace);
|
||||||
|
} else {
|
||||||
|
cd->inbuf->skip(m_inhop);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_prevOuthop = outhop;
|
m_prevOuthop = outhop;
|
||||||
@@ -496,35 +597,23 @@ R3StretcherImpl::synthesiseChannel(int c, int outhop)
|
|||||||
scale->accumulator.data() + toOffset);
|
scale->accumulator.data() + toOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mix and emit this channel
|
// Mix this channel and move the accumulator along
|
||||||
|
|
||||||
double *mixptr = cd->mixdown.data();
|
float *mixptr = cd->mixdown.data();
|
||||||
v_zero(mixptr, outhop);
|
v_zero(mixptr, outhop);
|
||||||
|
|
||||||
for (auto &it : cd->scales) {
|
for (auto &it : cd->scales) {
|
||||||
auto &scale = it.second;
|
auto &scale = it.second;
|
||||||
v_add(mixptr, scale->accumulator.data(), outhop);
|
|
||||||
}
|
|
||||||
|
|
||||||
cd->outbuf->write(mixptr, outhop);
|
|
||||||
|
|
||||||
for (auto &it : cd->scales) {
|
|
||||||
int fftSize = it.first;
|
|
||||||
auto &scale = it.second;
|
|
||||||
double *accptr = scale->accumulator.data();
|
double *accptr = scale->accumulator.data();
|
||||||
|
for (int i = 0; i < outhop; ++i) {
|
||||||
|
mixptr[i] += float(accptr[i]);
|
||||||
|
}
|
||||||
|
|
||||||
int n = scale->accumulator.size() - outhop;
|
int n = scale->accumulator.size() - outhop;
|
||||||
v_move(accptr, accptr + outhop, n);
|
v_move(accptr, accptr + outhop, n);
|
||||||
v_zero(accptr + n, outhop);
|
v_zero(accptr + n, outhop);
|
||||||
}
|
}
|
||||||
|
|
||||||
int readSpace = cd->inbuf->getReadSpace();
|
|
||||||
if (readSpace < m_inhop) {
|
|
||||||
// This should happen only when draining
|
|
||||||
cd->inbuf->skip(readSpace);
|
|
||||||
} else {
|
|
||||||
cd->inbuf->skip(m_inhop);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,64 +57,7 @@ public:
|
|||||||
|
|
||||||
R3StretcherImpl(Parameters parameters,
|
R3StretcherImpl(Parameters parameters,
|
||||||
double initialTimeRatio,
|
double initialTimeRatio,
|
||||||
double initialPitchScale) :
|
double initialPitchScale);
|
||||||
m_parameters(parameters),
|
|
||||||
m_timeRatio(initialTimeRatio),
|
|
||||||
m_pitchScale(initialPitchScale),
|
|
||||||
m_guide(Guide::Parameters(m_parameters.sampleRate, parameters.logger)),
|
|
||||||
m_guideConfiguration(m_guide.getConfiguration()),
|
|
||||||
m_channelAssembly(m_parameters.channels),
|
|
||||||
m_troughPicker(m_guideConfiguration.classificationFftSize / 2 + 1),
|
|
||||||
m_inhop(1),
|
|
||||||
m_prevOuthop(1),
|
|
||||||
m_draining(false)
|
|
||||||
{
|
|
||||||
BinSegmenter::Parameters segmenterParameters
|
|
||||||
(m_guideConfiguration.classificationFftSize,
|
|
||||||
m_parameters.sampleRate);
|
|
||||||
BinClassifier::Parameters classifierParameters
|
|
||||||
(m_guideConfiguration.classificationFftSize / 2 + 1,
|
|
||||||
9, 1, 10, 2.0, 2.0, 1.0e-7);
|
|
||||||
|
|
||||||
int ringBufferSize = m_guideConfiguration.longestFftSize * 2;
|
|
||||||
|
|
||||||
for (int c = 0; c < m_parameters.channels; ++c) {
|
|
||||||
m_channelData.push_back(std::make_shared<ChannelData>
|
|
||||||
(segmenterParameters,
|
|
||||||
classifierParameters,
|
|
||||||
ringBufferSize));
|
|
||||||
for (auto band: m_guideConfiguration.fftBandLimits) {
|
|
||||||
int fftSize = band.fftSize;
|
|
||||||
m_channelData[c]->scales[fftSize] =
|
|
||||||
std::make_shared<ChannelScaleData>
|
|
||||||
(fftSize, m_guideConfiguration.longestFftSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto band: m_guideConfiguration.fftBandLimits) {
|
|
||||||
int fftSize = band.fftSize;
|
|
||||||
GuidedPhaseAdvance::Parameters guidedParameters
|
|
||||||
(fftSize, m_parameters.sampleRate, m_parameters.channels,
|
|
||||||
m_parameters.logger);
|
|
||||||
m_scaleData[fftSize] = std::make_shared<ScaleData>(guidedParameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_calculator = std::unique_ptr<StretchCalculator>
|
|
||||||
(new StretchCalculator(int(round(m_parameters.sampleRate)), //!!! which is a double...
|
|
||||||
1, false)); // no fixed inputIncrement
|
|
||||||
|
|
||||||
Resampler::Parameters resamplerParameters;
|
|
||||||
resamplerParameters.quality = Resampler::FastestTolerable;
|
|
||||||
resamplerParameters.dynamism = Resampler::RatioOftenChanging;
|
|
||||||
resamplerParameters.ratioChange = Resampler::SmoothRatioChange;
|
|
||||||
resamplerParameters.initialSampleRate = m_parameters.sampleRate;
|
|
||||||
resamplerParameters.maxBufferSize = m_guideConfiguration.longestFftSize; //!!!???
|
|
||||||
m_resampler = std::unique_ptr<Resampler>
|
|
||||||
(new Resampler(resamplerParameters, m_parameters.channels));
|
|
||||||
|
|
||||||
calculateHop();
|
|
||||||
}
|
|
||||||
|
|
||||||
~R3StretcherImpl() { }
|
~R3StretcherImpl() { }
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
@@ -189,20 +132,24 @@ protected:
|
|||||||
BinSegmenter::Segmentation prevSegmentation;
|
BinSegmenter::Segmentation prevSegmentation;
|
||||||
BinSegmenter::Segmentation nextSegmentation;
|
BinSegmenter::Segmentation nextSegmentation;
|
||||||
Guide::Guidance guidance;
|
Guide::Guidance guidance;
|
||||||
FixedVector<double> mixdown;
|
FixedVector<float> mixdown;
|
||||||
|
FixedVector<float> resampled;
|
||||||
std::unique_ptr<RingBuffer<float>> inbuf;
|
std::unique_ptr<RingBuffer<float>> inbuf;
|
||||||
std::unique_ptr<RingBuffer<float>> outbuf;
|
std::unique_ptr<RingBuffer<float>> outbuf;
|
||||||
ChannelData(BinSegmenter::Parameters segmenterParameters,
|
ChannelData(BinSegmenter::Parameters segmenterParameters,
|
||||||
BinClassifier::Parameters classifierParameters,
|
BinClassifier::Parameters classifierParameters,
|
||||||
int ringBufferSize) :
|
int longestFftSize,
|
||||||
|
int inRingBufferSize,
|
||||||
|
int outRingBufferSize) :
|
||||||
scales(),
|
scales(),
|
||||||
readahead(segmenterParameters.fftSize),
|
readahead(segmenterParameters.fftSize),
|
||||||
segmenter(new BinSegmenter(segmenterParameters,
|
segmenter(new BinSegmenter(segmenterParameters,
|
||||||
classifierParameters)),
|
classifierParameters)),
|
||||||
segmentation(), prevSegmentation(), nextSegmentation(),
|
segmentation(), prevSegmentation(), nextSegmentation(),
|
||||||
mixdown(ringBufferSize, 0.f), //!!! could be shorter (bound is the max fft size I think)
|
mixdown(longestFftSize, 0.f), // though it could be shorter
|
||||||
inbuf(new RingBuffer<float>(ringBufferSize)),
|
resampled(outRingBufferSize, 0.f),
|
||||||
outbuf(new RingBuffer<float>(ringBufferSize)) { }
|
inbuf(new RingBuffer<float>(inRingBufferSize)),
|
||||||
|
outbuf(new RingBuffer<float>(outRingBufferSize)) { }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ChannelAssembly {
|
struct ChannelAssembly {
|
||||||
@@ -212,9 +159,12 @@ protected:
|
|||||||
FixedVector<double *> phase;
|
FixedVector<double *> phase;
|
||||||
FixedVector<Guide::Guidance *> guidance;
|
FixedVector<Guide::Guidance *> guidance;
|
||||||
FixedVector<double *> outPhase;
|
FixedVector<double *> outPhase;
|
||||||
|
FixedVector<float *> mixdown;
|
||||||
|
FixedVector<float *> resampled;
|
||||||
ChannelAssembly(int channels) :
|
ChannelAssembly(int channels) :
|
||||||
mag(channels, nullptr), phase(channels, nullptr),
|
mag(channels, nullptr), phase(channels, nullptr),
|
||||||
guidance(channels, nullptr), outPhase(channels, nullptr) { }
|
guidance(channels, nullptr), outPhase(channels, nullptr),
|
||||||
|
mixdown(channels, nullptr), resampled(channels, nullptr) { }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ScaleData {
|
struct ScaleData {
|
||||||
|
|||||||
Reference in New Issue
Block a user