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

PTT: vox (1)

This commit is contained in:
f4exb 2021-12-29 21:20:06 +01:00
parent 7de805b433
commit 79ac722e79
21 changed files with 684 additions and 16 deletions

View File

@ -1220,6 +1220,7 @@ int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index
HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev) HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev)
{ {
(void) dev;
return NULL; return NULL;
} }

View File

@ -1214,6 +1214,7 @@ int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index
HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev) HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev)
{ {
(void) dev;
return NULL; return NULL;
} }

View File

@ -147,6 +147,13 @@ bool SimplePTT::deserialize(const QByteArray& data)
} }
} }
void SimplePTT::getAudioPeak(float& peak)
{
if (m_worker) {
m_worker->getAudioPeak(peak);
}
}
void SimplePTT::applySettings(const SimplePTTSettings& settings, bool force) void SimplePTT::applySettings(const SimplePTTSettings& settings, bool force)
{ {
qDebug() << "SimplePTT::applySettings:" qDebug() << "SimplePTT::applySettings:"
@ -156,6 +163,11 @@ void SimplePTT::applySettings(const SimplePTTSettings& settings, bool force)
<< " m_txDeviceSetIndex: " << settings.m_txDeviceSetIndex << " m_txDeviceSetIndex: " << settings.m_txDeviceSetIndex
<< " m_rx2TxDelayMs: " << settings.m_rx2TxDelayMs << " m_rx2TxDelayMs: " << settings.m_rx2TxDelayMs
<< " m_tx2RxDelayMs: " << settings.m_tx2RxDelayMs << " m_tx2RxDelayMs: " << settings.m_tx2RxDelayMs
<< " m_vox: " << settings.m_vox
<< " m_voxEnable: " << settings.m_voxEnable
<< " m_audioDeviceName: " << settings.m_audioDeviceName
<< " m_voxLevel: " << settings.m_voxLevel
<< " m_voxHold: " << settings.m_voxHold
<< " force: " << force; << " force: " << force;
QList<QString> reverseAPIKeys; QList<QString> reverseAPIKeys;
@ -178,6 +190,18 @@ void SimplePTT::applySettings(const SimplePTTSettings& settings, bool force)
if ((m_settings.m_tx2RxDelayMs != settings.m_tx2RxDelayMs) || force) { if ((m_settings.m_tx2RxDelayMs != settings.m_tx2RxDelayMs) || force) {
reverseAPIKeys.append("tx2RxDelayMs"); reverseAPIKeys.append("tx2RxDelayMs");
} }
if ((m_settings.m_vox != settings.m_vox) || force) {
reverseAPIKeys.append("vox");
}
if ((m_settings.m_voxEnable != settings.m_voxEnable) || force) {
reverseAPIKeys.append("voxEnable");
}
if ((m_settings.m_voxHold != settings.m_voxHold) || force) {
reverseAPIKeys.append("voxHold");
}
if ((m_settings.m_voxLevel != settings.m_voxLevel) || force) {
reverseAPIKeys.append("voxLevel");
}
SimplePTTWorker::MsgConfigureSimplePTTWorker *msg = SimplePTTWorker::MsgConfigureSimplePTTWorker::create( SimplePTTWorker::MsgConfigureSimplePTTWorker *msg = SimplePTTWorker::MsgConfigureSimplePTTWorker::create(
settings, force settings, force
@ -321,6 +345,10 @@ void SimplePTT::webapiFormatFeatureSettings(
response.getSimplePttSettings()->setTxDeviceSetIndex(settings.m_txDeviceSetIndex); response.getSimplePttSettings()->setTxDeviceSetIndex(settings.m_txDeviceSetIndex);
response.getSimplePttSettings()->setRx2TxDelayMs(settings.m_rx2TxDelayMs); response.getSimplePttSettings()->setRx2TxDelayMs(settings.m_rx2TxDelayMs);
response.getSimplePttSettings()->setTx2RxDelayMs(settings.m_tx2RxDelayMs); response.getSimplePttSettings()->setTx2RxDelayMs(settings.m_tx2RxDelayMs);
response.getSimplePttSettings()->setVox(settings.m_vox ? 1 : 0);
response.getSimplePttSettings()->setVoxEnable(settings.m_voxEnable ? 1 : 0);
response.getSimplePttSettings()->setVoxHold(settings.m_voxHold);
response.getSimplePttSettings()->setVoxLevel(settings.m_voxLevel);
response.getSimplePttSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0); response.getSimplePttSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
@ -358,6 +386,18 @@ void SimplePTT::webapiUpdateFeatureSettings(
if (featureSettingsKeys.contains("tx2RxDelayMs")) { if (featureSettingsKeys.contains("tx2RxDelayMs")) {
settings.m_tx2RxDelayMs = response.getSimplePttSettings()->getTx2RxDelayMs(); settings.m_tx2RxDelayMs = response.getSimplePttSettings()->getTx2RxDelayMs();
} }
if (featureSettingsKeys.contains("vox")) {
settings.m_vox = response.getSimplePttSettings()->getVox() != 0;
}
if (featureSettingsKeys.contains("voxEnable")) {
settings.m_voxEnable = response.getSimplePttSettings()->getVoxEnable() != 0;
}
if (featureSettingsKeys.contains("voxHold")) {
settings.m_voxHold = response.getSimplePttSettings()->getVoxHold();
}
if (featureSettingsKeys.contains("voxLevel")) {
settings.m_voxLevel = response.getSimplePttSettings()->getVoxLevel();
}
if (featureSettingsKeys.contains("useReverseAPI")) { if (featureSettingsKeys.contains("useReverseAPI")) {
settings.m_useReverseAPI = response.getSimplePttSettings()->getUseReverseApi() != 0; settings.m_useReverseAPI = response.getSimplePttSettings()->getUseReverseApi() != 0;
} }
@ -410,6 +450,18 @@ void SimplePTT::webapiReverseSendSettings(QList<QString>& channelSettingsKeys, c
if (channelSettingsKeys.contains("tx2RxDelayMs") || force) { if (channelSettingsKeys.contains("tx2RxDelayMs") || force) {
swgSimplePTTSettings->setTx2RxDelayMs(settings.m_tx2RxDelayMs); swgSimplePTTSettings->setTx2RxDelayMs(settings.m_tx2RxDelayMs);
} }
if (channelSettingsKeys.contains("vox") || force) {
swgSimplePTTSettings->setVox(settings.m_vox ? 1 : 0);
}
if (channelSettingsKeys.contains("voxEnable") || force) {
swgSimplePTTSettings->setVoxEnable(settings.m_voxEnable ? 1 : 0);
}
if (channelSettingsKeys.contains("voxHold") || force) {
swgSimplePTTSettings->setVoxHold(settings.m_voxHold);
}
if (channelSettingsKeys.contains("voxLevel") || force) {
swgSimplePTTSettings->setVoxLevel(settings.m_voxLevel);
}
QString channelSettingsURL = QString("http://%1:%2/sdrangel/featureset/%3/feature/%4/settings") QString channelSettingsURL = QString("http://%1:%2/sdrangel/featureset/%3/feature/%4/settings")
.arg(settings.m_reverseAPIAddress) .arg(settings.m_reverseAPIAddress)

View File

@ -142,6 +142,8 @@ public:
const QStringList& featureSettingsKeys, const QStringList& featureSettingsKeys,
SWGSDRangel::SWGFeatureSettings& response); SWGSDRangel::SWGFeatureSettings& response);
void getAudioPeak(float& peak);
static const char* const m_featureIdURI; static const char* const m_featureIdURI;
static const char* const m_featureId; static const char* const m_featureId;

View File

@ -19,7 +19,11 @@
#include "feature/featureuiset.h" #include "feature/featureuiset.h"
#include "gui/basicfeaturesettingsdialog.h" #include "gui/basicfeaturesettingsdialog.h"
#include "gui/crightclickenabler.h"
#include "gui/audioselectdialog.h"
#include "dsp/dspengine.h"
#include "device/deviceset.h" #include "device/deviceset.h"
#include "util/db.h"
#include "maincore.h" #include "maincore.h"
#include "ui_simplepttgui.h" #include "ui_simplepttgui.h"
@ -89,6 +93,19 @@ bool SimplePTTGUI::handleMessage(const Message& message)
return true; return true;
} }
else if (SimplePTTReport::MsgVox::match(message))
{
qDebug("SimplePTTGUI::handleMessage: SimplePTTReport::MsgVox");
const SimplePTTReport::MsgVox& cfg = (const SimplePTTReport::MsgVox&) message;
if (cfg.getVox()) {
ui->voxLevelText->setStyleSheet("QLabel { background-color : green; }");
} else {
ui->voxLevelText->setStyleSheet("QLabel { background:rgb(79,79,79); }");
}
return true;
}
else if (SimplePTT::MsgPTT::match(message)) else if (SimplePTT::MsgPTT::match(message))
{ {
qDebug("SimplePTTGUI::handleMessage: SimplePTT::MsgPTT"); qDebug("SimplePTTGUI::handleMessage: SimplePTT::MsgPTT");
@ -145,9 +162,11 @@ SimplePTTGUI::SimplePTTGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Fea
connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &)));
connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
CRightClickEnabler *voxRightClickEnabler = new CRightClickEnabler(ui->vox);
connect(voxRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(audioSelect()));
connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus()));
m_statusTimer.start(1000); m_statusTimer.start(500);
m_statusTooltips.push_back("Idle"); // 0 - all off m_statusTooltips.push_back("Idle"); // 0 - all off
m_statusTooltips.push_back("Rx on"); // 1 - Rx on m_statusTooltips.push_back("Rx on"); // 1 - Rx on
@ -180,6 +199,11 @@ void SimplePTTGUI::displaySettings()
ui->rxtxDelay->setValue(m_settings.m_rx2TxDelayMs); ui->rxtxDelay->setValue(m_settings.m_rx2TxDelayMs);
ui->txrxDelay->setValue(m_settings.m_tx2RxDelayMs); ui->txrxDelay->setValue(m_settings.m_tx2RxDelayMs);
restoreState(m_settings.m_rollupState); restoreState(m_settings.m_rollupState);
ui->vox->setChecked(m_settings.m_vox);
ui->voxEnable->setChecked(m_settings.m_voxEnable);
ui->voxLevel->setValue(m_settings.m_voxLevel);
ui->voxLevelText->setText(tr("%1").arg(m_settings.m_voxLevel));
ui->voxHold->setValue(m_settings.m_voxHold);
blockApplySettings(false); blockApplySettings(false);
} }
@ -354,6 +378,31 @@ void SimplePTTGUI::on_ptt_toggled(bool checked)
applyPTT(checked); applyPTT(checked);
} }
void SimplePTTGUI::on_vox_toggled(bool checked)
{
m_settings.m_vox = checked;
applySettings();
}
void SimplePTTGUI::on_voxEnable_clicked(bool checked)
{
m_settings.m_voxEnable = checked;
applySettings();
}
void SimplePTTGUI::on_voxLevel_valueChanged(int value)
{
m_settings.m_voxLevel = value;
ui->voxLevelText->setText(tr("%1dB").arg(m_settings.m_voxLevel));
applySettings();
}
void SimplePTTGUI::on_voxHold_valueChanged(int value)
{
m_settings.m_voxHold = value;
applySettings();
}
void SimplePTTGUI::updateStatus() void SimplePTTGUI::updateStatus()
{ {
int state = m_simplePTT->getState(); int state = m_simplePTT->getState();
@ -381,6 +430,14 @@ void SimplePTTGUI::updateStatus()
m_lastFeatureState = state; m_lastFeatureState = state;
} }
if (m_settings.m_vox)
{
float peak;
m_simplePTT->getAudioPeak(peak);
int peakDB = CalcDb::dbPower(peak);
ui->audioPeak->setText(tr("%1 dB").arg(peakDB));
}
} }
void SimplePTTGUI::applySettings(bool force) void SimplePTTGUI::applySettings(bool force)
@ -400,3 +457,16 @@ void SimplePTTGUI::applyPTT(bool tx)
m_simplePTT->getInputMessageQueue()->push(message); m_simplePTT->getInputMessageQueue()->push(message);
} }
} }
void SimplePTTGUI::audioSelect()
{
qDebug("SimplePTTGUI::audioSelect");
AudioSelectDialog audioSelect(DSPEngine::instance()->getAudioDeviceManager(), m_settings.m_audioDeviceName);
audioSelect.exec();
if (audioSelect.m_selected)
{
m_settings.m_audioDeviceName = audioSelect.m_audioDeviceName;
applySettings();
}
}

