diff --git a/src/mumble/AudioConfigDialog.cpp b/src/mumble/AudioConfigDialog.cpp index 9df347bd670..6381f9ed941 100644 --- a/src/mumble/AudioConfigDialog.cpp +++ b/src/mumble/AudioConfigDialog.cpp @@ -586,7 +586,7 @@ void AudioInputDialog::showSpeexNoiseSuppressionSlider(bool show) { void AudioInputDialog::on_Tick_timeout() { AudioInputPtr ai = Global::get().ai; - if (!ai.get() || !ai->sppPreprocess) + if (!ai.get() || !ai->m_preprocessor) return; abSpeech->iBelow = qsTransmitMin->value(); diff --git a/src/mumble/AudioInput.cpp b/src/mumble/AudioInput.cpp index 0bb0337cdf3..3dc020c08b7 100644 --- a/src/mumble/AudioInput.cpp +++ b/src/mumble/AudioInput.cpp @@ -255,8 +255,7 @@ AudioInput::AudioInput() bEchoMulti = false; - sppPreprocess = nullptr; - sesEcho = nullptr; + sesEcho = nullptr; srsMic = srsEcho = nullptr; iEchoChannels = iMicChannels = 0; @@ -298,8 +297,6 @@ AudioInput::~AudioInput() { } #endif - if (sppPreprocess) - speex_preprocess_state_destroy(sppPreprocess); if (sesEcho) speex_echo_state_destroy(sesEcho); @@ -740,44 +737,34 @@ void AudioInput::resetAudioProcessor() { if (!bResetProcessor) return; - int iArg; - - if (sppPreprocess) - speex_preprocess_state_destroy(sppPreprocess); if (sesEcho) speex_echo_state_destroy(sesEcho); - sppPreprocess = speex_preprocess_state_init(iFrameSize, iSampleRate); + m_preprocessor.init(iSampleRate, iFrameSize); resync.reset(); selectNoiseCancel(); - iArg = 1; - speex_preprocess_ctl(sppPreprocess, SPEEX_PREPROCESS_SET_VAD, &iArg); - speex_preprocess_ctl(sppPreprocess, SPEEX_PREPROCESS_SET_AGC, &iArg); - speex_preprocess_ctl(sppPreprocess, SPEEX_PREPROCESS_SET_DEREVERB, &iArg); - - iArg = 30000; - speex_preprocess_ctl(sppPreprocess, SPEEX_PREPROCESS_SET_AGC_TARGET, &iArg); + m_preprocessor.toggleVAD(true); + m_preprocessor.toggleAGC(true); + m_preprocessor.toggleDereverb(true); - float v = 30000.0f / static_cast< float >(Global::get().s.iMinLoudness); - iArg = static_cast< int >(floorf(20.0f * log10f(v))); - speex_preprocess_ctl(sppPreprocess, SPEEX_PREPROCESS_SET_AGC_MAX_GAIN, &iArg); + m_preprocessor.setAGCTarget(30000); - iArg = -60; - speex_preprocess_ctl(sppPreprocess, SPEEX_PREPROCESS_SET_AGC_DECREMENT, &iArg); + const float v = 30000.0f / static_cast< float >(Global::get().s.iMinLoudness); + m_preprocessor.setAGCMaxGain(static_cast< std::int32_t >(floorf(20.0f * log10f(v)))); + m_preprocessor.setAGCDecrement(-60); if (noiseCancel == Settings::NoiseCancelSpeex) { - iArg = Global::get().s.iSpeexNoiseCancelStrength; - speex_preprocess_ctl(sppPreprocess, SPEEX_PREPROCESS_SET_NOISE_SUPPRESS, &iArg); + m_preprocessor.setNoiseSuppress(Global::get().s.iSpeexNoiseCancelStrength); } if (iEchoChannels > 0) { int filterSize = iFrameSize * (10 + resync.getNominalLag()); sesEcho = speex_echo_state_init_mc(iFrameSize, filterSize, 1, bEchoMulti ? static_cast< int >(iEchoChannels) : 1); - iArg = iSampleRate; + int iArg = iSampleRate; speex_echo_ctl(sesEcho, SPEEX_ECHO_SET_SAMPLING_RATE, &iArg); - speex_preprocess_ctl(sppPreprocess, SPEEX_PREPROCESS_SET_ECHO_STATE, sesEcho); + m_preprocessor.setEchoState(sesEcho); qWarning("AudioInput: ECHO CANCELLER ACTIVE"); } else { @@ -821,24 +808,24 @@ void AudioInput::selectNoiseCancel() { #endif } - int iArg = 0; + bool preprocessorDenoise = false; switch (noiseCancel) { case Settings::NoiseCancelOff: qWarning("AudioInput: Noise canceller disabled"); break; case Settings::NoiseCancelSpeex: qWarning("AudioInput: Using Speex as noise canceller"); - iArg = 1; + preprocessorDenoise = true; break; case Settings::NoiseCancelRNN: qWarning("AudioInput: Using ReNameNoise as noise canceller"); break; case Settings::NoiseCancelBoth: - iArg = 1; + preprocessorDenoise = true; qWarning("AudioInput: Using ReNameNoise and Speex as noise canceller"); break; } - speex_preprocess_ctl(sppPreprocess, SPEEX_PREPROCESS_SET_DENOISE, &iArg); + m_preprocessor.toggleDenoise(preprocessorDenoise); } int AudioInput::encodeOpusFrame(short *source, int size, EncodingOutputBuffer &buffer) { @@ -857,7 +844,6 @@ int AudioInput::encodeOpusFrame(short *source, int size, EncodingOutputBuffer &b } void AudioInput::encodeAudioFrame(AudioChunk chunk) { - int iArg; float sum; short max; @@ -897,11 +883,10 @@ void AudioInput::encodeAudioFrame(AudioChunk chunk) { QMutexLocker l(&qmSpeex); resetAudioProcessor(); - speex_preprocess_ctl(sppPreprocess, SPEEX_PREPROCESS_GET_AGC_GAIN, &iArg); - float gainValue = static_cast< float >(iArg); + const std::int32_t gainValue = m_preprocessor.getAGCGain(); + if (noiseCancel == Settings::NoiseCancelSpeex || noiseCancel == Settings::NoiseCancelBoth) { - iArg = Global::get().s.iSpeexNoiseCancelStrength - iArg; - speex_preprocess_ctl(sppPreprocess, SPEEX_PREPROCESS_SET_NOISE_SUPPRESS, &iArg); + m_preprocessor.setNoiseSuppress(Global::get().s.iSpeexNoiseCancelStrength - gainValue); } short psClean[iFrameSize]; @@ -924,7 +909,7 @@ void AudioInput::encodeAudioFrame(AudioChunk chunk) { } #endif - speex_preprocess_run(sppPreprocess, psSource); + m_preprocessor.run(*psSource); sum = 1.0f; for (unsigned int i = 0; i < iFrameSize; i++) @@ -942,12 +927,10 @@ void AudioInput::encodeAudioFrame(AudioChunk chunk) { static_cast< std::streamsize >(iFrameSize * sizeof(short))); } - spx_int32_t prob = 0; - speex_preprocess_ctl(sppPreprocess, SPEEX_PREPROCESS_GET_PROB, &prob); - fSpeechProb = static_cast< float >(prob) / 100.0f; + fSpeechProb = static_cast< float >(m_preprocessor.getSpeechProb()) / 100.0f; // clean microphone level: peak of filtered signal attenuated by AGC gain - dPeakCleanMic = qMax(dPeakSignal - gainValue, -96.0f); + dPeakCleanMic = qMax(dPeakSignal - static_cast< float >(gainValue), -96.0f); float level = (Global::get().s.vsVAD == Settings::SignalToNoise) ? fSpeechProb : (1.0f + dPeakCleanMic / 96.0f); bool bIsSpeech = false; @@ -1075,12 +1058,10 @@ void AudioInput::encodeAudioFrame(AudioChunk chunk) { } } - spx_int32_t increment = 0; - speex_preprocess_ctl(sppPreprocess, SPEEX_PREPROCESS_SET_AGC_INCREMENT, &increment); + m_preprocessor.setAGCIncrement(0); return; } else { - spx_int32_t increment = 12; - speex_preprocess_ctl(sppPreprocess, SPEEX_PREPROCESS_SET_AGC_INCREMENT, &increment); + m_preprocessor.setAGCIncrement(12); } if (bIsSpeech && !bPreviousVoice) { diff --git a/src/mumble/AudioInput.h b/src/mumble/AudioInput.h index 5598feda6aa..d8f09637389 100644 --- a/src/mumble/AudioInput.h +++ b/src/mumble/AudioInput.h @@ -21,11 +21,11 @@ #include #include -#include #include #include "Audio.h" #include "AudioOutputToken.h" +#include "AudioPreprocessor.h" #include "EchoCancelOption.h" #include "MumbleProtocol.h" #include "Settings.h" @@ -224,7 +224,7 @@ class AudioInput : public QThread { static const int iFrameSize = SAMPLE_RATE / 100; QMutex qmSpeex; - SpeexPreprocessState *sppPreprocess; + AudioPreprocessor m_preprocessor; SpeexEchoState *sesEcho; /// bResetEncoder is a flag that notifies diff --git a/src/mumble/AudioPreprocessor.cpp b/src/mumble/AudioPreprocessor.cpp new file mode 100644 index 00000000000..d5a256ef075 --- /dev/null +++ b/src/mumble/AudioPreprocessor.cpp @@ -0,0 +1,178 @@ +// Copyright 2024 The Mumble Developers. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file at the root of the +// Mumble source tree or at . + +#include "AudioPreprocessor.h" + +#include + +#include + +AudioPreprocessor::AudioPreprocessor(AudioPreprocessor &&other) : m_handle(std::exchange(other.m_handle, nullptr)) { +} + +AudioPreprocessor::~AudioPreprocessor() { + deinit(); +} + +AudioPreprocessor &AudioPreprocessor::operator=(AudioPreprocessor &&other) { + m_handle = std::exchange(other.m_handle, nullptr); + return *this; +} + +bool AudioPreprocessor::init(const std::uint32_t sampleRate, const std::uint32_t quantum) { + deinit(); + + m_handle = speex_preprocess_state_init(static_cast< int >(quantum), static_cast< int >(sampleRate)); + return m_handle != nullptr; +} + +void AudioPreprocessor::deinit() { + if (m_handle) { + speex_preprocess_state_destroy(m_handle); + } +} + +bool AudioPreprocessor::run(std::int16_t &samples) { + return speex_preprocess_run(m_handle, &samples); +} + +SpeexEchoState_ *AudioPreprocessor::getEchoState() { + SpeexEchoState_ *handle; + return speex_preprocess_ctl(m_handle, SPEEX_PREPROCESS_GET_ECHO_STATE, &handle) == 0 ? handle : nullptr; +} + +bool AudioPreprocessor::setEchoState(SpeexEchoState_ *handle) { + return speex_preprocess_ctl(m_handle, SPEEX_PREPROCESS_SET_ECHO_STATE, handle) == 0; +} + +bool AudioPreprocessor::usesAGC() const { + return getBool(SPEEX_PREPROCESS_GET_AGC); +} + +bool AudioPreprocessor::toggleAGC(const bool enable) { + return setBool(SPEEX_PREPROCESS_SET_AGC, enable); +} + +std::int32_t AudioPreprocessor::getAGCDecrement() const { + return getInt32(SPEEX_PREPROCESS_GET_AGC_DECREMENT); +} + +bool AudioPreprocessor::setAGCDecrement(const std::int32_t value) { + return setInt32(SPEEX_PREPROCESS_SET_AGC_DECREMENT, value); +} + +std::int32_t AudioPreprocessor::getAGCGain() const { + return getInt32(SPEEX_PREPROCESS_GET_AGC_GAIN); +} + +std::int32_t AudioPreprocessor::getAGCIncrement() const { + return getInt32(SPEEX_PREPROCESS_GET_AGC_INCREMENT); +} + +bool AudioPreprocessor::setAGCIncrement(const std::int32_t value) { + return setInt32(SPEEX_PREPROCESS_SET_AGC_INCREMENT, value); +} + +std::int32_t AudioPreprocessor::getAGCMaxGain() const { + return getInt32(SPEEX_PREPROCESS_GET_AGC_MAX_GAIN); +} + +bool AudioPreprocessor::setAGCMaxGain(const std::int32_t value) { + return setInt32(SPEEX_PREPROCESS_SET_AGC_MAX_GAIN, value); +} + +std::int32_t AudioPreprocessor::getAGCTarget() const { + return getInt32(SPEEX_PREPROCESS_GET_AGC_TARGET); +} + +bool AudioPreprocessor::setAGCTarget(const std::int32_t value) { + return setInt32(SPEEX_PREPROCESS_SET_AGC_TARGET, value); +} + +bool AudioPreprocessor::usesDenoise() const { + return getBool(SPEEX_PREPROCESS_GET_DENOISE); +} + +bool AudioPreprocessor::toggleDenoise(const bool enable) { + return setBool(SPEEX_PREPROCESS_SET_DENOISE, enable); +} + +bool AudioPreprocessor::usesDereverb() const { + return getBool(SPEEX_PREPROCESS_GET_DEREVERB); +} + +bool AudioPreprocessor::toggleDereverb(const bool enable) { + return setBool(SPEEX_PREPROCESS_SET_DEREVERB, enable); +} + +std::int32_t AudioPreprocessor::getNoiseSuppress() const { + return getInt32(SPEEX_PREPROCESS_GET_NOISE_SUPPRESS); +} + +bool AudioPreprocessor::setNoiseSuppress(const std::int32_t value) { + return setInt32(SPEEX_PREPROCESS_SET_NOISE_SUPPRESS, value); +} + +AudioPreprocessor::psd_t AudioPreprocessor::getPSD() const { + const auto size = getInt32(SPEEX_PREPROCESS_GET_PSD_SIZE); + if (!size) { + return {}; + } + + psd_t ret(static_cast< size_t >(size)); + if (speex_preprocess_ctl(m_handle, SPEEX_PREPROCESS_GET_PSD, ret.data()) != 0) { + return {}; + } + + return ret; +} + +AudioPreprocessor::psd_t AudioPreprocessor::getNoisePSD() const { + const auto size = getInt32(SPEEX_PREPROCESS_GET_PSD_SIZE); + if (!size) { + return {}; + } + + psd_t ret(static_cast< size_t >(size)); + if (speex_preprocess_ctl(m_handle, SPEEX_PREPROCESS_GET_NOISE_PSD, ret.data()) != 0) { + return {}; + } + + return ret; +} + +std::int32_t AudioPreprocessor::getSpeechProb() const { + return getInt32(SPEEX_PREPROCESS_GET_PROB); +} + +bool AudioPreprocessor::usesVAD() const { + return getBool(SPEEX_PREPROCESS_GET_VAD); +} + +bool AudioPreprocessor::toggleVAD(const bool enable) { + return setBool(SPEEX_PREPROCESS_SET_VAD, enable); +} + +bool AudioPreprocessor::getBool(const int op) const { + const auto val = getInt32(op); + return static_cast< bool >(val); +} + +bool AudioPreprocessor::setBool(const int op, const bool value) { + return setInt32(op, value); +} + +std::int32_t AudioPreprocessor::getInt32(const int op) const { + spx_int32_t value; + if (speex_preprocess_ctl(m_handle, op, &value) != 0) { + return 0; + } + + return value; +} + +bool AudioPreprocessor::setInt32(const int op, std::int32_t value) { + return speex_preprocess_ctl(m_handle, op, &value) == 0; +} diff --git a/src/mumble/AudioPreprocessor.h b/src/mumble/AudioPreprocessor.h new file mode 100644 index 00000000000..c89c459b726 --- /dev/null +++ b/src/mumble/AudioPreprocessor.h @@ -0,0 +1,215 @@ +// Copyright 2024 The Mumble Developers. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file at the root of the +// Mumble source tree or at . + +#ifndef MUMBLE_MUMBLE_AUDIOPREPROCESSOR_H_ +#define MUMBLE_MUMBLE_AUDIOPREPROCESSOR_H_ + +#include +#include + +struct SpeexEchoState_; +struct SpeexPreprocessState_; + +class AudioPreprocessor { +public: + using psd_t = std::vector< std::int32_t >; + + /** + * \return Whether the object is initialized and all its methods can be safely called. + */ + constexpr explicit operator bool() const { return m_handle != nullptr; } + + AudioPreprocessor() = default; + AudioPreprocessor(AudioPreprocessor &&other); + ~AudioPreprocessor(); + + AudioPreprocessor &operator=(AudioPreprocessor &&other); + + /** + * Initializes the object. This must be called at least once before any other function! + * + * \param sampleRate The sample rate in Hz. + * + * \param quantum The number of samples that are expected for each preprocess cycle. + * Should correspond to 10-20 ms (480-960 with a sample rate of 48000). + * + * \return Whether initialization succeeded. + */ + bool init(std::uint32_t sampleRate, std::uint32_t quantum); + /** + * Deinitializes the object, doesn't do anything if it's not initialized. + * Once that is done no function other than init() can be called. + */ + void deinit(); + + /** + * Runs the preprocessor, which analyzes and modifies the samples. + * + * \param samples The samples that should be processed (modified in-place). + * + * \return Whether speech was detected, if VAD is enabled. Always 'true' otherwise. + */ + bool run(std::int16_t &samples); + + /** + * \return Handle to the echo state object. + */ + SpeexEchoState_ *getEchoState(); + /** + * Set the echo state object, to toggle the echo canceller. + * + * \param handle Handle to the echo state. Passing 'nullptr' disables the feature. + * + * \return Whether the operation succeeded. + */ + bool setEchoState(SpeexEchoState_ *handle); + + /** + * \return Whether automatic gain control is enabled. + */ + bool usesAGC() const; + /** + * Toggles automatic gain control. + * + * \param enable 'true' to enable the feature, otherwise 'false'. + * + * \return Whether the operation succeeded. + */ + bool toggleAGC(bool enable); + + /** + * \return The maximal gain decrease in dB/second. + */ + std::int32_t getAGCDecrement() const; + /** + * Set the maximal gain decrease in dB/second. + * + * \param value Maximal gain decrease in dB/second. + * + * \return Whether the operation succeeded. + */ + bool setAGCDecrement(std::int32_t value); + /** + * \return The current gain in percentual scale (0-100). + */ + std::int32_t getAGCGain() const; + + /** + * \return The maximal gain increase in dB/second. + */ + std::int32_t getAGCIncrement() const; + /** + * Set the maximal gain increase in dB/second. + * + * \param value Maximal gain increase in dB/second. + * + * \return Whether the operation succeeded. + */ + bool setAGCIncrement(std::int32_t value); + /** + * \return The maximal gain in dB. + */ + std::int32_t getAGCMaxGain() const; + /** + * Set the maximal gain in dB. + * + * \param value Maximal gain in dB. + * + * \return Whether the operation succeeded. + */ + bool setAGCMaxGain(std::int32_t value); + /** + * \return The automatic gain control level, in a scale from 1 to 32768. + */ + std::int32_t getAGCTarget() const; + /** + * Set the automatic gain control level. + * + * \param value Automatic gain control level, in a scale from 1 to 32768. + * + * \return Whether the operation succeeded. + */ + bool setAGCTarget(std::int32_t value); + + /** + * \return Whether denoise is enabled. + */ + bool usesDenoise() const; + /** + * Toggles denoise. + * + * \param enable 'true' to enable the feature, otherwise 'false'. + * + * \return Whether the operation succeeded. + */ + bool toggleDenoise(bool enable); + + /** + * \return Whether dereverb is enabled. + */ + bool usesDereverb() const; + /** + * Toggles dereverb. + * + * \param enable 'true' to enable the feature, otherwise 'false'. + * + * \return Whether the operation succeeded. + */ + bool toggleDereverb(bool enable); + + /** + * \return The maximum attenuation of the noise in dB (negative number). + */ + std::int32_t getNoiseSuppress() const; + /** + * Set the maximum attenuation of the noise in dB. + * + * \param value Maximum attenuation of the noise in dB (negative number). + * + * \return Whether the operation succeeded. + */ + bool setNoiseSuppress(std::int32_t value); + + /** + * \return The power spectrum (vector of squared values). + */ + psd_t getPSD() const; + /** + * \return The noise estimate (vector of squared values). + */ + psd_t getNoisePSD() const; + + /** + * \return The amount of probability there was speech in the last processed frame, in percentual scale (0-100). + */ + std::int32_t getSpeechProb() const; + + /** + * \return Whether voice activity detection is enabled. + */ + bool usesVAD() const; + /** + * Toggles voice activity detection. + * + * \param enable 'true' to enable the feature, otherwise 'false'. + * + * \return Whether the operation succeeded. + */ + bool toggleVAD(bool enable); + +private: + AudioPreprocessor(const AudioPreprocessor &) = delete; + AudioPreprocessor &operator=(const AudioPreprocessor &) = delete; + + bool getBool(int op) const; + bool setBool(int op, bool value); + + std::int32_t getInt32(int op) const; + bool setInt32(int op, std::int32_t value); + + SpeexPreprocessState_ *m_handle = nullptr; +}; + +#endif diff --git a/src/mumble/AudioStats.cpp b/src/mumble/AudioStats.cpp index c75ee90ff92..290700e3072 100644 --- a/src/mumble/AudioStats.cpp +++ b/src/mumble/AudioStats.cpp @@ -216,34 +216,28 @@ void AudioNoiseWidget::paintEvent(QPaintEvent *) { paint.fillRect(rect(), pal.color(QPalette::Window)); AudioInputPtr ai = Global::get().ai; - if (!ai.get() || !ai->sppPreprocess) + if (!ai.get() || !ai->m_preprocessor) return; QPolygonF poly; ai->qmSpeex.lock(); - spx_int32_t ps_size = 0; - speex_preprocess_ctl(ai->sppPreprocess, SPEEX_PREPROCESS_GET_PSD_SIZE, &ps_size); - - static std::vector< spx_int32_t > noise; - noise.resize(static_cast< std::size_t >(ps_size)); - static std::vector< spx_int32_t > ps; - ps.resize(static_cast< std::size_t >(ps_size)); - - speex_preprocess_ctl(ai->sppPreprocess, SPEEX_PREPROCESS_GET_PSD, ps.data()); - speex_preprocess_ctl(ai->sppPreprocess, SPEEX_PREPROCESS_GET_NOISE_PSD, noise.data()); + const AudioPreprocessor::psd_t ps = ai->m_preprocessor.getPSD(); + const AudioPreprocessor::psd_t noise = ai->m_preprocessor.getNoisePSD(); ai->qmSpeex.unlock(); + assert(ps.size() == noise.size()); + qreal sx, sy; - sx = (static_cast< float >(width()) - 1.0f) / static_cast< float >(ps_size); + sx = (static_cast< float >(width()) - 1.0f) / static_cast< float >(ps.size()); sy = static_cast< float >(height()) - 1.0f; poly << QPointF(0.0f, height() - 1); float fftmul = 1.0 / (32768.0); - for (unsigned int i = 0; i < static_cast< unsigned int >(ps_size); i++) { + for (unsigned int i = 0; i < static_cast< unsigned int >(noise.size()); i++) { qreal xp, yp; xp = i * sx; yp = sqrtf(sqrtf(static_cast< float >(noise[i]))) - 1.0f; @@ -262,7 +256,7 @@ void AudioNoiseWidget::paintEvent(QPaintEvent *) { poly.clear(); - for (unsigned int i = 0; i < static_cast< unsigned int >(ps_size); i++) { + for (unsigned int i = 0; i < static_cast< unsigned int >(ps.size()); i++) { qreal xp, yp; xp = i * sx; yp = sqrtf(sqrtf(static_cast< float >(ps[i]))) - 1.0f; @@ -316,7 +310,7 @@ AudioStats::~AudioStats() { void AudioStats::on_Tick_timeout() { AudioInputPtr ai = Global::get().ai; - if (!ai.get() || !ai->sppPreprocess) + if (!ai.get() || !ai->m_preprocessor) return; bool nTalking = ai->isTransmitting(); @@ -332,22 +326,16 @@ void AudioStats::on_Tick_timeout() { FORMAT_TO_TXT("%06.2f dB", ai->dPeakSignal); qlSignalLevel->setText(txt); - spx_int32_t ps_size = 0; - speex_preprocess_ctl(ai->sppPreprocess, SPEEX_PREPROCESS_GET_PSD_SIZE, &ps_size); - - static std::vector< spx_int32_t > noise; - noise.resize(static_cast< std::size_t >(ps_size)); - static std::vector< spx_int32_t > ps; - ps.resize(static_cast< std::size_t >(ps_size)); + const AudioPreprocessor::psd_t ps = ai->m_preprocessor.getPSD(); + const AudioPreprocessor::psd_t noise = ai->m_preprocessor.getNoisePSD(); - speex_preprocess_ctl(ai->sppPreprocess, SPEEX_PREPROCESS_GET_PSD, ps.data()); - speex_preprocess_ctl(ai->sppPreprocess, SPEEX_PREPROCESS_GET_NOISE_PSD, noise.data()); + assert(ps.size() == noise.size()); float s = 0.0f; float n = 0.0001f; - unsigned int start = static_cast< unsigned int >(ps_size * 300) / SAMPLE_RATE; - unsigned int stop = static_cast< unsigned int >(ps_size * 2000) / SAMPLE_RATE; + unsigned int start = static_cast< unsigned int >(ps.size() * 300) / SAMPLE_RATE; + unsigned int stop = static_cast< unsigned int >(ps.size() * 2000) / SAMPLE_RATE; for (unsigned int i = start; i < stop; i++) { s += sqrtf(static_cast< float >(ps[i])); @@ -357,9 +345,7 @@ void AudioStats::on_Tick_timeout() { FORMAT_TO_TXT("%06.3f", s / n); qlMicSNR->setText(txt); - spx_int32_t v; - speex_preprocess_ctl(ai->sppPreprocess, SPEEX_PREPROCESS_GET_AGC_GAIN, &v); - float fv = powf(10.0f, (static_cast< float >(v) / 20.0f)); + float fv = powf(10.0f, (static_cast< float >(ai->m_preprocessor.getAGCGain()) / 20.0f)); FORMAT_TO_TXT("%03.0f%%", 100.0f / fv); qlMicVolume->setText(txt); diff --git a/src/mumble/CMakeLists.txt b/src/mumble/CMakeLists.txt index 7cf0f709d58..4703680b763 100644 --- a/src/mumble/CMakeLists.txt +++ b/src/mumble/CMakeLists.txt @@ -111,6 +111,8 @@ set(MUMBLE_SOURCES "AudioOutputBuffer.cpp" "AudioOutputBuffer.h" "AudioOutputToken.h" + "AudioPreprocessor.cpp" + "AudioPreprocessor.h" "AudioStats.cpp" "AudioStats.h" "AudioStats.ui"