From 1c0b256f92dfc4cc8886ddfc8825891bb5107edf Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 26 Jun 2013 16:22:54 -0700 Subject: [PATCH] remove echo cancellation from audio code --- interface/src/Application.cpp | 11 --- interface/src/Audio.cpp | 149 +--------------------------------- interface/src/Audio.h | 22 +---- 3 files changed, 3 insertions(+), 179 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 34aef00950..59c4215cfe 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -959,10 +959,6 @@ void Application::editPreferences() { headCameraPitchYawScale->setValue(_headCameraPitchYawScale); form->addRow("Head Camera Pitch/Yaw Scale:", headCameraPitchYawScale); - QCheckBox* audioEchoCancellation = new QCheckBox(); - audioEchoCancellation->setChecked(_audio.isCancellingEcho()); - form->addRow("Audio Echo Cancellation", audioEchoCancellation); - QDoubleSpinBox* leanScale = new QDoubleSpinBox(); leanScale->setValue(_myAvatar.getLeanScale()); form->addRow("Lean Scale:", leanScale); @@ -984,7 +980,6 @@ void Application::editPreferences() { QUrl url(avatarURL->text()); _myAvatar.getVoxels()->setVoxelURL(url); sendAvatarVoxelURLMessage(url); - _audio.setIsCancellingEcho( audioEchoCancellation->isChecked() ); _headCameraPitchYawScale = headCameraPitchYawScale->value(); _myAvatar.setLeanScale(leanScale->value()); _audioJitterBufferSamples = audioJitterBufferSamples->value(); @@ -2794,9 +2789,6 @@ void Application::loadSettings(QSettings* settings) { _viewFrustumOffsetDistance = loadSetting(settings, "viewFrustumOffsetDistance", 0.0f); _viewFrustumOffsetUp = loadSetting(settings, "viewFrustumOffsetUp" , 0.0f); settings->endGroup(); - settings->beginGroup("Audio Echo Cancellation"); - _audio.setIsCancellingEcho(settings->value("enabled", false).toBool()); - settings->endGroup(); scanMenuBar(&Application::loadAction, settings); getAvatar()->loadData(settings); @@ -2817,9 +2809,6 @@ void Application::saveSettings(QSettings* settings) { settings->setValue("viewFrustumOffsetDistance", _viewFrustumOffsetDistance); settings->setValue("viewFrustumOffsetUp", _viewFrustumOffsetUp); settings->endGroup(); - settings->beginGroup("Audio"); - settings->setValue("echoCancellation", _audio.isCancellingEcho()); - settings->endGroup(); scanMenuBar(&Application::saveAction, settings); getAvatar()->saveData(settings); diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index fd72826dfb..0edd778735 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -56,18 +56,7 @@ static const int AEC_BUFFERED_FRAMES = 6; static const int AEC_BUFFERED_SAMPLES_PER_CHANNEL = BUFFER_LENGTH_SAMPLES_PER_CHANNEL * AEC_BUFFERED_FRAMES; static const int AEC_BUFFERED_SAMPLES = AEC_BUFFERED_SAMPLES_PER_CHANNEL * AEC_N_CHANNELS_PLAY; static const int AEC_TMP_BUFFER_SIZE = (AEC_N_CHANNELS_MIC + // Temporary space for processing a - AEC_N_CHANNELS_PLAY) * BUFFER_LENGTH_SAMPLES_PER_CHANNEL; // single frame - -// Speex preprocessor and echo canceller configuration -static const int AEC_NOISE_REDUCTION = -80; // Noise reduction (important) -static const int AEC_RESIDUAL_ECHO_REDUCTION = -60; // Residual echo reduction -static const int AEC_RESIDUAL_ECHO_REDUCTION_ACTIVE = -45; // ~on active side -static const bool AEC_USE_AGC = true; // Automatic gain control -static const int AEC_AGC_MAX_GAIN = -30; // Gain in db -static const int AEC_AGC_TARGET_LEVEL = 9000; // Target reference level -static const int AEC_AGC_MAX_INC = 6; // Max increase in db/s -static const int AEC_AGC_MAX_DEC = 200; // Max decrease in db/s -static const bool AEC_USE_VAD = false; // Voice activity determination + AEC_N_CHANNELS_PLAY) * BUFFER_LENGTH_SAMPLES_PER_CHANNEL; // single frame // Ping test configuration static const float PING_PITCH = 16.f; // Ping wavelength, # samples / radian @@ -86,8 +75,6 @@ inline void Audio::performIO(int16_t* inputLeft, int16_t* outputLeft, int16_t* o AgentList* agentList = AgentList::getInstance(); Application* interface = Application::getInstance(); Avatar* interfaceAvatar = interface->getAvatar(); - - eventuallyCancelEcho(inputLeft); // Add Procedural effects to input samples addProceduralSounds(inputLeft, BUFFER_LENGTH_SAMPLES_PER_CHANNEL); @@ -104,13 +91,7 @@ inline void Audio::performIO(int16_t* inputLeft, int16_t* outputLeft, int16_t* o _lastInputLoudness = loudness; // add input (@microphone) data to the scope -#ifdef VISUALIZE_ECHO_CANCELLATION - if (! isCancellingEcho()) { -#endif _scope->addSamples(0, inputLeft, BUFFER_LENGTH_SAMPLES_PER_CHANNEL); -#ifdef VISUALIZE_ECHO_CANCELLATION - } -#endif Agent* audioMixer = agentList->soloAgentOfType(AGENT_TYPE_AUDIO_MIXER); @@ -276,19 +257,11 @@ inline void Audio::performIO(int16_t* inputLeft, int16_t* outputLeft, int16_t* o } eventuallySendRecvPing(inputLeft, outputLeft, outputRight); - eventuallyRecordEcho(outputLeft, outputRight); // add output (@speakers) data just written to the scope -#ifdef VISUALIZE_ECHO_CANCELLATION - if (! isCancellingEcho()) { - _scope->setColor(2, 0x00ffff); -#endif _scope->addSamples(1, outputLeft, BUFFER_LENGTH_SAMPLES_PER_CHANNEL); _scope->addSamples(2, outputRight, BUFFER_LENGTH_SAMPLES_PER_CHANNEL); -#ifdef VISUALIZE_ECHO_CANCELLATION - } -#endif gettimeofday(&_lastCallbackTime, NULL); } @@ -342,11 +315,6 @@ Audio::Audio(Oscilloscope* scope, int16_t initialJitterBufferSamples) : _totalPacketsReceived(0), _firstPacketReceivedTime(), _packetsReceivedThisPlayback(0), - _isCancellingEcho(false), - _echoDelay(0), - _echoSamplesLeft(0l), - _speexEchoState(NULL), - _speexPreprocessState(NULL), _isSendingEchoPing(false), _pingAnalysisPending(false), _pingFramesToRecord(0), @@ -393,39 +361,8 @@ Audio::Audio(Oscilloscope* scope, int16_t initialJitterBufferSamples) : } _echoSamplesLeft = new int16_t[AEC_BUFFERED_SAMPLES + AEC_TMP_BUFFER_SIZE]; - if (! _echoSamplesLeft) { - return; - } memset(_echoSamplesLeft, 0, AEC_BUFFERED_SAMPLES * sizeof(int16_t)); - _echoSamplesRight = _echoSamplesLeft + AEC_BUFFERED_SAMPLES_PER_CHANNEL; - _speexTmpBuf = _echoSamplesRight + AEC_BUFFERED_SAMPLES_PER_CHANNEL; - - _speexPreprocessState = speex_preprocess_state_init(BUFFER_LENGTH_SAMPLES_PER_CHANNEL, SAMPLE_RATE); - if (_speexPreprocessState) { - _speexEchoState = speex_echo_state_init_mc(BUFFER_LENGTH_SAMPLES_PER_CHANNEL, - AEC_FILTER_LENGTH, AEC_N_CHANNELS_MIC, AEC_N_CHANNELS_PLAY); - if (_speexEchoState) { - speex_preprocess_ctl(_speexPreprocessState, SPEEX_PREPROCESS_SET_ECHO_STATE, _speexEchoState); - int tmp; - speex_echo_ctl(_speexEchoState, SPEEX_ECHO_SET_SAMPLING_RATE, &(tmp = SAMPLE_RATE)); - tmp = AEC_NOISE_REDUCTION; - speex_preprocess_ctl(_speexPreprocessState, SPEEX_PREPROCESS_SET_NOISE_SUPPRESS, &tmp); - tmp = AEC_RESIDUAL_ECHO_REDUCTION; - speex_preprocess_ctl(_speexPreprocessState, SPEEX_PREPROCESS_SET_ECHO_SUPPRESS, &tmp); - tmp = AEC_RESIDUAL_ECHO_REDUCTION_ACTIVE; - speex_preprocess_ctl(_speexPreprocessState, SPEEX_PREPROCESS_SET_ECHO_SUPPRESS_ACTIVE, &tmp); - speex_preprocess_ctl(_speexPreprocessState, SPEEX_PREPROCESS_SET_AGC, &(tmp = int(AEC_USE_AGC))); - speex_preprocess_ctl(_speexPreprocessState, SPEEX_PREPROCESS_SET_AGC_MAX_GAIN, &(tmp = AEC_AGC_MAX_GAIN)); - speex_preprocess_ctl(_speexPreprocessState, SPEEX_PREPROCESS_SET_AGC_TARGET, &(tmp = AEC_AGC_TARGET_LEVEL)); - speex_preprocess_ctl(_speexPreprocessState, SPEEX_PREPROCESS_SET_AGC_INCREMENT, &(tmp = AEC_AGC_MAX_INC)); - speex_preprocess_ctl(_speexPreprocessState, SPEEX_PREPROCESS_SET_AGC_DECREMENT, &(tmp = AEC_AGC_MAX_DEC)); - speex_preprocess_ctl(_speexPreprocessState, SPEEX_PREPROCESS_SET_VAD, &(tmp = int(AEC_USE_VAD))); - } else { - speex_preprocess_state_destroy(_speexPreprocessState); - _speexPreprocessState = NULL; - } - } - + // start the stream now that sources are good to go outputPortAudioError(Pa_StartStream(_stream)); @@ -446,10 +383,6 @@ Audio::~Audio() { outputPortAudioError(Pa_CloseStream(_stream)); outputPortAudioError(Pa_Terminate()); } - if (_speexEchoState) { - speex_preprocess_state_destroy(_speexPreprocessState); - speex_echo_state_destroy(_speexEchoState); - } delete[] _echoSamplesLeft; } @@ -639,83 +572,6 @@ void Audio::addProceduralSounds(int16_t* inputBuffer, int numSamples) { } } -// ----------------------------- -// Speex-based echo cancellation -// ----------------------------- - -bool Audio::isCancellingEcho() const { - return _isCancellingEcho && ! (_pingFramesToRecord != 0 || _pingAnalysisPending || ! _speexPreprocessState); -} - -void Audio::setIsCancellingEcho(bool enable) { - if (enable && _speexPreprocessState) { - speex_echo_state_reset(_speexEchoState); - _echoWritePos = 0; - memset(_echoSamplesLeft, 0, AEC_BUFFERED_SAMPLES * sizeof(int16_t)); - } - _isCancellingEcho = enable; -} - -inline void Audio::eventuallyCancelEcho(int16_t* inputLeft) { - if (! isCancellingEcho()) { - return; - } - - // Construct an artificial frame from the captured playback - // that contains the appropriately delayed output to cancel - unsigned n = BUFFER_LENGTH_SAMPLES_PER_CHANNEL, n2 = 0; - unsigned readPos = (_echoWritePos + AEC_BUFFERED_SAMPLES_PER_CHANNEL - _echoDelay) % AEC_BUFFERED_SAMPLES_PER_CHANNEL; - unsigned readEnd = readPos + n; - if (readEnd >= AEC_BUFFERED_SAMPLES_PER_CHANNEL) { - n2 = (readEnd -= AEC_BUFFERED_SAMPLES_PER_CHANNEL); - n -= n2; - } - // Use two subsequent buffers for the two stereo channels - int16_t* playBufferLeft = _speexTmpBuf + BUFFER_LENGTH_SAMPLES_PER_CHANNEL; - memcpy(playBufferLeft, _echoSamplesLeft + readPos, n * sizeof(int16_t)); - memcpy(playBufferLeft + n, _echoSamplesLeft, n2 * sizeof(int16_t)); - int16_t* playBufferRight = playBufferLeft + BUFFER_LENGTH_SAMPLES_PER_CHANNEL; - memcpy(playBufferRight, _echoSamplesRight + readPos, n * sizeof(int16_t)); - memcpy(playBufferRight + n, _echoSamplesLeft, n2 * sizeof(int16_t)); - -#ifdef VISUALIZE_ECHO_CANCELLATION - // Visualize the input - _scope->addSamples(0, inputLeft, BUFFER_LENGTH_SAMPLES_PER_CHANNEL); - _scope->addSamples(1, playBufferLeft, BUFFER_LENGTH_SAMPLES_PER_CHANNEL); -#endif - - // Have Speex perform echo cancellation - speex_echo_cancellation(_speexEchoState, inputLeft, playBufferLeft, _speexTmpBuf); - memcpy(inputLeft, _speexTmpBuf, BUFFER_LENGTH_BYTES_PER_CHANNEL); - speex_preprocess_run(_speexPreprocessState, inputLeft); - -#ifdef VISUALIZE_ECHO_CANCELLATION - // Visualize the result - _scope->setColor(2, 0x00ff00); - _scope->addSamples(2, inputLeft, BUFFER_LENGTH_SAMPLES_PER_CHANNEL); -#endif -} - -inline void Audio::eventuallyRecordEcho(int16_t* outputLeft, int16_t* outputRight) { - if (! isCancellingEcho()) { - return; - } - - // Copy playback data to circular buffers - unsigned n = BUFFER_LENGTH_SAMPLES_PER_CHANNEL, n2 = 0; - unsigned writeEnd = _echoWritePos + n; - if (writeEnd >= AEC_BUFFERED_SAMPLES_PER_CHANNEL) { - n2 = (writeEnd -= AEC_BUFFERED_SAMPLES_PER_CHANNEL); - n -= n2; - } - memcpy(_echoSamplesLeft + _echoWritePos, outputLeft, n * sizeof(int16_t)); - memcpy(_echoSamplesLeft, outputLeft + n, n2 * sizeof(int16_t)); - memcpy(_echoSamplesRight + _echoWritePos, outputRight, n * sizeof(int16_t)); - memcpy(_echoSamplesRight, outputRight + n, n2 * sizeof(int16_t)); - - _echoWritePos = writeEnd; -} - // ----------------------------------------------------------- // Accoustic ping (audio system round trip time determination) // ----------------------------------------------------------- @@ -870,7 +726,6 @@ bool Audio::eventuallyAnalyzePing() { } _scope->inputPaused = true; analyzePing(); - setIsCancellingEcho(_isCancellingEcho); _pingAnalysisPending = false; return true; } diff --git a/interface/src/Audio.h b/interface/src/Audio.h index 1e2c69616f..f6f4ff9347 100644 --- a/interface/src/Audio.h +++ b/interface/src/Audio.h @@ -47,11 +47,6 @@ public: int getJitterBufferSamples() { return _jitterBufferSamples; }; void lowPassFilter(int16_t* inputBuffer); - - void startEchoTest(); - void renderEchoCompare(); - void setIsCancellingEcho(bool enabled); - bool isCancellingEcho() const; void ping(); @@ -79,16 +74,8 @@ private: int _totalPacketsReceived; timeval _firstPacketReceivedTime; int _packetsReceivedThisPlayback; - // Echo cancellation - volatile bool _isCancellingEcho; - unsigned _echoWritePos; - unsigned _echoDelay; - int16_t* _echoSamplesLeft; - int16_t* _echoSamplesRight; - int16_t* _speexTmpBuf; - SpeexEchoState* _speexEchoState; - SpeexPreprocessState* _speexPreprocessState; // Ping analysis + int16_t* _echoSamplesLeft; volatile bool _isSendingEchoPing; volatile bool _pingAnalysisPending; int _pingFramesToRecord; @@ -102,13 +89,6 @@ private: // Audio callback in class context. inline void performIO(int16_t* inputLeft, int16_t* outputLeft, int16_t* outputRight); - // When echo cancellation is enabled, subtract recorded echo from the input. - // Called from 'performIO' before the input has been processed. - inline void eventuallyCancelEcho(int16_t* inputLeft); - // When EC is enabled, record output samples. - // Called from 'performIO' after the output has been generated. - inline void eventuallyRecordEcho(int16_t* outputLeft, int16_t* outputRight); - // When requested, sends/receives a signal for round trip time determination. // Called from 'performIO'. inline void eventuallySendRecvPing(int16_t* inputLeft, int16_t* outputLeft, int16_t* outputRight);