diff --git a/samples/voiceclient/client.cpp b/samples/voiceclient/client.cpp index 41e2648dd..c1969dcec 100644 --- a/samples/voiceclient/client.cpp +++ b/samples/voiceclient/client.cpp @@ -14,6 +14,10 @@ Client::Client(QObject *parent) : connect(m_voiceClient, &IVoiceClient::micTestFinished, this, &Client::onMicTestFinished); connect(m_voiceClient, &IVoiceClient::connected, this, &Client::connectionStatusConnected); connect(m_voiceClient, &IVoiceClient::disconnected, this, &Client::connectionStatusDisconnected); + connect(m_voiceClient, &IVoiceClient::audioStarted, this, &Client::audioStartedStream); + connect(m_voiceClient, &IVoiceClient::audioStopped, this, &Client::audioStoppedStream); + connect(m_voiceClient, &IVoiceClient::userJoinedRoom, this, &Client::userJoinedRoom); + connect(m_voiceClient, &IVoiceClient::userLeftRoom, this, &Client::userLeftRoom); using namespace std::placeholders; m_commands["help"] = std::bind(&Client::help, this, _1); @@ -26,6 +30,7 @@ Client::Client(QObject *parent) : m_commands["termconnect"] = std::bind(&Client::terminateConnectionCmd, this, _1); m_commands["inputdevices"] = std::bind(&Client::inputDevicesCmd, this, _1); m_commands["outputdevices"] = std::bind(&Client::outputDevicesCmd, this, _1); + m_commands["users"] = std::bind(&Client::listUsersCmd, this, _1); } @@ -107,14 +112,14 @@ void Client::initiateConnectionCmd(QTextStream &args) QString channel; args >> hostname >> channel; std::cout << "Joining voice room: " << hostname.toStdString() << "/" << channel.toStdString() << std::endl; - m_voiceClient->joinVoiceRoom(0, BlackMisc::Voice::CVoiceRoom(hostname, channel)); + m_voiceClient->joinVoiceRoom(BlackCore::IVoiceClient::COM1, BlackMisc::Voice::CVoiceRoom(hostname, channel)); printLinePrefix(); } void Client::terminateConnectionCmd(QTextStream &args) { std::cout << "Leaving room." << std::endl; - m_voiceClient->leaveVoiceRoom(0); + m_voiceClient->leaveVoiceRoom(BlackCore::IVoiceClient::COM1); printLinePrefix(); } @@ -138,6 +143,17 @@ void Client::outputDevicesCmd(QTextStream &args) printLinePrefix(); } +void Client::listUsersCmd(QTextStream &args) +{ + Q_UNUSED(args) + QSet users = m_voiceClient->roomUserList(BlackCore::IVoiceClient::COM1); + foreach (QString user, users) + { + std::cout << " " << user.toStdString() << std::endl; + } + printLinePrefix(); +} + void Client::onSquelchTestFinished() { std::cout << "Input squelch: " << m_voiceClient->inputSquelch() << std::endl; @@ -150,7 +166,7 @@ void Client::onMicTestFinished() printLinePrefix(); } -void Client::connectionStatusConnected() +void Client::connectionStatusConnected(const BlackCore::IVoiceClient::ComUnit comUnit) { std::cout << "CONN_STATUS_CONNECTED" << std::endl; printLinePrefix(); @@ -162,3 +178,28 @@ void Client::connectionStatusDisconnected() printLinePrefix(); } +void Client::audioStartedStream(const BlackCore::IVoiceClient::ComUnit comUnit) +{ + std::cout << "Started stream in room index " << static_cast(comUnit) << std::endl; + printLinePrefix(); +} + +void Client::audioStoppedStream(const BlackCore::IVoiceClient::ComUnit comUnit) +{ + std::cout << "Stopped stream in room index " << static_cast(comUnit) << std::endl; + printLinePrefix(); +} + +void Client::userJoinedRoom(const QString &callsign) +{ + std::cout << callsign.toStdString() << " joined the voice room." << std::endl; + printLinePrefix(); +} + +void Client::userLeftRoom(const QString &callsign) +{ + std::cout << callsign.toStdString() << " left the voice room." << std::endl; + printLinePrefix(); +} + + diff --git a/samples/voiceclient/client.h b/samples/voiceclient/client.h index b2c38d5a7..db9ad46d1 100644 --- a/samples/voiceclient/client.h +++ b/samples/voiceclient/client.h @@ -35,6 +35,7 @@ private: //commands void terminateConnectionCmd(QTextStream &args); void inputDevicesCmd(QTextStream &args); void outputDevicesCmd(QTextStream &args); + void listUsersCmd(QTextStream &args); void printLinePrefix(); @@ -43,8 +44,12 @@ public slots: void onMicTestFinished(); private slots: - void connectionStatusConnected(); + void connectionStatusConnected(const BlackCore::IVoiceClient::ComUnit comUnit); void connectionStatusDisconnected(); + void audioStartedStream(const BlackCore::IVoiceClient::ComUnit comUnit); + void audioStoppedStream(const BlackCore::IVoiceClient::ComUnit comUnit); + void userJoinedRoom(const QString &callsign); + void userLeftRoom(const QString &callsign); private: QMap> m_commands; diff --git a/src/blackcore/voiceclient.cpp b/src/blackcore/voiceclient.cpp new file mode 100644 index 000000000..011e595ed --- /dev/null +++ b/src/blackcore/voiceclient.cpp @@ -0,0 +1,15 @@ +/* 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 "voiceclient.h" + +namespace BlackCore +{ + IVoiceClient::IVoiceClient(QObject *parent) + : QObject(parent) + { + qRegisterMetaType("IVoiceClient::ComUnit"); + } +} diff --git a/src/blackcore/voiceclient.h b/src/blackcore/voiceclient.h index d0df4faf9..637d28bcf 100644 --- a/src/blackcore/voiceclient.h +++ b/src/blackcore/voiceclient.h @@ -3,10 +3,6 @@ * 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/. */ -/*! - \file -*/ - #ifndef BLACKCORE_VOICE_H #define BLACKCORE_VOICE_H @@ -19,6 +15,7 @@ #include #include +#include namespace BlackCore { @@ -46,68 +43,184 @@ namespace BlackCore * \brief Default constructor with parent * \param parent */ - IVoiceClient(QObject *parent = 0) : QObject(parent) {} + IVoiceClient(QObject *parent = 0); public: - BLACK_INTERFACE(BlackCore::IVoice) + //! ComUnit + /*! IVoiceClient currently supports two different com units */ + enum ComUnit { + COM1 = 0, /*!< ComUnit 1 */ + COM2 /*!< ComUnit 2 */ + }; + + //! Virtual destructor. virtual ~IVoiceClient() {} - // The following method should be called everytime you receive a user update signal - virtual const QStringList roomUserList(const int32_t comUnit) = 0; + //! roomUserList + /*! + \return A list of users currently connected to the voice room + */ + virtual const QSet roomUserList(const ComUnit comUnit) = 0; - // Hardware devices + //! audioInputDevices + /*! + \return A list of available input devices + */ virtual const QList & audioInputDevices() const = 0; + + //! audioOutputDevices + /*! + \return A list of available output devices + */ virtual const QList & audioOutputDevices() const = 0; - virtual const BlackMisc::Voice::CInputAudioDevice & defaultAudioInputDevice() const = 0; - virtual const BlackMisc::Voice::COutputAudioDevice & defaultAudioOutputDevice() const = 0; + //! defaultAudioInputDevice + /*! + \return Default input device + */ + virtual const BlackMisc::Voice::CInputAudioDevice defaultAudioInputDevice() const = 0; + //! defaultAudioOutputDevice + /*! + \return Default output device + */ + virtual const BlackMisc::Voice::COutputAudioDevice defaultAudioOutputDevice() const = 0; + + //! setInputDevice + /*! + \param input device + */ virtual void setInputDevice(const BlackMisc::Voice::CInputAudioDevice &device) = 0; + + //! setOutputDevice + /*! + \param output device + */ virtual void setOutputDevice(const BlackMisc::Voice::COutputAudioDevice &device) = 0; - virtual void enableAudio(const int32_t comUnit) = 0; + //! enableAudio + /*! + \brief After you have joined a voice room, you must enable audio output. + \param comUnit + */ + virtual void enableAudio(const ComUnit comUnit) = 0; - // Mic tests + //! voiceRoom + /*! + \brief After you have joined a voice room, you must enable audio output. + \param comUnit + \return voiceRoom + */ + virtual const BlackMisc::Voice::CVoiceRoom voiceRoom (const ComUnit comUnit) = 0; + //! isConnected + /*! + \param comUnit + \return if connected + */ + virtual bool isConnected(const ComUnit comUnit) = 0; + + //! isReceiving + /*! + \param comUnit + */ + virtual bool isReceiving(const ComUnit comUnit) = 0; + + + /************************************************ + * SETUP TESTS + * *********************************************/ + + //! runSquelchTest + /*! + \brief Runs a 5 seconds test, measuring your background noise. + */ virtual void runSquelchTest() = 0; + + //! runMicTest + /*! + \brief Runs a 5 seconds test, measuring the qualitiy of your mic input + */ virtual void runMicTest() = 0; + //! inputSquelch + /*! + \return Value of the measured squelch + */ virtual float inputSquelch() const = 0; - virtual Cvatlib_Voice_Simple::agc micTestResult() const = 0; - virtual const BlackMisc::Voice::CVoiceRoom &voiceRoom (const uint32_t comUnit) = 0; + //! micTestResult + /*! + \return Result of the mic test. + */ + virtual int32_t micTestResult() const = 0; + + //! micTestResult + /*! + \return Result of the mic test as human readable string + */ + virtual QString micTestResultAsString() const = 0; public slots: - virtual void setCallsign(const BlackMisc::Aviation::CCallsign &callsign) = 0; - virtual void joinVoiceRoom(const int32_t comUnit, const BlackMisc::Voice::CVoiceRoom &voiceRoom) = 0; - virtual void leaveVoiceRoom(const int32_t comUnit) = 0; - virtual void setVolume(const int32_t comUnit, const uint32_t volumne) = 0; - virtual void startTransmitting(const int32_t comUnit) = 0; - virtual void stopTransmitting(const int32_t comUnit) = 0; - virtual bool isReceiving(const int32_t comUnit) = 0; - virtual bool isConnected(const int32_t comUnit) = 0; + //! setCallsign + /*! + \param callsign + */ + virtual void setCallsign(const BlackMisc::Aviation::CCallsign &callsign) = 0; + + //! joinVoiceRoom + /*! + \param comUnit + \param voiceRoom + */ + virtual void joinVoiceRoom(const ComUnit comUnit, const BlackMisc::Voice::CVoiceRoom &voiceRoom) = 0; + + //! leaveVoiceRoom + /*! + \param comUnit + */ + virtual void leaveVoiceRoom(const ComUnit comUnit) = 0; + + //! setVolume + /*! + \param comUnit + \param volumne + */ + virtual void setVolume(const ComUnit comUnit, const int32_t volumne) = 0; + + //! startTransmitting + /*! + \param comUnit + */ + virtual void startTransmitting(const ComUnit comUnit) = 0; + + //! stopTransmitting + /*! + \param comUnit + */ + virtual void stopTransmitting(const ComUnit comUnit) = 0; signals: // Signals regarding the voice server connection - void notConnected(const int32_t comUnit); - void connecting(const int32_t comUnit); - void connected(const int32_t comUnit); - void connectionFailed(const int32_t comUnit); - void kicked(const int32_t comUnit); - void disconnecting(const int32_t comUnit); - void disconnected(const int32_t comUnit); + void notConnected(const ComUnit comUnit); + void connecting(const ComUnit comUnit); + void connected(const ComUnit comUnit); + void connectionFailed(const ComUnit comUnit); + void kicked(const ComUnit comUnit); + void disconnecting(const ComUnit comUnit); + void disconnected(const ComUnit comUnit); // Signals about users joining and leaving void userJoinedRoom(const QString &callsign); void userLeftRoom(const QString &callsign); // Audio signals - void audioStarted(const int32_t comUnit); - void audioStopped(const int32_t comUnit); - void audioStarted(); - void audioStopped(); + void audioStarted(const ComUnit comUnit); + void audioStopped(const ComUnit comUnit); + void globalAudioStarted(); + void globalAudioStopped(); // Test signals void squelchTestFinished(); @@ -116,10 +229,13 @@ namespace BlackCore // non protocol related signals void exception(const QString &message, bool fatal = false); // let remote places know there was an exception - public slots: + protected: + }; } // namespace BlackCore +Q_DECLARE_METATYPE(BlackCore::IVoiceClient::ComUnit) + #endif // BLACKCORE_VOICE_H diff --git a/src/blackcore/voiceclient_vatlib.cpp b/src/blackcore/voiceclient_vatlib.cpp index 45cba2c77..63c889e50 100644 --- a/src/blackcore/voiceclient_vatlib.cpp +++ b/src/blackcore/voiceclient_vatlib.cpp @@ -14,6 +14,7 @@ namespace BlackCore IVoiceClient(parent), m_voice(Cvatlib_Voice_Simple::Create()), m_inputSquelch(-1), + m_micTestResult(Cvatlib_Voice_Simple::agc_Ok), m_queryUserRoomIndex(-1) { try @@ -39,112 +40,7 @@ namespace BlackCore { } - void CVoiceClientVatlib::setCallsign(const BlackMisc::Aviation::CCallsign &callsign) - { - m_callsign = callsign; - } - - void CVoiceClientVatlib::joinVoiceRoom(const int32_t comUnit, const BlackMisc::Voice::CVoiceRoom &voiceRoom) - { - if (!voiceRoom.isValid()) - { - qDebug() << "Error: voiceRoom is invalid."; - } - - Q_ASSERT_X (m_voice->IsValid() && m_voice->IsSetup(), "CVoiceClientVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); - - try - { - if (!m_voice->IsRoomValid(comUnit)) - { - qDebug() << "Error: room index out of bounds"; - return; - } - - QString serverSpec = voiceRoom.hostName() + "/" + voiceRoom.channel(); - - m_voice->JoinRoom(comUnit, m_callsign.toQString().toLatin1().constData(), serverSpec.toLatin1().constData()); - } - catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } - } - - void CVoiceClientVatlib::leaveVoiceRoom(const int32_t comUnit) - { - try - { - if (!m_voice->IsRoomValid(comUnit)) - { - qDebug() << "Error: room index out of bounds"; - return; - } - Q_ASSERT_X (m_voice->IsValid() && m_voice->IsSetup(), "CVoiceClientVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); - m_voice->LeaveRoom(comUnit); - } - catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } - } - - void CVoiceClientVatlib::setVolume(const int32_t comUnit, const uint32_t volumne) - { - - } - - void CVoiceClientVatlib::startTransmitting(const int32_t comUnit) - { - try - { - if (!m_voice->IsRoomValid(comUnit)) - { - qDebug() << "Error: room index out of bounds"; - return; - } - Q_ASSERT_X (m_voice->IsValid() && m_voice->IsSetup(), "CVoiceClientVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); - m_voice->SetMicState(comUnit, true); - } - catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } - } - - void CVoiceClientVatlib::stopTransmitting(const int32_t comUnit) - { - try - { - if (!m_voice->IsRoomValid(comUnit)) - { - qDebug() << "Error: room index out of bounds"; - return; - } - Q_ASSERT_X (m_voice->IsValid() && m_voice->IsSetup(), "CVoiceClientVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); - m_voice->SetMicState(comUnit, false); - } - catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } - } - - bool CVoiceClientVatlib::isReceiving(const int32_t comUnit) - { - try - { - if (!m_voice->IsRoomValid(comUnit)) - { - qDebug() << "Error: room index out of bounds"; - return false; - } - Q_ASSERT_X (m_voice->IsValid() && m_voice->IsSetup(), "CVoiceClientVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); - m_voice->IsAudioPlaying(comUnit); - } - catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } - - } - - bool CVoiceClientVatlib::isConnected(const int32_t comUnit) - { - try - { - Q_ASSERT_X (m_voice->IsValid() && m_voice->IsSetup(), "CVoiceClientVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); - return m_voice->IsRoomConnected(comUnit); - } - catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } - } - - const QStringList CVoiceClientVatlib::roomUserList(const int32_t comUnit) + const QSet CVoiceClientVatlib::roomUserList(const ComUnit comUnit) { /**/ return m_voiceRoomsUsers.value(comUnit); @@ -160,30 +56,26 @@ namespace BlackCore return m_outputDevices; } - const BlackMisc::Voice::CInputAudioDevice & CVoiceClientVatlib::defaultAudioInputDevice() const + const BlackMisc::Voice::CInputAudioDevice CVoiceClientVatlib::defaultAudioInputDevice() const { - foreach(BlackMisc::Voice::CInputAudioDevice inputDevice, m_inputDevices) - { - if(inputDevice.name().contains("[default]", Qt::CaseInsensitive)) - return inputDevice; - } - return BlackMisc::Voice::CInputAudioDevice(); + // Constructor creates already a default device + return BlackMisc::Voice::CInputAudioDevice(BlackMisc::Voice::CInputAudioDevice::defaultDevice(), "default"); } - const BlackMisc::Voice::COutputAudioDevice & CVoiceClientVatlib::defaultAudioOutputDevice() const + const BlackMisc::Voice::COutputAudioDevice CVoiceClientVatlib::defaultAudioOutputDevice() const { - foreach(BlackMisc::Voice::COutputAudioDevice outputDevice, m_outputDevices) - { - if(outputDevice.name().contains("[default]", Qt::CaseInsensitive)) - return outputDevice; - } - return BlackMisc::Voice::COutputAudioDevice(); + // Constructor creates already a default device + return BlackMisc::Voice::COutputAudioDevice(BlackMisc::Voice::COutputAudioDevice::defaultDevice(), "default"); } void CVoiceClientVatlib::setInputDevice(const BlackMisc::Voice::CInputAudioDevice &device) { + Q_ASSERT_X (m_voice->IsValid() && m_voice->IsSetup(), "CVoiceClientVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); if (!device.isValid()) + { + qWarning() << "Cannot set invalid input device!"; return; + } try { @@ -199,15 +91,14 @@ namespace BlackCore catch (...) { exceptionDispatcher(Q_FUNC_INFO); } } - void CVoiceClientVatlib::enableAudio(const int32_t comUnit) - { - m_voice->SetOutoutState(comUnit, 0, true); - } - void CVoiceClientVatlib::setOutputDevice(const BlackMisc::Voice::COutputAudioDevice &device) { + Q_ASSERT_X (m_voice->IsValid() && m_voice->IsSetup(), "CVoiceClientVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); if (!device.isValid()) + { + qWarning() << "Cannot set invalid output device!"; return; + } try { @@ -221,11 +112,56 @@ namespace BlackCore catch (...) { exceptionDispatcher(Q_FUNC_INFO); } } - void CVoiceClientVatlib::runSquelchTest() + void CVoiceClientVatlib::enableAudio(const ComUnit comUnit) { + Q_ASSERT_X (m_voice->IsValid() && m_voice->IsSetup(), "CVoiceClientVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); + Q_ASSERT_X (m_voice->IsRoomValid(static_cast(comUnit)), "CVoiceClientVatlib", "Room index out of bounds!"); + try + { + m_voice->SetOutoutState(static_cast(comUnit), 0, true); + } + catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } + } + + const BlackMisc::Voice::CVoiceRoom CVoiceClientVatlib::voiceRoom(const ComUnit comUnit) + { + return m_voiceRooms.value(comUnit); + } + + bool CVoiceClientVatlib::isConnected(const ComUnit comUnit) + { + bool result; + Q_ASSERT_X (m_voice->IsValid() && m_voice->IsSetup(), "CVoiceClientVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); + Q_ASSERT_X (m_voice->IsRoomValid(static_cast(comUnit)), "CVoiceClientVatlib", "Room index out of bounds!"); + try + { + result = m_voice->IsRoomConnected(static_cast(comUnit)); + } + catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } + + return result; + } + + bool CVoiceClientVatlib::isReceiving(const ComUnit comUnit) + { + bool receiving; + Q_ASSERT_X (m_voice->IsValid() && m_voice->IsSetup(), "CVoiceClientVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); + Q_ASSERT_X (m_voice->IsRoomValid(static_cast(comUnit)), "CVoiceClientVatlib", "Room index out of bounds!"); + try + { + receiving = m_voice->IsAudioPlaying(static_cast(comUnit)); + } + catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } + + return receiving; + } + + void CVoiceClientVatlib::runSquelchTest() + { + Q_ASSERT_X (m_voice->IsValid() && m_voice->IsSetup(), "CVoiceClientVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); + try { - Q_ASSERT_X (m_voice->IsValid() && m_voice->IsSetup(), "CVoiceClientVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); m_voice->BeginFindSquelch(); } catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } @@ -236,9 +172,10 @@ namespace BlackCore void CVoiceClientVatlib::runMicTest() { + Q_ASSERT_X (m_voice->IsValid() && m_voice->IsSetup(), "CVoiceClientVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); + try { - Q_ASSERT_X (m_voice->IsValid() && m_voice->IsSetup(), "CVoiceClientVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); m_voice->BeginMicTest(); } catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } @@ -252,21 +189,159 @@ namespace BlackCore return m_inputSquelch; } - Cvatlib_Voice_Simple::agc CVoiceClientVatlib::micTestResult() const + int32_t CVoiceClientVatlib::micTestResult() const { return m_micTestResult; } - const BlackMisc::Voice::CVoiceRoom &CVoiceClientVatlib::voiceRoom(const uint32_t comUnit) + QString CVoiceClientVatlib::micTestResultAsString() const { - return BlackMisc::Voice::CVoiceRoom(); + QString result; + switch (m_micTestResult) + { + case Cvatlib_Voice_Simple::agc_Ok: + result = "The test went ok"; + break; + case Cvatlib_Voice_Simple::agc_BkgndNoiseLoud: + result = "The overall background noise is very loud and may be a nuisance to others"; + break; + case Cvatlib_Voice_Simple::agc_TalkDrownedOut: + result = "The overall background noise is loud enough that others probably wont be able to distinguish speech from it"; + break; + case Cvatlib_Voice_Simple::agc_TalkMicHot: + result = "The overall mic volume is too hot, you should lower the volume in the windows volume control panel"; + break; + case Cvatlib_Voice_Simple::agc_TalkMicCold: + result = "The overall mic volume is too cold, you should raise the volume in the windows control panel and enable mic boost if needed"; + break; + default: + result = "Unknown result."; + break; + } + + return result; + } + + void CVoiceClientVatlib::setCallsign(const BlackMisc::Aviation::CCallsign &callsign) + { + m_callsign = callsign; + } + + void CVoiceClientVatlib::joinVoiceRoom(const ComUnit comUnit, const BlackMisc::Voice::CVoiceRoom &voiceRoom) + { + Q_ASSERT_X (m_voice->IsValid() && m_voice->IsSetup(), "CVoiceClientVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); + Q_ASSERT_X (m_voice->IsRoomValid(static_cast(comUnit)), "CVoiceClientVatlib", "Room index out of bounds!"); + + if (!voiceRoom.isValid()) + { + qDebug() << "Error: Cannot join invalid voice room."; + return; + } + + try + { + QString serverSpec = voiceRoom.hostName() + "/" + voiceRoom.channel(); + + m_voice->JoinRoom(static_cast(comUnit), m_callsign.toQString().toLatin1().constData(), serverSpec.toLatin1().constData()); + m_voiceRooms[comUnit] = voiceRoom; + } + catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } + } + + void CVoiceClientVatlib::leaveVoiceRoom(const ComUnit comUnit) + { + Q_ASSERT_X (m_voice->IsValid() && m_voice->IsSetup(), "CVoiceClientVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); + Q_ASSERT_X (m_voice->IsRoomValid(static_cast(comUnit)), "CVoiceClientVatlib", "Room index out of bounds!"); + + try + { + m_voice->LeaveRoom(static_cast(comUnit)); + m_voiceRooms.remove(comUnit); + } + catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } + } + + void CVoiceClientVatlib::setVolume(const ComUnit comUnit, const int32_t volumne) + { + Q_ASSERT_X (m_voice->IsValid() && m_voice->IsSetup(), "CVoiceClientVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); + Q_ASSERT_X (m_voice->IsRoomValid(static_cast(comUnit)), "CVoiceClientVatlib", "Room index out of bounds!"); + + try + { + m_voice->SetRoomVolume(static_cast(comUnit), volumne); + } + catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } + } + + void CVoiceClientVatlib::startTransmitting(const ComUnit comUnit) + { + Q_ASSERT_X (m_voice->IsValid() && m_voice->IsSetup(), "CVoiceClientVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); + Q_ASSERT_X (m_voice->IsRoomValid(static_cast(comUnit)), "CVoiceClientVatlib", "Room index out of bounds!"); + + try + { + m_voice->SetMicState(static_cast(comUnit), true); + } + catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } + } + + void CVoiceClientVatlib::stopTransmitting(const ComUnit comUnit) + { + Q_ASSERT_X (m_voice->IsValid() && m_voice->IsSetup(), "CVoiceClientVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); + Q_ASSERT_X (m_voice->IsRoomValid(static_cast(comUnit)), "CVoiceClientVatlib", "Room index out of bounds!"); + try + { + m_voice->SetMicState(static_cast(comUnit), false); + } + catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } + } + + void CVoiceClientVatlib::changeRoomStatus(ComUnit comUnit, Cvatlib_Voice_Simple::roomStatusUpdate upd) + { + m_voiceRoomsStatus[comUnit] = upd; + switch (upd) + { + case Cvatlib_Voice_Simple::roomStatusUpdate_JoinSuccess: + enableAudio(comUnit); + emit connected(comUnit); + break; + case Cvatlib_Voice_Simple::roomStatusUpdate_JoinFail: + emit connectionFailed(comUnit); + break; + case Cvatlib_Voice_Simple::roomStatusUpdate_UnexpectedDisconnectOrKicked: + emit kicked(comUnit); + break; + case Cvatlib_Voice_Simple::roomStatusUpdate_LeaveComplete: + m_voiceRoomsUsers.clear(); + emit disconnected(comUnit); + break; + case Cvatlib_Voice_Simple::roomStatusUpdate_UserJoinsLeaves: + // FIXME: We cannot call GetRoomUserList because vatlib is 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; + } } void CVoiceClientVatlib::timerEvent(QTimerEvent *) { + Q_ASSERT_X (m_voice->IsValid() && m_voice->IsSetup(), "CVoiceClientVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); + try { - Q_ASSERT_X (m_voice->IsValid() && m_voice->IsSetup(), "CVoiceClientVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); m_voice->DoProcessing(); } catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } @@ -274,13 +349,11 @@ namespace BlackCore void CVoiceClientVatlib::onEndFindSquelch() { + Q_ASSERT_X (m_voice->IsValid() && m_voice->IsSetup(), "CVoiceClientVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); + try { - Q_ASSERT_X (m_voice->IsValid() && m_voice->IsSetup(), "CVoiceClientVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); - m_voice->EndFindSquelch(); - - //TODO: store captured squelch m_inputSquelch = m_voice->GetInputSquelch(); emit squelchTestFinished(); } @@ -289,35 +362,65 @@ namespace BlackCore void CVoiceClientVatlib::onEndMicTest() { + Q_ASSERT_X (m_voice->IsValid() && m_voice->IsSetup(), "CVoiceClientVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); + try { - Q_ASSERT_X (m_voice->IsValid() && m_voice->IsSetup(), "CVoiceClientVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); - m_micTestResult = m_voice->EndMicTest(); emit micTestFinished(); } catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } } - void CVoiceClientVatlib::onUserJoinedLeft(const int32_t roomIndex) + void CVoiceClientVatlib::onUserJoinedLeft(const ComUnit comUnit) { + Q_ASSERT_X (m_voice->IsValid() && m_voice->IsSetup(), "CVoiceClientVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); + Q_ASSERT_X (m_queryUserRoomIndex == -1, "CVoiceClientVatlib::onUserJoinedLeft", "Cannot list users for two rooms in parallel!"); try { - Q_ASSERT_X (m_voice->IsValid() && m_voice->IsSetup(), "CVoiceClientVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); - Q_ASSERT_X (m_queryUserRoomIndex == -1, "CVoiceClientVatlib::onUserJoinedLeft", "Cannot list users for two rooms in parallel!"); - - if (!m_voice->IsRoomValid(roomIndex)) - { - qDebug() << "Error: room index out of bounds"; - return; - } - if (!m_voice->IsRoomConnected(roomIndex)) + // Paranoia... + if (!m_voice->IsRoomConnected(static_cast(comUnit))) return; // Store the room index for the slot. - m_queryUserRoomIndex = roomIndex; - m_voice->GetRoomUserList(roomIndex, onRoomUserReceived, this); + m_queryUserRoomIndex = static_cast(comUnit); + m_voice->GetRoomUserList(static_cast(comUnit), onRoomUserReceived, this); m_queryUserRoomIndex = -1; + + QSet temporaryUsers; + + foreach (QString callsign, m_voiceRoomsUsers.value(comUnit)) + { + if (m_voiceRoomUsersUpdate.contains(callsign)) + { + // The user is still there. + temporaryUsers.insert(callsign); + } + else + { + // He is has probably left + emit userLeftRoom(callsign); + } + } + + foreach (QString callsign, m_voiceRoomUsersUpdate) + { + if (m_voiceRoomsUsers.value(comUnit).contains(callsign)) + { + // User was already there before + temporaryUsers.insert(callsign); + } + else + { + // He joined + temporaryUsers.insert(callsign); + emit userJoinedRoom(callsign); + } + } + + // Finally we update it with our new list + m_voiceRoomsUsers[comUnit] = temporaryUsers; + m_voiceRoomUsersUpdate.clear(); } catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } } @@ -333,87 +436,43 @@ namespace BlackCore void CVoiceClientVatlib::onRoomStatusUpdate(Cvatlib_Voice_Simple *obj, Cvatlib_Voice_Simple::roomStatusUpdate upd, int32_t roomIndex, void *cbVar) { - cbvar_cast(cbVar)->m_voiceRoomsStatus[roomIndex] = upd; - switch (upd) - { - case Cvatlib_Voice_Simple::roomStatusUpdate_JoinSuccess: - cbvar_cast(cbVar)->enableAudio(roomIndex); - emit cbvar_cast(cbVar)->connected(roomIndex); - break; - case Cvatlib_Voice_Simple::roomStatusUpdate_JoinFail: - emit cbvar_cast(cbVar)->connectionFailed(roomIndex); - break; - case Cvatlib_Voice_Simple::roomStatusUpdate_UnexpectedDisconnectOrKicked: - emit cbvar_cast(cbVar)->kicked(roomIndex); - break; - case Cvatlib_Voice_Simple::roomStatusUpdate_LeaveComplete: - emit cbvar_cast(cbVar)->disconnected(roomIndex); - break; - case Cvatlib_Voice_Simple::roomStatusUpdate_UserJoinsLeaves: - // FIXME: We cannot call GetRoomUserList because vatlib is reentrent safe. - emit cbvar_cast(cbVar)->userJoinedLeft (roomIndex); - break; - case Cvatlib_Voice_Simple::roomStatusUpdate_RoomAudioStarted: - emit cbvar_cast(cbVar)->audioStarted (roomIndex); - break; - case Cvatlib_Voice_Simple::roomStatusUpdate_RoomAudioStopped: - emit cbvar_cast(cbVar)->audioStopped (roomIndex); - break; - case Cvatlib_Voice_Simple::roomStatusUpdate_AudioStarted: - emit cbvar_cast(cbVar)->audioStarted(); - break; - case Cvatlib_Voice_Simple::roomStatusUpdate_AudioStopped: - emit cbvar_cast(cbVar)->audioStopped(); - break; - default: - break; - } + Q_UNUSED(obj) + ComUnit comUnit = static_cast(roomIndex); + CVoiceClientVatlib *voiceClientVatlib = cbvar_cast(cbVar); + voiceClientVatlib->changeRoomStatus(comUnit, upd); } void CVoiceClientVatlib::onRoomUserReceived(Cvatlib_Voice_Simple *obj, const char *name, void *cbVar) { + Q_UNUSED(obj) CVoiceClientVatlib *voiceClientVatlib = cbvar_cast(cbVar); + ComUnit comUnit = static_cast(voiceClientVatlib->queryUserRoomIndex()); // This method retrieves first of all a list of the known users - QStringList users = voiceClientVatlib->roomUserList(voiceClientVatlib->queryUserRoomIndex()); + QSet users = voiceClientVatlib->roomUserList(comUnit); QString callsign = QString(name); if (callsign.isEmpty()) return; - // if the user is already known, the user had left - // else, the user joined. - if (users.contains(callsign)) - { - voiceClientVatlib->removeUserFromRoom(voiceClientVatlib->queryUserRoomIndex(), callsign); - } - else - { - voiceClientVatlib->addUserInRoom(voiceClientVatlib->queryUserRoomIndex(), callsign); - } + voiceClientVatlib->addUserInRoom(comUnit, callsign); } void CVoiceClientVatlib::onInputHardwareDeviceReceived(Cvatlib_Voice_Simple *obj, const char *name, void *cbVar) { + Q_UNUSED(obj) BlackMisc::Voice::CInputAudioDevice inputDevice(cbvar_cast(cbVar)->m_inputDevices.size(), QString(name)); cbvar_cast(cbVar)->m_inputDevices.append(inputDevice); } void CVoiceClientVatlib::onOutputHardwareDeviceReceived(Cvatlib_Voice_Simple *obj, const char *name, void *cbVar) { + Q_UNUSED(obj) BlackMisc::Voice::COutputAudioDevice outputDevice(cbvar_cast(cbVar)->m_outputDevices.size(), QString(name)); cbvar_cast(cbVar)->m_outputDevices.append(outputDevice); } - void CVoiceClientVatlib::addUserInRoom(const int32_t roomIndex, const QString &callsign) + void CVoiceClientVatlib::addUserInRoom(const ComUnit comUnit, const QString &callsign) { - m_voiceRoomsUsers[roomIndex].append(callsign); - bool test = m_voice->IsAudioPlaying(roomIndex); - emit userJoinedRoom(callsign); - } - - void CVoiceClientVatlib::removeUserFromRoom(const int32_t roomIndex, const QString &callsign) - { - m_voiceRoomsUsers[roomIndex].removeAll(callsign); - emit userLeftRoom(callsign); + m_voiceRoomUsersUpdate.insert(callsign); } void CVoiceClientVatlib::exceptionDispatcher(const char *caller) diff --git a/src/blackcore/voiceclient_vatlib.h b/src/blackcore/voiceclient_vatlib.h index fb825bed2..a41d00d1d 100644 --- a/src/blackcore/voiceclient_vatlib.h +++ b/src/blackcore/voiceclient_vatlib.h @@ -10,6 +10,8 @@ #include #include +#include +#include namespace BlackCore { @@ -24,16 +26,7 @@ namespace BlackCore CVoiceClientVatlib(QObject *parent = 0); virtual ~CVoiceClientVatlib(); - virtual void setCallsign(const BlackMisc::Aviation::CCallsign &callsign); - virtual void joinVoiceRoom(const int32_t comUnit, const BlackMisc::Voice::CVoiceRoom &voiceRoom); - virtual void leaveVoiceRoom(const int32_t comUnit); - virtual void setVolume(const int32_t comUnit, const uint32_t volumne); - virtual void startTransmitting(const int32_t comUnit); - virtual void stopTransmitting(const int32_t comUnit); - virtual bool isReceiving(const int32_t comUnit); - virtual bool isConnected(const int32_t comUnit); - - virtual const QStringList roomUserList(const int32_t comUnit); + virtual const QSet roomUserList(const ComUnit comUnit); // Hardware devices // TODO: Vatlib supports multiple output devices. That basically means, you could connect @@ -42,29 +35,54 @@ namespace BlackCore virtual const QList & audioInputDevices() const ; virtual const QList & audioOutputDevices() const; - virtual const BlackMisc::Voice::CInputAudioDevice & defaultAudioInputDevice() const; - virtual const BlackMisc::Voice::COutputAudioDevice & defaultAudioOutputDevice() const; + virtual const BlackMisc::Voice::CInputAudioDevice defaultAudioInputDevice() const; + virtual const BlackMisc::Voice::COutputAudioDevice defaultAudioOutputDevice() const; virtual void setInputDevice(const BlackMisc::Voice::CInputAudioDevice &device); virtual void setOutputDevice(const BlackMisc::Voice::COutputAudioDevice &device); - virtual void enableAudio(const int32_t comUnit); + virtual void enableAudio(const ComUnit comUnit); + + virtual const BlackMisc::Voice::CVoiceRoom voiceRoom (const ComUnit comUnit); + + virtual bool isConnected(const ComUnit comUnit); + + virtual bool isReceiving(const ComUnit comUnit); + + /************************************************ + * SETUP TESTS + * *********************************************/ // Mic tests virtual void runSquelchTest(); virtual void runMicTest(); virtual float inputSquelch() const; - virtual Cvatlib_Voice_Simple::agc micTestResult() const; + virtual int32_t micTestResult() const; + virtual QString micTestResultAsString() const; - virtual const BlackMisc::Voice::CVoiceRoom &voiceRoom (const uint32_t comUnit); + public slots: + virtual void setCallsign(const BlackMisc::Aviation::CCallsign &callsign); + virtual void joinVoiceRoom(const ComUnit comUnit, const BlackMisc::Voice::CVoiceRoom &voiceRoom); + virtual void leaveVoiceRoom(const ComUnit comUnit); + virtual void setVolume(const ComUnit comUnit, const int32_t volumne); + virtual void startTransmitting(const ComUnit comUnit); + virtual void stopTransmitting(const ComUnit comUnit); + + /************************************************ + * NON API METHODS: + * The following methods are not part of the + * public API. They are needed for internal + * workflow. + * *********************************************/ int32_t queryUserRoomIndex() const {return m_queryUserRoomIndex;} - signals: + void changeRoomStatus(ComUnit comUnit, Cvatlib_Voice_Simple::roomStatusUpdate upd); - public slots: + signals: + void userJoinedLeft(const ComUnit comUnit); protected: // QObject overrides virtual void timerEvent(QTimerEvent *); @@ -75,21 +93,18 @@ namespace BlackCore void onEndMicTest(); // slot to handle users - void onUserJoinedLeft(const int32_t roomIndex); - - signals: - void userJoinedLeft(const int32_t roomIndex); + void onUserJoinedLeft(const ComUnit comUnit); private: // shimlib callbacks - static void onRoomStatusUpdate(Cvatlib_Voice_Simple* obj, Cvatlib_Voice_Simple::roomStatusUpdate upd, INT roomIndex, void* cbVar); + static void onRoomStatusUpdate(Cvatlib_Voice_Simple* obj, Cvatlib_Voice_Simple::roomStatusUpdate upd, int32_t 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); - void addUserInRoom(const int32_t roomIndex, const QString &callsign); - void removeUserFromRoom(const int32_t roomIndex, const QString &callsign); + void addUserInRoom(const ComUnit comUnit, const QString &callsign); + void removeUserFromRoom(const ComUnit comUnit, const QString &callsign); void exceptionDispatcher(const char *caller); @@ -103,19 +118,21 @@ namespace BlackCore QScopedPointer m_voice; BlackMisc::Aviation::CCallsign m_callsign; - QMap m_voiceRooms; + QMap m_voiceRooms; QList m_inputDevices; QList m_outputDevices; float m_inputSquelch; Cvatlib_Voice_Simple::agc m_micTestResult; - typedef QMap TMapRoomsStatus; + typedef QMap TMapRoomsStatus; TMapRoomsStatus m_voiceRoomsStatus; - typedef QMap TMapRoomsUsers; + typedef QMap> TMapRoomsUsers; TMapRoomsUsers m_voiceRoomsUsers; + QSet m_voiceRoomUsersUpdate; + // Need to keep the roomIndex because GetRoomUserList does not specifiy it int32_t m_queryUserRoomIndex; }; diff --git a/src/blackmisc/vaudiodevice.h b/src/blackmisc/vaudiodevice.h index 94c4e2e36..ca184eecc 100644 --- a/src/blackmisc/vaudiodevice.h +++ b/src/blackmisc/vaudiodevice.h @@ -34,8 +34,10 @@ namespace BlackMisc /*! * Default constructor. + * If m_deviceIndex is -1, default should be used. However on Windows this doesnt work. Needs + * to be checked in Vatlib. */ - CAudioDevice() : m_deviceIndex(0), m_deviceName("") {} + CAudioDevice() : m_deviceIndex(invalidDevice()), m_deviceName("") {} /*! @@ -65,10 +67,10 @@ namespace BlackMisc const QString &name() const { return m_deviceName; } /*! - * \brief Valid voice room object? + * \brief Valid audio device object? * \return */ - bool isValid() const { return m_deviceIndex >= 0 && !m_deviceName.isEmpty(); } + bool isValid() const { return m_deviceIndex >= -1 && !m_deviceName.isEmpty(); } /*! * \brief Equal operator == @@ -94,6 +96,10 @@ namespace BlackMisc */ static void registerMetadata(); + static int16_t defaultDevice() {return -1;} + + static int16_t invalidDevice() {return -2;} + protected: /*!