# HG changeset patch # User Chris Cannam # Date 1568285539 -3600 # Node ID 6d6740b075c30472eb508dfb6987cf90813fb46c # Parent 85903b0e9b4246f35e3d6292f727fe08d867d2a8 Support optional max frequency setting, useful when we want to store caches of very constrained frequency ranges (as in melodic-range spectrogram, potentially) diff -r 85903b0e9b42 -r 6d6740b075c3 data/model/FFTModel.cpp --- a/data/model/FFTModel.cpp Wed Sep 11 13:20:40 2019 +0100 +++ b/data/model/FFTModel.cpp Thu Sep 12 11:52:19 2019 +0100 @@ -39,6 +39,7 @@ int windowIncrement, int fftSize) : m_model(modelId), + m_sampleRate(0), m_channel(channel), m_windowType(windowType), m_windowSize(windowSize), @@ -46,6 +47,7 @@ m_fftSize(fftSize), m_windower(windowType, windowSize), m_fft(fftSize), + m_maximumFrequency(0.0), m_cacheWriteIndex(0), m_cacheSize(3) { @@ -63,6 +65,8 @@ auto model = ModelById::getAs(m_model); if (model) { + m_sampleRate = model->getSampleRate(); + connect(model.get(), SIGNAL(modelChanged(ModelId)), this, SIGNAL(modelChanged(ModelId))); connect(model.get(), SIGNAL(modelChangedWithin(ModelId, sv_frame_t, sv_frame_t)), @@ -95,9 +99,13 @@ sv_samplerate_t FFTModel::getSampleRate() const { - auto model = ModelById::getAs(m_model); - if (model) return model->getSampleRate(); - else return 0; + return m_sampleRate; +} + +void +FFTModel::setMaximumFrequency(double freq) +{ + m_maximumFrequency = freq; } int @@ -112,7 +120,14 @@ int FFTModel::getHeight() const { - return m_fftSize / 2 + 1; + int height = m_fftSize / 2 + 1; + if (m_maximumFrequency != 0.0) { + int maxBin = int(ceil(m_maximumFrequency * m_fftSize) / m_sampleRate); + if (maxBin >= 0 && maxBin < height) { + return maxBin + 1; + } + } + return height; } QString @@ -120,7 +135,7 @@ { sv_samplerate_t sr = getSampleRate(); if (!sr) return ""; - QString name = tr("%1 Hz").arg((n * sr) / ((getHeight()-1) * 2)); + QString name = tr("%1 Hz").arg((double(n) * sr) / m_fftSize); return name; } @@ -178,6 +193,11 @@ void FFTModel::getValuesAt(int x, int y, float &re, float &im) const { + if (x < 0 || x >= getWidth() || y < 0 || y >= getHeight()) { + re = 0.f; + im = 0.f; + return; + } auto col = getFFTColumn(x); re = col[y].real(); im = col[y].imag(); @@ -339,9 +359,12 @@ return data; } -const FFTModel::cvec & +FFTModel::cvec FFTModel::getFFTColumn(int n) const { + int h = getHeight(); + bool truncate = (h < m_fftSize / 2 + 1); + // The small cache (i.e. the m_cached deque) is for cases where // values are looked up individually, and for e.g. peak-frequency // spectrograms where values from two consecutive columns are @@ -351,7 +374,11 @@ for (const auto &incache : m_cached) { if (incache.n == n) { inSmallCache.hit(); - return incache.col; + if (!truncate) { + return incache.col; + } else { + return cvec(incache.col.begin(), incache.col.begin() + h); + } } } inSmallCache.miss(); @@ -371,7 +398,11 @@ m_cacheWriteIndex = (m_cacheWriteIndex + 1) % m_cacheSize; - return col; + if (!truncate) { + return col; + } else { + return cvec(col.begin(), col.begin() + h); + } } bool diff -r 85903b0e9b42 -r 6d6740b075c3 data/model/FFTModel.h --- a/data/model/FFTModel.h Wed Sep 11 13:20:40 2019 +0100 +++ b/data/model/FFTModel.h Thu Sep 12 11:52:19 2019 +0100 @@ -93,6 +93,9 @@ int getWindowIncrement() const { return m_windowIncrement; } int getFFTSize() const { return m_fftSize; } + void setMaximumFrequency(double freq); + double getMaximumFrequency() const { return m_maximumFrequency; } + //!!! review which of these are ever actually called float getMagnitudeAt(int x, int y) const; @@ -140,6 +143,7 @@ FFTModel &operator=(const FFTModel &) =delete; const ModelId m_model; // a DenseTimeValueModel + sv_samplerate_t m_sampleRate; int m_channel; WindowType m_windowType; int m_windowSize; @@ -147,6 +151,7 @@ int m_fftSize; Window m_windower; mutable breakfastquay::FFT m_fft; + double m_maximumFrequency; int getPeakPickWindowSize(PeakPickType type, sv_samplerate_t sampleRate, int bin, double &dist) const; @@ -163,8 +168,8 @@ typedef std::vector> fvec; typedef std::vector, breakfastquay::StlAllocator>> cvec; - - const cvec &getFFTColumn(int column) const; // returns ref for immediate use only + + cvec getFFTColumn(int column) const; fvec getSourceSamples(int column) const; fvec getSourceData(std::pair) const; fvec getSourceDataUncached(std::pair) const;