diff --git a/samples/voiceclient/client.cpp b/samples/voiceclient/client.cpp index 73b090c93..c96d56838 100644 --- a/samples/voiceclient/client.cpp +++ b/samples/voiceclient/client.cpp @@ -23,6 +23,8 @@ Client::Client(QObject *parent) : m_inputDevice = m_voice->createInputDevice(); m_outputDevice = m_voice->createOutputDevice(); + m_voice->connectChannelOutputDevice(m_channelCom1.get(), m_outputDevice.get()); + using namespace BlackCore; connect(m_channelCom1.get(), &IVoiceChannel::connectionStatusChanged, this, &Client::connectionStatusChanged); connect(m_channelCom1.get(), &IVoiceChannel::audioStarted, this, &Client::audioStartedStream); @@ -163,15 +165,14 @@ void Client::listCallsignsCmd(QTextStream &args) void Client::enableLoopbackCmd(QTextStream &/*args*/) { std::cout << "Enabling audio loopback." << std::endl; - m_voice->enableAudioLoopback(true); + m_voice->enableAudioLoopback(m_inputDevice.get(), m_outputDevice.get()); printLinePrefix(); } void Client::disableLoopbackCmd(QTextStream &/*args*/) { std::cout << "Disabling audio loopback." << std::endl; - m_voice->enableAudioLoopback(false); - printLinePrefix(); + m_voice->enableAudioLoopback(m_inputDevice.get(), nullptr); printLinePrefix(); } diff --git a/src/blackcore/context_audio_impl.cpp b/src/blackcore/context_audio_impl.cpp index 2df5dfcc8..f2be23d06 100644 --- a/src/blackcore/context_audio_impl.cpp +++ b/src/blackcore/context_audio_impl.cpp @@ -39,6 +39,7 @@ namespace BlackCore // 2. Register PTT hotkey function m_inputManager = CInputManager::getInstance(); + m_handlePtt = m_inputManager->registerHotkeyFunc(CHotkeyFunction::Ptt(), this, &CContextAudio::ps_setVoiceTransmission); m_channelCom1 = m_voice->createVoiceChannel(); m_channelCom1->setMyAircraftCallsign(getIContextOwnAircraft()->getOwnAircraft().getCallsign()); @@ -50,6 +51,9 @@ namespace BlackCore m_voiceInputDevice = m_voice->createInputDevice(); m_voiceOutputDevice = m_voice->createOutputDevice(); + m_voice->connectChannelOutputDevice(m_channelCom1.get(), m_voiceOutputDevice.get()); + m_voice->connectChannelOutputDevice(m_channelCom2.get(), m_voiceOutputDevice.get()); + // 4. load sounds (init), not possible in own thread QTimer::singleShot(10 * 1000, this, SLOT(ps_initNotificationSounds())); } @@ -401,7 +405,10 @@ namespace BlackCore { Q_ASSERT(this->m_voice); CLogMessage(this, CLogCategory::contextSlot()).debug() << Q_FUNC_INFO; - m_voice->enableAudioLoopback(enable); + if (enable) + m_voice->enableAudioLoopback(m_voiceInputDevice.get(), m_voiceOutputDevice.get()); + else + m_voice->enableAudioLoopback(m_voiceInputDevice.get(), nullptr); } bool CContextAudio::parseCommandLine(const QString &commandLine) @@ -452,6 +459,13 @@ namespace BlackCore return false; } + void CContextAudio::ps_setVoiceTransmission(bool enable) + { + // FIXME: Use the 'active' channel instead of hardcoded COM1 + if (enable) m_voice->connectChannelInputDevice(m_voiceInputDevice.get(), m_channelCom1.get()); + else m_voice->connectChannelInputDevice(nullptr, m_channelCom1.get()); + } + /* * Connection status changed */ diff --git a/src/blackcore/context_audio_impl.h b/src/blackcore/context_audio_impl.h index 0135ce1dd..ece8a18a6 100644 --- a/src/blackcore/context_audio_impl.h +++ b/src/blackcore/context_audio_impl.h @@ -157,6 +157,8 @@ namespace BlackCore //! Init notification sounds void ps_initNotificationSounds(); + void ps_setVoiceTransmission(bool enable); + private: const int MinUnmuteVolume = 20; //!< minimum volume when unmuted const int VoiceRoomEnabledVolume = 95; //!< voice room volume when enabled diff --git a/src/blackcore/voice.h b/src/blackcore/voice.h index fea90fd47..dd0537645 100644 --- a/src/blackcore/voice.h +++ b/src/blackcore/voice.h @@ -47,11 +47,18 @@ namespace BlackCore //! Create output device object virtual std::unique_ptr createOutputDevice() = 0; + //! Connect voice channel to an audio output device + virtual void connectChannelOutputDevice(IVoiceChannel *channel, IAudioOutputDevice *device) = 0; + /*! - * \brief Enable audio loopback to route recorded voice from microphone to speakers - * \param enable (default true) + * Connect input device to voice channel to start/stop voice transmission + * \param device Audio input device, e.g. a microphone. Pass nullptr to stop transmission + * \param channel Voice channel audio should be transmitted to */ - virtual void enableAudioLoopback(bool enable = true) = 0; + virtual void connectChannelInputDevice(IAudioInputDevice *device, IVoiceChannel *channel) = 0; + + //! Connect audio input to an audio output to hear your own voice + virtual void enableAudioLoopback(IAudioInputDevice *input, IAudioOutputDevice *output) = 0; }; } // namespace BlackCore diff --git a/src/blackcore/voice_vatlib.cpp b/src/blackcore/voice_vatlib.cpp index c8ca6dbe1..85f16eb0e 100644 --- a/src/blackcore/voice_vatlib.cpp +++ b/src/blackcore/voice_vatlib.cpp @@ -53,6 +53,67 @@ namespace BlackCore return make_unique(m_audioService.data(), this); } + /* FIXME: + Can the following methods be more general somehow? + E.g.: + template + connectVoice(Input input, Output output) + { + ... + } + */ + + void CVoiceVatlib::connectChannelOutputDevice(IVoiceChannel *channel, IAudioOutputDevice *device) + { + auto voiceChannelVatlib = qobject_cast(channel); + Q_ASSERT_X(voiceChannelVatlib, "CVoiceVatlib::connectChannelOutputDevice", "No valid CVoiceChannelVatlib pointer."); + + if (!device) + { + Vat_VoiceConnect(voiceChannelVatlib->getVoiceChannel(), 0, nullptr, 0); + return; + } + + auto audioDeviceVatlib = qobject_cast(device); + Q_ASSERT_X(audioDeviceVatlib, "CVoiceVatlib::connectOutputDevice", "No valid CAudioOutputDeviceVatlib pointer."); + + Vat_VoiceConnect(voiceChannelVatlib->getVoiceChannel(), 0, audioDeviceVatlib->getVatLocalOutputCodec(), 0); + } + + void CVoiceVatlib::connectChannelInputDevice(IAudioInputDevice *device, IVoiceChannel *channel) + { + auto voiceChannelVatlib = qobject_cast(channel); + Q_ASSERT_X(voiceChannelVatlib, "CVoiceVatlib::connectChannelInputDevice", "No valid CVoiceChannelVatlib pointer."); + + if (!device) + { + Vat_VoiceConnect(voiceChannelVatlib->getVoiceChannel(), 0, nullptr, 0); + return; + } + + auto audioDeviceVatlib = qobject_cast(device); + Q_ASSERT_X(audioDeviceVatlib, "CVoiceVatlib::connectChannelInputDevice", "No valid CAudioInputDeviceVatlib pointer."); + + Vat_VoiceConnect(audioDeviceVatlib->getVatLocalInputCodec(), 0, voiceChannelVatlib->getVoiceChannel(), 0); + } + + void CVoiceVatlib::enableAudioLoopback(IAudioInputDevice *input, IAudioOutputDevice *output) + { + auto vatlibInput = qobject_cast(input); + Q_ASSERT_X(vatlibInput, "CVoiceVatlib::enableAudioLoopback", "No valid CAudioInputDeviceVatlib pointer."); + + if (!output) + { + Vat_VoiceConnect(vatlibInput->getVatLocalInputCodec(), 0, nullptr, 0); + return; + } + + auto vatlibOutput = qobject_cast(output); + Q_ASSERT_X(vatlibOutput, "CVoiceVatlib::enableAudioLoopback", "No valid CAudioOutputDeviceVatlib pointer."); + + Vat_VoiceConnect(vatlibInput->getVatLocalInputCodec(), 0, vatlibOutput->getVatLocalOutputCodec(), 0); + } + /* * Process voice handling */ diff --git a/src/blackcore/voice_vatlib.h b/src/blackcore/voice_vatlib.h index 8a9dfac18..bdc4d3a26 100644 --- a/src/blackcore/voice_vatlib.h +++ b/src/blackcore/voice_vatlib.h @@ -50,9 +50,14 @@ namespace BlackCore //! \copydoc IVoice::createOutputDevice() virtual std::unique_ptr createOutputDevice() override; - //! \copydoc IVoice::enableAudioLoopback - virtual void enableAudioLoopback(bool enable = true) override; + //! \copydoc IVoice::connectChannelOutputDevice() + virtual void connectChannelOutputDevice(IVoiceChannel *channel, IAudioOutputDevice *device) override; + //! \copydoc IVoice::connectChannelInputDevice() + virtual void connectChannelInputDevice(IAudioInputDevice *device, IVoiceChannel *channel) override; + + //! \copydoc IVoice::enableAudioLoopback() + virtual void enableAudioLoopback(IAudioInputDevice *input, IAudioOutputDevice *output) override; protected: // QObject overrides @@ -84,8 +89,6 @@ namespace BlackCore QScopedPointer m_audioService; QScopedPointer m_udpPort; - bool m_isAudioLoopbackEnabled; /*!< A flag whether audio loopback is enabled or not */ - }; } // namespace