blob: f7376a8956b1654e1c18d722c743203cb841522c [file] [log] [blame]
Andy Hung296b7412014-06-17 15:25:47 -07001/*
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
Andy Hung5e58b0a2014-06-23 19:07:29 -0700139/* Required for floating point volume. Some are needed for compilation but
140 * are not needed in execution and should be removed from the final build by
141 * an optimizing compiler.
142 */
143template <>
144inline float MixMul<float, float, float>(float value, float volume) {
145 return value * volume;
146}
147
148template <>
149inline float MixMul<float, int16_t, float>(int16_t value, float volume) {
150 static const float float_from_q_15 = 1. / (1 << 15);
151 return value * volume * float_from_q_15;
152}
153
154template <>
155inline int32_t MixMul<int32_t, int32_t, float>(int32_t value, float volume) {
156 LOG_ALWAYS_FATAL("MixMul<int32_t, int32_t, float> Runtime Should not be here");
157 return value * volume;
158}
159
160template <>
161inline int32_t MixMul<int32_t, int16_t, float>(int16_t value, float volume) {
162 LOG_ALWAYS_FATAL("MixMul<int32_t, int16_t, float> Runtime Should not be here");
163 static const float u4_12_from_float = (1 << 12);
164 return value * volume * u4_12_from_float;
165}
166
167template <>
168inline int16_t MixMul<int16_t, int16_t, float>(int16_t value, float volume) {
169 LOG_ALWAYS_FATAL("MixMul<int16_t, int16_t, float> Runtime Should not be here");
170 return value * volume;
171}
172
173template <>
174inline int16_t MixMul<int16_t, float, float>(float value, float volume) {
175 static const float q_15_from_float = (1 << 15);
176 return value * volume * q_15_from_float;
177}
178
Andy Hung296b7412014-06-17 15:25:47 -0700179/*
180 * MixAccum is used to add into an accumulator register of a possibly different
181 * type. The TO and TI types are the same as MixMul.
182 */
183
184template <typename TO, typename TI>
185inline void MixAccum(TO *auxaccum, TI value) {
186 if (!is_same<TO, TI>::value) {
Glenn Kastena4daf0b2014-07-28 16:34:45 -0700187 LOG_ALWAYS_FATAL("MixAccum type not properly specialized: %zu %zu\n",
Andy Hung296b7412014-06-17 15:25:47 -0700188 sizeof(TO), sizeof(TI));
189 }
190 *auxaccum += value;
191}
192
193template<>
194inline void MixAccum<float, int16_t>(float *auxaccum, int16_t value) {
195 static const float norm = 1. / (1 << 15);
196 *auxaccum += norm * value;
197}
198
199template<>
200inline void MixAccum<float, int32_t>(float *auxaccum, int32_t value) {
201 static const float norm = 1. / (1 << 27);
202 *auxaccum += norm * value;
203}
204
205template<>
206inline void MixAccum<int32_t, int16_t>(int32_t *auxaccum, int16_t value) {
207 *auxaccum += value << 12;
208}
209
210template<>
211inline void MixAccum<int32_t, float>(int32_t *auxaccum, float value) {
212 *auxaccum += clampq4_27_from_float(value);
213}
214
215/* MixMulAux is just like MixMul except it combines with
216 * an accumulator operation MixAccum.
217 */
218
219template <typename TO, typename TI, typename TV, typename TA>
220inline TO MixMulAux(TI value, TV volume, TA *auxaccum) {
221 MixAccum<TA, TI>(auxaccum, value);
222 return MixMul<TO, TI, TV>(value, volume);
223}
224
225/* MIXTYPE is used to determine how the samples in the input frame
226 * are mixed with volume gain into the output frame.
227 * See the volumeRampMulti functions below for more details.
228 */
229enum {
230 MIXTYPE_MULTI,
231 MIXTYPE_MONOEXPAND,
232 MIXTYPE_MULTI_SAVEONLY,
Andy Hunge93b6b72014-07-17 21:30:53 -0700233 MIXTYPE_MULTI_MONOVOL,
234 MIXTYPE_MULTI_SAVEONLY_MONOVOL,
Andy Hung296b7412014-06-17 15:25:47 -0700235};
236
237/*
238 * The volumeRampMulti and volumeRamp functions take a MIXTYPE
239 * which indicates the per-frame mixing and accumulation strategy.
240 *
241 * MIXTYPE_MULTI:
242 * NCHAN represents number of input and output channels.
243 * TO: int32_t (Q4.27) or float
244 * TI: int32_t (Q4.27) or int16_t (Q0.15) or float
245 * TV: int32_t (U4.28) or int16_t (U4.12) or float
246 * vol: represents a volume array.
247 *
248 * This accumulates into the out pointer.
249 *
250 * MIXTYPE_MONOEXPAND:
251 * Single input channel. NCHAN represents number of output channels.
252 * TO: int32_t (Q4.27) or float
253 * TI: int32_t (Q4.27) or int16_t (Q0.15) or float
254 * TV: int32_t (U4.28) or int16_t (U4.12) or float
255 * Input channel count is 1.
256 * vol: represents volume array.
257 *
258 * This accumulates into the out pointer.
259 *
260 * MIXTYPE_MULTI_SAVEONLY:
261 * NCHAN represents number of input and output channels.
262 * TO: int16_t (Q.15) or float
263 * TI: int32_t (Q4.27) or int16_t (Q0.15) or float
264 * TV: int32_t (U4.28) or int16_t (U4.12) or float
265 * vol: represents a volume array.
266 *
267 * MIXTYPE_MULTI_SAVEONLY does not accumulate into the out pointer.
Andy Hunge93b6b72014-07-17 21:30:53 -0700268 *
269 * MIXTYPE_MULTI_MONOVOL:
270 * Same as MIXTYPE_MULTI, but uses only volume[0].
271 *
272 * MIXTYPE_MULTI_SAVEONLY_MONOVOL:
273 * Same as MIXTYPE_MULTI_SAVEONLY, but uses only volume[0].
274 *
Andy Hung296b7412014-06-17 15:25:47 -0700275 */
276
277template <int MIXTYPE, int NCHAN,
278 typename TO, typename TI, typename TV, typename TA, typename TAV>
279inline void volumeRampMulti(TO* out, size_t frameCount,
280 const TI* in, TA* aux, TV *vol, const TV *volinc, TAV *vola, TAV volainc)
281{
282#ifdef ALOGVV
283 ALOGVV("volumeRampMulti, MIXTYPE:%d\n", MIXTYPE);
284#endif
285 if (aux != NULL) {
286 do {
287 TA auxaccum = 0;
288 switch (MIXTYPE) {
289 case MIXTYPE_MULTI:
290 for (int i = 0; i < NCHAN; ++i) {
291 *out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum);
292 vol[i] += volinc[i];
293 }
294 break;
Andy Hung296b7412014-06-17 15:25:47 -0700295 case MIXTYPE_MONOEXPAND:
296 for (int i = 0; i < NCHAN; ++i) {
297 *out++ += MixMulAux<TO, TI, TV, TA>(*in, vol[i], &auxaccum);
298 vol[i] += volinc[i];
299 }
300 in++;
301 break;
Andy Hunge93b6b72014-07-17 21:30:53 -0700302 case MIXTYPE_MULTI_SAVEONLY:
303 for (int i = 0; i < NCHAN; ++i) {
304 *out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum);
305 vol[i] += volinc[i];
306 }
307 break;
308 case MIXTYPE_MULTI_MONOVOL:
309 for (int i = 0; i < NCHAN; ++i) {
310 *out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[0], &auxaccum);
311 }
312 vol[0] += volinc[0];
313 break;
314 case MIXTYPE_MULTI_SAVEONLY_MONOVOL:
315 for (int i = 0; i < NCHAN; ++i) {
316 *out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[0], &auxaccum);
317 }
318 vol[0] += volinc[0];
319 break;
Andy Hung296b7412014-06-17 15:25:47 -0700320 default:
321 LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE);
322 break;
323 }
324 auxaccum /= NCHAN;
325 *aux++ += MixMul<TA, TA, TAV>(auxaccum, *vola);
326 vola[0] += volainc;
327 } while (--frameCount);
328 } else {
329 do {
330 switch (MIXTYPE) {
331 case MIXTYPE_MULTI:
332 for (int i = 0; i < NCHAN; ++i) {
333 *out++ += MixMul<TO, TI, TV>(*in++, vol[i]);
334 vol[i] += volinc[i];
335 }
336 break;
Andy Hung296b7412014-06-17 15:25:47 -0700337 case MIXTYPE_MONOEXPAND:
338 for (int i = 0; i < NCHAN; ++i) {
339 *out++ += MixMul<TO, TI, TV>(*in, vol[i]);
340 vol[i] += volinc[i];
341 }
342 in++;
343 break;
Andy Hunge93b6b72014-07-17 21:30:53 -0700344 case MIXTYPE_MULTI_SAVEONLY:
345 for (int i = 0; i < NCHAN; ++i) {
346 *out++ = MixMul<TO, TI, TV>(*in++, vol[i]);
347 vol[i] += volinc[i];
348 }
349 break;
350 case MIXTYPE_MULTI_MONOVOL:
351 for (int i = 0; i < NCHAN; ++i) {
352 *out++ += MixMul<TO, TI, TV>(*in++, vol[0]);
353 }
354 vol[0] += volinc[0];
355 break;
356 case MIXTYPE_MULTI_SAVEONLY_MONOVOL:
357 for (int i = 0; i < NCHAN; ++i) {
358 *out++ = MixMul<TO, TI, TV>(*in++, vol[0]);
359 }
360 vol[0] += volinc[0];
361 break;
Andy Hung296b7412014-06-17 15:25:47 -0700362 default:
363 LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE);
364 break;
365 }
366 } while (--frameCount);
367 }
368}
369
370template <int MIXTYPE, int NCHAN,
371 typename TO, typename TI, typename TV, typename TA, typename TAV>
372inline void volumeMulti(TO* out, size_t frameCount,
373 const TI* in, TA* aux, const TV *vol, TAV vola)
374{
375#ifdef ALOGVV
376 ALOGVV("volumeMulti MIXTYPE:%d\n", MIXTYPE);
377#endif
378 if (aux != NULL) {
379 do {
380 TA auxaccum = 0;
381 switch (MIXTYPE) {
382 case MIXTYPE_MULTI:
383 for (int i = 0; i < NCHAN; ++i) {
384 *out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum);
385 }
386 break;
Andy Hung296b7412014-06-17 15:25:47 -0700387 case MIXTYPE_MONOEXPAND:
388 for (int i = 0; i < NCHAN; ++i) {
389 *out++ += MixMulAux<TO, TI, TV, TA>(*in, vol[i], &auxaccum);
390 }
391 in++;
392 break;
Andy Hunge93b6b72014-07-17 21:30:53 -0700393 case MIXTYPE_MULTI_SAVEONLY:
394 for (int i = 0; i < NCHAN; ++i) {
395 *out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum);
396 }
397 break;
398 case MIXTYPE_MULTI_MONOVOL:
399 for (int i = 0; i < NCHAN; ++i) {
400 *out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[0], &auxaccum);
401 }
402 break;
403 case MIXTYPE_MULTI_SAVEONLY_MONOVOL:
404 for (int i = 0; i < NCHAN; ++i) {
405 *out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[0], &auxaccum);
406 }
407 break;
Andy Hung296b7412014-06-17 15:25:47 -0700408 default:
409 LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE);
410 break;
411 }
412 auxaccum /= NCHAN;
413 *aux++ += MixMul<TA, TA, TAV>(auxaccum, vola);
414 } while (--frameCount);
415 } else {
416 do {
417 switch (MIXTYPE) {
418 case MIXTYPE_MULTI:
419 for (int i = 0; i < NCHAN; ++i) {
420 *out++ += MixMul<TO, TI, TV>(*in++, vol[i]);
421 }
422 break;
Andy Hung296b7412014-06-17 15:25:47 -0700423 case MIXTYPE_MONOEXPAND:
424 for (int i = 0; i < NCHAN; ++i) {
425 *out++ += MixMul<TO, TI, TV>(*in, vol[i]);
426 }
427 in++;
428 break;
Andy Hunge93b6b72014-07-17 21:30:53 -0700429 case MIXTYPE_MULTI_SAVEONLY:
430 for (int i = 0; i < NCHAN; ++i) {
431 *out++ = MixMul<TO, TI, TV>(*in++, vol[i]);
432 }
433 break;
434 case MIXTYPE_MULTI_MONOVOL:
435 for (int i = 0; i < NCHAN; ++i) {
436 *out++ += MixMul<TO, TI, TV>(*in++, vol[0]);
437 }
438 break;
439 case MIXTYPE_MULTI_SAVEONLY_MONOVOL:
440 for (int i = 0; i < NCHAN; ++i) {
441 *out++ = MixMul<TO, TI, TV>(*in++, vol[0]);
442 }
443 break;
Andy Hung296b7412014-06-17 15:25:47 -0700444 default:
445 LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE);
446 break;
447 }
448 } while (--frameCount);
449 }
450}
451
452};
453
454#endif /* ANDROID_AUDIO_MIXER_OPS_H */