changeset 1780:6d6740b075c3

Support optional max frequency setting, useful when we want to store caches of very constrained frequency ranges (as in melodic-range spectrogram, potentially)
author Chris Cannam
date Thu, 12 Sep 2019 11:52:19 +0100
parents 85903b0e9b42
children 008e413f6aaf
files data/model/FFTModel.cpp data/model/FFTModel.h
diffstat 2 files changed, 46 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- 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<DenseTimeValueModel>(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<DenseTimeValueModel>(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
--- 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<float> 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<float, breakfastquay::StlAllocator<float>> fvec;
     typedef std::vector<std::complex<float>,
                         breakfastquay::StlAllocator<std::complex<float>>> 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<sv_frame_t, sv_frame_t>) const;
     fvec getSourceDataUncached(std::pair<sv_frame_t, sv_frame_t>) const;