From 45b9f60444d756a31e238cd2c53c7a96f0385f21 Mon Sep 17 00:00:00 2001 From: Roland Winklmeier Date: Sun, 1 Dec 2013 01:53:26 +0100 Subject: [PATCH] Finalized voice vatlib implementation. refs #36 - Implemented the rest of the methods - Added more commands to the voice sample, to login to voice server - Login successfull. However, no voice yet. refs #81 --- samples/voiceclient/client.cpp | 46 +++++- samples/voiceclient/client.h | 7 + src/blackcore/voiceclient.h | 55 ++++--- src/blackcore/voiceclient_vatlib.cpp | 217 +++++++++++++++++++++++---- src/blackcore/voiceclient_vatlib.h | 40 +++-- 5 files changed, 304 insertions(+), 61 deletions(-) diff --git a/samples/voiceclient/client.cpp b/samples/voiceclient/client.cpp index 1a8f94785..e9cf8357e 100644 --- a/samples/voiceclient/client.cpp +++ b/samples/voiceclient/client.cpp @@ -12,6 +12,8 @@ Client::Client(QObject *parent) : using namespace BlackCore; connect(m_voiceClient, &IVoiceClient::squelchTestFinished, this, &Client::onSquelchTestFinished); connect(m_voiceClient, &IVoiceClient::micTestFinished, this, &Client::onMicTestFinished); + connect(m_voiceClient, &IVoiceClient::connected, this, &Client::connectionStatusConnected); + @@ -19,8 +21,11 @@ Client::Client(QObject *parent) : m_commands["help"] = std::bind(&Client::help, this, _1); m_commands["echo"] = std::bind(&Client::echo, this, _1); m_commands["exit"] = std::bind(&Client::exit, this, _1); - m_commands["runsquelchtest"] = std::bind(&Client::runSquelchTest, this, _1); - m_commands["runmictest"] = std::bind(&Client::runMicTest, this, _1); + m_commands["squelchtest"] = std::bind(&Client::runSquelchTest, this, _1); + m_commands["mictest"] = std::bind(&Client::runMicTest, this, _1); + m_commands["setcallsign"] = std::bind(&Client::setCallsignCmd, this, _1); + m_commands["initconnect"] = std::bind(&Client::initiateConnectionCmd, this, _1); + m_commands["termconnect"] = std::bind(&Client::terminateConnectionCmd, this, _1); } @@ -89,6 +94,30 @@ void Client::runMicTest(QTextStream &args) m_voiceClient->runMicTest(); } +void Client::setCallsignCmd(QTextStream &args) +{ + QString callsign; + args >> callsign; + m_voiceClient->setCallsign(BlackMisc::Aviation::CCallsign(callsign)); +} + +void Client::initiateConnectionCmd(QTextStream &args) +{ + QString hostname; + 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)); + printLinePrefix(); +} + +void Client::terminateConnectionCmd(QTextStream &args) +{ + std::cout << "Leaving room." << std::endl; + m_voiceClient->leaveVoiceRoom(0); + printLinePrefix(); +} + void Client::onSquelchTestFinished() { std::cout << "Input squelch: " << m_voiceClient->inputSquelch() << std::endl; @@ -100,3 +129,16 @@ void Client::onMicTestFinished() std::cout << "Mic test result: " << (int)m_voiceClient->micTestResult() << std::endl; printLinePrefix(); } + +void Client::connectionStatusConnected() +{ + std::cout << "CONN_STATUS_CONNECTED" << std::endl; + printLinePrefix(); +} + +void Client::connectionStatusDisconnected() +{ + std::cout << "CONN_STATUS_DISCONNECTED" << std::endl; + printLinePrefix(); +} + diff --git a/samples/voiceclient/client.h b/samples/voiceclient/client.h index 32dc783cc..f7e7a5b4c 100644 --- a/samples/voiceclient/client.h +++ b/samples/voiceclient/client.h @@ -29,12 +29,19 @@ private: //commands void exit(QTextStream &args); void runSquelchTest(QTextStream &args); void runMicTest(QTextStream &args); + void setCallsignCmd(QTextStream &args); + void initiateConnectionCmd(QTextStream &args); + void terminateConnectionCmd(QTextStream &args); void printLinePrefix(); public slots: void onSquelchTestFinished(); void onMicTestFinished(); +private slots: + + void connectionStatusConnected(); + void connectionStatusDisconnected(); private: QMap> m_commands; diff --git a/src/blackcore/voiceclient.h b/src/blackcore/voiceclient.h index ea9045d78..d0df4faf9 100644 --- a/src/blackcore/voiceclient.h +++ b/src/blackcore/voiceclient.h @@ -18,11 +18,17 @@ #include #include +#include namespace BlackCore { /*! * Interface to a connection to a ATC voice server for use in flight simulation. + * + * \warning If an INetwork signal is connected to a slot, and that slot emits a signal + * which is connected to an INetwork slot, then at least one of those connections + * must be a Qt::QueuedConnection. + * Reason: IVoiceClient implementations are not re-entrant. */ class IVoiceClient : public QObject { @@ -47,18 +53,8 @@ namespace BlackCore virtual ~IVoiceClient() {} - virtual void setCallsign(const BlackMisc::Aviation::CCallsign &callsign) = 0; - virtual void joinVoiceRoom(const uint32_t comUnit, const BlackMisc::Voice::CVoiceRoom &voiceRoom) = 0; - virtual void leaveVoiceRoom(const uint32_t comUnit) = 0; - virtual void setVolume(const uint32_t comUnit, const uint32_t volumne) = 0; - virtual void startTransmitting(const uint32_t comUnit) = 0; - virtual void stopTransmitting(const uint32_t comUnit) = 0; - virtual bool isReceiving(const uint32_t comUnit) = 0; - virtual bool isConnected(const uint32_t comUnit) = 0; - - // The following method should be called everytime you receive a user update signal - virtual void roomUserList(const uint32_t comUnit) = 0; + virtual const QStringList roomUserList(const int32_t comUnit) = 0; // Hardware devices virtual const QList & audioInputDevices() const = 0; @@ -70,6 +66,8 @@ namespace BlackCore virtual void setInputDevice(const BlackMisc::Voice::CInputAudioDevice &device) = 0; virtual void setOutputDevice(const BlackMisc::Voice::COutputAudioDevice &device) = 0; + virtual void enableAudio(const int32_t comUnit) = 0; + // Mic tests virtual void runSquelchTest() = 0; @@ -80,23 +78,36 @@ namespace BlackCore virtual const BlackMisc::Voice::CVoiceRoom &voiceRoom (const uint32_t comUnit) = 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; + + signals: // Signals regarding the voice server connection - void notConnected(const uint32_t comUnit); - void connecting(const uint32_t comUnit); - void connected(const uint32_t comUnit); - void connectionFailed(const uint32_t comUnit); - void kicked(const uint32_t comUnit); - void disconnecting(const uint32_t comUnit); - void disconnected(const uint32_t comUnit); + 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); // Signals about users joining and leaving - void userJoined(const uint32_t comUnit, const BlackMisc::Aviation::CCallsign &callsign); - void userLeft(const uint32_t comUnit, const BlackMisc::Aviation::CCallsign &callsign); + void userJoinedRoom(const QString &callsign); + void userLeftRoom(const QString &callsign); // Audio signals - void audioStarted(const uint32_t comUnit); - void audioStopped(const uint32_t comUnit); + void audioStarted(const int32_t comUnit); + void audioStopped(const int32_t comUnit); + void audioStarted(); + void audioStopped(); // Test signals void squelchTestFinished(); diff --git a/src/blackcore/voiceclient_vatlib.cpp b/src/blackcore/voiceclient_vatlib.cpp index 4d8b8a282..bed914cc8 100644 --- a/src/blackcore/voiceclient_vatlib.cpp +++ b/src/blackcore/voiceclient_vatlib.cpp @@ -13,7 +13,8 @@ namespace BlackCore CVoiceClientVatlib::CVoiceClientVatlib(QObject *parent) : IVoiceClient(parent), m_voice(Create_Cvatlib_Voice_Simple()), - m_inputSquelch(-1) + m_inputSquelch(-1), + m_queryUserRoomIndex(-1) { try { @@ -26,6 +27,9 @@ namespace BlackCore setInputDevice(defaultAudioInputDevice()); setOutputDevice(defaultAudioOutputDevice()); + connect(this, &CVoiceClientVatlib::userJoinedLeft, this, &CVoiceClientVatlib::onUserJoinedLeft, Qt::QueuedConnection); + connect(this, &CVoiceClientVatlib::connected, this, &CVoiceClientVatlib::onUserJoinedLeft, Qt::QueuedConnection); + startTimer(100); } catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } @@ -40,44 +44,110 @@ namespace BlackCore m_callsign = callsign; } - void CVoiceClientVatlib::joinVoiceRoom(const uint32_t comUnit, const BlackMisc::Voice::CVoiceRoom &voiceRoom) + 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::leaveVoiceRoom(const uint32_t comUnit) + 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); } } - void CVoiceClientVatlib::setVolume(const uint32_t comUnit, const uint32_t volumne) + 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!"); + m_voice->IsRoomConnected(comUnit); + } + catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } } - void CVoiceClientVatlib::startTransmitting(const uint32_t comUnit) + const QStringList CVoiceClientVatlib::roomUserList(const int32_t comUnit) { - - } - - void CVoiceClientVatlib::stopTransmitting(const uint32_t comUnit) - { - - } - - bool CVoiceClientVatlib::isReceiving(const uint32_t comUnit) - { - - } - - bool CVoiceClientVatlib::isConnected(const uint32_t comUnit) - { - - } - - void CVoiceClientVatlib::roomUserList(const uint32_t comUnit) - { - + /**/ + return m_voiceRoomsUsers.value(comUnit); } const QList &CVoiceClientVatlib::audioInputDevices() const @@ -129,6 +199,11 @@ 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) { if (!device.isValid()) @@ -224,6 +299,29 @@ namespace BlackCore catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } } + void CVoiceClientVatlib::onUserJoinedLeft(const int32_t roomIndex) + { + 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)) + return; + + // Store the room index for the slot. + m_queryUserRoomIndex = roomIndex; + m_voice->GetRoomUserList(roomIndex, onRoomUserReceived, this); + m_queryUserRoomIndex = -1; + } + catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } + } + /********************************** * * * * * * * * * * * * * * * * * * * ************************************/ /********************************** shimlib callbacks ************************************/ /********************************** * * * * * * * * * * * * * * * * * * * ************************************/ @@ -235,12 +333,62 @@ 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; + } } void CVoiceClientVatlib::onRoomUserReceived(Cvatlib_Voice_Simple *obj, const char *name, void *cbVar) { + CVoiceClientVatlib *voiceClientVatlib = cbvar_cast(cbVar); + // This method retrieves first of all a list of the known users + QStringList users = voiceClientVatlib->roomUserList(voiceClientVatlib->queryUserRoomIndex()); + 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); + } } void CVoiceClientVatlib::onInputHardwareDeviceReceived(Cvatlib_Voice_Simple *obj, const char *name, void *cbVar) @@ -255,6 +403,19 @@ namespace BlackCore cbvar_cast(cbVar)->m_outputDevices.append(outputDevice); } + void CVoiceClientVatlib::addUserInRoom(const int32_t roomIndex, 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); + } + void CVoiceClientVatlib::exceptionDispatcher(const char *caller) { QString msg("Caller: "); diff --git a/src/blackcore/voiceclient_vatlib.h b/src/blackcore/voiceclient_vatlib.h index 68585206b..fb825bed2 100644 --- a/src/blackcore/voiceclient_vatlib.h +++ b/src/blackcore/voiceclient_vatlib.h @@ -25,15 +25,15 @@ namespace BlackCore virtual ~CVoiceClientVatlib(); virtual void setCallsign(const BlackMisc::Aviation::CCallsign &callsign); - virtual void joinVoiceRoom(const uint32_t comUnit, const BlackMisc::Voice::CVoiceRoom &voiceRoom); - virtual void leaveVoiceRoom(const uint32_t comUnit); - virtual void setVolume(const uint32_t comUnit, const uint32_t volumne); - virtual void startTransmitting(const uint32_t comUnit); - virtual void stopTransmitting(const uint32_t comUnit); - virtual bool isReceiving(const uint32_t comUnit); - virtual bool isConnected(const uint32_t comUnit); + 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 void roomUserList(const uint32_t comUnit); + virtual const QStringList roomUserList(const int32_t comUnit); // Hardware devices // TODO: Vatlib supports multiple output devices. That basically means, you could connect @@ -48,6 +48,8 @@ namespace BlackCore virtual void setInputDevice(const BlackMisc::Voice::CInputAudioDevice &device); virtual void setOutputDevice(const BlackMisc::Voice::COutputAudioDevice &device); + virtual void enableAudio(const int32_t comUnit); + // Mic tests virtual void runSquelchTest(); virtual void runMicTest(); @@ -58,6 +60,8 @@ namespace BlackCore virtual const BlackMisc::Voice::CVoiceRoom &voiceRoom (const uint32_t comUnit); + int32_t queryUserRoomIndex() const {return m_queryUserRoomIndex;} + signals: public slots: @@ -70,6 +74,12 @@ namespace BlackCore void onEndFindSquelch(); void onEndMicTest(); + // slot to handle users + void onUserJoinedLeft(const int32_t roomIndex); + + signals: + void userJoinedLeft(const int32_t roomIndex); + private: // shimlib callbacks @@ -78,6 +88,9 @@ namespace BlackCore 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 exceptionDispatcher(const char *caller); struct Cvatlib_Voice_Simple_Deleter @@ -90,12 +103,21 @@ namespace BlackCore QScopedPointer m_voice; BlackMisc::Aviation::CCallsign m_callsign; - QMap m_voiceRoomMap; + QMap m_voiceRooms; QList m_inputDevices; QList m_outputDevices; float m_inputSquelch; Cvatlib_Voice_Simple::agc m_micTestResult; + + typedef QMap TMapRoomsStatus; + TMapRoomsStatus m_voiceRoomsStatus; + + typedef QMap TMapRoomsUsers; + TMapRoomsUsers m_voiceRoomsUsers; + + // Need to keep the roomIndex because GetRoomUserList does not specifiy it + int32_t m_queryUserRoomIndex; }; } // namespace BlackCore