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
20 namespace android {
21
22 // Hack to make static_assert work in a constexpr
23 // https://en.cppreference.com/w/cpp/language/if
24 template <int N>
25 inline constexpr bool dependent_false = false;
26
27 /* MixMul is a multiplication operator to scale an audio input signal
28 * by a volume gain, with the formula:
29 *
30 * O(utput) = I(nput) * V(olume)
31 *
32 * The output, input, and volume may have different types.
33 * There are 27 variants, of which 14 are actually defined in an
34 * explicitly templated class.
35 *
36 * The following type variables and the underlying meaning:
37 *
38 * Output type TO: int32_t (Q4.27) or int16_t (Q.15) or float [-1,1]
39 * Input signal type TI: int32_t (Q4.27) or int16_t (Q.15) or float [-1,1]
40 * Volume type TV: int32_t (U4.28) or int16_t (U4.12) or float [-1,1]
41 *
42 * For high precision audio, only the <TO, TI, TV> = <float, float, float>
43 * needs to be accelerated. This is perhaps the easiest form to do quickly as well.
44 *
45 * A generic version is NOT defined to catch any mistake of using it.
46 */
47
48 template <typename TO, typename TI, typename TV>
49 TO MixMul(TI value, TV volume);
50
51 template <>
52 inline int32_t MixMul<int32_t, int16_t, int16_t>(int16_t value, int16_t volume) {
53 return value * volume;
54 }
55
56 template <>
57 inline int32_t MixMul<int32_t, int32_t, int16_t>(int32_t value, int16_t volume) {
58 return (value >> 12) * volume;
59 }
60
61 template <>
62 inline int32_t MixMul<int32_t, int16_t, int32_t>(int16_t value, int32_t volume) {
63 return value * (volume >> 16);
64 }
65
66 template <>
67 inline int32_t MixMul<int32_t, int32_t, int32_t>(int32_t value, int32_t volume) {
68 return (value >> 12) * (volume >> 16);
69 }
70
71 template <>
72 inline float MixMul<float, float, int16_t>(float value, int16_t volume) {
73 static const float norm = 1. / (1 << 12);
74 return value * volume * norm;
75 }
76
77 template <>
78 inline float MixMul<float, float, int32_t>(float value, int32_t volume) {
79 static const float norm = 1. / (1 << 28);
80 return value * volume * norm;
81 }
82
83 template <>
84 inline int16_t MixMul<int16_t, float, int16_t>(float value, int16_t volume) {
85 return clamp16_from_float(MixMul<float, float, int16_t>(value, volume));
86 }
87
88 template <>
89 inline int16_t MixMul<int16_t, float, int32_t>(float value, int32_t volume) {
90 return clamp16_from_float(MixMul<float, float, int32_t>(value, volume));
91 }
92
93 template <>
94 inline float MixMul<float, int16_t, int16_t>(int16_t value, int16_t volume) {
95 static const float norm = 1. / (1 << (15 + 12));
96 return static_cast<float>(value) * static_cast<float>(volume) * norm;
97 }
98
99 template <>
100 inline float MixMul<float, int16_t, int32_t>(int16_t value, int32_t volume) {
101 static const float norm = 1. / (1ULL << (15 + 28));
102 return static_cast<float>(value) * static_cast<float>(volume) * norm;
103 }
104
105 template <>
106 inline int16_t MixMul<int16_t, int16_t, int16_t>(int16_t value, int16_t volume) {
107 return clamp16(MixMul<int32_t, int16_t, int16_t>(value, volume) >> 12);
108 }
109
110 template <>
111 inline int16_t MixMul<int16_t, int32_t, int16_t>(int32_t value, int16_t volume) {
112 return clamp16(MixMul<int32_t, int32_t, int16_t>(value, volume) >> 12);
113 }
114
115 template <>
116 inline int16_t MixMul<int16_t, int16_t, int32_t>(int16_t value, int32_t volume) {
117 return clamp16(MixMul<int32_t, int16_t, int32_t>(value, volume) >> 12);
118 }
119
120 template <>
121 inline int16_t MixMul<int16_t, int32_t, int32_t>(int32_t value, int32_t volume) {
122 return clamp16(MixMul<int32_t, int32_t, int32_t>(value, volume) >> 12);
123 }
124
125 /* Required for floating point volume. Some are needed for compilation but
126 * are not needed in execution and should be removed from the final build by
127 * an optimizing compiler.
128 */
129 template <>
130 inline float MixMul<float, float, float>(float value, float volume) {
131 return value * volume;
132 }
133
134 template <>
135 inline float MixMul<float, int16_t, float>(int16_t value, float volume) {
136 static const float float_from_q_15 = 1. / (1 << 15);
137 return value * volume * float_from_q_15;
138 }
139
140 template <>
141 inline int32_t MixMul<int32_t, int32_t, float>(int32_t value, float volume) {
142 LOG_ALWAYS_FATAL("MixMul<int32_t, int32_t, float> Runtime Should not be here");
143 return value * volume;
144 }
145
146 template <>
147 inline int32_t MixMul<int32_t, int16_t, float>(int16_t value, float volume) {
148 LOG_ALWAYS_FATAL("MixMul<int32_t, int16_t, float> Runtime Should not be here");
149 static const float u4_12_from_float = (1 << 12);
150 return value * volume * u4_12_from_float;
151 }
152
153 template <>
154 inline int16_t MixMul<int16_t, int16_t, float>(int16_t value, float volume) {
155 LOG_ALWAYS_FATAL("MixMul<int16_t, int16_t, float> Runtime Should not be here");
156 return clamp16_from_float(MixMul<float, int16_t, float>(value, volume));
157 }
158
159 template <>
160 inline int16_t MixMul<int16_t, float, float>(float value, float volume) {
161 return clamp16_from_float(value * volume);
162 }
163
164 /*
165 * MixAccum is used to add into an accumulator register of a possibly different
166 * type. The TO and TI types are the same as MixMul.
167 */
168
169 template <typename TO, typename TI>
MixAccum(TO * auxaccum,TI value)170 inline void MixAccum(TO *auxaccum, TI value) {
171 if (!std::is_same_v<TO, TI>) {
172 LOG_ALWAYS_FATAL("MixAccum type not properly specialized: %zu %zu\n",
173 sizeof(TO), sizeof(TI));
174 }
175 *auxaccum += value;
176 }
177
178 template<>
179 inline void MixAccum<float, int16_t>(float *auxaccum, int16_t value) {
180 static constexpr float norm = 1. / (1 << 15);
181 *auxaccum += norm * value;
182 }
183
184 template<>
185 inline void MixAccum<float, int32_t>(float *auxaccum, int32_t value) {
186 static constexpr float norm = 1. / (1 << 27);
187 *auxaccum += norm * value;
188 }
189
190 template<>
191 inline void MixAccum<int32_t, int16_t>(int32_t *auxaccum, int16_t value) {
192 *auxaccum += value << 12;
193 }
194
195 template<>
196 inline void MixAccum<int32_t, float>(int32_t *auxaccum, float value) {
197 *auxaccum += clampq4_27_from_float(value);
198 }
199
200 /* MixMulAux is just like MixMul except it combines with
201 * an accumulator operation MixAccum.
202 */
203
204 template <typename TO, typename TI, typename TV, typename TA>
MixMulAux(TI value,TV volume,TA * auxaccum)205 inline TO MixMulAux(TI value, TV volume, TA *auxaccum) {
206 MixAccum<TA, TI>(auxaccum, value);
207 return MixMul<TO, TI, TV>(value, volume);
208 }
209
210 /* MIXTYPE is used to determine how the samples in the input frame
211 * are mixed with volume gain into the output frame.
212 * See the volumeRampMulti functions below for more details.
213 */
214 enum {
215 MIXTYPE_MULTI,
216 MIXTYPE_MONOEXPAND,
217 MIXTYPE_MULTI_SAVEONLY,
218 MIXTYPE_MULTI_MONOVOL,
219 MIXTYPE_MULTI_SAVEONLY_MONOVOL,
220 MIXTYPE_MULTI_STEREOVOL,
221 MIXTYPE_MULTI_SAVEONLY_STEREOVOL,
222 MIXTYPE_STEREOEXPAND,
223 };
224
225 /*
226 * TODO: We should work on non-interleaved streams - the
227 * complexity of working on interleaved streams is now getting
228 * too high, and likely limits compiler optimization.
229 */
230 template <int MIXTYPE, int NCHAN,
231 typename TO, typename TI, typename TV,
232 typename F>
stereoVolumeHelper(TO * & out,const TI * & in,const TV * vol,F f)233 void stereoVolumeHelper(TO*& out, const TI*& in, const TV *vol, F f) {
234 static_assert(NCHAN > 0 && NCHAN <= 8);
235 static_assert(MIXTYPE == MIXTYPE_MULTI_STEREOVOL
236 || MIXTYPE == MIXTYPE_MULTI_SAVEONLY_STEREOVOL
237 || MIXTYPE == MIXTYPE_STEREOEXPAND);
238 auto proc = [](auto& a, const auto& b) {
239 if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL
240 || MIXTYPE == MIXTYPE_STEREOEXPAND) {
241 a += b;
242 } else {
243 a = b;
244 }
245 };
246 auto inp = [&in]() -> const TI& {
247 if constexpr (MIXTYPE == MIXTYPE_STEREOEXPAND) {
248 return *in;
249 } else {
250 return *in++;
251 }
252 };
253
254 // HALs should only expose the canonical channel masks.
255 proc(*out++, f(inp(), vol[0])); // front left
256 if constexpr (NCHAN == 1) return;
257 proc(*out++, f(inp(), vol[1])); // front right
258 if constexpr (NCHAN == 2) return;
259 if constexpr (NCHAN == 4) {
260 proc(*out++, f(inp(), vol[0])); // back left
261 proc(*out++, f(inp(), vol[1])); // back right
262 return;
263 }
264
265 // TODO: Precompute center volume if not ramping.
266 std::decay_t<TV> center;
267 if constexpr (std::is_floating_point_v<TV>) {
268 center = (vol[0] + vol[1]) * 0.5; // do not use divide
269 } else {
270 center = (vol[0] >> 1) + (vol[1] >> 1); // rounds to 0.
271 }
272 proc(*out++, f(inp(), center)); // center (or 2.1 LFE)
273 if constexpr (NCHAN == 3) return;
274 if constexpr (NCHAN == 5) {
275 proc(*out++, f(inp(), vol[0])); // back left
276 proc(*out++, f(inp(), vol[1])); // back right
277 return;
278 }
279
280 proc(*out++, f(inp(), center)); // lfe
281 proc(*out++, f(inp(), vol[0])); // back left
282 proc(*out++, f(inp(), vol[1])); // back right
283 if constexpr (NCHAN == 6) return;
284 if constexpr (NCHAN == 7) {
285 proc(*out++, f(inp(), center)); // back center
286 return;
287 }
288 // NCHAN == 8
289 proc(*out++, f(inp(), vol[0])); // side left
290 proc(*out++, f(inp(), vol[1])); // side right
291 }
292
293 /*
294 * The volumeRampMulti and volumeRamp functions take a MIXTYPE
295 * which indicates the per-frame mixing and accumulation strategy.
296 *
297 * MIXTYPE_MULTI:
298 * NCHAN represents number of input and output channels.
299 * TO: int32_t (Q4.27) or float
300 * TI: int32_t (Q4.27) or int16_t (Q0.15) or float
301 * TA: int32_t (Q4.27) or float
302 * TV: int32_t (U4.28) or int16_t (U4.12) or float
303 * vol: represents a volume array.
304 *
305 * This accumulates into the out pointer.
306 *
307 * MIXTYPE_MONOEXPAND:
308 * Single input channel. NCHAN represents number of output channels.
309 * TO: int32_t (Q4.27) or float
310 * TI: int32_t (Q4.27) or int16_t (Q0.15) or float
311 * TA: int32_t (Q4.27) or float
312 * TV/TAV: int32_t (U4.28) or int16_t (U4.12) or float
313 * Input channel count is 1.
314 * vol: represents volume array.
315 *
316 * This accumulates into the out pointer.
317 *
318 * MIXTYPE_MULTI_SAVEONLY:
319 * NCHAN represents number of input and output channels.
320 * TO: int16_t (Q.15) or float
321 * TI: int32_t (Q4.27) or int16_t (Q0.15) or float
322 * TA: int32_t (Q4.27) or float
323 * TV/TAV: int32_t (U4.28) or int16_t (U4.12) or float
324 * vol: represents a volume array.
325 *
326 * MIXTYPE_MULTI_SAVEONLY does not accumulate into the out pointer.
327 *
328 * MIXTYPE_MULTI_MONOVOL:
329 * Same as MIXTYPE_MULTI, but uses only volume[0].
330 *
331 * MIXTYPE_MULTI_SAVEONLY_MONOVOL:
332 * Same as MIXTYPE_MULTI_SAVEONLY, but uses only volume[0].
333 *
334 * MIXTYPE_MULTI_STEREOVOL:
335 * Same as MIXTYPE_MULTI, but uses only volume[0] and volume[1].
336 *
337 * MIXTYPE_MULTI_SAVEONLY_STEREOVOL:
338 * Same as MIXTYPE_MULTI_SAVEONLY, but uses only volume[0] and volume[1].
339 *
340 * MIXTYPE_STEREOEXPAND:
341 * Stereo input channel. NCHAN represents number of output channels.
342 * Expand size 2 array "in" and "vol" to multi-channel output. Note
343 * that the 2 array is assumed to have replicated L+R.
344 *
345 */
346
347 template <int MIXTYPE, int NCHAN,
348 typename TO, typename TI, typename TV, typename TA, typename TAV>
volumeRampMulti(TO * out,size_t frameCount,const TI * in,TA * aux,TV * vol,const TV * volinc,TAV * vola,TAV volainc)349 inline void volumeRampMulti(TO* out, size_t frameCount,
350 const TI* in, TA* aux, TV *vol, const TV *volinc, TAV *vola, TAV volainc)
351 {
352 #ifdef ALOGVV
353 ALOGVV("volumeRampMulti, MIXTYPE:%d\n", MIXTYPE);
354 #endif
355 if (aux != NULL) {
356 do {
357 TA auxaccum = 0;
358 if constexpr (MIXTYPE == MIXTYPE_MULTI) {
359 for (int i = 0; i < NCHAN; ++i) {
360 *out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum);
361 vol[i] += volinc[i];
362 }
363 } else if constexpr (MIXTYPE == MIXTYPE_MONOEXPAND) {
364 for (int i = 0; i < NCHAN; ++i) {
365 *out++ += MixMulAux<TO, TI, TV, TA>(*in, vol[i], &auxaccum);
366 vol[i] += volinc[i];
367 }
368 in++;
369 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY) {
370 for (int i = 0; i < NCHAN; ++i) {
371 *out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum);
372 vol[i] += volinc[i];
373 }
374 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_MONOVOL) {
375 for (int i = 0; i < NCHAN; ++i) {
376 *out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[0], &auxaccum);
377 }
378 vol[0] += volinc[0];
379 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY_MONOVOL) {
380 for (int i = 0; i < NCHAN; ++i) {
381 *out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[0], &auxaccum);
382 }
383 vol[0] += volinc[0];
384 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL
385 || MIXTYPE == MIXTYPE_MULTI_SAVEONLY_STEREOVOL
386 || MIXTYPE == MIXTYPE_STEREOEXPAND) {
387 stereoVolumeHelper<MIXTYPE, NCHAN>(
388 out, in, vol, [&auxaccum] (auto &a, const auto &b) {
389 return MixMulAux<TO, TI, TV, TA>(a, b, &auxaccum);
390 });
391 if constexpr (MIXTYPE == MIXTYPE_STEREOEXPAND) in += 2;
392 vol[0] += volinc[0];
393 vol[1] += volinc[1];
394 } else /* constexpr */ {
395 static_assert(dependent_false<MIXTYPE>, "invalid mixtype");
396 }
397 auxaccum /= NCHAN;
398 *aux++ += MixMul<TA, TA, TAV>(auxaccum, *vola);
399 vola[0] += volainc;
400 } while (--frameCount);
401 } else {
402 do {
403 if constexpr (MIXTYPE == MIXTYPE_MULTI) {
404 for (int i = 0; i < NCHAN; ++i) {
405 *out++ += MixMul<TO, TI, TV>(*in++, vol[i]);
406 vol[i] += volinc[i];
407 }
408 } else if constexpr (MIXTYPE == MIXTYPE_MONOEXPAND) {
409 for (int i = 0; i < NCHAN; ++i) {
410 *out++ += MixMul<TO, TI, TV>(*in, vol[i]);
411 vol[i] += volinc[i];
412 }
413 in++;
414 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY) {
415 for (int i = 0; i < NCHAN; ++i) {
416 *out++ = MixMul<TO, TI, TV>(*in++, vol[i]);
417 vol[i] += volinc[i];
418 }
419 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_MONOVOL) {
420 for (int i = 0; i < NCHAN; ++i) {
421 *out++ += MixMul<TO, TI, TV>(*in++, vol[0]);
422 }
423 vol[0] += volinc[0];
424 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY_MONOVOL) {
425 for (int i = 0; i < NCHAN; ++i) {
426 *out++ = MixMul<TO, TI, TV>(*in++, vol[0]);
427 }
428 vol[0] += volinc[0];
429 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL
430 || MIXTYPE == MIXTYPE_MULTI_SAVEONLY_STEREOVOL
431 || MIXTYPE == MIXTYPE_STEREOEXPAND) {
432 stereoVolumeHelper<MIXTYPE, NCHAN>(out, in, vol, [] (auto &a, const auto &b) {
433 return MixMul<TO, TI, TV>(a, b);
434 });
435 if constexpr (MIXTYPE == MIXTYPE_STEREOEXPAND) in += 2;
436 vol[0] += volinc[0];
437 vol[1] += volinc[1];
438 } else /* constexpr */ {
439 static_assert(dependent_false<MIXTYPE>, "invalid mixtype");
440 }
441 } while (--frameCount);
442 }
443 }
444
445 template <int MIXTYPE, int NCHAN,
446 typename TO, typename TI, typename TV, typename TA, typename TAV>
volumeMulti(TO * out,size_t frameCount,const TI * in,TA * aux,const TV * vol,TAV vola)447 inline void volumeMulti(TO* out, size_t frameCount,
448 const TI* in, TA* aux, const TV *vol, TAV vola)
449 {
450 #ifdef ALOGVV
451 ALOGVV("volumeMulti MIXTYPE:%d\n", MIXTYPE);
452 #endif
453 if (aux != NULL) {
454 do {
455 TA auxaccum = 0;
456 if constexpr (MIXTYPE == MIXTYPE_MULTI) {
457 for (int i = 0; i < NCHAN; ++i) {
458 *out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum);
459 }
460 } else if constexpr (MIXTYPE == MIXTYPE_MONOEXPAND) {
461 for (int i = 0; i < NCHAN; ++i) {
462 *out++ += MixMulAux<TO, TI, TV, TA>(*in, vol[i], &auxaccum);
463 }
464 in++;
465 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY) {
466 for (int i = 0; i < NCHAN; ++i) {
467 *out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum);
468 }
469 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_MONOVOL) {
470 for (int i = 0; i < NCHAN; ++i) {
471 *out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[0], &auxaccum);
472 }
473 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY_MONOVOL) {
474 for (int i = 0; i < NCHAN; ++i) {
475 *out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[0], &auxaccum);
476 }
477 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL
478 || MIXTYPE == MIXTYPE_MULTI_SAVEONLY_STEREOVOL
479 || MIXTYPE == MIXTYPE_STEREOEXPAND) {
480 stereoVolumeHelper<MIXTYPE, NCHAN>(
481 out, in, vol, [&auxaccum] (auto &a, const auto &b) {
482 return MixMulAux<TO, TI, TV, TA>(a, b, &auxaccum);
483 });
484 if constexpr (MIXTYPE == MIXTYPE_STEREOEXPAND) in += 2;
485 } else /* constexpr */ {
486 static_assert(dependent_false<MIXTYPE>, "invalid mixtype");
487 }
488 auxaccum /= NCHAN;
489 *aux++ += MixMul<TA, TA, TAV>(auxaccum, vola);
490 } while (--frameCount);
491 } else {
492 do {
493 if constexpr (MIXTYPE == MIXTYPE_MULTI) {
494 for (int i = 0; i < NCHAN; ++i) {
495 *out++ += MixMul<TO, TI, TV>(*in++, vol[i]);
496 }
497 } else if constexpr (MIXTYPE == MIXTYPE_MONOEXPAND) {
498 for (int i = 0; i < NCHAN; ++i) {
499 *out++ += MixMul<TO, TI, TV>(*in, vol[i]);
500 }
501 in++;
502 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY) {
503 for (int i = 0; i < NCHAN; ++i) {
504 *out++ = MixMul<TO, TI, TV>(*in++, vol[i]);
505 }
506 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_MONOVOL) {
507 for (int i = 0; i < NCHAN; ++i) {
508 *out++ += MixMul<TO, TI, TV>(*in++, vol[0]);
509 }
510 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY_MONOVOL) {
511 for (int i = 0; i < NCHAN; ++i) {
512 *out++ = MixMul<TO, TI, TV>(*in++, vol[0]);
513 }
514 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL
515 || MIXTYPE == MIXTYPE_MULTI_SAVEONLY_STEREOVOL
516 || MIXTYPE == MIXTYPE_STEREOEXPAND) {
517 stereoVolumeHelper<MIXTYPE, NCHAN>(out, in, vol, [] (auto &a, const auto &b) {
518 return MixMul<TO, TI, TV>(a, b);
519 });
520 if constexpr (MIXTYPE == MIXTYPE_STEREOEXPAND) in += 2;
521 } else /* constexpr */ {
522 static_assert(dependent_false<MIXTYPE>, "invalid mixtype");
523 }
524 } while (--frameCount);
525 }
526 }
527
528 };
529
530 #endif /* ANDROID_AUDIO_MIXER_OPS_H */
531