View File

@ -81,7 +81,12 @@ private slots:
void on_rxtxDelay_valueChanged(int value); void on_rxtxDelay_valueChanged(int value);
void on_txrxDelay_valueChanged(int value); void on_txrxDelay_valueChanged(int value);
void on_ptt_toggled(bool checked); void on_ptt_toggled(bool checked);
void on_vox_toggled(bool checked);
void on_voxEnable_clicked(bool checked);
void on_voxLevel_valueChanged(int value);
void on_voxHold_valueChanged(int value);
void updateStatus(); void updateStatus();
void audioSelect();
}; };

View File

@ -92,6 +92,7 @@
</property> </property>
<property name="font"> <property name="font">
<font> <font>
<family>Liberation Sans</family>
<pointsize>20</pointsize> <pointsize>20</pointsize>
<weight>75</weight> <weight>75</weight>
<bold>true</bold> <bold>true</bold>
@ -132,6 +133,148 @@
</item> </item>
</layout> </layout>
</item> </item>
<item>
<layout class="QHBoxLayout" name="voxLayout">
<item>
<widget class="ButtonSwitch" name="vox">
<property name="minimumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="toolTip">
<string>Toggle vox system - right click to select audio device</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/audio_mic.png</normaloff>:/audio_mic.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="voxEnable">
<property name="toolTip">
<string>Enable vox to control PTT</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QDial" name="voxLevel">
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="toolTip">
<string>Vox threshold (dB)</string>
</property>
<property name="minimum">
<number>-99</number>
</property>
<property name="maximum">
<number>0</number>
</property>
<property name="singleStep">
<number>1</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="value">
<number>-20</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="voxLevelText">
<property name="minimumSize">
<size>
<width>30</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>-20</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="audioPeak">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Audio signal peak in dB (with Vox on)</string>
</property>
<property name="toolTipDuration">
<number>14</number>
</property>
<property name="text">
<string>-100 dB</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="voxHold">
<property name="toolTip">
<string>Vox delay (ms)</string>
</property>
<property name="minimum">
<number>200</number>
</property>
<property name="maximum">
<number>2000</number>
</property>
<property name="singleStep">
<number>100</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="voxHoldUnits">
<property name="text">
<string>ms</string>
</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> <item>
<layout class="QHBoxLayout" name="localDeviceLayout"> <layout class="QHBoxLayout" name="localDeviceLayout">
<item> <item>

