summaryrefslogtreecommitdiffstats
path: root/services/audioflinger/AudioMixerOps.h
diff options
context:
space:
mode:
authorAndy Hung <hunga@google.com>2014-06-17 15:25:47 -0700
committerAndy Hung <hunga@google.com>2014-06-30 16:35:59 -0700
commit296b741e8eb38e749e3202182f703a2e30ee5f1f (patch)
tree7924f384f8e3ef4f063993e8721c67eb466cf58a /services/audioflinger/AudioMixerOps.h
parentf92f22becdf7fce1f55d5ebd80ac2caa2ad55602 (diff)
downloadframeworks_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.h361
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 */