refs #255 move voice channel handling from CVoiceVatlib into a dedicated class CVoiceChannel

This commit is contained in:
Roland Winklmeier
2014-07-24 21:16:37 +02:00
parent c8f41beea9
commit c05b8808b8
9 changed files with 863 additions and 659 deletions

View File

@@ -4,7 +4,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "blackcorefreefunctions.h"
#include "voice.h"
#include "voice_channel.h"
#include "simulator.h"
#include <QThread>
@@ -15,10 +15,8 @@ namespace BlackCore
// for some reasons (ask RW) these are registered twice
qRegisterMetaType<ISimulator::Status>();
qRegisterMetaType<ISimulator::Status>("Status");
qRegisterMetaType<IVoice::ComUnit>();
qRegisterMetaType<IVoice::ComUnit>("ComUnit");
qRegisterMetaType<IVoice::ConnectionStatus>();
qRegisterMetaType<IVoice::ConnectionStatus>("ConnectionStatus");
qRegisterMetaType<IVoiceChannel::ConnectionStatus>();
qRegisterMetaType<IVoiceChannel::ConnectionStatus>("ConnectionStatus");
}
bool isCurrentThreadCreatingThread(QObject *toBeTested)

View File

@@ -21,6 +21,8 @@
namespace BlackCore
{
class IVoiceChannel;
/*!
* Interface to a connection to a ATC voice server for use in flight simulation.
*
@@ -49,35 +51,9 @@ namespace BlackCore
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 ~IVoice() {}
/*!
* \brief Own aircraft's callsign
* \param callsign
*/
virtual void setMyAircraftCallsign(const BlackMisc::Aviation::CCallsign &callsign) = 0;
/*!
* \brief Audio devices
* \return
@@ -128,6 +104,9 @@ namespace BlackCore
*/
virtual QString micTestResultAsString() const = 0;
//! Get voice channel object
virtual IVoiceChannel *getVoiceChannel(qint32 channelIndex) const = 0;
public slots:
/*!
@@ -150,68 +129,6 @@ namespace BlackCore
*/
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
* \param enable (default true)
@@ -220,40 +137,6 @@ namespace BlackCore
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
/*!
* \brief Squelch test completed
@@ -275,7 +158,4 @@ namespace BlackCore
} // namespace BlackCore
Q_DECLARE_METATYPE(BlackCore::IVoice::ComUnit)
Q_DECLARE_METATYPE(BlackCore::IVoice::ConnectionStatus)
#endif // guard

View 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

View 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();
}
}

View 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

View 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

View File

@@ -4,6 +4,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "voice_vatlib.h"
#include "voice_channel_vatlib.h"
#include <QDebug>
#include <QTimer>
@@ -24,8 +25,6 @@ namespace BlackCore
// m_audioOutput(nullptr), // removed #227
m_inputSquelch(-1),
m_micTestResult(Cvatlib_Voice_Simple::agc_Ok),
m_isAudioLoopbackEnabled(false),
m_temporaryUserRoomIndex(CVoiceVatlib::InvalidRoomIndex),
m_lockCurrentOutputDevice(QMutex::Recursive),
m_lockCurrentInputDevice(QMutex::Recursive),
m_lockDeviceList(QMutex::Recursive)
@@ -39,15 +38,15 @@ namespace BlackCore
m_vatlib->GetInputDevices(onInputHardwareDeviceReceived, 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_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
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)
{
if (enable == m_isAudioLoopbackEnabled)
@@ -334,203 +278,35 @@ namespace BlackCore
return result;
}
/*
* Callsign
*/
void CVoiceVatlib::setMyAircraftCallsign(const BlackMisc::Aviation::CCallsign &callsign)
IVoiceChannel *CVoiceVatlib::getVoiceChannel(qint32 channelIndex) const
{
QWriteLocker lockForWriting(&m_lockMyCallsign);
m_aircraftCallsign = callsign;
IVoiceChannel *channel = m_hashChannelIndex.value(channelIndex, nullptr);
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);
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!");
if (!voiceRoom.isValid())
qDebug() << "PTT";
if (!this->m_vatlib) return;
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.";
return;
getVoiceChannel(0)->startTransmitting();
getVoiceChannel(1)->startTransmitting();
}
try
else
{
// only connect, if not yet connected
if (this->m_vatlib->IsRoomConnected(static_cast<qint32>(comUnit))) return;
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;
getVoiceChannel(0)->stopTransmitting();
getVoiceChannel(1)->stopTransmitting();
}
}
@@ -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 ************************************/
/********************************** * * * * * * * * * * * * * * * * * * * ************************************/
@@ -664,36 +384,8 @@ namespace BlackCore
void CVoiceVatlib::onRoomStatusUpdate(Cvatlib_Voice_Simple *obj, Cvatlib_Voice_Simple::roomStatusUpdate upd, qint32 roomIndex, void *cbVar)
{
Q_UNUSED(obj)
ComUnit comUnit = static_cast<ComUnit>(roomIndex);
CVoiceVatlib *voiceClientVatlib = cbvar_cast_voice(cbVar);
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));
CVoiceVatlib *vatlibRoom = cbvar_cast_voice(cbVar);
vatlibRoom->onRoomStatusUpdate(roomIndex, upd);
}
/*
@@ -718,33 +410,6 @@ namespace BlackCore
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
*/
@@ -783,26 +448,21 @@ namespace BlackCore
}
}
// Change voice room status and emit signal
void CVoiceVatlib::changeConnectionStatus(ComUnit comUnit, ConnectionStatus newStatus)
void CVoiceVatlib::onRoomStatusUpdate(qint32 roomIndex, Cvatlib_Voice_Simple::roomStatusUpdate roomStatus)
{
QWriteLocker lockForWriting(&m_lockConnectionStatus);
ConnectionStatus currentStatus = m_connectionStatus.value(comUnit);
if (newStatus != currentStatus)
QList<IVoiceChannel *> voiceChannels = m_hashChannelIndex.values();
auto iterator = std::find_if(voiceChannels.begin(), voiceChannels.end(), [&](const IVoiceChannel * voiceChannel)
{
if (newStatus == Connected)
{
CVoiceRoom vr = this->voiceRoomForUnit(comUnit);
vr.setConnected(true);
this->setVoiceRoomForUnit(comUnit, vr);
}
return voiceChannel->getRoomIndex() == roomIndex;
});
// disconnecting the voice room will already be
// set in leave voice room
m_connectionStatus.insert(comUnit, newStatus);
emit connectionStatusChanged(comUnit, currentStatus, newStatus);
if (iterator == voiceChannels.end())
{
qWarning() << "Unknown room index";
return;
}
(*iterator)->updateRoomStatus(roomStatus);
}
} // namespace

