summaryrefslogtreecommitdiffstats
path: root/libaudio/EchoReference.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libaudio/EchoReference.cpp')
-rw-r--r--libaudio/EchoReference.cpp376
1 files changed, 376 insertions, 0 deletions
diff --git a/libaudio/EchoReference.cpp b/libaudio/EchoReference.cpp
new file mode 100644
index 0000000..2c10062
--- /dev/null
+++ b/libaudio/EchoReference.cpp
@@ -0,0 +1,376 @@
+/*
+** Copyright 2011, 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.
+*/
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "EchoReference"
+
+#include <utils/Log.h>
+#include "EchoReference.h"
+
+namespace android_audio_legacy {
+
+//------------------------------------------------------------------------------
+// Echo reference buffer
+//------------------------------------------------------------------------------
+
+EchoReference::EchoReference(audio_format_t rdFormat,
+ uint32_t rdChannelCount,
+ uint32_t rdSamplingRate,
+ audio_format_t wrFormat,
+ uint32_t wrChannelCount,
+ uint32_t wrSamplingRate)
+: mStatus (NO_INIT), mState(ECHOREF_IDLE),
+ mRdFormat(rdFormat), mRdChannelCount(rdChannelCount), mRdSamplingRate(rdSamplingRate),
+ mWrFormat(wrFormat), mWrChannelCount(wrChannelCount), mWrSamplingRate(wrSamplingRate),
+ mBuffer(NULL), mBufSize(0), mFramesIn(0), mWrBuf(NULL), mWrBufSize(0), mWrFramesIn(0),
+ mDownSampler(NULL)
+{
+ LOGV("EchoReference cstor");
+ if (rdFormat != AUDIO_FORMAT_PCM_16_BIT ||
+ rdFormat != wrFormat) {
+ LOGW("EchoReference cstor bad format rd %d, wr %d", rdFormat, wrFormat);
+ mStatus = BAD_VALUE;
+ return;
+ }
+ if ((rdChannelCount != 1 && rdChannelCount != 2) ||
+ wrChannelCount != 2) {
+ LOGW("EchoReference cstor bad channel count rd %d, wr %d", rdChannelCount, wrChannelCount);
+ mStatus = BAD_VALUE;
+ return;
+ }
+
+ if (wrSamplingRate < rdSamplingRate) {
+ LOGW("EchoReference cstor bad smp rate rd %d, wr %d", rdSamplingRate, wrSamplingRate);
+ mStatus = BAD_VALUE;
+ return;
+ }
+
+ mRdFrameSize = audio_bytes_per_sample(rdFormat) * rdChannelCount;
+ mWrFrameSize = audio_bytes_per_sample(wrFormat) * wrChannelCount;
+ mStatus = NO_ERROR;
+}
+
+
+EchoReference::~EchoReference() {
+ LOGV("EchoReference dstor");
+ reset_l();
+ delete mDownSampler;
+}
+
+status_t EchoReference::write(Buffer *buffer)
+{
+ if (mStatus != NO_ERROR) {
+ LOGV("EchoReference::write() ERROR, exiting early");
+ return mStatus;
+ }
+
+ AutoMutex _l(mLock);
+
+ if (buffer == NULL) {
+ LOGV("EchoReference::write() stop write");
+ mState &= ~ECHOREF_WRITING;
+ reset_l();
+ return NO_ERROR;
+ }
+
+ LOGV("EchoReference::write() START trying to write %d frames", buffer->frameCount);
+ LOGV("EchoReference::write() playbackTimestamp:[%lld].[%lld], mPlaybackDelay:[%ld]",
+ (int64_t)buffer->timeStamp.tv_sec,
+ (int64_t)buffer->timeStamp.tv_nsec, mPlaybackDelay);
+
+ //LOGV("EchoReference::write() %d frames", buffer->frameCount);
+ // discard writes until a valid time stamp is provided.
+
+ if ((buffer->timeStamp.tv_sec == 0) && (buffer->timeStamp.tv_nsec == 0) &&
+ (mWrRenderTime.tv_sec == 0) && (mWrRenderTime.tv_nsec == 0)) {
+ return NO_ERROR;
+ }
+
+ if ((mState & ECHOREF_WRITING) == 0) {
+ LOGV("EchoReference::write() start write");
+ if (mDownSampler != NULL) {
+ mDownSampler->reset();
+ }
+ mState |= ECHOREF_WRITING;
+ }
+
+ if ((mState & ECHOREF_READING) == 0) {
+ return NO_ERROR;
+ }
+
+ mWrRenderTime.tv_sec = buffer->timeStamp.tv_sec;
+ mWrRenderTime.tv_nsec = buffer->timeStamp.tv_nsec;
+
+ mPlaybackDelay = buffer->delayNs;
+
+ void *srcBuf;
+ size_t inFrames;
+ // do stereo to mono and down sampling if necessary
+ if (mRdChannelCount != mWrChannelCount ||
+ mRdSamplingRate != mWrSamplingRate) {
+ if (mWrBufSize < buffer->frameCount) {
+ mWrBufSize = buffer->frameCount;
+ //max buffer size is normally function of read sampling rate but as write sampling rate
+ //is always more than read sampling rate this works
+ mWrBuf = realloc(mWrBuf, mWrBufSize * mRdFrameSize);
+ }
+
+ inFrames = buffer->frameCount;
+ if (mRdChannelCount != mWrChannelCount) {
+ // must be stereo to mono
+ int16_t *src16 = (int16_t *)buffer->raw;
+ int16_t *dst16 = (int16_t *)mWrBuf;
+ size_t frames = buffer->frameCount;
+ while (frames--) {
+ *dst16++ = (int16_t)(((int32_t)*src16 + (int32_t)*(src16 + 1)) >> 1);
+ src16 += 2;
+ }
+ }
+ if (mWrSamplingRate != mRdSamplingRate) {
+ if (mDownSampler == NULL) {
+ LOGV("EchoReference::write() new ReSampler(%d, %d)",
+ mWrSamplingRate, mRdSamplingRate);
+ mDownSampler = new ReSampler(mWrSamplingRate,
+ mRdSamplingRate,
+ mRdChannelCount,
+ this);
+
+ }
+ // mWrSrcBuf and mWrFramesIn are used by getNexBuffer() called by the resampler
+ // to get new frames
+ if (mRdChannelCount != mWrChannelCount) {
+ mWrSrcBuf = mWrBuf;
+ } else {
+ mWrSrcBuf = buffer->raw;
+ }
+ mWrFramesIn = buffer->frameCount;
+ // inFrames is always more than we need here to get frames remaining from previous runs
+ // inFrames is updated by resample() with the number of frames produced
+ LOGV("EchoReference::write() ReSampling(%d, %d)",
+ mWrSamplingRate, mRdSamplingRate);
+ mDownSampler->resample((int16_t *)mWrBuf, &inFrames);
+ LOGV_IF(mWrFramesIn != 0,
+ "EchoReference::write() mWrFramesIn not 0 (%d) after resampler",
+ mWrFramesIn);
+ }
+ srcBuf = mWrBuf;
+ } else {
+ inFrames = buffer->frameCount;
+ srcBuf = buffer->raw;
+ }
+
+ if (mFramesIn + inFrames > mBufSize) {
+ LOGV("EchoReference::write() increasing buffer size from %d to %d",
+ mBufSize, mFramesIn + inFrames);
+ mBufSize = mFramesIn + inFrames;
+ mBuffer = realloc(mBuffer, mBufSize * mRdFrameSize);
+ }
+ memcpy((char *)mBuffer + mFramesIn * mRdFrameSize,
+ srcBuf,
+ inFrames * mRdFrameSize);
+ mFramesIn += inFrames;
+
+ LOGV("EchoReference::write_log() inFrames:[%d], mFramesInOld:[%d], "\
+ "mFramesInNew:[%d], mBufSize:[%d], mWrRenderTime:[%lld].[%lld], mPlaybackDelay:[%ld]",
+ inFrames, mFramesIn - inFrames, mFramesIn, mBufSize, (int64_t)mWrRenderTime.tv_sec,
+ (int64_t)mWrRenderTime.tv_nsec, mPlaybackDelay);
+
+ mCond.signal();
+ LOGV("EchoReference::write() END");
+ return NO_ERROR;
+}
+
+status_t EchoReference::read(EchoReference::Buffer *buffer)
+{
+ if (mStatus != NO_ERROR) {
+ return mStatus;
+ }
+ AutoMutex _l(mLock);
+
+ if (buffer == NULL) {
+ LOGV("EchoReference::read() stop read");
+ mState &= ~ECHOREF_READING;
+ return NO_ERROR;
+ }
+
+ LOGV("EchoReference::read() START, delayCapture:[%ld],mFramesIn:[%d],buffer->frameCount:[%d]",
+ buffer->delayNs, mFramesIn, buffer->frameCount);
+
+ if ((mState & ECHOREF_READING) == 0) {
+ LOGV("EchoReference::read() start read");
+ reset_l();
+ mState |= ECHOREF_READING;
+ }
+
+ if ((mState & ECHOREF_WRITING) == 0) {
+ memset(buffer->raw, 0, mRdFrameSize * buffer->frameCount);
+ buffer->delayNs = 0;
+ return NO_ERROR;
+ }
+
+// LOGV("EchoReference::read() %d frames", buffer->frameCount);
+
+ // allow some time for new frames to arrive if not enough frames are ready for read
+ if (mFramesIn < buffer->frameCount) {
+ uint32_t timeoutMs = (uint32_t)((1000 * buffer->frameCount) / mRdSamplingRate / 2);
+
+ mCond.waitRelative(mLock, milliseconds(timeoutMs));
+ if (mFramesIn < buffer->frameCount) {
+ LOGV("EchoReference::read() waited %d ms but still not enough frames"\
+ " mFramesIn: %d, buffer->frameCount = %d",
+ timeoutMs, mFramesIn, buffer->frameCount);
+ buffer->frameCount = mFramesIn;
+ }
+ }
+
+ int64_t timeDiff;
+ struct timespec tmp;
+
+ if ((mWrRenderTime.tv_sec == 0 && mWrRenderTime.tv_nsec == 0) ||
+ (buffer->timeStamp.tv_sec == 0 && buffer->timeStamp.tv_nsec == 0)) {
+ LOGV("read: NEW:timestamp is zero---------setting timeDiff = 0, "\
+ "not updating delay this time");
+ timeDiff = 0;
+ } else {
+ if (buffer->timeStamp.tv_nsec < mWrRenderTime.tv_nsec) {
+ tmp.tv_sec = buffer->timeStamp.tv_sec - mWrRenderTime.tv_sec - 1;
+ tmp.tv_nsec = 1000000000 + buffer->timeStamp.tv_nsec - mWrRenderTime.tv_nsec;
+ } else {
+ tmp.tv_sec = buffer->timeStamp.tv_sec - mWrRenderTime.tv_sec;
+ tmp.tv_nsec = buffer->timeStamp.tv_nsec - mWrRenderTime.tv_nsec;
+ }
+ timeDiff = (((int64_t)tmp.tv_sec * 1000000000 + tmp.tv_nsec));
+
+ int64_t expectedDelayNs = mPlaybackDelay + buffer->delayNs - timeDiff;
+
+ LOGV("expectedDelayNs[%lld] = mPlaybackDelay[%ld] + delayCapture[%ld] - timeDiff[%lld]",
+ expectedDelayNs, mPlaybackDelay, buffer->delayNs, timeDiff);
+
+ if (expectedDelayNs > 0) {
+ int64_t delayNs = ((int64_t)mFramesIn * 1000000000) / mRdSamplingRate;
+
+ delayNs -= expectedDelayNs;
+
+ if (abs(delayNs) >= sMinDelayUpdate) {
+ if (delayNs < 0) {
+ size_t previousFrameIn = mFramesIn;
+ mFramesIn = (expectedDelayNs * mRdSamplingRate)/1000000000;
+ int offset = mFramesIn - previousFrameIn;
+ LOGV("EchoReference::readlog: delayNs = NEGATIVE and ENOUGH : "\
+ "setting %d frames to zero mFramesIn: %d, previousFrameIn = %d",
+ offset, mFramesIn, previousFrameIn);
+
+ if (mFramesIn > mBufSize) {
+ mBufSize = mFramesIn;
+ mBuffer = realloc(mBuffer, mFramesIn * mRdFrameSize);
+ LOGV("EchoReference::read: increasing buffer size to %d", mBufSize);
+ }
+
+ if (offset > 0)
+ memset((char *)mBuffer + previousFrameIn * mRdFrameSize,
+ 0, offset * mRdFrameSize);
+ } else {
+ size_t previousFrameIn = mFramesIn;
+ int framesInInt = (int)(((int64_t)expectedDelayNs *
+ (int64_t)mRdSamplingRate)/1000000000);
+ int offset = previousFrameIn - framesInInt;
+
+ LOGV("EchoReference::readlog: delayNs = POSITIVE/ENOUGH :previousFrameIn: %d,"\
+ "framesInInt: [%d], offset:[%d], buffer->frameCount:[%d]",
+ previousFrameIn, framesInInt, offset, buffer->frameCount);
+
+ if (framesInInt < (int)buffer->frameCount) {
+ if (framesInInt > 0) {
+ memset((char *)mBuffer + framesInInt * mRdFrameSize,
+ 0, (buffer->frameCount-framesInInt) * mRdFrameSize);
+ LOGV("EchoReference::read: pushing [%d] zeros into ref buffer",
+ (buffer->frameCount-framesInInt));
+ } else {
+ LOGV("framesInInt = %d", framesInInt);
+ }
+ framesInInt = buffer->frameCount;
+ } else {
+ if (offset > 0) {
+ memcpy(mBuffer, (char *)mBuffer + (offset * mRdFrameSize),
+ framesInInt * mRdFrameSize);
+ LOGV("EchoReference::read: shifting ref buffer by [%d]",framesInInt);
+ }
+ }
+ mFramesIn = (size_t)framesInInt;
+ }
+ } else {
+ LOGV("EchoReference::read: NOT ENOUGH samples to update %lld", delayNs);
+ }
+ } else {
+ LOGV("NEGATIVE expectedDelayNs[%lld] = "\
+ "mPlaybackDelay[%ld] + delayCapture[%ld] - timeDiff[%lld]",
+ expectedDelayNs, mPlaybackDelay, buffer->delayNs, timeDiff);
+ }
+ }
+
+ memcpy(buffer->raw,
+ (char *)mBuffer,
+ buffer->frameCount * mRdFrameSize);
+
+ mFramesIn -= buffer->frameCount;
+ memcpy(mBuffer,
+ (char *)mBuffer + buffer->frameCount * mRdFrameSize,
+ mFramesIn * mRdFrameSize);
+
+ // As the reference buffer is now time aligned to the microphone signal there is a zero delay
+ buffer->delayNs = 0;
+
+ LOGV("EchoReference::read() END %d frames, total frames in %d",
+ buffer->frameCount, mFramesIn);
+
+ mCond.signal();
+ return NO_ERROR;
+}
+
+void EchoReference::reset_l() {
+ LOGV("EchoReference::reset_l()");
+ free(mBuffer);
+ mBuffer = NULL;
+ mBufSize = 0;
+ mFramesIn = 0;
+ free(mWrBuf);
+ mWrBuf = NULL;
+ mWrBufSize = 0;
+ mWrRenderTime.tv_sec = 0;
+ mWrRenderTime.tv_nsec = 0;
+}
+
+status_t EchoReference::getNextBuffer(ReSampler::BufferProvider::Buffer* buffer)
+{
+ if (mWrSrcBuf == NULL || mWrFramesIn == 0) {
+ buffer->raw = NULL;
+ buffer->frameCount = 0;
+ return NOT_ENOUGH_DATA;
+ }
+
+ buffer->frameCount = (buffer->frameCount > mWrFramesIn) ? mWrFramesIn : buffer->frameCount;
+ // this is mRdChannelCount here as we resample after stereo to mono conversion if any
+ buffer->i16 = (int16_t *)mWrSrcBuf + (mWrBufSize - mWrFramesIn) * mRdChannelCount;
+
+ return 0;
+}
+
+void EchoReference::releaseBuffer(ReSampler::BufferProvider::Buffer* buffer)
+{
+ mWrFramesIn -= buffer->frameCount;
+}
+
+}; // namespace android_audio_legacy