changeset 1744:b92bdcd4954b by-id

Update FFT model to ById
author Chris Cannam
date Tue, 02 Jul 2019 11:49:28 +0100
parents 7001b9570e37
children 074b860a7828
files base/ById.cpp base/ById.h data/model/FFTModel.cpp data/model/FFTModel.h data/model/test/TestFFTModel.h transform/FeatureExtractionModelTransformer.cpp
diffstat 6 files changed, 133 insertions(+), 95 deletions(-) [+]
line wrap: on
line diff
--- a/base/ById.cpp	Mon Jul 01 14:16:12 2019 +0100
+++ b/base/ById.cpp	Tue Jul 02 11:49:28 2019 +0100
@@ -60,6 +60,9 @@
     }
         
     void add(int id, std::shared_ptr<WithId> item) {
+        if (id == IdAlloc::NO_ID) {
+            throw std::logic_error("cannot add item with id of NO_ID");
+        }
         QMutexLocker locker(&m_mutex);
         if (m_items.find(id) != m_items.end()) {
             SVCERR << "ById::add: item with id " << id
@@ -73,6 +76,9 @@
     }
 
     void release(int id) {
+        if (id == IdAlloc::NO_ID) {
+            return;
+        }
         QMutexLocker locker(&m_mutex);
         if (m_items.find(id) == m_items.end()) {
             SVCERR << "ById::release: unknown item id " << id << endl;
@@ -82,6 +88,9 @@
     }
     
     std::shared_ptr<WithId> get(int id) const {
+        if (id == IdAlloc::NO_ID) {
+            return {}; // this id cannot be added: avoid locking
+        }
         QMutexLocker locker(&m_mutex);
         const auto &itr = m_items.find(id);
         if (itr != m_items.end()) {
--- a/base/ById.h	Mon Jul 01 14:16:12 2019 +0100
+++ b/base/ById.h	Tue Jul 02 11:49:28 2019 +0100
@@ -27,6 +27,8 @@
 
 #include "XmlExportable.h"
 
+//!!! todo: docs
+
 struct IdAlloc {
 
     // The value NO_ID (-1) is never allocated
@@ -153,7 +155,6 @@
 
     template <typename Derived>
     static std::shared_ptr<Derived> getAs(Id id) {
-        if (id.isNone()) return {}; // this id is never issued: avoid locking
         return AnyById::getAs<Derived>(id.untyped);
     }
 
--- a/data/model/FFTModel.cpp	Mon Jul 01 14:16:12 2019 +0100
+++ b/data/model/FFTModel.cpp	Tue Jul 02 11:49:28 2019 +0100
@@ -32,13 +32,13 @@
 static HitCount inSmallCache("FFTModel: Small FFT cache");
 static HitCount inSourceCache("FFTModel: Source data cache");
 
-FFTModel::FFTModel(const DenseTimeValueModel *model,
+FFTModel::FFTModel(ModelId modelId,
                    int channel,
                    WindowType windowType,
                    int windowSize,
                    int windowIncrement,
                    int fftSize) :
-    m_model(model),
+    m_model(modelId),
     m_channel(channel),
     m_windowType(windowType),
     m_windowSize(windowSize),
@@ -61,32 +61,51 @@
 
     m_fft.initFloat();
 
-    connect(model, SIGNAL(modelChanged()),
-            this, SIGNAL(modelChanged()));
-    connect(model, SIGNAL(modelChangedWithin(sv_frame_t, sv_frame_t)),
-            this, SIGNAL(modelChangedWithin(sv_frame_t, sv_frame_t)));
-    connect(model, SIGNAL(aboutToBeDeleted()),
-            this, SLOT(sourceModelAboutToBeDeleted()));
+    auto model = ModelById::getAs<DenseTimeValueModel>(m_model);
+    if (model) {
+        connect(model.get(), SIGNAL(modelChanged()),
+                this, SIGNAL(modelChanged()));
+        connect(model.get(), SIGNAL(modelChangedWithin(sv_frame_t, sv_frame_t)),
+                this, SIGNAL(modelChangedWithin(sv_frame_t, sv_frame_t)));
+    }
 }
 
 FFTModel::~FFTModel()
 {
 }
 
-void
-FFTModel::sourceModelAboutToBeDeleted()
+bool
+FFTModel::isOK() const
 {
-    if (m_model) {
-        SVDEBUG << "FFTModel[" << this << "]::sourceModelAboutToBeDeleted(" << m_model << ")" << endl;
-        m_model = nullptr;
+    auto model = ModelById::getAs<DenseTimeValueModel>(m_model);
+    return (model && model->isOK());
+}
+
+int
+FFTModel::getCompletion() const
+{
+    int c = 100;
+    auto model = ModelById::getAs<DenseTimeValueModel>(m_model);
+    if (model) {
+        if (model->isReady(&c)) return 100;
     }
+    return c;
+}
+
+sv_samplerate_t
+FFTModel::getSampleRate() const
+{
+    auto model = ModelById::getAs<DenseTimeValueModel>(m_model);
+    if (model) return model->getSampleRate();
+    else return 0;
 }
 
 int
 FFTModel::getWidth() const
 {
-    if (!m_model) return 0;
-    return int((m_model->getEndFrame() - m_model->getStartFrame())
+    auto model = ModelById::getAs<DenseTimeValueModel>(m_model);
+    if (!model) return 0;
+    return int((model->getEndFrame() - model->getStartFrame())
                / m_windowIncrement) + 1;
 }
 
@@ -277,7 +296,8 @@
 {
     Profiler profiler("FFTModel::getSourceDataUncached");
 
-    if (!m_model) return {};
+    auto model = ModelById::getAs<DenseTimeValueModel>(m_model);
+    if (!model) return {};
     
     decltype(range.first) pfx = 0;
     if (range.first < 0) {
@@ -285,14 +305,14 @@
         range = { 0, range.second };
     }
 
-    auto data = m_model->getData(m_channel,
-                                 range.first,
-                                 range.second - range.first);
+    auto data = model->getData(m_channel,
+                               range.first,
+                               range.second - range.first);
 
     if (data.empty()) {
         SVDEBUG << "NOTE: empty source data for range (" << range.first << ","
                 << range.second << ") (model end frame "
-                << m_model->getEndFrame() << ")" << endl;
+                << model->getEndFrame() << ")" << endl;
     }
     
     // don't return a partial frame
@@ -304,7 +324,7 @@
     }
     
     if (m_channel == -1) {
-        int channels = m_model->getChannelCount();
+        int channels = model->getChannelCount();
         if (channels > 1) {
             int n = int(data.size());
             float factor = 1.f / float(channels);
--- a/data/model/FFTModel.h	Mon Jul 01 14:16:12 2019 +0100
+++ b/data/model/FFTModel.h	Tue Jul 02 11:49:28 2019 +0100
@@ -50,7 +50,7 @@
      * unless the channel is -1 in which case merge all available
      * channels.
      */
-    FFTModel(const DenseTimeValueModel *model,
+    FFTModel(ModelId model, // a DenseTimeValueModel
              int channel,
              WindowType windowType,
              int windowSize,
@@ -63,14 +63,12 @@
     int getWidth() const override;
     int getHeight() const override;
     float getValueAt(int x, int y) const override { return getMagnitudeAt(x, y); }
-    bool isOK() const override { return m_model && m_model->isOK(); }
+    bool isOK() const override;
     sv_frame_t getStartFrame() const override { return 0; }
     sv_frame_t getTrueEndFrame() const override {
         return sv_frame_t(getWidth()) * getResolution() + getResolution();
     }
-    sv_samplerate_t getSampleRate() const override {
-        return isOK() ? m_model->getSampleRate() : 0;
-    }
+    sv_samplerate_t getSampleRate() const override;
     int getResolution() const override { return m_windowIncrement; }
     virtual int getYBinCount() const { return getHeight(); }
     float getMinimumLevel() const override { return 0.f; } // Can't provide
@@ -79,13 +77,7 @@
     virtual Column getPhases(int x) const;
     QString getBinName(int n) const override;
     bool shouldUseLogValueScale() const override { return true; }
-    int getCompletion() const override {
-        int c = 100;
-        if (m_model) {
-            if (m_model->isReady(&c)) return 100;
-        }
-        return c;
-    }
+    int getCompletion() const override;
     virtual QString getError() const { return ""; } //!!!???
     virtual sv_frame_t getFillExtent() const { return getEndFrame(); }
     QString toDelimitedDataString(QString, DataExportOptions,
@@ -143,14 +135,11 @@
 
     QString getTypeName() const override { return tr("FFT"); }
 
-public slots:
-    void sourceModelAboutToBeDeleted();
+private:
+    FFTModel(const FFTModel &) =delete;
+    FFTModel &operator=(const FFTModel &) =delete;
 
-private:
-    FFTModel(const FFTModel &); // not implemented
-    FFTModel &operator=(const FFTModel &); // not implemented
-
-    const DenseTimeValueModel *m_model;
+    const ModelId m_model; // a DenseTimeValueModel
     int m_channel;
     WindowType m_windowType;
     int m_windowSize;
--- a/data/model/test/TestFFTModel.h	Mon Jul 01 14:16:12 2019 +0100
+++ b/data/model/test/TestFFTModel.h	Tue Jul 02 11:49:28 2019 +0100
@@ -35,7 +35,7 @@
     Q_OBJECT
 
 private:
-    void test(DenseTimeValueModel *model,
+    void test(ModelId model, // a DenseTimeValueModel
               WindowType window, int windowSize, int windowIncrement, int fftSize,
               int columnNo, vector<vector<complex<float>>> expectedValues,
               int expectedWidth) {
@@ -88,6 +88,16 @@
         }
     }
 
+    ModelId makeMock(std::vector<Sort> sorts, int length, int pad) {
+        auto mwm = std::make_shared<MockWaveModel>(sorts, length, pad);
+        ModelById::add(mwm);
+        return mwm->getId();
+    }
+
+    void releaseMock(ModelId id) {
+        ModelById::release(id);
+    }
+
 private slots:
 
     // NB. FFTModel columns are centred on the sample frame, and in
@@ -101,153 +111,163 @@
     // are those of our expected signal.
     
     void dc_simple_rect() {
-        MockWaveModel mwm({ DC }, 16, 4);
-        test(&mwm, RectangularWindow, 8, 8, 8, 0,
+        auto mwm = makeMock({ DC }, 16, 4);
+        test(mwm, RectangularWindow, 8, 8, 8, 0,
              { { {}, {}, {}, {}, {} } }, 4);
-        test(&mwm, RectangularWindow, 8, 8, 8, 1,
+        test(mwm, RectangularWindow, 8, 8, 8, 1,
              { { { 4.f, 0.f }, {}, {}, {}, {} } }, 4);
-        test(&mwm, RectangularWindow, 8, 8, 8, 2,
+        test(mwm, RectangularWindow, 8, 8, 8, 2,
              { { { 4.f, 0.f }, {}, {}, {}, {} } }, 4);
-        test(&mwm, RectangularWindow, 8, 8, 8, 3,
+        test(mwm, RectangularWindow, 8, 8, 8, 3,
              { { {}, {}, {}, {}, {} } }, 4);
+        releaseMock(mwm);
     }
 
     void dc_simple_hann() {
         // The Hann window function is a simple sinusoid with period
         // equal to twice the window size, and it halves the DC energy
-        MockWaveModel mwm({ DC }, 16, 4);
-        test(&mwm, HanningWindow, 8, 8, 8, 0,
+        auto mwm = makeMock({ DC }, 16, 4);
+        test(mwm, HanningWindow, 8, 8, 8, 0,
              { { {}, {}, {}, {}, {} } }, 4);
-        test(&mwm, HanningWindow, 8, 8, 8, 1,
+        test(mwm, HanningWindow, 8, 8, 8, 1,
              { { { 4.f, 0.f }, { 2.f, 0.f }, {}, {}, {} } }, 4);
-        test(&mwm, HanningWindow, 8, 8, 8, 2,
+        test(mwm, HanningWindow, 8, 8, 8, 2,
              { { { 4.f, 0.f }, { 2.f, 0.f }, {}, {}, {} } }, 4);
-        test(&mwm, HanningWindow, 8, 8, 8, 3,
+        test(mwm, HanningWindow, 8, 8, 8, 3,
              { { {}, {}, {}, {}, {} } }, 4);
+        releaseMock(mwm);
     }
     
     void dc_simple_hann_halfoverlap() {
-        MockWaveModel mwm({ DC }, 16, 4);
-        test(&mwm, HanningWindow, 8, 4, 8, 0,
+        auto mwm = makeMock({ DC }, 16, 4);
+        test(mwm, HanningWindow, 8, 4, 8, 0,
              { { {}, {}, {}, {}, {} } }, 7);
-        test(&mwm, HanningWindow, 8, 4, 8, 2,
+        test(mwm, HanningWindow, 8, 4, 8, 2,
              { { { 4.f, 0.f }, { 2.f, 0.f }, {}, {}, {} } }, 7);
-        test(&mwm, HanningWindow, 8, 4, 8, 3,
+        test(mwm, HanningWindow, 8, 4, 8, 3,
              { { { 4.f, 0.f }, { 2.f, 0.f }, {}, {}, {} } }, 7);
-        test(&mwm, HanningWindow, 8, 4, 8, 6,
+        test(mwm, HanningWindow, 8, 4, 8, 6,
              { { {}, {}, {}, {}, {} } }, 7);
+        releaseMock(mwm);
     }
     
     void sine_simple_rect() {
-        MockWaveModel mwm({ Sine }, 16, 4);
+        auto mwm = makeMock({ Sine }, 16, 4);
         // Sine: output is purely imaginary. Note the sign is flipped
         // (normally the first half of the output would have negative
         // sign for a sine starting at 0) because the model does an
         // FFT shift to centre the phase
-        test(&mwm, RectangularWindow, 8, 8, 8, 0,
+        test(mwm, RectangularWindow, 8, 8, 8, 0,
              { { {}, {}, {}, {}, {} } }, 4);
-        test(&mwm, RectangularWindow, 8, 8, 8, 1,
+        test(mwm, RectangularWindow, 8, 8, 8, 1,
              { { {}, { 0.f, 2.f }, {}, {}, {} } }, 4);
-        test(&mwm, RectangularWindow, 8, 8, 8, 2,
+        test(mwm, RectangularWindow, 8, 8, 8, 2,
              { { {}, { 0.f, 2.f }, {}, {}, {} } }, 4);
-        test(&mwm, RectangularWindow, 8, 8, 8, 3,
+        test(mwm, RectangularWindow, 8, 8, 8, 3,
              { { {}, {}, {}, {}, {} } }, 4);
+        releaseMock(mwm);
     }
     
     void cosine_simple_rect() {
-        MockWaveModel mwm({ Cosine }, 16, 4);
+        auto mwm = makeMock({ Cosine }, 16, 4);
         // Cosine: output is purely real. Note the sign is flipped
         // because the model does an FFT shift to centre the phase
-        test(&mwm, RectangularWindow, 8, 8, 8, 0,
+        test(mwm, RectangularWindow, 8, 8, 8, 0,
              { { {}, {}, {}, {}, {} } }, 4);
-        test(&mwm, RectangularWindow, 8, 8, 8, 1,
+        test(mwm, RectangularWindow, 8, 8, 8, 1,
              { { {}, { -2.f, 0.f }, {}, {}, {} } }, 4);
-        test(&mwm, RectangularWindow, 8, 8, 8, 2,
+        test(mwm, RectangularWindow, 8, 8, 8, 2,
              { { {}, { -2.f, 0.f }, {}, {}, {} } }, 4);
-        test(&mwm, RectangularWindow, 8, 8, 8, 3,
+        test(mwm, RectangularWindow, 8, 8, 8, 3,
              { { {}, {}, {}, {}, {} } }, 4);
+        releaseMock(mwm);
     }
     
     void twochan_simple_rect() {
-        MockWaveModel mwm({ Sine, Cosine }, 16, 4);
+        auto mwm = makeMock({ Sine, Cosine }, 16, 4);
         // Test that the two channels are read and converted separately
-        test(&mwm, RectangularWindow, 8, 8, 8, 0,
+        test(mwm, RectangularWindow, 8, 8, 8, 0,
              {
                  { {}, {}, {}, {}, {} },
                  { {}, {}, {}, {}, {} }
              }, 4);
-        test(&mwm, RectangularWindow, 8, 8, 8, 1,
+        test(mwm, RectangularWindow, 8, 8, 8, 1,
              {
                  { {}, {  0.f, 2.f }, {}, {}, {} },
                  { {}, { -2.f, 0.f }, {}, {}, {} }
              }, 4);
-        test(&mwm, RectangularWindow, 8, 8, 8, 2,
+        test(mwm, RectangularWindow, 8, 8, 8, 2,
              {
                  { {}, {  0.f, 2.f }, {}, {}, {} },
                  { {}, { -2.f, 0.f }, {}, {}, {} }
              }, 4);
-        test(&mwm, RectangularWindow, 8, 8, 8, 3,
+        test(mwm, RectangularWindow, 8, 8, 8, 3,
              {
                  { {}, {}, {}, {}, {} },
                  { {}, {}, {}, {}, {} }
              }, 4);
+        releaseMock(mwm);
     }
     
     void nyquist_simple_rect() {
-        MockWaveModel mwm({ Nyquist }, 16, 4);
+        auto mwm = makeMock({ Nyquist }, 16, 4);
         // Again, the sign is flipped. This has the same amount of
         // energy as the DC example
-        test(&mwm, RectangularWindow, 8, 8, 8, 0,
+        test(mwm, RectangularWindow, 8, 8, 8, 0,
              { { {}, {}, {}, {}, {} } }, 4);
-        test(&mwm, RectangularWindow, 8, 8, 8, 1,
+        test(mwm, RectangularWindow, 8, 8, 8, 1,
              { { {}, {}, {}, {}, { -4.f, 0.f } } }, 4);
-        test(&mwm, RectangularWindow, 8, 8, 8, 2,
+        test(mwm, RectangularWindow, 8, 8, 8, 2,
              { { {}, {}, {}, {}, { -4.f, 0.f } } }, 4);
-        test(&mwm, RectangularWindow, 8, 8, 8, 3,
+        test(mwm, RectangularWindow, 8, 8, 8, 3,
              { { {}, {}, {}, {}, {} } }, 4);
+        releaseMock(mwm);
     }
     
     void dirac_simple_rect() {
-        MockWaveModel mwm({ Dirac }, 16, 4);
+        auto mwm = makeMock({ Dirac }, 16, 4);
         // The window scales by 0.5 and some signs are flipped. Only
         // column 1 has any data (the single impulse).
-        test(&mwm, RectangularWindow, 8, 8, 8, 0,
+        test(mwm, RectangularWindow, 8, 8, 8, 0,
              { { {}, {}, {}, {}, {} } }, 4);
-        test(&mwm, RectangularWindow, 8, 8, 8, 1,
+        test(mwm, RectangularWindow, 8, 8, 8, 1,
              { { { 0.5f, 0.f }, { -0.5f, 0.f }, { 0.5f, 0.f }, { -0.5f, 0.f }, { 0.5f, 0.f } } }, 4);
-        test(&mwm, RectangularWindow, 8, 8, 8, 2,
+        test(mwm, RectangularWindow, 8, 8, 8, 2,
              { { {}, {}, {}, {}, {} } }, 4);
-        test(&mwm, RectangularWindow, 8, 8, 8, 3,
+        test(mwm, RectangularWindow, 8, 8, 8, 3,
              { { {}, {}, {}, {}, {} } }, 4);
+        releaseMock(mwm);
     }
     
     void dirac_simple_rect_2() {
-        MockWaveModel mwm({ Dirac }, 16, 8);
+        auto mwm = makeMock({ Dirac }, 16, 8);
         // With 8 samples padding, the FFT shift places the first
         // Dirac impulse at the start of column 1, thus giving all
         // positive values
-        test(&mwm, RectangularWindow, 8, 8, 8, 0,
+        test(mwm, RectangularWindow, 8, 8, 8, 0,
              { { {}, {}, {}, {}, {} } }, 5);
-        test(&mwm, RectangularWindow, 8, 8, 8, 1,
+        test(mwm, RectangularWindow, 8, 8, 8, 1,
              { { { 0.5f, 0.f }, { 0.5f, 0.f }, { 0.5f, 0.f }, { 0.5f, 0.f }, { 0.5f, 0.f } } }, 5);
-        test(&mwm, RectangularWindow, 8, 8, 8, 2,
+        test(mwm, RectangularWindow, 8, 8, 8, 2,
              { { {}, {}, {}, {}, {} } }, 5);
-        test(&mwm, RectangularWindow, 8, 8, 8, 3,
+        test(mwm, RectangularWindow, 8, 8, 8, 3,
              { { {}, {}, {}, {}, {} } }, 5);
-        test(&mwm, RectangularWindow, 8, 8, 8, 4,
+        test(mwm, RectangularWindow, 8, 8, 8, 4,
              { { {}, {}, {}, {}, {} } }, 5);
+        releaseMock(mwm);
     }
 
     void dirac_simple_rect_halfoverlap() {
-        MockWaveModel mwm({ Dirac }, 16, 4);
-        test(&mwm, RectangularWindow, 8, 4, 8, 0,
+        auto mwm = makeMock({ Dirac }, 16, 4);
+        test(mwm, RectangularWindow, 8, 4, 8, 0,
              { { {}, {}, {}, {}, {} } }, 7);
-        test(&mwm, RectangularWindow, 8, 4, 8, 1,
+        test(mwm, RectangularWindow, 8, 4, 8, 1,
              { { { 0.5f, 0.f }, { 0.5f, 0.f }, { 0.5f, 0.f }, { 0.5f, 0.f }, { 0.5f, 0.f } } }, 7);
-        test(&mwm, RectangularWindow, 8, 4, 8, 2,
+        test(mwm, RectangularWindow, 8, 4, 8, 2,
              { { { 0.5f, 0.f }, { -0.5f, 0.f }, { 0.5f, 0.f }, { -0.5f, 0.f }, { 0.5f, 0.f } } }, 7);
-        test(&mwm, RectangularWindow, 8, 4, 8, 3,
+        test(mwm, RectangularWindow, 8, 4, 8, 3,
              { { {}, {}, {}, {}, {} } }, 7);
+        releaseMock(mwm);
     }
     
 };
--- a/transform/FeatureExtractionModelTransformer.cpp	Mon Jul 01 14:16:12 2019 +0100
+++ b/transform/FeatureExtractionModelTransformer.cpp	Tue Jul 02 11:49:28 2019 +0100
@@ -677,8 +677,7 @@
     if (frequencyDomain) {
         for (int ch = 0; ch < channelCount; ++ch) {
             FFTModel *model = new FFTModel
-//!!!                (input->getId(),
-                (nullptr,
+                (input->getId(),
                  channelCount == 1 ? m_input.getChannel() : ch,
                  primaryTransform.getWindowType(),
                  blockSize,