diff options
-rw-r--r-- | services/audioflinger/AudioMixer.cpp | 287 | ||||
-rw-r--r-- | services/audioflinger/AudioMixer.h | 37 |
2 files changed, 166 insertions, 158 deletions
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp index 319d4a8..ba135c6 100644 --- a/services/audioflinger/AudioMixer.cpp +++ b/services/audioflinger/AudioMixer.cpp @@ -176,50 +176,147 @@ void AudioMixer::CopyBufferProvider::reset() mConsumed = 0; } -AudioMixer::DownmixerBufferProvider::DownmixerBufferProvider() : AudioBufferProvider(), - mTrackBufferProvider(NULL), mDownmixHandle(NULL) +AudioMixer::DownmixerBufferProvider::DownmixerBufferProvider( + audio_channel_mask_t inputChannelMask, + audio_channel_mask_t outputChannelMask, audio_format_t format, + uint32_t sampleRate, int32_t sessionId, size_t bufferFrameCount) : + CopyBufferProvider( + audio_bytes_per_sample(format) * audio_channel_count_from_out_mask(inputChannelMask), + audio_bytes_per_sample(format) * audio_channel_count_from_out_mask(outputChannelMask), + bufferFrameCount) // set bufferFrameCount to 0 to do in-place { + ALOGV("DownmixerBufferProvider(%p)(%#x, %#x, %#x %u %d)", + this, inputChannelMask, outputChannelMask, format, + sampleRate, sessionId); + if (!sIsMultichannelCapable + || EffectCreate(&sDwnmFxDesc.uuid, + sessionId, + SESSION_ID_INVALID_AND_IGNORED, + &mDownmixHandle) != 0) { + ALOGE("DownmixerBufferProvider() error creating downmixer effect"); + mDownmixHandle = NULL; + return; + } + // channel input configuration will be overridden per-track + mDownmixConfig.inputCfg.channels = inputChannelMask; // FIXME: Should be bits + mDownmixConfig.outputCfg.channels = outputChannelMask; // FIXME: should be bits + mDownmixConfig.inputCfg.format = format; + mDownmixConfig.outputCfg.format = format; + mDownmixConfig.inputCfg.samplingRate = sampleRate; + mDownmixConfig.outputCfg.samplingRate = sampleRate; + mDownmixConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ; + mDownmixConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_WRITE; + // input and output buffer provider, and frame count will not be used as the downmix effect + // process() function is called directly (see DownmixerBufferProvider::getNextBuffer()) + mDownmixConfig.inputCfg.mask = EFFECT_CONFIG_SMP_RATE | EFFECT_CONFIG_CHANNELS | + EFFECT_CONFIG_FORMAT | EFFECT_CONFIG_ACC_MODE; + mDownmixConfig.outputCfg.mask = mDownmixConfig.inputCfg.mask; + + int cmdStatus; + uint32_t replySize = sizeof(int); + + // Configure downmixer + status_t status = (*mDownmixHandle)->command(mDownmixHandle, + EFFECT_CMD_SET_CONFIG /*cmdCode*/, sizeof(effect_config_t) /*cmdSize*/, + &mDownmixConfig /*pCmdData*/, + &replySize, &cmdStatus /*pReplyData*/); + if (status != 0 || cmdStatus != 0) { + ALOGE("DownmixerBufferProvider() error %d cmdStatus %d while configuring downmixer", + status, cmdStatus); + EffectRelease(mDownmixHandle); + mDownmixHandle = NULL; + return; + } + + // Enable downmixer + replySize = sizeof(int); + status = (*mDownmixHandle)->command(mDownmixHandle, + EFFECT_CMD_ENABLE /*cmdCode*/, 0 /*cmdSize*/, NULL /*pCmdData*/, + &replySize, &cmdStatus /*pReplyData*/); + if (status != 0 || cmdStatus != 0) { + ALOGE("DownmixerBufferProvider() error %d cmdStatus %d while enabling downmixer", + status, cmdStatus); + EffectRelease(mDownmixHandle); + mDownmixHandle = NULL; + return; + } + + // Set downmix type + // parameter size rounded for padding on 32bit boundary + const int psizePadded = ((sizeof(downmix_params_t) - 1)/sizeof(int) + 1) * sizeof(int); + const int downmixParamSize = + sizeof(effect_param_t) + psizePadded + sizeof(downmix_type_t); + effect_param_t * const param = (effect_param_t *) malloc(downmixParamSize); + param->psize = sizeof(downmix_params_t); + const downmix_params_t downmixParam = DOWNMIX_PARAM_TYPE; + memcpy(param->data, &downmixParam, param->psize); + const downmix_type_t downmixType = DOWNMIX_TYPE_FOLD; + param->vsize = sizeof(downmix_type_t); + memcpy(param->data + psizePadded, &downmixType, param->vsize); + replySize = sizeof(int); + status = (*mDownmixHandle)->command(mDownmixHandle, + EFFECT_CMD_SET_PARAM /* cmdCode */, downmixParamSize /* cmdSize */, + param /*pCmdData*/, &replySize, &cmdStatus /*pReplyData*/); + free(param); + if (status != 0 || cmdStatus != 0) { + ALOGE("DownmixerBufferProvider() error %d cmdStatus %d while setting downmix type", + status, cmdStatus); + EffectRelease(mDownmixHandle); + mDownmixHandle = NULL; + return; + } + ALOGV("DownmixerBufferProvider() downmix type set to %d", (int) downmixType); } AudioMixer::DownmixerBufferProvider::~DownmixerBufferProvider() { - ALOGV("AudioMixer deleting DownmixerBufferProvider (%p)", this); + ALOGV("~DownmixerBufferProvider (%p)", this); EffectRelease(mDownmixHandle); + mDownmixHandle = NULL; } -status_t AudioMixer::DownmixerBufferProvider::getNextBuffer(AudioBufferProvider::Buffer *pBuffer, - int64_t pts) { - //ALOGV("DownmixerBufferProvider::getNextBuffer()"); - if (mTrackBufferProvider != NULL) { - status_t res = mTrackBufferProvider->getNextBuffer(pBuffer, pts); - if (res == OK) { - mDownmixConfig.inputCfg.buffer.frameCount = pBuffer->frameCount; - mDownmixConfig.inputCfg.buffer.raw = pBuffer->raw; - mDownmixConfig.outputCfg.buffer.frameCount = pBuffer->frameCount; - mDownmixConfig.outputCfg.buffer.raw = mDownmixConfig.inputCfg.buffer.raw; - // in-place so overwrite the buffer contents, has been set in prepareTrackForDownmix() - //mDownmixConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_WRITE; - - res = (*mDownmixHandle)->process(mDownmixHandle, - &mDownmixConfig.inputCfg.buffer, &mDownmixConfig.outputCfg.buffer); - //ALOGV("getNextBuffer is downmixing"); - } - return res; - } else { - ALOGE("DownmixerBufferProvider::getNextBuffer() error: NULL track buffer provider"); +void AudioMixer::DownmixerBufferProvider::copyFrames(void *dst, const void *src, size_t frames) +{ + mDownmixConfig.inputCfg.buffer.frameCount = frames; + mDownmixConfig.inputCfg.buffer.raw = const_cast<void *>(src); + mDownmixConfig.outputCfg.buffer.frameCount = frames; + mDownmixConfig.outputCfg.buffer.raw = dst; + // may be in-place if src == dst. + status_t res = (*mDownmixHandle)->process(mDownmixHandle, + &mDownmixConfig.inputCfg.buffer, &mDownmixConfig.outputCfg.buffer); + ALOGE_IF(res != OK, "DownmixBufferProvider error %d", res); +} + +/* call once in a pthread_once handler. */ +/*static*/ status_t AudioMixer::DownmixerBufferProvider::init() +{ + // find multichannel downmix effect if we have to play multichannel content + uint32_t numEffects = 0; + int ret = EffectQueryNumberEffects(&numEffects); + if (ret != 0) { + ALOGE("AudioMixer() error %d querying number of effects", ret); return NO_INIT; } -} + ALOGV("EffectQueryNumberEffects() numEffects=%d", numEffects); -void AudioMixer::DownmixerBufferProvider::releaseBuffer(AudioBufferProvider::Buffer *pBuffer) { - //ALOGV("DownmixerBufferProvider::releaseBuffer()"); - if (mTrackBufferProvider != NULL) { - mTrackBufferProvider->releaseBuffer(pBuffer); - } else { - ALOGE("DownmixerBufferProvider::releaseBuffer() error: NULL track buffer provider"); + for (uint32_t i = 0 ; i < numEffects ; i++) { + if (EffectQueryEffect(i, &sDwnmFxDesc) == 0) { + ALOGV("effect %d is called %s", i, sDwnmFxDesc.name); + if (memcmp(&sDwnmFxDesc.type, EFFECT_UIID_DOWNMIX, sizeof(effect_uuid_t)) == 0) { + ALOGI("found effect \"%s\" from %s", + sDwnmFxDesc.name, sDwnmFxDesc.implementor); + sIsMultichannelCapable = true; + break; + } + } } + ALOGW_IF(!sIsMultichannelCapable, "unable to find downmix effect"); + return NO_INIT; } +/*static*/ bool AudioMixer::DownmixerBufferProvider::sIsMultichannelCapable = false; +/*static*/ effect_descriptor_t AudioMixer::DownmixerBufferProvider::sDwnmFxDesc; + AudioMixer::ReformatBufferProvider::ReformatBufferProvider(int32_t channels, audio_format_t inputFormat, audio_format_t outputFormat, size_t bufferFrameCount) : @@ -240,9 +337,6 @@ void AudioMixer::ReformatBufferProvider::copyFrames(void *dst, const void *src, } // ---------------------------------------------------------------------------- -bool AudioMixer::sIsMultichannelCapable = false; - -effect_descriptor_t AudioMixer::sDwnmFxDesc; // Ensure mConfiguredNames bitmask is initialized properly on all architectures. // The value of 1 << x is undefined in C when x >= 32. @@ -434,95 +528,20 @@ status_t AudioMixer::prepareTrackForDownmix(track_t* pTrack, int trackName) // discard the previous downmixer if there was one unprepareTrackForDownmix(pTrack, trackName); - - DownmixerBufferProvider* pDbp = new DownmixerBufferProvider(); - int32_t status; - - if (!sIsMultichannelCapable) { - ALOGE("prepareTrackForDownmix(%d) fails: mixer doesn't support multichannel content", - trackName); - goto noDownmixForActiveTrack; - } - - if (EffectCreate(&sDwnmFxDesc.uuid, - pTrack->sessionId /*sessionId*/, -2 /*ioId not relevant here, using random value*/, - &pDbp->mDownmixHandle/*pHandle*/) != 0) { - ALOGE("prepareTrackForDownmix(%d) fails: error creating downmixer effect", trackName); - goto noDownmixForActiveTrack; - } - - // channel input configuration will be overridden per-track - pDbp->mDownmixConfig.inputCfg.channels = pTrack->channelMask; - pDbp->mDownmixConfig.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO; - pDbp->mDownmixConfig.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT; - pDbp->mDownmixConfig.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT; - pDbp->mDownmixConfig.inputCfg.samplingRate = pTrack->sampleRate; - pDbp->mDownmixConfig.outputCfg.samplingRate = pTrack->sampleRate; - pDbp->mDownmixConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ; - pDbp->mDownmixConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_WRITE; - // input and output buffer provider, and frame count will not be used as the downmix effect - // process() function is called directly (see DownmixerBufferProvider::getNextBuffer()) - pDbp->mDownmixConfig.inputCfg.mask = EFFECT_CONFIG_SMP_RATE | EFFECT_CONFIG_CHANNELS | - EFFECT_CONFIG_FORMAT | EFFECT_CONFIG_ACC_MODE; - pDbp->mDownmixConfig.outputCfg.mask = pDbp->mDownmixConfig.inputCfg.mask; - - {// scope for local variables that are not used in goto label "noDownmixForActiveTrack" - int cmdStatus; - uint32_t replySize = sizeof(int); - - // Configure and enable downmixer - status = (*pDbp->mDownmixHandle)->command(pDbp->mDownmixHandle, - EFFECT_CMD_SET_CONFIG /*cmdCode*/, sizeof(effect_config_t) /*cmdSize*/, - &pDbp->mDownmixConfig /*pCmdData*/, - &replySize /*replySize*/, &cmdStatus /*pReplyData*/); - if ((status != 0) || (cmdStatus != 0)) { - ALOGE("error %d while configuring downmixer for track %d", status, trackName); - goto noDownmixForActiveTrack; - } - replySize = sizeof(int); - status = (*pDbp->mDownmixHandle)->command(pDbp->mDownmixHandle, - EFFECT_CMD_ENABLE /*cmdCode*/, 0 /*cmdSize*/, NULL /*pCmdData*/, - &replySize /*replySize*/, &cmdStatus /*pReplyData*/); - if ((status != 0) || (cmdStatus != 0)) { - ALOGE("error %d while enabling downmixer for track %d", status, trackName); - goto noDownmixForActiveTrack; + if (DownmixerBufferProvider::isMultichannelCapable()) { + DownmixerBufferProvider* pDbp = new DownmixerBufferProvider(pTrack->channelMask, + /* pTrack->mMixerChannelMask */ audio_channel_out_mask_from_count(2), + /* pTrack->mMixerInFormat */ AUDIO_FORMAT_PCM_16_BIT, + pTrack->sampleRate, pTrack->sessionId, kCopyBufferFrameCount); + + if (pDbp->isValid()) { // if constructor completed properly + pTrack->mMixerInFormat = AUDIO_FORMAT_PCM_16_BIT; // PCM 16 bit required for downmix + pTrack->downmixerBufferProvider = pDbp; + reconfigureBufferProviders(pTrack); + return NO_ERROR; } - - // Set downmix type - // parameter size rounded for padding on 32bit boundary - const int psizePadded = ((sizeof(downmix_params_t) - 1)/sizeof(int) + 1) * sizeof(int); - const int downmixParamSize = - sizeof(effect_param_t) + psizePadded + sizeof(downmix_type_t); - effect_param_t * const param = (effect_param_t *) malloc(downmixParamSize); - param->psize = sizeof(downmix_params_t); - const downmix_params_t downmixParam = DOWNMIX_PARAM_TYPE; - memcpy(param->data, &downmixParam, param->psize); - const downmix_type_t downmixType = DOWNMIX_TYPE_FOLD; - param->vsize = sizeof(downmix_type_t); - memcpy(param->data + psizePadded, &downmixType, param->vsize); - - status = (*pDbp->mDownmixHandle)->command(pDbp->mDownmixHandle, - EFFECT_CMD_SET_PARAM /* cmdCode */, downmixParamSize/* cmdSize */, - param /*pCmndData*/, &replySize /*replySize*/, &cmdStatus /*pReplyData*/); - - free(param); - - if ((status != 0) || (cmdStatus != 0)) { - ALOGE("error %d while setting downmix type for track %d", status, trackName); - goto noDownmixForActiveTrack; - } else { - ALOGV("downmix type set to %d for track %d", (int) downmixType, trackName); - } - }// end of scope for local variables that are not used in goto label "noDownmixForActiveTrack" - - // initialization successful: - pTrack->mMixerInFormat = AUDIO_FORMAT_PCM_16_BIT; // 16 bit input is required for downmix - pTrack->downmixerBufferProvider = pDbp; - reconfigureBufferProviders(pTrack); - return NO_ERROR; - -noDownmixForActiveTrack: - delete pDbp; + delete pDbp; + } pTrack->downmixerBufferProvider = NULL; reconfigureBufferProviders(pTrack); return NO_INIT; @@ -561,7 +580,7 @@ void AudioMixer::reconfigureBufferProviders(track_t* pTrack) pTrack->bufferProvider = pTrack->mReformatBufferProvider; } if (pTrack->downmixerBufferProvider) { - pTrack->downmixerBufferProvider->mTrackBufferProvider = pTrack->bufferProvider; + pTrack->downmixerBufferProvider->setBufferProvider(pTrack->bufferProvider); pTrack->bufferProvider = pTrack->downmixerBufferProvider; } } @@ -1806,29 +1825,9 @@ int64_t AudioMixer::calculateOutputPTS(const track_t& t, int64_t basePTS, /*static*/ void AudioMixer::sInitRoutine() { LocalClock lc; - sLocalTimeFreq = lc.getLocalFreq(); - - // find multichannel downmix effect if we have to play multichannel content - uint32_t numEffects = 0; - int ret = EffectQueryNumberEffects(&numEffects); - if (ret != 0) { - ALOGE("AudioMixer() error %d querying number of effects", ret); - return; - } - ALOGV("EffectQueryNumberEffects() numEffects=%d", numEffects); + sLocalTimeFreq = lc.getLocalFreq(); // for the resampler - for (uint32_t i = 0 ; i < numEffects ; i++) { - if (EffectQueryEffect(i, &sDwnmFxDesc) == 0) { - ALOGV("effect %d is called %s", i, sDwnmFxDesc.name); - if (memcmp(&sDwnmFxDesc.type, EFFECT_UIID_DOWNMIX, sizeof(effect_uuid_t)) == 0) { - ALOGI("found effect \"%s\" from %s", - sDwnmFxDesc.name, sDwnmFxDesc.implementor); - sIsMultichannelCapable = true; - break; - } - } - } - ALOGW_IF(!sIsMultichannelCapable, "unable to find downmix effect"); + DownmixerBufferProvider::init(); // for the downmixer } template <int MIXTYPE, int NCHAN, bool USEFLOATVOL, bool ADJUSTVOL, diff --git a/services/audioflinger/AudioMixer.h b/services/audioflinger/AudioMixer.h index f08d9b5..7ad2e75 100644 --- a/services/audioflinger/AudioMixer.h +++ b/services/audioflinger/AudioMixer.h @@ -153,7 +153,6 @@ private: struct state_t; struct track_t; - class DownmixerBufferProvider; class CopyBufferProvider; typedef void (*hook_t)(track_t* t, int32_t* output, size_t numOutFrames, int32_t* temp, @@ -208,7 +207,7 @@ private: // 16-byte boundary AudioBufferProvider* mInputBufferProvider; // externally provided buffer provider. CopyBufferProvider* mReformatBufferProvider; // provider wrapper for reformatting. - DownmixerBufferProvider* downmixerBufferProvider; // 4 bytes + CopyBufferProvider* downmixerBufferProvider; // wrapper for channel conversion. int32_t sessionId; @@ -253,7 +252,8 @@ private: track_t tracks[MAX_NUM_TRACKS] __attribute__((aligned(32))); }; - // Base AudioBufferProvider class used for ReformatBufferProvider. + // Base AudioBufferProvider class used for ReformatBufferProvider and + // DownmixerBufferProvider. // It handles a private buffer for use in converting format or channel masks from the // input data to a form acceptable by the mixer. // TODO: Make a ResamplerBufferProvider when integers are entirely removed from the @@ -299,17 +299,31 @@ private: size_t mConsumed; }; - // AudioBufferProvider that wraps a track AudioBufferProvider by a call to a downmix effect - class DownmixerBufferProvider : public AudioBufferProvider { + // DownmixerBufferProvider wraps a track AudioBufferProvider to provide + // position dependent downmixing by an Audio Effect. + class DownmixerBufferProvider : public CopyBufferProvider { public: - virtual status_t getNextBuffer(Buffer* buffer, int64_t pts); - virtual void releaseBuffer(Buffer* buffer); - DownmixerBufferProvider(); + DownmixerBufferProvider(audio_channel_mask_t inputChannelMask, + audio_channel_mask_t outputChannelMask, audio_format_t format, + uint32_t sampleRate, int32_t sessionId, size_t bufferFrameCount); virtual ~DownmixerBufferProvider(); + virtual void copyFrames(void *dst, const void *src, size_t frames); + bool isValid() const { return mDownmixHandle != NULL; } - AudioBufferProvider* mTrackBufferProvider; + static status_t init(); + static bool isMultichannelCapable() { return sIsMultichannelCapable; } + + protected: effect_handle_t mDownmixHandle; effect_config_t mDownmixConfig; + + // effect descriptor for the downmixer used by the mixer + static effect_descriptor_t sDwnmFxDesc; + // indicates whether a downmix effect has been found and is usable by this mixer + static bool sIsMultichannelCapable; + // FIXME: should we allow effects outside of the framework? + // We need to here. A special ioId that must be <= -2 so it does not map to a session. + static const int32_t SESSION_ID_INVALID_AND_IGNORED = -2; }; // ReformatBufferProvider wraps a track AudioBufferProvider to convert the input data @@ -342,11 +356,6 @@ public: private: state_t mState __attribute__((aligned(32))); - // effect descriptor for the downmixer used by the mixer - static effect_descriptor_t sDwnmFxDesc; - // indicates whether a downmix effect has been found and is usable by this mixer - static bool sIsMultichannelCapable; - // Call after changing either the enabled status of a track, or parameters of an enabled track. // OK to call more often than that, but unnecessary. void invalidateState(uint32_t mask); |