From 83f2b7607b77f03cfaacfef0ab266c09ac7a55c5 Mon Sep 17 00:00:00 2001 From: Chris Cannam Date: Thu, 26 May 2022 15:08:07 +0100 Subject: [PATCH] An attempt to do the right thing when the hop changes - but this is not enough --- src/finer/Guide.h | 1 - src/finer/R3StretcherImpl.cpp | 98 +++++++++++++++++++++++++++-------- src/finer/R3StretcherImpl.h | 22 +++----- 3 files changed, 82 insertions(+), 39 deletions(-) diff --git a/src/finer/Guide.h b/src/finer/Guide.h index 9c36f34..ef79b62 100644 --- a/src/finer/Guide.h +++ b/src/finer/Guide.h @@ -119,7 +119,6 @@ public: m_maxLower(1100.0), m_maxHigher(7000.0) { double rate = m_parameters.sampleRate; - double nyquist = rate / 2.0; int bandFftSize = roundUp(int(ceil(rate/16.0))); m_configuration.fftBandLimits[0] = diff --git a/src/finer/R3StretcherImpl.cpp b/src/finer/R3StretcherImpl.cpp index d637064..ea5ddf0 100644 --- a/src/finer/R3StretcherImpl.cpp +++ b/src/finer/R3StretcherImpl.cpp @@ -91,6 +91,7 @@ R3StretcherImpl::R3StretcherImpl(Parameters parameters, calculateHop(); + m_prevInhop = m_inhop; m_prevOuthop = int(round(m_inhop * getEffectiveRatio())); 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 R3StretcherImpl::setTimeRatio(double ratio) { @@ -133,6 +164,8 @@ R3StretcherImpl::calculateHop() } m_inhop = int(round(inhop)); + + std::cout << "R3StretcherImpl::calculateHop: inhop " << m_inhop << ", mean outhop " << m_inhop * ratio << std::endl; } double @@ -168,6 +201,7 @@ R3StretcherImpl::reset() size_t R3StretcherImpl::getSamplesRequired() const { + if (available() != 0) return 0; int longest = m_guideConfiguration.longestFftSize; size_t rs = m_channelData[0]->inbuf->getReadSpace(); if (rs < longest) { @@ -260,6 +294,15 @@ R3StretcherImpl::consume() // 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) { // NB our ChannelData, ScaleData, and ChannelScaleData maps @@ -282,7 +325,7 @@ R3StretcherImpl::consume() // Analysis 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 @@ -303,7 +346,7 @@ R3StretcherImpl::consume() m_channelAssembly.phase.data(), m_guideConfiguration, m_channelAssembly.guidance.data(), - inhop, + m_prevInhop, //!!! or inhop? outhop); } @@ -351,11 +394,12 @@ R3StretcherImpl::consume() } } + m_prevInhop = inhop; m_prevOuthop = outhop; } 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 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 - // its current data from the readahead and the readahead from - // further down the long unwindowed frame. - - //!!! (This causes us to get out of sync when inhop changes - is - //!!! it better to vary outhop?) + // the readahead from further down the long unwindowed frame. auto &classifyScale = cd->scales.at(classify); ClassificationReadaheadData &readahead = cd->readahead; @@ -399,6 +439,17 @@ R3StretcherImpl::analyseChannel(int c, int inhop, int prevOuthop) m_scaleData.at(classify)->analysisWindow.cut (buf + (longest - classify) / 2 + inhop, 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 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 // conversion for each FFT size. - // For the classification scale we need magnitudes for the - // full range (polar only in a subset) and we operate in - // the readahead, pulling current values from the existing - // readahead + // For the classification scale we need magnitudes for the full + // range (polar only in a subset) and we operate in the readahead, + // pulling current values from the existing readahead (except + // 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_copy(classifyScale->mag.data(), - readahead.mag.data(), - classifyScale->bufSize); - - v_copy(classifyScale->phase.data(), - readahead.phase.data(), - classifyScale->bufSize); + if (inhop == prevInhop) { + v_copy(classifyScale->mag.data(), + readahead.mag.data(), + classifyScale->bufSize); + v_copy(classifyScale->phase.data(), + readahead.phase.data(), + classifyScale->bufSize); + } m_scaleData.at(classify)->fft.forward(readahead.timeDomain.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 - // and restrict the range for cartesian-polar conversion + // For the others (and the classify if the inhop has changed) we + // operate directly in the scale data and restrict the range for + // cartesian-polar conversion for (auto &it: cd->scales) { int fftSize = it.first; - if (fftSize == classify) continue; + if (fftSize == classify && inhop == prevInhop) continue; auto &scale = it.second; v_fftshift(scale->timeDomain.data(), fftSize); diff --git a/src/finer/R3StretcherImpl.h b/src/finer/R3StretcherImpl.h index 8d549f8..b14160c 100644 --- a/src/finer/R3StretcherImpl.h +++ b/src/finer/R3StretcherImpl.h @@ -182,21 +182,10 @@ protected: synthesisWindowLength(fftSize)), guided(guidedParameters) { } - WindowType analysisWindowShape(int fftSize) { - if (fftSize == 4096) return HannWindow; - else return NiemitaloForwardWindow; - } - 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; - } + WindowType analysisWindowShape(int fftSize); + int analysisWindowLength(int fftSize); + WindowType synthesisWindowShape(int fftSize); + int synthesisWindowLength(int fftSize); }; Parameters m_parameters; @@ -213,12 +202,13 @@ protected: std::unique_ptr m_calculator; std::unique_ptr m_resampler; std::atomic m_inhop; + int m_prevInhop; int m_prevOuthop; bool m_draining; void consume(); 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); double getEffectiveRatio() const {