回音消除一直是语音处理中的一个难点,很多软回音消除的算法处理并不理想。
但是android 2.3中自带了回音消除处理模块,效果还不错,代码的位置在
frameworks/base/voip/jni/rtp/EchoSuppressor.cpp
frameworks/base/voip/jni/rtp/EchoSuppressor.h
代码如下
- /*
- * Copyrightm (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- #include <stdio.h>
- #include <string.h>
- #include <stdint.h>
- #include <math.h>
- #define LOG_TAG "Echo"
- #include <utils/Log.h>
- #include "EchoSuppressor.h"
- // It is very difficult to do echo cancellation at this level due to the lack of
- // the timing information of the samples being played and recorded. Therefore,
- // for the first release only echo suppression is implemented.
- // The algorithm is derived from the "previous works" summarized in
- // A new class of doubletalk detectors based on cross-correlation,
- // J Benesty, DR Morgan, JH Cho, IEEE Trans. on Speech and Audio Processing.
- // The method proposed in that paper is not used because of its high complexity.
- // It is well known that cross-correlation can be computed using convolution,
- // but unfortunately not every mobile processor has a (fast enough) FPU. Thus
- // we use integer arithmetic as much as possible and do lots of bookkeeping.
- // Again, parameters and thresholds are chosen by experiments.
- EchoSuppressor::EchoSuppressor(int sampleCount, int tailLength)
- {
- tailLength += sampleCount * 4;
- int shift = 0;
- while ((sampleCount >> shift) > 1 && (tailLength >> shift) > 256) {
- ++shift;
- }
- mShift = shift + 4;
- mScale = 1 << shift;
- mSampleCount = sampleCount;
- mWindowSize = sampleCount >> shift;
- mTailLength = tailLength >> shift;
- mRecordLength = tailLength * 2 / sampleCount;
- mRecordOffset = 0;
- mXs = new uint16_t[mTailLength + mWindowSize];
- memset(mXs, 0, sizeof(*mXs) * (mTailLength + mWindowSize));
- mXSums = new uint32_t[mTailLength];
- memset(mXSums, 0, sizeof(*mXSums) * mTailLength);
- mX2Sums = new uint32_t[mTailLength];
- memset(mX2Sums, 0, sizeof(*mX2Sums) * mTailLength);
- mXRecords = new uint16_t[mRecordLength * mWindowSize];
- memset(mXRecords, 0, sizeof(*mXRecords) * mRecordLength * mWindowSize);
- mYSum = 0;
- mY2Sum = 0;
- mYRecords = new uint32_t[mRecordLength];
- memset(mYRecords, 0, sizeof(*mYRecords) * mRecordLength);
- mY2Records = new uint32_t[mRecordLength];
- memset(mY2Records, 0, sizeof(*mY2Records) * mRecordLength);
- mXYSums = new uint32_t[mTailLength];
- memset(mXYSums, 0, sizeof(*mXYSums) * mTailLength);
- mXYRecords = new uint32_t[mRecordLength * mTailLength];
- memset(mXYRecords, 0, sizeof(*mXYRecords) * mRecordLength * mTailLength);
- mLastX = 0;
- mLastY = 0;
- mWeight = 1.0f / (mRecordLength * mWindowSize);
- }
- EchoSuppressor::~EchoSuppressor()
- {
- delete [] mXs;
- delete [] mXSums;
- delete [] mX2Sums;
- delete [] mXRecords;
- delete [] mYRecords;
- delete [] mY2Records;
- delete [] mXYSums;
- delete [] mXYRecords;
- }
- void EchoSuppressor::run(int16_t *playbacked, int16_t *recorded)
- {
- // Update Xs.
- for (int i = mTailLength - 1; i >= 0; --i) {
- mXs[i + mWindowSize] = mXs[i];
- }
- for (int i = mWindowSize - 1, j = 0; i >= 0; --i, j += mScale) {
- uint32_t sum = 0;
- for (int k = 0; k < mScale; ++k) {
- int32_t x = playbacked[j + k] << 15;
- mLastX += x;
- sum += ((mLastX >= 0) ? mLastX : -mLastX) >> 15;
- mLastX -= (mLastX >> 10) + x;
- }
- mXs[i] = sum >> mShift;
- }
- // Update XSums, X2Sums, and XRecords.
- for (int i = mTailLength - mWindowSize - 1; i >= 0; --i) {
- mXSums[i + mWindowSize] = mXSums[i];
- mX2Sums[i + mWindowSize] = mX2Sums[i];
- }
- uint16_t *xRecords = &mXRecords[mRecordOffset * mWindowSize];
- for (int i = mWindowSize - 1; i >= 0; --i) {
- uint16_t x = mXs[i];
- mXSums[i] = mXSums[i + 1] + x - xRecords[i];
- mX2Sums[i] = mX2Sums[i + 1] + x * x - xRecords[i] * xRecords[i];
- xRecords[i] = x;
- }
- // Compute Ys.
- uint16_t ys[mWindowSize];
- for (int i = mWindowSize - 1, j = 0; i >= 0; --i, j += mScale) {
- uint32_t sum = 0;
- for (int k = 0; k < mScale; ++k) {
- int32_t y = recorded[j + k] << 15;
- mLastY += y;
- sum += ((mLastY >= 0) ? mLastY : -mLastY) >> 15;
- mLastY -= (mLastY >> 10) + y;
- }
- ys[i] = sum >> mShift;
- }
- // Update YSum, Y2Sum, YRecords, and Y2Records.
- uint32_t ySum = 0;
- uint32_t y2Sum = 0;
- for (int i = mWindowSize - 1; i >= 0; --i) {
- ySum += ys[i];
- y2Sum += ys[i] * ys[i];
- }
- mYSum += ySum - mYRecords[mRecordOffset];
- mY2Sum += y2Sum - mY2Records[mRecordOffset];
- mYRecords[mRecordOffset] = ySum;
- mY2Records[mRecordOffset] = y2Sum;
- // Update XYSums and XYRecords.
- uint32_t *xyRecords = &mXYRecords[mRecordOffset * mTailLength];
- for (int i = mTailLength - 1; i >= 0; --i) {
- uint32_t xySum = 0;
- for (int j = mWindowSize - 1; j >= 0; --j) {
- xySum += mXs[i + j] * ys[j];
- }
- mXYSums[i] += xySum - xyRecords[i];
- xyRecords[i] = xySum;
- }
- // Compute correlations.
- float corr2 = 0.0f;
- int latency = 0;
- float varY = mY2Sum - mWeight * mYSum * mYSum;
- for (int i = mTailLength - 1; i >= 0; --i) {
- float varX = mX2Sums[i] - mWeight * mXSums[i] * mXSums[i];
- float cov = mXYSums[i] - mWeight * mXSums[i] * mYSum;
- float c2 = cov * cov / (varX * varY + 1);
- if (c2 > corr2) {
- corr2 = c2;
- latency = i;
- }
- }
- //LOGI("correlation^2 = %.10f, latency = %d", corr2, latency * mScale);
- // Do echo suppression.
- if (corr2 > 0.1f) {
- int factor = (corr2 > 1.0f) ? 0 : (1.0f - sqrtf(corr2)) * 4096;
- for (int i = 0; i < mSampleCount; ++i) {
- recorded[i] = recorded[i] * factor >> 16;
- }
- }
- // Increase RecordOffset.
- ++mRecordOffset;
- if (mRecordOffset == mRecordLength) {
- mRecordOffset = 0;
- }
- }
头文件
- /*
- * Copyrightm (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- #ifndef __ECHO_SUPPRESSOR_H__
- #define __ECHO_SUPPRESSOR_H__
- #include <stdint.h>
- class EchoSuppressor
- {
- public:
- // The sampleCount must be power of 2.
- EchoSuppressor(int sampleCount, int tailLength);
- ~EchoSuppressor();
- void run(int16_t *playbacked, int16_t *recorded);
- private:
- int mShift;
- int mScale;
- int mSampleCount;
- int mWindowSize;
- int mTailLength;
- int mRecordLength;
- int mRecordOffset;
- uint16_t *mXs;
- uint32_t *mXSums;
- uint32_t *mX2Sums;
- uint16_t *mXRecords;
- uint32_t mYSum;
- uint32_t mY2Sum;
- uint32_t *mYRecords;
- uint32_t *mY2Records;
- uint32_t *mXYSums;
- uint32_t *mXYRecords;
- int32_t mLastX;
- int32_t mLastY;
- float mWeight;
- };
- #endif
|