Merge "AudioMixer: Preserve stereo volume in multi-channel mixing"

gugelfrei
Andy Hung 4 years ago committed by Android (Google) Code Review
commit 068be4d22e

@ -643,8 +643,16 @@ void AudioMixerBase::process__validate()
if (n & NEEDS_RESAMPLE) {
all16BitsStereoNoResample = false;
resampling = true;
t->hook = TrackBase::getTrackHook(TRACKTYPE_RESAMPLE, t->mMixerChannelCount,
t->mMixerInFormat, t->mMixerFormat);
if ((n & NEEDS_CHANNEL_COUNT__MASK) >= NEEDS_CHANNEL_2
&& t->useStereoVolume()) {
t->hook = TrackBase::getTrackHook(
TRACKTYPE_RESAMPLESTEREO, t->mMixerChannelCount,
t->mMixerInFormat, t->mMixerFormat);
} else {
t->hook = TrackBase::getTrackHook(
TRACKTYPE_RESAMPLE, t->mMixerChannelCount,
t->mMixerInFormat, t->mMixerFormat);
}
ALOGV_IF((n & NEEDS_CHANNEL_COUNT__MASK) > NEEDS_CHANNEL_2,
"Track %d needs downmix + resample", name);
} else {
@ -658,8 +666,11 @@ void AudioMixerBase::process__validate()
all16BitsStereoNoResample = false;
}
if ((n & NEEDS_CHANNEL_COUNT__MASK) >= NEEDS_CHANNEL_2){
t->hook = TrackBase::getTrackHook(TRACKTYPE_NORESAMPLE, t->mMixerChannelCount,
t->mMixerInFormat, t->mMixerFormat);
t->hook = TrackBase::getTrackHook(
t->useStereoVolume() ? TRACKTYPE_NORESAMPLESTEREO
: TRACKTYPE_NORESAMPLE,
t->mMixerChannelCount, t->mMixerInFormat,
t->mMixerFormat);
ALOGV_IF((n & NEEDS_CHANNEL_COUNT__MASK) > NEEDS_CHANNEL_2,
"Track %d needs downmix", name);
}
@ -691,7 +702,8 @@ void AudioMixerBase::process__validate()
// special case handling due to implicit channel duplication.
// Stereo or Multichannel should actually be fine here.
mHook = getProcessHook(PROCESSTYPE_NORESAMPLEONETRACK,
t->mMixerChannelCount, t->mMixerInFormat, t->mMixerFormat);
t->mMixerChannelCount, t->mMixerInFormat, t->mMixerFormat,
t->useStereoVolume());
}
}
}
@ -726,7 +738,8 @@ void AudioMixerBase::process__validate()
const std::shared_ptr<TrackBase> &t = mTracks[mEnabled[0]];
// Muted single tracks handled by allMuted above.
mHook = getProcessHook(PROCESSTYPE_NORESAMPLEONETRACK,
t->mMixerChannelCount, t->mMixerInFormat, t->mMixerFormat);
t->mMixerChannelCount, t->mMixerInFormat, t->mMixerFormat,
t->useStereoVolume());
}
}
}
@ -1450,7 +1463,7 @@ void AudioMixerBase::process__noResampleOneTrack()
}
const size_t outFrames = b.frameCount;
t->volumeMix<MIXTYPE, is_same<TI, float>::value /* USEFLOATVOL */, false /* ADJUSTVOL */> (
t->volumeMix<MIXTYPE, std::is_same_v<TI, float> /* USEFLOATVOL */, false /* ADJUSTVOL */> (
out, outFrames, in, aux, ramp);
out += outFrames * channels;
@ -1463,7 +1476,7 @@ void AudioMixerBase::process__noResampleOneTrack()
t->bufferProvider->releaseBuffer(&b);
}
if (ramp) {
t->adjustVolumeRamp(aux != NULL, is_same<TI, float>::value);
t->adjustVolumeRamp(aux != NULL, std::is_same_v<TI, float>);
}
}
@ -1489,7 +1502,7 @@ void AudioMixerBase::TrackBase::track__Resample(TO* out, size_t outFrameCount, T
memset(temp, 0, outFrameCount * mMixerChannelCount * sizeof(TO));
mResampler->resample((int32_t*)temp, outFrameCount, bufferProvider);
volumeMix<MIXTYPE, is_same<TI, float>::value /* USEFLOATVOL */, true /* ADJUSTVOL */>(
volumeMix<MIXTYPE, std::is_same_v<TI, float> /* USEFLOATVOL */, true /* ADJUSTVOL */>(
out, outFrameCount, temp, aux, ramp);
} else { // constant volume gain
@ -1513,7 +1526,7 @@ void AudioMixerBase::TrackBase::track__NoResample(
ALOGVV("track__NoResample\n");
const TI *in = static_cast<const TI *>(mIn);
volumeMix<MIXTYPE, is_same<TI, float>::value /* USEFLOATVOL */, true /* ADJUSTVOL */>(
volumeMix<MIXTYPE, std::is_same_v<TI, float> /* USEFLOATVOL */, true /* ADJUSTVOL */>(
out, frameCount, in, aux, needsRamp());
// MIXTYPE_MONOEXPAND reads a single input channel and expands to NCHAN output channels.
@ -1601,6 +1614,21 @@ AudioMixerBase::hook_t AudioMixerBase::TrackBase::getTrackHook(int trackType, ui
break;
}
break;
case TRACKTYPE_RESAMPLESTEREO:
switch (mixerInFormat) {
case AUDIO_FORMAT_PCM_FLOAT:
return (AudioMixerBase::hook_t) &TrackBase::track__Resample<
MIXTYPE_MULTI_STEREOVOL, float /*TO*/, float /*TI*/,
TYPE_AUX>;
case AUDIO_FORMAT_PCM_16_BIT:
return (AudioMixerBase::hook_t) &TrackBase::track__Resample<
MIXTYPE_MULTI_STEREOVOL, int32_t /*TO*/, int16_t /*TI*/,
TYPE_AUX>;
default:
LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
break;
}
break;
case TRACKTYPE_NORESAMPLEMONO:
switch (mixerInFormat) {
case AUDIO_FORMAT_PCM_FLOAT:
@ -1627,6 +1655,21 @@ AudioMixerBase::hook_t AudioMixerBase::TrackBase::getTrackHook(int trackType, ui
break;
}
break;
case TRACKTYPE_NORESAMPLESTEREO:
switch (mixerInFormat) {
case AUDIO_FORMAT_PCM_FLOAT:
return (AudioMixerBase::hook_t) &TrackBase::track__NoResample<
MIXTYPE_MULTI_STEREOVOL, float /*TO*/, float /*TI*/,
TYPE_AUX>;
case AUDIO_FORMAT_PCM_16_BIT:
return (AudioMixerBase::hook_t) &TrackBase::track__NoResample<
MIXTYPE_MULTI_STEREOVOL, int32_t /*TO*/, int16_t /*TI*/,
TYPE_AUX>;
default:
LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
break;
}
break;
default:
LOG_ALWAYS_FATAL("bad trackType: %d", trackType);
break;
@ -1644,7 +1687,8 @@ AudioMixerBase::hook_t AudioMixerBase::TrackBase::getTrackHook(int trackType, ui
/* static */
AudioMixerBase::process_hook_t AudioMixerBase::getProcessHook(
int processType, uint32_t channelCount,
audio_format_t mixerInFormat, audio_format_t mixerOutFormat)
audio_format_t mixerInFormat, audio_format_t mixerOutFormat,
bool stereoVolume)
{
if (processType != PROCESSTYPE_NORESAMPLEONETRACK) { // Only NORESAMPLEONETRACK
LOG_ALWAYS_FATAL("bad processType: %d", processType);
@ -1654,36 +1698,79 @@ AudioMixerBase::process_hook_t AudioMixerBase::getProcessHook(
return &AudioMixerBase::process__oneTrack16BitsStereoNoResampling;
}
LOG_ALWAYS_FATAL_IF(channelCount > MAX_NUM_CHANNELS);
switch (mixerInFormat) {
case AUDIO_FORMAT_PCM_FLOAT:
switch (mixerOutFormat) {
if (stereoVolume) { // templated arguments require explicit values.
switch (mixerInFormat) {
case AUDIO_FORMAT_PCM_FLOAT:
return &AudioMixerBase::process__noResampleOneTrack<
MIXTYPE_MULTI_SAVEONLY, float /*TO*/, float /*TI*/, TYPE_AUX>;
case AUDIO_FORMAT_PCM_16_BIT:
return &AudioMixerBase::process__noResampleOneTrack<
MIXTYPE_MULTI_SAVEONLY, int16_t /*TO*/, float /*TI*/, TYPE_AUX>;
default:
LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
switch (mixerOutFormat) {
case AUDIO_FORMAT_PCM_FLOAT:
return &AudioMixerBase::process__noResampleOneTrack<
MIXTYPE_MULTI_SAVEONLY_STEREOVOL, float /*TO*/,
float /*TI*/, TYPE_AUX>;
case AUDIO_FORMAT_PCM_16_BIT:
return &AudioMixerBase::process__noResampleOneTrack<
MIXTYPE_MULTI_SAVEONLY_STEREOVOL, int16_t /*TO*/,
float /*TI*/, TYPE_AUX>;
default:
LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
break;
}
break;
}
break;
case AUDIO_FORMAT_PCM_16_BIT:
switch (mixerOutFormat) {
case AUDIO_FORMAT_PCM_FLOAT:
return &AudioMixerBase::process__noResampleOneTrack<
MIXTYPE_MULTI_SAVEONLY, float /*TO*/, int16_t /*TI*/, TYPE_AUX>;
case AUDIO_FORMAT_PCM_16_BIT:
return &AudioMixerBase::process__noResampleOneTrack<
MIXTYPE_MULTI_SAVEONLY, int16_t /*TO*/, int16_t /*TI*/, TYPE_AUX>;
switch (mixerOutFormat) {
case AUDIO_FORMAT_PCM_FLOAT:
return &AudioMixerBase::process__noResampleOneTrack<
MIXTYPE_MULTI_SAVEONLY_STEREOVOL, float /*TO*/,
int16_t /*TI*/, TYPE_AUX>;
case AUDIO_FORMAT_PCM_16_BIT:
return &AudioMixerBase::process__noResampleOneTrack<
MIXTYPE_MULTI_SAVEONLY_STEREOVOL, int16_t /*TO*/,
int16_t /*TI*/, TYPE_AUX>;
default:
LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
break;
}
break;
default:
LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
break;
}
break;
default:
LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
break;
} else {
switch (mixerInFormat) {
case AUDIO_FORMAT_PCM_FLOAT:
switch (mixerOutFormat) {
case AUDIO_FORMAT_PCM_FLOAT:
return &AudioMixerBase::process__noResampleOneTrack<
MIXTYPE_MULTI_SAVEONLY, float /*TO*/,
float /*TI*/, TYPE_AUX>;
case AUDIO_FORMAT_PCM_16_BIT:
return &AudioMixerBase::process__noResampleOneTrack<
MIXTYPE_MULTI_SAVEONLY, int16_t /*TO*/,
float /*TI*/, TYPE_AUX>;
default:
LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
break;
}
break;
case AUDIO_FORMAT_PCM_16_BIT:
switch (mixerOutFormat) {
case AUDIO_FORMAT_PCM_FLOAT:
return &AudioMixerBase::process__noResampleOneTrack<
MIXTYPE_MULTI_SAVEONLY, float /*TO*/,
int16_t /*TI*/, TYPE_AUX>;
case AUDIO_FORMAT_PCM_16_BIT:
return &AudioMixerBase::process__noResampleOneTrack<
MIXTYPE_MULTI_SAVEONLY, int16_t /*TO*/,
int16_t /*TI*/, TYPE_AUX>;
default:
LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
break;
}
break;
default:
LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
break;
}
}
return NULL;
}

@ -19,21 +19,10 @@
namespace android {
/* Behavior of is_same<>::value is true if the types are identical,
* false otherwise. Identical to the STL std::is_same.
*/
template<typename T, typename U>
struct is_same
{
static const bool value = false;
};
template<typename T>
struct is_same<T, T> // partial specialization
{
static const bool value = true;
};
// Hack to make static_assert work in a constexpr
// https://en.cppreference.com/w/cpp/language/if
template <int N>
inline constexpr bool dependent_false = false;
/* MixMul is a multiplication operator to scale an audio input signal
* by a volume gain, with the formula:
@ -179,7 +168,7 @@ inline int16_t MixMul<int16_t, float, float>(float value, float volume) {
template <typename TO, typename TI>
inline void MixAccum(TO *auxaccum, TI value) {
if (!is_same<TO, TI>::value) {
if (!std::is_same_v<TO, TI>) {
LOG_ALWAYS_FATAL("MixAccum type not properly specialized: %zu %zu\n",
sizeof(TO), sizeof(TI));
}
@ -228,8 +217,68 @@ enum {
MIXTYPE_MULTI_SAVEONLY,
MIXTYPE_MULTI_MONOVOL,
MIXTYPE_MULTI_SAVEONLY_MONOVOL,
MIXTYPE_MULTI_STEREOVOL,
MIXTYPE_MULTI_SAVEONLY_STEREOVOL,
};
/*
* TODO: We should work on non-interleaved streams - the
* complexity of working on interleaved streams is now getting
* too high, and likely limits compiler optimization.
*/
template <int MIXTYPE, int NCHAN,
typename TO, typename TI, typename TV,
typename F>
void stereoVolumeHelper(TO*& out, const TI*& in, const TV *vol, F f) {
static_assert(NCHAN > 0 && NCHAN <= 8);
static_assert(MIXTYPE == MIXTYPE_MULTI_STEREOVOL
|| MIXTYPE == MIXTYPE_MULTI_SAVEONLY_STEREOVOL);
auto proc = [](auto& a, const auto& b) {
if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL) {
a += b;
} else {
a = b;
}
};
// HALs should only expose the canonical channel masks.
proc(*out++, f(*in++, vol[0])); // front left
if constexpr (NCHAN == 1) return;
proc(*out++, f(*in++, vol[1])); // front right
if constexpr (NCHAN == 2) return;
if constexpr (NCHAN == 4) {
proc(*out++, f(*in++, vol[0])); // back left
proc(*out++, f(*in++, vol[1])); // back right
return;
}
// TODO: Precompute center volume if not ramping.
std::decay_t<TV> center;
if constexpr (std::is_floating_point_v<TV>) {
center = (vol[0] + vol[1]) * 0.5; // do not use divide
} else {
center = (vol[0] >> 1) + (vol[1] >> 1); // rounds to 0.
}
proc(*out++, f(*in++, center)); // center (or 2.1 LFE)
if constexpr (NCHAN == 3) return;
if constexpr (NCHAN == 5) {
proc(*out++, f(*in++, vol[0])); // back left
proc(*out++, f(*in++, vol[1])); // back right
return;
}
proc(*out++, f(*in++, center)); // lfe
proc(*out++, f(*in++, vol[0])); // back left
proc(*out++, f(*in++, vol[1])); // back right
if constexpr (NCHAN == 6) return;
if constexpr (NCHAN == 7) {
proc(*out++, f(*in++, center)); // back center
return;
}
// NCHAN == 8
proc(*out++, f(*in++, vol[0])); // side left
proc(*out++, f(*in++, vol[1])); // side right
}
/*
* The volumeRampMulti and volumeRamp functions take a MIXTYPE
* which indicates the per-frame mixing and accumulation strategy.
@ -271,6 +320,12 @@ enum {
* MIXTYPE_MULTI_SAVEONLY_MONOVOL:
* Same as MIXTYPE_MULTI_SAVEONLY, but uses only volume[0].
*
* MIXTYPE_MULTI_STEREOVOL:
* Same as MIXTYPE_MULTI, but uses only volume[0] and volume[1].
*
* MIXTYPE_MULTI_SAVEONLY_STEREOVOL:
* Same as MIXTYPE_MULTI_SAVEONLY, but uses only volume[0] and volume[1].
*
*/
template <int MIXTYPE, int NCHAN,
@ -284,41 +339,42 @@ inline void volumeRampMulti(TO* out, size_t frameCount,
if (aux != NULL) {
do {
TA auxaccum = 0;
switch (MIXTYPE) {
case MIXTYPE_MULTI:
if constexpr (MIXTYPE == MIXTYPE_MULTI) {
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:
} else if constexpr (MIXTYPE == 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:
} else if constexpr (MIXTYPE == 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_MULTI_MONOVOL:
} else if constexpr (MIXTYPE == MIXTYPE_MULTI_MONOVOL) {
for (int i = 0; i < NCHAN; ++i) {
*out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[0], &auxaccum);
}
vol[0] += volinc[0];
break;
case MIXTYPE_MULTI_SAVEONLY_MONOVOL:
} else if constexpr (MIXTYPE == 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);
break;
} else if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL
|| MIXTYPE == MIXTYPE_MULTI_SAVEONLY_STEREOVOL) {
stereoVolumeHelper<MIXTYPE, NCHAN>(
out, in, vol, [&auxaccum] (auto &a, const auto &b) {
return MixMulAux<TO, TI, TV, TA>(a, b, &auxaccum);
});
vol[0] += volinc[0];
vol[1] += volinc[1];
} else /* constexpr */ {
static_assert(dependent_false<MIXTYPE>, "invalid mixtype");
}
auxaccum /= NCHAN;
*aux++ += MixMul<TA, TA, TAV>(auxaccum, *vola);
@ -326,41 +382,41 @@ inline void volumeRampMulti(TO* out, size_t frameCount,
} while (--frameCount);
} else {
do {
switch (MIXTYPE) {
case MIXTYPE_MULTI:
if constexpr (MIXTYPE == MIXTYPE_MULTI) {
for (int i = 0; i < NCHAN; ++i) {
*out++ += MixMul<TO, TI, TV>(*in++, vol[i]);
vol[i] += volinc[i];
}
break;
case MIXTYPE_MONOEXPAND:
} else if constexpr (MIXTYPE == 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:
} else if constexpr (MIXTYPE == 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_MULTI_MONOVOL:
} else if constexpr (MIXTYPE == MIXTYPE_MULTI_MONOVOL) {
for (int i = 0; i < NCHAN; ++i) {
*out++ += MixMul<TO, TI, TV>(*in++, vol[0]);
}
vol[0] += volinc[0];
break;
case MIXTYPE_MULTI_SAVEONLY_MONOVOL:
} else if constexpr (MIXTYPE == 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);
break;
} else if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL
|| MIXTYPE == MIXTYPE_MULTI_SAVEONLY_STEREOVOL) {
stereoVolumeHelper<MIXTYPE, NCHAN>(out, in, vol, [] (auto &a, const auto &b) {
return MixMul<TO, TI, TV>(a, b);
});
vol[0] += volinc[0];
vol[1] += volinc[1];
} else /* constexpr */ {
static_assert(dependent_false<MIXTYPE>, "invalid mixtype");
}
} while (--frameCount);
}
@ -377,72 +433,69 @@ inline void volumeMulti(TO* out, size_t frameCount,
if (aux != NULL) {
do {
TA auxaccum = 0;
switch (MIXTYPE) {
case MIXTYPE_MULTI:
if constexpr (MIXTYPE == MIXTYPE_MULTI) {
for (int i = 0; i < NCHAN; ++i) {
*out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum);
}
break;
case MIXTYPE_MONOEXPAND:
} else if constexpr (MIXTYPE == 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:
} else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY) {
for (int i = 0; i < NCHAN; ++i) {
*out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum);
}
break;
case MIXTYPE_MULTI_MONOVOL:
} else if constexpr (MIXTYPE == MIXTYPE_MULTI_MONOVOL) {
for (int i = 0; i < NCHAN; ++i) {
*out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[0], &auxaccum);
}
break;
case MIXTYPE_MULTI_SAVEONLY_MONOVOL:
} else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY_MONOVOL) {
for (int i = 0; i < NCHAN; ++i) {
*out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[0], &auxaccum);
}
break;
default:
LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE);
break;
} else if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL
|| MIXTYPE == MIXTYPE_MULTI_SAVEONLY_STEREOVOL) {
stereoVolumeHelper<MIXTYPE, NCHAN>(
out, in, vol, [&auxaccum] (auto &a, const auto &b) {
return MixMulAux<TO, TI, TV, TA>(a, b, &auxaccum);
});
} else /* constexpr */ {
static_assert(dependent_false<MIXTYPE>, "invalid mixtype");
}
auxaccum /= NCHAN;
*aux++ += MixMul<TA, TA, TAV>(auxaccum, vola);
} while (--frameCount);
} else {
do {
switch (MIXTYPE) {
case MIXTYPE_MULTI:
if constexpr (MIXTYPE == MIXTYPE_MULTI) {
for (int i = 0; i < NCHAN; ++i) {
*out++ += MixMul<TO, TI, TV>(*in++, vol[i]);
}
break;
case MIXTYPE_MONOEXPAND:
} else if constexpr (MIXTYPE == MIXTYPE_MONOEXPAND) {
for (int i = 0; i < NCHAN; ++i) {
*out++ += MixMul<TO, TI, TV>(*in, vol[i]);
}
in++;
break;
case MIXTYPE_MULTI_SAVEONLY:
} else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY) {
for (int i = 0; i < NCHAN; ++i) {
*out++ = MixMul<TO, TI, TV>(*in++, vol[i]);
}
break;
case MIXTYPE_MULTI_MONOVOL:
} else if constexpr (MIXTYPE == MIXTYPE_MULTI_MONOVOL) {
for (int i = 0; i < NCHAN; ++i) {
*out++ += MixMul<TO, TI, TV>(*in++, vol[0]);
}
break;
case MIXTYPE_MULTI_SAVEONLY_MONOVOL:
} else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY_MONOVOL) {
for (int i = 0; i < NCHAN; ++i) {
*out++ = MixMul<TO, TI, TV>(*in++, vol[0]);
}
break;
default:
LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE);
break;
} else if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL
|| MIXTYPE == MIXTYPE_MULTI_SAVEONLY_STEREOVOL) {
stereoVolumeHelper<MIXTYPE, NCHAN>(out, in, vol, [] (auto &a, const auto &b) {
return MixMul<TO, TI, TV>(a, b);
});
} else /* constexpr */ {
static_assert(dependent_false<MIXTYPE>, "invalid mixtype");
}
} while (--frameCount);
}

