diff options
author | Andy Hung <hunga@google.com> | 2014-05-27 18:32:15 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2014-05-27 18:32:15 +0000 |
commit | 018d324dc599c5f4238a4cd631baac47c28d59fe (patch) | |
tree | 3c45f7d16bb23590c06eae6fa59852882c16f897 /services/audioflinger | |
parent | 1a83b736737e7f625371093519ff7a10b05e0c63 (diff) | |
parent | ef7c7fbd0e3fb36af14cd7d39f64c949031516a5 (diff) | |
download | frameworks_av-018d324dc599c5f4238a4cd631baac47c28d59fe.zip frameworks_av-018d324dc599c5f4238a4cd631baac47c28d59fe.tar.gz frameworks_av-018d324dc599c5f4238a4cd631baac47c28d59fe.tar.bz2 |
Merge "Add multiple format capability to AudioMixer"
Diffstat (limited to 'services/audioflinger')
-rw-r--r-- | services/audioflinger/AudioMixer.cpp | 193 | ||||
-rw-r--r-- | services/audioflinger/AudioMixer.h | 48 | ||||
-rw-r--r-- | services/audioflinger/FastMixer.cpp | 6 |
3 files changed, 217 insertions, 30 deletions
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp index 342364e..8d57451 100644 --- a/services/audioflinger/AudioMixer.cpp +++ b/services/audioflinger/AudioMixer.cpp @@ -34,6 +34,7 @@ #include <system/audio.h> #include <audio_utils/primitives.h> +#include <audio_utils/format.h> #include <common_time/local_clock.h> #include <common_time/cc_helper.h> @@ -88,6 +89,103 @@ void AudioMixer::DownmixerBufferProvider::releaseBuffer(AudioBufferProvider::Buf } } +template <typename T> +T min(const T& a, const T& b) +{ + return a < b ? a : b; +} + +AudioMixer::ReformatBufferProvider::ReformatBufferProvider(int32_t channels, + audio_format_t inputFormat, audio_format_t outputFormat) : + mTrackBufferProvider(NULL), + mChannels(channels), + mInputFormat(inputFormat), + mOutputFormat(outputFormat), + mInputFrameSize(channels * audio_bytes_per_sample(inputFormat)), + mOutputFrameSize(channels * audio_bytes_per_sample(outputFormat)), + mOutputData(NULL), + mOutputCount(0), + mConsumed(0) +{ + ALOGV("ReformatBufferProvider(%p)(%d, %#x, %#x)", this, channels, inputFormat, outputFormat); + if (requiresInternalBuffers()) { + mOutputCount = 256; + (void)posix_memalign(&mOutputData, 32, mOutputCount * mOutputFrameSize); + } + mBuffer.frameCount = 0; +} + +AudioMixer::ReformatBufferProvider::~ReformatBufferProvider() +{ + ALOGV("~ReformatBufferProvider(%p)", this); + if (mBuffer.frameCount != 0) { + mTrackBufferProvider->releaseBuffer(&mBuffer); + } + free(mOutputData); +} + +status_t AudioMixer::ReformatBufferProvider::getNextBuffer(AudioBufferProvider::Buffer *pBuffer, + int64_t pts) { + //ALOGV("ReformatBufferProvider(%p)::getNextBuffer(%p (%zu), %lld)", + // this, pBuffer, pBuffer->frameCount, pts); + if (!requiresInternalBuffers()) { + status_t res = mTrackBufferProvider->getNextBuffer(pBuffer, pts); + if (res == OK) { + memcpy_by_audio_format(pBuffer->raw, mOutputFormat, pBuffer->raw, mInputFormat, + pBuffer->frameCount * mChannels); + } + return res; + } + if (mBuffer.frameCount == 0) { + mBuffer.frameCount = pBuffer->frameCount; + status_t res = mTrackBufferProvider->getNextBuffer(&mBuffer, pts); + // TODO: Track down a bug in the upstream provider + // LOG_ALWAYS_FATAL_IF(res == OK && mBuffer.frameCount == 0, + // "ReformatBufferProvider::getNextBuffer():" + // " Invalid zero framecount returned from getNextBuffer()"); + if (res != OK || mBuffer.frameCount == 0) { + pBuffer->raw = NULL; + pBuffer->frameCount = 0; + return res; + } + } + ALOG_ASSERT(mConsumed < mBuffer.frameCount); + size_t count = min(mOutputCount, mBuffer.frameCount - mConsumed); + count = min(count, pBuffer->frameCount); + pBuffer->raw = mOutputData; + pBuffer->frameCount = count; + //ALOGV("reformatting %d frames from %#x to %#x, %d chan", + // pBuffer->frameCount, mInputFormat, mOutputFormat, mChannels); + memcpy_by_audio_format(pBuffer->raw, mOutputFormat, + (uint8_t*)mBuffer.raw + mConsumed * mInputFrameSize, mInputFormat, + pBuffer->frameCount * mChannels); + return OK; +} + +void AudioMixer::ReformatBufferProvider::releaseBuffer(AudioBufferProvider::Buffer *pBuffer) { + //ALOGV("ReformatBufferProvider(%p)::releaseBuffer(%p(%zu))", + // this, pBuffer, pBuffer->frameCount); + if (!requiresInternalBuffers()) { + mTrackBufferProvider->releaseBuffer(pBuffer); + return; + } + // LOG_ALWAYS_FATAL_IF(pBuffer->frameCount == 0, "Invalid framecount"); + mConsumed += pBuffer->frameCount; // TODO: update for efficiency to reuse existing content + if (mConsumed != 0 && mConsumed >= mBuffer.frameCount) { + mConsumed = 0; + mTrackBufferProvider->releaseBuffer(&mBuffer); + // ALOG_ASSERT(mBuffer.frameCount == 0); + } + pBuffer->raw = NULL; + pBuffer->frameCount = 0; +} + +void AudioMixer::ReformatBufferProvider::reset() { + if (mBuffer.frameCount != 0) { + mTrackBufferProvider->releaseBuffer(&mBuffer); + } + mConsumed = 0; +} // ---------------------------------------------------------------------------- bool AudioMixer::sIsMultichannelCapable = false; @@ -181,7 +279,8 @@ int AudioMixer::getTrackName(audio_channel_mask_t channelMask, // t->frameCount t->channelCount = audio_channel_count_from_out_mask(channelMask); t->enabled = false; - t->format = 16; + ALOGV_IF(channelMask != AUDIO_CHANNEL_OUT_STEREO, + "Non-stereo channel mask: %d\n", channelMask); t->channelMask = channelMask; t->sessionId = sessionId; // setBufferProvider(name, AudioBufferProvider *) is required before enable(name) @@ -196,9 +295,15 @@ int AudioMixer::getTrackName(audio_channel_mask_t channelMask, // setParameter(name, TRACK, MAIN_BUFFER, mixBuffer) is required before enable(name) t->mainBuffer = NULL; t->auxBuffer = NULL; + t->mInputBufferProvider = NULL; + t->mReformatBufferProvider = NULL; t->downmixerBufferProvider = NULL; t->mMixerFormat = AUDIO_FORMAT_PCM_16_BIT; t->mFormat = format; + t->mMixerInFormat = AUDIO_FORMAT_PCM_16_BIT; + if (t->mFormat != t->mMixerInFormat) { + prepareTrackForReformat(t, n); + } status_t status = initTrackDownmix(&mState.tracks[n], n, channelMask); if (status != OK) { ALOGE("AudioMixer::getTrackName invalid channelMask (%#x)", channelMask); @@ -242,9 +347,9 @@ void AudioMixer::unprepareTrackForDownmix(track_t* pTrack, int trackName __unuse if (pTrack->downmixerBufferProvider != NULL) { // this track had previously been configured with a downmixer, delete it ALOGV(" deleting old downmixer"); - pTrack->bufferProvider = pTrack->downmixerBufferProvider->mTrackBufferProvider; delete pTrack->downmixerBufferProvider; pTrack->downmixerBufferProvider = NULL; + reconfigureBufferProviders(pTrack); } else { ALOGV(" nothing to do, no downmixer to delete"); } @@ -338,21 +443,51 @@ status_t AudioMixer::prepareTrackForDownmix(track_t* pTrack, int trackName) }// end of scope for local variables that are not used in goto label "noDownmixForActiveTrack" // initialization successful: - // - keep track of the real buffer provider in case it was set before - pDbp->mTrackBufferProvider = pTrack->bufferProvider; - // - we'll use the downmix effect integrated inside this - // track's buffer provider, and we'll use it as the track's buffer provider pTrack->downmixerBufferProvider = pDbp; - pTrack->bufferProvider = pDbp; - + reconfigureBufferProviders(pTrack); return NO_ERROR; noDownmixForActiveTrack: delete pDbp; pTrack->downmixerBufferProvider = NULL; + reconfigureBufferProviders(pTrack); return NO_INIT; } +void AudioMixer::unprepareTrackForReformat(track_t* pTrack, int trackName __unused) { + ALOGV("AudioMixer::unprepareTrackForReformat(%d)", trackName); + if (pTrack->mReformatBufferProvider != NULL) { + delete pTrack->mReformatBufferProvider; + pTrack->mReformatBufferProvider = NULL; + reconfigureBufferProviders(pTrack); + } +} + +status_t AudioMixer::prepareTrackForReformat(track_t* pTrack, int trackName) +{ + ALOGV("AudioMixer::prepareTrackForReformat(%d) with format %#x", trackName, pTrack->mFormat); + // discard the previous reformatter if there was one + unprepareTrackForReformat(pTrack, trackName); + pTrack->mReformatBufferProvider = new ReformatBufferProvider( + audio_channel_count_from_out_mask(pTrack->channelMask), + pTrack->mFormat, pTrack->mMixerInFormat); + reconfigureBufferProviders(pTrack); + return NO_ERROR; +} + +void AudioMixer::reconfigureBufferProviders(track_t* pTrack) +{ + pTrack->bufferProvider = pTrack->mInputBufferProvider; + if (pTrack->mReformatBufferProvider) { + pTrack->mReformatBufferProvider->mTrackBufferProvider = pTrack->bufferProvider; + pTrack->bufferProvider = pTrack->mReformatBufferProvider; + } + if (pTrack->downmixerBufferProvider) { + pTrack->downmixerBufferProvider->mTrackBufferProvider = pTrack->bufferProvider; + pTrack->bufferProvider = pTrack->downmixerBufferProvider; + } +} + void AudioMixer::deleteTrackName(int name) { ALOGV("AudioMixer::deleteTrackName(%d)", name); @@ -369,6 +504,8 @@ void AudioMixer::deleteTrackName(int name) track.resampler = NULL; // delete the downmixer unprepareTrackForDownmix(&mState.tracks[name], name); + // delete the reformatter + unprepareTrackForReformat(&mState.tracks[name], name); mTrackNames &= ~(1<<name); } @@ -440,9 +577,20 @@ void AudioMixer::setParameter(int name, int target, int param, void *value) invalidateState(1 << name); } break; - case FORMAT: - ALOG_ASSERT(valueInt == AUDIO_FORMAT_PCM_16_BIT); - break; + case FORMAT: { + audio_format_t format = static_cast<audio_format_t>(valueInt); + if (track.mFormat != format) { + ALOG_ASSERT(audio_is_linear_pcm(format), "Invalid format %#x", format); + track.mFormat = format; + ALOGV("setParameter(TRACK, FORMAT, %#x)", format); + //if (track.mFormat != track.mMixerInFormat) + { + ALOGD("Reformatting!"); + prepareTrackForReformat(&track, name); + } + invalidateState(1 << name); + } + } break; // FIXME do we want to support setting the downmix type from AudioFlinger? // for a specific track? or per mixer? /* case DOWNMIX_TYPE: @@ -555,8 +703,9 @@ bool AudioMixer::track_t::setResampler(uint32_t value, uint32_t devSampleRate) } else { quality = AudioResampler::DEFAULT_QUALITY; } + const int bits = mMixerInFormat == AUDIO_FORMAT_PCM_16_BIT ? 16 : /* FLOAT */ 32; resampler = AudioResampler::create( - format, + bits, // the resampler sees the number of channels after the downmixer, if any (int) (downmixerBufferProvider != NULL ? MAX_NUM_CHANNELS : channelCount), devSampleRate, quality); @@ -601,21 +750,13 @@ void AudioMixer::setBufferProvider(int name, AudioBufferProvider* bufferProvider name -= TRACK0; ALOG_ASSERT(uint32_t(name) < MAX_NUM_TRACKS, "bad track name %d", name); - if (mState.tracks[name].downmixerBufferProvider != NULL) { - // update required? - if (mState.tracks[name].downmixerBufferProvider->mTrackBufferProvider != bufferProvider) { - ALOGV("AudioMixer::setBufferProvider(%p) for downmix", bufferProvider); - // setting the buffer provider for a track that gets downmixed consists in: - // 1/ setting the buffer provider to the "downmix / buffer provider" wrapper - // so it's the one that gets called when the buffer provider is needed, - mState.tracks[name].bufferProvider = mState.tracks[name].downmixerBufferProvider; - // 2/ saving the buffer provider for the track so the wrapper can use it - // when it downmixes. - mState.tracks[name].downmixerBufferProvider->mTrackBufferProvider = bufferProvider; - } - } else { - mState.tracks[name].bufferProvider = bufferProvider; + if (mState.tracks[name].mReformatBufferProvider != NULL) { + mState.tracks[name].mReformatBufferProvider->reset(); + } else if (mState.tracks[name].downmixerBufferProvider != NULL) { } + + mState.tracks[name].mInputBufferProvider = bufferProvider; + reconfigureBufferProviders(&mState.tracks[name]); } diff --git a/services/audioflinger/AudioMixer.h b/services/audioflinger/AudioMixer.h index 4a39f88..573ba96 100644 --- a/services/audioflinger/AudioMixer.h +++ b/services/audioflinger/AudioMixer.h @@ -126,7 +126,10 @@ public: size_t getUnreleasedFrames(int name) const; static inline bool isValidPcmTrackFormat(audio_format_t format) { - return format == AUDIO_FORMAT_PCM_16_BIT; + return format == AUDIO_FORMAT_PCM_16_BIT || + format == AUDIO_FORMAT_PCM_24_BIT_PACKED || + format == AUDIO_FORMAT_PCM_32_BIT || + format == AUDIO_FORMAT_PCM_FLOAT; } private: @@ -150,6 +153,7 @@ private: struct state_t; struct track_t; class DownmixerBufferProvider; + class ReformatBufferProvider; typedef void (*hook_t)(track_t* t, int32_t* output, size_t numOutFrames, int32_t* temp, int32_t* aux); @@ -177,7 +181,7 @@ private: uint16_t frameCount; uint8_t channelCount; // 1 or 2, redundant with (needs & NEEDS_CHANNEL_COUNT__MASK) - uint8_t format; // always 16 + uint8_t unused_padding; // formerly format, was always 16 uint16_t enabled; // actually bool audio_channel_mask_t channelMask; @@ -200,13 +204,19 @@ private: int32_t* auxBuffer; // 16-byte boundary - + AudioBufferProvider* mInputBufferProvider; // 4 bytes + ReformatBufferProvider* mReformatBufferProvider; // 4 bytes DownmixerBufferProvider* downmixerBufferProvider; // 4 bytes int32_t sessionId; + // 16-byte boundary audio_format_t mMixerFormat; // output mix format: AUDIO_FORMAT_PCM_(FLOAT|16_BIT) audio_format_t mFormat; // input track format + audio_format_t mMixerInFormat; // mix internal format AUDIO_FORMAT_PCM_(FLOAT|16_BIT) + // each track must be converted to this format. + + int32_t mUnused[1]; // alignment padding // 16-byte boundary @@ -245,6 +255,35 @@ private: effect_config_t mDownmixConfig; }; + // AudioBufferProvider wrapper that reformats track to acceptable mixer input type + class ReformatBufferProvider : public AudioBufferProvider { + public: + ReformatBufferProvider(int32_t channels, + audio_format_t inputFormat, audio_format_t outputFormat); + virtual ~ReformatBufferProvider(); + + // overrides AudioBufferProvider methods + virtual status_t getNextBuffer(Buffer* buffer, int64_t pts); + virtual void releaseBuffer(Buffer* buffer); + + void reset(); + inline bool requiresInternalBuffers() { + return true; //mInputFrameSize < mOutputFrameSize; + } + + AudioBufferProvider* mTrackBufferProvider; + int32_t mChannels; + audio_format_t mInputFormat; + audio_format_t mOutputFormat; + size_t mInputFrameSize; + size_t mOutputFrameSize; + // (only) required for reformatting to a larger size. + AudioBufferProvider::Buffer mBuffer; + void* mOutputData; + size_t mOutputCount; + size_t mConsumed; + }; + // bitmask of allocated track names, where bit 0 corresponds to TRACK0 etc. uint32_t mTrackNames; @@ -272,6 +311,9 @@ private: static status_t initTrackDownmix(track_t* pTrack, int trackNum, audio_channel_mask_t mask); static status_t prepareTrackForDownmix(track_t* pTrack, int trackNum); static void unprepareTrackForDownmix(track_t* pTrack, int trackName); + static status_t prepareTrackForReformat(track_t* pTrack, int trackNum); + static void unprepareTrackForReformat(track_t* pTrack, int trackName); + static void reconfigureBufferProviders(track_t* pTrack); static void track__genericResample(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux); diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp index 7e15bef..f0ce902 100644 --- a/services/audioflinger/FastMixer.cpp +++ b/services/audioflinger/FastMixer.cpp @@ -231,6 +231,8 @@ void FastMixer::onStateChange() mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER, (void *) mixBuffer); // newly allocated track names default to full scale volume + mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::FORMAT, + (void *)(uintptr_t)fastTrack->mFormat); mixer->enable(name); } generations[i] = fastTrack->mGeneration; @@ -259,6 +261,8 @@ void FastMixer::onStateChange() } mixer->setParameter(name, AudioMixer::RESAMPLE, AudioMixer::REMOVE, NULL); + mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::FORMAT, + (void *)(uintptr_t)fastTrack->mFormat); mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::CHANNEL_MASK, (void *)(uintptr_t) fastTrack->mChannelMask); // already enabled @@ -281,7 +285,7 @@ void FastMixer::onWork() const size_t frameCount = current->mFrameCount; if ((command & FastMixerState::MIX) && (mixer != NULL) && isWarm) { - ALOG_ASSERT(mixBuffer != NULL); + ALOG_ASSERT(mMixerBuffer != NULL); // for each track, update volume and check for underrun unsigned currentTrackMask = current->mTrackMask; while (currentTrackMask != 0) { |