Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4fc04f16 authored by Chia-chi Yeh's avatar Chia-chi Yeh Committed by Android (Google) Code Review
Browse files

Merge "RTP: Add a baseline echo suppressor." into gingerbread

parents aa1a694d a8a10096
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ LOCAL_MODULE := librtp_jni
LOCAL_SRC_FILES := \
	AudioCodec.cpp \
	AudioGroup.cpp \
	EchoSuppressor.cpp \
	RtpStream.cpp \
	util.cpp \
	rtp_jni.cpp
+5 −2
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@
#include "JNIHelp.h"

#include "AudioCodec.h"
#include "EchoSuppressor.h"

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());

    // 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.
    setsockopt(deviceSocket, SOL_SOCKET, SO_RCVBUF, &output, sizeof(output));
@@ -839,7 +842,7 @@ bool AudioGroup::DeviceThread::threadLoop()
            if (mode == NORMAL) {
                send(deviceSocket, input, sizeof(input), MSG_DONTWAIT);
            } else {
                // TODO: Echo canceller runs here.
                echo.run(output, input);
                send(deviceSocket, input, sizeof(input), MSG_DONTWAIT);
            }
        }
+171 −0
Original line number Diff line number Diff line
/*
 * 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 −0
Original line number Diff line number Diff line
/*
 * 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