diff options
Diffstat (limited to 'libaudio/ReSampler.cpp')
-rw-r--r-- | libaudio/ReSampler.cpp | 171 |
1 files changed, 171 insertions, 0 deletions
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 |