diff --git a/doc/img/AudioInput_plugin.png b/doc/img/AudioInput_plugin.png new file mode 100644 index 000000000..2a875dad3 Binary files /dev/null and b/doc/img/AudioInput_plugin.png differ diff --git a/doc/img/AudioInput_plugin.xcf b/doc/img/AudioInput_plugin.xcf new file mode 100644 index 000000000..988be4707 Binary files /dev/null and b/doc/img/AudioInput_plugin.xcf differ diff --git a/plugins/samplesource/CMakeLists.txt b/plugins/samplesource/CMakeLists.txt index c86941e2d..29e40bb00 100644 --- a/plugins/samplesource/CMakeLists.txt +++ b/plugins/samplesource/CMakeLists.txt @@ -62,4 +62,5 @@ if(ENABLE_USRP AND UHD_FOUND) add_subdirectory(usrpinput) endif() +add_subdirectory(audioinput) add_subdirectory(kiwisdr) \ No newline at end of file diff --git a/plugins/samplesource/audioinput/CMakeLists.txt b/plugins/samplesource/audioinput/CMakeLists.txt new file mode 100644 index 000000000..13233fdfc --- /dev/null +++ b/plugins/samplesource/audioinput/CMakeLists.txt @@ -0,0 +1,57 @@ +project(audioinput) + +set(audioinput_SOURCES + audioinput.cpp + audioinputplugin.cpp + audioinputsettings.cpp + audioinputwebapiadapter.cpp + audioinputthread.cpp +) + +set(audioinput_HEADERS + audioinput.h + audioinputplugin.h + audioinputsettings.h + audioinputwebapiadapter.h + audioinputthread.h +) + +include_directories( + ${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client +) + +if(NOT SERVER_MODE) + set(audioinput_SOURCES + ${audioinput_SOURCES} + audioinputgui.cpp + audioinputgui.ui + ) + set(audioinput_HEADERS + ${audioinput_HEADERS} + audioinputgui.h + ) + + set(TARGET_NAME inputaudio) + set(TARGET_LIB "Qt5::Widgets") + set(TARGET_LIB_GUI "sdrgui") + set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR}) +else() + set(TARGET_NAME inputaudiosrv) + set(TARGET_LIB "") + set(TARGET_LIB_GUI "") + set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR}) +endif() + +add_library(${TARGET_NAME} SHARED + ${audioinput_SOURCES} +) + +target_link_libraries(${TARGET_NAME} + Qt5::Core + ${TARGET_LIB} + sdrbase + ${TARGET_LIB_GUI} + swagger +) + +install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER}) diff --git a/plugins/samplesource/audioinput/audioinput.cpp b/plugins/samplesource/audioinput/audioinput.cpp new file mode 100644 index 000000000..e03173627 --- /dev/null +++ b/plugins/samplesource/audioinput/audioinput.cpp @@ -0,0 +1,519 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015-2018 Edouard Griffiths, F4EXB // +// Copyright (C) 2020 Jon Beniston, M7RCE // +// // +// 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 + +#include "SWGDeviceSettings.h" +#include "SWGDeviceState.h" + +#include "dsp/dspcommands.h" +#include "dsp/dspengine.h" +#include "device/deviceapi.h" + +#include "audioinput.h" +#include "audioinputthread.h" + +MESSAGE_CLASS_DEFINITION(AudioInputSource::AudioInput::MsgConfigureAudioInput, Message) +MESSAGE_CLASS_DEFINITION(AudioInputSource::AudioInput::MsgStartStop, Message) + +namespace AudioInputSource { + +AudioInput::AudioInput(DeviceAPI *deviceAPI) : + m_deviceAPI(deviceAPI), + m_settings(), + m_thread(nullptr), + m_deviceDescription("AudioInput"), + m_running(false) +{ + m_fifo.setSize(20*AudioInputThread::m_convBufSamples); + openDevice(); + m_deviceAPI->setNbSourceStreams(1); + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); +} + +AudioInput::~AudioInput() +{ + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + delete m_networkManager; + + if (m_running) { + stop(); + } + + closeDevice(); +} + +void AudioInput::destroy() +{ + delete this; +} + +bool AudioInput::openDevice() +{ + if (!openAudioDevice(m_settings.m_deviceName, m_settings.m_sampleRate)) + { + qCritical("AudioInput::openDevice: could not open audio source"); + return false; + } + else + return true; +} + +bool AudioInput::openAudioDevice(QString deviceName, qint32 sampleRate) +{ + AudioDeviceManager *audioDeviceManager = DSPEngine::instance()->getAudioDeviceManager(); + const QList& audioList = audioDeviceManager->getInputDevices(); + + for (const auto &itAudio : audioList) + { + if (AudioInputSettings::getFullDeviceName(itAudio) == deviceName) + { + // FIXME: getInputDeviceIndex needs a realm parameter (itAudio.realm()) - need to look on Linux to see if it uses default? + int deviceIndex = audioDeviceManager->getInputDeviceIndex(itAudio.deviceName()); + m_audioInput.start(deviceIndex, sampleRate); + m_audioInput.addFifo(&m_fifo); + return true; + } + } + return false; +} + +void AudioInput::init() +{ + applySettings(m_settings, true); +} + +bool AudioInput::start() +{ + qDebug() << "AudioInput::start"; + + if (m_running) stop(); + + if(!m_sampleFifo.setSize(96000*4)) + { + qCritical("Could not allocate SampleFifo"); + return false; + } + + m_thread = new AudioInputThread(&m_sampleFifo, &m_fifo); + m_thread->setLog2Decimation(m_settings.m_log2Decim); + m_thread->setIQMapping(m_settings.m_iqMapping); + m_thread->startWork(); + + applySettings(m_settings, true); + + qDebug("AudioInput::started"); + m_running = true; + + return true; +} + +void AudioInput::closeDevice() +{ + m_audioInput.removeFifo(&m_fifo); + m_audioInput.stop(); +} + +void AudioInput::stop() +{ + if (m_thread) + { + m_thread->stopWork(); + // wait for thread to quit ? + delete m_thread; + m_thread = nullptr; + } + + m_running = false; +} + +QByteArray AudioInput::serialize() const +{ + return m_settings.serialize(); +} + +bool AudioInput::deserialize(const QByteArray& data) +{ + bool success = true; + + if (!m_settings.deserialize(data)) + { + m_settings.resetToDefaults(); + success = false; + } + + MsgConfigureAudioInput* message = MsgConfigureAudioInput::create(m_settings, true); + m_inputMessageQueue.push(message); + + if (m_guiMessageQueue) + { + MsgConfigureAudioInput* messageToGUI = MsgConfigureAudioInput::create(m_settings, true); + m_guiMessageQueue->push(messageToGUI); + } + + return success; +} + +const QString& AudioInput::getDeviceDescription() const +{ + return m_deviceDescription; +} + +int AudioInput::getSampleRate() const +{ + return m_settings.m_sampleRate/(1<initDeviceEngine()) + { + m_deviceAPI->startDeviceEngine(); + } + } + else + { + m_deviceAPI->stopDeviceEngine(); + } + + if (m_settings.m_useReverseAPI) { + webapiReverseSendStartStop(cmd.getStartStop()); + } + + return true; + } + else + { + return false; + } +} + +void AudioInput::applySettings(const AudioInputSettings& settings, bool force) +{ + bool forwardChange = false; + QList reverseAPIKeys; + + if ((m_settings.m_deviceName != settings.m_deviceName) + || (m_settings.m_sampleRate != settings.m_sampleRate) || force) + { + closeDevice(); + if (openAudioDevice(settings.m_deviceName, settings.m_sampleRate)) + qDebug() << "AudioInput::applySettings: opened device " << settings.m_deviceName << " with sample rate " << m_audioInput.getRate(); + else + qCritical() << "AudioInput::applySettings: failed to open device " << settings.m_deviceName; + } + + if ((m_settings.m_deviceName != settings.m_deviceName) || force) + { + //reverseAPIKeys.append("device"); + } + + if ((m_settings.m_sampleRate != settings.m_sampleRate) || force) + { + //reverseAPIKeys.append("sampleRate"); + forwardChange = true; + } + + if ((m_settings.m_volume != settings.m_volume) || force) + { + //reverseAPIKeys.append("volume"); + m_audioInput.setVolume(settings.m_volume); + qDebug() << "AudioInput::applySettings: set volume to " << settings.m_volume; + } + + if ((m_settings.m_log2Decim != settings.m_log2Decim) || force) + { + reverseAPIKeys.append("log2Decim"); + forwardChange = true; + + if (m_thread) + { + m_thread->setLog2Decimation(settings.m_log2Decim); + qDebug() << "AudioInput::applySettings: set decimation to " << (1<setIQMapping(settings.m_iqMapping); + } + + if (settings.m_useReverseAPI) + { + bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || + (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || + (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || + (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex); + webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force); + } + + m_settings = settings; + + if (forwardChange) + { + DSPSignalNotification *notif = new DSPSignalNotification(m_settings.m_sampleRate/(1<getDeviceEngineInputMessageQueue()->push(notif); + } +} + +int AudioInput::webapiRunGet( + SWGSDRangel::SWGDeviceState& response, + QString& errorMessage) +{ + (void) errorMessage; + m_deviceAPI->getDeviceEngineStateStr(*response.getState()); + return 200; +} + +int AudioInput::webapiRun( + bool run, + SWGSDRangel::SWGDeviceState& response, + QString& errorMessage) +{ + (void) errorMessage; + m_deviceAPI->getDeviceEngineStateStr(*response.getState()); + MsgStartStop *message = MsgStartStop::create(run); + m_inputMessageQueue.push(message); + + if (m_guiMessageQueue) // forward to GUI if any + { + MsgStartStop *msgToGUI = MsgStartStop::create(run); + m_guiMessageQueue->push(msgToGUI); + } + + return 200; +} + +int AudioInput::webapiSettingsGet( + SWGSDRangel::SWGDeviceSettings& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setAudioInputSettings(new SWGSDRangel::SWGAudioInputSettings()); + response.getAudioInputSettings()->init(); + webapiFormatDeviceSettings(response, m_settings); + + return 200; +} + +int AudioInput::webapiSettingsPutPatch( + bool force, + const QStringList& deviceSettingsKeys, + SWGSDRangel::SWGDeviceSettings& response, // query + response + QString& errorMessage) +{ + (void) errorMessage; + AudioInputSettings settings = m_settings; + webapiUpdateDeviceSettings(settings, deviceSettingsKeys, response); + + MsgConfigureAudioInput *msg = MsgConfigureAudioInput::create(settings, force); + m_inputMessageQueue.push(msg); + + if (m_guiMessageQueue) // forward to GUI if any + { + MsgConfigureAudioInput *msgToGUI = MsgConfigureAudioInput::create(settings, force); + m_guiMessageQueue->push(msgToGUI); + } + + webapiFormatDeviceSettings(response, settings); + return 200; +} + +void AudioInput::webapiUpdateDeviceSettings( + AudioInputSettings& settings, + const QStringList& deviceSettingsKeys, + SWGSDRangel::SWGDeviceSettings& response) +{ + if (deviceSettingsKeys.contains("device")) { + settings.m_deviceName = *response.getAudioInputSettings()->getDevice(); + } + if (deviceSettingsKeys.contains("devSampleRate")) { + settings.m_sampleRate = response.getAudioInputSettings()->getDevSampleRate(); + } + if (deviceSettingsKeys.contains("volume")) { + settings.m_volume = response.getAudioInputSettings()->getVolume(); + } + if (deviceSettingsKeys.contains("log2Decim")) { + settings.m_log2Decim = response.getAudioInputSettings()->getLog2Decim(); + } + if (deviceSettingsKeys.contains("iqMapping")) { + settings.m_iqMapping = (AudioInputSettings::IQMapping)response.getAudioInputSettings()->getIqMapping(); + } + if (deviceSettingsKeys.contains("useReverseAPI")) { + settings.m_useReverseAPI = response.getAudioInputSettings()->getUseReverseApi() != 0; + } + if (deviceSettingsKeys.contains("reverseAPIAddress")) { + settings.m_reverseAPIAddress = *response.getAudioInputSettings()->getReverseApiAddress(); + } + if (deviceSettingsKeys.contains("reverseAPIPort")) { + settings.m_reverseAPIPort = response.getAudioInputSettings()->getReverseApiPort(); + } + if (deviceSettingsKeys.contains("reverseAPIDeviceIndex")) { + settings.m_reverseAPIDeviceIndex = response.getAudioInputSettings()->getReverseApiDeviceIndex(); + } +} + +void AudioInput::webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const AudioInputSettings& settings) +{ + response.getAudioInputSettings()->setDevice(new QString(settings.m_deviceName)); + response.getAudioInputSettings()->setDevSampleRate(settings.m_sampleRate); + response.getAudioInputSettings()->setVolume(settings.m_volume); + response.getAudioInputSettings()->setLog2Decim(settings.m_log2Decim); + response.getAudioInputSettings()->setIqMapping((int)settings.m_iqMapping); + + response.getAudioInputSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0); + + if (response.getAudioInputSettings()->getReverseApiAddress()) { + *response.getAudioInputSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress; + } else { + response.getAudioInputSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress)); + } + + response.getAudioInputSettings()->setReverseApiPort(settings.m_reverseAPIPort); + response.getAudioInputSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex); +} + +void AudioInput::webapiReverseSendSettings(QList& deviceSettingsKeys, const AudioInputSettings& settings, bool force) +{ + SWGSDRangel::SWGDeviceSettings *swgDeviceSettings = new SWGSDRangel::SWGDeviceSettings(); + swgDeviceSettings->setDirection(0); // single Rx + swgDeviceSettings->setOriginatorIndex(m_deviceAPI->getDeviceSetIndex()); + swgDeviceSettings->setDeviceHwType(new QString("AudioInput")); + swgDeviceSettings->setAudioInputSettings(new SWGSDRangel::SWGAudioInputSettings()); + SWGSDRangel::SWGAudioInputSettings *swgAudioInputSettings = swgDeviceSettings->getAudioInputSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (deviceSettingsKeys.contains("device") || force) { + swgAudioInputSettings->setDevice(new QString(settings.m_deviceName)); + } + if (deviceSettingsKeys.contains("devSampleRate") || force) { + swgAudioInputSettings->setDevSampleRate(settings.m_sampleRate); + } + if (deviceSettingsKeys.contains("volume") || force) { + swgAudioInputSettings->setVolume(settings.m_volume); + } + if (deviceSettingsKeys.contains("log2Decim") || force) { + swgAudioInputSettings->setLog2Decim(settings.m_log2Decim); + } + if (deviceSettingsKeys.contains("iqMapping") || force) { + swgAudioInputSettings->setIqMapping(settings.m_iqMapping); + } + + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer = new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgDeviceSettings->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 swgDeviceSettings; +} + +void AudioInput::webapiReverseSendStartStop(bool start) +{ + SWGSDRangel::SWGDeviceSettings *swgDeviceSettings = new SWGSDRangel::SWGDeviceSettings(); + swgDeviceSettings->setDirection(0); // single Rx + swgDeviceSettings->setOriginatorIndex(m_deviceAPI->getDeviceSetIndex()); + swgDeviceSettings->setDeviceHwType(new QString("AudioInput")); + + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/run") + .arg(m_settings.m_reverseAPIAddress) + .arg(m_settings.m_reverseAPIPort) + .arg(m_settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer = new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgDeviceSettings->asJson().toUtf8()); + buffer->seek(0); + QNetworkReply *reply; + + if (start) { + reply = m_networkManager->sendCustomRequest(m_networkRequest, "POST", buffer); + } else { + reply = m_networkManager->sendCustomRequest(m_networkRequest, "DELETE", buffer); + } + + buffer->setParent(reply); + delete swgDeviceSettings; +} + +void AudioInput::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "AudioInput::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + } + else + { + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("AudioInput::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); + } + + reply->deleteLater(); +} + +} diff --git a/plugins/samplesource/audioinput/audioinput.h b/plugins/samplesource/audioinput/audioinput.h new file mode 100644 index 000000000..2632acbfa --- /dev/null +++ b/plugins/samplesource/audioinput/audioinput.h @@ -0,0 +1,161 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015-2018 Edouard Griffiths, F4EXB // +// Copyright (C) 2020 Jon Beniston, M7RCE // +// // +// 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_AUDIOINPUT_H +#define INCLUDE_AUDIOINPUT_H + +#include + +#include +#include +#include + +#include "dsp/devicesamplesource.h" +#include "audio/audioinput.h" +#include "audio/audiofifo.h" + +#include "audioinputsettings.h" + +class QNetworkAccessManager; +class QNetworkReply; +class DeviceAPI; +class AudioInputThread; + +// AudioInput is used in sdrbase/audio/audioinput.h +namespace AudioInputSource { + +class AudioInput : public DeviceSampleSource { + Q_OBJECT +public: + class MsgConfigureAudioInput : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const AudioInputSettings& getSettings() const { return m_settings; } + bool getForce() const { return m_force; } + + static MsgConfigureAudioInput* create(const AudioInputSettings& settings, bool force) + { + return new MsgConfigureAudioInput(settings, force); + } + + private: + AudioInputSettings m_settings; + bool m_force; + + MsgConfigureAudioInput(const AudioInputSettings& settings, bool force) : + Message(), + m_settings(settings), + 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) + { } + }; + + AudioInput(DeviceAPI *deviceAPI); + virtual ~AudioInput(); + virtual void destroy(); + + virtual void init(); + virtual bool start(); + virtual void stop(); + + virtual QByteArray serialize() const; + virtual bool deserialize(const QByteArray& data); + + virtual void setMessageQueueToGUI(MessageQueue *queue) { m_guiMessageQueue = queue; } + virtual const QString& getDeviceDescription() const; + virtual int getSampleRate() const; + virtual void setSampleRate(int sampleRate) { (void) sampleRate; } + virtual quint64 getCenterFrequency() const; + virtual void setCenterFrequency(qint64 centerFrequency); + + virtual bool handleMessage(const Message& message); + + virtual int webapiSettingsGet( + SWGSDRangel::SWGDeviceSettings& response, + QString& errorMessage); + + virtual int webapiSettingsPutPatch( + bool force, + const QStringList& deviceSettingsKeys, + SWGSDRangel::SWGDeviceSettings& response, // query + response + QString& errorMessage); + + virtual int webapiRunGet( + SWGSDRangel::SWGDeviceState& response, + QString& errorMessage); + + virtual int webapiRun( + bool run, + SWGSDRangel::SWGDeviceState& response, + QString& errorMessage); + + static void webapiFormatDeviceSettings( + SWGSDRangel::SWGDeviceSettings& response, + const AudioInputSettings& settings); + + static void webapiUpdateDeviceSettings( + AudioInputSettings& settings, + const QStringList& deviceSettingsKeys, + SWGSDRangel::SWGDeviceSettings& response); + +private: + DeviceAPI *m_deviceAPI; + ::AudioInput m_audioInput; + AudioFifo m_fifo; + QMutex m_mutex; + AudioInputSettings m_settings; + AudioInputThread* m_thread; + QString m_deviceDescription; + bool m_running; + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; + + bool openDevice(); + void closeDevice(); + bool openAudioDevice(QString deviceName, int sampleRate); + void applySettings(const AudioInputSettings& settings, bool force); + + void webapiReverseSendSettings(QList& deviceSettingsKeys, const AudioInputSettings& settings, bool force); + void webapiReverseSendStartStop(bool start); + +private slots: + void networkManagerFinished(QNetworkReply *reply); +}; + +} + +#endif // INCLUDE_AUDIOINPUT_H diff --git a/plugins/samplesource/audioinput/audioinputgui.cpp b/plugins/samplesource/audioinput/audioinputgui.cpp new file mode 100644 index 000000000..32fdac22c --- /dev/null +++ b/plugins/samplesource/audioinput/audioinputgui.cpp @@ -0,0 +1,304 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 Edouard Griffiths, F4EXB // +// Copyright (C) 2020 Jon Beniston, M7RCE // +// // +// 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 "ui_audioinputgui.h" +#include "gui/colormapper.h" +#include "gui/glspectrum.h" +#include "gui/crightclickenabler.h" +#include "gui/basicdevicesettingsdialog.h" +#include "dsp/dspengine.h" +#include "dsp/dspcommands.h" +#include "audioinputgui.h" + +#include "device/deviceapi.h" +#include "device/deviceuiset.h" + +AudioInputGui::AudioInputGui(DeviceUISet *deviceUISet, QWidget* parent) : + DeviceGUI(parent), + ui(new Ui::AudioInputGui), + m_deviceUISet(deviceUISet), + m_forceSettings(true), + m_settings(), + m_sampleSource(NULL) +{ + m_sampleSource = (AudioInputSource::AudioInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); + + ui->setupUi(this); + + connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware())); + + CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); + connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + + displaySettings(); + + connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); + m_sampleSource->setMessageQueueToGUI(&m_inputMessageQueue); +} + +AudioInputGui::~AudioInputGui() +{ + delete ui; +} + +void AudioInputGui::destroy() +{ + delete this; +} + +void AudioInputGui::resetToDefaults() +{ + m_settings.resetToDefaults(); + displaySettings(); + sendSettings(); +} + +QByteArray AudioInputGui::serialize() const +{ + return m_settings.serialize(); +} + +bool AudioInputGui::deserialize(const QByteArray& data) +{ + if(m_settings.deserialize(data)) + { + displaySettings(); + m_forceSettings = true; + sendSettings(); + return true; + } + else + { + resetToDefaults(); + return false; + } +} + +bool AudioInputGui::handleMessage(const Message& message) +{ + if (AudioInputSource::AudioInput::MsgConfigureAudioInput::match(message)) + { + const AudioInputSource::AudioInput::MsgConfigureAudioInput& cfg = (AudioInputSource::AudioInput::MsgConfigureAudioInput&) message; + m_settings = cfg.getSettings(); + blockApplySettings(true); + displaySettings(); + blockApplySettings(false); + return true; + } + else if (AudioInputSource::AudioInput::MsgStartStop::match(message)) + { + AudioInputSource::AudioInput::MsgStartStop& notif = (AudioInputSource::AudioInput::MsgStartStop&) message; + blockApplySettings(true); + ui->startStop->setChecked(notif.getStartStop()); + blockApplySettings(false); + + return true; + } + else + { + return false; + } +} + +void AudioInputGui::handleInputMessages() +{ + Message* message; + + while ((message = m_inputMessageQueue.pop()) != 0) + { + qDebug("AudioInputGui::handleInputMessages: message: %s", message->getIdentifier()); + + if (DSPSignalNotification::match(*message)) + { + DSPSignalNotification* notif = (DSPSignalNotification*) message; + m_sampleRate = notif->getSampleRate(); + qDebug("AudioInputGui::handleInputMessages: DSPSignalNotification: SampleRate: %d", notif->getSampleRate()); + updateSampleRateAndFrequency(); + + delete message; + } + else + { + if (handleMessage(*message)) + { + delete message; + } + } + } +} + +void AudioInputGui::updateSampleRateAndFrequency() +{ +/* + // Can't seem to get main spectrum to only display real part for mono/I-channel only + if (m_settings.m_iqMapping <= AudioInputSettings::IQMapping::R) + { + m_deviceUISet->getSpectrum()->setSampleRate(m_sampleRate / 2); + m_deviceUISet->getSpectrum()->setCenterFrequency(m_sampleRate / 4); + m_deviceUISet->getSpectrum()->setSsbSpectrum(true); + m_deviceUISet->getSpectrum()->setLsbDisplay(false); + } + else +*/ + { + m_deviceUISet->getSpectrum()->setSampleRate(m_sampleRate); + m_deviceUISet->getSpectrum()->setCenterFrequency(0); + m_deviceUISet->getSpectrum()->setSsbSpectrum(false); + m_deviceUISet->getSpectrum()->setLsbDisplay(false); + } + ui->deviceRateText->setText(tr("%1k").arg((float)m_sampleRate / 1000)); +} + +void AudioInputGui::refreshDeviceList() +{ + ui->device->blockSignals(true); + AudioDeviceManager *audioDeviceManager = DSPEngine::instance()->getAudioDeviceManager(); + const QList& audioList = audioDeviceManager->getInputDevices(); + + ui->device->clear(); + for (const auto &itAudio : audioList) + { + ui->device->addItem(AudioInputSettings::getFullDeviceName(itAudio)); + } + ui->device->blockSignals(false); +} + +void AudioInputGui::refreshSampleRates(QString deviceName) +{ + ui->sampleRate->blockSignals(true); + ui->sampleRate->clear(); + const auto deviceInfos = QAudioDeviceInfo::availableDevices(QAudio::AudioInput); + for (const QAudioDeviceInfo &deviceInfo : deviceInfos) + { + if (deviceName == AudioInputSettings::getFullDeviceName(deviceInfo)) + { + QList sampleRates = deviceInfo.supportedSampleRates(); + for(int i = 0; i < sampleRates.size(); ++i) + { + ui->sampleRate->addItem(QString("%1").arg(sampleRates[i])); + } + } + } + ui->sampleRate->blockSignals(false); + + int index = ui->sampleRate->findText(QString("%1").arg(m_settings.m_sampleRate)); + if (index >= 0) + ui->sampleRate->setCurrentIndex(index); + else + ui->sampleRate->setCurrentIndex(0); +} + +void AudioInputGui::displaySettings() +{ + refreshDeviceList(); + int index = ui->device->findText(m_settings.m_deviceName); + if (index >= 0) + ui->device->setCurrentIndex(index); + else + ui->device->setCurrentIndex(0); + ui->decim->setCurrentIndex(m_settings.m_log2Decim); + ui->volume->setValue((int)(m_settings.m_volume*10.0f)); + ui->volumeText->setText(QString("%1").arg(m_settings.m_volume, 3, 'f', 1)); + ui->channels->setCurrentIndex((int)m_settings.m_iqMapping); + refreshSampleRates(ui->device->currentText()); +} + +void AudioInputGui::on_device_currentIndexChanged(int index) +{ + m_settings.m_deviceName = ui->device->currentText(); + refreshSampleRates(m_settings.m_deviceName); + sendSettings(); +} + +void AudioInputGui::on_sampleRate_currentIndexChanged(int index) +{ + m_settings.m_sampleRate = ui->sampleRate->currentText().toInt(); + sendSettings(); +} + +void AudioInputGui::on_decim_currentIndexChanged(int index) +{ + if ((index < 0) || (index > 6)) { + return; + } + + m_settings.m_log2Decim = index; + sendSettings(); +} + +void AudioInputGui::on_volume_valueChanged(int value) +{ + m_settings.m_volume = value/10.0f; + ui->volumeText->setText(QString("%1").arg(m_settings.m_volume, 3, 'f', 1)); + sendSettings(); +} + +void AudioInputGui::on_channels_currentIndexChanged(int index) +{ + m_settings.m_iqMapping = (AudioInputSettings::IQMapping)index; + updateSampleRateAndFrequency(); + sendSettings(); +} + +void AudioInputGui::on_startStop_toggled(bool checked) +{ + if (m_doApplySettings) + { + AudioInputSource::AudioInput::MsgStartStop *message = AudioInputSource::AudioInput::MsgStartStop::create(checked); + m_sampleSource->getInputMessageQueue()->push(message); + } +} + +void AudioInputGui::sendSettings() +{ + if(!m_updateTimer.isActive()) + m_updateTimer.start(100); +} + +void AudioInputGui::updateHardware() +{ + if (m_doApplySettings) + { + AudioInputSource::AudioInput::MsgConfigureAudioInput* message = AudioInputSource::AudioInput::MsgConfigureAudioInput::create(m_settings, m_forceSettings); + m_sampleSource->getInputMessageQueue()->push(message); + m_forceSettings = false; + m_updateTimer.stop(); + } +} + +void AudioInputGui::openDeviceSettingsDialog(const QPoint& p) +{ + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + + dialog.move(p); + dialog.exec(); + + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + + sendSettings(); +} diff --git a/plugins/samplesource/audioinput/audioinputgui.h b/plugins/samplesource/audioinput/audioinputgui.h new file mode 100644 index 000000000..8c8507f77 --- /dev/null +++ b/plugins/samplesource/audioinput/audioinputgui.h @@ -0,0 +1,83 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 Edouard Griffiths, F4EXB // +// Copyright (C) 2020 Jon Beniston, M7RCE // +// // +// 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_AUDIOINPUTGUI_H +#define INCLUDE_AUDIOINPUTGUI_H + +#include +#include +#include + +#include "util/messagequeue.h" + +#include "audioinput.h" + +class QWidget; +class DeviceUISet; + +namespace Ui { + class AudioInputGui; +} + +class AudioInputGui : public DeviceGUI { + Q_OBJECT + +public: + explicit AudioInputGui(DeviceUISet *deviceUISet, QWidget* parent = 0); + virtual ~AudioInputGui(); + virtual void destroy(); + + void resetToDefaults(); + QByteArray serialize() const; + bool deserialize(const QByteArray& data); + virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + +private: + Ui::AudioInputGui* ui; + + DeviceUISet* m_deviceUISet; + bool m_doApplySettings; + bool m_forceSettings; + AudioInputSettings m_settings; + QTimer m_updateTimer; + DeviceSampleSource* m_sampleSource; + int m_sampleRate; + + MessageQueue m_inputMessageQueue; + + void blockApplySettings(bool block) { m_doApplySettings = !block; } + void refreshDeviceList(); + void refreshSampleRates(QString deviceName); + void displaySettings(); + void sendSettings(); + void updateSampleRateAndFrequency(); + bool handleMessage(const Message& message); + +private slots: + void handleInputMessages(); + void on_device_currentIndexChanged(int index); + void on_sampleRate_currentIndexChanged(int index); + void on_decim_currentIndexChanged(int index); + void on_volume_valueChanged(int value); + void on_channels_currentIndexChanged(int index); + void on_startStop_toggled(bool checked); + void updateHardware(); + void openDeviceSettingsDialog(const QPoint& p); +}; + +#endif // INCLUDE_AUDIOINPUTGUI_H diff --git a/plugins/samplesource/audioinput/audioinputgui.ui b/plugins/samplesource/audioinput/audioinputgui.ui new file mode 100644 index 000000000..5c3511e66 --- /dev/null +++ b/plugins/samplesource/audioinput/audioinputgui.ui @@ -0,0 +1,391 @@ + + + AudioInputGui + + + + 0 + 0 + 320 + 350 + + + + + 0 + 0 + + + + + 320 + 350 + + + + + Liberation Sans + 9 + + + + FunCubeDongle + + + + 3 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + 4 + + + + + + + + + start/stop acquisition + + + + + + + :/play.png + :/stop.png:/play.png + + + + + + + + + + + Baseband I/Q sample rate kS/s + + + 00000k + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + + Qt::Horizontal + + + + + + + + + + 0 + 0 + + + + Device + + + + + + + + 0 + 0 + + + + + 220 + 0 + + + + + + + + Refresh list of audio devices + + + ... + + + + :/recycle.png:/recycle.png + + + + + + + + + Qt::Horizontal + + + + + + + + + SR + + + + + + + + 100 + 0 + + + + Audio sample rate in Hz + + + + + + + Hz + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 50 + 16777215 + + + + Decimation factor + + + + 1 + + + + + 2 + + + + + 4 + + + + + 8 + + + + + + + + Dec + + + + + + + + + Qt::Horizontal + + + + + + + + + Volume + + + + + + + + 24 + 0 + + + + + 24 + 24 + + + + Audio volume. Not supported by all devices + + + 10 + + + 10 + + + + + + + 1.0 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Channel Map + + + + + + + + 80 + 0 + + + + How audio channels map to IQ data + + + 0 + + + + I=L, Q=0 + + + + + I=R, Q=0 + + + + + I=L, Q=R + + + + + I=R, Q=L + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
+
+ + + + +
diff --git a/plugins/samplesource/audioinput/audioinputplugin.cpp b/plugins/samplesource/audioinput/audioinputplugin.cpp new file mode 100644 index 000000000..d55faa3cb --- /dev/null +++ b/plugins/samplesource/audioinput/audioinputplugin.cpp @@ -0,0 +1,151 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 Edouard Griffiths, F4EXB // +// Copyright (C) 2020 Jon Beniston, M7RCE // +// // +// 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" +#include "util/simpleserializer.h" +#include "audioinputplugin.h" +#include "audioinputwebapiadapter.h" + +#ifdef SERVER_MODE +#include "audioinput.h" +#else +#include "audioinputgui.h" +#endif + +const PluginDescriptor AudioInputPlugin::m_pluginDescriptor = { + QString("AudioInput"), + QString("Audio Input"), + QString("4.22.0"), + QString("(c) Jon Beniston, M7RCE and Edouard Griffiths, F4EXB"), + QString("https://github.com/f4exb/sdrangel"), + true, + QString("https://github.com/f4exb/sdrangel") +}; + +const QString AudioInputPlugin::m_hardwareID = "AudioInput"; +const QString AudioInputPlugin::m_deviceTypeID = AUDIOINPUT_DEVICE_TYPE_ID; + +AudioInputPlugin::AudioInputPlugin(QObject* parent) : + QObject(parent) +{ +} + +const PluginDescriptor& AudioInputPlugin::getPluginDescriptor() const +{ + return m_pluginDescriptor; +} + +void AudioInputPlugin::initPlugin(PluginAPI* pluginAPI) +{ + pluginAPI->registerSampleSource(m_deviceTypeID, this); +} + +void AudioInputPlugin::enumOriginDevices(QStringList& listedHwIds, OriginDevices& originDevices) +{ + if (listedHwIds.contains(m_hardwareID)) { // check if it was done + return; + } + + // We could list all input audio devices here separately + // but I thought it makes it simpler to switch between inputs + // if they are in the AudioInput GUI + originDevices.append(OriginDevice( + "Audio", + m_hardwareID, + "0", + 0, + 1, // nb Rx + 0 // nb Tx + )); +} + +PluginInterface::SamplingDevices AudioInputPlugin::enumSampleSources(const OriginDevices& originDevices) +{ + SamplingDevices result; + + for (OriginDevices::const_iterator it = originDevices.begin(); it != originDevices.end(); ++it) + { + if (it->hardwareId == m_hardwareID) + { + for (unsigned int j = 0; j < it->nbRxStreams; j++) + { + result.append(SamplingDevice( + it->displayableName, + it->hardwareId, + m_deviceTypeID, + it->serial, + it->sequence, + PluginInterface::SamplingDevice::PhysicalDevice, + PluginInterface::SamplingDevice::StreamSingleRx, + it->nbRxStreams, + j)); + } + } + } + + return result; +} + +#ifdef SERVER_MODE +DeviceGUI* AudioInputPlugin::createSampleSourcePluginInstanceGUI( + const QString& sourceId, + QWidget **widget, + DeviceUISet *deviceUISet) +{ + (void) sourceId; + (void) widget; + (void) deviceUISet; + return 0; +} +#else +DeviceGUI* AudioInputPlugin::createSampleSourcePluginInstanceGUI( + const QString& sourceId, + QWidget **widget, + DeviceUISet *deviceUISet) +{ + if(sourceId == m_deviceTypeID) + { + AudioInputGui* gui = new AudioInputGui(deviceUISet); + *widget = gui; + return gui; + } + else + { + return 0; + } +} +#endif + +DeviceSampleSource *AudioInputPlugin::createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI) +{ + if (sourceId == m_deviceTypeID) + { + AudioInputSource::AudioInput* input = new AudioInputSource::AudioInput(deviceAPI); + return input; + } + else + { + return 0; + } +} + +DeviceWebAPIAdapter *AudioInputPlugin::createDeviceWebAPIAdapter() const +{ + return new AudioInputWebAPIAdapter(); +} diff --git a/plugins/samplesource/audioinput/audioinputplugin.h b/plugins/samplesource/audioinput/audioinputplugin.h new file mode 100644 index 000000000..23976ca78 --- /dev/null +++ b/plugins/samplesource/audioinput/audioinputplugin.h @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 Edouard Griffiths, F4EXB // +// Copyright (C) 2020 Jon Beniston, M7RCE // +// // +// 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_AUDIOINPUTPLUGIN_H +#define INCLUDE_AUDIOINPUTPLUGIN_H + +#include +#include "plugin/plugininterface.h" + +#define AUDIOINPUT_DEVICE_TYPE_ID "sdrangel.samplesource.audioinput" + +class PluginAPI; + +class AudioInputPlugin : public QObject, public PluginInterface { + Q_OBJECT + Q_INTERFACES(PluginInterface) + Q_PLUGIN_METADATA(IID AUDIOINPUT_DEVICE_TYPE_ID) + +public: + explicit AudioInputPlugin(QObject* parent = NULL); + + const PluginDescriptor& getPluginDescriptor() const; + void initPlugin(PluginAPI* pluginAPI); + + virtual void enumOriginDevices(QStringList& listedHwIds, OriginDevices& originDevices); + virtual SamplingDevices enumSampleSources(const OriginDevices& originDevices); + virtual DeviceGUI* createSampleSourcePluginInstanceGUI( + const QString& sourceId, + QWidget **widget, + DeviceUISet *deviceUISet); + virtual DeviceSampleSource* createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI); + virtual DeviceWebAPIAdapter* createDeviceWebAPIAdapter() const; + + static const QString m_hardwareID; + static const QString m_deviceTypeID; + +private: + static const PluginDescriptor m_pluginDescriptor; +}; + +#endif // INCLUDE_AUDIOINPUTPLUGIN_H diff --git a/plugins/samplesource/audioinput/audioinputsettings.cpp b/plugins/samplesource/audioinput/audioinputsettings.cpp new file mode 100644 index 000000000..84fb1ae07 --- /dev/null +++ b/plugins/samplesource/audioinput/audioinputsettings.cpp @@ -0,0 +1,99 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 Edouard Griffiths, F4EXB // +// Copyright (C) 2020 Jon Beniston, M7RCE // +// // +// 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 "util/simpleserializer.h" +#include "audioinputsettings.h" + +AudioInputSettings::AudioInputSettings() +{ + resetToDefaults(); +} + +void AudioInputSettings::resetToDefaults() +{ + m_deviceName = ""; + m_sampleRate = 48000; + m_volume = 1.0f; + m_log2Decim = 0; + m_iqMapping = L; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; +} + +QByteArray AudioInputSettings::serialize() const +{ + SimpleSerializer s(1); + + s.writeString(1, m_deviceName); + s.writeS32(2, m_sampleRate); + s.writeFloat(3, m_volume); + s.writeU32(4, m_log2Decim); + s.writeS32(5, (int)m_iqMapping); + + s.writeBool(24, m_useReverseAPI); + s.writeString(25, m_reverseAPIAddress); + s.writeU32(26, m_reverseAPIPort); + s.writeU32(27, m_reverseAPIDeviceIndex); + + return s.final(); +} + +bool AudioInputSettings::deserialize(const QByteArray& data) +{ + SimpleDeserializer d(data); + + if (!d.isValid()) + { + resetToDefaults(); + return false; + } + + if (d.getVersion() == 1) + { + uint32_t uintval; + + d.readString(1, &m_deviceName, ""); + d.readS32(2, &m_sampleRate, 48000); + d.readFloat(3, &m_volume, 1.0f); + d.readU32(4, &m_log2Decim, 0); + d.readS32(5, (int *)&m_iqMapping, IQMapping::L); + + d.readBool(24, &m_useReverseAPI, false); + d.readString(25, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(26, &uintval, 0); + + if ((uintval > 1023) && (uintval < 65535)) { + m_reverseAPIPort = uintval; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(27, &uintval, 0); + m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; + + return true; + } + else + { + resetToDefaults(); + return false; + } +} diff --git a/plugins/samplesource/audioinput/audioinputsettings.h b/plugins/samplesource/audioinput/audioinputsettings.h new file mode 100644 index 000000000..108d965d9 --- /dev/null +++ b/plugins/samplesource/audioinput/audioinputsettings.h @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 Edouard Griffiths, F4EXB // +// Copyright (C) 2020 Jon Beniston, M7RCE // +// // +// 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 _AUDIOINPUT_AUDIOINPUTSETTINGS_H_ +#define _AUDIOINPUT_AUDIOINPUTSETTINGS_H_ + +#include +#include + +struct AudioInputSettings { + + QString m_deviceName; // Including realm, as from getFullDeviceName below + int m_sampleRate; + float m_volume; + quint32 m_log2Decim; + enum IQMapping { + L, + R, + LR, + RL + } m_iqMapping; + + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; + + AudioInputSettings(); + void resetToDefaults(); + QByteArray serialize() const; + bool deserialize(const QByteArray& data); + + // Append realm to device names, because there may be multiple devices with the same name on Windows + static QString getFullDeviceName(const QAudioDeviceInfo &deviceInfo) + { +#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) + return deviceInfo.deviceName(); +#else + QString realm = deviceInfo.realm(); + if (realm != "" && realm != "default") + return deviceInfo.deviceName() + " " + realm; + else + return deviceInfo.deviceName(); +#endif + } + +}; + +#endif /* _AUDIOINPUT_AUDIOINPUTSETTINGS_H_ */ diff --git a/plugins/samplesource/audioinput/audioinputthread.cpp b/plugins/samplesource/audioinput/audioinputthread.cpp new file mode 100644 index 000000000..9cb3affbd --- /dev/null +++ b/plugins/samplesource/audioinput/audioinputthread.cpp @@ -0,0 +1,141 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany // +// written by Christian Daniel // +// Copyright (C) 2020 Jon Beniston, M7RCE // +// // +// 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 + +#include "dsp/samplesinkfifo.h" +#include "audio/audiofifo.h" + +#include "audioinputthread.h" + +AudioInputThread::AudioInputThread(SampleSinkFifo* sampleFifo, AudioFifo *fifo, QObject* parent) : + QThread(parent), + m_fifo(fifo), + m_running(false), + m_log2Decim(0), + m_iqMapping(AudioInputSettings::IQMapping::L), + m_convertBuffer(m_convBufSamples), + m_sampleFifo(sampleFifo) +{ + start(); +} + +AudioInputThread::~AudioInputThread() +{ +} + +void AudioInputThread::startWork() +{ + m_startWaitMutex.lock(); + + start(); + + while(!m_running) + { + m_startWaiter.wait(&m_startWaitMutex, 100); + } + + m_startWaitMutex.unlock(); +} + +void AudioInputThread::stopWork() +{ + m_running = false; + wait(); +} + +void AudioInputThread::run() +{ + m_running = true; + qDebug("AudioInputThread::run: start running loop"); + + while (m_running) + { + workIQ(m_convBufSamples); + } + + qDebug("AudioInputThread::run: running loop stopped"); + m_running = false; +} + +void AudioInputThread::workIQ(unsigned int samples) +{ + // Most of the time, this returns 0, because of the low sample rate. + // Could be more efficient in this case to have blocking wait? + uint32_t nbRead = m_fifo->read((unsigned char *) m_buf, samples); + + // Map between left and right audio channels and IQ channels + if (m_iqMapping == AudioInputSettings::IQMapping::L) + { + for (uint32_t i = 0; i < nbRead; i++) + m_buf[i*2+1] = 0; + } + else if (m_iqMapping == AudioInputSettings::IQMapping::R) + { + for (uint32_t i = 0; i < nbRead; i++) + { + m_buf[i*2] = m_buf[i*2+1]; + m_buf[i*2+1] = 0; + } + } + else if (m_iqMapping == AudioInputSettings::IQMapping::LR) + { + for (uint32_t i = 0; i < nbRead; i++) + { + qint16 t = m_buf[i*2]; + m_buf[i*2] = m_buf[i*2+1]; + m_buf[i*2+1] = t; + } + } + + SampleVector::iterator it = m_convertBuffer.begin(); + + switch (m_log2Decim) + { + case 0: + m_decimatorsIQ.decimate1(&it, m_buf, 2*nbRead); + break; + case 1: + m_decimatorsIQ.decimate2_cen(&it, m_buf, 2*nbRead); + break; + case 2: + m_decimatorsIQ.decimate4_cen(&it, m_buf, 2*nbRead); + break; + case 3: + m_decimatorsIQ.decimate8_cen(&it, m_buf, 2*nbRead); + break; + case 4: + m_decimatorsIQ.decimate16_cen(&it, m_buf, 2*nbRead); + break; + case 5: + m_decimatorsIQ.decimate32_cen(&it, m_buf, 2*nbRead); + break; + case 6: + m_decimatorsIQ.decimate64_cen(&it, m_buf, 2*nbRead); + break; + default: + break; + } + + m_sampleFifo->write(m_convertBuffer.begin(), it); +} diff --git a/plugins/samplesource/audioinput/audioinputthread.h b/plugins/samplesource/audioinput/audioinputthread.h new file mode 100644 index 000000000..3d6a5b3f2 --- /dev/null +++ b/plugins/samplesource/audioinput/audioinputthread.h @@ -0,0 +1,63 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015-2018 Edouard Griffiths, F4EXB // +// Copyright (C) 2020 Jon Beniston, M7RCE // +// // +// 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_AUDIOINPUTTHREAD_H +#define INCLUDE_AUDIOINPUTTHREAD_H + +#include +#include +#include + +#include "dsp/samplesinkfifo.h" +#include "dsp/decimators.h" +#include "audioinputsettings.h" + +class AudioFifo; + +class AudioInputThread : public QThread { + Q_OBJECT + +public: + AudioInputThread(SampleSinkFifo* sampleFifo, AudioFifo *fifo, QObject* parent = nullptr); + ~AudioInputThread(); + + void startWork(); + void stopWork(); + void setLog2Decimation(unsigned int log2_decim) {m_log2Decim = log2_decim;} + void setIQMapping(AudioInputSettings::IQMapping iqMapping) {m_iqMapping = iqMapping;} + + static const int m_convBufSamples = 4096; +private: + AudioFifo* m_fifo; + + QMutex m_startWaitMutex; + QWaitCondition m_startWaiter; + bool m_running; + unsigned int m_log2Decim; + AudioInputSettings::IQMapping m_iqMapping; + + qint16 m_buf[m_convBufSamples*2]; // stereo (I, Q) + SampleVector m_convertBuffer; + SampleSinkFifo* m_sampleFifo; + Decimators m_decimatorsIQ; + + void run(); + void workIQ(unsigned int samples); +}; + +#endif // INCLUDE_AUDIOINPUTTHREAD_H diff --git a/plugins/samplesource/audioinput/audioinputwebapiadapter.cpp b/plugins/samplesource/audioinput/audioinputwebapiadapter.cpp new file mode 100644 index 000000000..ae87bbbfc --- /dev/null +++ b/plugins/samplesource/audioinput/audioinputwebapiadapter.cpp @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB // +// Copyright (C) 2020 Jon Beniston, M7RCE // +// // +// 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 "SWGDeviceSettings.h" +#include "audioinput.h" +#include "audioinputwebapiadapter.h" + +AudioInputWebAPIAdapter::AudioInputWebAPIAdapter() +{} + +AudioInputWebAPIAdapter::~AudioInputWebAPIAdapter() +{} + +int AudioInputWebAPIAdapter::webapiSettingsGet( + SWGSDRangel::SWGDeviceSettings& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setAirspyHfSettings(new SWGSDRangel::SWGAirspyHFSettings()); + response.getAirspyHfSettings()->init(); + AudioInputSource::AudioInput::webapiFormatDeviceSettings(response, m_settings); + return 200; +} + +int AudioInputWebAPIAdapter::webapiSettingsPutPatch( + bool force, + const QStringList& deviceSettingsKeys, + SWGSDRangel::SWGDeviceSettings& response, // query + response + QString& errorMessage) +{ + (void) errorMessage; + AudioInputSource::AudioInput::webapiUpdateDeviceSettings(m_settings, deviceSettingsKeys, response); + return 200; +} diff --git a/plugins/samplesource/audioinput/audioinputwebapiadapter.h b/plugins/samplesource/audioinput/audioinputwebapiadapter.h new file mode 100644 index 000000000..fc32f9e8a --- /dev/null +++ b/plugins/samplesource/audioinput/audioinputwebapiadapter.h @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB // +// Copyright (C) 2020 Jon Beniston, M7RCE // +// // +// 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 "device/devicewebapiadapter.h" +#include "audioinputsettings.h" + +class AudioInputWebAPIAdapter : public DeviceWebAPIAdapter +{ +public: + AudioInputWebAPIAdapter(); + virtual ~AudioInputWebAPIAdapter(); + virtual QByteArray serialize() { return m_settings.serialize(); } + virtual bool deserialize(const QByteArray& data) { return m_settings.deserialize(data); } + + virtual int webapiSettingsGet( + SWGSDRangel::SWGDeviceSettings& response, + QString& errorMessage); + + virtual int webapiSettingsPutPatch( + bool force, + const QStringList& deviceSettingsKeys, + SWGSDRangel::SWGDeviceSettings& response, // query + response + QString& errorMessage); + +private: + AudioInputSettings m_settings; +}; diff --git a/plugins/samplesource/audioinput/readme.md b/plugins/samplesource/audioinput/readme.md new file mode 100644 index 000000000..aed7270bb --- /dev/null +++ b/plugins/samplesource/audioinput/readme.md @@ -0,0 +1,46 @@ +

