Per-channel window source buffer, & connect it up

This commit is contained in:
Chris Cannam
2022-08-04 10:31:36 +01:00
parent d1386b0a0c
commit fe9e86bc3c
2 changed files with 37 additions and 41 deletions

View File

@@ -44,7 +44,6 @@ R3Stretcher::R3Stretcher(Parameters parameters,
m_parameters.options & RubberBandStretcher::OptionWindowShort), m_parameters.options & RubberBandStretcher::OptionWindowShort),
m_log), m_log),
m_guideConfiguration(m_guide.getConfiguration()), m_guideConfiguration(m_guide.getConfiguration()),
m_windowSourceBuffer(getWindowSourceBufferLength()),
m_channelAssembly(m_parameters.channels), m_channelAssembly(m_parameters.channels),
m_inhop(1), m_inhop(1),
m_prevInhop(1), m_prevInhop(1),
@@ -91,14 +90,15 @@ R3Stretcher::R3Stretcher(Parameters parameters,
BinClassifier::Parameters classifierParameters BinClassifier::Parameters classifierParameters
(classificationBins, 9, 1, 10, 2.0, 2.0); (classificationBins, 9, 1, 10, 2.0, 2.0);
int inRingBufferSize = getWindowSourceBufferLength() * 2; int inRingBufferSize = getWindowSourceSize() * 2;
int outRingBufferSize = getWindowSourceBufferLength() * 16; int outRingBufferSize = getWindowSourceSize() * 16;
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, m_guideConfiguration.longestFftSize,
getWindowSourceSize(),
inRingBufferSize, inRingBufferSize,
outRingBufferSize)); outRingBufferSize));
for (int b = 0; b < m_guideConfiguration.fftBandLimitCount; ++b) { for (int b = 0; b < m_guideConfiguration.fftBandLimitCount; ++b) {
@@ -450,7 +450,7 @@ R3Stretcher::getPreferredStartPad() const
if (!isRealTime()) { if (!isRealTime()) {
return 0; return 0;
} else { } else {
return m_windowSourceBuffer.size() / 2; return getWindowSourceSize() / 2;
} }
} }
@@ -461,7 +461,7 @@ R3Stretcher::getStartDelay() const
return 0; return 0;
} else { } else {
double factor = 0.5 / m_pitchScale; double factor = 0.5 / m_pitchScale;
return size_t(ceil(m_windowSourceBuffer.size() * factor)); return size_t(ceil(getWindowSourceSize() * factor));
} }
} }
@@ -535,8 +535,8 @@ R3Stretcher::getSamplesRequired() const
{ {
if (available() != 0) return 0; if (available() != 0) return 0;
int rs = m_channelData[0]->inbuf->getReadSpace(); int rs = m_channelData[0]->inbuf->getReadSpace();
if (rs < m_windowSourceBuffer.size()) { if (rs < getWindowSourceSize()) {
return m_windowSourceBuffer.size() - rs; return getWindowSourceSize() - rs;
} else { } else {
return 0; return 0;
} }
@@ -546,7 +546,7 @@ void
R3Stretcher::setMaxProcessSize(size_t n) R3Stretcher::setMaxProcessSize(size_t n)
{ {
size_t oldSize = m_channelData[0]->inbuf->getSize(); size_t oldSize = m_channelData[0]->inbuf->getSize();
size_t newSize = m_windowSourceBuffer.size() + n; size_t newSize = getWindowSourceSize() + n;
if (newSize > oldSize) { if (newSize > oldSize) {
m_log.log(1, "setMaxProcessSize: resizing from and to", oldSize, newSize); m_log.log(1, "setMaxProcessSize: resizing from and to", oldSize, newSize);
@@ -605,7 +605,7 @@ R3Stretcher::process(const float *const *input, size_t samples, bool final)
// don't do this -- it's better to start with a swoosh // don't do this -- it's better to start with a swoosh
// than introduce more latency, and we don't want gaps // than introduce more latency, and we don't want gaps
// when the ratio changes. // when the ratio changes.
int pad = m_windowSourceBuffer.size() / 2; int pad = getWindowSourceSize() / 2;
m_log.log(1, "offline mode: prefilling with", pad); m_log.log(1, "offline mode: prefilling with", pad);
for (int c = 0; c < m_parameters.channels; ++c) { for (int c = 0; c < m_parameters.channels; ++c) {
m_channelData[c]->inbuf->zero(pad); m_channelData[c]->inbuf->zero(pad);
@@ -737,7 +737,7 @@ R3Stretcher::consume()
// the map iterators // the map iterators
int readSpace = cd0->inbuf->getReadSpace(); int readSpace = cd0->inbuf->getReadSpace();
if (readSpace < m_windowSourceBuffer.size()) { if (readSpace < getWindowSourceSize()) {
if (m_mode == ProcessMode::Finished) { if (m_mode == ProcessMode::Finished) {
if (readSpace == 0) { if (readSpace == 0) {
int fill = cd0->scales.at(longest)->accumulatorFill; int fill = cd0->scales.at(longest)->accumulatorFill;
@@ -879,34 +879,33 @@ R3Stretcher::analyseChannel(int c, int inhop, int prevInhop, int prevOuthop)
{ {
Profiler profiler("R3Stretcher::analyseChannel"); Profiler profiler("R3Stretcher::analyseChannel");
int longest = m_guideConfiguration.longestFftSize;
int classify = m_guideConfiguration.classificationFftSize;
auto &cd = m_channelData.at(c); auto &cd = m_channelData.at(c);
process_t *buf = cd->scales.at(longest)->timeDomain.data();
//!!! review int bufSize = cd->windowSource.size();
process_t *buf = cd->windowSource.data();
int readSpace = cd->inbuf->getReadSpace(); int readSpace = cd->inbuf->getReadSpace();
if (readSpace < longest) { if (readSpace < bufSize) {
cd->inbuf->peek(buf, readSpace); cd->inbuf->peek(buf, readSpace);
v_zero(buf + readSpace, longest - readSpace); v_zero(buf + readSpace, bufSize - readSpace);
} else { } else {
cd->inbuf->peek(buf, longest); cd->inbuf->peek(buf, bufSize);
} }
// We have a single unwindowed frame at the longest FFT size // We have an unwindowed time-domain frame in buf that is as long
// ("scale"). Populate the shorter FFT sizes from the centre of // as required for the union of all FFT sizes and readahead
// it, windowing as we copy. The classification scale is handled // hops. Populate the various sizes from it with aligned centres,
// separately because it has readahead, so skip it here as well as // windowing as we copy. The classification scale is handled
// the longest. (In practice this means we are probably only // separately because it has readahead, so skip it here. (In
// populating one scale in multi-window mode, and none at all in // single-window mode that means we do nothing here, since the
// single-window mode) // classification scale is the only one.)
int classify = m_guideConfiguration.classificationFftSize;
for (auto &it: cd->scales) { for (auto &it: cd->scales) {
int fftSize = it.first; int fftSize = it.first;
if (fftSize == classify || fftSize == longest) continue; if (fftSize == classify) continue;
int offset = (longest - fftSize) / 2; int offset = (bufSize - fftSize) / 2;
m_scaleData.at(fftSize)->analysisWindow.cut m_scaleData.at(fftSize)->analysisWindow.cut
(buf + offset, it.second->timeDomain.data()); (buf + offset, it.second->timeDomain.data());
} }
@@ -918,27 +917,22 @@ R3Stretcher::analyseChannel(int c, int inhop, int prevInhop, int prevOuthop)
ClassificationReadaheadData &readahead = cd->readahead; ClassificationReadaheadData &readahead = cd->readahead;
m_scaleData.at(classify)->analysisWindow.cut m_scaleData.at(classify)->analysisWindow.cut
(buf + (longest - classify) / 2 + inhop, (buf + (bufSize - classify) / 2 + inhop,
readahead.timeDomain.data()); readahead.timeDomain.data());
// If inhop has changed since the previous frame, we'll have to // If inhop has changed since the previous frame, we must populate
// populate the classification scale (but for analysis/resynthesis // the classification scale (but for analysis/resynthesis rather
// rather than classification) anew rather than reuse the previous // than classification) anew rather than reuse the previous
// readahead. Pity... // frame's readahead.
bool haveValidReadahead = cd->haveReadahead; bool haveValidReadahead = cd->haveReadahead;
if (inhop != prevInhop) haveValidReadahead = false; if (inhop != prevInhop) haveValidReadahead = false;
if (!haveValidReadahead) { if (!haveValidReadahead) {
m_scaleData.at(classify)->analysisWindow.cut m_scaleData.at(classify)->analysisWindow.cut
(buf + (longest - classify) / 2, (buf + (bufSize - classify) / 2,
classifyScale->timeDomain.data()); classifyScale->timeDomain.data());
} }
// Finally window the longest scale
if (classify != longest) {
m_scaleData.at(longest)->analysisWindow.cut(buf);
}
// FFT shift, forward FFT, and carry out cartesian-polar // FFT shift, forward FFT, and carry out cartesian-polar
// conversion for each FFT size. // conversion for each FFT size.

View File

@@ -185,6 +185,7 @@ protected:
struct ChannelData { struct ChannelData {
std::map<int, std::shared_ptr<ChannelScaleData>> scales; std::map<int, std::shared_ptr<ChannelScaleData>> scales;
FixedVector<process_t> windowSource;
ClassificationReadaheadData readahead; ClassificationReadaheadData readahead;
bool haveReadahead; bool haveReadahead;
std::unique_ptr<BinClassifier> classifier; std::unique_ptr<BinClassifier> classifier;
@@ -203,9 +204,11 @@ protected:
ChannelData(BinSegmenter::Parameters segmenterParameters, ChannelData(BinSegmenter::Parameters segmenterParameters,
BinClassifier::Parameters classifierParameters, BinClassifier::Parameters classifierParameters,
int longestFftSize, int longestFftSize,
int windowSourceSize,
int inRingBufferSize, int inRingBufferSize,
int outRingBufferSize) : int outRingBufferSize) :
scales(), scales(),
windowSource(windowSourceSize, 0.0),
readahead(segmenterParameters.fftSize), readahead(segmenterParameters.fftSize),
haveReadahead(false), haveReadahead(false),
classifier(new BinClassifier(classifierParameters)), classifier(new BinClassifier(classifierParameters)),
@@ -297,7 +300,6 @@ protected:
std::map<int, std::shared_ptr<ScaleData>> m_scaleData; std::map<int, std::shared_ptr<ScaleData>> m_scaleData;
Guide m_guide; Guide m_guide;
Guide::Configuration m_guideConfiguration; Guide::Configuration m_guideConfiguration;
FixedVector<process_t> m_windowSourceBuffer;
ChannelAssembly m_channelAssembly; ChannelAssembly m_channelAssembly;
std::unique_ptr<StretchCalculator> m_calculator; std::unique_ptr<StretchCalculator> m_calculator;
std::unique_ptr<Resampler> m_resampler; std::unique_ptr<Resampler> m_resampler;
@@ -377,7 +379,7 @@ protected:
RubberBandStretcher::OptionWindowShort; RubberBandStretcher::OptionWindowShort;
} }
int getWindowSourceBufferLength() const { int getWindowSourceSize() const {
if (m_guideConfiguration.longestFftSize > if (m_guideConfiguration.longestFftSize >
m_guideConfiguration.classificationFftSize) { m_guideConfiguration.classificationFftSize) {
return m_guideConfiguration.longestFftSize; return m_guideConfiguration.longestFftSize;