blob: e22313681d819ed2d9d5719ce3eba8edd56a2264 [file] [log] [blame]
Chia-chi Yeha8a10092010-10-05 01:17:13 +08001/*
2 * Copyrightm (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <stdio.h>
Chung-yih Wangfb116fb2010-10-06 16:46:59 +080018#include <string.h>
Chia-chi Yeha8a10092010-10-05 01:17:13 +080019#include <stdint.h>
Marco Nelissenfd1e4ad2010-10-06 12:23:02 -070020#include <string.h>
Chia-chi Yeha8a10092010-10-05 01:17:13 +080021#include <math.h>
22
23#define LOG_TAG "Echo"
24#include <utils/Log.h>
25
26#include "EchoSuppressor.h"
27
Chia-chi Yeh8a68b522010-10-21 23:39:35 +080028// It is very difficult to do echo cancellation at this level due to the lack of
29// the timing information of the samples being played and recorded. Therefore,
30// for the first release only echo suppression is implemented.
31
32// The algorithm is derived from the "previous works" summarized in
33// A new class of doubletalk detectors based on cross-correlation,
34// J Benesty, DR Morgan, JH Cho, IEEE Trans. on Speech and Audio Processing.
35// The method proposed in that paper is not used because of its high complexity.
36
37// It is well known that cross-correlation can be computed using convolution,
38// but unfortunately not every mobile processor has a (fast enough) FPU. Thus
39// we use integer arithmetic as much as possible and do lots of bookkeeping.
40// Again, parameters and thresholds are chosen by experiments.
41
42EchoSuppressor::EchoSuppressor(int sampleCount, int tailLength)
Chia-chi Yeha8a10092010-10-05 01:17:13 +080043{
Chia-chi Yeh8a68b522010-10-21 23:39:35 +080044 tailLength += sampleCount * 4;
45
46 int shift = 0;
47 while ((sampleCount >> shift) > 1 && (tailLength >> shift) > 256) {
48 ++shift;
Chia-chi Yeha8a10092010-10-05 01:17:13 +080049 }
50
Chia-chi Yeh8a68b522010-10-21 23:39:35 +080051 mShift = shift + 4;
52 mScale = 1 << shift;
Chia-chi Yeha8a10092010-10-05 01:17:13 +080053 mSampleCount = sampleCount;
Chia-chi Yeh8a68b522010-10-21 23:39:35 +080054 mWindowSize = sampleCount >> shift;
55 mTailLength = tailLength >> shift;
56 mRecordLength = tailLength * 2 / sampleCount;
Chia-chi Yeha8a10092010-10-05 01:17:13 +080057 mRecordOffset = 0;
58
Chia-chi Yeh8a68b522010-10-21 23:39:35 +080059 mXs = new uint16_t[mTailLength + mWindowSize];
60 memset(mXs, 0, sizeof(*mXs) * (mTailLength + mWindowSize));
61 mXSums = new uint32_t[mTailLength];
62 memset(mXSums, 0, sizeof(*mXSums) * mTailLength);
63 mX2Sums = new uint32_t[mTailLength];
64 memset(mX2Sums, 0, sizeof(*mX2Sums) * mTailLength);
65 mXRecords = new uint16_t[mRecordLength * mWindowSize];
66 memset(mXRecords, 0, sizeof(*mXRecords) * mRecordLength * mWindowSize);
Chia-chi Yeha8a10092010-10-05 01:17:13 +080067
Chia-chi Yeh8a68b522010-10-21 23:39:35 +080068 mYSum = 0;
69 mY2Sum = 0;
70 mYRecords = new uint32_t[mRecordLength];
71 memset(mYRecords, 0, sizeof(*mYRecords) * mRecordLength);
72 mY2Records = new uint32_t[mRecordLength];
73 memset(mY2Records, 0, sizeof(*mY2Records) * mRecordLength);
74
75 mXYSums = new uint32_t[mTailLength];
76 memset(mXYSums, 0, sizeof(*mXYSums) * mTailLength);
77 mXYRecords = new uint32_t[mRecordLength * mTailLength];
78 memset(mXYRecords, 0, sizeof(*mXYRecords) * mRecordLength * mTailLength);
Chia-chi Yeha8a10092010-10-05 01:17:13 +080079
80 mLastX = 0;
81 mLastY = 0;
Chia-chi Yeh8a68b522010-10-21 23:39:35 +080082 mWeight = 1.0f / (mRecordLength * mWindowSize);
Chia-chi Yeha8a10092010-10-05 01:17:13 +080083}
84
85EchoSuppressor::~EchoSuppressor()
86{
87 delete [] mXs;
Chia-chi Yeh8a68b522010-10-21 23:39:35 +080088 delete [] mXSums;
89 delete [] mX2Sums;
90 delete [] mXRecords;
91 delete [] mYRecords;
92 delete [] mY2Records;
93 delete [] mXYSums;
Chia-chi Yeha8a10092010-10-05 01:17:13 +080094 delete [] mXYRecords;
Chia-chi Yeha8a10092010-10-05 01:17:13 +080095}
96
97void EchoSuppressor::run(int16_t *playbacked, int16_t *recorded)
98{
Chia-chi Yeha8a10092010-10-05 01:17:13 +080099 // Update Xs.
Chia-chi Yeh8a68b522010-10-21 23:39:35 +0800100 for (int i = mTailLength - 1; i >= 0; --i) {
101 mXs[i + mWindowSize] = mXs[i];
Chia-chi Yeha8a10092010-10-05 01:17:13 +0800102 }
Chia-chi Yeh8a68b522010-10-21 23:39:35 +0800103 for (int i = mWindowSize - 1, j = 0; i >= 0; --i, j += mScale) {
104 uint32_t sum = 0;
Chia-chi Yeha8a10092010-10-05 01:17:13 +0800105 for (int k = 0; k < mScale; ++k) {
Chia-chi Yeh8a68b522010-10-21 23:39:35 +0800106 int32_t x = playbacked[j + k] << 15;
Chia-chi Yeha8a10092010-10-05 01:17:13 +0800107 mLastX += x;
Chia-chi Yeh8a68b522010-10-21 23:39:35 +0800108 sum += ((mLastX >= 0) ? mLastX : -mLastX) >> 15;
109 mLastX -= (mLastX >> 10) + x;
Chia-chi Yeha8a10092010-10-05 01:17:13 +0800110 }
Chia-chi Yeh8a68b522010-10-21 23:39:35 +0800111 mXs[i] = sum >> mShift;
Chia-chi Yeha8a10092010-10-05 01:17:13 +0800112 }
113
Chia-chi Yeh8a68b522010-10-21 23:39:35 +0800114 // Update XSums, X2Sums, and XRecords.
115 for (int i = mTailLength - mWindowSize - 1; i >= 0; --i) {
116 mXSums[i + mWindowSize] = mXSums[i];
117 mX2Sums[i + mWindowSize] = mX2Sums[i];
Chia-chi Yeha8a10092010-10-05 01:17:13 +0800118 }
Chia-chi Yeh8a68b522010-10-21 23:39:35 +0800119 uint16_t *xRecords = &mXRecords[mRecordOffset * mWindowSize];
120 for (int i = mWindowSize - 1; i >= 0; --i) {
121 uint16_t x = mXs[i];
122 mXSums[i] = mXSums[i + 1] + x - xRecords[i];
123 mX2Sums[i] = mX2Sums[i + 1] + x * x - xRecords[i] * xRecords[i];
124 xRecords[i] = x;
Chia-chi Yeha8a10092010-10-05 01:17:13 +0800125 }
126
127 // Compute Ys.
Chia-chi Yeh8a68b522010-10-21 23:39:35 +0800128 uint16_t ys[mWindowSize];
129 for (int i = mWindowSize - 1, j = 0; i >= 0; --i, j += mScale) {
130 uint32_t sum = 0;
Chia-chi Yeha8a10092010-10-05 01:17:13 +0800131 for (int k = 0; k < mScale; ++k) {
Chia-chi Yeh8a68b522010-10-21 23:39:35 +0800132 int32_t y = recorded[j + k] << 15;
Chia-chi Yeha8a10092010-10-05 01:17:13 +0800133 mLastY += y;
Chia-chi Yeh8a68b522010-10-21 23:39:35 +0800134 sum += ((mLastY >= 0) ? mLastY : -mLastY) >> 15;
135 mLastY -= (mLastY >> 10) + y;
Chia-chi Yeha8a10092010-10-05 01:17:13 +0800136 }
Chia-chi Yeh8a68b522010-10-21 23:39:35 +0800137 ys[i] = sum >> mShift;
Chia-chi Yeha8a10092010-10-05 01:17:13 +0800138 }
139
Chia-chi Yeh8a68b522010-10-21 23:39:35 +0800140 // Update YSum, Y2Sum, YRecords, and Y2Records.
141 uint32_t ySum = 0;
142 uint32_t y2Sum = 0;
143 for (int i = mWindowSize - 1; i >= 0; --i) {
144 ySum += ys[i];
145 y2Sum += ys[i] * ys[i];
Chia-chi Yeha8a10092010-10-05 01:17:13 +0800146 }
Chia-chi Yeh8a68b522010-10-21 23:39:35 +0800147 mYSum += ySum - mYRecords[mRecordOffset];
148 mY2Sum += y2Sum - mY2Records[mRecordOffset];
149 mYRecords[mRecordOffset] = ySum;
150 mY2Records[mRecordOffset] = y2Sum;
151
152 // Update XYSums and XYRecords.
153 uint32_t *xyRecords = &mXYRecords[mRecordOffset * mTailLength];
154 for (int i = mTailLength - 1; i >= 0; --i) {
155 uint32_t xySum = 0;
156 for (int j = mWindowSize - 1; j >= 0; --j) {
157 xySum += mXs[i + j] * ys[j];
158 }
159 mXYSums[i] += xySum - xyRecords[i];
160 xyRecords[i] = xySum;
Chia-chi Yeha8a10092010-10-05 01:17:13 +0800161 }
162
Chia-chi Yeh8a68b522010-10-21 23:39:35 +0800163 // Compute correlations.
Chia-chi Yeha8a10092010-10-05 01:17:13 +0800164 int latency = 0;
Chia-chi Yeh0c7d3062010-10-27 17:04:46 +0800165 float corr2 = 0.0f;
166 float varX = 0.0f;
Chia-chi Yeh8a68b522010-10-21 23:39:35 +0800167 float varY = mY2Sum - mWeight * mYSum * mYSum;
168 for (int i = mTailLength - 1; i >= 0; --i) {
Chia-chi Yeh8a68b522010-10-21 23:39:35 +0800169 float cov = mXYSums[i] - mWeight * mXSums[i] * mYSum;
Chia-chi Yeh0c7d3062010-10-27 17:04:46 +0800170 if (cov > 0.0f) {
171 float varXi = mX2Sums[i] - mWeight * mXSums[i] * mXSums[i];
172 float corr2i = cov * cov / (varXi * varY + 1);
173 if (corr2i > corr2) {
174 varX = varXi;
175 corr2 = corr2i;
176 latency = i;
177 }
Chia-chi Yeha8a10092010-10-05 01:17:13 +0800178 }
179 }
Steve Block6215d3f2012-01-04 20:05:49 +0000180 //ALOGI("corr^2 %.5f, var %8.0f %8.0f, latency %d", corr2, varX, varY,
Chia-chi Yeh0c7d3062010-10-27 17:04:46 +0800181 // latency * mScale);
Chia-chi Yeha8a10092010-10-05 01:17:13 +0800182
Chia-chi Yeh8a68b522010-10-21 23:39:35 +0800183 // Do echo suppression.
Chia-chi Yeh0c7d3062010-10-27 17:04:46 +0800184 if (corr2 > 0.1f && varX > 10000.0f) {
Chia-chi Yeh8a68b522010-10-21 23:39:35 +0800185 int factor = (corr2 > 1.0f) ? 0 : (1.0f - sqrtf(corr2)) * 4096;
Chia-chi Yeha8a10092010-10-05 01:17:13 +0800186 for (int i = 0; i < mSampleCount; ++i) {
Chia-chi Yeh8a68b522010-10-21 23:39:35 +0800187 recorded[i] = recorded[i] * factor >> 16;
Chia-chi Yeha8a10092010-10-05 01:17:13 +0800188 }
189 }
Chia-chi Yeha8a10092010-10-05 01:17:13 +0800190
191 // Increase RecordOffset.
192 ++mRecordOffset;
193 if (mRecordOffset == mRecordLength) {
194 mRecordOffset = 0;
195 }
196}