mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-11-25 17:28:50 -05:00
AMBE feature: creation and changes to DSD demod
This commit is contained in:
parent
ee65186057
commit
1d72798d42
@ -39,6 +39,7 @@
|
||||
#include "dsp/dspcommands.h"
|
||||
#include "device/deviceapi.h"
|
||||
#include "feature/feature.h"
|
||||
#include "feature/featureset.h"
|
||||
#include "settings/serializable.h"
|
||||
#include "util/db.h"
|
||||
#include "maincore.h"
|
||||
@ -46,6 +47,8 @@
|
||||
#include "dsddemod.h"
|
||||
|
||||
MESSAGE_CLASS_DEFINITION(DSDDemod::MsgConfigureDSDDemod, Message)
|
||||
MESSAGE_CLASS_DEFINITION(DSDDemod::MsgQueryAvailableAMBEFeatures, Message)
|
||||
MESSAGE_CLASS_DEFINITION(DSDDemod::MsgReportAvailableAMBEFeatures, Message)
|
||||
|
||||
const char* const DSDDemod::m_channelIdURI = "sdrangel.channel.dsddemod";
|
||||
const char* const DSDDemod::m_channelId = "DSDDemod";
|
||||
@ -82,6 +85,20 @@ DSDDemod::DSDDemod(DeviceAPI *deviceAPI) :
|
||||
this,
|
||||
&DSDDemod::handleIndexInDeviceSetChanged
|
||||
);
|
||||
QObject::connect(
|
||||
MainCore::instance(),
|
||||
&MainCore::featureAdded,
|
||||
this,
|
||||
&DSDDemod::handleFeatureAdded
|
||||
);
|
||||
QObject::connect(
|
||||
MainCore::instance(),
|
||||
&MainCore::featureRemoved,
|
||||
this,
|
||||
&DSDDemod::handleFeatureRemoved
|
||||
);
|
||||
|
||||
scanAvailableAMBEFeatures();
|
||||
}
|
||||
|
||||
DSDDemod::~DSDDemod()
|
||||
@ -176,6 +193,11 @@ bool DSDDemod::handleMessage(const Message& cmd)
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (MsgQueryAvailableAMBEFeatures::match(cmd))
|
||||
{
|
||||
notifyUpdateAMBEFeatures();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
@ -219,6 +241,8 @@ void DSDDemod::applySettings(const DSDDemodSettings& settings, bool force)
|
||||
<< " m_traceStroke: " << settings.m_traceStroke
|
||||
<< " m_traceDecay: " << settings.m_traceDecay
|
||||
<< " m_streamIndex: " << settings.m_streamIndex
|
||||
<< " m_ambeFeatureIndex: " << settings.m_ambeFeatureIndex
|
||||
<< " m_connectAMBE: " << settings.m_connectAMBE
|
||||
<< " force: " << force;
|
||||
|
||||
QList<QString> reverseAPIKeys;
|
||||
@ -280,6 +304,30 @@ void DSDDemod::applySettings(const DSDDemodSettings& settings, bool force)
|
||||
if ((settings.m_audioDeviceName != m_settings.m_audioDeviceName) || force) {
|
||||
reverseAPIKeys.append("audioDeviceName");
|
||||
}
|
||||
if ((settings.m_ambeFeatureIndex != m_settings.m_ambeFeatureIndex) || force) {
|
||||
reverseAPIKeys.append("ambeFeatureIndex");
|
||||
}
|
||||
if ((settings.m_connectAMBE != m_settings.m_connectAMBE) || force) {
|
||||
reverseAPIKeys.append("connectAMBE");
|
||||
}
|
||||
|
||||
if ((m_settings.m_connectAMBE != settings.m_connectAMBE)
|
||||
|| (m_settings.m_ambeFeatureIndex != settings.m_ambeFeatureIndex) || force)
|
||||
{
|
||||
if (settings.m_connectAMBE)
|
||||
{
|
||||
for (const auto& feature : m_availableAMBEFeatures)
|
||||
{
|
||||
if (feature.m_featureIndex == settings.m_ambeFeatureIndex) {
|
||||
m_basebandSink->setAMBEFeatureMessageQueue(feature.m_feature->getInputMessageQueue());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_basebandSink->setAMBEFeatureMessageQueue(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_settings.m_streamIndex != settings.m_streamIndex)
|
||||
{
|
||||
@ -339,6 +387,32 @@ bool DSDDemod::deserialize(const QByteArray& data)
|
||||
}
|
||||
}
|
||||
|
||||
void DSDDemod::scanAvailableAMBEFeatures()
|
||||
{
|
||||
MainCore *mainCore = MainCore::instance();
|
||||
int nbFeatures = mainCore->getFeatureeSets()[0]->getNumberOfFeatures();
|
||||
m_availableAMBEFeatures.clear();
|
||||
|
||||
for (int i = 0; i < nbFeatures; i++)
|
||||
{
|
||||
Feature *feature = mainCore->getFeatureeSets()[0]->getFeatureAt(i);
|
||||
|
||||
if (feature->getURI() == "sdrangel.feature.ambe") {
|
||||
m_availableAMBEFeatures[feature] = DSDDemodSettings::AvailableAMBEFeature{i, feature};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DSDDemod::notifyUpdateAMBEFeatures()
|
||||
{
|
||||
if (getMessageQueueToGUI())
|
||||
{
|
||||
MsgReportAvailableAMBEFeatures *msg = MsgReportAvailableAMBEFeatures::create();
|
||||
msg->getFeatures() = m_availableAMBEFeatures.values();
|
||||
getMessageQueueToGUI()->push(msg);
|
||||
}
|
||||
}
|
||||
|
||||
void DSDDemod::sendSampleRateToDemodAnalyzer()
|
||||
{
|
||||
QList<ObjectPipe*> pipes;
|
||||
@ -794,3 +868,40 @@ void DSDDemod::handleIndexInDeviceSetChanged(int index)
|
||||
m_basebandSink->setFifoLabel(fifoLabel);
|
||||
m_basebandSink->setAudioFifoLabel(fifoLabel);
|
||||
}
|
||||
|
||||
void DSDDemod::handleFeatureAdded(int featureSetIndex, Feature *feature)
|
||||
{
|
||||
if (featureSetIndex != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((feature->getURI() == "sdrangel.feature.ambe") && !m_availableAMBEFeatures.contains(feature))
|
||||
{
|
||||
m_availableAMBEFeatures[feature] = DSDDemodSettings::AvailableAMBEFeature{feature->getIndexInFeatureSet(), feature};
|
||||
|
||||
if (m_settings.m_connectAMBE && (m_settings.m_ambeFeatureIndex == feature->getIndexInFeatureSet())) {
|
||||
m_basebandSink->setAMBEFeatureMessageQueue(feature->getInputMessageQueue());
|
||||
}
|
||||
|
||||
notifyUpdateAMBEFeatures();
|
||||
}
|
||||
}
|
||||
|
||||
void DSDDemod::handleFeatureRemoved(int featureSetIndex, Feature *feature)
|
||||
{
|
||||
if (featureSetIndex != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_availableAMBEFeatures.contains(feature))
|
||||
{
|
||||
if (m_settings.m_ambeFeatureIndex == m_availableAMBEFeatures[feature].m_featureIndex)
|
||||
{
|
||||
m_settings.m_connectAMBE = false;
|
||||
m_basebandSink->setAMBEFeatureMessageQueue(nullptr);
|
||||
}
|
||||
|
||||
m_availableAMBEFeatures.remove(feature);
|
||||
notifyUpdateAMBEFeatures();
|
||||
}
|
||||
}
|
||||
|
@ -62,6 +62,38 @@ public:
|
||||
{ }
|
||||
};
|
||||
|
||||
class MsgQueryAvailableAMBEFeatures : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
static MsgQueryAvailableAMBEFeatures* create() {
|
||||
return new MsgQueryAvailableAMBEFeatures();
|
||||
}
|
||||
|
||||
protected:
|
||||
MsgQueryAvailableAMBEFeatures() :
|
||||
Message()
|
||||
{ }
|
||||
};
|
||||
|
||||
class MsgReportAvailableAMBEFeatures : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
QList<DSDDemodSettings::AvailableAMBEFeature>& getFeatures() { return m_availableFeatures; }
|
||||
|
||||
static MsgReportAvailableAMBEFeatures* create() {
|
||||
return new MsgReportAvailableAMBEFeatures();
|
||||
}
|
||||
|
||||
private:
|
||||
QList<DSDDemodSettings::AvailableAMBEFeature> m_availableFeatures;
|
||||
|
||||
MsgReportAvailableAMBEFeatures() :
|
||||
Message()
|
||||
{}
|
||||
};
|
||||
|
||||
DSDDemod(DeviceAPI *deviceAPI);
|
||||
virtual ~DSDDemod();
|
||||
virtual void destroy() { delete this; }
|
||||
@ -140,6 +172,7 @@ private:
|
||||
DSDDemodBaseband *m_basebandSink;
|
||||
DSDDemodSettings m_settings;
|
||||
int m_basebandSampleRate; //!< stored from device message used when starting baseband sink
|
||||
QHash<Feature*, DSDDemodSettings::AvailableAMBEFeature> m_availableAMBEFeatures;
|
||||
|
||||
QNetworkAccessManager *m_networkManager;
|
||||
QNetworkRequest m_networkRequest;
|
||||
@ -148,6 +181,8 @@ private:
|
||||
|
||||
virtual bool handleMessage(const Message& cmd);
|
||||
void applySettings(const DSDDemodSettings& settings, bool force = false);
|
||||
void scanAvailableAMBEFeatures();
|
||||
void notifyUpdateAMBEFeatures();
|
||||
void sendSampleRateToDemodAnalyzer();
|
||||
void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response);
|
||||
void webapiReverseSendSettings(QList<QString>& channelSettingsKeys, const DSDDemodSettings& settings, bool force);
|
||||
@ -167,6 +202,8 @@ private:
|
||||
private slots:
|
||||
void networkManagerFinished(QNetworkReply *reply);
|
||||
void handleIndexInDeviceSetChanged(int index);
|
||||
void handleFeatureAdded(int featureSetIndex, Feature *feature);
|
||||
void handleFeatureRemoved(int featureSetIndex, Feature *feature);
|
||||
};
|
||||
|
||||
#endif // INCLUDE_DSDDEMOD_H
|
||||
|
@ -75,6 +75,7 @@ public:
|
||||
void setChannel(ChannelAPI *channel);
|
||||
void setFifoLabel(const QString& label) { m_sampleFifo.setLabel(label); }
|
||||
void setAudioFifoLabel(const QString& label) { m_sink.setAudioFifoLabel(label); }
|
||||
void setAMBEFeatureMessageQueue(MessageQueue *ambeFeatureMessageQueue) { m_sink.setAmbeFeatureMessageQueue(ambeFeatureMessageQueue); }
|
||||
|
||||
private:
|
||||
SampleSinkFifo m_sampleFifo;
|
||||
|
@ -113,6 +113,13 @@ bool DSDDemodGUI::handleMessage(const Message& message)
|
||||
updateAbsoluteCenterFrequency();
|
||||
return true;
|
||||
}
|
||||
else if (DSDDemod::MsgReportAvailableAMBEFeatures::match(message))
|
||||
{
|
||||
DSDDemod::MsgReportAvailableAMBEFeatures& report = (DSDDemod::MsgReportAvailableAMBEFeatures&) message;
|
||||
m_availableAMBEFeatures = report.getFeatures();
|
||||
updateAMBEFeaturesList();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
@ -263,6 +270,19 @@ void DSDDemodGUI::on_symbolPLLLock_toggled(bool checked)
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void DSDDemodGUI::on_ambeSupport_clicked(bool checked)
|
||||
{
|
||||
m_settings.m_connectAMBE = checked;
|
||||
m_settings.m_ambeFeatureIndex = m_availableAMBEFeatures[ui->ambeFeatures->currentIndex()].m_featureIndex;
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void DSDDemodGUI::on_ambeFeatures_currentIndexChanged(int index)
|
||||
{
|
||||
m_settings.m_ambeFeatureIndex = m_availableAMBEFeatures[index].m_featureIndex;
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void DSDDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown)
|
||||
{
|
||||
(void) widget;
|
||||
@ -490,6 +510,17 @@ void DSDDemodGUI::displaySettings()
|
||||
ui->traceDecayText->setText(QString("%1").arg(m_settings.m_traceDecay));
|
||||
m_scopeVisXY->setDecay(m_settings.m_traceDecay);
|
||||
|
||||
ui->ambeSupport->setChecked(m_settings.m_connectAMBE);
|
||||
|
||||
for (int i = 0; i < ui->ambeFeatures->count(); i++)
|
||||
{
|
||||
if (ui->ambeFeatures->itemData(i).toInt() == m_settings.m_ambeFeatureIndex)
|
||||
{
|
||||
ui->ambeFeatures->setCurrentIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
updateIndexLabel();
|
||||
|
||||
getRollupContents()->restoreState(m_rollupState);
|
||||
@ -497,6 +528,32 @@ void DSDDemodGUI::displaySettings()
|
||||
blockApplySettings(false);
|
||||
}
|
||||
|
||||
void DSDDemodGUI::updateAMBEFeaturesList()
|
||||
{
|
||||
ui->ambeFeatures->blockSignals(true);
|
||||
ui->ambeSupport->blockSignals(true);
|
||||
ui->ambeFeatures->clear();
|
||||
bool unsetAMBE = true;
|
||||
|
||||
for (int i = 0; i < m_availableAMBEFeatures.count(); i++)
|
||||
{
|
||||
ui->ambeFeatures->addItem(tr("F:%1").arg(m_availableAMBEFeatures[i].m_featureIndex), m_availableAMBEFeatures[i].m_featureIndex);
|
||||
|
||||
if (m_settings.m_ambeFeatureIndex == m_availableAMBEFeatures[i].m_featureIndex)
|
||||
{
|
||||
unsetAMBE = false;
|
||||
ui->ambeFeatures->setCurrentIndex(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (unsetAMBE) {
|
||||
ui->ambeSupport->setChecked(false);
|
||||
}
|
||||
|
||||
ui->ambeSupport->blockSignals(false);
|
||||
ui->ambeFeatures->blockSignals(false);
|
||||
}
|
||||
|
||||
void DSDDemodGUI::applySettings(bool force)
|
||||
{
|
||||
if (m_doApplySettings)
|
||||
@ -658,6 +715,8 @@ void DSDDemodGUI::makeUIConnections()
|
||||
QObject::connect(ui->audioMute, &QToolButton::toggled, this, &DSDDemodGUI::on_audioMute_toggled);
|
||||
QObject::connect(ui->symbolPLLLock, &QToolButton::toggled, this, &DSDDemodGUI::on_symbolPLLLock_toggled);
|
||||
QObject::connect(ui->viewStatusLog, &QPushButton::clicked, this, &DSDDemodGUI::on_viewStatusLog_clicked);
|
||||
QObject::connect(ui->ambeSupport, &QCheckBox::clicked, this, &DSDDemodGUI::on_ambeSupport_clicked);
|
||||
QObject::connect(ui->ambeFeatures, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &DSDDemodGUI::on_ambeFeatures_currentIndexChanged);
|
||||
}
|
||||
|
||||
void DSDDemodGUI::updateAbsoluteCenterFrequency()
|
||||
|
@ -90,6 +90,7 @@ private:
|
||||
qint64 m_deviceCenterFrequency;
|
||||
int m_basebandSampleRate;
|
||||
bool m_doApplySettings;
|
||||
QList<DSDDemodSettings::AvailableAMBEFeature> m_availableAMBEFeatures;
|
||||
|
||||
ScopeVisXY* m_scopeVisXY;
|
||||
|
||||
@ -117,6 +118,7 @@ private:
|
||||
void blockApplySettings(bool block);
|
||||
void applySettings(bool force = false);
|
||||
void displaySettings();
|
||||
void updateAMBEFeaturesList();
|
||||
void updateMyPosition();
|
||||
bool handleMessage(const Message& message);
|
||||
void makeUIConnections();
|
||||
@ -145,6 +147,8 @@ private slots:
|
||||
void on_highPassFilter_toggled(bool checked);
|
||||
void on_audioMute_toggled(bool checked);
|
||||
void on_symbolPLLLock_toggled(bool checked);
|
||||
void on_ambeSupport_clicked(bool checked);
|
||||
void on_ambeFeatures_currentIndexChanged(int index);
|
||||
void onWidgetRolled(QWidget* widget, bool rollDown);
|
||||
void onMenuDialogCalled(const QPoint& p);
|
||||
void on_viewStatusLog_clicked();
|
||||
|
@ -1231,6 +1231,29 @@
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QCheckBox" name="ambeSupport">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>170</y>
|
||||
<width>71</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>AMBE</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QComboBox" name="ambeFeatures">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>80</x>
|
||||
<y>170</y>
|
||||
<width>50</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
@ -30,7 +30,7 @@
|
||||
const PluginDescriptor DSDDemodPlugin::m_pluginDescriptor = {
|
||||
DSDDemod::m_channelId,
|
||||
QStringLiteral("DSD Demodulator"),
|
||||
QStringLiteral("7.0.0"),
|
||||
QStringLiteral("7.2.0"),
|
||||
QStringLiteral("(c) Edouard Griffiths, F4EXB"),
|
||||
QStringLiteral("https://github.com/f4exb/sdrangel"),
|
||||
true,
|
||||
|
@ -61,6 +61,8 @@ void DSDDemodSettings::resetToDefaults()
|
||||
m_reverseAPIChannelIndex = 0;
|
||||
m_workspaceIndex = 0;
|
||||
m_hidden = false;
|
||||
m_ambeFeatureIndex = -1;
|
||||
m_connectAMBE = false;
|
||||
}
|
||||
|
||||
QByteArray DSDDemodSettings::serialize() const
|
||||
@ -107,6 +109,8 @@ QByteArray DSDDemodSettings::serialize() const
|
||||
s.writeS32(32, m_workspaceIndex);
|
||||
s.writeBlob(33, m_geometryBytes);
|
||||
s.writeBool(34, m_hidden);
|
||||
s.writeS32(35, m_ambeFeatureIndex);
|
||||
s.writeBool(36, m_connectAMBE);
|
||||
|
||||
return s.final();
|
||||
}
|
||||
@ -190,6 +194,8 @@ bool DSDDemodSettings::deserialize(const QByteArray& data)
|
||||
d.readS32(32, &m_workspaceIndex, 0);
|
||||
d.readBlob(33, &m_geometryBytes);
|
||||
d.readBool(34, &m_hidden, false);
|
||||
d.readS32(35, &m_ambeFeatureIndex, -1);
|
||||
d.readBool(36, &m_connectAMBE, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -21,9 +21,20 @@
|
||||
#include <QByteArray>
|
||||
|
||||
class Serializable;
|
||||
class Feature;
|
||||
|
||||
struct DSDDemodSettings
|
||||
{
|
||||
struct AvailableAMBEFeature
|
||||
{
|
||||
int m_featureIndex;
|
||||
Feature *m_feature;
|
||||
|
||||
AvailableAMBEFeature() = default;
|
||||
AvailableAMBEFeature(const AvailableAMBEFeature&) = default;
|
||||
AvailableAMBEFeature& operator=(const AvailableAMBEFeature&) = default;
|
||||
};
|
||||
|
||||
qint64 m_inputFrequencyOffset;
|
||||
Real m_rfBandwidth;
|
||||
Real m_fmDeviation;
|
||||
@ -55,6 +66,8 @@ struct DSDDemodSettings
|
||||
int m_workspaceIndex;
|
||||
QByteArray m_geometryBytes;
|
||||
bool m_hidden;
|
||||
int m_ambeFeatureIndex;
|
||||
bool m_connectAMBE;
|
||||
|
||||
Serializable *m_channelMarker;
|
||||
Serializable *m_rollupState;
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include "dsp/dspengine.h"
|
||||
#include "dsp/basebandsamplesink.h"
|
||||
#include "dsp/datafifo.h"
|
||||
#include "dsp/dspcommands.h"
|
||||
#include "audio/audiooutputdevice.h"
|
||||
#include "util/db.h"
|
||||
#include "util/messagequeue.h"
|
||||
@ -44,6 +45,7 @@
|
||||
DSDDemodSink::DSDDemodSink() :
|
||||
m_channelSampleRate(48000),
|
||||
m_channelFrequencyOffset(0),
|
||||
m_ambeFeatureMessageQueue(nullptr),
|
||||
m_audioSampleRate(48000),
|
||||
m_interpolatorDistance(0.0f),
|
||||
m_interpolatorDistanceRemain(0.0f),
|
||||
@ -229,20 +231,32 @@ void DSDDemodSink::feed(const SampleVector::const_iterator& begin, const SampleV
|
||||
m_scopeSampleBuffer.push_back(s);
|
||||
}
|
||||
|
||||
if (DSPEngine::instance()->hasDVSerialSupport())
|
||||
// if (DSPEngine::instance()->hasDVSerialSupport())
|
||||
if (m_ambeFeatureMessageQueue)
|
||||
{
|
||||
if ((m_settings.m_slot1On) && m_dsdDecoder.mbeDVReady1())
|
||||
{
|
||||
if (!m_settings.m_audioMute)
|
||||
{
|
||||
DSPEngine::instance()->pushMbeFrame(
|
||||
m_ambeFeatureMessageQueue->push(
|
||||
new DSPPushMbeFrame(
|
||||
m_dsdDecoder.getMbeDVFrame1(),
|
||||
m_dsdDecoder.getMbeRateIndex(),
|
||||
m_settings.m_volume * 10.0,
|
||||
m_settings.m_tdmaStereo ? 1 : 3, // left or both channels
|
||||
m_settings.m_highPassFilter,
|
||||
m_audioSampleRate/8000, // upsample from native 8k
|
||||
&m_audioFifo1);
|
||||
&m_audioFifo1
|
||||
)
|
||||
);
|
||||
// DSPEngine::instance()->pushMbeFrame(
|
||||
// m_dsdDecoder.getMbeDVFrame1(),
|
||||
// m_dsdDecoder.getMbeRateIndex(),
|
||||
// m_settings.m_volume * 10.0,
|
||||
// m_settings.m_tdmaStereo ? 1 : 3, // left or both channels
|
||||
// m_settings.m_highPassFilter,
|
||||
// m_audioSampleRate/8000, // upsample from native 8k
|
||||
// &m_audioFifo1);
|
||||
}
|
||||
|
||||
m_dsdDecoder.resetMbeDV1();
|
||||
@ -252,14 +266,25 @@ void DSDDemodSink::feed(const SampleVector::const_iterator& begin, const SampleV
|
||||
{
|
||||
if (!m_settings.m_audioMute)
|
||||
{
|
||||
DSPEngine::instance()->pushMbeFrame(
|
||||
m_ambeFeatureMessageQueue->push(
|
||||
new DSPPushMbeFrame(
|
||||
m_dsdDecoder.getMbeDVFrame2(),
|
||||
m_dsdDecoder.getMbeRateIndex(),
|
||||
m_settings.m_volume * 10.0,
|
||||
m_settings.m_tdmaStereo ? 2 : 3, // right or both channels
|
||||
m_settings.m_highPassFilter,
|
||||
m_audioSampleRate/8000, // upsample from native 8k
|
||||
&m_audioFifo2);
|
||||
&m_audioFifo2
|
||||
)
|
||||
);
|
||||
// DSPEngine::instance()->pushMbeFrame(
|
||||
// m_dsdDecoder.getMbeDVFrame2(),
|
||||
// m_dsdDecoder.getMbeRateIndex(),
|
||||
// m_settings.m_volume * 10.0,
|
||||
// m_settings.m_tdmaStereo ? 2 : 3, // right or both channels
|
||||
// m_settings.m_highPassFilter,
|
||||
// m_audioSampleRate/8000, // upsample from native 8k
|
||||
// &m_audioFifo2);
|
||||
}
|
||||
|
||||
m_dsdDecoder.resetMbeDV2();
|
||||
@ -270,7 +295,8 @@ void DSDDemodSink::feed(const SampleVector::const_iterator& begin, const SampleV
|
||||
}
|
||||
}
|
||||
|
||||
if (!DSPEngine::instance()->hasDVSerialSupport())
|
||||
if (!m_ambeFeatureMessageQueue)
|
||||
// if (!DSPEngine::instance()->hasDVSerialSupport())
|
||||
{
|
||||
if (m_settings.m_slot1On)
|
||||
{
|
||||
|
@ -83,6 +83,7 @@ public:
|
||||
}
|
||||
|
||||
const char *updateAndGetStatusText();
|
||||
void setAmbeFeatureMessageQueue(MessageQueue *queue) { m_ambeFeatureMessageQueue = queue; }
|
||||
|
||||
private:
|
||||
struct MagSqLevelsStore
|
||||
@ -114,6 +115,7 @@ private:
|
||||
int m_channelFrequencyOffset;
|
||||
DSDDemodSettings m_settings;
|
||||
ChannelAPI *m_channel;
|
||||
MessageQueue *m_ambeFeatureMessageQueue;
|
||||
int m_audioSampleRate;
|
||||
QVector<qint16> m_demodBuffer;
|
||||
int m_demodBufferFill;
|
||||
|
@ -39,3 +39,7 @@ endif()
|
||||
if (ENABLE_LIMESUITE AND LIMESUITE_FOUND)
|
||||
add_subdirectory(limerfe)
|
||||
endif()
|
||||
|
||||
if (LIBSERIALDV_FOUND)
|
||||
add_subdirectory(ambe)
|
||||
endif()
|
||||
|
65
plugins/feature/ambe/CMakeLists.txt
Normal file
65
plugins/feature/ambe/CMakeLists.txt
Normal file
@ -0,0 +1,65 @@
|
||||
project(ambe)
|
||||
|
||||
set(ambe_SOURCES
|
||||
ambe.cpp
|
||||
ambesettings.cpp
|
||||
ambeplugin.cpp
|
||||
ambeengine.cpp
|
||||
ambeworker.cpp
|
||||
# ambewebapiadapter.cpp
|
||||
)
|
||||
|
||||
set(ambe_HEADERS
|
||||
ambe.h
|
||||
ambesettings.h
|
||||
ambeplugin.h
|
||||
ambeengine.h
|
||||
ambeworker.h
|
||||
# ambewebapiadapter.h
|
||||
)
|
||||
|
||||
include_directories(
|
||||
${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client
|
||||
${LIBSERIALDV_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
if(NOT SERVER_MODE)
|
||||
set(ambe_SOURCES
|
||||
${ambe_SOURCES}
|
||||
ambegui.cpp
|
||||
ambegui.ui
|
||||
)
|
||||
set(ambe_HEADERS
|
||||
${ambe_HEADERS}
|
||||
ambegui.h
|
||||
)
|
||||
|
||||
set(TARGET_NAME featureambe)
|
||||
set(TARGET_LIB "Qt5::Widgets")
|
||||
set(TARGET_LIB_GUI "sdrgui")
|
||||
set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR})
|
||||
else()
|
||||
set(TARGET_NAME featureambesrv)
|
||||
set(TARGET_LIB "")
|
||||
set(TARGET_LIB_GUI "")
|
||||
set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR})
|
||||
endif()
|
||||
|
||||
add_library(${TARGET_NAME} SHARED
|
||||
${ambe_SOURCES}
|
||||
)
|
||||
|
||||
target_link_libraries(${TARGET_NAME}
|
||||
Qt5::Core
|
||||
${TARGET_LIB}
|
||||
sdrbase
|
||||
${TARGET_LIB_GUI}
|
||||
${LIBSERIALDV_LIBRARY}
|
||||
)
|
||||
|
||||
install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER})
|
||||
|
||||
# Install debug symbols
|
||||
if (WIN32)
|
||||
install(FILES $<TARGET_PDB_FILE:${TARGET_NAME}> CONFIGURATIONS Debug RelWithDebInfo DESTINATION ${INSTALL_FOLDER} )
|
||||
endif()
|
170
plugins/feature/ambe/ambe.cpp
Normal file
170
plugins/feature/ambe/ambe.cpp
Normal file
@ -0,0 +1,170 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2022 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 <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QBuffer>
|
||||
|
||||
#include "SWGFeatureSettings.h"
|
||||
#include "SWGFeatureReport.h"
|
||||
#include "SWGFeatureActions.h"
|
||||
|
||||
#include "settings/serializable.h"
|
||||
#include "util/simpleserializer.h"
|
||||
#include "dsp/dspcommands.h"
|
||||
|
||||
#include "ambe.h"
|
||||
|
||||
MESSAGE_CLASS_DEFINITION(AMBE::MsgConfigureAMBE, Message)
|
||||
|
||||
const char* const AMBE::m_featureIdURI = "sdrangel.feature.ambe";
|
||||
const char* const AMBE::m_featureId = "AMBE";
|
||||
|
||||
AMBE::AMBE(WebAPIAdapterInterface *webAPIAdapterInterface) :
|
||||
Feature(m_featureIdURI, webAPIAdapterInterface)
|
||||
{
|
||||
setObjectName(m_featureId);
|
||||
m_state = StIdle;
|
||||
m_errorMessage = "AMBE error";
|
||||
m_networkManager = new QNetworkAccessManager();
|
||||
QObject::connect(
|
||||
m_networkManager,
|
||||
&QNetworkAccessManager::finished,
|
||||
this,
|
||||
&AMBE::networkManagerFinished
|
||||
);
|
||||
}
|
||||
|
||||
AMBE::~AMBE()
|
||||
{
|
||||
QObject::disconnect(
|
||||
m_networkManager,
|
||||
&QNetworkAccessManager::finished,
|
||||
this,
|
||||
&AMBE::networkManagerFinished
|
||||
);
|
||||
delete m_networkManager;
|
||||
}
|
||||
|
||||
void AMBE::start()
|
||||
{
|
||||
qDebug("AMBE::start");
|
||||
m_state = StRunning;
|
||||
}
|
||||
|
||||
void AMBE::stop()
|
||||
{
|
||||
qDebug("AMBE::stop");
|
||||
m_state = StIdle;
|
||||
}
|
||||
|
||||
void AMBE::applySettings(const AMBESettings& settings, bool force)
|
||||
{
|
||||
(void) force;
|
||||
m_settings = settings;
|
||||
}
|
||||
|
||||
bool AMBE::handleMessage(const Message& cmd)
|
||||
{
|
||||
if (MsgConfigureAMBE::match(cmd))
|
||||
{
|
||||
MsgConfigureAMBE& cfg = (MsgConfigureAMBE&) cmd;
|
||||
qDebug() << "AMBE::handleMessage: MsgConfigureAMBE";
|
||||
applySettings(cfg.getSettings(), cfg.getForce());
|
||||
return true;
|
||||
}
|
||||
else if (DSPPushMbeFrame::match(cmd))
|
||||
{
|
||||
DSPPushMbeFrame& cfg = (DSPPushMbeFrame&) cmd;
|
||||
m_ambeEngine.pushMbeFrame(
|
||||
cfg.getMbeFrame(),
|
||||
cfg.getMbeRateIndex(),
|
||||
cfg.getMbeVolumeIndex(),
|
||||
cfg.getChannels(),
|
||||
cfg.getUseHP(),
|
||||
cfg.getUpsampling(),
|
||||
cfg.getAudioFifo()
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray AMBE::serialize() const
|
||||
{
|
||||
SimpleSerializer s(1);
|
||||
s.writeBlob(1, m_settings.serialize());
|
||||
return s.final();
|
||||
}
|
||||
|
||||
bool AMBE::deserialize(const QByteArray& data)
|
||||
{
|
||||
SimpleDeserializer d(data);
|
||||
|
||||
if (!d.isValid())
|
||||
{
|
||||
m_settings.resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (d.getVersion() == 1)
|
||||
{
|
||||
QByteArray bytetmp;
|
||||
d.readBlob(1, &bytetmp);
|
||||
|
||||
if (m_settings.deserialize(bytetmp))
|
||||
{
|
||||
MsgConfigureAMBE *msg = MsgConfigureAMBE::create(m_settings, true);
|
||||
m_inputMessageQueue.push(msg);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_settings.resetToDefaults();
|
||||
MsgConfigureAMBE *msg = MsgConfigureAMBE::create(m_settings, true);
|
||||
m_inputMessageQueue.push(msg);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void AMBE::networkManagerFinished(QNetworkReply *reply)
|
||||
{
|
||||
QNetworkReply::NetworkError replyError = reply->error();
|
||||
|
||||
if (replyError)
|
||||
{
|
||||
qWarning() << "AMBE::networkManagerFinished:"
|
||||
<< " error(" << (int) replyError
|
||||
<< "): " << replyError
|
||||
<< ": " << reply->errorString();
|
||||
}
|
||||
else
|
||||
{
|
||||
QString answer = reply->readAll();
|
||||
answer.chop(1); // remove last \n
|
||||
qDebug("AMBE::networkManagerFinished: reply:\n%s", answer.toStdString().c_str());
|
||||
}
|
||||
|
||||
reply->deleteLater();
|
||||
}
|
92
plugins/feature/ambe/ambe.h
Normal file
92
plugins/feature/ambe/ambe.h
Normal file
@ -0,0 +1,92 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2022 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_FEATURE_AMBE_H_
|
||||
#define INCLUDE_FEATURE_AMBE_H_
|
||||
|
||||
#include <QNetworkRequest>
|
||||
#include <QSet>
|
||||
|
||||
#include "feature/feature.h"
|
||||
#include "util/message.h"
|
||||
|
||||
#include "ambeengine.h"
|
||||
#include "ambesettings.h"
|
||||
|
||||
class WebAPIAdapterInterface;
|
||||
class QNetworkAccessManager;
|
||||
class QNetworkReply;
|
||||
|
||||
class AMBE : public Feature
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
class MsgConfigureAMBE : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
const AMBESettings& getSettings() const { return m_settings; }
|
||||
bool getForce() const { return m_force; }
|
||||
|
||||
static MsgConfigureAMBE* create(const AMBESettings& settings, bool force) {
|
||||
return new MsgConfigureAMBE(settings, force);
|
||||
}
|
||||
|
||||
private:
|
||||
AMBESettings m_settings;
|
||||
bool m_force;
|
||||
|
||||
MsgConfigureAMBE(const AMBESettings& settings, bool force) :
|
||||
Message(),
|
||||
m_settings(settings),
|
||||
m_force(force)
|
||||
{ }
|
||||
};
|
||||
|
||||
AMBE(WebAPIAdapterInterface *webAPIAdapterInterface);
|
||||
virtual ~AMBE();
|
||||
virtual void destroy() { delete this; }
|
||||
virtual bool handleMessage(const Message& cmd);
|
||||
|
||||
virtual void getIdentifier(QString& id) const { id = objectName(); }
|
||||
virtual QString getIdentifier() const { return objectName(); }
|
||||
virtual void getTitle(QString& title) const { title = m_settings.m_title; }
|
||||
|
||||
virtual QByteArray serialize() const;
|
||||
virtual bool deserialize(const QByteArray& data);
|
||||
|
||||
AMBEEngine *getAMBEEngine() { return &m_ambeEngine; }
|
||||
|
||||
static const char* const m_featureIdURI;
|
||||
static const char* const m_featureId;
|
||||
|
||||
private:
|
||||
AMBESettings m_settings;
|
||||
AMBEEngine m_ambeEngine;
|
||||
|
||||
QNetworkAccessManager *m_networkManager;
|
||||
QNetworkRequest m_networkRequest;
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
void applySettings(const AMBESettings& settings, bool force = false);
|
||||
|
||||
private slots:
|
||||
void networkManagerFinished(QNetworkReply *reply);
|
||||
};
|
||||
|
||||
#endif
|
402
plugins/feature/ambe/ambeengine.cpp
Normal file
402
plugins/feature/ambe/ambeengine.cpp
Normal file
@ -0,0 +1,402 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2019 F4EXB //
|
||||
// written by Edouard Griffiths //
|
||||
// //
|
||||
// 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 __APPLE__
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
#include <libgen.h>
|
||||
#endif
|
||||
|
||||
#if !defined(_WIN32) && !defined(__APPLE__)
|
||||
#include <termios.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/serial.h>
|
||||
#endif
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include <QThread>
|
||||
#include <QBuffer>
|
||||
#include <QDataStream>
|
||||
|
||||
#include "ambeworker.h"
|
||||
#include "ambeengine.h"
|
||||
|
||||
AMBEEngine::AMBEEngine()
|
||||
{}
|
||||
|
||||
AMBEEngine::~AMBEEngine()
|
||||
{
|
||||
qDebug("AMBEEngine::~AMBEEngine: %lu controllers", m_controllers.size());
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
void AMBEEngine::getComList()
|
||||
{
|
||||
m_comList.clear();
|
||||
m_comList8250.clear();
|
||||
char comCStr[16];
|
||||
|
||||
// Arbitrarily set the list to the 20 first COM ports
|
||||
for (int i = 1; i <= 20; i++)
|
||||
{
|
||||
sprintf(comCStr, "COM%d", i);
|
||||
m_comList.push_back(std::string(comCStr));
|
||||
}
|
||||
}
|
||||
|
||||
// Do not activate serial support at all for windows
|
||||
void AMBEEngine::scan(std::vector<QString>& ambeDevices)
|
||||
{
|
||||
(void) ambeDevices;
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
void AMBEEngine::getComList()
|
||||
{
|
||||
}
|
||||
void AMBEEngine::scan(std::vector<QString>& ambeDevices)
|
||||
{
|
||||
(void) ambeDevices;
|
||||
}
|
||||
#else
|
||||
void AMBEEngine::getComList()
|
||||
{
|
||||
int n;
|
||||
struct dirent **namelist;
|
||||
m_comList.clear();
|
||||
m_comList8250.clear();
|
||||
const char* sysdir = "/sys/class/tty/";
|
||||
|
||||
// Scan through /sys/class/tty - it contains all tty-devices in the system
|
||||
n = scandir(sysdir, &namelist, NULL, alphasort);
|
||||
if (n < 0)
|
||||
{
|
||||
perror("scandir");
|
||||
}
|
||||
else
|
||||
{
|
||||
while (n--)
|
||||
{
|
||||
if (strcmp(namelist[n]->d_name, "..") && strcmp(namelist[n]->d_name, "."))
|
||||
{
|
||||
// Construct full absolute file path
|
||||
std::string devicedir = sysdir;
|
||||
devicedir += namelist[n]->d_name;
|
||||
// Register the device
|
||||
register_comport(m_comList, m_comList8250, devicedir);
|
||||
}
|
||||
|
||||
free(namelist[n]);
|
||||
}
|
||||
|
||||
free(namelist);
|
||||
}
|
||||
|
||||
// Only non-serial8250 has been added to comList without any further testing
|
||||
// serial8250-devices must be probe to check for validity
|
||||
probe_serial8250_comports(m_comList, m_comList8250);
|
||||
}
|
||||
#endif // not Windows nor Apple
|
||||
|
||||
#if !defined(_WIN32) && !defined(__APPLE__)
|
||||
void AMBEEngine::register_comport(
|
||||
std::vector<std::string>& comList,
|
||||
std::vector<std::string>& comList8250,
|
||||
const std::string& dir)
|
||||
{
|
||||
// Get the driver the device is using
|
||||
std::string driver = get_driver(dir);
|
||||
|
||||
// Skip devices without a driver
|
||||
if (driver.size() > 0)
|
||||
{
|
||||
//std::cerr << "register_comport: dir: "<< dir << " driver: " << driver << std::endl;
|
||||
std::string devfile = std::string("/dev/") + basename((char *) dir.c_str());
|
||||
|
||||
// Put serial8250-devices in a seperate list
|
||||
if (driver == "serial8250") {
|
||||
comList8250.push_back(devfile);
|
||||
} else {
|
||||
comList.push_back(devfile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AMBEEngine::probe_serial8250_comports(
|
||||
std::vector<std::string>& comList,
|
||||
std::vector<std::string> comList8250)
|
||||
{
|
||||
struct serial_struct serinfo;
|
||||
std::vector<std::string>::iterator it = comList8250.begin();
|
||||
|
||||
// Iterate over all serial8250-devices
|
||||
while (it != comList8250.end())
|
||||
{
|
||||
|
||||
// Try to open the device
|
||||
int fd = open((*it).c_str(), O_RDWR | O_NONBLOCK | O_NOCTTY);
|
||||
|
||||
if (fd >= 0)
|
||||
{
|
||||
// Get serial_info
|
||||
if (ioctl(fd, TIOCGSERIAL, &serinfo) == 0)
|
||||
{
|
||||
// If device type is no PORT_UNKNOWN we accept the port
|
||||
if (serinfo.type != PORT_UNKNOWN) {
|
||||
comList.push_back(*it);
|
||||
}
|
||||
}
|
||||
|
||||
close(fd);
|
||||
}
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
std::string AMBEEngine::get_driver(const std::string& tty)
|
||||
{
|
||||
struct stat st;
|
||||
std::string devicedir = tty;
|
||||
// Append '/device' to the tty-path
|
||||
devicedir += "/device";
|
||||
|
||||
// Stat the devicedir and handle it if it is a symlink
|
||||
if (lstat(devicedir.c_str(), &st) == 0 && S_ISLNK(st.st_mode))
|
||||
{
|
||||
char buffer[1024];
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
// Append '/driver' and return basename of the target
|
||||
devicedir += "/driver";
|
||||
|
||||
if (readlink(devicedir.c_str(), buffer, sizeof(buffer)) > 0) {
|
||||
return basename(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
void AMBEEngine::scan(std::vector<QString>& ambeDevices)
|
||||
{
|
||||
getComList();
|
||||
std::vector<std::string>::const_iterator it = m_comList.begin();
|
||||
ambeDevices.clear();
|
||||
|
||||
while (it != m_comList.end())
|
||||
{
|
||||
AMBEWorker *worker = new AMBEWorker();
|
||||
qDebug("AMBEEngine::scan: com: %s", it->c_str());
|
||||
|
||||
if (worker->open(*it))
|
||||
{
|
||||
ambeDevices.push_back(QString(it->c_str()));
|
||||
worker->close();
|
||||
}
|
||||
|
||||
delete worker;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
#endif // not Windows nor Apple
|
||||
|
||||
bool AMBEEngine::registerController(const std::string& deviceRef)
|
||||
{
|
||||
AMBEWorker *worker = new AMBEWorker();
|
||||
|
||||
if (worker->open(deviceRef))
|
||||
{
|
||||
qDebug("AMBEEngine::registerController: device: %s", deviceRef.c_str());
|
||||
m_controllers.push_back(AMBEController());
|
||||
m_controllers.back().worker = worker;
|
||||
m_controllers.back().thread = new QThread();
|
||||
m_controllers.back().device = deviceRef;
|
||||
|
||||
m_controllers.back().worker->moveToThread(m_controllers.back().thread);
|
||||
connect(m_controllers.back().worker, SIGNAL(finished()), m_controllers.back().thread, SLOT(quit()));
|
||||
connect(m_controllers.back().worker, SIGNAL(finished()), m_controllers.back().worker, SLOT(deleteLater()));
|
||||
connect(m_controllers.back().thread, SIGNAL(finished()), m_controllers.back().thread, SLOT(deleteLater()));
|
||||
connect(&m_controllers.back().worker->m_inputMessageQueue, SIGNAL(messageEnqueued()), m_controllers.back().worker, SLOT(handleInputMessages()));
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
m_controllers.back().thread->start();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
qWarning("AMBEEngine::registerController: failed to register device: %s", deviceRef.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
void AMBEEngine::releaseController(const std::string& deviceRef)
|
||||
{
|
||||
std::vector<AMBEController>::iterator it = m_controllers.begin();
|
||||
|
||||
while (it != m_controllers.end())
|
||||
{
|
||||
if (it->device == deviceRef)
|
||||
{
|
||||
disconnect(&it->worker->m_inputMessageQueue, SIGNAL(messageEnqueued()), it->worker, SLOT(handleInputMessages()));
|
||||
it->worker->stop();
|
||||
it->thread->wait(100);
|
||||
it->worker->m_inputMessageQueue.clear();
|
||||
it->worker->close();
|
||||
qDebug() << "AMBEEngine::releaseController: closed device at: " << it->device.c_str();
|
||||
m_controllers.erase(it);
|
||||
break;
|
||||
}
|
||||
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
void AMBEEngine::releaseAll()
|
||||
{
|
||||
std::vector<AMBEController>::iterator it = m_controllers.begin();
|
||||
|
||||
while (it != m_controllers.end())
|
||||
{
|
||||
disconnect(&it->worker->m_inputMessageQueue, SIGNAL(messageEnqueued()), it->worker, SLOT(handleInputMessages()));
|
||||
it->worker->stop();
|
||||
it->thread->wait(100);
|
||||
it->worker->m_inputMessageQueue.clear();
|
||||
it->worker->close();
|
||||
qDebug() << "AMBEEngine::release: closed device at: " << it->device.c_str();
|
||||
++it;
|
||||
}
|
||||
|
||||
m_controllers.clear();
|
||||
}
|
||||
|
||||
void AMBEEngine::getDeviceRefs(std::vector<QString>& deviceNames)
|
||||
{
|
||||
std::vector<AMBEController>::const_iterator it = m_controllers.begin();
|
||||
|
||||
while (it != m_controllers.end())
|
||||
{
|
||||
deviceNames.push_back(QString(it->device.c_str()));
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
void AMBEEngine::pushMbeFrame(
|
||||
const unsigned char *mbeFrame,
|
||||
int mbeRateIndex,
|
||||
int mbeVolumeIndex,
|
||||
unsigned char channels,
|
||||
bool useLP,
|
||||
int upsampling,
|
||||
AudioFifo *audioFifo)
|
||||
{
|
||||
std::vector<AMBEController>::iterator it = m_controllers.begin();
|
||||
std::vector<AMBEController>::iterator itAvail = m_controllers.end();
|
||||
bool done = false;
|
||||
QMutexLocker locker(&m_mutex);
|
||||
|
||||
while (it != m_controllers.end())
|
||||
{
|
||||
if (it->worker->hasFifo(audioFifo))
|
||||
{
|
||||
it->worker->pushMbeFrame(mbeFrame, mbeRateIndex, mbeVolumeIndex, channels, useLP, upsampling, audioFifo);
|
||||
done = true;
|
||||
}
|
||||
else if (it->worker->isAvailable())
|
||||
{
|
||||
itAvail = it;
|
||||
}
|
||||
|
||||
++it;
|
||||
}
|
||||
|
||||
if (!done)
|
||||
{
|
||||
if (itAvail != m_controllers.end())
|
||||
{
|
||||
int wNum = itAvail - m_controllers.begin();
|
||||
|
||||
qDebug("AMBEEngine::pushMbeFrame: push %p on empty queue %d", audioFifo, wNum);
|
||||
itAvail->worker->pushMbeFrame(mbeFrame, mbeRateIndex, mbeVolumeIndex, channels, useLP, upsampling, audioFifo);
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug("AMBEEngine::pushMbeFrame: no DV device available. MBE frame dropped");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray AMBEEngine::serialize() const
|
||||
{
|
||||
qDebug("AMBEEngine::serialize");
|
||||
QStringList qDeviceList;
|
||||
std::vector<AMBEController>::const_iterator it = m_controllers.begin();
|
||||
|
||||
while (it != m_controllers.end())
|
||||
{
|
||||
qDebug("AMBEEngine::serialize: %s", it->device.c_str());
|
||||
qDeviceList << QString(it->device.c_str());
|
||||
++it;
|
||||
}
|
||||
|
||||
QByteArray data;
|
||||
QDataStream *stream = new QDataStream(&data, QIODevice::WriteOnly);
|
||||
(*stream) << qDeviceList;
|
||||
delete stream;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
bool AMBEEngine::deserialize(const QByteArray& data)
|
||||
{
|
||||
if (data.size() <= 0)
|
||||
{
|
||||
qDebug("AMBEEngine::deserialize: invalid or no data");
|
||||
return false;
|
||||
}
|
||||
|
||||
QStringList qDeviceList;
|
||||
QDataStream *stream = new QDataStream(data);
|
||||
(*stream) >> qDeviceList;
|
||||
delete stream;
|
||||
|
||||
releaseAll();
|
||||
|
||||
for (int i = 0; i < qDeviceList.size(); ++i)
|
||||
{
|
||||
qDebug(" AMBEEngine::deserialize: %s", qDeviceList.at(i).toStdString().c_str());
|
||||
registerController(qDeviceList.at(i).toStdString());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AMBEEngine::formatTo(SWGSDRangel::SWGObject *swgObject) const
|
||||
{
|
||||
(void) swgObject;
|
||||
// TODO
|
||||
}
|
||||
|
||||
void AMBEEngine::updateFrom(const QStringList& keys, const SWGSDRangel::SWGObject *swgObject)
|
||||
{
|
||||
(void) keys;
|
||||
(void) swgObject;
|
||||
// TODO
|
||||
}
|
93
plugins/feature/ambe/ambeengine.h
Normal file
93
plugins/feature/ambe/ambeengine.h
Normal file
@ -0,0 +1,93 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2019 F4EXB //
|
||||
// written by Edouard Griffiths //
|
||||
// //
|
||||
// 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 SDRBASE_AMBE_AMBEENGINE_H_
|
||||
#define SDRBASE_AMBE_AMBEENGINE_H_
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include <QObject>
|
||||
#include <QMutex>
|
||||
#include <QString>
|
||||
#include <QByteArray>
|
||||
|
||||
#include "settings/serializable.h"
|
||||
|
||||
class QThread;
|
||||
class AMBEWorker;
|
||||
class AudioFifo;
|
||||
|
||||
class AMBEEngine : public QObject, public Serializable
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
AMBEEngine();
|
||||
~AMBEEngine();
|
||||
|
||||
void scan(std::vector<QString>& ambeDevices);
|
||||
void releaseAll();
|
||||
|
||||
int getNbDevices() const { return m_controllers.size(); } //!< number of devices used
|
||||
void getDeviceRefs(std::vector<QString>& devicesRefs); //!< reference of the devices used (device path or url)
|
||||
bool registerController(const std::string& deviceRef); //!< create a new controller for the device in reference
|
||||
void releaseController(const std::string& deviceRef); //!< release controller resources for the device in reference
|
||||
|
||||
void pushMbeFrame(
|
||||
const unsigned char *mbeFrame,
|
||||
int mbeRateIndex,
|
||||
int mbeVolumeIndex,
|
||||
unsigned char channels,
|
||||
bool useHP,
|
||||
int upsampling,
|
||||
AudioFifo *audioFifo);
|
||||
|
||||
virtual QByteArray serialize() const;
|
||||
virtual bool deserialize(const QByteArray& data);
|
||||
virtual void formatTo(SWGSDRangel::SWGObject *swgObject) const; //!< Serialize to API
|
||||
virtual void updateFrom(const QStringList& keys, const SWGSDRangel::SWGObject *swgObject); //!< Deserialize from API
|
||||
|
||||
private:
|
||||
struct AMBEController
|
||||
{
|
||||
AMBEController() :
|
||||
thread(nullptr),
|
||||
worker(nullptr)
|
||||
{}
|
||||
|
||||
QThread *thread;
|
||||
AMBEWorker *worker;
|
||||
std::string device;
|
||||
};
|
||||
|
||||
#ifndef _WIN32
|
||||
static std::string get_driver(const std::string& tty);
|
||||
static void register_comport(std::vector<std::string>& comList, std::vector<std::string>& comList8250, const std::string& dir);
|
||||
static void probe_serial8250_comports(std::vector<std::string>& comList, std::vector<std::string> comList8250);
|
||||
#endif
|
||||
void getComList();
|
||||
|
||||
std::vector<AMBEController> m_controllers;
|
||||
std::vector<std::string> m_comList;
|
||||
std::vector<std::string> m_comList8250;
|
||||
QMutex m_mutex;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* SDRBASE_AMBE_AMBEENGINE_H_ */
|
343
plugins/feature/ambe/ambegui.cpp
Normal file
343
plugins/feature/ambe/ambegui.cpp
Normal file
@ -0,0 +1,343 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2022 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 "feature/featureuiset.h"
|
||||
#include "gui/basicfeaturesettingsdialog.h"
|
||||
#include "gui/crightclickenabler.h"
|
||||
|
||||
#include "ui_ambegui.h"
|
||||
#include "ambegui.h"
|
||||
#include "ambe.h"
|
||||
|
||||
AMBEGUI* AMBEGUI::create(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature)
|
||||
{
|
||||
AMBEGUI* gui = new AMBEGUI(pluginAPI, featureUISet, feature);
|
||||
return gui;
|
||||
}
|
||||
|
||||
void AMBEGUI::destroy()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
AMBEGUI::AMBEGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature, QWidget* parent) :
|
||||
FeatureGUI(parent),
|
||||
ui(new Ui::AMBEGUI),
|
||||
m_pluginAPI(pluginAPI),
|
||||
m_featureUISet(featureUISet),
|
||||
m_doApplySettings(true)
|
||||
{
|
||||
m_feature = feature;
|
||||
setAttribute(Qt::WA_DeleteOnClose, true);
|
||||
m_helpURL = "plugins/feature/ambe/readme.md";
|
||||
RollupContents *rollupContents = getRollupContents();
|
||||
ui->setupUi(rollupContents);
|
||||
setSizePolicy(rollupContents->sizePolicy());
|
||||
rollupContents->arrangeRollups();
|
||||
connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool)));
|
||||
|
||||
m_ambe = reinterpret_cast<AMBE*>(feature);
|
||||
m_ambe->setMessageQueueToGUI(&m_inputMessageQueue);
|
||||
|
||||
m_settings.setRollupState(&m_rollupState);
|
||||
|
||||
connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &)));
|
||||
connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
|
||||
|
||||
populateSerialList();
|
||||
refreshInUseList();
|
||||
displaySettings();
|
||||
makeUIConnections();
|
||||
}
|
||||
|
||||
AMBEGUI::~AMBEGUI()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void AMBEGUI::resetToDefaults()
|
||||
{
|
||||
m_settings.resetToDefaults();
|
||||
displaySettings();
|
||||
applySettings(true);
|
||||
}
|
||||
|
||||
QByteArray AMBEGUI::serialize() const
|
||||
{
|
||||
return m_settings.serialize();
|
||||
}
|
||||
|
||||
bool AMBEGUI::deserialize(const QByteArray& data)
|
||||
{
|
||||
if (m_settings.deserialize(data))
|
||||
{
|
||||
m_feature->setWorkspaceIndex(m_settings.m_workspaceIndex);
|
||||
displaySettings();
|
||||
refreshInUseList();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void AMBEGUI::setWorkspaceIndex(int index)
|
||||
{
|
||||
m_settings.m_workspaceIndex = index;
|
||||
m_feature->setWorkspaceIndex(index);
|
||||
}
|
||||
|
||||
void AMBEGUI::resizeEvent(QResizeEvent* size)
|
||||
{
|
||||
int maxWidth = getRollupContents()->maximumWidth();
|
||||
int minHeight = getRollupContents()->minimumHeight() + getAdditionalHeight();
|
||||
resize(width() < maxWidth ? width() : maxWidth, minHeight);
|
||||
size->accept();
|
||||
}
|
||||
|
||||
void AMBEGUI::onWidgetRolled(QWidget* widget, bool rollDown)
|
||||
{
|
||||
(void) widget;
|
||||
(void) rollDown;
|
||||
|
||||
getRollupContents()->saveState(m_rollupState);
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void AMBEGUI::onMenuDialogCalled(const QPoint &p)
|
||||
{
|
||||
if (m_contextMenuType == ContextMenuChannelSettings)
|
||||
{
|
||||
BasicFeatureSettingsDialog dialog(this);
|
||||
dialog.setTitle(m_settings.m_title);
|
||||
dialog.setUseReverseAPI(m_settings.m_useReverseAPI);
|
||||
dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress);
|
||||
dialog.setReverseAPIPort(m_settings.m_reverseAPIPort);
|
||||
dialog.setReverseAPIFeatureSetIndex(m_settings.m_reverseAPIFeatureSetIndex);
|
||||
dialog.setReverseAPIFeatureIndex(m_settings.m_reverseAPIFeatureIndex);
|
||||
dialog.setDefaultTitle(m_displayedName);
|
||||
|
||||
dialog.move(p);
|
||||
dialog.exec();
|
||||
|
||||
m_settings.m_title = dialog.getTitle();
|
||||
m_settings.m_useReverseAPI = dialog.useReverseAPI();
|
||||
m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress();
|
||||
m_settings.m_reverseAPIPort = dialog.getReverseAPIPort();
|
||||
m_settings.m_reverseAPIFeatureSetIndex = dialog.getReverseAPIFeatureSetIndex();
|
||||
m_settings.m_reverseAPIFeatureIndex = dialog.getReverseAPIFeatureIndex();
|
||||
|
||||
setTitle(m_settings.m_title);
|
||||
setTitleColor(m_settings.m_rgbColor);
|
||||
|
||||
applySettings();
|
||||
}
|
||||
|
||||
resetContextMenuType();
|
||||
}
|
||||
|
||||
void AMBEGUI::displaySettings()
|
||||
{
|
||||
setTitleColor(m_settings.m_rgbColor);
|
||||
setWindowTitle(m_settings.m_title);
|
||||
setTitle(m_settings.m_title);
|
||||
}
|
||||
|
||||
void AMBEGUI::applySettings(bool force)
|
||||
{
|
||||
if (m_doApplySettings)
|
||||
{
|
||||
AMBE::MsgConfigureAMBE* message = AMBE::MsgConfigureAMBE::create( m_settings, force);
|
||||
m_ambe->getInputMessageQueue()->push(message);
|
||||
}
|
||||
}
|
||||
|
||||
bool AMBEGUI::handleMessage(const Message& message)
|
||||
{
|
||||
if (AMBE::MsgConfigureAMBE::match(message))
|
||||
{
|
||||
qDebug("AMBEGUI::handleMessage: AMBE::MsgConfigureAMBE");
|
||||
const AMBE::MsgConfigureAMBE& cfg = (AMBE::MsgConfigureAMBE&) message;
|
||||
m_settings = cfg.getSettings();
|
||||
displaySettings();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void AMBEGUI::handleInputMessages()
|
||||
{
|
||||
Message* message;
|
||||
|
||||
while ((message = getInputMessageQueue()->pop()))
|
||||
{
|
||||
if (handleMessage(*message)) {
|
||||
delete message;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AMBEGUI::populateSerialList()
|
||||
{
|
||||
std::vector<QString> ambeSerialDevices;
|
||||
m_ambe->getAMBEEngine()->scan(ambeSerialDevices);
|
||||
ui->ambeSerialDevices->clear();
|
||||
std::vector<QString>::const_iterator it = ambeSerialDevices.begin();
|
||||
|
||||
for (; it != ambeSerialDevices.end(); ++it) {
|
||||
ui->ambeSerialDevices->addItem(QString(*it));
|
||||
}
|
||||
}
|
||||
|
||||
void AMBEGUI::refreshInUseList()
|
||||
{
|
||||
std::vector<QString> inUseDevices;
|
||||
m_ambe->getAMBEEngine()->getDeviceRefs(inUseDevices);
|
||||
ui->ambeDeviceRefs->clear();
|
||||
std::vector<QString>::const_iterator it = inUseDevices.begin();
|
||||
|
||||
for (; it != inUseDevices.end(); ++it)
|
||||
{
|
||||
qDebug("AMBEGUI::refreshInUseList: %s", qPrintable(*it));
|
||||
ui->ambeDeviceRefs->addItem(*it);
|
||||
}
|
||||
}
|
||||
void AMBEGUI::on_importSerial_clicked()
|
||||
{
|
||||
QListWidgetItem *serialItem = ui->ambeSerialDevices->currentItem();
|
||||
|
||||
if (!serialItem)
|
||||
{
|
||||
ui->statusText->setText("No selection");
|
||||
return;
|
||||
}
|
||||
|
||||
QString serialName = serialItem->text();
|
||||
QList<QListWidgetItem*> foundItems = ui->ambeDeviceRefs->findItems(serialName, Qt::MatchFixedString|Qt::MatchCaseSensitive);
|
||||
|
||||
if (foundItems.size() == 0)
|
||||
{
|
||||
if (m_ambe->getAMBEEngine()->registerController(serialName.toStdString()))
|
||||
{
|
||||
ui->ambeDeviceRefs->addItem(serialName);
|
||||
ui->statusText->setText(tr("%1 added").arg(serialName));
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->statusText->setText(tr("Cannot open %1").arg(serialName));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->statusText->setText("Device already in use");
|
||||
}
|
||||
}
|
||||
|
||||
void AMBEGUI::on_importAllSerial_clicked()
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
for (int i = 0; i < ui->ambeSerialDevices->count(); i++)
|
||||
{
|
||||
const QListWidgetItem *serialItem = ui->ambeSerialDevices->item(i);
|
||||
QString serialName = serialItem->text();
|
||||
QList<QListWidgetItem*> foundItems = ui->ambeDeviceRefs->findItems(serialName, Qt::MatchFixedString|Qt::MatchCaseSensitive);
|
||||
|
||||
if (foundItems.size() == 0)
|
||||
{
|
||||
if (m_ambe->getAMBEEngine()->registerController(serialName.toStdString()))
|
||||
{
|
||||
ui->ambeDeviceRefs->addItem(serialName);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ui->statusText->setText(tr("%1 devices added").arg(count));
|
||||
}
|
||||
|
||||
void AMBEGUI::on_removeAmbeDevice_clicked()
|
||||
{
|
||||
QListWidgetItem *deviceItem = ui->ambeDeviceRefs->currentItem();
|
||||
|
||||
if (!deviceItem)
|
||||
{
|
||||
ui->statusText->setText("No selection");
|
||||
return;
|
||||
}
|
||||
|
||||
QString deviceName = deviceItem->text();
|
||||
m_ambe->getAMBEEngine()->releaseController(deviceName.toStdString());
|
||||
ui->statusText->setText(tr("%1 removed").arg(deviceName));
|
||||
refreshInUseList();
|
||||
}
|
||||
|
||||
void AMBEGUI::on_refreshAmbeList_clicked()
|
||||
{
|
||||
refreshInUseList();
|
||||
ui->statusText->setText("In use refreshed");
|
||||
}
|
||||
|
||||
void AMBEGUI::on_clearAmbeList_clicked()
|
||||
{
|
||||
if (ui->ambeDeviceRefs->count() == 0)
|
||||
{
|
||||
ui->statusText->setText("No active items");
|
||||
return;
|
||||
}
|
||||
|
||||
m_ambe->getAMBEEngine()->releaseAll();
|
||||
ui->ambeDeviceRefs->clear();
|
||||
ui->statusText->setText("All items released");
|
||||
}
|
||||
|
||||
void AMBEGUI::on_importAddress_clicked()
|
||||
{
|
||||
QString addressAndPort = ui->ambeAddressText->text();
|
||||
|
||||
QList<QListWidgetItem*> foundItems = ui->ambeDeviceRefs->findItems(addressAndPort, Qt::MatchFixedString|Qt::MatchCaseSensitive);
|
||||
|
||||
if (foundItems.size() == 0)
|
||||
{
|
||||
if (m_ambe->getAMBEEngine()->registerController(addressAndPort.toStdString()))
|
||||
{
|
||||
ui->ambeDeviceRefs->addItem(addressAndPort);
|
||||
ui->statusText->setText(tr("%1 added").arg(addressAndPort));
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->statusText->setText(tr("Cannot open %1").arg(addressAndPort));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->statusText->setText("Address already in use");
|
||||
}
|
||||
}
|
||||
|
||||
void AMBEGUI::makeUIConnections()
|
||||
{
|
||||
QObject::connect(ui->importSerial, &QPushButton::clicked, this, &AMBEGUI::on_importSerial_clicked);
|
||||
QObject::connect(ui->importAllSerial, &QPushButton::clicked, this, &AMBEGUI::on_importAllSerial_clicked);
|
||||
QObject::connect(ui->removeAmbeDevice, &QPushButton::clicked, this, &AMBEGUI::on_removeAmbeDevice_clicked);
|
||||
QObject::connect(ui->refreshAmbeList, &QPushButton::clicked, this, &AMBEGUI::on_refreshAmbeList_clicked);
|
||||
QObject::connect(ui->clearAmbeList, &QPushButton::clicked, this, &AMBEGUI::on_clearAmbeList_clicked);
|
||||
QObject::connect(ui->importAddress, &QPushButton::clicked, this, &AMBEGUI::on_importAddress_clicked);
|
||||
}
|
92
plugins/feature/ambe/ambegui.h
Normal file
92
plugins/feature/ambe/ambegui.h
Normal file
@ -0,0 +1,92 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2022 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_FEATURE_AMBEGUI_H_
|
||||
#define INCLUDE_FEATURE_AMBEGUI_H_
|
||||
|
||||
#include "feature/featuregui.h"
|
||||
#include "util/messagequeue.h"
|
||||
#include "util/movingaverage.h"
|
||||
#include "settings/rollupstate.h"
|
||||
|
||||
#include "ambesettings.h"
|
||||
|
||||
class PluginAPI;
|
||||
class FeatureUISet;
|
||||
class Feature;
|
||||
class AMBE;
|
||||
class DSPDeviceSourceEngine;
|
||||
class DSPDeviceSinkEngine;
|
||||
|
||||
namespace Ui {
|
||||
class AMBEGUI;
|
||||
}
|
||||
|
||||
class AMBEGUI : public FeatureGUI
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
static AMBEGUI* create(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature);
|
||||
virtual void destroy();
|
||||
|
||||
void resetToDefaults();
|
||||
QByteArray serialize() const;
|
||||
bool deserialize(const QByteArray& data);
|
||||
virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
|
||||
virtual void setWorkspaceIndex(int index);
|
||||
virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }
|
||||
virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }
|
||||
virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent* size);
|
||||
|
||||
private:
|
||||
Ui::AMBEGUI* ui;
|
||||
AMBE *m_ambe;
|
||||
PluginAPI* m_pluginAPI;
|
||||
FeatureUISet* m_featureUISet;
|
||||
AMBESettings m_settings;
|
||||
RollupState m_rollupState;
|
||||
bool m_doApplySettings;
|
||||
MessageQueue m_inputMessageQueue;
|
||||
|
||||
explicit AMBEGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature, QWidget* parent = nullptr);
|
||||
virtual ~AMBEGUI();
|
||||
|
||||
void populateSerialList();
|
||||
void refreshInUseList();
|
||||
|
||||
void blockApplySettings(bool block) { m_doApplySettings = !block; }
|
||||
void applySettings(bool force = false);
|
||||
void displaySettings();
|
||||
bool handleMessage(const Message& message);
|
||||
void makeUIConnections();
|
||||
|
||||
private slots:
|
||||
void onMenuDialogCalled(const QPoint &p);
|
||||
void onWidgetRolled(QWidget* widget, bool rollDown);
|
||||
void handleInputMessages();
|
||||
void on_importSerial_clicked();
|
||||
void on_importAllSerial_clicked();
|
||||
void on_removeAmbeDevice_clicked();
|
||||
void on_refreshAmbeList_clicked();
|
||||
void on_clearAmbeList_clicked();
|
||||
void on_importAddress_clicked();
|
||||
};
|
||||
|
||||
#endif
|
285
plugins/feature/ambe/ambegui.ui
Normal file
285
plugins/feature/ambe/ambegui.ui
Normal file
@ -0,0 +1,285 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>AMBEGUI</class>
|
||||
<widget class="RollupContents" name="AMBEGUI">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>360</width>
|
||||
<height>452</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>360</width>
|
||||
<height>452</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>560</width>
|
||||
<height>452</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Liberation Sans</family>
|
||||
<pointsize>9</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>AMBE Decoder Controller</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="settingsContainer" native="true">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>358</width>
|
||||
<height>450</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Settings</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="ipTextLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="ambeServerLabel">
|
||||
<property name="text">
|
||||
<string>AMBE server IP and port or direct input</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="ipLayout">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="ambeAddressText">
|
||||
<property name="toolTip">
|
||||
<string>AMBE server address as ip:port or direct input</string>
|
||||
</property>
|
||||
<property name="inputMask">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="inUseLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="ambeSerialLabel">
|
||||
<property name="text">
|
||||
<string>In use</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="importAddress">
|
||||
<property name="toolTip">
|
||||
<string>Use server</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||
<normaloff>:/arrow_down.png</normaloff>:/arrow_down.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="removeAmbeDevice">
|
||||
<property name="toolTip">
|
||||
<string>Release all devices and servers</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||
<normaloff>:/bin.png</normaloff>:/bin.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="refreshAmbeList">
|
||||
<property name="toolTip">
|
||||
<string>Refresh list of devices and servers in use</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||
<normaloff>:/recycle.png</normaloff>:/recycle.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="clearAmbeList">
|
||||
<property name="toolTip">
|
||||
<string>Remove all devices</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||
<normaloff>:/sweep.png</normaloff>:/sweep.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="localDeviceLayout">
|
||||
<item>
|
||||
<widget class="QListWidget" name="ambeDeviceRefs">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>140</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>List of devices/servers in use</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="serialLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="ambeSerialLabel_2">
|
||||
<property name="text">
|
||||
<string>Serial devices</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="importSerial">
|
||||
<property name="toolTip">
|
||||
<string>Use serial device</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||
<normaloff>:/arrow_up.png</normaloff>:/arrow_up.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="importAllSerial">
|
||||
<property name="toolTip">
|
||||
<string>Use all serial devices</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||
<normaloff>:/double_arrow_up.png</normaloff>:/double_arrow_up.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="serialLayout_2">
|
||||
<item>
|
||||
<widget class="QListWidget" name="ambeSerialDevices">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>140</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>List of available AMBE serial devices available</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="statusLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="statusText">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>RollupContents</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>gui/rollupcontents.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="../../../sdrgui/resources/res.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
80
plugins/feature/ambe/ambeplugin.cpp
Normal file
80
plugins/feature/ambe/ambeplugin.cpp
Normal file
@ -0,0 +1,80 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2022 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"
|
||||
|
||||
#ifndef SERVER_MODE
|
||||
#include "ambegui.h"
|
||||
#endif
|
||||
#include "ambe.h"
|
||||
#include "ambeplugin.h"
|
||||
// #include "simplepttwebapiadapter.h"
|
||||
|
||||
const PluginDescriptor AMBEPlugin::m_pluginDescriptor = {
|
||||
AMBE::m_featureId,
|
||||
QStringLiteral("AMBE Controller"),
|
||||
QStringLiteral("7.2.0"),
|
||||
QStringLiteral("(c) Edouard Griffiths, F4EXB"),
|
||||
QStringLiteral("https://github.com/f4exb/sdrangel"),
|
||||
true,
|
||||
QStringLiteral("https://github.com/f4exb/sdrangel")
|
||||
};
|
||||
|
||||
AMBEPlugin::AMBEPlugin(QObject* parent) :
|
||||
QObject(parent),
|
||||
m_pluginAPI(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
const PluginDescriptor& AMBEPlugin::getPluginDescriptor() const
|
||||
{
|
||||
return m_pluginDescriptor;
|
||||
}
|
||||
|
||||
void AMBEPlugin::initPlugin(PluginAPI* pluginAPI)
|
||||
{
|
||||
m_pluginAPI = pluginAPI;
|
||||
|
||||
// register Simple PTT feature
|
||||
m_pluginAPI->registerFeature(AMBE::m_featureIdURI, AMBE::m_featureId, this);
|
||||
}
|
||||
|
||||
#ifdef SERVER_MODE
|
||||
FeatureGUI* AMBEPlugin::createFeatureGUI(FeatureUISet *featureUISet, Feature *feature) const
|
||||
{
|
||||
(void) featureUISet;
|
||||
(void) feature;
|
||||
return nullptr;
|
||||
}
|
||||
#else
|
||||
FeatureGUI* AMBEPlugin::createFeatureGUI(FeatureUISet *featureUISet, Feature *feature) const
|
||||
{
|
||||
return AMBEGUI::create(m_pluginAPI, featureUISet, feature);
|
||||
}
|
||||
#endif
|
||||
|
||||
Feature* AMBEPlugin::createFeature(WebAPIAdapterInterface* webAPIAdapterInterface) const
|
||||
{
|
||||
return new AMBE(webAPIAdapterInterface);
|
||||
}
|
||||
|
||||
FeatureWebAPIAdapter* AMBEPlugin::createFeatureWebAPIAdapter() const
|
||||
{
|
||||
return nullptr; // TODO new SimplePTTWebAPIAdapter();
|
||||
}
|
48
plugins/feature/ambe/ambeplugin.h
Normal file
48
plugins/feature/ambe/ambeplugin.h
Normal file
@ -0,0 +1,48 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2022 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_FEATURE_AMBEPLUGIN_H
|
||||
#define INCLUDE_FEATURE_AMBEPLUGIN_H
|
||||
|
||||
#include <QObject>
|
||||
#include "plugin/plugininterface.h"
|
||||
|
||||
class FeatureGUI;
|
||||
class WebAPIAdapterInterface;
|
||||
|
||||
class AMBEPlugin : public QObject, PluginInterface {
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(PluginInterface)
|
||||
Q_PLUGIN_METADATA(IID "sdrangel.feature.ambe")
|
||||
|
||||
public:
|
||||
explicit AMBEPlugin(QObject* parent = nullptr);
|
||||
|
||||
const PluginDescriptor& getPluginDescriptor() const;
|
||||
void initPlugin(PluginAPI* pluginAPI);
|
||||
|
||||
virtual FeatureGUI* createFeatureGUI(FeatureUISet *featureUISet, Feature *feature) const;
|
||||
virtual Feature* createFeature(WebAPIAdapterInterface *webAPIAdapterInterface) const;
|
||||
virtual FeatureWebAPIAdapter* createFeatureWebAPIAdapter() const;
|
||||
|
||||
private:
|
||||
static const PluginDescriptor m_pluginDescriptor;
|
||||
|
||||
PluginAPI* m_pluginAPI;
|
||||
};
|
||||
|
||||
#endif // INCLUDE_FEATURE_AMBEPLUGIN_H
|
114
plugins/feature/ambe/ambesettings.cpp
Normal file
114
plugins/feature/ambe/ambesettings.cpp
Normal file
@ -0,0 +1,114 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2022 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 <QColor>
|
||||
|
||||
#include "util/simpleserializer.h"
|
||||
#include "settings/serializable.h"
|
||||
|
||||
#include "ambesettings.h"
|
||||
|
||||
AMBESettings::AMBESettings() :
|
||||
m_rollupState(nullptr)
|
||||
{
|
||||
resetToDefaults();
|
||||
}
|
||||
|
||||
void AMBESettings::resetToDefaults()
|
||||
{
|
||||
m_title = "AMBE Controller";
|
||||
m_rgbColor = QColor(255, 0, 0).rgb();
|
||||
m_useReverseAPI = false;
|
||||
m_reverseAPIAddress = "127.0.0.1";
|
||||
m_reverseAPIPort = 8888;
|
||||
m_reverseAPIFeatureSetIndex = 0;
|
||||
m_reverseAPIFeatureIndex = 0;
|
||||
m_workspaceIndex = 0;
|
||||
}
|
||||
|
||||
QByteArray AMBESettings::serialize() const
|
||||
{
|
||||
SimpleSerializer s(1);
|
||||
|
||||
s.writeString(1, m_title);
|
||||
s.writeU32(2, m_rgbColor);
|
||||
s.writeBool(7, m_useReverseAPI);
|
||||
s.writeString(8, m_reverseAPIAddress);
|
||||
s.writeU32(9, m_reverseAPIPort);
|
||||
s.writeU32(10, m_reverseAPIFeatureSetIndex);
|
||||
s.writeU32(11, m_reverseAPIFeatureIndex);
|
||||
|
||||
if (m_rollupState) {
|
||||
s.writeBlob(12, m_rollupState->serialize());
|
||||
}
|
||||
|
||||
s.writeS32(18, m_workspaceIndex);
|
||||
s.writeBlob(19, m_geometryBytes);
|
||||
|
||||
return s.final();
|
||||
}
|
||||
|
||||
bool AMBESettings::deserialize(const QByteArray& data)
|
||||
{
|
||||
SimpleDeserializer d(data);
|
||||
|
||||
if(!d.isValid())
|
||||
{
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
|
||||
if(d.getVersion() == 1)
|
||||
{
|
||||
QByteArray bytetmp;
|
||||
uint32_t utmp;
|
||||
QString strtmp;
|
||||
|
||||
d.readString(1, &m_title, "Simple PTT");
|
||||
d.readU32(2, &m_rgbColor, QColor(255, 0, 0).rgb());
|
||||
d.readBool(7, &m_useReverseAPI, false);
|
||||
d.readString(8, &m_reverseAPIAddress, "127.0.0.1");
|
||||
d.readU32(9, &utmp, 0);
|
||||
|
||||
if ((utmp > 1023) && (utmp < 65535)) {
|
||||
m_reverseAPIPort = utmp;
|
||||
} else {
|
||||
m_reverseAPIPort = 8888;
|
||||
}
|
||||
|
||||
d.readU32(10, &utmp, 0);
|
||||
m_reverseAPIFeatureSetIndex = utmp > 99 ? 99 : utmp;
|
||||
d.readU32(11, &utmp, 0);
|
||||
m_reverseAPIFeatureIndex = utmp > 99 ? 99 : utmp;
|
||||
|
||||
if (m_rollupState)
|
||||
{
|
||||
d.readBlob(12, &bytetmp);
|
||||
m_rollupState->deserialize(bytetmp);
|
||||
}
|
||||
|
||||
d.readS32(18, &m_workspaceIndex, 0);
|
||||
d.readBlob(19, &m_geometryBytes);
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
}
|
46
plugins/feature/ambe/ambesettings.h
Normal file
46
plugins/feature/ambe/ambesettings.h
Normal file
@ -0,0 +1,46 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2022 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_FEATURE_AMBESETTINGS_H_
|
||||
#define INCLUDE_FEATURE_AMBESETTINGS_H_
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QString>
|
||||
|
||||
class Serializable;
|
||||
|
||||
struct AMBESettings
|
||||
{
|
||||
QString m_title;
|
||||
quint32 m_rgbColor;
|
||||
bool m_useReverseAPI;
|
||||
QString m_reverseAPIAddress;
|
||||
uint16_t m_reverseAPIPort;
|
||||
uint16_t m_reverseAPIFeatureSetIndex;
|
||||
uint16_t m_reverseAPIFeatureIndex;
|
||||
Serializable *m_rollupState;
|
||||
int m_workspaceIndex;
|
||||
QByteArray m_geometryBytes;
|
||||
|
||||
AMBESettings();
|
||||
void resetToDefaults();
|
||||
QByteArray serialize() const;
|
||||
bool deserialize(const QByteArray& data);
|
||||
void setRollupState(Serializable *rollupState) { m_rollupState = rollupState; }
|
||||
};
|
||||
|
||||
#endif // INCLUDE_FEATURE_AMBESETTINGS_H_
|
232
plugins/feature/ambe/ambeworker.cpp
Normal file
232
plugins/feature/ambe/ambeworker.cpp
Normal file
@ -0,0 +1,232 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2019 F4EXB //
|
||||
// written by Edouard Griffiths //
|
||||
// //
|
||||
// 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 <algorithm>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include "audio/audiofifo.h"
|
||||
#include "ambeworker.h"
|
||||
|
||||
MESSAGE_CLASS_DEFINITION(AMBEWorker::MsgMbeDecode, Message)
|
||||
MESSAGE_CLASS_DEFINITION(AMBEWorker::MsgTest, Message)
|
||||
|
||||
AMBEWorker::AMBEWorker() :
|
||||
m_running(false),
|
||||
m_currentGainIn(0),
|
||||
m_currentGainOut(0),
|
||||
m_upsamplerLastValue(0.0f),
|
||||
m_phase(0),
|
||||
m_upsampling(1),
|
||||
m_volume(1.0f)
|
||||
{
|
||||
m_audioBuffer.resize(48000);
|
||||
m_audioBufferFill = 0;
|
||||
m_audioFifo = 0;
|
||||
std::fill(m_dvAudioSamples, m_dvAudioSamples+SerialDV::MBE_AUDIO_BLOCK_SIZE, 0);
|
||||
setVolumeFactors();
|
||||
}
|
||||
|
||||
AMBEWorker::~AMBEWorker()
|
||||
{}
|
||||
|
||||
bool AMBEWorker::open(const std::string& deviceRef)
|
||||
{
|
||||
return m_dvController.open(deviceRef);
|
||||
}
|
||||
|
||||
void AMBEWorker::close()
|
||||
{
|
||||
m_dvController.close();
|
||||
}
|
||||
|
||||
void AMBEWorker::process()
|
||||
{
|
||||
m_running = true;
|
||||
qDebug("AMBEWorker::process: started");
|
||||
|
||||
while (m_running)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
}
|
||||
|
||||
qDebug("AMBEWorker::process: stopped");
|
||||
emit finished();
|
||||
}
|
||||
|
||||
void AMBEWorker::stop()
|
||||
{
|
||||
m_running = false;
|
||||
}
|
||||
|
||||
void AMBEWorker::handleInputMessages()
|
||||
{
|
||||
Message* message;
|
||||
m_audioBufferFill = 0;
|
||||
AudioFifo *audioFifo = 0;
|
||||
|
||||
while ((message = m_inputMessageQueue.pop()) != 0)
|
||||
{
|
||||
if (MsgMbeDecode::match(*message))
|
||||
{
|
||||
MsgMbeDecode *decodeMsg = (MsgMbeDecode *) message;
|
||||
int dBVolume = (decodeMsg->getVolumeIndex() - 30) / 4;
|
||||
float volume = pow(10.0, dBVolume / 10.0f);
|
||||
int upsampling = decodeMsg->getUpsampling();
|
||||
upsampling = upsampling > 6 ? 6 : upsampling < 1 ? 1 : upsampling;
|
||||
|
||||
if ((volume != m_volume) || (upsampling != m_upsampling))
|
||||
{
|
||||
m_volume = volume;
|
||||
m_upsampling = upsampling;
|
||||
setVolumeFactors();
|
||||
}
|
||||
|
||||
m_upsampleFilter.useHP(decodeMsg->getUseHP());
|
||||
|
||||
if (m_dvController.decode(m_dvAudioSamples, decodeMsg->getMbeFrame(), decodeMsg->getMbeRate()))
|
||||
{
|
||||
if (upsampling > 1) {
|
||||
upsample(upsampling, m_dvAudioSamples, SerialDV::MBE_AUDIO_BLOCK_SIZE, decodeMsg->getChannels());
|
||||
} else {
|
||||
noUpsample(m_dvAudioSamples, SerialDV::MBE_AUDIO_BLOCK_SIZE, decodeMsg->getChannels());
|
||||
}
|
||||
|
||||
audioFifo = decodeMsg->getAudioFifo();
|
||||
|
||||
if (audioFifo && (m_audioBufferFill >= m_audioBuffer.size() - 960))
|
||||
{
|
||||
uint res = audioFifo->write((const quint8*)&m_audioBuffer[0], m_audioBufferFill);
|
||||
|
||||
if (res != m_audioBufferFill) {
|
||||
qDebug("AMBEWorker::handleInputMessages: %u/%u audio samples written", res, m_audioBufferFill);
|
||||
}
|
||||
|
||||
m_audioBufferFill = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug("AMBEWorker::handleInputMessages: MsgMbeDecode: decode failed");
|
||||
}
|
||||
}
|
||||
|
||||
delete message;
|
||||
|
||||
if (m_inputMessageQueue.size() > 100)
|
||||
{
|
||||
qDebug("AMBEWorker::handleInputMessages: MsgMbeDecode: too many messages in queue. Flushing...");
|
||||
m_inputMessageQueue.clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (audioFifo)
|
||||
{
|
||||
uint res = audioFifo->write((const quint8*)&m_audioBuffer[0], m_audioBufferFill);
|
||||
|
||||
if (res != m_audioBufferFill) {
|
||||
qDebug("AMBEWorker::handleInputMessages: %u/%u audio samples written", res, m_audioBufferFill);
|
||||
}
|
||||
|
||||
m_audioBufferFill = 0;
|
||||
}
|
||||
|
||||
m_timestamp = QDateTime::currentDateTime();
|
||||
}
|
||||
|
||||
void AMBEWorker::pushMbeFrame(const unsigned char *mbeFrame,
|
||||
int mbeRateIndex,
|
||||
int mbeVolumeIndex,
|
||||
unsigned char channels,
|
||||
bool useHP,
|
||||
int upsampling,
|
||||
AudioFifo *audioFifo)
|
||||
{
|
||||
m_audioFifo = audioFifo;
|
||||
m_inputMessageQueue.push(MsgMbeDecode::create(mbeFrame, mbeRateIndex, mbeVolumeIndex, channels, useHP, upsampling, audioFifo));
|
||||
}
|
||||
|
||||
bool AMBEWorker::isAvailable()
|
||||
{
|
||||
if (m_audioFifo == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return m_timestamp.time().msecsTo(QDateTime::currentDateTime().time()) > 1000; // 1 second inactivity timeout
|
||||
}
|
||||
|
||||
bool AMBEWorker::hasFifo(AudioFifo *audioFifo)
|
||||
{
|
||||
return m_audioFifo == audioFifo;
|
||||
}
|
||||
|
||||
void AMBEWorker::upsample(int upsampling, short *in, int nbSamplesIn, unsigned char channels)
|
||||
{
|
||||
for (int i = 0; i < nbSamplesIn; i++)
|
||||
{
|
||||
//float cur = m_upsampleFilter.usesHP() ? m_upsampleFilter.runHP((float) m_compressor.compress(in[i])) : (float) m_compressor.compress(in[i]);
|
||||
float cur = m_upsampleFilter.usesHP() ? m_upsampleFilter.runHP((float) in[i]) : (float) in[i];
|
||||
float prev = m_upsamplerLastValue;
|
||||
qint16 upsample;
|
||||
|
||||
for (int j = 1; j <= upsampling; j++)
|
||||
{
|
||||
upsample = (qint16) m_upsampleFilter.runLP(cur*m_upsamplingFactors[j] + prev*m_upsamplingFactors[upsampling-j]);
|
||||
m_audioBuffer[m_audioBufferFill].l = channels & 1 ? m_compressor.compress(upsample) : 0;
|
||||
m_audioBuffer[m_audioBufferFill].r = (channels>>1) & 1 ? m_compressor.compress(upsample) : 0;
|
||||
|
||||
if (m_audioBufferFill < m_audioBuffer.size() - 1) {
|
||||
++m_audioBufferFill;
|
||||
}
|
||||
}
|
||||
|
||||
m_upsamplerLastValue = cur;
|
||||
}
|
||||
|
||||
if (m_audioBufferFill >= m_audioBuffer.size() - 1) {
|
||||
qDebug("AMBEWorker::upsample(%d): audio buffer is full check its size", upsampling);
|
||||
}
|
||||
}
|
||||
|
||||
void AMBEWorker::noUpsample(short *in, int nbSamplesIn, unsigned char channels)
|
||||
{
|
||||
for (int i = 0; i < nbSamplesIn; i++)
|
||||
{
|
||||
float cur = m_upsampleFilter.usesHP() ? m_upsampleFilter.runHP((float) in[i]) : (float) in[i];
|
||||
m_audioBuffer[m_audioBufferFill].l = channels & 1 ? cur*m_upsamplingFactors[0] : 0;
|
||||
m_audioBuffer[m_audioBufferFill].r = (channels>>1) & 1 ? cur*m_upsamplingFactors[0] : 0;
|
||||
|
||||
if (m_audioBufferFill < m_audioBuffer.size() - 1) {
|
||||
++m_audioBufferFill;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_audioBufferFill >= m_audioBuffer.size() - 1) {
|
||||
qDebug("AMBEWorker::noUpsample: audio buffer is full check its size");
|
||||
}
|
||||
}
|
||||
|
||||
void AMBEWorker::setVolumeFactors()
|
||||
{
|
||||
m_upsamplingFactors[0] = m_volume;
|
||||
|
||||
for (int i = 1; i <= m_upsampling; i++) {
|
||||
m_upsamplingFactors[i] = (i*m_volume) / (float) m_upsampling;
|
||||
}
|
||||
}
|
156
plugins/feature/ambe/ambeworker.h
Normal file
156
plugins/feature/ambe/ambeworker.h
Normal file
@ -0,0 +1,156 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2019 F4EXB //
|
||||
// written by Edouard Griffiths //
|
||||
// //
|
||||
// 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 SDRBASE_AMBE_AMBEWORKER_H_
|
||||
#define SDRBASE_AMBE_AMBEWORKER_H_
|
||||
|
||||
#include <QObject>
|
||||
#include <QDebug>
|
||||
#include <QDateTime>
|
||||
|
||||
#include "export.h"
|
||||
#include "dvcontroller.h"
|
||||
|
||||
#include "util/messagequeue.h"
|
||||
#include "util/message.h"
|
||||
#include "dsp/filtermbe.h"
|
||||
#include "dsp/dsptypes.h"
|
||||
#include "audio/audiocompressor.h"
|
||||
|
||||
class AudioFifo;
|
||||
|
||||
class SDRBASE_API AMBEWorker : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
class MsgTest : public Message
|
||||
{
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
public:
|
||||
static MsgTest* create() { return new MsgTest(); }
|
||||
private:
|
||||
MsgTest() {}
|
||||
};
|
||||
|
||||
class MsgMbeDecode : public Message
|
||||
{
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
public:
|
||||
const unsigned char *getMbeFrame() const { return m_mbeFrame; }
|
||||
SerialDV::DVRate getMbeRate() const { return m_mbeRate; }
|
||||
int getVolumeIndex() const { return m_volumeIndex; }
|
||||
unsigned char getChannels() const { return m_channels % 4; }
|
||||
bool getUseHP() const { return m_useHP; }
|
||||
int getUpsampling() const { return m_upsampling; }
|
||||
AudioFifo *getAudioFifo() { return m_audioFifo; }
|
||||
|
||||
static MsgMbeDecode* create(
|
||||
const unsigned char *mbeFrame,
|
||||
int mbeRateIndex,
|
||||
int volumeIndex,
|
||||
unsigned char channels,
|
||||
bool useHP,
|
||||
int upsampling,
|
||||
AudioFifo *audioFifo)
|
||||
{
|
||||
return new MsgMbeDecode(mbeFrame, (SerialDV::DVRate) mbeRateIndex, volumeIndex, channels, useHP, upsampling, audioFifo);
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned char m_mbeFrame[SerialDV::MBE_FRAME_MAX_LENGTH_BYTES];
|
||||
SerialDV::DVRate m_mbeRate;
|
||||
int m_volumeIndex;
|
||||
unsigned char m_channels;
|
||||
bool m_useHP;
|
||||
int m_upsampling;
|
||||
AudioFifo *m_audioFifo;
|
||||
|
||||
MsgMbeDecode(const unsigned char *mbeFrame,
|
||||
SerialDV::DVRate mbeRate,
|
||||
int volumeIndex,
|
||||
unsigned char channels,
|
||||
bool useHP,
|
||||
int upsampling,
|
||||
AudioFifo *audioFifo) :
|
||||
Message(),
|
||||
m_mbeRate(mbeRate),
|
||||
m_volumeIndex(volumeIndex),
|
||||
m_channels(channels),
|
||||
m_useHP(useHP),
|
||||
m_upsampling(upsampling),
|
||||
m_audioFifo(audioFifo)
|
||||
{
|
||||
memcpy((void *) m_mbeFrame, (const void *) mbeFrame, SerialDV::DVController::getNbMbeBytes(m_mbeRate));
|
||||
}
|
||||
};
|
||||
|
||||
AMBEWorker();
|
||||
~AMBEWorker();
|
||||
|
||||
void pushMbeFrame(const unsigned char *mbeFrame,
|
||||
int mbeRateIndex,
|
||||
int mbeVolumeIndex,
|
||||
unsigned char channels,
|
||||
bool useHP,
|
||||
int upsampling,
|
||||
AudioFifo *audioFifo);
|
||||
|
||||
bool open(const std::string& deviceRef); //!< Either serial device or ip:port
|
||||
void close();
|
||||
void process();
|
||||
void stop();
|
||||
bool isAvailable();
|
||||
bool hasFifo(AudioFifo *audioFifo);
|
||||
|
||||
void postTest()
|
||||
{
|
||||
//emit inputMessageReady();
|
||||
m_inputMessageQueue.push(MsgTest::create());
|
||||
}
|
||||
|
||||
MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication
|
||||
|
||||
signals:
|
||||
void finished();
|
||||
|
||||
public slots:
|
||||
void handleInputMessages();
|
||||
|
||||
private:
|
||||
void upsample(int upsampling, short *in, int nbSamplesIn, unsigned char channels);
|
||||
void noUpsample(short *in, int nbSamplesIn, unsigned char channels);
|
||||
void setVolumeFactors();
|
||||
|
||||
SerialDV::DVController m_dvController;
|
||||
AudioFifo *m_audioFifo;
|
||||
QDateTime m_timestamp;
|
||||
volatile bool m_running;
|
||||
int m_currentGainIn;
|
||||
int m_currentGainOut;
|
||||
short m_dvAudioSamples[SerialDV::MBE_AUDIO_BLOCK_SIZE];
|
||||
AudioVector m_audioBuffer;
|
||||
uint m_audioBufferFill;
|
||||
float m_upsamplerLastValue;
|
||||
float m_phase;
|
||||
MBEAudioInterpolatorFilter m_upsampleFilter;
|
||||
int m_upsampling;
|
||||
float m_volume;
|
||||
float m_upsamplingFactors[7];
|
||||
AudioCompressor m_compressor;
|
||||
};
|
||||
|
||||
#endif // SDRBASE_AMBE_AMBEWORKER_H_
|
@ -44,3 +44,4 @@ MESSAGE_CLASS_DEFINITION(DSPSignalNotification, Message)
|
||||
MESSAGE_CLASS_DEFINITION(DSPMIMOSignalNotification, Message)
|
||||
MESSAGE_CLASS_DEFINITION(DSPConfigureChannelizer, Message)
|
||||
MESSAGE_CLASS_DEFINITION(DSPConfigureAudio, Message)
|
||||
MESSAGE_CLASS_DEFINITION(DSPPushMbeFrame, Message)
|
||||
|
@ -345,4 +345,45 @@ private:
|
||||
AudioType m_autioType;
|
||||
};
|
||||
|
||||
class SDRBASE_API DSPPushMbeFrame : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
DSPPushMbeFrame(
|
||||
const unsigned char *mbeFrame,
|
||||
int mbeRateIndex,
|
||||
int mbeVolumeIndex,
|
||||
unsigned char channels,
|
||||
bool useHP,
|
||||
int upsampling,
|
||||
AudioFifo *audioFifo
|
||||
) :
|
||||
Message(),
|
||||
m_mbeFrame(mbeFrame),
|
||||
m_mbeRateIndex(mbeRateIndex),
|
||||
m_mbeVolumeIndex(mbeVolumeIndex),
|
||||
m_channels(channels),
|
||||
m_useHP(useHP),
|
||||
m_upsampling(upsampling),
|
||||
m_audioFifo(audioFifo)
|
||||
{ }
|
||||
|
||||
const unsigned char * getMbeFrame() const { return m_mbeFrame; }
|
||||
int getMbeRateIndex() const { return m_mbeRateIndex; }
|
||||
int getMbeVolumeIndex() const { return m_mbeVolumeIndex; }
|
||||
unsigned char getChannels() const { return m_channels; }
|
||||
bool getUseHP() const { return m_useHP; }
|
||||
int getUpsampling() const { return m_upsampling; }
|
||||
AudioFifo *getAudioFifo() const { return m_audioFifo; }
|
||||
|
||||
private:
|
||||
const unsigned char *m_mbeFrame;
|
||||
int m_mbeRateIndex;
|
||||
int m_mbeVolumeIndex;
|
||||
unsigned char m_channels;
|
||||
bool m_useHP;
|
||||
int m_upsampling;
|
||||
AudioFifo *m_audioFifo;
|
||||
};
|
||||
|
||||
#endif // INCLUDE_DSPCOMMANDS_H
|
||||
|
Loading…
Reference in New Issue
Block a user