View File

@@ -101,40 +101,8 @@ namespace BlackCore
//! \copydoc IVoice::micTestResultAsString
virtual QString micTestResultAsString() const override;
public slots:
//! \copydoc IVoice::setMyAircraftCallsign()
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::getVoiceChannel
virtual IVoiceChannel *getVoiceChannel(qint32 channelIndex) const override;
//! \copydoc IVoice::setInputDevice
virtual void setInputDevice(const BlackMisc::Audio::CAudioDevice &device) override;
@@ -148,58 +116,15 @@ namespace BlackCore
//! \copydoc IVoice::getCurrentOutputDevice()
virtual BlackMisc::Audio::CAudioDevice getCurrentOutputDevice() const override;
//! \copydoc IVoice::switchAudioOutput
virtual void switchAudioOutput(const ComUnit comUnit, bool enable) override;
//! \copydoc IVoice::enableAudioLoopback
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
* \param value
*/
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
/*!
@@ -212,48 +137,25 @@ namespace BlackCore
void onEndFindSquelch();
void onEndMicTest();
/*!
* \brief User (identified by callsign) joined or left voice room
*/
void onUserJoinedLeft(const ComUnit comUnit);
private:
// shimlib callbacks
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 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 enableAudio(const ComUnit comUnit);
void changeConnectionStatus(ComUnit comUnit, ConnectionStatus newStatus);
void onRoomStatusUpdate(qint32 roomIndex, Cvatlib_Voice_Simple::roomStatusUpdate roomStatus);
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::CAudioDevice m_currentOutputDevice;
BlackMisc::Audio::CAudioDevice m_currentInputDevice;
std::atomic<float> m_inputSquelch;
std::atomic<Cvatlib_Voice_Simple::agc> m_micTestResult;
QMap <ComUnit, BlackMisc::Aviation::CCallsignList> m_voiceRoomCallsigns; /*!< voice room callsigns */
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 */
QHash<qint32, IVoiceChannel *> m_hashChannelIndex;
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
mutable QMutex m_lockCurrentOutputDevice;
mutable QMutex m_lockCurrentInputDevice;

View File

@@ -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