View File

@ -18,6 +18,7 @@
#include "simplepttreport.h" #include "simplepttreport.h"
MESSAGE_CLASS_DEFINITION(SimplePTTReport::MsgRadioState, Message) MESSAGE_CLASS_DEFINITION(SimplePTTReport::MsgRadioState, Message)
MESSAGE_CLASS_DEFINITION(SimplePTTReport::MsgVox, Message)
SimplePTTReport::SimplePTTReport() SimplePTTReport::SimplePTTReport()
{} {}

View File

@ -34,8 +34,7 @@ public:
public: public:
RadioState getState() const { return m_state; } RadioState getState() const { return m_state; }
static MsgRadioState* create(RadioState state) static MsgRadioState* create(RadioState state) {
{
return new MsgRadioState(state); return new MsgRadioState(state);
} }
@ -48,6 +47,25 @@ public:
{ } { }
}; };
class MsgVox : public Message {
MESSAGE_CLASS_DECLARATION
public:
bool getVox() const { return m_vox; }
static MsgVox* create(bool vox) {
return new MsgVox(vox);
}
private:
bool m_vox;
MsgVox(bool vox) :
Message(),
m_vox(vox)
{}
};
SimplePTTReport(); SimplePTTReport();
~SimplePTTReport(); ~SimplePTTReport();
}; };

