mirror of
https://github.com/swift-project/pilotclient.git
synced 2026-03-22 23:05:36 +08:00
[AFV] Ref T730, improved "locking"
* use more fine granular locks * do NOT lock signals (when emit is used)
This commit is contained in:
committed by
Mat Sutcliffe
parent
b802933422
commit
c604ced11c
@@ -11,6 +11,8 @@
|
||||
#include "blackcore/application.h"
|
||||
#include "blacksound/audioutilities.h"
|
||||
#include "blackmisc/audio/audiodeviceinfolist.h"
|
||||
#include "blackmisc/threadutils.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
using namespace BlackCore::Context;
|
||||
@@ -43,7 +45,7 @@ namespace BlackCore
|
||||
m_output(new Output(this)),
|
||||
m_voiceServerPositionTimer(new QTimer(this))
|
||||
{
|
||||
|
||||
this->setObjectName("AFV client");
|
||||
m_connection->setReceiveAudio(false);
|
||||
|
||||
connect(m_input, &CInput::opusDataAvailable, this, &CAfvClient::opusDataAvailable);
|
||||
@@ -62,17 +64,31 @@ namespace BlackCore
|
||||
CLogMessage(this).info(u"UserClient instantiated");
|
||||
}
|
||||
|
||||
QString CAfvClient::getCallsign() const
|
||||
{
|
||||
QMutexLocker lock(&m_mutexCallsign);
|
||||
return m_callsign;
|
||||
}
|
||||
|
||||
void CAfvClient::setCallsign(const QString &callsign)
|
||||
{
|
||||
QMutexLocker lock(&m_mutexCallsign);
|
||||
m_callsign = callsign;
|
||||
}
|
||||
|
||||
void CAfvClient::initTransceivers()
|
||||
{
|
||||
QMutexLocker lock(&m_mutex);
|
||||
m_transceivers =
|
||||
{
|
||||
{ 0, UniCom, 48.5, 11.5, 1000.0, 1000.0 },
|
||||
{ 1, UniCom, 48.5, 11.5, 1000.0, 1000.0 }
|
||||
};
|
||||
QMutexLocker lock(&m_mutexTransceivers);
|
||||
m_transceivers =
|
||||
{
|
||||
{ 0, UniCom, 48.5, 11.5, 1000.0, 1000.0 },
|
||||
{ 1, UniCom, 48.5, 11.5, 1000.0, 1000.0 }
|
||||
};
|
||||
|
||||
m_enabledTransceivers = { 0, 1 };
|
||||
m_transmittingTransceivers = { { 0 } }; // TxTransceiverDto
|
||||
m_enabledTransceivers = { 0, 1 };
|
||||
m_transmittingTransceivers = { { 0 } }; // TxTransceiverDto
|
||||
}
|
||||
|
||||
// init with context values
|
||||
this->initWithContext();
|
||||
@@ -83,7 +99,7 @@ namespace BlackCore
|
||||
if (!hasContext()) { return; }
|
||||
this->disconnect(sApp->getIContextOwnAircraft());
|
||||
sApp->getIContextOwnAircraft()->disconnect(this);
|
||||
connect(sApp->getIContextOwnAircraft(), &IContextOwnAircraft::changedAircraftCockpit, this, &CAfvClient::updateTransceiversFromContext);
|
||||
connect(sApp->getIContextOwnAircraft(), &IContextOwnAircraft::changedAircraftCockpit, this, &CAfvClient::updateTransceiversFromContext, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void CAfvClient::connectTo(const QString &cid, const QString &password, const QString &callsign)
|
||||
@@ -91,12 +107,15 @@ namespace BlackCore
|
||||
if (QThread::currentThread() != thread())
|
||||
{
|
||||
// Method needs to be executed in the object thread since it will create new QObject children
|
||||
QMetaObject::invokeMethod(this, [ = ]() { connectTo(cid, password, callsign); });
|
||||
QPointer<CAfvClient> myself(this);
|
||||
QMetaObject::invokeMethod(this, [ = ]() { if (myself) { connectTo(cid, password, callsign); }});
|
||||
return;
|
||||
}
|
||||
|
||||
// called in CAfvClient thread
|
||||
this->initWithContext();
|
||||
m_callsign = callsign;
|
||||
this->setCallsign(callsign);
|
||||
|
||||
m_connection->connectTo(cid, password, callsign);
|
||||
this->updateTransceivers(); // uses context if available
|
||||
|
||||
@@ -111,7 +130,8 @@ namespace BlackCore
|
||||
if (QThread::currentThread() != thread())
|
||||
{
|
||||
// Method needs to be executed in the object thread since it will create new QObject children
|
||||
QMetaObject::invokeMethod(this, [ = ]() { disconnectFrom(); });
|
||||
QPointer<CAfvClient> myself(this);
|
||||
QMetaObject::invokeMethod(this, [ = ]() { if (myself) disconnectFrom(); });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -132,9 +152,9 @@ namespace BlackCore
|
||||
void CAfvClient::setBypassEffects(bool value)
|
||||
{
|
||||
QMutexLocker lock(&m_mutex);
|
||||
if (soundcardSampleProvider)
|
||||
if (m_soundcardSampleProvider)
|
||||
{
|
||||
soundcardSampleProvider->setBypassEffects(value);
|
||||
m_soundcardSampleProvider->setBypassEffects(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,6 +170,14 @@ namespace BlackCore
|
||||
|
||||
bool CAfvClient::restartWithNewDevices(const CAudioDeviceInfo &inputDevice, const CAudioDeviceInfo &outputDevice)
|
||||
{
|
||||
if (QThread::currentThread() != this->thread())
|
||||
{
|
||||
// Method needs to be executed in the object thread since it will create new QObject children
|
||||
QPointer<CAfvClient> myself(this);
|
||||
QMetaObject::invokeMethod(this, [ = ]() { if (myself) restartWithNewDevices(inputDevice, outputDevice); });
|
||||
return true;
|
||||
}
|
||||
|
||||
this->stopAudio();
|
||||
this->startAudio(inputDevice, outputDevice, allTransceiverIds());
|
||||
return true;
|
||||
@@ -172,17 +200,20 @@ namespace BlackCore
|
||||
|
||||
this->initTransceivers();
|
||||
|
||||
soundcardSampleProvider = new CSoundcardSampleProvider(SampleRate, transceiverIDs, this);
|
||||
connect(soundcardSampleProvider, &CSoundcardSampleProvider::receivingCallsignsChanged, this, &CAfvClient::receivingCallsignsChanged);
|
||||
outputSampleProvider = new CVolumeSampleProvider(soundcardSampleProvider, this);
|
||||
outputSampleProvider->setVolume(m_outputVolume);
|
||||
if (m_soundcardSampleProvider) { m_soundcardSampleProvider->deleteLater(); }
|
||||
m_soundcardSampleProvider = new CSoundcardSampleProvider(SampleRate, transceiverIDs, this);
|
||||
connect(m_soundcardSampleProvider, &CSoundcardSampleProvider::receivingCallsignsChanged, this, &CAfvClient::receivingCallsignsChanged);
|
||||
|
||||
m_output->start(outputDevice, outputSampleProvider);
|
||||
if (m_outputSampleProvider) { m_outputSampleProvider->deleteLater(); }
|
||||
m_outputSampleProvider = new CVolumeSampleProvider(m_soundcardSampleProvider, this);
|
||||
m_outputSampleProvider->setVolume(m_outputVolume);
|
||||
|
||||
m_output->start(outputDevice, m_outputSampleProvider);
|
||||
m_input->start(inputDevice);
|
||||
|
||||
m_startDateTimeUtc = QDateTime::currentDateTimeUtc();
|
||||
m_connection->setReceiveAudio(true);
|
||||
m_voiceServerPositionTimer->start(5000);
|
||||
m_voiceServerPositionTimer->start(PositionUpdatesMs);
|
||||
this->onSettingsChanged(); // make sure all settings are applied
|
||||
m_isStarted = true;
|
||||
CLogMessage(this).info(u"Started [Input: %1] [Output: %2]") << inputDevice.getName() << outputDevice.getName();
|
||||
@@ -197,25 +228,32 @@ namespace BlackCore
|
||||
|
||||
void CAfvClient::stopAudio()
|
||||
{
|
||||
if (QThread::currentThread() != this->thread())
|
||||
{
|
||||
// Method needs to be executed in the object thread since it will create new QObject children
|
||||
QMetaObject::invokeMethod(this, [ = ]() { stopAudio(); });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_isStarted)
|
||||
{
|
||||
CLogMessage(this).info(u"Client NOT started");
|
||||
return;
|
||||
}
|
||||
|
||||
if (QThread::currentThread() != this->thread())
|
||||
{
|
||||
// Method needs to be executed in the object thread since it will create new QObject children
|
||||
QPointer<CAfvClient> myself(this);
|
||||
QMetaObject::invokeMethod(this, [ = ]() { if (myself) stopAudio(); });
|
||||
return;
|
||||
}
|
||||
|
||||
m_isStarted = false;
|
||||
m_connection->setReceiveAudio(false);
|
||||
|
||||
m_transceivers.clear();
|
||||
// transceivers
|
||||
{
|
||||
QMutexLocker lock(&m_mutexTransceivers);
|
||||
m_transceivers.clear();
|
||||
}
|
||||
this->updateTransceivers(false);
|
||||
|
||||
// stop input/output
|
||||
m_updateTimer.stop();
|
||||
m_input->stop();
|
||||
m_output->stop();
|
||||
CLogMessage(this).info(u"Client stopped");
|
||||
@@ -223,10 +261,11 @@ namespace BlackCore
|
||||
|
||||
void CAfvClient::enableTransceiver(quint16 id, bool enable)
|
||||
{
|
||||
QMutexLocker lock(&m_mutex);
|
||||
|
||||
if (enable) { m_enabledTransceivers.insert(id); }
|
||||
else { m_enabledTransceivers.remove(id); }
|
||||
{
|
||||
QMutexLocker lock(&m_mutexTransceivers);
|
||||
if (enable) { m_enabledTransceivers.insert(id); }
|
||||
else { m_enabledTransceivers.remove(id); }
|
||||
}
|
||||
|
||||
this->updateTransceivers();
|
||||
}
|
||||
@@ -239,8 +278,11 @@ namespace BlackCore
|
||||
bool CAfvClient::isEnabledTransceiver(quint16 id) const
|
||||
{
|
||||
// we double check, enabled and exist!
|
||||
if (!m_enabledTransceivers.contains(id)) { return false; }
|
||||
for (const TransceiverDto &dto : m_transceivers)
|
||||
const auto enabledTransceivers = this->getEnabledTransceivers(); // threadsafe
|
||||
if (!enabledTransceivers.contains(id)) { return false; }
|
||||
|
||||
const auto transceivers = this->getTransceivers(); // threadsafe
|
||||
for (const TransceiverDto &dto : transceivers)
|
||||
{
|
||||
if (dto.id == id) { return true; }
|
||||
}
|
||||
@@ -258,27 +300,38 @@ namespace BlackCore
|
||||
|
||||
// Fix rounding issues like 128074999 Hz -> 128075000 Hz
|
||||
quint32 roundedFrequencyHz = static_cast<quint32>(qRound(frequencyHz / 1000.0)) * 1000;
|
||||
|
||||
QMutexLocker lock(&m_mutex);
|
||||
auto it = std::find_if(m_aliasedStations.begin(), m_aliasedStations.end(), [roundedFrequencyHz](const StationDto & d)
|
||||
{
|
||||
return d.frequencyAlias == roundedFrequencyHz;
|
||||
});
|
||||
QMutexLocker lock(&m_mutex);
|
||||
auto it = std::find_if(m_aliasedStations.begin(), m_aliasedStations.end(), [roundedFrequencyHz](const StationDto & d)
|
||||
{
|
||||
return d.frequencyAliasHz == roundedFrequencyHz;
|
||||
});
|
||||
|
||||
if (it != m_aliasedStations.end())
|
||||
{
|
||||
qDebug() << "Aliasing" << frequencyHz << "Hz [VHF] to" << it->frequency << "Hz [HF]";
|
||||
roundedFrequencyHz = it->frequency;
|
||||
if (it != m_aliasedStations.end())
|
||||
{
|
||||
CLogMessage(this).debug(u"Aliasing %1Hz [VHF] to %2Hz [HF]") << frequencyHz << it->frequencyHz;
|
||||
roundedFrequencyHz = it->frequencyHz;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_transceivers.size() >= id + 1)
|
||||
bool updateTransceivers = false;
|
||||
{
|
||||
if (m_transceivers[id].frequencyHz != roundedFrequencyHz)
|
||||
QMutexLocker lockTransceivers(&m_mutexTransceivers);
|
||||
if (m_transceivers.size() >= id + 1)
|
||||
{
|
||||
m_transceivers[id].frequencyHz = roundedFrequencyHz;
|
||||
this->updateTransceivers(false); // no frequency update
|
||||
if (m_transceivers[id].frequencyHz != roundedFrequencyHz)
|
||||
{
|
||||
updateTransceivers = true;
|
||||
m_transceivers[id].frequencyHz = roundedFrequencyHz;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// outside lock to avoid deadlock
|
||||
if (updateTransceivers)
|
||||
{
|
||||
this->updateTransceivers(false); // no frequency update
|
||||
}
|
||||
}
|
||||
|
||||
void CAfvClient::updateComFrequency(CComSystem::ComUnit comUnit, const CFrequency &comFrequency)
|
||||
@@ -294,7 +347,7 @@ namespace BlackCore
|
||||
|
||||
void CAfvClient::updatePosition(double latitudeDeg, double longitudeDeg, double heightMeters)
|
||||
{
|
||||
QMutexLocker lock(&m_mutex);
|
||||
QMutexLocker lock(&m_mutexTransceivers);
|
||||
for (TransceiverDto &transceiver : m_transceivers)
|
||||
{
|
||||
transceiver.LatDeg = latitudeDeg;
|
||||
@@ -321,26 +374,32 @@ namespace BlackCore
|
||||
}
|
||||
}
|
||||
|
||||
QVector<TransceiverDto> enabledTransceivers;
|
||||
for (const TransceiverDto &transceiver : m_transceivers)
|
||||
// threadsafe copies
|
||||
const auto transceivers = this->getTransceivers();
|
||||
const auto enabledTransceivers = this->getEnabledTransceivers();
|
||||
const QString callsign = this->getCallsign();
|
||||
|
||||
// transceivers
|
||||
QVector<TransceiverDto> newEnabledTransceivers;
|
||||
for (const TransceiverDto &transceiver : transceivers)
|
||||
{
|
||||
if (m_enabledTransceivers.contains(transceiver.id))
|
||||
if (enabledTransceivers.contains(transceiver.id))
|
||||
{
|
||||
enabledTransceivers.push_back(transceiver);
|
||||
newEnabledTransceivers.push_back(transceiver);
|
||||
}
|
||||
}
|
||||
m_connection->updateTransceivers(m_callsign, enabledTransceivers);
|
||||
m_connection->updateTransceivers(callsign, newEnabledTransceivers);
|
||||
|
||||
if (soundcardSampleProvider)
|
||||
if (m_soundcardSampleProvider)
|
||||
{
|
||||
soundcardSampleProvider->updateRadioTransceivers(m_transceivers);
|
||||
m_soundcardSampleProvider->updateRadioTransceivers(m_transceivers);
|
||||
}
|
||||
}
|
||||
|
||||
void CAfvClient::setTransmittingTransceiver(quint16 transceiverID)
|
||||
{
|
||||
TxTransceiverDto tx = { transceiverID };
|
||||
setTransmittingTransceivers({ tx });
|
||||
this->setTransmittingTransceivers({ tx });
|
||||
}
|
||||
|
||||
void CAfvClient::setTransmittingComUnit(CComSystem::ComUnit comUnit)
|
||||
@@ -350,13 +409,13 @@ namespace BlackCore
|
||||
|
||||
void CAfvClient::setTransmittingTransceivers(const QVector<TxTransceiverDto> &transceivers)
|
||||
{
|
||||
QMutexLocker lock(&m_mutex);
|
||||
QMutexLocker lock(&m_mutexTransceivers);
|
||||
m_transmittingTransceivers = transceivers;
|
||||
}
|
||||
|
||||
bool CAfvClient::isTransmittingTransceiver(quint16 id) const
|
||||
{
|
||||
QMutexLocker lock(&m_mutex);
|
||||
QMutexLocker lock(&m_mutexTransceivers);
|
||||
for (const TxTransceiverDto &dto : m_transmittingTransceivers)
|
||||
{
|
||||
if (dto.id == id) { return true; }
|
||||
@@ -369,6 +428,24 @@ namespace BlackCore
|
||||
return this->isTransmittingTransceiver(comUnitToTransceiverId(comUnit));
|
||||
}
|
||||
|
||||
QVector<TransceiverDto> CAfvClient::getTransceivers() const
|
||||
{
|
||||
QMutexLocker lock(&m_mutexTransceivers);
|
||||
return m_transceivers;
|
||||
}
|
||||
|
||||
QSet<quint16> CAfvClient::getEnabledTransceivers() const
|
||||
{
|
||||
QMutexLocker lock(&m_mutexTransceivers);
|
||||
return m_enabledTransceivers;
|
||||
}
|
||||
|
||||
QVector<TxTransceiverDto> CAfvClient::getTransmittingTransceivers() const
|
||||
{
|
||||
QMutexLocker lock(&m_mutexTransceivers);
|
||||
return m_transmittingTransceivers;
|
||||
}
|
||||
|
||||
void CAfvClient::setPtt(bool active)
|
||||
{
|
||||
this->setPttForCom(active, COMUnspecified);
|
||||
@@ -385,27 +462,25 @@ namespace BlackCore
|
||||
}
|
||||
|
||||
if (m_transmit == active) { return; }
|
||||
|
||||
QMutexLocker lock(&m_mutex);
|
||||
m_transmit = active;
|
||||
|
||||
if (soundcardSampleProvider)
|
||||
{
|
||||
soundcardSampleProvider->pttUpdate(active, m_transmittingTransceivers);
|
||||
}
|
||||
QMutexLocker lock(&m_mutex);
|
||||
if (m_soundcardSampleProvider)
|
||||
{
|
||||
m_soundcardSampleProvider->pttUpdate(active, m_transmittingTransceivers);
|
||||
}
|
||||
|
||||
if (!active)
|
||||
{
|
||||
//AGC
|
||||
//if (maxDbReadingInPTTInterval > -1)
|
||||
// InputVolumeDb = InputVolumeDb - 1;
|
||||
//if(maxDbReadingInPTTInterval < -4)
|
||||
// InputVolumeDb = InputVolumeDb + 1;
|
||||
m_maxDbReadingInPTTInterval = -100;
|
||||
if (!active)
|
||||
{
|
||||
//AGC
|
||||
// if (maxDbReadingInPTTInterval > -1) InputVolumeDb = InputVolumeDb - 1;
|
||||
// if (maxDbReadingInPTTInterval < -4) InputVolumeDb = InputVolumeDb + 1;
|
||||
m_maxDbReadingInPTTInterval = -100;
|
||||
}
|
||||
}
|
||||
|
||||
emit this->ptt(active, com, this->identifier());
|
||||
// qDebug() << "PTT:" << active;
|
||||
}
|
||||
|
||||
void CAfvClient::setInputVolumeDb(double valueDb)
|
||||
@@ -457,21 +532,24 @@ namespace BlackCore
|
||||
|
||||
double CAfvClient::getInputVolumePeakVU() const
|
||||
{
|
||||
QMutexLocker lock(&m_mutex);
|
||||
QMutexLocker lock(&m_mutexInputStream);
|
||||
return m_inputVolumeStream.PeakVU;
|
||||
}
|
||||
|
||||
double CAfvClient::getOutputVolumePeakVU() const
|
||||
{
|
||||
QMutexLocker lock(&m_mutex);
|
||||
QMutexLocker lock(&m_mutexOutputStream);
|
||||
return m_outputVolumeStream.PeakVU;
|
||||
}
|
||||
|
||||
void CAfvClient::opusDataAvailable(const OpusDataAvailableArgs &args)
|
||||
{
|
||||
QMutexLocker lock(&m_mutex);
|
||||
const bool transmit = m_transmit;
|
||||
const bool loopback = m_loopbackOn;
|
||||
const bool transmitHistory = m_transmitHistory;
|
||||
const auto transceivers = this->getTransceivers();
|
||||
|
||||
if (m_loopbackOn && m_transmit)
|
||||
if (loopback && transmit)
|
||||
{
|
||||
IAudioDto audioData;
|
||||
audioData.audio = QByteArray(args.audio.data(), args.audio.size());
|
||||
@@ -479,77 +557,85 @@ namespace BlackCore
|
||||
audioData.lastPacket = false;
|
||||
audioData.sequenceCounter = 0;
|
||||
|
||||
RxTransceiverDto com1 = { 0, transceivers.size() > 0 ? transceivers[0].frequencyHz : UniCom, 1.0 };
|
||||
RxTransceiverDto com2 = { 1, transceivers.size() > 1 ? transceivers[1].frequencyHz : UniCom, 1.0 };
|
||||
|
||||
RxTransceiverDto com1 = { 0, m_transceivers.size() > 0 ? m_transceivers[0].frequencyHz : UniCom, 1.0 };
|
||||
RxTransceiverDto com2 = { 1, m_transceivers.size() > 1 ? m_transceivers[1].frequencyHz : UniCom, 1.0 };
|
||||
|
||||
soundcardSampleProvider->addOpusSamples(audioData, { com1, com2 });
|
||||
QMutexLocker lock(&m_mutex);
|
||||
m_soundcardSampleProvider->addOpusSamples(audioData, { com1, com2 });
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_transmittingTransceivers.size() > 0)
|
||||
const QString callsign = this->getCallsign();
|
||||
const auto transmittingTransceivers = this->getTransmittingTransceivers(); // threadsafe
|
||||
if (transmittingTransceivers.size() > 0)
|
||||
{
|
||||
if (m_transmit)
|
||||
if (transmit)
|
||||
{
|
||||
QMutexLocker lock(&m_mutex);
|
||||
if (m_connection->isConnected())
|
||||
{
|
||||
AudioTxOnTransceiversDto dto;
|
||||
dto.callsign = m_callsign.toStdString();
|
||||
dto.callsign = callsign.toStdString();
|
||||
dto.sequenceCounter = args.sequenceCounter;
|
||||
dto.audio = std::vector<char>(args.audio.begin(), args.audio.end());
|
||||
dto.lastPacket = false;
|
||||
dto.transceivers = m_transmittingTransceivers.toStdVector();
|
||||
dto.transceivers = transmittingTransceivers.toStdVector();
|
||||
m_connection->sendToVoiceServer(dto);
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_transmit && m_transmitHistory)
|
||||
if (!transmit && transmitHistory)
|
||||
{
|
||||
QMutexLocker lock(&m_mutex);
|
||||
if (m_connection->isConnected())
|
||||
{
|
||||
AudioTxOnTransceiversDto dto;
|
||||
dto.callsign = m_callsign.toStdString();
|
||||
dto.callsign = callsign.toStdString();
|
||||
dto.sequenceCounter = args.sequenceCounter;
|
||||
dto.audio = std::vector<char>(args.audio.begin(), args.audio.end());
|
||||
dto.lastPacket = true;
|
||||
dto.transceivers = m_transmittingTransceivers.toStdVector();
|
||||
dto.transceivers = transmittingTransceivers.toStdVector();
|
||||
m_connection->sendToVoiceServer(dto);
|
||||
}
|
||||
}
|
||||
m_transmitHistory = m_transmit;
|
||||
m_transmitHistory = transmit;
|
||||
}
|
||||
}
|
||||
|
||||
void CAfvClient::audioOutDataAvailable(const AudioRxOnTransceiversDto &dto)
|
||||
{
|
||||
IAudioDto audioData;
|
||||
audioData.audio = QByteArray(dto.audio.data(), static_cast<int>(dto.audio.size()));
|
||||
audioData.callsign = QString::fromStdString(dto.callsign);
|
||||
audioData.lastPacket = dto.lastPacket;
|
||||
audioData.audio = QByteArray(dto.audio.data(), static_cast<int>(dto.audio.size()));
|
||||
audioData.callsign = QString::fromStdString(dto.callsign);
|
||||
audioData.lastPacket = dto.lastPacket;
|
||||
audioData.sequenceCounter = dto.sequenceCounter;
|
||||
soundcardSampleProvider->addOpusSamples(audioData, QVector<RxTransceiverDto>::fromStdVector(dto.transceivers));
|
||||
m_soundcardSampleProvider->addOpusSamples(audioData, QVector<RxTransceiverDto>::fromStdVector(dto.transceivers));
|
||||
}
|
||||
|
||||
void CAfvClient::inputVolumeStream(const InputVolumeStreamArgs &args)
|
||||
{
|
||||
QMutexLocker lock(&m_mutex);
|
||||
m_inputVolumeStream = args;
|
||||
emit inputVolumePeakVU(m_inputVolumeStream.PeakVU);
|
||||
{
|
||||
QMutexLocker lock(&m_mutexInputStream);
|
||||
m_inputVolumeStream = args;
|
||||
}
|
||||
emit inputVolumePeakVU(args.PeakVU);
|
||||
}
|
||||
|
||||
void CAfvClient::outputVolumeStream(const OutputVolumeStreamArgs &args)
|
||||
{
|
||||
QMutexLocker lock(&m_mutex);
|
||||
m_outputVolumeStream = args;
|
||||
emit outputVolumePeakVU(m_outputVolumeStream.PeakVU);
|
||||
{
|
||||
QMutexLocker lock(&m_mutexOutputStream);
|
||||
m_outputVolumeStream = args;
|
||||
}
|
||||
emit outputVolumePeakVU(args.PeakVU);
|
||||
}
|
||||
|
||||
QString CAfvClient::getReceivingCallsignsCom1()
|
||||
{
|
||||
QMutexLocker lock(&m_mutex);
|
||||
if (soundcardSampleProvider)
|
||||
if (m_soundcardSampleProvider)
|
||||
{
|
||||
return soundcardSampleProvider->getReceivingCallsigns(0);
|
||||
return m_soundcardSampleProvider->getReceivingCallsigns(0);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@@ -557,13 +643,18 @@ namespace BlackCore
|
||||
QString CAfvClient::getReceivingCallsignsCom2()
|
||||
{
|
||||
QMutexLocker lock(&m_mutex);
|
||||
if (soundcardSampleProvider)
|
||||
if (m_soundcardSampleProvider)
|
||||
{
|
||||
return soundcardSampleProvider->getReceivingCallsigns(1);
|
||||
return m_soundcardSampleProvider->getReceivingCallsigns(1);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void CAfvClient::initialize()
|
||||
{
|
||||
CLogMessage(this).info(u"Initialize AFV client in thread: %1") << CThreadUtils::threadInfo(this->thread());
|
||||
}
|
||||
|
||||
void CAfvClient::onPositionUpdateTimer()
|
||||
{
|
||||
this->updateTransceivers();
|
||||
@@ -634,16 +725,17 @@ namespace BlackCore
|
||||
|
||||
QMutexLocker lock(&m_mutex);
|
||||
m_outputVolumeDb = valueDb;
|
||||
|
||||
m_outputVolume = qPow(10, m_outputVolumeDb / 20.0);
|
||||
if (outputSampleProvider)
|
||||
|
||||
if (m_outputSampleProvider)
|
||||
{
|
||||
outputSampleProvider->setVolume(m_outputVolume);
|
||||
m_outputSampleProvider->setVolume(m_outputVolume);
|
||||
}
|
||||
}
|
||||
|
||||
const CAudioDeviceInfo &CAfvClient::getInputDevice() const
|
||||
{
|
||||
QMutexLocker lock(&m_mutex);
|
||||
if (m_input) { return m_input->device(); }
|
||||
static const CAudioDeviceInfo nullDevice;
|
||||
return nullDevice;
|
||||
@@ -651,6 +743,7 @@ namespace BlackCore
|
||||
|
||||
const CAudioDeviceInfo &CAfvClient::getOutputDevice() const
|
||||
{
|
||||
QMutexLocker lock(&m_mutex);
|
||||
if (m_output) { return m_output->device(); }
|
||||
static const CAudioDeviceInfo nullDevice;
|
||||
return nullDevice;
|
||||
|
||||
@@ -69,7 +69,11 @@ namespace BlackCore
|
||||
virtual ~CAfvClient() override { this->stopAudio(); }
|
||||
|
||||
//! Corresponding callsign
|
||||
QString callsign() const { return m_callsign; }
|
||||
//! \threadsafe
|
||||
//! @{
|
||||
QString getCallsign() const;
|
||||
void setCallsign(const QString &getCallsign);
|
||||
//! @}
|
||||
|
||||
//! Is connected to network?
|
||||
bool isConnected() const { return m_connection->isConnected(); }
|
||||
@@ -78,7 +82,7 @@ namespace BlackCore
|
||||
ConnectionStatus getConnectionStatus() const;
|
||||
|
||||
//! Connect to network
|
||||
Q_INVOKABLE void connectTo(const QString &cid, const QString &password, const QString &callsign);
|
||||
Q_INVOKABLE void connectTo(const QString &cid, const QString &password, const QString &getCallsign);
|
||||
|
||||
//! Disconnect from network
|
||||
Q_INVOKABLE void disconnectFrom();
|
||||
@@ -107,31 +111,48 @@ namespace BlackCore
|
||||
Q_INVOKABLE void startAudio(const QString &inputDeviceName, const QString &outputDeviceName);
|
||||
void stopAudio();
|
||||
|
||||
//! Enable COM unit/transceiver @{
|
||||
//! Enable COM unit/transceiver
|
||||
//! \threadsafe
|
||||
//! @{
|
||||
Q_INVOKABLE void enableTransceiver(quint16 id, bool enable);
|
||||
void enableComUnit(BlackMisc::Aviation::CComSystem::ComUnit comUnit, bool enable);
|
||||
bool isEnabledTransceiver(quint16 id) const;
|
||||
bool isEnabledComUnit(BlackMisc::Aviation::CComSystem::ComUnit comUnit) const;
|
||||
//! @}
|
||||
|
||||
//! Set transmitting transceivers @{
|
||||
//! Set transmitting transceivers
|
||||
//! \threadsafe
|
||||
//! @{
|
||||
void setTransmittingTransceiver(quint16 transceiverID);
|
||||
void setTransmittingComUnit(BlackMisc::Aviation::CComSystem::ComUnit comUnit);
|
||||
void setTransmittingTransceivers(const QVector<TxTransceiverDto> &transceivers);
|
||||
//! @}
|
||||
|
||||
//! Transmitting transceiver/COM unit
|
||||
//! \threadsafe
|
||||
//! @{
|
||||
bool isTransmittingTransceiver(quint16 id) const;
|
||||
bool isTransmittingdComUnit(BlackMisc::Aviation::CComSystem::ComUnit comUnit) const;
|
||||
//! @}
|
||||
|
||||
//! Update frequency @{
|
||||
//! Get transceivers
|
||||
//! \threadsafe
|
||||
//! @{
|
||||
QVector<TransceiverDto> getTransceivers() const;
|
||||
QVector<TxTransceiverDto> getTransmittingTransceivers() const;
|
||||
QSet<quint16> getEnabledTransceivers() const;
|
||||
//! @}
|
||||
|
||||
//! Update frequency
|
||||
//! \threadsafe
|
||||
//! @{
|
||||
Q_INVOKABLE void updateComFrequency(quint16 id, quint32 frequencyHz);
|
||||
void updateComFrequency(BlackMisc::Aviation::CComSystem::ComUnit comUnit, const BlackMisc::PhysicalQuantities::CFrequency &comFrequency);
|
||||
void updateComFrequency(BlackMisc::Aviation::CComSystem::ComUnit comUnit, const BlackMisc::Aviation::CComSystem &comSystem);
|
||||
//! @}
|
||||
|
||||
//! Update own aircraft position
|
||||
//! \threadsafe
|
||||
Q_INVOKABLE void updatePosition(double latitudeDeg, double longitudeDeg, double heightMeters);
|
||||
|
||||
//! Push to talk @{
|
||||
@@ -139,7 +160,9 @@ namespace BlackCore
|
||||
void setPttForCom(bool active, BlackMisc::Audio::PTTCOM com);
|
||||
//! @}
|
||||
|
||||
//! Loopback @{
|
||||
//! Loopback
|
||||
//! \threadsafe
|
||||
//! @{
|
||||
Q_INVOKABLE void setLoopBack(bool on) { m_loopbackOn = on; }
|
||||
Q_INVOKABLE bool isLoopback() const { return m_loopbackOn; }
|
||||
//! @}
|
||||
@@ -195,13 +218,15 @@ namespace BlackCore
|
||||
void outputVolumePeakVU(double value);
|
||||
//! @}
|
||||
|
||||
protected:
|
||||
//! \copydoc BlackMisc::CContinuousWorker::initialize
|
||||
virtual void initialize() override;
|
||||
|
||||
private:
|
||||
void opusDataAvailable(const Audio::OpusDataAvailableArgs &args);
|
||||
void audioOutDataAvailable(const AudioRxOnTransceiversDto &dto);
|
||||
void inputVolumeStream(const Audio::InputVolumeStreamArgs &args);
|
||||
void outputVolumeStream(const Audio::OutputVolumeStreamArgs &args);
|
||||
|
||||
|
||||
void inputOpusDataAvailable();
|
||||
|
||||
void onPositionUpdateTimer();
|
||||
@@ -210,6 +235,7 @@ namespace BlackCore
|
||||
void updateTransceivers(bool updateFrequencies = true);
|
||||
void updateTransceiversFromContext(const BlackMisc::Simulation::CSimulatedAircraft &aircraft, const BlackMisc::CIdentifier &originator);
|
||||
|
||||
static constexpr int PositionUpdatesMs = 5000; //!< position timer
|
||||
static constexpr int SampleRate = 48000;
|
||||
static constexpr int FrameSize = 960; // 20ms
|
||||
static constexpr double MinDbIn = -18.0;
|
||||
@@ -228,16 +254,18 @@ namespace BlackCore
|
||||
Audio::CInput *m_input = nullptr;
|
||||
Audio::Output *m_output = nullptr;
|
||||
|
||||
Audio::CSoundcardSampleProvider *soundcardSampleProvider = nullptr;
|
||||
BlackSound::SampleProvider::CVolumeSampleProvider *outputSampleProvider = nullptr;
|
||||
Audio::CSoundcardSampleProvider *m_soundcardSampleProvider = nullptr;
|
||||
BlackSound::SampleProvider::CVolumeSampleProvider *m_outputSampleProvider = nullptr;
|
||||
|
||||
std::atomic_bool m_transmit = { false };
|
||||
bool m_transmitHistory = false;
|
||||
std::atomic_bool m_transmit { false };
|
||||
std::atomic_bool m_transmitHistory { false };
|
||||
QVector<TxTransceiverDto> m_transmittingTransceivers;
|
||||
QVector<TransceiverDto> m_transceivers;
|
||||
QSet<quint16> m_enabledTransceivers;
|
||||
static const QVector<quint16> &allTransceiverIds() { static const QVector<quint16> transceiverIds{0, 1}; return transceiverIds; }
|
||||
|
||||
bool m_isStarted = false;
|
||||
std::atomic_bool m_loopbackOn = { false };
|
||||
std::atomic_bool m_isStarted { false };
|
||||
std::atomic_bool m_loopbackOn { false };
|
||||
QDateTime m_startDateTimeUtc;
|
||||
|
||||
double m_inputVolumeDb;
|
||||
@@ -245,10 +273,8 @@ namespace BlackCore
|
||||
double m_outputVolume = 1.0;
|
||||
double m_maxDbReadingInPTTInterval = -100;
|
||||
|
||||
QTimer *m_voiceServerPositionTimer = nullptr;
|
||||
QVector<TransceiverDto> m_transceivers;
|
||||
QSet<quint16> m_enabledTransceivers;
|
||||
QVector<StationDto> m_aliasedStations;
|
||||
QTimer *m_voiceServerPositionTimer = nullptr;
|
||||
QVector<StationDto> m_aliasedStations;
|
||||
|
||||
Audio::InputVolumeStreamArgs m_inputVolumeStream;
|
||||
Audio::OutputVolumeStreamArgs m_outputVolumeStream;
|
||||
@@ -258,6 +284,10 @@ namespace BlackCore
|
||||
static bool hasContext();
|
||||
|
||||
mutable QMutex m_mutex;
|
||||
mutable QMutex m_mutexInputStream;
|
||||
mutable QMutex m_mutexOutputStream;
|
||||
mutable QMutex m_mutexTransceivers;
|
||||
mutable QMutex m_mutexCallsign;
|
||||
};
|
||||
} // ns
|
||||
} // ns
|
||||
|
||||
Reference in New Issue
Block a user