summaryrefslogtreecommitdiffstats
path: root/libaudio
diff options
context:
space:
mode:
Diffstat (limited to 'libaudio')
-rw-r--r--libaudio/Android.mk26
-rw-r--r--libaudio/AudioHardware.cpp514
-rw-r--r--libaudio/AudioHardware.h80
-rw-r--r--libaudio/EchoReference.cpp300
-rw-r--r--libaudio/EchoReference.h99
-rw-r--r--libaudio/README7
-rw-r--r--libaudio/ReSampler.cpp171
-rw-r--r--libaudio/ReSampler.h80
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,
+ &param->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