View File

@ -19,6 +19,7 @@
#include "util/simpleserializer.h" #include "util/simpleserializer.h"
#include "settings/serializable.h" #include "settings/serializable.h"
#include "audio/audiodevicemanager.h"
#include "simplepttsettings.h" #include "simplepttsettings.h"
@ -35,6 +36,11 @@ void SimplePTTSettings::resetToDefaults()
m_txDeviceSetIndex = -1; m_txDeviceSetIndex = -1;
m_rx2TxDelayMs = 100; m_rx2TxDelayMs = 100;
m_tx2RxDelayMs = 100; m_tx2RxDelayMs = 100;
m_audioDeviceName = AudioDeviceManager::m_defaultDeviceName;
m_voxLevel = -20;
m_voxHold = 500;
m_vox = false;
m_voxEnable = false;
m_useReverseAPI = false; m_useReverseAPI = false;
m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIAddress = "127.0.0.1";
m_reverseAPIPort = 8888; m_reverseAPIPort = 8888;
@ -58,6 +64,11 @@ QByteArray SimplePTTSettings::serialize() const
s.writeU32(10, m_reverseAPIFeatureSetIndex); s.writeU32(10, m_reverseAPIFeatureSetIndex);
s.writeU32(11, m_reverseAPIFeatureIndex); s.writeU32(11, m_reverseAPIFeatureIndex);
s.writeBlob(12, m_rollupState); s.writeBlob(12, m_rollupState);
s.writeString(13, m_audioDeviceName);
s.writeS32(14, m_voxLevel);
s.writeBool(15, m_vox);
s.writeBool(16, m_voxEnable);
s.writeS32(17, m_voxHold);
return s.final(); return s.final();
} }
@ -99,6 +110,11 @@ bool SimplePTTSettings::deserialize(const QByteArray& data)
d.readU32(11, &utmp, 0); d.readU32(11, &utmp, 0);
m_reverseAPIFeatureIndex = utmp > 99 ? 99 : utmp; m_reverseAPIFeatureIndex = utmp > 99 ? 99 : utmp;
d.readBlob(12, &m_rollupState); d.readBlob(12, &m_rollupState);
d.readString(13, &m_audioDeviceName, AudioDeviceManager::m_defaultDeviceName);
d.readS32(14, &m_voxLevel, -20);
d.readBool(15, &m_vox, false);
d.readBool(16, &m_voxEnable, false);
d.readS32(16, &m_voxHold, 500);
return true; return true;
} }

View File

@ -31,6 +31,11 @@ struct SimplePTTSettings
int m_txDeviceSetIndex; int m_txDeviceSetIndex;
unsigned int m_rx2TxDelayMs; unsigned int m_rx2TxDelayMs;
unsigned int m_tx2RxDelayMs; unsigned int m_tx2RxDelayMs;
QString m_audioDeviceName; //!< for Vox
int m_voxLevel; //!< Vox threshold level in dB
int m_voxHold; //!< Vox hold in milliseconds
bool m_vox;
bool m_voxEnable;
bool m_useReverseAPI; bool m_useReverseAPI;
QString m_reverseAPIAddress; QString m_reverseAPIAddress;
uint16_t m_reverseAPIPort; uint16_t m_reverseAPIPort;

View File

