/* 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 #include using namespace BlackMisc; using namespace BlackMisc::Audio; using namespace BlackMisc::Aviation; namespace BlackCore { /* * Constructor */ CVoiceVatlib::CVoiceVatlib(QObject *parent) : IVoice(parent), m_vatlib(Cvatlib_Voice_Simple::Create(), Cvatlib_Voice_Simple_Deleter::cleanup), // m_audioOutput(nullptr), // removed #227 m_inputSquelch(-1), m_micTestResult(Cvatlib_Voice_Simple::agc_Ok), m_isAudioLoopbackEnabled(false), m_temporaryUserRoomIndex(CVoiceVatlib::InvalidRoomIndex), m_lockVoiceRooms(QReadWriteLock::Recursive), m_lockCallsigns(QReadWriteLock::Recursive), m_lockCurrentOutputDevice(QReadWriteLock::Recursive), m_lockCurrentInputDevice(QReadWriteLock::Recursive), m_lockDeviceList(QReadWriteLock::Recursive), m_lockOutputEnabled(QReadWriteLock::Recursive), m_lockSquelch(QReadWriteLock::Recursive), m_lockTestResult(QReadWriteLock::Recursive), m_lockMyCallsign(QReadWriteLock::Recursive), m_lockConnectionStatus(QReadWriteLock::Recursive), { try { // we use reset here until issue #277 is resolved // easier to find root cause // m_audioOutput.reset(new QAudioOutput()); m_vatlib->Setup(true, 3290, 2, 1, onRoomStatusUpdate, this); m_vatlib->GetInputDevices(onInputHardwareDeviceReceived, this); m_vatlib->GetOutputDevices(onOutputHardwareDeviceReceived, this); connect(this, &CVoiceVatlib::userJoinedLeft, this, &CVoiceVatlib::onUserJoinedLeft, Qt::QueuedConnection); this->m_vatlibRooms.push_back(CVoiceRoom()); // COM1 this->m_vatlibRooms.push_back(CVoiceRoom()); // COM2 this->m_outputEnabled.insert(COM1, true); this->m_outputEnabled.insert(COM2, true); this->m_currentInputDevice = this->defaultAudioInputDevice(); this->m_currentOutputDevice = this->defaultAudioOutputDevice(); // this->m_audioOutput->setVolume(1.0); // make sure the overall sound is not muted // do processing this->startTimer(10); } catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } } /* * Destructor */ CVoiceVatlib::~CVoiceVatlib() {} /* * Devices */ const BlackMisc::Audio::CAudioDeviceList &CVoiceVatlib::audioDevices() const { QReadLocker lockForReading(&m_lockDeviceList); return m_devices; } /* * Default input device */ const BlackMisc::Audio::CAudioDevice CVoiceVatlib::defaultAudioInputDevice() const { // Constructor creates already a default device return BlackMisc::Audio::CAudioDevice(BlackMisc::Audio::CAudioDevice::InputDevice, BlackMisc::Audio::CAudioDevice::defaultDeviceIndex(), "default"); } /* * Default output device */ const BlackMisc::Audio::CAudioDevice CVoiceVatlib::defaultAudioOutputDevice() const { // Constructor creates already a default device return BlackMisc::Audio::CAudioDevice(BlackMisc::Audio::CAudioDevice::OutputDevice, BlackMisc::Audio::CAudioDevice::defaultDeviceIndex(), "default"); } /* * Current output device */ CAudioDevice CVoiceVatlib::getCurrentOutputDevice() const { QReadLocker lockForReading(&m_lockCurrentOutputDevice); return m_currentOutputDevice; } /* * Current input device */ CAudioDevice CVoiceVatlib::getCurrentInputDevice() const { QReadLocker lockForReading(&m_lockCurrentInputDevice); return m_currentInputDevice; } /* * Set input device */ void CVoiceVatlib::setInputDevice(const BlackMisc::Audio::CAudioDevice &device) { std::lock_guard locker(m_vatlib); Q_ASSERT_X(m_vatlib->IsValid() && m_vatlib->IsSetup(), "CVoiceVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); if (!device.isValid()) { qWarning() << "Cannot set invalid input device!"; return; } try { if (!m_vatlib->SetInputDevice(device.getIndex())) { qWarning() << "Setting input device failed"; } if (!m_vatlib->IsInputDeviceAlive()) { qWarning() << "Input device hit a fatal error"; } QWriteLocker lockForWriting(&m_lockCurrentInputDevice); this->m_currentInputDevice = device; } catch (...) { exceptionDispatcher(Q_FUNC_INFO); } } /* * Set output device */ void CVoiceVatlib::setOutputDevice(const BlackMisc::Audio::CAudioDevice &device) { std::lock_guard locker(m_vatlib); Q_ASSERT_X(m_vatlib->IsValid() && m_vatlib->IsSetup(), "CVoiceVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); if (!device.isValid()) { qWarning() << "Cannot set invalid output device!"; return; } try { // there is no return value here: https://dev.vatsim-germany.org/issues/115 m_vatlib->SetOutputDevice(0, device.getIndex()); if (!m_vatlib->IsOutputDeviceAlive(0)) { qWarning() << "Output device hit a fatal error"; } QWriteLocker lockForWriting(&m_lockCurrentOutputDevice); this->m_currentOutputDevice = device; } catch (...) { exceptionDispatcher(Q_FUNC_INFO); } } /* * Get voice rooms, with the latest status updated */ BlackMisc::Audio::CVoiceRoomList CVoiceVatlib::getComVoiceRoomsWithAudioStatus() const { QReadLocker lockForReading(&m_lockVoiceRooms); Q_ASSERT_X(m_vatlibRooms.size() == 2, "CVoiceVatlib", "Wrong numer of COM voice rooms"); if (!m_vatlib->IsValid() || !m_vatlib->IsSetup()) return CVoiceRoomList::twoEmptyRooms(); QMutexLocker lockerVatlib(&m_mutexVatlib); // valid state, update CVoiceRoom com1 = this->m_vatlibRooms[0]; CVoiceRoom com2 = this->m_vatlibRooms[1]; com1.setConnected(m_vatlib->IsRoomConnected(static_cast(COM1))); com2.setConnected(m_vatlib->IsRoomConnected(static_cast(COM2))); com1.setAudioPlaying(com1.isConnected() ? m_vatlib->IsAudioPlaying(static_cast(COM1)) : false); com2.setAudioPlaying(com2.isConnected() ? m_vatlib->IsAudioPlaying(static_cast(COM2)) : false); CVoiceRoomList voiceRooms; voiceRooms.push_back(com1); voiceRooms.push_back(com2); return voiceRooms; } /* * Voice room callsigns */ CCallsignList CVoiceVatlib::getVoiceRoomCallsigns(const IVoice::ComUnit comUnit) const { QReadLocker lockForReading(&m_lockCallsigns); CCallsignList callsigns; if (!this->m_vatlibRoomCallsigns.contains(comUnit)) return callsigns; return this->m_vatlibRoomCallsigns[comUnit]; } /* * Enable audio */ void CVoiceVatlib::switchAudioOutput(const ComUnit comUnit, bool enable) { QMutexLocker lockerVatlib(&m_mutexVatlib); Q_ASSERT_X(m_vatlib->IsValid() && m_vatlib->IsSetup(), "CVoiceVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); Q_ASSERT_X(m_vatlib->IsRoomValid(static_cast(comUnit)), "CVoiceVatlib", "Room index out of bounds!"); try { m_vatlib->SetOutputState(static_cast(comUnit), 0, enable); QWriteLocker lockForWriting(&m_lockOutputEnabled); this->m_outputEnabled[comUnit] = enable; } catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } } void CVoiceVatlib::enableAudioLoopback(bool enable) { if (enable == m_isAudioLoopbackEnabled) return; std::lock_guard locker(m_vatlib); Q_ASSERT_X(m_vatlib->IsValid() && m_vatlib->IsSetup(), "CVoiceVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); try { m_vatlib->SetAudioLoopback(0, enable); } catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } // bools are atomic. No need to protect it with a mutex m_isAudioLoopbackEnabled = enable; } /* * Squelch test */ void CVoiceVatlib::runSquelchTest() { std::lock_guard locker(m_vatlib); Q_ASSERT_X(m_vatlib->IsValid() && m_vatlib->IsSetup(), "CVoiceVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); try { m_vatlib->BeginFindSquelch(); } catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } // Start the timer only if no exception was thrown before QTimer::singleShot(5000, this, SLOT(onEndFindSquelch())); } /* * Start microphone test */ void CVoiceVatlib::runMicrophoneTest() { std::lock_guard locker(m_vatlib); Q_ASSERT_X(m_vatlib->IsValid() && m_vatlib->IsSetup(), "CVoiceVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); try { m_vatlib->BeginMicTest(); // Start the timer only if no exception was thrown before QTimer::singleShot(5000, this, SLOT(onEndMicTest())); } catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } } /* * Input squelch volume */ float CVoiceVatlib::inputSquelch() const { QReadLocker lockForReading(&m_lockSquelch); return m_inputSquelch; } /* * Mic test */ qint32 CVoiceVatlib::micTestResult() const { QReadLocker lockForReading(&m_lockTestResult); return m_micTestResult; } /* * Mic test, result as string */ QString CVoiceVatlib::micTestResultAsString() const { QString result; QReadLocker lockForReading(&m_lockTestResult); 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) { QWriteLocker lockForWriting(&m_lockMyCallsign); m_aircraftCallsign = callsign; } /* * Voice room */ void CVoiceVatlib::joinVoiceRoom(const ComUnit comUnit, const BlackMisc::Audio::CVoiceRoom &voiceRoom) { QMutexLocker lockerVatlib(&m_mutexVatlib); Q_ASSERT_X(m_vatlib->IsValid() && m_vatlib->IsSetup(), "CVoiceVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); Q_ASSERT_X(m_vatlib->IsRoomValid(static_cast(comUnit)), "CVoiceVatlib", "Room index out of bounds!"); if (!voiceRoom.isValid()) { qDebug() << "Error: Cannot join invalid voice room."; return; } try { // only connect, if not yet connected if (this->m_vatlib->IsRoomConnected(static_cast(comUnit))) return; changeConnectionStatus(comUnit, Connecting); QString serverSpec = voiceRoom.getVoiceRoomUrl(); QReadLocker lockForReading(&m_lockMyCallsign); bool jr = m_vatlib->JoinRoom(static_cast(comUnit), m_aircraftCallsign.toQString().toLatin1().constData(), serverSpec.toLatin1().constData()); if (jr) { // do not(!) set as connected right now, this will be done in "status changed" // when room really is connected this->setVoiceRoomForUnit(comUnit, voiceRoom); } else { qWarning("Could not join voice room"); } } catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } } /* * Leave a room */ void CVoiceVatlib::leaveVoiceRoom(const ComUnit comUnit) { QMutexLocker lockerVatlib(&m_mutexVatlib); Q_ASSERT_X(m_vatlib->IsValid() && m_vatlib->IsSetup(), "CVoiceVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); Q_ASSERT_X(m_vatlib->IsRoomValid(static_cast(comUnit)), "CVoiceVatlib", "Room index out of bounds!"); try { // update "meta" object for room this->setVoiceRoomForUnit(comUnit, CVoiceRoom()); // an empty voice room is easier to detect // only leave is room is physically connected if (this->m_vatlib->IsRoomConnected(static_cast(comUnit))) { m_vatlib->LeaveRoom(static_cast(comUnit)); this->m_vatlibRoomCallsigns[comUnit] = CCallsignList(); // empty list for this room changeConnectionStatus(comUnit, Disconnecting); } } catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } } /* * Leave all voice rooms */ void CVoiceVatlib::leaveAllVoiceRooms() { this->leaveVoiceRoom(COM1); this->leaveVoiceRoom(COM2); } /* * Room output volume as per COM unit */ void CVoiceVatlib::setRoomOutputVolume(const ComUnit comUnit, const qint32 volume) { QMutexLocker lockerVatlib(&m_mutexVatlib); Q_ASSERT_X(m_vatlib->IsValid() && m_vatlib->IsSetup(), "CVoiceVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); Q_ASSERT_X(m_vatlib->IsRoomValid(static_cast(comUnit)), "CVoiceVatlib", "Room index out of bounds!"); try { m_vatlib->SetRoomVolume(static_cast(comUnit), volume); } catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } } /* * Start transmitting */ void CVoiceVatlib::startTransmitting(const ComUnit comUnit) { QMutexLocker lockerVatlib(&m_mutexVatlib); Q_ASSERT_X(m_vatlib->IsValid() && m_vatlib->IsSetup(), "CVoiceVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); Q_ASSERT_X(m_vatlib->IsRoomValid(static_cast(comUnit)), "CVoiceVatlib", "Room index out of bounds!"); try { m_vatlib->SetMicState(static_cast(comUnit), true); } catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } } /* * Start transmitting */ void CVoiceVatlib::stopTransmitting(const ComUnit comUnit) { QMutexLocker lockerVatlib(&m_mutexVatlib); Q_ASSERT_X(m_vatlib->IsValid() && m_vatlib->IsSetup(), "CVoiceVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); Q_ASSERT_X(m_vatlib->IsRoomValid(static_cast(comUnit)), "CVoiceVatlib", "Room index out of bounds!"); try { m_vatlib->SetMicState(static_cast(comUnit), false); } catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } } /* * Change room status */ void CVoiceVatlib::changeRoomStatus(ComUnit comUnit, Cvatlib_Voice_Simple::roomStatusUpdate roomStatus) { qDebug() << comUnit << roomStatus; // KB_REMOVE CVoiceRoom vr = this->voiceRoomForUnit(comUnit); switch (roomStatus) { case Cvatlib_Voice_Simple::roomStatusUpdate_JoinSuccess: { m_lockOutputEnabled.lockForRead(); bool isOutputEnabled = this->m_outputEnabled[comUnit]; m_lockOutputEnabled.unlock(); switchAudioOutput(comUnit, isOutputEnabled); vr.setConnected(true); this->setVoiceRoomForUnit(comUnit, vr); changeConnectionStatus(comUnit, Connected); emit userJoinedLeft(comUnit); break; } case Cvatlib_Voice_Simple::roomStatusUpdate_JoinFail: vr.setConnected(false); this->setVoiceRoomForUnit(comUnit, vr); changeConnectionStatus(comUnit, ConnectingFailed); break; case Cvatlib_Voice_Simple::roomStatusUpdate_UnexpectedDisconnectOrKicked: vr.setConnected(false); this->setVoiceRoomForUnit(comUnit, vr); changeConnectionStatus(comUnit, DisconnectedError); break; case Cvatlib_Voice_Simple::roomStatusUpdate_LeaveComplete: m_lockCallsigns.lockForWrite(); m_voiceRoomCallsigns.clear(); m_lockCallsigns.unlock(); changeConnectionStatus(comUnit, Disconnected); break; case Cvatlib_Voice_Simple::roomStatusUpdate_UserJoinsLeaves: // FIXME: We cannot call GetRoomUserList because vatlib is not reentrent safe. emit userJoinedLeft(comUnit); break; case Cvatlib_Voice_Simple::roomStatusUpdate_RoomAudioStarted: emit audioStarted(comUnit); break; case Cvatlib_Voice_Simple::roomStatusUpdate_RoomAudioStopped: emit audioStopped(comUnit); break; case Cvatlib_Voice_Simple::roomStatusUpdate_AudioStarted: emit globalAudioStarted(); break; case Cvatlib_Voice_Simple::roomStatusUpdate_AudioStopped: emit globalAudioStopped(); break; default: break; } } /* * Process voice handling */ void CVoiceVatlib::timerEvent(QTimerEvent *) { std::lock_guard locker(m_vatlib); Q_ASSERT_X(m_vatlib->IsValid() && m_vatlib->IsSetup(), "CVoiceVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); try { this->m_vatlib->DoProcessing(); } catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } } /* * Find squelch */ void CVoiceVatlib::onEndFindSquelch() { std::lock_guard locker(m_vatlib); Q_ASSERT_X(m_vatlib->IsValid() && m_vatlib->IsSetup(), "CVoiceVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); try { m_vatlib->EndFindSquelch(); QWriteLocker lockForWriting(&m_lockSquelch); m_inputSquelch = m_vatlib->GetInputSquelch(); emit squelchTestFinished(); } catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } } void CVoiceVatlib::onEndMicTest() { std::lock_guard locker(m_vatlib); Q_ASSERT_X(m_vatlib->IsValid() && m_vatlib->IsSetup(), "CVoiceVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); try { QWriteLocker lockForWriting(&m_lockTestResult); m_micTestResult = m_vatlib->EndMicTest(); emit micTestFinished(); } catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } } /* * User joined / left room */ void CVoiceVatlib::onUserJoinedLeft(const ComUnit comUnit) { QMutexLocker lockerVatlib(&m_mutexVatlib); Q_ASSERT_X(m_vatlib->IsValid() && m_vatlib->IsSetup(), "CVoiceVatlib", "Cvatlib_Voice_Simple invalid or not setup!"); Q_ASSERT_X(m_temporaryUserRoomIndex == CVoiceVatlib::InvalidRoomIndex, "CVoiceClientVatlib::onUserJoinedLeft", "Cannot list users for two rooms in parallel!"); try { // Paranoia... clear list completely if (!m_vatlib->IsRoomConnected(static_cast(comUnit))) { this->m_voiceRoomCallsigns[comUnit] = CCallsignList(); this->m_temporaryVoiceRoomCallsigns.clear(); return; } // Store the room index for the slot (called in static callback) m_temporaryUserRoomIndex = static_cast(comUnit); // Callbacks already completed when function GetRoomUserList returns, // thereafter m_voiceRoomCallsignsUpdate is filled with the latest callsigns m_vatlib->GetRoomUserList(static_cast(comUnit), onRoomUserReceived, this); m_temporaryUserRoomIndex = CVoiceVatlib::InvalidRoomIndex; // reset // we have all current users in m_temporaryVoiceRoomCallsigns foreach(CCallsign callsign, m_voiceRoomCallsigns.value(comUnit)) { if (!m_temporaryVoiceRoomCallsigns.contains(callsign)) { // User has left emit userLeftRoom(callsign); } } QWriteLocker lockForWriting(&m_lockCallsigns); foreach(CCallsign callsign, m_temporaryVoiceRoomCallsigns) { if (!m_voiceRoomCallsigns.value(comUnit).contains(callsign)) { // he joined emit userJoinedRoom(callsign); } } // Finally we update it with our new list this->m_voiceRoomCallsigns[comUnit] = this->m_temporaryVoiceRoomCallsigns; this->m_temporaryVoiceRoomCallsigns.clear(); } catch (...) { this->exceptionDispatcher(Q_FUNC_INFO); } } /********************************** * * * * * * * * * * * * * * * * * * * ************************************/ /********************************** shimlib callbacks ************************************/ /********************************** * * * * * * * * * * * * * * * * * * * ************************************/ /*! * \brief Cast from *void to *CVoiceVatlib * \param cbvar * \return */ 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, qint32 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 callsign CVoiceVatlib *voiceClientVatlib = cbvar_cast_voice(cbVar); ComUnit comUnit = static_cast(voiceClientVatlib->temporaryUserRoomIndex()); // add user // callsign might contain: VATSIM id, user name if (callsign.contains(" ")) { QStringList parts = callsign.split(" "); callsign = parts[0]; // I throw away VATSIM id here, maybe we could use it } voiceClientVatlib->addTemporaryCallsignForRoom(comUnit, CCallsign(callsign)); } /* * Input hardware received */ void CVoiceVatlib::onInputHardwareDeviceReceived(Cvatlib_Voice_Simple *obj, const char *name, void *cbVar) { Q_UNUSED(obj) BlackMisc::Audio::CAudioDevice inputDevice(BlackMisc::Audio::CAudioDevice::InputDevice, cbvar_cast_voice(cbVar)->m_devices.count(BlackMisc::Audio::CAudioDevice::InputDevice), QString(name)); QWriteLocker lockForWriting(&(cbvar_cast_voice(cbVar)->m_lockDeviceList)); 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::Audio::CAudioDevice outputDevice(BlackMisc::Audio::CAudioDevice::OutputDevice, cbvar_cast_voice(cbVar)->m_devices.count(BlackMisc::Audio::CAudioDevice::OutputDevice), QString(name)); QWriteLocker lockForWriting(&(cbvar_cast_voice(cbVar)->m_lockDeviceList)); cbvar_cast_voice(cbVar)->m_devices.push_back(outputDevice); } /* * COM unit to Voice room */ CVoiceRoom CVoiceVatlib::voiceRoomForUnit(const IVoice::ComUnit comUnit) const { QReadLocker lockForReading(&m_lockVoiceRooms); return (comUnit == COM1) ? this->m_voiceRooms[0] : this->m_voiceRooms[1]; } /* * Voice room for respectice COM unit */ void CVoiceVatlib::setVoiceRoomForUnit(const IVoice::ComUnit comUnit, const CVoiceRoom &voiceRoom) { QWriteLocker lockForWriting(&m_lockVoiceRooms); this->m_voiceRooms[comUnit == COM1 ? 0 : 1] = voiceRoom; } /* * Add temp.callsign for room */ void CVoiceVatlib::addTemporaryCallsignForRoom(const ComUnit /** comUnit **/, const CCallsign &callsign) { if (m_temporaryVoiceRoomCallsigns.contains(callsign)) return; m_temporaryVoiceRoomCallsigns.push_back(callsign); } /* * Forward exception as signal */ 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->statusMessage(CStatusMessage::getErrorMessage(msg, CStatusMessage::TypeAudio)); qDebug() << "NetworkNotConnectedException caught in " << caller << "\n" << e.what(); } catch (const VatlibException &e) { msg.append("VatlibException").append(" ").append(e.what()); emit this->statusMessage(CStatusMessage::getErrorMessage(msg, CStatusMessage::TypeAudio)); qFatal("VatlibException caught in %s\n%s", caller, e.what()); } catch (const std::exception &e) { msg.append("std::exception").append(" ").append(e.what()); emit this->statusMessage(CStatusMessage::getErrorMessage(msg, CStatusMessage::TypeAudio)); qFatal("std::exception caught in %s\n%s", caller, e.what()); } catch (...) { msg.append("unknown exception"); emit this->statusMessage(CStatusMessage::getErrorMessage(msg, CStatusMessage::TypeAudio)); qFatal("Unknown exception caught in %s", caller); } } // Change voice room status and emit signal void CVoiceVatlib::changeConnectionStatus(ComUnit comUnit, ConnectionStatus newStatus) { QWriteLocker lockForWriting(&m_lockConnectionStatus); ConnectionStatus currentStatus = m_connectionStatus.value(comUnit); if (newStatus != currentStatus) { if (newStatus == Connected) { CVoiceRoom vr = this->voiceRoomForUnit(comUnit); vr.setConnected(true); this->setVoiceRoomForUnit(comUnit, vr); } // disconnecting the voice room will already be // set in leave voice room m_connectionStatus.insert(comUnit, newStatus); emit connectionStatusChanged(comUnit, currentStatus, newStatus); } } } // namespace