@ -188,13 +188,20 @@ public:
enum {
TRACKTYPE_NOP,
TRACKTYPE_RESAMPLE,
TRACKTYPE_RESAMPLESTEREO,
TRACKTYPE_NORESAMPLE,
TRACKTYPE_NORESAMPLEMONO,
TRACKTYPE_NORESAMPLESTEREO,
};
// process hook functionality
using process_hook_t = void(AudioMixerBase::*)();
static bool isAudioChannelPositionMask(audio_channel_mask_t channelMask) {
return audio_channel_mask_get_representation(channelMask)
== AUDIO_CHANNEL_REPRESENTATION_POSITION;
}
struct TrackBase;
using hook_t = void(TrackBase::*)(
int32_t* output, size_t numOutFrames, int32_t* temp, int32_t* aux);
@ -219,6 +226,9 @@ public:
size_t getUnreleasedFrames() const { return mResampler.get() != nullptr ?
mResampler->getUnreleasedFrames() : 0; };
bool useStereoVolume() const { return channelMask == AUDIO_CHANNEL_OUT_STEREO
&& isAudioChannelPositionMask(mMixerChannelMask); }
static hook_t getTrackHook(int trackType, uint32_t channelCount,
audio_format_t mixerInFormat, audio_format_t mixerOutFormat);
@ -327,7 +337,8 @@ public:
void process__noResampleOneTrack();
static process_hook_t getProcessHook(int processType, uint32_t channelCount,
audio_format_t mixerInFormat, audio_format_t mixerOutFormat);
audio_format_t mixerInFormat, audio_format_t mixerOutFormat,
bool useStereoVolume);
static void convertMixerFormat(void *out, audio_format_t mixerOutFormat,
void *in, audio_format_t mixerInFormat, size_t sampleCount);