@ -22,6 +22,9 @@
#include "SWGErrorResponse.h" #include "SWGErrorResponse.h"
#include "webapi/webapiadapterinterface.h" #include "webapi/webapiadapterinterface.h"
#include "audio/audiodevicemanager.h"
#include "dsp/dspengine.h"
#include "util/db.h"
#include "simplepttreport.h" #include "simplepttreport.h"
#include "simplepttworker.h" #include "simplepttworker.h"
@ -34,9 +37,16 @@ SimplePTTWorker::SimplePTTWorker(WebAPIAdapterInterface *webAPIAdapterInterface)
m_msgQueueToGUI(nullptr), m_msgQueueToGUI(nullptr),
m_running(false), m_running(false),
m_tx(false), m_tx(false),
m_audioFifo(12000),
m_audioSampleRate(48000),
m_voxLevel(1.0),
m_voxHoldCount(0),
m_voxState(false),
m_updateTimer(this), m_updateTimer(this),
m_mutex(QMutex::Recursive) m_mutex(QMutex::Recursive)
{ {
m_audioReadBuffer.resize(16384);
m_audioReadBufferFill = 0;
qDebug("SimplePTTWorker::SimplePTTWorker"); qDebug("SimplePTTWorker::SimplePTTWorker");
connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware())); connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware()));
} }
@ -44,6 +54,8 @@ SimplePTTWorker::SimplePTTWorker(WebAPIAdapterInterface *webAPIAdapterInterface)
SimplePTTWorker::~SimplePTTWorker() SimplePTTWorker::~SimplePTTWorker()
{ {
m_inputMessageQueue.clear(); m_inputMessageQueue.clear();
AudioDeviceManager *audioDeviceManager = DSPEngine::instance()->getAudioDeviceManager();
audioDeviceManager->removeAudioSource(&m_audioFifo);
} }
void SimplePTTWorker::reset() void SimplePTTWorker::reset()
@ -115,7 +127,48 @@ void SimplePTTWorker::applySettings(const SimplePTTSettings& settings, bool forc
<< " m_txDeviceSetIndex: " << settings.m_txDeviceSetIndex << " m_txDeviceSetIndex: " << settings.m_txDeviceSetIndex
<< " m_rx2TxDelayMs: " << settings.m_rx2TxDelayMs << " m_rx2TxDelayMs: " << settings.m_rx2TxDelayMs
<< " m_tx2RxDelayMs: " << settings.m_tx2RxDelayMs << " m_tx2RxDelayMs: " << settings.m_tx2RxDelayMs
<< " m_vox: " << settings.m_vox
<< " m_voxEnable: " << settings.m_voxEnable
<< " m_audioDeviceName: " << settings.m_audioDeviceName
<< " m_voxLevel: " << settings.m_voxLevel
<< " m_voxHold: " << settings.m_voxHold
<< " force: " << force; << " force: " << force;
if ((settings.m_audioDeviceName != m_settings.m_audioDeviceName) || force)
{
QMutexLocker mlock(&m_mutex);
AudioDeviceManager *audioDeviceManager = DSPEngine::instance()->getAudioDeviceManager();
int audioDeviceIndex = audioDeviceManager->getInputDeviceIndex(settings.m_audioDeviceName);
audioDeviceManager->removeAudioSource(&m_audioFifo);
audioDeviceManager->addAudioSource(&m_audioFifo, getInputMessageQueue(), audioDeviceIndex);
m_audioSampleRate = audioDeviceManager->getInputSampleRate(audioDeviceIndex);
}
if ((settings.m_vox != m_settings.m_vox) || force)
{
QMutexLocker mlock(&m_mutex);
m_voxHoldCount = 0;
m_audioReadBufferFill = 0;
m_voxState = false;
if (m_msgQueueToGUI)
{
SimplePTTReport::MsgVox *msg = SimplePTTReport::MsgVox::create(false);
m_msgQueueToGUI->push(msg);
}
if (settings.m_vox) {
connect(&m_audioFifo, SIGNAL(dataReady()), this, SLOT(handleAudio()));
} else {
disconnect(&m_audioFifo, SIGNAL(dataReady()), this, SLOT(handleAudio()));
}
}
if ((settings.m_voxLevel != m_settings.m_voxLevel) || force)
{
m_voxLevel = CalcDb::powerFromdB(settings.m_voxLevel);
}
m_settings = settings; m_settings = settings;
} }
@ -126,29 +179,29 @@ void SimplePTTWorker::sendPTT(bool tx)
bool switchedOff = false; bool switchedOff = false;
m_mutex.lock(); m_mutex.lock();
if (tx) if (tx)
{ {
if (m_settings.m_rxDeviceSetIndex >= 0) if (m_settings.m_rxDeviceSetIndex >= 0)
{ {
m_tx = false; m_tx = false;
switchedOff = turnDevice(false); switchedOff = turnDevice(false);
} }
if (m_settings.m_txDeviceSetIndex >= 0) if (m_settings.m_txDeviceSetIndex >= 0)
{ {
m_tx = true; m_tx = true;
m_updateTimer.start(m_settings.m_rx2TxDelayMs); m_updateTimer.start(m_settings.m_rx2TxDelayMs);
} }
} }
else else
{ {
if (m_settings.m_txDeviceSetIndex >= 0) if (m_settings.m_txDeviceSetIndex >= 0)
{ {
m_tx = true; m_tx = true;
switchedOff = turnDevice(false); switchedOff = turnDevice(false);
} }
if (m_settings.m_rxDeviceSetIndex >= 0) if (m_settings.m_rxDeviceSetIndex >= 0)
{ {
m_tx = false; m_tx = false;
m_updateTimer.start(m_settings.m_tx2RxDelayMs); m_updateTimer.start(m_settings.m_tx2RxDelayMs);
@ -190,7 +243,7 @@ bool SimplePTTWorker::turnDevice(bool on)
SWGSDRangel::SWGDeviceState response; SWGSDRangel::SWGDeviceState response;
SWGSDRangel::SWGErrorResponse error; SWGSDRangel::SWGErrorResponse error;
int httpCode; int httpCode;
if (on) { if (on) {
httpCode = m_webAPIAdapterInterface->devicesetDeviceRunPost( httpCode = m_webAPIAdapterInterface->devicesetDeviceRunPost(
m_tx ? m_settings.m_txDeviceSetIndex : m_settings.m_rxDeviceSetIndex, response, error); m_tx ? m_settings.m_txDeviceSetIndex : m_settings.m_rxDeviceSetIndex, response, error);
@ -208,4 +261,63 @@ bool SimplePTTWorker::turnDevice(bool on)
qWarning("SimplePTTWorker::turnDevice: error: %s", qPrintable(*error.getMessage())); qWarning("SimplePTTWorker::turnDevice: error: %s", qPrintable(*error.getMessage()));
return false; return false;
} }
} }
void SimplePTTWorker::handleAudio()
{
unsigned int nbRead;
QMutexLocker mlock(&m_mutex);
while ((nbRead = m_audioFifo.read(reinterpret_cast<quint8*>(&m_audioReadBuffer[m_audioReadBufferFill]), 4096)) != 0)
{
if (m_audioReadBufferFill + nbRead + 4096 < m_audioReadBuffer.size())
{
m_audioReadBufferFill += nbRead;
}
else
{
bool voxState = m_voxState;
for (const auto &it : m_audioReadBuffer)
{
std::complex<float> za{it.l / 32768.0f, it.r / 32768.0f};
float magSq = std::norm(za);
if (magSq > m_audioMagsqPeak) {
m_audioMagsqPeak = magSq;
}
if (magSq > m_voxLevel)
{
voxState = true;
m_voxHoldCount = 0;
}
else
{
if (m_voxHoldCount < (m_settings.m_voxHold * m_audioSampleRate) / 1000) {
m_voxHoldCount++;
} else {
voxState = false;
}
}
if (voxState != m_voxState)
{
if (m_settings.m_voxEnable) {
sendPTT(voxState);
}
if (m_msgQueueToGUI)
{
SimplePTTReport::MsgVox *msg = SimplePTTReport::MsgVox::create(voxState);
m_msgQueueToGUI->push(msg);
}
m_voxState = voxState;
}
}
m_audioReadBufferFill = 0;
}
}
}

View File

@ -23,6 +23,7 @@
#include "util/message.h" #include "util/message.h"
#include "util/messagequeue.h" #include "util/messagequeue.h"
#include "audio/audiofifo.h"
#include "simplepttsettings.h" #include "simplepttsettings.h"
@ -83,6 +84,12 @@ public:
MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
void setMessageQueueToGUI(MessageQueue *messageQueue) { m_msgQueueToGUI = messageQueue; } void setMessageQueueToGUI(MessageQueue *messageQueue) { m_msgQueueToGUI = messageQueue; }
void getAudioPeak(float& peak)
{
peak = m_audioMagsqPeak;
m_audioMagsqPeak = 0;
}
private: private:
WebAPIAdapterInterface *m_webAPIAdapterInterface; WebAPIAdapterInterface *m_webAPIAdapterInterface;
MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication
@ -90,6 +97,14 @@ private:
SimplePTTSettings m_settings; SimplePTTSettings m_settings;
bool m_running; bool m_running;
bool m_tx; bool m_tx;
AudioFifo m_audioFifo;
AudioVector m_audioReadBuffer;
unsigned int m_audioReadBufferFill;
int m_audioSampleRate;
float m_audioMagsqPeak;
float m_voxLevel;
int m_voxHoldCount;
bool m_voxState;
QTimer m_updateTimer; QTimer m_updateTimer;
QMutex m_mutex; QMutex m_mutex;
@ -101,6 +116,7 @@ private:
private slots: private slots:
void handleInputMessages(); void handleInputMessages();
void updateHardware(); void updateHardware();
void handleAudio();
}; };
#endif // INCLUDE_FEATURE_SIMPLEPTTWORKER_H_ #endif // INCLUDE_FEATURE_SIMPLEPTTWORKER_H_

