am 4fc04f16
: Merge "RTP: Add a baseline echo suppressor." into gingerbread
Merge commit '4fc04f160f5ad99ce618084c689b239a2644deca' into gingerbread-plus-aosp * commit '4fc04f160f5ad99ce618084c689b239a2644deca': RTP: Add a baseline echo suppressor.
This commit is contained in:
@ -22,6 +22,7 @@ LOCAL_MODULE := librtp_jni
|
|||||||
LOCAL_SRC_FILES := \
|
LOCAL_SRC_FILES := \
|
||||||
AudioCodec.cpp \
|
AudioCodec.cpp \
|
||||||
AudioGroup.cpp \
|
AudioGroup.cpp \
|
||||||
|
EchoSuppressor.cpp \
|
||||||
RtpStream.cpp \
|
RtpStream.cpp \
|
||||||
util.cpp \
|
util.cpp \
|
||||||
rtp_jni.cpp
|
rtp_jni.cpp
|
||||||
|
@ -44,6 +44,7 @@
|
|||||||
#include "JNIHelp.h"
|
#include "JNIHelp.h"
|
||||||
|
|
||||||
#include "AudioCodec.h"
|
#include "AudioCodec.h"
|
||||||
|
#include "EchoSuppressor.h"
|
||||||
|
|
||||||
extern int parse(JNIEnv *env, jstring jAddress, int port, sockaddr_storage *ss);
|
extern int parse(JNIEnv *env, jstring jAddress, int port, sockaddr_storage *ss);
|
||||||
|
|
||||||
@ -766,7 +767,9 @@ bool AudioGroup::DeviceThread::threadLoop()
|
|||||||
}
|
}
|
||||||
LOGD("latency: output %d, input %d", track.latency(), record.latency());
|
LOGD("latency: output %d, input %d", track.latency(), record.latency());
|
||||||
|
|
||||||
// TODO: initialize echo canceler here.
|
// Initialize echo canceler.
|
||||||
|
EchoSuppressor echo(sampleRate, sampleCount, sampleCount * 2 +
|
||||||
|
(track.latency() + record.latency()) * sampleRate / 1000);
|
||||||
|
|
||||||
// Give device socket a reasonable buffer size.
|
// Give device socket a reasonable buffer size.
|
||||||
setsockopt(deviceSocket, SOL_SOCKET, SO_RCVBUF, &output, sizeof(output));
|
setsockopt(deviceSocket, SOL_SOCKET, SO_RCVBUF, &output, sizeof(output));
|
||||||
@ -839,7 +842,7 @@ bool AudioGroup::DeviceThread::threadLoop()
|
|||||||
if (mode == NORMAL) {
|
if (mode == NORMAL) {
|
||||||
send(deviceSocket, input, sizeof(input), MSG_DONTWAIT);
|
send(deviceSocket, input, sizeof(input), MSG_DONTWAIT);
|
||||||
} else {
|
} else {
|
||||||
// TODO: Echo canceller runs here.
|
echo.run(output, input);
|
||||||
send(deviceSocket, input, sizeof(input), MSG_DONTWAIT);
|
send(deviceSocket, input, sizeof(input), MSG_DONTWAIT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
171
voip/jni/rtp/EchoSuppressor.cpp
Normal file
171
voip/jni/rtp/EchoSuppressor.cpp
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
/*
|
||||||
|
* 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 <stdint.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#define LOG_TAG "Echo"
|
||||||
|
#include <utils/Log.h>
|
||||||
|
|
||||||
|
#include "EchoSuppressor.h"
|
||||||
|
|
||||||
|
EchoSuppressor::EchoSuppressor(int sampleRate, int sampleCount, int tailLength)
|
||||||
|
{
|
||||||
|
int scale = 1;
|
||||||
|
while (tailLength > 200 * scale) {
|
||||||
|
scale <<= 1;
|
||||||
|
}
|
||||||
|
if (scale > sampleCount) {
|
||||||
|
scale = sampleCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
mScale = scale;
|
||||||
|
mSampleCount = sampleCount;
|
||||||
|
mWindowSize = sampleCount / scale;
|
||||||
|
mTailLength = (tailLength + scale - 1) / scale;
|
||||||
|
mRecordLength = (sampleRate + sampleCount - 1) / sampleCount;
|
||||||
|
mRecordOffset = 0;
|
||||||
|
|
||||||
|
mXs = new float[mTailLength + mWindowSize];
|
||||||
|
memset(mXs, 0, sizeof(float) * (mTailLength + mWindowSize));
|
||||||
|
mXYs = new float[mTailLength];
|
||||||
|
memset(mXYs, 0, sizeof(float) * mTailLength);
|
||||||
|
mXXs = new float[mTailLength];
|
||||||
|
memset(mXYs, 0, sizeof(float) * mTailLength);
|
||||||
|
mYY = 0;
|
||||||
|
|
||||||
|
mXYRecords = new float[mRecordLength * mTailLength];
|
||||||
|
memset(mXYRecords, 0, sizeof(float) * mRecordLength * mTailLength);
|
||||||
|
mXXRecords = new float[mRecordLength * mWindowSize];
|
||||||
|
memset(mXXRecords, 0, sizeof(float) * mRecordLength * mWindowSize);
|
||||||
|
mYYRecords = new float[mRecordLength];
|
||||||
|
memset(mYYRecords, 0, sizeof(float) * mRecordLength);
|
||||||
|
|
||||||
|
mLastX = 0;
|
||||||
|
mLastY = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EchoSuppressor::~EchoSuppressor()
|
||||||
|
{
|
||||||
|
delete [] mXs;
|
||||||
|
delete [] mXYs;
|
||||||
|
delete [] mXXs;
|
||||||
|
delete [] mXYRecords;
|
||||||
|
delete [] mXXRecords;
|
||||||
|
delete [] mYYRecords;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EchoSuppressor::run(int16_t *playbacked, int16_t *recorded)
|
||||||
|
{
|
||||||
|
float *records;
|
||||||
|
|
||||||
|
// Update Xs.
|
||||||
|
for (int i = 0; i < mTailLength; ++i) {
|
||||||
|
mXs[i] = mXs[mWindowSize + i];
|
||||||
|
}
|
||||||
|
for (int i = 0, j = 0; i < mWindowSize; ++i, j += mScale) {
|
||||||
|
float sum = 0;
|
||||||
|
for (int k = 0; k < mScale; ++k) {
|
||||||
|
float x = playbacked[j + k] >> 8;
|
||||||
|
mLastX += x;
|
||||||
|
sum += (mLastX >= 0) ? mLastX : -mLastX;
|
||||||
|
mLastX = 0.005f * mLastX - x;
|
||||||
|
}
|
||||||
|
mXs[mTailLength - 1 + i] = sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update XXs and XXRecords.
|
||||||
|
for (int i = 0; i < mTailLength - mWindowSize; ++i) {
|
||||||
|
mXXs[i] = mXXs[mWindowSize + i];
|
||||||
|
}
|
||||||
|
records = &mXXRecords[mRecordOffset * mWindowSize];
|
||||||
|
for (int i = 0, j = mTailLength - mWindowSize; i < mWindowSize; ++i, ++j) {
|
||||||
|
float xx = mXs[mTailLength - 1 + i] * mXs[mTailLength - 1 + i];
|
||||||
|
mXXs[j] = mXXs[j - 1] + xx - records[i];
|
||||||
|
records[i] = xx;
|
||||||
|
if (mXXs[j] < 0) {
|
||||||
|
mXXs[j] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute Ys.
|
||||||
|
float ys[mWindowSize];
|
||||||
|
for (int i = 0, j = 0; i < mWindowSize; ++i, j += mScale) {
|
||||||
|
float sum = 0;
|
||||||
|
for (int k = 0; k < mScale; ++k) {
|
||||||
|
float y = recorded[j + k] >> 8;
|
||||||
|
mLastY += y;
|
||||||
|
sum += (mLastY >= 0) ? mLastY : -mLastY;
|
||||||
|
mLastY = 0.005f * mLastY - y;
|
||||||
|
}
|
||||||
|
ys[i] = sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update YY and YYRecords.
|
||||||
|
float yy = 0;
|
||||||
|
for (int i = 0; i < mWindowSize; ++i) {
|
||||||
|
yy += ys[i] * ys[i];
|
||||||
|
}
|
||||||
|
mYY += yy - mYYRecords[mRecordOffset];
|
||||||
|
mYYRecords[mRecordOffset] = yy;
|
||||||
|
if (mYY < 0) {
|
||||||
|
mYY = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update XYs and XYRecords.
|
||||||
|
records = &mXYRecords[mRecordOffset * mTailLength];
|
||||||
|
for (int i = 0; i < mTailLength; ++i) {
|
||||||
|
float xy = 0;
|
||||||
|
for (int j = 0;j < mWindowSize; ++j) {
|
||||||
|
xy += mXs[i + j] * ys[j];
|
||||||
|
}
|
||||||
|
mXYs[i] += xy - records[i];
|
||||||
|
records[i] = xy;
|
||||||
|
if (mXYs[i] < 0) {
|
||||||
|
mXYs[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Computes correlations from XYs, XXs, and YY.
|
||||||
|
float weight = 1.0f / (mYY + 1);
|
||||||
|
float correlation = 0;
|
||||||
|
int latency = 0;
|
||||||
|
for (int i = 0; i < mTailLength; ++i) {
|
||||||
|
float c = mXYs[i] * mXYs[i] * weight / (mXXs[i] + 1);
|
||||||
|
if (c > correlation) {
|
||||||
|
correlation = c;
|
||||||
|
latency = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
correlation = sqrtf(correlation);
|
||||||
|
if (correlation > 0.3f) {
|
||||||
|
float factor = 1.0f - correlation;
|
||||||
|
factor *= factor;
|
||||||
|
for (int i = 0; i < mSampleCount; ++i) {
|
||||||
|
recorded[i] *= factor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// LOGI("latency %5d, correlation %.10f", latency, correlation);
|
||||||
|
|
||||||
|
|
||||||
|
// Increase RecordOffset.
|
||||||
|
++mRecordOffset;
|
||||||
|
if (mRecordOffset == mRecordLength) {
|
||||||
|
mRecordOffset = 0;
|
||||||
|
}
|
||||||
|
}
|
51
voip/jni/rtp/EchoSuppressor.h
Normal file
51
voip/jni/rtp/EchoSuppressor.h
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* 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 sampleRate, int sampleCount, int tailLength);
|
||||||
|
~EchoSuppressor();
|
||||||
|
void run(int16_t *playbacked, int16_t *recorded);
|
||||||
|
|
||||||
|
private:
|
||||||
|
int mScale;
|
||||||
|
int mSampleCount;
|
||||||
|
int mWindowSize;
|
||||||
|
int mTailLength;
|
||||||
|
int mRecordLength;
|
||||||
|
int mRecordOffset;
|
||||||
|
|
||||||
|
float *mXs;
|
||||||
|
float *mXYs;
|
||||||
|
float *mXXs;
|
||||||
|
float mYY;
|
||||||
|
|
||||||
|
float *mXYRecords;
|
||||||
|
float *mXXRecords;
|
||||||
|
float *mYYRecords;
|
||||||
|
|
||||||
|
float mLastX;
|
||||||
|
float mLastY;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
Reference in New Issue
Block a user