@ -55,3 +55,26 @@ cc_binary {
srcs: ["test-resampler.cpp"],
static_libs: ["libsndfile"],
}
//
// build mixerops objdump
//
// This is used to verify proper optimization of the code.
//
// For example, use:
// ./prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/bin/aarch64-linux-android-objdump
// -d --source ./out/target/product/crosshatch/symbols/system/bin/mixerops_objdump
//
cc_binary {
name: "mixerops_objdump",
srcs: ["mixerops_objdump.cpp"],
}
//
// build mixerops benchmark
//
cc_benchmark {
name: "mixerops_benchmark",
srcs: ["mixerops_benchmark.cpp"],
static_libs: ["libgoogle-benchmark"],
}

@ -0,0 +1,102 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <inttypes.h>
#include <type_traits>
#include "../../../../system/media/audio_utils/include/audio_utils/primitives.h"
#define LOG_ALWAYS_FATAL(...)
#include <../AudioMixerOps.h>
#include <benchmark/benchmark.h>
using namespace android;
template <int MIXTYPE, int NCHAN>
static void BM_VolumeRampMulti(benchmark::State& state) {
constexpr size_t FRAME_COUNT = 1000;
constexpr size_t SAMPLE_COUNT = FRAME_COUNT * NCHAN;
// data inialized to 0.
float out[SAMPLE_COUNT]{};
float in[SAMPLE_COUNT]{};
float aux[FRAME_COUNT]{};
// volume initialized to 0
float vola = 0.f;
float vol[2] = {0.f, 0.f};
// some volume increment
float volainc = 0.01f;
float volinc[2] = {0.01f, 0.01f};
while (state.KeepRunning()) {
benchmark::DoNotOptimize(out);
benchmark::DoNotOptimize(in);
volumeRampMulti<MIXTYPE, NCHAN>(out, FRAME_COUNT, in, aux, vol, volinc, &vola, volainc);
benchmark::ClobberMemory();
}
}
template <int MIXTYPE, int NCHAN>
static void BM_VolumeMulti(benchmark::State& state) {
constexpr size_t FRAME_COUNT = 1000;
constexpr size_t SAMPLE_COUNT = FRAME_COUNT * NCHAN;
// data inialized to 0.
float out[SAMPLE_COUNT]{};
float in[SAMPLE_COUNT]{};
float aux[FRAME_COUNT]{};
// volume initialized to 0
float vola = 0.f;
float vol[2] = {0.f, 0.f};
while (state.KeepRunning()) {
benchmark::DoNotOptimize(out);
benchmark::DoNotOptimize(in);
volumeMulti<MIXTYPE, NCHAN>(out, FRAME_COUNT, in, aux, vol, vola);
benchmark::ClobberMemory();
}
}
BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI, 2);
BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI_SAVEONLY, 2);
BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI_STEREOVOL, 2);
BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI_SAVEONLY_STEREOVOL, 2);
BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI, 4);
BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI_SAVEONLY, 4);
BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI_STEREOVOL, 4);
BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI_SAVEONLY_STEREOVOL, 4);
BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI, 5);
BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI_SAVEONLY, 5);
BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI_STEREOVOL, 5);
BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI_SAVEONLY_STEREOVOL, 5);
BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI, 8);
BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI_SAVEONLY, 8);
BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI_STEREOVOL, 8);
BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI_SAVEONLY_STEREOVOL, 8);
BENCHMARK_TEMPLATE(BM_VolumeMulti, MIXTYPE_MULTI, 8);
BENCHMARK_TEMPLATE(BM_VolumeMulti, MIXTYPE_MULTI_SAVEONLY, 8);
BENCHMARK_TEMPLATE(BM_VolumeMulti, MIXTYPE_MULTI_STEREOVOL, 8);
BENCHMARK_TEMPLATE(BM_VolumeMulti, MIXTYPE_MULTI_SAVEONLY_STEREOVOL, 8);
BENCHMARK_MAIN();

@ -0,0 +1,52 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <inttypes.h>
#include <type_traits>
#include "../../../../system/media/audio_utils/include/audio_utils/primitives.h"
#define LOG_ALWAYS_FATAL(...)
#include <../AudioMixerOps.h>
using namespace android;
template <int MIXTYPE, int NCHAN>
static void checkVolumeRampMulti() {
constexpr size_t FRAME_COUNT = 1000;
constexpr size_t SAMPLE_COUNT = FRAME_COUNT * NCHAN;
// data inialized to 0.
float out[SAMPLE_COUNT]{};
float in[SAMPLE_COUNT]{};
float aux[FRAME_COUNT]{};
// volume initialized to 0
float vola = 0.f;
float vol[2] = {0.f, 0.f};
// some volume increment
float volainc = 0.01f;
float volinc[2] = {0.01f, 0.01f};
// try the multi ramp code.
volumeRampMulti<MIXTYPE, NCHAN>(out, FRAME_COUNT, in, aux, vol, volinc, &vola, volainc);
}
// Use this to check the objdump to ensure reasonable code.
int main() {
checkVolumeRampMulti<MIXTYPE_MULTI_STEREOVOL, 5>();
return EXIT_SUCCESS;
}
Loading…
Cancel
Save