Audio input plugin

+ +

Introduction

+ +This input sample source plugin gets its samples from an audio device. + +

Interface

+ +![Audio input plugin GUI](../../../doc/img/AudioInput_plugin.png) + +

1: Start/Stop

+ +Device start / stop button. + + - Blue triangle icon: device is ready and can be started + - Green square icon: device is running and can be stopped + - Magenta (or pink) square icon: an error occurred. In the case the device was accidentally disconnected you may click on the icon, plug back in and start again. + +

2: Device

+ +The audio device to use. + +

3: Refresh devices

+ +Refresh the list of audio devices. + +

4: Audio sample rate

+ +Audio sample rate in Hz (Sa/s). + +

5: Decimation

+ +A decimation factor to apply to the audio data. The baseband sample rate will be the audio sample, divided by this decimation factor. + +

6: Volume

+ +A control to set the input volume. This is not supported by all input audio devices. + +

7: Channel Map

+ +This controls how the left and right stereo audio channels map on to the IQ channels. + +* I=L, Q=0 - The left audio channel is driven to the I channel. The Q channel is set to 0. +* I=R, Q=0 - The right audio channel is driven to the I channel. The Q channel is set to 0. +* I=L, Q=R - The left audio channel is driven to the I channel. The right audio channel is driven to the Q channel. +* I=R, Q=L - The right audio channel is driven to the I channel. The left audio channel is driven to the Q channel. diff --git a/sdrbase/resources/webapi/doc/html2/index.html b/sdrbase/resources/webapi/doc/html2/index.html index 59aea4610..d94da3fa7 100644 --- a/sdrbase/resources/webapi/doc/html2/index.html +++ b/sdrbase/resources/webapi/doc/html2/index.html @@ -1574,6 +1574,44 @@ margin-bottom: 20px; } }, "description" : "Audio input device" +}; + defs.AudioInputSettings = { + "properties" : { + "device" : { + "type" : "string", + "description" : "The name of the audio device" + }, + "devSampleRate" : { + "type" : "integer", + "description" : "Audio sample rate" + }, + "volume" : { + "type" : "number", + "format" : "float" + }, + "log2Decim" : { + "type" : "integer", + "description" : "Decimation factor" + }, + "iqMapping" : { + "type" : "integer", + "description" : "Audio channel to IQ mapping\n * 0 - I=L, Q=0\n * 1 - I=R, Q=0\n * 2 - I=L, Q=R\n * 3 - I=R, Q=L\n" + }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API (1 for yes, 0 for no)" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIDeviceIndex" : { + "type" : "integer" + } + }, + "description" : "AudioInput" }; defs.AudioOutputDevice = { "properties" : { @@ -3428,6 +3466,9 @@ margin-bottom: 20px; "airspyHFSettings" : { "$ref" : "#/definitions/AirspyHFSettings" }, + "audioInputSettings" : { + "$ref" : "#/definitions/AudioInputSettings" + }, "bladeRF1InputSettings" : { "$ref" : "#/definitions/BladeRF1InputSettings" }, @@ -40584,7 +40625,7 @@ except ApiException as e:
- Generated 2020-11-09T17:45:17.371+01:00 + Generated 2020-11-09T22:06:31.158+01:00
diff --git a/sdrbase/webapi/webapirequestmapper.cpp b/sdrbase/webapi/webapirequestmapper.cpp index 90677da92..dcdcfa804 100644 --- a/sdrbase/webapi/webapirequestmapper.cpp +++ b/sdrbase/webapi/webapirequestmapper.cpp @@ -3800,6 +3800,11 @@ bool WebAPIRequestMapper::getDeviceSettings( deviceSettings->setAirspyHfSettings(new SWGSDRangel::SWGAirspyHFSettings()); deviceSettings->getAirspyHfSettings()->fromJsonObject(settingsJsonObject); } + else if (deviceSettingsKey == "audioInputSettings") + { + deviceSettings->setAudioInputSettings(new SWGSDRangel::SWGAudioInputSettings()); + deviceSettings->getAudioInputSettings()->fromJsonObject(settingsJsonObject); + } else if (deviceSettingsKey == "bladeRF1InputSettings") { deviceSettings->setBladeRf1InputSettings(new SWGSDRangel::SWGBladeRF1InputSettings()); @@ -4109,6 +4114,7 @@ void WebAPIRequestMapper::resetDeviceSettings(SWGSDRangel::SWGDeviceSettings& de deviceSettings.setDeviceHwType(nullptr); deviceSettings.setAirspySettings(nullptr); deviceSettings.setAirspyHfSettings(nullptr); + deviceSettings.setAudioInputSettings(nullptr); deviceSettings.setBladeRf1InputSettings(nullptr); deviceSettings.setBladeRf1OutputSettings(nullptr); deviceSettings.setFcdProPlusSettings(nullptr); diff --git a/sdrbase/webapi/webapiutils.cpp b/sdrbase/webapi/webapiutils.cpp index f392c639f..716555541 100644 --- a/sdrbase/webapi/webapiutils.cpp +++ b/sdrbase/webapi/webapiutils.cpp @@ -68,6 +68,7 @@ const QMap WebAPIUtils::m_channelURIToSettingsKey = { const QMap WebAPIUtils::m_deviceIdToSettingsKey = { {"sdrangel.samplesource.airspy", "airspySettings"}, {"sdrangel.samplesource.airspyhf", "airspyHFSettings"}, + {"sdrangel.samplesource.audio", "audioInputSettings"}, {"sdrangel.samplesource.bladerf1input", "bladeRF1InputSettings"}, {"sdrangel.samplesource.bladerf", "bladeRF1InputSettings"}, // remap {"sdrangel.samplesink.bladerf1output", "bladeRF1OutputSettings"}, @@ -156,6 +157,7 @@ const QMap WebAPIUtils::m_channelTypeToActionsKey = { const QMap WebAPIUtils::m_sourceDeviceHwIdToSettingsKey = { {"Airspy", "airspySettings"}, {"AirspyHF", "airspyHFSettings"}, + {"AudioInput", "audioInputSettings"}, {"BladeRF1", "bladeRF1InputSettings"}, {"BladeRF2", "bladeRF2InputSettings"}, {"FCDPro", "fcdProSettings"}, @@ -180,6 +182,7 @@ const QMap WebAPIUtils::m_sourceDeviceHwIdToSettingsKey = { const QMap WebAPIUtils::m_sourceDeviceHwIdToActionsKey = { {"Airspy", "airspyActions"}, {"AirspyHF", "airspyHFActions"}, + {"AudioInput", "audioInputActions"}, {"BladeRF1", "bladeRF1InputActions"}, {"FCDPro", "fcdProActions"}, {"FCDPro+", "fcdProPlusActions"}, diff --git a/swagger/sdrangel/api/swagger/include/AudioInput.yaml b/swagger/sdrangel/api/swagger/include/AudioInput.yaml new file mode 100644 index 000000000..540442dd8 --- /dev/null +++ b/swagger/sdrangel/api/swagger/include/AudioInput.yaml @@ -0,0 +1,32 @@ +AudioInputSettings: + description: AudioInput + properties: + device: + description: The name of the audio device + type: string + devSampleRate: + description: Audio sample rate + type: integer + volume: + type: number + format: float + log2Decim: + description: Decimation factor + type: integer + iqMapping: + type: integer + description: > + Audio channel to IQ mapping + * 0 - I=L, Q=0 + * 1 - I=R, Q=0 + * 2 - I=L, Q=R + * 3 - I=R, Q=L + useReverseAPI: + description: Synchronize with reverse API (1 for yes, 0 for no) + type: integer + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIDeviceIndex: + type: integer diff --git a/swagger/sdrangel/api/swagger/include/DeviceSettings.yaml b/swagger/sdrangel/api/swagger/include/DeviceSettings.yaml index dffaedf40..1264e24bb 100644 --- a/swagger/sdrangel/api/swagger/include/DeviceSettings.yaml +++ b/swagger/sdrangel/api/swagger/include/DeviceSettings.yaml @@ -18,6 +18,8 @@ DeviceSettings: $ref: "http://swgserver:8081/api/swagger/include/Airspy.yaml#/AirspySettings" airspyHFSettings: $ref: "http://swgserver:8081/api/swagger/include/AirspyHF.yaml#/AirspyHFSettings" + audioInputSettings: + $ref: "http://swgserver:8081/api/swagger/include/AudioInput.yaml#/AudioInputSettings" bladeRF1InputSettings: $ref: "http://swgserver:8081/api/swagger/include/BladeRF1.yaml#/BladeRF1InputSettings" bladeRF2InputSettings: diff --git a/swagger/sdrangel/code/html2/index.html b/swagger/sdrangel/code/html2/index.html index 59aea4610..d94da3fa7 100644 --- a/swagger/sdrangel/code/html2/index.html +++ b/swagger/sdrangel/code/html2/index.html @@ -1574,6 +1574,44 @@ margin-bottom: 20px; } }, "description" : "Audio input device" +}; + defs.AudioInputSettings = { + "properties" : { + "device" : { + "type" : "string", + "description" : "The name of the audio device" + }, + "devSampleRate" : { + "type" : "integer", + "description" : "Audio sample rate" + }, + "volume" : { + "type" : "number", + "format" : "float" + }, + "log2Decim" : { + "type" : "integer", + "description" : "Decimation factor" + }, + "iqMapping" : { + "type" : "integer", + "description" : "Audio channel to IQ mapping\n * 0 - I=L, Q=0\n * 1 - I=R, Q=0\n * 2 - I=L, Q=R\n * 3 - I=R, Q=L\n" + }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API (1 for yes, 0 for no)" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIDeviceIndex" : { + "type" : "integer" + } + }, + "description" : "AudioInput" }; defs.AudioOutputDevice = { "properties" : { @@ -3428,6 +3466,9 @@ margin-bottom: 20px; "airspyHFSettings" : { "$ref" : "#/definitions/AirspyHFSettings" }, + "audioInputSettings" : { + "$ref" : "#/definitions/AudioInputSettings" + }, "bladeRF1InputSettings" : { "$ref" : "#/definitions/BladeRF1InputSettings" }, @@ -40584,7 +40625,7 @@ except ApiException as e:
- Generated 2020-11-09T17:45:17.371+01:00 + Generated 2020-11-09T22:06:31.158+01:00
diff --git a/swagger/sdrangel/code/qt5/client/SWGAudioInputSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGAudioInputSettings.cpp new file mode 100644 index 000000000..ea3421e5d --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGAudioInputSettings.cpp @@ -0,0 +1,296 @@ +/** + * 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, USRP 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: 4.15.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 "SWGAudioInputSettings.h" + +#include "SWGHelpers.h" + +#include +#include +#include +#include + +namespace SWGSDRangel { + +SWGAudioInputSettings::SWGAudioInputSettings(QString* json) { + init(); + this->fromJson(*json); +} + +SWGAudioInputSettings::SWGAudioInputSettings() { + device = nullptr; + m_device_isSet = false; + dev_sample_rate = 0; + m_dev_sample_rate_isSet = false; + volume = 0.0f; + m_volume_isSet = false; + log2_decim = 0; + m_log2_decim_isSet = false; + iq_mapping = 0; + m_iq_mapping_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_device_index = 0; + m_reverse_api_device_index_isSet = false; +} + +SWGAudioInputSettings::~SWGAudioInputSettings() { + this->cleanup(); +} + +void +SWGAudioInputSettings::init() { + device = new QString(""); + m_device_isSet = false; + dev_sample_rate = 0; + m_dev_sample_rate_isSet = false; + volume = 0.0f; + m_volume_isSet = false; + log2_decim = 0; + m_log2_decim_isSet = false; + iq_mapping = 0; + m_iq_mapping_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_device_index = 0; + m_reverse_api_device_index_isSet = false; +} + +void +SWGAudioInputSettings::cleanup() { + if(device != nullptr) { + delete device; + } + + + + + + if(reverse_api_address != nullptr) { + delete reverse_api_address; + } + + +} + +SWGAudioInputSettings* +SWGAudioInputSettings::fromJson(QString &json) { + QByteArray array (json.toStdString().c_str()); + QJsonDocument doc = QJsonDocument::fromJson(array); + QJsonObject jsonObject = doc.object(); + this->fromJsonObject(jsonObject); + return this; +} + +void +SWGAudioInputSettings::fromJsonObject(QJsonObject &pJson) { + ::SWGSDRangel::setValue(&device, pJson["device"], "QString", "QString"); + + ::SWGSDRangel::setValue(&dev_sample_rate, pJson["devSampleRate"], "qint32", ""); + + ::SWGSDRangel::setValue(&volume, pJson["volume"], "float", ""); + + ::SWGSDRangel::setValue(&log2_decim, pJson["log2Decim"], "qint32", ""); + + ::SWGSDRangel::setValue(&iq_mapping, pJson["iqMapping"], "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_device_index, pJson["reverseAPIDeviceIndex"], "qint32", ""); + +} + +QString +SWGAudioInputSettings::asJson () +{ + QJsonObject* obj = this->asJsonObject(); + + QJsonDocument doc(*obj); + QByteArray bytes = doc.toJson(); + delete obj; + return QString(bytes); +} + +QJsonObject* +SWGAudioInputSettings::asJsonObject() { + QJsonObject* obj = new QJsonObject(); + if(device != nullptr && *device != QString("")){ + toJsonValue(QString("device"), device, obj, QString("QString")); + } + if(m_dev_sample_rate_isSet){ + obj->insert("devSampleRate", QJsonValue(dev_sample_rate)); + } + if(m_volume_isSet){ + obj->insert("volume", QJsonValue(volume)); + } + if(m_log2_decim_isSet){ + obj->insert("log2Decim", QJsonValue(log2_decim)); + } + if(m_iq_mapping_isSet){ + obj->insert("iqMapping", QJsonValue(iq_mapping)); + } + 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_device_index_isSet){ + obj->insert("reverseAPIDeviceIndex", QJsonValue(reverse_api_device_index)); + } + + return obj; +} + +QString* +SWGAudioInputSettings::getDevice() { + return device; +} +void +SWGAudioInputSettings::setDevice(QString* device) { + this->device = device; + this->m_device_isSet = true; +} + +qint32 +SWGAudioInputSettings::getDevSampleRate() { + return dev_sample_rate; +} +void +SWGAudioInputSettings::setDevSampleRate(qint32 dev_sample_rate) { + this->dev_sample_rate = dev_sample_rate; + this->m_dev_sample_rate_isSet = true; +} + +float +SWGAudioInputSettings::getVolume() { + return volume; +} +void +SWGAudioInputSettings::setVolume(float volume) { + this->volume = volume; + this->m_volume_isSet = true; +} + +qint32 +SWGAudioInputSettings::getLog2Decim() { + return log2_decim; +} +void +SWGAudioInputSettings::setLog2Decim(qint32 log2_decim) { + this->log2_decim = log2_decim; + this->m_log2_decim_isSet = true; +} + +qint32 +SWGAudioInputSettings::getIqMapping() { + return iq_mapping; +} +void +SWGAudioInputSettings::setIqMapping(qint32 iq_mapping) { + this->iq_mapping = iq_mapping; + this->m_iq_mapping_isSet = true; +} + +qint32 +SWGAudioInputSettings::getUseReverseApi() { + return use_reverse_api; +} +void +SWGAudioInputSettings::setUseReverseApi(qint32 use_reverse_api) { + this->use_reverse_api = use_reverse_api; + this->m_use_reverse_api_isSet = true; +} + +QString* +SWGAudioInputSettings::getReverseApiAddress() { + return reverse_api_address; +} +void +SWGAudioInputSettings::setReverseApiAddress(QString* reverse_api_address) { + this->reverse_api_address = reverse_api_address; + this->m_reverse_api_address_isSet = true; +} + +qint32 +SWGAudioInputSettings::getReverseApiPort() { + return reverse_api_port; +} +void +SWGAudioInputSettings::setReverseApiPort(qint32 reverse_api_port) { + this->reverse_api_port = reverse_api_port; + this->m_reverse_api_port_isSet = true; +} + +qint32 +SWGAudioInputSettings::getReverseApiDeviceIndex() { + return reverse_api_device_index; +} +void +SWGAudioInputSettings::setReverseApiDeviceIndex(qint32 reverse_api_device_index) { + this->reverse_api_device_index = reverse_api_device_index; + this->m_reverse_api_device_index_isSet = true; +} + + +bool +SWGAudioInputSettings::isSet(){ + bool isObjectUpdated = false; + do{ + if(device && *device != QString("")){ + isObjectUpdated = true; break; + } + if(m_dev_sample_rate_isSet){ + isObjectUpdated = true; break; + } + if(m_volume_isSet){ + isObjectUpdated = true; break; + } + if(m_log2_decim_isSet){ + isObjectUpdated = true; break; + } + if(m_iq_mapping_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_device_index_isSet){ + isObjectUpdated = true; break; + } + }while(false); + return isObjectUpdated; +} +} + diff --git a/swagger/sdrangel/code/qt5/client/SWGAudioInputSettings.h b/swagger/sdrangel/code/qt5/client/SWGAudioInputSettings.h new file mode 100644 index 000000000..c227d338d --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGAudioInputSettings.h @@ -0,0 +1,107 @@ +/** + * 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, USRP 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: 4.15.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. + */ + +/* + * SWGAudioInputSettings.h + * + * AudioInput + */ + +#ifndef SWGAudioInputSettings_H_ +#define SWGAudioInputSettings_H_ + +#include + + +#include + +#include "SWGObject.h" +#include "export.h" + +namespace SWGSDRangel { + +class SWG_API SWGAudioInputSettings: public SWGObject { +public: + SWGAudioInputSettings(); + SWGAudioInputSettings(QString* json); + virtual ~SWGAudioInputSettings(); + void init(); + void cleanup(); + + virtual QString asJson () override; + virtual QJsonObject* asJsonObject() override; + virtual void fromJsonObject(QJsonObject &json) override; + virtual SWGAudioInputSettings* fromJson(QString &jsonString) override; + + QString* getDevice(); + void setDevice(QString* device); + + qint32 getDevSampleRate(); + void setDevSampleRate(qint32 dev_sample_rate); + + float getVolume(); + void setVolume(float volume); + + qint32 getLog2Decim(); + void setLog2Decim(qint32 log2_decim); + + qint32 getIqMapping(); + void setIqMapping(qint32 iq_mapping); + + 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 getReverseApiDeviceIndex(); + void setReverseApiDeviceIndex(qint32 reverse_api_device_index); + + + virtual bool isSet() override; + +private: + QString* device; + bool m_device_isSet; + + qint32 dev_sample_rate; + bool m_dev_sample_rate_isSet; + + float volume; + bool m_volume_isSet; + + qint32 log2_decim; + bool m_log2_decim_isSet; + + qint32 iq_mapping; + bool m_iq_mapping_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_device_index; + bool m_reverse_api_device_index_isSet; + +}; + +} + +#endif /* SWGAudioInputSettings_H_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGDeviceSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGDeviceSettings.cpp index f7523e093..9b4d9c73a 100644 --- a/swagger/sdrangel/code/qt5/client/SWGDeviceSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGDeviceSettings.cpp @@ -38,6 +38,8 @@ SWGDeviceSettings::SWGDeviceSettings() { m_airspy_settings_isSet = false; airspy_hf_settings = nullptr; m_airspy_hf_settings_isSet = false; + audio_input_settings = nullptr; + m_audio_input_settings_isSet = false; blade_rf1_input_settings = nullptr; m_blade_rf1_input_settings_isSet = false; blade_rf2_input_settings = nullptr; @@ -114,6 +116,8 @@ SWGDeviceSettings::init() { m_airspy_settings_isSet = false; airspy_hf_settings = new SWGAirspyHFSettings(); m_airspy_hf_settings_isSet = false; + audio_input_settings = new SWGAudioInputSettings(); + m_audio_input_settings_isSet = false; blade_rf1_input_settings = new SWGBladeRF1InputSettings(); m_blade_rf1_input_settings_isSet = false; blade_rf2_input_settings = new SWGBladeRF2InputSettings(); @@ -187,6 +191,9 @@ SWGDeviceSettings::cleanup() { if(airspy_hf_settings != nullptr) { delete airspy_hf_settings; } + if(audio_input_settings != nullptr) { + delete audio_input_settings; + } if(blade_rf1_input_settings != nullptr) { delete blade_rf1_input_settings; } @@ -297,6 +304,8 @@ SWGDeviceSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&airspy_hf_settings, pJson["airspyHFSettings"], "SWGAirspyHFSettings", "SWGAirspyHFSettings"); + ::SWGSDRangel::setValue(&audio_input_settings, pJson["audioInputSettings"], "SWGAudioInputSettings", "SWGAudioInputSettings"); + ::SWGSDRangel::setValue(&blade_rf1_input_settings, pJson["bladeRF1InputSettings"], "SWGBladeRF1InputSettings", "SWGBladeRF1InputSettings"); ::SWGSDRangel::setValue(&blade_rf2_input_settings, pJson["bladeRF2InputSettings"], "SWGBladeRF2InputSettings", "SWGBladeRF2InputSettings"); @@ -386,6 +395,9 @@ SWGDeviceSettings::asJsonObject() { if((airspy_hf_settings != nullptr) && (airspy_hf_settings->isSet())){ toJsonValue(QString("airspyHFSettings"), airspy_hf_settings, obj, QString("SWGAirspyHFSettings")); } + if((audio_input_settings != nullptr) && (audio_input_settings->isSet())){ + toJsonValue(QString("audioInputSettings"), audio_input_settings, obj, QString("SWGAudioInputSettings")); + } if((blade_rf1_input_settings != nullptr) && (blade_rf1_input_settings->isSet())){ toJsonValue(QString("bladeRF1InputSettings"), blade_rf1_input_settings, obj, QString("SWGBladeRF1InputSettings")); } @@ -527,6 +539,16 @@ SWGDeviceSettings::setAirspyHfSettings(SWGAirspyHFSettings* airspy_hf_settings) this->m_airspy_hf_settings_isSet = true; } +SWGAudioInputSettings* +SWGDeviceSettings::getAudioInputSettings() { + return audio_input_settings; +} +void +SWGDeviceSettings::setAudioInputSettings(SWGAudioInputSettings* audio_input_settings) { + this->audio_input_settings = audio_input_settings; + this->m_audio_input_settings_isSet = true; +} + SWGBladeRF1InputSettings* SWGDeviceSettings::getBladeRf1InputSettings() { return blade_rf1_input_settings; @@ -837,6 +859,9 @@ SWGDeviceSettings::isSet(){ if(airspy_hf_settings && airspy_hf_settings->isSet()){ isObjectUpdated = true; break; } + if(audio_input_settings && audio_input_settings->isSet()){ + isObjectUpdated = true; break; + } if(blade_rf1_input_settings && blade_rf1_input_settings->isSet()){ isObjectUpdated = true; break; } diff --git a/swagger/sdrangel/code/qt5/client/SWGDeviceSettings.h b/swagger/sdrangel/code/qt5/client/SWGDeviceSettings.h index 29b912654..d97af0475 100644 --- a/swagger/sdrangel/code/qt5/client/SWGDeviceSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGDeviceSettings.h @@ -24,6 +24,7 @@ #include "SWGAirspyHFSettings.h" #include "SWGAirspySettings.h" +#include "SWGAudioInputSettings.h" #include "SWGBladeRF1InputSettings.h" #include "SWGBladeRF1OutputSettings.h" #include "SWGBladeRF2InputSettings.h" @@ -88,6 +89,9 @@ public: SWGAirspyHFSettings* getAirspyHfSettings(); void setAirspyHfSettings(SWGAirspyHFSettings* airspy_hf_settings); + SWGAudioInputSettings* getAudioInputSettings(); + void setAudioInputSettings(SWGAudioInputSettings* audio_input_settings); + SWGBladeRF1InputSettings* getBladeRf1InputSettings(); void setBladeRf1InputSettings(SWGBladeRF1InputSettings* blade_rf1_input_settings); @@ -194,6 +198,9 @@ private: SWGAirspyHFSettings* airspy_hf_settings; bool m_airspy_hf_settings_isSet; + SWGAudioInputSettings* audio_input_settings; + bool m_audio_input_settings_isSet; + SWGBladeRF1InputSettings* blade_rf1_input_settings; bool m_blade_rf1_input_settings_isSet; diff --git a/swagger/sdrangel/code/qt5/client/SWGModelFactory.h b/swagger/sdrangel/code/qt5/client/SWGModelFactory.h index 3d1e9ea96..977f8080f 100644 --- a/swagger/sdrangel/code/qt5/client/SWGModelFactory.h +++ b/swagger/sdrangel/code/qt5/client/SWGModelFactory.h @@ -36,6 +36,7 @@ #include "SWGArgValue.h" #include "SWGAudioDevices.h" #include "SWGAudioInputDevice.h" +#include "SWGAudioInputSettings.h" #include "SWGAudioOutputDevice.h" #include "SWGBFMDemodReport.h" #include "SWGBFMDemodSettings.h" @@ -279,6 +280,9 @@ namespace SWGSDRangel { if(QString("SWGAudioInputDevice").compare(type) == 0) { return new SWGAudioInputDevice(); } + if(QString("SWGAudioInputSettings").compare(type) == 0) { + return new SWGAudioInputSettings(); + } if(QString("SWGAudioOutputDevice").compare(type) == 0) { return new SWGAudioOutputDevice(); }