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