kopia lustrzana https://github.com/f4exb/sdrangel
Audio output plugin
rodzic
b3476e4757
commit
87d8816881
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 13 KiB |
Plik binarny nie jest wyświetlany.
|
@ -3,6 +3,7 @@ project(samplesink)
|
|||
add_subdirectory(testsink)
|
||||
add_subdirectory(fileoutput)
|
||||
add_subdirectory(localoutput)
|
||||
add_subdirectory(audiooutput)
|
||||
|
||||
if(CM256CC_FOUND)
|
||||
add_subdirectory(remoteoutput)
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
project(audiooutput)
|
||||
|
||||
set(audiooutput_SOURCES
|
||||
audiooutput.cpp
|
||||
audiooutputplugin.cpp
|
||||
audiooutputsettings.cpp
|
||||
# audiooutputwebapiadapter.cpp
|
||||
audiooutputworker.cpp
|
||||
)
|
||||
|
||||
set(audiooutput_HEADERS
|
||||
audiooutput.h
|
||||
audiooutputplugin.h
|
||||
audiooutputsettings.h
|
||||
# audiooutputwebapiadapter.h
|
||||
audiooutputworker.h
|
||||
)
|
||||
|
||||
include_directories(
|
||||
${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client
|
||||
)
|
||||
|
||||
if(NOT SERVER_MODE)
|
||||
set(audiooutput_SOURCES
|
||||
${audiooutput_SOURCES}
|
||||
audiooutputgui.cpp
|
||||
audiooutputgui.ui
|
||||
)
|
||||
set(audiooutput_HEADERS
|
||||
${audiooutput_HEADERS}
|
||||
audiooutputgui.h
|
||||
)
|
||||
|
||||
set(TARGET_NAME outputaudio)
|
||||
set(TARGET_LIB "Qt5::Widgets")
|
||||
set(TARGET_LIB_GUI "sdrgui")
|
||||
set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR})
|
||||
else()
|
||||
set(TARGET_NAME outputaudiosrv)
|
||||
set(TARGET_LIB "")
|
||||
set(TARGET_LIB_GUI "")
|
||||
set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR})
|
||||
endif()
|
||||
|
||||
add_library(${TARGET_NAME} SHARED
|
||||
${audiooutput_SOURCES}
|
||||
)
|
||||
|
||||
target_link_libraries(${TARGET_NAME}
|
||||
Qt5::Core
|
||||
${TARGET_LIB}
|
||||
sdrbase
|
||||
${TARGET_LIB_GUI}
|
||||
swagger
|
||||
)
|
||||
|
||||
install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER})
|
|
@ -0,0 +1,444 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU General Public License V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <QDebug>
|
||||
#include <QNetworkReply>
|
||||
#include <QBuffer>
|
||||
|
||||
#include "SWGDeviceSettings.h"
|
||||
#include "SWGDeviceState.h"
|
||||
|
||||
#include "device/deviceapi.h"
|
||||
#include "audio/audiodevicemanager.h"
|
||||
#include "dsp/dspcommands.h"
|
||||
#include "dsp/dspengine.h"
|
||||
|
||||
#include "audiooutputworker.h"
|
||||
#include "audiooutput.h"
|
||||
|
||||
MESSAGE_CLASS_DEFINITION(AudioOutput::MsgConfigureAudioOutput, Message)
|
||||
MESSAGE_CLASS_DEFINITION(AudioOutput::MsgStartStop, Message)
|
||||
|
||||
AudioOutput::AudioOutput(DeviceAPI *deviceAPI) :
|
||||
m_deviceAPI(deviceAPI),
|
||||
m_audioFifo(48000),
|
||||
m_settings(),
|
||||
m_audioDeviceIndex(-1),
|
||||
m_centerFrequency(0),
|
||||
m_worker(nullptr),
|
||||
m_deviceDescription("AudioOutput")
|
||||
{
|
||||
m_deviceAPI->setNbSinkStreams(1);
|
||||
AudioDeviceManager *audioDeviceManager = DSPEngine::instance()->getAudioDeviceManager();
|
||||
m_sampleRate = audioDeviceManager->getOutputSampleRate(m_audioDeviceIndex);
|
||||
m_settings.m_deviceName = AudioDeviceManager::m_defaultDeviceName;
|
||||
m_sampleSourceFifo.resize(SampleSourceFifo::getSizePolicy(48000));
|
||||
}
|
||||
|
||||
AudioOutput::~AudioOutput()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
void AudioOutput::destroy()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
void AudioOutput::init()
|
||||
{
|
||||
applySettings(m_settings, true);
|
||||
}
|
||||
|
||||
bool AudioOutput::start()
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
qDebug("AudioOutput::start");
|
||||
|
||||
AudioDeviceManager *audioDeviceManager = DSPEngine::instance()->getAudioDeviceManager();
|
||||
audioDeviceManager->addAudioSink(&m_audioFifo, getInputMessageQueue(), m_audioDeviceIndex);
|
||||
|
||||
m_worker = new AudioOutputWorker(&m_sampleSourceFifo, &m_audioFifo);
|
||||
m_worker->moveToThread(&m_workerThread);
|
||||
m_worker->setSamplerate(m_sampleRate);
|
||||
m_worker->setIQMapping(m_settings.m_iqMapping);
|
||||
m_worker->connectTimer(m_deviceAPI->getMasterTimer());
|
||||
m_worker->startWork();
|
||||
m_workerThread.start();
|
||||
|
||||
mutexLocker.unlock();
|
||||
|
||||
qDebug("AudioOutput::start: started");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AudioOutput::stop()
|
||||
{
|
||||
qDebug("AudioOutput::stop");
|
||||
if (m_worker)
|
||||
{
|
||||
m_worker->stopWork();
|
||||
m_workerThread.quit();
|
||||
m_workerThread.wait();
|
||||
delete m_worker;
|
||||
m_worker = nullptr;
|
||||
}
|
||||
|
||||
AudioDeviceManager *audioDeviceManager = DSPEngine::instance()->getAudioDeviceManager();
|
||||
audioDeviceManager->removeAudioSink(&m_audioFifo);
|
||||
|
||||
m_running = false;
|
||||
qDebug("AudioOutput::stop: stopped");
|
||||
}
|
||||
|
||||
QByteArray AudioOutput::serialize() const
|
||||
{
|
||||
return m_settings.serialize();
|
||||
}
|
||||
|
||||
bool AudioOutput::deserialize(const QByteArray& data)
|
||||
{
|
||||
bool success = true;
|
||||
|
||||
if (!m_settings.deserialize(data))
|
||||
{
|
||||
m_settings.resetToDefaults();
|
||||
success = false;
|
||||
}
|
||||
|
||||
MsgConfigureAudioOutput* message = MsgConfigureAudioOutput::create(m_settings, true);
|
||||
m_inputMessageQueue.push(message);
|
||||
|
||||
if (m_guiMessageQueue)
|
||||
{
|
||||
MsgConfigureAudioOutput* messageToGUI = MsgConfigureAudioOutput::create(m_settings, true);
|
||||
m_guiMessageQueue->push(messageToGUI);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
const QString& AudioOutput::getDeviceDescription() const
|
||||
{
|
||||
return m_deviceDescription;
|
||||
}
|
||||
|
||||
int AudioOutput::getSampleRate() const
|
||||
{
|
||||
return m_sampleRate;
|
||||
}
|
||||
|
||||
quint64 AudioOutput::getCenterFrequency() const
|
||||
{
|
||||
return m_centerFrequency;
|
||||
}
|
||||
|
||||
bool AudioOutput::handleMessage(const Message& message)
|
||||
{
|
||||
if(MsgConfigureAudioOutput::match(message))
|
||||
{
|
||||
qDebug() << "AudioOutput::handleMessage: MsgConfigureAudioOutput";
|
||||
MsgConfigureAudioOutput& conf = (MsgConfigureAudioOutput&) message;
|
||||
applySettings(conf.getSettings(), conf.getForce());
|
||||
return true;
|
||||
}
|
||||
else if (MsgStartStop::match(message))
|
||||
{
|
||||
MsgStartStop& cmd = (MsgStartStop&) message;
|
||||
qDebug() << "AudioOutput::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 AudioOutput::applySettings(const AudioOutputSettings& settings, bool force)
|
||||
{
|
||||
bool forwardChange = false;
|
||||
QList<QString> reverseAPIKeys;
|
||||
|
||||
if ((m_settings.m_deviceName != settings.m_deviceName) || force)
|
||||
{
|
||||
reverseAPIKeys.append("deviceName");
|
||||
AudioDeviceManager *audioDeviceManager = DSPEngine::instance()->getAudioDeviceManager();
|
||||
m_audioDeviceIndex = audioDeviceManager->getOutputDeviceIndex(settings.m_deviceName);
|
||||
//qDebug("AMDemod::applySettings: audioDeviceName: %s audioDeviceIndex: %d", qPrintable(settings.m_audioDeviceName), audioDeviceIndex);
|
||||
audioDeviceManager->removeAudioSink(&m_audioFifo);
|
||||
audioDeviceManager->addAudioSink(&m_audioFifo, getInputMessageQueue(), m_audioDeviceIndex);
|
||||
m_sampleRate = audioDeviceManager->getOutputSampleRate(m_audioDeviceIndex);
|
||||
forwardChange = true;
|
||||
}
|
||||
|
||||
if ((m_settings.m_volume != settings.m_volume) || force)
|
||||
{
|
||||
reverseAPIKeys.append("volume");
|
||||
m_audioOutputDevice.setVolume(settings.m_volume);
|
||||
qDebug() << "AudioOutput::applySettings: set volume to " << settings.m_volume;
|
||||
}
|
||||
|
||||
if ((m_settings.m_iqMapping != settings.m_iqMapping) || force)
|
||||
{
|
||||
reverseAPIKeys.append("iqMapping");
|
||||
forwardChange = true;
|
||||
|
||||
if (m_worker) {
|
||||
m_worker->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)
|
||||
{
|
||||
if (m_worker) {
|
||||
m_worker->setSamplerate(m_sampleRate);
|
||||
}
|
||||
|
||||
DSPSignalNotification *notif = new DSPSignalNotification(m_sampleRate, 0);
|
||||
m_centerFrequency = 0;
|
||||
m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif);
|
||||
}
|
||||
}
|
||||
|
||||
int AudioOutput::webapiRunGet(
|
||||
SWGSDRangel::SWGDeviceState& response,
|
||||
QString& errorMessage)
|
||||
{
|
||||
(void) errorMessage;
|
||||
m_deviceAPI->getDeviceEngineStateStr(*response.getState());
|
||||
return 200;
|
||||
}
|
||||
|
||||
int AudioOutput::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 AudioOutput::webapiSettingsGet(
|
||||
SWGSDRangel::SWGDeviceSettings& response,
|
||||
QString& errorMessage)
|
||||
{
|
||||
(void) errorMessage;
|
||||
response.setAudioOutputSettings(new SWGSDRangel::SWGAudioOutputSettings());
|
||||
response.getAudioOutputSettings()->init();
|
||||
webapiFormatDeviceSettings(response, m_settings);
|
||||
|
||||
return 200;
|
||||
}
|
||||
|
||||
int AudioOutput::webapiSettingsPutPatch(
|
||||
bool force,
|
||||
const QStringList& deviceSettingsKeys,
|
||||
SWGSDRangel::SWGDeviceSettings& response, // query + response
|
||||
QString& errorMessage)
|
||||
{
|
||||
(void) errorMessage;
|
||||
AudioOutputSettings settings = m_settings;
|
||||
webapiUpdateDeviceSettings(settings, deviceSettingsKeys, response);
|
||||
|
||||
MsgConfigureAudioOutput *msg = MsgConfigureAudioOutput::create(settings, force);
|
||||
m_inputMessageQueue.push(msg);
|
||||
|
||||
if (m_guiMessageQueue) // forward to GUI if any
|
||||
{
|
||||
MsgConfigureAudioOutput *msgToGUI = MsgConfigureAudioOutput::create(settings, force);
|
||||
m_guiMessageQueue->push(msgToGUI);
|
||||
}
|
||||
|
||||
webapiFormatDeviceSettings(response, settings);
|
||||
return 200;
|
||||
}
|
||||
|
||||
void AudioOutput::webapiUpdateDeviceSettings(
|
||||
AudioOutputSettings& settings,
|
||||
const QStringList& deviceSettingsKeys,
|
||||
SWGSDRangel::SWGDeviceSettings& response)
|
||||
{
|
||||
if (deviceSettingsKeys.contains("deviceName")) {
|
||||
settings.m_deviceName = *response.getAudioOutputSettings()->getDeviceName();
|
||||
}
|
||||
if (deviceSettingsKeys.contains("volume")) {
|
||||
settings.m_volume = response.getAudioOutputSettings()->getVolume();
|
||||
}
|
||||
if (deviceSettingsKeys.contains("iqMapping")) {
|
||||
settings.m_iqMapping = (AudioOutputSettings::IQMapping) response.getAudioOutputSettings()->getIqMapping();
|
||||
}
|
||||
if (deviceSettingsKeys.contains("useReverseAPI")) {
|
||||
settings.m_useReverseAPI = response.getAudioOutputSettings()->getUseReverseApi() != 0;
|
||||
}
|
||||
if (deviceSettingsKeys.contains("reverseAPIAddress")) {
|
||||
settings.m_reverseAPIAddress = *response.getAudioOutputSettings()->getReverseApiAddress();
|
||||
}
|
||||
if (deviceSettingsKeys.contains("reverseAPIPort")) {
|
||||
settings.m_reverseAPIPort = response.getAudioOutputSettings()->getReverseApiPort();
|
||||
}
|
||||
if (deviceSettingsKeys.contains("reverseAPIDeviceIndex")) {
|
||||
settings.m_reverseAPIDeviceIndex = response.getAudioOutputSettings()->getReverseApiDeviceIndex();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioOutput::webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const AudioOutputSettings& settings)
|
||||
{
|
||||
response.getAudioOutputSettings()->setDeviceName(new QString(settings.m_deviceName));
|
||||
response.getAudioOutputSettings()->setVolume(settings.m_volume);
|
||||
response.getAudioOutputSettings()->setIqMapping((int) settings.m_iqMapping);
|
||||
|
||||
response.getAudioOutputSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
|
||||
|
||||
if (response.getAudioOutputSettings()->getReverseApiAddress()) {
|
||||
*response.getAudioOutputSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress;
|
||||
} else {
|
||||
response.getAudioOutputSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress));
|
||||
}
|
||||
|
||||
response.getAudioOutputSettings()->setReverseApiPort(settings.m_reverseAPIPort);
|
||||
response.getAudioOutputSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex);
|
||||
}
|
||||
|
||||
void AudioOutput::webapiReverseSendSettings(QList<QString>& deviceSettingsKeys, const AudioOutputSettings& settings, bool force)
|
||||
{
|
||||
SWGSDRangel::SWGDeviceSettings *swgDeviceSettings = new SWGSDRangel::SWGDeviceSettings();
|
||||
swgDeviceSettings->setDirection(1); // single Tx
|
||||
swgDeviceSettings->setOriginatorIndex(m_deviceAPI->getDeviceSetIndex());
|
||||
swgDeviceSettings->setDeviceHwType(new QString("AudioOutput"));
|
||||
swgDeviceSettings->setAudioOutputSettings(new SWGSDRangel::SWGAudioOutputSettings());
|
||||
SWGSDRangel::SWGAudioOutputSettings *swgAudioOutputSettings = swgDeviceSettings->getAudioOutputSettings();
|
||||
|
||||
// transfer data that has been modified. When force is on transfer all data except reverse API data
|
||||
|
||||
if (deviceSettingsKeys.contains("deviceName") || force) {
|
||||
swgAudioOutputSettings->setDeviceName(new QString(settings.m_deviceName));
|
||||
}
|
||||
if (deviceSettingsKeys.contains("volume") || force) {
|
||||
swgAudioOutputSettings->setVolume(settings.m_volume);
|
||||
}
|
||||
if (deviceSettingsKeys.contains("iqMapping") || force) {
|
||||
swgAudioOutputSettings->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 AudioOutput::webapiReverseSendStartStop(bool start)
|
||||
{
|
||||
SWGSDRangel::SWGDeviceSettings *swgDeviceSettings = new SWGSDRangel::SWGDeviceSettings();
|
||||
swgDeviceSettings->setDirection(1); // single Tx
|
||||
swgDeviceSettings->setOriginatorIndex(m_deviceAPI->getDeviceSetIndex());
|
||||
swgDeviceSettings->setDeviceHwType(new QString("AudioOutput"));
|
||||
|
||||
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 AudioOutput::networkManagerFinished(QNetworkReply *reply)
|
||||
{
|
||||
QNetworkReply::NetworkError replyError = reply->error();
|
||||
|
||||
if (replyError)
|
||||
{
|
||||
qWarning() << "AudioOutput::networkManagerFinished:"
|
||||
<< " error(" << (int) replyError
|
||||
<< "): " << replyError
|
||||
<< ": " << reply->errorString();
|
||||
}
|
||||
else
|
||||
{
|
||||
QString answer = reply->readAll();
|
||||
answer.chop(1); // remove last \n
|
||||
qDebug("AudioOutput::networkManagerFinished: reply:\n%s", answer.toStdString().c_str());
|
||||
}
|
||||
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
|
@ -0,0 +1,153 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU General Public License V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _AUDIOOUTPUT_AUDIOOUTPUT_H_
|
||||
#define _AUDIOOUTPUT_AUDIOOUTPUT_H_
|
||||
|
||||
#include <QThread>
|
||||
#include <QNetworkRequest>
|
||||
|
||||
#include "dsp/devicesamplesink.h"
|
||||
#include "audio/audiooutputdevice.h"
|
||||
#include "audio/audiofifo.h"
|
||||
|
||||
#include "audiooutputsettings.h"
|
||||
|
||||
class QNetworkAccessManager;
|
||||
class QNetworkReply;
|
||||
class AudioOutputWorker;
|
||||
class DeviceAPI;
|
||||
|
||||
class AudioOutput : public DeviceSampleSink {
|
||||
public:
|
||||
class MsgConfigureAudioOutput : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
const AudioOutputSettings& getSettings() const { return m_settings; }
|
||||
bool getForce() const { return m_force; }
|
||||
|
||||
static MsgConfigureAudioOutput* create(const AudioOutputSettings& settings, bool force)
|
||||
{
|
||||
return new MsgConfigureAudioOutput(settings, force);
|
||||
}
|
||||
|
||||
private:
|
||||
AudioOutputSettings m_settings;
|
||||
bool m_force;
|
||||
|
||||
MsgConfigureAudioOutput(const AudioOutputSettings& 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)
|
||||
{ }
|
||||
};
|
||||
|
||||
AudioOutput(DeviceAPI *deviceAPI);
|
||||
virtual ~AudioOutput();
|
||||
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) { (void) centerFrequency; }
|
||||
const QString& getDeviceName() const { return m_settings.m_deviceName; }
|
||||
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 AudioOutputSettings& settings);
|
||||
|
||||
static void webapiUpdateDeviceSettings(
|
||||
AudioOutputSettings& settings,
|
||||
const QStringList& deviceSettingsKeys,
|
||||
SWGSDRangel::SWGDeviceSettings& response);
|
||||
|
||||
private:
|
||||
DeviceAPI *m_deviceAPI;
|
||||
AudioOutputDevice m_audioOutputDevice;
|
||||
AudioFifo m_audioFifo;
|
||||
QMutex m_mutex;
|
||||
AudioOutputSettings m_settings;
|
||||
int m_audioDeviceIndex;
|
||||
int m_sampleRate;
|
||||
qint64 m_centerFrequency;
|
||||
AudioOutputWorker* m_worker;
|
||||
QThread m_workerThread;
|
||||
QString m_deviceDescription;
|
||||
bool m_running;
|
||||
QNetworkAccessManager *m_networkManager;
|
||||
QNetworkRequest m_networkRequest;
|
||||
|
||||
void applySettings(const AudioOutputSettings& settings, bool force);
|
||||
|
||||
void webapiReverseSendSettings(QList<QString>& deviceSettingsKeys, const AudioOutputSettings& settings, bool force);
|
||||
void webapiReverseSendStartStop(bool start);
|
||||
|
||||
private slots:
|
||||
void networkManagerFinished(QNetworkReply *reply);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,239 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU General Public License V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <QFileDialog>
|
||||
|
||||
#include "ui_audiooutputgui.h"
|
||||
#include "gui/colormapper.h"
|
||||
#include "gui/glspectrum.h"
|
||||
#include "gui/crightclickenabler.h"
|
||||
#include "gui/basicdevicesettingsdialog.h"
|
||||
#include "gui/audioselectdialog.h"
|
||||
#include "dsp/dspengine.h"
|
||||
#include "dsp/dspcommands.h"
|
||||
#include "audiooutputgui.h"
|
||||
|
||||
#include "device/deviceapi.h"
|
||||
#include "device/deviceuiset.h"
|
||||
|
||||
AudioOutputGui::AudioOutputGui(DeviceUISet *deviceUISet, QWidget* parent) :
|
||||
DeviceGUI(parent),
|
||||
ui(new Ui::AudioOutputGui),
|
||||
m_deviceUISet(deviceUISet),
|
||||
m_doApplySettings(true),
|
||||
m_forceSettings(true),
|
||||
m_settings(),
|
||||
m_centerFrequency(0)
|
||||
{
|
||||
m_audioOutput = (AudioOutput*) m_deviceUISet->m_deviceAPI->getSampleSink();
|
||||
|
||||
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 &)));
|
||||
|
||||
m_sampleRate = m_audioOutput->getSampleRate();
|
||||
m_centerFrequency = m_audioOutput->getCenterFrequency();
|
||||
m_settings.m_deviceName = m_audioOutput->getDeviceName();
|
||||
updateSampleRateAndFrequency();
|
||||
displaySettings();
|
||||
|
||||
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection);
|
||||
m_audioOutput->setMessageQueueToGUI(&m_inputMessageQueue);
|
||||
}
|
||||
|
||||
AudioOutputGui::~AudioOutputGui()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void AudioOutputGui::destroy()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
void AudioOutputGui::resetToDefaults()
|
||||
{
|
||||
m_settings.resetToDefaults();
|
||||
displaySettings();
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
QByteArray AudioOutputGui::serialize() const
|
||||
{
|
||||
return m_settings.serialize();
|
||||
}
|
||||
|
||||
bool AudioOutputGui::deserialize(const QByteArray& data)
|
||||
{
|
||||
if(m_settings.deserialize(data))
|
||||
{
|
||||
displaySettings();
|
||||
m_forceSettings = true;
|
||||
sendSettings();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool AudioOutputGui::handleMessage(const Message& message)
|
||||
{
|
||||
if (AudioOutput::MsgConfigureAudioOutput::match(message))
|
||||
{
|
||||
const AudioOutput::MsgConfigureAudioOutput& cfg = (AudioOutput::MsgConfigureAudioOutput&) message;
|
||||
m_settings = cfg.getSettings();
|
||||
blockApplySettings(true);
|
||||
displaySettings();
|
||||
blockApplySettings(false);
|
||||
return true;
|
||||
}
|
||||
else if (AudioOutput::MsgStartStop::match(message))
|
||||
{
|
||||
AudioOutput::MsgStartStop& notif = (AudioOutput::MsgStartStop&) message;
|
||||
blockApplySettings(true);
|
||||
ui->startStop->setChecked(notif.getStartStop());
|
||||
blockApplySettings(false);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioOutputGui::handleInputMessages()
|
||||
{
|
||||
Message* message;
|
||||
|
||||
while ((message = m_inputMessageQueue.pop()) != 0)
|
||||
{
|
||||
qDebug("AudioOutputGui::handleInputMessages: message: %s", message->getIdentifier());
|
||||
|
||||
if (DSPSignalNotification::match(*message))
|
||||
{
|
||||
DSPSignalNotification* notif = (DSPSignalNotification*) message;
|
||||
m_sampleRate = notif->getSampleRate();
|
||||
m_centerFrequency = notif->getCenterFrequency();
|
||||
qDebug("AudioOutputGui::handleInputMessages: DSPSignalNotification: SampleRate: %d", notif->getSampleRate());
|
||||
updateSampleRateAndFrequency();
|
||||
|
||||
delete message;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (handleMessage(*message)) {
|
||||
delete message;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioOutputGui::updateSampleRateAndFrequency()
|
||||
{
|
||||
m_deviceUISet->getSpectrum()->setSampleRate(m_sampleRate);
|
||||
m_deviceUISet->getSpectrum()->setCenterFrequency(m_centerFrequency);
|
||||
m_deviceUISet->getSpectrum()->setSsbSpectrum(false);
|
||||
m_deviceUISet->getSpectrum()->setLsbDisplay(false);
|
||||
ui->deviceRateText->setText(tr("%1k").arg((float)m_sampleRate / 1000));
|
||||
}
|
||||
|
||||
void AudioOutputGui::displaySettings()
|
||||
{
|
||||
ui->deviceLabel->setText(m_settings.m_deviceName);
|
||||
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);
|
||||
}
|
||||
|
||||
void AudioOutputGui::on_deviceSelect_clicked()
|
||||
{
|
||||
AudioSelectDialog audioSelect(DSPEngine::instance()->getAudioDeviceManager(), m_settings.m_deviceName, false, this);
|
||||
audioSelect.exec();
|
||||
|
||||
if (audioSelect.m_selected)
|
||||
{
|
||||
m_settings.m_deviceName = audioSelect.m_audioDeviceName;
|
||||
ui->deviceLabel->setText(m_settings.m_deviceName);
|
||||
sendSettings();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioOutputGui::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 AudioOutputGui::on_channels_currentIndexChanged(int index)
|
||||
{
|
||||
m_settings.m_iqMapping = (AudioOutputSettings::IQMapping) index;
|
||||
updateSampleRateAndFrequency();
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void AudioOutputGui::on_startStop_toggled(bool checked)
|
||||
{
|
||||
if (m_doApplySettings)
|
||||
{
|
||||
AudioOutput::MsgStartStop *message = AudioOutput::MsgStartStop::create(checked);
|
||||
m_audioOutput->getInputMessageQueue()->push(message);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioOutputGui::sendSettings()
|
||||
{
|
||||
if(!m_updateTimer.isActive())
|
||||
m_updateTimer.start(100);
|
||||
}
|
||||
|
||||
void AudioOutputGui::updateHardware()
|
||||
{
|
||||
if (m_doApplySettings)
|
||||
{
|
||||
AudioOutput::MsgConfigureAudioOutput* message = AudioOutput::MsgConfigureAudioOutput::create(m_settings, m_forceSettings);
|
||||
m_audioOutput->getInputMessageQueue()->push(message);
|
||||
m_forceSettings = false;
|
||||
m_updateTimer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioOutputGui::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();
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU General Public License V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef INCLUDE_AUDIOOUTPUTGUI_H
|
||||
#define INCLUDE_AUDIOOUTPUTGUI_H
|
||||
|
||||
#include <QTimer>
|
||||
#include <QWidget>
|
||||
|
||||
#include "device/devicegui.h"
|
||||
#include "util/messagequeue.h"
|
||||
|
||||
#include "audiooutput.h"
|
||||
|
||||
class QWidget;
|
||||
class DeviceUISet;
|
||||
|
||||
namespace Ui {
|
||||
class AudioOutputGui;
|
||||
}
|
||||
|
||||
class AudioOutputGui : public DeviceGUI {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit AudioOutputGui(DeviceUISet *deviceUISet, QWidget* parent = nullptr);
|
||||
virtual ~AudioOutputGui();
|
||||
virtual void destroy();
|
||||
|
||||
void resetToDefaults();
|
||||
QByteArray serialize() const;
|
||||
bool deserialize(const QByteArray& data);
|
||||
virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
|
||||
|
||||
private:
|
||||
Ui::AudioOutputGui* ui;
|
||||
|
||||
DeviceUISet* m_deviceUISet;
|
||||
AudioOutput* m_audioOutput;
|
||||
bool m_doApplySettings;
|
||||
bool m_forceSettings;
|
||||
AudioOutputSettings m_settings;
|
||||
QTimer m_updateTimer;
|
||||
int m_sampleRate;
|
||||
qint64 m_centerFrequency;
|
||||
|
||||
MessageQueue m_inputMessageQueue;
|
||||
|
||||
void blockApplySettings(bool block) { m_doApplySettings = !block; }
|
||||
void displaySettings();
|
||||
void sendSettings();
|
||||
void updateSampleRateAndFrequency();
|
||||
bool handleMessage(const Message& message);
|
||||
|
||||
private slots:
|
||||
void handleInputMessages();
|
||||
void on_deviceSelect_clicked();
|
||||
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
|
|
@ -0,0 +1,290 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>AudioOutputGui</class>
|
||||
<widget class="QWidget" name="AudioOutputGui">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>320</width>
|
||||
<height>200</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>200</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Liberation Sans</family>
|
||||
<pointsize>9</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Audio Output</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="QPushButton" name="deviceSelect">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>24</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Open dialog to select output device</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||
<normaloff>:/sound_on.png</normaloff>:/sound_on.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="deviceLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Output device selected</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</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="channelsLabel">
|
||||
<property name="text">
|
||||
<string>Channels</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=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>
|
|
@ -0,0 +1,148 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU General Public License V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <QtPlugin>
|
||||
|
||||
#include "plugin/pluginapi.h"
|
||||
#include "util/simpleserializer.h"
|
||||
#include "audiooutputplugin.h"
|
||||
#include "audiooutputwebapiadapter.h"
|
||||
|
||||
#ifdef SERVER_MODE
|
||||
#include "audiooutput.h"
|
||||
#else
|
||||
#include "audiooutputgui.h"
|
||||
#endif
|
||||
|
||||
const PluginDescriptor AudioOutputPlugin::m_pluginDescriptor = {
|
||||
QString("AudioOutput"),
|
||||
QString("Audio output"),
|
||||
QString("6.1.0"),
|
||||
QString("(c) Edouard Griffiths, F4EXB"),
|
||||
QString("https://github.com/f4exb/sdrangel"),
|
||||
true,
|
||||
QString("https://github.com/f4exb/sdrangel")
|
||||
};
|
||||
|
||||
const QString AudioOutputPlugin::m_hardwareID = "AudioOutput";
|
||||
const QString AudioOutputPlugin::m_deviceTypeID = AUDIOOUTPUT_DEVICE_TYPE_ID;
|
||||
|
||||
AudioOutputPlugin::AudioOutputPlugin(QObject* parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
const PluginDescriptor& AudioOutputPlugin::getPluginDescriptor() const
|
||||
{
|
||||
return m_pluginDescriptor;
|
||||
}
|
||||
|
||||
void AudioOutputPlugin::initPlugin(PluginAPI* pluginAPI)
|
||||
{
|
||||
pluginAPI->registerSampleSink(m_deviceTypeID, this);
|
||||
}
|
||||
|
||||
void AudioOutputPlugin::enumOriginDevices(QStringList& listedHwIds, OriginDevices& originDevices)
|
||||
{
|
||||
if (listedHwIds.contains(m_hardwareID)) { // check if it was done
|
||||
return;
|
||||
}
|
||||
|
||||
originDevices.append(OriginDevice(
|
||||
"AudioOutput",
|
||||
m_hardwareID,
|
||||
QString(),
|
||||
0, // Sequence
|
||||
0, // nb Rx
|
||||
1 // nb Tx
|
||||
));
|
||||
|
||||
listedHwIds.append(m_hardwareID);
|
||||
}
|
||||
|
||||
PluginInterface::SamplingDevices AudioOutputPlugin::enumSampleSinks(const OriginDevices& originDevices)
|
||||
{
|
||||
SamplingDevices result;
|
||||
|
||||
for (OriginDevices::const_iterator it = originDevices.begin(); it != originDevices.end(); ++it)
|
||||
{
|
||||
if (it->hardwareId == m_hardwareID)
|
||||
{
|
||||
result.append(SamplingDevice(
|
||||
it->displayableName,
|
||||
it->hardwareId,
|
||||
m_deviceTypeID,
|
||||
it->serial,
|
||||
it->sequence,
|
||||
PluginInterface::SamplingDevice::BuiltInDevice,
|
||||
PluginInterface::SamplingDevice::StreamSingleTx,
|
||||
1,
|
||||
0
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef SERVER_MODE
|
||||
DeviceGUI* AudioOutputPlugin::createSampleSinkPluginInstanceGUI(
|
||||
const QString& sinkId,
|
||||
QWidget **widget,
|
||||
DeviceUISet *deviceUISet)
|
||||
{
|
||||
(void) sinkId;
|
||||
(void) widget;
|
||||
(void) deviceUISet;
|
||||
return nullptr;
|
||||
}
|
||||
#else
|
||||
DeviceGUI* AudioOutputPlugin::createSampleSinkPluginInstanceGUI(
|
||||
const QString& sinkId,
|
||||
QWidget **widget,
|
||||
DeviceUISet *deviceUISet)
|
||||
{
|
||||
if (sinkId == m_deviceTypeID)
|
||||
{
|
||||
AudioOutputGui* gui = new AudioOutputGui(deviceUISet);
|
||||
*widget = gui;
|
||||
return gui;
|
||||
}
|
||||
else
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
DeviceSampleSink* AudioOutputPlugin::createSampleSinkPluginInstance(const QString& sinkId, DeviceAPI *deviceAPI)
|
||||
{
|
||||
if(sinkId == m_deviceTypeID)
|
||||
{
|
||||
AudioOutput* output = new AudioOutput(deviceAPI);
|
||||
return output;
|
||||
}
|
||||
else
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
DeviceWebAPIAdapter *AudioOutputPlugin::createDeviceWebAPIAdapter() const
|
||||
{
|
||||
return new AudioOutputWebAPIAdapter();
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU General Public License V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef INCLUDE_AUDIOOUTPUTPLUGIN_H
|
||||
#define INCLUDE_AUDIOOUTPUTPLUGIN_H
|
||||
|
||||
#include <QObject>
|
||||
#include "plugin/plugininterface.h"
|
||||
|
||||
#define AUDIOOUTPUT_DEVICE_TYPE_ID "sdrangel.samplesink.audiooutput"
|
||||
|
||||
class PluginAPI;
|
||||
class DeviceAPI;
|
||||
|
||||
class AudioOutputPlugin : public QObject, public PluginInterface {
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(PluginInterface)
|
||||
Q_PLUGIN_METADATA(IID AUDIOOUTPUT_DEVICE_TYPE_ID)
|
||||
|
||||
public:
|
||||
explicit AudioOutputPlugin(QObject* parent = nullptr);
|
||||
|
||||
const PluginDescriptor& getPluginDescriptor() const;
|
||||
void initPlugin(PluginAPI* pluginAPI);
|
||||
|
||||
virtual void enumOriginDevices(QStringList& listedHwIds, OriginDevices& originDevices);
|
||||
virtual SamplingDevices enumSampleSinks(const OriginDevices& originDevices);
|
||||
virtual DeviceGUI* createSampleSinkPluginInstanceGUI(
|
||||
const QString& sinkId,
|
||||
QWidget **widget,
|
||||
DeviceUISet *deviceUISet);
|
||||
virtual DeviceSampleSink* createSampleSinkPluginInstance(const QString& sinkId, DeviceAPI *deviceAPI);
|
||||
virtual DeviceWebAPIAdapter* createDeviceWebAPIAdapter() const;
|
||||
|
||||
static const QString m_hardwareID;
|
||||
static const QString m_deviceTypeID;
|
||||
|
||||
private:
|
||||
static const PluginDescriptor m_pluginDescriptor;
|
||||
};
|
||||
|
||||
#endif // INCLUDE_AUDIOOUTPUTPLUGIN_H
|
|
@ -0,0 +1,93 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU General Public License V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
#include "util/simpleserializer.h"
|
||||
#include "audiooutputsettings.h"
|
||||
|
||||
AudioOutputSettings::AudioOutputSettings()
|
||||
{
|
||||
resetToDefaults();
|
||||
}
|
||||
|
||||
void AudioOutputSettings::resetToDefaults()
|
||||
{
|
||||
m_deviceName = "";
|
||||
m_volume = 1.0f;
|
||||
m_iqMapping = LR;
|
||||
m_useReverseAPI = false;
|
||||
m_reverseAPIAddress = "127.0.0.1";
|
||||
m_reverseAPIPort = 8888;
|
||||
m_reverseAPIDeviceIndex = 0;
|
||||
}
|
||||
|
||||
QByteArray AudioOutputSettings::serialize() const
|
||||
{
|
||||
SimpleSerializer s(1);
|
||||
|
||||
s.writeString(1, m_deviceName);
|
||||
s.writeFloat(3, m_volume);
|
||||
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 AudioOutputSettings::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.readFloat(3, &m_volume, 1.0f);
|
||||
d.readS32(5, (int *)&m_iqMapping, IQMapping::LR);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU General Public License V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _AUDIOOUTPUT_AUDIOOUTPUTSETTINGS_H_
|
||||
#define _AUDIOOUTPUT_AUDIOOUTPUTSETTINGS_H_
|
||||
|
||||
#include <QString>
|
||||
#include <QAudioDeviceInfo>
|
||||
|
||||
struct AudioOutputSettings {
|
||||
|
||||
QString m_deviceName; // Including realm, as from getFullDeviceName below
|
||||
float m_volume;
|
||||
enum IQMapping {
|
||||
LR,
|
||||
RL
|
||||
} m_iqMapping;
|
||||
|
||||
bool m_useReverseAPI;
|
||||
QString m_reverseAPIAddress;
|
||||
uint16_t m_reverseAPIPort;
|
||||
uint16_t m_reverseAPIDeviceIndex;
|
||||
|
||||
AudioOutputSettings();
|
||||
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 /* _AUDIOOUTPUT_AUDIOOUTPUTSETTINGS_H_ */
|
|
@ -0,0 +1,49 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU General Public License V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "SWGDeviceSettings.h"
|
||||
#include "audiooutput.h"
|
||||
#include "audiooutputwebapiadapter.h"
|
||||
|
||||
AudioOutputWebAPIAdapter::AudioOutputWebAPIAdapter()
|
||||
{}
|
||||
|
||||
AudioOutputWebAPIAdapter::~AudioOutputWebAPIAdapter()
|
||||
{}
|
||||
|
||||
int AudioOutputWebAPIAdapter::webapiSettingsGet(
|
||||
SWGSDRangel::SWGDeviceSettings& response,
|
||||
QString& errorMessage)
|
||||
{
|
||||
(void) errorMessage;
|
||||
response.setAirspyHfSettings(new SWGSDRangel::SWGAirspyHFSettings());
|
||||
response.getAirspyHfSettings()->init();
|
||||
AudioOutput::webapiFormatDeviceSettings(response, m_settings);
|
||||
return 200;
|
||||
}
|
||||
|
||||
int AudioOutputWebAPIAdapter::webapiSettingsPutPatch(
|
||||
bool force,
|
||||
const QStringList& deviceSettingsKeys,
|
||||
SWGSDRangel::SWGDeviceSettings& response, // query + response
|
||||
QString& errorMessage)
|
||||
{
|
||||
(void) force; // no action
|
||||
(void) errorMessage;
|
||||
AudioOutput::webapiUpdateDeviceSettings(m_settings, deviceSettingsKeys, response);
|
||||
return 200;
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU General Public License V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "device/devicewebapiadapter.h"
|
||||
#include "audiooutputsettings.h"
|
||||
|
||||
class AudioOutputWebAPIAdapter : public DeviceWebAPIAdapter
|
||||
{
|
||||
public:
|
||||
AudioOutputWebAPIAdapter();
|
||||
virtual ~AudioOutputWebAPIAdapter();
|
||||
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:
|
||||
AudioOutputSettings m_settings;
|
||||
};
|
|
@ -0,0 +1,157 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU General Public License V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <QTimer>
|
||||
#include <QDebug>
|
||||
|
||||
#include "dsp/samplesourcefifo.h"
|
||||
#include "audio/audiofifo.h"
|
||||
|
||||
#include "audiooutputworker.h"
|
||||
|
||||
#define AUDIOOUTPUT_THROTTLE_MS 50
|
||||
|
||||
AudioOutputWorker::AudioOutputWorker(SampleSourceFifo* sampleFifo, AudioFifo *fifo, QObject* parent) :
|
||||
QObject(parent),
|
||||
m_running(false),
|
||||
m_samplerate(0),
|
||||
m_throttlems(AUDIOOUTPUT_THROTTLE_MS),
|
||||
m_maxThrottlems(50),
|
||||
m_throttleToggle(false),
|
||||
m_iqMapping(AudioOutputSettings::IQMapping::LR),
|
||||
m_buf(nullptr),
|
||||
m_samplesChunkSize(0),
|
||||
m_sampleFifo(sampleFifo),
|
||||
m_audioFifo(fifo)
|
||||
{
|
||||
m_audioBuffer.resize(1<<14);
|
||||
m_audioBufferFill = 0;
|
||||
}
|
||||
|
||||
AudioOutputWorker::~AudioOutputWorker()
|
||||
{
|
||||
}
|
||||
|
||||
void AudioOutputWorker::startWork()
|
||||
{
|
||||
qDebug("AudioOutputWorker::startWork");
|
||||
m_running = true;
|
||||
}
|
||||
|
||||
void AudioOutputWorker::stopWork()
|
||||
{
|
||||
qDebug("AudioOutputWorker::stopWork");
|
||||
m_running = false;
|
||||
}
|
||||
|
||||
void AudioOutputWorker::connectTimer(const QTimer& timer)
|
||||
{
|
||||
qDebug() << "AudioOutputWorker::connectTimer";
|
||||
connect(&timer, SIGNAL(timeout()), this, SLOT(tick()));
|
||||
}
|
||||
|
||||
void AudioOutputWorker::setSamplerate(int samplerate)
|
||||
{
|
||||
if (samplerate != m_samplerate)
|
||||
{
|
||||
bool wasRunning = false;
|
||||
|
||||
if (m_running)
|
||||
{
|
||||
stopWork();
|
||||
wasRunning = true;
|
||||
}
|
||||
|
||||
// resize sample FIFO
|
||||
if (m_sampleFifo) {
|
||||
m_sampleFifo->resize(SampleSourceFifo::getSizePolicy(samplerate)); // 1s buffer
|
||||
}
|
||||
|
||||
qDebug() << "AudioOutputWorker::setSamplerate:"
|
||||
<< " new:" << samplerate
|
||||
<< " old:" << m_samplerate
|
||||
<< " m_sampleFifo size:" << m_sampleFifo->size()
|
||||
<< " m_audioFifo size:" << m_audioFifo->size()
|
||||
<< " sample i/q size" << sizeof(FixReal);
|
||||
|
||||
// resize output buffer
|
||||
if (m_buf) {
|
||||
delete[] m_buf;
|
||||
}
|
||||
|
||||
m_buf = new int16_t[samplerate*2];
|
||||
|
||||
m_samplerate = samplerate;
|
||||
m_samplesChunkSize = (m_samplerate * m_throttlems) / 1000;
|
||||
|
||||
if (wasRunning) {
|
||||
startWork();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioOutputWorker::tick()
|
||||
{
|
||||
if (m_running)
|
||||
{
|
||||
qint64 throttlems = m_elapsedTimer.restart();
|
||||
|
||||
if (throttlems != m_throttlems)
|
||||
{
|
||||
m_throttlems = throttlems;
|
||||
m_samplesChunkSize = (m_samplerate * (m_throttlems+(m_throttleToggle ? 1 : 0))) / 1000;
|
||||
m_throttleToggle = !m_throttleToggle;
|
||||
}
|
||||
|
||||
unsigned int iPart1Begin, iPart1End, iPart2Begin, iPart2End;
|
||||
SampleVector& data = m_sampleFifo->getData();
|
||||
m_sampleFifo->read(m_samplesChunkSize, iPart1Begin, iPart1End, iPart2Begin, iPart2End);
|
||||
|
||||
if (iPart1Begin != iPart1End) {
|
||||
callbackPart(data, iPart1Begin, iPart1End);
|
||||
}
|
||||
|
||||
if (iPart2Begin != iPart2End) {
|
||||
callbackPart(data, iPart2Begin, iPart2End);
|
||||
}
|
||||
|
||||
// qDebug("AudioOutputWorker::tick: %d samples fill: %u", m_samplesChunkSize, m_audioFifo->fill());
|
||||
}
|
||||
}
|
||||
|
||||
void AudioOutputWorker::callbackPart(SampleVector& data, unsigned int iBegin, unsigned int iEnd)
|
||||
{
|
||||
for (unsigned int i = iBegin; i < iEnd; i++)
|
||||
{
|
||||
m_audioBuffer[m_audioBufferFill].l = m_iqMapping == AudioOutputSettings::LR ? data[i].m_real : data[i].m_imag;
|
||||
m_audioBuffer[m_audioBufferFill].r = m_iqMapping == AudioOutputSettings::LR ? data[i].m_imag : data[i].m_real;
|
||||
m_audioBufferFill++;
|
||||
|
||||
if (m_audioBufferFill >= m_audioBuffer.size())
|
||||
{
|
||||
uint res = m_audioFifo->write((const quint8*)&m_audioBuffer[0], m_audioBufferFill);
|
||||
|
||||
if (res != m_audioBufferFill)
|
||||
{
|
||||
qDebug("AudioOutputWorker::callbackPart: %u/%u audio samples written", res, m_audioBufferFill);
|
||||
m_audioFifo->clear();
|
||||
}
|
||||
|
||||
m_audioBufferFill = 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU General Public License V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef INCLUDE_AUDIOOUTPUTWORKER_H
|
||||
#define INCLUDE_AUDIOOUTPUTWORKER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QElapsedTimer>
|
||||
|
||||
#include "dsp/interpolators.h"
|
||||
#include "audiooutputsettings.h"
|
||||
|
||||
class QTimer;
|
||||
class SampleSourceFifo;
|
||||
class AudioFifo;
|
||||
|
||||
class AudioOutputWorker : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
AudioOutputWorker(SampleSourceFifo* sampleFifo, AudioFifo *fifo, QObject* parent = nullptr);
|
||||
~AudioOutputWorker();
|
||||
|
||||
void startWork();
|
||||
void stopWork();
|
||||
void setSamplerate(int samplerate);
|
||||
void setIQMapping(AudioOutputSettings::IQMapping iqMapping) {m_iqMapping = iqMapping;}
|
||||
void connectTimer(const QTimer& timer);
|
||||
|
||||
private:
|
||||
bool m_running;
|
||||
int m_samplerate;
|
||||
int m_throttlems;
|
||||
int m_maxThrottlems;
|
||||
QElapsedTimer m_elapsedTimer;
|
||||
bool m_throttleToggle;
|
||||
AudioOutputSettings::IQMapping m_iqMapping;
|
||||
AudioVector m_audioBuffer;
|
||||
uint32_t m_audioBufferFill;
|
||||
qint16 *m_buf; // stereo (I, Q)
|
||||
unsigned int m_samplesChunkSize;
|
||||
SampleSourceFifo* m_sampleFifo;
|
||||
AudioFifo* m_audioFifo;
|
||||
Interpolators<qint16, SDR_TX_SAMP_SZ, 16> m_interpolators;
|
||||
|
||||
void callbackPart(SampleVector& data, unsigned int iBegin, unsigned int iEnd);
|
||||
|
||||
private slots:
|
||||
void tick();
|
||||
};
|
||||
|
||||
#endif // INCLUDE_AUDIOOUTPUTWORKER_H
|
|
@ -0,0 +1,36 @@
|
|||
<h1>Audio output plugin</h1>
|
||||
|
||||
<h2>Introduction</h2>
|
||||
|
||||
This output plugin sends its samples to an audio device.
|
||||
|
||||
<h2>Interface</h2>
|
||||
|
||||
![Audio output plugin GUI](../../../doc/img/AudioOutput_plugin.png)
|
||||
|
||||
<h3>1: Start/Stop</h3>
|
||||
|
||||
Device start / stop button. Use this switch button to play or stop audio playback
|
||||
|
||||
<h3>2: Audio sample rate</h3>
|
||||
|
||||
Audio sample rate in Hz (Sa/s) with multiplier indicator (k).
|
||||
|
||||
<h3>3: Select audio device</h3>
|
||||
|
||||
Use this push button to open a dialog that lets you choose the audio playback device. See [audio management documentation](../../../sdrgui/audio.md) for details.
|
||||
|
||||
<h3>4: Audio device</h3>
|
||||
|
||||
The name of the audio device in use.
|
||||
|
||||
<h3>5: Volume</h3>
|
||||
|
||||
A control to set the output volume. This is not supported by all output audio devices.
|
||||
|
||||
<h3>6: Channel Map</h3>
|
||||
|
||||
This controls how the left and right audio channels map on to the IQ channels.
|
||||
|
||||
* I=L, Q=R - The left audio channel is driven to the I channel. The right audio channel is driven to the Q channel for a complex (analytic signal)input.
|
||||
* I=R, Q=L - The right audio channel is driven to the I channel. The left audio channel is driven to the Q channel for a complex (analytic signal)input.
|
|
@ -25,7 +25,7 @@
|
|||
#define MIN(x, y) ((x) < (y) ? (x) : (y))
|
||||
|
||||
AudioFifo::AudioFifo() :
|
||||
m_fifo(0),
|
||||
m_fifo(nullptr),
|
||||
m_sampleSize(sizeof(AudioSample))
|
||||
{
|
||||
m_size = 0;
|
||||
|
@ -35,7 +35,7 @@ AudioFifo::AudioFifo() :
|
|||
}
|
||||
|
||||
AudioFifo::AudioFifo(uint32_t numSamples) :
|
||||
m_fifo(0),
|
||||
m_fifo(nullptr),
|
||||
m_sampleSize(sizeof(AudioSample))
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
|
@ -47,10 +47,10 @@ AudioFifo::~AudioFifo()
|
|||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
|
||||
if (m_fifo != 0)
|
||||
if (m_fifo)
|
||||
{
|
||||
delete[] m_fifo;
|
||||
m_fifo = 0;
|
||||
m_fifo = nullptr;
|
||||
}
|
||||
|
||||
m_size = 0;
|
||||
|
@ -69,7 +69,7 @@ uint AudioFifo::write(const quint8* data, uint32_t numSamples)
|
|||
uint32_t remaining;
|
||||
uint32_t copyLen;
|
||||
|
||||
if (m_fifo == 0) {
|
||||
if (!m_fifo) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -83,6 +83,11 @@ uint AudioFifo::write(const quint8* data, uint32_t numSamples)
|
|||
if (isFull())
|
||||
{
|
||||
m_mutex.unlock();
|
||||
|
||||
if (total - remaining > 0) {
|
||||
emit dataReady();
|
||||
}
|
||||
|
||||
return total - remaining; // written so far
|
||||
}
|
||||
|
||||
|
@ -107,7 +112,7 @@ uint AudioFifo::read(quint8* data, uint32_t numSamples)
|
|||
uint32_t remaining;
|
||||
uint32_t copyLen;
|
||||
|
||||
if (m_fifo == 0) {
|
||||
if (!m_fifo) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -165,10 +170,10 @@ void AudioFifo::clear()
|
|||
|
||||
bool AudioFifo::create(uint32_t numSamples)
|
||||
{
|
||||
if(m_fifo != 0)
|
||||
if (m_fifo)
|
||||
{
|
||||
delete[] m_fifo;
|
||||
m_fifo = 0;
|
||||
m_fifo = nullptr;
|
||||
}
|
||||
|
||||
m_fill = 0;
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
<file>webapi/doc/swagger/include/ATVDemod.yaml</file>
|
||||
<file>webapi/doc/swagger/include/ATVMod.yaml</file>
|
||||
<file>webapi/doc/swagger/include/AudioInput.yaml</file>
|
||||
<file>webapi/doc/swagger/include/AudioOutput.yaml</file>
|
||||
<file>webapi/doc/swagger/include/BeamSteeringCWMod.yaml</file>
|
||||
<file>webapi/doc/swagger/include/BFMDemod.yaml</file>
|
||||
<file>webapi/doc/swagger/include/BladeRF1.yaml</file>
|
||||
|
|
|
@ -1665,6 +1665,36 @@ margin-bottom: 20px;
|
|||
}
|
||||
},
|
||||
"description" : "Audio output device"
|
||||
};
|
||||
defs.AudioOutputSettings = {
|
||||
"properties" : {
|
||||
"deviceName" : {
|
||||
"type" : "string",
|
||||
"description" : "The name of the audio device"
|
||||
},
|
||||
"volume" : {
|
||||
"type" : "number",
|
||||
"format" : "float"
|
||||
},
|
||||
"iqMapping" : {
|
||||
"type" : "integer",
|
||||
"description" : "Audio channel to IQ mapping\n * 0 - I=L, Q=R\n * 1 - 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" : "AudioOutput"
|
||||
};
|
||||
defs.BFMDemodReport = {
|
||||
"properties" : {
|
||||
|
@ -3600,6 +3630,9 @@ margin-bottom: 20px;
|
|||
"audioInputSettings" : {
|
||||
"$ref" : "#/definitions/AudioInputSettings"
|
||||
},
|
||||
"audioOutputSettings" : {
|
||||
"$ref" : "#/definitions/AudioOutputSettings"
|
||||
},
|
||||
"bladeRF1InputSettings" : {
|
||||
"$ref" : "#/definitions/BladeRF1InputSettings"
|
||||
},
|
||||
|
@ -44596,7 +44629,7 @@ except ApiException as e:
|
|||
</div>
|
||||
<div id="generator">
|
||||
<div class="content">
|
||||
Generated 2020-11-11T13:32:52.276+01:00
|
||||
Generated 2020-11-21T10:29:19.215+01:00
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
AudioOutputSettings:
|
||||
description: AudioOutput
|
||||
properties:
|
||||
deviceName:
|
||||
description: The name of the audio device
|
||||
type: string
|
||||
volume:
|
||||
type: number
|
||||
format: float
|
||||
iqMapping:
|
||||
type: integer
|
||||
description: >
|
||||
Audio channel to IQ mapping
|
||||
* 0 - I=L, Q=R
|
||||
* 1 - 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
|
|
@ -20,6 +20,8 @@ DeviceSettings:
|
|||
$ref: "/doc/swagger/include/AirspyHF.yaml#/AirspyHFSettings"
|
||||
audioInputSettings:
|
||||
$ref: "/doc/swagger/include/AudioInput.yaml#/AudioInputSettings"
|
||||
audioOutputSettings:
|
||||
$ref: "/doc/swagger/include/AudioOutput.yaml#/AudioOutputSettings"
|
||||
bladeRF1InputSettings:
|
||||
$ref: "/doc/swagger/include/BladeRF1.yaml#/BladeRF1InputSettings"
|
||||
bladeRF2InputSettings:
|
||||
|
|
|
@ -4096,6 +4096,11 @@ bool WebAPIRequestMapper::getDeviceSettings(
|
|||
deviceSettings->setAudioInputSettings(new SWGSDRangel::SWGAudioInputSettings());
|
||||
deviceSettings->getAudioInputSettings()->fromJsonObject(settingsJsonObject);
|
||||
}
|
||||
else if (deviceSettingsKey == "audioOutputSettings")
|
||||
{
|
||||
deviceSettings->setAudioOutputSettings(new SWGSDRangel::SWGAudioOutputSettings());
|
||||
deviceSettings->getAudioOutputSettings()->fromJsonObject(settingsJsonObject);
|
||||
}
|
||||
else if (deviceSettingsKey == "bladeRF1InputSettings")
|
||||
{
|
||||
deviceSettings->setBladeRf1InputSettings(new SWGSDRangel::SWGBladeRF1InputSettings());
|
||||
|
|
|
@ -68,7 +68,8 @@ 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.audioinput", "audioInputSettings"},
|
||||
{"sdrangel.samplesink.audiooutput", "audioOutputSettings"},
|
||||
{"sdrangel.samplesource.bladerf1input", "bladeRF1InputSettings"},
|
||||
{"sdrangel.samplesource.bladerf", "bladeRF1InputSettings"}, // remap
|
||||
{"sdrangel.samplesink.bladerf1output", "bladeRF1OutputSettings"},
|
||||
|
@ -204,6 +205,7 @@ const QMap<QString, QString> WebAPIUtils::m_sourceDeviceHwIdToActionsKey = {
|
|||
};
|
||||
|
||||
const QMap<QString, QString> WebAPIUtils::m_sinkDeviceHwIdToSettingsKey = {
|
||||
{"AudioOutput", "AudioOutputSettings"},
|
||||
{"BladeRF1", "bladeRF1OutputSettings"},
|
||||
{"BladeRF2", "bladeRF2OutputSettings"},
|
||||
{"HackRF", "hackRFOutputSettings"},
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
AudioOutputSettings:
|
||||
description: AudioOutput
|
||||
properties:
|
||||
deviceName:
|
||||
description: The name of the audio device
|
||||
type: string
|
||||
volume:
|
||||
type: number
|
||||
format: float
|
||||
iqMapping:
|
||||
type: integer
|
||||
description: >
|
||||
Audio channel to IQ mapping
|
||||
* 0 - I=L, Q=R
|
||||
* 1 - 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
|
|
@ -20,6 +20,8 @@ DeviceSettings:
|
|||
$ref: "http://swgserver:8081/api/swagger/include/AirspyHF.yaml#/AirspyHFSettings"
|
||||
audioInputSettings:
|
||||
$ref: "http://swgserver:8081/api/swagger/include/AudioInput.yaml#/AudioInputSettings"
|
||||
audioOutputSettings:
|
||||
$ref: "http://swgserver:8081/api/swagger/include/AudioOutput.yaml#/AudioOutputSettings"
|
||||
bladeRF1InputSettings:
|
||||
$ref: "http://swgserver:8081/api/swagger/include/BladeRF1.yaml#/BladeRF1InputSettings"
|
||||
bladeRF2InputSettings:
|
||||
|
|
|
@ -1665,6 +1665,36 @@ margin-bottom: 20px;
|
|||
}
|
||||
},
|
||||
"description" : "Audio output device"
|
||||
};
|
||||
defs.AudioOutputSettings = {
|
||||
"properties" : {
|
||||
"deviceName" : {
|
||||
"type" : "string",
|
||||
"description" : "The name of the audio device"
|
||||
},
|
||||
"volume" : {
|
||||
"type" : "number",
|
||||
"format" : "float"
|
||||
},
|
||||
"iqMapping" : {
|
||||
"type" : "integer",
|
||||
"description" : "Audio channel to IQ mapping\n * 0 - I=L, Q=R\n * 1 - 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" : "AudioOutput"
|
||||
};
|
||||
defs.BFMDemodReport = {
|
||||
"properties" : {
|
||||
|
@ -3600,6 +3630,9 @@ margin-bottom: 20px;
|
|||
"audioInputSettings" : {
|
||||
"$ref" : "#/definitions/AudioInputSettings"
|
||||
},
|
||||
"audioOutputSettings" : {
|
||||
"$ref" : "#/definitions/AudioOutputSettings"
|
||||
},
|
||||
"bladeRF1InputSettings" : {
|
||||
"$ref" : "#/definitions/BladeRF1InputSettings"
|
||||
},
|
||||
|
@ -44596,7 +44629,7 @@ except ApiException as e:
|
|||
</div>
|
||||
<div id="generator">
|
||||
<div class="content">
|
||||
Generated 2020-11-11T13:32:52.276+01:00
|
||||
Generated 2020-11-21T10:29:19.215+01:00
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,250 @@
|
|||
/**
|
||||
* SDRangel
|
||||
* This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time ---
|
||||
*
|
||||
* OpenAPI spec version: 6.0.0
|
||||
* Contact: f4exb06@gmail.com
|
||||
*
|
||||
* NOTE: This class is auto generated by the swagger code generator program.
|
||||
* https://github.com/swagger-api/swagger-codegen.git
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
#include "SWGAudioOutputSettings.h"
|
||||
|
||||
#include "SWGHelpers.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonArray>
|
||||
#include <QObject>
|
||||
#include <QDebug>
|
||||
|
||||
namespace SWGSDRangel {
|
||||
|
||||
SWGAudioOutputSettings::SWGAudioOutputSettings(QString* json) {
|
||||
init();
|
||||
this->fromJson(*json);
|
||||
}
|
||||
|
||||
SWGAudioOutputSettings::SWGAudioOutputSettings() {
|
||||
device_name = nullptr;
|
||||
m_device_name_isSet = false;
|
||||
volume = 0.0f;
|
||||
m_volume_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;
|
||||
}
|
||||
|
||||
SWGAudioOutputSettings::~SWGAudioOutputSettings() {
|
||||
this->cleanup();
|
||||
}
|
||||
|
||||
void
|
||||
SWGAudioOutputSettings::init() {
|
||||
device_name = new QString("");
|
||||
m_device_name_isSet = false;
|
||||
volume = 0.0f;
|
||||
m_volume_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
|
||||
SWGAudioOutputSettings::cleanup() {
|
||||
if(device_name != nullptr) {
|
||||
delete device_name;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if(reverse_api_address != nullptr) {
|
||||
delete reverse_api_address;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
SWGAudioOutputSettings*
|
||||
SWGAudioOutputSettings::fromJson(QString &json) {
|
||||
QByteArray array (json.toStdString().c_str());
|
||||
QJsonDocument doc = QJsonDocument::fromJson(array);
|
||||
QJsonObject jsonObject = doc.object();
|
||||
this->fromJsonObject(jsonObject);
|
||||
return this;
|
||||
}
|
||||
|
||||
void
|
||||
SWGAudioOutputSettings::fromJsonObject(QJsonObject &pJson) {
|
||||
::SWGSDRangel::setValue(&device_name, pJson["deviceName"], "QString", "QString");
|
||||
|
||||
::SWGSDRangel::setValue(&volume, pJson["volume"], "float", "");
|
||||
|
||||
::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
|
||||
SWGAudioOutputSettings::asJson ()
|
||||
{
|
||||
QJsonObject* obj = this->asJsonObject();
|
||||
|
||||
QJsonDocument doc(*obj);
|
||||
QByteArray bytes = doc.toJson();
|
||||
delete obj;
|
||||
return QString(bytes);
|
||||
}
|
||||
|
||||
QJsonObject*
|
||||
SWGAudioOutputSettings::asJsonObject() {
|
||||
QJsonObject* obj = new QJsonObject();
|
||||
if(device_name != nullptr && *device_name != QString("")){
|
||||
toJsonValue(QString("deviceName"), device_name, obj, QString("QString"));
|
||||
}
|
||||
if(m_volume_isSet){
|
||||
obj->insert("volume", QJsonValue(volume));
|
||||
}
|
||||
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*
|
||||
SWGAudioOutputSettings::getDeviceName() {
|
||||
return device_name;
|
||||
}
|
||||
void
|
||||
SWGAudioOutputSettings::setDeviceName(QString* device_name) {
|
||||
this->device_name = device_name;
|
||||
this->m_device_name_isSet = true;
|
||||
}
|
||||
|
||||
float
|
||||
SWGAudioOutputSettings::getVolume() {
|
||||
return volume;
|
||||
}
|
||||
void
|
||||
SWGAudioOutputSettings::setVolume(float volume) {
|
||||
this->volume = volume;
|
||||
this->m_volume_isSet = true;
|
||||
}
|
||||
|
||||
qint32
|
||||
SWGAudioOutputSettings::getIqMapping() {
|
||||
return iq_mapping;
|
||||
}
|
||||
void
|
||||
SWGAudioOutputSettings::setIqMapping(qint32 iq_mapping) {
|
||||
this->iq_mapping = iq_mapping;
|
||||
this->m_iq_mapping_isSet = true;
|
||||
}
|
||||
|
||||
qint32
|
||||
SWGAudioOutputSettings::getUseReverseApi() {
|
||||
return use_reverse_api;
|
||||
}
|
||||
void
|
||||
SWGAudioOutputSettings::setUseReverseApi(qint32 use_reverse_api) {
|
||||
this->use_reverse_api = use_reverse_api;
|
||||
this->m_use_reverse_api_isSet = true;
|
||||
}
|
||||
|
||||
QString*
|
||||
SWGAudioOutputSettings::getReverseApiAddress() {
|
||||
return reverse_api_address;
|
||||
}
|
||||
void
|
||||
SWGAudioOutputSettings::setReverseApiAddress(QString* reverse_api_address) {
|
||||
this->reverse_api_address = reverse_api_address;
|
||||
this->m_reverse_api_address_isSet = true;
|
||||
}
|
||||
|
||||
qint32
|
||||
SWGAudioOutputSettings::getReverseApiPort() {
|
||||
return reverse_api_port;
|
||||
}
|
||||
void
|
||||
SWGAudioOutputSettings::setReverseApiPort(qint32 reverse_api_port) {
|
||||
this->reverse_api_port = reverse_api_port;
|
||||
this->m_reverse_api_port_isSet = true;
|
||||
}
|
||||
|
||||
qint32
|
||||
SWGAudioOutputSettings::getReverseApiDeviceIndex() {
|
||||
return reverse_api_device_index;
|
||||
}
|
||||
void
|
||||
SWGAudioOutputSettings::setReverseApiDeviceIndex(qint32 reverse_api_device_index) {
|
||||
this->reverse_api_device_index = reverse_api_device_index;
|
||||
this->m_reverse_api_device_index_isSet = true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
SWGAudioOutputSettings::isSet(){
|
||||
bool isObjectUpdated = false;
|
||||
do{
|
||||
if(device_name && *device_name != QString("")){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(m_volume_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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
/**
|
||||
* SDRangel
|
||||
* This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time ---
|
||||
*
|
||||
* OpenAPI spec version: 6.0.0
|
||||
* Contact: f4exb06@gmail.com
|
||||
*
|
||||
* NOTE: This class is auto generated by the swagger code generator program.
|
||||
* https://github.com/swagger-api/swagger-codegen.git
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
/*
|
||||
* SWGAudioOutputSettings.h
|
||||
*
|
||||
* AudioOutput
|
||||
*/
|
||||
|
||||
#ifndef SWGAudioOutputSettings_H_
|
||||
#define SWGAudioOutputSettings_H_
|
||||
|
||||
#include <QJsonObject>
|
||||
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include "SWGObject.h"
|
||||
#include "export.h"
|
||||
|
||||
namespace SWGSDRangel {
|
||||
|
||||
class SWG_API SWGAudioOutputSettings: public SWGObject {
|
||||
public:
|
||||
SWGAudioOutputSettings();
|
||||
SWGAudioOutputSettings(QString* json);
|
||||
virtual ~SWGAudioOutputSettings();
|
||||
void init();
|
||||
void cleanup();
|
||||
|
||||
virtual QString asJson () override;
|
||||
virtual QJsonObject* asJsonObject() override;
|
||||
virtual void fromJsonObject(QJsonObject &json) override;
|
||||
virtual SWGAudioOutputSettings* fromJson(QString &jsonString) override;
|
||||
|
||||
QString* getDeviceName();
|
||||
void setDeviceName(QString* device_name);
|
||||
|
||||
float getVolume();
|
||||
void setVolume(float volume);
|
||||
|
||||
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_name;
|
||||
bool m_device_name_isSet;
|
||||
|
||||
float volume;
|
||||
bool m_volume_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 /* SWGAudioOutputSettings_H_ */
|
|
@ -40,6 +40,8 @@ SWGDeviceSettings::SWGDeviceSettings() {
|
|||
m_airspy_hf_settings_isSet = false;
|
||||
audio_input_settings = nullptr;
|
||||
m_audio_input_settings_isSet = false;
|
||||
audio_output_settings = nullptr;
|
||||
m_audio_output_settings_isSet = false;
|
||||
blade_rf1_input_settings = nullptr;
|
||||
m_blade_rf1_input_settings_isSet = false;
|
||||
blade_rf2_input_settings = nullptr;
|
||||
|
@ -130,6 +132,8 @@ SWGDeviceSettings::init() {
|
|||
m_airspy_hf_settings_isSet = false;
|
||||
audio_input_settings = new SWGAudioInputSettings();
|
||||
m_audio_input_settings_isSet = false;
|
||||
audio_output_settings = new SWGAudioOutputSettings();
|
||||
m_audio_output_settings_isSet = false;
|
||||
blade_rf1_input_settings = new SWGBladeRF1InputSettings();
|
||||
m_blade_rf1_input_settings_isSet = false;
|
||||
blade_rf2_input_settings = new SWGBladeRF2InputSettings();
|
||||
|
@ -218,6 +222,9 @@ SWGDeviceSettings::cleanup() {
|
|||
if(audio_input_settings != nullptr) {
|
||||
delete audio_input_settings;
|
||||
}
|
||||
if(audio_output_settings != nullptr) {
|
||||
delete audio_output_settings;
|
||||
}
|
||||
if(blade_rf1_input_settings != nullptr) {
|
||||
delete blade_rf1_input_settings;
|
||||
}
|
||||
|
@ -348,6 +355,8 @@ SWGDeviceSettings::fromJsonObject(QJsonObject &pJson) {
|
|||
|
||||
::SWGSDRangel::setValue(&audio_input_settings, pJson["audioInputSettings"], "SWGAudioInputSettings", "SWGAudioInputSettings");
|
||||
|
||||
::SWGSDRangel::setValue(&audio_output_settings, pJson["audioOutputSettings"], "SWGAudioOutputSettings", "SWGAudioOutputSettings");
|
||||
|
||||
::SWGSDRangel::setValue(&blade_rf1_input_settings, pJson["bladeRF1InputSettings"], "SWGBladeRF1InputSettings", "SWGBladeRF1InputSettings");
|
||||
|
||||
::SWGSDRangel::setValue(&blade_rf2_input_settings, pJson["bladeRF2InputSettings"], "SWGBladeRF2InputSettings", "SWGBladeRF2InputSettings");
|
||||
|
@ -452,6 +461,9 @@ SWGDeviceSettings::asJsonObject() {
|
|||
if((audio_input_settings != nullptr) && (audio_input_settings->isSet())){
|
||||
toJsonValue(QString("audioInputSettings"), audio_input_settings, obj, QString("SWGAudioInputSettings"));
|
||||
}
|
||||
if((audio_output_settings != nullptr) && (audio_output_settings->isSet())){
|
||||
toJsonValue(QString("audioOutputSettings"), audio_output_settings, obj, QString("SWGAudioOutputSettings"));
|
||||
}
|
||||
if((blade_rf1_input_settings != nullptr) && (blade_rf1_input_settings->isSet())){
|
||||
toJsonValue(QString("bladeRF1InputSettings"), blade_rf1_input_settings, obj, QString("SWGBladeRF1InputSettings"));
|
||||
}
|
||||
|
@ -621,6 +633,16 @@ SWGDeviceSettings::setAudioInputSettings(SWGAudioInputSettings* audio_input_sett
|
|||
this->m_audio_input_settings_isSet = true;
|
||||
}
|
||||
|
||||
SWGAudioOutputSettings*
|
||||
SWGDeviceSettings::getAudioOutputSettings() {
|
||||
return audio_output_settings;
|
||||
}
|
||||
void
|
||||
SWGDeviceSettings::setAudioOutputSettings(SWGAudioOutputSettings* audio_output_settings) {
|
||||
this->audio_output_settings = audio_output_settings;
|
||||
this->m_audio_output_settings_isSet = true;
|
||||
}
|
||||
|
||||
SWGBladeRF1InputSettings*
|
||||
SWGDeviceSettings::getBladeRf1InputSettings() {
|
||||
return blade_rf1_input_settings;
|
||||
|
@ -994,6 +1016,9 @@ SWGDeviceSettings::isSet(){
|
|||
if(audio_input_settings && audio_input_settings->isSet()){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(audio_output_settings && audio_output_settings->isSet()){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(blade_rf1_input_settings && blade_rf1_input_settings->isSet()){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "SWGAirspyHFSettings.h"
|
||||
#include "SWGAirspySettings.h"
|
||||
#include "SWGAudioInputSettings.h"
|
||||
#include "SWGAudioOutputSettings.h"
|
||||
#include "SWGBladeRF1InputSettings.h"
|
||||
#include "SWGBladeRF1OutputSettings.h"
|
||||
#include "SWGBladeRF2InputSettings.h"
|
||||
|
@ -98,6 +99,9 @@ public:
|
|||
SWGAudioInputSettings* getAudioInputSettings();
|
||||
void setAudioInputSettings(SWGAudioInputSettings* audio_input_settings);
|
||||
|
||||
SWGAudioOutputSettings* getAudioOutputSettings();
|
||||
void setAudioOutputSettings(SWGAudioOutputSettings* audio_output_settings);
|
||||
|
||||
SWGBladeRF1InputSettings* getBladeRf1InputSettings();
|
||||
void setBladeRf1InputSettings(SWGBladeRF1InputSettings* blade_rf1_input_settings);
|
||||
|
||||
|
@ -225,6 +229,9 @@ private:
|
|||
SWGAudioInputSettings* audio_input_settings;
|
||||
bool m_audio_input_settings_isSet;
|
||||
|
||||
SWGAudioOutputSettings* audio_output_settings;
|
||||
bool m_audio_output_settings_isSet;
|
||||
|
||||
SWGBladeRF1InputSettings* blade_rf1_input_settings;
|
||||
bool m_blade_rf1_input_settings_isSet;
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#include "SWGAudioInputDevice.h"
|
||||
#include "SWGAudioInputSettings.h"
|
||||
#include "SWGAudioOutputDevice.h"
|
||||
#include "SWGAudioOutputSettings.h"
|
||||
#include "SWGBFMDemodReport.h"
|
||||
#include "SWGBFMDemodSettings.h"
|
||||
#include "SWGBandwidth.h"
|
||||
|
@ -306,6 +307,9 @@ namespace SWGSDRangel {
|
|||
if(QString("SWGAudioOutputDevice").compare(type) == 0) {
|
||||
return new SWGAudioOutputDevice();
|
||||
}
|
||||
if(QString("SWGAudioOutputSettings").compare(type) == 0) {
|
||||
return new SWGAudioOutputSettings();
|
||||
}
|
||||
if(QString("SWGBFMDemodReport").compare(type) == 0) {
|
||||
return new SWGBFMDemodReport();
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue