/* * Copyright (C) 2017 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_TAG "RecordBufferConverter" //#define LOG_NDEBUG 0 #include <audio_utils/primitives.h> #include <audio_utils/format.h> #include <media/AudioMixer.h> // for UNITY_GAIN_FLOAT #include <media/AudioResampler.h> #include <media/BufferProviders.h> #include <media/RecordBufferConverter.h> #include <utils/Log.h> #ifndef ARRAY_SIZE #define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) #endif template <typename T> static inline T max(const T& a, const T& b) { return a > b ? a : b; } namespace android { RecordBufferConverter::RecordBufferConverter( audio_channel_mask_t srcChannelMask, audio_format_t srcFormat, uint32_t srcSampleRate, audio_channel_mask_t dstChannelMask, audio_format_t dstFormat, uint32_t dstSampleRate) : mSrcChannelMask(AUDIO_CHANNEL_INVALID), // updateParameters will set following vars // mSrcFormat // mSrcSampleRate // mDstChannelMask // mDstFormat // mDstSampleRate // mSrcChannelCount // mDstChannelCount // mDstFrameSize mBuf(NULL), mBufFrames(0), mBufFrameSize(0), mResampler(NULL), mIsLegacyDownmix(false), mIsLegacyUpmix(false), mRequiresFloat(false), mInputConverterProvider(NULL) { (void)updateParameters(srcChannelMask, srcFormat, srcSampleRate, dstChannelMask, dstFormat, dstSampleRate); } RecordBufferConverter::~RecordBufferConverter() { free(mBuf); delete mResampler; delete mInputConverterProvider; } void RecordBufferConverter::reset() { if (mResampler != NULL) { mResampler->reset(); } } size_t RecordBufferConverter::convert(void *dst, AudioBufferProvider *provider, size_t frames) { if (mInputConverterProvider != NULL) { mInputConverterProvider->setBufferProvider(provider); provider = mInputConverterProvider; } if (mResampler == NULL) { ALOGV("NO RESAMPLING sampleRate:%u mSrcFormat:%#x mDstFormat:%#x", mSrcSampleRate, mSrcFormat, mDstFormat); AudioBufferProvider::Buffer buffer; for (size_t i = frames; i > 0; ) { buffer.frameCount = i; status_t status = provider->getNextBuffer(&buffer); if (status != OK || buffer.frameCount == 0) { frames -= i; // cannot fill request. break; } // format convert to destination buffer convertNoResampler(dst, buffer.raw, buffer.frameCount); dst = (int8_t*)dst + buffer.frameCount * mDstFrameSize; i -= buffer.frameCount; provider->releaseBuffer(&buffer); } } else { ALOGV("RESAMPLING mSrcSampleRate:%u mDstSampleRate:%u mSrcFormat:%#x mDstFormat:%#x", mSrcSampleRate, mDstSampleRate, mSrcFormat, mDstFormat); // reallocate buffer if needed if (mBufFrameSize != 0 && mBufFrames < frames) { free(mBuf); mBufFrames = frames; (void)posix_memalign(&mBuf, 32, mBufFrames * mBufFrameSize); } // resampler accumulates, but we only have one source track memset(mBuf, 0, frames * mBufFrameSize); frames = mResampler->resample((int32_t*)mBuf, frames, provider); // format convert to destination buffer convertResampler(dst, mBuf, frames); } return frames; } status_t RecordBufferConverter::updateParameters( audio_channel_mask_t srcChannelMask, audio_format_t srcFormat, uint32_t srcSampleRate, audio_channel_mask_t dstChannelMask, audio_format_t dstFormat, uint32_t dstSampleRate) { // quick evaluation if there is any change. if (mSrcFormat == srcFormat && mSrcChannelMask == srcChannelMask && mSrcSampleRate == srcSampleRate && mDstFormat == dstFormat && mDstChannelMask == dstChannelMask && mDstSampleRate == dstSampleRate) { return NO_ERROR; } ALOGV("RecordBufferConverter updateParameters srcMask:%#x dstMask:%#x" " srcFormat:%#x dstFormat:%#x srcRate:%u dstRate:%u", srcChannelMask, dstChannelMask, srcFormat, dstFormat, srcSampleRate, dstSampleRate); const bool valid = audio_is_input_channel(srcChannelMask) && audio_is_input_channel(dstChannelMask) && audio_is_valid_format(srcFormat) && audio_is_linear_pcm(srcFormat) && audio_is_valid_format(dstFormat) && audio_is_linear_pcm(dstFormat) && (srcSampleRate <= dstSampleRate * AUDIO_RESAMPLER_DOWN_RATIO_MAX) ; // no upsampling checks for now if (!valid) { return BAD_VALUE; } mSrcFormat = srcFormat; mSrcChannelMask = srcChannelMask; mSrcSampleRate = srcSampleRate; mDstFormat = dstFormat; mDstChannelMask = dstChannelMask; mDstSampleRate = dstSampleRate; // compute derived parameters mSrcChannelCount = audio_channel_count_from_in_mask(srcChannelMask); mDstChannelCount = audio_channel_count_from_in_mask(dstChannelMask); mDstFrameSize = mDstChannelCount * audio_bytes_per_sample(mDstFormat); // do we need to resample? delete mResampler; mResampler = NULL; if (mSrcSampleRate != mDstSampleRate) { mResampler = AudioResampler::create(AUDIO_FORMAT_PCM_FLOAT, mSrcChannelCount, mDstSampleRate); mResampler->setSampleRate(mSrcSampleRate); mResampler->setVolume(AudioMixer::UNITY_GAIN_FLOAT, AudioMixer::UNITY_GAIN_FLOAT); } // are we running legacy channel conversion modes? mIsLegacyDownmix = (mSrcChannelMask == AUDIO_CHANNEL_IN_STEREO || mSrcChannelMask == AUDIO_CHANNEL_IN_FRONT_BACK) && mDstChannelMask == AUDIO_CHANNEL_IN_MONO; mIsLegacyUpmix = mSrcChannelMask == AUDIO_CHANNEL_IN_MONO && (mDstChannelMask == AUDIO_CHANNEL_IN_STEREO || mDstChannelMask == AUDIO_CHANNEL_IN_FRONT_BACK); // do we need to process in float? mRequiresFloat = mResampler != NULL || mIsLegacyDownmix || mIsLegacyUpmix; // do we need a staging buffer to convert for destination (we can still optimize this)? // we use mBufFrameSize > 0 to indicate both frame size as well as buffer necessity if (mResampler != NULL) { mBufFrameSize = max(mSrcChannelCount, (uint32_t)FCC_2) * audio_bytes_per_sample(AUDIO_FORMAT_PCM_FLOAT); } else if (mIsLegacyUpmix || mIsLegacyDownmix) { // legacy modes always float mBufFrameSize = mDstChannelCount * audio_bytes_per_sample(AUDIO_FORMAT_PCM_FLOAT); } else if (mSrcChannelMask != mDstChannelMask && mDstFormat != mSrcFormat) { mBufFrameSize = mDstChannelCount * audio_bytes_per_sample(mSrcFormat); } else { mBufFrameSize = 0; } mBufFrames = 0; // force the buffer to be resized. // do we need an input converter buffer provider to give us float? delete mInputConverterProvider; mInputConverterProvider = NULL; if (mRequiresFloat && mSrcFormat != AUDIO_FORMAT_PCM_FLOAT) { mInputConverterProvider = new ReformatBufferProvider( audio_channel_count_from_in_mask(mSrcChannelMask), mSrcFormat, AUDIO_FORMAT_PCM_FLOAT, 256 /* provider buffer frame count */); } // do we need a remixer to do channel mask conversion if (!mIsLegacyDownmix && !mIsLegacyUpmix && mSrcChannelMask != mDstChannelMask) { (void) memcpy_by_index_array_initialization_from_channel_mask( mIdxAry, ARRAY_SIZE(mIdxAry), mDstChannelMask, mSrcChannelMask); } return NO_ERROR; } void RecordBufferConverter::convertNoResampler( void *dst, const void *src, size_t frames) { // src is native type unless there is legacy upmix or downmix, whereupon it is float. if (mBufFrameSize != 0 && mBufFrames < frames) { free(mBuf); mBufFrames = frames; (void)posix_memalign(&mBuf, 32, mBufFrames * mBufFrameSize); } // do we need to do legacy upmix and downmix? if (mIsLegacyUpmix || mIsLegacyDownmix) { void *dstBuf = mBuf != NULL ? mBuf : dst; if (mIsLegacyUpmix) { upmix_to_stereo_float_from_mono_float((float *)dstBuf, (const float *)src, frames); } else /*mIsLegacyDownmix */ { downmix_to_mono_float_from_stereo_float((float *)dstBuf, (const float *)src, frames); } if (mBuf != NULL) { memcpy_by_audio_format(dst, mDstFormat, mBuf, AUDIO_FORMAT_PCM_FLOAT, frames * mDstChannelCount); } return; } // do we need to do channel mask conversion? if (mSrcChannelMask != mDstChannelMask) { void *dstBuf = mBuf != NULL ? mBuf : dst; memcpy_by_index_array(dstBuf, mDstChannelCount, src, mSrcChannelCount, mIdxAry, audio_bytes_per_sample(mSrcFormat), frames); if (dstBuf == dst) { return; // format is the same } } // convert to destination buffer const void *convertBuf = mBuf != NULL ? mBuf : src; memcpy_by_audio_format(dst, mDstFormat, convertBuf, mSrcFormat, frames * mDstChannelCount); } void RecordBufferConverter::convertResampler( void *dst, /*not-a-const*/ void *src, size_t frames) { // src buffer format is ALWAYS float when entering this routine if (mIsLegacyUpmix) { ; // mono to stereo already handled by resampler } else if (mIsLegacyDownmix || (mSrcChannelMask == mDstChannelMask && mSrcChannelCount == 1)) { // the resampler outputs stereo for mono input channel (a feature?) // must convert to mono downmix_to_mono_float_from_stereo_float((float *)src, (const float *)src, frames); } else if (mSrcChannelMask != mDstChannelMask) { // convert to mono channel again for channel mask conversion (could be skipped // with further optimization). if (mSrcChannelCount == 1) { downmix_to_mono_float_from_stereo_float((float *)src, (const float *)src, frames); } // convert to destination format (in place, OK as float is larger than other types) if (mDstFormat != AUDIO_FORMAT_PCM_FLOAT) { memcpy_by_audio_format(src, mDstFormat, src, AUDIO_FORMAT_PCM_FLOAT, frames * mSrcChannelCount); } // channel convert and save to dst memcpy_by_index_array(dst, mDstChannelCount, src, mSrcChannelCount, mIdxAry, audio_bytes_per_sample(mDstFormat), frames); return; } // convert to destination format and save to dst memcpy_by_audio_format(dst, mDstFormat, src, AUDIO_FORMAT_PCM_FLOAT, frames * mDstChannelCount); } // ---------------------------------------------------------------------------- } // namespace android