diff options
author | Andy Hung <hunga@google.com> | 2014-06-17 15:25:47 -0700 |
---|---|---|
committer | Andy Hung <hunga@google.com> | 2014-06-30 16:35:59 -0700 |
commit | 296b741e8eb38e749e3202182f703a2e30ee5f1f (patch) | |
tree | 7924f384f8e3ef4f063993e8721c67eb466cf58a /services/audioflinger/AudioMixerOps.h | |
parent | f92f22becdf7fce1f55d5ebd80ac2caa2ad55602 (diff) | |
download | frameworks_av-296b741e8eb38e749e3202182f703a2e30ee5f1f.zip frameworks_av-296b741e8eb38e749e3202182f703a2e30ee5f1f.tar.gz frameworks_av-296b741e8eb38e749e3202182f703a2e30ee5f1f.tar.bz2 |
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
Diffstat (limited to 'services/audioflinger/AudioMixerOps.h')
-rw-r--r-- | services/audioflinger/AudioMixerOps.h | 361 |
1 files changed, 361 insertions, 0 deletions
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<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; +}; + + +/* 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 <TO, TI, TV> = <float, float, float> + * needs to be accelerated. This is perhaps the easiest form to do quickly as well. + */ + +template <typename TO, typename TI, typename TV> +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<int32_t, int16_t, int16_t>(int16_t value, int16_t volume) { + return value * volume; +} + +template <> +inline int32_t MixMul<int32_t, int32_t, int16_t>(int32_t value, int16_t volume) { + return (value >> 12) * volume; +} + +template <> +inline int32_t MixMul<int32_t, int16_t, int32_t>(int16_t value, int32_t volume) { + return value * (volume >> 16); +} + +template <> +inline int32_t MixMul<int32_t, int32_t, int32_t>(int32_t value, int32_t volume) { + return (value >> 12) * (volume >> 16); +} + +template <> +inline float MixMul<float, float, int16_t>(float value, int16_t volume) { + static const float norm = 1. / (1 << 12); + return value * volume * norm; +} + +template <> +inline float MixMul<float, float, int32_t>(float value, int32_t volume) { + static const float norm = 1. / (1 << 28); + return value * volume * norm; +} + +template <> +inline int16_t MixMul<int16_t, float, int16_t>(float value, int16_t volume) { + return clamp16_from_float(MixMul<float, float, int16_t>(value, volume)); +} + +template <> +inline int16_t MixMul<int16_t, float, int32_t>(float value, int32_t volume) { + return clamp16_from_float(MixMul<float, float, int32_t>(value, volume)); +} + +template <> +inline float MixMul<float, int16_t, int16_t>(int16_t value, int16_t volume) { + static const float norm = 1. / (1 << (15 + 12)); + return static_cast<float>(value) * static_cast<float>(volume) * norm; +} + +template <> +inline float MixMul<float, int16_t, int32_t>(int16_t value, int32_t volume) { + static const float norm = 1. / (1ULL << (15 + 28)); + return static_cast<float>(value) * static_cast<float>(volume) * norm; +} + +template <> +inline int16_t MixMul<int16_t, int16_t, int16_t>(int16_t value, int16_t volume) { + return clamp16(MixMul<int32_t, int16_t, int16_t>(value, volume) >> 12); +} + +template <> +inline int16_t MixMul<int16_t, int32_t, int16_t>(int32_t value, int16_t volume) { + return clamp16(MixMul<int32_t, int32_t, int16_t>(value, volume) >> 12); +} + +template <> +inline int16_t MixMul<int16_t, int16_t, int32_t>(int16_t value, int32_t volume) { + return clamp16(MixMul<int32_t, int16_t, int32_t>(value, volume) >> 12); +} + +template <> +inline int16_t MixMul<int16_t, int32_t, int32_t>(int32_t value, int32_t volume) { + return clamp16(MixMul<int32_t, int32_t, int32_t>(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 <typename TO, typename TI> +inline void MixAccum(TO *auxaccum, TI value) { + if (!is_same<TO, TI>::value) { + LOG_ALWAYS_FATAL("MixAccum type not properly specialized: %d %d\n", + sizeof(TO), sizeof(TI)); + } + *auxaccum += value; +} + +template<> +inline void MixAccum<float, int16_t>(float *auxaccum, int16_t value) { + static const float norm = 1. / (1 << 15); + *auxaccum += norm * value; +} + +template<> +inline void MixAccum<float, int32_t>(float *auxaccum, int32_t value) { + static const float norm = 1. / (1 << 27); + *auxaccum += norm * value; +} + +template<> +inline void MixAccum<int32_t, int16_t>(int32_t *auxaccum, int16_t value) { + *auxaccum += value << 12; +} + +template<> +inline void MixAccum<int32_t, float>(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 <typename TO, typename TI, typename TV, typename TA> +inline TO MixMulAux(TI value, TV volume, TA *auxaccum) { + MixAccum<TA, TI>(auxaccum, value); + return MixMul<TO, TI, TV>(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 <int MIXTYPE, int NCHAN, + typename TO, typename TI, typename TV, typename TA, typename TAV> +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<TO, TI, TV, TA>(*in++, vol[i], &auxaccum); + vol[i] += volinc[i]; + } + 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: + for (int i = 0; i < NCHAN; ++i) { + *out++ += MixMulAux<TO, TI, TV, TA>(*in, vol[i], &auxaccum); + vol[i] += volinc[i]; + } + in++; + break; + default: + LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE); + break; + } + auxaccum /= NCHAN; + *aux++ += MixMul<TA, TA, TAV>(auxaccum, *vola); + vola[0] += volainc; + } while (--frameCount); + } else { + do { + switch (MIXTYPE) { + case MIXTYPE_MULTI: + for (int i = 0; i < NCHAN; ++i) { + *out++ += MixMul<TO, TI, TV>(*in++, vol[i]); + vol[i] += volinc[i]; + } + 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: + for (int i = 0; i < NCHAN; ++i) { + *out++ += MixMul<TO, TI, TV>(*in, vol[i]); + vol[i] += volinc[i]; + } + in++; + break; + default: + LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE); + break; + } + } while (--frameCount); + } +} + +template <int MIXTYPE, int NCHAN, + typename TO, typename TI, typename TV, typename TA, typename TAV> +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<TO, TI, TV, TA>(*in++, vol[i], &auxaccum); + } + 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: + for (int i = 0; i < NCHAN; ++i) { + *out++ += MixMulAux<TO, TI, TV, TA>(*in, vol[i], &auxaccum); + } + in++; + break; + default: + LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE); + break; + } + auxaccum /= NCHAN; + *aux++ += MixMul<TA, TA, TAV>(auxaccum, vola); + } while (--frameCount); + } else { + do { + switch (MIXTYPE) { + case MIXTYPE_MULTI: + for (int i = 0; i < NCHAN; ++i) { + *out++ += MixMul<TO, TI, TV>(*in++, vol[i]); + } + break; + case MIXTYPE_MULTI_SAVEONLY: + for (int i = 0; i < NCHAN; ++i) { + *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; + default: + LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE); + break; + } + } while (--frameCount); + } +} + +}; + +#endif /* ANDROID_AUDIO_MIXER_OPS_H */ |