From 5d60f09df4b59c3a42c7544fd92c78710dd9d5b0 Mon Sep 17 00:00:00 2001 From: sudden6 Date: Thu, 27 Apr 2017 00:06:26 +0200 Subject: [PATCH] feat(audio): make echo cancellation work and improve some minor stuff --- src/audio/audio.h | 2 +- src/audio/backend/openal2.cpp | 146 ++++++++++++++++++---------------- src/audio/backend/openal2.h | 2 + 3 files changed, 82 insertions(+), 68 deletions(-) diff --git a/src/audio/audio.h b/src/audio/audio.h index 647525f45..2b78b84d4 100644 --- a/src/audio/audio.h +++ b/src/audio/audio.h @@ -100,7 +100,7 @@ public: static constexpr uint32_t AUDIO_FRAME_DURATION = 20; static constexpr uint32_t AUDIO_FRAME_SAMPLE_COUNT = AUDIO_FRAME_DURATION * AUDIO_SAMPLE_RATE / 1000; - static constexpr uint32_t AUDIO_CHANNELS = 2; + static constexpr uint32_t AUDIO_CHANNELS = 1; signals: void frameAvailable(const int16_t* pcm, size_t sample_count, uint8_t channels, diff --git a/src/audio/backend/openal2.cpp b/src/audio/backend/openal2.cpp index 5228102a5..efbf0a8a8 100644 --- a/src/audio/backend/openal2.cpp +++ b/src/audio/backend/openal2.cpp @@ -316,7 +316,7 @@ bool OpenAL2::initOutput(const QString& deviceName) } // check for the needed extensions for echo cancelation - if (echoCancelSupported = (alcIsExtensionPresent(NULL, "ALC_SOFT_loopback") == AL_TRUE)) { + if (echoCancelSupported = (alcIsExtensionPresent(alOutDev, "ALC_SOFT_loopback") == AL_TRUE)) { qDebug() << "Device supports loopback"; } if (alIsExtensionPresent("AL_SOFT_source_latency") == AL_TRUE) { @@ -540,98 +540,89 @@ void OpenAL2::playMono16SoundCleanup() } /** - * @brief Called on the captureTimer events to capture audio + * @brief Handle audio output */ -void OpenAL2::doAudio() +void OpenAL2::doOutput() { - QMutexLocker lock(&audioLock); + alcMakeContextCurrent(alOutContext); + ALuint bufids[PROXY_BUFFER_COUNT]; + ALint processed = 0, queued = 0; + alGetSourcei(alProxySource, AL_BUFFERS_PROCESSED, &processed); + alGetSourcei(alProxySource, AL_BUFFERS_QUEUED, &queued); - static int outBufcnt = 0; - static double outLatency = 0; + //qDebug() << "Speedtest processed: " << processed << " queued: " << queued; - // output section - if(echoCancelSupported && outputInitialized) { - alcMakeContextCurrent(alOutContext); - ALuint bufids[PROXY_BUFFER_COUNT]; - ALint processed = 0, queued = 0; - alGetSourcei(alProxySource, AL_BUFFERS_PROCESSED, &processed); - alGetSourcei(alProxySource, AL_BUFFERS_QUEUED, &queued); + if (processed > 0) { + // unqueue all processed buffers + alSourceUnqueueBuffers(alProxySource, 1, bufids); + } else if (queued < PROXY_BUFFER_COUNT) { + // create new buffer until the maximum is reached + alGenBuffers(1, bufids); + } else { + return; + } - //qDebug() << "Speedtest processed: " << processed << " queued: " << queued; - - if (processed > 0) { - // unqueue all processed buffers - alSourceUnqueueBuffers(alProxySource, 1, bufids); - } else if (queued < PROXY_BUFFER_COUNT) { - // create new buffer until the maximum is reached - alGenBuffers(1, bufids); - } else { - return; - } - - LPALGETSOURCEDVSOFT alGetSourcedvSOFT = - reinterpret_cast (alcGetProcAddress(alOutDev, "alGetSourcedvSOFT")); - ALdouble latency[2] = {0}; + LPALGETSOURCEDVSOFT alGetSourcedvSOFT = + reinterpret_cast (alcGetProcAddress(alOutDev, "alGetSourcedvSOFT")); + ALdouble latency[2] = {0}; + if(alGetSourcedvSOFT) { alGetSourcedvSOFT(alProxySource, AL_SEC_OFFSET_LATENCY_SOFT, latency); checkAlError(); - //qDebug() << "Playback latency: " << latency[1] << "offset: " << latency[0]; - outLatency = latency[1]; + } + //qDebug() << "Playback latency: " << latency[1] << "offset: " << latency[0]; - ALshort outBuf[AUDIO_FRAME_SAMPLE_COUNT * AUDIO_CHANNELS] = {0}; - alcMakeContextCurrent(alProxyContext); - LPALCRENDERSAMPLESSOFT alcRenderSamplesSOFT = - reinterpret_cast (alcGetProcAddress(alOutDev, "alcRenderSamplesSOFT")); - alcRenderSamplesSOFT(alProxyDev, outBuf, AUDIO_FRAME_SAMPLE_COUNT); + ALshort outBuf[AUDIO_FRAME_SAMPLE_COUNT * AUDIO_CHANNELS] = {0}; + alcMakeContextCurrent(alProxyContext); + LPALCRENDERSAMPLESSOFT alcRenderSamplesSOFT = + reinterpret_cast (alcGetProcAddress(alOutDev, "alcRenderSamplesSOFT")); + alcRenderSamplesSOFT(alProxyDev, outBuf, AUDIO_FRAME_SAMPLE_COUNT); - alcMakeContextCurrent(alOutContext); - alBufferData(bufids[0], (AUDIO_CHANNELS == 1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16, outBuf, - AUDIO_FRAME_SAMPLE_COUNT * 2 * AUDIO_CHANNELS, AUDIO_SAMPLE_RATE); - alSourceQueueBuffers(alProxySource, 1, bufids); - ++outBufcnt; + alcMakeContextCurrent(alOutContext); + alBufferData(bufids[0], (AUDIO_CHANNELS == 1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16, outBuf, + AUDIO_FRAME_SAMPLE_COUNT * 2 * AUDIO_CHANNELS, AUDIO_SAMPLE_RATE); + alSourceQueueBuffers(alProxySource, 1, bufids); - // initialize echo canceler if supported - if(!filterer) { - filterer = new_filter_audio(AUDIO_SAMPLE_RATE); - int16_t filterLatency = outLatency*1000 + AUDIO_FRAME_DURATION; - qDebug() << "Setting filter delay to: " << filterLatency << "ms"; - set_echo_delay_ms(filterer, filterLatency); - } - - // do echo cancel - pass_audio_output(filterer, outBuf, AUDIO_FRAME_SAMPLE_COUNT); - - ALint state; - alGetSourcei(alProxySource, AL_SOURCE_STATE, &state); - if (state != AL_PLAYING) { - qDebug() << "Proxy source underflow detected"; - alSourcePlay(alProxySource); - } + // initialize echo canceler if supported + if(!filterer) { + filterer = new_filter_audio(AUDIO_SAMPLE_RATE); + int16_t filterLatency = latency[1]*1000*2 + AUDIO_FRAME_DURATION; + qDebug() << "Setting filter delay to: " << filterLatency << "ms"; + set_echo_delay_ms(filterer, filterLatency); + enable_disable_filters(filterer, 1, 1, 1, 0); } - // input section + // do echo cancel + int retVal = pass_audio_output(filterer, outBuf, AUDIO_FRAME_SAMPLE_COUNT); - if (!alInDev || !inSubscriptions) - return; + ALint state; + alGetSourcei(alProxySource, AL_SOURCE_STATE, &state); + if (state != AL_PLAYING) { + qDebug() << "Proxy source underflow detected"; + alSourcePlay(alProxySource); + } +} +/** + * @brief handles recording of audio frames + */ +void OpenAL2::doInput() +{ ALint curSamples = 0; alcGetIntegerv(alInDev, ALC_CAPTURE_SAMPLES, sizeof(curSamples), &curSamples); - if (curSamples < AUDIO_FRAME_SAMPLE_COUNT) + if (curSamples < AUDIO_FRAME_SAMPLE_COUNT) { return; - // check for dropped buffers in echo cancel - if(outBufcnt > 1) { - qDebug() << "Echo cancel frame dropped"; } - outBufcnt = 0; int16_t buf[AUDIO_FRAME_SAMPLE_COUNT * AUDIO_CHANNELS]; alcCaptureSamples(alInDev, buf, AUDIO_FRAME_SAMPLE_COUNT); + int retVal = 0; if(echoCancelSupported && filterer) { - filter_audio(filterer, buf, AUDIO_FRAME_SAMPLE_COUNT); + retVal = filter_audio(filterer, buf, AUDIO_FRAME_SAMPLE_COUNT); } + // gain amplification with clipping to 16-bit boundaries for (quint32 i = 0; i < AUDIO_FRAME_SAMPLE_COUNT * AUDIO_CHANNELS; ++i) { - // gain amplification with clipping to 16-bit boundaries int ampPCM = qBound(std::numeric_limits::min(), qRound(buf[i] * inputGainFactor()), std::numeric_limits::max()); @@ -642,6 +633,27 @@ void OpenAL2::doAudio() emit Audio::frameAvailable(buf, AUDIO_FRAME_SAMPLE_COUNT, AUDIO_CHANNELS, AUDIO_SAMPLE_RATE); } +/** + * @brief Called on the captureTimer events to capture audio + */ +void OpenAL2::doAudio() +{ + QMutexLocker lock(&audioLock); + + // output section + if(echoCancelSupported && outputInitialized && !peerSources.isEmpty()) { + doOutput(); + } else { + kill_filter_audio(filterer); + filterer = nullptr; + } + + // input section + if (alInDev && inSubscriptions) { + doInput(); + } +} + /** * @brief Returns true if the output device is open */ diff --git a/src/audio/backend/openal2.h b/src/audio/backend/openal2.h index 921324d1e..37e3ad3d0 100644 --- a/src/audio/backend/openal2.h +++ b/src/audio/backend/openal2.h @@ -106,6 +106,8 @@ private: void playMono16SoundCleanup(); void doAudio(); qreal inputGainFactor() const; + void doInput(); + void doOutput(); private: QThread* audioThread;