An attempt to do the right thing when the hop changes - but this is not enough

This commit is contained in:
Chris Cannam
2022-05-26 15:08:07 +01:00
parent b07f74e5b9
commit 83f2b7607b
3 changed files with 82 additions and 39 deletions

View File

@@ -119,7 +119,6 @@ public:
m_maxLower(1100.0), m_maxHigher(7000.0) m_maxLower(1100.0), m_maxHigher(7000.0)
{ {
double rate = m_parameters.sampleRate; double rate = m_parameters.sampleRate;
double nyquist = rate / 2.0;
int bandFftSize = roundUp(int(ceil(rate/16.0))); int bandFftSize = roundUp(int(ceil(rate/16.0)));
m_configuration.fftBandLimits[0] = m_configuration.fftBandLimits[0] =

View File

@@ -91,6 +91,7 @@ R3StretcherImpl::R3StretcherImpl(Parameters parameters,
calculateHop(); calculateHop();
m_prevInhop = m_inhop;
m_prevOuthop = int(round(m_inhop * getEffectiveRatio())); m_prevOuthop = int(round(m_inhop * getEffectiveRatio()));
if (!m_inhop.is_lock_free()) { if (!m_inhop.is_lock_free()) {
@@ -101,6 +102,36 @@ R3StretcherImpl::R3StretcherImpl(Parameters parameters,
} }
} }
WindowType
R3StretcherImpl::ScaleData::analysisWindowShape(int fftSize)
{
//!!! return HannWindow;
if (fftSize == 4096) return HannWindow;
else return NiemitaloForwardWindow;
}
int
R3StretcherImpl::ScaleData::analysisWindowLength(int fftSize)
{
return fftSize;
}
WindowType
R3StretcherImpl::ScaleData::synthesisWindowShape(int fftSize)
{
//!!! return HannWindow;
if (fftSize == 4096) return HannWindow;
else return NiemitaloReverseWindow;
}
int
R3StretcherImpl::ScaleData::synthesisWindowLength(int fftSize)
{
//!!! return fftSize/2;
if (fftSize == 4096) return fftSize/2;
else return fftSize;
}
void void
R3StretcherImpl::setTimeRatio(double ratio) R3StretcherImpl::setTimeRatio(double ratio)
{ {
@@ -133,6 +164,8 @@ R3StretcherImpl::calculateHop()
} }
m_inhop = int(round(inhop)); m_inhop = int(round(inhop));
std::cout << "R3StretcherImpl::calculateHop: inhop " << m_inhop << ", mean outhop " << m_inhop * ratio << std::endl;
} }
double double
@@ -168,6 +201,7 @@ R3StretcherImpl::reset()
size_t size_t
R3StretcherImpl::getSamplesRequired() const R3StretcherImpl::getSamplesRequired() const
{ {
if (available() != 0) return 0;
int longest = m_guideConfiguration.longestFftSize; int longest = m_guideConfiguration.longestFftSize;
size_t rs = m_channelData[0]->inbuf->getReadSpace(); size_t rs = m_channelData[0]->inbuf->getReadSpace();
if (rs < longest) { if (rs < longest) {
@@ -260,6 +294,15 @@ R3StretcherImpl::consume()
// std::cout << "outhop = " << outhop << std::endl; // std::cout << "outhop = " << outhop << std::endl;
if (inhop != m_prevInhop) {
std::cout << "Note: inhop has changed from " << m_prevInhop
<< " to " << inhop << std::endl;
}
if (outhop != m_prevOuthop) {
std::cout << "Note: outhop has changed from " << m_prevOuthop
<< " to " << outhop << std::endl;
}
while (m_channelData.at(0)->outbuf->getWriteSpace() >= outhop) { while (m_channelData.at(0)->outbuf->getWriteSpace() >= outhop) {
// NB our ChannelData, ScaleData, and ChannelScaleData maps // NB our ChannelData, ScaleData, and ChannelScaleData maps
@@ -282,7 +325,7 @@ R3StretcherImpl::consume()
// Analysis // Analysis
for (int c = 0; c < channels; ++c) { for (int c = 0; c < channels; ++c) {
analyseChannel(c, inhop, m_prevOuthop); analyseChannel(c, inhop, m_prevInhop, m_prevOuthop);
} }
// Phase update. This is synchronised across all channels // Phase update. This is synchronised across all channels
@@ -303,7 +346,7 @@ R3StretcherImpl::consume()
m_channelAssembly.phase.data(), m_channelAssembly.phase.data(),
m_guideConfiguration, m_guideConfiguration,
m_channelAssembly.guidance.data(), m_channelAssembly.guidance.data(),
inhop, m_prevInhop, //!!! or inhop?
outhop); outhop);
} }
@@ -351,11 +394,12 @@ R3StretcherImpl::consume()
} }
} }
m_prevInhop = inhop;
m_prevOuthop = outhop; m_prevOuthop = outhop;
} }
void void
R3StretcherImpl::analyseChannel(int c, int inhop, int prevOuthop) R3StretcherImpl::analyseChannel(int c, int inhop, int prevInhop, int prevOuthop)
{ {
int longest = m_guideConfiguration.longestFftSize; int longest = m_guideConfiguration.longestFftSize;
int classify = m_guideConfiguration.classificationFftSize; int classify = m_guideConfiguration.classificationFftSize;
@@ -387,11 +431,7 @@ R3StretcherImpl::analyseChannel(int c, int inhop, int prevOuthop)
} }
// The classification scale has a one-hop readahead, so populate // The classification scale has a one-hop readahead, so populate
// its current data from the readahead and the readahead from // the readahead from further down the long unwindowed frame.
// further down the long unwindowed frame.
//!!! (This causes us to get out of sync when inhop changes - is
//!!! it better to vary outhop?)
auto &classifyScale = cd->scales.at(classify); auto &classifyScale = cd->scales.at(classify);
ClassificationReadaheadData &readahead = cd->readahead; ClassificationReadaheadData &readahead = cd->readahead;
@@ -399,6 +439,17 @@ R3StretcherImpl::analyseChannel(int c, int inhop, int prevOuthop)
m_scaleData.at(classify)->analysisWindow.cut m_scaleData.at(classify)->analysisWindow.cut
(buf + (longest - classify) / 2 + inhop, (buf + (longest - classify) / 2 + inhop,
readahead.timeDomain.data()); readahead.timeDomain.data());
// If inhop has changed since the previous frame, we'll have to
// populate the classification scale (but for analysis/resynthesis
// rather than classification) anew rather than reuse the previous
// readahead. Pity...
if (inhop != prevInhop) {
m_scaleData.at(classify)->analysisWindow.cut
(buf + (longest - classify) / 2,
classifyScale->timeDomain.data());
}
// Finally window the longest scale // Finally window the longest scale
m_scaleData.at(longest)->analysisWindow.cut(buf); m_scaleData.at(longest)->analysisWindow.cut(buf);
@@ -406,20 +457,22 @@ R3StretcherImpl::analyseChannel(int c, int inhop, int prevOuthop)
// 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.
// For the classification scale we need magnitudes for the // For the classification scale we need magnitudes for the full
// full range (polar only in a subset) and we operate in // range (polar only in a subset) and we operate in the readahead,
// the readahead, pulling current values from the existing // pulling current values from the existing readahead (except
// readahead // where the inhop has changed as above, in which case we need to
// do both readahead and current)
v_fftshift(readahead.timeDomain.data(), classify); v_fftshift(readahead.timeDomain.data(), classify);
v_copy(classifyScale->mag.data(), if (inhop == prevInhop) {
readahead.mag.data(), v_copy(classifyScale->mag.data(),
classifyScale->bufSize); readahead.mag.data(),
classifyScale->bufSize);
v_copy(classifyScale->phase.data(), v_copy(classifyScale->phase.data(),
readahead.phase.data(), readahead.phase.data(),
classifyScale->bufSize); classifyScale->bufSize);
}
m_scaleData.at(classify)->fft.forward(readahead.timeDomain.data(), m_scaleData.at(classify)->fft.forward(readahead.timeDomain.data(),
classifyScale->real.data(), classifyScale->real.data(),
@@ -455,12 +508,13 @@ R3StretcherImpl::analyseChannel(int c, int inhop, int prevOuthop)
} }
} }
// For the others we operate directly in the scale data // For the others (and the classify if the inhop has changed) we
// and restrict the range for cartesian-polar conversion // operate directly in the scale data and restrict the range for
// cartesian-polar conversion
for (auto &it: cd->scales) { for (auto &it: cd->scales) {
int fftSize = it.first; int fftSize = it.first;
if (fftSize == classify) continue; if (fftSize == classify && inhop == prevInhop) continue;
auto &scale = it.second; auto &scale = it.second;
v_fftshift(scale->timeDomain.data(), fftSize); v_fftshift(scale->timeDomain.data(), fftSize);

View File

@@ -182,21 +182,10 @@ protected:
synthesisWindowLength(fftSize)), synthesisWindowLength(fftSize)),
guided(guidedParameters) { } guided(guidedParameters) { }
WindowType analysisWindowShape(int fftSize) { WindowType analysisWindowShape(int fftSize);
if (fftSize == 4096) return HannWindow; int analysisWindowLength(int fftSize);
else return NiemitaloForwardWindow; WindowType synthesisWindowShape(int fftSize);
} int synthesisWindowLength(int fftSize);
int analysisWindowLength(int fftSize) {
return fftSize;
}
WindowType synthesisWindowShape(int fftSize) {
if (fftSize == 4096) return HannWindow;
else return NiemitaloReverseWindow;
}
int synthesisWindowLength(int fftSize) {
if (fftSize == 4096) return fftSize/2;
else return fftSize;
}
}; };
Parameters m_parameters; Parameters m_parameters;
@@ -213,12 +202,13 @@ protected:
std::unique_ptr<StretchCalculator> m_calculator; std::unique_ptr<StretchCalculator> m_calculator;
std::unique_ptr<Resampler> m_resampler; std::unique_ptr<Resampler> m_resampler;
std::atomic<int> m_inhop; std::atomic<int> m_inhop;
int m_prevInhop;
int m_prevOuthop; int m_prevOuthop;
bool m_draining; bool m_draining;
void consume(); void consume();
void calculateHop(); void calculateHop();
void analyseChannel(int channel, int inhop, int prevOuthop); void analyseChannel(int channel, int inhop, int prevInhop, int prevOuthop);
void synthesiseChannel(int channel, int outhop); void synthesiseChannel(int channel, int outhop);
double getEffectiveRatio() const { double getEffectiveRatio() const {