summaryrefslogtreecommitdiffstats
path: root/libaudio/AudioHardware.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libaudio/AudioHardware.cpp')
-rw-r--r--libaudio/AudioHardware.cpp514
1 files changed, 346 insertions, 168 deletions
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
//------------------------------------------------------------------------------