diff --git a/sdrbase/CMakeLists.txt b/sdrbase/CMakeLists.txt index cff149d99..45198cb76 100644 --- a/sdrbase/CMakeLists.txt +++ b/sdrbase/CMakeLists.txt @@ -93,6 +93,7 @@ set(sdrbase_SOURCES dsp/dspdevicemimoengine.cpp dsp/fftcorr.cpp dsp/fftengine.cpp + dsp/fftfactory.cpp dsp/fftfilt.cpp dsp/fftwindow.cpp dsp/filterrc.cpp @@ -216,6 +217,7 @@ set(sdrbase_HEADERS dsp/dsptypes.h dsp/fftcorr.h dsp/fftengine.h + dsp/fftfactory.h dsp/fftfilt.h dsp/fftwengine.h dsp/fftwindow.h diff --git a/sdrbase/dsp/dspengine.cpp b/sdrbase/dsp/dspengine.cpp index 57f1a519d..18bd33349 100644 --- a/sdrbase/dsp/dspengine.cpp +++ b/sdrbase/dsp/dspengine.cpp @@ -23,13 +23,15 @@ #include "dsp/dspdevicesourceengine.h" #include "dsp/dspdevicesinkengine.h" #include "dsp/dspdevicemimoengine.h" +#include "dsp/fftfactory.h" DSPEngine::DSPEngine() : m_deviceSourceEnginesUIDSequence(0), m_deviceSinkEnginesUIDSequence(0), m_deviceMIMOEnginesUIDSequence(0), m_audioInputDeviceIndex(-1), // default device - m_audioOutputDeviceIndex(-1) // default device + m_audioOutputDeviceIndex(-1), // default device + m_fftFactory(nullptr) { m_dvSerialSupport = false; m_mimoSupport = false; @@ -45,6 +47,10 @@ DSPEngine::~DSPEngine() delete *it; ++it; } + + if (m_fftFactory) { + delete m_fftFactory; + } } Q_GLOBAL_STATIC(DSPEngine, dspEngine) @@ -185,3 +191,13 @@ void DSPEngine::pushMbeFrame( { m_ambeEngine.pushMbeFrame(mbeFrame, mbeRateIndex, mbeVolumeIndex, channels, useHP, upsampling, audioFifo); } + +void DSPEngine::createFFTFactory(const QString& fftWisdomFileName) +{ + m_fftFactory = new FFTFactory(fftWisdomFileName); +} + +void DSPEngine::preAllocateFFTs() +{ + m_fftFactory->preallocate(7, 12, 1, 0); // pre-acllocate forward FFT only 1 per size from 128 to 4096 +} \ No newline at end of file diff --git a/sdrbase/dsp/dspengine.h b/sdrbase/dsp/dspengine.h index c28bda5e9..8dd95c701 100644 --- a/sdrbase/dsp/dspengine.h +++ b/sdrbase/dsp/dspengine.h @@ -32,6 +32,7 @@ class DSPDeviceSourceEngine; class DSPDeviceSinkEngine; class DSPDeviceMIMOEngine; +class FFTFactory; class SDRBASE_API DSPEngine : public QObject { Q_OBJECT @@ -84,6 +85,9 @@ public: const QTimer& getMasterTimer() const { return m_masterTimer; } void setMIMOSupport(bool mimoSupport) { m_mimoSupport = mimoSupport; } bool getMIMOSupport() const { return m_mimoSupport; } + void createFFTFactory(const QString& fftWisdomFileName); + void preAllocateFFTs(); + FFTFactory *getFFTFactory() { return m_fftFactory; } private: std::vector m_deviceSourceEngines; @@ -99,6 +103,7 @@ private: bool m_dvSerialSupport; bool m_mimoSupport; AMBEEngine m_ambeEngine; + FFTFactory *m_fftFactory; }; #endif // INCLUDE_DSPENGINE_H diff --git a/sdrbase/dsp/fftcorr.cpp b/sdrbase/dsp/fftcorr.cpp index 1846dac23..4da63c18a 100644 --- a/sdrbase/dsp/fftcorr.cpp +++ b/sdrbase/dsp/fftcorr.cpp @@ -46,9 +46,9 @@ void fftcorr::init_fft() fftcorr::fftcorr(int len) : flen(len), flen2(len>>1), - fftA(FFTEngine::create()), - fftB(FFTEngine::create()), - fftInvA(FFTEngine::create()) + fftA(FFTEngine::create(QString(""))), // TODO: use factory + fftB(FFTEngine::create(QString(""))), // TODO: use factory + fftInvA(FFTEngine::create(QString(""))) // TODO: use factory { init_fft(); } diff --git a/sdrbase/dsp/fftengine.cpp b/sdrbase/dsp/fftengine.cpp index ddd2cb6b2..9c7821818 100644 --- a/sdrbase/dsp/fftengine.cpp +++ b/sdrbase/dsp/fftengine.cpp @@ -10,13 +10,14 @@ FFTEngine::~FFTEngine() { } -FFTEngine* FFTEngine::create() +FFTEngine* FFTEngine::create(const QString& fftWisdomFileName) { #ifdef USE_FFTW qDebug("FFTEngine::create: using FFTW engine"); - return new FFTWEngine; + return new FFTWEngine(fftWisdomFileName); #elif USE_KISSFFT qDebug("FFTEngine::create: using KissFFT engine"); + (void) fftWisdomFileName; return new KissEngine; #else // USE_KISSFFT qCritical("FFTEngine::create: no engine built"); diff --git a/sdrbase/dsp/fftengine.h b/sdrbase/dsp/fftengine.h index 78028a8d5..8206a9657 100644 --- a/sdrbase/dsp/fftengine.h +++ b/sdrbase/dsp/fftengine.h @@ -1,6 +1,8 @@ #ifndef INCLUDE_FFTENGINE_H #define INCLUDE_FFTENGINE_H +#include + #include "dsp/dsptypes.h" #include "export.h" @@ -14,7 +16,7 @@ public: virtual Complex* in() = 0; virtual Complex* out() = 0; - static FFTEngine* create(); + static FFTEngine* create(const QString& fftWisdomFileName); }; #endif // INCLUDE_FFTENGINE_H diff --git a/sdrbase/dsp/fftfactory.cpp b/sdrbase/dsp/fftfactory.cpp new file mode 100644 index 000000000..39ad208f2 --- /dev/null +++ b/sdrbase/dsp/fftfactory.cpp @@ -0,0 +1,128 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2020 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "fftfactory.h" + +FFTFactory::FFTFactory(const QString& fftwWisdomFileName) : + m_fftwWisdomFileName(fftwWisdomFileName) +{} + +FFTFactory::~FFTFactory() +{ + qDebug("FFTFactory::~FFTFactory: deleting FFTs"); + + for (auto mIt = m_fftEngineBySize.begin(); mIt != m_fftEngineBySize.end(); ++mIt) + { + for (auto eIt = mIt->second.begin(); eIt != mIt->second.end(); ++eIt) { + delete eIt->m_engine; + } + } +} + +void FFTFactory::preallocate( + unsigned int minLog2Size, + unsigned int maxLog2Size, + unsigned int numberFFT, + unsigned int numberInvFFT) +{ + if (minLog2Size <= maxLog2Size) + { + for (unsigned int log2Size = minLog2Size; log2Size <= maxLog2Size; log2Size++) + { + unsigned int fftSize = 1<>(fftSize, std::vector())); + m_invFFTEngineBySize.insert(std::pair>(fftSize, std::vector())); + std::vector& fftEngines = m_fftEngineBySize[fftSize]; + std::vector& invFFTEngines = m_invFFTEngineBySize[fftSize]; + + for (unsigned int i = 0; i < numberFFT; i++) + { + fftEngines.push_back(AllocatedEngine()); + fftEngines.back().m_engine = FFTEngine::create(m_fftwWisdomFileName); + fftEngines.back().m_engine->configure(fftSize, false); + } + + for (unsigned int i = 0; i < numberInvFFT; i++) + { + invFFTEngines.push_back(AllocatedEngine()); + invFFTEngines.back().m_engine = FFTEngine::create(m_fftwWisdomFileName); + invFFTEngines.back().m_engine->configure(fftSize, true); + } + } + } +} + +unsigned int FFTFactory::getEngine(unsigned int fftSize, bool inverse, FFTEngine **engine) +{ + std::map>& enginesBySize = inverse ? + m_invFFTEngineBySize : m_fftEngineBySize; + + if (enginesBySize.find(fftSize) == enginesBySize.end()) + { + enginesBySize.insert(std::pair>(fftSize, std::vector())); + std::vector& engines = enginesBySize[fftSize]; + engines.push_back(AllocatedEngine()); + engines.back().m_inUse = true; + engines.back().m_engine = FFTEngine::create(m_fftwWisdomFileName); + engines.back().m_engine->configure(fftSize, true); + *engine = engines.back().m_engine; + return 0; + } + else + { + unsigned int i = 0; + + for (; i < enginesBySize[fftSize].size(); i++) + { + if (!enginesBySize[fftSize][i].m_inUse) { + break; + } + } + + if (i < enginesBySize[fftSize].size()) + { + enginesBySize[fftSize][i].m_inUse = true; + *engine = enginesBySize[fftSize][i].m_engine; + return i; + } + else + { + std::vector& engines = enginesBySize[fftSize]; + engines.push_back(AllocatedEngine()); + engines.back().m_inUse = true; + engines.back().m_engine = FFTEngine::create(m_fftwWisdomFileName); + engines.back().m_engine->configure(fftSize, true); + *engine = engines.back().m_engine; + return engines.size() - 1; + } + } +} + +void FFTFactory::releaseEngine(unsigned int fftSize, bool inverse, int engineSequence) +{ + std::map>& enginesBySize = inverse ? + m_invFFTEngineBySize : m_fftEngineBySize; + + if (enginesBySize.find(fftSize) != enginesBySize.end()) + { + std::vector& engines = enginesBySize[fftSize]; + + if (engineSequence < engines.size()) { + engines[engineSequence].m_inUse = false; + } + } +} \ No newline at end of file diff --git a/sdrbase/dsp/fftfactory.h b/sdrbase/dsp/fftfactory.h new file mode 100644 index 000000000..654fc9e70 --- /dev/null +++ b/sdrbase/dsp/fftfactory.h @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2020 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef _SDRBASE_FFTWFACTORY_H +#define _SDRBASE_FFTWFACTORY_H + +#include +#include + +#include + +#include "export.h" +#include "dsp/dsptypes.h" +#include "fftengine.h" + +class SDRBASE_API FFTFactory { +public: + FFTFactory(const QString& fftwWisdomFileName); + ~FFTFactory(); + + void preallocate(unsigned int minLog2Size, unsigned int maxLog2Size, unsigned int numberFFT, unsigned int numberInvFFT); + unsigned int getEngine(unsigned int fftSize, bool inverse, FFTEngine **engine); //!< returns an engine sequence + void releaseEngine(unsigned int fftSize, bool inverse, int engineSequence); + +private: + struct AllocatedEngine + { + FFTEngine *m_engine; + bool m_inUse; + + AllocatedEngine() : + m_engine(nullptr), + m_inUse(false) + {} + }; + + QString m_fftwWisdomFileName; + std::map> m_fftEngineBySize; + std::map> m_invFFTEngineBySize; +}; + +#endif // _SDRBASE_FFTWFACTORY_H diff --git a/sdrbase/dsp/fftwengine.cpp b/sdrbase/dsp/fftwengine.cpp index 76b189ead..6dc5f9632 100644 --- a/sdrbase/dsp/fftwengine.cpp +++ b/sdrbase/dsp/fftwengine.cpp @@ -1,7 +1,8 @@ #include #include "dsp/fftwengine.h" -FFTWEngine::FFTWEngine() : +FFTWEngine::FFTWEngine(const QString& fftWisdomFileName) : + m_fftWisdomFileName(fftWisdomFileName), m_plans(), m_currentPlan(NULL) { diff --git a/sdrbase/dsp/fftwengine.h b/sdrbase/dsp/fftwengine.h index 3d269aeb4..303879e84 100644 --- a/sdrbase/dsp/fftwengine.h +++ b/sdrbase/dsp/fftwengine.h @@ -2,6 +2,8 @@ #define INCLUDE_FFTWENGINE_H #include +#include + #include #include #include "dsp/fftengine.h" @@ -9,7 +11,7 @@ class SDRBASE_API FFTWEngine : public FFTEngine { public: - FFTWEngine(); + FFTWEngine(const QString& fftWisdomFileName); ~FFTWEngine(); void configure(int n, bool inverse); @@ -20,6 +22,7 @@ public: protected: static QMutex m_globalPlanMutex; + QString m_fftWisdomFileName; struct Plan { int n; diff --git a/sdrgui/dsp/spectrumvis.cpp b/sdrgui/dsp/spectrumvis.cpp index 97adccd8e..e3b52fca6 100644 --- a/sdrgui/dsp/spectrumvis.cpp +++ b/sdrgui/dsp/spectrumvis.cpp @@ -1,6 +1,8 @@ #include "dsp/spectrumvis.h" #include "gui/glspectrum.h" #include "dsp/dspcommands.h" +#include "dsp/dspengine.h" +#include "dsp/fftfactory.h" #include "util/messagequeue.h" #define MAX_FFT_SIZE 4096 @@ -19,7 +21,8 @@ const Real SpectrumVis::m_mult = (10.0f / log2f(10.0f)); SpectrumVis::SpectrumVis(Real scalef, GLSpectrum* glSpectrum) : BasebandSampleSink(), - m_fft(FFTEngine::create()), + m_fft(nullptr), + m_fftEngineSequence(0), m_fftBuffer(MAX_FFT_SIZE), m_powerSpectrum(MAX_FFT_SIZE), m_fftBufferFill(0), @@ -39,7 +42,8 @@ SpectrumVis::SpectrumVis(Real scalef, GLSpectrum* glSpectrum) : SpectrumVis::~SpectrumVis() { - delete m_fft; + FFTFactory *fftFactory = DSPEngine::instance()->getFFTFactory(); + fftFactory->releaseEngine(m_fftSize, false, m_fftEngineSequence); } void SpectrumVis::configure(MessageQueue* msgQueue, @@ -374,8 +378,10 @@ void SpectrumVis::handleConfigure(int fftSize, m_overlapPercent = overlapPercent; } + FFTFactory *fftFactory = DSPEngine::instance()->getFFTFactory(); + fftFactory->releaseEngine(m_fftSize, false, m_fftEngineSequence); + m_fftEngineSequence = fftFactory->getEngine(fftSize, false, &m_fft); m_fftSize = fftSize; - m_fft->configure(m_fftSize, false); m_window.create(window, m_fftSize); m_overlapSize = (m_fftSize * m_overlapPercent) / 100; m_refillSize = m_fftSize - m_overlapSize; diff --git a/sdrgui/dsp/spectrumvis.h b/sdrgui/dsp/spectrumvis.h index 668f67ac5..678ffffe3 100644 --- a/sdrgui/dsp/spectrumvis.h +++ b/sdrgui/dsp/spectrumvis.h @@ -98,6 +98,7 @@ public: private: FFTEngine* m_fft; FFTWindow m_window; + unsigned int m_fftEngineSequence; std::vector m_fftBuffer; std::vector m_powerSpectrum; diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index 1bf868e57..a507851d5 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -187,6 +187,11 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse m_masterTimer.setTimerType(Qt::PreciseTimer); m_masterTimer.start(50); + splash->showStatusMessage("allocate FFTs...", Qt::white); + splash->showStatusMessage("allocate FFTs...", Qt::white); + m_dspEngine->createFFTFactory(parser.getFFTWFWisdomFileName()); + m_dspEngine->preAllocateFFTs(); + splash->showStatusMessage("load settings...", Qt::white); qDebug() << "MainWindow::MainWindow: load settings..."; diff --git a/sdrsrv/maincore.cpp b/sdrsrv/maincore.cpp index 2305162c6..712610abb 100644 --- a/sdrsrv/maincore.cpp +++ b/sdrsrv/maincore.cpp @@ -63,14 +63,20 @@ MainCore::MainCore(qtwebapp::LoggerWithFile *logger, const MainParser& parser, Q m_settings.setAudioDeviceManager(m_dspEngine->getAudioDeviceManager()); m_settings.setAMBEEngine(m_dspEngine->getAMBEEngine()); + qDebug() << "MainCore::MainCore: create FFT factory..."; + m_dspEngine->createFFTFactory(parser.getFFTWFWisdomFileName()); + + qDebug() << "MainCore::MainCore: load plugins..."; m_pluginManager = new PluginManager(this); m_pluginManager->loadPlugins(QString("pluginssrv")); connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleMessages()), Qt::QueuedConnection); m_masterTimer.start(50); + qDebug() << "MainCore::MainCore: load setings..."; loadSettings(); + qDebug() << "MainCore::MainCore: finishing..."; QString applicationDirPath = QCoreApplication::instance()->applicationDirPath(); m_apiAdapter = new WebAPIAdapterSrv(*this);