AudioMixerOps.h revision 296b741e8eb38e749e3202182f703a2e30ee5f1f
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef ANDROID_AUDIO_MIXER_OPS_H
18#define ANDROID_AUDIO_MIXER_OPS_H
19
20namespace android {
21
22/* Behavior of is_same<>::value is true if the types are identical,
23 * false otherwise. Identical to the STL std::is_same.
24 */
25template<typename T, typename U>
26struct is_same
27{
28    static const bool value = false;
29};
30
31template<typename T>
32struct is_same<T, T>  // partial specialization
33{
34    static const bool value = true;
35};
36
37
38/* MixMul is a multiplication operator to scale an audio input signal
39 * by a volume gain, with the formula:
40 *
41 * O(utput) = I(nput) * V(olume)
42 *
43 * The output, input, and volume may have different types.
44 * There are 27 variants, of which 14 are actually defined in an
45 * explicitly templated class.
46 *
47 * The following type variables and the underlying meaning:
48 *
49 * Output type       TO: int32_t (Q4.27) or int16_t (Q.15) or float [-1,1]
50 * Input signal type TI: int32_t (Q4.27) or int16_t (Q.15) or float [-1,1]
51 * Volume type       TV: int32_t (U4.28) or int16_t (U4.12) or float [-1,1]
52 *
53 * For high precision audio, only the <TO, TI, TV> = <float, float, float>
54 * needs to be accelerated. This is perhaps the easiest form to do quickly as well.
55 */
56
57template <typename TO, typename TI, typename TV>
58inline TO MixMul(TI value, TV volume) {
59    COMPILE_TIME_ASSERT_FUNCTION_SCOPE(false);
60    // should not be here :-).
61    // To avoid mistakes, this template is always specialized.
62    return value * volume;
63}
64
65template <>
66inline int32_t MixMul<int32_t, int16_t, int16_t>(int16_t value, int16_t volume) {
67    return value * volume;
68}
69
70template <>
71inline int32_t MixMul<int32_t, int32_t, int16_t>(int32_t value, int16_t volume) {
72    return (value >> 12) * volume;
73}
74
75template <>
76inline int32_t MixMul<int32_t, int16_t, int32_t>(int16_t value, int32_t volume) {
77    return value * (volume >> 16);
78}
79
80template <>
81inline int32_t MixMul<int32_t, int32_t, int32_t>(int32_t value, int32_t volume) {
82    return (value >> 12) * (volume >> 16);
83}
84
85template <>
86inline float MixMul<float, float, int16_t>(float value, int16_t volume) {
87    static const float norm = 1. / (1 << 12);
88    return value * volume * norm;
89}
90
91template <>
92inline float MixMul<float, float, int32_t>(float value, int32_t volume) {
93    static const float norm = 1. / (1 << 28);
94    return value * volume * norm;
95}
96
97template <>
98inline int16_t MixMul<int16_t, float, int16_t>(float value, int16_t volume) {
99    return clamp16_from_float(MixMul<float, float, int16_t>(value, volume));
100}
101
102template <>
103inline int16_t MixMul<int16_t, float, int32_t>(float value, int32_t volume) {
104    return clamp16_from_float(MixMul<float, float, int32_t>(value, volume));
105}
106
107template <>
108inline float MixMul<float, int16_t, int16_t>(int16_t value, int16_t volume) {
109    static const float norm = 1. / (1 << (15 + 12));
110    return static_cast<float>(value) * static_cast<float>(volume) * norm;
111}
112
113template <>
114inline float MixMul<float, int16_t, int32_t>(int16_t value, int32_t volume) {
115    static const float norm = 1. / (1ULL << (15 + 28));
116    return static_cast<float>(value) * static_cast<float>(volume) * norm;
117}
118
119template <>
120inline int16_t MixMul<int16_t, int16_t, int16_t>(int16_t value, int16_t volume) {
121    return clamp16(MixMul<int32_t, int16_t, int16_t>(value, volume) >> 12);
122}
123
124template <>
125inline int16_t MixMul<int16_t, int32_t, int16_t>(int32_t value, int16_t volume) {
126    return clamp16(MixMul<int32_t, int32_t, int16_t>(value, volume) >> 12);
127}
128
129template <>
130inline int16_t MixMul<int16_t, int16_t, int32_t>(int16_t value, int32_t volume) {
131    return clamp16(MixMul<int32_t, int16_t, int32_t>(value, volume) >> 12);
132}
133
134template <>
135inline int16_t MixMul<int16_t, int32_t, int32_t>(int32_t value, int32_t volume) {
136    return clamp16(MixMul<int32_t, int32_t, int32_t>(value, volume) >> 12);
137}
138
139/*
140 * MixAccum is used to add into an accumulator register of a possibly different
141 * type. The TO and TI types are the same as MixMul.
142 */
143
144template <typename TO, typename TI>
145inline void MixAccum(TO *auxaccum, TI value) {
146    if (!is_same<TO, TI>::value) {
147        LOG_ALWAYS_FATAL("MixAccum type not properly specialized: %d %d\n",
148                sizeof(TO), sizeof(TI));
149    }
150    *auxaccum += value;
151}
152
153template<>
154inline void MixAccum<float, int16_t>(float *auxaccum, int16_t value) {
155    static const float norm = 1. / (1 << 15);
156    *auxaccum += norm * value;
157}
158
159template<>
160inline void MixAccum<float, int32_t>(float *auxaccum, int32_t value) {
161    static const float norm = 1. / (1 << 27);
162    *auxaccum += norm * value;
163}
164
165template<>
166inline void MixAccum<int32_t, int16_t>(int32_t *auxaccum, int16_t value) {
167    *auxaccum += value << 12;
168}
169
170template<>
171inline void MixAccum<int32_t, float>(int32_t *auxaccum, float value) {
172    *auxaccum += clampq4_27_from_float(value);
173}
174
175/* MixMulAux is just like MixMul except it combines with
176 * an accumulator operation MixAccum.
177 */
178
179template <typename TO, typename TI, typename TV, typename TA>
180inline TO MixMulAux(TI value, TV volume, TA *auxaccum) {
181    MixAccum<TA, TI>(auxaccum, value);
182    return MixMul<TO, TI, TV>(value, volume);
183}
184
185/* MIXTYPE is used to determine how the samples in the input frame
186 * are mixed with volume gain into the output frame.
187 * See the volumeRampMulti functions below for more details.
188 */
189enum {
190    MIXTYPE_MULTI,
191    MIXTYPE_MONOEXPAND,
192    MIXTYPE_MULTI_SAVEONLY,
193};
194
195/*
196 * The volumeRampMulti and volumeRamp functions take a MIXTYPE
197 * which indicates the per-frame mixing and accumulation strategy.
198 *
199 * MIXTYPE_MULTI:
200 *   NCHAN represents number of input and output channels.
201 *   TO: int32_t (Q4.27) or float
202 *   TI: int32_t (Q4.27) or int16_t (Q0.15) or float
203 *   TV: int32_t (U4.28) or int16_t (U4.12) or float
204 *   vol: represents a volume array.
205 *
206 *   This accumulates into the out pointer.
207 *
208 * MIXTYPE_MONOEXPAND:
209 *   Single input channel. NCHAN represents number of output channels.
210 *   TO: int32_t (Q4.27) or float
211 *   TI: int32_t (Q4.27) or int16_t (Q0.15) or float
212 *   TV: int32_t (U4.28) or int16_t (U4.12) or float
213 *   Input channel count is 1.
214 *   vol: represents volume array.
215 *
216 *   This accumulates into the out pointer.
217 *
218 * MIXTYPE_MULTI_SAVEONLY:
219 *   NCHAN represents number of input and output channels.
220 *   TO: int16_t (Q.15) or float
221 *   TI: int32_t (Q4.27) or int16_t (Q0.15) or float
222 *   TV: int32_t (U4.28) or int16_t (U4.12) or float
223 *   vol: represents a volume array.
224 *
225 *   MIXTYPE_MULTI_SAVEONLY does not accumulate into the out pointer.
226 */
227
228template <int MIXTYPE, int NCHAN,
229        typename TO, typename TI, typename TV, typename TA, typename TAV>
230inline void volumeRampMulti(TO* out, size_t frameCount,
231        const TI* in, TA* aux, TV *vol, const TV *volinc, TAV *vola, TAV volainc)
232{
233#ifdef ALOGVV
234    ALOGVV("volumeRampMulti, MIXTYPE:%d\n", MIXTYPE);
235#endif
236    if (aux != NULL) {
237        do {
238            TA auxaccum = 0;
239            switch (MIXTYPE) {
240            case MIXTYPE_MULTI:
241                for (int i = 0; i < NCHAN; ++i) {
242                    *out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum);
243                    vol[i] += volinc[i];
244                }
245                break;
246            case MIXTYPE_MULTI_SAVEONLY:
247                for (int i = 0; i < NCHAN; ++i) {
248                    *out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum);
249                    vol[i] += volinc[i];
250                }
251                break;
252            case MIXTYPE_MONOEXPAND:
253                for (int i = 0; i < NCHAN; ++i) {
254                    *out++ += MixMulAux<TO, TI, TV, TA>(*in, vol[i], &auxaccum);
255                    vol[i] += volinc[i];
256                }
257                in++;
258                break;
259            default:
260                LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE);
261                break;
262            }
263            auxaccum /= NCHAN;
264            *aux++ += MixMul<TA, TA, TAV>(auxaccum, *vola);
265            vola[0] += volainc;
266        } while (--frameCount);
267    } else {
268        do {
269            switch (MIXTYPE) {
270            case MIXTYPE_MULTI:
271                for (int i = 0; i < NCHAN; ++i) {
272                    *out++ += MixMul<TO, TI, TV>(*in++, vol[i]);
273                    vol[i] += volinc[i];
274                }
275                break;
276            case MIXTYPE_MULTI_SAVEONLY:
277                for (int i = 0; i < NCHAN; ++i) {
278                    *out++ = MixMul<TO, TI, TV>(*in++, vol[i]);
279                    vol[i] += volinc[i];
280                }
281                break;
282            case MIXTYPE_MONOEXPAND:
283                for (int i = 0; i < NCHAN; ++i) {
284                    *out++ += MixMul<TO, TI, TV>(*in, vol[i]);
285                    vol[i] += volinc[i];
286                }
287                in++;
288                break;
289            default:
290                LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE);
291                break;
292            }
293        } while (--frameCount);
294    }
295}
296
297template <int MIXTYPE, int NCHAN,
298        typename TO, typename TI, typename TV, typename TA, typename TAV>
299inline void volumeMulti(TO* out, size_t frameCount,
300        const TI* in, TA* aux, const TV *vol, TAV vola)
301{
302#ifdef ALOGVV
303    ALOGVV("volumeMulti MIXTYPE:%d\n", MIXTYPE);
304#endif
305    if (aux != NULL) {
306        do {
307            TA auxaccum = 0;
308            switch (MIXTYPE) {
309            case MIXTYPE_MULTI:
310                for (int i = 0; i < NCHAN; ++i) {
311                    *out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum);
312                }
313                break;
314            case MIXTYPE_MULTI_SAVEONLY:
315                for (int i = 0; i < NCHAN; ++i) {
316                    *out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum);
317                }
318                break;
319            case MIXTYPE_MONOEXPAND:
320                for (int i = 0; i < NCHAN; ++i) {
321                    *out++ += MixMulAux<TO, TI, TV, TA>(*in, vol[i], &auxaccum);
322                }
323                in++;
324                break;
325            default:
326                LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE);
327                break;
328            }
329            auxaccum /= NCHAN;
330            *aux++ += MixMul<TA, TA, TAV>(auxaccum, vola);
331        } while (--frameCount);
332    } else {
333        do {
334            switch (MIXTYPE) {
335            case MIXTYPE_MULTI:
336                for (int i = 0; i < NCHAN; ++i) {
337                    *out++ += MixMul<TO, TI, TV>(*in++, vol[i]);
338                }
339                break;
340            case MIXTYPE_MULTI_SAVEONLY:
341                for (int i = 0; i < NCHAN; ++i) {
342                    *out++ = MixMul<TO, TI, TV>(*in++, vol[i]);
343                }
344                break;
345            case MIXTYPE_MONOEXPAND:
346                for (int i = 0; i < NCHAN; ++i) {
347                    *out++ += MixMul<TO, TI, TV>(*in, vol[i]);
348                }
349                in++;
350                break;
351            default:
352                LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE);
353                break;
354            }
355        } while (--frameCount);
356    }
357}
358
359};
360
361#endif /* ANDROID_AUDIO_MIXER_OPS_H */
362