From b3184d71bc6cee9fcbb36343e379143329be00ce Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Wed, 17 Aug 2011 18:36:09 -0700 Subject: [PATCH] Converted libaudioutils implementation to C. Converted libaudioutils implementation and interfaces from C++ to C and removed dependencies from frameworks/base classes so that it can be used by any audio HAL implementation. Change-Id: I3f7ce541be8495d41864661451540971b067359b --- CleanSpec.mk | 1 + audio_utils/Android.mk | 8 +- audio_utils/EchoReference.cpp | 376 ----------------- audio_utils/ReSampler.cpp | 171 -------- audio_utils/echo_reference.c | 498 +++++++++++++++++++++++ audio_utils/include/audio_utils/EchoReference.h | 118 ------ audio_utils/include/audio_utils/ReSampler.h | 80 ---- audio_utils/include/audio_utils/echo_reference.h | 66 +++ audio_utils/include/audio_utils/resampler.h | 109 +++++ audio_utils/resampler.c | 263 ++++++++++++ 10 files changed, 941 insertions(+), 749 deletions(-) delete mode 100644 audio_utils/EchoReference.cpp delete mode 100644 audio_utils/ReSampler.cpp create mode 100644 audio_utils/echo_reference.c delete mode 100644 audio_utils/include/audio_utils/EchoReference.h delete mode 100644 audio_utils/include/audio_utils/ReSampler.h create mode 100644 audio_utils/include/audio_utils/echo_reference.h create mode 100644 audio_utils/include/audio_utils/resampler.h create mode 100644 audio_utils/resampler.c diff --git a/CleanSpec.mk b/CleanSpec.mk index 599de4ac..2422a698 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk @@ -50,6 +50,7 @@ $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/filter $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/filterpack_text_intermediates) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/filterpack_ui_intermediates) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/filterpack_videosrc_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libaudioutils_intermediates) # ************************************************ # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST diff --git a/audio_utils/Android.mk b/audio_utils/Android.mk index 43ba0d7e..8cdde413 100644 --- a/audio_utils/Android.mk +++ b/audio_utils/Android.mk @@ -6,8 +6,8 @@ LOCAL_MODULE := libaudioutils LOCAL_MODULE_TAGS := optional LOCAL_SRC_FILES:= \ - ReSampler.cpp \ - EchoReference.cpp + resampler.c \ + echo_reference.c LOCAL_C_INCLUDES += $(call include-path-for, speex) LOCAL_C_INCLUDES += \ @@ -15,7 +15,7 @@ LOCAL_C_INCLUDES += \ system/media/audio_utils/include LOCAL_SHARED_LIBRARIES := \ - libutils \ - libspeexresampler + libcutils \ + libspeexresampler include $(BUILD_SHARED_LIBRARY) diff --git a/audio_utils/EchoReference.cpp b/audio_utils/EchoReference.cpp deleted file mode 100644 index a17a17bf..00000000 --- a/audio_utils/EchoReference.cpp +++ /dev/null @@ -1,376 +0,0 @@ -/* -** 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 -#include - -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 diff --git a/audio_utils/ReSampler.cpp b/audio_utils/ReSampler.cpp deleted file mode 100644 index 854b7dcd..00000000 --- a/audio_utils/ReSampler.cpp +++ /dev/null @@ -1,171 +0,0 @@ -/* -** 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 "ReSampler" - -#include -#include - -namespace android_audio_legacy { - - -//------------------------------------------------------------------------------ -// speex based resampler -//------------------------------------------------------------------------------ - -#define RESAMPLER_QUALITY 2 - -ReSampler::ReSampler(uint32_t inSampleRate, - uint32_t outSampleRate, - uint32_t channelCount, - BufferProvider* provider) - : mStatus(NO_INIT), mSpeexResampler(NULL), mProvider(provider), - mInSampleRate(inSampleRate), mOutSampleRate(outSampleRate), mChannelCount(channelCount), - mInBuf(NULL), mInBufSize(0) -{ - LOGV("ReSampler() cstor %p In SR %d Out SR %d channels %d", - this, mInSampleRate, mOutSampleRate, mChannelCount); - - if (mProvider == NULL) { - return; - } - - int error; - mSpeexResampler = speex_resampler_init(channelCount, - inSampleRate, - outSampleRate, - RESAMPLER_QUALITY, - &error); - if (mSpeexResampler == NULL) { - LOGW("ReSampler: Cannot create speex resampler: %s", speex_resampler_strerror(error)); - return; - } - - reset(); - - int frames = speex_resampler_get_input_latency(mSpeexResampler); - mSpeexDelayNs = (int32_t)((1000000000 * (int64_t)frames) / mInSampleRate); - frames = speex_resampler_get_output_latency(mSpeexResampler); - mSpeexDelayNs += (int32_t)((1000000000 * (int64_t)frames) / mOutSampleRate); - - mStatus = NO_ERROR; -} - -ReSampler::~ReSampler() -{ - free(mInBuf); - - if (mSpeexResampler != NULL) { - speex_resampler_destroy(mSpeexResampler); - } -} - -void ReSampler::reset() -{ - mFramesIn = 0; - mFramesRq = 0; - - if (mSpeexResampler != NULL) { - speex_resampler_reset_mem(mSpeexResampler); - } -} - -int32_t ReSampler::delayNs() -{ - int32_t delay = (int32_t)((1000000000 * (int64_t)mFramesIn) / mInSampleRate); - delay += mSpeexDelayNs; - - return delay; -} - -// outputs a number of frames less or equal to *outFrameCount and updates *outFrameCount -// with the actual number of frames produced. -int ReSampler::resample(int16_t *out, size_t *outFrameCount) -{ - if (mStatus != NO_ERROR) { - return mStatus; - } - - if (out == NULL || outFrameCount == NULL) { - return BAD_VALUE; - } - - size_t framesRq = *outFrameCount; - // update and cache the number of frames needed at the input sampling rate to produce - // the number of frames requested at the output sampling rate - if (framesRq != mFramesRq) { - mFramesNeeded = (framesRq * mOutSampleRate) / mInSampleRate + 1; - mFramesRq = framesRq; - } - - size_t framesWr = 0; - size_t inFrames = 0; - while (framesWr < framesRq) { - if (mFramesIn < mFramesNeeded) { - // make sure that the number of frames present in mInBuf (mFramesIn) is at least - // the number of frames needed to produce the number of frames requested at - // the output sampling rate - if (mInBufSize < mFramesNeeded) { - mInBufSize = mFramesNeeded; - mInBuf = (int16_t *)realloc(mInBuf, mInBufSize * mChannelCount * sizeof(int16_t)); - } - BufferProvider::Buffer buf; - buf.frameCount = mFramesNeeded - mFramesIn; - mProvider->getNextBuffer(&buf); - if (buf.raw == NULL) { - break; - } - memcpy(mInBuf + mFramesIn * mChannelCount, - buf.raw, - buf.frameCount * mChannelCount * sizeof(int16_t)); - mFramesIn += buf.frameCount; - mProvider->releaseBuffer(&buf); - } - - size_t outFrames = framesRq - framesWr; - inFrames = mFramesIn; - if (mChannelCount == 1) { - speex_resampler_process_int(mSpeexResampler, - 0, - mInBuf, - &inFrames, - out + framesWr * mChannelCount, - &outFrames); - } else { - speex_resampler_process_interleaved_int(mSpeexResampler, - mInBuf, - &inFrames, - out + framesWr * mChannelCount, - &outFrames); - } - framesWr += outFrames; - mFramesIn -= inFrames; - LOGW_IF((framesWr != framesRq) && (mFramesIn != 0), - "ReSampler::resample() remaining %d frames in and %d frames out", - mFramesIn, (framesRq - framesWr)); - } - if (mFramesIn) { - memmove(mInBuf, - mInBuf + inFrames * mChannelCount, - mFramesIn * mChannelCount * sizeof(int16_t)); - } - *outFrameCount = framesWr; - - return NO_ERROR; -} - -}; // namespace android_audio_legacy diff --git a/audio_utils/echo_reference.c b/audio_utils/echo_reference.c new file mode 100644 index 00000000..3dda2705 --- /dev/null +++ b/audio_utils/echo_reference.c @@ -0,0 +1,498 @@ +/* +** 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 "echo_reference" + +#include +#include +#include +#include +#include +#include +#include + +// echo reference state: bit field indicating if read, write or both are active. +enum state { + ECHOREF_IDLE = 0x00, // idle + ECHOREF_READING = 0x01, // reading is active + ECHOREF_WRITING = 0x02 // writing is active +}; + +struct echo_reference { + struct echo_reference_itfe itfe; + int status; // init status + uint32_t state; // active state: reading, writing or both + audio_format_t rd_format; // read sample format + uint32_t rd_channel_count; // read number of channels + uint32_t rd_sampling_rate; // read sampling rate in Hz + size_t rd_frame_size; // read frame size (bytes per sample) + audio_format_t wr_format; // write sample format + uint32_t wr_channel_count; // write number of channels + uint32_t wr_sampling_rate; // write sampling rate in Hz + size_t wr_frame_size; // write frame size (bytes per sample) + void *buffer; // main buffer + size_t buf_size; // main buffer size in frames + size_t frames_in; // number of frames in main buffer + void *wr_buf; // buffer for input conversions + size_t wr_buf_size; // size of conversion buffer in frames + size_t wr_frames_in; // number of frames in conversion buffer + void *wr_src_buf; // resampler input buf (either wr_buf or buffer used by write()) + struct timespec wr_render_time; // latest render time indicated by write() + // default ALSA gettimeofday() format + int32_t playback_delay; // playback buffer delay indicated by last write() + pthread_mutex_t lock; // mutex protecting read/write concurrency + pthread_cond_t cond; // condition signaled when data is ready to read + struct resampler_itfe *down_sampler; // input resampler + struct resampler_buffer_provider provider; // resampler buffer provider +}; + + +// container_of is used to retrieve the containing struct echo_reference of +// a struct buffer_provider passed to echo_reference_get_next_buffer() or +// echo_reference_release_buffer() +// - ptr: pointer to the contained member +// - type: type of the containing struct +// - member: name of the member in the containing struct +#ifndef container_of +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) +#endif + +int echo_reference_get_next_buffer(struct resampler_buffer_provider *buffer_provider, + struct resampler_buffer* buffer) +{ + struct echo_reference *er; + + if (buffer_provider == NULL) { + return -EINVAL; + } + + er = container_of(buffer_provider, struct echo_reference, provider); + + if (er->wr_src_buf == NULL || er->wr_frames_in == 0) { + buffer->raw = NULL; + buffer->frame_count = 0; + return -ENODATA; + } + + buffer->frame_count = (buffer->frame_count > er->wr_frames_in) ? er->wr_frames_in : buffer->frame_count; + // this is er->rd_channel_count here as we resample after stereo to mono conversion if any + buffer->i16 = (int16_t *)er->wr_src_buf + (er->wr_buf_size - er->wr_frames_in) * er->rd_channel_count; + + return 0; +} + +void echo_reference_release_buffer(struct resampler_buffer_provider *buffer_provider, + struct resampler_buffer* buffer) +{ + struct echo_reference *er; + + if (buffer_provider == NULL) { + return; + } + + er = container_of(buffer_provider, struct echo_reference, provider); + + er->wr_frames_in -= buffer->frame_count; +} + +static void echo_reference_reset_l(struct echo_reference *er) +{ + LOGV("echo_reference_reset_l()"); + free(er->buffer); + er->buffer = NULL; + er->buf_size = 0; + er->frames_in = 0; + free(er->wr_buf); + er->wr_buf = NULL; + er->wr_buf_size = 0; + er->wr_render_time.tv_sec = 0; + er->wr_render_time.tv_nsec = 0; +} + +static int echo_reference_write(struct echo_reference_itfe *echo_reference, + struct echo_reference_buffer *buffer) +{ + struct echo_reference *er = (struct echo_reference *)echo_reference; + int status = 0; + + if (er == NULL) { + return -EINVAL; + } + + pthread_mutex_lock(&er->lock); + + if (buffer == NULL) { + LOGV("echo_reference_write() stop write"); + er->state &= ~ECHOREF_WRITING; + echo_reference_reset_l(er); + goto exit; + } + + LOGV("echo_reference_write() START trying to write %d frames", buffer->frame_count); + LOGV("echo_reference_write() playbackTimestamp:[%d].[%d], er->playback_delay:[%d]", + (int)buffer->time_stamp.tv_sec, + (int)buffer->time_stamp.tv_nsec, er->playback_delay); + + //LOGV("echo_reference_write() %d frames", buffer->frame_count); + // discard writes until a valid time stamp is provided. + + if ((buffer->time_stamp.tv_sec == 0) && (buffer->time_stamp.tv_nsec == 0) && + (er->wr_render_time.tv_sec == 0) && (er->wr_render_time.tv_nsec == 0)) { + goto exit; + } + + if ((er->state & ECHOREF_WRITING) == 0) { + LOGV("echo_reference_write() start write"); + if (er->down_sampler != NULL) { + er->down_sampler->reset(er->down_sampler); + } + er->state |= ECHOREF_WRITING; + } + + if ((er->state & ECHOREF_READING) == 0) { + goto exit; + } + + er->wr_render_time.tv_sec = buffer->time_stamp.tv_sec; + er->wr_render_time.tv_nsec = buffer->time_stamp.tv_nsec; + + er->playback_delay = buffer->delay_ns; + + void *srcBuf; + size_t inFrames; + // do stereo to mono and down sampling if necessary + if (er->rd_channel_count != er->wr_channel_count || + er->rd_sampling_rate != er->wr_sampling_rate) { + if (er->wr_buf_size < buffer->frame_count) { + er->wr_buf_size = buffer->frame_count; + //max buffer size is normally function of read sampling rate but as write sampling rate + //is always more than read sampling rate this works + er->wr_buf = realloc(er->wr_buf, er->wr_buf_size * er->rd_frame_size); + } + + inFrames = buffer->frame_count; + if (er->rd_channel_count != er->wr_channel_count) { + // must be stereo to mono + int16_t *src16 = (int16_t *)buffer->raw; + int16_t *dst16 = (int16_t *)er->wr_buf; + size_t frames = buffer->frame_count; + while (frames--) { + *dst16++ = (int16_t)(((int32_t)*src16 + (int32_t)*(src16 + 1)) >> 1); + src16 += 2; + } + } + if (er->wr_sampling_rate != er->rd_sampling_rate) { + if (er->down_sampler == NULL) { + int rc; + LOGV("echo_reference_write() new ReSampler(%d, %d)", + er->wr_sampling_rate, er->rd_sampling_rate); + er->provider.get_next_buffer = echo_reference_get_next_buffer; + er->provider.release_buffer = echo_reference_release_buffer; + rc = create_resampler(er->wr_sampling_rate, + er->rd_sampling_rate, + er->rd_channel_count, + RESAMPLER_QUALITY_VOIP, + &er->provider, + &er->down_sampler); + if (rc != 0) { + er->down_sampler = NULL; + LOGV("echo_reference_write() failure to create resampler %d", rc); + status = -ENODEV; + goto exit; + } + } + // er->wr_src_buf and er->wr_frames_in are used by getNexBuffer() called by the resampler + // to get new frames + if (er->rd_channel_count != er->wr_channel_count) { + er->wr_src_buf = er->wr_buf; + } else { + er->wr_src_buf = buffer->raw; + } + er->wr_frames_in = buffer->frame_count; + // 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("echo_reference_write() ReSampling(%d, %d)", + er->wr_sampling_rate, er->rd_sampling_rate); + er->down_sampler->resample_from_provider(er->down_sampler, + (int16_t *)er->wr_buf, &inFrames); + LOGV_IF(er->wr_frames_in != 0, + "echo_reference_write() er->wr_frames_in not 0 (%d) after resampler", + er->wr_frames_in); + } + srcBuf = er->wr_buf; + } else { + inFrames = buffer->frame_count; + srcBuf = buffer->raw; + } + + if (er->frames_in + inFrames > er->buf_size) { + LOGV("echo_reference_write() increasing buffer size from %d to %d", + er->buf_size, er->frames_in + inFrames); + er->buf_size = er->frames_in + inFrames; + er->buffer = realloc(er->buffer, er->buf_size * er->rd_frame_size); + } + memcpy((char *)er->buffer + er->frames_in * er->rd_frame_size, + srcBuf, + inFrames * er->rd_frame_size); + er->frames_in += inFrames; + + LOGV("EchoReference::write_log() inFrames:[%d], mFramesInOld:[%d], "\ + "mFramesInNew:[%d], er->buf_size:[%d], er->wr_render_time:[%d].[%d]," + "er->playback_delay:[%d]", + inFrames, er->frames_in - inFrames, er->frames_in, er->buf_size, + (int)er->wr_render_time.tv_sec, + (int)er->wr_render_time.tv_nsec, er->playback_delay); + + pthread_cond_signal(&er->cond); +exit: + pthread_mutex_unlock(&er->lock); + LOGV("echo_reference_write() END"); + return status; +} + +#define MIN_DELAY_UPDATE_NS 62500 // delay jump threshold to update ref buffer + // 0.5 samples at 8kHz in nsecs + + +static int echo_reference_read(struct echo_reference_itfe *echo_reference, + struct echo_reference_buffer *buffer) +{ + struct echo_reference *er = (struct echo_reference *)echo_reference; + + if (er == NULL) { + return -EINVAL; + } + + pthread_mutex_lock(&er->lock); + + if (buffer == NULL) { + LOGV("EchoReference::read() stop read"); + er->state &= ~ECHOREF_READING; + goto exit; + } + + LOGV("EchoReference::read() START, delayCapture:[%d],er->frames_in:[%d],buffer->frame_count:[%d]", + buffer->delay_ns, er->frames_in, buffer->frame_count); + + if ((er->state & ECHOREF_READING) == 0) { + LOGV("EchoReference::read() start read"); + echo_reference_reset_l(er); + er->state |= ECHOREF_READING; + } + + if ((er->state & ECHOREF_WRITING) == 0) { + memset(buffer->raw, 0, er->rd_frame_size * buffer->frame_count); + buffer->delay_ns = 0; + goto exit; + } + +// LOGV("EchoReference::read() %d frames", buffer->frame_count); + + // allow some time for new frames to arrive if not enough frames are ready for read + if (er->frames_in < buffer->frame_count) { + uint32_t timeoutMs = (uint32_t)((1000 * buffer->frame_count) / er->rd_sampling_rate / 2); + struct timespec ts; + + ts.tv_sec = timeoutMs/1000; + ts.tv_nsec = timeoutMs%1000; + pthread_cond_timedwait_relative_np(&er->cond, &er->lock, &ts); + + if (er->frames_in < buffer->frame_count) { + LOGV("EchoReference::read() waited %d ms but still not enough frames"\ + " er->frames_in: %d, buffer->frame_count = %d", + timeoutMs, er->frames_in, buffer->frame_count); + buffer->frame_count = er->frames_in; + } + } + + int64_t timeDiff; + struct timespec tmp; + + if ((er->wr_render_time.tv_sec == 0 && er->wr_render_time.tv_nsec == 0) || + (buffer->time_stamp.tv_sec == 0 && buffer->time_stamp.tv_nsec == 0)) { + LOGV("read: NEW:timestamp is zero---------setting timeDiff = 0, "\ + "not updating delay this time"); + timeDiff = 0; + } else { + if (buffer->time_stamp.tv_nsec < er->wr_render_time.tv_nsec) { + tmp.tv_sec = buffer->time_stamp.tv_sec - er->wr_render_time.tv_sec - 1; + tmp.tv_nsec = 1000000000 + buffer->time_stamp.tv_nsec - er->wr_render_time.tv_nsec; + } else { + tmp.tv_sec = buffer->time_stamp.tv_sec - er->wr_render_time.tv_sec; + tmp.tv_nsec = buffer->time_stamp.tv_nsec - er->wr_render_time.tv_nsec; + } + timeDiff = (((int64_t)tmp.tv_sec * 1000000000 + tmp.tv_nsec)); + + int64_t expectedDelayNs = er->playback_delay + buffer->delay_ns - timeDiff; + + LOGV("expectedDelayNs[%lld] = er->playback_delay[%d] + delayCapture[%d] - timeDiff[%lld]", + expectedDelayNs, er->playback_delay, buffer->delay_ns, timeDiff); + + if (expectedDelayNs > 0) { + int64_t delayNs = ((int64_t)er->frames_in * 1000000000) / er->rd_sampling_rate; + + delayNs -= expectedDelayNs; + + if (abs(delayNs) >= MIN_DELAY_UPDATE_NS) { + if (delayNs < 0) { + size_t previousFrameIn = er->frames_in; + er->frames_in = (expectedDelayNs * er->rd_sampling_rate)/1000000000; + int offset = er->frames_in - previousFrameIn; + LOGV("EchoReference::readlog: delayNs = NEGATIVE and ENOUGH : "\ + "setting %d frames to zero er->frames_in: %d, previousFrameIn = %d", + offset, er->frames_in, previousFrameIn); + + if (er->frames_in > er->buf_size) { + er->buf_size = er->frames_in; + er->buffer = realloc(er->buffer, er->frames_in * er->rd_frame_size); + LOGV("EchoReference::read: increasing buffer size to %d", er->buf_size); + } + + if (offset > 0) + memset((char *)er->buffer + previousFrameIn * er->rd_frame_size, + 0, offset * er->rd_frame_size); + } else { + size_t previousFrameIn = er->frames_in; + int framesInInt = (int)(((int64_t)expectedDelayNs * + (int64_t)er->rd_sampling_rate)/1000000000); + int offset = previousFrameIn - framesInInt; + + LOGV("EchoReference::readlog: delayNs = POSITIVE/ENOUGH :previousFrameIn: %d,"\ + "framesInInt: [%d], offset:[%d], buffer->frame_count:[%d]", + previousFrameIn, framesInInt, offset, buffer->frame_count); + + if (framesInInt < (int)buffer->frame_count) { + if (framesInInt > 0) { + memset((char *)er->buffer + framesInInt * er->rd_frame_size, + 0, (buffer->frame_count-framesInInt) * er->rd_frame_size); + LOGV("EchoReference::read: pushing [%d] zeros into ref buffer", + (buffer->frame_count-framesInInt)); + } else { + LOGV("framesInInt = %d", framesInInt); + } + framesInInt = buffer->frame_count; + } else { + if (offset > 0) { + memcpy(er->buffer, (char *)er->buffer + (offset * er->rd_frame_size), + framesInInt * er->rd_frame_size); + LOGV("EchoReference::read: shifting ref buffer by [%d]",framesInInt); + } + } + er->frames_in = (size_t)framesInInt; + } + } else { + LOGV("EchoReference::read: NOT ENOUGH samples to update %lld", delayNs); + } + } else { + LOGV("NEGATIVE expectedDelayNs[%lld] = "\ + "er->playback_delay[%d] + delayCapture[%d] - timeDiff[%lld]", + expectedDelayNs, er->playback_delay, buffer->delay_ns, timeDiff); + } + } + + memcpy(buffer->raw, + (char *)er->buffer, + buffer->frame_count * er->rd_frame_size); + + er->frames_in -= buffer->frame_count; + memcpy(er->buffer, + (char *)er->buffer + buffer->frame_count * er->rd_frame_size, + er->frames_in * er->rd_frame_size); + + // As the reference buffer is now time aligned to the microphone signal there is a zero delay + buffer->delay_ns = 0; + + LOGV("EchoReference::read() END %d frames, total frames in %d", + buffer->frame_count, er->frames_in); + + pthread_cond_signal(&er->cond); + +exit: + pthread_mutex_unlock(&er->lock); + return 0; +} + + +int create_echo_reference(audio_format_t rdFormat, + uint32_t rdChannelCount, + uint32_t rdSamplingRate, + audio_format_t wrFormat, + uint32_t wrChannelCount, + uint32_t wrSamplingRate, + struct echo_reference_itfe **echo_reference) +{ + struct echo_reference *er; + + LOGV("create_echo_reference()"); + + if (echo_reference == NULL) { + return -EINVAL; + } + + *echo_reference = NULL; + + if (rdFormat != AUDIO_FORMAT_PCM_16_BIT || + rdFormat != wrFormat) { + LOGW("create_echo_reference bad format rd %d, wr %d", rdFormat, wrFormat); + return -EINVAL; + } + if ((rdChannelCount != 1 && rdChannelCount != 2) || + wrChannelCount != 2) { + LOGW("create_echo_reference bad channel count rd %d, wr %d", rdChannelCount, wrChannelCount); + return -EINVAL; + } + + if (wrSamplingRate < rdSamplingRate) { + LOGW("create_echo_reference bad smp rate rd %d, wr %d", rdSamplingRate, wrSamplingRate); + return -EINVAL; + } + + er = (struct echo_reference *)calloc(1, sizeof(struct echo_reference)); + + er->itfe.read = echo_reference_read; + er->itfe.write = echo_reference_write; + + er->state = ECHOREF_IDLE; + er->rd_format = rdFormat; + er->rd_channel_count = rdChannelCount; + er->rd_sampling_rate = rdSamplingRate; + er->wr_format = wrFormat; + er->wr_channel_count = wrChannelCount; + er->wr_sampling_rate = wrSamplingRate; + er->rd_frame_size = audio_bytes_per_sample(rdFormat) * rdChannelCount; + er->wr_frame_size = audio_bytes_per_sample(wrFormat) * wrChannelCount; + *echo_reference = &er->itfe; + return 0; +} + +void release_echo_reference(struct echo_reference_itfe *echo_reference) { + struct echo_reference *er = (struct echo_reference *)echo_reference; + + if (er == NULL) { + return; + } + + LOGV("EchoReference dstor"); + echo_reference_reset_l(er); + if (er->down_sampler != NULL) { + release_resampler(er->down_sampler); + } + free(er); +} + diff --git a/audio_utils/include/audio_utils/EchoReference.h b/audio_utils/include/audio_utils/EchoReference.h deleted file mode 100644 index e20ec69d..00000000 --- a/audio_utils/include/audio_utils/EchoReference.h +++ /dev/null @@ -1,118 +0,0 @@ -/* -** 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. -*/ - -#ifndef ANDROID_ECHO_REFERENCE_H -#define ANDROID_ECHO_REFERENCE_H - -#include -#include -#include -#include -#include - -namespace android_audio_legacy { -using android::Mutex; -using android::AutoMutex; - -class EchoReference : public ReSampler::BufferProvider { - -public: - - EchoReference(audio_format_t rdFormat, - uint32_t rdChannelCount, - uint32_t rdSamplingRate, - audio_format_t wrFormat, - uint32_t wrChannelCount, - uint32_t wrSamplingRate); - virtual ~EchoReference(); - - // echo reference state: it field indicating if read, write or both are active. - enum state { - ECHOREF_IDLE = 0x00, // idle - ECHOREF_READING = 0x01, // reading is active - ECHOREF_WRITING = 0x02 // writing is active - }; - - // Buffer descriptor used by read() and write() methods, including the time stamp and delay. - class Buffer { - public: - void *raw; // pointer to audio frame - size_t frameCount; // number of frames in buffer - int32_t delayNs; // delay for this buffer (see comment below) - struct timespec timeStamp; // time stamp for this buffer (see comment below) - }; - // when used for EchoReference::write(): - // + as input: - // - delayNs is the delay introduced by playback buffers - // - timeStamp is the time stamp corresponding to the delay calculation - // + as output: - // unused - // when used for EchoReference::read(): - // + as input: - // - delayNs is the delay introduced by capture buffers - // - timeStamp is the time stamp corresponding to the delay calculation - // + as output: - // - delayNs is the delay between the returned frames and the capture time derived from - // delay and time stamp indicated as input. This delay is to be communicated to the AEC. - // - frameCount is updated with the actual number of frames returned - - - // BufferProvider - status_t getNextBuffer(ReSampler::BufferProvider::Buffer* buffer); - void releaseBuffer(ReSampler::BufferProvider::Buffer* buffer); - - status_t initCheck() { return mStatus; } - - status_t read(Buffer *buffer); - status_t write(Buffer *buffer); - - -private: - - void reset_l(); - - status_t mStatus; // init status - uint32_t mState; // active state: reading, writing or both - audio_format_t mRdFormat; // read sample format - uint32_t mRdChannelCount; // read number of channels - uint32_t mRdSamplingRate; // read sampling rate - size_t mRdFrameSize; // read frame size (bytes per sample) - audio_format_t mWrFormat; // write sample format - uint32_t mWrChannelCount; // write number of channels - uint32_t mWrSamplingRate; // write sampling rate - size_t mWrFrameSize; // write frame size (bytes per sample) - void *mBuffer; // main buffer - size_t mBufSize; // main buffer size in frames - size_t mFramesIn; // number of frames in main buffer - void *mWrBuf; // buffer for input conversions - size_t mWrBufSize; // size of conversion buffer in frames - size_t mWrFramesIn; // number of frames in conversion buffer - void *mWrSrcBuf; // resampler input buf (either mWrBuf or buffer used by write() - struct timespec mWrRenderTime; // latest render time indicated by write() - int32_t mPlaybackDelay; - - uint32_t mRdDurationUs; // Duration of last buffer read (used for mCond wait timeout) - android::Mutex mLock; // Mutex protecting read/write concurrency - android::Condition mCond; // Condition signaled when data is ready to read - ReSampler *mDownSampler; // input resampler - - static const int sMinDelayUpdate = 62500; // delay jump threshold to update ref buffer - // 0.5 samples at 8kHz in nsecs -}; - -}; // namespace android - -#endif // ANDROID_ECHO_REFERENCE_H diff --git a/audio_utils/include/audio_utils/ReSampler.h b/audio_utils/include/audio_utils/ReSampler.h deleted file mode 100644 index f531900d..00000000 --- a/audio_utils/include/audio_utils/ReSampler.h +++ /dev/null @@ -1,80 +0,0 @@ -/* -** Copyright 2008, 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 ANDROID_RESAMPLER_H -#define ANDROID_RESAMPLER_H - -#include -#include -#include -#include "speex/speex_resampler.h" - -namespace android_audio_legacy { - -class ReSampler { -public: - - class BufferProvider - { - public: - - struct Buffer { - union { - void* raw; - short* i16; - int8_t* i8; - }; - size_t frameCount; - }; - - virtual ~BufferProvider() {} - - virtual status_t getNextBuffer(Buffer* buffer) = 0; - virtual void releaseBuffer(Buffer* buffer) = 0; - }; - - ReSampler(uint32_t inSampleRate, - uint32_t outSampleRate, - uint32_t channelCount, - BufferProvider* provider); - - virtual ~ReSampler(); - - status_t initCheck() { return mStatus; } - void reset(); - int resample(int16_t* out, size_t *outFrameCount); - int32_t delayNs(); - - -private: - status_t mStatus; // init status - SpeexResamplerState *mSpeexResampler; // handle on speex resampler - BufferProvider* mProvider; // buffer provider installed by client - uint32_t mInSampleRate; // input sampling rate - uint32_t mOutSampleRate; // output sampling rate - uint32_t mChannelCount; // number of channels - int16_t *mInBuf; // input buffer - size_t mInBufSize; // input buffer size - size_t mFramesIn; // number of frames in input buffer - size_t mFramesRq; // cached number of output frames - size_t mFramesNeeded; // minimum number of input frames to produce mFramesRq - // output frames - int32_t mSpeexDelayNs; // delay introduced by speex resampler in ns -}; - -}; // namespace android_audio_legacy - -#endif // ANDROID_RESAMPLER_H diff --git a/audio_utils/include/audio_utils/echo_reference.h b/audio_utils/include/audio_utils/echo_reference.h new file mode 100644 index 00000000..15edda4d --- /dev/null +++ b/audio_utils/include/audio_utils/echo_reference.h @@ -0,0 +1,66 @@ +/* +** 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. +*/ + +#ifndef ANDROID_ECHO_REFERENCE_H +#define ANDROID_ECHO_REFERENCE_H + +#include +#include + +__BEGIN_DECLS + +/* Buffer descriptor used by read() and write() methods, including the time stamp and delay. */ +struct echo_reference_buffer { + void *raw; // pointer to audio frame + size_t frame_count; // number of frames in buffer + int32_t delay_ns; // delay for this buffer (see comment below) + struct timespec time_stamp; // time stamp for this buffer (see comment below) + // default ALSA gettimeofday() format +}; +/** + * + as input: + * - delay_ns is the delay introduced by playback buffers + * - time_stamp is the time stamp corresponding to the delay calculation + * + as output: + * unused + * when used for EchoReference::read(): + * + as input: + * - delay_ns is the delay introduced by capture buffers + * - time_stamp is the time stamp corresponding to the delay calculation + * + as output: + * - delay_ns is the delay between the returned frames and the capture time derived from + * delay and time stamp indicated as input. This delay is to be communicated to the AEC. + * - frame_count is updated with the actual number of frames returned + */ + +struct echo_reference_itfe { + int (*read)(struct echo_reference_itfe *echo_reference, struct echo_reference_buffer *buffer); + int (*write)(struct echo_reference_itfe *echo_reference, struct echo_reference_buffer *buffer); +}; + +int create_echo_reference(audio_format_t rdFormat, + uint32_t rdChannelCount, + uint32_t rdSamplingRate, + audio_format_t wrFormat, + uint32_t wrChannelCount, + uint32_t wrSamplingRate, + struct echo_reference_itfe **); + +void release_echo_reference(struct echo_reference_itfe *echo_reference); + +__END_DECLS + +#endif // ANDROID_ECHO_REFERENCE_H diff --git a/audio_utils/include/audio_utils/resampler.h b/audio_utils/include/audio_utils/resampler.h new file mode 100644 index 00000000..0c7046f5 --- /dev/null +++ b/audio_utils/include/audio_utils/resampler.h @@ -0,0 +1,109 @@ +/* +** Copyright 2008, 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 ANDROID_RESAMPLER_H +#define ANDROID_RESAMPLER_H + +#include +#include + +__BEGIN_DECLS + + +#define RESAMPLER_QUALITY_MAX 10 +#define RESAMPLER_QUALITY_MIN 0 +#define RESAMPLER_QUALITY_DEFAULT 4 +#define RESAMPLER_QUALITY_VOIP 3 +#define RESAMPLER_QUALITY_DESKTOP 5 + +struct resampler_buffer { + union { + void* raw; + short* i16; + int8_t* i8; + }; + size_t frame_count; +}; + +/* call back interface used by the resampler to get new data */ +struct resampler_buffer_provider +{ + /** + * get a new buffer of data: + * as input: buffer->frame_count is the number of frames requested + * as output: buffer->frame_count is the number of frames returned + * buffer->raw points to data returned + */ + int (*get_next_buffer)(struct resampler_buffer_provider *provider, + struct resampler_buffer *buffer); + /** + * release a consumed buffer of data: + * as input: buffer->frame_count is the number of frames released + * buffer->raw points to data released + */ + void (*release_buffer)(struct resampler_buffer_provider *provider, + struct resampler_buffer *buffer); +}; + +/* resampler interface */ +struct resampler_itfe { + /** + * reset resampler state + */ + void (*reset)(struct resampler_itfe *resampler); + /** + * resample input from buffer provider and output at most *outFrameCount to out buffer. + * *outFrameCount is updated with the actual number of frames produced. + */ + int (*resample_from_provider)(struct resampler_itfe *resampler, + int16_t *out, + size_t *outFrameCount); + /** + * resample at most *inFrameCount frames from in buffer and output at most + * *outFrameCount to out buffer. *inFrameCount and *outFrameCount are updated respectively + * with the number of frames remaining in input and written to output. + */ + int (*resample_from_input)(struct resampler_itfe *resampler, + int16_t *in, + size_t *inFrameCount, + int16_t *out, + size_t *outFrameCount); + /** + * return the latency introduced by the resampler in ns. + */ + int32_t (*delay_ns)(struct resampler_itfe *resampler); +}; + +/** + * create a resampler according to input parameters passed. + * If resampler_buffer_provider is not NULL only resample_from_provider() can be called. + * If resampler_buffer_provider is NULL only resample_from_input() can be called. + */ +int create_resampler(uint32_t inSampleRate, + uint32_t outSampleRate, + uint32_t channelCount, + uint32_t quality, + struct resampler_buffer_provider *provider, + struct resampler_itfe **); + +/** + * release resampler resources. + */ +void release_resampler(struct resampler_itfe *); + +__END_DECLS + +#endif // ANDROID_RESAMPLER_H diff --git a/audio_utils/resampler.c b/audio_utils/resampler.c new file mode 100644 index 00000000..d41dd42c --- /dev/null +++ b/audio_utils/resampler.c @@ -0,0 +1,263 @@ +/* +** 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 "resampler" + +#include +#include +#include +#include +#include +#include + + +struct resampler { + struct resampler_itfe itfe; + SpeexResamplerState *speex_resampler; // handle on speex resampler + struct resampler_buffer_provider *provider; // buffer provider installed by client + uint32_t in_sample_rate; // input sampling rate in Hz + uint32_t out_sample_rate; // output sampling rate in Hz + uint32_t channel_count; // number of channels (interleaved) + int16_t *in_buf; // input buffer + size_t in_buf_size; // input buffer size + size_t frames_in; // number of frames in input buffer + size_t frames_rq; // cached number of output frames + size_t frames_needed; // minimum number of input frames to produce + // frames_rq output frames + int32_t speex_delay_ns; // delay introduced by speex resampler in ns +}; + + +//------------------------------------------------------------------------------ +// speex based resampler +//------------------------------------------------------------------------------ + +static void resampler_reset(struct resampler_itfe *resampler) +{ + struct resampler *rsmp = (struct resampler *)resampler; + + rsmp->frames_in = 0; + rsmp->frames_rq = 0; + + if (rsmp != NULL && rsmp->speex_resampler != NULL) { + speex_resampler_reset_mem(rsmp->speex_resampler); + } +} + +static int32_t resampler_delay_ns(struct resampler_itfe *resampler) +{ + struct resampler *rsmp = (struct resampler *)resampler; + + int32_t delay = (int32_t)((1000000000 * (int64_t)rsmp->frames_in) / rsmp->in_sample_rate); + delay += rsmp->speex_delay_ns; + + return delay; +} + +// outputs a number of frames less or equal to *outFrameCount and updates *outFrameCount +// with the actual number of frames produced. +int resampler_resample_from_provider(struct resampler_itfe *resampler, + int16_t *out, + size_t *outFrameCount) +{ + struct resampler *rsmp = (struct resampler *)resampler; + + if (rsmp == NULL || out == NULL || outFrameCount == NULL) { + return -EINVAL; + } + if (rsmp->provider == NULL) { + *outFrameCount = 0; + return -ENOSYS; + } + + size_t framesRq = *outFrameCount; + // update and cache the number of frames needed at the input sampling rate to produce + // the number of frames requested at the output sampling rate + if (framesRq != rsmp->frames_rq) { + rsmp->frames_needed = (framesRq * rsmp->out_sample_rate) / rsmp->in_sample_rate + 1; + rsmp->frames_rq = framesRq; + } + + size_t framesWr = 0; + size_t inFrames = 0; + while (framesWr < framesRq) { + if (rsmp->frames_in < rsmp->frames_needed) { + // make sure that the number of frames present in rsmp->in_buf (rsmp->frames_in) is at + // least the number of frames needed to produce the number of frames requested at + // the output sampling rate + if (rsmp->in_buf_size < rsmp->frames_needed) { + rsmp->in_buf_size = rsmp->frames_needed; + rsmp->in_buf = (int16_t *)realloc(rsmp->in_buf, + rsmp->in_buf_size * rsmp->channel_count * sizeof(int16_t)); + } + struct resampler_buffer buf; + buf.frame_count = rsmp->frames_needed - rsmp->frames_in; + rsmp->provider->get_next_buffer(rsmp->provider, &buf); + if (buf.raw == NULL) { + break; + } + memcpy(rsmp->in_buf + rsmp->frames_in * rsmp->channel_count, + buf.raw, + buf.frame_count * rsmp->channel_count * sizeof(int16_t)); + rsmp->frames_in += buf.frame_count; + rsmp->provider->release_buffer(rsmp->provider, &buf); + } + + size_t outFrames = framesRq - framesWr; + inFrames = rsmp->frames_in; + if (rsmp->channel_count == 1) { + speex_resampler_process_int(rsmp->speex_resampler, + 0, + rsmp->in_buf, + &inFrames, + out + framesWr, + &outFrames); + } else { + speex_resampler_process_interleaved_int(rsmp->speex_resampler, + rsmp->in_buf, + &inFrames, + out + framesWr * rsmp->channel_count, + &outFrames); + } + framesWr += outFrames; + rsmp->frames_in -= inFrames; + LOGW_IF((framesWr != framesRq) && (rsmp->frames_in != 0), + "ReSampler::resample() remaining %d frames in and %d frames out", + rsmp->frames_in, (framesRq - framesWr)); + } + if (rsmp->frames_in) { + memmove(rsmp->in_buf, + rsmp->in_buf + inFrames * rsmp->channel_count, + rsmp->frames_in * rsmp->channel_count * sizeof(int16_t)); + } + *outFrameCount = framesWr; + + return 0; +} + +int resampler_resample_from_input(struct resampler_itfe *resampler, + int16_t *in, + size_t *inFrameCount, + int16_t *out, + size_t *outFrameCount) +{ + struct resampler *rsmp = (struct resampler *)resampler; + + if (rsmp == NULL || in == NULL || inFrameCount == NULL || + out == NULL || outFrameCount == NULL) { + return -EINVAL; + } + if (rsmp->provider != NULL) { + *outFrameCount = 0; + return -ENOSYS; + } + + if (rsmp->channel_count == 1) { + speex_resampler_process_int(rsmp->speex_resampler, + 0, + in, + inFrameCount, + out, + outFrameCount); + } else { + speex_resampler_process_interleaved_int(rsmp->speex_resampler, + in, + inFrameCount, + out, + outFrameCount); + } + + LOGV("resampler_resample_from_input() DONE in %d out % d", *inFrameCount, *outFrameCount); + + return 0; +} + +int create_resampler(uint32_t inSampleRate, + uint32_t outSampleRate, + uint32_t channelCount, + uint32_t quality, + struct resampler_buffer_provider* provider, + struct resampler_itfe **resampler) +{ + int error; + struct resampler *rsmp; + + LOGV("create_resampler() In SR %d Out SR %d channels %d", + inSampleRate, outSampleRate, channelCount); + + if (resampler == NULL) { + return -EINVAL; + } + + *resampler = NULL; + + if (quality <= RESAMPLER_QUALITY_MIN || quality >= RESAMPLER_QUALITY_MAX) { + return -EINVAL; + } + + rsmp = (struct resampler *)calloc(1, sizeof(struct resampler)); + + rsmp->speex_resampler = speex_resampler_init(channelCount, + inSampleRate, + outSampleRate, + quality, + &error); + if (rsmp->speex_resampler == NULL) { + LOGW("ReSampler: Cannot create speex resampler: %s", speex_resampler_strerror(error)); + return -ENODEV; + } + + rsmp->itfe.reset = resampler_reset; + rsmp->itfe.resample_from_provider = resampler_resample_from_provider; + rsmp->itfe.resample_from_input = resampler_resample_from_input; + rsmp->itfe.delay_ns = resampler_delay_ns; + + rsmp->provider = provider; + rsmp->in_sample_rate = inSampleRate; + rsmp->out_sample_rate = outSampleRate; + rsmp->channel_count = channelCount; + rsmp->in_buf = NULL; + rsmp->in_buf_size = 0; + + resampler_reset(&rsmp->itfe); + + int frames = speex_resampler_get_input_latency(rsmp->speex_resampler); + rsmp->speex_delay_ns = (int32_t)((1000000000 * (int64_t)frames) / rsmp->in_sample_rate); + frames = speex_resampler_get_output_latency(rsmp->speex_resampler); + rsmp->speex_delay_ns += (int32_t)((1000000000 * (int64_t)frames) / rsmp->out_sample_rate); + + *resampler = &rsmp->itfe; + LOGV("create_resampler() DONE rsmp %p &rsmp->itfe %p speex %p", + rsmp, &rsmp->itfe, rsmp->speex_resampler); + return 0; +} + +void release_resampler(struct resampler_itfe *resampler) +{ + struct resampler *rsmp = (struct resampler *)resampler; + + if (rsmp == NULL) { + return; + } + + free(rsmp->in_buf); + + if (rsmp->speex_resampler != NULL) { + speex_resampler_destroy(rsmp->speex_resampler); + } + free(rsmp); +} -- 2.11.0