summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--services/audioflinger/AudioMixer.cpp429
-rw-r--r--services/audioflinger/AudioMixer.h45
-rw-r--r--services/audioflinger/AudioMixerOps.h81
-rwxr-xr-xservices/audioflinger/tests/mixer_to_wav_tests.sh4
-rw-r--r--services/audioflinger/tests/test-mixer.cpp32
-rw-r--r--services/audioflinger/tests/test_utils.h4
6 files changed, 420 insertions, 175 deletions
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
index 529f2af..e777de5 100644
--- a/services/audioflinger/AudioMixer.cpp
+++ b/services/audioflinger/AudioMixer.cpp
@@ -44,12 +44,14 @@
#include "AudioMixerOps.h"
#include "AudioMixer.h"
-// Use the FCC_2 macro for code assuming Fixed Channel Count of 2 and
-// whose stereo assumption may need to be revisited later.
+// The FCC_2 macro refers to the Fixed Channel Count of 2 for the legacy integer mixer.
#ifndef FCC_2
#define FCC_2 2
#endif
+// Look for MONO_HACK for any Mono hack involving legacy mono channel to
+// stereo channel conversion.
+
/* VERY_VERY_VERBOSE_LOGGING will show exactly which process hook and track hook is
* being used. This is a considerable amount of log spam, so don't enable unless you
* are verifying the hook based code.
@@ -99,7 +101,7 @@ AudioMixer::CopyBufferProvider::CopyBufferProvider(size_t inputFrameSize,
ALOGV("CopyBufferProvider(%p)(%zu, %zu, %zu)", this,
inputFrameSize, outputFrameSize, bufferFrameCount);
LOG_ALWAYS_FATAL_IF(inputFrameSize < outputFrameSize && bufferFrameCount == 0,
- "Requires local buffer if inputFrameSize(%d) < outputFrameSize(%d)",
+ "Requires local buffer if inputFrameSize(%zu) < outputFrameSize(%zu)",
inputFrameSize, outputFrameSize);
if (mLocalBufferFrameCount) {
(void)posix_memalign(&mLocalBufferData, 32, mLocalBufferFrameCount * mOutputFrameSize);
@@ -335,7 +337,7 @@ AudioMixer::RemixBufferProvider::RemixBufferProvider(audio_channel_mask_t inputC
mInputChannels(audio_channel_count_from_out_mask(inputChannelMask)),
mOutputChannels(audio_channel_count_from_out_mask(outputChannelMask))
{
- ALOGV("RemixBufferProvider(%p)(%#x, %#x, %#x) %d %d",
+ ALOGV("RemixBufferProvider(%p)(%#x, %#x, %#x) %zu %zu",
this, format, inputChannelMask, outputChannelMask,
mInputChannels, mOutputChannels);
// TODO: consider channel representation in index array formulation
@@ -379,18 +381,12 @@ AudioMixer::AudioMixer(size_t frameCount, uint32_t sampleRate, uint32_t maxNumTr
: mTrackNames(0), mConfiguredNames((maxNumTracks >= 32 ? 0 : 1 << maxNumTracks) - 1),
mSampleRate(sampleRate)
{
- // AudioMixer is not yet capable of multi-channel beyond stereo
- COMPILE_TIME_ASSERT_FUNCTION_SCOPE(2 == MAX_NUM_CHANNELS);
-
ALOG_ASSERT(maxNumTracks <= MAX_NUM_TRACKS, "maxNumTracks %u > MAX_NUM_TRACKS %u",
maxNumTracks, MAX_NUM_TRACKS);
// AudioMixer is not yet capable of more than 32 active track inputs
ALOG_ASSERT(32 >= MAX_NUM_TRACKS, "bad MAX_NUM_TRACKS %d", MAX_NUM_TRACKS);
- // 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);
-
pthread_once(&sOnceControl, &sInitRoutine);
mState.enabledTracks= 0;
@@ -476,7 +472,7 @@ int AudioMixer::getTrackName(audio_channel_mask_t channelMask,
// t->frameCount
t->channelCount = audio_channel_count_from_out_mask(channelMask);
t->enabled = false;
- ALOGV_IF(channelMask != AUDIO_CHANNEL_OUT_STEREO,
+ ALOGV_IF(audio_channel_mask_get_bits(channelMask) != AUDIO_CHANNEL_OUT_STEREO,
"Non-stereo channel mask: %d\n", channelMask);
t->channelMask = channelMask;
t->sessionId = sessionId;
@@ -499,8 +495,11 @@ int AudioMixer::getTrackName(audio_channel_mask_t channelMask,
t->mFormat = format;
t->mMixerInFormat = kUseFloat && kUseNewMixer
? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT;
+ t->mMixerChannelMask = audio_channel_mask_from_representation_and_bits(
+ AUDIO_CHANNEL_REPRESENTATION_POSITION, AUDIO_CHANNEL_OUT_STEREO);
+ t->mMixerChannelCount = audio_channel_count_from_out_mask(t->mMixerChannelMask);
// Check the downmixing (or upmixing) requirements.
- status_t status = initTrackDownmix(t, n, channelMask);
+ status_t status = initTrackDownmix(t, n);
if (status != OK) {
ALOGE("AudioMixer::getTrackName invalid channelMask (%#x)", channelMask);
return -1;
@@ -525,21 +524,69 @@ void AudioMixer::invalidateState(uint32_t mask)
}
}
-status_t AudioMixer::initTrackDownmix(track_t* pTrack, int trackNum, audio_channel_mask_t mask)
+// Called when channel masks have changed for a track name
+// TODO: Fix Downmixbufferprofider not to (possibly) change mixer input format,
+// which will simplify this logic.
+bool AudioMixer::setChannelMasks(int name,
+ audio_channel_mask_t trackChannelMask, audio_channel_mask_t mixerChannelMask) {
+ track_t &track = mState.tracks[name];
+
+ if (trackChannelMask == track.channelMask
+ && mixerChannelMask == track.mMixerChannelMask) {
+ return false; // no need to change
+ }
+ // always recompute for both channel masks even if only one has changed.
+ const uint32_t trackChannelCount = audio_channel_count_from_out_mask(trackChannelMask);
+ const uint32_t mixerChannelCount = audio_channel_count_from_out_mask(mixerChannelMask);
+ const bool mixerChannelCountChanged = track.mMixerChannelCount != mixerChannelCount;
+
+ ALOG_ASSERT((trackChannelCount <= MAX_NUM_CHANNELS_TO_DOWNMIX)
+ && trackChannelCount
+ && mixerChannelCount);
+ track.channelMask = trackChannelMask;
+ track.channelCount = trackChannelCount;
+ track.mMixerChannelMask = mixerChannelMask;
+ track.mMixerChannelCount = mixerChannelCount;
+
+ // channel masks have changed, does this track need a downmixer?
+ // update to try using our desired format (if we aren't already using it)
+ const audio_format_t prevMixerInFormat = track.mMixerInFormat;
+ track.mMixerInFormat = kUseFloat && kUseNewMixer
+ ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT;
+ const status_t status = initTrackDownmix(&mState.tracks[name], name);
+ ALOGE_IF(status != OK,
+ "initTrackDownmix error %d, track channel mask %#x, mixer channel mask %#x",
+ status, track.channelMask, track.mMixerChannelMask);
+
+ const bool mixerInFormatChanged = prevMixerInFormat != track.mMixerInFormat;
+ if (mixerInFormatChanged) {
+ prepareTrackForReformat(&track, name); // because of downmixer, track format may change!
+ }
+
+ if (track.resampler && (mixerInFormatChanged || mixerChannelCountChanged)) {
+ // resampler input format or channels may have changed.
+ const uint32_t resetToSampleRate = track.sampleRate;
+ delete track.resampler;
+ track.resampler = NULL;
+ track.sampleRate = mSampleRate; // without resampler, track rate is device sample rate.
+ // recreate the resampler with updated format, channels, saved sampleRate.
+ track.setResampler(resetToSampleRate /*trackSampleRate*/, mSampleRate /*devSampleRate*/);
+ }
+ return true;
+}
+
+status_t AudioMixer::initTrackDownmix(track_t* pTrack, int trackName)
{
- 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) {
- pTrack->channelMask = mask;
- pTrack->channelCount = channelCount;
- ALOGV("initTrackDownmix(track=%d, mask=0x%x) calls prepareTrackForDownmix()",
- trackNum, mask);
- status = prepareTrackForDownmix(pTrack, trackNum);
- } else {
- unprepareTrackForDownmix(pTrack, trackNum);
+ // Only remix (upmix or downmix) if the track and mixer/device channel masks
+ // are not the same and not handled internally, as mono -> stereo currently is.
+ if (pTrack->channelMask != pTrack->mMixerChannelMask
+ && !(pTrack->channelMask == AUDIO_CHANNEL_OUT_MONO
+ && pTrack->mMixerChannelMask == AUDIO_CHANNEL_OUT_STEREO)) {
+ return prepareTrackForDownmix(pTrack, trackName);
}
- return status;
+ // no remix necessary
+ unprepareTrackForDownmix(pTrack, trackName);
+ return NO_ERROR;
}
void AudioMixer::unprepareTrackForDownmix(track_t* pTrack, int trackName __unused) {
@@ -564,8 +611,8 @@ status_t AudioMixer::prepareTrackForDownmix(track_t* pTrack, int trackName)
unprepareTrackForDownmix(pTrack, trackName);
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->mMixerChannelMask,
+ AUDIO_FORMAT_PCM_16_BIT /* TODO: use pTrack->mMixerInFormat, now only PCM 16 */,
pTrack->sampleRate, pTrack->sessionId, kCopyBufferFrameCount);
if (pDbp->isValid()) { // if constructor completed properly
@@ -576,9 +623,14 @@ status_t AudioMixer::prepareTrackForDownmix(track_t* pTrack, int trackName)
}
delete pDbp;
}
- pTrack->downmixerBufferProvider = NULL;
+
+ // Effect downmixer does not accept the channel conversion. Let's use our remixer.
+ RemixBufferProvider* pRbp = new RemixBufferProvider(pTrack->channelMask,
+ pTrack->mMixerChannelMask, pTrack->mMixerInFormat, kCopyBufferFrameCount);
+ // Remix always finds a conversion whereas Downmixer effect above may fail.
+ pTrack->downmixerBufferProvider = pRbp;
reconfigureBufferProviders(pTrack);
- return NO_INIT;
+ return NO_ERROR;
}
void AudioMixer::unprepareTrackForReformat(track_t* pTrack, int trackName __unused) {
@@ -748,23 +800,10 @@ void AudioMixer::setParameter(int name, int target, int param, void *value)
case TRACK:
switch (param) {
case CHANNEL_MASK: {
- audio_channel_mask_t mask =
- static_cast<audio_channel_mask_t>(reinterpret_cast<uintptr_t>(value));
- if (track.channelMask != 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;
- // the mask has changed, does this track need a downmixer?
- // update to try using our desired format (if we aren't already using it)
- track.mMixerInFormat = kUseFloat && kUseNewMixer
- ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT;
- status_t status = initTrackDownmix(&mState.tracks[name], name, mask);
- ALOGE_IF(status != OK,
- "Invalid channel mask %#x, initTrackDownmix returned %d",
- mask, status);
- ALOGV("setParameter(TRACK, CHANNEL_MASK, %x)", mask);
- prepareTrackForReformat(&track, name); // format may have changed
+ const audio_channel_mask_t trackChannelMask =
+ static_cast<audio_channel_mask_t>(valueInt);
+ if (setChannelMasks(name, trackChannelMask, track.mMixerChannelMask)) {
+ ALOGV("setParameter(TRACK, CHANNEL_MASK, %x)", trackChannelMask);
invalidateState(1 << name);
}
} break;
@@ -803,6 +842,14 @@ void AudioMixer::setParameter(int name, int target, int param, void *value)
ALOGV("setParameter(TRACK, MIXER_FORMAT, %#x)", format);
}
} break;
+ case MIXER_CHANNEL_MASK: {
+ const audio_channel_mask_t mixerChannelMask =
+ static_cast<audio_channel_mask_t>(valueInt);
+ if (setChannelMasks(name, track.channelMask, mixerChannelMask)) {
+ ALOGV("setParameter(TRACK, MIXER_CHANNEL_MASK, %#x)", mixerChannelMask);
+ invalidateState(1 << name);
+ }
+ } break;
default:
LOG_ALWAYS_FATAL("setParameter track: bad param %d", param);
}
@@ -836,20 +883,6 @@ void AudioMixer::setParameter(int name, int target, int param, void *value)
case RAMP_VOLUME:
case VOLUME:
switch (param) {
- case VOLUME0:
- case VOLUME1:
- if (setVolumeRampVariables(*reinterpret_cast<float*>(value),
- target == RAMP_VOLUME ? mState.frameCount : 0,
- &track.volume[param - VOLUME0], &track.prevVolume[param - VOLUME0],
- &track.volumeInc[param - VOLUME0],
- &track.mVolume[param - VOLUME0], &track.mPrevVolume[param - VOLUME0],
- &track.mVolumeInc[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:
if (setVolumeRampVariables(*reinterpret_cast<float*>(value),
target == RAMP_VOLUME ? mState.frameCount : 0,
@@ -861,7 +894,21 @@ void AudioMixer::setParameter(int name, int target, int param, void *value)
}
break;
default:
- LOG_ALWAYS_FATAL("setParameter volume: bad param %d", param);
+ if ((unsigned)param >= VOLUME0 && (unsigned)param < VOLUME0 + MAX_NUM_VOLUMES) {
+ if (setVolumeRampVariables(*reinterpret_cast<float*>(value),
+ target == RAMP_VOLUME ? mState.frameCount : 0,
+ &track.volume[param - VOLUME0], &track.prevVolume[param - VOLUME0],
+ &track.volumeInc[param - VOLUME0],
+ &track.mVolume[param - VOLUME0], &track.mPrevVolume[param - VOLUME0],
+ &track.mVolumeInc[param - VOLUME0])) {
+ ALOGV("setParameter(%s, VOLUME%d: %04x)",
+ target == VOLUME ? "VOLUME" : "RAMP_VOLUME", param - VOLUME0,
+ track.volume[param - VOLUME0]);
+ invalidateState(1 << name);
+ }
+ } else {
+ LOG_ALWAYS_FATAL("setParameter volume: bad param %d", param);
+ }
}
break;
@@ -870,30 +917,34 @@ void AudioMixer::setParameter(int name, int target, int param, void *value)
}
}
-bool AudioMixer::track_t::setResampler(uint32_t value, uint32_t devSampleRate)
+bool AudioMixer::track_t::setResampler(uint32_t trackSampleRate, uint32_t devSampleRate)
{
- if (value != devSampleRate || resampler != NULL) {
- if (sampleRate != value) {
- sampleRate = value;
+ if (trackSampleRate != devSampleRate || resampler != NULL) {
+ if (sampleRate != trackSampleRate) {
+ sampleRate = trackSampleRate;
if (resampler == NULL) {
- ALOGV("creating resampler from track %d Hz to device %d Hz", value, devSampleRate);
+ ALOGV("Creating resampler from track %d Hz to device %d Hz",
+ trackSampleRate, devSampleRate);
AudioResampler::src_quality quality;
// force lowest quality level resampler if use case isn't music or video
// FIXME this is flawed for dynamic sample rates, as we choose the resampler
// quality level based on the initial ratio, but that could change later.
// Should have a way to distinguish tracks with static ratios vs. dynamic ratios.
- if (!((value == 44100 && devSampleRate == 48000) ||
- (value == 48000 && devSampleRate == 44100))) {
+ if (!((trackSampleRate == 44100 && devSampleRate == 48000) ||
+ (trackSampleRate == 48000 && devSampleRate == 44100))) {
quality = AudioResampler::DYN_LOW_QUALITY;
} else {
quality = AudioResampler::DEFAULT_QUALITY;
}
- ALOGVV("Creating resampler with %d bits\n", bits);
+ // TODO: Remove MONO_HACK. Resampler sees #channels after the downmixer
+ // but if none exists, it is the channel count (1 for mono).
+ const int resamplerChannelCount = downmixerBufferProvider != NULL
+ ? mMixerChannelCount : channelCount;
+ ALOGVV("Creating resampler with %#x format\n", mMixerInFormat);
resampler = AudioResampler::create(
mMixerInFormat,
- // the resampler sees the number of channels after the downmixer, if any
- (int) (downmixerBufferProvider != NULL ? MAX_NUM_CHANNELS : channelCount),
+ resamplerChannelCount,
devSampleRate, quality);
resampler->setLocalTimeFreq(sLocalTimeFreq);
}
@@ -919,20 +970,19 @@ bool AudioMixer::track_t::setResampler(uint32_t value, uint32_t devSampleRate)
inline void AudioMixer::track_t::adjustVolumeRamp(bool aux, bool useFloat)
{
if (useFloat) {
- for (uint32_t i=0 ; i<MAX_NUM_CHANNELS ; i++) {
+ for (uint32_t i = 0; i < MAX_NUM_VOLUMES; i++) {
if (mVolumeInc[i] != 0 && fabs(mVolume[i] - mPrevVolume[i]) <= fabs(mVolumeInc[i])) {
volumeInc[i] = 0;
prevVolume[i] = volume[i] << 16;
mVolumeInc[i] = 0.;
mPrevVolume[i] = mVolume[i];
-
} else {
//ALOGV("ramp: %f %f %f", mVolume[i], mPrevVolume[i], mVolumeInc[i]);
prevVolume[i] = u4_28_from_float(mPrevVolume[i]);
}
}
} else {
- for (uint32_t i=0 ; i<MAX_NUM_CHANNELS ; i++) {
+ for (uint32_t i = 0; i < MAX_NUM_VOLUMES; i++) {
if (((volumeInc[i]>0) && (((prevVolume[i]+volumeInc[i])>>16) >= volume[i])) ||
((volumeInc[i]<0) && (((prevVolume[i]+volumeInc[i])>>16) <= volume[i]))) {
volumeInc[i] = 0;
@@ -1051,18 +1101,21 @@ void AudioMixer::process__validate(state_t* state, int64_t pts)
if (n & NEEDS_RESAMPLE) {
all16BitsStereoNoResample = false;
resampling = true;
- t.hook = getTrackHook(TRACKTYPE_RESAMPLE, FCC_2,
+ t.hook = getTrackHook(TRACKTYPE_RESAMPLE, t.mMixerChannelCount,
t.mMixerInFormat, t.mMixerFormat);
ALOGV_IF((n & NEEDS_CHANNEL_COUNT__MASK) > NEEDS_CHANNEL_2,
"Track %d needs downmix + resample", i);
} else {
if ((n & NEEDS_CHANNEL_COUNT__MASK) == NEEDS_CHANNEL_1){
- t.hook = getTrackHook(TRACKTYPE_NORESAMPLEMONO, FCC_2,
+ t.hook = getTrackHook(
+ t.mMixerChannelCount == 2 // TODO: MONO_HACK.
+ ? TRACKTYPE_NORESAMPLEMONO : TRACKTYPE_NORESAMPLE,
+ t.mMixerChannelCount,
t.mMixerInFormat, t.mMixerFormat);
all16BitsStereoNoResample = false;
}
if ((n & NEEDS_CHANNEL_COUNT__MASK) >= NEEDS_CHANNEL_2){
- t.hook = getTrackHook(TRACKTYPE_NORESAMPLE, FCC_2,
+ t.hook = getTrackHook(TRACKTYPE_NORESAMPLE, t.mMixerChannelCount,
t.mMixerInFormat, t.mMixerFormat);
ALOGV_IF((n & NEEDS_CHANNEL_COUNT__MASK) > NEEDS_CHANNEL_2,
"Track %d needs downmix", i);
@@ -1096,8 +1149,8 @@ void AudioMixer::process__validate(state_t* state, int64_t pts)
if (countActiveTracks == 1) {
const int i = 31 - __builtin_clz(state->enabledTracks);
track_t& t = state->tracks[i];
- state->hook = getProcessHook(PROCESSTYPE_NORESAMPLEONETRACK, FCC_2,
- t.mMixerInFormat, t.mMixerFormat);
+ state->hook = getProcessHook(PROCESSTYPE_NORESAMPLEONETRACK,
+ t.mMixerChannelCount, t.mMixerInFormat, t.mMixerFormat);
}
}
}
@@ -1130,7 +1183,10 @@ void AudioMixer::process__validate(state_t* state, int64_t pts)
state->hook = process__nop;
} else if (all16BitsStereoNoResample) {
if (countActiveTracks == 1) {
- state->hook = process__OneTrack16BitsStereoNoResampling;
+ const int i = 31 - __builtin_clz(state->enabledTracks);
+ track_t& t = state->tracks[i];
+ state->hook = getProcessHook(PROCESSTYPE_NORESAMPLEONETRACK,
+ t.mMixerChannelCount, t.mMixerInFormat, t.mMixerFormat);
}
}
}
@@ -1147,9 +1203,8 @@ void AudioMixer::track__genericResample(track_t* t, int32_t* out, size_t outFram
if (aux != NULL) {
// 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_FLOAT, UNITY_GAIN_FLOAT);
- memset(temp, 0, outFrameCount * MAX_NUM_CHANNELS * sizeof(int32_t));
+ memset(temp, 0, outFrameCount * t->mMixerChannelCount * sizeof(int32_t));
t->resampler->resample(temp, outFrameCount, t->bufferProvider);
if (CC_UNLIKELY(t->volumeInc[0]|t->volumeInc[1]|t->auxInc)) {
volumeRampStereo(t, out, outFrameCount, temp, aux);
@@ -1434,7 +1489,6 @@ void AudioMixer::process__nop(state_t* state, int64_t pts)
{
ALOGVV("process__nop\n");
uint32_t e0 = state->enabledTracks;
- 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
@@ -1453,7 +1507,7 @@ void AudioMixer::process__nop(state_t* state, int64_t pts)
}
e0 &= ~(e1);
- memset(t1.mainBuffer, 0, sampleCount
+ memset(t1.mainBuffer, 0, state->frameCount * t1.mMixerChannelCount
* audio_bytes_per_sample(t1.mMixerFormat));
}
@@ -1538,8 +1592,8 @@ void AudioMixer::process__genericNoResampling(state_t* state, int64_t pts)
}
size_t inFrames = (t.frameCount > outFrames)?outFrames:t.frameCount;
if (inFrames > 0) {
- t.hook(&t, outTemp + (BLOCKSIZE-outFrames)*MAX_NUM_CHANNELS, inFrames,
- state->resampleTemp, aux);
+ t.hook(&t, outTemp + (BLOCKSIZE - outFrames) * t.mMixerChannelCount,
+ inFrames, state->resampleTemp, aux);
t.frameCount -= inFrames;
outFrames -= inFrames;
if (CC_UNLIKELY(aux != NULL)) {
@@ -1565,10 +1619,11 @@ void AudioMixer::process__genericNoResampling(state_t* state, int64_t pts)
}
convertMixerFormat(out, t1.mMixerFormat, outTemp, t1.mMixerInFormat,
- BLOCKSIZE * FCC_2);
+ BLOCKSIZE * t1.mMixerChannelCount);
// TODO: fix ugly casting due to choice of out pointer type
out = reinterpret_cast<int32_t*>((uint8_t*)out
- + BLOCKSIZE * FCC_2 * audio_bytes_per_sample(t1.mMixerFormat));
+ + BLOCKSIZE * t1.mMixerChannelCount
+ * audio_bytes_per_sample(t1.mMixerFormat));
numFrames += BLOCKSIZE;
} while (numFrames < state->frameCount);
}
@@ -1590,8 +1645,6 @@ void AudioMixer::process__genericResampling(state_t* state, int64_t pts)
ALOGVV("process__genericResampling\n");
// this const just means that local variable outTemp doesn't change
int32_t* const outTemp = state->outputTemp;
- const size_t size = sizeof(int32_t) * MAX_NUM_CHANNELS * state->frameCount;
-
size_t numFrames = state->frameCount;
uint32_t e0 = state->enabledTracks;
@@ -1612,7 +1665,7 @@ void AudioMixer::process__genericResampling(state_t* state, int64_t pts)
}
e0 &= ~(e1);
int32_t *out = t1.mainBuffer;
- memset(outTemp, 0, size);
+ memset(outTemp, 0, sizeof(*outTemp) * t1.mMixerChannelCount * state->frameCount);
while (e1) {
const int i = 31 - __builtin_clz(e1);
e1 &= ~(1<<i);
@@ -1644,14 +1697,15 @@ void AudioMixer::process__genericResampling(state_t* state, int64_t pts)
if (CC_UNLIKELY(aux != NULL)) {
aux += outFrames;
}
- t.hook(&t, outTemp + outFrames*MAX_NUM_CHANNELS, t.buffer.frameCount,
+ t.hook(&t, outTemp + outFrames * t.mMixerChannelCount, t.buffer.frameCount,
state->resampleTemp, aux);
outFrames += t.buffer.frameCount;
t.bufferProvider->releaseBuffer(&t.buffer);
}
}
}
- convertMixerFormat(out, t1.mMixerFormat, outTemp, t1.mMixerInFormat, numFrames * FCC_2);
+ convertMixerFormat(out, t1.mMixerFormat,
+ outTemp, t1.mMixerInFormat, numFrames * t1.mMixerChannelCount);
}
}
@@ -1687,7 +1741,7 @@ void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state,
// been enabled for mixing.
if (in == NULL || (((uintptr_t)in) & 3)) {
memset(out, 0, numFrames
- * MAX_NUM_CHANNELS * audio_bytes_per_sample(t.mMixerFormat));
+ * t.mMixerChannelCount * 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);
@@ -1864,31 +1918,129 @@ int64_t AudioMixer::calculateOutputPTS(const track_t& t, int64_t basePTS,
DownmixerBufferProvider::init(); // for the downmixer
}
-template <int MIXTYPE, int NCHAN, bool USEFLOATVOL, bool ADJUSTVOL,
+/* TODO: consider whether this level of optimization is necessary.
+ * Perhaps just stick with a single for loop.
+ */
+
+// Needs to derive a compile time constant (constexpr). Could be targeted to go
+// to a MONOVOL mixtype based on MAX_NUM_VOLUMES, but that's an unnecessary complication.
+#define MIXTYPE_MONOVOL(mixtype) (mixtype == MIXTYPE_MULTI ? MIXTYPE_MULTI_MONOVOL : \
+ mixtype == MIXTYPE_MULTI_SAVEONLY ? MIXTYPE_MULTI_SAVEONLY_MONOVOL : mixtype)
+
+/* MIXTYPE (see AudioMixerOps.h MIXTYPE_* enumeration)
+ * TO: int32_t (Q4.27) or float
+ * TI: int32_t (Q4.27) or int16_t (Q0.15) or float
+ * TA: int32_t (Q4.27)
+ */
+template <int MIXTYPE,
+ typename TO, typename TI, typename TV, typename TA, typename TAV>
+static void volumeRampMulti(uint32_t channels, TO* out, size_t frameCount,
+ const TI* in, TA* aux, TV *vol, const TV *volinc, TAV *vola, TAV volainc)
+{
+ switch (channels) {
+ case 1:
+ volumeRampMulti<MIXTYPE, 1>(out, frameCount, in, aux, vol, volinc, vola, volainc);
+ break;
+ case 2:
+ volumeRampMulti<MIXTYPE, 2>(out, frameCount, in, aux, vol, volinc, vola, volainc);
+ break;
+ case 3:
+ volumeRampMulti<MIXTYPE_MONOVOL(MIXTYPE), 3>(out,
+ frameCount, in, aux, vol, volinc, vola, volainc);
+ break;
+ case 4:
+ volumeRampMulti<MIXTYPE_MONOVOL(MIXTYPE), 4>(out,
+ frameCount, in, aux, vol, volinc, vola, volainc);
+ break;
+ case 5:
+ volumeRampMulti<MIXTYPE_MONOVOL(MIXTYPE), 5>(out,
+ frameCount, in, aux, vol, volinc, vola, volainc);
+ break;
+ case 6:
+ volumeRampMulti<MIXTYPE_MONOVOL(MIXTYPE), 6>(out,
+ frameCount, in, aux, vol, volinc, vola, volainc);
+ break;
+ case 7:
+ volumeRampMulti<MIXTYPE_MONOVOL(MIXTYPE), 7>(out,
+ frameCount, in, aux, vol, volinc, vola, volainc);
+ break;
+ case 8:
+ volumeRampMulti<MIXTYPE_MONOVOL(MIXTYPE), 8>(out,
+ frameCount, in, aux, vol, volinc, vola, volainc);
+ break;
+ }
+}
+
+/* MIXTYPE (see AudioMixerOps.h MIXTYPE_* enumeration)
+ * TO: int32_t (Q4.27) or float
+ * TI: int32_t (Q4.27) or int16_t (Q0.15) or float
+ * TA: int32_t (Q4.27)
+ */
+template <int MIXTYPE,
+ typename TO, typename TI, typename TV, typename TA, typename TAV>
+static void volumeMulti(uint32_t channels, TO* out, size_t frameCount,
+ const TI* in, TA* aux, const TV *vol, TAV vola)
+{
+ switch (channels) {
+ case 1:
+ volumeMulti<MIXTYPE, 1>(out, frameCount, in, aux, vol, vola);
+ break;
+ case 2:
+ volumeMulti<MIXTYPE, 2>(out, frameCount, in, aux, vol, vola);
+ break;
+ case 3:
+ volumeMulti<MIXTYPE_MONOVOL(MIXTYPE), 3>(out, frameCount, in, aux, vol, vola);
+ break;
+ case 4:
+ volumeMulti<MIXTYPE_MONOVOL(MIXTYPE), 4>(out, frameCount, in, aux, vol, vola);
+ break;
+ case 5:
+ volumeMulti<MIXTYPE_MONOVOL(MIXTYPE), 5>(out, frameCount, in, aux, vol, vola);
+ break;
+ case 6:
+ volumeMulti<MIXTYPE_MONOVOL(MIXTYPE), 6>(out, frameCount, in, aux, vol, vola);
+ break;
+ case 7:
+ volumeMulti<MIXTYPE_MONOVOL(MIXTYPE), 7>(out, frameCount, in, aux, vol, vola);
+ break;
+ case 8:
+ volumeMulti<MIXTYPE_MONOVOL(MIXTYPE), 8>(out, frameCount, in, aux, vol, vola);
+ break;
+ }
+}
+
+/* MIXTYPE (see AudioMixerOps.h MIXTYPE_* enumeration)
+ * USEFLOATVOL (set to true if float volume is used)
+ * ADJUSTVOL (set to true if volume ramp parameters needs adjustment afterwards)
+ * TO: int32_t (Q4.27) or float
+ * TI: int32_t (Q4.27) or int16_t (Q0.15) or float
+ * TA: int32_t (Q4.27)
+ */
+template <int MIXTYPE, bool USEFLOATVOL, bool ADJUSTVOL,
typename TO, typename TI, typename TA>
void AudioMixer::volumeMix(TO *out, size_t outFrames,
const TI *in, TA *aux, bool ramp, AudioMixer::track_t *t)
{
if (USEFLOATVOL) {
if (ramp) {
- volumeRampMulti<MIXTYPE, NCHAN>(out, outFrames, in, aux,
+ volumeRampMulti<MIXTYPE>(t->mMixerChannelCount, out, outFrames, in, aux,
t->mPrevVolume, t->mVolumeInc, &t->prevAuxLevel, t->auxInc);
if (ADJUSTVOL) {
t->adjustVolumeRamp(aux != NULL, true);
}
} else {
- volumeMulti<MIXTYPE, NCHAN>(out, outFrames, in, aux,
+ volumeMulti<MIXTYPE>(t->mMixerChannelCount, out, outFrames, in, aux,
t->mVolume, t->auxLevel);
}
} else {
if (ramp) {
- volumeRampMulti<MIXTYPE, NCHAN>(out, outFrames, in, aux,
+ volumeRampMulti<MIXTYPE>(t->mMixerChannelCount, out, outFrames, in, aux,
t->prevVolume, t->volumeInc, &t->prevAuxLevel, t->auxInc);
if (ADJUSTVOL) {
t->adjustVolumeRamp(aux != NULL);
}
} else {
- volumeMulti<MIXTYPE, NCHAN>(out, outFrames, in, aux,
+ volumeMulti<MIXTYPE>(t->mMixerChannelCount, out, outFrames, in, aux,
t->volume, t->auxLevel);
}
}
@@ -1897,8 +2049,13 @@ void AudioMixer::volumeMix(TO *out, size_t outFrames,
/* This process hook is called when there is a single track without
* aux buffer, volume ramp, or resampling.
* TODO: Update the hook selection: this can properly handle aux and ramp.
+ *
+ * MIXTYPE (see AudioMixerOps.h MIXTYPE_* enumeration)
+ * TO: int32_t (Q4.27) or float
+ * TI: int32_t (Q4.27) or int16_t (Q0.15) or float
+ * TA: int32_t (Q4.27)
*/
-template <int MIXTYPE, int NCHAN, typename TO, typename TI, typename TA>
+template <int MIXTYPE, typename TO, typename TI, typename TA>
void AudioMixer::process_NoResampleOneTrack(state_t* state, int64_t pts)
{
ALOGVV("process_NoResampleOneTrack\n");
@@ -1906,6 +2063,7 @@ void AudioMixer::process_NoResampleOneTrack(state_t* state, int64_t pts)
const int i = 31 - __builtin_clz(state->enabledTracks);
ALOG_ASSERT((1 << i) == state->enabledTracks, "more than 1 track enabled");
track_t *t = &state->tracks[i];
+ const uint32_t channels = t->mMixerChannelCount;
TO* out = reinterpret_cast<TO*>(t->mainBuffer);
TA* aux = reinterpret_cast<TA*>(t->auxBuffer);
const bool ramp = t->needsRamp();
@@ -1922,7 +2080,7 @@ void AudioMixer::process_NoResampleOneTrack(state_t* state, int64_t pts)
// been enabled for mixing.
if (in == NULL || (((uintptr_t)in) & 3)) {
memset(out, 0, numFrames
- * NCHAN * audio_bytes_per_sample(t->mMixerFormat));
+ * channels * audio_bytes_per_sample(t->mMixerFormat));
ALOGE_IF((((uintptr_t)in) & 3), "process_NoResampleOneTrack: bus error: "
"buffer %p track %p, channels %d, needs %#x",
in, t, t->channelCount, t->needs);
@@ -1930,12 +2088,12 @@ void AudioMixer::process_NoResampleOneTrack(state_t* state, int64_t pts)
}
const size_t outFrames = b.frameCount;
- volumeMix<MIXTYPE, NCHAN, is_same<TI, float>::value, false> (out,
- outFrames, in, aux, ramp, t);
+ volumeMix<MIXTYPE, is_same<TI, float>::value, false> (
+ out, outFrames, in, aux, ramp, t);
- out += outFrames * NCHAN;
+ out += outFrames * channels;
if (aux != NULL) {
- aux += NCHAN;
+ aux += channels;
}
numFrames -= b.frameCount;
@@ -1949,24 +2107,28 @@ void AudioMixer::process_NoResampleOneTrack(state_t* state, int64_t pts)
/* This track hook is called to do resampling then mixing,
* pulling from the track's upstream AudioBufferProvider.
+ *
+ * MIXTYPE (see AudioMixerOps.h MIXTYPE_* enumeration)
+ * TO: int32_t (Q4.27) or float
+ * TI: int32_t (Q4.27) or int16_t (Q0.15) or float
+ * TA: int32_t (Q4.27)
*/
-template <int MIXTYPE, int NCHAN, typename TO, typename TI, typename TA>
+template <int MIXTYPE, typename TO, typename TI, typename TA>
void AudioMixer::track__Resample(track_t* t, TO* out, size_t outFrameCount, TO* temp, TA* aux)
{
ALOGVV("track__Resample\n");
t->resampler->setSampleRate(t->sampleRate);
-
const bool ramp = t->needsRamp();
if (ramp || aux != NULL) {
// if ramp: resample with unity gain to temp buffer and scale/mix in 2nd step.
// if aux != NULL: resample with unity gain to temp buffer then apply send level.
t->resampler->setVolume(UNITY_GAIN_FLOAT, UNITY_GAIN_FLOAT);
- memset(temp, 0, outFrameCount * NCHAN * sizeof(TO));
+ memset(temp, 0, outFrameCount * t->mMixerChannelCount * sizeof(TO));
t->resampler->resample((int32_t*)temp, outFrameCount, t->bufferProvider);
- volumeMix<MIXTYPE, NCHAN, is_same<TI, float>::value, true>(out, outFrameCount,
- temp, aux, ramp, t);
+ volumeMix<MIXTYPE, is_same<TI, float>::value, true>(
+ out, outFrameCount, temp, aux, ramp, t);
} else { // constant volume gain
t->resampler->setVolume(t->mVolume[0], t->mVolume[1]);
@@ -1976,20 +2138,25 @@ void AudioMixer::track__Resample(track_t* t, TO* out, size_t outFrameCount, TO*
/* This track hook is called to mix a track, when no resampling is required.
* The input buffer should be present in t->in.
+ *
+ * MIXTYPE (see AudioMixerOps.h MIXTYPE_* enumeration)
+ * TO: int32_t (Q4.27) or float
+ * TI: int32_t (Q4.27) or int16_t (Q0.15) or float
+ * TA: int32_t (Q4.27)
*/
-template <int MIXTYPE, int NCHAN, typename TO, typename TI, typename TA>
+template <int MIXTYPE, typename TO, typename TI, typename TA>
void AudioMixer::track__NoResample(track_t* t, TO* out, size_t frameCount,
TO* temp __unused, TA* aux)
{
ALOGVV("track__NoResample\n");
const TI *in = static_cast<const TI *>(t->in);
- volumeMix<MIXTYPE, NCHAN, is_same<TI, float>::value, true>(out, frameCount,
- in, aux, t->needsRamp(), t);
+ volumeMix<MIXTYPE, is_same<TI, float>::value, true>(
+ out, frameCount, in, aux, t->needsRamp(), t);
// MIXTYPE_MONOEXPAND reads a single input channel and expands to NCHAN output channels.
// MIXTYPE_MULTI reads NCHAN input channels and places to NCHAN output channels.
- in += (MIXTYPE == MIXTYPE_MONOEXPAND) ? frameCount : frameCount * NCHAN;
+ in += (MIXTYPE == MIXTYPE_MONOEXPAND) ? frameCount : frameCount * t->mMixerChannelCount;
t->in = in;
}
@@ -2036,10 +2203,10 @@ void AudioMixer::convertMixerFormat(void *out, audio_format_t mixerOutFormat,
/* Returns the proper track hook to use for mixing the track into the output buffer.
*/
-AudioMixer::hook_t AudioMixer::getTrackHook(int trackType, int channels,
+AudioMixer::hook_t AudioMixer::getTrackHook(int trackType, uint32_t channelCount,
audio_format_t mixerInFormat, audio_format_t mixerOutFormat __unused)
{
- if (!kUseNewMixer && channels == FCC_2 && mixerInFormat == AUDIO_FORMAT_PCM_16_BIT) {
+ if (!kUseNewMixer && channelCount == FCC_2 && mixerInFormat == AUDIO_FORMAT_PCM_16_BIT) {
switch (trackType) {
case TRACKTYPE_NOP:
return track__nop;
@@ -2054,7 +2221,7 @@ AudioMixer::hook_t AudioMixer::getTrackHook(int trackType, int channels,
break;
}
}
- LOG_ALWAYS_FATAL_IF(channels != FCC_2); // TODO: must be stereo right now
+ LOG_ALWAYS_FATAL_IF(channelCount > MAX_NUM_CHANNELS);
switch (trackType) {
case TRACKTYPE_NOP:
return track__nop;
@@ -2062,10 +2229,10 @@ AudioMixer::hook_t AudioMixer::getTrackHook(int trackType, int channels,
switch (mixerInFormat) {
case AUDIO_FORMAT_PCM_FLOAT:
return (AudioMixer::hook_t)
- track__Resample<MIXTYPE_MULTI, 2, float, float, int32_t>;
+ track__Resample<MIXTYPE_MULTI, float /*TO*/, float /*TI*/, int32_t /*TA*/>;
case AUDIO_FORMAT_PCM_16_BIT:
return (AudioMixer::hook_t)\
- track__Resample<MIXTYPE_MULTI, 2, int32_t, int16_t, int32_t>;
+ track__Resample<MIXTYPE_MULTI, int32_t, int16_t, int32_t>;
default:
LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
break;
@@ -2075,10 +2242,10 @@ AudioMixer::hook_t AudioMixer::getTrackHook(int trackType, int channels,
switch (mixerInFormat) {
case AUDIO_FORMAT_PCM_FLOAT:
return (AudioMixer::hook_t)
- track__NoResample<MIXTYPE_MONOEXPAND, 2, float, float, int32_t>;
+ track__NoResample<MIXTYPE_MONOEXPAND, float, float, int32_t>;
case AUDIO_FORMAT_PCM_16_BIT:
return (AudioMixer::hook_t)
- track__NoResample<MIXTYPE_MONOEXPAND, 2, int32_t, int16_t, int32_t>;
+ track__NoResample<MIXTYPE_MONOEXPAND, int32_t, int16_t, int32_t>;
default:
LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
break;
@@ -2088,10 +2255,10 @@ AudioMixer::hook_t AudioMixer::getTrackHook(int trackType, int channels,
switch (mixerInFormat) {
case AUDIO_FORMAT_PCM_FLOAT:
return (AudioMixer::hook_t)
- track__NoResample<MIXTYPE_MULTI, 2, float, float, int32_t>;
+ track__NoResample<MIXTYPE_MULTI, float, float, int32_t>;
case AUDIO_FORMAT_PCM_16_BIT:
return (AudioMixer::hook_t)
- track__NoResample<MIXTYPE_MULTI, 2, int32_t, int16_t, int32_t>;
+ track__NoResample<MIXTYPE_MULTI, int32_t, int16_t, int32_t>;
default:
LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
break;
@@ -2107,25 +2274,25 @@ AudioMixer::hook_t AudioMixer::getTrackHook(int trackType, int channels,
/* Returns the proper process hook for mixing tracks. Currently works only for
* PROCESSTYPE_NORESAMPLEONETRACK, a mix involving one track, no resampling.
*/
-AudioMixer::process_hook_t AudioMixer::getProcessHook(int processType, int channels,
+AudioMixer::process_hook_t AudioMixer::getProcessHook(int processType, uint32_t channelCount,
audio_format_t mixerInFormat, audio_format_t mixerOutFormat)
{
if (processType != PROCESSTYPE_NORESAMPLEONETRACK) { // Only NORESAMPLEONETRACK
LOG_ALWAYS_FATAL("bad processType: %d", processType);
return NULL;
}
- if (!kUseNewMixer && channels == FCC_2 && mixerInFormat == AUDIO_FORMAT_PCM_16_BIT) {
+ if (!kUseNewMixer && channelCount == FCC_2 && mixerInFormat == AUDIO_FORMAT_PCM_16_BIT) {
return process__OneTrack16BitsStereoNoResampling;
}
- LOG_ALWAYS_FATAL_IF(channels != FCC_2); // TODO: must be stereo right now
+ LOG_ALWAYS_FATAL_IF(channelCount > MAX_NUM_CHANNELS);
switch (mixerInFormat) {
case AUDIO_FORMAT_PCM_FLOAT:
switch (mixerOutFormat) {
case AUDIO_FORMAT_PCM_FLOAT:
- return process_NoResampleOneTrack<MIXTYPE_MULTI_SAVEONLY, 2,
- float, float, int32_t>;
+ return process_NoResampleOneTrack<MIXTYPE_MULTI_SAVEONLY,
+ float /*TO*/, float /*TI*/, int32_t /*TA*/>;
case AUDIO_FORMAT_PCM_16_BIT:
- return process_NoResampleOneTrack<MIXTYPE_MULTI_SAVEONLY, 2,
+ return process_NoResampleOneTrack<MIXTYPE_MULTI_SAVEONLY,
int16_t, float, int32_t>;
default:
LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
@@ -2135,10 +2302,10 @@ AudioMixer::process_hook_t AudioMixer::getProcessHook(int processType, int chann
case AUDIO_FORMAT_PCM_16_BIT:
switch (mixerOutFormat) {
case AUDIO_FORMAT_PCM_FLOAT:
- return process_NoResampleOneTrack<MIXTYPE_MULTI_SAVEONLY, 2,
+ return process_NoResampleOneTrack<MIXTYPE_MULTI_SAVEONLY,
float, int16_t, int32_t>;
case AUDIO_FORMAT_PCM_16_BIT:
- return process_NoResampleOneTrack<MIXTYPE_MULTI_SAVEONLY, 2,
+ return process_NoResampleOneTrack<MIXTYPE_MULTI_SAVEONLY,
int16_t, int16_t, int32_t>;
default:
LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
diff --git a/services/audioflinger/AudioMixer.h b/services/audioflinger/AudioMixer.h
index 09a4d89..86f72e4 100644
--- a/services/audioflinger/AudioMixer.h
+++ b/services/audioflinger/AudioMixer.h
@@ -51,12 +51,11 @@ public:
static const uint32_t MAX_NUM_TRACKS = 32;
// maximum number of channels supported by the mixer
- // This mixer has a hard-coded upper limit of 2 channels for output.
- // There is support for > 2 channel tracks down-mixed to 2 channel output via a down-mix effect.
- // Adding support for > 2 channel output would require more than simply changing this value.
- static const uint32_t MAX_NUM_CHANNELS = 2;
+ // This mixer has a hard-coded upper limit of 8 channels for output.
+ static const uint32_t MAX_NUM_CHANNELS = 8;
+ static const uint32_t MAX_NUM_VOLUMES = 2; // stereo volume only
// maximum number of channels supported for the content
- static const uint32_t MAX_NUM_CHANNELS_TO_DOWNMIX = 8;
+ static const uint32_t MAX_NUM_CHANNELS_TO_DOWNMIX = AUDIO_CHANNEL_COUNT_MAX;
static const uint16_t UNITY_GAIN_INT = 0x1000;
static const float UNITY_GAIN_FLOAT = 1.0f;
@@ -82,6 +81,7 @@ public:
AUX_BUFFER = 0x4003,
DOWNMIX_TYPE = 0X4004,
MIXER_FORMAT = 0x4005, // AUDIO_FORMAT_PCM_(FLOAT|16_BIT)
+ MIXER_CHANNEL_MASK = 0x4006, // Channel mask for mixer output
// for target RESAMPLE
SAMPLE_RATE = 0x4100, // Configure sample rate conversion on this track name;
// parameter 'value' is the new sample rate in Hz.
@@ -164,15 +164,15 @@ private:
// TODO: Eventually remove legacy integer volume settings
union {
- int16_t volume[MAX_NUM_CHANNELS]; // U4.12 fixed point (top bit should be zero)
+ int16_t volume[MAX_NUM_VOLUMES]; // U4.12 fixed point (top bit should be zero)
int32_t volumeRL;
};
- int32_t prevVolume[MAX_NUM_CHANNELS];
+ int32_t prevVolume[MAX_NUM_VOLUMES];
// 16-byte boundary
- int32_t volumeInc[MAX_NUM_CHANNELS];
+ int32_t volumeInc[MAX_NUM_VOLUMES];
int32_t auxInc;
int32_t prevAuxLevel;
@@ -217,18 +217,20 @@ private:
audio_format_t mMixerInFormat; // mix internal format AUDIO_FORMAT_PCM_(FLOAT|16_BIT)
// each track must be converted to this format.
- float mVolume[MAX_NUM_CHANNELS]; // floating point set volume
- float mPrevVolume[MAX_NUM_CHANNELS]; // floating point previous volume
- float mVolumeInc[MAX_NUM_CHANNELS]; // floating point volume increment
+ float mVolume[MAX_NUM_VOLUMES]; // floating point set volume
+ float mPrevVolume[MAX_NUM_VOLUMES]; // floating point previous volume
+ float mVolumeInc[MAX_NUM_VOLUMES]; // floating point volume increment
float mAuxLevel; // floating point set aux level
float mPrevAuxLevel; // floating point prev aux level
float mAuxInc; // floating point aux increment
// 16-byte boundary
+ audio_channel_mask_t mMixerChannelMask;
+ uint32_t mMixerChannelCount;
bool needsRamp() { return (volumeInc[0] | volumeInc[1] | auxInc) != 0; }
- bool setResampler(uint32_t sampleRate, uint32_t devSampleRate);
+ bool setResampler(uint32_t trackSampleRate, uint32_t devSampleRate);
bool doesResample() const { return resampler != NULL; }
void resetResampler() { if (resampler != NULL) resampler->reset(); }
void adjustVolumeRamp(bool aux, bool useFloat = false);
@@ -377,7 +379,11 @@ private:
// OK to call more often than that, but unnecessary.
void invalidateState(uint32_t mask);
- static status_t initTrackDownmix(track_t* pTrack, int trackNum, audio_channel_mask_t mask);
+ bool setChannelMasks(int name,
+ audio_channel_mask_t trackChannelMask, audio_channel_mask_t mixerChannelMask);
+
+ // TODO: remove unused trackName/trackNum from functions below.
+ static status_t initTrackDownmix(track_t* pTrack, int trackName);
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);
@@ -418,27 +424,26 @@ private:
* in AudioMixerOps.h). The template parameters are as follows:
*
* MIXTYPE (see AudioMixerOps.h MIXTYPE_* enumeration)
- * NCHAN (number of channels, 2 for now)
* USEFLOATVOL (set to true if float volume is used)
* ADJUSTVOL (set to true if volume ramp parameters needs adjustment afterwards)
* TO: int32_t (Q4.27) or float
* TI: int32_t (Q4.27) or int16_t (Q0.15) or float
* TA: int32_t (Q4.27)
*/
- template <int MIXTYPE, int NCHAN, bool USEFLOATVOL, bool ADJUSTVOL,
+ template <int MIXTYPE, bool USEFLOATVOL, bool ADJUSTVOL,
typename TO, typename TI, typename TA>
static void volumeMix(TO *out, size_t outFrames,
const TI *in, TA *aux, bool ramp, AudioMixer::track_t *t);
// multi-format process hooks
- template <int MIXTYPE, int NCHAN, typename TO, typename TI, typename TA>
+ template <int MIXTYPE, typename TO, typename TI, typename TA>
static void process_NoResampleOneTrack(state_t* state, int64_t pts);
// multi-format track hooks
- template <int MIXTYPE, int NCHAN, typename TO, typename TI, typename TA>
+ template <int MIXTYPE, typename TO, typename TI, typename TA>
static void track__Resample(track_t* t, TO* out, size_t frameCount,
TO* temp __unused, TA* aux);
- template <int MIXTYPE, int NCHAN, typename TO, typename TI, typename TA>
+ template <int MIXTYPE, typename TO, typename TI, typename TA>
static void track__NoResample(track_t* t, TO* out, size_t frameCount,
TO* temp __unused, TA* aux);
@@ -457,9 +462,9 @@ private:
};
// functions for determining the proper process and track hooks.
- static process_hook_t getProcessHook(int processType, int channels,
+ static process_hook_t getProcessHook(int processType, uint32_t channelCount,
audio_format_t mixerInFormat, audio_format_t mixerOutFormat);
- static hook_t getTrackHook(int trackType, int channels,
+ static hook_t getTrackHook(int trackType, uint32_t channelCount,
audio_format_t mixerInFormat, audio_format_t mixerOutFormat);
};
diff --git a/services/audioflinger/AudioMixerOps.h b/services/audioflinger/AudioMixerOps.h
index ad739ff..49131f6 100644
--- a/services/audioflinger/AudioMixerOps.h
+++ b/services/audioflinger/AudioMixerOps.h
@@ -230,6 +230,8 @@ enum {
MIXTYPE_MULTI,
MIXTYPE_MONOEXPAND,
MIXTYPE_MULTI_SAVEONLY,
+ MIXTYPE_MULTI_MONOVOL,
+ MIXTYPE_MULTI_SAVEONLY_MONOVOL,
};
/*
@@ -263,6 +265,13 @@ enum {
* vol: represents a volume array.
*
* MIXTYPE_MULTI_SAVEONLY does not accumulate into the out pointer.
+ *
+ * MIXTYPE_MULTI_MONOVOL:
+ * Same as MIXTYPE_MULTI, but uses only volume[0].
+ *
+ * MIXTYPE_MULTI_SAVEONLY_MONOVOL:
+ * Same as MIXTYPE_MULTI_SAVEONLY, but uses only volume[0].
+ *
*/
template <int MIXTYPE, int NCHAN,
@@ -283,18 +292,30 @@ inline void volumeRampMulti(TO* out, size_t frameCount,
vol[i] += volinc[i];
}
break;
+ case MIXTYPE_MONOEXPAND:
+ for (int i = 0; i < NCHAN; ++i) {
+ *out++ += MixMulAux<TO, TI, TV, TA>(*in, vol[i], &auxaccum);
+ vol[i] += volinc[i];
+ }
+ in++;
+ break;
case MIXTYPE_MULTI_SAVEONLY:
for (int i = 0; i < NCHAN; ++i) {
*out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum);
vol[i] += volinc[i];
}
break;
- case MIXTYPE_MONOEXPAND:
+ case MIXTYPE_MULTI_MONOVOL:
for (int i = 0; i < NCHAN; ++i) {
- *out++ += MixMulAux<TO, TI, TV, TA>(*in, vol[i], &auxaccum);
- vol[i] += volinc[i];
+ *out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[0], &auxaccum);
}
- in++;
+ vol[0] += volinc[0];
+ break;
+ case MIXTYPE_MULTI_SAVEONLY_MONOVOL:
+ for (int i = 0; i < NCHAN; ++i) {
+ *out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[0], &auxaccum);
+ }
+ vol[0] += volinc[0];
break;
default:
LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE);
@@ -313,18 +334,30 @@ inline void volumeRampMulti(TO* out, size_t frameCount,
vol[i] += volinc[i];
}
break;
+ case MIXTYPE_MONOEXPAND:
+ for (int i = 0; i < NCHAN; ++i) {
+ *out++ += MixMul<TO, TI, TV>(*in, vol[i]);
+ vol[i] += volinc[i];
+ }
+ in++;
+ break;
case MIXTYPE_MULTI_SAVEONLY:
for (int i = 0; i < NCHAN; ++i) {
*out++ = MixMul<TO, TI, TV>(*in++, vol[i]);
vol[i] += volinc[i];
}
break;
- case MIXTYPE_MONOEXPAND:
+ case MIXTYPE_MULTI_MONOVOL:
for (int i = 0; i < NCHAN; ++i) {
- *out++ += MixMul<TO, TI, TV>(*in, vol[i]);
- vol[i] += volinc[i];
+ *out++ += MixMul<TO, TI, TV>(*in++, vol[0]);
}
- in++;
+ vol[0] += volinc[0];
+ break;
+ case MIXTYPE_MULTI_SAVEONLY_MONOVOL:
+ for (int i = 0; i < NCHAN; ++i) {
+ *out++ = MixMul<TO, TI, TV>(*in++, vol[0]);
+ }
+ vol[0] += volinc[0];
break;
default:
LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE);
@@ -351,16 +384,26 @@ inline void volumeMulti(TO* out, size_t frameCount,
*out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum);
}
break;
+ case MIXTYPE_MONOEXPAND:
+ for (int i = 0; i < NCHAN; ++i) {
+ *out++ += MixMulAux<TO, TI, TV, TA>(*in, vol[i], &auxaccum);
+ }
+ in++;
+ break;
case MIXTYPE_MULTI_SAVEONLY:
for (int i = 0; i < NCHAN; ++i) {
*out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum);
}
break;
- case MIXTYPE_MONOEXPAND:
+ case MIXTYPE_MULTI_MONOVOL:
for (int i = 0; i < NCHAN; ++i) {
- *out++ += MixMulAux<TO, TI, TV, TA>(*in, vol[i], &auxaccum);
+ *out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[0], &auxaccum);
+ }
+ break;
+ case MIXTYPE_MULTI_SAVEONLY_MONOVOL:
+ for (int i = 0; i < NCHAN; ++i) {
+ *out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[0], &auxaccum);
}
- in++;
break;
default:
LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE);
@@ -377,16 +420,26 @@ inline void volumeMulti(TO* out, size_t frameCount,
*out++ += MixMul<TO, TI, TV>(*in++, vol[i]);
}
break;
+ case MIXTYPE_MONOEXPAND:
+ for (int i = 0; i < NCHAN; ++i) {
+ *out++ += MixMul<TO, TI, TV>(*in, vol[i]);
+ }
+ in++;
+ break;
case MIXTYPE_MULTI_SAVEONLY:
for (int i = 0; i < NCHAN; ++i) {
*out++ = MixMul<TO, TI, TV>(*in++, vol[i]);
}
break;
- case MIXTYPE_MONOEXPAND:
+ case MIXTYPE_MULTI_MONOVOL:
for (int i = 0; i < NCHAN; ++i) {
- *out++ += MixMul<TO, TI, TV>(*in, vol[i]);
+ *out++ += MixMul<TO, TI, TV>(*in++, vol[0]);
+ }
+ break;
+ case MIXTYPE_MULTI_SAVEONLY_MONOVOL:
+ for (int i = 0; i < NCHAN; ++i) {
+ *out++ = MixMul<TO, TI, TV>(*in++, vol[0]);
}
- in++;
break;
default:
LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE);
diff --git a/services/audioflinger/tests/mixer_to_wav_tests.sh b/services/audioflinger/tests/mixer_to_wav_tests.sh
index 93bff47..9b39e77 100755
--- a/services/audioflinger/tests/mixer_to_wav_tests.sh
+++ b/services/audioflinger/tests/mixer_to_wav_tests.sh
@@ -72,9 +72,9 @@ function createwav() {
# track__Resample / track__genericResample
# track__NoResample / track__16BitsStereo / track__16BitsMono
# Aux buffer
- adb shell test-mixer $1 -s 9307 \
+ adb shell test-mixer $1 -c 5 -s 9307 \
-a /sdcard/aux9307gra.wav -o /sdcard/tm9307gra.wav \
- sine:2,1000,3000 sine:1,2000,9307 chirp:2,9307
+ sine:4,1000,3000 sine:1,2000,9307 chirp:3,9307
adb pull /sdcard/tm9307gra.wav $2
adb pull /sdcard/aux9307gra.wav $2
diff --git a/services/audioflinger/tests/test-mixer.cpp b/services/audioflinger/tests/test-mixer.cpp
index 3940702..5a00f40 100644
--- a/services/audioflinger/tests/test-mixer.cpp
+++ b/services/audioflinger/tests/test-mixer.cpp
@@ -36,11 +36,12 @@
using namespace android;
static void usage(const char* name) {
- fprintf(stderr, "Usage: %s [-f] [-m]"
+ fprintf(stderr, "Usage: %s [-f] [-m] [-c channels]"
" [-s sample-rate] [-o <output-file>] [-a <aux-buffer-file>] [-P csv]"
" (<input-file> | <command>)+\n", name);
fprintf(stderr, " -f enable floating point input track\n");
fprintf(stderr, " -m enable floating point mixer output\n");
+ fprintf(stderr, " -c number of mixer output channels\n");
fprintf(stderr, " -s mixer sample-rate\n");
fprintf(stderr, " -o <output-file> WAV file, pcm16 (or float if -m specified)\n");
fprintf(stderr, " -a <aux-buffer-file>\n");
@@ -90,7 +91,7 @@ int main(int argc, char* argv[]) {
std::vector<int32_t> Names;
std::vector<SignalProvider> Providers;
- for (int ch; (ch = getopt(argc, argv, "fms:o:a:P:")) != -1;) {
+ for (int ch; (ch = getopt(argc, argv, "fmc:s:o:a:P:")) != -1;) {
switch (ch) {
case 'f':
useInputFloat = true;
@@ -98,6 +99,9 @@ int main(int argc, char* argv[]) {
case 'm':
useMixerFloat = true;
break;
+ case 'c':
+ outputChannels = atoi(optarg);
+ break;
case 's':
outputSampleRate = atoi(optarg);
break;
@@ -160,7 +164,7 @@ int main(int argc, char* argv[]) {
parseCSV(argv[i] + strlen(sine), v);
if (v.size() == 3) {
- printf("creating sine(%d %d)\n", v[0], v[1]);
+ printf("creating sine(%d %d %d)\n", v[0], v[1], v[2]);
if (useInputFloat) {
Providers[i].setSine<float>(v[0], v[1], v[2], kSeconds);
} else {
@@ -191,6 +195,8 @@ int main(int argc, char* argv[]) {
const size_t outputFrameSize = outputChannels
* (useMixerFloat ? sizeof(float) : sizeof(int16_t));
const size_t outputSize = outputFrames * outputFrameSize;
+ const audio_channel_mask_t outputChannelMask =
+ audio_channel_out_mask_from_count(outputChannels);
void *outputAddr = NULL;
(void) posix_memalign(&outputAddr, 32, outputSize);
memset(outputAddr, 0, outputSize);
@@ -224,15 +230,29 @@ int main(int argc, char* argv[]) {
Names.push_back(name);
mixer->setBufferProvider(name, &Providers[i]);
mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER,
- (void *) outputAddr);
+ (void *)outputAddr);
mixer->setParameter(
name,
AudioMixer::TRACK,
- AudioMixer::MIXER_FORMAT, (void *)mixerFormat);
- mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::FORMAT,
+ AudioMixer::MIXER_FORMAT,
+ (void *)(uintptr_t)mixerFormat);
+ mixer->setParameter(
+ name,
+ AudioMixer::TRACK,
+ AudioMixer::FORMAT,
(void *)(uintptr_t)inputFormat);
mixer->setParameter(
name,
+ AudioMixer::TRACK,
+ AudioMixer::MIXER_CHANNEL_MASK,
+ (void *)(uintptr_t)outputChannelMask);
+ mixer->setParameter(
+ name,
+ AudioMixer::TRACK,
+ AudioMixer::CHANNEL_MASK,
+ (void *)(uintptr_t)channelMask);
+ mixer->setParameter(
+ name,
AudioMixer::RESAMPLE,
AudioMixer::SAMPLE_RATE,
(void *)(uintptr_t)Providers[i].getSampleRate());
diff --git a/services/audioflinger/tests/test_utils.h b/services/audioflinger/tests/test_utils.h
index f954292..e446216 100644
--- a/services/audioflinger/tests/test_utils.h
+++ b/services/audioflinger/tests/test_utils.h
@@ -195,7 +195,7 @@ static void createSine(void *vbuffer, size_t frames,
T yt = convertValue<T>(y);
for (size_t j = 0; j < channels; ++j) {
- buffer[i*channels + j] = yt / (j + 1);
+ buffer[i*channels + j] = yt / T(j + 1);
}
}
}
@@ -221,7 +221,7 @@ static void createChirp(void *vbuffer, size_t frames,
T yt = convertValue<T>(y);
for (size_t j = 0; j < channels; ++j) {
- buffer[i*channels + j] = yt / (j + 1);
+ buffer[i*channels + j] = yt / T(j + 1);
}
}
}