summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cmds/dumpstate/dumpstate.c28
-rw-r--r--core/java/android/hardware/Camera.java6
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java27
-rw-r--r--core/java/com/android/internal/app/ExternalMediaFormatActivity.java17
-rw-r--r--include/camera/Camera.h12
-rw-r--r--libs/ui/EventHub.cpp2
-rw-r--r--opengl/libagl/egl.cpp4
-rw-r--r--services/camera/libcameraservice/CameraService.cpp56
-rw-r--r--services/camera/libcameraservice/CameraService.h6
-rw-r--r--voip/jni/rtp/AudioGroup.cpp2
-rw-r--r--voip/jni/rtp/EchoSuppressor.cpp203
-rw-r--r--voip/jni/rtp/EchoSuppressor.h27
12 files changed, 225 insertions, 165 deletions
diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c
index 822f62d..0723f67 100644
--- a/cmds/dumpstate/dumpstate.c
+++ b/cmds/dumpstate/dumpstate.c
@@ -216,19 +216,21 @@ int main(int argc, char *argv[]) {
fclose(cmdline);
}
- /* switch to non-root user and group */
- gid_t groups[] = { AID_LOG, AID_SDCARD_RW, AID_MOUNT };
- if (setgroups(sizeof(groups)/sizeof(groups[0]), groups) != 0) {
- LOGE("Unable to setgroups, aborting: %s\n", strerror(errno));
- return -1;
- }
- if (setgid(AID_SHELL) != 0) {
- LOGE("Unable to setgid, aborting: %s\n", strerror(errno));
- return -1;
- }
- if (setuid(AID_SHELL) != 0) {
- LOGE("Unable to setuid, aborting: %s\n", strerror(errno));
- return -1;
+ if (getuid() == 0) {
+ /* switch to non-root user and group */
+ gid_t groups[] = { AID_LOG, AID_SDCARD_RW, AID_MOUNT };
+ if (setgroups(sizeof(groups)/sizeof(groups[0]), groups) != 0) {
+ LOGE("Unable to setgroups, aborting: %s\n", strerror(errno));
+ return -1;
+ }
+ if (setgid(AID_SHELL) != 0) {
+ LOGE("Unable to setgid, aborting: %s\n", strerror(errno));
+ return -1;
+ }
+ if (setuid(AID_SHELL) != 0) {
+ LOGE("Unable to setuid, aborting: %s\n", strerror(errno));
+ return -1;
+ }
}
char path[PATH_MAX], tmp_path[PATH_MAX];
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 6ff5a40..275e2eb 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -779,9 +779,9 @@ public class Camera {
* Set the clockwise rotation of preview display in degrees. This affects
* the preview frames and the picture displayed after snapshot. This method
* is useful for portrait mode applications. Note that preview display of
- * front-facing cameras is flipped horizontally, that is, the image is
- * reflected along the central vertical axis of the camera sensor. So the
- * users can see themselves as looking into a mirror.
+ * front-facing cameras is flipped horizontally before the rotation, that
+ * is, the image is reflected along the central vertical axis of the camera
+ * sensor. So the users can see themselves as looking into a mirror.
*
* This does not affect the order of byte array passed in {@link
* PreviewCallback#onPreviewFrame}, JPEG pictures, or recorded videos. This
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 2b083dc..9645a17 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -452,17 +452,14 @@ public abstract class WallpaperService extends Service {
private void dispatchPointer(MotionEvent event) {
synchronized (mLock) {
if (event.getAction() == MotionEvent.ACTION_MOVE) {
- if (mPendingMove != null) {
- mCaller.removeMessages(MSG_TOUCH_EVENT, mPendingMove);
- mPendingMove.recycle();
- }
mPendingMove = event;
} else {
mPendingMove = null;
}
- Message msg = mCaller.obtainMessageO(MSG_TOUCH_EVENT, event);
- mCaller.sendMessage(msg);
}
+
+ Message msg = mCaller.obtainMessageO(MSG_TOUCH_EVENT, event);
+ mCaller.sendMessage(msg);
}
void updateSurface(boolean forceRelayout, boolean forceReport, boolean redrawNeeded) {
@@ -905,14 +902,22 @@ public abstract class WallpaperService extends Service {
mEngine.doOffsetsChanged();
} break;
case MSG_TOUCH_EVENT: {
+ boolean skip = false;
MotionEvent ev = (MotionEvent)message.obj;
- synchronized (mEngine.mLock) {
- if (mEngine.mPendingMove == ev) {
- mEngine.mPendingMove = null;
+ if (ev.getAction() == MotionEvent.ACTION_MOVE) {
+ synchronized (mEngine.mLock) {
+ if (mEngine.mPendingMove == ev) {
+ mEngine.mPendingMove = null;
+ } else {
+ // this is not the motion event we are looking for....
+ skip = true;
+ }
}
}
- if (DEBUG) Log.v(TAG, "Delivering touch event: " + ev);
- mEngine.onTouchEvent(ev);
+ if (!skip) {
+ if (DEBUG) Log.v(TAG, "Delivering touch event: " + ev);
+ mEngine.onTouchEvent(ev);
+ }
ev.recycle();
} break;
default :
diff --git a/core/java/com/android/internal/app/ExternalMediaFormatActivity.java b/core/java/com/android/internal/app/ExternalMediaFormatActivity.java
index 98dcb8b..5ab9217 100644
--- a/core/java/com/android/internal/app/ExternalMediaFormatActivity.java
+++ b/core/java/com/android/internal/app/ExternalMediaFormatActivity.java
@@ -16,6 +16,8 @@
package com.android.internal.app;
+import com.android.internal.os.storage.ExternalStorageFormatter;
+
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -23,10 +25,6 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
-import android.os.storage.IMountService;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.Environment;
import android.util.Log;
/**
@@ -95,14 +93,9 @@ public class ExternalMediaFormatActivity extends AlertActivity implements Dialog
public void onClick(DialogInterface dialog, int which) {
if (which == POSITIVE_BUTTON) {
- IMountService mountService = IMountService.Stub.asInterface(ServiceManager
- .getService("mount"));
- if (mountService != null) {
- try {
- mountService.formatVolume(Environment.getExternalStorageDirectory().toString());
- } catch (RemoteException e) {
- }
- }
+ Intent intent = new Intent(ExternalStorageFormatter.FORMAT_ONLY);
+ intent.setComponent(ExternalStorageFormatter.COMPONENT_NAME);
+ startService(intent);
}
// No matter what, finish the activity
diff --git a/include/camera/Camera.h b/include/camera/Camera.h
index 964700b..e6d84ba 100644
--- a/include/camera/Camera.h
+++ b/include/camera/Camera.h
@@ -83,6 +83,18 @@ enum {
enum {
CAMERA_CMD_START_SMOOTH_ZOOM = 1,
CAMERA_CMD_STOP_SMOOTH_ZOOM = 2,
+ // Set the clockwise rotation of preview display (setPreviewDisplay) in
+ // degrees. This affects the preview frames and the picture displayed after
+ // snapshot. This method is useful for portrait mode applications. Note that
+ // preview display of front-facing cameras is flipped horizontally before
+ // the rotation, that is, the image is reflected along the central vertical
+ // axis of the camera sensor. So the users can see themselves as looking
+ // into a mirror.
+ //
+ // This does not affect the order of byte array of CAMERA_MSG_PREVIEW_FRAME,
+ // CAMERA_MSG_VIDEO_FRAME, CAMERA_MSG_POSTVIEW_FRAME, CAMERA_MSG_RAW_IMAGE,
+ // or CAMERA_MSG_COMPRESSED_IMAGE. This is not allowed to be set during
+ // preview.
CAMERA_CMD_SET_DISPLAY_ORIENTATION = 3,
};
diff --git a/libs/ui/EventHub.cpp b/libs/ui/EventHub.cpp
index 944731d..5c618fb 100644
--- a/libs/ui/EventHub.cpp
+++ b/libs/ui/EventHub.cpp
@@ -818,7 +818,7 @@ int EventHub::openDevice(const char *deviceName) {
}
// See if this device has a gamepad.
- for (size_t i = 0; i < sizeof(GAMEPAD_KEYCODES); i++) {
+ for (size_t i = 0; i < sizeof(GAMEPAD_KEYCODES)/sizeof(GAMEPAD_KEYCODES[0]); i++) {
if (hasKeycodeLocked(device, GAMEPAD_KEYCODES[i])) {
device->classes |= INPUT_DEVICE_CLASS_GAMEPAD;
break;
diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp
index 1ea8d06..ba33e17 100644
--- a/opengl/libagl/egl.cpp
+++ b/opengl/libagl/egl.cpp
@@ -996,8 +996,8 @@ static config_pair_t const config_4_attribute_list[] = {
{ EGL_GREEN_SIZE, 8 },
{ EGL_RED_SIZE, 8 },
{ EGL_DEPTH_SIZE, 0 },
- { EGL_NATIVE_VISUAL_ID, GGL_PIXEL_FORMAT_RGBA_8888 },
{ EGL_CONFIG_ID, 2 },
+ { EGL_NATIVE_VISUAL_ID, GGL_PIXEL_FORMAT_RGBA_8888 },
{ EGL_SURFACE_TYPE, EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT },
};
@@ -1033,8 +1033,8 @@ static config_pair_t const config_7_attribute_list[] = {
{ EGL_GREEN_SIZE, 0 },
{ EGL_RED_SIZE, 0 },
{ EGL_DEPTH_SIZE, 16 },
- { EGL_NATIVE_VISUAL_ID, GGL_PIXEL_FORMAT_A_8 },
{ EGL_CONFIG_ID, 5 },
+ { EGL_NATIVE_VISUAL_ID, GGL_PIXEL_FORMAT_A_8 },
{ EGL_SURFACE_TYPE, EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT },
};
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 58209fd..121819a 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -150,7 +150,10 @@ sp<ICamera> CameraService::connect(
LOGE("Fail to open camera hardware (id=%d)", cameraId);
return NULL;
}
- client = new Client(this, cameraClient, hardware, cameraId, callingPid);
+ CameraInfo info;
+ HAL_getCameraInfo(cameraId, &info);
+ client = new Client(this, cameraClient, hardware, cameraId, info.facing,
+ info.orientation, callingPid);
mClient[cameraId] = client;
LOG1("CameraService::connect X");
return client;
@@ -292,7 +295,7 @@ void CameraService::playSound(sound_kind kind) {
CameraService::Client::Client(const sp<CameraService>& cameraService,
const sp<ICameraClient>& cameraClient,
const sp<CameraHardwareInterface>& hardware,
- int cameraId, int clientPid) {
+ int cameraId, int cameraFacing, int cameraOrientation, int clientPid) {
int callingPid = getCallingPid();
LOG1("Client::Client E (pid %d)", callingPid);
@@ -300,6 +303,8 @@ CameraService::Client::Client(const sp<CameraService>& cameraService,
mCameraClient = cameraClient;
mHardware = hardware;
mCameraId = cameraId;
+ mCameraFacing = cameraFacing;
+ mCameraOrientation = cameraOrientation;
mClientPid = clientPid;
mUseOverlay = mHardware->useOverlay();
mMsgEnabled = 0;
@@ -318,7 +323,7 @@ CameraService::Client::Client(const sp<CameraService>& cameraService,
// Callback is disabled by default
mPreviewCallbackFlag = FRAME_CALLBACK_FLAG_NOOP;
- mOrientation = 0;
+ mOrientation = getOrientation(0, mCameraFacing == CAMERA_FACING_FRONT);
mOrientationChanged = false;
cameraService->setCameraBusy(cameraId);
cameraService->loadSound();
@@ -823,22 +828,10 @@ status_t CameraService::Client::sendCommand(int32_t cmd, int32_t arg1, int32_t a
if (mHardware->previewEnabled()) {
return INVALID_OPERATION;
}
- switch (arg1) {
- case 0:
- orientation = ISurface::BufferHeap::ROT_0;
- break;
- case 90:
- orientation = ISurface::BufferHeap::ROT_90;
- break;
- case 180:
- orientation = ISurface::BufferHeap::ROT_180;
- break;
- case 270:
- orientation = ISurface::BufferHeap::ROT_270;
- break;
- default:
- return BAD_VALUE;
- }
+ // Mirror the preview if the camera is front-facing.
+ orientation = getOrientation(arg1, mCameraFacing == CAMERA_FACING_FRONT);
+ if (orientation == -1) return BAD_VALUE;
+
if (mOrientation != orientation) {
mOrientation = orientation;
if (mOverlayRef != 0) mOrientationChanged = true;
@@ -1204,6 +1197,31 @@ void CameraService::Client::copyFrameAndPostCopiedFrame(
client->dataCallback(CAMERA_MSG_PREVIEW_FRAME, frame);
}
+int CameraService::Client::getOrientation(int degrees, bool mirror) {
+ if (!mirror) {
+ if (degrees == 0) return 0;
+ else if (degrees == 90) return HAL_TRANSFORM_ROT_90;
+ else if (degrees == 180) return HAL_TRANSFORM_ROT_180;
+ else if (degrees == 270) return HAL_TRANSFORM_ROT_270;
+ } else { // mirror (horizontal flip)
+ // Now overlay does ROT_90 before FLIP_V or FLIP_H. It should be FLIP_V
+ // or FLIP_H first.
+ // TODO: change this after overlay is fixed.
+ if (degrees == 0) { // FLIP_H and ROT_0
+ return HAL_TRANSFORM_FLIP_H;
+ } else if (degrees == 90) { // FLIP_H and ROT_90
+ return HAL_TRANSFORM_ROT_90 | HAL_TRANSFORM_FLIP_V;
+ } else if (degrees == 180) { // FLIP_H and ROT_180
+ return HAL_TRANSFORM_FLIP_V;
+ } else if (degrees == 270) { // FLIP_H and ROT_270
+ return HAL_TRANSFORM_ROT_90 | HAL_TRANSFORM_FLIP_H;
+ }
+ }
+ LOGE("Invalid setDisplayOrientation degrees=%d", degrees);
+ return -1;
+}
+
+
// ----------------------------------------------------------------------------
static const int kDumpLockRetries = 50;
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 8f0ed75..0dec2ab 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -108,6 +108,8 @@ private:
const sp<ICameraClient>& cameraClient,
const sp<CameraHardwareInterface>& hardware,
int cameraId,
+ int cameraFacing,
+ int mCameraOrientation,
int clientPid);
~Client();
@@ -153,10 +155,14 @@ private:
const sp<IMemoryHeap>& heap,
size_t offset, size_t size);
+ int getOrientation(int orientation, bool mirror);
+
// these are initialized in the constructor.
sp<CameraService> mCameraService; // immutable after constructor
sp<ICameraClient> mCameraClient;
int mCameraId; // immutable after constructor
+ int mCameraFacing; // immutable after constructor
+ int mCameraOrientation; // immutable after constructor
pid_t mClientPid;
sp<CameraHardwareInterface> mHardware; // cleared after disconnect()
bool mUseOverlay; // immutable after constructor
diff --git a/voip/jni/rtp/AudioGroup.cpp b/voip/jni/rtp/AudioGroup.cpp
index 9da560a..0c8a725 100644
--- a/voip/jni/rtp/AudioGroup.cpp
+++ b/voip/jni/rtp/AudioGroup.cpp
@@ -768,7 +768,7 @@ bool AudioGroup::DeviceThread::threadLoop()
LOGD("latency: output %d, input %d", track.latency(), record.latency());
// Initialize echo canceler.
- EchoSuppressor echo(sampleRate, sampleCount, sampleCount * 2 +
+ EchoSuppressor echo(sampleCount,
(track.latency() + record.latency()) * sampleRate / 1000);
// Give device socket a reasonable buffer size.
diff --git a/voip/jni/rtp/EchoSuppressor.cpp b/voip/jni/rtp/EchoSuppressor.cpp
index ad63cd6..d5cff6e 100644
--- a/voip/jni/rtp/EchoSuppressor.cpp
+++ b/voip/jni/rtp/EchoSuppressor.cpp
@@ -24,146 +24,163 @@
#include "EchoSuppressor.h"
-EchoSuppressor::EchoSuppressor(int sampleRate, int sampleCount, int tailLength)
+// 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)
{
- int scale = 1;
- while (tailLength > 200 * scale) {
- scale <<= 1;
- }
- if (scale > sampleCount) {
- scale = sampleCount;
+ tailLength += sampleCount * 4;
+
+ int shift = 0;
+ while ((sampleCount >> shift) > 1 && (tailLength >> shift) > 256) {
+ ++shift;
}
- mScale = scale;
+ mShift = shift + 4;
+ mScale = 1 << shift;
mSampleCount = sampleCount;
- mWindowSize = sampleCount / scale;
- mTailLength = (tailLength + scale - 1) / scale;
- mRecordLength = (sampleRate + sampleCount - 1) / sampleCount;
+ mWindowSize = sampleCount >> shift;
+ mTailLength = tailLength >> shift;
+ mRecordLength = tailLength * 2 / 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);
+ 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 [] mXYs;
- delete [] mXXs;
+ delete [] mXSums;
+ delete [] mX2Sums;
+ delete [] mXRecords;
+ delete [] mYRecords;
+ delete [] mY2Records;
+ delete [] mXYSums;
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 = mTailLength - 1; i >= 0; --i) {
+ mXs[i + mWindowSize] = mXs[i];
}
- for (int i = 0, j = 0; i < mWindowSize; ++i, j += mScale) {
- float sum = 0;
+ for (int i = mWindowSize - 1, j = 0; i >= 0; --i, j += mScale) {
+ uint32_t sum = 0;
for (int k = 0; k < mScale; ++k) {
- float x = playbacked[j + k] >> 8;
+ int32_t x = playbacked[j + k] << 15;
mLastX += x;
- sum += (mLastX >= 0) ? mLastX : -mLastX;
- mLastX = 0.005f * mLastX - x;
+ sum += ((mLastX >= 0) ? mLastX : -mLastX) >> 15;
+ mLastX -= (mLastX >> 10) + x;
}
- mXs[mTailLength - 1 + i] = sum;
+ mXs[i] = sum >> mShift;
}
- // Update XXs and XXRecords.
- for (int i = 0; i < mTailLength - mWindowSize; ++i) {
- mXXs[i] = mXXs[mWindowSize + i];
+ // Update XSums, X2Sums, and XRecords.
+ for (int i = mTailLength - mWindowSize - 1; i >= 0; --i) {
+ mXSums[i + mWindowSize] = mXSums[i];
+ mX2Sums[i + mWindowSize] = mX2Sums[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;
- }
+ 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.
- float ys[mWindowSize];
- for (int i = 0, j = 0; i < mWindowSize; ++i, j += mScale) {
- float sum = 0;
+ 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) {
- float y = recorded[j + k] >> 8;
+ int32_t y = recorded[j + k] << 15;
mLastY += y;
- sum += (mLastY >= 0) ? mLastY : -mLastY;
- mLastY = 0.005f * mLastY - y;
+ sum += ((mLastY >= 0) ? mLastY : -mLastY) >> 15;
+ mLastY -= (mLastY >> 10) + y;
}
- ys[i] = sum;
+ ys[i] = sum >> mShift;
}
- // 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 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];
}
-
- // 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;
+ 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;
}
- // Computes correlations from XYs, XXs, and YY.
- float weight = 1.0f / (mYY + 1);
- float correlation = 0;
+ // Compute correlations.
+ float corr2 = 0.0f;
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;
+ 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);
- correlation = sqrtf(correlation);
- if (correlation > 0.3f) {
- float factor = 1.0f - correlation;
- factor *= factor;
- factor /= 2.0; // suppress harder
+ // 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] *= factor;
+ recorded[i] = recorded[i] * factor >> 16;
}
}
- //LOGI("latency %5d, correlation %.10f", latency, correlation);
-
// Increase RecordOffset.
++mRecordOffset;
diff --git a/voip/jni/rtp/EchoSuppressor.h b/voip/jni/rtp/EchoSuppressor.h
index 85decf5..2f3b593 100644
--- a/voip/jni/rtp/EchoSuppressor.h
+++ b/voip/jni/rtp/EchoSuppressor.h
@@ -23,11 +23,12 @@ class EchoSuppressor
{
public:
// The sampleCount must be power of 2.
- EchoSuppressor(int sampleRate, int sampleCount, int tailLength);
+ EchoSuppressor(int sampleCount, int tailLength);
~EchoSuppressor();
void run(int16_t *playbacked, int16_t *recorded);
private:
+ int mShift;
int mScale;
int mSampleCount;
int mWindowSize;
@@ -35,17 +36,23 @@ private:
int mRecordLength;
int mRecordOffset;
- float *mXs;
- float *mXYs;
- float *mXXs;
- float mYY;
+ uint16_t *mXs;
+ uint32_t *mXSums;
+ uint32_t *mX2Sums;
+ uint16_t *mXRecords;
- float *mXYRecords;
- float *mXXRecords;
- float *mYYRecords;
+ uint32_t mYSum;
+ uint32_t mY2Sum;
+ uint32_t *mYRecords;
+ uint32_t *mY2Records;
- float mLastX;
- float mLastY;
+ uint32_t *mXYSums;
+ uint32_t *mXYRecords;
+
+ int32_t mLastX;
+ int32_t mLastY;
+
+ float mWeight;
};
#endif