From 296b741e8eb38e749e3202182f703a2e30ee5f1f Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Tue, 17 Jun 2014 15:25:47 -0700 Subject: Add new AudioMixer processing hooks This change adds new process and thread hooks for multi-format handling. It is enabled by setting kUseNewMixer = true. Change-Id: I262a3d2e4411f8cef7370a497b77a34eb55f1f86 --- services/audioflinger/AudioMixerOps.h | 361 ++++++++++++++++++++++++++++++++++ 1 file changed, 361 insertions(+) create mode 100644 services/audioflinger/AudioMixerOps.h (limited to 'services/audioflinger/AudioMixerOps.h') diff --git a/services/audioflinger/AudioMixerOps.h b/services/audioflinger/AudioMixerOps.h new file mode 100644 index 0000000..de92946 --- /dev/null +++ b/services/audioflinger/AudioMixerOps.h @@ -0,0 +1,361 @@ +/* + * Copyright (C) 2014 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. + */ + +#ifndef ANDROID_AUDIO_MIXER_OPS_H +#define ANDROID_AUDIO_MIXER_OPS_H + +namespace android { + +/* Behavior of is_same<>::value is true if the types are identical, + * false otherwise. Identical to the STL std::is_same. + */ +template +struct is_same +{ + static const bool value = false; +}; + +template +struct is_same // partial specialization +{ + static const bool value = true; +}; + + +/* MixMul is a multiplication operator to scale an audio input signal + * by a volume gain, with the formula: + * + * O(utput) = I(nput) * V(olume) + * + * The output, input, and volume may have different types. + * There are 27 variants, of which 14 are actually defined in an + * explicitly templated class. + * + * The following type variables and the underlying meaning: + * + * Output type TO: int32_t (Q4.27) or int16_t (Q.15) or float [-1,1] + * Input signal type TI: int32_t (Q4.27) or int16_t (Q.15) or float [-1,1] + * Volume type TV: int32_t (U4.28) or int16_t (U4.12) or float [-1,1] + * + * For high precision audio, only the = + * needs to be accelerated. This is perhaps the easiest form to do quickly as well. + */ + +template +inline TO MixMul(TI value, TV volume) { + COMPILE_TIME_ASSERT_FUNCTION_SCOPE(false); + // should not be here :-). + // To avoid mistakes, this template is always specialized. + return value * volume; +} + +template <> +inline int32_t MixMul(int16_t value, int16_t volume) { + return value * volume; +} + +template <> +inline int32_t MixMul(int32_t value, int16_t volume) { + return (value >> 12) * volume; +} + +template <> +inline int32_t MixMul(int16_t value, int32_t volume) { + return value * (volume >> 16); +} + +template <> +inline int32_t MixMul(int32_t value, int32_t volume) { + return (value >> 12) * (volume >> 16); +} + +template <> +inline float MixMul(float value, int16_t volume) { + static const float norm = 1. / (1 << 12); + return value * volume * norm; +} + +template <> +inline float MixMul(float value, int32_t volume) { + static const float norm = 1. / (1 << 28); + return value * volume * norm; +} + +template <> +inline int16_t MixMul(float value, int16_t volume) { + return clamp16_from_float(MixMul(value, volume)); +} + +template <> +inline int16_t MixMul(float value, int32_t volume) { + return clamp16_from_float(MixMul(value, volume)); +} + +template <> +inline float MixMul(int16_t value, int16_t volume) { + static const float norm = 1. / (1 << (15 + 12)); + return static_cast(value) * static_cast(volume) * norm; +} + +template <> +inline float MixMul(int16_t value, int32_t volume) { + static const float norm = 1. / (1ULL << (15 + 28)); + return static_cast(value) * static_cast(volume) * norm; +} + +template <> +inline int16_t MixMul(int16_t value, int16_t volume) { + return clamp16(MixMul(value, volume) >> 12); +} + +template <> +inline int16_t MixMul(int32_t value, int16_t volume) { + return clamp16(MixMul(value, volume) >> 12); +} + +template <> +inline int16_t MixMul(int16_t value, int32_t volume) { + return clamp16(MixMul(value, volume) >> 12); +} + +template <> +inline int16_t MixMul(int32_t value, int32_t volume) { + return clamp16(MixMul(value, volume) >> 12); +} + +/* + * MixAccum is used to add into an accumulator register of a possibly different + * type. The TO and TI types are the same as MixMul. + */ + +template +inline void MixAccum(TO *auxaccum, TI value) { + if (!is_same::value) { + LOG_ALWAYS_FATAL("MixAccum type not properly specialized: %d %d\n", + sizeof(TO), sizeof(TI)); + } + *auxaccum += value; +} + +template<> +inline void MixAccum(float *auxaccum, int16_t value) { + static const float norm = 1. / (1 << 15); + *auxaccum += norm * value; +} + +template<> +inline void MixAccum(float *auxaccum, int32_t value) { + static const float norm = 1. / (1 << 27); + *auxaccum += norm * value; +} + +template<> +inline void MixAccum(int32_t *auxaccum, int16_t value) { + *auxaccum += value << 12; +} + +template<> +inline void MixAccum(int32_t *auxaccum, float value) { + *auxaccum += clampq4_27_from_float(value); +} + +/* MixMulAux is just like MixMul except it combines with + * an accumulator operation MixAccum. + */ + +template +inline TO MixMulAux(TI value, TV volume, TA *auxaccum) { + MixAccum(auxaccum, value); + return MixMul(value, volume); +} + +/* MIXTYPE is used to determine how the samples in the input frame + * are mixed with volume gain into the output frame. + * See the volumeRampMulti functions below for more details. + */ +enum { + MIXTYPE_MULTI, + MIXTYPE_MONOEXPAND, + MIXTYPE_MULTI_SAVEONLY, +}; + +/* + * The volumeRampMulti and volumeRamp functions take a MIXTYPE + * which indicates the per-frame mixing and accumulation strategy. + * + * MIXTYPE_MULTI: + * NCHAN represents number of input and output channels. + * TO: int32_t (Q4.27) or float + * TI: int32_t (Q4.27) or int16_t (Q0.15) or float + * TV: int32_t (U4.28) or int16_t (U4.12) or float + * vol: represents a volume array. + * + * This accumulates into the out pointer. + * + * MIXTYPE_MONOEXPAND: + * Single input channel. NCHAN represents number of output channels. + * TO: int32_t (Q4.27) or float + * TI: int32_t (Q4.27) or int16_t (Q0.15) or float + * TV: int32_t (U4.28) or int16_t (U4.12) or float + * Input channel count is 1. + * vol: represents volume array. + * + * This accumulates into the out pointer. + * + * MIXTYPE_MULTI_SAVEONLY: + * NCHAN represents number of input and output channels. + * TO: int16_t (Q.15) or float + * TI: int32_t (Q4.27) or int16_t (Q0.15) or float + * TV: int32_t (U4.28) or int16_t (U4.12) or float + * vol: represents a volume array. + * + * MIXTYPE_MULTI_SAVEONLY does not accumulate into the out pointer. + */ + +template +inline void volumeRampMulti(TO* out, size_t frameCount, + const TI* in, TA* aux, TV *vol, const TV *volinc, TAV *vola, TAV volainc) +{ +#ifdef ALOGVV + ALOGVV("volumeRampMulti, MIXTYPE:%d\n", MIXTYPE); +#endif + if (aux != NULL) { + do { + TA auxaccum = 0; + switch (MIXTYPE) { + case MIXTYPE_MULTI: + for (int i = 0; i < NCHAN; ++i) { + *out++ += MixMulAux(*in++, vol[i], &auxaccum); + vol[i] += volinc[i]; + } + break; + case MIXTYPE_MULTI_SAVEONLY: + for (int i = 0; i < NCHAN; ++i) { + *out++ = MixMulAux(*in++, vol[i], &auxaccum); + vol[i] += volinc[i]; + } + break; + case MIXTYPE_MONOEXPAND: + for (int i = 0; i < NCHAN; ++i) { + *out++ += MixMulAux(*in, vol[i], &auxaccum); + vol[i] += volinc[i]; + } + in++; + break; + default: + LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE); + break; + } + auxaccum /= NCHAN; + *aux++ += MixMul(auxaccum, *vola); + vola[0] += volainc; + } while (--frameCount); + } else { + do { + switch (MIXTYPE) { + case MIXTYPE_MULTI: + for (int i = 0; i < NCHAN; ++i) { + *out++ += MixMul(*in++, vol[i]); + vol[i] += volinc[i]; + } + break; + case MIXTYPE_MULTI_SAVEONLY: + for (int i = 0; i < NCHAN; ++i) { + *out++ = MixMul(*in++, vol[i]); + vol[i] += volinc[i]; + } + break; + case MIXTYPE_MONOEXPAND: + for (int i = 0; i < NCHAN; ++i) { + *out++ += MixMul(*in, vol[i]); + vol[i] += volinc[i]; + } + in++; + break; + default: + LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE); + break; + } + } while (--frameCount); + } +} + +template +inline void volumeMulti(TO* out, size_t frameCount, + const TI* in, TA* aux, const TV *vol, TAV vola) +{ +#ifdef ALOGVV + ALOGVV("volumeMulti MIXTYPE:%d\n", MIXTYPE); +#endif + if (aux != NULL) { + do { + TA auxaccum = 0; + switch (MIXTYPE) { + case MIXTYPE_MULTI: + for (int i = 0; i < NCHAN; ++i) { + *out++ += MixMulAux(*in++, vol[i], &auxaccum); + } + break; + case MIXTYPE_MULTI_SAVEONLY: + for (int i = 0; i < NCHAN; ++i) { + *out++ = MixMulAux(*in++, vol[i], &auxaccum); + } + break; + case MIXTYPE_MONOEXPAND: + for (int i = 0; i < NCHAN; ++i) { + *out++ += MixMulAux(*in, vol[i], &auxaccum); + } + in++; + break; + default: + LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE); + break; + } + auxaccum /= NCHAN; + *aux++ += MixMul(auxaccum, vola); + } while (--frameCount); + } else { + do { + switch (MIXTYPE) { + case MIXTYPE_MULTI: + for (int i = 0; i < NCHAN; ++i) { + *out++ += MixMul(*in++, vol[i]); + } + break; + case MIXTYPE_MULTI_SAVEONLY: + for (int i = 0; i < NCHAN; ++i) { + *out++ = MixMul(*in++, vol[i]); + } + break; + case MIXTYPE_MONOEXPAND: + for (int i = 0; i < NCHAN; ++i) { + *out++ += MixMul(*in, vol[i]); + } + in++; + break; + default: + LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE); + break; + } + } while (--frameCount); + } +} + +}; + +#endif /* ANDROID_AUDIO_MIXER_OPS_H */ -- cgit v1.1