/////////////////////////////////////////////////////////////////////////////////// // Copyright (C) 2020 Edouard Griffiths, F4EXB // // // // This program is free software; you can redistribute it and/or modify // // it under the terms of the GNU General Public License as published by // // the Free Software Foundation as version 3 of the License, or // // (at your option) any later version. // // // // This program is distributed in the hope that it will be useful, // // but WITHOUT ANY WARRANTY; without even the implied warranty of // // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // // GNU General Public License V3 for more details. // // // // You should have received a copy of the GNU General Public License // // along with this program. If not, see . // /////////////////////////////////////////////////////////////////////////////////// #include #include "SWGDeviceState.h" #include "SWGSuccessResponse.h" #include "SWGErrorResponse.h" #include "webapi/webapiadapterinterface.h" #include "audio/audiodevicemanager.h" #include "dsp/dspengine.h" #include "util/db.h" #include "simplepttreport.h" #include "simplepttworker.h" MESSAGE_CLASS_DEFINITION(SimplePTTWorker::MsgConfigureSimplePTTWorker, Message) MESSAGE_CLASS_DEFINITION(SimplePTTWorker::MsgPTT, Message) SimplePTTWorker::SimplePTTWorker(WebAPIAdapterInterface *webAPIAdapterInterface) : m_webAPIAdapterInterface(webAPIAdapterInterface), m_msgQueueToGUI(nullptr), m_running(false), m_tx(false), m_audioFifo(12000), m_audioSampleRate(48000), m_voxLevel(1.0), m_voxHoldCount(0), m_voxState(false), m_updateTimer(this), m_mutex(QMutex::Recursive) { m_audioReadBuffer.resize(16384); m_audioReadBufferFill = 0; qDebug("SimplePTTWorker::SimplePTTWorker"); connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware())); } SimplePTTWorker::~SimplePTTWorker() { m_inputMessageQueue.clear(); AudioDeviceManager *audioDeviceManager = DSPEngine::instance()->getAudioDeviceManager(); audioDeviceManager->removeAudioSource(&m_audioFifo); } void SimplePTTWorker::reset() { QMutexLocker mutexLocker(&m_mutex); m_inputMessageQueue.clear(); } bool SimplePTTWorker::startWork() { QMutexLocker mutexLocker(&m_mutex); connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); m_running = true; return m_running; } void SimplePTTWorker::stopWork() { QMutexLocker mutexLocker(&m_mutex); disconnect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); m_running = false; } void SimplePTTWorker::handleInputMessages() { Message* message; while ((message = m_inputMessageQueue.pop()) != nullptr) { if (handleMessage(*message)) { delete message; } } } bool SimplePTTWorker::handleMessage(const Message& cmd) { if (MsgConfigureSimplePTTWorker::match(cmd)) { QMutexLocker mutexLocker(&m_mutex); MsgConfigureSimplePTTWorker& cfg = (MsgConfigureSimplePTTWorker&) cmd; qDebug() << "SimplePTTWorker::handleMessage: MsgConfigureSimplePTTWorker"; applySettings(cfg.getSettings(), cfg.getForce()); return true; } else if (MsgPTT::match(cmd)) { MsgPTT& cfg = (MsgPTT&) cmd; qDebug() << "SimplePTTWorker::handleMessage: MsgPTT"; sendPTT(cfg.getTx()); return true; } else { return false; } } void SimplePTTWorker::applySettings(const SimplePTTSettings& settings, bool force) { qDebug() << "SimplePTTWorker::applySettings:" << " m_title: " << settings.m_title << " m_rgbColor: " << settings.m_rgbColor << " m_rxDeviceSetIndex: " << settings.m_rxDeviceSetIndex << " m_txDeviceSetIndex: " << settings.m_txDeviceSetIndex << " m_rx2TxDelayMs: " << settings.m_rx2TxDelayMs << " 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; 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; } void SimplePTTWorker::sendPTT(bool tx) { if (!m_updateTimer.isActive()) { bool switchedOff = false; m_mutex.lock(); if (tx) { if (m_settings.m_rxDeviceSetIndex >= 0) { m_tx = false; switchedOff = turnDevice(false); } if (m_settings.m_txDeviceSetIndex >= 0) { m_tx = true; m_updateTimer.start(m_settings.m_rx2TxDelayMs); } } else { if (m_settings.m_txDeviceSetIndex >= 0) { m_tx = true; switchedOff = turnDevice(false); } if (m_settings.m_rxDeviceSetIndex >= 0) { m_tx = false; m_updateTimer.start(m_settings.m_tx2RxDelayMs); } } if (switchedOff && (m_msgQueueToGUI)) { SimplePTTReport::MsgRadioState *msg = SimplePTTReport::MsgRadioState::create(SimplePTTReport::RadioIdle); m_msgQueueToGUI->push(msg); } } } void SimplePTTWorker::updateHardware() { SWGSDRangel::SWGSuccessResponse response; SWGSDRangel::SWGErrorResponse error; m_updateTimer.stop(); m_mutex.unlock(); if (turnDevice(true)) { m_webAPIAdapterInterface->devicesetFocusPatch( m_tx ? m_settings.m_txDeviceSetIndex : m_settings.m_rxDeviceSetIndex, response, error); if (m_msgQueueToGUI) { SimplePTTReport::MsgRadioState *msg = SimplePTTReport::MsgRadioState::create( m_tx ? SimplePTTReport::RadioTx : SimplePTTReport::RadioRx ); m_msgQueueToGUI->push(msg); } } } bool SimplePTTWorker::turnDevice(bool on) { SWGSDRangel::SWGDeviceState response; SWGSDRangel::SWGErrorResponse error; int httpCode; if (on) { httpCode = m_webAPIAdapterInterface->devicesetDeviceRunPost( m_tx ? m_settings.m_txDeviceSetIndex : m_settings.m_rxDeviceSetIndex, response, error); } else { httpCode = m_webAPIAdapterInterface->devicesetDeviceRunDelete( m_tx ? m_settings.m_txDeviceSetIndex : m_settings.m_rxDeviceSetIndex, response, error); } if (httpCode/100 == 2) { return true; } else { qWarning("SimplePTTWorker::turnDevice: error: %s", qPrintable(*error.getMessage())); return false; } } void SimplePTTWorker::handleAudio() { unsigned int nbRead; QMutexLocker mlock(&m_mutex); while ((nbRead = m_audioFifo.read(reinterpret_cast(&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 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; } } }