diff --git a/samples/voiceclient/client.cpp b/samples/voiceclient/client.cpp index 98e76c12a..73b090c93 100644 --- a/samples/voiceclient/client.cpp +++ b/samples/voiceclient/client.cpp @@ -19,16 +19,16 @@ Client::Client(QObject *parent) : QObject(parent), m_voice(new BlackCore::CVoiceVatlib()) { - m_channelCom1 = m_voice->getVoiceChannel(0); + m_channelCom1 = m_voice->createVoiceChannel(); m_inputDevice = m_voice->createInputDevice(); m_outputDevice = m_voice->createOutputDevice(); using namespace BlackCore; - connect(m_channelCom1.data(), &IVoiceChannel::connectionStatusChanged, this, &Client::connectionStatusChanged); - connect(m_channelCom1.data(), &IVoiceChannel::audioStarted, this, &Client::audioStartedStream); - connect(m_channelCom1.data(), &IVoiceChannel::audioStopped, this, &Client::audioStoppedStream); - connect(m_channelCom1.data(), &IVoiceChannel::userJoinedRoom, this, &Client::userJoinedRoom); - connect(m_channelCom1.data(), &IVoiceChannel::userLeftRoom, this, &Client::userLeftRoom); + connect(m_channelCom1.get(), &IVoiceChannel::connectionStatusChanged, this, &Client::connectionStatusChanged); + connect(m_channelCom1.get(), &IVoiceChannel::audioStarted, this, &Client::audioStartedStream); + connect(m_channelCom1.get(), &IVoiceChannel::audioStopped, this, &Client::audioStoppedStream); + connect(m_channelCom1.get(), &IVoiceChannel::userJoinedRoom, this, &Client::userJoinedRoom); + connect(m_channelCom1.get(), &IVoiceChannel::userLeftRoom, this, &Client::userLeftRoom); using namespace std::placeholders; m_commands["help"] = std::bind(&Client::help, this, _1); diff --git a/samples/voiceclient/client.h b/samples/voiceclient/client.h index 035a38e48..8f5419f8a 100644 --- a/samples/voiceclient/client.h +++ b/samples/voiceclient/client.h @@ -58,7 +58,7 @@ private: BlackCore::IVoice *m_voice; std::unique_ptr m_inputDevice; std::unique_ptr m_outputDevice; - QPointer m_channelCom1; + std::unique_ptr m_channelCom1; }; diff --git a/src/blackcore/blackcorefreefunctions.cpp b/src/blackcore/blackcorefreefunctions.cpp index 81ce707d2..27b77f5bf 100644 --- a/src/blackcore/blackcorefreefunctions.cpp +++ b/src/blackcore/blackcorefreefunctions.cpp @@ -15,8 +15,7 @@ namespace BlackCore // for some reasons (ask RW) these are registered twice qRegisterMetaType(); qRegisterMetaType("Status"); - qRegisterMetaType(); - qRegisterMetaType("ConnectionStatus"); + qRegisterMetaType(); } bool isCurrentThreadCreatingThread(QObject *toBeTested) diff --git a/src/blackcore/context_audio_impl.cpp b/src/blackcore/context_audio_impl.cpp index 5b381ba31..2df5dfcc8 100644 --- a/src/blackcore/context_audio_impl.cpp +++ b/src/blackcore/context_audio_impl.cpp @@ -8,6 +8,7 @@ #include "context_ownaircraft.h" #include "context_application.h" #include "voice_channel.h" +#include "voice_vatlib.h" #include "blacksound/soundgenerator.h" #include "blackmisc/notificationsounds.h" @@ -38,14 +39,14 @@ namespace BlackCore // 2. Register PTT hotkey function m_inputManager = CInputManager::getInstance(); - m_handlePtt = m_inputManager->registerHotkeyFunc(CHotkeyFunction::Ptt(), voice, &CVoiceVatlib::handlePushToTalk); - m_channelCom1 = m_voice->getVoiceChannel(0); + m_channelCom1 = m_voice->createVoiceChannel(); m_channelCom1->setMyAircraftCallsign(getIContextOwnAircraft()->getOwnAircraft().getCallsign()); - connect(m_channelCom1.data(), &IVoiceChannel::connectionStatusChanged, this, &CContextAudio::ps_com1ConnectionStatusChanged); - m_channelCom2 = m_voice->getVoiceChannel(1); + connect(m_channelCom1.get(), &IVoiceChannel::connectionStatusChanged, this, &CContextAudio::ps_com1ConnectionStatusChanged); + m_channelCom2 = m_voice->createVoiceChannel(); m_channelCom2->setMyAircraftCallsign(getIContextOwnAircraft()->getOwnAircraft().getCallsign()); - connect(m_channelCom2.data(), &IVoiceChannel::connectionStatusChanged, this, &CContextAudio::ps_com2ConnectionStatusChanged); + connect(m_channelCom2.get(), &IVoiceChannel::connectionStatusChanged, this, &CContextAudio::ps_com2ConnectionStatusChanged); + m_voiceInputDevice = m_voice->createInputDevice(); m_voiceOutputDevice = m_voice->createOutputDevice(); diff --git a/src/blackcore/context_audio_impl.h b/src/blackcore/context_audio_impl.h index 0024349f2..0135ce1dd 100644 --- a/src/blackcore/context_audio_impl.h +++ b/src/blackcore/context_audio_impl.h @@ -164,12 +164,12 @@ namespace BlackCore //! Connection in transition bool inTransitionState() const; - std::unique_ptr m_voice; //!< underlying voice lib + std::unique_ptr m_voice; //!< underlying voice lib CInputManager *m_inputManager = nullptr; CInputManager::RegistrationHandle m_handlePtt; - QPointer m_channelCom1; - QPointer m_channelCom2; + std::unique_ptr m_channelCom1; + std::unique_ptr m_channelCom2; std::unique_ptr m_voiceOutputDevice; int m_outDeviceVolume = 100; std::unique_ptr m_voiceInputDevice; diff --git a/src/blackcore/voice.h b/src/blackcore/voice.h index 574186b80..fea90fd47 100644 --- a/src/blackcore/voice.h +++ b/src/blackcore/voice.h @@ -7,6 +7,8 @@ #define BLACKCORE_VOICE_H #include "audio_device.h" +#include "voice_channel.h" + #include "../blackmisc/avcallsignlist.h" #include "../blackmisc/avselcal.h" #include "../blackmisc/nwuserlist.h" @@ -20,7 +22,6 @@ namespace BlackCore { - class IVoiceChannel; //! Interface to a connection to a ATC voice server for use in flight simulation. class IVoice : public QObject { @@ -37,15 +38,15 @@ namespace BlackCore //! Virtual destructor. virtual ~IVoice() {} + //! Create voice channel object + virtual std::unique_ptr createVoiceChannel() = 0; + //! Create input device object virtual std::unique_ptr createInputDevice() = 0; //! Create output device object virtual std::unique_ptr createOutputDevice() = 0; - //! Get voice channel object - virtual IVoiceChannel *getVoiceChannel(qint32 channelIndex) const = 0; - /*! * \brief Enable audio loopback to route recorded voice from microphone to speakers * \param enable (default true) diff --git a/src/blackcore/voice_channel.h b/src/blackcore/voice_channel.h index 42e6e650a..07d3651bf 100644 --- a/src/blackcore/voice_channel.h +++ b/src/blackcore/voice_channel.h @@ -12,7 +12,6 @@ #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" @@ -40,44 +39,35 @@ namespace BlackCore ConnectingFailed, //!< Failed to connect }; + //! Constructor + IVoiceChannel(QObject *parent = nullptr) : QObject(parent) {} + + //! Destructor + virtual ~IVoiceChannel() {} + //! Join voice room virtual void joinVoiceRoom(const BlackMisc::Audio::CVoiceRoom &voiceRoom) = 0; //! Leave voice room virtual void leaveVoiceRoom() = 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 0..100 - virtual void setVolume(quint32 volume) = 0; + virtual void setVolume(int volume) = 0; //! Get channel volume 0..100 - virtual quint32 getVolume() const = 0; - - //! Update room status - virtual void updateRoomStatus(Cvatlib_Voice_Simple::roomStatusUpdate roomStatus) = 0; + virtual int getVolume() const = 0; signals: @@ -105,12 +95,6 @@ namespace BlackCore protected: - //! Constructor - IVoiceChannel(QObject *parent = nullptr) : QObject(parent) {} - - //! Destructor - virtual ~IVoiceChannel() {} - }; } diff --git a/src/blackcore/voice_channel_vatlib.cpp b/src/blackcore/voice_channel_vatlib.cpp index 81b39719e..ae262a9c0 100644 --- a/src/blackcore/voice_channel_vatlib.cpp +++ b/src/blackcore/voice_channel_vatlib.cpp @@ -22,190 +22,97 @@ namespace BlackCore // Room data hash shared between all CVoiceChannel objects QHash> CVoiceChannelVatlibPrivate::m_sharedRoomData; - // Static list of available rooms - QList 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), + CVoiceChannelVatlibPrivate::CVoiceChannelVatlibPrivate(VatAudioService audioService, VatUDPAudioPort udpPort, CVoiceChannelVatlib *parent) + : m_audioService(audioService), + m_udpPort(udpPort), q_ptr(parent) { - m_roomIndex.store(InvalidRoomIndex); - m_connectionRefCount.store(0); - m_outputEnabled.store(true); - m_roomStatus.store(IVoiceChannel::Disconnected); + Q_ASSERT(m_audioService); + Q_ASSERT(m_udpPort); - connect(this, &CVoiceChannelVatlibPrivate::userJoinedLeft, this, &CVoiceChannelVatlibPrivate::processUserJoinedLeft, Qt::QueuedConnection); + m_connectionRefCount = 0; + m_roomStatus = IVoiceChannel::Disconnected; + + m_voiceChannels.push_back(parent); + + m_voiceChannel.reset(Vat_CreateVoiceChannel(m_audioService, "", 3782, "", "", m_udpPort)); + Vat_SetConnectionChangedHandler(m_voiceChannel.data(), onRoomStatusUpdate, this); + Vat_SetClientJoinedHandler(m_voiceChannel.data(), processUserJoined, this); + Vat_SetClientLeftHandler(m_voiceChannel.data(), processUserLeft, this); + Vat_SetVoiceTransmissionChangedHandler(m_voiceChannel.data(), processTransmissionChange, this); } CVoiceChannelVatlibPrivate::~CVoiceChannelVatlibPrivate() { } - void CVoiceChannelVatlibPrivate::changeConnectionStatus(IVoiceChannel::ConnectionStatus newStatus) + void CVoiceChannelVatlibPrivate::setRoomOutputVolume(int volume) { - Q_Q(CVoiceChannelVatlib); - - m_roomStatus = newStatus; - emit q->connectionStatusChanged(m_roomStatus, newStatus); + // FIXME + Q_UNUSED(volume) } - void CVoiceChannelVatlibPrivate::switchAudioOutput(bool enable) + void CVoiceChannelVatlibPrivate::updateRoomStatus(VatVoiceChannel /* channel */, VatConnectionStatus /** oldVatStatus **/, VatConnectionStatus newVatStatus) { - std::lock_guard 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 + IVoiceChannel::ConnectionStatus oldStatus = m_roomStatus; + switch (newVatStatus) { - m_vatlib->SetOutputState(m_roomIndex, 0, enable); - } - catch (...) + case vatStatusConnecting: { - exceptionDispatcher(Q_FUNC_INFO); + m_roomStatus = IVoiceChannel::Connecting; + break; } + case vatStatusConnected: + { + m_voiceRoom.setConnected(true); + m_roomStatus = IVoiceChannel::Connected; + break; + } + case vatStatusDisconnecting: + { + m_roomStatus = IVoiceChannel::Disconnecting; + break; + } + case vatStatusDisconnected: + { + // Clear all internals + m_listCallsigns.clear(); + m_voiceRoom = {}; + m_roomStatus = IVoiceChannel::Disconnected; + break; + } + default: + break; + } + emit q_ptr->connectionStatusChanged(oldStatus, m_roomStatus); } - void CVoiceChannelVatlibPrivate::startTransmitting() + void CVoiceChannelVatlibPrivate::userJoinedVoiceRoom(VatVoiceChannel, int /** id **/, const char *name) { - std::lock_guard 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!"); + CCallsign callsign(extractCallsign(name)); + m_listCallsigns.push_back(callsign); - if (m_roomStatus != IVoiceChannel::Connected) return; - - try - { - m_vatlib->SetMicState(m_roomIndex, true); - } - catch (...) - { - this->exceptionDispatcher(Q_FUNC_INFO); - } + for (const auto &e : m_voiceChannels) + emit e->userJoinedRoom(callsign); } - void CVoiceChannelVatlibPrivate::stopTransmitting() + void CVoiceChannelVatlibPrivate::userLeftVoiceRoom(VatVoiceChannel, int /** id **/, const char *name) { - std::lock_guard 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!"); + CCallsign callsign(extractCallsign(name)); + m_listCallsigns.remove(callsign); - if (m_roomStatus != IVoiceChannel::Connected) return; - - try - { - m_vatlib->SetMicState(m_roomIndex, false); - } - catch (...) - { - this->exceptionDispatcher(Q_FUNC_INFO); - } + for (const auto &e : m_voiceChannels) + emit e->userLeftRoom(callsign); } - void CVoiceChannelVatlibPrivate::setRoomOutputVolume(const qint32 volume) + void CVoiceChannelVatlibPrivate::transmissionChanged(VatVoiceChannel, VatVoiceTransmissionStatus status) { - std::lock_guard 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 - CLogMessage(q).debug() << "NetworkNotConnectedException" << e.what() << "in" << caller; - } - catch (const VatlibException &e) - { - CLogMessage(q).error("VatlibException %1 in %2") << e.what() << caller; - Q_ASSERT(false); - } - catch (const std::exception &e) - { - CLogMessage(q).error("std::exception %1 in %2") << e.what() << caller; - Q_ASSERT(false); - } - catch (...) - { - CLogMessage(q).error("Unknown exception in %1") << caller; - Q_ASSERT(false); - } - } - - void CVoiceChannelVatlibPrivate::processUserJoinedLeft() - { - Q_Q(CVoiceChannelVatlib); - - try - { - std::lock_guard 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); - } + if (status == vatVoiceStarted) + emit q_ptr->audioStarted(); + else + emit q_ptr->audioStopped(); } CVoiceChannelVatlibPrivate *cbvar_cast_voiceChannelPrivate(void *cbvar) @@ -213,39 +120,52 @@ namespace BlackCore return static_cast(cbvar); } - /* - * Room user received - */ - void CVoiceChannelVatlibPrivate::updateRoomUsers(Cvatlib_Voice_Simple *obj, const char *name, void *cbVar) + CCallsign CVoiceChannelVatlibPrivate::extractCallsign(const QString &name) { - Q_UNUSED(obj) + CCallsign callsign; + if (name.isEmpty()) return callsign; - // 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(" ")) + if (name.contains(" ")) { - QStringList parts = callsign.split(" "); - callsign = parts[0]; + QStringList parts = name.split(" "); + callsign = CCallsign(parts[0]); // I throw away VATSIM id here, maybe we could use it } + else + { + callsign = CCallsign(name); + } - voiceChannelPrivate->addTemporaryCallsignForRoom(CCallsign(callsign)); + return callsign; + } + + void CVoiceChannelVatlibPrivate::processUserJoined(VatVoiceChannel channel, int id, const char *name, void *cbVar) + { + CVoiceChannelVatlibPrivate *voiceChannelPrivate = cbvar_cast_voiceChannelPrivate(cbVar); + voiceChannelPrivate->userJoinedVoiceRoom(channel, id, name); + } + + void CVoiceChannelVatlibPrivate::processUserLeft(VatVoiceChannel channel, int id, const char *name, void *cbVar) + { + CVoiceChannelVatlibPrivate *voiceChannelPrivate = cbvar_cast_voiceChannelPrivate(cbVar); + voiceChannelPrivate->userLeftVoiceRoom(channel, id, name); + } + + void CVoiceChannelVatlibPrivate::processTransmissionChange(VatVoiceChannel channel, VatVoiceTransmissionStatus status, void *cbVar) + { + CVoiceChannelVatlibPrivate *voiceChannelPrivate = cbvar_cast_voiceChannelPrivate(cbVar); + + voiceChannelPrivate->transmissionChanged(channel, status); } /* - * Add temp.callsign for room + * Room status update */ - void CVoiceChannelVatlibPrivate::addTemporaryCallsignForRoom(const CCallsign &callsign) + void CVoiceChannelVatlibPrivate::onRoomStatusUpdate(VatVoiceChannel channel, VatConnectionStatus oldStatus, VatConnectionStatus newStatus, void *cbVar) { - if (m_temporaryVoiceRoomCallsigns.contains(callsign)) return; - m_temporaryVoiceRoomCallsigns.push_back(callsign); + auto obj = cbvar_cast_voiceChannelPrivate(cbVar); + obj->updateRoomStatus(channel, oldStatus, newStatus); } // Get shared room data @@ -254,23 +174,17 @@ namespace BlackCore 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) + CVoiceChannelVatlib::CVoiceChannelVatlib(VatAudioService audioService, VatUDPAudioPort udpPort, QObject *parent) : IVoiceChannel(parent), - d_ptr(new CVoiceChannelVatlibPrivate(vatlib, this)) + d_ptr(new CVoiceChannelVatlibPrivate(audioService, udpPort, this)) { } // Destructor CVoiceChannelVatlib::~CVoiceChannelVatlib() { + d_ptr->m_voiceChannels.removeAll(this); } // Join room @@ -283,46 +197,40 @@ namespace BlackCore return roomData->m_voiceRoom.getVoiceRoomUrl() == voiceRoom.getVoiceRoomUrl(); }); - try + // If we found another channel + if (iterator != roomDataList.end()) { - // If we found an another channel - if (iterator != roomDataList.end()) - { - // Increase the connection reference counter - (*iterator)->m_connectionRefCount++; + // Increase the connection reference counter + (*iterator)->m_connectionRefCount++; - d_ptr = (*iterator); + d_ptr = (*iterator); - // Assign shared room data to this channel index - CVoiceChannelVatlibPrivate::getSharedRoomData().insert(this, *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; + // Add ourselfes as listener + d_ptr->m_voiceChannels.push_back(this); - std::lock_guard 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; - } + // 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); } - catch (...) + else { - d_ptr->exceptionDispatcher(Q_FUNC_INFO); + // No one else is using this voice room, so prepare to join + d_ptr->m_voiceRoom = voiceRoom; + Vat_SetRoomInfo(d_ptr->m_voiceChannel.data(), qPrintable(voiceRoom.getHostname()), 3782, + qPrintable(voiceRoom.getChannel()), + qPrintable(d_ptr->m_callsign.toQString())); + + d_ptr->m_roomStatus = IVoiceChannel::Disconnected; + Vat_JoinRoom(d_ptr->m_voiceChannel.data()); + + CVoiceChannelVatlibPrivate::m_sharedRoomData.insert(this, d_ptr); + ++d_ptr->m_connectionRefCount; } + } // Leave room @@ -337,143 +245,60 @@ namespace BlackCore // 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 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); - } + qDebug() << "Leaving voice room!"; + Vat_DisconnectFromRoom(d_ptr->m_voiceChannel.data()); } else { + d_ptr->m_voiceChannels.removeAll(this); + // 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)); + VatAudioService audioService = d_ptr->m_audioService; + VatUDPAudioPort udpPort = d_ptr->m_udpPort; + + d_ptr.reset(new CVoiceChannelVatlibPrivate(audioService, udpPort, this)); CVoiceChannelVatlibPrivate::getSharedRoomData().insert(this, d_ptr); // Simulate the state change - d_ptr->changeConnectionStatus(IVoiceChannel::Disconnecting); - d_ptr->changeConnectionStatus(IVoiceChannel::Disconnected); + emit connectionStatusChanged(IVoiceChannel::Connected, IVoiceChannel::Disconnecting); + emit connectionStatusChanged(IVoiceChannel::Disconnecting, IVoiceChannel::Disconnected); } } - 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) + void CVoiceChannelVatlib::setVolume(int 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); + d_ptr->setRoomOutputVolume(volume); } - quint32 CVoiceChannelVatlib::getVolume() const + int CVoiceChannelVatlib::getVolume() const { - return d_ptr->m_volume.load(); + // FIXME + return 100; + } + + VatVoiceChannel CVoiceChannelVatlib::getVoiceChannel() const + { + return d_ptr->m_voiceChannel.data(); } } diff --git a/src/blackcore/voice_channel_vatlib.h b/src/blackcore/voice_channel_vatlib.h index aebf99990..939fbd9f5 100644 --- a/src/blackcore/voice_channel_vatlib.h +++ b/src/blackcore/voice_channel_vatlib.h @@ -32,7 +32,7 @@ namespace BlackCore public: //! Default constructor - CVoiceChannelVatlib(TVatlibPointer vatlib, QObject *parent = nullptr); + CVoiceChannelVatlib(VatAudioService audioService, VatUDPAudioPort udpPort, QObject *parent = nullptr); //! Destructor virtual ~CVoiceChannelVatlib(); @@ -43,38 +43,26 @@ namespace BlackCore //! \copydoc IVoiceChannel::leaveVoiceRoom virtual void leaveVoiceRoom() 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; + virtual void setVolume(int volume) override; //! Get channel volume - virtual quint32 getVolume() const override; + virtual int getVolume() const override; - //! \copydoc IVoiceChannel::updateRoomStatus - virtual void updateRoomStatus(Cvatlib_Voice_Simple::roomStatusUpdate roomStatus) override; + //! Get vatlib channel pointer + VatVoiceChannel getVoiceChannel() const; private: QSharedPointer d_ptr; diff --git a/src/blackcore/voice_channel_vatlib_p.h b/src/blackcore/voice_channel_vatlib_p.h index 5c0717a46..91a45150c 100644 --- a/src/blackcore/voice_channel_vatlib_p.h +++ b/src/blackcore/voice_channel_vatlib_p.h @@ -31,67 +31,48 @@ namespace BlackCore public: + struct VatVoiceChannelDeleter + { + static inline void cleanup(VatProducerConsumer_tag *obj) + { + if (!obj) Vat_DestroyVoiceChannel(obj); + } + }; + // Default constructor - CVoiceChannelVatlibPrivate(TVatlibPointer vatlib, CVoiceChannelVatlib *parent); + CVoiceChannelVatlibPrivate(VatAudioService audioService, VatUDPAudioPort udpPort, 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); + void setRoomOutputVolume(int volume); - // FIXME Move into free function - void exceptionDispatcher(const char *caller); + void updateRoomStatus(VatVoiceChannel channel, VatConnectionStatus /** oldStatus **/, VatConnectionStatus newStatus); - // Update connected room users - static void updateRoomUsers(Cvatlib_Voice_Simple *obj, const char *name, void *cbVar); + BlackMisc::Aviation::CCallsign extractCallsign(const QString &name); - public slots: + static void processUserJoined(VatVoiceChannel channel, int id, const char *name, void *cbVar); + static void processUserLeft(VatVoiceChannel channel, int id, const char *name, void *cbVar); + static void processTransmissionChange(VatVoiceChannel channel, VatVoiceTransmissionStatus status, void *cbVar); - // 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); + static void onRoomStatusUpdate(VatVoiceChannel channel, VatConnectionStatus oldStatus, VatConnectionStatus newStatus, void *cbVar); // Get shared room data for this channel static QHash> &getSharedRoomData(); - TVatlibPointer m_vatlib; // Shared pointer to vatlib object BlackMisc::Aviation::CCallsign m_callsign; // Own callsign - std::atomic m_roomIndex; // Room index BlackMisc::Audio::CVoiceRoom m_voiceRoom; // Voice Room - std::atomic m_volume; // Room volume std::atomic m_connectionRefCount; // Connection reference couting std::atomic m_outputEnabled; // Is room output enabled? BlackMisc::Aviation::CCallsignList m_listCallsigns; // Callsigns connected to room std::atomic m_roomStatus; // Room connection status + QList m_voiceChannels; - // Mutexes - QMutex m_mutexSharedRoomData; - QMutex m_mutexCallSign; - QMutex m_mutexVoiceRoom; - QMutex m_mutexCallsignList; + VatAudioService m_audioService; + VatUDPAudioPort m_udpPort; + QScopedPointer m_voiceChannel; - BlackMisc::Aviation::CCallsignList m_temporaryVoiceRoomCallsigns; // temp. storage of voice rooms during update - - static QList m_availableRooms; // Static list of not used room indexes static QHash> m_sharedRoomData; const static qint32 InvalidRoomIndex = -1; // Invalid room index @@ -100,11 +81,9 @@ namespace BlackCore private: - // Change room connection status - void changeConnectionStatus(IVoiceChannel::ConnectionStatus newStatus); - - // Allocate a not used room identified by its index - qint32 allocateRoom(); + void userJoinedVoiceRoom(VatVoiceChannel, int id, const char *name); + void userLeftVoiceRoom(VatVoiceChannel, int id, const char *name); + void transmissionChanged(VatVoiceChannel, VatVoiceTransmissionStatus status); }; //! \endcond } diff --git a/src/blackcore/voice_vatlib.cpp b/src/blackcore/voice_vatlib.cpp index 83bf4539b..c8ca6dbe1 100644 --- a/src/blackcore/voice_vatlib.cpp +++ b/src/blackcore/voice_vatlib.cpp @@ -38,6 +38,11 @@ namespace BlackCore */ CVoiceVatlib::~CVoiceVatlib() {} + std::unique_ptr CVoiceVatlib::createVoiceChannel() + { + return make_unique(m_audioService.data(), m_udpPort.data(), this); + } + std::unique_ptr CVoiceVatlib::createInputDevice() { return make_unique(m_audioService.data(), this); @@ -48,38 +53,6 @@ namespace BlackCore return make_unique(m_audioService.data(), this); } - IVoiceChannel *CVoiceVatlib::getVoiceChannel(qint32 channelIndex) const - { - IVoiceChannel *channel = m_hashChannelIndex.value(channelIndex, nullptr); - Q_ASSERT(channel); - - return channel; - } - - /* - * Handle PTT - */ - void CVoiceVatlib::handlePushToTalk(bool value) - { - 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) - { - getVoiceChannel(0)->startTransmitting(); - getVoiceChannel(1)->startTransmitting(); - } - else - { - getVoiceChannel(0)->stopTransmitting(); - getVoiceChannel(1)->stopTransmitting(); - } - } - /* * Process voice handling */ @@ -89,33 +62,6 @@ namespace BlackCore Vat_ExecuteTasks(m_audioService.data()); } - /* - * Room status update - */ - void CVoiceVatlib::onRoomStatusUpdate(Cvatlib_Voice_Simple *obj, Cvatlib_Voice_Simple::roomStatusUpdate upd, qint32 roomIndex, void *cbVar) - { - Q_UNUSED(obj) - CVoiceVatlib *vatlibRoom = cbvar_cast_voice(cbVar); - vatlibRoom->onRoomStatusUpdate(roomIndex, upd); - } - - void CVoiceVatlib::onRoomStatusUpdate(qint32 roomIndex, Cvatlib_Voice_Simple::roomStatusUpdate roomStatus) - { - QList voiceChannels = m_hashChannelIndex.values(); - auto iterator = std::find_if(voiceChannels.begin(), voiceChannels.end(), [&](const IVoiceChannel * voiceChannel) - { - return voiceChannel->getRoomIndex() == roomIndex; - }); - - if (iterator == voiceChannels.end()) - { - qWarning() << "Unknown room index"; - return; - } - - (*iterator)->updateRoomStatus(roomStatus); - } - void CVoiceVatlib::voiceErrorHandler(const char *message) { CLogMessage(static_cast(nullptr)).error(message); diff --git a/src/blackcore/voice_vatlib.h b/src/blackcore/voice_vatlib.h index 34600ca6a..8a9dfac18 100644 --- a/src/blackcore/voice_vatlib.h +++ b/src/blackcore/voice_vatlib.h @@ -41,23 +41,18 @@ namespace BlackCore //! \brief Destructor virtual ~CVoiceVatlib(); + //! \copydoc IVoice::createVoiceChannel() + virtual std::unique_ptr createVoiceChannel() override; + //! \copydoc IVoice::createInputDevice() virtual std::unique_ptr createInputDevice() override; //! \copydoc IVoice::createOutputDevice() virtual std::unique_ptr createOutputDevice() override; - //! \copydoc IVoice::getVoiceChannel - virtual IVoiceChannel *getVoiceChannel(qint32 channelIndex) const override; - //! \copydoc IVoice::enableAudioLoopback virtual void enableAudioLoopback(bool enable = true) override; - /*! - * \brief Starts or stops voice transmission - * \param value - */ - void handlePushToTalk(bool value = false); protected: // QObject overrides @@ -87,14 +82,8 @@ namespace BlackCore static void voiceErrorHandler(const char *message); - // shimlib callbacks - static void onRoomStatusUpdate(Cvatlib_Voice_Simple *obj, Cvatlib_Voice_Simple::roomStatusUpdate upd, qint32 roomIndex, void *cbVar); - - void onRoomStatusUpdate(qint32 roomIndex, Cvatlib_Voice_Simple::roomStatusUpdate roomStatus); - QScopedPointer m_audioService; QScopedPointer m_udpPort; - QHash m_hashChannelIndex; bool m_isAudioLoopbackEnabled; /*!< A flag whether audio loopback is enabled or not */ };