# HG changeset patch # User Chris Cannam # Date 1562064568 -3600 # Node ID b92bdcd4954b4052bd8023e713b2489a1b7ff929 # Parent 7001b9570e378db392859b32010fcca1ff56d2c5 Update FFT model to ById diff -r 7001b9570e37 -r b92bdcd4954b base/ById.cpp --- 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 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 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()) { diff -r 7001b9570e37 -r b92bdcd4954b base/ById.h --- 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 static std::shared_ptr getAs(Id id) { - if (id.isNone()) return {}; // this id is never issued: avoid locking return AnyById::getAs(id.untyped); } diff -r 7001b9570e37 -r b92bdcd4954b data/model/FFTModel.cpp --- 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(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(m_model); + return (model && model->isOK()); +} + +int +FFTModel::getCompletion() const +{ + int c = 100; + auto model = ModelById::getAs(m_model); + if (model) { + if (model->isReady(&c)) return 100; } + return c; +} + +sv_samplerate_t +FFTModel::getSampleRate() const +{ + auto model = ModelById::getAs(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(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(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); diff -r 7001b9570e37 -r b92bdcd4954b data/model/FFTModel.h --- 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; diff -r 7001b9570e37 -r b92bdcd4954b data/model/test/TestFFTModel.h --- 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>> expectedValues, int expectedWidth) { @@ -88,6 +88,16 @@ } } + ModelId makeMock(std::vector sorts, int length, int pad) { + auto mwm = std::make_shared(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); } }; diff -r 7001b9570e37 -r b92bdcd4954b transform/FeatureExtractionModelTransformer.cpp --- 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,