diff options
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 |