From 1c1fdabd2ef06a7b5c68381519aed8784508fc7a Mon Sep 17 00:00:00 2001 From: Klaus Basan Date: Tue, 7 Jan 2014 23:53:17 +0000 Subject: [PATCH] all voice changes from kbatclist branch refs #81 --- samples/blackcore/tool.cpp | 9 + samples/voiceclient/client.cpp | 49 +- samples/voiceclient/client.h | 12 +- samples/voiceclient/main.cpp | 4 +- src/blackcore/blackcore.contextnetwork.xml | 6 +- src/blackcore/blackcore.contextvoice.xml | 39 ++ src/blackcore/context_network.cpp | 9 - src/blackcore/context_network.h | 5 +- src/blackcore/context_network_atc.cpp | 21 + src/blackcore/context_network_interface.cpp | 8 +- src/blackcore/context_network_interface.h | 7 +- src/blackcore/context_voice.cpp | 169 +++++ src/blackcore/context_voice.h | 147 +++++ src/blackcore/context_voice_interface.cpp | 130 ++++ src/blackcore/context_voice_interface.h | 169 +++++ src/blackcore/{voiceclient.cpp => voice.cpp} | 8 +- src/blackcore/{voiceclient.h => voice.h} | 175 +++--- src/blackcore/voice_vatlib.cpp | 621 +++++++++++++++++++ src/blackcore/voice_vatlib.h | 283 +++++++++ src/blackcore/voice_vatlib_ptt.cpp | 74 +++ src/blackcore/voiceclient_vatlib.cpp | 513 --------------- src/blackcore/voiceclient_vatlib.h | 142 ----- src/blackgui/atcstationlistmodel.cpp | 3 + src/blackmisc/avatcstation.cpp | 18 +- src/blackmisc/avatcstation.h | 25 +- src/blackmisc/blackmiscfreefunctions.cpp | 5 +- src/blackmisc/vaudiodevice.cpp | 86 ++- src/blackmisc/vaudiodevice.h | 100 +-- src/blackmisc/vaudiodevicelist.cpp | 76 +++ src/blackmisc/vaudiodevicelist.h | 83 +++ src/blackmisc/voiceallclasses.h | 2 + src/blackmisc/vvoiceroom.cpp | 70 ++- src/blackmisc/vvoiceroom.h | 79 ++- src/blackmisc/vvoiceroomlist.cpp | 35 ++ src/blackmisc/vvoiceroomlist.h | 64 ++ 35 files changed, 2311 insertions(+), 935 deletions(-) create mode 100644 src/blackcore/blackcore.contextvoice.xml create mode 100644 src/blackcore/context_voice.cpp create mode 100644 src/blackcore/context_voice.h create mode 100644 src/blackcore/context_voice_interface.cpp create mode 100644 src/blackcore/context_voice_interface.h rename src/blackcore/{voiceclient.cpp => voice.cpp} (60%) rename src/blackcore/{voiceclient.h => voice.h} (59%) create mode 100644 src/blackcore/voice_vatlib.cpp create mode 100644 src/blackcore/voice_vatlib.h create mode 100644 src/blackcore/voice_vatlib_ptt.cpp delete mode 100644 src/blackcore/voiceclient_vatlib.cpp delete mode 100644 src/blackcore/voiceclient_vatlib.h create mode 100644 src/blackmisc/vaudiodevicelist.cpp create mode 100644 src/blackmisc/vaudiodevicelist.h create mode 100644 src/blackmisc/vvoiceroomlist.cpp create mode 100644 src/blackmisc/vvoiceroomlist.h diff --git a/samples/blackcore/tool.cpp b/samples/blackcore/tool.cpp index 3fc610c0f..f7db17cf0 100644 --- a/samples/blackcore/tool.cpp +++ b/samples/blackcore/tool.cpp @@ -39,6 +39,7 @@ namespace BlackMiscTest while (line != "x") { const BlackCore::IContextNetwork *networkContext = core->getIContextNetwork(); + const BlackCore::IContextVoice *voiceContext = core->getIContextVoice(); // display current status qDebug() << "-------------"; @@ -58,6 +59,7 @@ namespace BlackMiscTest qDebug() << "2 .. ATC online"; qDebug() << "3 .. Aircrafts in range"; qDebug() << "4 .. my aircraft"; + qDebug() << "5 .. voice rooms"; line = qtin.readLine(); @@ -85,6 +87,13 @@ namespace BlackMiscTest qDebug() << "my aircraft"; qDebug() << networkContext->getOwnAircraft(); } + else if (line.startsWith("5")) + { + qDebug() << "-------------"; + qDebug() << "voice rooms"; + qDebug() << voiceContext->getComVoiceRooms(); + } + } } } // namespace diff --git a/samples/voiceclient/client.cpp b/samples/voiceclient/client.cpp index b112da23b..b21e485da 100644 --- a/samples/voiceclient/client.cpp +++ b/samples/voiceclient/client.cpp @@ -4,20 +4,26 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "client.h" +#include "blackmisc/vaudiodevicelist.h" +using namespace BlackMisc::Voice; + +/* + * Client + */ Client::Client(QObject *parent) : QObject(parent), - m_voiceClient(&BlackMisc::IContext::getInstance().getObject()) + m_voiceClient(&BlackMisc::IContext::getInstance().getObject()) { 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); - 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); + connect(m_voiceClient, &IVoice::squelchTestFinished, this, &Client::onSquelchTestFinished); + connect(m_voiceClient, &IVoice::micTestFinished, this, &Client::onMicTestFinished); + connect(m_voiceClient, &IVoice::connected, this, &Client::connectionStatusConnected); + connect(m_voiceClient, &IVoice::disconnected, this, &Client::connectionStatusDisconnected); + connect(m_voiceClient, &IVoice::audioStarted, this, &Client::audioStartedStream); + connect(m_voiceClient, &IVoice::audioStopped, this, &Client::audioStoppedStream); + connect(m_voiceClient, &IVoice::userJoinedRoom, this, &Client::userJoinedRoom); + connect(m_voiceClient, &IVoice::userLeftRoom, this, &Client::userLeftRoom); using namespace std::placeholders; m_commands["help"] = std::bind(&Client::help, this, _1); @@ -112,33 +118,34 @@ void Client::initiateConnectionCmd(QTextStream &args) QString channel; args >> hostname >> channel; std::cout << "Joining voice room: " << hostname.toStdString() << "/" << channel.toStdString() << std::endl; - m_voiceClient->joinVoiceRoom(BlackCore::IVoiceClient::COM1, BlackMisc::Voice::CVoiceRoom(hostname, channel)); + m_voiceClient->joinVoiceRoom(BlackCore::IVoice::COM1, BlackMisc::Voice::CVoiceRoom(hostname, channel)); printLinePrefix(); } void Client::terminateConnectionCmd(QTextStream & /** args **/) { std::cout << "Leaving room." << std::endl; - m_voiceClient->leaveVoiceRoom(BlackCore::IVoiceClient::COM1); + m_voiceClient->leaveVoiceRoom(BlackCore::IVoice::COM1); printLinePrefix(); } void Client::inputDevicesCmd(QTextStream & /** args **/) { - QList devices = m_voiceClient->audioInputDevices(); - foreach(BlackMisc::Voice::CInputAudioDevice device, devices) + foreach(BlackMisc::Voice::CAudioDevice device, this->m_voiceClient->audioDevices().getInputDevices()) { - std::cout << device.name().toStdString() << std::endl; + std::cout << device.getName().toStdString() << std::endl; } printLinePrefix(); } +/* + * Output devices + */ void Client::outputDevicesCmd(QTextStream & /** args **/) { - QList devices = m_voiceClient->audioOutputDevices(); - foreach(BlackMisc::Voice::COutputAudioDevice device, devices) + foreach(BlackMisc::Voice::CAudioDevice device, this->m_voiceClient->audioDevices().getOutputDevices()) { - std::cout << " " << device.name().toStdString() << std::endl; + std::cout << device.getName().toStdString() << std::endl; } printLinePrefix(); } @@ -149,7 +156,7 @@ void Client::outputDevicesCmd(QTextStream & /** args **/) void Client::listCallsignsCmd(QTextStream &args) { Q_UNUSED(args) - QSet users = m_voiceClient->roomUserList(BlackCore::IVoiceClient::COM1); + QSet users = m_voiceClient->getVoiceRoomCallsings(BlackCore::IVoice::COM1); foreach(QString user, users) { std::cout << " " << user.toStdString() << std::endl; @@ -169,7 +176,7 @@ void Client::onMicTestFinished() printLinePrefix(); } -void Client::connectionStatusConnected(const BlackCore::IVoiceClient::ComUnit /** comUnit **/) +void Client::connectionStatusConnected(const BlackCore::IVoice::ComUnit /** comUnit **/) { std::cout << "CONN_STATUS_CONNECTED" << std::endl; printLinePrefix(); @@ -181,13 +188,13 @@ void Client::connectionStatusDisconnected() printLinePrefix(); } -void Client::audioStartedStream(const BlackCore::IVoiceClient::ComUnit comUnit) +void Client::audioStartedStream(const BlackCore::IVoice::ComUnit comUnit) { std::cout << "Started stream in room index " << static_cast(comUnit) << std::endl; printLinePrefix(); } -void Client::audioStoppedStream(const BlackCore::IVoiceClient::ComUnit comUnit) +void Client::audioStoppedStream(const BlackCore::IVoice::ComUnit comUnit) { std::cout << "Stopped stream in room index " << static_cast(comUnit) << std::endl; printLinePrefix(); diff --git a/samples/voiceclient/client.h b/samples/voiceclient/client.h index 151cb5558..da9da4876 100644 --- a/samples/voiceclient/client.h +++ b/samples/voiceclient/client.h @@ -6,7 +6,7 @@ #ifndef SAMPLE_VOICECLIENT #define SAMPLE_VOICECLIENT -#include "blackcore/voiceclient.h" +#include "blackcore/voice.h" #include #include @@ -45,17 +45,17 @@ public slots: void onMicTestFinished(); private slots: - void connectionStatusConnected(const BlackCore::IVoiceClient::ComUnit comUnit); + void connectionStatusConnected(const BlackCore::IVoice::ComUnit comUnit); void connectionStatusDisconnected(); - void audioStartedStream(const BlackCore::IVoiceClient::ComUnit comUnit); - void audioStoppedStream(const BlackCore::IVoiceClient::ComUnit comUnit); + void audioStartedStream(const BlackCore::IVoice::ComUnit comUnit); + void audioStoppedStream(const BlackCore::IVoice::ComUnit comUnit); void userJoinedRoom(const QString &callsign); void userLeftRoom(const QString &callsign); private: QMap> m_commands; - BlackCore::IVoiceClient *m_voiceClient; + BlackCore::IVoice *m_voiceClient; }; -#endif // CLIENT_H +#endif // SAMPLE_VOICECLIENT diff --git a/samples/voiceclient/main.cpp b/samples/voiceclient/main.cpp index 3ac3c3a49..be01e0ec0 100644 --- a/samples/voiceclient/main.cpp +++ b/samples/voiceclient/main.cpp @@ -3,7 +3,7 @@ * 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 "blackcore/voiceclient_vatlib.h" +#include "blackcore/voice_vatlib.h" #include "client.h" #include "reader.h" @@ -17,7 +17,7 @@ int main(int argc, char *argv[]) { QCoreApplication app (argc, argv); BlackMisc::IContext::getInstance().setObject(*new BlackMisc::CDebug()); - BlackMisc::IContext::getInstance().setObject(*new BlackCore::CVoiceClientVatlib()); + BlackMisc::IContext::getInstance().setObject(*new BlackCore::CVoiceVatlib()); Client client; LineReader reader; diff --git a/src/blackcore/blackcore.contextnetwork.xml b/src/blackcore/blackcore.contextnetwork.xml index 2e9fd6071..62a420b76 100644 --- a/src/blackcore/blackcore.contextnetwork.xml +++ b/src/blackcore/blackcore.contextnetwork.xml @@ -69,9 +69,9 @@ - - - + + + diff --git a/src/blackcore/blackcore.contextvoice.xml b/src/blackcore/blackcore.contextvoice.xml new file mode 100644 index 000000000..a4707e832 --- /dev/null +++ b/src/blackcore/blackcore.contextvoice.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/blackcore/context_network.cpp b/src/blackcore/context_network.cpp index 9b1093b57..bebc9884b 100644 --- a/src/blackcore/context_network.cpp +++ b/src/blackcore/context_network.cpp @@ -328,15 +328,6 @@ namespace BlackCore this->m_aircraftsInRange.applyIf(&CAircraft::getCallsign, callsign, vm); } - - /* - * Ping, is DBus alive? - */ - qint64 CContextNetwork::ping(qint64 token) const - { - return token; - } - /* * Exception to status message */ diff --git a/src/blackcore/context_network.h b/src/blackcore/context_network.h index 3f55ac66b..74cbbadae 100644 --- a/src/blackcore/context_network.h +++ b/src/blackcore/context_network.h @@ -176,11 +176,10 @@ namespace BlackCore virtual BlackMisc::Aviation::CInformationMessage getMetar(const QString &airportIcaoCode); /*! - * \brief Used to check if network is alive - * \param token + * \brief Selected COM1/2 frequencies as voice rooms, "" means no resolution * \return */ - virtual qint64 ping(qint64 token) const; + virtual BlackMisc::Voice::CVoiceRoomList getSelectedVoiceRooms() const; private: BlackMisc::Aviation::CAtcStationList m_atcStationsOnline; diff --git a/src/blackcore/context_network_atc.cpp b/src/blackcore/context_network_atc.cpp index 9826bc841..4f3617ab3 100644 --- a/src/blackcore/context_network_atc.cpp +++ b/src/blackcore/context_network_atc.cpp @@ -18,6 +18,7 @@ using namespace BlackMisc::PhysicalQuantities; using namespace BlackMisc::Aviation; using namespace BlackMisc::Network; using namespace BlackMisc::Geo; +using namespace BlackMisc::Voice; namespace BlackCore { @@ -79,6 +80,26 @@ namespace BlackCore return metar; } + /* + * Selected voice rooms + */ + CVoiceRoomList CContextNetwork::getSelectedVoiceRooms() const + { + CFrequency com1 = this->m_ownAircraft.getCom1System().getFrequencyActive(); + CFrequency com2 = this->m_ownAircraft.getCom2System().getFrequencyActive(); + + CAtcStationList stationsCom1 = this->m_atcStationsOnline.findBy(&CAtcStation::getFrequency, com1); + CAtcStationList stationsCom2 = this->m_atcStationsOnline.findBy(&CAtcStation::getFrequency, com2); + stationsCom1.sortBy(&CAtcStation::getDistanceToPlane); + stationsCom1.sortBy(&CAtcStation::getDistanceToPlane); + + CVoiceRoom vr; + CVoiceRoomList rooms; + rooms.push_back(stationsCom1.isEmpty() ? vr : stationsCom1[0].getVoiceRoom()); + rooms.push_back(stationsCom2.isEmpty() ? vr : stationsCom2[0].getVoiceRoom()); + return rooms; + } + /* * ATC Position update */ diff --git a/src/blackcore/context_network_interface.cpp b/src/blackcore/context_network_interface.cpp index 03bb26f08..a88f9bd7e 100644 --- a/src/blackcore/context_network_interface.cpp +++ b/src/blackcore/context_network_interface.cpp @@ -4,11 +4,11 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "blackcore/context_network_interface.h" +#include "blackmisc/vvoiceroomlist.h" #include #include #include - namespace BlackCore { @@ -165,11 +165,11 @@ namespace BlackCore } /* - * Ping, is DBus alive? + * Relay to DBus */ - qint64 IContextNetwork::ping(qint64 token) const + BlackMisc::Voice::CVoiceRoomList IContextNetwork::getSelectedVoiceRooms() const { - return this->m_dBusInterface->callDBusRet(QLatin1Literal("ping"), token); + return this->m_dBusInterface->callDBusRet(QLatin1Literal("getSelectedVoiceRooms")); } /* diff --git a/src/blackcore/context_network_interface.h b/src/blackcore/context_network_interface.h index 618cb0b61..d0eb935b6 100644 --- a/src/blackcore/context_network_interface.h +++ b/src/blackcore/context_network_interface.h @@ -12,6 +12,7 @@ #include "blackmisc/nwtextmessagelist.h" #include "blackmisc/genericdbusinterface.h" #include "blackcore/network_vatlib.h" +#include "blackmisc/vvoiceroomlist.h" #include #include #include @@ -256,12 +257,10 @@ namespace BlackCore virtual BlackMisc::Aviation::CInformationMessage getMetar(const QString &airportIcaoCode); /*! - * \brief Ping - * \param token + * \brief Use the selected COM1/2 frequencies, and get the corresponding voice room for it * \return */ - virtual qint64 ping(qint64 token) const; - + virtual BlackMisc::Voice::CVoiceRoomList getSelectedVoiceRooms() const; }; } diff --git a/src/blackcore/context_voice.cpp b/src/blackcore/context_voice.cpp new file mode 100644 index 000000000..41e49b05c --- /dev/null +++ b/src/blackcore/context_voice.cpp @@ -0,0 +1,169 @@ +/* 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 "context_voice.h" +#include "coreruntime.h" + +using namespace BlackMisc; +using namespace BlackMisc::Aviation; +using namespace BlackMisc::Voice; + +namespace BlackCore +{ + + /* + * Init this context + */ + CContextVoice::CContextVoice(CCoreRuntime *parent) : + IContextVoice(parent), m_voice(nullptr), m_currentInputDevice(), m_currentOutputDevice() + { + // 1. Init by "network driver" + this->m_voice = new CVoiceVatlib(this); + this->m_currentInputDevice = this->m_voice->defaultAudioInputDevice(); + this->m_currentOutputDevice = this->m_voice->defaultAudioOutputDevice(); + } + + /* + * Cleanup + */ + CContextVoice::~CContextVoice() + { + Q_ASSERT(this->m_voice); + this->leaveAllVoiceRooms(); + } + + /* + * Voice rooms for COM + */ + CVoiceRoomList CContextVoice::getComVoiceRoomsWithAudioStatus() + { + Q_ASSERT(this->m_voice); + return this->m_voice->getComVoiceRoomsWithAudioStatus(); + } + + /* + * Voice rooms for COM (const) + */ + CVoiceRoomList CContextVoice::getComVoiceRooms() const + { + Q_ASSERT(this->m_voice); + return this->m_voice->getComVoiceRooms(); + } + + /* + * Leave all voice rooms + */ + void CContextVoice::leaveAllVoiceRooms() + { + Q_ASSERT(this->m_voice); + this->m_voice->leaveAllVoiceRooms(); + } + + /* + * Audio devices + */ + CAudioDeviceList CContextVoice::getAudioDevices() const + { + Q_ASSERT(this->m_voice); + return this->m_voice->audioDevices(); + } + + /* + * Audio default devices + */ + CAudioDeviceList CContextVoice::getCurrentAudioDevices() const + { + Q_ASSERT(this->m_voice); + CAudioDeviceList devices; + devices.push_back(this->m_currentInputDevice); + devices.push_back(this->m_currentOutputDevice); + return devices; + } + + /* + * Set current device + */ + void CContextVoice::setCurrentAudioDevice(const CAudioDevice &audioDevice) + { + Q_ASSERT(this->m_voice); + Q_ASSERT(audioDevice.getType() != CAudioDevice::Unknown); + this->log(Q_FUNC_INFO, audioDevice.toQString()); + if (audioDevice.getType() == CAudioDevice::InputDevice) + { + this->m_voice->setInputDevice(audioDevice); + this->m_currentInputDevice = audioDevice; + } + else + { + this->m_voice->setOutputDevice(audioDevice); + this->m_currentOutputDevice = audioDevice; + } + } + + /* + * Set volumnes + */ + void CContextVoice::setVolumes(const CComSystem &com1, const CComSystem &com2) + { + Q_ASSERT(this->m_voice); + this->log(Q_FUNC_INFO, com1.toQString(), com2.toQString()); + this->m_voice->setRoomOutputVolume(IVoice::COM1, com1.getVolumeInput()); + this->m_voice->setRoomOutputVolume(IVoice::COM2, com2.getVolumeInput()); + } + + /* + * Set voice rooms + */ + void CContextVoice::setComVoiceRooms(const CVoiceRoom &voiceRoomCom1, const CVoiceRoom &voiceRoomCom2) + { + Q_ASSERT(this->m_voice); + CVoiceRoomList currentRooms = this->m_voice->getComVoiceRoomsWithAudioStatus(); + CVoiceRoom currentRoom1 = currentRooms[0]; + CVoiceRoom currentRoom2 = currentRooms[1]; + if (currentRoom1 != voiceRoomCom1) + { + if (currentRoom1.isValid()) this->m_voice->leaveVoiceRoom(IVoice::COM1); + if (voiceRoomCom1.isValid()) this->m_voice->joinVoiceRoom(IVoice::COM1, voiceRoomCom1); + } + if (currentRoom2 != voiceRoomCom2) + { + if (currentRoom2.isValid()) this->m_voice->leaveVoiceRoom(IVoice::COM2); + if (voiceRoomCom2.isValid()) this->m_voice->joinVoiceRoom(IVoice::COM2, voiceRoomCom2); + } + } + + /* + * Room 1 callsigns + */ + QList CContextVoice::getCom1RoomCallsigns() const + { + Q_ASSERT(this->m_voice); + QSet signs = this->m_voice->getVoiceRoomCallsings(IVoice::COM1); + QList callsigns; + foreach(QString sign, signs) + { + callsigns.append(sign); + } + return callsigns; + } + + /* + * Room 2 callsigns + */ + QList CContextVoice::getCom2RoomCallsigns() const + { + Q_ASSERT(this->m_voice); + QSet signs = this->m_voice->getVoiceRoomCallsings(IVoice::COM2); + QList callsigns; + foreach(QString sign, signs) + { + callsigns.append(sign); + } + return callsigns; + } + + + +} // namespace diff --git a/src/blackcore/context_voice.h b/src/blackcore/context_voice.h new file mode 100644 index 000000000..c0b92f547 --- /dev/null +++ b/src/blackcore/context_voice.h @@ -0,0 +1,147 @@ +/* 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/. */ + +#ifndef BLACKCORE_CONTEXTVOICE_H +#define BLACKCORE_CONTEXTVOICE_H + +#include "blackcore/dbus_server.h" +#include "blackcore/voice_vatlib.h" +#include "blackcore/context_voice_interface.h" +#include "blackmisc/avallclasses.h" +#include "blackmisc/statusmessage.h" +#include "blackmisc/statusmessagelist.h" +#include "blackmisc/nwuserlist.h" +#include +#include +#include +#include +#include +#include +#include + +#define BLACKCORE_CONTEXTVOICE_INTERFACENAME "blackcore.contextvoice" + +namespace BlackCore +{ + class CCoreRuntime; + + /*! + * \brief Network context + */ + class CContextVoice : public IContextVoice + { + // Register by same name, make signals sender independent + // http://dbus.freedesktop.org/doc/dbus-faq.html#idp48032144 + Q_CLASSINFO("D-Bus Interface", BLACKCORE_CONTEXTVOICE_INTERFACENAME) + Q_OBJECT + + public: + + /*! + * \brief With link to server + * \param server + */ + CContextVoice(CCoreRuntime *parent); + + /*! + * \brief Destructor + */ + virtual ~CContextVoice(); + + /*! + * \brief Register myself in DBus + * \param server + */ + void registerWithDBus(CDBusServer *server) + { + server->addObject(IContextVoice::ServicePath(), this); + } + + /*! + * \brief Runtime + * \return + */ + const CCoreRuntime *getRuntime() const + { + return reinterpret_cast(this->parent()); + } + + /*! + * \brief Using local objects? + * \return + */ + virtual bool usingLocalObjects() const { return true; } + + public slots: + /*! + * Get voice rooms for COM1, COM2, but not with the latest status + * \return + */ + virtual BlackMisc::Voice::CVoiceRoomList getComVoiceRooms() const; + + /*! + * Get voice rooms for COM1, COM2: From this connection status + * etc. can be obtained + * \return + */ + virtual BlackMisc::Voice::CVoiceRoomList getComVoiceRoomsWithAudioStatus(); + + /*! + * \brief Set voice rooms + * \param voiceRoomCom1 + * \param voiceRoomCom2 + */ + virtual void setComVoiceRooms(const BlackMisc::Voice::CVoiceRoom &voiceRoomCom1, const BlackMisc::Voice::CVoiceRoom &voiceRoomCom2); + + /*! + * \brief COM1 room user's callsigns + * \return + */ + virtual QList getCom1RoomCallsigns() const; + + /*! + * \brief COM2 room user's callsigns + * \return + */ + virtual QList getCom2RoomCallsigns() const; + + /*! + * Leave all voice rooms + */ + virtual void leaveAllVoiceRooms(); + + /*! + * \brief Audio devices + * \return + */ + virtual BlackMisc::Voice::CAudioDeviceList getAudioDevices() const; + + /*! + * \brief Set current audio device + * \param audioDevice + */ + virtual BlackMisc::Voice::CAudioDeviceList getCurrentAudioDevices() const; + + /*! + * \brief Set current audio device + * \param audioDevice + */ + virtual void setCurrentAudioDevice(const BlackMisc::Voice::CAudioDevice &audioDevice); + + /*! + * \brief Set volumes + * \param com1 + * \param com2 + */ + virtual void setVolumes(const BlackMisc::Aviation::CComSystem &com1, const BlackMisc::Aviation::CComSystem &com2); + + private: + CVoiceVatlib *m_voice; + BlackMisc::Voice::CAudioDevice m_currentInputDevice; + BlackMisc::Voice::CAudioDevice m_currentOutputDevice; + }; +} + +#endif // guard diff --git a/src/blackcore/context_voice_interface.cpp b/src/blackcore/context_voice_interface.cpp new file mode 100644 index 000000000..0fbc86840 --- /dev/null +++ b/src/blackcore/context_voice_interface.cpp @@ -0,0 +1,130 @@ +/* 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 "blackcore/context_voice_interface.h" +#include +#include + +using namespace BlackMisc::Voice; +using namespace BlackMisc::Network; + +namespace BlackCore +{ + + /* + * Constructor for DBus + */ + IContextVoice::IContextVoice(const QString &serviceName, QDBusConnection &connection, QObject *parent) : QObject(parent), m_dBusInterface(0) + { + this->m_dBusInterface = new BlackMisc::CGenericDBusInterface(serviceName , IContextVoice::ServicePath(), IContextVoice::InterfaceName(), connection, this); + this->relaySignals(serviceName, connection); + } + + /* + * Workaround for signals, not working without, but why? + */ + void IContextVoice::relaySignals(const QString & /** serviceName **/, QDBusConnection & /** connection **/) + { + // void + } + + /* + * Leave all voice rooms + */ + void IContextVoice::leaveAllVoiceRooms() + { + this->m_dBusInterface->callDBus(QLatin1Literal("leaveAllVoiceRooms")); + } + + /* + * COM1 users + */ + QList IContextVoice::getCom1RoomCallsigns() const + { + return this->m_dBusInterface->callDBusRet >(QLatin1Literal("getCom1RoomCallsigns")); + } + + /* + * COM2 users + */ + QList IContextVoice::getCom2RoomCallsigns() const + { + return this->m_dBusInterface->callDBusRet >(QLatin1Literal("getCom2RoomCallsigns")); + } + + /* + * Audio devices + */ + CAudioDeviceList IContextVoice::getAudioDevices() const + { + return this->m_dBusInterface->callDBusRet(QLatin1Literal("getAudioDevices")); + } + + /* + * Set current audio device + */ + BlackMisc::Voice::CAudioDeviceList IContextVoice::getCurrentAudioDevices() const + { + return this->m_dBusInterface->callDBusRet(QLatin1Literal("getCurrentAudioDevices")); + } + + /* + * Relay to DBus + */ + CVoiceRoomList IContextVoice::getComVoiceRoomsWithAudioStatus() + { + return this->m_dBusInterface->callDBusRet(QLatin1Literal("getComVoiceRoomsWithAudioStatus")); + } + + /* + * Relay to DBus + */ + CVoiceRoomList IContextVoice::getComVoiceRooms() const + { + return this->m_dBusInterface->callDBusRet(QLatin1Literal("getComVoiceRooms")); + } + + /* + * Set voice rooms + */ + void IContextVoice::setComVoiceRooms(const BlackMisc::Voice::CVoiceRoom &voiceRoomCom1, const BlackMisc::Voice::CVoiceRoom &voiceRoomCom2) + { + this->m_dBusInterface->callDBus(QLatin1Literal("setComVoiceRooms"), voiceRoomCom1, voiceRoomCom2); + } + + /* + * Set current audio device + */ + void IContextVoice::setCurrentAudioDevice(const CAudioDevice &audioDevice) + { + this->m_dBusInterface->callDBus(QLatin1Literal("setCurrentAudioDevice"), audioDevice); + } + + /* + * Volumes, by COM systems + */ + void IContextVoice::setVolumes(const BlackMisc::Aviation::CComSystem &com1, const BlackMisc::Aviation::CComSystem &com2) + { + this->m_dBusInterface->callDBus(QLatin1Literal("setVolumes"), com1, com2); + } + + /* + * Logging + */ + void IContextVoice::log(const QString &method, const QString &m1, const QString &m2, const QString &m3, const QString &m4) const + { + if (m1.isEmpty()) + qDebug() << " LOG: " << method; + else if (m2.isEmpty()) + qDebug() << " LOG: " << method << m1; + else if (m3.isEmpty()) + qDebug() << " LOG: " << method << m1 << m2; + else if (m4.isEmpty()) + qDebug() << " LOG: " << method << m1 << m2 << m3; + else + qDebug() << " LOG: " << method << m1 << m2 << m3 << m4; + } + +} // namespace diff --git a/src/blackcore/context_voice_interface.h b/src/blackcore/context_voice_interface.h new file mode 100644 index 000000000..9c6e0cdb4 --- /dev/null +++ b/src/blackcore/context_voice_interface.h @@ -0,0 +1,169 @@ +/* Copyright (C) 2013x 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/. */ + +#ifndef BLACKCORE_CONTEXTVOICE_INTERFACE_H +#define BLACKCORE_CONTEXTVOICE_INTERFACE_H + +#include "blackmisc/genericdbusinterface.h" +#include "blackmisc/vaudiodevicelist.h" +#include "blackmisc/vvoiceroomlist.h" +#include "blackmisc/nwuserlist.h" +#include "blackmisc/aviocomsystem.h" +#include "blackcore/voice_vatlib.h" +#include +#include + +#define BLACKCORE_CONTEXTVOICE_INTERFACENAME "blackcore.contextvoice" +#define BLACKCORE_CONTEXTVOICE_SERVICEPATH "/voice" + +// SERVICENAME must contain at least one ".", otherwise generation fails +// as this is interpreted in the way comain.somename + +namespace BlackCore +{ + + /*! + * \brief The IContextVoice class + */ + class IContextVoice : public QObject + { + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", BLACKCORE_CONTEXTVOICE_INTERFACENAME) + + public: + /*! + * \brief Service name + * \return + */ + static const QString &InterfaceName() + { + static QString s(BLACKCORE_CONTEXTVOICE_INTERFACENAME); + return s; + } + + /*! + * \brief Service path + * \return + */ + static const QString &ServicePath() + { + static QString s(BLACKCORE_CONTEXTVOICE_SERVICEPATH); + return s; + } + + /*! + * \brief DBus version constructor + * \param serviceName + * \param connection + * \param parent + */ + IContextVoice(const QString &serviceName, QDBusConnection &connection, QObject *parent = 0); + + /*! + * Destructor + */ + ~IContextVoice() {} + + /*! + * \brief Using local objects? + * \return + */ + virtual bool usingLocalObjects() const { return false; } + + private: + BlackMisc::CGenericDBusInterface *m_dBusInterface; + + /*! + * Relay connection signals to local signals + * No idea why this has to be wired and is not done automatically + * \param connection + */ + void relaySignals(const QString &serviceName, QDBusConnection &connection); + + protected: + /*! + * \brief IContextVoice + * \param parent + */ + IContextVoice(QObject *parent = nullptr) : QObject(parent), m_dBusInterface(nullptr) {} + + /*! + * \brief Helper for logging, likely to be removed / changed + * \param method + * \param m1 + * \param m2 + * \param m3 + * \param m4 + */ + void log(const QString &method, const QString &m1 = "", const QString &m2 = "", const QString &m3 = "", const QString &m4 = "") const; + + public slots: + + /*! + * Get voice rooms for COM1, COM2: + * From this connection status etc. can be obtained + * \return + */ + virtual BlackMisc::Voice::CVoiceRoomList getComVoiceRoomsWithAudioStatus(); + + /*! + * Get voice rooms for COM1, COM2, but not with the latest status + * \return + */ + virtual BlackMisc::Voice::CVoiceRoomList getComVoiceRooms() const; + + /*! + * \brief Set voice rooms + * \param voiceRoomCom1 + * \param voiceRoomCom2 + */ + virtual void setComVoiceRooms(const BlackMisc::Voice::CVoiceRoom &voiceRoomCom1, const BlackMisc::Voice::CVoiceRoom &voiceRoomCom2); + + /*! + * Leave all voice rooms + */ + virtual void leaveAllVoiceRooms(); + + /*! + * \brief COM1 room users + * \return + */ + virtual QList getCom1RoomCallsigns() const; + + /*! + * \brief COM2 room users + * \return + */ + virtual QList getCom2RoomCallsigns() const; + + /*! + * \brief Audio devices + * \return + */ + virtual BlackMisc::Voice::CAudioDeviceList getAudioDevices() const; + + /*! + * \brief Set current audio device + * \param audioDevice + */ + virtual BlackMisc::Voice::CAudioDeviceList getCurrentAudioDevices() const; + + /*! + * \brief Set current audio device + * \param audioDevice + */ + virtual void setCurrentAudioDevice(const BlackMisc::Voice::CAudioDevice &audioDevice); + + /*! + * \brief Set volumnes + * \param com1 + * \param com2 + */ + virtual void setVolumes(const BlackMisc::Aviation::CComSystem &com1, const BlackMisc::Aviation::CComSystem &com2); + + }; +} + +#endif // guard diff --git a/src/blackcore/voiceclient.cpp b/src/blackcore/voice.cpp similarity index 60% rename from src/blackcore/voiceclient.cpp rename to src/blackcore/voice.cpp index 011e595ed..527c13e61 100644 --- a/src/blackcore/voiceclient.cpp +++ b/src/blackcore/voice.cpp @@ -3,13 +3,13 @@ * 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" +#include "voice.h" namespace BlackCore { - IVoiceClient::IVoiceClient(QObject *parent) - : QObject(parent) + IVoice::IVoice(QObject *parent) : QObject(parent) { - qRegisterMetaType("IVoiceClient::ComUnit"); + // http://qt-project.org/forums/viewthread/27495 + qRegisterMetaType("IVoice::ComUnit"); } } diff --git a/src/blackcore/voiceclient.h b/src/blackcore/voice.h similarity index 59% rename from src/blackcore/voiceclient.h rename to src/blackcore/voice.h index 637d28bcf..ff759be19 100644 --- a/src/blackcore/voiceclient.h +++ b/src/blackcore/voice.h @@ -8,12 +8,12 @@ #include "../blackmisc/context.h" #include "../blackmisc/avcallsign.h" -#include "../blackmisc/vvoiceroom.h" -#include "../blackmisc/vaudiodevice.h" - +#include "../blackmisc/nwuserlist.h" +#include "../blackmisc/vvoiceroomlist.h" +#include "../blackmisc/vaudiodevicelist.h" #include - #include +#include #include #include @@ -27,7 +27,7 @@ namespace BlackCore * must be a Qt::QueuedConnection. * Reason: IVoiceClient implementations are not re-entrant. */ - class IVoiceClient : public QObject + class IVoice : public QObject { /* TODOS: @@ -43,90 +43,44 @@ namespace BlackCore * \brief Default constructor with parent * \param parent */ - IVoiceClient(QObject *parent = 0); + IVoice(QObject *parent = nullptr); public: //! ComUnit /*! IVoiceClient currently supports two different com units */ - enum ComUnit { + enum ComUnit + { COM1 = 0, /*!< ComUnit 1 */ COM2 /*!< ComUnit 2 */ }; //! Virtual destructor. - virtual ~IVoiceClient() {} + virtual ~IVoice() {} - //! roomUserList /*! - \return A list of users currently connected to the voice room - */ - virtual const QSet roomUserList(const ComUnit comUnit) = 0; + * \brief Own aircraft's callsign + * \param callsign + */ + virtual void setMyAircraftCallsign(const BlackMisc::Aviation::CCallsign &callsign) = 0; - //! audioInputDevices /*! - \return A list of available input devices - */ - virtual const QList & audioInputDevices() const = 0; + * \brief Audio devices + * \return + */ + virtual const BlackMisc::Voice::CAudioDeviceList &audioDevices() const = 0; - //! audioOutputDevices /*! - \return A list of available output devices - */ - virtual const QList & audioOutputDevices() const = 0; + * \brief Default input device + * \return + */ + virtual const BlackMisc::Voice::CAudioDevice defaultAudioInputDevice() 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; - - //! enableAudio - /*! - \brief After you have joined a voice room, you must enable audio output. - \param comUnit - */ - virtual void enableAudio(const ComUnit comUnit) = 0; - - //! 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; - + * \brief Default output device + * \return + */ + virtual const BlackMisc::Voice::CAudioDevice defaultAudioOutputDevice() const = 0; /************************************************ * SETUP TESTS @@ -164,44 +118,75 @@ namespace BlackCore public slots: - //! setCallsign + //! setInputDevice /*! - \param callsign + \param input device */ - virtual void setCallsign(const BlackMisc::Aviation::CCallsign &callsign) = 0; + virtual void setInputDevice(const BlackMisc::Voice::CAudioDevice &device) = 0; - //! joinVoiceRoom + //! setOutputDevice /*! - \param comUnit - \param voiceRoom + \param output device */ + virtual void setOutputDevice(const BlackMisc::Voice::CAudioDevice &device) = 0; + + /*! + * Get COM1/2 voice rooms, which then allows to retrieve information + * such as connection status etc. + * \return + */ + virtual BlackMisc::Voice::CVoiceRoomList getComVoiceRoomsWithAudioStatus() = 0; + + /*! + * Get COM1/2 voice rooms, const and with no status update + * \return + */ + virtual BlackMisc::Voice::CVoiceRoomList getComVoiceRooms() const = 0; + + /*! + * \brief Join voice room + * \param comUnit + * \param voiceRoom + */ virtual void joinVoiceRoom(const ComUnit comUnit, const BlackMisc::Voice::CVoiceRoom &voiceRoom) = 0; - //! leaveVoiceRoom /*! - \param comUnit - */ + * \brief Leave voice room + * \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; + * \brief Leave all voice rooms + */ + virtual void leaveAllVoiceRooms() = 0; - //! startTransmitting /*! - \param comUnit - */ + * \brief Set room output volume + * \param comUnit + * \param volumne + */ + virtual void setRoomOutputVolume(const ComUnit comUnit, const int32_t volumne) = 0; + + /*! + * \brief Start transmitting + * \param comUnit + */ virtual void startTransmitting(const ComUnit comUnit) = 0; - //! stopTransmitting /*! - \param comUnit - */ + * \brief Stop transmitting + * \param comUnit + */ virtual void stopTransmitting(const ComUnit comUnit) = 0; + /*! + * \brief Get voice room callsings + * \param comUnit + * \return + */ + virtual QSet getVoiceRoomCallsings(const ComUnit comUnit) const = 0; + signals: // Signals regarding the voice server connection void notConnected(const ComUnit comUnit); @@ -228,14 +213,10 @@ namespace BlackCore // non protocol related signals void exception(const QString &message, bool fatal = false); // let remote places know there was an exception - - protected: - - }; } // namespace BlackCore -Q_DECLARE_METATYPE(BlackCore::IVoiceClient::ComUnit) +Q_DECLARE_METATYPE(BlackCore::IVoice::ComUnit) -#endif // BLACKCORE_VOICE_H +#endif // guard diff --git a/src/blackcore/voice_vatlib.cpp b/src/blackcore/voice_vatlib.cpp new file mode 100644 index 000000000..ad7ec9b23 --- /dev/null +++ b/src/blackcore/voice_vatlib.cpp @@ -0,0 +1,621 @@ +/* Copyright (C) 2013 VATSIM Community / authors + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "voice_vatlib.h" +#include +#include + +using namespace BlackMisc::Voice; +using namespace BlackMisc::Aviation; + +namespace BlackCore +{ + /* + * Constructor + */ + CVoiceVatlib::CVoiceVatlib(QObject *parent) : + IVoice(parent), + m_voice(Cvatlib_Voice_Simple::Create()), + m_aircraftCallsign(), m_voiceRooms(), m_devices(), + m_keyboardPtt(new CKeyboard(nullptr)), + m_pushToTalk(false), + m_inputSquelch(-1), + m_micTestResult(Cvatlib_Voice_Simple::agc_Ok), + m_queryUserRoomIndex(-1) + { + try + { + m_voice->Setup(true, 3290, 2, 1, onRoomStatusUpdate, this); + m_voice->GetInputDevices(onInputHardwareDeviceReceived, this); + m_voice->GetOutputDevices(onOutputHardwareDeviceReceived, this); + + // TODO: read audio device settings here and init with the same devices + // If not settings are there or it is the first run, use the default one + // TODO: KB, setting this kind of default device results in an error + // setInputDevice(defaultAudioInputDevice()); + // setOutputDevice(defaultAudioOutputDevice()); + + connect(this, &CVoiceVatlib::userJoinedLeft, this, &CVoiceVatlib::onUserJoinedLeft, Qt::QueuedConnection); + connect(this, &CVoiceVatlib::connected, this, &CVoiceVatlib::onUserJoinedLeft, Qt::QueuedConnection); + + this->m_voiceRooms.push_back(CVoiceRoom()); // COM1 + this->m_voiceRooms.push_back(CVoiceRoom()); // COM2 + + // do processing + this->startTimer(100); + + // as last thing enable keyboard handling + this->m_keyboardPtt.data()->s_voice = this; + } + catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } + } + + /* + * Destructor + */ + CVoiceVatlib::~CVoiceVatlib() {} + + /* + * Devices + */ + const BlackMisc::Voice::CAudioDeviceList &CVoiceVatlib::audioDevices() const + { + return m_devices; + } + + /* + * Default input device + */ + const BlackMisc::Voice::CAudioDevice CVoiceVatlib::defaultAudioInputDevice() const + { + // Constructor creates already a default device + return BlackMisc::Voice::CAudioDevice(BlackMisc::Voice::CAudioDevice::InputDevice, BlackMisc::Voice::CAudioDevice::defaultDevice(), "default"); + } + + /* + * Default output device + */ + const BlackMisc::Voice::CAudioDevice CVoiceVatlib::defaultAudioOutputDevice() const + { + // Constructor creates already a default device + return BlackMisc::Voice::CAudioDevice(BlackMisc::Voice::CAudioDevice::OutputDevice, BlackMisc::Voice::CAudioDevice::defaultDevice(), "default"); + } + + /* + * Set input device + */ + void CVoiceVatlib::setInputDevice(const BlackMisc::Voice::CAudioDevice &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 + { + if (!m_voice->SetInputDevice(device.getIndex())) + { + qWarning() << "SetInputDevice() failed"; + } + if (!m_voice->IsInputDeviceAlive()) + { + qWarning() << "Input device hit a fatal error"; + } + } + catch (...) { exceptionDispatcher(Q_FUNC_INFO); } + } + + /* + * Set output device + */ + void CVoiceVatlib::setOutputDevice(const BlackMisc::Voice::CAudioDevice &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 + { + m_voice->SetOutputDevice(0, device.getIndex()); + if (!m_voice->IsOutputDeviceAlive(0)) + { + qWarning() << "Input device hit a fatal error"; + } + } + catch (...) { exceptionDispatcher(Q_FUNC_INFO); } + } + + /* + * Get voice rooms, with the latest status updated + */ + BlackMisc::Voice::CVoiceRoomList CVoiceVatlib::getComVoiceRoomsWithAudioStatus() + { + Q_ASSERT_X(m_voiceRooms.size() == 2, "CVoiceClientVatlib", "Wrong numer of COM voice rooms"); + + if (m_voice->IsValid() && m_voice->IsSetup()) + { + // valid state, update + CVoiceRoom com1 = this->m_voiceRooms[0]; + CVoiceRoom com2 = this->m_voiceRooms[1]; + com1.setConnected(m_voice->IsRoomConnected(static_cast(COM1))); + com2.setConnected(m_voice->IsRoomConnected(static_cast(COM2))); + com1.setAudioPlaying(com1.isConnected() ? m_voice->IsAudioPlaying(static_cast(COM1)) : false); + com2.setAudioPlaying(com2.isConnected() ? m_voice->IsAudioPlaying(static_cast(COM2)) : false); + this->setVoiceRoomForUnit(COM1, com1); + this->setVoiceRoomForUnit(COM2, com2); + } + return this->m_voiceRooms; + } + + /* + * Enable audio + */ + void CVoiceVatlib::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); } + } + + /* + * Squelch test + */ + void CVoiceVatlib::runSquelchTest() + { + Q_ASSERT_X(m_voice->IsValid() && m_voice->IsSetup(), "CVoiceClientVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); + + try + { + m_voice->BeginFindSquelch(); + } + catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } + + // Start the timer only if no exception was thrown before + QTimer::singleShot(5000, this, SLOT(onEndFindSquelch())); + } + + void CVoiceVatlib::runMicTest() + { + Q_ASSERT_X(m_voice->IsValid() && m_voice->IsSetup(), "CVoiceClientVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); + + try + { + m_voice->BeginMicTest(); + } + catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } + + // Start the timer only if no exception was thrown before + QTimer::singleShot(5000, this, SLOT(onEndMicTest())); + } + + float CVoiceVatlib::inputSquelch() const + { + return m_inputSquelch; + } + + int32_t CVoiceVatlib::micTestResult() const + { + return m_micTestResult; + } + + + QString CVoiceVatlib::micTestResultAsString() const + { + 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; + } + + /* + * Callsign + */ + void CVoiceVatlib::setMyAircraftCallsign(const BlackMisc::Aviation::CCallsign &callsign) + { + m_aircraftCallsign = callsign; + } + + /* + * Voice room + */ + void CVoiceVatlib::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 + { + CVoiceRoom vr = this->voiceRoomForUnit(comUnit); + if (vr.isConnected()) return; // already joined + vr = voiceRoom; + vr.setConnected(true); + this->setVoiceRoomForUnit(comUnit, vr); + QString serverSpec = voiceRoom.getVoiceRoomUrl(); + m_voice->JoinRoom(static_cast(comUnit), m_aircraftCallsign.toQString().toLatin1().constData(), serverSpec.toLatin1().constData()); + } + catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } + } + + /* + * Leave a room + */ + void CVoiceVatlib::leaveVoiceRoom(const ComUnit comUnit) + { + CVoiceRoom vr = this->voiceRoomForUnit(comUnit); + if (!vr.isConnected()) return; + + 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)); + vr.setConnected(false); + this->setVoiceRoomForUnit(comUnit, vr); + } + catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } + } + + /* + * Leave all voice rooms + */ + void CVoiceVatlib::leaveAllVoiceRooms() + { + this->leaveVoiceRoom(COM1); + this->leaveVoiceRoom(COM2); + } + + /* + * Room output volume as per COM unit + */ + void CVoiceVatlib::setRoomOutputVolume(const ComUnit comUnit, const 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); } + } + + /* + * Start transmitting + */ + void CVoiceVatlib::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); } + } + + /* + * Start transmitting + */ + void CVoiceVatlib::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); } + } + + /* + * Change room status + */ + void CVoiceVatlib::changeRoomStatus(ComUnit comUnit, Cvatlib_Voice_Simple::roomStatusUpdate upd) + { + CVoiceRoom vr = this->voiceRoomForUnit(comUnit); + + switch (upd) + { + case Cvatlib_Voice_Simple::roomStatusUpdate_JoinSuccess: + enableAudio(comUnit); + vr.setConnected(true); + this->setVoiceRoomForUnit(comUnit, vr); + emit connected(comUnit); + break; + case Cvatlib_Voice_Simple::roomStatusUpdate_JoinFail: + vr.setConnected(false); + this->setVoiceRoomForUnit(comUnit, vr); + emit connectionFailed(comUnit); + break; + case Cvatlib_Voice_Simple::roomStatusUpdate_UnexpectedDisconnectOrKicked: + vr.setConnected(false); + this->setVoiceRoomForUnit(comUnit, vr); + emit kicked(comUnit); + break; + case Cvatlib_Voice_Simple::roomStatusUpdate_LeaveComplete: + m_voiceRoomCallsigns.clear(); + emit disconnected(comUnit); + break; + case Cvatlib_Voice_Simple::roomStatusUpdate_UserJoinsLeaves: + // FIXME: We cannot call GetRoomUserList because vatlib is not reentrent safe. + emit userJoinedLeft(comUnit); + break; + case Cvatlib_Voice_Simple::roomStatusUpdate_RoomAudioStarted: + emit audioStarted(comUnit); + break; + case Cvatlib_Voice_Simple::roomStatusUpdate_RoomAudioStopped: + emit audioStopped(comUnit); + break; + case Cvatlib_Voice_Simple::roomStatusUpdate_AudioStarted: + emit globalAudioStarted(); + break; + case Cvatlib_Voice_Simple::roomStatusUpdate_AudioStopped: + emit globalAudioStopped(); + break; + default: + break; + } + } + + /* + * Process voice handling + */ + void CVoiceVatlib::timerEvent(QTimerEvent *) + { + Q_ASSERT_X(m_voice->IsValid() && m_voice->IsSetup(), "CVoiceClientVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); + + try + { + this->m_voice->DoProcessing(); + this->handlePushToTalk(); + } + catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } + } + + /* + * Find squelch + */ + void CVoiceVatlib::onEndFindSquelch() + { + Q_ASSERT_X(m_voice->IsValid() && m_voice->IsSetup(), "CVoiceClientVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); + + try + { + m_voice->EndFindSquelch(); + m_inputSquelch = m_voice->GetInputSquelch(); + emit squelchTestFinished(); + } + catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } + } + + void CVoiceVatlib::onEndMicTest() + { + Q_ASSERT_X(m_voice->IsValid() && m_voice->IsSetup(), "CVoiceClientVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); + + try + { + m_micTestResult = m_voice->EndMicTest(); + emit micTestFinished(); + } + catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } + } + + /* + * User joined / left room + */ + void CVoiceVatlib::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 + { + // Paranoia... + if (!m_voice->IsRoomConnected(static_cast(comUnit))) + return; + + // Store the room index for the slot. + m_queryUserRoomIndex = static_cast(comUnit); + m_voice->GetRoomUserList(static_cast(comUnit), onRoomUserReceived, this); + m_queryUserRoomIndex = -1; + + QSet temporaryUsers; + + foreach(QString callsign, m_voiceRoomCallsigns.value(comUnit)) + { + if (m_voiceRoomCallsignsUpdate.contains(callsign)) + { + // The user is still there. + temporaryUsers.insert(callsign); + } + else + { + // He is has probably left + emit userLeftRoom(callsign); + } + } + + foreach(QString callsign, m_voiceRoomCallsignsUpdate) + { + if (m_voiceRoomCallsigns.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_voiceRoomCallsignsUpdate.clear(); + } + catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } + } + + /********************************** * * * * * * * * * * * * * * * * * * * ************************************/ + /********************************** shimlib callbacks ************************************/ + /********************************** * * * * * * * * * * * * * * * * * * * ************************************/ + + /* + * Cast + */ + CVoiceVatlib *cbvar_cast_voice(void *cbvar) + { + return static_cast(cbvar); + } + + /* + * Room status update + */ + void CVoiceVatlib::onRoomStatusUpdate(Cvatlib_Voice_Simple *obj, Cvatlib_Voice_Simple::roomStatusUpdate upd, int32_t roomIndex, void *cbVar) + { + Q_UNUSED(obj) + ComUnit comUnit = static_cast(roomIndex); + CVoiceVatlib *voiceClientVatlib = cbvar_cast_voice(cbVar); + voiceClientVatlib->changeRoomStatus(comUnit, upd); + } + + /* + * Room user received + */ + void CVoiceVatlib::onRoomUserReceived(Cvatlib_Voice_Simple *obj, const char *name, void *cbVar) + { + Q_UNUSED(obj) + + // sanity check + QString callsign = QString(name); + if (callsign.isEmpty()) return; + + // add user + CVoiceVatlib *voiceClientVatlib = cbvar_cast_voice(cbVar); + ComUnit comUnit = static_cast(voiceClientVatlib->queryUserRoomIndex()); + + // add user + voiceClientVatlib->addUserInRoom(comUnit, callsign); + } + + /* + * Input hardware received + */ + void CVoiceVatlib::onInputHardwareDeviceReceived(Cvatlib_Voice_Simple *obj, const char *name, void *cbVar) + { + Q_UNUSED(obj) + BlackMisc::Voice::CAudioDevice inputDevice(BlackMisc::Voice::CAudioDevice::InputDevice, cbvar_cast_voice(cbVar)->m_devices.count(BlackMisc::Voice::CAudioDevice::InputDevice), QString(name)); + cbvar_cast_voice(cbVar)->m_devices.push_back(inputDevice); + } + + /* + * Output hardware received + */ + void CVoiceVatlib::onOutputHardwareDeviceReceived(Cvatlib_Voice_Simple *obj, const char *name, void *cbVar) + { + Q_UNUSED(obj) + BlackMisc::Voice::CAudioDevice outputDevice(BlackMisc::Voice::CAudioDevice::OutputDevice, cbvar_cast_voice(cbVar)->m_devices.count(BlackMisc::Voice::CAudioDevice::OutputDevice), QString(name)); + cbvar_cast_voice(cbVar)->m_devices.push_back(outputDevice); + } + + /* + * COM unit to Voice room + */ + CVoiceRoom CVoiceVatlib::voiceRoomForUnit(const IVoice::ComUnit comUnit) const + { + return (comUnit == COM1) ? this->m_voiceRooms[0] : this->m_voiceRooms[1]; + } + + /* + * Voice room for respectice COM unit + */ + void CVoiceVatlib::setVoiceRoomForUnit(const IVoice::ComUnit comUnit, const CVoiceRoom &voiceRoom) + { + this->m_voiceRooms[comUnit == COM1 ? 0 : 1] = voiceRoom; + } + + /* + * User in room + */ + void CVoiceVatlib::addUserInRoom(const ComUnit /** comUnit **/, const QString &callsign) + { + m_voiceRoomCallsignsUpdate.insert(callsign); + } + + /* + * Forward exception as signal + */ + void CVoiceVatlib::exceptionDispatcher(const char *caller) + { + QString msg("Caller: "); + msg.append(caller).append(" ").append("Exception: "); + try + { + throw; + } + catch (const NetworkNotConnectedException &e) + { + // this could be caused by a race condition during normal operation, so not an error + msg.append("NetworkNotConnectedException").append(" ").append(e.what()); + emit this->exception(msg); + qDebug() << "NetworkNotConnectedException caught in " << caller << "\n" << e.what(); + } + catch (const VatlibException &e) + { + msg.append("VatlibException").append(" ").append(e.what()); + emit this->exception(msg, true); + qFatal("VatlibException caught in %s\n%s", caller, e.what()); + } + catch (const std::exception &e) + { + msg.append("std::exception").append(" ").append(e.what()); + emit this->exception(msg, true); + qFatal("std::exception caught in %s\n%s", caller, e.what()); + } + catch (...) + { + msg.append("unknown exception"); + emit this->exception(msg, true); + qFatal("Unknown exception caught in %s", caller); + } + } + +} // namespace diff --git a/src/blackcore/voice_vatlib.h b/src/blackcore/voice_vatlib.h new file mode 100644 index 000000000..dc6625f83 --- /dev/null +++ b/src/blackcore/voice_vatlib.h @@ -0,0 +1,283 @@ +/* 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/. */ + +#ifndef BLACKCORE_VOICE_VATLIB_H +#define BLACKCORE_VOICE_VATLIB_H + +#include "voice.h" +#include "../blackmisc/vaudiodevicelist.h" +#include "../blackmisc/nwuserlist.h" +#include +#include +#include +#include +#include + +namespace BlackCore +{ + /*! + * Vatlib implementation of the IVoiceClient interface. + */ + class CVoiceVatlib : public IVoice + { + Q_OBJECT + + public: + /*! + * \brief Constructor + * \param parent + */ + CVoiceVatlib(QObject *parent = nullptr); + + /*! + * \brief Destructor + */ + virtual ~CVoiceVatlib(); + + // Hardware devices + // TODO: Vatlib supports multiple output devices. That basically means, you could connect + // to different voice rooms and send their audio to different devices, e.g. ATIS to loudspeakers + // and ATC to headspeakers. Is not important to implement that now, if ever. + virtual const BlackMisc::Voice::CAudioDeviceList &audioDevices() const ; + virtual const BlackMisc::Voice::CAudioDevice defaultAudioInputDevice() const; + virtual const BlackMisc::Voice::CAudioDevice defaultAudioOutputDevice() const; + virtual void setInputDevice(const BlackMisc::Voice::CAudioDevice &device); + virtual void setOutputDevice(const BlackMisc::Voice::CAudioDevice &device); + + /************************************************ + * SETUP TESTS + * *********************************************/ + + // Mic tests + virtual void runSquelchTest(); + virtual void runMicTest(); + + virtual float inputSquelch() const; + virtual int32_t micTestResult() const; + virtual QString micTestResultAsString() const; + + + public slots: + /*! + * \brief ATC station callsign, used for voice room designator + * \param callsign + */ + virtual void setMyAircraftCallsign(const BlackMisc::Aviation::CCallsign &callsign); + + /*! + * \brief Join a given voice room + * \param comUnit + * \param voiceRoom + */ + virtual void joinVoiceRoom(const ComUnit comUnit, const BlackMisc::Voice::CVoiceRoom &voiceRoom); + + /*! + * \brief Leave voice room + * \param comUnit + */ + virtual void leaveVoiceRoom(const ComUnit comUnit); + + /*! + * \brief Leave all voice rooms + */ + virtual void leaveAllVoiceRooms(); + + /*! + * \brief Room output volume as per COM unit + * \param comUnit + * \param volumne + */ + virtual void setRoomOutputVolume(const ComUnit comUnit, const int32_t volumne); + + /*! + * \brief Start transmitting ("talk") + * \param comUnit + */ + virtual void startTransmitting(const ComUnit comUnit); + + /*! + * \brief Stop transmitting ("talk") + * \param comUnit + */ + virtual void stopTransmitting(const ComUnit comUnit); + + /*! + * Get COM1/2 voice rooms, which then allows to retrieve information + * such as connection status etc. + * \return + */ + virtual BlackMisc::Voice::CVoiceRoomList getComVoiceRoomsWithAudioStatus(); + + /*! + * \brief Voice rooms, const version with no updates + * \return + */ + virtual BlackMisc::Voice::CVoiceRoomList getComVoiceRooms() const + { + return this->m_voiceRooms; + } + + /*! + * \brief Get voice room callsings + * \param comUnit + * \return + */ + virtual QSet getVoiceRoomCallsings(const ComUnit comUnit) const + { + if (!this->m_voiceRoomCallsigns.contains(comUnit)) return QSet(); + return this->m_voiceRoomCallsigns[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;} + + void changeRoomStatus(ComUnit comUnit, Cvatlib_Voice_Simple::roomStatusUpdate upd); + + signals: + + /*! + * \brief User joined or left + * \param comUnit + */ + void userJoinedLeft(const ComUnit comUnit); + + protected: // QObject overrides + + /*! + * \brief Process voice lib + */ + virtual void timerEvent(QTimerEvent *); + + private slots: + // slots for Mic tests + void onEndFindSquelch(); + void onEndMicTest(); + + // slot to handle users + void onUserJoinedLeft(const ComUnit comUnit); + + private: + + // shimlib callbacks + 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); + + BlackMisc::Voice::CVoiceRoom voiceRoomForUnit(const ComUnit comUnit) const; + void setVoiceRoomForUnit(const IVoice::ComUnit comUnit, const BlackMisc::Voice::CVoiceRoom &voiceRoom); + void addUserInRoom(const ComUnit comUnit, const QString &callsign); + void removeUserFromRoom(const ComUnit comUnit, const QString &callsign); + void exceptionDispatcher(const char *caller); + void enableAudio(const ComUnit comUnit); + void handlePushToTalk(); + + /*! + * \brief Deleter + */ + struct Cvatlib_Voice_Simple_Deleter + { + /*! + * \brief Cleanup + * \param pointer + */ + static inline void cleanup(Cvatlib_Voice_Simple *pointer) + { + pointer->Destroy(); + } + }; + +#ifdef Q_OS_WIN + + /*! + * \brief Keyboard PTT handling class + */ + class CKeyboard + { + public: + // Keyboard hook + static HHOOK s_keyboardHook; + static CVoiceVatlib *s_voice; + static LRESULT CALLBACK keyboardProcedure(int nCode, WPARAM wParam, LPARAM lParam); + + /*! + * \brief Constructor, keyboard handling + */ + CKeyboard(CVoiceVatlib *vatlib) + { + CVoiceVatlib::CKeyboard::s_voice = vatlib; + CVoiceVatlib::CKeyboard::s_keyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, CVoiceVatlib::CKeyboard::keyboardProcedure, GetModuleHandle(NULL), 0); + } + + /*! + * Destructor + */ + ~CKeyboard() + { + if (!CVoiceVatlib::CKeyboard::s_keyboardHook) return; + UnhookWindowsHookEx(CVoiceVatlib::CKeyboard::s_keyboardHook); + CVoiceVatlib::CKeyboard::s_keyboardHook = nullptr; + CVoiceVatlib::CKeyboard::s_voice = nullptr; + } + }; + +#else + + /*! + * \brief Keyboard PTT handling class + */ + class CKeyboard + { + public: + static CVoiceVatlib *s_voice; + + /*! + * \brief Constructor, keyboard handling + */ + CKeyboard(CVoiceVatlib *vatlib) + { + CVoiceVatlib::CKeyboard::s_voice = vatlib; + } + + /*! + * Destructor + */ + ~CKeyboard() + { + // void + } + }; + +#endif + + QScopedPointer m_voice; + BlackMisc::Aviation::CCallsign m_aircraftCallsign; /*!< own callsign to join voice rooms */ + BlackMisc::Voice::CVoiceRoomList m_voiceRooms; + BlackMisc::Voice::CAudioDeviceList m_devices; /*!< in and output devices */ + QScopedPointer m_keyboardPtt; /*!< handler for PTT */ + bool m_pushToTalk; /*!< flag, PTT pressed */ + float m_inputSquelch; + Cvatlib_Voice_Simple::agc m_micTestResult; + + typedef QMap> TMapRoomCallsigns; + TMapRoomCallsigns m_voiceRoomCallsigns; + QSet m_voiceRoomCallsignsUpdate; + + // Need to keep the roomIndex because GetRoomUserList does not specifiy it + // KB: I would remove this approach, it is potentially unsafe + // Maybe just use 2 "wrapper" callbacks, which then set explicitly the voice room (it is only 2 methods) + int32_t m_queryUserRoomIndex; + + }; + +} // namespace + +#endif // guard diff --git a/src/blackcore/voice_vatlib_ptt.cpp b/src/blackcore/voice_vatlib_ptt.cpp new file mode 100644 index 000000000..ba573ad3f --- /dev/null +++ b/src/blackcore/voice_vatlib_ptt.cpp @@ -0,0 +1,74 @@ +/* Copyright (C) 2013 VATSIM Community / authors + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "voice_vatlib.h" + +using namespace BlackMisc::Voice; + +namespace BlackCore +{ + + CVoiceVatlib *CVoiceVatlib::CKeyboard::s_voice = nullptr; + +#ifdef Q_OS_WIN + + HHOOK CVoiceVatlib::CKeyboard::s_keyboardHook = nullptr; + + /* + * Keyboard handling itself + */ + LRESULT CALLBACK CVoiceVatlib::CKeyboard::keyboardProcedure(int nCode, WPARAM wParam, LPARAM /** lParam **/) + { + // precheck + if (!CVoiceVatlib::CKeyboard::s_keyboardHook || !CVoiceVatlib::CKeyboard::s_voice) return false; + + // Check for a key down press + if (nCode == HC_ACTION) + { + // http://msdn.microsoft.com/en-us/library/windows/desktop/ms644967(v=vs.85).aspx + // http://stackoverflow.com/questions/18917716/windows-how-to-query-state-of-modifier-keys-within-low-level-keyboard-hook + if (wParam == WM_KEYDOWN) + { + // KBDLLHOOKSTRUCT *pKeyboard = (KBDLLHOOKSTRUCT *)lParam; + // CTRL + if (GetAsyncKeyState(VK_CONTROL) & 0x8000) CVoiceVatlib::CKeyboard::s_voice->m_pushToTalk = true; + } + else if (wParam == WM_KEYUP) + { + // KBDLLHOOKSTRUCT *pKeyboard = (KBDLLHOOKSTRUCT *)lParam; + if (GetAsyncKeyState(VK_CONTROL) & 0x8000) CVoiceVatlib::CKeyboard::s_voice->m_pushToTalk = false; + } + } + return false; // not processed flag + } + + /* + * Handle PTT + */ + void CVoiceVatlib::handlePushToTalk() + { + if (!this->m_voice) return; + CVoiceRoomList rooms = this->getComVoiceRoomsWithAudioStatus(); + CVoiceRoom room1 = rooms[0]; + CVoiceRoom room2 = rooms[1]; + if (room1.isConnected()) + { + if (this->m_pushToTalk && !this->m_voice->IsAudioPlaying(IVoice::COM1)) + this->startTransmitting(IVoice::COM1); + else if (!this->m_pushToTalk) + this->stopTransmitting(IVoice::COM1); + } + if (room2.isConnected()) + { + if (this->m_pushToTalk && !this->m_voice->IsAudioPlaying(IVoice::COM2)) + this->startTransmitting(IVoice::COM2); + else if (!this->m_pushToTalk) + this->stopTransmitting(IVoice::COM2); + } + } + +#endif + +} // namespace diff --git a/src/blackcore/voiceclient_vatlib.cpp b/src/blackcore/voiceclient_vatlib.cpp deleted file mode 100644 index a2e301bd1..000000000 --- a/src/blackcore/voiceclient_vatlib.cpp +++ /dev/null @@ -1,513 +0,0 @@ -/* Copyright (C) 2013 VATSIM Community / authors - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "voiceclient_vatlib.h" - -#include -#include - -namespace BlackCore -{ - CVoiceClientVatlib::CVoiceClientVatlib(QObject *parent) : - IVoiceClient(parent), - m_voice(Cvatlib_Voice_Simple::Create()), - m_inputSquelch(-1), - m_micTestResult(Cvatlib_Voice_Simple::agc_Ok), - m_queryUserRoomIndex(-1) - { - try - { - m_voice->Setup(true, 3290, 2, 1, onRoomStatusUpdate, this); - m_voice->GetInputDevices(onInputHardwareDeviceReceived, this); - m_voice->GetOutputDevices(onOutputHardwareDeviceReceived, this); - - // TODO: read audio device settings here and init with the same devices - // If not settings are there or it is the first run, use the default one - 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); } - } - - CVoiceClientVatlib::~CVoiceClientVatlib() - { - } - - const QSet CVoiceClientVatlib::roomUserList(const ComUnit comUnit) - { - /**/ - return m_voiceRoomsUsers.value(comUnit); - } - - const QList &CVoiceClientVatlib::audioInputDevices() const - { - return m_inputDevices; - } - - const QList &CVoiceClientVatlib::audioOutputDevices() const - { - return m_outputDevices; - } - - const BlackMisc::Voice::CInputAudioDevice CVoiceClientVatlib::defaultAudioInputDevice() const - { - // Constructor creates already a default device - return BlackMisc::Voice::CInputAudioDevice(BlackMisc::Voice::CInputAudioDevice::defaultDevice(), "default"); - } - - const BlackMisc::Voice::COutputAudioDevice CVoiceClientVatlib::defaultAudioOutputDevice() const - { - // 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 - { - if( !m_voice->SetInputDevice(device.index())) - { - qWarning() << "SetInputDevice() failed"; - } - if (!m_voice->IsInputDeviceAlive()) - { - qWarning() << "Input device hit a fatal error"; - } - } - catch (...) { exceptionDispatcher(Q_FUNC_INFO); } - } - - 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 - { - m_voice->SetOutputDevice(0, device.index()); - - if (!m_voice->IsOutputDeviceAlive(0)) - { - qWarning() << "Input device hit a fatal error"; - } - } - catch (...) { exceptionDispatcher(Q_FUNC_INFO); } - } - - 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 - { - m_voice->BeginFindSquelch(); - } - catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } - - // Start the timer only if no exception was thrown before - QTimer::singleShot(5000, this, SLOT(onEndFindSquelch())); - } - - void CVoiceClientVatlib::runMicTest() - { - Q_ASSERT_X (m_voice->IsValid() && m_voice->IsSetup(), "CVoiceClientVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); - - try - { - m_voice->BeginMicTest(); - } - catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } - - // Start the timer only if no exception was thrown before - QTimer::singleShot(5000, this, SLOT(onEndMicTest())); - } - - float CVoiceClientVatlib::inputSquelch() const - { - return m_inputSquelch; - } - - int32_t CVoiceClientVatlib::micTestResult() const - { - return m_micTestResult; - } - - QString CVoiceClientVatlib::micTestResultAsString() const - { - 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 - { - m_voice->DoProcessing(); - } - catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } - } - - void CVoiceClientVatlib::onEndFindSquelch() - { - Q_ASSERT_X (m_voice->IsValid() && m_voice->IsSetup(), "CVoiceClientVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); - - try - { - m_voice->EndFindSquelch(); - m_inputSquelch = m_voice->GetInputSquelch(); - emit squelchTestFinished(); - } - catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } - } - - void CVoiceClientVatlib::onEndMicTest() - { - Q_ASSERT_X (m_voice->IsValid() && m_voice->IsSetup(), "CVoiceClientVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); - - try - { - m_micTestResult = m_voice->EndMicTest(); - emit micTestFinished(); - } - catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } - } - - 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 - { - // Paranoia... - if (!m_voice->IsRoomConnected(static_cast(comUnit))) - return; - - // Store the room index for the slot. - 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); } - } - - /********************************** * * * * * * * * * * * * * * * * * * * ************************************/ - /********************************** shimlib callbacks ************************************/ - /********************************** * * * * * * * * * * * * * * * * * * * ************************************/ - - CVoiceClientVatlib *cbvar_cast(void *cbvar) - { - return static_cast(cbvar); - } - - void CVoiceClientVatlib::onRoomStatusUpdate(Cvatlib_Voice_Simple *obj, Cvatlib_Voice_Simple::roomStatusUpdate upd, int32_t roomIndex, void *cbVar) - { - 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 - QSet users = voiceClientVatlib->roomUserList(comUnit); - QString callsign = QString(name); - if (callsign.isEmpty()) - return; - - 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 ComUnit /** comUnit **/, const QString &callsign) - { - m_voiceRoomUsersUpdate.insert(callsign); - } - - void CVoiceClientVatlib::exceptionDispatcher(const char *caller) - { - QString msg("Caller: "); - msg.append(caller).append(" ").append("Exception: "); - try - { - throw; - } - catch (const NetworkNotConnectedException &e) - { - // this could be caused by a race condition during normal operation, so not an error - msg.append("NetworkNotConnectedException").append(" ").append(e.what()); - emit this->exception(msg); - qDebug() << "NetworkNotConnectedException caught in " << caller << "\n" << e.what(); - } - catch (const VatlibException &e) - { - msg.append("VatlibException").append(" ").append(e.what()); - emit this->exception(msg, true); - qFatal("VatlibException caught in %s\n%s", caller, e.what()); - } - catch (const std::exception &e) - { - msg.append("std::exception").append(" ").append(e.what()); - emit this->exception(msg, true); - qFatal("std::exception caught in %s\n%s", caller, e.what()); - } - catch (...) - { - msg.append("unknown exception"); - emit this->exception(msg, true); - qFatal("Unknown exception caught in %s", caller); - } - } - -} // namespace BlackCore diff --git a/src/blackcore/voiceclient_vatlib.h b/src/blackcore/voiceclient_vatlib.h deleted file mode 100644 index a41d00d1d..000000000 --- a/src/blackcore/voiceclient_vatlib.h +++ /dev/null @@ -1,142 +0,0 @@ -/* Copyright (C) 2013 VATSIM Community / authors - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef VOICECLIENT_VATLIB_H -#define VOICECLIENT_VATLIB_H - -#include "voiceclient.h" - -#include -#include -#include -#include - -namespace BlackCore -{ - /*! - * Vatlib implementation of the IVoiceClient interface. - */ - class CVoiceClientVatlib : public IVoiceClient - { - Q_OBJECT - - public: - CVoiceClientVatlib(QObject *parent = 0); - virtual ~CVoiceClientVatlib(); - - virtual const QSet roomUserList(const ComUnit comUnit); - - // Hardware devices - // TODO: Vatlib supports multiple output devices. That basically means, you could connect - // to different voice rooms and send their audio to different devices, e.g. ATIS to loudspeakers - // and ATC to headspeakers. Is not important to implement that now, if ever. - 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 void setInputDevice(const BlackMisc::Voice::CInputAudioDevice &device); - virtual void setOutputDevice(const BlackMisc::Voice::COutputAudioDevice &device); - - 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 int32_t micTestResult() const; - virtual QString micTestResultAsString() const; - - - 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;} - - void changeRoomStatus(ComUnit comUnit, Cvatlib_Voice_Simple::roomStatusUpdate upd); - - signals: - void userJoinedLeft(const ComUnit comUnit); - - protected: // QObject overrides - virtual void timerEvent(QTimerEvent *); - - private slots: - // slots for Mic tests - void onEndFindSquelch(); - void onEndMicTest(); - - // slot to handle users - void onUserJoinedLeft(const ComUnit comUnit); - - private: - - // shimlib callbacks - 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 ComUnit comUnit, const QString &callsign); - void removeUserFromRoom(const ComUnit comUnit, const QString &callsign); - - void exceptionDispatcher(const char *caller); - - struct Cvatlib_Voice_Simple_Deleter - { - static inline void cleanup(Cvatlib_Voice_Simple *pointer) - { - pointer->Destroy(); - } - }; - - QScopedPointer m_voice; - BlackMisc::Aviation::CCallsign m_callsign; - 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; - - QSet m_voiceRoomUsersUpdate; - - // Need to keep the roomIndex because GetRoomUserList does not specifiy it - int32_t m_queryUserRoomIndex; - }; - -} // namespace BlackCore - -#endif // VOICECLIENT_VATLIB_H diff --git a/src/blackgui/atcstationlistmodel.cpp b/src/blackgui/atcstationlistmodel.cpp index 894e13802..02c62d551 100644 --- a/src/blackgui/atcstationlistmodel.cpp +++ b/src/blackgui/atcstationlistmodel.cpp @@ -22,6 +22,8 @@ namespace BlackGui this->m_columns.addColumn(CAtcStation::IndexIsOnline, "online"); this->m_columns.addColumn(CAtcStation::IndexBookedFrom, "bookedfrom"); this->m_columns.addColumn(CAtcStation::IndexBookedUntil, "bookeduntil"); + this->m_columns.addColumn(CAtcStation::IndexVoiceRoomUrl, "voiceroomurl"); + // default sort order this->setSortColumnByPropertyIndex(CAtcStation::IndexDistance); @@ -35,5 +37,6 @@ namespace BlackGui (void)QT_TRANSLATE_NOOP("ViewAtcList", "online"); (void)QT_TRANSLATE_NOOP("ViewAtcList", "bookedfrom"); (void)QT_TRANSLATE_NOOP("ViewAtcList", "bookeduntil"); + (void)QT_TRANSLATE_NOOP("ViewAtcList", "voiceroomurl"); } } diff --git a/src/blackmisc/avatcstation.cpp b/src/blackmisc/avatcstation.cpp index 863371f4e..e5d8d05e9 100644 --- a/src/blackmisc/avatcstation.cpp +++ b/src/blackmisc/avatcstation.cpp @@ -1,10 +1,12 @@ #include "avatcstation.h" #include "aviocomsystem.h" +#include "vvoiceroom.h" #include "blackmiscfreefunctions.h" using namespace BlackMisc::PhysicalQuantities; using namespace BlackMisc::Geo; using namespace BlackMisc::Network; +using namespace BlackMisc::Voice; namespace BlackMisc { @@ -142,7 +144,7 @@ namespace BlackMisc (void)QT_TRANSLATE_NOOP("Aviation", "until(UTC)"); (void)QT_TRANSLATE_NOOP("Aviation", "range"); (void)QT_TRANSLATE_NOOP("Aviation", "distance"); - + (void)QT_TRANSLATE_NOOP("Network", "voiceroom"); } /* @@ -161,6 +163,7 @@ namespace BlackMisc argument << this->m_bookedUntilUtc; argument << this->m_atis; argument << this->m_metar; + argument << this->m_voiceRoom; } /* @@ -179,6 +182,7 @@ namespace BlackMisc argument >> this->m_bookedUntilUtc; argument >> this->m_atis; argument >> this->m_metar; + argument >> this->m_voiceRoom; } /* @@ -205,6 +209,7 @@ namespace BlackMisc if (other.getController() != this->getController()) return false; if (other.getAtis() != this->getAtis()) return false; if (other.getMetar() != this->getMetar()) return false; + if (other.getVoiceRoom() != this->getVoiceRoom()) return false; return this->getBookedFromUtc() == other.getBookedFromUtc() && this->getBookedUntilUtc() == other.getBookedUntilUtc(); @@ -292,6 +297,7 @@ namespace BlackMisc hashs << this->m_distanceToPlane.getValueHash(); hashs << this->m_metar.getValueHash(); hashs << this->m_atis.getValueHash(); + hashs << this->m_voiceRoom.getValueHash(); hashs << qHash(this->m_isOnline ? 1 : 3); hashs << qHash(this->m_bookedFromUtc); hashs << qHash(this->m_bookedUntilUtc); @@ -343,6 +349,10 @@ namespace BlackMisc return this->m_metar.toQVariant(); case IndexMetarMessage: return QVariant(this->m_metar.getMessage()); + case IndexVoiceRoom: + return QVariant(this->m_voiceRoom.toQVariant()); + case IndexVoiceRoomUrl: + return QVariant(this->m_voiceRoom.getVoiceRoomUrl()); default: break; } @@ -407,6 +417,12 @@ namespace BlackMisc case IndexMetarMessage: this->setMetarMessage(variant.value()); break; + case IndexVoiceRoom: + this->setVoiceRoom(variant.value()); + break; + case IndexVoiceRoomUrl: + this->setVoiceRoom(CVoiceRoom(variant.toString())); + break; default: Q_ASSERT_X(false, "CAtcStation", "index unknown (setter)"); break; diff --git a/src/blackmisc/avatcstation.h b/src/blackmisc/avatcstation.h index 9ff74a1f1..801411ec7 100644 --- a/src/blackmisc/avatcstation.h +++ b/src/blackmisc/avatcstation.h @@ -9,11 +9,13 @@ #ifndef BLACKMISC_ATCSTATION_H #define BLACKMISC_ATCSTATION_H + #include "valuemap.h" #include "pqfrequency.h" #include "pqlength.h" #include "pqtime.h" #include "nwuser.h" +#include "vvoiceroom.h" #include "coordinategeodetic.h" #include "avcallsign.h" #include "avinformationmessage.h" @@ -55,7 +57,9 @@ namespace BlackMisc IndexAtis, IndexAtisMessage, IndexMetar, - IndexMetarMessage + IndexMetarMessage, + IndexVoiceRoom, + IndexVoiceRoomUrl }; /*! @@ -283,6 +287,24 @@ namespace BlackMisc */ void setOnline(bool online) { this->m_isOnline = online; } + /*! + * \brief Get voice room + * \return + */ + const BlackMisc::Voice::CVoiceRoom &getVoiceRoom() const { return this->m_voiceRoom; } + + /*! + * \brief Set voice room + * \param + */ + void setVoiceRoom(const BlackMisc::Voice::CVoiceRoom &voiceRoom) { this->m_voiceRoom = voiceRoom; } + + /*! + * \brief Valid voice room? + * \return + */ + bool hasValidVoiceRoom() const { return this->m_voiceRoom.isValid(); } + /*! * Booked date/time if any. * This represents the closest booking within a time frame as there can be multiple bookings. @@ -468,6 +490,7 @@ namespace BlackMisc QDateTime m_bookedUntilUtc; CInformationMessage m_atis; CInformationMessage m_metar; + BlackMisc::Voice::CVoiceRoom m_voiceRoom; }; } // namespace diff --git a/src/blackmisc/blackmiscfreefunctions.cpp b/src/blackmisc/blackmiscfreefunctions.cpp index 8a86718df..49abec738 100644 --- a/src/blackmisc/blackmiscfreefunctions.cpp +++ b/src/blackmisc/blackmiscfreefunctions.cpp @@ -102,9 +102,10 @@ void BlackMisc::Settings::registerMetadata() */ void BlackMisc::Voice::registerMetadata() { - COutputAudioDevice::registerMetadata(); - CInputAudioDevice::registerMetadata(); + CAudioDevice::registerMetadata(); + CAudioDeviceList::registerMetadata(); CVoiceRoom::registerMetadata(); + CVoiceRoomList::registerMetadata(); } /* diff --git a/src/blackmisc/vaudiodevice.cpp b/src/blackmisc/vaudiodevice.cpp index f3d7588ff..1f9c291c8 100644 --- a/src/blackmisc/vaudiodevice.cpp +++ b/src/blackmisc/vaudiodevice.cpp @@ -9,22 +9,48 @@ #include "vaudiodevice.h" #include "blackmisc/blackmiscfreefunctions.h" +#include namespace BlackMisc { namespace Voice { + /* + * Constructor + */ + CAudioDevice::CAudioDevice() : + m_type(Unknown), m_deviceIndex(invalidDevice()), + m_deviceName(""), m_hostName(CAudioDevice::hostName()) + { + // void + } + + /* + * Constructor + */ + CAudioDevice::CAudioDevice(DeviceType type, const int16_t index, const QString &name) : + m_type(type), m_deviceIndex(index), + m_deviceName(name), m_hostName(CAudioDevice::hostName()) + { + // void + } + + /* + * Host name + */ + QString CAudioDevice::hostName() + { + QHostInfo hostInfo = QHostInfo::fromName(QHostInfo::localHostName()); + return hostInfo.localHostName(); + } + + /* + * Same device? + */ bool CAudioDevice::operator ==(const CAudioDevice &other) const { - if (&other == this) - { - return true; - } - - if (m_deviceIndex == other.m_deviceIndex) - { - return true; - } + if (&other == this) return true; + if (m_deviceIndex == other.m_deviceIndex && m_type == other.m_type) return true; // otherwise return false; @@ -38,23 +64,30 @@ namespace BlackMisc return !((*this) == other); } + /* + * Hash + */ uint CAudioDevice::getValueHash() const { QList hashs; + hashs << qHash(static_cast(m_type)); hashs << qHash(m_deviceIndex); hashs << qHash(m_deviceName); + hashs << qHash(m_hostName); return BlackMisc::calculateHash(hashs, "CAudioDevice"); } - void CAudioDevice::registerMetadata() - { - qRegisterMetaType(); - qDBusRegisterMetaType(); - } - + /* + * As String + */ QString CAudioDevice::convertToQString(bool /* i18n */) const { - return m_deviceName; + if (this->m_hostName.isEmpty()) return m_deviceName; + QString s(this->m_deviceName); + s.append(" ["); + s.append(this->hostName()); + s.append("]"); + return s; } /* @@ -62,8 +95,10 @@ namespace BlackMisc */ void CAudioDevice::marshallToDbus(QDBusArgument &argument) const { + argument << static_cast(m_type); argument << m_deviceIndex; argument << m_deviceName; + argument << m_hostName; } /* @@ -71,20 +106,21 @@ namespace BlackMisc */ void CAudioDevice::unmarshallFromDbus(const QDBusArgument &argument) { + uint t; + argument >> t; + this->m_type = static_cast(t); argument >> m_deviceIndex; argument >> m_deviceName; + argument >> m_hostName; } - void CInputAudioDevice::registerMetadata() + /* + * Register + */ + void CAudioDevice::registerMetadata() { - qRegisterMetaType(); - qDBusRegisterMetaType(); - } - - void COutputAudioDevice::registerMetadata() - { - qRegisterMetaType(); - qDBusRegisterMetaType(); + qRegisterMetaType(); + qDBusRegisterMetaType(); } } // Voice diff --git a/src/blackmisc/vaudiodevice.h b/src/blackmisc/vaudiodevice.h index b0755c58a..581526f3b 100644 --- a/src/blackmisc/vaudiodevice.h +++ b/src/blackmisc/vaudiodevice.h @@ -11,7 +11,6 @@ */ #include "valueobject.h" - #include #ifdef Q_OS_WIN @@ -31,18 +30,27 @@ namespace BlackMisc { public: + /*! + * \brief Type + */ + enum DeviceType + { + InputDevice, + OutputDevice, + Unknown + }; + /*! * 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(invalidDevice()), m_deviceName("") {} - + CAudioDevice(); /*! * Constructor. */ - CAudioDevice(const int16_t index, const QString &name) : m_deviceIndex(index), m_deviceName(name) {} + CAudioDevice(DeviceType type, const int16_t index, const QString &getName); /*! * \brief QVariant, required for DBus QVariant lists @@ -57,13 +65,19 @@ namespace BlackMisc * Get the device index * \return */ - int16_t index() const { return m_deviceIndex; } + int16_t getIndex() const { return m_deviceIndex; } /*! * Get the device name * \return */ - const QString &name() const { return m_deviceName; } + const QString &getName() const { return m_deviceName; } + + /*! + * \brief Type + * \return + */ + DeviceType getType() const { return m_type; } /*! * \brief Valid audio device object? @@ -95,8 +109,16 @@ namespace BlackMisc */ static void registerMetadata(); + /*! + * \brief Device type + * \return + */ static int16_t defaultDevice() {return -1;} + /*! + * \brief Device type + * \return + */ static int16_t invalidDevice() {return -2;} protected: @@ -125,76 +147,22 @@ namespace BlackMisc * deviceIndex is the number is the reference for the VVL. The device is selected by this index. * The managing class needs to take care, that indexes are valid. */ + DeviceType m_type; int16_t m_deviceIndex; QString m_deviceName; + QString m_hostName; - }; - - class CInputAudioDevice : public CAudioDevice - { - public: + private: /*! - * Default constructor. - */ - CInputAudioDevice() : CAudioDevice() {} - - - /*! - * Constructor. - */ - CInputAudioDevice(const int16_t index, const QString &name) : CAudioDevice(index, name) {} - - /*! - * \brief QVariant, required for DBus QVariant lists + * \brief Own host name * \return */ - virtual QVariant toQVariant() const - { - return QVariant::fromValue(*this); - } - - /*! - * \brief Register metadata - */ - static void registerMetadata(); - + static QString hostName(); }; - class COutputAudioDevice : public CAudioDevice - { - public: - /*! - * Default constructor. - */ - COutputAudioDevice() : CAudioDevice() {} - - - /*! - * Constructor. - */ - COutputAudioDevice(const int16_t index, const QString &name) : CAudioDevice(index, name) {} - - /*! - * \brief QVariant, required for DBus QVariant lists - * \return - */ - virtual QVariant toQVariant() const - { - return QVariant::fromValue(*this); - } - - /*! - * \brief Register metadata - */ - static void registerMetadata(); - - }; } // Voice } // BlackMisc Q_DECLARE_METATYPE(BlackMisc::Voice::CAudioDevice) -Q_DECLARE_METATYPE(BlackMisc::Voice::COutputAudioDevice) -Q_DECLARE_METATYPE(BlackMisc::Voice::CInputAudioDevice) - -#endif // BLACKMISC_AUDIODEVICE_H +#endif // guard diff --git a/src/blackmisc/vaudiodevicelist.cpp b/src/blackmisc/vaudiodevicelist.cpp new file mode 100644 index 000000000..9e699d069 --- /dev/null +++ b/src/blackmisc/vaudiodevicelist.cpp @@ -0,0 +1,76 @@ +/* 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 "vaudiodevicelist.h" +#include "predicates.h" + +namespace BlackMisc +{ + namespace Voice + { + /* + * Default constructor + */ + CAudioDeviceList::CAudioDeviceList() { } + + /* + * Construct from base class object + */ + CAudioDeviceList::CAudioDeviceList(const CSequence &other) : + CSequence(other) + { } + + /* + * Output devices + */ + CAudioDeviceList CAudioDeviceList::getOutputDevices() const + { + CAudioDeviceList outList; + if (this->isEmpty()) return outList; + foreach(CAudioDevice device, *this) + { + if (device.getType() == CAudioDevice::OutputDevice) outList.push_back(device); + } + return outList; + } + + /* + * Output devices + */ + CAudioDeviceList CAudioDeviceList::getInputDevices() const + { + CAudioDeviceList inList; + if (this->isEmpty()) return inList; + foreach(CAudioDevice device, *this) + { + if (device.getType() == CAudioDevice::InputDevice) inList.push_back(device); + } + return inList; + } + + /* + * Count as of type + */ + int CAudioDeviceList::count(CAudioDevice::DeviceType type) const + { + int c = 0; + foreach(CAudioDevice device, *this) + { + if (device.getType() == type) c++; + } + return c; + } + + /* + * Register metadata + */ + void CAudioDeviceList::registerMetadata() + { + qRegisterMetaType(); + qDBusRegisterMetaType(); + } + + } // namespace +} // namespace diff --git a/src/blackmisc/vaudiodevicelist.h b/src/blackmisc/vaudiodevicelist.h new file mode 100644 index 000000000..0c0045bcf --- /dev/null +++ b/src/blackmisc/vaudiodevicelist.h @@ -0,0 +1,83 @@ +/* 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/. */ + +/*! + \file +*/ + +#ifndef BLACKMISC_AUDIODEVICELIST_H +#define BLACKMISC_AUDIODEVICELIST_H + +#include "vaudiodevice.h" +#include "sequence.h" +#include "collection.h" +#include +#include +#include + +namespace BlackMisc +{ + namespace Voice + { + /*! + * Value object encapsulating a list of audio devices. + */ + class CAudioDeviceList : public CSequence + { + public: + /*! + * \brief Default constructor. + */ + CAudioDeviceList(); + + /*! + * \brief Construct from a base class object. + * \param other + */ + CAudioDeviceList(const CSequence &other); + + /*! + * \brief QVariant, required for DBus QVariant lists + * \return + */ + virtual QVariant asQVariant() const + { + return QVariant::fromValue(*this); + } + + /*! + * \brief Get output devices in that list + * \return + */ + CAudioDeviceList getOutputDevices() const; + + /*! + * \brief Get output devices in that list + * \return + */ + CAudioDeviceList getInputDevices() const; + + /*! + * \brief Count (as of type) + * \param type + * \return + */ + int count(CAudioDevice::DeviceType type) const; + + /*! + * \brief Register metadata + */ + static void registerMetadata(); + + }; + + } //namespace +} // namespace + +Q_DECLARE_METATYPE(BlackMisc::Voice::CAudioDeviceList) +Q_DECLARE_METATYPE(BlackMisc::CCollection) +Q_DECLARE_METATYPE(BlackMisc::CSequence) + +#endif //guard diff --git a/src/blackmisc/voiceallclasses.h b/src/blackmisc/voiceallclasses.h index eed272608..4fae9ff83 100644 --- a/src/blackmisc/voiceallclasses.h +++ b/src/blackmisc/voiceallclasses.h @@ -7,6 +7,8 @@ #define BLACKMISC_VOICEALLCLASSES_H #include "blackmisc/vaudiodevice.h" +#include "blackmisc/vaudiodevicelist.h" #include "blackmisc/vvoiceroom.h" +#include "blackmisc/vvoiceroomlist.h" #endif // guard diff --git a/src/blackmisc/vvoiceroom.cpp b/src/blackmisc/vvoiceroom.cpp index eb096e567..9cd73d191 100644 --- a/src/blackmisc/vvoiceroom.cpp +++ b/src/blackmisc/vvoiceroom.cpp @@ -17,11 +17,15 @@ namespace BlackMisc { namespace Voice { - CVoiceRoom::CVoiceRoom(const QString &serverSpec) : m_hostname(""), m_channel("") + CVoiceRoom::CVoiceRoom(const QString &serverUrl, bool connected) : + m_hostname(""), m_channel(""), m_connected(connected), m_audioPlaying(false) { - if (serverSpec.contains("/")) + if (serverUrl.contains("/")) { - QStringList splittedSpec = serverSpec.split("/"); + QString url = serverUrl.trimmed().toLower(); + url.replace(CVoiceRoom::protocolComplete(), ""); + url.replace(CVoiceRoom::protocol(), ""); + QStringList splittedSpec = serverUrl.split("/"); m_hostname = splittedSpec.at(0); m_channel = splittedSpec.at(1); } @@ -32,18 +36,11 @@ namespace BlackMisc */ bool CVoiceRoom::operator ==(const CVoiceRoom &other) const { - if (&other == this) - { - return true; - } - - if (m_hostname == other.m_hostname && m_channel == other.m_channel) - { - return true; - } - - // otherwise - return false; + if (&other == this) return true; + return (m_hostname == other.m_hostname && + m_channel == other.m_channel && + m_connected == other.m_connected && + m_audioPlaying == other.m_audioPlaying); } /* @@ -54,25 +51,37 @@ namespace BlackMisc return !((*this) == other); } + /* + * Value hash + */ uint CVoiceRoom::getValueHash() const { QList hashs; hashs << qHash(m_hostname); hashs << qHash(m_channel); + hashs << qHash(m_connected); + hashs << qHash(m_audioPlaying); return BlackMisc::calculateHash(hashs, "CVoiceRoom"); } + /* + * Metadata + */ void CVoiceRoom::registerMetadata() { qRegisterMetaType(); qDBusRegisterMetaType(); } - QString CVoiceRoom::convertToQString(bool /* i18n */ ) const + /* + * To string + */ + QString CVoiceRoom::convertToQString(bool /* i18n */) const { - if (m_hostname.isEmpty() || m_channel.isEmpty()) return "Unknown"; - QString s = m_hostname; - s.append("/").append(m_channel); + if (!this->isValid()) return "Invalid"; + QString s = this->getVoiceRoomUrl(false); + s.append(this ->isConnected() ? " connected" : " unconnected"); + if (this->m_audioPlaying) s.append(" playing"); return s; } @@ -83,6 +92,8 @@ namespace BlackMisc { argument << m_hostname; argument << m_channel; + argument << m_connected; + argument << m_audioPlaying; } /* @@ -92,8 +103,29 @@ namespace BlackMisc { argument >> m_hostname; argument >> m_channel; + argument >> m_connected; + argument >> m_audioPlaying; } + /* + * Server URL + */ + QString CVoiceRoom::getVoiceRoomUrl(bool noProtocol) const + { + if (!this->isValid()) return ""; + QString url(noProtocol ? "" : CVoiceRoom::protocolComplete()); + url.append(this->m_hostname); + url.append("/"); + url.append(this->m_channel); + return url; + } + /* + * ATIS voice channel + */ + bool CVoiceRoom::isAtis() const + { + return (this->m_channel.contains("ATIS", Qt::CaseInsensitive)); + } } // Voice } // BlackMisc diff --git a/src/blackmisc/vvoiceroom.h b/src/blackmisc/vvoiceroom.h index 436ff1c1b..5bd5d32d8 100644 --- a/src/blackmisc/vvoiceroom.h +++ b/src/blackmisc/vvoiceroom.h @@ -8,10 +8,8 @@ */ #include "valueobject.h" - #include - #ifndef BLACKMISC_VOICEROOM_H #define BLACKMISC_VOICEROOM_H @@ -29,17 +27,23 @@ namespace BlackMisc /*! * Default constructor. */ - CVoiceRoom() : m_hostname(""), m_channel("") {} + CVoiceRoom() : + m_hostname(""), m_channel(""), m_connected(false), m_audioPlaying(false) {} /*! * Constructor. + * \param hostname + * \param channel */ - CVoiceRoom(const QString &hostname, const QString &channel) : m_hostname(hostname), m_channel(channel) {} + CVoiceRoom(const QString &hostname, const QString &channel) : + m_hostname(hostname), m_channel(channel), m_audioPlaying(false) {} /*! * Constructor. + * \param serverUrl + * \param connected */ - CVoiceRoom(const QString &serverSpec); + CVoiceRoom(const QString &serverUrl, bool connected = false); /*! * \brief QVariant, required for DBus QVariant lists @@ -54,13 +58,13 @@ namespace BlackMisc * Get the host name * \return */ - const QString &hostName() const { return m_hostname; } + const QString &getHostName() const { return m_hostname; } /*! * Get the voice room * \return */ - const QString &channel() const { return m_channel; } + const QString &getChannel() const { return m_channel; } /*! * Set the host name @@ -74,6 +78,13 @@ namespace BlackMisc */ void setChannel(const QString &channel) { m_channel = channel; } + /*! + * \brief Server URL + * \param noProtocol + * \return + */ + QString getVoiceRoomUrl(bool noProtocol = true) const; + /*! * \brief Valid voice room object? * \return @@ -81,10 +92,40 @@ namespace BlackMisc bool isValid() const { return !this->m_hostname.isEmpty() && !this->m_channel.isEmpty(); } /*! - * \brief Equal operator == - * \param other - * @return + * \brief Is connected + * \return */ + bool isConnected() const { return this->isValid() && this->m_connected; } + + /*! + * \brief Set connected status + * \param isConnected + */ + void setConnected(bool isConnected) { this->m_connected = isConnected; } + + /*! + * \brief Is audio playing in this room? + * \return + */ + bool isAudioPlaying() const { return this->m_audioPlaying; } + + /*! + * \brief Set audio playing + * \param playing + */ + void setAudioPlaying(bool playing) { this->m_audioPlaying = playing; } + + /*! + * \brief Is ATIS voice channel + * \return + */ + bool isAtis() const; + + /*! + * \brief Equal operator == + * \param other + * @return + */ bool operator ==(const CVoiceRoom &other) const; /*! @@ -104,6 +145,18 @@ namespace BlackMisc */ static void registerMetadata(); + /*! + * \brief Protocol + * \return + */ + static const QString &protocol() { static QString p("vvl"); return p; } + + /*! + * \brief Protocol + * \return + */ + static const QString &protocolComplete() { static QString p("vvl://"); return p; } + protected: /*! @@ -125,15 +178,15 @@ namespace BlackMisc */ virtual void unmarshallFromDbus(const QDBusArgument &argument); - private: QString m_hostname; QString m_channel; - + bool m_connected; + bool m_audioPlaying; }; } // Voice } // BlackMisc Q_DECLARE_METATYPE(BlackMisc::Voice::CVoiceRoom) -#endif // BLACKMISC_VOICEROOM_H +#endif // guard diff --git a/src/blackmisc/vvoiceroomlist.cpp b/src/blackmisc/vvoiceroomlist.cpp new file mode 100644 index 000000000..6760ccf3f --- /dev/null +++ b/src/blackmisc/vvoiceroomlist.cpp @@ -0,0 +1,35 @@ +/* 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 "vvoiceroomlist.h" +#include "predicates.h" + +namespace BlackMisc +{ + namespace Voice + { + /* + * Default constructor + */ + CVoiceRoomList::CVoiceRoomList() { } + + /* + * Construct from base class object + */ + CVoiceRoomList::CVoiceRoomList(const CSequence &other) : + CSequence(other) + { } + + /* + * Register metadata + */ + void CVoiceRoomList::registerMetadata() + { + qRegisterMetaType(); + qDBusRegisterMetaType(); + } + + } // namespace +} // namespace diff --git a/src/blackmisc/vvoiceroomlist.h b/src/blackmisc/vvoiceroomlist.h new file mode 100644 index 000000000..5337ef413 --- /dev/null +++ b/src/blackmisc/vvoiceroomlist.h @@ -0,0 +1,64 @@ +/* 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/. */ + +/*! + \file +*/ + +#ifndef BLACKMISC_VOICEROOMLIST_H +#define BLACKMISC_VOICEROOMLIST_H + +#include "vvoiceroom.h" +#include "sequence.h" +#include "collection.h" +#include +#include +#include + +namespace BlackMisc +{ + namespace Voice + { + /*! + * Value object encapsulating a list of voice rooms. + */ + class CVoiceRoomList : public CSequence + { + public: + /*! + * \brief Default constructor. + */ + CVoiceRoomList(); + + /*! + * \brief Construct from a base class object. + * \param other + */ + CVoiceRoomList(const CSequence &other); + + /*! + * \brief QVariant, required for DBus QVariant lists + * \return + */ + virtual QVariant asQVariant() const + { + return QVariant::fromValue(*this); + } + + /*! + * \brief Register metadata + */ + static void registerMetadata(); + + }; + + } //namespace +} // namespace + +Q_DECLARE_METATYPE(BlackMisc::Voice::CVoiceRoomList) +Q_DECLARE_METATYPE(BlackMisc::CCollection) +Q_DECLARE_METATYPE(BlackMisc::CSequence) + +#endif //guard