Work on proper sizing of internal buffers

This commit is contained in:
Chris Cannam
2023-06-01 10:51:20 +01:00
parent 173c7212f1
commit af6759f74b
2 changed files with 84 additions and 46 deletions

View File

@@ -105,16 +105,19 @@ R3Stretcher::initialise()
int inRingBufferSize = getWindowSourceSize() * 16; int inRingBufferSize = getWindowSourceSize() * 16;
int outRingBufferSize = getWindowSourceSize() * 16; int outRingBufferSize = getWindowSourceSize() * 16;
int hopBufferSize =
2 * std::max(m_limits.maxInhop, m_limits.maxPreferredOuthop);
m_channelData.clear(); m_channelData.clear();
for (int c = 0; c < m_parameters.channels; ++c) { for (int c = 0; c < m_parameters.channels; ++c) {
m_channelData.push_back(std::make_shared<ChannelData> m_channelData.push_back(std::make_shared<ChannelData>
(segmenterParameters, (segmenterParameters,
classifierParameters, classifierParameters,
m_guideConfiguration.longestFftSize,
getWindowSourceSize(), getWindowSourceSize(),
inRingBufferSize, inRingBufferSize,
outRingBufferSize)); outRingBufferSize,
hopBufferSize));
for (int b = 0; b < m_guideConfiguration.fftBandLimitCount; ++b) { for (int b = 0; b < m_guideConfiguration.fftBandLimitCount; ++b) {
const auto &band = m_guideConfiguration.fftBandLimits[b]; const auto &band = m_guideConfiguration.fftBandLimits[b];
int fftSize = band.fftSize; int fftSize = band.fftSize;
@@ -636,17 +639,30 @@ R3Stretcher::getSamplesRequired() const
void void
R3Stretcher::setMaxProcessSize(size_t n) R3Stretcher::setMaxProcessSize(size_t n)
{ {
size_t oldSize = m_channelData[0]->inbuf->getSize(); size_t oldInSize = m_channelData[0]->inbuf->getSize();
size_t newSize = getWindowSourceSize() + n; size_t newInSize = getWindowSourceSize() + n;
if (newSize > oldSize) { size_t oldOutSize = m_channelData[0]->outbuf->getSize();
m_log.log(1, "setMaxProcessSize: resizing from and to", oldSize, newSize); size_t newOutSize = newInSize;
if (newInSize > oldInSize) {
m_log.log(1, "setMaxProcessSize: resizing inbuf from and to", oldInSize, newInSize);
for (int c = 0; c < m_parameters.channels; ++c) { for (int c = 0; c < m_parameters.channels; ++c) {
m_channelData[c]->inbuf = std::unique_ptr<RingBuffer<float>> m_channelData[c]->inbuf = std::unique_ptr<RingBuffer<float>>
(m_channelData[c]->inbuf->resized(newSize)); (m_channelData[c]->inbuf->resized(newInSize));
} }
} else { } else {
m_log.log(1, "setMaxProcessSize: nothing to be done, newSize <= oldSize", newSize, oldSize); m_log.log(1, "setMaxProcessSize: nothing to be done at inbuf, newInSize <= oldInSize", newInSize, oldInSize);
}
if (newOutSize > oldOutSize) {
m_log.log(1, "setMaxProcessSize: resizing outbuf from and to", oldOutSize, newOutSize);
for (int c = 0; c < m_parameters.channels; ++c) {
m_channelData[c]->outbuf = std::unique_ptr<RingBuffer<float>>
(m_channelData[c]->outbuf->resized(newOutSize));
}
} else {
m_log.log(1, "setMaxProcessSize: nothing to be done at outbuf, newOutSize <= oldOutSize", newOutSize, oldOutSize);
} }
} }
@@ -709,17 +725,6 @@ R3Stretcher::process(const float *const *input, size_t samples, bool final)
} }
} }
if (final) {
// We don't distinguish between Finished and "draining, but
// haven't yet delivered all the samples" because the
// distinction is meaningless internally - it only affects
// whether available() finds any samples in the buffer
m_log.log(1, "final is set, entering Finished mode");
m_mode = ProcessMode::Finished;
} else {
m_mode = ProcessMode::Processing;
}
bool resamplingBefore = false; bool resamplingBefore = false;
areWeResampling(&resamplingBefore, nullptr); areWeResampling(&resamplingBefore, nullptr);
@@ -730,7 +735,7 @@ R3Stretcher::process(const float *const *input, size_t samples, bool final)
m_log.log(2, "process: no samples but final specified, consuming"); m_log.log(2, "process: no samples but final specified, consuming");
consume(); consume(true);
} else while (inputIx < int(samples)) { } else while (inputIx < int(samples)) {
@@ -738,7 +743,7 @@ R3Stretcher::process(const float *const *input, size_t samples, bool final)
int ws = m_channelData[0]->inbuf->getWriteSpace(); int ws = m_channelData[0]->inbuf->getWriteSpace();
if (ws == 0) { if (ws == 0) {
consume(); consume(false);
ws = m_channelData[0]->inbuf->getWriteSpace(); ws = m_channelData[0]->inbuf->getWriteSpace();
} }
@@ -772,13 +777,16 @@ R3Stretcher::process(const float *const *input, size_t samples, bool final)
prepareInput(input, inputIx, resampleInput); prepareInput(input, inputIx, resampleInput);
bool finalHop = (final &&
inputIx + resampleInput >= int(samples));
int resampleOutput = m_resampler->resample int resampleOutput = m_resampler->resample
(m_channelAssembly.resampled.data(), (m_channelAssembly.resampled.data(),
maxResampleOutput, maxResampleOutput,
m_channelAssembly.input.data(), m_channelAssembly.input.data(),
resampleInput, resampleInput,
1.0 / m_pitchScale, 1.0 / m_pitchScale,
final); finalHop);
inputIx += resampleInput; inputIx += resampleInput;
@@ -804,7 +812,18 @@ R3Stretcher::process(const float *const *input, size_t samples, bool final)
inputIx += toWrite; inputIx += toWrite;
} }
consume(); consume(final && inputIx >= int(samples));
}
if (final) {
// We don't distinguish between Finished and "draining, but
// haven't yet delivered all the samples" because the
// distinction is meaningless internally - it only affects
// whether available() finds any samples in the buffer
m_log.log(1, "final is set, entering Finished mode");
m_mode = ProcessMode::Finished;
} else {
m_mode = ProcessMode::Processing;
} }
} }
@@ -826,6 +845,8 @@ R3Stretcher::retrieve(float *const *output, size_t samples) const
int got = samples; int got = samples;
m_log.log(2, "retrieve: requested, outbuf has", samples, m_channelData[0]->outbuf->getReadSpace());
for (int c = 0; c < m_parameters.channels; ++c) { for (int c = 0; c < m_parameters.channels; ++c) {
int gotHere = m_channelData[c]->outbuf->read(output[c], got); int gotHere = m_channelData[c]->outbuf->read(output[c], got);
if (gotHere < got) { if (gotHere < got) {
@@ -847,6 +868,8 @@ R3Stretcher::retrieve(float *const *output, size_t samples) const
} }
} }
m_log.log(2, "retrieve: returning, outbuf now has", got, m_channelData[0]->outbuf->getReadSpace());
return got; return got;
} }
@@ -874,7 +897,7 @@ R3Stretcher::prepareInput(const float *const *input, int ix, int n)
} }
void void
R3Stretcher::consume() R3Stretcher::consume(bool final)
{ {
Profiler profiler("R3Stretcher::consume"); Profiler profiler("R3Stretcher::consume");
@@ -927,23 +950,23 @@ R3Stretcher::consume()
auto &cd0 = m_channelData.at(0); auto &cd0 = m_channelData.at(0);
m_log.log(2, "consume: write space and outhop", cd0->outbuf->getWriteSpace(), outhop); m_log.log(2, "consume: write space and outhop", cd0->outbuf->getWriteSpace(), outhop);
while (cd0->outbuf->getWriteSpace() >= outhop) { // NB our ChannelData, ScaleData, and ChannelScaleData maps
// contain shared_ptrs; whenever we retain one of them in a
// variable, we do so by reference to avoid copying the shared_ptr
// (as that is not realtime safe). Same goes for the map iterators
while (true) {
Profiler profiler("R3Stretcher::consume/loop"); Profiler profiler("R3Stretcher::consume/loop");
// NB our ChannelData, ScaleData, and ChannelScaleData maps
// contain shared_ptrs; whenever we retain one of them in a
// variable, we do so by reference to avoid copying the
// shared_ptr (as that is not realtime safe). Same goes for
// the map iterators
int readSpace = cd0->inbuf->getReadSpace(); int readSpace = cd0->inbuf->getReadSpace();
int writeSpace = cd0->outbuf->getWriteSpace();
m_log.log(2, "consume: read space and window source size", readSpace, getWindowSourceSize()); m_log.log(2, "consume: read space and write space", readSpace, writeSpace);
if (readSpace < getWindowSourceSize()) { if (readSpace < getWindowSourceSize()) {
if (m_mode == ProcessMode::Finished) { if (final) {
if (readSpace == 0) { if (readSpace == 0) {
int fill = cd0->scales.at(longest)->accumulatorFill; int fill = cd0->scales.at(longest)->accumulatorFill;
if (fill == 0) { if (fill == 0) {
@@ -957,7 +980,20 @@ R3Stretcher::consume()
break; break;
} }
} }
while (writeSpace < outhop) {
m_log.log(0, "R3Stretcher::process: WARNING: Forced to increase output buffer size. Using smaller process blocks or an artificially larger value for setMaxProcessSize may avoid this");
size_t oldSize = m_channelData[0]->outbuf->getSize();
size_t newSize = oldSize * 2;
m_log.log(0, "R3Stretcher::process: old and new sizes", oldSize, newSize);
for (int c = 0; c < m_parameters.channels; ++c) {
auto newBuf = m_channelData[c]->outbuf->resized(newSize);
m_channelData[c]->outbuf =
std::unique_ptr<RingBuffer<float>>(newBuf);
}
writeSpace = m_channelData[0]->outbuf->getWriteSpace();
}
// Analysis // Analysis
for (int c = 0; c < channels; ++c) { for (int c = 0; c < channels; ++c) {
@@ -1009,9 +1045,9 @@ R3Stretcher::consume()
m_channelAssembly.resampled[c] = cd->resampled.data(); m_channelAssembly.resampled[c] = cd->resampled.data();
} }
bool final = (m_mode == ProcessMode::Finished && bool finalHop = (final &&
readSpace < inhop && readSpace < inhop &&
cd0->scales.at(longest)->accumulatorFill <= outhop); cd0->scales.at(longest)->accumulatorFill <= outhop);
resampledCount = m_resampler->resample resampledCount = m_resampler->resample
(m_channelAssembly.resampled.data(), (m_channelAssembly.resampled.data(),
@@ -1019,7 +1055,7 @@ R3Stretcher::consume()
m_channelAssembly.mixdown.data(), m_channelAssembly.mixdown.data(),
outhop, outhop,
1.0 / m_pitchScale, 1.0 / m_pitchScale,
final); finalHop);
} }
// Emit // Emit
@@ -1041,8 +1077,8 @@ R3Stretcher::consume()
int advanceCount = inhop; int advanceCount = inhop;
if (advanceCount > readSpace) { if (advanceCount > readSpace) {
// This should happen only when draining (Finished) // This should happen only when draining
if (m_mode != ProcessMode::Finished) { if (!final) {
m_log.log(0, "R3Stretcher: WARNING: readSpace < inhop when processing is not yet finished", readSpace, inhop); m_log.log(0, "R3Stretcher: WARNING: readSpace < inhop when processing is not yet finished", readSpace, inhop);
} }
advanceCount = readSpace; advanceCount = readSpace;
@@ -1074,6 +1110,8 @@ R3Stretcher::consume()
m_prevInhop = inhop; m_prevInhop = inhop;
m_prevOuthop = outhop; m_prevOuthop = outhop;
} }
m_log.log(2, "consume: write space reduced to", cd0->outbuf->getWriteSpace());
} }
void void

View File

@@ -228,10 +228,10 @@ protected:
std::unique_ptr<FormantData> formant; std::unique_ptr<FormantData> formant;
ChannelData(BinSegmenter::Parameters segmenterParameters, ChannelData(BinSegmenter::Parameters segmenterParameters,
BinClassifier::Parameters classifierParameters, BinClassifier::Parameters classifierParameters,
int /*!!! longestFftSize */,
int windowSourceSize, int windowSourceSize,
int inRingBufferSize, int inRingBufferSize,
int outRingBufferSize) : int outRingBufferSize,
int hopBufferSize) :
scales(), scales(),
windowSource(windowSourceSize, 0.0), windowSource(windowSourceSize, 0.0),
readahead(segmenterParameters.fftSize), readahead(segmenterParameters.fftSize),
@@ -243,8 +243,8 @@ protected:
BinClassifier::Classification::Residual), BinClassifier::Classification::Residual),
segmenter(new BinSegmenter(segmenterParameters)), segmenter(new BinSegmenter(segmenterParameters)),
segmentation(), prevSegmentation(), nextSegmentation(), segmentation(), prevSegmentation(), nextSegmentation(),
mixdown(inRingBufferSize, 0.f), mixdown(hopBufferSize, 0.f),
resampled(outRingBufferSize, 0.f), resampled(hopBufferSize, 0.f),
inbuf(new RingBuffer<float>(inRingBufferSize)), inbuf(new RingBuffer<float>(inRingBufferSize)),
outbuf(new RingBuffer<float>(outRingBufferSize)), outbuf(new RingBuffer<float>(outRingBufferSize)),
formant(new FormantData(segmenterParameters.fftSize)) { } formant(new FormantData(segmenterParameters.fftSize)) { }
@@ -359,7 +359,7 @@ protected:
void initialise(); void initialise();
void prepareInput(const float *const *input, int ix, int n); void prepareInput(const float *const *input, int ix, int n);
void consume(); void consume(bool final);
void createResampler(); void createResampler();
void calculateHop(); void calculateHop();
void updateRatioFromMap(); void updateRatioFromMap();