View File

@ -11231,6 +11231,25 @@ margin-bottom: 20px;
"type" : "integer", "type" : "integer",
"description" : "Delay in milliseconds from Tx off to Rx on" "description" : "Delay in milliseconds from Tx off to Rx on"
}, },
"vox" : {
"type" : "integer",
"description" : "Activate vox system\n * 0 - not active\n * 1 - active\n"
},
"voxEnable" : {
"type" : "integer",
"description" : "Allow vox to control PTT\n * 0 - vox does not control PTT\n * 1 - vox controls PTT\n"
},
"voxLevel" : {
"type" : "integer",
"description" : "Vox threshold level in dB"
},
"voxHold" : {
"type" : "integer",
"description" : "Vox hold timeout in milliseconds"
},
"audioDeviceName" : {
"type" : "string"
},
"useReverseAPI" : { "useReverseAPI" : {
"type" : "integer", "type" : "integer",
"description" : "Synchronize with reverse API (1 for yes, 0 for no)" "description" : "Synchronize with reverse API (1 for yes, 0 for no)"
@ -51634,7 +51653,7 @@ except ApiException as e:
</div> </div>
<div id="generator"> <div id="generator">
<div class="content"> <div class="content">
Generated 2021-12-27T21:57:14.290+01:00 Generated 2021-12-29T17:23:49.058+01:00
</div> </div>
</div> </div>
</div> </div>

View File

@ -17,6 +17,26 @@ SimplePTTSettings:
tx2RxDelayMs: tx2RxDelayMs:
description: Delay in milliseconds from Tx off to Rx on description: Delay in milliseconds from Tx off to Rx on
type: integer type: integer
vox:
type: integer
description: >
Activate vox system
* 0 - not active
* 1 - active
voxEnable:
type: integer
description: >
Allow vox to control PTT
* 0 - vox does not control PTT
* 1 - vox controls PTT
voxLevel:
type: integer
description: Vox threshold level in dB
voxHold:
type: integer
description: Vox hold timeout in milliseconds
audioDeviceName:
type: string
useReverseAPI: useReverseAPI:
description: Synchronize with reverse API (1 for yes, 0 for no) description: Synchronize with reverse API (1 for yes, 0 for no)
type: integer type: integer

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

@ -1,5 +1,6 @@
<RCC> <RCC>
<qresource prefix="/"> <qresource prefix="/">
<file>audio_mic.png</file>
<file>info.png</file> <file>info.png</file>
<file>darklight.png</file> <file>darklight.png</file>
<file>lightdark.png</file> <file>lightdark.png</file>

View File

@ -17,6 +17,26 @@ SimplePTTSettings:
tx2RxDelayMs: tx2RxDelayMs:
description: Delay in milliseconds from Tx off to Rx on description: Delay in milliseconds from Tx off to Rx on
type: integer type: integer
vox:
type: integer
description: >
Activate vox system
* 0 - not active
* 1 - active
voxEnable:
type: integer
description: >
Allow vox to control PTT
* 0 - vox does not control PTT
* 1 - vox controls PTT
voxLevel:
type: integer
description: Vox threshold level in dB
voxHold:
type: integer
description: Vox hold timeout in milliseconds
audioDeviceName:
type: string
useReverseAPI: useReverseAPI:
description: Synchronize with reverse API (1 for yes, 0 for no) description: Synchronize with reverse API (1 for yes, 0 for no)
type: integer type: integer

View File

@ -11231,6 +11231,25 @@ margin-bottom: 20px;
"type" : "integer", "type" : "integer",
"description" : "Delay in milliseconds from Tx off to Rx on" "description" : "Delay in milliseconds from Tx off to Rx on"
}, },
"vox" : {
"type" : "integer",
"description" : "Activate vox system\n * 0 - not active\n * 1 - active\n"
},
"voxEnable" : {
"type" : "integer",
"description" : "Allow vox to control PTT\n * 0 - vox does not control PTT\n * 1 - vox controls PTT\n"
},
"voxLevel" : {
"type" : "integer",
"description" : "Vox threshold level in dB"
},
"voxHold" : {
"type" : "integer",
"description" : "Vox hold timeout in milliseconds"
},
"audioDeviceName" : {
"type" : "string"
},
"useReverseAPI" : { "useReverseAPI" : {
"type" : "integer", "type" : "integer",
"description" : "Synchronize with reverse API (1 for yes, 0 for no)" "description" : "Synchronize with reverse API (1 for yes, 0 for no)"
@ -51634,7 +51653,7 @@ except ApiException as e:
</div> </div>
<div id="generator"> <div id="generator">
<div class="content"> <div class="content">
Generated 2021-12-27T21:57:14.290+01:00 Generated 2021-12-29T17:23:49.058+01:00
</div> </div>
</div> </div>
</div> </div>

View File

@ -40,6 +40,16 @@ SWGSimplePTTSettings::SWGSimplePTTSettings() {
m_rx2_tx_delay_ms_isSet = false; m_rx2_tx_delay_ms_isSet = false;
tx2_rx_delay_ms = 0; tx2_rx_delay_ms = 0;
m_tx2_rx_delay_ms_isSet = false; m_tx2_rx_delay_ms_isSet = false;
vox = 0;
m_vox_isSet = false;
vox_enable = 0;
m_vox_enable_isSet = false;
vox_level = 0;
m_vox_level_isSet = false;
vox_hold = 0;
m_vox_hold_isSet = false;
audio_device_name = nullptr;
m_audio_device_name_isSet = false;
use_reverse_api = 0; use_reverse_api = 0;
m_use_reverse_api_isSet = false; m_use_reverse_api_isSet = false;
reverse_api_address = nullptr; reverse_api_address = nullptr;
@ -70,6 +80,16 @@ SWGSimplePTTSettings::init() {
m_rx2_tx_delay_ms_isSet = false; m_rx2_tx_delay_ms_isSet = false;
tx2_rx_delay_ms = 0; tx2_rx_delay_ms = 0;
m_tx2_rx_delay_ms_isSet = false; m_tx2_rx_delay_ms_isSet = false;
vox = 0;
m_vox_isSet = false;
vox_enable = 0;
m_vox_enable_isSet = false;
vox_level = 0;
m_vox_level_isSet = false;
vox_hold = 0;
m_vox_hold_isSet = false;
audio_device_name = new QString("");
m_audio_device_name_isSet = false;
use_reverse_api = 0; use_reverse_api = 0;
m_use_reverse_api_isSet = false; m_use_reverse_api_isSet = false;
reverse_api_address = new QString(""); reverse_api_address = new QString("");
@ -93,6 +113,13 @@ SWGSimplePTTSettings::cleanup() {
if(audio_device_name != nullptr) {
delete audio_device_name;
}
if(reverse_api_address != nullptr) { if(reverse_api_address != nullptr) {
delete reverse_api_address; delete reverse_api_address;
} }
@ -124,6 +151,16 @@ SWGSimplePTTSettings::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&tx2_rx_delay_ms, pJson["tx2RxDelayMs"], "qint32", ""); ::SWGSDRangel::setValue(&tx2_rx_delay_ms, pJson["tx2RxDelayMs"], "qint32", "");
::SWGSDRangel::setValue(&vox, pJson["vox"], "qint32", "");
::SWGSDRangel::setValue(&vox_enable, pJson["voxEnable"], "qint32", "");
::SWGSDRangel::setValue(&vox_level, pJson["voxLevel"], "qint32", "");
::SWGSDRangel::setValue(&vox_hold, pJson["voxHold"], "qint32", "");
::SWGSDRangel::setValue(&audio_device_name, pJson["audioDeviceName"], "QString", "QString");
::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", ""); ::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", "");
::SWGSDRangel::setValue(&reverse_api_address, pJson["reverseAPIAddress"], "QString", "QString"); ::SWGSDRangel::setValue(&reverse_api_address, pJson["reverseAPIAddress"], "QString", "QString");
@ -168,6 +205,21 @@ SWGSimplePTTSettings::asJsonObject() {
if(m_tx2_rx_delay_ms_isSet){ if(m_tx2_rx_delay_ms_isSet){
obj->insert("tx2RxDelayMs", QJsonValue(tx2_rx_delay_ms)); obj->insert("tx2RxDelayMs", QJsonValue(tx2_rx_delay_ms));
} }
if(m_vox_isSet){
obj->insert("vox", QJsonValue(vox));
}
if(m_vox_enable_isSet){
obj->insert("voxEnable", QJsonValue(vox_enable));
}
if(m_vox_level_isSet){
obj->insert("voxLevel", QJsonValue(vox_level));
}
if(m_vox_hold_isSet){
obj->insert("voxHold", QJsonValue(vox_hold));
}
if(audio_device_name != nullptr && *audio_device_name != QString("")){
toJsonValue(QString("audioDeviceName"), audio_device_name, obj, QString("QString"));
}
if(m_use_reverse_api_isSet){ if(m_use_reverse_api_isSet){
obj->insert("useReverseAPI", QJsonValue(use_reverse_api)); obj->insert("useReverseAPI", QJsonValue(use_reverse_api));
} }
@ -247,6 +299,56 @@ SWGSimplePTTSettings::setTx2RxDelayMs(qint32 tx2_rx_delay_ms) {
this->m_tx2_rx_delay_ms_isSet = true; this->m_tx2_rx_delay_ms_isSet = true;
} }
qint32
SWGSimplePTTSettings::getVox() {
return vox;
}
void
SWGSimplePTTSettings::setVox(qint32 vox) {
this->vox = vox;
this->m_vox_isSet = true;
}
qint32
SWGSimplePTTSettings::getVoxEnable() {
return vox_enable;
}
void
SWGSimplePTTSettings::setVoxEnable(qint32 vox_enable) {
this->vox_enable = vox_enable;
this->m_vox_enable_isSet = true;
}
qint32
SWGSimplePTTSettings::getVoxLevel() {
return vox_level;
}
void
SWGSimplePTTSettings::setVoxLevel(qint32 vox_level) {
this->vox_level = vox_level;
this->m_vox_level_isSet = true;
}
qint32
SWGSimplePTTSettings::getVoxHold() {
return vox_hold;
}
void
SWGSimplePTTSettings::setVoxHold(qint32 vox_hold) {
this->vox_hold = vox_hold;
this->m_vox_hold_isSet = true;
}
QString*
SWGSimplePTTSettings::getAudioDeviceName() {
return audio_device_name;
}
void
SWGSimplePTTSettings::setAudioDeviceName(QString* audio_device_name) {
this->audio_device_name = audio_device_name;
this->m_audio_device_name_isSet = true;
}
qint32 qint32
SWGSimplePTTSettings::getUseReverseApi() { SWGSimplePTTSettings::getUseReverseApi() {
return use_reverse_api; return use_reverse_api;
@ -320,6 +422,21 @@ SWGSimplePTTSettings::isSet(){
if(m_tx2_rx_delay_ms_isSet){ if(m_tx2_rx_delay_ms_isSet){
isObjectUpdated = true; break; isObjectUpdated = true; break;
} }
if(m_vox_isSet){
isObjectUpdated = true; break;
}
if(m_vox_enable_isSet){
isObjectUpdated = true; break;
}
if(m_vox_level_isSet){
isObjectUpdated = true; break;
}
if(m_vox_hold_isSet){
isObjectUpdated = true; break;
}
if(audio_device_name && *audio_device_name != QString("")){
isObjectUpdated = true; break;
}
if(m_use_reverse_api_isSet){ if(m_use_reverse_api_isSet){
isObjectUpdated = true; break; isObjectUpdated = true; break;
} }

View File

@ -60,6 +60,21 @@ public:
qint32 getTx2RxDelayMs(); qint32 getTx2RxDelayMs();
void setTx2RxDelayMs(qint32 tx2_rx_delay_ms); void setTx2RxDelayMs(qint32 tx2_rx_delay_ms);
qint32 getVox();
void setVox(qint32 vox);
qint32 getVoxEnable();
void setVoxEnable(qint32 vox_enable);
qint32 getVoxLevel();
void setVoxLevel(qint32 vox_level);
qint32 getVoxHold();
void setVoxHold(qint32 vox_hold);
QString* getAudioDeviceName();
void setAudioDeviceName(QString* audio_device_name);
qint32 getUseReverseApi(); qint32 getUseReverseApi();
void setUseReverseApi(qint32 use_reverse_api); void setUseReverseApi(qint32 use_reverse_api);
@ -97,6 +112,21 @@ private:
qint32 tx2_rx_delay_ms; qint32 tx2_rx_delay_ms;
bool m_tx2_rx_delay_ms_isSet; bool m_tx2_rx_delay_ms_isSet;
qint32 vox;
bool m_vox_isSet;
qint32 vox_enable;
bool m_vox_enable_isSet;
qint32 vox_level;
bool m_vox_level_isSet;
qint32 vox_hold;
bool m_vox_hold_isSet;
QString* audio_device_name;
bool m_audio_device_name_isSet;
qint32 use_reverse_api; qint32 use_reverse_api;
bool m_use_reverse_api_isSet; bool m_use_reverse_api_isSet;