diff options
-rw-r--r-- | cmds/dumpstate/dumpstate.c | 28 | ||||
-rw-r--r-- | core/java/android/hardware/Camera.java | 6 | ||||
-rw-r--r-- | core/java/android/service/wallpaper/WallpaperService.java | 27 | ||||
-rw-r--r-- | core/java/com/android/internal/app/ExternalMediaFormatActivity.java | 17 | ||||
-rw-r--r-- | include/camera/Camera.h | 12 | ||||
-rw-r--r-- | libs/ui/EventHub.cpp | 2 | ||||
-rw-r--r-- | opengl/libagl/egl.cpp | 4 | ||||
-rw-r--r-- | services/camera/libcameraservice/CameraService.cpp | 56 | ||||
-rw-r--r-- | services/camera/libcameraservice/CameraService.h | 6 | ||||
-rw-r--r-- | voip/jni/rtp/AudioGroup.cpp | 2 | ||||
-rw-r--r-- | voip/jni/rtp/EchoSuppressor.cpp | 203 | ||||
-rw-r--r-- | voip/jni/rtp/EchoSuppressor.h | 27 |
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 |