diff options
author | Eric Laurent <elaurent@google.com> | 2011-06-18 09:47:37 -0700 |
---|---|---|
committer | Eric Laurent <elaurent@google.com> | 2011-07-25 14:37:35 -0700 |
commit | 40a09525b7c484764c64daa1ca663a314a1b6d31 (patch) | |
tree | bd3c889b82ff41979f512967cdcd106e50489c7e /libaudio | |
parent | c593b8893fef34a1ff38d617a4e229447a9466cf (diff) | |
download | device_samsung_crespo-40a09525b7c484764c64daa1ca663a314a1b6d31.zip device_samsung_crespo-40a09525b7c484764c64daa1ca663a314a1b6d31.tar.gz device_samsung_crespo-40a09525b7c484764c64daa1ca663a314a1b6d31.tar.bz2 |
Audio HAL: added echo reference
Added EchoReference class enabling the input stream to access the
audio data written to the output stream and push it as echo reference
to the AEC.
Also added methods to calculate the echo delay.
Moved ReSampler class to a separate source file.
Change-Id: I9c3388f39101d567240545eab271eb61c97e7b56
Diffstat (limited to 'libaudio')
-rw-r--r-- | libaudio/Android.mk | 26 | ||||
-rw-r--r-- | libaudio/AudioHardware.cpp | 514 | ||||
-rw-r--r-- | libaudio/AudioHardware.h | 80 | ||||
-rw-r--r-- | libaudio/EchoReference.cpp | 300 | ||||
-rw-r--r-- | libaudio/EchoReference.h | 99 | ||||
-rw-r--r-- | libaudio/README | 7 | ||||
-rw-r--r-- | libaudio/ReSampler.cpp | 171 | ||||
-rw-r--r-- | libaudio/ReSampler.h | 80 |
8 files changed, 1055 insertions, 222 deletions
diff --git a/libaudio/Android.mk b/libaudio/Android.mk index b1494ed..3a3f615 100644 --- a/libaudio/Android.mk +++ b/libaudio/Android.mk @@ -1,10 +1,28 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) -LOCAL_SRC_FILES:= AudioHardware.cpp + +LOCAL_MODULE := libaudioutils +LOCAL_MODULE_TAGS := optional + +LOCAL_SRC_FILES:= \ + ReSampler.cpp \ + EchoReference.cpp + +LOCAL_C_INCLUDES += $(call include-path-for, speex) + +LOCAL_SHARED_LIBRARIES := libcutils + + +include $(BUILD_STATIC_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES:= \ + AudioHardware.cpp + LOCAL_MODULE := audio.primary.herring LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw -LOCAL_STATIC_LIBRARIES:= libmedia_helper +LOCAL_STATIC_LIBRARIES:= libmedia_helper libaudioutils LOCAL_SHARED_LIBRARIES:= \ libutils \ libhardware_legacy \ @@ -16,7 +34,9 @@ LOCAL_MODULE_TAGS := optional LOCAL_SHARED_LIBRARIES += libdl LOCAL_C_INCLUDES += $(call include-path-for, speex) -LOCAL_C_INCLUDES += external/tinyalsa/include +LOCAL_C_INCLUDES += \ + external/tinyalsa/include \ + system/media/audio_effects/include include $(BUILD_SHARED_LIBRARY) diff --git a/libaudio/AudioHardware.cpp b/libaudio/AudioHardware.cpp index 4430eae..e65f454 100644 --- a/libaudio/AudioHardware.cpp +++ b/libaudio/AudioHardware.cpp @@ -17,7 +17,6 @@ #include <math.h> //#define LOG_NDEBUG 0 - #define LOG_TAG "AudioHardware" #include <utils/Log.h> @@ -35,6 +34,7 @@ #include "AudioHardware.h" #include <media/AudioRecord.h> #include <hardware_legacy/power.h> +#include <audio_effects/effect_aec.h> extern "C" { #include <tinyalsa/asoundlib.h> @@ -97,6 +97,7 @@ AudioHardware::AudioHardware() : mSecRilLibHandle(NULL), mRilClient(0), mActivatedCP(false), + mEchoReference(NULL), mDriverOp(DRV_NONE) { loadRILD(); @@ -996,6 +997,37 @@ status_t AudioHardware::setInputSource_l(audio_source source) return NO_ERROR; } +EchoReference *AudioHardware::getEchoReference(audio_format_t format, + uint32_t channelCount, + uint32_t samplingRate) +{ + LOGV("AudioHardware::getEchoReference %p", mEchoReference); + if (mEchoReference == NULL && mOutput != NULL) { + uint32_t wrChannelCount = popcount(mOutput->channels()); + uint32_t wrSampleRate = mOutput->sampleRate(); + mEchoReference = new EchoReference(AUDIO_FORMAT_PCM_16_BIT, + channelCount, + samplingRate, + AUDIO_FORMAT_PCM_16_BIT, + wrChannelCount, + wrSampleRate); + mOutput->addEchoReference(mEchoReference); + return mEchoReference; + } + return NULL; + +} + +void AudioHardware::releaseEchoReference(EchoReference *reference) +{ + LOGV("AudioHardware::releaseEchoReference %p", mEchoReference); + if (reference == mEchoReference && mOutput != NULL) { + mOutput->removeEchoReference(reference); + delete mEchoReference; + mEchoReference = NULL; + } +} + //------------------------------------------------------------------------------ // AudioStreamOutALSA @@ -1005,7 +1037,7 @@ AudioHardware::AudioStreamOutALSA::AudioStreamOutALSA() : mHardware(0), mPcm(0), mMixer(0), mRouteCtl(0), mStandby(true), mDevices(0), mChannels(AUDIO_HW_OUT_CHANNELS), mSampleRate(AUDIO_HW_OUT_SAMPLERATE), mBufferSize(AUDIO_HW_OUT_PERIOD_BYTES), - mDriverOp(DRV_NONE), mStandbyCnt(0), mSleepReq(false) + mDriverOp(DRV_NONE), mStandbyCnt(0), mSleepReq(false), mEchoReference(NULL) { } @@ -1051,6 +1083,34 @@ AudioHardware::AudioStreamOutALSA::~AudioStreamOutALSA() standby(); } +int AudioHardware::AudioStreamOutALSA::computeEchoReferenceDelay(size_t frames, + struct timespec *echoRefRenderTime) +{ + size_t kernelFr; + + int rc = pcm_get_htimestamp(mPcm, &kernelFr, echoRefRenderTime); + if (rc < 0) { + LOGV("computeEchoReferenceDelay(): pcm_get_htimestamp error"); + echoRefRenderTime->tv_sec = 0; + echoRefRenderTime->tv_nsec = 0; + return rc; + } + + kernelFr = pcm_get_buffer_size(mPcm) - kernelFr; + + // adjust render time stamp with delay added by current driver buffer. + // Add the duration of current frame as we want the render time of the last + // sample being written. + long delayNs = (long)(((int64_t)(kernelFr + frames)* 1000000000) / + AUDIO_HW_OUT_SAMPLERATE); + delayNs += echoRefRenderTime->tv_nsec; + + echoRefRenderTime->tv_nsec = delayNs % 1000000000; + echoRefRenderTime->tv_sec += delayNs / 1000000000; + + return 0; +} + ssize_t AudioHardware::AudioStreamOutALSA::write(const void* buffer, size_t bytes) { // LOGV("AudioStreamOutALSA::write(%p, %u)", buffer, bytes); @@ -1113,6 +1173,14 @@ ssize_t AudioHardware::AudioStreamOutALSA::write(const void* buffer, size_t byte mStandby = false; } + if (mEchoReference != NULL) { + EchoReference::Buffer b; + b.raw = (void *)buffer; + b.frameCount = bytes / frameSize(); + computeEchoReferenceDelay(bytes / frameSize(), &b.tstamp); + mEchoReference->write(&b); + } + TRACE_DRIVER_IN(DRV_PCM_WRITE) ret = pcm_write(mPcm,(void*) p, bytes); TRACE_DRIVER_OUT @@ -1159,6 +1227,10 @@ void AudioHardware::AudioStreamOutALSA::doStandby_l() if (!mStandby) { LOGD("AudioHardware pcm playback is going to standby."); release_wake_lock("AudioOutLock"); + // stop echo reference capture + if (mEchoReference != NULL) { + mEchoReference->write(NULL); + } mStandby = true; } @@ -1328,6 +1400,24 @@ void AudioHardware::AudioStreamOutALSA::unlock() { mLock.unlock(); } +void AudioHardware::AudioStreamOutALSA::addEchoReference(EchoReference *reference) +{ + LOGV("AudioStreamOutALSA::addEchoReference %p", mEchoReference); + if (mEchoReference == NULL) { + mEchoReference = reference; + } +} + +void AudioHardware::AudioStreamOutALSA::removeEchoReference(EchoReference *reference) +{ + LOGV("AudioStreamOutALSA::removeEchoReference %p", mEchoReference); + if (mEchoReference == reference) { + mEchoReference->write(NULL); + mEchoReference = NULL; + } +} + + //------------------------------------------------------------------------------ // AudioStreamInALSA //------------------------------------------------------------------------------ @@ -1338,7 +1428,8 @@ AudioHardware::AudioStreamInALSA::AudioStreamInALSA() : mSampleRate(AUDIO_HW_IN_SAMPLERATE), mBufferSize(AUDIO_HW_IN_PERIOD_BYTES), mDownSampler(NULL), mReadStatus(NO_ERROR), mInputBuf(NULL), mDriverOp(DRV_NONE), mStandbyCnt(0), mSleepReq(false), - mProcBuf(NULL), mProcBufSize(0) + mProcBuf(NULL), mProcBufSize(0), mRefBuf(NULL), mRefBufSize(0), + mEchoReference(NULL), mNeedEchoReference(false) { } @@ -1375,7 +1466,7 @@ status_t AudioHardware::AudioStreamInALSA::set( mChannelCount = AudioSystem::popCount(mChannels); mSampleRate = rate; if (mSampleRate != AUDIO_HW_OUT_SAMPLERATE) { - mDownSampler = new AudioHardware::ReSampler(AUDIO_HW_OUT_SAMPLERATE, + mDownSampler = new ReSampler(AUDIO_HW_OUT_SAMPLERATE, mSampleRate, mChannelCount, this); @@ -1387,6 +1478,7 @@ status_t AudioHardware::AudioStreamInALSA::set( } } mInputBuf = new int16_t[AUDIO_HW_IN_PERIOD_SZ * mChannelCount]; + return NO_ERROR; } @@ -1410,7 +1502,7 @@ ssize_t AudioHardware::AudioStreamInALSA::readFrames(void* buffer, ssize_t frame (int16_t *)((char *)buffer + framesWr * frameSize()), &framesRd); } else { - AudioHardware::BufferProvider::Buffer buf; + ReSampler::BufferProvider::Buffer buf; buf.frameCount = framesRd; getNextBuffer(&buf); if (buf.raw != NULL) { @@ -1443,7 +1535,8 @@ ssize_t AudioHardware::AudioStreamInALSA::processFrames(void* buffer, ssize_t fr mProcBufSize = (size_t)frames; mProcBuf = (int16_t *)realloc(mProcBuf, mProcBufSize * mChannelCount * sizeof(int16_t)); - LOGV("read: mProcBuf size extended to %d frames", mProcBufSize); + LOGV("processFrames(): mProcBuf %p size extended to %d frames", + mProcBuf, mProcBufSize); } ssize_t framesRd = readFrames(mProcBuf + mProcFramesIn * mChannelCount, frames - mProcFramesIn); @@ -1454,6 +1547,10 @@ ssize_t AudioHardware::AudioStreamInALSA::processFrames(void* buffer, ssize_t fr mProcFramesIn += framesRd; } + if (mEchoReference != NULL) { + pushEchoReference(mProcFramesIn); + } + // inBuf.frameCount and outBuf.frameCount indicate respectively the maximum number of frames // to be consumed and produced by process() audio_buffer_t inBuf = { @@ -1474,10 +1571,12 @@ ssize_t AudioHardware::AudioStreamInALSA::processFrames(void* buffer, ssize_t fr // process() has updated the number of frames consumed and produced in // inBuf.frameCount and outBuf.frameCount respectively // move remaining frames to the beginning of mProcBuf - memcpy(mProcBuf, - mProcBuf + inBuf.frameCount * mChannelCount, - (mProcFramesIn - inBuf.frameCount) * mChannelCount * sizeof(int16_t)); mProcFramesIn -= inBuf.frameCount; + if (mProcFramesIn) { + memcpy(mProcBuf, + mProcBuf + inBuf.frameCount * mChannelCount, + mProcFramesIn * mChannelCount * sizeof(int16_t)); + } // if not enough frames were passed to process(), read more and retry. if (outBuf.frameCount == 0) { @@ -1488,6 +1587,167 @@ ssize_t AudioHardware::AudioStreamInALSA::processFrames(void* buffer, ssize_t fr return framesWr; } +void AudioHardware::AudioStreamInALSA::updateEchoReference(size_t frames) +{ + if (mRefFramesIn < frames) { + if (mRefBufSize < frames) { + mRefBufSize = frames; + mRefBuf = (int16_t *)realloc(mRefBuf, + mRefBufSize * mChannelCount * sizeof(int16_t)); + } + + EchoReference::Buffer b; + b.frameCount = frames - mRefFramesIn; + b.raw = (void *)(mRefBuf + mRefFramesIn * mChannelCount); + if (mEchoReference->read(&b) == NO_ERROR) { + mRefFramesIn += b.frameCount; + // Echo delay calculation: updates mEchoDelayUs + updateEchoDelay(frames, &b.tstamp); + } else { + mEchoDelayUs = 0; + } + } +} + +void AudioHardware::AudioStreamInALSA::pushEchoReference(size_t frames) +{ + // read frames from echo reference buffer and update echo delay + // mRefFramesIn is updated with frames available in mRefBuf + updateEchoReference(frames); + + if (mRefFramesIn < frames) { + frames = mRefFramesIn; + } + + audio_buffer_t refBuf = { + frames, + {mRefBuf} + }; + + for (size_t i = 0; i < mPreprocessors.size(); i++) { + if ((*mPreprocessors[i])->process_reverse == NULL) { + continue; + } + (*mPreprocessors[i])->process_reverse(mPreprocessors[i], + &refBuf, + NULL); + setPreProcessorEchoDelay(mPreprocessors[i], mEchoDelayUs); + } + + mRefFramesIn -= refBuf.frameCount; + if (mRefFramesIn) { + memcpy(mRefBuf, + mRefBuf + refBuf.frameCount * mChannelCount, + mRefFramesIn * mChannelCount * sizeof(int16_t)); + } +} + +status_t AudioHardware::AudioStreamInALSA::setPreProcessorEchoDelay(effect_handle_t handle, + int32_t delayUs) +{ + uint32_t buf[sizeof(effect_param_t) / sizeof(uint32_t) + 2]; + effect_param_t *param = (effect_param_t *)buf; + + param->psize = sizeof(uint32_t); + param->vsize = sizeof(uint32_t); + *(uint32_t *)param->data = AEC_PARAM_ECHO_DELAY; + *((int32_t *)param->data + 1) = delayUs; + + LOGV("setPreProcessorEchoDelay: %d us", delayUs); + + return setPreprocessorParam(handle, param); +} + +status_t AudioHardware::AudioStreamInALSA::setPreprocessorParam(effect_handle_t handle, + effect_param_t *param) +{ + uint32_t size = sizeof(int); + uint32_t psize = ((param->psize - 1) / sizeof(int) + 1) * sizeof(int) + param->vsize; + + status_t status = (*handle)->command(handle, + EFFECT_CMD_SET_PARAM, + sizeof (effect_param_t) + psize, + param, + &size, + ¶m->status); + if (status == NO_ERROR) { + status = param->status; + } + return status; +} + +void AudioHardware::AudioStreamInALSA::updateEchoDelay(size_t frames, + struct timespec *echoRefRenderTime) +{ + // read frames available in kernel driver buffer + size_t kernelFr; + struct timespec tstamp; + if (pcm_get_htimestamp(mPcm, &kernelFr, &tstamp) < 0) { + mEchoDelayUs = 0; + LOGW("read updateEchoDelay(): pcm_get_htimestamp error"); + return; + } + + if (echoRefRenderTime->tv_sec == 0 && echoRefRenderTime->tv_nsec == 0) { + mEchoDelayUs = 0; + LOGV("read updateEchoDelay(): echo ref render time is 0"); + return; + } + + long kernelDelay = (long)(((int64_t)kernelFr * 1000000000) / AUDIO_HW_IN_SAMPLERATE); + + // read frames available in audio HAL input buffer + // add number of frames being read as we want the capture time of first sample in current + // buffer + long bufDelay = (long)(((int64_t)(mInputFramesIn + mProcFramesIn + frames) * 1000000000) + / AUDIO_HW_IN_SAMPLERATE); + + // add delay introduced by resampler + long rsmpDelay = 0; + if (mDownSampler) { + rsmpDelay = mDownSampler->delayNs(); + } + + // correct capture time stamp + long delay = kernelDelay + bufDelay + rsmpDelay; + struct timespec tmp; + tmp.tv_sec = delay / 1000000000; + tmp.tv_nsec = delay % 1000000000; + + if (tstamp.tv_nsec < tmp.tv_nsec) + { + tmp.tv_sec = tstamp.tv_sec - tmp.tv_sec - 1; + tmp.tv_nsec = 1000000000 + tstamp.tv_nsec - tmp.tv_nsec; + } else { + tmp.tv_sec = tstamp.tv_sec - tmp.tv_sec; + tmp.tv_nsec = tstamp.tv_nsec - tmp.tv_nsec; + } + + // caculate echo delay = echo reference render time - capture time + if (echoRefRenderTime->tv_nsec < tmp.tv_nsec) + { + tmp.tv_sec = echoRefRenderTime->tv_sec - tmp.tv_sec - 1; + tmp.tv_nsec = 1000000000 + echoRefRenderTime->tv_nsec - tmp.tv_nsec; + } else { + tmp.tv_sec = echoRefRenderTime->tv_sec - tmp.tv_sec; + tmp.tv_nsec = echoRefRenderTime->tv_nsec - tmp.tv_nsec; + } + + mEchoDelayUs = (int32_t)(((int64_t)tmp.tv_sec * 1000000000 + tmp.tv_nsec) / 1000); + + if (mEchoDelayUs < 0) { + LOGW("negative echo delay !!! %d", mEchoDelayUs); + mEchoDelayUs = 0; + } + +// LOGV("updateEchoDelay() ref render TS %d.%d capture TS %d.%d delta TS %d.%d" +// " mEchoDelayUs %d kernelDelay %d bufDelay %d rsmpDelay %d", +// (int)echoRefRenderTime->tv_sec, (int)echoRefRenderTime->tv_nsec, +// (int)tstamp.tv_sec, (int)tstamp.tv_nsec, +// (int)tmp.tv_sec, (int)tmp.tv_nsec, +// mEchoDelayUs, (int)kernelDelay, (int)bufDelay, (int)rsmpDelay); +} + ssize_t AudioHardware::AudioStreamInALSA::read(void* buffer, ssize_t bytes) { // LOGV("AudioStreamInALSA::read(%p, %d)", buffer, (int)bytes); @@ -1512,34 +1772,36 @@ ssize_t AudioHardware::AudioStreamInALSA::read(void* buffer, ssize_t bytes) sp<AudioStreamOutALSA> spOut = mHardware->output(); while (spOut != 0) { - if (!spOut->checkStandby()) { - int cnt = spOut->prepareLock(); - mHardware->lock().unlock(); - mLock.unlock(); - // Mutex acquisition order is always out -> in -> hw - spOut->lock(); - mLock.lock(); - mHardware->lock().lock(); - // make sure that another thread did not change output state - // while the mutex is released - if ((spOut == mHardware->output()) && (cnt == spOut->standbyCnt())) { - LOGV("AudioStreamInALSA::read() force output standby"); - spOut->close_l(); - break; - } - spOut->unlock(); - spOut = mHardware->output(); - } else { - spOut.clear(); + spOut->prepareLock(); + mHardware->lock().unlock(); + mLock.unlock(); + // Mutex acquisition order is always out -> in -> hw + spOut->lock(); + mLock.lock(); + mHardware->lock().lock(); + // make sure that another thread did not change output state + // while the mutex is released + if (spOut == mHardware->output()) { + break; } + spOut->unlock(); + spOut = mHardware->output(); } - // spOut is not 0 here only if the output was active and has been - // closed above - // open output before input if (spOut != 0) { - if (spOut->open_l() != NO_ERROR) { - spOut->doStandby_l(); + if (!spOut->checkStandby()) { + LOGV("AudioStreamInALSA::read() force output standby"); + spOut->close_l(); + if (spOut->open_l() != NO_ERROR) { + spOut->doStandby_l(); + } + } + LOGV("AudioStreamInALSA exit standby mNeedEchoReference %d mEchoReference %p", + mNeedEchoReference, mEchoReference); + if (mNeedEchoReference && mEchoReference == NULL) { + mEchoReference = mHardware->getEchoReference(AUDIO_FORMAT_PCM_16_BIT, + mChannelCount, + mSampleRate); } spOut->unlock(); } @@ -1605,6 +1867,25 @@ void AudioHardware::AudioStreamInALSA::doStandby_l() if (!mStandby) { LOGD("AudioHardware pcm capture is going to standby."); release_wake_lock("AudioInLock"); + + if (mEchoReference != NULL) { + // stop reading from echo reference + mEchoReference->read(NULL); + // Mutex acquisition order is always out -> in -> hw + sp<AudioStreamOutALSA> spOut = mHardware->output(); + if (spOut != 0) { + spOut->prepareLock(); + mHardware->lock().unlock(); + mLock.unlock(); + spOut->lock(); + mLock.lock(); + mHardware->lock().lock(); + mHardware->releaseEchoReference(mEchoReference); + spOut->unlock(); + } + mEchoReference = NULL; + } + mStandby = true; } close_l(); @@ -1628,6 +1909,9 @@ void AudioHardware::AudioStreamInALSA::close_l() delete[] mProcBuf; mProcBuf = NULL; mProcBufSize = 0; + delete[] mRefBuf; + mRefBuf = NULL; + mRefBufSize = 0; } status_t AudioHardware::AudioStreamInALSA::open_l() @@ -1662,6 +1946,9 @@ status_t AudioHardware::AudioStreamInALSA::open_l() mProcBufSize = 0; mProcFramesIn = 0; + mRefBufSize = 0; + mRefFramesIn = 0; + mEchoDelayUs = 0; mMixer = mHardware->openMixer_l(); if (mMixer) { @@ -1792,6 +2079,19 @@ status_t AudioHardware::AudioStreamInALSA::addAudioEffect(effect_handle_t effect { LOGV("AudioStreamInALSA::addAudioEffect() %p", effect); + effect_descriptor_t desc; + status_t status = (*effect)->get_descriptor(effect, &desc); + if (status == 0) { + if (memcmp(&desc.type, FX_IID_AEC, sizeof(effect_uuid_t)) == 0) { + LOGV("AudioStreamInALSA::addAudioEffect() mNeedEchoReference true"); + mNeedEchoReference = true; + standby(); + } + LOGV("AudioStreamInALSA::addAudioEffect() name %s", desc.name); + } else { + LOGV("AudioStreamInALSA::addAudioEffect() get_descriptor() error"); + } + AutoMutex lock(mLock); mPreprocessors.add(effect); return NO_ERROR; @@ -1812,10 +2112,21 @@ status_t AudioHardware::AudioStreamInALSA::removeAudioEffect(effect_handle_t eff } } + if (status == NO_ERROR) { + effect_descriptor_t desc; + if ((*effect)->get_descriptor(effect, &desc) == 0) { + if (memcmp(&desc.type, FX_IID_AEC, sizeof(effect_uuid_t)) == 0) { + LOGV("AudioStreamInALSA::removeAudioEffect() mNeedEchoReference false"); + mNeedEchoReference = false; + standby(); + } + } + } + return status; } -status_t AudioHardware::AudioStreamInALSA::getNextBuffer(AudioHardware::BufferProvider::Buffer* buffer) +status_t AudioHardware::AudioStreamInALSA::getNextBuffer(ReSampler::BufferProvider::Buffer* buffer) { if (mPcm == NULL) { buffer->raw = NULL; @@ -1842,7 +2153,7 @@ status_t AudioHardware::AudioStreamInALSA::getNextBuffer(AudioHardware::BufferPr return mReadStatus; } -void AudioHardware::AudioStreamInALSA::releaseBuffer(AudioHardware::BufferProvider::Buffer* buffer) +void AudioHardware::AudioStreamInALSA::releaseBuffer(ReSampler::BufferProvider::Buffer* buffer) { mInputFramesIn -= buffer->frameCount; } @@ -1883,139 +2194,6 @@ void AudioHardware::AudioStreamInALSA::unlock() { } //------------------------------------------------------------------------------ -// speex based resampler -//------------------------------------------------------------------------------ - -#define RESAMPLER_QUALITY 2 - -AudioHardware::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("AudioHardware::ReSampler() cstor %p In SR %d Out SR %d channels %d", - this, mInSampleRate, mInSampleRate, 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(); - - mStatus = NO_ERROR; -} - -AudioHardware::ReSampler::~ReSampler() -{ - free(mInBuf); - - if (mSpeexResampler != NULL) { - speex_resampler_destroy(mSpeexResampler); - } -} - -void AudioHardware::ReSampler::reset() -{ - mFramesIn = 0; - mFramesRq = 0; - - if (mSpeexResampler != NULL) { - speex_resampler_reset_mem(mSpeexResampler); - } -} - -// outputs a number of frames less or equal to *outFrameCount and updates *outFrameCount -// with the actual number of frames produced. -int AudioHardware::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)); - } - AudioHardware::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; -} - - -//------------------------------------------------------------------------------ // Factory //------------------------------------------------------------------------------ diff --git a/libaudio/AudioHardware.h b/libaudio/AudioHardware.h index 5eec70e..8824ca5 100644 --- a/libaudio/AudioHardware.h +++ b/libaudio/AudioHardware.h @@ -29,7 +29,8 @@ #include "secril-client.h" -#include "speex/speex_resampler.h" +#include "ReSampler.h" +#include "EchoReference.h" extern "C" { struct pcm; @@ -78,6 +79,7 @@ class AudioHardware : public AudioHardwareBase { class AudioStreamOutALSA; class AudioStreamInALSA; + public: // input path names used to translate from input sources to driver paths @@ -139,6 +141,11 @@ public: sp <AudioStreamOutALSA> output() { return mOutput; } + EchoReference *getEchoReference(audio_format_t format, + uint32_t channelCount, + uint32_t samplingRate); + void releaseEchoReference(EchoReference *reference); + protected: virtual status_t dump(int fd, const Vector<String16>& args); @@ -180,6 +187,7 @@ private: int (*setCallClockSync)(HRilClient, SoundClockCondition); void loadRILD(void); status_t connectRILDIfRequired(void); + EchoReference* mEchoReference; // trace driver operations for dump int mDriverOp; @@ -240,8 +248,13 @@ private: void lock(); void unlock(); + void addEchoReference(EchoReference *reference); + void removeEchoReference(EchoReference *reference); + private: + int computeEchoReferenceDelay(size_t frames, struct timespec *echoRefRenderTime); + Mutex mLock; AudioHardware* mHardware; struct pcm *mPcm; @@ -257,56 +270,10 @@ private: int mDriverOp; int mStandbyCnt; bool mSleepReq; + EchoReference *mEchoReference; }; - 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; - }; - - class ReSampler { - public: - 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); - - private: - status_t mStatus; - SpeexResamplerState *mSpeexResampler; - BufferProvider* mProvider; - uint32_t mInSampleRate; - uint32_t mOutSampleRate; - uint32_t mChannelCount; - int16_t *mInBuf; - size_t mInBufSize; - size_t mFramesIn; - size_t mFramesRq; - size_t mFramesNeeded; - }; - - - class AudioStreamInALSA : public AudioStreamIn, public BufferProvider, public RefBase + class AudioStreamInALSA : public AudioStreamIn, public ReSampler::BufferProvider, public RefBase { public: @@ -342,8 +309,8 @@ private: static size_t getBufferSize(uint32_t sampleRate, int channelCount); // BufferProvider - virtual status_t getNextBuffer(BufferProvider::Buffer* buffer); - virtual void releaseBuffer(BufferProvider::Buffer* buffer); + virtual status_t getNextBuffer(ReSampler::BufferProvider::Buffer* buffer); + virtual void releaseBuffer(ReSampler::BufferProvider::Buffer* buffer); int prepareLock(); void lock(); @@ -353,6 +320,11 @@ private: ssize_t readFrames(void* buffer, ssize_t frames); ssize_t processFrames(void* buffer, ssize_t frames); + void updateEchoReference(size_t frames); + void pushEchoReference(size_t frames); + void updateEchoDelay(size_t frames, struct timespec *echoRefRenderTime); + status_t setPreProcessorEchoDelay(effect_handle_t handle, int32_t delayUs); + status_t setPreprocessorParam(effect_handle_t handle, effect_param_t *param); Mutex mLock; AudioHardware* mHardware; @@ -378,6 +350,12 @@ private: int16_t *mProcBuf; size_t mProcBufSize; size_t mProcFramesIn; + int16_t *mRefBuf; + size_t mRefBufSize; + size_t mRefFramesIn; + EchoReference *mEchoReference; + bool mNeedEchoReference; + int32_t mEchoDelayUs; }; }; diff --git a/libaudio/EchoReference.cpp b/libaudio/EchoReference.cpp new file mode 100644 index 0000000..cfd07de --- /dev/null +++ b/libaudio/EchoReference.cpp @@ -0,0 +1,300 @@ +/* +** 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(EchoReference::Buffer *buffer) +{ + if (mStatus != NO_ERROR) { + return mStatus; + } + + AutoMutex _l(mLock); + + if (buffer == NULL) { + LOGV("EchoReference::write() stop write"); + mState &= ~ECHOREF_WRITING; + reset_l(); + return NO_ERROR; + } + +// LOGV("EchoReference::write() %d frames", buffer->frameCount); + + 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; + } + + + // discard writes until a valid time stamp is provided. Once a valid TS has been received + // reuse last good TS if none is provided. + if (buffer->tstamp.tv_sec == 0 && buffer->tstamp.tv_nsec == 0 ) { + if (mWrRenderTime.tv_sec == 0 && mWrRenderTime.tv_nsec == 0) { + return NO_ERROR; + } + } else { + mWrRenderTime.tv_sec = buffer->tstamp.tv_sec; + mWrRenderTime.tv_nsec = buffer->tstamp.tv_nsec; + } + + 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 + 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) { + mBufSize = mFramesIn + inFrames; + mBuffer = realloc(mBuffer, mBufSize * mRdFrameSize); + LOGV("EchoReference::write() increasing buffer size to %d", mBufSize); + } + memcpy((char *)mBuffer + mFramesIn * mRdFrameSize, + srcBuf, + inFrames * mRdFrameSize); + mFramesIn += inFrames; + + LOGV("EchoReference::write() frames %d, total frames in %d", inFrames, mFramesIn); + + mCond.signal(); + 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; + } + + 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->tstamp.tv_sec = 0; + buffer->tstamp.tv_nsec = 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) { + buffer->frameCount = mFramesIn; + LOGV("EchoReference::read() waited %d ms but still not enough frames", timeoutMs); + } + } + + // computeRenderTime() must be called before subtracting frames read from mFramesIn because + // we subtract the duration of the whole echo reference buffer including the buffer being read. + // This is because the time stamp stored in mWrRenderTime corresponds to the last sample written + // to the echo reference buffer and we want to return the render time of the first sample of + // the buffer being read. + computeRenderTime(&buffer->tstamp); + + memcpy(buffer->raw, + (char *)mBuffer, + buffer->frameCount * mRdFrameSize); + + mFramesIn -= buffer->frameCount; + memcpy(mBuffer, + (char *)mBuffer + buffer->frameCount * mRdFrameSize, + mFramesIn * mRdFrameSize); + + LOGV("EchoReference::read() %d frames, total frames in %d", buffer->frameCount, mFramesIn); + + mCond.signal(); + return NO_ERROR; +} + +void EchoReference::computeRenderTime(struct timespec *renderTime) +{ + int64_t delayNs = ((int64_t)mFramesIn * 1000000000) / mRdSamplingRate; + + if (mDownSampler != NULL) { + delayNs += mDownSampler->delayNs(); + } + + struct timespec tmp; + tmp.tv_nsec = delayNs % 1000000000; + tmp.tv_sec = delayNs / 1000000000; + + if (mWrRenderTime.tv_nsec < tmp.tv_nsec) + { + renderTime->tv_sec = mWrRenderTime.tv_sec - tmp.tv_sec - 1; + renderTime->tv_nsec = 1000000000 + mWrRenderTime.tv_nsec - tmp.tv_nsec; + } else { + renderTime->tv_sec = mWrRenderTime.tv_sec - tmp.tv_sec; + renderTime->tv_nsec = mWrRenderTime.tv_nsec - tmp.tv_nsec; + } +} + +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/libaudio/EchoReference.h b/libaudio/EchoReference.h new file mode 100644 index 0000000..5d54fac --- /dev/null +++ b/libaudio/EchoReference.h @@ -0,0 +1,99 @@ +/* +** 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 <stdint.h> +#include <sys/types.h> +#include <utils/threads.h> +#include <hardware_legacy/AudioSystemLegacy.h> +#include "ReSampler.h" + +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 render time stamp. + // for write(), The time stamp is the render time for the last sample in the buffer written + // for read(), the time stamp is the render time for the first sample in the buffer read + class Buffer { + public: + void *raw; + size_t frameCount; + struct timespec tstamp; // render time stamp of buffer. 0.0 means if unknown + }; + + // 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(); + void computeRenderTime(struct timespec *renderTime); + + 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() + 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 +}; + +}; // namespace android + +#endif // ANDROID_ECHO_REFERENCE_H diff --git a/libaudio/README b/libaudio/README new file mode 100644 index 0000000..d446f90 --- /dev/null +++ b/libaudio/README @@ -0,0 +1,7 @@ +The code in this library is implemented according to legacy audio HAL scheme: + - use of AudioHardwareInterface C++ interface. + - use of C++ classes in frameworks/base. + +It should not be used as an example for newer audio HALs implementations. + + diff --git a/libaudio/ReSampler.cpp b/libaudio/ReSampler.cpp new file mode 100644 index 0000000..aa1d5e0 --- /dev/null +++ b/libaudio/ReSampler.cpp @@ -0,0 +1,171 @@ +/* +** 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 <utils/Log.h> +#include "ReSampler.h" + +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/libaudio/ReSampler.h b/libaudio/ReSampler.h new file mode 100644 index 0000000..f531900 --- /dev/null +++ b/libaudio/ReSampler.h @@ -0,0 +1,80 @@ +/* +** 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 <stdint.h> +#include <sys/types.h> +#include <hardware_legacy/AudioSystemLegacy.h> +#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 |