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.cpp531
1 files changed, 375 insertions, 156 deletions
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
index f92421e..d73292e 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>
@@ -58,7 +59,7 @@ AudioMixer::DownmixerBufferProvider::~DownmixerBufferProvider()
status_t AudioMixer::DownmixerBufferProvider::getNextBuffer(AudioBufferProvider::Buffer *pBuffer,
int64_t pts) {
//ALOGV("DownmixerBufferProvider::getNextBuffer()");
- if (this->mTrackBufferProvider != NULL) {
+ if (mTrackBufferProvider != NULL) {
status_t res = mTrackBufferProvider->getNextBuffer(pBuffer, pts);
if (res == OK) {
mDownmixConfig.inputCfg.buffer.frameCount = pBuffer->frameCount;
@@ -81,18 +82,115 @@ status_t AudioMixer::DownmixerBufferProvider::getNextBuffer(AudioBufferProvider:
void AudioMixer::DownmixerBufferProvider::releaseBuffer(AudioBufferProvider::Buffer *pBuffer) {
//ALOGV("DownmixerBufferProvider::releaseBuffer()");
- if (this->mTrackBufferProvider != NULL) {
+ if (mTrackBufferProvider != NULL) {
mTrackBufferProvider->releaseBuffer(pBuffer);
} else {
ALOGE("DownmixerBufferProvider::releaseBuffer() error: NULL track buffer provider");
}
}
+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::isMultichannelCapable = false;
+bool AudioMixer::sIsMultichannelCapable = false;
-effect_descriptor_t AudioMixer::dwnmFxDesc;
+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.
@@ -113,8 +211,6 @@ AudioMixer::AudioMixer(size_t frameCount, uint32_t sampleRate, uint32_t maxNumTr
// AudioMixer is not yet capable of multi-channel output beyond stereo
ALOG_ASSERT(2 == MAX_NUM_CHANNELS, "bad MAX_NUM_CHANNELS %d", MAX_NUM_CHANNELS);
- LocalClock lc;
-
pthread_once(&sOnceControl, &sInitRoutine);
mState.enabledTracks= 0;
@@ -136,27 +232,6 @@ AudioMixer::AudioMixer(size_t frameCount, uint32_t sampleRate, uint32_t maxNumTr
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()
@@ -176,18 +251,22 @@ void AudioMixer::setLog(NBLog::Writer *log)
mState.mLog = log;
}
-int AudioMixer::getTrackName(audio_channel_mask_t channelMask, int sessionId)
+int AudioMixer::getTrackName(audio_channel_mask_t channelMask,
+ audio_format_t format, int sessionId)
{
+ if (!isValidPcmTrackFormat(format)) {
+ ALOGE("AudioMixer::getTrackName invalid format (%#x)", format);
+ return -1;
+ }
uint32_t names = (~mTrackNames) & mConfiguredNames;
if (names != 0) {
int n = __builtin_ctz(names);
ALOGV("add track (%d)", n);
- mTrackNames |= 1 << n;
// assume default parameters for the track, except where noted below
track_t* t = &mState.tracks[n];
t->needs = 0;
- t->volume[0] = UNITY_GAIN;
- t->volume[1] = UNITY_GAIN;
+ t->volume[0] = UNITY_GAIN_INT;
+ t->volume[1] = UNITY_GAIN_INT;
// no initialization needed
// t->prevVolume[0]
// t->prevVolume[1]
@@ -198,10 +277,11 @@ int AudioMixer::getTrackName(audio_channel_mask_t channelMask, int sessionId)
// no initialization needed
// t->prevAuxLevel
// t->frameCount
- t->channelCount = 2;
+ t->channelCount = audio_channel_count_from_out_mask(channelMask);
t->enabled = false;
- t->format = 16;
- t->channelMask = AUDIO_CHANNEL_OUT_STEREO;
+ 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)
t->bufferProvider = NULL;
@@ -215,21 +295,30 @@ int AudioMixer::getTrackName(audio_channel_mask_t channelMask, int sessionId)
// 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) {
- return TRACK0 + n;
+ if (status != OK) {
+ ALOGE("AudioMixer::getTrackName invalid channelMask (%#x)", channelMask);
+ return -1;
}
- ALOGE("AudioMixer::getTrackName(0x%x) failed, error preparing track for downmix",
- channelMask);
+ mTrackNames |= 1 << n;
+ return TRACK0 + n;
}
+ ALOGE("AudioMixer::getTrackName out of available tracks");
return -1;
}
void AudioMixer::invalidateState(uint32_t mask)
{
- if (mask) {
+ if (mask != 0) {
mState.needsChanged |= mask;
mState.hook = process__validate;
}
@@ -237,7 +326,7 @@ void AudioMixer::invalidateState(uint32_t mask)
status_t AudioMixer::initTrackDownmix(track_t* pTrack, int trackNum, audio_channel_mask_t mask)
{
- uint32_t channelCount = popcount(mask);
+ uint32_t channelCount = audio_channel_count_from_out_mask(mask);
ALOG_ASSERT((channelCount <= MAX_NUM_CHANNELS_TO_DOWNMIX) && channelCount);
status_t status = OK;
if (channelCount > MAX_NUM_CHANNELS) {
@@ -252,15 +341,15 @@ status_t AudioMixer::initTrackDownmix(track_t* pTrack, int trackNum, audio_chann
return status;
}
-void AudioMixer::unprepareTrackForDownmix(track_t* pTrack, int trackName) {
+void AudioMixer::unprepareTrackForDownmix(track_t* pTrack, int trackName __unused) {
ALOGV("AudioMixer::unprepareTrackForDownmix(%d)", trackName);
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");
}
@@ -276,13 +365,13 @@ status_t AudioMixer::prepareTrackForDownmix(track_t* pTrack, int trackName)
DownmixerBufferProvider* pDbp = new DownmixerBufferProvider();
int32_t status;
- if (!isMultichannelCapable) {
+ if (!sIsMultichannelCapable) {
ALOGE("prepareTrackForDownmix(%d) fails: mixer doesn't support multichannel content",
trackName);
goto noDownmixForActiveTrack;
}
- if (EffectCreate(&dwnmFxDesc.uuid,
+ 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);
@@ -354,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);
@@ -385,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);
}
@@ -415,6 +536,44 @@ void AudioMixer::disable(int name)
}
}
+/* Sets the volume ramp variables for the AudioMixer.
+ *
+ * The volume ramp variables are used to transition between the previous
+ * volume to the target volume. The duration of the transition is
+ * set by ramp, which is either 0 for immediate, or typically one state
+ * framecount period.
+ *
+ * @param newFloatValue new volume target in float [0.0, 1.0].
+ * @param ramp number of frames to increment over. ramp is 0 if the volume
+ * should be set immediately.
+ * @param volume reference to the U4.12 target volume, set on return.
+ * @param prevVolume reference to the U4.27 previous volume, set on return.
+ * @param volumeInc reference to the increment per output audio frame, set on return.
+ * @return true if the volume has changed, false if volume is same.
+ */
+static inline bool setVolumeRampVariables(float newFloatValue, int32_t ramp,
+ int16_t &volume, int32_t &prevVolume, int32_t &volumeInc) {
+ int32_t newValue = newFloatValue * AudioMixer::UNITY_GAIN_INT;
+ if (newValue > AudioMixer::UNITY_GAIN_INT) {
+ newValue = AudioMixer::UNITY_GAIN_INT;
+ } else if (newValue < 0) {
+ ALOGE("negative volume %.7g", newFloatValue);
+ newValue = 0; // should never happen, but for safety check.
+ }
+ if (newValue == volume) {
+ return false;
+ }
+ if (ramp != 0) {
+ volumeInc = ((newValue - volume) << 16) / ramp;
+ prevVolume = (volumeInc == 0 ? newValue : volume) << 16;
+ } else {
+ volumeInc = 0;
+ prevVolume = newValue << 16;
+ }
+ volume = newValue;
+ return true;
+}
+
void AudioMixer::setParameter(int name, int target, int param, void *value)
{
name -= TRACK0;
@@ -432,7 +591,7 @@ void AudioMixer::setParameter(int name, int target, int param, void *value)
audio_channel_mask_t mask =
static_cast<audio_channel_mask_t>(reinterpret_cast<uintptr_t>(value));
if (track.channelMask != mask) {
- uint32_t channelCount = popcount(mask);
+ uint32_t channelCount = audio_channel_count_from_out_mask(mask);
ALOG_ASSERT((channelCount <= MAX_NUM_CHANNELS_TO_DOWNMIX) && channelCount);
track.channelMask = mask;
track.channelCount = channelCount;
@@ -456,15 +615,33 @@ 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:
break */
+ case MIXER_FORMAT: {
+ audio_format_t format = static_cast<audio_format_t>(valueInt);
+ if (track.mMixerFormat != format) {
+ track.mMixerFormat = format;
+ ALOGV("setParameter(TRACK, MIXER_FORMAT, %#x)", format);
+ }
+ } break;
default:
- LOG_FATAL("bad param");
+ LOG_ALWAYS_FATAL("setParameter track: bad param %d", param);
}
break;
@@ -489,7 +666,7 @@ void AudioMixer::setParameter(int name, int target, int param, void *value)
invalidateState(1 << name);
break;
default:
- LOG_FATAL("bad param");
+ LOG_ALWAYS_FATAL("setParameter resample: bad param %d", param);
}
break;
@@ -498,51 +675,33 @@ void AudioMixer::setParameter(int name, int target, int param, void *value)
switch (param) {
case VOLUME0:
case VOLUME1:
- if (track.volume[param-VOLUME0] != valueInt) {
- ALOGV("setParameter(VOLUME, VOLUME0/1: %04x)", valueInt);
- track.prevVolume[param-VOLUME0] = track.volume[param-VOLUME0] << 16;
- track.volume[param-VOLUME0] = valueInt;
- if (target == VOLUME) {
- track.prevVolume[param-VOLUME0] = valueInt << 16;
- track.volumeInc[param-VOLUME0] = 0;
- } else {
- int32_t d = (valueInt<<16) - track.prevVolume[param-VOLUME0];
- int32_t volInc = d / int32_t(mState.frameCount);
- track.volumeInc[param-VOLUME0] = volInc;
- if (volInc == 0) {
- track.prevVolume[param-VOLUME0] = valueInt << 16;
- }
- }
+ if (setVolumeRampVariables(*reinterpret_cast<float*>(value),
+ target == RAMP_VOLUME ? mState.frameCount : 0,
+ track.volume[param - VOLUME0], track.prevVolume[param - VOLUME0],
+ track.volumeInc[param - VOLUME0])) {
+ ALOGV("setParameter(%s, VOLUME%d: %04x)",
+ target == VOLUME ? "VOLUME" : "RAMP_VOLUME", param - VOLUME0,
+ track.volume[param - VOLUME0]);
invalidateState(1 << name);
}
break;
case AUXLEVEL:
//ALOG_ASSERT(0 <= valueInt && valueInt <= MAX_GAIN_INT, "bad aux level %d", valueInt);
- if (track.auxLevel != valueInt) {
- ALOGV("setParameter(VOLUME, AUXLEVEL: %04x)", valueInt);
- track.prevAuxLevel = track.auxLevel << 16;
- track.auxLevel = valueInt;
- if (target == VOLUME) {
- track.prevAuxLevel = valueInt << 16;
- track.auxInc = 0;
- } else {
- int32_t d = (valueInt<<16) - track.prevAuxLevel;
- int32_t volInc = d / int32_t(mState.frameCount);
- track.auxInc = volInc;
- if (volInc == 0) {
- track.prevAuxLevel = valueInt << 16;
- }
- }
+ if (setVolumeRampVariables(*reinterpret_cast<float*>(value),
+ target == RAMP_VOLUME ? mState.frameCount : 0,
+ track.auxLevel, track.prevAuxLevel, track.auxInc)) {
+ ALOGV("setParameter(%s, AUXLEVEL: %04x)",
+ target == VOLUME ? "VOLUME" : "RAMP_VOLUME", track.auxLevel);
invalidateState(1 << name);
}
break;
default:
- LOG_FATAL("bad param");
+ LOG_ALWAYS_FATAL("setParameter volume: bad param %d", param);
}
break;
default:
- LOG_FATAL("bad target");
+ LOG_ALWAYS_FATAL("setParameter: bad target %d", target);
}
}
@@ -560,14 +719,15 @@ bool AudioMixer::track_t::setResampler(uint32_t value, uint32_t devSampleRate)
// Should have a way to distinguish tracks with static ratios vs. dynamic ratios.
if (!((value == 44100 && devSampleRate == 48000) ||
(value == 48000 && devSampleRate == 44100))) {
- quality = AudioResampler::LOW_QUALITY;
+ quality = AudioResampler::DYN_LOW_QUALITY;
} 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
- downmixerBufferProvider != NULL ? MAX_NUM_CHANNELS : channelCount,
+ (int) (downmixerBufferProvider != NULL ? MAX_NUM_CHANNELS : channelCount),
devSampleRate, quality);
resampler->setLocalTimeFreq(sLocalTimeFreq);
}
@@ -610,21 +770,16 @@ 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].mInputBufferProvider == bufferProvider) {
+ return; // don't reset any buffer providers if identical.
+ }
+ 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]);
}
@@ -668,27 +823,29 @@ void AudioMixer::process__validate(state_t* state, int64_t pts)
countActiveTracks++;
track_t& t = state->tracks[i];
uint32_t n = 0;
+ // FIXME can overflow (mask is only 3 bits)
n |= NEEDS_CHANNEL_1 + t.channelCount - 1;
- n |= NEEDS_FORMAT_16;
- n |= t.doesResample() ? NEEDS_RESAMPLE_ENABLED : NEEDS_RESAMPLE_DISABLED;
+ if (t.doesResample()) {
+ n |= NEEDS_RESAMPLE;
+ }
if (t.auxLevel != 0 && t.auxBuffer != NULL) {
- n |= NEEDS_AUX_ENABLED;
+ n |= NEEDS_AUX;
}
if (t.volumeInc[0]|t.volumeInc[1]) {
volumeRamp = true;
} else if (!t.doesResample() && t.volumeRL == 0) {
- n |= NEEDS_MUTE_ENABLED;
+ n |= NEEDS_MUTE;
}
t.needs = n;
- if ((n & NEEDS_MUTE__MASK) == NEEDS_MUTE_ENABLED) {
+ if (n & NEEDS_MUTE) {
t.hook = track__nop;
} else {
- if ((n & NEEDS_AUX__MASK) == NEEDS_AUX_ENABLED) {
+ if (n & NEEDS_AUX) {
all16BitsStereoNoResample = false;
}
- if ((n & NEEDS_RESAMPLE__MASK) == NEEDS_RESAMPLE_ENABLED) {
+ if (n & NEEDS_RESAMPLE) {
all16BitsStereoNoResample = false;
resampling = true;
t.hook = track__genericResample;
@@ -710,7 +867,7 @@ void AudioMixer::process__validate(state_t* state, int64_t pts)
// select the processing hooks
state->hook = process__nop;
- if (countActiveTracks) {
+ if (countActiveTracks > 0) {
if (resampling) {
if (!state->outputTemp) {
state->outputTemp = new int32_t[MAX_NUM_CHANNELS * state->frameCount];
@@ -746,16 +903,15 @@ void AudioMixer::process__validate(state_t* state, int64_t pts)
// Now that the volume ramp has been done, set optimal state and
// track hooks for subsequent mixer process
- if (countActiveTracks) {
+ if (countActiveTracks > 0) {
bool allMuted = true;
uint32_t en = state->enabledTracks;
while (en) {
const int i = 31 - __builtin_clz(en);
en &= ~(1<<i);
track_t& t = state->tracks[i];
- if (!t.doesResample() && t.volumeRL == 0)
- {
- t.needs |= NEEDS_MUTE_ENABLED;
+ if (!t.doesResample() && t.volumeRL == 0) {
+ t.needs |= NEEDS_MUTE;
t.hook = track__nop;
} else {
allMuted = false;
@@ -782,7 +938,7 @@ void AudioMixer::track__genericResample(track_t* t, int32_t* out, size_t outFram
// always resample with unity gain when sending to auxiliary buffer to be able
// to apply send level after resampling
// TODO: modify each resampler to support aux channel?
- t->resampler->setVolume(UNITY_GAIN, UNITY_GAIN);
+ t->resampler->setVolume(UNITY_GAIN_INT, UNITY_GAIN_INT);
memset(temp, 0, outFrameCount * MAX_NUM_CHANNELS * sizeof(int32_t));
t->resampler->resample(temp, outFrameCount, t->bufferProvider);
if (CC_UNLIKELY(t->volumeInc[0]|t->volumeInc[1]|t->auxInc)) {
@@ -792,7 +948,7 @@ void AudioMixer::track__genericResample(track_t* t, int32_t* out, size_t outFram
}
} else {
if (CC_UNLIKELY(t->volumeInc[0]|t->volumeInc[1])) {
- t->resampler->setVolume(UNITY_GAIN, UNITY_GAIN);
+ t->resampler->setVolume(UNITY_GAIN_INT, UNITY_GAIN_INT);
memset(temp, 0, outFrameCount * MAX_NUM_CHANNELS * sizeof(int32_t));
t->resampler->resample(temp, outFrameCount, t->bufferProvider);
volumeRampStereo(t, out, outFrameCount, temp, aux);
@@ -806,8 +962,8 @@ void AudioMixer::track__genericResample(track_t* t, int32_t* out, size_t outFram
}
}
-void AudioMixer::track__nop(track_t* t, int32_t* out, size_t outFrameCount, int32_t* temp,
- int32_t* aux)
+void AudioMixer::track__nop(track_t* t __unused, int32_t* out __unused,
+ size_t outFrameCount __unused, int32_t* temp __unused, int32_t* aux __unused)
{
}
@@ -883,8 +1039,8 @@ void AudioMixer::volumeStereo(track_t* t, int32_t* out, size_t frameCount, int32
}
}
-void AudioMixer::track__16BitsStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp,
- int32_t* aux)
+void AudioMixer::track__16BitsStereo(track_t* t, int32_t* out, size_t frameCount,
+ int32_t* temp __unused, int32_t* aux)
{
const int16_t *in = static_cast<const int16_t *>(t->in);
@@ -974,8 +1130,8 @@ void AudioMixer::track__16BitsStereo(track_t* t, int32_t* out, size_t frameCount
t->in = in;
}
-void AudioMixer::track__16BitsMono(track_t* t, int32_t* out, size_t frameCount, int32_t* temp,
- int32_t* aux)
+void AudioMixer::track__16BitsMono(track_t* t, int32_t* out, size_t frameCount,
+ int32_t* temp __unused, int32_t* aux)
{
const int16_t *in = static_cast<int16_t const *>(t->in);
@@ -1065,7 +1221,7 @@ void AudioMixer::track__16BitsMono(track_t* t, int32_t* out, size_t frameCount,
void AudioMixer::process__nop(state_t* state, int64_t pts)
{
uint32_t e0 = state->enabledTracks;
- size_t bufSize = state->frameCount * sizeof(int16_t) * MAX_NUM_CHANNELS;
+ size_t sampleCount = state->frameCount * MAX_NUM_CHANNELS;
while (e0) {
// process by group of tracks with same output buffer to
// avoid multiple memset() on same buffer
@@ -1084,7 +1240,8 @@ void AudioMixer::process__nop(state_t* state, int64_t pts)
}
e0 &= ~(e1);
- memset(t1.mainBuffer, 0, bufSize);
+ memset(t1.mainBuffer, 0, sampleCount
+ * audio_bytes_per_sample(t1.mMixerFormat));
}
while (e1) {
@@ -1154,7 +1311,7 @@ void AudioMixer::process__genericNoResampling(state_t* state, int64_t pts)
track_t& t = state->tracks[i];
size_t outFrames = BLOCKSIZE;
int32_t *aux = NULL;
- if (CC_UNLIKELY((t.needs & NEEDS_AUX__MASK) == NEEDS_AUX_ENABLED)) {
+ if (CC_UNLIKELY(t.needs & NEEDS_AUX)) {
aux = t.auxBuffer + numFrames;
}
while (outFrames) {
@@ -1166,7 +1323,7 @@ void AudioMixer::process__genericNoResampling(state_t* state, int64_t pts)
break;
}
size_t inFrames = (t.frameCount > outFrames)?outFrames:t.frameCount;
- if (inFrames) {
+ if (inFrames > 0) {
t.hook(&t, outTemp + (BLOCKSIZE-outFrames)*MAX_NUM_CHANNELS, inFrames,
state->resampleTemp, aux);
t.frameCount -= inFrames;
@@ -1192,8 +1349,18 @@ void AudioMixer::process__genericNoResampling(state_t* state, int64_t pts)
}
}
}
- ditherAndClamp(out, outTemp, BLOCKSIZE);
- out += BLOCKSIZE;
+ switch (t1.mMixerFormat) {
+ case AUDIO_FORMAT_PCM_FLOAT:
+ memcpy_to_float_from_q4_27(reinterpret_cast<float *>(out), outTemp, BLOCKSIZE * 2);
+ out += BLOCKSIZE * 2; // output is 2 floats/frame.
+ break;
+ case AUDIO_FORMAT_PCM_16_BIT:
+ ditherAndClamp(out, outTemp, BLOCKSIZE);
+ out += BLOCKSIZE; // output is 1 int32_t (2 int16_t samples)/frame
+ break;
+ default:
+ LOG_ALWAYS_FATAL("bad mixer format: %d", t1.mMixerFormat);
+ }
numFrames += BLOCKSIZE;
} while (numFrames < state->frameCount);
}
@@ -1242,14 +1409,14 @@ void AudioMixer::process__genericResampling(state_t* state, int64_t pts)
e1 &= ~(1<<i);
track_t& t = state->tracks[i];
int32_t *aux = NULL;
- if (CC_UNLIKELY((t.needs & NEEDS_AUX__MASK) == NEEDS_AUX_ENABLED)) {
+ if (CC_UNLIKELY(t.needs & NEEDS_AUX)) {
aux = t.auxBuffer;
}
// this is a little goofy, on the resampling case we don't
// acquire/release the buffers because it's done by
// the resampler.
- if ((t.needs & NEEDS_RESAMPLE__MASK) == NEEDS_RESAMPLE_ENABLED) {
+ if (t.needs & NEEDS_RESAMPLE) {
t.resampler->setPTS(pts);
t.hook(&t, outTemp, numFrames, state->resampleTemp, aux);
} else {
@@ -1275,7 +1442,16 @@ void AudioMixer::process__genericResampling(state_t* state, int64_t pts)
}
}
}
- ditherAndClamp(out, outTemp, numFrames);
+ switch (t1.mMixerFormat) {
+ case AUDIO_FORMAT_PCM_FLOAT:
+ memcpy_to_float_from_q4_27(reinterpret_cast<float*>(out), outTemp, numFrames*2);
+ break;
+ case AUDIO_FORMAT_PCM_16_BIT:
+ ditherAndClamp(out, outTemp, numFrames);
+ break;
+ default:
+ LOG_ALWAYS_FATAL("bad mixer format: %d", t1.mMixerFormat);
+ }
}
}
@@ -1294,6 +1470,7 @@ void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state,
AudioBufferProvider::Buffer& b(t.buffer);
int32_t* out = t.mainBuffer;
+ float *fout = reinterpret_cast<float*>(out);
size_t numFrames = state->frameCount;
const int16_t vl = t.volume[0];
@@ -1307,36 +1484,55 @@ void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state,
// in == NULL can happen if the track was flushed just after having
// been enabled for mixing.
- if (in == NULL || ((unsigned long)in & 3)) {
- memset(out, 0, numFrames*MAX_NUM_CHANNELS*sizeof(int16_t));
- ALOGE_IF(((unsigned long)in & 3), "process stereo track: input buffer alignment pb: "
+ if (in == NULL || (((uintptr_t)in) & 3)) {
+ memset(out, 0, numFrames
+ * MAX_NUM_CHANNELS * audio_bytes_per_sample(t.mMixerFormat));
+ ALOGE_IF((((uintptr_t)in) & 3), "process stereo track: input buffer alignment pb: "
"buffer %p track %d, channels %d, needs %08x",
in, i, t.channelCount, t.needs);
return;
}
size_t outFrames = b.frameCount;
- if (CC_UNLIKELY(uint32_t(vl) > UNITY_GAIN || uint32_t(vr) > UNITY_GAIN)) {
- // volume is boosted, so we might need to clamp even though
- // we process only one track.
- do {
- uint32_t rl = *reinterpret_cast<const uint32_t *>(in);
- in += 2;
- int32_t l = mulRL(1, rl, vrl) >> 12;
- int32_t r = mulRL(0, rl, vrl) >> 12;
- // clamping...
- l = clamp16(l);
- r = clamp16(r);
- *out++ = (r<<16) | (l & 0xFFFF);
- } while (--outFrames);
- } else {
+ switch (t.mMixerFormat) {
+ case AUDIO_FORMAT_PCM_FLOAT:
do {
uint32_t rl = *reinterpret_cast<const uint32_t *>(in);
in += 2;
- int32_t l = mulRL(1, rl, vrl) >> 12;
- int32_t r = mulRL(0, rl, vrl) >> 12;
- *out++ = (r<<16) | (l & 0xFFFF);
+ int32_t l = mulRL(1, rl, vrl);
+ int32_t r = mulRL(0, rl, vrl);
+ *fout++ = float_from_q4_27(l);
+ *fout++ = float_from_q4_27(r);
+ // Note: In case of later int16_t sink output,
+ // conversion and clamping is done by memcpy_to_i16_from_float().
} while (--outFrames);
+ break;
+ case AUDIO_FORMAT_PCM_16_BIT:
+ if (CC_UNLIKELY(uint32_t(vl) > UNITY_GAIN_INT || uint32_t(vr) > UNITY_GAIN_INT)) {
+ // volume is boosted, so we might need to clamp even though
+ // we process only one track.
+ do {
+ uint32_t rl = *reinterpret_cast<const uint32_t *>(in);
+ in += 2;
+ int32_t l = mulRL(1, rl, vrl) >> 12;
+ int32_t r = mulRL(0, rl, vrl) >> 12;
+ // clamping...
+ l = clamp16(l);
+ r = clamp16(r);
+ *out++ = (r<<16) | (l & 0xFFFF);
+ } while (--outFrames);
+ } else {
+ do {
+ uint32_t rl = *reinterpret_cast<const uint32_t *>(in);
+ in += 2;
+ int32_t l = mulRL(1, rl, vrl) >> 12;
+ int32_t r = mulRL(0, rl, vrl) >> 12;
+ *out++ = (r<<16) | (l & 0xFFFF);
+ } while (--outFrames);
+ }
+ break;
+ default:
+ LOG_ALWAYS_FATAL("bad mixer format: %d", t.mMixerFormat);
}
numFrames -= b.frameCount;
t.bufferProvider->releaseBuffer(&b);
@@ -1449,8 +1645,9 @@ void AudioMixer::process__TwoTracks16BitsStereoNoResampling(state_t* state,
int64_t AudioMixer::calculateOutputPTS(const track_t& t, int64_t basePTS,
int outputFrameIndex)
{
- if (AudioBufferProvider::kInvalidPTS == basePTS)
+ if (AudioBufferProvider::kInvalidPTS == basePTS) {
return AudioBufferProvider::kInvalidPTS;
+ }
return basePTS + ((outputFrameIndex * sLocalTimeFreq) / t.sampleRate);
}
@@ -1462,6 +1659,28 @@ int64_t AudioMixer::calculateOutputPTS(const track_t& t, int64_t basePTS,
{
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);
+
+ 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");
}
// ----------------------------------------------------------------------------