1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-26 01:39:05 -05:00

Merge pull request #699 from srcejon/audio_input

Add audio input sample source
This commit is contained in:
Edouard Griffiths 2020-11-09 23:50:00 +01:00 committed by GitHub
commit dc40755324
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 2806 additions and 3 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

View File

@ -62,4 +62,5 @@ if(ENABLE_USRP AND UHD_FOUND)
add_subdirectory(usrpinput)
endif()
add_subdirectory(audioinput)
add_subdirectory(kiwisdr)

View File

@ -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})

View File

@ -0,0 +1,525 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <string.h>
#include <errno.h>
#include <QDebug>
#include <QNetworkReply>
#include <QBuffer>
#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<QAudioDeviceInfo>& audioList = audioDeviceManager->getInputDevices();
for (const auto &itAudio : audioList)
{
if (AudioInputSettings::getFullDeviceName(itAudio) == deviceName)
{
// FIXME: getInputDeviceIndex needs a realm parameter (itAudio.realm())
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;
}
applySettings(m_settings, true, true);
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();
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<<m_settings.m_log2Decim);
}
quint64 AudioInput::getCenterFrequency() const
{
return 0;
}
void AudioInput::setCenterFrequency(qint64 centerFrequency)
{
}
bool AudioInput::handleMessage(const Message& message)
{
if(MsgConfigureAudioInput::match(message))
{
qDebug() << "AudioInput::handleMessage: MsgConfigureAudioInput";
MsgConfigureAudioInput& conf = (MsgConfigureAudioInput&) message;
applySettings(conf.getSettings(), conf.getForce());
return true;
}
else if (MsgStartStop::match(message))
{
MsgStartStop& cmd = (MsgStartStop&) message;
qDebug() << "AudioInput::handleMessage: MsgStartStop: " << (cmd.getStartStop() ? "start" : "stop");
if (cmd.getStartStop())
{
if (m_deviceAPI->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 starting)
{
bool forwardChange = false;
QList<QString> reverseAPIKeys;
if ((m_settings.m_deviceName != settings.m_deviceName)
|| (m_settings.m_sampleRate != settings.m_sampleRate) || force)
{
// Don't call openAudioDevice if called from start(), otherwise ::AudioInput
// will be created on wrong thread and we'll crash after ::AudioInput::stop calls delete
if (!starting)
{
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<<settings.m_log2Decim);
}
}
if ((m_settings.m_iqMapping != settings.m_iqMapping) || force)
{
reverseAPIKeys.append("iqMapping");
if (m_thread)
m_thread->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<<m_settings.m_log2Decim), 0);
m_deviceAPI->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<QString>& 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();
}
}

View File

@ -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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_AUDIOINPUT_H
#define INCLUDE_AUDIOINPUT_H
#include <inttypes.h>
#include <QString>
#include <QByteArray>
#include <QNetworkRequest>
#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, bool starting=false);
void webapiReverseSendSettings(QList<QString>& deviceSettingsKeys, const AudioInputSettings& settings, bool force);
void webapiReverseSendStartStop(bool start);
private slots:
void networkManagerFinished(QNetworkReply *reply);
};
}
#endif // INCLUDE_AUDIOINPUT_H

View File

@ -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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QMessageBox>
#include <QFileDialog>
#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<QAudioDeviceInfo>& 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<int> 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();
}

View File

@ -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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_AUDIOINPUTGUI_H
#define INCLUDE_AUDIOINPUTGUI_H
#include <device/devicegui.h>
#include <QTimer>
#include <QWidget>
#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

View File

@ -0,0 +1,391 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AudioInputGui</class>
<widget class="QWidget" name="AudioInputGui">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>320</width>
<height>350</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>320</width>
<height>350</height>
</size>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
<pointsize>9</pointsize>
</font>
</property>
<property name="windowTitle">
<string>FunCubeDongle</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>3</number>
</property>
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<layout class="QHBoxLayout" name="layoutFrequency">
<property name="topMargin">
<number>4</number>
</property>
<item>
<layout class="QVBoxLayout" name="deviceUILayout">
<item>
<layout class="QHBoxLayout" name="deviceButtonsLayout">
<item>
<widget class="ButtonSwitch" name="startStop">
<property name="toolTip">
<string>start/stop acquisition</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/play.png</normaloff>
<normalon>:/stop.png</normalon>:/play.png</iconset>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="deviceRateLayout">
<item>
<widget class="QLabel" name="deviceRateText">
<property name="toolTip">
<string>Baseband I/Q sample rate kS/s</string>
</property>
<property name="text">
<string>00000k</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLabel" name="deviceLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Device</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="device">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>220</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="refreshDevices">
<property name="toolTip">
<string>Refresh list of audio devices</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/recycle.png</normaloff>:/recycle.png</iconset>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="sampleRateLabel">
<property name="text">
<string>SR</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="sampleRate">
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Audio sample rate in Hz</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="sampleRateUnits">
<property name="text">
<string> Hz</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QComboBox" name="decim">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Decimation factor</string>
</property>
<item>
<property name="text">
<string>1</string>
</property>
</item>
<item>
<property name="text">
<string>2</string>
</property>
</item>
<item>
<property name="text">
<string>4</string>
</property>
</item>
<item>
<property name="text">
<string>8</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLabel" name="decimLabel">
<property name="text">
<string>Dec</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="volumeLabel">
<property name="text">
<string>Volume</string>
</property>
</widget>
</item>
<item>
<widget class="QDial" name="volume">
<property name="minimumSize">
<size>
<width>24</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="toolTip">
<string>Audio volume. Not supported by all devices</string>
</property>
<property name="maximum">
<number>10</number>
</property>
<property name="value">
<number>10</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="volumeText">
<property name="text">
<string>1.0</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Channel Map</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="channels">
<property name="minimumSize">
<size>
<width>80</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>How audio channels map to IQ data</string>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<item>
<property name="text">
<string>I=L, Q=0</string>
</property>
</item>
<item>
<property name="text">
<string>I=R, Q=0</string>
</property>
</item>
<item>
<property name="text">
<string>I=L, Q=R</string>
</property>
</item>
<item>
<property name="text">
<string>I=R, Q=L</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="padLayout">
<item>
<spacer name="verticalPadSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>ButtonSwitch</class>
<extends>QToolButton</extends>
<header>gui/buttonswitch.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="../../../sdrgui/resources/res.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QtPlugin>
#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();
}

