diff options
author | Jean-Michel Trivi <jmtrivi@google.com> | 2012-04-10 09:09:09 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2012-04-10 09:09:09 -0700 |
commit | d2469c3fe382203eb7cd5060c94ed204bec98116 (patch) | |
tree | 86544632fcea8ba779575b45b53ec8934a302153 | |
parent | ed5b285520ae1ddad78b7c945d659a2c1f3140b8 (diff) | |
parent | 7d5b26230a179cd7bcc01f6578cd80d8c15a92a5 (diff) | |
download | frameworks_av-d2469c3fe382203eb7cd5060c94ed204bec98116.zip frameworks_av-d2469c3fe382203eb7cd5060c94ed204bec98116.tar.gz frameworks_av-d2469c3fe382203eb7cd5060c94ed204bec98116.tar.bz2 |
Merge "AudioMixer uses downmix effect for multichannel content"
-rw-r--r-- | media/libeffects/data/audio_effects.conf | 7 | ||||
-rw-r--r-- | media/libeffects/downmix/EffectDownmix.c | 2 | ||||
-rw-r--r-- | services/audioflinger/AudioFlinger.cpp | 16 | ||||
-rw-r--r-- | services/audioflinger/AudioFlinger.h | 9 | ||||
-rw-r--r-- | services/audioflinger/AudioMixer.cpp | 221 | ||||
-rw-r--r-- | services/audioflinger/AudioMixer.h | 39 |
6 files changed, 277 insertions, 17 deletions
diff --git a/media/libeffects/data/audio_effects.conf b/media/libeffects/data/audio_effects.conf index ce25bc8..d681c69 100644 --- a/media/libeffects/data/audio_effects.conf +++ b/media/libeffects/data/audio_effects.conf @@ -18,6 +18,9 @@ libraries { pre_processing { path /system/lib/soundfx/libaudiopreprocessing.so } + downmix { + path /system/lib/soundfx/libdownmix.so + } } # list of effects to load. Each effect element must contain a "library" and a "uuid" element. @@ -72,6 +75,10 @@ effects { library visualizer uuid d069d9e0-8329-11df-9168-0002a5d5c51b } + downmix { + library downmix + uuid 93f04452-e4fe-41cc-91f9-e475b6d1d69f + } agc { library pre_processing uuid aa8130e0-66fc-11e0-bad0-0002a5d5c51b diff --git a/media/libeffects/downmix/EffectDownmix.c b/media/libeffects/downmix/EffectDownmix.c index a325172..302f1d7 100644 --- a/media/libeffects/downmix/EffectDownmix.c +++ b/media/libeffects/downmix/EffectDownmix.c @@ -610,7 +610,7 @@ int Downmix_setParameter(downmix_object_t *pDownmixer, int32_t param, size_t siz } value16 = *(int16_t *)pValue; ALOGV("set DOWNMIX_PARAM_TYPE, type %d", value16); - if (!((value16 > DOWNMIX_TYPE_INVALID) && (value16 < DOWNMIX_TYPE_LAST))) { + if (!((value16 > DOWNMIX_TYPE_INVALID) && (value16 <= DOWNMIX_TYPE_LAST))) { ALOGE("Downmix_setParameter invalid DOWNMIX_PARAM_TYPE value %d", value16); return -EINVAL; } else { diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 9e6a6df..0ce1153 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -2639,9 +2639,15 @@ void AudioFlinger::MixerThread::invalidateTracks(audio_stream_type_t streamType) } // getTrackName_l() must be called with ThreadBase::mLock held -int AudioFlinger::MixerThread::getTrackName_l() +int AudioFlinger::MixerThread::getTrackName_l(audio_channel_mask_t channelMask) { - return mAudioMixer->getTrackName(); + int name = mAudioMixer->getTrackName(); + if (name >= 0) { + mAudioMixer->setParameter(name, + AudioMixer::TRACK, + AudioMixer::CHANNEL_MASK, (void *)channelMask); + } + return name; } // deleteTrackName_l() must be called with ThreadBase::mLock held @@ -2738,7 +2744,7 @@ bool AudioFlinger::MixerThread::checkForNewParameters_l() readOutputParameters(); mAudioMixer = new AudioMixer(mFrameCount, mSampleRate); for (size_t i = 0; i < mTracks.size() ; i++) { - int name = getTrackName_l(); + int name = getTrackName_l((audio_channel_mask_t)mTracks[i]->mChannelMask); if (name < 0) break; mTracks[i]->mName = name; // limit track sample rate to 2 x new output sample rate @@ -3066,7 +3072,7 @@ void AudioFlinger::DirectOutputThread::threadLoop_sleepTime() } // getTrackName_l() must be called with ThreadBase::mLock held -int AudioFlinger::DirectOutputThread::getTrackName_l() +int AudioFlinger::DirectOutputThread::getTrackName_l(audio_channel_mask_t channelMask) { return 0; } @@ -3528,7 +3534,7 @@ AudioFlinger::PlaybackThread::Track::Track( // 16 bit because data is converted to 16 bit before being stored in buffer by AudioTrack mCblk->frameSize = audio_is_linear_pcm(format) ? mChannelCount * sizeof(int16_t) : sizeof(uint8_t); // to avoid leaking a track name, do not allocate one unless there is an mCblk - mName = thread->getTrackName_l(); + mName = thread->getTrackName_l((audio_channel_mask_t)channelMask); if (mName < 0) { ALOGE("no more track names available"); } diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index e493a9a..8a787e1 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -964,8 +964,9 @@ public: protected: SortedVector< wp<Track> > mActiveTracks; - // Allocate a track name. Returns name >= 0 if successful, -1 on failure. - virtual int getTrackName_l() = 0; + // Allocate a track name for a given channel mask. + // Returns name >= 0 if successful, -1 on failure. + virtual int getTrackName_l(audio_channel_mask_t channelMask) = 0; virtual void deleteTrackName_l(int name) = 0; // Time to sleep between cycles when: @@ -1057,7 +1058,7 @@ public: protected: virtual mixer_state prepareTracks_l(Vector< sp<Track> > *tracksToRemove); - virtual int getTrackName_l(); + virtual int getTrackName_l(audio_channel_mask_t channelMask); virtual void deleteTrackName_l(int name); virtual uint32_t idleSleepTimeUs() const; virtual uint32_t suspendSleepTimeUs() const; @@ -1082,7 +1083,7 @@ public: virtual bool checkForNewParameters_l(); protected: - virtual int getTrackName_l(); + virtual int getTrackName_l(audio_channel_mask_t channelMask); virtual void deleteTrackName_l(int name); virtual uint32_t activeSleepTimeUs() const; virtual uint32_t idleSleepTimeUs() const; diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp index 0e6ea12..399d987 100644 --- a/services/audioflinger/AudioMixer.cpp +++ b/services/audioflinger/AudioMixer.cpp @@ -36,11 +36,62 @@ #include <common_time/local_clock.h> #include <common_time/cc_helper.h> +#include <media/EffectsFactoryApi.h> + #include "AudioMixer.h" namespace android { // ---------------------------------------------------------------------------- +AudioMixer::DownmixerBufferProvider::DownmixerBufferProvider() : AudioBufferProvider(), + mTrackBufferProvider(NULL), mDownmixHandle(NULL) +{ +} + +AudioMixer::DownmixerBufferProvider::~DownmixerBufferProvider() +{ + ALOGV("AudioMixer deleting DownmixerBufferProvider (%p)", this); + EffectRelease(mDownmixHandle); +} + +status_t AudioMixer::DownmixerBufferProvider::getNextBuffer(AudioBufferProvider::Buffer *pBuffer, + int64_t pts) { + //ALOGV("DownmixerBufferProvider::getNextBuffer()"); + if (this->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"); + return NO_INIT; + } +} + +void AudioMixer::DownmixerBufferProvider::releaseBuffer(AudioBufferProvider::Buffer *pBuffer) { + ALOGV("DownmixerBufferProvider::releaseBuffer()"); + if (this->mTrackBufferProvider != NULL) { + mTrackBufferProvider->releaseBuffer(pBuffer); + } else { + ALOGE("DownmixerBufferProvider::releaseBuffer() error: NULL track buffer provider"); + } +} + + +// ---------------------------------------------------------------------------- +bool AudioMixer::isMultichannelCapable = false; + +effect_descriptor_t AudioMixer::dwnmFxDesc; AudioMixer::AudioMixer(size_t frameCount, uint32_t sampleRate, uint32_t maxNumTracks) : mTrackNames(0), mConfiguredNames((1 << maxNumTracks) - 1), mSampleRate(sampleRate) @@ -70,6 +121,28 @@ AudioMixer::AudioMixer(size_t frameCount, uint32_t sampleRate, uint32_t maxNumTr t->localTimeFreq = lc.getLocalFreq(); t++; } + + // 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); + + for (uint32_t i = 0 ; i < numEffects ; i++) { + if (EffectQueryEffect(i, &dwnmFxDesc) == 0) { + ALOGV("effect %d is called %s", i, dwnmFxDesc.name); + if (memcmp(&dwnmFxDesc.type, EFFECT_UIID_DOWNMIX, sizeof(effect_uuid_t)) == 0) { + ALOGI("found effect \"%s\" from %s", + dwnmFxDesc.name, dwnmFxDesc.implementor); + isMultichannelCapable = true; + break; + } + } + } + ALOGE_IF(!isMultichannelCapable, "unable to find downmix effect"); } AudioMixer::~AudioMixer() @@ -111,6 +184,7 @@ int AudioMixer::getTrackName() t->channelMask = AUDIO_CHANNEL_OUT_STEREO; // setBufferProvider(name, AudioBufferProvider *) is required before enable(name) t->bufferProvider = NULL; + t->downmixerBufferProvider = NULL; t->buffer.raw = NULL; // no initialization needed // t->buffer.frameCount @@ -135,6 +209,113 @@ void AudioMixer::invalidateState(uint32_t mask) } } +status_t AudioMixer::prepareTrackForDownmix(track_t* pTrack, int trackName) +{ + ALOGV("AudioMixer::prepareTrackForDownmix(%d) with mask 0x%x", trackName, pTrack->channelMask); + + if (pTrack->downmixerBufferProvider != NULL) { + // this track had previously been configured with a downmixer, reset it + ALOGV("AudioMixer::prepareTrackForDownmix(%d) deleting old downmixer", trackName); + pTrack->bufferProvider = pTrack->downmixerBufferProvider->mTrackBufferProvider; + delete pTrack->downmixerBufferProvider; + } + + DownmixerBufferProvider* pDbp = new DownmixerBufferProvider(); + int32_t status; + + if (!isMultichannelCapable) { + ALOGE("prepareTrackForDownmix(%d) fails: mixer doesn't support multichannel content", + trackName); + goto noDownmixForActiveTrack; + } + + if (EffectCreate(&dwnmFxDesc.uuid, + -2 /*sessionId*/, -2 /*ioId*/,// both 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; + } + + // 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: + // - 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; + + return NO_ERROR; + +noDownmixForActiveTrack: + delete pDbp; + pTrack->downmixerBufferProvider = NULL; + return NO_INIT; +} + void AudioMixer::deleteTrackName(int name) { name -= TRACK0; @@ -177,6 +358,11 @@ void AudioMixer::disable(int name) track_t& track = mState.tracks[name]; if (track.enabled) { + if (track.downmixerBufferProvider != NULL) { + ALOGV("AudioMixer::disable(%d) deleting downmixerBufferProvider", name); + delete track.downmixerBufferProvider; + track.downmixerBufferProvider = NULL; + } track.enabled = false; ALOGV("disable(%d)", name); invalidateState(1 << name); @@ -200,10 +386,14 @@ void AudioMixer::setParameter(int name, int target, int param, void *value) uint32_t mask = (uint32_t)value; if (track.channelMask != mask) { uint32_t channelCount = popcount(mask); - ALOG_ASSERT((channelCount <= MAX_NUM_CHANNELS) && (channelCount), - "bad channel count %u", channelCount); + ALOG_ASSERT((channelCount <= MAX_NUM_CHANNELS_TO_DOWNMIX) && channelCount); track.channelMask = mask; track.channelCount = channelCount; + if (channelCount > MAX_NUM_CHANNELS) { + ALOGV("AudioMixer::setParameter(TRACK, CHANNEL_MASK, mask=0x%x count=%d)", + mask, channelCount); + status_t status = prepareTrackForDownmix(&mState.tracks[name], name); + } ALOGV("setParameter(TRACK, CHANNEL_MASK, %x)", mask); invalidateState(1 << name); } @@ -225,6 +415,10 @@ void AudioMixer::setParameter(int name, int target, int param, void *value) case FORMAT: ALOG_ASSERT(valueInt == AUDIO_FORMAT_PCM_16_BIT); break; + // FIXME do we want to support setting the downmix type from AudioFlinger? + // for a specific track? or per mixer? + /* case DOWNMIX_TYPE: + break */ default: LOG_FATAL("bad param"); } @@ -350,7 +544,22 @@ void AudioMixer::setBufferProvider(int name, AudioBufferProvider* bufferProvider { name -= TRACK0; ALOG_ASSERT(uint32_t(name) < MAX_NUM_TRACKS, "bad track name %d", name); - mState.tracks[name].bufferProvider = bufferProvider; + + 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; + } } @@ -419,13 +628,17 @@ void AudioMixer::process__validate(state_t* state, int64_t pts) all16BitsStereoNoResample = false; resampling = true; t.hook = track__genericResample; + ALOGV_IF((n & NEEDS_CHANNEL_COUNT__MASK) > NEEDS_CHANNEL_2, + "Track needs downmix + resample"); } else { if ((n & NEEDS_CHANNEL_COUNT__MASK) == NEEDS_CHANNEL_1){ t.hook = track__16BitsMono; all16BitsStereoNoResample = false; } - if ((n & NEEDS_CHANNEL_COUNT__MASK) == NEEDS_CHANNEL_2){ + if ((n & NEEDS_CHANNEL_COUNT__MASK) >= NEEDS_CHANNEL_2){ t.hook = track__16BitsStereo; + ALOGV_IF((n & NEEDS_CHANNEL_COUNT__MASK) > NEEDS_CHANNEL_2, + "Track needs downmix"); } } } diff --git a/services/audioflinger/AudioMixer.h b/services/audioflinger/AudioMixer.h index 856450c..a04fe95 100644 --- a/services/audioflinger/AudioMixer.h +++ b/services/audioflinger/AudioMixer.h @@ -21,9 +21,14 @@ #include <stdint.h> #include <sys/types.h> +#include <utils/threads.h> + #include "AudioBufferProvider.h" #include "AudioResampler.h" +#include <audio_effects/effect_downmix.h> +#include <system/audio.h> + namespace android { // ---------------------------------------------------------------------------- @@ -37,7 +42,10 @@ public: /*virtual*/ ~AudioMixer(); // non-virtual saves a v-table, restore if sub-classed static const uint32_t MAX_NUM_TRACKS = 32; + // maximum number of channels supported by the mixer static const uint32_t MAX_NUM_CHANNELS = 2; + // maximum number of channels supported for the content + static const uint32_t MAX_NUM_CHANNELS_TO_DOWNMIX = 8; static const uint16_t UNITY_GAIN = 0x1000; @@ -60,6 +68,7 @@ public: FORMAT = 0x4001, MAIN_BUFFER = 0x4002, AUX_BUFFER = 0x4003, + DOWNMIX_TYPE = 0X4004, // for target RESAMPLE SAMPLE_RATE = 0x4100, RESET = 0x4101, @@ -94,7 +103,7 @@ public: private: enum { - NEEDS_CHANNEL_COUNT__MASK = 0x00000003, + NEEDS_CHANNEL_COUNT__MASK = 0x00000007, NEEDS_FORMAT__MASK = 0x000000F0, NEEDS_MUTE__MASK = 0x00000100, NEEDS_RESAMPLE__MASK = 0x00001000, @@ -119,6 +128,7 @@ private: struct state_t; struct track_t; + class DownmixerBufferProvider; typedef void (*hook_t)(track_t* t, int32_t* output, size_t numOutFrames, int32_t* temp, int32_t* aux); static const int BLOCKSIZE = 16; // 4 cache lines @@ -147,8 +157,10 @@ private: uint8_t channelCount; // 1 or 2, redundant with (needs & NEEDS_CHANNEL_COUNT__MASK) uint8_t format; // always 16 uint16_t enabled; // actually bool - uint32_t channelMask; // currently under-used + audio_channel_mask_t channelMask; + // actual buffer provider used by the track hooks, see DownmixerBufferProvider below + // for how the Track buffer provider is wrapped by another one when dowmixing is required AudioBufferProvider* bufferProvider; // 16-byte boundary @@ -169,7 +181,9 @@ private: uint64_t localTimeFreq; - int64_t padding; + DownmixerBufferProvider* downmixerBufferProvider; // 4 bytes + + int32_t padding; // 16-byte boundary @@ -194,6 +208,19 @@ private: track_t tracks[MAX_NUM_TRACKS]; __attribute__((aligned(32))); }; + // AudioBufferProvider that wraps a track AudioBufferProvider by a call to a downmix effect + class DownmixerBufferProvider : public AudioBufferProvider { + public: + virtual status_t getNextBuffer(Buffer* buffer, int64_t pts); + virtual void releaseBuffer(Buffer* buffer); + DownmixerBufferProvider(); + virtual ~DownmixerBufferProvider(); + + AudioBufferProvider* mTrackBufferProvider; + effect_handle_t mDownmixHandle; + effect_config_t mDownmixConfig; + }; + // bitmask of allocated track names, where bit 0 corresponds to TRACK0 etc. uint32_t mTrackNames; @@ -205,7 +232,13 @@ private: state_t mState __attribute__((aligned(32))); + // effect descriptor for the downmixer used by the mixer + static effect_descriptor_t dwnmFxDesc; + // indicates whether a downmix effect has been found and is usable by this mixer + static bool isMultichannelCapable; + void invalidateState(uint32_t mask); + static status_t prepareTrackForDownmix(track_t* pTrack, int trackNum); static void track__genericResample(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux); static void track__nop(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux); |