xref: /aosp_15_r20/frameworks/av/media/libaudioprocessing/AudioMixerOps.h (revision ec779b8e0859a360c3d303172224686826e6e0e1)
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         [10] = AUDIO_CHANNEL_OUT_5POINT1POINT4,
351         [12] = AUDIO_CHANNEL_OUT_7POINT1POINT4,
352         [14] = AUDIO_CHANNEL_OUT_9POINT1POINT4,
353         [16] = AUDIO_CHANNEL_OUT_9POINT1POINT6,
354         [24] = AUDIO_CHANNEL_OUT_22POINT2,
355     };
356     return channelCount < std::size(canonical) ? canonical[channelCount] : AUDIO_CHANNEL_NONE;
357 }
358 
359 template <int MIXTYPE, int NCHAN,
360         typename TO, typename TI, typename TV,
361         typename F>
stereoVolumeHelper(TO * & out,const TI * & in,const TV * vol,F f)362 void stereoVolumeHelper(TO*& out, const TI*& in, const TV *vol, F f) {
363     static_assert(NCHAN > 0 && NCHAN <= FCC_LIMIT);
364     static_assert(MIXTYPE == MIXTYPE_MULTI_STEREOVOL
365             || MIXTYPE == MIXTYPE_MULTI_SAVEONLY_STEREOVOL
366             || MIXTYPE == MIXTYPE_STEREOEXPAND
367             || MIXTYPE == MIXTYPE_MONOEXPAND);
368     constexpr audio_channel_mask_t MASK{canonicalChannelMaskFromCount(NCHAN)};
369     if constexpr (MASK == AUDIO_CHANNEL_NONE) {
370         ALOGE("%s: Invalid position count %d", __func__, NCHAN);
371         return; // not a valid system mask, ignore.
372     }
373     stereoVolumeHelperWithChannelMask<MIXTYPE, MASK, TO, TI, TV, F>(out, in, vol, f);
374 }
375 
376 /*
377  * The volumeRampMulti and volumeRamp functions take a MIXTYPE
378  * which indicates the per-frame mixing and accumulation strategy.
379  *
380  * MIXTYPE_MULTI:
381  *   NCHAN represents number of input and output channels.
382  *   TO: int32_t (Q4.27) or float
383  *   TI: int32_t (Q4.27) or int16_t (Q0.15) or float
384  *   TA: int32_t (Q4.27) or float
385  *   TV: int32_t (U4.28) or int16_t (U4.12) or float
386  *   vol: represents a volume array.
387  *
388  *   This accumulates into the out pointer.
389  *
390  * MIXTYPE_MONOEXPAND:
391  *   Single input channel. NCHAN represents number of output channels.
392  *   TO: int32_t (Q4.27) or float
393  *   TI: int32_t (Q4.27) or int16_t (Q0.15) or float
394  *   TA: int32_t (Q4.27) or float
395  *   TV/TAV: int32_t (U4.28) or int16_t (U4.12) or float
396  *   Input channel count is 1.
397  *   vol: represents volume array.
398  *   This uses stereo balanced volume vol[0] and vol[1].
399  *   Before R, this was a full volume array but was called only for channels <= 2.
400  *
401  *   This accumulates into the out pointer.
402  *
403  * MIXTYPE_MULTI_SAVEONLY:
404  *   NCHAN represents number of input and output channels.
405  *   TO: int16_t (Q.15) or float
406  *   TI: int32_t (Q4.27) or int16_t (Q0.15) or float
407  *   TA: int32_t (Q4.27) or float
408  *   TV/TAV: int32_t (U4.28) or int16_t (U4.12) or float
409  *   vol: represents a volume array.
410  *
411  *   MIXTYPE_MULTI_SAVEONLY does not accumulate into the out pointer.
412  *
413  * MIXTYPE_MULTI_MONOVOL:
414  *   Same as MIXTYPE_MULTI, but uses only volume[0].
415  *
416  * MIXTYPE_MULTI_SAVEONLY_MONOVOL:
417  *   Same as MIXTYPE_MULTI_SAVEONLY, but uses only volume[0].
418  *
419  * MIXTYPE_MULTI_STEREOVOL:
420  *   Same as MIXTYPE_MULTI, but uses only volume[0] and volume[1].
421  *
422  * MIXTYPE_MULTI_SAVEONLY_STEREOVOL:
423  *   Same as MIXTYPE_MULTI_SAVEONLY, but uses only volume[0] and volume[1].
424  *
425  * MIXTYPE_STEREOEXPAND:
426  *   Stereo input channel. NCHAN represents number of output channels.
427  *   Expand size 2 array "in" and "vol" to multi-channel output. Note
428  *   that the 2 array is assumed to have replicated L+R.
429  *
430  */
431 
432 template <int MIXTYPE, int NCHAN,
433         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)434 inline void volumeRampMulti(TO* out, size_t frameCount,
435         const TI* in, TA* aux, TV *vol, const TV *volinc, TAV *vola, TAV volainc)
436 {
437 #ifdef ALOGVV
438     ALOGVV("volumeRampMulti, MIXTYPE:%d\n", MIXTYPE);
439 #endif
440     if (aux != NULL) {
441         do {
442             TA auxaccum = 0;
443             if constexpr (MIXTYPE == MIXTYPE_MULTI) {
444                 static_assert(NCHAN <= 2);
445                 for (int i = 0; i < NCHAN; ++i) {
446                     *out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum);
447                     vol[i] += volinc[i];
448                 }
449             } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY) {
450                 static_assert(NCHAN <= 2);
451                 for (int i = 0; i < NCHAN; ++i) {
452                     *out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum);
453                     vol[i] += volinc[i];
454                 }
455             } else if constexpr (MIXTYPE == MIXTYPE_MULTI_MONOVOL) {
456                 for (int i = 0; i < NCHAN; ++i) {
457                     *out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[0], &auxaccum);
458                 }
459                 vol[0] += volinc[0];
460             } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY_MONOVOL) {
461                 for (int i = 0; i < NCHAN; ++i) {
462                     *out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[0], &auxaccum);
463                 }
464                 vol[0] += volinc[0];
465             } else if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL
466                     || MIXTYPE == MIXTYPE_MULTI_SAVEONLY_STEREOVOL
467                     || MIXTYPE == MIXTYPE_MONOEXPAND
468                     || MIXTYPE == MIXTYPE_STEREOEXPAND) {
469                 stereoVolumeHelper<MIXTYPE, NCHAN>(
470                         out, in, vol, [&auxaccum] (auto &a, const auto &b) {
471                     return MixMulAux<TO, TI, TV, TA>(a, b, &auxaccum);
472                 });
473                 if constexpr (MIXTYPE == MIXTYPE_MONOEXPAND) in += 1;
474                 if constexpr (MIXTYPE == MIXTYPE_STEREOEXPAND) in += 2;
475                 vol[0] += volinc[0];
476                 vol[1] += volinc[1];
477             } else /* constexpr */ {
478                 static_assert(dependent_false<MIXTYPE>, "invalid mixtype");
479             }
480             auxaccum /= NCHAN;
481             *aux++ += MixMul<TA, TA, TAV>(auxaccum, *vola);
482             vola[0] += volainc;
483         } while (--frameCount);
484     } else {
485         do {
486             if constexpr (MIXTYPE == MIXTYPE_MULTI) {
487                 static_assert(NCHAN <= 2);
488                 for (int i = 0; i < NCHAN; ++i) {
489                     *out++ += MixMul<TO, TI, TV>(*in++, vol[i]);
490                     vol[i] += volinc[i];
491                 }
492             } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY) {
493                 static_assert(NCHAN <= 2);
494                 for (int i = 0; i < NCHAN; ++i) {
495                     *out++ = MixMul<TO, TI, TV>(*in++, vol[i]);
496                     vol[i] += volinc[i];
497                 }
498             } else if constexpr (MIXTYPE == MIXTYPE_MULTI_MONOVOL) {
499                 for (int i = 0; i < NCHAN; ++i) {
500                     *out++ += MixMul<TO, TI, TV>(*in++, vol[0]);
501                 }
502                 vol[0] += volinc[0];
503             } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY_MONOVOL) {
504                 for (int i = 0; i < NCHAN; ++i) {
505                     *out++ = MixMul<TO, TI, TV>(*in++, vol[0]);
506                 }
507                 vol[0] += volinc[0];
508             } else if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL
509                     || MIXTYPE == MIXTYPE_MULTI_SAVEONLY_STEREOVOL
510                     || MIXTYPE == MIXTYPE_MONOEXPAND
511                     || MIXTYPE == MIXTYPE_STEREOEXPAND) {
512                 stereoVolumeHelper<MIXTYPE, NCHAN>(out, in, vol, [] (auto &a, const auto &b) {
513                     return MixMul<TO, TI, TV>(a, b);
514                 });
515                 if constexpr (MIXTYPE == MIXTYPE_MONOEXPAND) in += 1;
516                 if constexpr (MIXTYPE == MIXTYPE_STEREOEXPAND) in += 2;
517                 vol[0] += volinc[0];
518                 vol[1] += volinc[1];
519             } else /* constexpr */ {
520                 static_assert(dependent_false<MIXTYPE>, "invalid mixtype");
521             }
522         } while (--frameCount);
523     }
524 }
525 
526 template <int MIXTYPE, int NCHAN,
527         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)528 inline void volumeMulti(TO* out, size_t frameCount,
529         const TI* in, TA* aux, const TV *vol, TAV vola)
530 {
531 #ifdef ALOGVV
532     ALOGVV("volumeMulti MIXTYPE:%d\n", MIXTYPE);
533 #endif
534     if (aux != NULL) {
535         do {
536             TA auxaccum = 0;
537             if constexpr (MIXTYPE == MIXTYPE_MULTI) {
538                 static_assert(NCHAN <= 2);
539                 for (int i = 0; i < NCHAN; ++i) {
540                     *out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum);
541                 }
542             } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY) {
543                 static_assert(NCHAN <= 2);
544                 for (int i = 0; i < NCHAN; ++i) {
545                     *out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum);
546                 }
547             } else if constexpr (MIXTYPE == MIXTYPE_MULTI_MONOVOL) {
548                 for (int i = 0; i < NCHAN; ++i) {
549                     *out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[0], &auxaccum);
550                 }
551             } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY_MONOVOL) {
552                 for (int i = 0; i < NCHAN; ++i) {
553                     *out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[0], &auxaccum);
554                 }
555             } else if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL
556                     || MIXTYPE == MIXTYPE_MULTI_SAVEONLY_STEREOVOL
557                     || MIXTYPE == MIXTYPE_MONOEXPAND
558                     || MIXTYPE == MIXTYPE_STEREOEXPAND) {
559                 stereoVolumeHelper<MIXTYPE, NCHAN>(
560                         out, in, vol, [&auxaccum] (auto &a, const auto &b) {
561                     return MixMulAux<TO, TI, TV, TA>(a, b, &auxaccum);
562                 });
563                 if constexpr (MIXTYPE == MIXTYPE_MONOEXPAND) in += 1;
564                 if constexpr (MIXTYPE == MIXTYPE_STEREOEXPAND) in += 2;
565             } else /* constexpr */ {
566                 static_assert(dependent_false<MIXTYPE>, "invalid mixtype");
567             }
568             auxaccum /= NCHAN;
569             *aux++ += MixMul<TA, TA, TAV>(auxaccum, vola);
570         } while (--frameCount);
571     } else {
572         do {
573             // ALOGD("Mixtype:%d NCHAN:%d", MIXTYPE, NCHAN);
574             if constexpr (MIXTYPE == MIXTYPE_MULTI) {
575                 static_assert(NCHAN <= 2);
576                 for (int i = 0; i < NCHAN; ++i) {
577                     *out++ += MixMul<TO, TI, TV>(*in++, vol[i]);
578                 }
579             } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY) {
580                 static_assert(NCHAN <= 2);
581                 for (int i = 0; i < NCHAN; ++i) {
582                     *out++ = MixMul<TO, TI, TV>(*in++, vol[i]);
583                 }
584             } else if constexpr (MIXTYPE == MIXTYPE_MULTI_MONOVOL) {
585                 for (int i = 0; i < NCHAN; ++i) {
586                     *out++ += MixMul<TO, TI, TV>(*in++, vol[0]);
587                 }
588             } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY_MONOVOL) {
589                 for (int i = 0; i < NCHAN; ++i) {
590                     *out++ = MixMul<TO, TI, TV>(*in++, vol[0]);
591                 }
592             } else if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL
593                     || MIXTYPE == MIXTYPE_MULTI_SAVEONLY_STEREOVOL
594                     || MIXTYPE == MIXTYPE_MONOEXPAND
595                     || MIXTYPE == MIXTYPE_STEREOEXPAND) {
596                 stereoVolumeHelper<MIXTYPE, NCHAN>(out, in, vol, [] (auto &a, const auto &b) {
597                     return MixMul<TO, TI, TV>(a, b);
598                 });
599                 if constexpr (MIXTYPE == MIXTYPE_MONOEXPAND) in += 1;
600                 if constexpr (MIXTYPE == MIXTYPE_STEREOEXPAND) in += 2;
601             } else /* constexpr */ {
602                 static_assert(dependent_false<MIXTYPE>, "invalid mixtype");
603             }
604         } while (--frameCount);
605     }
606 }
607 
608 };
609 
610 #endif /* ANDROID_AUDIO_MIXER_OPS_H */
611