mirror of
https://github.com/swift-project/pilotclient.git
synced 2026-04-21 04:45:31 +08:00
[AFV] First version of threaded CAfvClient
This commit is contained in:
committed by
Mat Sutcliffe
parent
d1006160f8
commit
d3a1eb1d60
15
samples/afvclient/afvclientbridge.cpp
Normal file
15
samples/afvclient/afvclientbridge.cpp
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#include "afvclientbridge.h"
|
||||||
|
|
||||||
|
using namespace BlackCore::Afv::Clients;
|
||||||
|
|
||||||
|
CAfvClientBridge::CAfvClientBridge(CAfvClient *afvClient, QObject *parent) :
|
||||||
|
QObject(parent),
|
||||||
|
m_afvClient(afvClient)
|
||||||
|
{
|
||||||
|
connect(afvClient, &CAfvClient::receivingCallsignsChanged, this, &CAfvClientBridge::receivingCallsignsChanged);
|
||||||
|
connect(afvClient, &CAfvClient::connectionStatusChanged, this, &CAfvClientBridge::connectionStatusChanged);
|
||||||
|
connect(afvClient, &CAfvClient::updatedFromOwnAircraftCockpit, this, &CAfvClientBridge::updatedFromOwnAircraftCockpit);
|
||||||
|
connect(afvClient, &CAfvClient::ptt, this, &CAfvClientBridge::ptt);
|
||||||
|
connect(afvClient, &CAfvClient::inputVolumePeakVU, this, &CAfvClientBridge::inputVolumePeakVU);
|
||||||
|
connect(afvClient, &CAfvClient::outputVolumePeakVU, this, &CAfvClientBridge::outputVolumePeakVU);
|
||||||
|
}
|
||||||
97
samples/afvclient/afvclientbridge.h
Normal file
97
samples/afvclient/afvclientbridge.h
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
#ifndef AFVCLIENTBRIDGE_H
|
||||||
|
#define AFVCLIENTBRIDGE_H
|
||||||
|
|
||||||
|
#include "blackcore/afv/clients/afvclient.h"
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
class CAfvClientBridge : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(double inputVolumePeakVU READ getInputVolumePeakVU NOTIFY inputVolumePeakVU)
|
||||||
|
Q_PROPERTY(double outputVolumePeakVU READ getOutputVolumePeakVU NOTIFY outputVolumePeakVU)
|
||||||
|
Q_PROPERTY(BlackCore::Afv::Clients::CAfvClient::ConnectionStatus connectionStatus READ getConnectionStatus NOTIFY connectionStatusChanged)
|
||||||
|
Q_PROPERTY(QString receivingCallsignsCom1 READ getReceivingCallsignsCom1 NOTIFY receivingCallsignsChanged)
|
||||||
|
Q_PROPERTY(QString receivingCallsignsCom2 READ getReceivingCallsignsCom2 NOTIFY receivingCallsignsChanged)
|
||||||
|
|
||||||
|
public:
|
||||||
|
CAfvClientBridge(BlackCore::Afv::Clients::CAfvClient *afvClient, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
double getInputVolumePeakVU() const { return m_afvClient->getInputVolumePeakVU(); }
|
||||||
|
double getOutputVolumePeakVU() const { return m_afvClient->getOutputVolumePeakVU(); }
|
||||||
|
|
||||||
|
BlackCore::Afv::Clients::CAfvClient::ConnectionStatus getConnectionStatus() const
|
||||||
|
{
|
||||||
|
return m_afvClient->getConnectionStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString getReceivingCallsignsCom1() { return m_afvClient->getReceivingCallsignsCom1(); }
|
||||||
|
QString getReceivingCallsignsCom2() { return m_afvClient->getReceivingCallsignsCom2(); }
|
||||||
|
|
||||||
|
Q_INVOKABLE void connectTo(const QString &cid, const QString &password, const QString &callsign)
|
||||||
|
{
|
||||||
|
m_afvClient->connectTo(cid, password, callsign);
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_INVOKABLE void disconnectFrom() { m_afvClient->disconnectFrom(); }
|
||||||
|
|
||||||
|
//! Audio devices @{
|
||||||
|
Q_INVOKABLE QStringList availableInputDevices() const { return m_afvClient->availableInputDevices(); }
|
||||||
|
Q_INVOKABLE QStringList availableOutputDevices() const { return m_afvClient->availableOutputDevices(); }
|
||||||
|
//! @}
|
||||||
|
|
||||||
|
//! Enable/disable VHF simulation, true means effects are NOT used
|
||||||
|
Q_INVOKABLE void setBypassEffects(bool value) { /*m_afvClient->setBypassEffects(value);*/ }
|
||||||
|
|
||||||
|
Q_INVOKABLE void startAudio(const QString &inputDeviceName, const QString &outputDeviceName) { m_afvClient->startAudio(inputDeviceName, outputDeviceName); }
|
||||||
|
|
||||||
|
Q_INVOKABLE void enableTransceiver(quint16 id, bool enable) { /*m_afvClient->enableTransceiver(id, enable);*/ }
|
||||||
|
|
||||||
|
Q_INVOKABLE void updateComFrequency(quint16 id, quint32 frequencyHz) { m_afvClient->updateComFrequency(id, frequencyHz); }
|
||||||
|
|
||||||
|
//! Update own aircraft position
|
||||||
|
Q_INVOKABLE void updatePosition(double latitudeDeg, double longitudeDeg, double heightMeters)
|
||||||
|
{
|
||||||
|
m_afvClient->updatePosition(latitudeDeg, longitudeDeg, heightMeters);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Push to talk @{
|
||||||
|
Q_INVOKABLE void setPtt(bool active) { m_afvClient->setPtt(active); }
|
||||||
|
//! @}
|
||||||
|
|
||||||
|
//! Loopback @{
|
||||||
|
Q_INVOKABLE void setLoopBack(bool on) { m_afvClient->setLoopBack(on); }
|
||||||
|
Q_INVOKABLE bool isLoopback() const { return false; m_afvClient->isLoopback(); }
|
||||||
|
//! @}
|
||||||
|
|
||||||
|
//! Input volume in dB, +-18dB @{
|
||||||
|
Q_INVOKABLE void setInputVolumeDb(double valueDb) { /*m_afvClient->setInputVolumeDb(valueDb);*/ }
|
||||||
|
//! @}
|
||||||
|
|
||||||
|
//! Output volume in dB, +-18dB @{
|
||||||
|
Q_INVOKABLE void setOutputVolumeDb(double valueDb) { /*m_afvClient->setOutputVolumeDb(valueDb);*/ }
|
||||||
|
//! @}
|
||||||
|
|
||||||
|
signals:
|
||||||
|
//! Receiving callsigns have been changed
|
||||||
|
//! \remark callsigns I do receive
|
||||||
|
void receivingCallsignsChanged(const BlackCore::Afv::Audio::TransceiverReceivingCallsignsChangedArgs &args);
|
||||||
|
|
||||||
|
//! Connection status has been changed
|
||||||
|
void connectionStatusChanged(BlackCore::Afv::Clients::CAfvClient::ConnectionStatus status);
|
||||||
|
|
||||||
|
//! Client updated from own aicraft data
|
||||||
|
void updatedFromOwnAircraftCockpit();
|
||||||
|
|
||||||
|
//! PTT status in this particular AFV client
|
||||||
|
void ptt(bool active, BlackMisc::Audio::PTTCOM pttcom, const BlackMisc::CIdentifier &identifier);
|
||||||
|
|
||||||
|
//! VU levels @{
|
||||||
|
void inputVolumePeakVU(double value);
|
||||||
|
void outputVolumePeakVU(double value);
|
||||||
|
//! @}
|
||||||
|
|
||||||
|
private:
|
||||||
|
BlackCore::Afv::Clients::CAfvClient *m_afvClient = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CAFVCLIENTBRIDGE_H
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
// #include "voiceclientui.h"
|
#include "afvclientbridge.h"
|
||||||
|
|
||||||
#include "blackcore/afv/model/atcstationmodel.h"
|
#include "blackcore/afv/model/atcstationmodel.h"
|
||||||
#include "blackcore/afv/model/afvmapreader.h"
|
#include "blackcore/afv/model/afvmapreader.h"
|
||||||
#include "blackcore/afv/clients/afvclient.h"
|
#include "blackcore/afv/clients/afvclient.h"
|
||||||
|
#include "blackcore/registermetadata.h"
|
||||||
|
|
||||||
#include "blackcore/application.h"
|
#include "blackcore/application.h"
|
||||||
#include "blackmisc/network/user.h"
|
#include "blackmisc/network/user.h"
|
||||||
#include "blackmisc/obfuscation.h"
|
#include "blackmisc/obfuscation.h"
|
||||||
@@ -27,12 +28,21 @@ int main(int argc, char *argv[])
|
|||||||
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||||
QGuiApplication qa(argc, argv);
|
QGuiApplication qa(argc, argv);
|
||||||
|
|
||||||
CApplication a("sampleafvclient", CApplicationInfo::Sample);
|
BlackCore::registerMetadata();
|
||||||
|
|
||||||
|
BlackCore::CApplication a("sampleafvclient", CApplicationInfo::Sample);
|
||||||
|
|
||||||
CAfvMapReader *afvMapReader = new CAfvMapReader(&a);
|
CAfvMapReader *afvMapReader = new CAfvMapReader(&a);
|
||||||
afvMapReader->updateFromMap();
|
afvMapReader->updateFromMap();
|
||||||
|
|
||||||
CAfvClient voiceClient("https://voice1.vatsim.uk");
|
CAfvClient *voiceClient = new CAfvClient("https://voice1.vatsim.uk", &qa);
|
||||||
|
CAfvClientBridge *voiceClientBridge = new CAfvClientBridge(voiceClient, &qa);
|
||||||
|
voiceClient->start(QThread::TimeCriticalPriority);
|
||||||
|
|
||||||
|
QObject::connect(&qa, &QCoreApplication::aboutToQuit, [voiceClient]()
|
||||||
|
{
|
||||||
|
voiceClient->quitAndWait();
|
||||||
|
});
|
||||||
|
|
||||||
// default user name
|
// default user name
|
||||||
QString defaultUserName("1234567");
|
QString defaultUserName("1234567");
|
||||||
@@ -45,7 +55,7 @@ int main(int argc, char *argv[])
|
|||||||
QQmlApplicationEngine engine;
|
QQmlApplicationEngine engine;
|
||||||
QQmlContext *ctxt = engine.rootContext();
|
QQmlContext *ctxt = engine.rootContext();
|
||||||
ctxt->setContextProperty("afvMapReader", afvMapReader);
|
ctxt->setContextProperty("afvMapReader", afvMapReader);
|
||||||
ctxt->setContextProperty("voiceClient", &voiceClient);
|
ctxt->setContextProperty("voiceClient", voiceClientBridge);
|
||||||
ctxt->setContextProperty("userName", defaultUserName);
|
ctxt->setContextProperty("userName", defaultUserName);
|
||||||
|
|
||||||
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
|
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ namespace BlackCore
|
|||||||
}
|
}
|
||||||
|
|
||||||
CAfvClient::CAfvClient(const QString &apiServer, QObject *parent) :
|
CAfvClient::CAfvClient(const QString &apiServer, QObject *parent) :
|
||||||
QObject(parent), CIdentifiable(this),
|
CContinuousWorker(parent, "CAfvClient"),
|
||||||
m_connection(new CClientConnection(apiServer, this)),
|
m_connection(new CClientConnection(apiServer, this)),
|
||||||
m_input(new CInput(SampleRate, this)),
|
m_input(new CInput(SampleRate, this)),
|
||||||
m_output(new Output(this)),
|
m_output(new Output(this)),
|
||||||
@@ -64,6 +64,7 @@ namespace BlackCore
|
|||||||
|
|
||||||
void CAfvClient::initTransceivers()
|
void CAfvClient::initTransceivers()
|
||||||
{
|
{
|
||||||
|
QMutexLocker lock(&m_mutex);
|
||||||
m_transceivers =
|
m_transceivers =
|
||||||
{
|
{
|
||||||
{ 0, UniCom, 48.5, 11.5, 1000.0, 1000.0 },
|
{ 0, UniCom, 48.5, 11.5, 1000.0, 1000.0 },
|
||||||
@@ -87,6 +88,13 @@ namespace BlackCore
|
|||||||
|
|
||||||
void CAfvClient::connectTo(const QString &cid, const QString &password, const QString &callsign)
|
void CAfvClient::connectTo(const QString &cid, const QString &password, const QString &callsign)
|
||||||
{
|
{
|
||||||
|
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); });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this->initWithContext();
|
this->initWithContext();
|
||||||
m_callsign = callsign;
|
m_callsign = callsign;
|
||||||
m_connection->connectTo(cid, password, callsign);
|
m_connection->connectTo(cid, password, callsign);
|
||||||
@@ -100,6 +108,13 @@ namespace BlackCore
|
|||||||
|
|
||||||
void CAfvClient::disconnectFrom()
|
void CAfvClient::disconnectFrom()
|
||||||
{
|
{
|
||||||
|
if (QThread::currentThread() != thread())
|
||||||
|
{
|
||||||
|
// Method needs to be executed in the object thread since it will create new QObject children
|
||||||
|
QMetaObject::invokeMethod(this, [ = ]() { disconnectFrom(); });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
m_connection->disconnectFrom();
|
m_connection->disconnectFrom();
|
||||||
emit connectionStatusChanged(Disconnected);
|
emit connectionStatusChanged(Disconnected);
|
||||||
}
|
}
|
||||||
@@ -116,6 +131,7 @@ namespace BlackCore
|
|||||||
|
|
||||||
void CAfvClient::setBypassEffects(bool value)
|
void CAfvClient::setBypassEffects(bool value)
|
||||||
{
|
{
|
||||||
|
QMutexLocker lock(&m_mutex);
|
||||||
if (soundcardSampleProvider)
|
if (soundcardSampleProvider)
|
||||||
{
|
{
|
||||||
soundcardSampleProvider->setBypassEffects(value);
|
soundcardSampleProvider->setBypassEffects(value);
|
||||||
@@ -134,12 +150,12 @@ namespace BlackCore
|
|||||||
|
|
||||||
bool CAfvClient::restartWithNewDevices(const CAudioDeviceInfo &inputDevice, const CAudioDeviceInfo &outputDevice)
|
bool CAfvClient::restartWithNewDevices(const CAudioDeviceInfo &inputDevice, const CAudioDeviceInfo &outputDevice)
|
||||||
{
|
{
|
||||||
this->stop();
|
this->stopAudio();
|
||||||
this->start(inputDevice, outputDevice, allTransceiverIds());
|
this->startAudio(inputDevice, outputDevice, allTransceiverIds());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CAfvClient::start(const CAudioDeviceInfo &inputDevice, const CAudioDeviceInfo &outputDevice, const QVector<quint16> &transceiverIDs)
|
void CAfvClient::startAudio(const CAudioDeviceInfo &inputDevice, const CAudioDeviceInfo &outputDevice, const QVector<quint16> &transceiverIDs)
|
||||||
{
|
{
|
||||||
if (m_isStarted)
|
if (m_isStarted)
|
||||||
{
|
{
|
||||||
@@ -147,6 +163,13 @@ namespace BlackCore
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (QThread::currentThread() != this->thread())
|
||||||
|
{
|
||||||
|
// Method needs to be executed in the object thread since it will create new QObject children
|
||||||
|
QMetaObject::invokeMethod(this, [ = ]() { startAudio(inputDevice, outputDevice, transceiverIDs); });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this->initTransceivers();
|
this->initTransceivers();
|
||||||
|
|
||||||
soundcardSampleProvider = new CSoundcardSampleProvider(SampleRate, transceiverIDs, this);
|
soundcardSampleProvider = new CSoundcardSampleProvider(SampleRate, transceiverIDs, this);
|
||||||
@@ -165,21 +188,22 @@ namespace BlackCore
|
|||||||
CLogMessage(this).info(u"Started [Input: %1] [Output: %2]") << inputDevice.getName() << outputDevice.getName();
|
CLogMessage(this).info(u"Started [Input: %1] [Output: %2]") << inputDevice.getName() << outputDevice.getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CAfvClient::start(const QString &inputDeviceName, const QString &outputDeviceName)
|
void CAfvClient::startAudio(const QString &inputDeviceName, const QString &outputDeviceName)
|
||||||
|
{
|
||||||
|
const CAudioDeviceInfo i(CAudioDeviceInfo::InputDevice, inputDeviceName);
|
||||||
|
const CAudioDeviceInfo o(CAudioDeviceInfo::OutputDevice, outputDeviceName);
|
||||||
|
this->startAudio(i, o, allTransceiverIds());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CAfvClient::stopAudio()
|
||||||
{
|
{
|
||||||
if (QThread::currentThread() != this->thread())
|
if (QThread::currentThread() != this->thread())
|
||||||
{
|
{
|
||||||
QMetaObject::invokeMethod(this, "start", Q_ARG(QString, inputDeviceName), Q_ARG(QString, outputDeviceName));
|
// Method needs to be executed in the object thread since it will create new QObject children
|
||||||
|
QMetaObject::invokeMethod(this, [ = ]() { stopAudio(); });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CAudioDeviceInfo i(CAudioDeviceInfo::InputDevice, inputDeviceName);
|
|
||||||
const CAudioDeviceInfo o(CAudioDeviceInfo::OutputDevice, outputDeviceName);
|
|
||||||
this->start(i, o, allTransceiverIds());
|
|
||||||
}
|
|
||||||
|
|
||||||
void CAfvClient::stop()
|
|
||||||
{
|
|
||||||
if (!m_isStarted)
|
if (!m_isStarted)
|
||||||
{
|
{
|
||||||
CLogMessage(this).info(u"Client NOT started");
|
CLogMessage(this).info(u"Client NOT started");
|
||||||
@@ -199,6 +223,8 @@ namespace BlackCore
|
|||||||
|
|
||||||
void CAfvClient::enableTransceiver(quint16 id, bool enable)
|
void CAfvClient::enableTransceiver(quint16 id, bool enable)
|
||||||
{
|
{
|
||||||
|
QMutexLocker lock(&m_mutex);
|
||||||
|
|
||||||
if (enable) { m_enabledTransceivers.insert(id); }
|
if (enable) { m_enabledTransceivers.insert(id); }
|
||||||
else { m_enabledTransceivers.remove(id); }
|
else { m_enabledTransceivers.remove(id); }
|
||||||
|
|
||||||
@@ -233,6 +259,7 @@ namespace BlackCore
|
|||||||
// Fix rounding issues like 128074999 Hz -> 128075000 Hz
|
// Fix rounding issues like 128074999 Hz -> 128075000 Hz
|
||||||
quint32 roundedFrequencyHz = static_cast<quint32>(qRound(frequencyHz / 1000.0)) * 1000;
|
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)
|
auto it = std::find_if(m_aliasedStations.begin(), m_aliasedStations.end(), [roundedFrequencyHz](const StationDto & d)
|
||||||
{
|
{
|
||||||
return d.frequencyAlias == roundedFrequencyHz;
|
return d.frequencyAlias == roundedFrequencyHz;
|
||||||
@@ -267,6 +294,7 @@ namespace BlackCore
|
|||||||
|
|
||||||
void CAfvClient::updatePosition(double latitudeDeg, double longitudeDeg, double heightMeters)
|
void CAfvClient::updatePosition(double latitudeDeg, double longitudeDeg, double heightMeters)
|
||||||
{
|
{
|
||||||
|
QMutexLocker lock(&m_mutex);
|
||||||
for (TransceiverDto &transceiver : m_transceivers)
|
for (TransceiverDto &transceiver : m_transceivers)
|
||||||
{
|
{
|
||||||
transceiver.LatDeg = latitudeDeg;
|
transceiver.LatDeg = latitudeDeg;
|
||||||
@@ -322,11 +350,13 @@ namespace BlackCore
|
|||||||
|
|
||||||
void CAfvClient::setTransmittingTransceivers(const QVector<TxTransceiverDto> &transceivers)
|
void CAfvClient::setTransmittingTransceivers(const QVector<TxTransceiverDto> &transceivers)
|
||||||
{
|
{
|
||||||
|
QMutexLocker lock(&m_mutex);
|
||||||
m_transmittingTransceivers = transceivers;
|
m_transmittingTransceivers = transceivers;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CAfvClient::isTransmittingTransceiver(quint16 id) const
|
bool CAfvClient::isTransmittingTransceiver(quint16 id) const
|
||||||
{
|
{
|
||||||
|
QMutexLocker lock(&m_mutex);
|
||||||
for (const TxTransceiverDto &dto : m_transmittingTransceivers)
|
for (const TxTransceiverDto &dto : m_transmittingTransceivers)
|
||||||
{
|
{
|
||||||
if (dto.id == id) { return true; }
|
if (dto.id == id) { return true; }
|
||||||
@@ -355,6 +385,8 @@ namespace BlackCore
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (m_transmit == active) { return; }
|
if (m_transmit == active) { return; }
|
||||||
|
|
||||||
|
QMutexLocker lock(&m_mutex);
|
||||||
m_transmit = active;
|
m_transmit = active;
|
||||||
|
|
||||||
if (soundcardSampleProvider)
|
if (soundcardSampleProvider)
|
||||||
@@ -380,6 +412,8 @@ namespace BlackCore
|
|||||||
{
|
{
|
||||||
if (valueDb > MaxDbIn) { valueDb = MaxDbIn; }
|
if (valueDb > MaxDbIn) { valueDb = MaxDbIn; }
|
||||||
if (valueDb < MinDbIn) { valueDb = MinDbIn; }
|
if (valueDb < MinDbIn) { valueDb = MinDbIn; }
|
||||||
|
|
||||||
|
QMutexLocker lock(&m_mutex);
|
||||||
m_inputVolumeDb = valueDb;
|
m_inputVolumeDb = valueDb;
|
||||||
if (m_input)
|
if (m_input)
|
||||||
{
|
{
|
||||||
@@ -421,8 +455,22 @@ namespace BlackCore
|
|||||||
this->setOutputVolumeDb(dB);
|
this->setOutputVolumeDb(dB);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double CAfvClient::getInputVolumePeakVU() const
|
||||||
|
{
|
||||||
|
QMutexLocker lock(&m_mutex);
|
||||||
|
return m_inputVolumeStream.PeakVU;
|
||||||
|
}
|
||||||
|
|
||||||
|
double CAfvClient::getOutputVolumePeakVU() const
|
||||||
|
{
|
||||||
|
QMutexLocker lock(&m_mutex);
|
||||||
|
return m_outputVolumeStream.PeakVU;
|
||||||
|
}
|
||||||
|
|
||||||
void CAfvClient::opusDataAvailable(const OpusDataAvailableArgs &args)
|
void CAfvClient::opusDataAvailable(const OpusDataAvailableArgs &args)
|
||||||
{
|
{
|
||||||
|
QMutexLocker lock(&m_mutex);
|
||||||
|
|
||||||
if (m_loopbackOn && m_transmit)
|
if (m_loopbackOn && m_transmit)
|
||||||
{
|
{
|
||||||
IAudioDto audioData;
|
IAudioDto audioData;
|
||||||
@@ -431,6 +479,7 @@ namespace BlackCore
|
|||||||
audioData.lastPacket = false;
|
audioData.lastPacket = false;
|
||||||
audioData.sequenceCounter = 0;
|
audioData.sequenceCounter = 0;
|
||||||
|
|
||||||
|
|
||||||
RxTransceiverDto com1 = { 0, m_transceivers.size() > 0 ? m_transceivers[0].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 };
|
RxTransceiverDto com2 = { 1, m_transceivers.size() > 1 ? m_transceivers[1].frequencyHz : UniCom, 1.0 };
|
||||||
|
|
||||||
@@ -483,18 +532,21 @@ namespace BlackCore
|
|||||||
|
|
||||||
void CAfvClient::inputVolumeStream(const InputVolumeStreamArgs &args)
|
void CAfvClient::inputVolumeStream(const InputVolumeStreamArgs &args)
|
||||||
{
|
{
|
||||||
|
QMutexLocker lock(&m_mutex);
|
||||||
m_inputVolumeStream = args;
|
m_inputVolumeStream = args;
|
||||||
emit inputVolumePeakVU(m_inputVolumeStream.PeakVU);
|
emit inputVolumePeakVU(m_inputVolumeStream.PeakVU);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CAfvClient::outputVolumeStream(const OutputVolumeStreamArgs &args)
|
void CAfvClient::outputVolumeStream(const OutputVolumeStreamArgs &args)
|
||||||
{
|
{
|
||||||
|
QMutexLocker lock(&m_mutex);
|
||||||
m_outputVolumeStream = args;
|
m_outputVolumeStream = args;
|
||||||
emit outputVolumePeakVU(m_outputVolumeStream.PeakVU);
|
emit outputVolumePeakVU(m_outputVolumeStream.PeakVU);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString CAfvClient::getReceivingCallsignsCom1()
|
QString CAfvClient::getReceivingCallsignsCom1()
|
||||||
{
|
{
|
||||||
|
QMutexLocker lock(&m_mutex);
|
||||||
if (soundcardSampleProvider)
|
if (soundcardSampleProvider)
|
||||||
{
|
{
|
||||||
return soundcardSampleProvider->getReceivingCallsigns(0);
|
return soundcardSampleProvider->getReceivingCallsigns(0);
|
||||||
@@ -504,6 +556,7 @@ namespace BlackCore
|
|||||||
|
|
||||||
QString CAfvClient::getReceivingCallsignsCom2()
|
QString CAfvClient::getReceivingCallsignsCom2()
|
||||||
{
|
{
|
||||||
|
QMutexLocker lock(&m_mutex);
|
||||||
if (soundcardSampleProvider)
|
if (soundcardSampleProvider)
|
||||||
{
|
{
|
||||||
return soundcardSampleProvider->getReceivingCallsigns(1);
|
return soundcardSampleProvider->getReceivingCallsigns(1);
|
||||||
@@ -578,6 +631,8 @@ namespace BlackCore
|
|||||||
{
|
{
|
||||||
if (valueDb > MaxDbOut) { valueDb = MaxDbOut; }
|
if (valueDb > MaxDbOut) { valueDb = MaxDbOut; }
|
||||||
if (valueDb < MinDbOut) { valueDb = MinDbOut; }
|
if (valueDb < MinDbOut) { valueDb = MinDbOut; }
|
||||||
|
|
||||||
|
QMutexLocker lock(&m_mutex);
|
||||||
m_outputVolumeDb = valueDb;
|
m_outputVolumeDb = valueDb;
|
||||||
|
|
||||||
m_outputVolume = qPow(10, m_outputVolumeDb / 20.0);
|
m_outputVolume = qPow(10, m_outputVolumeDb / 20.0);
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
#include "blackmisc/logcategorylist.h"
|
#include "blackmisc/logcategorylist.h"
|
||||||
#include "blackmisc/identifiable.h"
|
#include "blackmisc/identifiable.h"
|
||||||
#include "blackmisc/settingscache.h"
|
#include "blackmisc/settingscache.h"
|
||||||
|
#include "blackmisc/worker.h"
|
||||||
|
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QAudioInput>
|
#include <QAudioInput>
|
||||||
@@ -35,6 +36,8 @@
|
|||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
namespace BlackCore
|
namespace BlackCore
|
||||||
{
|
{
|
||||||
namespace Afv
|
namespace Afv
|
||||||
@@ -42,12 +45,12 @@ namespace BlackCore
|
|||||||
namespace Clients
|
namespace Clients
|
||||||
{
|
{
|
||||||
//! AFV client
|
//! AFV client
|
||||||
class BLACKCORE_EXPORT CAfvClient final : public QObject, public BlackMisc::CIdentifiable
|
class BLACKCORE_EXPORT CAfvClient final : public BlackMisc::CContinuousWorker
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(double inputVolumePeakVU READ getInputVolumePeakVU NOTIFY inputVolumePeakVU)
|
Q_PROPERTY(double inputVolumePeakVU READ getInputVolumePeakVU NOTIFY inputVolumePeakVU)
|
||||||
Q_PROPERTY(double outputVolumePeakVU READ getOutputVolumePeakVU NOTIFY outputVolumePeakVU)
|
Q_PROPERTY(double outputVolumePeakVU READ getOutputVolumePeakVU NOTIFY outputVolumePeakVU)
|
||||||
Q_PROPERTY(ConnectionStatus connectionStatus READ getConnectionStatus NOTIFY connectionStatusChanged)
|
Q_PROPERTY(BlackCore::Afv::Clients::CAfvClient::ConnectionStatus connectionStatus READ getConnectionStatus NOTIFY connectionStatusChanged)
|
||||||
Q_PROPERTY(QString receivingCallsignsCom1 READ getReceivingCallsignsCom1 NOTIFY receivingCallsignsChanged)
|
Q_PROPERTY(QString receivingCallsignsCom1 READ getReceivingCallsignsCom1 NOTIFY receivingCallsignsChanged)
|
||||||
Q_PROPERTY(QString receivingCallsignsCom2 READ getReceivingCallsignsCom2 NOTIFY receivingCallsignsChanged)
|
Q_PROPERTY(QString receivingCallsignsCom2 READ getReceivingCallsignsCom2 NOTIFY receivingCallsignsChanged)
|
||||||
|
|
||||||
@@ -63,7 +66,7 @@ namespace BlackCore
|
|||||||
CAfvClient(const QString &apiServer, QObject *parent = nullptr);
|
CAfvClient(const QString &apiServer, QObject *parent = nullptr);
|
||||||
|
|
||||||
//! Dtor
|
//! Dtor
|
||||||
virtual ~CAfvClient() override { this->stop(); }
|
virtual ~CAfvClient() override { this->stopAudio(); }
|
||||||
|
|
||||||
//! Corresponding callsign
|
//! Corresponding callsign
|
||||||
QString callsign() const { return m_callsign; }
|
QString callsign() const { return m_callsign; }
|
||||||
@@ -100,9 +103,9 @@ namespace BlackCore
|
|||||||
//! @}
|
//! @}
|
||||||
|
|
||||||
bool restartWithNewDevices(const BlackMisc::Audio::CAudioDeviceInfo &inputDevice, const BlackMisc::Audio::CAudioDeviceInfo &outputDevice);
|
bool restartWithNewDevices(const BlackMisc::Audio::CAudioDeviceInfo &inputDevice, const BlackMisc::Audio::CAudioDeviceInfo &outputDevice);
|
||||||
void start(const BlackMisc::Audio::CAudioDeviceInfo &inputDevice, const BlackMisc::Audio::CAudioDeviceInfo &outputDevice, const QVector<quint16> &transceiverIDs);
|
void startAudio(const BlackMisc::Audio::CAudioDeviceInfo &inputDevice, const BlackMisc::Audio::CAudioDeviceInfo &outputDevice, const QVector<quint16> &transceiverIDs);
|
||||||
Q_INVOKABLE void start(const QString &inputDeviceName, const QString &outputDeviceName);
|
Q_INVOKABLE void startAudio(const QString &inputDeviceName, const QString &outputDeviceName);
|
||||||
void stop();
|
void stopAudio();
|
||||||
|
|
||||||
//! Enable COM unit/transceiver @{
|
//! Enable COM unit/transceiver @{
|
||||||
Q_INVOKABLE void enableTransceiver(quint16 id, bool enable);
|
Q_INVOKABLE void enableTransceiver(quint16 id, bool enable);
|
||||||
@@ -159,8 +162,8 @@ namespace BlackCore
|
|||||||
//! @}
|
//! @}
|
||||||
|
|
||||||
//! VU values, 0..1 @{
|
//! VU values, 0..1 @{
|
||||||
double getInputVolumePeakVU() const { return m_inputVolumeStream.PeakVU; }
|
double getInputVolumePeakVU() const;
|
||||||
double getOutputVolumePeakVU() const { return m_outputVolumeStream.PeakVU; }
|
double getOutputVolumePeakVU() const;
|
||||||
//! @}
|
//! @}
|
||||||
|
|
||||||
//! Recently used device @{
|
//! Recently used device @{
|
||||||
@@ -176,7 +179,7 @@ namespace BlackCore
|
|||||||
signals:
|
signals:
|
||||||
//! Receiving callsigns have been changed
|
//! Receiving callsigns have been changed
|
||||||
//! \remark callsigns I do receive
|
//! \remark callsigns I do receive
|
||||||
void receivingCallsignsChanged(const Audio::TransceiverReceivingCallsignsChangedArgs &args);
|
void receivingCallsignsChanged(const BlackCore::Afv::Audio::TransceiverReceivingCallsignsChangedArgs &args);
|
||||||
|
|
||||||
//! Connection status has been changed
|
//! Connection status has been changed
|
||||||
void connectionStatusChanged(ConnectionStatus status);
|
void connectionStatusChanged(ConnectionStatus status);
|
||||||
@@ -228,13 +231,13 @@ namespace BlackCore
|
|||||||
Audio::CSoundcardSampleProvider *soundcardSampleProvider = nullptr;
|
Audio::CSoundcardSampleProvider *soundcardSampleProvider = nullptr;
|
||||||
BlackSound::SampleProvider::CVolumeSampleProvider *outputSampleProvider = nullptr;
|
BlackSound::SampleProvider::CVolumeSampleProvider *outputSampleProvider = nullptr;
|
||||||
|
|
||||||
bool m_transmit = false;
|
std::atomic_bool m_transmit = { false };
|
||||||
bool m_transmitHistory = false;
|
bool m_transmitHistory = false;
|
||||||
QVector<TxTransceiverDto> m_transmittingTransceivers;
|
QVector<TxTransceiverDto> m_transmittingTransceivers;
|
||||||
static const QVector<quint16> &allTransceiverIds() { static const QVector<quint16> transceiverIds{0, 1}; return transceiverIds; }
|
static const QVector<quint16> &allTransceiverIds() { static const QVector<quint16> transceiverIds{0, 1}; return transceiverIds; }
|
||||||
|
|
||||||
bool m_isStarted = false;
|
bool m_isStarted = false;
|
||||||
bool m_loopbackOn = false;
|
std::atomic_bool m_loopbackOn = { false };
|
||||||
QDateTime m_startDateTimeUtc;
|
QDateTime m_startDateTimeUtc;
|
||||||
|
|
||||||
double m_inputVolumeDb;
|
double m_inputVolumeDb;
|
||||||
@@ -253,6 +256,8 @@ namespace BlackCore
|
|||||||
void initTransceivers();
|
void initTransceivers();
|
||||||
void initWithContext();
|
void initWithContext();
|
||||||
static bool hasContext();
|
static bool hasContext();
|
||||||
|
|
||||||
|
mutable QMutex m_mutex;
|
||||||
};
|
};
|
||||||
} // ns
|
} // ns
|
||||||
} // ns
|
} // ns
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ namespace BlackCore
|
|||||||
|
|
||||||
QNetworkRequest request(url);
|
QNetworkRequest request(url);
|
||||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||||
QEventLoop loop(sApp);
|
QEventLoop loop;
|
||||||
|
|
||||||
// posted in QAM thread, reply is nullptr if called from another thread
|
// posted in QAM thread, reply is nullptr if called from another thread
|
||||||
QNetworkReply *reply = sApp->postToNetwork(request, CApplication::NoLogRequestId, QJsonDocument(obj).toJson(),
|
QNetworkReply *reply = sApp->postToNetwork(request, CApplication::NoLogRequestId, QJsonDocument(obj).toJson(),
|
||||||
@@ -160,7 +160,7 @@ namespace BlackCore
|
|||||||
{
|
{
|
||||||
if (isShuttingDown()) { return {}; }
|
if (isShuttingDown()) { return {}; }
|
||||||
|
|
||||||
QEventLoop loop(sApp);
|
QEventLoop loop;
|
||||||
QByteArray receivedData;
|
QByteArray receivedData;
|
||||||
|
|
||||||
// posted in QAM thread, reply is nullptr if called from another thread
|
// posted in QAM thread, reply is nullptr if called from another thread
|
||||||
@@ -196,7 +196,7 @@ namespace BlackCore
|
|||||||
{
|
{
|
||||||
if (isShuttingDown()) { return {}; }
|
if (isShuttingDown()) { return {}; }
|
||||||
|
|
||||||
QEventLoop loop(sApp);
|
QEventLoop loop;
|
||||||
QByteArray receivedData;
|
QByteArray receivedData;
|
||||||
|
|
||||||
// posted in QAM thread, reply is nullptr if called from another thread
|
// posted in QAM thread, reply is nullptr if called from another thread
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
#include "blackmisc/logcategorylist.h"
|
#include "blackmisc/logcategorylist.h"
|
||||||
#include "blackcore/afv/dto.h"
|
#include "blackcore/afv/dto.h"
|
||||||
#include "blackcore/application.h"
|
#include "blackcore/application.h"
|
||||||
|
#include "blackmisc/logmessage.h"
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QNetworkAccessManager>
|
#include <QNetworkAccessManager>
|
||||||
@@ -78,7 +79,7 @@ namespace BlackCore
|
|||||||
{
|
{
|
||||||
if (!m_isAuthenticated)
|
if (!m_isAuthenticated)
|
||||||
{
|
{
|
||||||
CLogMessage(this).debug(u"AFV not authenticated");
|
BlackMisc::CLogMessage(this).debug(u"AFV not authenticated");
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,7 +102,7 @@ namespace BlackCore
|
|||||||
{
|
{
|
||||||
if (! m_isAuthenticated)
|
if (! m_isAuthenticated)
|
||||||
{
|
{
|
||||||
CLogMessage(this).debug(u"AFV not authenticated");
|
BlackMisc::CLogMessage(this).debug(u"AFV not authenticated");
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ namespace BlackCore
|
|||||||
|
|
||||||
IContextAudio::~IContextAudio()
|
IContextAudio::~IContextAudio()
|
||||||
{
|
{
|
||||||
m_voiceClient.stop();
|
m_voiceClient.stopAudio();
|
||||||
}
|
}
|
||||||
|
|
||||||
const CIdentifier &IContextAudio::audioRunsWhere() const
|
const CIdentifier &IContextAudio::audioRunsWhere() const
|
||||||
@@ -394,11 +394,11 @@ namespace BlackCore
|
|||||||
{
|
{
|
||||||
const CUser connectedUser = this->getIContextNetwork()->getConnectedServer().getUser();
|
const CUser connectedUser = this->getIContextNetwork()->getConnectedServer().getUser();
|
||||||
m_voiceClient.connectTo(connectedUser.getId(), connectedUser.getPassword(), connectedUser.getCallsign().asString());
|
m_voiceClient.connectTo(connectedUser.getId(), connectedUser.getPassword(), connectedUser.getCallsign().asString());
|
||||||
m_voiceClient.start(CAudioDeviceInfo::getDefaultInputDevice(), CAudioDeviceInfo::getDefaultOutputDevice(), {0, 1});
|
m_voiceClient.startAudio(CAudioDeviceInfo::getDefaultInputDevice(), CAudioDeviceInfo::getDefaultOutputDevice(), {0, 1});
|
||||||
}
|
}
|
||||||
else if (to.isDisconnected())
|
else if (to.isDisconnected())
|
||||||
{
|
{
|
||||||
m_voiceClient.stop();
|
m_voiceClient.stopAudio();
|
||||||
m_voiceClient.disconnectFrom();
|
m_voiceClient.disconnectFrom();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#include "blackcore/fsd/fsdclient.h"
|
#include "blackcore/fsd/fsdclient.h"
|
||||||
#include "blackcore/webreaderflags.h"
|
#include "blackcore/webreaderflags.h"
|
||||||
#include "blackcore/aircraftmatcher.h"
|
#include "blackcore/aircraftmatcher.h"
|
||||||
|
#include "blackcore/afv/clients/afvclient.h"
|
||||||
#include "blackmisc/dbus.h"
|
#include "blackmisc/dbus.h"
|
||||||
#include "blackmisc/network/network.h"
|
#include "blackmisc/network/network.h"
|
||||||
#include "blackmisc/valueobject.h"
|
#include "blackmisc/valueobject.h"
|
||||||
@@ -33,6 +34,10 @@ namespace BlackCore
|
|||||||
// however, does not harm if it is redundant
|
// however, does not harm if it is redundant
|
||||||
qRegisterMetaType<CWebReaderFlags::WebReader>();
|
qRegisterMetaType<CWebReaderFlags::WebReader>();
|
||||||
qRegisterMetaType<CWebReaderFlags::WebReaderFlag>();
|
qRegisterMetaType<CWebReaderFlags::WebReaderFlag>();
|
||||||
|
qRegisterMetaType<BlackCore::Afv::Clients::CAfvClient::ConnectionStatus>();
|
||||||
|
qRegisterMetaType<BlackCore::Afv::Clients::CAfvClient::ConnectionStatus>("ConnectionStatus");
|
||||||
|
qRegisterMetaType<BlackCore::Afv::Audio::TransceiverReceivingCallsignsChangedArgs>();
|
||||||
|
qRegisterMetaType<BlackCore::Afv::Audio::TransceiverReceivingCallsignsChangedArgs>("TransceiverReceivingCallsignsChangedArgs");
|
||||||
|
|
||||||
qDBusRegisterMetaType<Context::CLogSubscriptionHash>();
|
qDBusRegisterMetaType<Context::CLogSubscriptionHash>();
|
||||||
qDBusRegisterMetaType<Context::CLogSubscriptionPair>();
|
qDBusRegisterMetaType<Context::CLogSubscriptionPair>();
|
||||||
|
|||||||
@@ -36,32 +36,23 @@ namespace BlackSound
|
|||||||
{
|
{
|
||||||
if (m_data->fileName.isEmpty()) { return false; }
|
if (m_data->fileName.isEmpty()) { return false; }
|
||||||
|
|
||||||
QObject *parent = QCoreApplication::instance();
|
CWavFile wavFile;
|
||||||
Q_ASSERT(parent);
|
m_data->samples.clear();
|
||||||
CWorker *worker = CWorker::fromTask(parent, "loadResourceSound", [this]()
|
if (wavFile.open(m_data->fileName))
|
||||||
{
|
{
|
||||||
CWavFile wavFile;
|
if (wavFile.fileFormat().sampleType() == QAudioFormat::Float)
|
||||||
|
|
||||||
m_data->samples.clear();
|
|
||||||
if (wavFile.open(m_data->fileName))
|
|
||||||
{
|
{
|
||||||
if (wavFile.fileFormat().sampleType() == QAudioFormat::Float)
|
// Not implemented
|
||||||
{
|
// m_samples = convertFloatBytesTo16BitPCM(wavFile.audioData());
|
||||||
// Not implemented
|
|
||||||
// m_samples = convertFloatBytesTo16BitPCM(wavFile.audioData());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_data->samples = convertBytesTo32BitFloatPCM(wavFile.audioData());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
else
|
||||||
worker->then([this]()
|
{
|
||||||
{
|
m_data->samples = convertBytesTo32BitFloatPCM(wavFile.audioData());
|
||||||
m_data->isLoaded = true;
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
return worker ? true : false;
|
m_data->isLoaded = true;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CResourceSound::isSameFileName(const QString &fn) const
|
bool CResourceSound::isSameFileName(const QString &fn) const
|
||||||
|
|||||||
Reference in New Issue
Block a user