View File

@ -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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_AUDIOINPUTPLUGIN_H
#define INCLUDE_AUDIOINPUTPLUGIN_H
#include <QObject>
#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

View File

@ -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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QtGlobal>
#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;
}
}

View File

@ -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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef _AUDIOINPUT_AUDIOINPUTSETTINGS_H_
#define _AUDIOINPUT_AUDIOINPUTSETTINGS_H_
#include <QString>
#include <QAudioDeviceInfo>
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" && realm != "alsa")
return deviceInfo.deviceName() + " " + realm;
else
return deviceInfo.deviceName();
#endif
}
};
#endif /* _AUDIOINPUT_AUDIOINPUTSETTINGS_H_ */

View File

@ -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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QDebug>
#include <stdio.h>
#include <errno.h>
#include <chrono>
#include <thread>
#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);
}

View File

@ -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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_AUDIOINPUTTHREAD_H
#define INCLUDE_AUDIOINPUTTHREAD_H
#include <QThread>
#include <QMutex>
#include <QWaitCondition>
#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<qint32, qint16, SDR_RX_SAMP_SZ, 16, true> m_decimatorsIQ;
void run();
void workIQ(unsigned int samples);
};
#endif // INCLUDE_AUDIOINPUTTHREAD_H

View File

@ -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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#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;
}

View File

@ -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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#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;
};

View File

@ -0,0 +1,46 @@
<h1>Audio input plugin</h1>
<h2>Introduction</h2>
This input sample source plugin gets its samples from an audio device.
<h2>Interface</h2>
![Audio input plugin GUI](../../../doc/img/AudioInput_plugin.png)
<h3>1: Start/Stop</h3>
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.
<h3>2: Device</h3>
The audio device to use.
<h3>3: Refresh devices</h3>
Refresh the list of audio devices.
<h3>4: Audio sample rate</h3>
Audio sample rate in Hz (Sa/s).
<h3>5: Decimation</h3>
A decimation factor to apply to the audio data. The baseband sample rate will be the audio sample, divided by this decimation factor.
<h3>6: Volume</h3>
A control to set the input volume. This is not supported by all input audio devices.
<h3>7: Channel Map</h3>
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.

View File

@ -189,3 +189,9 @@ qint64 AudioInput::writeData(const char *data, qint64 len)
return len;
}
void AudioInput::setVolume(float volume)
{
m_volume = volume;
if (m_audioInput != nullptr)
m_audioInput->setVolume(m_volume);
}

View File

@ -44,7 +44,7 @@ public:
uint getRate() const { return m_audioFormat.sampleRate(); }
void setOnExit(bool onExit) { m_onExit = onExit; }
void setVolume(float volume) { m_volume = volume; }
void setVolume(float volume);
private:
QMutex m_mutex;

View File

@ -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:
</div>
<div id="generator">
<div class="content">
Generated 2020-11-09T17:45:17.371+01:00
Generated 2020-11-09T22:06:31.158+01:00
</div>
</div>
</div>

View File

@ -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);

View File

@ -68,6 +68,7 @@ const QMap<QString, QString> WebAPIUtils::m_channelURIToSettingsKey = {
const QMap<QString, QString> 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<QString, QString> WebAPIUtils::m_channelTypeToActionsKey = {
const QMap<QString, QString> WebAPIUtils::m_sourceDeviceHwIdToSettingsKey = {
{"Airspy", "airspySettings"},
{"AirspyHF", "airspyHFSettings"},
{"AudioInput", "audioInputSettings"},
{"BladeRF1", "bladeRF1InputSettings"},
{"BladeRF2", "bladeRF2InputSettings"},
{"FCDPro", "fcdProSettings"},
@ -180,6 +182,7 @@ const QMap<QString, QString> WebAPIUtils::m_sourceDeviceHwIdToSettingsKey = {
const QMap<QString, QString> WebAPIUtils::m_sourceDeviceHwIdToActionsKey = {
{"Airspy", "airspyActions"},
{"AirspyHF", "airspyHFActions"},
{"AudioInput", "audioInputActions"},
{"BladeRF1", "bladeRF1InputActions"},
{"FCDPro", "fcdProActions"},
{"FCDPro+", "fcdProPlusActions"},

View File

@ -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

View File

@ -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:

View File

@ -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:
</div>
<div id="generator">
<div class="content">
Generated 2020-11-09T17:45:17.371+01:00
Generated 2020-11-09T22:06:31.158+01:00
</div>
</div>
</div>

View File

@ -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 <QJsonDocument>
#include <QJsonArray>
#include <QObject>
#include <QDebug>
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;
}
}

View File

@ -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 <QJsonObject>
#include <QString>
#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_ */

View File

@ -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;
}

View File

@ -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;

View File

@ -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();
}