mirror of
https://github.com/swift-project/pilotclient.git
synced 2026-05-05 01:35:45 +08:00
refs #255 move voice channel handling from CVoiceVatlib into a dedicated class CVoiceChannel
This commit is contained in:
@@ -4,7 +4,7 @@
|
|||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "blackcorefreefunctions.h"
|
#include "blackcorefreefunctions.h"
|
||||||
#include "voice.h"
|
#include "voice_channel.h"
|
||||||
#include "simulator.h"
|
#include "simulator.h"
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
|
||||||
@@ -15,10 +15,8 @@ namespace BlackCore
|
|||||||
// for some reasons (ask RW) these are registered twice
|
// for some reasons (ask RW) these are registered twice
|
||||||
qRegisterMetaType<ISimulator::Status>();
|
qRegisterMetaType<ISimulator::Status>();
|
||||||
qRegisterMetaType<ISimulator::Status>("Status");
|
qRegisterMetaType<ISimulator::Status>("Status");
|
||||||
qRegisterMetaType<IVoice::ComUnit>();
|
qRegisterMetaType<IVoiceChannel::ConnectionStatus>();
|
||||||
qRegisterMetaType<IVoice::ComUnit>("ComUnit");
|
qRegisterMetaType<IVoiceChannel::ConnectionStatus>("ConnectionStatus");
|
||||||
qRegisterMetaType<IVoice::ConnectionStatus>();
|
|
||||||
qRegisterMetaType<IVoice::ConnectionStatus>("ConnectionStatus");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isCurrentThreadCreatingThread(QObject *toBeTested)
|
bool isCurrentThreadCreatingThread(QObject *toBeTested)
|
||||||
|
|||||||
@@ -21,6 +21,8 @@
|
|||||||
|
|
||||||
namespace BlackCore
|
namespace BlackCore
|
||||||
{
|
{
|
||||||
|
class IVoiceChannel;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Interface to a connection to a ATC voice server for use in flight simulation.
|
* Interface to a connection to a ATC voice server for use in flight simulation.
|
||||||
*
|
*
|
||||||
@@ -49,35 +51,9 @@ namespace BlackCore
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief IVoice currently supports two different com units
|
|
||||||
*/
|
|
||||||
enum ComUnit
|
|
||||||
{
|
|
||||||
COM1 = 0, /*!< ComUnit 1 */
|
|
||||||
COM2 /*!< ComUnit 2 */
|
|
||||||
};
|
|
||||||
|
|
||||||
//! Com status
|
|
||||||
enum ConnectionStatus
|
|
||||||
{
|
|
||||||
Disconnected = 0, //!< Not connected
|
|
||||||
Disconnecting, //!< In transition to disconnected
|
|
||||||
DisconnectedError, //!< Disconnected due to socket error
|
|
||||||
Connecting, //!< Connection initiated but not established
|
|
||||||
Connected, //!< Connection established
|
|
||||||
ConnectingFailed, //!< Failed to connect
|
|
||||||
};
|
|
||||||
|
|
||||||
//! Virtual destructor.
|
//! Virtual destructor.
|
||||||
virtual ~IVoice() {}
|
virtual ~IVoice() {}
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Own aircraft's callsign
|
|
||||||
* \param callsign
|
|
||||||
*/
|
|
||||||
virtual void setMyAircraftCallsign(const BlackMisc::Aviation::CCallsign &callsign) = 0;
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Audio devices
|
* \brief Audio devices
|
||||||
* \return
|
* \return
|
||||||
@@ -128,6 +104,9 @@ namespace BlackCore
|
|||||||
*/
|
*/
|
||||||
virtual QString micTestResultAsString() const = 0;
|
virtual QString micTestResultAsString() const = 0;
|
||||||
|
|
||||||
|
//! Get voice channel object
|
||||||
|
virtual IVoiceChannel *getVoiceChannel(qint32 channelIndex) const = 0;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -150,68 +129,6 @@ namespace BlackCore
|
|||||||
*/
|
*/
|
||||||
virtual void setInputDevice(const BlackMisc::Audio::CAudioDevice &device) = 0;
|
virtual void setInputDevice(const BlackMisc::Audio::CAudioDevice &device) = 0;
|
||||||
|
|
||||||
/*!
|
|
||||||
* Get COM1/2 voice rooms, which then allows to retrieve information
|
|
||||||
* such as audio status etc.
|
|
||||||
*/
|
|
||||||
virtual BlackMisc::Audio::CVoiceRoomList getComVoiceRoomsWithAudioStatus() const = 0;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Get COM1/2 voice rooms, const and with no status update
|
|
||||||
*/
|
|
||||||
virtual BlackMisc::Audio::CVoiceRoomList getComVoiceRooms() const = 0;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Join voice room
|
|
||||||
* \param comUnit COM1/2
|
|
||||||
* \param voiceRoom
|
|
||||||
*/
|
|
||||||
virtual void joinVoiceRoom(const ComUnit comUnit, const BlackMisc::Audio::CVoiceRoom &voiceRoom) = 0;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Leave voice room
|
|
||||||
* \param comUnit COM1/2
|
|
||||||
*/
|
|
||||||
virtual void leaveVoiceRoom(const ComUnit comUnit) = 0;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Leave all voice rooms
|
|
||||||
*/
|
|
||||||
virtual void leaveAllVoiceRooms() = 0;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Set room output volume for COM unit
|
|
||||||
*/
|
|
||||||
virtual void setRoomOutputVolume(const ComUnit comUnit, const qint32 volumne) = 0;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Start transmitting
|
|
||||||
*/
|
|
||||||
virtual void startTransmitting(const ComUnit comUnit) = 0;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Stop transmitting
|
|
||||||
*/
|
|
||||||
virtual void stopTransmitting(const ComUnit comUnit) = 0;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Get voice room callsings
|
|
||||||
* \return
|
|
||||||
*/
|
|
||||||
virtual BlackMisc::Aviation::CCallsignList getVoiceRoomCallsigns(const ComUnit comUnit) const = 0;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Is muted?
|
|
||||||
*/
|
|
||||||
virtual bool isMuted() const = 0;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Switch audio output, enable or disable given COM unit.
|
|
||||||
* \param comUnit
|
|
||||||
* \param enable enable or disable output
|
|
||||||
*/
|
|
||||||
virtual void switchAudioOutput(const ComUnit comUnit, bool enable) = 0;
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Enable audio loopback to route recorded voice from microphone to speakers
|
* \brief Enable audio loopback to route recorded voice from microphone to speakers
|
||||||
* \param enable (default true)
|
* \param enable (default true)
|
||||||
@@ -220,40 +137,6 @@ namespace BlackCore
|
|||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
||||||
//! The status of a room has changed.
|
|
||||||
void connectionStatusChanged(ComUnit comUnit, ConnectionStatus oldStatus, ConnectionStatus newStatus);
|
|
||||||
|
|
||||||
// Signals about users joining and leaving
|
|
||||||
/*!
|
|
||||||
* \brief User with callsign joined room
|
|
||||||
*/
|
|
||||||
void userJoinedRoom(const BlackMisc::Aviation::CCallsign &callsign);
|
|
||||||
/*!
|
|
||||||
* \brief User with callsign left room
|
|
||||||
*/
|
|
||||||
void userLeftRoom(const BlackMisc::Aviation::CCallsign &callsign);
|
|
||||||
|
|
||||||
// Audio signals
|
|
||||||
/*!
|
|
||||||
* \brief Audio for given unit started
|
|
||||||
*/
|
|
||||||
void audioStarted(const ComUnit comUnit);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Audio for given unit stopped
|
|
||||||
*/
|
|
||||||
void audioStopped(const ComUnit comUnit);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Audio started
|
|
||||||
*/
|
|
||||||
void globalAudioStarted();
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Audio stopped
|
|
||||||
*/
|
|
||||||
void globalAudioStopped();
|
|
||||||
|
|
||||||
// Test signals
|
// Test signals
|
||||||
/*!
|
/*!
|
||||||
* \brief Squelch test completed
|
* \brief Squelch test completed
|
||||||
@@ -275,7 +158,4 @@ namespace BlackCore
|
|||||||
|
|
||||||
} // namespace BlackCore
|
} // namespace BlackCore
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(BlackCore::IVoice::ComUnit)
|
|
||||||
Q_DECLARE_METATYPE(BlackCore::IVoice::ConnectionStatus)
|
|
||||||
|
|
||||||
#endif // guard
|
#endif // guard
|
||||||
|
|||||||
121
src/blackcore/voice_channel.h
Normal file
121
src/blackcore/voice_channel.h
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
/* Copyright (C) 2014
|
||||||
|
* swift project community / contributors
|
||||||
|
*
|
||||||
|
* This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level
|
||||||
|
* directory of this distribution and at http://www.swift-project.org/license.html. No part of Swift Project,
|
||||||
|
* including this file, may be copied, modified, propagated, or distributed except according to the terms
|
||||||
|
* contained in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BLACKCORE_VOICE_CHANNEL_H
|
||||||
|
#define BLACKCORE_VOICE_CHANNEL_H
|
||||||
|
|
||||||
|
#include "voice_vatlib.h"
|
||||||
|
#include "blackmisc/statusmessage.h"
|
||||||
|
#include "blackmisc/voiceroomlist.h"
|
||||||
|
#include "blackmisc/avcallsignlist.h"
|
||||||
|
#include <QObject>
|
||||||
|
#include <QScopedPointer>
|
||||||
|
|
||||||
|
//! \file
|
||||||
|
|
||||||
|
namespace BlackCore
|
||||||
|
{
|
||||||
|
//! Interface to a voice channel
|
||||||
|
class IVoiceChannel : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
//! Com status
|
||||||
|
enum ConnectionStatus
|
||||||
|
{
|
||||||
|
Disconnected = 0, //!< Not connected
|
||||||
|
Disconnecting, //!< In transition to disconnected
|
||||||
|
DisconnectedError, //!< Disconnected due to socket error
|
||||||
|
Connecting, //!< Connection initiated but not established
|
||||||
|
Connected, //!< Connection established
|
||||||
|
ConnectingFailed, //!< Failed to connect
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Join voice room
|
||||||
|
virtual void joinVoiceRoom(const BlackMisc::Audio::CVoiceRoom &voiceRoom) = 0;
|
||||||
|
|
||||||
|
//! Leave voice room
|
||||||
|
virtual void leaveVoiceRoom() = 0;
|
||||||
|
|
||||||
|
//! Set room output volume
|
||||||
|
virtual void setRoomOutputVolume(const qint32 volume) = 0;
|
||||||
|
|
||||||
|
//! Start transmitting
|
||||||
|
virtual void startTransmitting() = 0;
|
||||||
|
|
||||||
|
//! Stop transmitting
|
||||||
|
virtual void stopTransmitting() = 0;
|
||||||
|
|
||||||
|
//! Get voice room callsings
|
||||||
|
virtual BlackMisc::Aviation::CCallsignList getVoiceRoomCallsigns() const = 0;
|
||||||
|
|
||||||
|
//! Switch audio output, enable or disable
|
||||||
|
virtual void switchAudioOutput(bool enable) = 0;
|
||||||
|
|
||||||
|
//! Set own aircraft's callsign
|
||||||
|
virtual void setMyAircraftCallsign(const BlackMisc::Aviation::CCallsign &callsign) = 0;
|
||||||
|
|
||||||
|
//! Get voice room
|
||||||
|
virtual BlackMisc::Audio::CVoiceRoom getVoiceRoom() const = 0;
|
||||||
|
|
||||||
|
//! Get assigned room index
|
||||||
|
virtual qint32 getRoomIndex() const = 0;
|
||||||
|
|
||||||
|
//! Is channel muted?
|
||||||
|
virtual bool isMuted() const = 0;
|
||||||
|
|
||||||
|
//! Set channel volume
|
||||||
|
virtual void setVolume(quint32 volume) = 0;
|
||||||
|
|
||||||
|
//! Get channel volume
|
||||||
|
virtual quint32 getVolume() const = 0;
|
||||||
|
|
||||||
|
//! Update room status
|
||||||
|
virtual void updateRoomStatus(Cvatlib_Voice_Simple::roomStatusUpdate roomStatus) = 0;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
//! We sent a message about the status of the network connection, for the attention of the user.
|
||||||
|
void statusMessage(const BlackMisc::CStatusMessage &message);
|
||||||
|
|
||||||
|
//! The status of a room has changed.
|
||||||
|
void connectionStatusChanged(ConnectionStatus oldStatus, ConnectionStatus newStatus);
|
||||||
|
|
||||||
|
// Signals about users joining and leaving
|
||||||
|
|
||||||
|
//! User with callsign joined room
|
||||||
|
void userJoinedRoom(const BlackMisc::Aviation::CCallsign &callsign);
|
||||||
|
|
||||||
|
//! User with callsign left room
|
||||||
|
void userLeftRoom(const BlackMisc::Aviation::CCallsign &callsign);
|
||||||
|
|
||||||
|
// Audio signals
|
||||||
|
|
||||||
|
//! Audio for given unit started
|
||||||
|
void audioStarted();
|
||||||
|
|
||||||
|
//! Audio for given unit stopped
|
||||||
|
void audioStopped();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
//! Constructor
|
||||||
|
IVoiceChannel(QObject *parent = nullptr) : QObject(parent) {}
|
||||||
|
|
||||||
|
//! Destructor
|
||||||
|
virtual ~IVoiceChannel() {}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(BlackCore::IVoiceChannel::ConnectionStatus)
|
||||||
|
|
||||||
|
#endif // guard
|
||||||
490
src/blackcore/voice_channel_vatlib.cpp
Normal file
490
src/blackcore/voice_channel_vatlib.cpp
Normal file
@@ -0,0 +1,490 @@
|
|||||||
|
/* Copyright (C) 2014
|
||||||
|
* swift project community / contributors
|
||||||
|
*
|
||||||
|
* This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level
|
||||||
|
* directory of this distribution and at http://www.swift-project.org/license.html. No part of Swift Project,
|
||||||
|
* including this file, may be copied, modified, propagated, or distributed except according to the terms
|
||||||
|
* contained in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "voice_channel_vatlib.h"
|
||||||
|
#include "voice_channel_vatlib_p.h"
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
using namespace BlackMisc;
|
||||||
|
using namespace BlackMisc::Audio;
|
||||||
|
using namespace BlackMisc::Aviation;
|
||||||
|
|
||||||
|
namespace BlackCore
|
||||||
|
{
|
||||||
|
// Room data hash shared between all CVoiceChannel objects
|
||||||
|
QHash<CVoiceChannelVatlib * const, QSharedPointer<CVoiceChannelVatlibPrivate>> CVoiceChannelVatlibPrivate::m_sharedRoomData;
|
||||||
|
|
||||||
|
// Static list of available rooms
|
||||||
|
QList<qint32> CVoiceChannelVatlibPrivate::m_availableRooms = {0, 1};
|
||||||
|
|
||||||
|
// Constructor
|
||||||
|
// Don't set the QObject parent. It will conflict with @QSharedPointer@ memory management
|
||||||
|
CVoiceChannelVatlibPrivate::CVoiceChannelVatlibPrivate(TVatlibPointer vatlib, CVoiceChannelVatlib *parent)
|
||||||
|
: m_vatlib(vatlib),
|
||||||
|
m_mutexSharedRoomData(QMutex::Recursive),
|
||||||
|
m_mutexCallSign(QMutex::Recursive),
|
||||||
|
m_mutexVoiceRoom(QMutex::Recursive),
|
||||||
|
m_mutexCallsignList(QMutex::Recursive),
|
||||||
|
q_ptr(parent)
|
||||||
|
{
|
||||||
|
m_roomIndex.store(InvalidRoomIndex);
|
||||||
|
m_volume.store(100);
|
||||||
|
m_connectionRefCount.store(0);
|
||||||
|
m_outputEnabled.store(true);
|
||||||
|
m_roomStatus.store(IVoiceChannel::Disconnected);
|
||||||
|
|
||||||
|
connect(this, &CVoiceChannelVatlibPrivate::userJoinedLeft, this, &CVoiceChannelVatlibPrivate::processUserJoinedLeft, Qt::QueuedConnection);
|
||||||
|
}
|
||||||
|
|
||||||
|
CVoiceChannelVatlibPrivate::~CVoiceChannelVatlibPrivate()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CVoiceChannelVatlibPrivate::changeConnectionStatus(IVoiceChannel::ConnectionStatus newStatus)
|
||||||
|
{
|
||||||
|
Q_Q(CVoiceChannelVatlib);
|
||||||
|
|
||||||
|
m_roomStatus = newStatus;
|
||||||
|
emit q->connectionStatusChanged(m_roomStatus, newStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CVoiceChannelVatlibPrivate::switchAudioOutput(bool enable)
|
||||||
|
{
|
||||||
|
std::lock_guard<TVatlibPointer> locker(m_vatlib);
|
||||||
|
Q_ASSERT_X(m_vatlib->IsValid() && m_vatlib->IsSetup(), "CVoiceChannelVatlibPrivate", "Cvatlib_Voice_Simple invalid or not setup!");
|
||||||
|
Q_ASSERT_X(m_vatlib->IsRoomValid(m_roomIndex), "CVoiceChannelVatlibPrivate", "Room index out of bounds!");
|
||||||
|
|
||||||
|
m_outputEnabled = enable;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
m_vatlib->SetOutputState(m_roomIndex, 0, enable);
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
exceptionDispatcher(Q_FUNC_INFO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CVoiceChannelVatlibPrivate::startTransmitting()
|
||||||
|
{
|
||||||
|
std::lock_guard<TVatlibPointer> locker(m_vatlib);
|
||||||
|
Q_ASSERT_X(m_vatlib->IsValid() && m_vatlib->IsSetup(), "CVoiceChannelVatlibPrivate", "Cvatlib_Voice_Simple invalid or not setup!");
|
||||||
|
Q_ASSERT_X(m_vatlib->IsRoomValid(m_roomIndex), "CVoiceChannelVatlibPrivate", "Room index out of bounds!");
|
||||||
|
|
||||||
|
if (m_roomStatus != IVoiceChannel::Connected) return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
m_vatlib->SetMicState(m_roomIndex, true);
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
this->exceptionDispatcher(Q_FUNC_INFO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CVoiceChannelVatlibPrivate::stopTransmitting()
|
||||||
|
{
|
||||||
|
std::lock_guard<TVatlibPointer> locker(m_vatlib);
|
||||||
|
Q_ASSERT_X(m_vatlib->IsValid() && m_vatlib->IsSetup(), "CVoiceChannelVatlibPrivate", "Cvatlib_Voice_Simple invalid or not setup!");
|
||||||
|
Q_ASSERT_X(m_vatlib->IsRoomValid(m_roomIndex), "CVoiceChannelVatlibPrivate", "Room index out of bounds!");
|
||||||
|
|
||||||
|
if (m_roomStatus != IVoiceChannel::Connected) return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
m_vatlib->SetMicState(m_roomIndex, false);
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
this->exceptionDispatcher(Q_FUNC_INFO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CVoiceChannelVatlibPrivate::setRoomOutputVolume(const qint32 volume)
|
||||||
|
{
|
||||||
|
std::lock_guard<TVatlibPointer> locker(m_vatlib);
|
||||||
|
Q_ASSERT_X(m_vatlib->IsValid() && m_vatlib->IsSetup(), "CVoiceChannelVatlibPrivate", "Cvatlib_Voice_Simple invalid or not setup!");
|
||||||
|
Q_ASSERT_X(m_vatlib->IsRoomValid(m_roomIndex), "CVoiceChannelVatlibPrivate", "Room index out of bounds!");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
m_vatlib->SetRoomVolume(m_roomIndex, volume);
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
this->exceptionDispatcher(Q_FUNC_INFO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forward exception as signal
|
||||||
|
void CVoiceChannelVatlibPrivate::exceptionDispatcher(const char *caller)
|
||||||
|
{
|
||||||
|
Q_Q(CVoiceChannelVatlib);
|
||||||
|
|
||||||
|
QString msg("Caller: ");
|
||||||
|
msg.append(caller).append(" ").append("Exception: ");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
catch (const NetworkNotConnectedException &e)
|
||||||
|
{
|
||||||
|
// this could be caused by a race condition during normal operation, so not an error
|
||||||
|
msg.append("NetworkNotConnectedException").append(" ").append(e.what());
|
||||||
|
emit q->statusMessage(CStatusMessage::getErrorMessage(msg, CStatusMessage::TypeAudio));
|
||||||
|
qDebug() << "NetworkNotConnectedException caught in " << caller << "\n" << e.what();
|
||||||
|
}
|
||||||
|
catch (const VatlibException &e)
|
||||||
|
{
|
||||||
|
msg.append("VatlibException").append(" ").append(e.what());
|
||||||
|
emit q->statusMessage(CStatusMessage::getErrorMessage(msg, CStatusMessage::TypeAudio));
|
||||||
|
qFatal("VatlibException caught in %s\n%s", caller, e.what());
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
msg.append("std::exception").append(" ").append(e.what());
|
||||||
|
emit q->statusMessage(CStatusMessage::getErrorMessage(msg, CStatusMessage::TypeAudio));
|
||||||
|
qFatal("std::exception caught in %s\n%s", caller, e.what());
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
msg.append("unknown exception");
|
||||||
|
emit q->statusMessage(CStatusMessage::getErrorMessage(msg, CStatusMessage::TypeAudio));
|
||||||
|
qFatal("Unknown exception caught in %s", caller);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CVoiceChannelVatlibPrivate::processUserJoinedLeft()
|
||||||
|
{
|
||||||
|
Q_Q(CVoiceChannelVatlib);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
std::lock_guard<TVatlibPointer> locker(m_vatlib);
|
||||||
|
// Paranoia... clear list completely
|
||||||
|
if (!m_vatlib->IsRoomConnected(m_roomIndex))
|
||||||
|
{
|
||||||
|
QMutexLocker lockCallsignList(&m_mutexCallsignList);
|
||||||
|
m_listCallsigns.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Callbacks already completed when function GetRoomUserList returns,
|
||||||
|
// thereafter m_voiceRoomCallsignsUpdate is filled with the latest callsigns
|
||||||
|
|
||||||
|
m_vatlib->GetRoomUserList(m_roomIndex, updateRoomUsers, this);
|
||||||
|
|
||||||
|
QMutexLocker lockCallsignList(&m_mutexCallsignList);
|
||||||
|
// we have all current users in m_temporaryVoiceRoomCallsigns
|
||||||
|
foreach(CCallsign callsign, m_listCallsigns)
|
||||||
|
{
|
||||||
|
if (!m_temporaryVoiceRoomCallsigns.contains(callsign))
|
||||||
|
{
|
||||||
|
// User has left
|
||||||
|
emit q->userLeftRoom(callsign);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach(CCallsign callsign, m_temporaryVoiceRoomCallsigns)
|
||||||
|
{
|
||||||
|
if (!m_listCallsigns.contains(callsign))
|
||||||
|
{
|
||||||
|
// he joined
|
||||||
|
emit q->userJoinedRoom(callsign);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally we update it with our new list
|
||||||
|
m_listCallsigns = m_temporaryVoiceRoomCallsigns;
|
||||||
|
m_temporaryVoiceRoomCallsigns.clear();
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
this->exceptionDispatcher(Q_FUNC_INFO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CVoiceChannelVatlibPrivate *cbvar_cast_voiceChannelPrivate(void *cbvar)
|
||||||
|
{
|
||||||
|
return static_cast<CVoiceChannelVatlibPrivate *>(cbvar);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Room user received
|
||||||
|
*/
|
||||||
|
void CVoiceChannelVatlibPrivate::updateRoomUsers(Cvatlib_Voice_Simple *obj, const char *name, void *cbVar)
|
||||||
|
{
|
||||||
|
Q_UNUSED(obj)
|
||||||
|
|
||||||
|
// sanity check
|
||||||
|
QString callsign = QString(name);
|
||||||
|
if (callsign.isEmpty()) return;
|
||||||
|
|
||||||
|
// add callsign
|
||||||
|
CVoiceChannelVatlibPrivate *voiceChannelPrivate = cbvar_cast_voiceChannelPrivate(cbVar);
|
||||||
|
|
||||||
|
// add user
|
||||||
|
// callsign might contain: VATSIM id, user name
|
||||||
|
if (callsign.contains(" "))
|
||||||
|
{
|
||||||
|
QStringList parts = callsign.split(" ");
|
||||||
|
callsign = parts[0];
|
||||||
|
// I throw away VATSIM id here, maybe we could use it
|
||||||
|
}
|
||||||
|
|
||||||
|
voiceChannelPrivate->addTemporaryCallsignForRoom(CCallsign(callsign));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add temp.callsign for room
|
||||||
|
*/
|
||||||
|
void CVoiceChannelVatlibPrivate::addTemporaryCallsignForRoom(const CCallsign &callsign)
|
||||||
|
{
|
||||||
|
if (m_temporaryVoiceRoomCallsigns.contains(callsign)) return;
|
||||||
|
m_temporaryVoiceRoomCallsigns.push_back(callsign);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get shared room data
|
||||||
|
QHash<CVoiceChannelVatlib * const, QSharedPointer<CVoiceChannelVatlibPrivate>> &CVoiceChannelVatlibPrivate::getSharedRoomData()
|
||||||
|
{
|
||||||
|
return m_sharedRoomData;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate a new room
|
||||||
|
qint32 CVoiceChannelVatlibPrivate::allocateRoom()
|
||||||
|
{
|
||||||
|
Q_ASSERT(!m_availableRooms.isEmpty());
|
||||||
|
return m_availableRooms.takeFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constructor
|
||||||
|
CVoiceChannelVatlib::CVoiceChannelVatlib(TVatlibPointer vatlib, QObject *parent)
|
||||||
|
: IVoiceChannel(parent),
|
||||||
|
d_ptr(new CVoiceChannelVatlibPrivate(vatlib, this))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destructor
|
||||||
|
CVoiceChannelVatlib::~CVoiceChannelVatlib()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// Join room
|
||||||
|
void CVoiceChannelVatlib::joinVoiceRoom(const CVoiceRoom &voiceRoom)
|
||||||
|
{
|
||||||
|
// Find if a different channel is connected already to this voice room
|
||||||
|
auto roomDataList = CVoiceChannelVatlibPrivate::getSharedRoomData().values();
|
||||||
|
auto iterator = std::find_if(roomDataList.begin(), roomDataList.end(), [&](const QSharedPointer<CVoiceChannelVatlibPrivate> roomData)
|
||||||
|
{
|
||||||
|
return roomData->m_voiceRoom.getVoiceRoomUrl() == voiceRoom.getVoiceRoomUrl();
|
||||||
|
});
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// If we found an another channel
|
||||||
|
if (iterator != roomDataList.end())
|
||||||
|
{
|
||||||
|
// Increase the connection reference counter
|
||||||
|
(*iterator)->m_connectionRefCount++;
|
||||||
|
|
||||||
|
d_ptr = (*iterator);
|
||||||
|
|
||||||
|
// Assign shared room data to this channel index
|
||||||
|
CVoiceChannelVatlibPrivate::getSharedRoomData().insert(this, *iterator);
|
||||||
|
|
||||||
|
// Since the room is used already, we have to simulate the state changes
|
||||||
|
emit connectionStatusChanged(IVoiceChannel::Disconnected, IVoiceChannel::Connecting);
|
||||||
|
emit connectionStatusChanged(IVoiceChannel::Connecting, IVoiceChannel::Connected);
|
||||||
|
emit d_ptr->userJoinedLeft();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QMutexLocker lockVoiceRoom(&d_ptr->m_mutexVoiceRoom);
|
||||||
|
// No one else is using this voice room, so prepare to join
|
||||||
|
d_ptr->m_voiceRoom = voiceRoom;
|
||||||
|
d_ptr->m_roomIndex = d_ptr->allocateRoom();
|
||||||
|
d_ptr->m_roomStatus = IVoiceChannel::Disconnected;
|
||||||
|
|
||||||
|
std::lock_guard<TVatlibPointer> locker(d_ptr->m_vatlib);
|
||||||
|
QMutexLocker lockerCallsign(&d_ptr->m_mutexCallSign);
|
||||||
|
bool jr = d_ptr->m_vatlib->JoinRoom(d_ptr->m_roomIndex, d_ptr->m_callsign.toQString().toLatin1().constData(),
|
||||||
|
d_ptr->m_voiceRoom.getVoiceRoomUrl().toLatin1().constData());
|
||||||
|
if (!jr) qWarning() << "Could not join voice room";
|
||||||
|
CVoiceChannelVatlibPrivate::m_sharedRoomData.insert(this, d_ptr);
|
||||||
|
++d_ptr->m_connectionRefCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
d_ptr->exceptionDispatcher(Q_FUNC_INFO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Leave room
|
||||||
|
void CVoiceChannelVatlib::leaveVoiceRoom()
|
||||||
|
{
|
||||||
|
// If this room is not connected, there is nothing to do
|
||||||
|
if (d_ptr->m_roomStatus == IVoiceChannel::Disconnecting || d_ptr->m_roomStatus == IVoiceChannel::Disconnected) return;
|
||||||
|
|
||||||
|
// Decrease the connection reference counter
|
||||||
|
--d_ptr->m_connectionRefCount;
|
||||||
|
|
||||||
|
// If this was the last channel, connected to the room, leave it.
|
||||||
|
if (d_ptr->m_connectionRefCount == 0)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
qDebug() << "Leaving voice room!";
|
||||||
|
if(d_ptr->m_vatlib->IsRoomConnected(d_ptr->m_roomIndex))
|
||||||
|
{
|
||||||
|
std::lock_guard<TVatlibPointer> locker(d_ptr->m_vatlib);
|
||||||
|
d_ptr->m_vatlib->LeaveRoom(d_ptr->m_roomIndex);
|
||||||
|
d_ptr->m_availableRooms.append(d_ptr->m_roomIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
d_ptr->exceptionDispatcher(Q_FUNC_INFO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// We need to assign a private class
|
||||||
|
// This automatically clears callsign list etc.
|
||||||
|
TVatlibPointer vatlib = d_ptr->m_vatlib;
|
||||||
|
d_ptr.reset(new CVoiceChannelVatlibPrivate(vatlib, this));
|
||||||
|
CVoiceChannelVatlibPrivate::getSharedRoomData().insert(this, d_ptr);
|
||||||
|
|
||||||
|
// Simulate the state change
|
||||||
|
d_ptr->changeConnectionStatus(IVoiceChannel::Disconnecting);
|
||||||
|
d_ptr->changeConnectionStatus(IVoiceChannel::Disconnected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set room volume
|
||||||
|
void CVoiceChannelVatlib::setRoomOutputVolume(const qint32 volume)
|
||||||
|
{
|
||||||
|
d_ptr->setRoomOutputVolume(volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CVoiceChannelVatlib::startTransmitting()
|
||||||
|
{
|
||||||
|
d_ptr->startTransmitting();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CVoiceChannelVatlib::stopTransmitting()
|
||||||
|
{
|
||||||
|
d_ptr->stopTransmitting();
|
||||||
|
}
|
||||||
|
|
||||||
|
CCallsignList CVoiceChannelVatlib::getVoiceRoomCallsigns() const
|
||||||
|
{
|
||||||
|
QMutexLocker lockCallsignList(&d_ptr->m_mutexCallsignList);
|
||||||
|
return d_ptr->m_listCallsigns;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CVoiceChannelVatlib::switchAudioOutput(bool enable)
|
||||||
|
{
|
||||||
|
d_ptr->switchAudioOutput(enable);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CVoiceChannelVatlib::setMyAircraftCallsign(const CCallsign &callsign)
|
||||||
|
{
|
||||||
|
QMutexLocker lockerCallsign(&d_ptr->m_mutexCallSign);
|
||||||
|
d_ptr->m_callsign = callsign;
|
||||||
|
}
|
||||||
|
|
||||||
|
BlackMisc::Audio::CVoiceRoom CVoiceChannelVatlib::getVoiceRoom() const
|
||||||
|
{
|
||||||
|
QMutexLocker lockVoiceRoom(&d_ptr->m_mutexVoiceRoom);
|
||||||
|
return d_ptr->m_voiceRoom;
|
||||||
|
}
|
||||||
|
|
||||||
|
qint32 CVoiceChannelVatlib::getRoomIndex() const
|
||||||
|
{
|
||||||
|
return d_ptr->m_roomIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CVoiceChannelVatlib::updateRoomStatus(Cvatlib_Voice_Simple::roomStatusUpdate roomStatus)
|
||||||
|
{
|
||||||
|
switch (roomStatus)
|
||||||
|
{
|
||||||
|
case Cvatlib_Voice_Simple::roomStatusUpdate_JoinSuccess:
|
||||||
|
{
|
||||||
|
QMutexLocker lockVoiceRoom(&d_ptr->m_mutexVoiceRoom);
|
||||||
|
d_ptr->m_voiceRoom.setConnected(true);
|
||||||
|
d_ptr->changeConnectionStatus(IVoiceChannel::Connected);
|
||||||
|
bool isOutputEnabled = d_ptr->m_outputEnabled;
|
||||||
|
switchAudioOutput(isOutputEnabled);
|
||||||
|
emit d_ptr->userJoinedLeft();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Cvatlib_Voice_Simple::roomStatusUpdate_JoinFail:
|
||||||
|
{
|
||||||
|
QMutexLocker lockVoiceRoom(&d_ptr->m_mutexVoiceRoom);
|
||||||
|
d_ptr->m_voiceRoom.setConnected(false);
|
||||||
|
d_ptr->changeConnectionStatus(IVoiceChannel::ConnectingFailed);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Cvatlib_Voice_Simple::roomStatusUpdate_UnexpectedDisconnectOrKicked:
|
||||||
|
d_ptr->m_voiceRoom.setConnected(false);
|
||||||
|
d_ptr->changeConnectionStatus(IVoiceChannel::DisconnectedError);
|
||||||
|
break;
|
||||||
|
case Cvatlib_Voice_Simple::roomStatusUpdate_LeaveComplete:
|
||||||
|
{
|
||||||
|
// Instead of clearing and resetting all internals, we just assign a new default room data
|
||||||
|
// The former one will be deallocated automatically.
|
||||||
|
TVatlibPointer vatlib = d_ptr->m_vatlib;
|
||||||
|
d_ptr.reset(new CVoiceChannelVatlibPrivate(vatlib, this));
|
||||||
|
CVoiceChannelVatlibPrivate::getSharedRoomData().insert(this, d_ptr);
|
||||||
|
d_ptr->changeConnectionStatus(IVoiceChannel::Disconnected);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Cvatlib_Voice_Simple::roomStatusUpdate_UserJoinsLeaves:
|
||||||
|
// FIXME: We cannot call GetRoomUserList because vatlib is not reentrent safe.
|
||||||
|
emit d_ptr->userJoinedLeft();
|
||||||
|
break;
|
||||||
|
case Cvatlib_Voice_Simple::roomStatusUpdate_AudioStarted:
|
||||||
|
break;
|
||||||
|
case Cvatlib_Voice_Simple::roomStatusUpdate_AudioStopped:
|
||||||
|
break;
|
||||||
|
case Cvatlib_Voice_Simple::roomStatusUpdate_RoomAudioStarted:
|
||||||
|
emit audioStarted();
|
||||||
|
break;
|
||||||
|
case Cvatlib_Voice_Simple::roomStatusUpdate_RoomAudioStopped:
|
||||||
|
emit audioStopped();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CVoiceChannelVatlib::isMuted() const
|
||||||
|
{
|
||||||
|
return !d_ptr->m_outputEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CVoiceChannelVatlib::setVolume(quint32 volume)
|
||||||
|
{
|
||||||
|
d_ptr->m_volume.store(volume);
|
||||||
|
Q_ASSERT_X(d_ptr->m_vatlib->IsValid() && d_ptr->m_vatlib->IsSetup(), "CVoiceChannelVatlibPrivate", "Cvatlib_Voice_Simple invalid or not setup!");
|
||||||
|
Q_ASSERT_X(d_ptr->m_vatlib->IsRoomValid(d_ptr->m_roomIndex.load()), "CVoiceChannelVatlibPrivate", "Room index out of bounds!");
|
||||||
|
|
||||||
|
d_ptr->m_vatlib->SetOutputVolume(d_ptr->m_roomIndex.load(), volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
quint32 CVoiceChannelVatlib::getVolume() const
|
||||||
|
{
|
||||||
|
return d_ptr->m_volume.load();
|
||||||
|
}
|
||||||
|
}
|
||||||
87
src/blackcore/voice_channel_vatlib.h
Normal file
87
src/blackcore/voice_channel_vatlib.h
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
/* Copyright (C) 2014
|
||||||
|
* swift project community / contributors
|
||||||
|
*
|
||||||
|
* This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level
|
||||||
|
* directory of this distribution and at http://www.swift-project.org/license.html. No part of Swift Project,
|
||||||
|
* including this file, may be copied, modified, propagated, or distributed except according to the terms
|
||||||
|
* contained in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BLACKCORE_VOICE_CHANNEL_VATLIB_H
|
||||||
|
#define BLACKCORE_VOICE_CHANNEL_VATLIB_H
|
||||||
|
|
||||||
|
#include "voice_channel.h"
|
||||||
|
#include "voice_vatlib.h"
|
||||||
|
#include "blackmisc/statusmessage.h"
|
||||||
|
#include "blackmisc/voiceroomlist.h"
|
||||||
|
#include "blackmisc/avcallsignlist.h"
|
||||||
|
#include <QObject>
|
||||||
|
#include <QSharedPointer>
|
||||||
|
|
||||||
|
//! \file
|
||||||
|
|
||||||
|
namespace BlackCore
|
||||||
|
{
|
||||||
|
class CVoiceChannelVatlibPrivate;
|
||||||
|
|
||||||
|
//! Class implementing the voice channel interface
|
||||||
|
class CVoiceChannelVatlib : public IVoiceChannel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
//! Default constructor
|
||||||
|
CVoiceChannelVatlib(TVatlibPointer vatlib, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
//! Destructor
|
||||||
|
virtual ~CVoiceChannelVatlib();
|
||||||
|
|
||||||
|
//! \copydoc IVoiceChannel::joinVoiceRoom
|
||||||
|
virtual void joinVoiceRoom(const BlackMisc::Audio::CVoiceRoom &voiceRoom) override;
|
||||||
|
|
||||||
|
//! \copydoc IVoiceChannel::leaveVoiceRoom
|
||||||
|
virtual void leaveVoiceRoom() override;
|
||||||
|
|
||||||
|
//! \copydoc IVoiceChannel::setRoomOutputVolume
|
||||||
|
virtual void setRoomOutputVolume(const qint32 volume) override;
|
||||||
|
|
||||||
|
//! \copydoc IVoiceChannel::startTransmitting
|
||||||
|
virtual void startTransmitting() override;
|
||||||
|
|
||||||
|
//! \copydoc IVoiceChannel::stopTransmitting
|
||||||
|
virtual void stopTransmitting() override;
|
||||||
|
|
||||||
|
//! \copydoc IVoiceChannel::getVoiceRoomCallsigns
|
||||||
|
virtual BlackMisc::Aviation::CCallsignList getVoiceRoomCallsigns() const override;
|
||||||
|
|
||||||
|
//! \copydoc IVoiceChannel::switchAudioOutput
|
||||||
|
virtual void switchAudioOutput(bool enable) override;
|
||||||
|
|
||||||
|
//! \copydoc IVoiceChannel::setMyAircraftCallsign
|
||||||
|
virtual void setMyAircraftCallsign(const BlackMisc::Aviation::CCallsign &callsign) override;
|
||||||
|
|
||||||
|
//! \copydoc IVoiceChannel::getVoiceRoom
|
||||||
|
virtual BlackMisc::Audio::CVoiceRoom getVoiceRoom() const override;
|
||||||
|
|
||||||
|
//! \copydoc IVoiceChannel::getRoomIndex
|
||||||
|
virtual qint32 getRoomIndex() const override;
|
||||||
|
|
||||||
|
//! \copydoc IVoiceChannel::isMuted
|
||||||
|
virtual bool isMuted() const override;
|
||||||
|
|
||||||
|
//! Set channel volume
|
||||||
|
virtual void setVolume(quint32 volume) override;
|
||||||
|
|
||||||
|
//! Get channel volume
|
||||||
|
virtual quint32 getVolume() const override;
|
||||||
|
|
||||||
|
//! \copydoc IVoiceChannel::updateRoomStatus
|
||||||
|
virtual void updateRoomStatus(Cvatlib_Voice_Simple::roomStatusUpdate roomStatus) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QSharedPointer<CVoiceChannelVatlibPrivate> d_ptr;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // guard
|
||||||
112
src/blackcore/voice_channel_vatlib_p.h
Normal file
112
src/blackcore/voice_channel_vatlib_p.h
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
/* Copyright (C) 2014
|
||||||
|
* swift project community / contributors
|
||||||
|
*
|
||||||
|
* This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level
|
||||||
|
* directory of this distribution and at http://www.swift-project.org/license.html. No part of Swift Project,
|
||||||
|
* including this file, may be copied, modified, propagated, or distributed except according to the terms
|
||||||
|
* contained in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BLACKCORE_VOICE_CHANNEL_P_H
|
||||||
|
#define BLACKCORE_VOICE_CHANNEL_P_H
|
||||||
|
|
||||||
|
#include "voice_channel_vatlib.h"
|
||||||
|
#include "blackmisc/voiceroom.h"
|
||||||
|
#include "blackmisc/avcallsign.h"
|
||||||
|
#include <QObject>
|
||||||
|
#include <QMutex>
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
//! \file
|
||||||
|
|
||||||
|
namespace BlackCore
|
||||||
|
{
|
||||||
|
// Inhibit doxygen warnings about missing documentation
|
||||||
|
//! \cond PRIVATE
|
||||||
|
|
||||||
|
// Private Implementation of CVoiceChannelVatlib
|
||||||
|
class CVoiceChannelVatlibPrivate : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
// Default constructor
|
||||||
|
CVoiceChannelVatlibPrivate(TVatlibPointer vatlib, CVoiceChannelVatlib *parent);
|
||||||
|
|
||||||
|
// Destructor
|
||||||
|
~CVoiceChannelVatlibPrivate();
|
||||||
|
|
||||||
|
// Enable or disable channel audio output
|
||||||
|
void switchAudioOutput(bool enable);
|
||||||
|
|
||||||
|
// Start transmitting
|
||||||
|
void startTransmitting();
|
||||||
|
|
||||||
|
// Stop transmitting
|
||||||
|
void stopTransmitting();
|
||||||
|
|
||||||
|
// Set room output volume
|
||||||
|
void setRoomOutputVolume(const qint32 volume);
|
||||||
|
|
||||||
|
// FIXME Move into free function
|
||||||
|
void exceptionDispatcher(const char *caller);
|
||||||
|
|
||||||
|
// Update connected room users
|
||||||
|
static void updateRoomUsers(Cvatlib_Voice_Simple *obj, const char *name, void *cbVar);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
|
||||||
|
// Process user joined/left signals
|
||||||
|
void processUserJoinedLeft();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
// Signal when user has joined or left
|
||||||
|
void userJoinedLeft();
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
// Add a callsign temporarily. This is used for the getUserList callback
|
||||||
|
void addTemporaryCallsignForRoom(const BlackMisc::Aviation::CCallsign &callsign);
|
||||||
|
|
||||||
|
// Get shared room data for this channel
|
||||||
|
static QHash<CVoiceChannelVatlib * const, QSharedPointer<CVoiceChannelVatlibPrivate>> &getSharedRoomData();
|
||||||
|
|
||||||
|
TVatlibPointer m_vatlib; // Shared pointer to vatlib object
|
||||||
|
BlackMisc::Aviation::CCallsign m_callsign; // Own callsign
|
||||||
|
std::atomic<qint32> m_roomIndex; // Room index
|
||||||
|
BlackMisc::Audio::CVoiceRoom m_voiceRoom; // Voice Room
|
||||||
|
std::atomic<qint32> m_volume; // Room volume
|
||||||
|
std::atomic<quint16> m_connectionRefCount; // Connection reference couting
|
||||||
|
std::atomic<bool> m_outputEnabled; // Is room output enabled?
|
||||||
|
BlackMisc::Aviation::CCallsignList m_listCallsigns; // Callsigns connected to room
|
||||||
|
std::atomic<IVoiceChannel::ConnectionStatus> m_roomStatus; // Room connection status
|
||||||
|
|
||||||
|
// Mutexes
|
||||||
|
QMutex m_mutexSharedRoomData;
|
||||||
|
QMutex m_mutexCallSign;
|
||||||
|
QMutex m_mutexVoiceRoom;
|
||||||
|
QMutex m_mutexCallsignList;
|
||||||
|
|
||||||
|
BlackMisc::Aviation::CCallsignList m_temporaryVoiceRoomCallsigns; // temp. storage of voice rooms during update
|
||||||
|
|
||||||
|
static QList<qint32> m_availableRooms; // Static list of not used room indexes
|
||||||
|
static QHash<CVoiceChannelVatlib * const, QSharedPointer<CVoiceChannelVatlibPrivate>> m_sharedRoomData;
|
||||||
|
const static qint32 InvalidRoomIndex = -1; // Invalid room index
|
||||||
|
|
||||||
|
CVoiceChannelVatlib * const q_ptr = nullptr;
|
||||||
|
Q_DECLARE_PUBLIC(CVoiceChannelVatlib)
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
// Change room connection status
|
||||||
|
void changeConnectionStatus(IVoiceChannel::ConnectionStatus newStatus);
|
||||||
|
|
||||||
|
// Allocate a not used room identified by its index
|
||||||
|
qint32 allocateRoom();
|
||||||
|
};
|
||||||
|
//! \endcond
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // guard
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "voice_vatlib.h"
|
#include "voice_vatlib.h"
|
||||||
|
#include "voice_channel_vatlib.h"
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
@@ -24,8 +25,6 @@ namespace BlackCore
|
|||||||
// m_audioOutput(nullptr), // removed #227
|
// m_audioOutput(nullptr), // removed #227
|
||||||
m_inputSquelch(-1),
|
m_inputSquelch(-1),
|
||||||
m_micTestResult(Cvatlib_Voice_Simple::agc_Ok),
|
m_micTestResult(Cvatlib_Voice_Simple::agc_Ok),
|
||||||
m_isAudioLoopbackEnabled(false),
|
|
||||||
m_temporaryUserRoomIndex(CVoiceVatlib::InvalidRoomIndex),
|
|
||||||
m_lockCurrentOutputDevice(QMutex::Recursive),
|
m_lockCurrentOutputDevice(QMutex::Recursive),
|
||||||
m_lockCurrentInputDevice(QMutex::Recursive),
|
m_lockCurrentInputDevice(QMutex::Recursive),
|
||||||
m_lockDeviceList(QMutex::Recursive)
|
m_lockDeviceList(QMutex::Recursive)
|
||||||
@@ -39,15 +38,15 @@ namespace BlackCore
|
|||||||
m_vatlib->GetInputDevices(onInputHardwareDeviceReceived, this);
|
m_vatlib->GetInputDevices(onInputHardwareDeviceReceived, this);
|
||||||
m_vatlib->GetOutputDevices(onOutputHardwareDeviceReceived, this);
|
m_vatlib->GetOutputDevices(onOutputHardwareDeviceReceived, this);
|
||||||
|
|
||||||
connect(this, &CVoiceVatlib::userJoinedLeft, this, &CVoiceVatlib::onUserJoinedLeft, Qt::QueuedConnection);
|
|
||||||
|
|
||||||
this->m_vatlibRooms.push_back(CVoiceRoom()); // COM1
|
|
||||||
this->m_vatlibRooms.push_back(CVoiceRoom()); // COM2
|
|
||||||
this->m_outputEnabled.insert(COM1, true);
|
|
||||||
this->m_outputEnabled.insert(COM2, true);
|
|
||||||
this->m_currentInputDevice = this->defaultAudioInputDevice();
|
this->m_currentInputDevice = this->defaultAudioInputDevice();
|
||||||
this->m_currentOutputDevice = this->defaultAudioOutputDevice();
|
this->m_currentOutputDevice = this->defaultAudioOutputDevice();
|
||||||
// this->m_audioOutput->setVolume(1.0); // make sure the overall sound is not muted
|
|
||||||
|
for (int ii = 0; ii < 2; ++ii)
|
||||||
|
{
|
||||||
|
IVoiceChannel *channel = new CVoiceChannelVatlib(m_vatlib, this);
|
||||||
|
Q_ASSERT(channel);
|
||||||
|
m_hashChannelIndex.insert(ii, channel);
|
||||||
|
}
|
||||||
|
|
||||||
// do processing
|
// do processing
|
||||||
this->startTimer(10);
|
this->startTimer(10);
|
||||||
@@ -170,61 +169,6 @@ namespace BlackCore
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Get voice rooms, with the latest status updated
|
|
||||||
*/
|
|
||||||
BlackMisc::Audio::CVoiceRoomList CVoiceVatlib::getComVoiceRoomsWithAudioStatus() const
|
|
||||||
{
|
|
||||||
QReadLocker lockForReading(&m_lockVoiceRooms);
|
|
||||||
Q_ASSERT_X(m_vatlibRooms.size() == 2, "CVoiceVatlib", "Wrong numer of COM voice rooms");
|
|
||||||
|
|
||||||
if (!m_vatlib->IsValid() || !m_vatlib->IsSetup()) return CVoiceRoomList::twoEmptyRooms();
|
|
||||||
|
|
||||||
QMutexLocker lockerVatlib(&m_mutexVatlib);
|
|
||||||
// valid state, update
|
|
||||||
CVoiceRoom com1 = this->m_vatlibRooms[0];
|
|
||||||
CVoiceRoom com2 = this->m_vatlibRooms[1];
|
|
||||||
com1.setConnected(m_vatlib->IsRoomConnected(static_cast<qint32>(COM1)));
|
|
||||||
com2.setConnected(m_vatlib->IsRoomConnected(static_cast<qint32>(COM2)));
|
|
||||||
com1.setAudioPlaying(com1.isConnected() ? m_vatlib->IsAudioPlaying(static_cast<qint32>(COM1)) : false);
|
|
||||||
com2.setAudioPlaying(com2.isConnected() ? m_vatlib->IsAudioPlaying(static_cast<qint32>(COM2)) : false);
|
|
||||||
CVoiceRoomList voiceRooms;
|
|
||||||
voiceRooms.push_back(com1);
|
|
||||||
voiceRooms.push_back(com2);
|
|
||||||
return voiceRooms;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Voice room callsigns
|
|
||||||
*/
|
|
||||||
CCallsignList CVoiceVatlib::getVoiceRoomCallsigns(const IVoice::ComUnit comUnit) const
|
|
||||||
{
|
|
||||||
QReadLocker lockForReading(&m_lockCallsigns);
|
|
||||||
CCallsignList callsigns;
|
|
||||||
if (!this->m_vatlibRoomCallsigns.contains(comUnit)) return callsigns;
|
|
||||||
return this->m_vatlibRoomCallsigns[comUnit];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Enable audio
|
|
||||||
*/
|
|
||||||
void CVoiceVatlib::switchAudioOutput(const ComUnit comUnit, bool enable)
|
|
||||||
{
|
|
||||||
QMutexLocker lockerVatlib(&m_mutexVatlib);
|
|
||||||
Q_ASSERT_X(m_vatlib->IsValid() && m_vatlib->IsSetup(), "CVoiceVatlib", "Cvatlib_Voice_Simple invalid or not setup!");
|
|
||||||
Q_ASSERT_X(m_vatlib->IsRoomValid(static_cast<qint32>(comUnit)), "CVoiceVatlib", "Room index out of bounds!");
|
|
||||||
try
|
|
||||||
{
|
|
||||||
m_vatlib->SetOutputState(static_cast<qint32>(comUnit), 0, enable);
|
|
||||||
QWriteLocker lockForWriting(&m_lockOutputEnabled);
|
|
||||||
this->m_outputEnabled[comUnit] = enable;
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
this->exceptionDispatcher(Q_FUNC_INFO);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CVoiceVatlib::enableAudioLoopback(bool enable)
|
void CVoiceVatlib::enableAudioLoopback(bool enable)
|
||||||
{
|
{
|
||||||
if (enable == m_isAudioLoopbackEnabled)
|
if (enable == m_isAudioLoopbackEnabled)
|
||||||
@@ -334,203 +278,35 @@ namespace BlackCore
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
IVoiceChannel *CVoiceVatlib::getVoiceChannel(qint32 channelIndex) const
|
||||||
* Callsign
|
|
||||||
*/
|
|
||||||
void CVoiceVatlib::setMyAircraftCallsign(const BlackMisc::Aviation::CCallsign &callsign)
|
|
||||||
{
|
{
|
||||||
QWriteLocker lockForWriting(&m_lockMyCallsign);
|
IVoiceChannel *channel = m_hashChannelIndex.value(channelIndex, nullptr);
|
||||||
m_aircraftCallsign = callsign;
|
Q_ASSERT(channel);
|
||||||
|
|
||||||
|
return channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Voice room
|
* Handle PTT
|
||||||
*/
|
*/
|
||||||
void CVoiceVatlib::joinVoiceRoom(const ComUnit comUnit, const BlackMisc::Audio::CVoiceRoom &voiceRoom)
|
void CVoiceVatlib::handlePushToTalk(bool value)
|
||||||
{
|
{
|
||||||
QMutexLocker lockerVatlib(&m_mutexVatlib);
|
qDebug() << "PTT";
|
||||||
Q_ASSERT_X(m_vatlib->IsValid() && m_vatlib->IsSetup(), "CVoiceVatlib", "Cvatlib_Voice_Simple invalid or not setup!");
|
if (!this->m_vatlib) return;
|
||||||
Q_ASSERT_X(m_vatlib->IsRoomValid(static_cast<qint32>(comUnit)), "CVoiceVatlib", "Room index out of bounds!");
|
|
||||||
if (!voiceRoom.isValid())
|
if (value) qDebug() << "Start transmitting...";
|
||||||
|
else qDebug() << "Stop transmitting...";
|
||||||
|
|
||||||
|
// FIXME: Set only once channel to active for transmitting
|
||||||
|
if (value)
|
||||||
{
|
{
|
||||||
qDebug() << "Error: Cannot join invalid voice room.";
|
getVoiceChannel(0)->startTransmitting();
|
||||||
return;
|
getVoiceChannel(1)->startTransmitting();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
try
|
|
||||||
{
|
{
|
||||||
// only connect, if not yet connected
|
getVoiceChannel(0)->stopTransmitting();
|
||||||
if (this->m_vatlib->IsRoomConnected(static_cast<qint32>(comUnit))) return;
|
getVoiceChannel(1)->stopTransmitting();
|
||||||
changeConnectionStatus(comUnit, Connecting);
|
|
||||||
QString serverSpec = voiceRoom.getVoiceRoomUrl();
|
|
||||||
QReadLocker lockForReading(&m_lockMyCallsign);
|
|
||||||
bool jr = m_vatlib->JoinRoom(static_cast<qint32>(comUnit), m_aircraftCallsign.toQString().toLatin1().constData(), serverSpec.toLatin1().constData());
|
|
||||||
if (jr)
|
|
||||||
{
|
|
||||||
// do not(!) set as connected right now, this will be done in "status changed"
|
|
||||||
// when room really is connected
|
|
||||||
this->setVoiceRoomForUnit(comUnit, voiceRoom);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
qWarning("Could not join voice room");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
this->exceptionDispatcher(Q_FUNC_INFO);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Leave a room
|
|
||||||
*/
|
|
||||||
void CVoiceVatlib::leaveVoiceRoom(const ComUnit comUnit)
|
|
||||||
{
|
|
||||||
QMutexLocker lockerVatlib(&m_mutexVatlib);
|
|
||||||
|
|
||||||
Q_ASSERT_X(m_vatlib->IsValid() && m_vatlib->IsSetup(), "CVoiceVatlib", "Cvatlib_Voice_Simple invalid or not setup!");
|
|
||||||
Q_ASSERT_X(m_vatlib->IsRoomValid(static_cast<qint32>(comUnit)), "CVoiceVatlib", "Room index out of bounds!");
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// update "meta" object for room
|
|
||||||
this->setVoiceRoomForUnit(comUnit, CVoiceRoom()); // an empty voice room is easier to detect
|
|
||||||
|
|
||||||
// only leave is room is physically connected
|
|
||||||
if (this->m_vatlib->IsRoomConnected(static_cast<qint32>(comUnit)))
|
|
||||||
{
|
|
||||||
m_vatlib->LeaveRoom(static_cast<qint32>(comUnit));
|
|
||||||
this->m_vatlibRoomCallsigns[comUnit] = CCallsignList(); // empty list for this room
|
|
||||||
changeConnectionStatus(comUnit, Disconnecting);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
this->exceptionDispatcher(Q_FUNC_INFO);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Leave all voice rooms
|
|
||||||
*/
|
|
||||||
void CVoiceVatlib::leaveAllVoiceRooms()
|
|
||||||
{
|
|
||||||
this->leaveVoiceRoom(COM1);
|
|
||||||
this->leaveVoiceRoom(COM2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Room output volume as per COM unit
|
|
||||||
*/
|
|
||||||
void CVoiceVatlib::setRoomOutputVolume(const ComUnit comUnit, const qint32 volume)
|
|
||||||
{
|
|
||||||
QMutexLocker lockerVatlib(&m_mutexVatlib);
|
|
||||||
Q_ASSERT_X(m_vatlib->IsValid() && m_vatlib->IsSetup(), "CVoiceVatlib", "Cvatlib_Voice_Simple invalid or not setup!");
|
|
||||||
Q_ASSERT_X(m_vatlib->IsRoomValid(static_cast<qint32>(comUnit)), "CVoiceVatlib", "Room index out of bounds!");
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
m_vatlib->SetRoomVolume(static_cast<qint32>(comUnit), volume);
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
this->exceptionDispatcher(Q_FUNC_INFO);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Start transmitting
|
|
||||||
*/
|
|
||||||
void CVoiceVatlib::startTransmitting(const ComUnit comUnit)
|
|
||||||
{
|
|
||||||
QMutexLocker lockerVatlib(&m_mutexVatlib);
|
|
||||||
Q_ASSERT_X(m_vatlib->IsValid() && m_vatlib->IsSetup(), "CVoiceVatlib", "Cvatlib_Voice_Simple invalid or not setup!");
|
|
||||||
Q_ASSERT_X(m_vatlib->IsRoomValid(static_cast<qint32>(comUnit)), "CVoiceVatlib", "Room index out of bounds!");
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
m_vatlib->SetMicState(static_cast<qint32>(comUnit), true);
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
this->exceptionDispatcher(Q_FUNC_INFO);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Start transmitting
|
|
||||||
*/
|
|
||||||
void CVoiceVatlib::stopTransmitting(const ComUnit comUnit)
|
|
||||||
{
|
|
||||||
QMutexLocker lockerVatlib(&m_mutexVatlib);
|
|
||||||
Q_ASSERT_X(m_vatlib->IsValid() && m_vatlib->IsSetup(), "CVoiceVatlib", "Cvatlib_Voice_Simple invalid or not setup!");
|
|
||||||
Q_ASSERT_X(m_vatlib->IsRoomValid(static_cast<qint32>(comUnit)), "CVoiceVatlib", "Room index out of bounds!");
|
|
||||||
try
|
|
||||||
{
|
|
||||||
m_vatlib->SetMicState(static_cast<qint32>(comUnit), false);
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
this->exceptionDispatcher(Q_FUNC_INFO);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Change room status
|
|
||||||
*/
|
|
||||||
void CVoiceVatlib::changeRoomStatus(ComUnit comUnit, Cvatlib_Voice_Simple::roomStatusUpdate roomStatus)
|
|
||||||
{
|
|
||||||
qDebug() << comUnit << roomStatus; // KB_REMOVE
|
|
||||||
CVoiceRoom vr = this->voiceRoomForUnit(comUnit);
|
|
||||||
switch (roomStatus)
|
|
||||||
{
|
|
||||||
case Cvatlib_Voice_Simple::roomStatusUpdate_JoinSuccess:
|
|
||||||
{
|
|
||||||
m_lockOutputEnabled.lockForRead();
|
|
||||||
bool isOutputEnabled = this->m_outputEnabled[comUnit];
|
|
||||||
m_lockOutputEnabled.unlock();
|
|
||||||
switchAudioOutput(comUnit, isOutputEnabled);
|
|
||||||
vr.setConnected(true);
|
|
||||||
this->setVoiceRoomForUnit(comUnit, vr);
|
|
||||||
changeConnectionStatus(comUnit, Connected);
|
|
||||||
emit userJoinedLeft(comUnit);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Cvatlib_Voice_Simple::roomStatusUpdate_JoinFail:
|
|
||||||
vr.setConnected(false);
|
|
||||||
this->setVoiceRoomForUnit(comUnit, vr);
|
|
||||||
changeConnectionStatus(comUnit, ConnectingFailed);
|
|
||||||
break;
|
|
||||||
case Cvatlib_Voice_Simple::roomStatusUpdate_UnexpectedDisconnectOrKicked:
|
|
||||||
vr.setConnected(false);
|
|
||||||
this->setVoiceRoomForUnit(comUnit, vr);
|
|
||||||
changeConnectionStatus(comUnit, DisconnectedError);
|
|
||||||
break;
|
|
||||||
case Cvatlib_Voice_Simple::roomStatusUpdate_LeaveComplete:
|
|
||||||
m_lockCallsigns.lockForWrite();
|
|
||||||
m_voiceRoomCallsigns.clear();
|
|
||||||
m_lockCallsigns.unlock();
|
|
||||||
changeConnectionStatus(comUnit, Disconnected);
|
|
||||||
break;
|
|
||||||
case Cvatlib_Voice_Simple::roomStatusUpdate_UserJoinsLeaves:
|
|
||||||
// FIXME: We cannot call GetRoomUserList because vatlib is not reentrent safe.
|
|
||||||
emit userJoinedLeft(comUnit);
|
|
||||||
break;
|
|
||||||
case Cvatlib_Voice_Simple::roomStatusUpdate_RoomAudioStarted:
|
|
||||||
emit audioStarted(comUnit);
|
|
||||||
break;
|
|
||||||
case Cvatlib_Voice_Simple::roomStatusUpdate_RoomAudioStopped:
|
|
||||||
emit audioStopped(comUnit);
|
|
||||||
break;
|
|
||||||
case Cvatlib_Voice_Simple::roomStatusUpdate_AudioStarted:
|
|
||||||
emit globalAudioStarted();
|
|
||||||
break;
|
|
||||||
case Cvatlib_Voice_Simple::roomStatusUpdate_AudioStopped:
|
|
||||||
emit globalAudioStopped();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -588,62 +364,6 @@ namespace BlackCore
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* User joined / left room
|
|
||||||
*/
|
|
||||||
void CVoiceVatlib::onUserJoinedLeft(const ComUnit comUnit)
|
|
||||||
{
|
|
||||||
QMutexLocker lockerVatlib(&m_mutexVatlib);
|
|
||||||
Q_ASSERT_X(m_vatlib->IsValid() && m_vatlib->IsSetup(), "CVoiceVatlib", "Cvatlib_Voice_Simple invalid or not setup!");
|
|
||||||
Q_ASSERT_X(m_temporaryUserRoomIndex == CVoiceVatlib::InvalidRoomIndex, "CVoiceClientVatlib::onUserJoinedLeft", "Cannot list users for two rooms in parallel!");
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Paranoia... clear list completely
|
|
||||||
if (!m_vatlib->IsRoomConnected(static_cast<qint32>(comUnit)))
|
|
||||||
{
|
|
||||||
this->m_voiceRoomCallsigns[comUnit] = CCallsignList();
|
|
||||||
this->m_temporaryVoiceRoomCallsigns.clear();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store the room index for the slot (called in static callback)
|
|
||||||
m_temporaryUserRoomIndex = static_cast<qint32>(comUnit);
|
|
||||||
|
|
||||||
// Callbacks already completed when function GetRoomUserList returns,
|
|
||||||
// thereafter m_voiceRoomCallsignsUpdate is filled with the latest callsigns
|
|
||||||
m_vatlib->GetRoomUserList(static_cast<qint32>(comUnit), onRoomUserReceived, this);
|
|
||||||
m_temporaryUserRoomIndex = CVoiceVatlib::InvalidRoomIndex; // reset
|
|
||||||
|
|
||||||
// we have all current users in m_temporaryVoiceRoomCallsigns
|
|
||||||
foreach(CCallsign callsign, m_voiceRoomCallsigns.value(comUnit))
|
|
||||||
{
|
|
||||||
if (!m_temporaryVoiceRoomCallsigns.contains(callsign))
|
|
||||||
{
|
|
||||||
// User has left
|
|
||||||
emit userLeftRoom(callsign);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QWriteLocker lockForWriting(&m_lockCallsigns);
|
|
||||||
foreach(CCallsign callsign, m_temporaryVoiceRoomCallsigns)
|
|
||||||
{
|
|
||||||
if (!m_voiceRoomCallsigns.value(comUnit).contains(callsign))
|
|
||||||
{
|
|
||||||
// he joined
|
|
||||||
emit userJoinedRoom(callsign);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finally we update it with our new list
|
|
||||||
this->m_voiceRoomCallsigns[comUnit] = this->m_temporaryVoiceRoomCallsigns;
|
|
||||||
this->m_temporaryVoiceRoomCallsigns.clear();
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
this->exceptionDispatcher(Q_FUNC_INFO);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/********************************** * * * * * * * * * * * * * * * * * * * ************************************/
|
/********************************** * * * * * * * * * * * * * * * * * * * ************************************/
|
||||||
/********************************** shimlib callbacks ************************************/
|
/********************************** shimlib callbacks ************************************/
|
||||||
/********************************** * * * * * * * * * * * * * * * * * * * ************************************/
|
/********************************** * * * * * * * * * * * * * * * * * * * ************************************/
|
||||||
@@ -664,36 +384,8 @@ namespace BlackCore
|
|||||||
void CVoiceVatlib::onRoomStatusUpdate(Cvatlib_Voice_Simple *obj, Cvatlib_Voice_Simple::roomStatusUpdate upd, qint32 roomIndex, void *cbVar)
|
void CVoiceVatlib::onRoomStatusUpdate(Cvatlib_Voice_Simple *obj, Cvatlib_Voice_Simple::roomStatusUpdate upd, qint32 roomIndex, void *cbVar)
|
||||||
{
|
{
|
||||||
Q_UNUSED(obj)
|
Q_UNUSED(obj)
|
||||||
ComUnit comUnit = static_cast<ComUnit>(roomIndex);
|
CVoiceVatlib *vatlibRoom = cbvar_cast_voice(cbVar);
|
||||||
CVoiceVatlib *voiceClientVatlib = cbvar_cast_voice(cbVar);
|
vatlibRoom->onRoomStatusUpdate(roomIndex, upd);
|
||||||
voiceClientVatlib->changeRoomStatus(comUnit, upd);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Room user received
|
|
||||||
*/
|
|
||||||
void CVoiceVatlib::onRoomUserReceived(Cvatlib_Voice_Simple *obj, const char *name, void *cbVar)
|
|
||||||
{
|
|
||||||
Q_UNUSED(obj)
|
|
||||||
|
|
||||||
// sanity check
|
|
||||||
QString callsign = QString(name);
|
|
||||||
if (callsign.isEmpty()) return;
|
|
||||||
|
|
||||||
// add callsign
|
|
||||||
CVoiceVatlib *voiceClientVatlib = cbvar_cast_voice(cbVar);
|
|
||||||
ComUnit comUnit = static_cast<ComUnit>(voiceClientVatlib->temporaryUserRoomIndex());
|
|
||||||
|
|
||||||
// add user
|
|
||||||
// callsign might contain: VATSIM id, user name
|
|
||||||
if (callsign.contains(" "))
|
|
||||||
{
|
|
||||||
QStringList parts = callsign.split(" ");
|
|
||||||
callsign = parts[0];
|
|
||||||
// I throw away VATSIM id here, maybe we could use it
|
|
||||||
}
|
|
||||||
|
|
||||||
voiceClientVatlib->addTemporaryCallsignForRoom(comUnit, CCallsign(callsign));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -718,33 +410,6 @@ namespace BlackCore
|
|||||||
cbvar_cast_voice(cbVar)->m_devices.push_back(outputDevice);
|
cbvar_cast_voice(cbVar)->m_devices.push_back(outputDevice);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* COM unit to Voice room
|
|
||||||
*/
|
|
||||||
CVoiceRoom CVoiceVatlib::voiceRoomForUnit(const IVoice::ComUnit comUnit) const
|
|
||||||
{
|
|
||||||
QReadLocker lockForReading(&m_lockVoiceRooms);
|
|
||||||
return (comUnit == COM1) ? this->m_voiceRooms[0] : this->m_voiceRooms[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Voice room for respectice COM unit
|
|
||||||
*/
|
|
||||||
void CVoiceVatlib::setVoiceRoomForUnit(const IVoice::ComUnit comUnit, const CVoiceRoom &voiceRoom)
|
|
||||||
{
|
|
||||||
QWriteLocker lockForWriting(&m_lockVoiceRooms);
|
|
||||||
this->m_voiceRooms[comUnit == COM1 ? 0 : 1] = voiceRoom;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Add temp.callsign for room
|
|
||||||
*/
|
|
||||||
void CVoiceVatlib::addTemporaryCallsignForRoom(const ComUnit /** comUnit **/, const CCallsign &callsign)
|
|
||||||
{
|
|
||||||
if (m_temporaryVoiceRoomCallsigns.contains(callsign)) return;
|
|
||||||
m_temporaryVoiceRoomCallsigns.push_back(callsign);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Forward exception as signal
|
* Forward exception as signal
|
||||||
*/
|
*/
|
||||||
@@ -783,26 +448,21 @@ namespace BlackCore
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Change voice room status and emit signal
|
void CVoiceVatlib::onRoomStatusUpdate(qint32 roomIndex, Cvatlib_Voice_Simple::roomStatusUpdate roomStatus)
|
||||||
void CVoiceVatlib::changeConnectionStatus(ComUnit comUnit, ConnectionStatus newStatus)
|
|
||||||
{
|
{
|
||||||
QWriteLocker lockForWriting(&m_lockConnectionStatus);
|
QList<IVoiceChannel *> voiceChannels = m_hashChannelIndex.values();
|
||||||
ConnectionStatus currentStatus = m_connectionStatus.value(comUnit);
|
auto iterator = std::find_if(voiceChannels.begin(), voiceChannels.end(), [&](const IVoiceChannel * voiceChannel)
|
||||||
if (newStatus != currentStatus)
|
|
||||||
{
|
{
|
||||||
if (newStatus == Connected)
|
return voiceChannel->getRoomIndex() == roomIndex;
|
||||||
{
|
});
|
||||||
CVoiceRoom vr = this->voiceRoomForUnit(comUnit);
|
|
||||||
vr.setConnected(true);
|
|
||||||
this->setVoiceRoomForUnit(comUnit, vr);
|
|
||||||
}
|
|
||||||
|
|
||||||
// disconnecting the voice room will already be
|
if (iterator == voiceChannels.end())
|
||||||
// set in leave voice room
|
{
|
||||||
|
qWarning() << "Unknown room index";
|
||||||
m_connectionStatus.insert(comUnit, newStatus);
|
return;
|
||||||
emit connectionStatusChanged(comUnit, currentStatus, newStatus);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
(*iterator)->updateRoomStatus(roomStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
@@ -101,40 +101,8 @@ namespace BlackCore
|
|||||||
//! \copydoc IVoice::micTestResultAsString
|
//! \copydoc IVoice::micTestResultAsString
|
||||||
virtual QString micTestResultAsString() const override;
|
virtual QString micTestResultAsString() const override;
|
||||||
|
|
||||||
public slots:
|
//! \copydoc IVoice::getVoiceChannel
|
||||||
//! \copydoc IVoice::setMyAircraftCallsign()
|
virtual IVoiceChannel *getVoiceChannel(qint32 channelIndex) const override;
|
||||||
virtual void setMyAircraftCallsign(const BlackMisc::Aviation::CCallsign &callsign) override;
|
|
||||||
|
|
||||||
//! \copydoc IVoice::joinVoiceRoom()
|
|
||||||
virtual void joinVoiceRoom(const ComUnit comUnit, const BlackMisc::Audio::CVoiceRoom &voiceRoom) override;
|
|
||||||
|
|
||||||
//! \copydoc IVoice::leaveVoiceRoom()
|
|
||||||
virtual void leaveVoiceRoom(const ComUnit comUnit) override;
|
|
||||||
|
|
||||||
//! \copydoc IVoice::leaveAllVoiceRooms()
|
|
||||||
virtual void leaveAllVoiceRooms() override;
|
|
||||||
|
|
||||||
//! \copydoc IVoice::setRoomOutputVolume()
|
|
||||||
virtual void setRoomOutputVolume(const ComUnit comUnit, const qint32 volumne) override;
|
|
||||||
|
|
||||||
//! \copydoc IVoice::startTransmitting()
|
|
||||||
virtual void startTransmitting(const ComUnit comUnit) override;
|
|
||||||
|
|
||||||
//! \copydoc IVoice::stopTransmitting()
|
|
||||||
virtual void stopTransmitting(const ComUnit comUnit) override;
|
|
||||||
|
|
||||||
//! \copydoc IVoice::getComVoiceRoomsWithAudioStatus()
|
|
||||||
virtual BlackMisc::Audio::CVoiceRoomList getComVoiceRoomsWithAudioStatus() const override;
|
|
||||||
|
|
||||||
//! \copydoc IVoice::getComVoiceRooms()
|
|
||||||
virtual BlackMisc::Audio::CVoiceRoomList getComVoiceRooms() const override
|
|
||||||
{
|
|
||||||
QReadLocker lockForReading(&m_lockVoiceRooms);
|
|
||||||
return this->m_voiceRooms;
|
|
||||||
}
|
|
||||||
|
|
||||||
//! \copydoc IVoice::getVoiceRoomCallsigns()
|
|
||||||
virtual BlackMisc::Aviation::CCallsignList getVoiceRoomCallsigns(const ComUnit comUnit) const override;
|
|
||||||
|
|
||||||
//! \copydoc IVoice::setInputDevice
|
//! \copydoc IVoice::setInputDevice
|
||||||
virtual void setInputDevice(const BlackMisc::Audio::CAudioDevice &device) override;
|
virtual void setInputDevice(const BlackMisc::Audio::CAudioDevice &device) override;
|
||||||
@@ -148,58 +116,15 @@ namespace BlackCore
|
|||||||
//! \copydoc IVoice::getCurrentOutputDevice()
|
//! \copydoc IVoice::getCurrentOutputDevice()
|
||||||
virtual BlackMisc::Audio::CAudioDevice getCurrentOutputDevice() const override;
|
virtual BlackMisc::Audio::CAudioDevice getCurrentOutputDevice() const override;
|
||||||
|
|
||||||
//! \copydoc IVoice::switchAudioOutput
|
|
||||||
virtual void switchAudioOutput(const ComUnit comUnit, bool enable) override;
|
|
||||||
|
|
||||||
//! \copydoc IVoice::enableAudioLoopback
|
//! \copydoc IVoice::enableAudioLoopback
|
||||||
virtual void enableAudioLoopback(bool enable = true) override;
|
virtual void enableAudioLoopback(bool enable = true) override;
|
||||||
|
|
||||||
//! \copydoc IVoice::isMuted
|
|
||||||
virtual bool isMuted() const override
|
|
||||||
{
|
|
||||||
QReadLocker lockForReading(&m_lockOutputEnabled);
|
|
||||||
if (this->m_outputEnabled.isEmpty()) return false;
|
|
||||||
bool enabled = this->m_outputEnabled[COM1] || this->m_outputEnabled[COM2];
|
|
||||||
return !enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Starts or stops voice transmission
|
* \brief Starts or stops voice transmission
|
||||||
* \param value
|
* \param value
|
||||||
*/
|
*/
|
||||||
void handlePushToTalk(bool value = false);
|
void handlePushToTalk(bool value = false);
|
||||||
|
|
||||||
/************************************************
|
|
||||||
* NON API METHODS:
|
|
||||||
* The following methods are not part of the
|
|
||||||
* public API. They are needed for internal
|
|
||||||
* workflow.
|
|
||||||
* *********************************************/
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Voice room index
|
|
||||||
* \return
|
|
||||||
*/
|
|
||||||
qint32 temporaryUserRoomIndex() const
|
|
||||||
{
|
|
||||||
return m_temporaryUserRoomIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Room status update, used in callback
|
|
||||||
* \param comUnit
|
|
||||||
* \param roomStatus
|
|
||||||
*/
|
|
||||||
void changeRoomStatus(ComUnit comUnit, Cvatlib_Voice_Simple::roomStatusUpdate roomStatus);
|
|
||||||
|
|
||||||
signals:
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief User joined or left
|
|
||||||
* \param comUnit
|
|
||||||
*/
|
|
||||||
void userJoinedLeft(const ComUnit comUnit);
|
|
||||||
|
|
||||||
protected: // QObject overrides
|
protected: // QObject overrides
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -212,48 +137,25 @@ namespace BlackCore
|
|||||||
void onEndFindSquelch();
|
void onEndFindSquelch();
|
||||||
void onEndMicTest();
|
void onEndMicTest();
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief User (identified by callsign) joined or left voice room
|
|
||||||
*/
|
|
||||||
void onUserJoinedLeft(const ComUnit comUnit);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
// shimlib callbacks
|
// shimlib callbacks
|
||||||
static void onRoomStatusUpdate(Cvatlib_Voice_Simple *obj, Cvatlib_Voice_Simple::roomStatusUpdate upd, qint32 roomIndex, void *cbVar);
|
static void onRoomStatusUpdate(Cvatlib_Voice_Simple *obj, Cvatlib_Voice_Simple::roomStatusUpdate upd, qint32 roomIndex, void *cbVar);
|
||||||
static void onRoomUserReceived(Cvatlib_Voice_Simple *obj, const char *name, void *cbVar);
|
|
||||||
static void onInputHardwareDeviceReceived(Cvatlib_Voice_Simple *obj, const char *name, void *cbVar);
|
static void onInputHardwareDeviceReceived(Cvatlib_Voice_Simple *obj, const char *name, void *cbVar);
|
||||||
static void onOutputHardwareDeviceReceived(Cvatlib_Voice_Simple *obj, const char *name, void *cbVar);
|
static void onOutputHardwareDeviceReceived(Cvatlib_Voice_Simple *obj, const char *name, void *cbVar);
|
||||||
|
|
||||||
BlackMisc::Audio::CVoiceRoom voiceRoomForUnit(const ComUnit comUnit) const;
|
|
||||||
void setVoiceRoomForUnit(const IVoice::ComUnit comUnit, const BlackMisc::Audio::CVoiceRoom &voiceRoom);
|
|
||||||
void addTemporaryCallsignForRoom(const ComUnit comUnit, const BlackMisc::Aviation::CCallsign &callsign);
|
|
||||||
void removeUserFromRoom(const ComUnit comUnit, const QString &callsign);
|
|
||||||
void exceptionDispatcher(const char *caller);
|
void exceptionDispatcher(const char *caller);
|
||||||
void enableAudio(const ComUnit comUnit);
|
void onRoomStatusUpdate(qint32 roomIndex, Cvatlib_Voice_Simple::roomStatusUpdate roomStatus);
|
||||||
void changeConnectionStatus(ComUnit comUnit, ConnectionStatus newStatus);
|
|
||||||
|
|
||||||
TVatlibPointer m_vatlib;
|
TVatlibPointer m_vatlib;
|
||||||
// QScopedPointer<QAudioOutput> m_audioOutput; #227
|
|
||||||
BlackMisc::Aviation::CCallsign m_aircraftCallsign; /*!< own callsign to join voice rooms */
|
|
||||||
BlackMisc::Audio::CVoiceRoomList m_voiceRooms;
|
|
||||||
BlackMisc::Audio::CAudioDeviceList m_devices; /*!< in and output devices */
|
BlackMisc::Audio::CAudioDeviceList m_devices; /*!< in and output devices */
|
||||||
BlackMisc::Audio::CAudioDevice m_currentOutputDevice;
|
BlackMisc::Audio::CAudioDevice m_currentOutputDevice;
|
||||||
BlackMisc::Audio::CAudioDevice m_currentInputDevice;
|
BlackMisc::Audio::CAudioDevice m_currentInputDevice;
|
||||||
std::atomic<float> m_inputSquelch;
|
std::atomic<float> m_inputSquelch;
|
||||||
std::atomic<Cvatlib_Voice_Simple::agc> m_micTestResult;
|
std::atomic<Cvatlib_Voice_Simple::agc> m_micTestResult;
|
||||||
QMap <ComUnit, BlackMisc::Aviation::CCallsignList> m_voiceRoomCallsigns; /*!< voice room callsigns */
|
QHash<qint32, IVoiceChannel *> m_hashChannelIndex;
|
||||||
BlackMisc::Aviation::CCallsignList m_temporaryVoiceRoomCallsigns; /*!< temp. storage of voice rooms during update */
|
|
||||||
QMap<ComUnit, bool> m_outputEnabled; /*!< output enabled, basically a mute flag */
|
|
||||||
QMap<ComUnit, ConnectionStatus> m_connectionStatus; /*!< holds connection status for each com unit */
|
|
||||||
bool m_isAudioLoopbackEnabled; /*!< A flag whether audio loopback is enabled or not */
|
bool m_isAudioLoopbackEnabled; /*!< A flag whether audio loopback is enabled or not */
|
||||||
|
|
||||||
// Need to keep the roomIndex?
|
|
||||||
// KB: I would remove this approach, it is potentially unsafe
|
|
||||||
// Maybe just use 2 "wrapper" callbacks, which then set explicitly the voice room (it is only 2 methods)
|
|
||||||
qint32 m_temporaryUserRoomIndex; /*!< temp. storage of voice room, in order to retrieve it in static callback */
|
|
||||||
const static qint32 InvalidRoomIndex = -1; /*! marks invalid room */
|
|
||||||
|
|
||||||
// Thread serialization
|
// Thread serialization
|
||||||
mutable QMutex m_lockCurrentOutputDevice;
|
mutable QMutex m_lockCurrentOutputDevice;
|
||||||
mutable QMutex m_lockCurrentInputDevice;
|
mutable QMutex m_lockCurrentInputDevice;
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
/* Copyright (C) 2013 VATSIM Community / authors
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
#include "voice_vatlib.h"
|
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
using namespace BlackMisc::Audio;
|
|
||||||
|
|
||||||
namespace BlackCore
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Handle PTT
|
|
||||||
*/
|
|
||||||
void CVoiceVatlib::handlePushToTalk(bool value)
|
|
||||||
{
|
|
||||||
qDebug() << "PTT";
|
|
||||||
if (!this->m_voice) return;
|
|
||||||
CVoiceRoomList rooms = this->getComVoiceRoomsWithAudioStatus();
|
|
||||||
CVoiceRoom room1 = rooms[0];
|
|
||||||
CVoiceRoom room2 = rooms[1];
|
|
||||||
|
|
||||||
if (value) qDebug() << "Start transmitting...";
|
|
||||||
else qDebug() << "Stop transmitting...";
|
|
||||||
|
|
||||||
if (room1.isConnected())
|
|
||||||
{
|
|
||||||
if (value)
|
|
||||||
{
|
|
||||||
this->startTransmitting(IVoice::COM1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this->stopTransmitting(IVoice::COM1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (room2.isConnected())
|
|
||||||
{
|
|
||||||
if (value)
|
|
||||||
this->startTransmitting(IVoice::COM2);
|
|
||||||
else
|
|
||||||
this->stopTransmitting(IVoice::COM2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
Reference in New Issue
Block a user