summaryrefslogtreecommitdiffstats
path: root/services/audioflinger/AudioMixer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'services/audioflinger/AudioMixer.cpp')
-rw-r--r--services/audioflinger/AudioMixer.cpp221
1 files changed, 217 insertions, 4 deletions
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");
}
}
}