From 455f8a8034a310bae421c7dad3f4d86dcb78a8db Mon Sep 17 00:00:00 2001 From: f4exb Date: Tue, 6 Jan 2026 07:54:29 +0100 Subject: [PATCH] Denoiser feature base commit --- CMakeLists.txt | 2 + CMakePresets.json | 3 +- cmake/Modules/FindRNnoise.cmake | 38 + plugins/feature/CMakeLists.txt | 6 + plugins/feature/denoiser/CMakeLists.txt | 69 ++ plugins/feature/denoiser/denoiser.cpp | 648 ++++++++++++++++++ plugins/feature/denoiser/denoiser.h | 226 ++++++ plugins/feature/denoiser/denoisergui.cpp | 398 +++++++++++ plugins/feature/denoiser/denoisergui.h | 99 +++ plugins/feature/denoiser/denoisergui.ui | 419 +++++++++++ plugins/feature/denoiser/denoiserplugin.cpp | 78 +++ plugins/feature/denoiser/denoiserplugin.h | 48 ++ plugins/feature/denoiser/denoisersettings.cpp | 220 ++++++ plugins/feature/denoiser/denoisersettings.h | 62 ++ .../denoiser/denoiserwebapiadapter.cpp | 51 ++ .../feature/denoiser/denoiserwebapiadapter.h | 49 ++ plugins/feature/denoiser/denoiserworker.cpp | 245 +++++++ plugins/feature/denoiser/denoiserworker.h | 150 ++++ sdrbase/resources/webapi.qrc | 1 + sdrbase/resources/webapi/doc/html2/index.html | 68 +- .../webapi/doc/swagger/include/Denoiser.yaml | 57 ++ .../doc/swagger/include/FeatureActions.yaml | 2 + .../doc/swagger/include/FeatureSettings.yaml | 2 + .../api/swagger/include/Denoiser.yaml | 57 ++ .../api/swagger/include/FeatureActions.yaml | 2 + .../api/swagger/include/FeatureSettings.yaml | 2 + swagger/sdrangel/code/html2/index.html | 68 +- .../code/qt5/client/SWGDenoiserActions.cpp | 131 ++++ .../code/qt5/client/SWGDenoiserActions.h | 64 ++ .../code/qt5/client/SWGDenoiserSettings.cpp | 369 ++++++++++ .../code/qt5/client/SWGDenoiserSettings.h | 126 ++++ .../code/qt5/client/SWGFeatureActions.cpp | 25 + .../code/qt5/client/SWGFeatureActions.h | 7 + .../code/qt5/client/SWGFeatureSettings.cpp | 25 + .../code/qt5/client/SWGFeatureSettings.h | 7 + .../code/qt5/client/SWGModelFactory.h | 12 + 36 files changed, 3833 insertions(+), 3 deletions(-) create mode 100644 cmake/Modules/FindRNnoise.cmake create mode 100644 plugins/feature/denoiser/CMakeLists.txt create mode 100644 plugins/feature/denoiser/denoiser.cpp create mode 100644 plugins/feature/denoiser/denoiser.h create mode 100644 plugins/feature/denoiser/denoisergui.cpp create mode 100644 plugins/feature/denoiser/denoisergui.h create mode 100644 plugins/feature/denoiser/denoisergui.ui create mode 100644 plugins/feature/denoiser/denoiserplugin.cpp create mode 100644 plugins/feature/denoiser/denoiserplugin.h create mode 100644 plugins/feature/denoiser/denoisersettings.cpp create mode 100644 plugins/feature/denoiser/denoisersettings.h create mode 100644 plugins/feature/denoiser/denoiserwebapiadapter.cpp create mode 100644 plugins/feature/denoiser/denoiserwebapiadapter.h create mode 100644 plugins/feature/denoiser/denoiserworker.cpp create mode 100644 plugins/feature/denoiser/denoiserworker.h create mode 100644 sdrbase/resources/webapi/doc/swagger/include/Denoiser.yaml create mode 100644 swagger/sdrangel/api/swagger/include/Denoiser.yaml create mode 100644 swagger/sdrangel/code/qt5/client/SWGDenoiserActions.cpp create mode 100644 swagger/sdrangel/code/qt5/client/SWGDenoiserActions.h create mode 100644 swagger/sdrangel/code/qt5/client/SWGDenoiserSettings.cpp create mode 100644 swagger/sdrangel/code/qt5/client/SWGDenoiserSettings.h diff --git a/CMakeLists.txt b/CMakeLists.txt index fb40e6f46..f1f8bc394 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -152,6 +152,7 @@ option(ENABLE_FEATURE_REMOTECONTROL "Enable feature remote control plugin" ON) option(ENABLE_FEATURE_SKYMAP "Enable feature sky map plugin" ON) option(ENABLE_FEATURE_SID "Enable feature sid plugin" ON) option(ENABLE_FEATURE_MORSEDECODER "Enable feature morsedecoder plugin" ON) +option(ENABLE_FEATURE_DENOISER "Enable feature denoiser plugin" ON) # on windows always build external libraries if(WIN32) @@ -824,6 +825,7 @@ if (NOT ENABLE_EXTERNAL_LIBRARIES OR (ENABLE_EXTERNAL_LIBRARIES STREQUAL "AUTO") find_package(HIDAPI) find_package(FFmpeg COMPONENTS AVCODEC AVFORMAT AVUTIL SWSCALE) find_package(GGMorse) + find_package(RNnoise) # Devices if(ENABLE_AIRSPY) diff --git a/CMakePresets.json b/CMakePresets.json index cb453a905..d0d0a251b 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -3,7 +3,7 @@ "configurePresets": [ { "name": "default", - "binaryDir": "build-default", + "binaryDir": "build", "cacheVariables": { "DEBUG_OUTPUT": "ON", "AIRSPYHF_DIR": "/opt/install/libairspyhf", @@ -29,6 +29,7 @@ "UHD_DIR": "/opt/install/uhd", "XTRX_DIR": "/opt/install/xtrx-images", "GGMORSE_DIR": "/opt/install/ggmorse", + "RNNOISE_DIR": "/opt/install/rnnoise", "CMAKE_INSTALL_PREFIX": "/opt/install/sdrangel" }, "warnings": { diff --git a/cmake/Modules/FindRNnoise.cmake b/cmake/Modules/FindRNnoise.cmake new file mode 100644 index 000000000..92e846a35 --- /dev/null +++ b/cmake/Modules/FindRNnoise.cmake @@ -0,0 +1,38 @@ +if (NOT RNNOISE_FOUND) + INCLUDE(FindPkgConfig) + PKG_CHECK_MODULES(PC_RNNoise "librnnoise") + + FIND_PATH(RNNOISE_INCLUDE_DIR + NAMES rnnoise.h + HINTS ${RNNOISE_DIR}/include + ${PC_RNNOISE_INCLUDE_DIR} + ${CMAKE_INSTALL_PREFIX}/include + PATHS /usr/local/include + /usr/include + ) + + FIND_LIBRARY(RNNOISE_LIBRARIES + NAMES rnnoise librnnoise + HINTS ${RNNOISE_DIR}/lib + ${RNNOISE_DIR}/lib64 + ${PC_RNNOISE_LIBDIR} + ${CMAKE_INSTALL_PREFIX}/lib + ${CMAKE_INSTALL_PREFIX}/lib64 + PATHS /usr/local/lib + /usr/local/lib64 + /usr/lib + /usr/lib64 + ) + + if (RNNOISE_INCLUDE_DIR AND RNNOISE_LIBRARIES) + set(RNNOISE_FOUND TRUE CACHE INTERNAL "RNNoise found") + message(STATUS "Found RNNoise: ${RNNOISE_INCLUDE_DIR}, ${RNNOISE_LIBRARIES}") + else (RNNOISE_INCLUDE_DIR AND RNNOISE_LIBRARIES) + set(RNNOISE_FOUND FALSE CACHE INTERNAL "RNNoise found") + message(STATUS "RNNoise not found") + endif (RNNOISE_INCLUDE_DIR AND RNNOISE_LIBRARIES) + + INCLUDE(FindPackageHandleStandardArgs) + FIND_PACKAGE_HANDLE_STANDARD_ARGS(RNNoise DEFAULT_MSG RNNOISE_LIBRARIES RNNOISE_INCLUDE_DIR) + MARK_AS_ADVANCED(RNNOISE_LIBRARIES RNNOISE_INCLUDE_DIR) +endif (NOT RNNOISE_FOUND) diff --git a/plugins/feature/CMakeLists.txt b/plugins/feature/CMakeLists.txt index 1baf410cb..beab035a8 100644 --- a/plugins/feature/CMakeLists.txt +++ b/plugins/feature/CMakeLists.txt @@ -132,3 +132,9 @@ if (ENABLE_FEATURE_MORSEDECODER AND GGMORSE_FOUND) else() message(STATUS "Not building morsedecoder (ENABLE_FEATURE_MORSEDECODER=${ENABLE_FEATURE_MORSEDECODER} GGMODSE_FOUND=${GGMORSE_FOUND})") endif() + +if (ENABLE_FEATURE_DENOISER AND RNNOISE_FOUND) + add_subdirectory(denoiser) +else() + message(STATUS "Not building denoiser (ENABLE_FEATURE_DENOISER=${ENABLE_FEATURE_DENOISER} RNNOISE_FOUND=${RNNOISE_FOUND})") +endif() diff --git a/plugins/feature/denoiser/CMakeLists.txt b/plugins/feature/denoiser/CMakeLists.txt new file mode 100644 index 000000000..b0da23deb --- /dev/null +++ b/plugins/feature/denoiser/CMakeLists.txt @@ -0,0 +1,69 @@ +project(denoiser) + +set(denoiser_SOURCES + denoiser.cpp + denoisersettings.cpp + denoiserplugin.cpp + denoiserworker.cpp + denoiserwebapiadapter.cpp +) + +set(denoiser_HEADERS + denoiser.h + denoisersettings.h + denoiserplugin.h + denoiserworker.h + denoiserwebapiadapter.h +) + +include_directories( + ${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client + ${RNNOISE_INCLUDE_DIR} +) + +if(NOT SERVER_MODE) + set(denoiser_SOURCES + ${denoiser_SOURCES} + denoisergui.cpp + denoisergui.ui + ) + set(denoiser_HEADERS + ${denoiser_HEADERS} + denoisergui.h + ) + + set(TARGET_NAME ${PLUGINS_PREFIX}featuredenoiser) + set(TARGET_LIB "Qt::Widgets") + set(TARGET_LIB_GUI "sdrgui") + set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR}) +else() + set(TARGET_NAME ${PLUGINSSRV_PREFIX}featuredenoisersrv) + set(TARGET_LIB "") + set(TARGET_LIB_GUI "") + set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR}) +endif() + +if(NOT Qt6_FOUND) + add_library(${TARGET_NAME} ${denoiser_SOURCES}) +else() + qt_add_plugin(${TARGET_NAME} CLASS_NAME DenoiserPlugin ${denoiser_SOURCES}) +endif() + +if(NOT BUILD_SHARED_LIBS) + set_property(GLOBAL APPEND PROPERTY STATIC_PLUGINS_PROPERTY ${TARGET_NAME}) +endif() + +target_link_libraries(${TARGET_NAME} PRIVATE + Qt::Core + ${TARGET_LIB} + sdrbase + ${TARGET_LIB_GUI} + ${RNNOISE_LIBRARIES} +) + +install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER}) + +# Install debug symbols +if (WIN32) + install(FILES $ CONFIGURATIONS Debug RelWithDebInfo DESTINATION ${INSTALL_FOLDER} ) +endif() diff --git a/plugins/feature/denoiser/denoiser.cpp b/plugins/feature/denoiser/denoiser.cpp new file mode 100644 index 000000000..04705d838 --- /dev/null +++ b/plugins/feature/denoiser/denoiser.cpp @@ -0,0 +1,648 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2026 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 +#include +#include +#include + +#include "SWGFeatureSettings.h" +#include "SWGFeatureActions.h" +#include "SWGDeviceState.h" + +#include "dsp/dspcommands.h" +#include "dsp/datafifo.h" +#include "settings/serializable.h" +#include "channel/channelapi.h" +#include "maincore.h" + +#include "denoiserworker.h" +#include "denoiser.h" + +MESSAGE_CLASS_DEFINITION(Denoiser::MsgConfigureDenoiser, Message) +MESSAGE_CLASS_DEFINITION(Denoiser::MsgStartStop, Message) +MESSAGE_CLASS_DEFINITION(Denoiser::MsgReportChannels, Message) +MESSAGE_CLASS_DEFINITION(Denoiser::MsgSelectChannel, Message) +MESSAGE_CLASS_DEFINITION(Denoiser::MsgReportSampleRate, Message) + +const char* const Denoiser::m_featureIdURI = "sdrangel.feature.denoiser"; +const char* const Denoiser::m_featureId = "Denoiser"; + +Denoiser::Denoiser(WebAPIAdapterInterface *webAPIAdapterInterface) : + Feature(m_featureIdURI, webAPIAdapterInterface), + m_thread(nullptr), + m_running(false), + m_worker(nullptr), + m_availableChannelOrFeatureHandler(DenoiserSettings::m_channelURIs), + m_selectedChannel(nullptr), + m_dataPipe(nullptr) +{ + qDebug("Denoiser::Denoiser: webAPIAdapterInterface: %p", webAPIAdapterInterface); + setObjectName(m_featureId); + m_state = StIdle; + m_errorMessage = "Denoiser error"; + m_networkManager = new QNetworkAccessManager(); + QObject::connect( + m_networkManager, + &QNetworkAccessManager::finished, + this, + &Denoiser::networkManagerFinished + ); + QObject::connect( + &m_availableChannelOrFeatureHandler, + &AvailableChannelOrFeatureHandler::channelsOrFeaturesChanged, + this, + &Denoiser::channelsOrFeaturesChanged + ); + m_availableChannelOrFeatureHandler.scanAvailableChannelsAndFeatures(); +} + +Denoiser::~Denoiser() +{ + QObject::disconnect( + &m_availableChannelOrFeatureHandler, + &AvailableChannelOrFeatureHandler::channelsOrFeaturesChanged, + this, + &Denoiser::channelsOrFeaturesChanged + ); + QObject::disconnect( + m_networkManager, + &QNetworkAccessManager::finished, + this, + &Denoiser::networkManagerFinished + ); + delete m_networkManager; + stop(); +} + +void Denoiser::start() +{ + QMutexLocker m_lock(&m_mutex); + + if (m_running) { + return; + } + + qDebug("Denoiser::start"); + m_thread = new QThread(); + m_worker = new DenoiserWorker(); + m_worker->moveToThread(m_thread); + + QObject::connect( + m_thread, + &QThread::started, + m_worker, + &DenoiserWorker::startWork + ); + QObject::connect( + m_thread, + &QThread::finished, + m_worker, + &QObject::deleteLater + ); + QObject::connect( + m_thread, + &QThread::finished, + m_thread, + &QThread::deleteLater + ); + + m_worker->setMessageQueueToFeature(getInputMessageQueue()); + m_worker->startWork(); + m_state = StRunning; + m_thread->start(); + + DenoiserWorker::MsgConfigureDenoiserWorker *msg + = DenoiserWorker::MsgConfigureDenoiserWorker::create(m_settings, QList(), true); + m_worker->getInputMessageQueue()->push(msg); + m_worker->applySampleRate(m_sampleRate); + + if (m_dataPipe) + { + DataFifo *fifo = qobject_cast(m_dataPipe->m_element); + + if (fifo) + { + DenoiserWorker::MsgConnectFifo *msg = DenoiserWorker::MsgConnectFifo::create(fifo, true); + m_worker->getInputMessageQueue()->push(msg); + } + } + + m_running = true; +} + +void Denoiser::stop() +{ + QMutexLocker m_lock(&m_mutex); + + if (!m_running) { + return; + } + + qDebug("Denoiser::stop"); + m_running = false; + + if (m_dataPipe) + { + DataFifo *fifo = qobject_cast(m_dataPipe->m_element); + + if (fifo) + { + DenoiserWorker::MsgConnectFifo *msg = DenoiserWorker::MsgConnectFifo::create(fifo, false); + m_worker->getInputMessageQueue()->push(msg); + } + } + + m_worker->stopWork(); + m_state = StIdle; + m_thread->quit(); + m_thread->wait(); +} + +double Denoiser::getMagSqAvg() const +{ + return m_running ? m_worker->getMagSqAvg() : 0.0; +} + +bool Denoiser::handleMessage(const Message& cmd) +{ + if (MsgConfigureDenoiser::match(cmd)) + { + MsgConfigureDenoiser& cfg = (MsgConfigureDenoiser&) cmd; + qDebug() << "Denoiser::handleMessage: MsgConfigureDenoiser"; + applySettings(cfg.getSettings(), cfg.getSettingsKeys(), cfg.getForce()); + + return true; + } + else if (MsgStartStop::match(cmd)) + { + MsgStartStop& cfg = (MsgStartStop&) cmd; + qDebug() << "Denoiser::handleMessage: MsgStartStop: start:" << cfg.getStartStop(); + + if (cfg.getStartStop()) { + start(); + } else { + stop(); + } + + return true; + } + else if (MsgSelectChannel::match(cmd)) + { + MsgSelectChannel& cfg = (MsgSelectChannel&) cmd; + ChannelAPI *selectedChannel = cfg.getChannel(); + qDebug("Denoiser::handleMessage: MsgSelectChannel: %p %s", + selectedChannel, qPrintable(selectedChannel->objectName())); + setChannel(selectedChannel); + MainCore::MsgChannelDemodQuery *msg = MainCore::MsgChannelDemodQuery::create(); + selectedChannel->getInputMessageQueue()->push(msg); + + return true; + } + else if (MainCore::MsgChannelDemodReport::match(cmd)) + { + qDebug() << "Denoiser::handleMessage: MainCore::MsgChannelDemodReport"; + MainCore::MsgChannelDemodReport& report = (MainCore::MsgChannelDemodReport&) cmd; + + if (report.getChannelAPI() == m_selectedChannel) + { + m_sampleRate = report.getSampleRate(); + + if (m_running) { + m_worker->applySampleRate(m_sampleRate); + } + + if (m_dataPipe) + { + DataFifo *fifo = qobject_cast(m_dataPipe->m_element); + + if (fifo) { + fifo->setSize(2*m_sampleRate); + } + } + + if (getMessageQueueToGUI()) + { + MsgReportSampleRate *msg = MsgReportSampleRate::create(m_sampleRate); + getMessageQueueToGUI()->push(msg); + } + } + + return true; + } + else + { + return false; + } +} + +QByteArray Denoiser::serialize() const +{ + return m_settings.serialize(); +} + +bool Denoiser::deserialize(const QByteArray& data) +{ + if (m_settings.deserialize(data)) + { + MsgConfigureDenoiser *msg = MsgConfigureDenoiser::create(m_settings, QList(), true); + m_inputMessageQueue.push(msg); + return true; + } + else + { + m_settings.resetToDefaults(); + MsgConfigureDenoiser *msg = MsgConfigureDenoiser::create(m_settings, QList(), true); + m_inputMessageQueue.push(msg); + return false; + } +} + +void Denoiser::applySettings(const DenoiserSettings& settings, const QList& settingsKeys, bool force) +{ + qDebug() << "Denoiser::applySettings:" << settings.getDebugString(settingsKeys, force) << " force: " << force; + + if (m_running) + { + DenoiserWorker::MsgConfigureDenoiserWorker *msg = DenoiserWorker::MsgConfigureDenoiserWorker::create( + settings, settingsKeys, force + ); + m_worker->getInputMessageQueue()->push(msg); + } + + + if (settings.m_useReverseAPI) + { + bool fullUpdate = (settingsKeys.contains("useReverseAPI") && settings.m_useReverseAPI) || + settingsKeys.contains("reverseAPIAddress") || + settingsKeys.contains("reverseAPIPort") || + settingsKeys.contains("reverseAPIFeatureSetIndex") || + settingsKeys.contains("m_reverseAPIFeatureIndex"); + webapiReverseSendSettings(settingsKeys, settings, fullUpdate || force); + } + + if (force) { + m_settings = settings; + } else { + m_settings.applySettings(settingsKeys, settings); + } +} + +void Denoiser::channelsOrFeaturesChanged(const QStringList& renameFrom, const QStringList& renameTo) +{ + m_availableChannels = m_availableChannelOrFeatureHandler.getAvailableChannelOrFeatureList(); + notifyUpdate(renameFrom, renameTo); +} + +void Denoiser::notifyUpdate(const QStringList& renameFrom, const QStringList& renameTo) +{ + if (getMessageQueueToGUI()) + { + MsgReportChannels *msg = MsgReportChannels::create(renameFrom, renameTo); + msg->getAvailableChannels() = m_availableChannels; + getMessageQueueToGUI()->push(msg); + } +} + +void Denoiser::getAvailableChannelsReport() +{ + notifyUpdate(QStringList{}, QStringList{}); +} + + +void Denoiser::setChannel(ChannelAPI *selectedChannel) +{ + if ((selectedChannel == m_selectedChannel) || (m_availableChannels.indexOfObject(selectedChannel) == -1)) { + return; + } + + MainCore *mainCore = MainCore::instance(); + + if (m_selectedChannel) + { + ObjectPipe *pipe = mainCore->getDataPipes().unregisterProducerToConsumer(m_selectedChannel, this, "demod"); + DataFifo *fifo = qobject_cast(pipe->m_element); + + if ((fifo) && m_running) + { + DenoiserWorker::MsgConnectFifo *msg = DenoiserWorker::MsgConnectFifo::create(fifo, false); + m_worker->getInputMessageQueue()->push(msg); + } + + ObjectPipe *messagePipe = mainCore->getMessagePipes().unregisterProducerToConsumer(m_selectedChannel, this, "reportdemod"); + + if (messagePipe) + { + MessageQueue *messageQueue = qobject_cast(messagePipe->m_element); + + if (messageQueue) { + disconnect(messageQueue, &MessageQueue::messageEnqueued, this, nullptr); // Have to use nullptr, as slot is a lambda. + } + } + } + + m_dataPipe = mainCore->getDataPipes().registerProducerToConsumer(selectedChannel, this, "demod"); + connect(m_dataPipe, SIGNAL(toBeDeleted(int, QObject*)), this, SLOT(handleDataPipeToBeDeleted(int, QObject*))); + DataFifo *fifo = qobject_cast(m_dataPipe->m_element); + + if (fifo) + { + fifo->setSize(96000); + + if (m_running) + { + DenoiserWorker::MsgConnectFifo *msg = DenoiserWorker::MsgConnectFifo::create(fifo, true); + m_worker->getInputMessageQueue()->push(msg); + } + } + + ObjectPipe *messagePipe = mainCore->getMessagePipes().registerProducerToConsumer(selectedChannel, this, "reportdemod"); + + if (messagePipe) + { + MessageQueue *messageQueue = qobject_cast(messagePipe->m_element); + + if (messageQueue) + { + QObject::connect( + messageQueue, + &MessageQueue::messageEnqueued, + this, + [=](){ this->handleChannelMessageQueue(messageQueue); }, + Qt::QueuedConnection + ); + } + } + + m_selectedChannel = selectedChannel; +} + +int Denoiser::webapiRun(bool run, + SWGSDRangel::SWGDeviceState& response, + QString& errorMessage) +{ + (void) errorMessage; + getFeatureStateStr(*response.getState()); + MsgStartStop *msg = MsgStartStop::create(run); + getInputMessageQueue()->push(msg); + return 202; +} + +int Denoiser::webapiSettingsGet( + SWGSDRangel::SWGFeatureSettings& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setDenoiserSettings(new SWGSDRangel::SWGDenoiserSettings()); + response.getDenoiserSettings()->init(); + webapiFormatFeatureSettings(response, m_settings); + return 200; +} + +int Denoiser::webapiSettingsPutPatch( + bool force, + const QStringList& featureSettingsKeys, + SWGSDRangel::SWGFeatureSettings& response, + QString& errorMessage) +{ + (void) errorMessage; + DenoiserSettings settings = m_settings; + webapiUpdateFeatureSettings(settings, featureSettingsKeys, response); + + MsgConfigureDenoiser *msg = MsgConfigureDenoiser::create(settings, featureSettingsKeys, force); + m_inputMessageQueue.push(msg); + + qDebug("Denoiser::webapiSettingsPutPatch: forward to GUI: %p", m_guiMessageQueue); + if (m_guiMessageQueue) // forward to GUI if any + { + MsgConfigureDenoiser *msgToGUI = MsgConfigureDenoiser::create(settings, featureSettingsKeys, force); + m_guiMessageQueue->push(msgToGUI); + } + + webapiFormatFeatureSettings(response, settings); + + return 200; +} + +void Denoiser::webapiFormatFeatureSettings( + SWGSDRangel::SWGFeatureSettings& response, + const DenoiserSettings& settings) +{ + if (response.getDenoiserSettings()->getTitle()) { + *response.getDenoiserSettings()->getTitle() = settings.m_title; + } else { + response.getDenoiserSettings()->setTitle(new QString(settings.m_title)); + } + + response.getDenoiserSettings()->setRgbColor(settings.m_rgbColor); + response.getDenoiserSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0); + + if (response.getDenoiserSettings()->getReverseApiAddress()) { + *response.getDenoiserSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress; + } else { + response.getDenoiserSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress)); + } + + response.getDenoiserSettings()->setReverseApiPort(settings.m_reverseAPIPort); + response.getDenoiserSettings()->setReverseApiFeatureSetIndex(settings.m_reverseAPIFeatureSetIndex); + response.getDenoiserSettings()->setReverseApiFeatureIndex(settings.m_reverseAPIFeatureIndex); + + if (response.getDenoiserSettings()->getFileRecordName()) { + *response.getDenoiserSettings()->getFileRecordName() = settings.m_fileRecordName; + } else { + response.getDenoiserSettings()->setFileRecordName(new QString(settings.m_fileRecordName)); + } + + response.getDenoiserSettings()->setRecordToFile(settings.m_recordToFile ? 1 : 0); + + if (settings.m_rollupState) + { + if (response.getDenoiserSettings()->getRollupState()) + { + settings.m_rollupState->formatTo(response.getDenoiserSettings()->getRollupState()); + } + else + { + SWGSDRangel::SWGRollupState *swgRollupState = new SWGSDRangel::SWGRollupState(); + settings.m_rollupState->formatTo(swgRollupState); + response.getDenoiserSettings()->setRollupState(swgRollupState); + } + } +} + +void Denoiser::webapiUpdateFeatureSettings( + DenoiserSettings& settings, + const QStringList& featureSettingsKeys, + SWGSDRangel::SWGFeatureSettings& response) +{ + if (featureSettingsKeys.contains("title")) { + settings.m_title = *response.getDenoiserSettings()->getTitle(); + } + if (featureSettingsKeys.contains("rgbColor")) { + settings.m_rgbColor = response.getDenoiserSettings()->getRgbColor(); + } + if (featureSettingsKeys.contains("useReverseAPI")) { + settings.m_useReverseAPI = response.getDenoiserSettings()->getUseReverseApi() != 0; + } + if (featureSettingsKeys.contains("reverseAPIAddress")) { + settings.m_reverseAPIAddress = *response.getDenoiserSettings()->getReverseApiAddress(); + } + if (featureSettingsKeys.contains("reverseAPIPort")) { + settings.m_reverseAPIPort = response.getDenoiserSettings()->getReverseApiPort(); + } + if (featureSettingsKeys.contains("reverseAPIFeatureSetIndex")) { + settings.m_reverseAPIFeatureSetIndex = response.getDenoiserSettings()->getReverseApiFeatureSetIndex(); + } + if (featureSettingsKeys.contains("reverseAPIFeatureIndex")) { + settings.m_reverseAPIFeatureIndex = response.getDenoiserSettings()->getReverseApiFeatureIndex(); + } + if (settings.m_rollupState && featureSettingsKeys.contains("rollupState")) { + settings.m_rollupState->updateFrom(featureSettingsKeys, response.getDenoiserSettings()->getRollupState()); + } + if (featureSettingsKeys.contains("fileRecordName")) { + settings.m_fileRecordName = *response.getDenoiserSettings()->getFileRecordName(); + } + if (featureSettingsKeys.contains("recordToFile")) { + settings.m_recordToFile = response.getDenoiserSettings()->getRecordToFile() != 0; + } +} + +void Denoiser::webapiReverseSendSettings(const QList& featureSettingsKeys, const DenoiserSettings& settings, bool force) +{ + SWGSDRangel::SWGFeatureSettings *swgFeatureSettings = new SWGSDRangel::SWGFeatureSettings(); + // swgFeatureSettings->setOriginatorFeatureIndex(getIndexInDeviceSet()); + // swgFeatureSettings->setOriginatorFeatureSetIndex(getDeviceSetIndex()); + swgFeatureSettings->setFeatureType(new QString("Denoiser")); + swgFeatureSettings->setDenoiserSettings(new SWGSDRangel::SWGDenoiserSettings()); + SWGSDRangel::SWGDenoiserSettings *swgDenoiserSettings = swgFeatureSettings->getDenoiserSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (featureSettingsKeys.contains("title") || force) { + swgDenoiserSettings->setTitle(new QString(settings.m_title)); + } + if (featureSettingsKeys.contains("rgbColor") || force) { + swgDenoiserSettings->setRgbColor(settings.m_rgbColor); + } + if (featureSettingsKeys.contains("fileRecordName")) { + swgDenoiserSettings->setFileRecordName(new QString(settings.m_fileRecordName)); + } + if (featureSettingsKeys.contains("recordToFile")) { + swgDenoiserSettings->setRecordToFile(settings.m_recordToFile ? 1 : 0); + } + if (settings.m_rollupState && (featureSettingsKeys.contains("rollupState") || force)) { + SWGSDRangel::SWGRollupState *swgRollupState = new SWGSDRangel::SWGRollupState(); + settings.m_rollupState->formatTo(swgRollupState); + swgDenoiserSettings->setRollupState(swgRollupState); + } + + QString channelSettingsURL = QString("http://%1:%2/sdrangel/featureset/%3/feature/%4/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIFeatureSetIndex) + .arg(settings.m_reverseAPIFeatureIndex); + m_networkRequest.setUrl(QUrl(channelSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer = new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgFeatureSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + QNetworkReply *reply = m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + buffer->setParent(reply); + + delete swgFeatureSettings; +} + +void Denoiser::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "Denoiser::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + } + else + { + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("Denoiser::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); + } + + reply->deleteLater(); +} + +void Denoiser::handleChannelMessageQueue(MessageQueue* messageQueue) +{ + Message* message; + + while ((message = messageQueue->pop()) != nullptr) + { + if (handleMessage(*message)) { + delete message; + } + } +} + +void Denoiser::handleDataPipeToBeDeleted(int reason, QObject *object) +{ + qDebug("Denoiser::handleDataPipeToBeDeleted: %d %p", reason, object); + + if ((reason == 0) && (m_selectedChannel == object)) + { + DataFifo *fifo = qobject_cast(m_dataPipe->m_element); + + if ((fifo) && m_running) + { + DenoiserWorker::MsgConnectFifo *msg = DenoiserWorker::MsgConnectFifo::create(fifo, false); + m_worker->getInputMessageQueue()->push(msg); + } + + m_selectedChannel = nullptr; + } +} + +int Denoiser::webapiActionsPost( + const QStringList&, + SWGSDRangel::SWGFeatureActions& query, + QString& errorMessage) { + + MainCore* m_core = MainCore::instance(); + auto action = query.getDenoiserActions(); + if (action == nullptr) { + errorMessage = QString("missing DenoiserActions in request"); + return 404; + } + + auto deviceId = action->getDeviceId(); + auto chanId = action->getChannelId(); + + ChannelAPI * chan = m_core->getChannel(deviceId, chanId); + if (chan == nullptr) { + errorMessage = QString("device(%1) or channel (%2) on the device does not exist").arg(deviceId).arg(chanId); + return 404; + } + + MsgSelectChannel *msg = MsgSelectChannel::create(chan); + getInputMessageQueue()->push(msg); + return 200; +} diff --git a/plugins/feature/denoiser/denoiser.h b/plugins/feature/denoiser/denoiser.h new file mode 100644 index 000000000..f95146995 --- /dev/null +++ b/plugins/feature/denoiser/denoiser.h @@ -0,0 +1,226 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2026 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 INCLUDE_FEATURE_DENOISER_H_ +#define INCLUDE_FEATURE_DENOISER_H_ + +#include +#include +#include + +#include "feature/feature.h" +#include "util/message.h" +#include "availablechannelorfeaturehandler.h" + +#include "denoisersettings.h" + +class WebAPIAdapterInterface; +class DenoiserWorker; +class QNetworkAccessManager; +class QNetworkReply; +class QThread; +class ObjectPipe; + +namespace SWGSDRangel { + class SWGDeviceState; +} + +class Denoiser : public Feature +{ + Q_OBJECT +public: + class MsgConfigureDenoiser : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const DenoiserSettings& getSettings() const { return m_settings; } + const QList& getSettingsKeys() const { return m_settingsKeys; } + bool getForce() const { return m_force; } + + static MsgConfigureDenoiser* create(const DenoiserSettings& settings, const QList& settingsKeys, bool force) { + return new MsgConfigureDenoiser(settings, settingsKeys, force); + } + + private: + DenoiserSettings m_settings; + QList m_settingsKeys; + bool m_force; + + MsgConfigureDenoiser(const DenoiserSettings& settings, const QList& settingsKeys, bool force) : + Message(), + m_settings(settings), + m_settingsKeys(settingsKeys), + m_force(force) + { } + }; + + class MsgStartStop : public Message { + MESSAGE_CLASS_DECLARATION + + public: + bool getStartStop() const { return m_startStop; } + + static MsgStartStop* create(bool startStop) { + return new MsgStartStop(startStop); + } + + protected: + bool m_startStop; + + MsgStartStop(bool startStop) : + Message(), + m_startStop(startStop) + { } + }; + + class MsgReportChannels : public Message { + MESSAGE_CLASS_DECLARATION + + public: + AvailableChannelOrFeatureList& getAvailableChannels() { return m_availableChannels; } + const QStringList& getRenameFrom() const { return m_renameFrom; } + const QStringList& getRenameTo() const { return m_renameTo; } + + static MsgReportChannels* create(const QStringList& renameFrom, const QStringList& renameTo) { + return new MsgReportChannels(renameFrom, renameTo); + } + + private: + AvailableChannelOrFeatureList m_availableChannels; + QStringList m_renameFrom; + QStringList m_renameTo; + + MsgReportChannels(const QStringList& renameFrom, const QStringList& renameTo) : + Message(), + m_renameFrom(renameFrom), + m_renameTo(renameTo) + {} + }; + + class MsgSelectChannel : public Message { + MESSAGE_CLASS_DECLARATION + + public: + ChannelAPI *getChannel() { return m_channel; } + static MsgSelectChannel* create(ChannelAPI *channel) { + return new MsgSelectChannel(channel); + } + + protected: + ChannelAPI *m_channel; + + MsgSelectChannel(ChannelAPI *channel) : + Message(), + m_channel(channel) + { } + }; + + class MsgReportSampleRate : public Message { + MESSAGE_CLASS_DECLARATION + + public: + int getSampleRate() const { return m_sampleRate; } + + static MsgReportSampleRate* create(int sampleRate) { + return new MsgReportSampleRate(sampleRate); + } + + private: + int m_sampleRate; + + MsgReportSampleRate(int sampleRate) : + Message(), + m_sampleRate(sampleRate) + {} + }; + + Denoiser(WebAPIAdapterInterface *webAPIAdapterInterface); + virtual ~Denoiser(); + virtual void destroy() { delete this; } + double getMagSqAvg() const; + virtual bool handleMessage(const Message& cmd); + + virtual void getIdentifier(QString& id) const { id = objectName(); } + virtual QString getIdentifier() const { return objectName(); } + virtual void getTitle(QString& title) const { title = m_settings.m_title; } + + virtual QByteArray serialize() const; + virtual bool deserialize(const QByteArray& data); + + virtual int webapiRun(bool run, + SWGSDRangel::SWGDeviceState& response, + QString& errorMessage); + + virtual int webapiSettingsGet( + SWGSDRangel::SWGFeatureSettings& response, + QString& errorMessage); + + virtual int webapiSettingsPutPatch( + bool force, + const QStringList& featureSettingsKeys, + SWGSDRangel::SWGFeatureSettings& response, + QString& errorMessage); + + virtual int webapiActionsPost( + const QStringList& featureActionsKeys, + SWGSDRangel::SWGFeatureActions& query, + QString& errorMessage); + + static void webapiFormatFeatureSettings( + SWGSDRangel::SWGFeatureSettings& response, + const DenoiserSettings& settings); + + static void webapiUpdateFeatureSettings( + DenoiserSettings& settings, + const QStringList& featureSettingsKeys, + SWGSDRangel::SWGFeatureSettings& response); + + void getAvailableChannelsReport(); + + static const char* const m_featureIdURI; + static const char* const m_featureId; + +private: + QThread *m_thread; + QRecursiveMutex m_mutex; + bool m_running; + DenoiserWorker *m_worker; + DenoiserSettings m_settings; + AvailableChannelOrFeatureList m_availableChannels; + AvailableChannelOrFeatureHandler m_availableChannelOrFeatureHandler; + ChannelAPI *m_selectedChannel; + ObjectPipe *m_dataPipe; + int m_sampleRate; + + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; + + void start(); + void stop(); + void applySettings(const DenoiserSettings& settings, const QList& settingsKeys, bool force = false); + void notifyUpdate(const QStringList& renameFrom, const QStringList& renameTo); + void setChannel(ChannelAPI *selectedChannel); + void webapiReverseSendSettings(const QList& featureSettingsKeys, const DenoiserSettings& settings, bool force); + +private slots: + void networkManagerFinished(QNetworkReply *reply); + void handleChannelMessageQueue(MessageQueue *messageQueues); + void channelsOrFeaturesChanged(const QStringList& renameFrom, const QStringList& renameTo); + void handleDataPipeToBeDeleted(int reason, QObject *object); +}; + +#endif // INCLUDE_FEATURE_DENOISER_H_ diff --git a/plugins/feature/denoiser/denoisergui.cpp b/plugins/feature/denoiser/denoisergui.cpp new file mode 100644 index 000000000..4eaf27da9 --- /dev/null +++ b/plugins/feature/denoiser/denoisergui.cpp @@ -0,0 +1,398 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2026 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 +#include + +#include "feature/featureuiset.h" +#include "gui/basicfeaturesettingsdialog.h" +#include "gui/dialpopup.h" +#include "gui/dialogpositioner.h" +#include "util/db.h" +#include "maincore.h" + +#include "ui_denoisergui.h" +#include "denoiser.h" +#include "denoisergui.h" + +DenoiserGUI* DenoiserGUI::create(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature) +{ + DenoiserGUI* gui = new DenoiserGUI(pluginAPI, featureUISet, feature); + return gui; +} + +void DenoiserGUI::destroy() +{ + delete this; +} + +void DenoiserGUI::resetToDefaults() +{ + m_settings.resetToDefaults(); + displaySettings(); + applySettings(true); +} + +QByteArray DenoiserGUI::serialize() const +{ + return m_settings.serialize(); +} + +bool DenoiserGUI::deserialize(const QByteArray& data) +{ + if (m_settings.deserialize(data)) + { + m_feature->setWorkspaceIndex(m_settings.m_workspaceIndex); + displaySettings(); + applySettings(true); + return true; + } + else + { + resetToDefaults(); + return false; + } +} + +bool DenoiserGUI::handleMessage(const Message& message) +{ + if (Denoiser::MsgConfigureDenoiser::match(message)) + { + qDebug("DenoiserGUI::handleMessage: Denoiser::MsgConfigureDenoiser"); + const Denoiser::MsgConfigureDenoiser& cfg = (Denoiser::MsgConfigureDenoiser&) message; + + if (cfg.getForce()) { + m_settings = cfg.getSettings(); + } else { + m_settings.applySettings(cfg.getSettingsKeys(), cfg.getSettings()); + } + + blockApplySettings(true); + displaySettings(); + blockApplySettings(false); + + return true; + } + else if (Denoiser::MsgReportChannels::match(message)) + { + qDebug("DenoiserGUI::handleMessage: Denoiser::MsgReportChannels"); + Denoiser::MsgReportChannels& report = (Denoiser::MsgReportChannels&) message; + m_availableChannels = report.getAvailableChannels(); + updateChannelList(); + + return true; + } + else if (Denoiser::MsgReportSampleRate::match(message)) + { + Denoiser::MsgReportSampleRate& report = (Denoiser::MsgReportSampleRate&) message; + int sampleRate = report.getSampleRate(); + qDebug("DenoiserGUI::handleMessage: Denoiser::MsgReportSampleRate: %d", sampleRate); + displaySampleRate(sampleRate); + m_sampleRate = sampleRate; + + return true; + } + + return false; +} + +void DenoiserGUI::handleInputMessages() +{ + Message* message; + + while ((message = getInputMessageQueue()->pop())) + { + if (handleMessage(*message)) { + delete message; + } + } +} + +void DenoiserGUI::onWidgetRolled(QWidget* widget, bool rollDown) +{ + (void) widget; + (void) rollDown; + + RollupContents *rollupContents = getRollupContents(); + + rollupContents->saveState(m_rollupState); + applySettings(); +} + +DenoiserGUI::DenoiserGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature, QWidget* parent) : + FeatureGUI(parent), + ui(new Ui::DenoiserGUI), + m_pluginAPI(pluginAPI), + m_featureUISet(featureUISet), + m_sampleRate(48000), + m_doApplySettings(true), + m_lastFeatureState(0), + m_selectedChannel(nullptr) +{ + m_feature = feature; + setAttribute(Qt::WA_DeleteOnClose, true); + m_helpURL = "plugins/feature/denoiser/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + + m_denoiser = reinterpret_cast(feature); + m_denoiser->setMessageQueueToGUI(&m_inputMessageQueue); + + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); + connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); + + connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); + m_statusTimer.start(1000); + + displaySampleRate(m_sampleRate); + + connect(&MainCore::instance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); + + m_settings.setRollupState(&m_rollupState); + + displaySettings(); + applySettings(true); + makeUIConnections(); + DialPopup::addPopupsToChildDials(this); + m_resizer.enableChildMouseTracking(); + m_denoiser->getAvailableChannelsReport(); +} + +DenoiserGUI::~DenoiserGUI() +{ + delete ui; +} + +void DenoiserGUI::blockApplySettings(bool block) +{ + m_doApplySettings = !block; +} + +void DenoiserGUI::setWorkspaceIndex(int index) +{ + m_settings.m_workspaceIndex = index; + m_feature->setWorkspaceIndex(index); +} + +void DenoiserGUI::displaySettings() +{ + setTitleColor(m_settings.m_rgbColor); + setWindowTitle(m_settings.m_title); + setTitle(m_settings.m_title); + blockApplySettings(true); + ui->record->setChecked(m_settings.m_recordToFile); + ui->fileNameText->setText(m_settings.m_fileRecordName); + ui->showFileDialog->setEnabled(!m_settings.m_recordToFile); + getRollupContents()->restoreState(m_rollupState); + blockApplySettings(false); +} + +void DenoiserGUI::displaySampleRate(int sampleRate) +{ + QString s = QString::number(sampleRate/1000.0, 'f', 1); + ui->sinkSampleRateText->setText(tr("%1 kS/s").arg(s)); +} + +void DenoiserGUI::updateChannelList() +{ + ui->channels->blockSignals(true); + ui->channels->clear(); + + AvailableChannelOrFeatureList::const_iterator it = m_availableChannels.begin(); + int selectedItem = -1; + + for (int i = 0; it != m_availableChannels.end(); ++it, i++) + { + ui->channels->addItem(it->getLongId()); + + if (it->m_object == m_selectedChannel) { + selectedItem = i; + } + } + + ui->channels->blockSignals(false); + + if (m_availableChannels.size() > 0) + { + if (selectedItem >= 0) { + ui->channels->setCurrentIndex(selectedItem); + } else { + ui->channels->setCurrentIndex(0); + } + } +} + +void DenoiserGUI::onMenuDialogCalled(const QPoint &p) +{ + if (m_contextMenuType == ContextMenuType::ContextMenuChannelSettings) + { + BasicFeatureSettingsDialog dialog(this); + dialog.setTitle(m_settings.m_title); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIFeatureSetIndex(m_settings.m_reverseAPIFeatureSetIndex); + dialog.setReverseAPIFeatureIndex(m_settings.m_reverseAPIFeatureIndex); + dialog.setDefaultTitle(m_displayedName); + + dialog.move(p); + new DialogPositioner(&dialog, false); + dialog.exec(); + + m_settings.m_title = dialog.getTitle(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIFeatureSetIndex = dialog.getReverseAPIFeatureSetIndex(); + m_settings.m_reverseAPIFeatureIndex = dialog.getReverseAPIFeatureIndex(); + + setTitle(m_settings.m_title); + setTitleColor(m_settings.m_rgbColor); + + m_settingsKeys.append("title"); + m_settingsKeys.append("rgbColor"); + m_settingsKeys.append("useReverseAPI"); + m_settingsKeys.append("reverseAPIAddress"); + m_settingsKeys.append("reverseAPIPort"); + m_settingsKeys.append("reverseAPIFeatureSetIndex"); + m_settingsKeys.append("reverseAPIFeatureIndex"); + + applySettings(); + } + + resetContextMenuType(); +} + +void DenoiserGUI::on_startStop_toggled(bool checked) +{ + if (m_doApplySettings) + { + Denoiser::MsgStartStop *message = Denoiser::MsgStartStop::create(checked); + m_denoiser->getInputMessageQueue()->push(message); + } +} + +void DenoiserGUI::on_channels_currentIndexChanged(int index) +{ + if ((index >= 0) && (index < m_availableChannels.size())) + { + m_selectedChannel = qobject_cast(m_availableChannels[index].m_object); + Denoiser::MsgSelectChannel *msg = Denoiser::MsgSelectChannel::create(m_selectedChannel); + m_denoiser->getInputMessageQueue()->push(msg); + } +} + +void DenoiserGUI::on_channelApply_clicked() +{ + if (ui->channels->count() > 0) { + on_channels_currentIndexChanged(ui->channels->currentIndex()); + } +} + +void DenoiserGUI::on_record_toggled(bool checked) +{ + ui->showFileDialog->setEnabled(!checked); + m_settings.m_recordToFile = checked; + m_settingsKeys.append("recordToFile"); + applySettings(); +} + +void DenoiserGUI::on_showFileDialog_clicked(bool checked) +{ + (void) checked; + QFileDialog fileDialog( + this, + tr("Save record file"), + m_settings.m_fileRecordName, + tr("WAV Files (*.wav)") + ); + + fileDialog.setOptions(QFileDialog::DontUseNativeDialog); + fileDialog.setFileMode(QFileDialog::AnyFile); + QStringList fileNames; + + if (fileDialog.exec()) + { + fileNames = fileDialog.selectedFiles(); + + if (fileNames.size() > 0) + { + m_settings.m_fileRecordName = fileNames.at(0); + ui->fileNameText->setText(m_settings.m_fileRecordName); + m_settingsKeys.append("fileRecordName"); + applySettings(); + } + } +} + +void DenoiserGUI::tick() +{ + m_channelPowerAvg(m_denoiser->getMagSqAvg()); + double powDb = CalcDb::dbPower((double) m_channelPowerAvg); + ui->channelPower->setText(tr("%1 dB").arg(powDb, 0, 'f', 1)); +} + +void DenoiserGUI::updateStatus() +{ + int state = m_denoiser->getState(); + + if (m_lastFeatureState != state) + { + switch (state) + { + case Feature::StNotStarted: + ui->startStop->setStyleSheet("QToolButton { background:rgb(79,79,79); }"); + break; + case Feature::StIdle: + ui->startStop->setStyleSheet("QToolButton { background-color : blue; }"); + break; + case Feature::StRunning: + ui->startStop->setStyleSheet("QToolButton { background-color : green; }"); + break; + case Feature::StError: + ui->startStop->setStyleSheet("QToolButton { background-color : red; }"); + QMessageBox::information(this, tr("Message"), m_denoiser->getErrorMessage()); + break; + default: + break; + } + + m_lastFeatureState = state; + } +} + +void DenoiserGUI::applySettings(bool force) +{ + if (m_doApplySettings) + { + Denoiser::MsgConfigureDenoiser* message = Denoiser::MsgConfigureDenoiser::create( m_settings, m_settingsKeys, force); + m_denoiser->getInputMessageQueue()->push(message); + } + + m_settingsKeys.clear(); +} + +void DenoiserGUI::makeUIConnections() +{ + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &DenoiserGUI::on_startStop_toggled); + QObject::connect(ui->channels, qOverload(&QComboBox::currentIndexChanged), this, &DenoiserGUI::on_channels_currentIndexChanged); + QObject::connect(ui->channelApply, &QPushButton::clicked, this, &DenoiserGUI::on_channelApply_clicked); + QObject::connect(ui->record, &ButtonSwitch::toggled, this, &DenoiserGUI::on_record_toggled); + QObject::connect(ui->showFileDialog, &QPushButton::clicked, this, &DenoiserGUI::on_showFileDialog_clicked); +} diff --git a/plugins/feature/denoiser/denoisergui.h b/plugins/feature/denoiser/denoisergui.h new file mode 100644 index 000000000..25580d1e7 --- /dev/null +++ b/plugins/feature/denoiser/denoisergui.h @@ -0,0 +1,99 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2026 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 INCLUDE_FEATURE_DENOISERGUI_H_ +#define INCLUDE_FEATURE_DENOISERGUI_H_ + +#include +#include + +#include "feature/featuregui.h" +#include "util/movingaverage.h" +#include "util/messagequeue.h" +#include "availablechannelorfeaturehandler.h" +#include "settings/rollupstate.h" + +#include "denoisersettings.h" + +class PluginAPI; +class FeatureUISet; +class Denoiser; +class Feature; + +namespace Ui { + class DenoiserGUI; +} + +class DenoiserGUI : public FeatureGUI { + Q_OBJECT +public: + static DenoiserGUI* create(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature); + virtual void destroy(); + + void resetToDefaults(); + QByteArray serialize() const; + bool deserialize(const QByteArray& data); + virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index); + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } + +private: + Ui::DenoiserGUI* ui; + PluginAPI* m_pluginAPI; + FeatureUISet* m_featureUISet; + DenoiserSettings m_settings; + QList m_settingsKeys; + RollupState m_rollupState; + int m_sampleRate; + bool m_doApplySettings; + + Denoiser* m_denoiser; + MessageQueue m_inputMessageQueue; + QTimer m_statusTimer; + int m_lastFeatureState; + AvailableChannelOrFeatureList m_availableChannels; + ChannelAPI *m_selectedChannel; + MovingAverageUtil m_channelPowerAvg; + + explicit DenoiserGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature, QWidget* parent = nullptr); + virtual ~DenoiserGUI(); + + void blockApplySettings(bool block); + void applySettings(bool force = false); + void displaySettings(); + void displaySampleRate(int sampleRate); + void updateChannelList(); + bool handleMessage(const Message& message); + void makeUIConnections(); + +private slots: + void onMenuDialogCalled(const QPoint &p); + void onWidgetRolled(QWidget* widget, bool rollDown); + void handleInputMessages(); + void on_startStop_toggled(bool checked); + void on_channels_currentIndexChanged(int index); + void on_channelApply_clicked(); + void on_record_toggled(bool checked); + void on_showFileDialog_clicked(bool checked); + void updateStatus(); + void tick(); +}; + + +#endif // INCLUDE_FEATURE_DENOISERGUI_H_ diff --git a/plugins/feature/denoiser/denoisergui.ui b/plugins/feature/denoiser/denoisergui.ui new file mode 100644 index 000000000..e708c6d14 --- /dev/null +++ b/plugins/feature/denoiser/denoisergui.ui @@ -0,0 +1,419 @@ + + + DenoiserGUI + + + + 0 + 0 + 452 + 200 + + + + + 0 + 0 + + + + + 452 + 200 + + + + + Liberation Sans + 9 + + + + Denoiser + + + + + 0 + 2 + 450 + 151 + + + + + 450 + 0 + + + + Settings + + + + 3 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + 2 + + + + + + 0 + 22 + + + + start/stop acquisition + + + + + + + :/play.png + :/stop.png:/play.png + + + + + + + Chan + + + + + + + + 200 + 0 + + + + Channel index + + + + + + + + 24 + 24 + + + + (Re) apply channel selection + + + + + + + :/checkmark.png:/checkmark.png + + + + + + + Analyzer (sink) sample rate + + + 00000.0 kS/s + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 52 + 0 + + + + Channel power + + + Qt::LeftToRight + + + -100.0 dB + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + 0 + + + 0 + + + + + Noise reduction scheme + + + + None + + + + + RNnoise + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Left: Mute/Unmute audio Right: view/select audio device + + + ... + + + + :/sound_on.png + :/sound_off.png:/sound_on.png + + + true + + + + + + + + + Qt::Horizontal + + + + + + + + + Vol + + + + + + + + 24 + 24 + + + + Audio input gain + + + 50 + + + 1 + + + 10 + + + + + + + + 25 + 0 + + + + Audio input gain value + + + 1.0 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + Liberation Mono + 8 + + + + Level (% full range) top trace: average, bottom trace: instantaneous peak, tip: peak hold + + + + + + + + + Qt::Horizontal + + + + + + + + + + 24 + 16777215 + + + + Start/stop recording + + + + + + + :/record_off.png:/record_off.png + + + + + + + + 24 + 24 + + + + + 24 + 24 + + + + Open file + + + + + + + :/preset-load.png:/preset-load.png + + + + + + + true + + + Current recording file + + + ... + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + + + + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
+ + RollupContents + QWidget +
gui/rollupcontents.h
+ 1 +
+ + LevelMeterVU + QWidget +
gui/levelmeter.h
+ 1 +
+
+ + + + +
diff --git a/plugins/feature/denoiser/denoiserplugin.cpp b/plugins/feature/denoiser/denoiserplugin.cpp new file mode 100644 index 000000000..198d4feb8 --- /dev/null +++ b/plugins/feature/denoiser/denoiserplugin.cpp @@ -0,0 +1,78 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2026 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 +#include "plugin/pluginapi.h" + +#ifndef SERVER_MODE +#include "denoisergui.h" +#endif +#include "denoiser.h" +#include "denoiserplugin.h" +#include "denoiserwebapiadapter.h" +const PluginDescriptor DenoiserPlugin::m_pluginDescriptor = { + Denoiser::m_featureId, + QStringLiteral("Denoiser"), + QStringLiteral("7.23.0"), + QStringLiteral("(c) Edouard Griffiths, F4EXB"), + QStringLiteral("https://github.com/f4exb/sdrangel"), + true, + QStringLiteral("https://github.com/f4exb/sdrangel") +}; + +DenoiserPlugin::DenoiserPlugin(QObject* parent) : + QObject(parent), + m_pluginAPI(nullptr) +{ +} + +const PluginDescriptor& DenoiserPlugin::getPluginDescriptor() const +{ + return m_pluginDescriptor; +} + +void DenoiserPlugin::initPlugin(PluginAPI* pluginAPI) +{ + m_pluginAPI = pluginAPI; + + // register RigCtl Server feature + m_pluginAPI->registerFeature(Denoiser::m_featureIdURI, Denoiser::m_featureId, this); +} + +#ifdef SERVER_MODE +FeatureGUI* DenoiserPlugin::createFeatureGUI(FeatureUISet *featureUISet, Feature *feature) const +{ + (void) featureUISet; + (void) feature; + return nullptr; +} +#else +FeatureGUI* DenoiserPlugin::createFeatureGUI(FeatureUISet *featureUISet, Feature *feature) const +{ + return DenoiserGUI::create(m_pluginAPI, featureUISet, feature); +} +#endif + +Feature* DenoiserPlugin::createFeature(WebAPIAdapterInterface* webAPIAdapterInterface) const +{ + return new Denoiser(webAPIAdapterInterface); +} + +FeatureWebAPIAdapter* DenoiserPlugin::createFeatureWebAPIAdapter() const +{ + return new DenoiserWebAPIAdapter(); +} diff --git a/plugins/feature/denoiser/denoiserplugin.h b/plugins/feature/denoiser/denoiserplugin.h new file mode 100644 index 000000000..5a52b9968 --- /dev/null +++ b/plugins/feature/denoiser/denoiserplugin.h @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2026 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 INCLUDE_FEATURE_DENOISERPLUGIN_H +#define INCLUDE_FEATURE_DENOISERPLUGIN_H + +#include +#include "plugin/plugininterface.h" + +class FeatureGUI; +class WebAPIAdapterInterface; + +class DenoiserPlugin : public QObject, PluginInterface { + Q_OBJECT + Q_INTERFACES(PluginInterface) + Q_PLUGIN_METADATA(IID "sdrangel.feature.denoiser") + +public: + explicit DenoiserPlugin(QObject* parent = nullptr); + + const PluginDescriptor& getPluginDescriptor() const; + void initPlugin(PluginAPI* pluginAPI); + + virtual FeatureGUI* createFeatureGUI(FeatureUISet *featureUISet, Feature *feature) const; + virtual Feature* createFeature(WebAPIAdapterInterface *webAPIAdapterInterface) const; + virtual FeatureWebAPIAdapter* createFeatureWebAPIAdapter() const; + +private: + static const PluginDescriptor m_pluginDescriptor; + + PluginAPI* m_pluginAPI; +}; + +#endif // INCLUDE_FEATURE_DENOISERPLUGIN_H diff --git a/plugins/feature/denoiser/denoisersettings.cpp b/plugins/feature/denoiser/denoisersettings.cpp new file mode 100644 index 000000000..8e2c520f0 --- /dev/null +++ b/plugins/feature/denoiser/denoisersettings.cpp @@ -0,0 +1,220 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2026 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 "util/simpleserializer.h" +#include "settings/serializable.h" + +#include "denoisersettings.h" + +const QStringList DenoiserSettings::m_channelURIs = { + QStringLiteral("sdrangel.channel.amdemod"), + QStringLiteral("sdrangel.channel.bfm"), + QStringLiteral("sdrangel.channel.dabdemod"), + QStringLiteral("sdrangel.channel.dsddemod"), + QStringLiteral("sdrangel.channel.m17demod"), + QStringLiteral("sdrangel.channel.nfmdemod"), + QStringLiteral("sdrangel.channel.ssbdemod"), + QStringLiteral("sdrangel.channel.wfmdemod"), + QStringLiteral("sdrangel.channel.wdsprx"), +}; + +DenoiserSettings::DenoiserSettings() : + m_rollupState(nullptr) +{ + resetToDefaults(); +} + +void DenoiserSettings::resetToDefaults() +{ + m_denoiserType = DenoiserType::DenoiserType_RNnoise; + m_title = "Denoiser"; + m_audioMute = false; + m_rgbColor = 0xffd700; // gold + m_useReverseAPI = false; + m_reverseAPIAddress = "localhost"; + m_reverseAPIPort = 8888; + m_reverseAPIFeatureSetIndex = 0; + m_reverseAPIFeatureIndex = 0; + m_fileRecordName = "denoiser_record.wav"; + m_recordToFile = false; + m_workspaceIndex = -1; + m_geometryBytes.clear(); +} + +QByteArray DenoiserSettings::serialize() const +{ + SimpleSerializer s(1); + + s.writeS32(1, static_cast(m_denoiserType)); + s.writeBool(14, m_audioMute); + s.writeString(2, m_title); + s.writeU32(3, m_rgbColor); + s.writeBool(4, m_useReverseAPI); + s.writeString(5, m_reverseAPIAddress); + s.writeU32(6, m_reverseAPIPort); + s.writeU32(7, m_reverseAPIFeatureSetIndex); + s.writeU32(8, m_reverseAPIFeatureIndex); + s.writeString(9, m_fileRecordName); + s.writeBool(10, m_recordToFile); + s.writeS32(11, m_workspaceIndex); + s.writeBlob(12, m_geometryBytes); + + if (m_rollupState) { + s.writeBlob(13, m_rollupState->serialize()); + } + + return s.final(); +} + +bool DenoiserSettings::deserialize(const QByteArray& data) +{ + SimpleDeserializer d(data); + + if (!d.isValid()) + { + resetToDefaults(); + return false; + } + + if (d.getVersion() == 1) + { + qint32 itmp; + QByteArray bytetmp; + uint32_t utmp; + + d.readS32(1, &itmp, 1); + m_denoiserType = static_cast(itmp); + d.readBool(14, &m_audioMute, false); + d.readString(2, &m_title, "Denoiser"); + d.readU32(3, &m_rgbColor, 0xffd700); // gold + d.readBool(4, &m_useReverseAPI, false); + d.readString(5, &m_reverseAPIAddress, "localhost"); + d.readU32(6, &utmp, 8888); + + if ((utmp > 1023) && (utmp < 65535)) { + m_reverseAPIPort = utmp; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(7, &utmp, 0); + m_reverseAPIFeatureSetIndex = utmp > 99 ? 99 : utmp; + d.readU32(8, &utmp, 0); + m_reverseAPIFeatureIndex = utmp > 99 ? 99 : utmp; + d.readString(9, &m_fileRecordName, "denoiser_record.wav"); + d.readBool(10, &m_recordToFile, false); + d.readS32(11, &m_workspaceIndex, -1); + d.readBlob(12, &m_geometryBytes); + + if (m_rollupState) + { + d.readBlob(13, &bytetmp); + m_rollupState->deserialize(bytetmp); + } + + return true; + } + else + { + resetToDefaults(); + return false; + } +} + +void DenoiserSettings::applySettings(const QStringList& settingsKeys, const DenoiserSettings& settings) +{ + if (settingsKeys.contains("denoiserType")) { + m_denoiserType = settings.m_denoiserType; + } + if (settingsKeys.contains("audioMute")) { + m_audioMute = settings.m_audioMute; + } + if (settingsKeys.contains("title")) { + m_title = settings.m_title; + } + if (settingsKeys.contains("rgbColor")) { + m_rgbColor = settings.m_rgbColor; + } + if (settingsKeys.contains("useReverseAPI")) { + m_useReverseAPI = settings.m_useReverseAPI; + } + if (settingsKeys.contains("reverseAPIAddress")) { + m_reverseAPIAddress = settings.m_reverseAPIAddress; + } + if (settingsKeys.contains("reverseAPIPort")) { + m_reverseAPIPort = settings.m_reverseAPIPort; + } + if (settingsKeys.contains("reverseAPIFeatureSetIndex")) { + m_reverseAPIFeatureSetIndex = settings.m_reverseAPIFeatureSetIndex; + } + if (settingsKeys.contains("reverseAPIFeatureIndex")) { + m_reverseAPIFeatureIndex = settings.m_reverseAPIFeatureIndex; + } + if (settingsKeys.contains("workspaceIndex")) { + m_workspaceIndex = settings.m_workspaceIndex; + } + if (settingsKeys.contains("fileRecordName")) { + m_fileRecordName = settings.m_fileRecordName; + } + if (settingsKeys.contains("recordToFile")) { + m_recordToFile = settings.m_recordToFile; + } +} + +QString DenoiserSettings::getDebugString(const QStringList& settingsKeys, bool force) const +{ + QString debugString; + + if (settingsKeys.contains("denoiserType") || force) { + debugString += QString("DenoiserType: %1 ").arg(static_cast(m_denoiserType)); + } + if (settingsKeys.contains("audioMute") || force) { + debugString += QString("Audio Mute: %1 ").arg(m_audioMute ? "true" : "false"); + } + if (settingsKeys.contains("title") || force) { + debugString += QString("Title: %1 ").arg(m_title); + } + if (settingsKeys.contains("rgbColor") || force) { + debugString += QString("RGB Color: 0x%1 ").arg(QString::number(m_rgbColor, 16).rightJustified(6, '0')); + } + if (settingsKeys.contains("useReverseAPI") || force) { + debugString += QString("Use Reverse API: %1 ").arg(m_useReverseAPI ? "true" : "false"); + } + if (settingsKeys.contains("reverseAPIAddress") || force) { + debugString += QString("Reverse API Address: %1 ").arg(m_reverseAPIAddress); + } + if (settingsKeys.contains("reverseAPIPort") || force) { + debugString += QString("Reverse API Port: %1 ").arg(m_reverseAPIPort); + } + if (settingsKeys.contains("reverseAPIFeatureSetIndex") || force) { + debugString += QString("Reverse API Feature Set Index: %1 ").arg(m_reverseAPIFeatureSetIndex); + } + if (settingsKeys.contains("reverseAPIFeatureIndex") || force) { + debugString += QString("Reverse API Feature Index: %1 ").arg(m_reverseAPIFeatureIndex); + } + if (settingsKeys.contains("workspaceIndex") || force) { + debugString += QString("Workspace Index: %1 ").arg(m_workspaceIndex); + } + if (settingsKeys.contains("fileRecordName") || force) { + debugString += QString("File Record Name: %1 ").arg(m_fileRecordName); + } + if (settingsKeys.contains("recordToFile") || force) { + debugString += QString("Record To File: %1 ").arg(m_recordToFile ? "true" : "false"); + } + + return debugString; +} diff --git a/plugins/feature/denoiser/denoisersettings.h b/plugins/feature/denoiser/denoisersettings.h new file mode 100644 index 000000000..22f95b513 --- /dev/null +++ b/plugins/feature/denoiser/denoisersettings.h @@ -0,0 +1,62 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2026 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 INCLUDE_FEATURE_DENOISER_DENOISERSETTINGS_H_ +#define INCLUDE_FEATURE_DENOISER_DENOISERSETTINGS_H_ + +#include +#include +#include + +class Serializable; + +struct DenoiserSettings +{ + enum class DenoiserType + { + DenoiserType_None = 0, + DenoiserType_RNnoise = 1, + }; + + DenoiserType m_denoiserType; + bool m_audioMute; + QString m_title; + quint32 m_rgbColor; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIFeatureSetIndex; + uint16_t m_reverseAPIFeatureIndex; + QString m_fileRecordName; + bool m_recordToFile; + Serializable *m_rollupState; + int m_workspaceIndex; + QByteArray m_geometryBytes; + + DenoiserSettings(); + ~DenoiserSettings() = default; + void resetToDefaults(); + QByteArray serialize() const; + bool deserialize(const QByteArray& data); + void setRollupState(Serializable *rollupState) { m_rollupState = rollupState; } + void applySettings(const QStringList& settingsKeys, const DenoiserSettings& settings); + QString getDebugString(const QStringList& settingsKeys, bool force=false) const; + + static const QStringList m_channelURIs; +}; + + +#endif // INCLUDE_FEATURE_DENOISER_DENOISERSETTINGS_H_ diff --git a/plugins/feature/denoiser/denoiserwebapiadapter.cpp b/plugins/feature/denoiser/denoiserwebapiadapter.cpp new file mode 100644 index 000000000..1a372123f --- /dev/null +++ b/plugins/feature/denoiser/denoiserwebapiadapter.cpp @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2026 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 "SWGFeatureSettings.h" +#include "denoiser.h" +#include "denoiserwebapiadapter.h" + +DenoiserWebAPIAdapter::DenoiserWebAPIAdapter() +{} + +DenoiserWebAPIAdapter::~DenoiserWebAPIAdapter() +{} + +int DenoiserWebAPIAdapter::webapiSettingsGet( + SWGSDRangel::SWGFeatureSettings& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setDenoiserSettings(new SWGSDRangel::SWGDenoiserSettings()); + response.getDenoiserSettings()->init(); + Denoiser::webapiFormatFeatureSettings(response, m_settings); + + return 200; +} + +int DenoiserWebAPIAdapter::webapiSettingsPutPatch( + bool force, + const QStringList& featureSettingsKeys, + SWGSDRangel::SWGFeatureSettings& response, + QString& errorMessage) +{ + (void) force; // no action + (void) errorMessage; + Denoiser::webapiUpdateFeatureSettings(m_settings, featureSettingsKeys, response); + + return 200; +} diff --git a/plugins/feature/denoiser/denoiserwebapiadapter.h b/plugins/feature/denoiser/denoiserwebapiadapter.h new file mode 100644 index 000000000..7e7a84fc7 --- /dev/null +++ b/plugins/feature/denoiser/denoiserwebapiadapter.h @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2026 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 INCLUDE_DENOISER_WEBAPIADAPTER_H +#define INCLUDE_DENOISER_WEBAPIADAPTER_H + +#include "feature/featurewebapiadapter.h" +#include "denoisersettings.h" + +/** + * Standalone API adapter only for the settings + */ +class DenoiserWebAPIAdapter : public FeatureWebAPIAdapter { +public: + DenoiserWebAPIAdapter(); + virtual ~DenoiserWebAPIAdapter(); + + virtual QByteArray serialize() const { return m_settings.serialize(); } + virtual bool deserialize(const QByteArray& data) { return m_settings.deserialize(data); } + + virtual int webapiSettingsGet( + SWGSDRangel::SWGFeatureSettings& response, + QString& errorMessage); + + virtual int webapiSettingsPutPatch( + bool force, + const QStringList& featureSettingsKeys, + SWGSDRangel::SWGFeatureSettings& response, + QString& errorMessage); + +private: + DenoiserSettings m_settings; +}; + +#endif // INCLUDE_DENOISER_WEBAPIADAPTER_H diff --git a/plugins/feature/denoiser/denoiserworker.cpp b/plugins/feature/denoiser/denoiserworker.cpp new file mode 100644 index 000000000..f60d4901e --- /dev/null +++ b/plugins/feature/denoiser/denoiserworker.cpp @@ -0,0 +1,245 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2026 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 "dsp/wavfilerecord.h" + +#include "denoiserworker.h" + +MESSAGE_CLASS_DEFINITION(DenoiserWorker::MsgConfigureDenoiserWorker, Message) +MESSAGE_CLASS_DEFINITION(DenoiserWorker::MsgConnectFifo, Message) + +DenoiserWorker::DenoiserWorker(QObject *parent) : + QObject(parent), + m_dataFifo(nullptr), + m_sinkSampleRate(0), + m_msgQueueToFeature(nullptr), + m_magsq(0.0), + m_sampleBufferSize(0), + m_channelPowerAvg(), + m_wavFileRecord(nullptr), + m_nbBytes(0) +{ +} + +DenoiserWorker::~DenoiserWorker() +{ + m_inputMessageQueue.clear(); +} + +void DenoiserWorker::reset() +{ + QMutexLocker mutexLocker(&m_mutex); + m_inputMessageQueue.clear(); +} + +void DenoiserWorker::startWork() +{ + QMutexLocker mutexLocker(&m_mutex); + m_wavFileRecord = new WavFileRecord(m_sinkSampleRate); + connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); +} + +void DenoiserWorker::stopWork() +{ + QMutexLocker mutexLocker(&m_mutex); + if (m_wavFileRecord) + { + delete m_wavFileRecord; + m_wavFileRecord = nullptr; + } + disconnect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); +} + +void DenoiserWorker::feedPart( + const QByteArray::const_iterator& begin, + const QByteArray::const_iterator& end, + DataFifo::DataType dataType +) +{ + int nbBytes; + + switch(dataType) + { + case DataFifo::DataTypeCI16: + nbBytes = 4; + break; + case DataFifo::DataTypeI16: + default: + nbBytes = 2; + } + + if ((nbBytes != m_nbBytes) && m_wavFileRecord) + { + m_wavFileRecord->stopRecording(); + m_wavFileRecord->setMono(nbBytes == 2); + } + + m_nbBytes = nbBytes; + int countSamples = (end - begin) / nbBytes; + + if (countSamples > m_sampleBufferSize) + { + m_sampleBuffer.resize(countSamples); + m_sampleBufferSize = countSamples; + } + + for (int i = 0; i < countSamples; i++) { + processSample(dataType, begin, i); + } + + if (m_settings.m_recordToFile && m_wavFileRecord) + { + for (int is = 0; is < countSamples; is++) + { + const Sample& sample = m_sampleBuffer[is]; + + if ((sample.m_real == 0) && (sample.m_imag == 0)) + { + if (m_wavFileRecord->isRecording()) { + m_wavFileRecord->stopRecording(); + } + } + else + { + if (!m_wavFileRecord->isRecording()) { + m_wavFileRecord->startRecording(); + } + writeSampleToFile(sample); + } + } + } +} + +void DenoiserWorker::writeSampleToFile(const Sample& sample) +{ + if (!m_wavFileRecord) { + return; + } + + if (SDR_RX_SAMP_SZ == 16) + { + if (m_nbBytes == 2) { + m_wavFileRecord->writeMono(sample.m_real); + } else { + m_wavFileRecord->write(sample.m_real, sample.m_imag); + } + } + else + { + if (m_nbBytes == 2) { + m_wavFileRecord->writeMono(sample.m_real >> 8); + } else { + m_wavFileRecord->write(sample.m_real >> 8, sample.m_imag >> 8); + } + } +} + +void DenoiserWorker::handleInputMessages() +{ + Message* message = nullptr; + + while ((message = m_inputMessageQueue.pop()) != nullptr) + { + const Message& cmd = *message; + handleMessage(cmd); + } +} + +bool DenoiserWorker::handleMessage(const Message& cmd) +{ + if (MsgConfigureDenoiserWorker::match(cmd)) + { + const MsgConfigureDenoiserWorker& conf = static_cast(cmd); + qDebug("DenoiserWorker::handleMessage: MsgConfigureDenoiserWorker"); + applySettings(conf.getSettings(), conf.getSettingsKeys(), conf.getForce()); + return true; + } + else if (MsgConnectFifo::match(cmd)) + { + QMutexLocker mutexLocker(&m_mutex); + MsgConnectFifo& msg = (MsgConnectFifo&) cmd; + m_dataFifo = msg.getFifo(); + bool doConnect = msg.getConnect(); + qDebug("DenoiserWorker::handleMessage: MsgConnectFifo: %s", (doConnect ? "connect" : "disconnect")); + + if (doConnect) { + QObject::connect( + m_dataFifo, + &DataFifo::dataReady, + this, + &DenoiserWorker::handleData, + Qt::QueuedConnection + ); + } + else + { + QObject::disconnect( + m_dataFifo, + &DataFifo::dataReady, + this, + &DenoiserWorker::handleData + ); + } + + return true; + } + + return false; +} + +void DenoiserWorker::applySettings(const DenoiserSettings& settings, const QStringList& settingsKeys, bool force) +{ + QMutexLocker mutexLocker(&m_mutex); + Q_UNUSED(settingsKeys) + Q_UNUSED(force) + + m_settings = settings; +} + +void DenoiserWorker::applySampleRate(int sampleRate) +{ + QMutexLocker mutexLocker(&m_mutex); + m_sinkSampleRate = sampleRate; +} + +void DenoiserWorker::handleData() +{ + QMutexLocker mutexLocker(&m_mutex); + + while ((m_dataFifo->fill() > 0) && (m_inputMessageQueue.size() == 0)) + { + QByteArray::iterator part1begin; + QByteArray::iterator part1end; + QByteArray::iterator part2begin; + QByteArray::iterator part2end; + DataFifo::DataType dataType; + + std::size_t count = m_dataFifo->readBegin(m_dataFifo->fill(), &part1begin, &part1end, &part2begin, &part2end, dataType); + + // first part of FIFO data + if (part1begin != part1end) { + feedPart(part1begin, part1end, dataType); + } + + // second part of FIFO data (used when block wraps around) + if (part2begin != part2end) { + feedPart(part2begin, part2end, dataType); + } + + m_dataFifo->readCommit((unsigned int) count); + } +} diff --git a/plugins/feature/denoiser/denoiserworker.h b/plugins/feature/denoiser/denoiserworker.h new file mode 100644 index 000000000..2a4baf0cc --- /dev/null +++ b/plugins/feature/denoiser/denoiserworker.h @@ -0,0 +1,150 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2026 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 INCLUDE_FEATURE_DENOISER_DENOISERWORKER_H_ +#define INCLUDE_FEATURE_DENOISER_DENOISERWORKER_H_ + +#include +#include +#include + +#include "util/movingaverage.h" +#include "util/message.h" +#include "util/messagequeue.h" +#include "dsp/dsptypes.h" +#include "dsp/datafifo.h" + +#include "denoisersettings.h" + +class WavFileRecord; + +class DenoiserWorker : public QObject { + Q_OBJECT +public: + class MsgConfigureDenoiserWorker : public Message { + MESSAGE_CLASS_DECLARATION + public: + const DenoiserSettings& getSettings() const { return m_settings; } + const QStringList& getSettingsKeys() const { return m_settingsKeys; } + bool getForce() const { return m_force; } + static MsgConfigureDenoiserWorker* create(const DenoiserSettings& settings, const QStringList& settingsKeys, bool force) + { + return new MsgConfigureDenoiserWorker(settings, settingsKeys, force); + } + private: + DenoiserSettings m_settings; + QStringList m_settingsKeys; + bool m_force; + MsgConfigureDenoiserWorker(const DenoiserSettings& settings, const QStringList& settingsKeys, bool force) : + Message(), + m_settings(settings), + m_settingsKeys(settingsKeys), + m_force(force) + { } + }; + + class MsgConnectFifo : public Message { + MESSAGE_CLASS_DECLARATION + public: + DataFifo *getFifo() { return m_fifo; } + bool getConnect() const { return m_connect; } + static MsgConnectFifo* create(DataFifo *fifo, bool connect) { + return new MsgConnectFifo(fifo, connect); + } + private: + DataFifo *m_fifo; + bool m_connect; + MsgConnectFifo(DataFifo *fifo, bool connect) : + Message(), + m_fifo(fifo), + m_connect(connect) + { } + }; + + explicit DenoiserWorker(QObject *parent = nullptr); + ~DenoiserWorker() override; + void reset(); + void startWork(); + void stopWork(); + MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + void setMessageQueueToFeature(MessageQueue *messageQueue) { m_msgQueueToFeature = messageQueue; } + void applySampleRate(int sampleRate); + void applySettings(const DenoiserSettings& settings, const QStringList& settingsKeys, bool force = false); + double getMagSq() const { return m_magsq; } + double getMagSqAvg() const { return (double) m_channelPowerAvg; } + +private: + DataFifo *m_dataFifo; + int m_sinkSampleRate; + MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication + MessageQueue *m_msgQueueToFeature; //!< Queue to report channel change to main feature object + DenoiserSettings m_settings; + double m_magsq; + SampleVector m_sampleBuffer; + int m_sampleBufferSize; + MovingAverageUtil m_channelPowerAvg; + WavFileRecord* m_wavFileRecord; + int m_nbBytes; + QRecursiveMutex m_mutex; + + void feedPart( + const QByteArray::const_iterator& begin, + const QByteArray::const_iterator& end, + DataFifo::DataType dataType + ); + + bool handleMessage(const Message& cmd); + void writeSampleToFile(const Sample& sample); + + inline void processSample( + DataFifo::DataType dataType, + const QByteArray::const_iterator& begin, + int i + ) + { + switch(dataType) + { + case DataFifo::DataTypeI16: { + int16_t *s = (int16_t*) begin; + double re = s[i] / (double) std::numeric_limits::max(); + m_magsq = re*re; + m_channelPowerAvg(m_magsq); + + m_sampleBuffer[i].setReal(re * SDR_RX_SCALEF); + m_sampleBuffer[i].setImag(0); + } + break; + case DataFifo::DataTypeCI16: { + int16_t *s = (int16_t*) begin; + double re = s[2*i] / (double) std::numeric_limits::max(); + double im = s[2*i+1] / (double) std::numeric_limits::max(); + m_magsq = re*re + im*im; + m_channelPowerAvg(m_magsq); + + m_sampleBuffer[i].setReal(re * SDR_RX_SCALEF); + m_sampleBuffer[i].setImag(im * SDR_RX_SCALEF); + } + break; + } + } + + +private slots: + void handleInputMessages(); + void handleData(); //!< Handle data when samples have to be processed +}; + +#endif // INCLUDE_FEATURE_DENOISER_DENOISERWORKER_H_ diff --git a/sdrbase/resources/webapi.qrc b/sdrbase/resources/webapi.qrc index d9480d0bc..7decb2748 100644 --- a/sdrbase/resources/webapi.qrc +++ b/sdrbase/resources/webapi.qrc @@ -40,6 +40,7 @@ webapi/doc/swagger/include/DATVDemod.yaml webapi/doc/swagger/include/DATVMod.yaml webapi/doc/swagger/include/DemodAnalyzer.yaml + webapi/doc/swagger/include/Denoiser.yaml webapi/doc/swagger/include/DeviceActions.yaml webapi/doc/swagger/include/DeviceReports.yaml webapi/doc/swagger/include/DeviceSettings.yaml diff --git a/sdrbase/resources/webapi/doc/html2/index.html b/sdrbase/resources/webapi/doc/html2/index.html index 8a8040057..8504b3d9c 100644 --- a/sdrbase/resources/webapi/doc/html2/index.html +++ b/sdrbase/resources/webapi/doc/html2/index.html @@ -5625,6 +5625,66 @@ margin-bottom: 20px; } }, "description" : "DemodAnalyzer" +}; + defs.DenoiserActions = { + "required" : [ "channelId", "deviceId" ], + "properties" : { + "deviceId" : { + "type" : "integer", + "description" : "Set source device Id/Number that channel belongs to" + }, + "channelId" : { + "type" : "integer", + "description" : "Set source channel Id/Number of the channel within the device" + } + }, + "description" : "Denoiser actions" +}; + defs.DenoiserSettings = { + "properties" : { + "denoiserType" : { + "type" : "integer", + "description" : "Denoiser type\n * 0 - none\n * 1 - RNnoise\n" + }, + "audioMute" : { + "type" : "integer", + "description" : "Audio mute\n * 1 - mute\n * 0 - unmute\n" + }, + "title" : { + "type" : "string" + }, + "rgbColor" : { + "type" : "integer" + }, + "fileRecordName" : { + "type" : "string", + "description" : "Output wave file name" + }, + "recordToFile" : { + "type" : "integer", + "description" : "Recording status\n * 1 - recording\n * 0 - not recording\n" + }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API\n * 1 - yes\n * 0 - no\n" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIFeatureSetIndex" : { + "type" : "integer" + }, + "reverseAPIFeatureIndex" : { + "type" : "integer" + }, + "rollupState" : { + "$ref" : "#/definitions/RollupState" + } + }, + "description" : "Denoiser" }; defs.DeviceActions = { "required" : [ "deviceHwType", "direction" ], @@ -6518,6 +6578,9 @@ margin-bottom: 20px; }, "DemodAnalyzerActions" : { "$ref" : "#/definitions/DemodAnalyzerActions" + }, + "DenoiserActions" : { + "$ref" : "#/definitions/DenoiserActions" } }, "description" : "Base feature actions. Only the feature actions corresponding to the feature specified in the featureType field is or should be present." @@ -6735,6 +6798,9 @@ margin-bottom: 20px; "DemodAnalyzerSettings" : { "$ref" : "#/definitions/DemodAnalyzerSettings" }, + "DenoiserSettings" : { + "$ref" : "#/definitions/DenoiserSettings" + }, "JogdialControllerSettings" : { "$ref" : "#/definitions/JogdialControllerSettings" }, @@ -59654,7 +59720,7 @@ except ApiException as e:
- Generated 2026-01-04T12:41:15.650+01:00 + Generated 2026-01-06T07:31:33.605+01:00
diff --git a/sdrbase/resources/webapi/doc/swagger/include/Denoiser.yaml b/sdrbase/resources/webapi/doc/swagger/include/Denoiser.yaml new file mode 100644 index 000000000..d0da09d97 --- /dev/null +++ b/sdrbase/resources/webapi/doc/swagger/include/Denoiser.yaml @@ -0,0 +1,57 @@ +DenoiserSettings: + description: Denoiser + properties: + denoiserType: + type: integer + description: > + Denoiser type + * 0 - none + * 1 - RNnoise + audioMute: + type: integer + description: > + Audio mute + * 1 - mute + * 0 - unmute + title: + type: string + rgbColor: + type: integer + fileRecordName: + type: string + description: Output wave file name + recordToFile: + type: integer + description: > + Recording status + * 1 - recording + * 0 - not recording + useReverseAPI: + type: integer + description: > + Synchronize with reverse API + * 1 - yes + * 0 - no + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIFeatureSetIndex: + type: integer + reverseAPIFeatureIndex: + type: integer + rollupState: + $ref: "/doc/swagger/include/RollupState.yaml#/RollupState" + +DenoiserActions: + description: "Denoiser actions" + required: + - deviceId + - channelId + properties: + deviceId: + type: integer + description: "Set source device Id/Number that channel belongs to" + channelId: + type: integer + description: "Set source channel Id/Number of the channel within the device" diff --git a/sdrbase/resources/webapi/doc/swagger/include/FeatureActions.yaml b/sdrbase/resources/webapi/doc/swagger/include/FeatureActions.yaml index a842fdab2..64f10fcaa 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/FeatureActions.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/FeatureActions.yaml @@ -41,3 +41,5 @@ FeatureActions: $ref: "/doc/swagger/include/VORLocalizer.yaml#/VORLocalizerActions" DemodAnalyzerActions: $ref: "/doc/swagger/include/DemodAnalyzer.yaml#/DemodAnalyzerActions" + DenoiserActions: + $ref: "/doc/swagger/include/Denoiser.yaml#/DenoiserActions" diff --git a/sdrbase/resources/webapi/doc/swagger/include/FeatureSettings.yaml b/sdrbase/resources/webapi/doc/swagger/include/FeatureSettings.yaml index 1c1b54a50..1058a0f3f 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/FeatureSettings.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/FeatureSettings.yaml @@ -25,6 +25,8 @@ FeatureSettings: $ref: "/doc/swagger/include/APRS.yaml#/APRSSettings" DemodAnalyzerSettings: $ref: "/doc/swagger/include/DemodAnalyzer.yaml#/DemodAnalyzerSettings" + DenoiserSettings: + $ref: "/doc/swagger/include/Denoiser.yaml#/DenoiserSettings" JogdialControllerSettings: $ref: "/doc/swagger/include/JogdialController.yaml#/JogdialControllerSettings" GS232ControllerSettings: diff --git a/swagger/sdrangel/api/swagger/include/Denoiser.yaml b/swagger/sdrangel/api/swagger/include/Denoiser.yaml new file mode 100644 index 000000000..8739e4c56 --- /dev/null +++ b/swagger/sdrangel/api/swagger/include/Denoiser.yaml @@ -0,0 +1,57 @@ +DenoiserSettings: + description: Denoiser + properties: + denoiserType: + type: integer + description: > + Denoiser type + * 0 - none + * 1 - RNnoise + audioMute: + type: integer + description: > + Audio mute + * 1 - mute + * 0 - unmute + title: + type: string + rgbColor: + type: integer + fileRecordName: + type: string + description: Output wave file name + recordToFile: + type: integer + description: > + Recording status + * 1 - recording + * 0 - not recording + useReverseAPI: + type: integer + description: > + Synchronize with reverse API + * 1 - yes + * 0 - no + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIFeatureSetIndex: + type: integer + reverseAPIFeatureIndex: + type: integer + rollupState: + $ref: "http://swgserver:8081/api/swagger/include/RollupState.yaml#/RollupState" + +DenoiserActions: + description: "Denoiser actions" + required: + - deviceId + - channelId + properties: + deviceId: + type: integer + description: "Set source device Id/Number that channel belongs to" + channelId: + type: integer + description: "Set source channel Id/Number of the channel within the device" diff --git a/swagger/sdrangel/api/swagger/include/FeatureActions.yaml b/swagger/sdrangel/api/swagger/include/FeatureActions.yaml index d4d27c157..d1d96f690 100644 --- a/swagger/sdrangel/api/swagger/include/FeatureActions.yaml +++ b/swagger/sdrangel/api/swagger/include/FeatureActions.yaml @@ -41,3 +41,5 @@ FeatureActions: $ref: "http://swgserver:8081/api/swagger/include/VORLocalizer.yaml#/VORLocalizerActions" DemodAnalyzerActions: $ref: "http://swgserver:8081/api/swagger/include/DemodAnalyzer.yaml#/DemodAnalyzerActions" + DenoiserActions: + $ref: "http://swgserver:8081/api/swagger/include/Denoiser.yaml#/DenoiserActions" diff --git a/swagger/sdrangel/api/swagger/include/FeatureSettings.yaml b/swagger/sdrangel/api/swagger/include/FeatureSettings.yaml index 3ed2293f0..991e60db5 100644 --- a/swagger/sdrangel/api/swagger/include/FeatureSettings.yaml +++ b/swagger/sdrangel/api/swagger/include/FeatureSettings.yaml @@ -25,6 +25,8 @@ FeatureSettings: $ref: "http://swgserver:8081/api/swagger/include/APRS.yaml#/APRSSettings" DemodAnalyzerSettings: $ref: "http://swgserver:8081/api/swagger/include/DemodAnalyzer.yaml#/DemodAnalyzerSettings" + DenoiserSettings: + $ref: "http://swgserver:8081/api/swagger/include/Denoiser.yaml#/DenoiserSettings" JogdialControllerSettings: $ref: "http://swgserver:8081/api/swagger/include/JogdialController.yaml#/JogdialControllerSettings" GS232ControllerSettings: diff --git a/swagger/sdrangel/code/html2/index.html b/swagger/sdrangel/code/html2/index.html index 8a8040057..8504b3d9c 100644 --- a/swagger/sdrangel/code/html2/index.html +++ b/swagger/sdrangel/code/html2/index.html @@ -5625,6 +5625,66 @@ margin-bottom: 20px; } }, "description" : "DemodAnalyzer" +}; + defs.DenoiserActions = { + "required" : [ "channelId", "deviceId" ], + "properties" : { + "deviceId" : { + "type" : "integer", + "description" : "Set source device Id/Number that channel belongs to" + }, + "channelId" : { + "type" : "integer", + "description" : "Set source channel Id/Number of the channel within the device" + } + }, + "description" : "Denoiser actions" +}; + defs.DenoiserSettings = { + "properties" : { + "denoiserType" : { + "type" : "integer", + "description" : "Denoiser type\n * 0 - none\n * 1 - RNnoise\n" + }, + "audioMute" : { + "type" : "integer", + "description" : "Audio mute\n * 1 - mute\n * 0 - unmute\n" + }, + "title" : { + "type" : "string" + }, + "rgbColor" : { + "type" : "integer" + }, + "fileRecordName" : { + "type" : "string", + "description" : "Output wave file name" + }, + "recordToFile" : { + "type" : "integer", + "description" : "Recording status\n * 1 - recording\n * 0 - not recording\n" + }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API\n * 1 - yes\n * 0 - no\n" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIFeatureSetIndex" : { + "type" : "integer" + }, + "reverseAPIFeatureIndex" : { + "type" : "integer" + }, + "rollupState" : { + "$ref" : "#/definitions/RollupState" + } + }, + "description" : "Denoiser" }; defs.DeviceActions = { "required" : [ "deviceHwType", "direction" ], @@ -6518,6 +6578,9 @@ margin-bottom: 20px; }, "DemodAnalyzerActions" : { "$ref" : "#/definitions/DemodAnalyzerActions" + }, + "DenoiserActions" : { + "$ref" : "#/definitions/DenoiserActions" } }, "description" : "Base feature actions. Only the feature actions corresponding to the feature specified in the featureType field is or should be present." @@ -6735,6 +6798,9 @@ margin-bottom: 20px; "DemodAnalyzerSettings" : { "$ref" : "#/definitions/DemodAnalyzerSettings" }, + "DenoiserSettings" : { + "$ref" : "#/definitions/DenoiserSettings" + }, "JogdialControllerSettings" : { "$ref" : "#/definitions/JogdialControllerSettings" }, @@ -59654,7 +59720,7 @@ except ApiException as e:
- Generated 2026-01-04T12:41:15.650+01:00 + Generated 2026-01-06T07:31:33.605+01:00
diff --git a/swagger/sdrangel/code/qt5/client/SWGDenoiserActions.cpp b/swagger/sdrangel/code/qt5/client/SWGDenoiserActions.cpp new file mode 100644 index 000000000..e50da2bf8 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGDenoiserActions.cpp @@ -0,0 +1,131 @@ +/** + * SDRangel + * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- + * + * OpenAPI spec version: 7.0.0 + * Contact: f4exb06@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + + +#include "SWGDenoiserActions.h" + +#include "SWGHelpers.h" + +#include +#include +#include +#include + +namespace SWGSDRangel { + +SWGDenoiserActions::SWGDenoiserActions(QString* json) { + init(); + this->fromJson(*json); +} + +SWGDenoiserActions::SWGDenoiserActions() { + device_id = 0; + m_device_id_isSet = false; + channel_id = 0; + m_channel_id_isSet = false; +} + +SWGDenoiserActions::~SWGDenoiserActions() { + this->cleanup(); +} + +void +SWGDenoiserActions::init() { + device_id = 0; + m_device_id_isSet = false; + channel_id = 0; + m_channel_id_isSet = false; +} + +void +SWGDenoiserActions::cleanup() { + + +} + +SWGDenoiserActions* +SWGDenoiserActions::fromJson(QString &json) { + QByteArray array (json.toStdString().c_str()); + QJsonDocument doc = QJsonDocument::fromJson(array); + QJsonObject jsonObject = doc.object(); + this->fromJsonObject(jsonObject); + return this; +} + +void +SWGDenoiserActions::fromJsonObject(QJsonObject &pJson) { + ::SWGSDRangel::setValue(&device_id, pJson["deviceId"], "qint32", ""); + + ::SWGSDRangel::setValue(&channel_id, pJson["channelId"], "qint32", ""); + +} + +QString +SWGDenoiserActions::asJson () +{ + QJsonObject* obj = this->asJsonObject(); + + QJsonDocument doc(*obj); + QByteArray bytes = doc.toJson(); + delete obj; + return QString(bytes); +} + +QJsonObject* +SWGDenoiserActions::asJsonObject() { + QJsonObject* obj = new QJsonObject(); + if(m_device_id_isSet){ + obj->insert("deviceId", QJsonValue(device_id)); + } + if(m_channel_id_isSet){ + obj->insert("channelId", QJsonValue(channel_id)); + } + + return obj; +} + +qint32 +SWGDenoiserActions::getDeviceId() { + return device_id; +} +void +SWGDenoiserActions::setDeviceId(qint32 device_id) { + this->device_id = device_id; + this->m_device_id_isSet = true; +} + +qint32 +SWGDenoiserActions::getChannelId() { + return channel_id; +} +void +SWGDenoiserActions::setChannelId(qint32 channel_id) { + this->channel_id = channel_id; + this->m_channel_id_isSet = true; +} + + +bool +SWGDenoiserActions::isSet(){ + bool isObjectUpdated = false; + do{ + if(m_device_id_isSet){ + isObjectUpdated = true; break; + } + if(m_channel_id_isSet){ + isObjectUpdated = true; break; + } + }while(false); + return isObjectUpdated; +} +} + diff --git a/swagger/sdrangel/code/qt5/client/SWGDenoiserActions.h b/swagger/sdrangel/code/qt5/client/SWGDenoiserActions.h new file mode 100644 index 000000000..7254fa2cc --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGDenoiserActions.h @@ -0,0 +1,64 @@ +/** + * SDRangel + * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- + * + * OpenAPI spec version: 7.0.0 + * Contact: f4exb06@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +/* + * SWGDenoiserActions.h + * + * Denoiser actions + */ + +#ifndef SWGDenoiserActions_H_ +#define SWGDenoiserActions_H_ + +#include + + + +#include "SWGObject.h" +#include "export.h" + +namespace SWGSDRangel { + +class SWG_API SWGDenoiserActions: public SWGObject { +public: + SWGDenoiserActions(); + SWGDenoiserActions(QString* json); + virtual ~SWGDenoiserActions(); + void init(); + void cleanup(); + + virtual QString asJson () override; + virtual QJsonObject* asJsonObject() override; + virtual void fromJsonObject(QJsonObject &json) override; + virtual SWGDenoiserActions* fromJson(QString &jsonString) override; + + qint32 getDeviceId(); + void setDeviceId(qint32 device_id); + + qint32 getChannelId(); + void setChannelId(qint32 channel_id); + + + virtual bool isSet() override; + +private: + qint32 device_id; + bool m_device_id_isSet; + + qint32 channel_id; + bool m_channel_id_isSet; + +}; + +} + +#endif /* SWGDenoiserActions_H_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGDenoiserSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGDenoiserSettings.cpp new file mode 100644 index 000000000..dfcdf27c3 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGDenoiserSettings.cpp @@ -0,0 +1,369 @@ +/** + * SDRangel + * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- + * + * OpenAPI spec version: 7.0.0 + * Contact: f4exb06@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + + +#include "SWGDenoiserSettings.h" + +#include "SWGHelpers.h" + +#include +#include +#include +#include + +namespace SWGSDRangel { + +SWGDenoiserSettings::SWGDenoiserSettings(QString* json) { + init(); + this->fromJson(*json); +} + +SWGDenoiserSettings::SWGDenoiserSettings() { + denoiser_type = 0; + m_denoiser_type_isSet = false; + audio_mute = 0; + m_audio_mute_isSet = false; + title = nullptr; + m_title_isSet = false; + rgb_color = 0; + m_rgb_color_isSet = false; + file_record_name = nullptr; + m_file_record_name_isSet = false; + record_to_file = 0; + m_record_to_file_isSet = false; + use_reverse_api = 0; + m_use_reverse_api_isSet = false; + reverse_api_address = nullptr; + m_reverse_api_address_isSet = false; + reverse_api_port = 0; + m_reverse_api_port_isSet = false; + reverse_api_feature_set_index = 0; + m_reverse_api_feature_set_index_isSet = false; + reverse_api_feature_index = 0; + m_reverse_api_feature_index_isSet = false; + rollup_state = nullptr; + m_rollup_state_isSet = false; +} + +SWGDenoiserSettings::~SWGDenoiserSettings() { + this->cleanup(); +} + +void +SWGDenoiserSettings::init() { + denoiser_type = 0; + m_denoiser_type_isSet = false; + audio_mute = 0; + m_audio_mute_isSet = false; + title = new QString(""); + m_title_isSet = false; + rgb_color = 0; + m_rgb_color_isSet = false; + file_record_name = new QString(""); + m_file_record_name_isSet = false; + record_to_file = 0; + m_record_to_file_isSet = false; + use_reverse_api = 0; + m_use_reverse_api_isSet = false; + reverse_api_address = new QString(""); + m_reverse_api_address_isSet = false; + reverse_api_port = 0; + m_reverse_api_port_isSet = false; + reverse_api_feature_set_index = 0; + m_reverse_api_feature_set_index_isSet = false; + reverse_api_feature_index = 0; + m_reverse_api_feature_index_isSet = false; + rollup_state = new SWGRollupState(); + m_rollup_state_isSet = false; +} + +void +SWGDenoiserSettings::cleanup() { + + + if(title != nullptr) { + delete title; + } + + if(file_record_name != nullptr) { + delete file_record_name; + } + + + if(reverse_api_address != nullptr) { + delete reverse_api_address; + } + + + + if(rollup_state != nullptr) { + delete rollup_state; + } +} + +SWGDenoiserSettings* +SWGDenoiserSettings::fromJson(QString &json) { + QByteArray array (json.toStdString().c_str()); + QJsonDocument doc = QJsonDocument::fromJson(array); + QJsonObject jsonObject = doc.object(); + this->fromJsonObject(jsonObject); + return this; +} + +void +SWGDenoiserSettings::fromJsonObject(QJsonObject &pJson) { + ::SWGSDRangel::setValue(&denoiser_type, pJson["denoiserType"], "qint32", ""); + + ::SWGSDRangel::setValue(&audio_mute, pJson["audioMute"], "qint32", ""); + + ::SWGSDRangel::setValue(&title, pJson["title"], "QString", "QString"); + + ::SWGSDRangel::setValue(&rgb_color, pJson["rgbColor"], "qint32", ""); + + ::SWGSDRangel::setValue(&file_record_name, pJson["fileRecordName"], "QString", "QString"); + + ::SWGSDRangel::setValue(&record_to_file, pJson["recordToFile"], "qint32", ""); + + ::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_address, pJson["reverseAPIAddress"], "QString", "QString"); + + ::SWGSDRangel::setValue(&reverse_api_port, pJson["reverseAPIPort"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_feature_set_index, pJson["reverseAPIFeatureSetIndex"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_feature_index, pJson["reverseAPIFeatureIndex"], "qint32", ""); + + ::SWGSDRangel::setValue(&rollup_state, pJson["rollupState"], "SWGRollupState", "SWGRollupState"); + +} + +QString +SWGDenoiserSettings::asJson () +{ + QJsonObject* obj = this->asJsonObject(); + + QJsonDocument doc(*obj); + QByteArray bytes = doc.toJson(); + delete obj; + return QString(bytes); +} + +QJsonObject* +SWGDenoiserSettings::asJsonObject() { + QJsonObject* obj = new QJsonObject(); + if(m_denoiser_type_isSet){ + obj->insert("denoiserType", QJsonValue(denoiser_type)); + } + if(m_audio_mute_isSet){ + obj->insert("audioMute", QJsonValue(audio_mute)); + } + if(title != nullptr && *title != QString("")){ + toJsonValue(QString("title"), title, obj, QString("QString")); + } + if(m_rgb_color_isSet){ + obj->insert("rgbColor", QJsonValue(rgb_color)); + } + if(file_record_name != nullptr && *file_record_name != QString("")){ + toJsonValue(QString("fileRecordName"), file_record_name, obj, QString("QString")); + } + if(m_record_to_file_isSet){ + obj->insert("recordToFile", QJsonValue(record_to_file)); + } + if(m_use_reverse_api_isSet){ + obj->insert("useReverseAPI", QJsonValue(use_reverse_api)); + } + if(reverse_api_address != nullptr && *reverse_api_address != QString("")){ + toJsonValue(QString("reverseAPIAddress"), reverse_api_address, obj, QString("QString")); + } + if(m_reverse_api_port_isSet){ + obj->insert("reverseAPIPort", QJsonValue(reverse_api_port)); + } + if(m_reverse_api_feature_set_index_isSet){ + obj->insert("reverseAPIFeatureSetIndex", QJsonValue(reverse_api_feature_set_index)); + } + if(m_reverse_api_feature_index_isSet){ + obj->insert("reverseAPIFeatureIndex", QJsonValue(reverse_api_feature_index)); + } + if((rollup_state != nullptr) && (rollup_state->isSet())){ + toJsonValue(QString("rollupState"), rollup_state, obj, QString("SWGRollupState")); + } + + return obj; +} + +qint32 +SWGDenoiserSettings::getDenoiserType() { + return denoiser_type; +} +void +SWGDenoiserSettings::setDenoiserType(qint32 denoiser_type) { + this->denoiser_type = denoiser_type; + this->m_denoiser_type_isSet = true; +} + +qint32 +SWGDenoiserSettings::getAudioMute() { + return audio_mute; +} +void +SWGDenoiserSettings::setAudioMute(qint32 audio_mute) { + this->audio_mute = audio_mute; + this->m_audio_mute_isSet = true; +} + +QString* +SWGDenoiserSettings::getTitle() { + return title; +} +void +SWGDenoiserSettings::setTitle(QString* title) { + this->title = title; + this->m_title_isSet = true; +} + +qint32 +SWGDenoiserSettings::getRgbColor() { + return rgb_color; +} +void +SWGDenoiserSettings::setRgbColor(qint32 rgb_color) { + this->rgb_color = rgb_color; + this->m_rgb_color_isSet = true; +} + +QString* +SWGDenoiserSettings::getFileRecordName() { + return file_record_name; +} +void +SWGDenoiserSettings::setFileRecordName(QString* file_record_name) { + this->file_record_name = file_record_name; + this->m_file_record_name_isSet = true; +} + +qint32 +SWGDenoiserSettings::getRecordToFile() { + return record_to_file; +} +void +SWGDenoiserSettings::setRecordToFile(qint32 record_to_file) { + this->record_to_file = record_to_file; + this->m_record_to_file_isSet = true; +} + +qint32 +SWGDenoiserSettings::getUseReverseApi() { + return use_reverse_api; +} +void +SWGDenoiserSettings::setUseReverseApi(qint32 use_reverse_api) { + this->use_reverse_api = use_reverse_api; + this->m_use_reverse_api_isSet = true; +} + +QString* +SWGDenoiserSettings::getReverseApiAddress() { + return reverse_api_address; +} +void +SWGDenoiserSettings::setReverseApiAddress(QString* reverse_api_address) { + this->reverse_api_address = reverse_api_address; + this->m_reverse_api_address_isSet = true; +} + +qint32 +SWGDenoiserSettings::getReverseApiPort() { + return reverse_api_port; +} +void +SWGDenoiserSettings::setReverseApiPort(qint32 reverse_api_port) { + this->reverse_api_port = reverse_api_port; + this->m_reverse_api_port_isSet = true; +} + +qint32 +SWGDenoiserSettings::getReverseApiFeatureSetIndex() { + return reverse_api_feature_set_index; +} +void +SWGDenoiserSettings::setReverseApiFeatureSetIndex(qint32 reverse_api_feature_set_index) { + this->reverse_api_feature_set_index = reverse_api_feature_set_index; + this->m_reverse_api_feature_set_index_isSet = true; +} + +qint32 +SWGDenoiserSettings::getReverseApiFeatureIndex() { + return reverse_api_feature_index; +} +void +SWGDenoiserSettings::setReverseApiFeatureIndex(qint32 reverse_api_feature_index) { + this->reverse_api_feature_index = reverse_api_feature_index; + this->m_reverse_api_feature_index_isSet = true; +} + +SWGRollupState* +SWGDenoiserSettings::getRollupState() { + return rollup_state; +} +void +SWGDenoiserSettings::setRollupState(SWGRollupState* rollup_state) { + this->rollup_state = rollup_state; + this->m_rollup_state_isSet = true; +} + + +bool +SWGDenoiserSettings::isSet(){ + bool isObjectUpdated = false; + do{ + if(m_denoiser_type_isSet){ + isObjectUpdated = true; break; + } + if(m_audio_mute_isSet){ + isObjectUpdated = true; break; + } + if(title && *title != QString("")){ + isObjectUpdated = true; break; + } + if(m_rgb_color_isSet){ + isObjectUpdated = true; break; + } + if(file_record_name && *file_record_name != QString("")){ + isObjectUpdated = true; break; + } + if(m_record_to_file_isSet){ + isObjectUpdated = true; break; + } + if(m_use_reverse_api_isSet){ + isObjectUpdated = true; break; + } + if(reverse_api_address && *reverse_api_address != QString("")){ + isObjectUpdated = true; break; + } + if(m_reverse_api_port_isSet){ + isObjectUpdated = true; break; + } + if(m_reverse_api_feature_set_index_isSet){ + isObjectUpdated = true; break; + } + if(m_reverse_api_feature_index_isSet){ + isObjectUpdated = true; break; + } + if(rollup_state && rollup_state->isSet()){ + isObjectUpdated = true; break; + } + }while(false); + return isObjectUpdated; +} +} + diff --git a/swagger/sdrangel/code/qt5/client/SWGDenoiserSettings.h b/swagger/sdrangel/code/qt5/client/SWGDenoiserSettings.h new file mode 100644 index 000000000..e8d13a618 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGDenoiserSettings.h @@ -0,0 +1,126 @@ +/** + * SDRangel + * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- + * + * OpenAPI spec version: 7.0.0 + * Contact: f4exb06@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +/* + * SWGDenoiserSettings.h + * + * Denoiser + */ + +#ifndef SWGDenoiserSettings_H_ +#define SWGDenoiserSettings_H_ + +#include + + +#include "SWGRollupState.h" +#include + +#include "SWGObject.h" +#include "export.h" + +namespace SWGSDRangel { + +class SWG_API SWGDenoiserSettings: public SWGObject { +public: + SWGDenoiserSettings(); + SWGDenoiserSettings(QString* json); + virtual ~SWGDenoiserSettings(); + void init(); + void cleanup(); + + virtual QString asJson () override; + virtual QJsonObject* asJsonObject() override; + virtual void fromJsonObject(QJsonObject &json) override; + virtual SWGDenoiserSettings* fromJson(QString &jsonString) override; + + qint32 getDenoiserType(); + void setDenoiserType(qint32 denoiser_type); + + qint32 getAudioMute(); + void setAudioMute(qint32 audio_mute); + + QString* getTitle(); + void setTitle(QString* title); + + qint32 getRgbColor(); + void setRgbColor(qint32 rgb_color); + + QString* getFileRecordName(); + void setFileRecordName(QString* file_record_name); + + qint32 getRecordToFile(); + void setRecordToFile(qint32 record_to_file); + + qint32 getUseReverseApi(); + void setUseReverseApi(qint32 use_reverse_api); + + QString* getReverseApiAddress(); + void setReverseApiAddress(QString* reverse_api_address); + + qint32 getReverseApiPort(); + void setReverseApiPort(qint32 reverse_api_port); + + qint32 getReverseApiFeatureSetIndex(); + void setReverseApiFeatureSetIndex(qint32 reverse_api_feature_set_index); + + qint32 getReverseApiFeatureIndex(); + void setReverseApiFeatureIndex(qint32 reverse_api_feature_index); + + SWGRollupState* getRollupState(); + void setRollupState(SWGRollupState* rollup_state); + + + virtual bool isSet() override; + +private: + qint32 denoiser_type; + bool m_denoiser_type_isSet; + + qint32 audio_mute; + bool m_audio_mute_isSet; + + QString* title; + bool m_title_isSet; + + qint32 rgb_color; + bool m_rgb_color_isSet; + + QString* file_record_name; + bool m_file_record_name_isSet; + + qint32 record_to_file; + bool m_record_to_file_isSet; + + qint32 use_reverse_api; + bool m_use_reverse_api_isSet; + + QString* reverse_api_address; + bool m_reverse_api_address_isSet; + + qint32 reverse_api_port; + bool m_reverse_api_port_isSet; + + qint32 reverse_api_feature_set_index; + bool m_reverse_api_feature_set_index_isSet; + + qint32 reverse_api_feature_index; + bool m_reverse_api_feature_index_isSet; + + SWGRollupState* rollup_state; + bool m_rollup_state_isSet; + +}; + +} + +#endif /* SWGDenoiserSettings_H_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGFeatureActions.cpp b/swagger/sdrangel/code/qt5/client/SWGFeatureActions.cpp index 5f1773a73..f3aaa31d7 100644 --- a/swagger/sdrangel/code/qt5/client/SWGFeatureActions.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGFeatureActions.cpp @@ -62,6 +62,8 @@ SWGFeatureActions::SWGFeatureActions() { m_vor_localizer_actions_isSet = false; demod_analyzer_actions = nullptr; m_demod_analyzer_actions_isSet = false; + denoiser_actions = nullptr; + m_denoiser_actions_isSet = false; } SWGFeatureActions::~SWGFeatureActions() { @@ -104,6 +106,8 @@ SWGFeatureActions::init() { m_vor_localizer_actions_isSet = false; demod_analyzer_actions = new SWGDemodAnalyzerActions(); m_demod_analyzer_actions_isSet = false; + denoiser_actions = new SWGDenoiserActions(); + m_denoiser_actions_isSet = false; } void @@ -155,6 +159,9 @@ SWGFeatureActions::cleanup() { if(demod_analyzer_actions != nullptr) { delete demod_analyzer_actions; } + if(denoiser_actions != nullptr) { + delete denoiser_actions; + } } SWGFeatureActions* @@ -202,6 +209,8 @@ SWGFeatureActions::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&demod_analyzer_actions, pJson["DemodAnalyzerActions"], "SWGDemodAnalyzerActions", "SWGDemodAnalyzerActions"); + ::SWGSDRangel::setValue(&denoiser_actions, pJson["DenoiserActions"], "SWGDenoiserActions", "SWGDenoiserActions"); + } QString @@ -269,6 +278,9 @@ SWGFeatureActions::asJsonObject() { if((demod_analyzer_actions != nullptr) && (demod_analyzer_actions->isSet())){ toJsonValue(QString("DemodAnalyzerActions"), demod_analyzer_actions, obj, QString("SWGDemodAnalyzerActions")); } + if((denoiser_actions != nullptr) && (denoiser_actions->isSet())){ + toJsonValue(QString("DenoiserActions"), denoiser_actions, obj, QString("SWGDenoiserActions")); + } return obj; } @@ -443,6 +455,16 @@ SWGFeatureActions::setDemodAnalyzerActions(SWGDemodAnalyzerActions* demod_analyz this->m_demod_analyzer_actions_isSet = true; } +SWGDenoiserActions* +SWGFeatureActions::getDenoiserActions() { + return denoiser_actions; +} +void +SWGFeatureActions::setDenoiserActions(SWGDenoiserActions* denoiser_actions) { + this->denoiser_actions = denoiser_actions; + this->m_denoiser_actions_isSet = true; +} + bool SWGFeatureActions::isSet(){ @@ -499,6 +521,9 @@ SWGFeatureActions::isSet(){ if(demod_analyzer_actions && demod_analyzer_actions->isSet()){ isObjectUpdated = true; break; } + if(denoiser_actions && denoiser_actions->isSet()){ + isObjectUpdated = true; break; + } }while(false); return isObjectUpdated; } diff --git a/swagger/sdrangel/code/qt5/client/SWGFeatureActions.h b/swagger/sdrangel/code/qt5/client/SWGFeatureActions.h index 0d117221e..1cda2aea2 100644 --- a/swagger/sdrangel/code/qt5/client/SWGFeatureActions.h +++ b/swagger/sdrangel/code/qt5/client/SWGFeatureActions.h @@ -25,6 +25,7 @@ #include "SWGAFCActions.h" #include "SWGAMBEActions.h" #include "SWGDemodAnalyzerActions.h" +#include "SWGDenoiserActions.h" #include "SWGGS232ControllerActions.h" #include "SWGLimeRFEActions.h" #include "SWGMapActions.h" @@ -107,6 +108,9 @@ public: SWGDemodAnalyzerActions* getDemodAnalyzerActions(); void setDemodAnalyzerActions(SWGDemodAnalyzerActions* demod_analyzer_actions); + SWGDenoiserActions* getDenoiserActions(); + void setDenoiserActions(SWGDenoiserActions* denoiser_actions); + virtual bool isSet() override; @@ -162,6 +166,9 @@ private: SWGDemodAnalyzerActions* demod_analyzer_actions; bool m_demod_analyzer_actions_isSet; + SWGDenoiserActions* denoiser_actions; + bool m_denoiser_actions_isSet; + }; } diff --git a/swagger/sdrangel/code/qt5/client/SWGFeatureSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGFeatureSettings.cpp index fdce886bd..bbaf36643 100644 --- a/swagger/sdrangel/code/qt5/client/SWGFeatureSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGFeatureSettings.cpp @@ -46,6 +46,8 @@ SWGFeatureSettings::SWGFeatureSettings() { m_aprs_settings_isSet = false; demod_analyzer_settings = nullptr; m_demod_analyzer_settings_isSet = false; + denoiser_settings = nullptr; + m_denoiser_settings_isSet = false; jogdial_controller_settings = nullptr; m_jogdial_controller_settings_isSet = false; gs232_controller_settings = nullptr; @@ -100,6 +102,8 @@ SWGFeatureSettings::init() { m_aprs_settings_isSet = false; demod_analyzer_settings = new SWGDemodAnalyzerSettings(); m_demod_analyzer_settings_isSet = false; + denoiser_settings = new SWGDenoiserSettings(); + m_denoiser_settings_isSet = false; jogdial_controller_settings = new SWGJogdialControllerSettings(); m_jogdial_controller_settings_isSet = false; gs232_controller_settings = new SWGGS232ControllerSettings(); @@ -155,6 +159,9 @@ SWGFeatureSettings::cleanup() { if(demod_analyzer_settings != nullptr) { delete demod_analyzer_settings; } + if(denoiser_settings != nullptr) { + delete denoiser_settings; + } if(jogdial_controller_settings != nullptr) { delete jogdial_controller_settings; } @@ -228,6 +235,8 @@ SWGFeatureSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&demod_analyzer_settings, pJson["DemodAnalyzerSettings"], "SWGDemodAnalyzerSettings", "SWGDemodAnalyzerSettings"); + ::SWGSDRangel::setValue(&denoiser_settings, pJson["DenoiserSettings"], "SWGDenoiserSettings", "SWGDenoiserSettings"); + ::SWGSDRangel::setValue(&jogdial_controller_settings, pJson["JogdialControllerSettings"], "SWGJogdialControllerSettings", "SWGJogdialControllerSettings"); ::SWGSDRangel::setValue(&gs232_controller_settings, pJson["GS232ControllerSettings"], "SWGGS232ControllerSettings", "SWGGS232ControllerSettings"); @@ -299,6 +308,9 @@ SWGFeatureSettings::asJsonObject() { if((demod_analyzer_settings != nullptr) && (demod_analyzer_settings->isSet())){ toJsonValue(QString("DemodAnalyzerSettings"), demod_analyzer_settings, obj, QString("SWGDemodAnalyzerSettings")); } + if((denoiser_settings != nullptr) && (denoiser_settings->isSet())){ + toJsonValue(QString("DenoiserSettings"), denoiser_settings, obj, QString("SWGDenoiserSettings")); + } if((jogdial_controller_settings != nullptr) && (jogdial_controller_settings->isSet())){ toJsonValue(QString("JogdialControllerSettings"), jogdial_controller_settings, obj, QString("SWGJogdialControllerSettings")); } @@ -435,6 +447,16 @@ SWGFeatureSettings::setDemodAnalyzerSettings(SWGDemodAnalyzerSettings* demod_ana this->m_demod_analyzer_settings_isSet = true; } +SWGDenoiserSettings* +SWGFeatureSettings::getDenoiserSettings() { + return denoiser_settings; +} +void +SWGFeatureSettings::setDenoiserSettings(SWGDenoiserSettings* denoiser_settings) { + this->denoiser_settings = denoiser_settings; + this->m_denoiser_settings_isSet = true; +} + SWGJogdialControllerSettings* SWGFeatureSettings::getJogdialControllerSettings() { return jogdial_controller_settings; @@ -607,6 +629,9 @@ SWGFeatureSettings::isSet(){ if(demod_analyzer_settings && demod_analyzer_settings->isSet()){ isObjectUpdated = true; break; } + if(denoiser_settings && denoiser_settings->isSet()){ + isObjectUpdated = true; break; + } if(jogdial_controller_settings && jogdial_controller_settings->isSet()){ isObjectUpdated = true; break; } diff --git a/swagger/sdrangel/code/qt5/client/SWGFeatureSettings.h b/swagger/sdrangel/code/qt5/client/SWGFeatureSettings.h index 6de1b9ad6..41e1b5823 100644 --- a/swagger/sdrangel/code/qt5/client/SWGFeatureSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGFeatureSettings.h @@ -28,6 +28,7 @@ #include "SWGAPRSSettings.h" #include "SWGAntennaToolsSettings.h" #include "SWGDemodAnalyzerSettings.h" +#include "SWGDenoiserSettings.h" #include "SWGGS232ControllerSettings.h" #include "SWGJogdialControllerSettings.h" #include "SWGLimeRFESettings.h" @@ -89,6 +90,9 @@ public: SWGDemodAnalyzerSettings* getDemodAnalyzerSettings(); void setDemodAnalyzerSettings(SWGDemodAnalyzerSettings* demod_analyzer_settings); + SWGDenoiserSettings* getDenoiserSettings(); + void setDenoiserSettings(SWGDenoiserSettings* denoiser_settings); + SWGJogdialControllerSettings* getJogdialControllerSettings(); void setJogdialControllerSettings(SWGJogdialControllerSettings* jogdial_controller_settings); @@ -162,6 +166,9 @@ private: SWGDemodAnalyzerSettings* demod_analyzer_settings; bool m_demod_analyzer_settings_isSet; + SWGDenoiserSettings* denoiser_settings; + bool m_denoiser_settings_isSet; + SWGJogdialControllerSettings* jogdial_controller_settings; bool m_jogdial_controller_settings_isSet; diff --git a/swagger/sdrangel/code/qt5/client/SWGModelFactory.h b/swagger/sdrangel/code/qt5/client/SWGModelFactory.h index bdface496..e12058e8d 100644 --- a/swagger/sdrangel/code/qt5/client/SWGModelFactory.h +++ b/swagger/sdrangel/code/qt5/client/SWGModelFactory.h @@ -115,6 +115,8 @@ #include "SWGDVSerialDevices.h" #include "SWGDemodAnalyzerActions.h" #include "SWGDemodAnalyzerSettings.h" +#include "SWGDenoiserActions.h" +#include "SWGDenoiserSettings.h" #include "SWGDeviceActions.h" #include "SWGDeviceConfig.h" #include "SWGDeviceListItem.h" @@ -897,6 +899,16 @@ namespace SWGSDRangel { obj->init(); return obj; } + if(QString("SWGDenoiserActions").compare(type) == 0) { + SWGDenoiserActions *obj = new SWGDenoiserActions(); + obj->init(); + return obj; + } + if(QString("SWGDenoiserSettings").compare(type) == 0) { + SWGDenoiserSettings *obj = new SWGDenoiserSettings(); + obj->init(); + return obj; + } if(QString("SWGDeviceActions").compare(type) == 0) { SWGDeviceActions *obj = new SWGDeviceActions(); obj->init();