blob: d3a26ad1c8cc09c5d5d653374ebd3139c05d2dea [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
Judy Hsiao19e533c2019-08-14 16:52:51 +080022// Hack to make static_assert work in a constexpr
23// https://en.cppreference.com/w/cpp/language/if
24template <int N>
25inline constexpr bool dependent_false = false;
Andy Hung296b7412014-06-17 15:25:47 -070026
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.
Chih-Hung Hsieh65025402014-12-11 13:06:46 -080044 *
45 * A generic version is NOT defined to catch any mistake of using it.
Andy Hung296b7412014-06-17 15:25:47 -070046 */
47
48template <typename TO, typename TI, typename TV>
Chih-Hung Hsieh65025402014-12-11 13:06:46 -080049TO MixMul(TI value, TV volume);
Andy Hung296b7412014-06-17 15:25:47 -070050
51template <>
52inline int32_t MixMul<int32_t, int16_t, int16_t>(int16_t value, int16_t volume) {
53 return value * volume;
54}
55
56template <>
57inline int32_t MixMul<int32_t, int32_t, int16_t>(int32_t value, int16_t volume) {
58 return (value >> 12) * volume;
59}
60
61template <>
62inline int32_t MixMul<int32_t, int16_t, int32_t>(int16_t value, int32_t volume) {
63 return value * (volume >> 16);
64}
65
66template <>
67inline int32_t MixMul<int32_t, int32_t, int32_t>(int32_t value, int32_t volume) {
68 return (value >> 12) * (volume >> 16);
69}
70
71template <>
72inline 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
77template <>
78inline 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
83template <>
84inline 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
88template <>
89inline 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
93template <>
94inline 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
99template <>
100inline 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
105template <>
106inline 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
110template <>
111inline 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
115template <>
116inline 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
120template <>
121inline 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
Andy Hung5e58b0a2014-06-23 19:07:29 -0700125/* 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 */
129template <>
130inline float MixMul<float, float, float>(float value, float volume) {
131 return value * volume;
132}
133
134template <>
135inline 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
140template <>
141inline 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
146template <>
147inline 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
153template <>
154inline 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");
Andy Hung83ffcfb2015-06-18 17:34:40 -0700156 return clamp16_from_float(MixMul<float, int16_t, float>(value, volume));
Andy Hung5e58b0a2014-06-23 19:07:29 -0700157}
158
159template <>
160inline int16_t MixMul<int16_t, float, float>(float value, float volume) {
Andy Hung83ffcfb2015-06-18 17:34:40 -0700161 return clamp16_from_float(value * volume);
Andy Hung5e58b0a2014-06-23 19:07:29 -0700162}
163
Andy Hung296b7412014-06-17 15:25:47 -0700164/*
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
169template <typename TO, typename TI>
170inline void MixAccum(TO *auxaccum, TI value) {
Judy Hsiao19e533c2019-08-14 16:52:51 +0800171 if (!std::is_same_v<TO, TI>) {
Glenn Kastena4daf0b2014-07-28 16:34:45 -0700172 LOG_ALWAYS_FATAL("MixAccum type not properly specialized: %zu %zu\n",
Andy Hung296b7412014-06-17 15:25:47 -0700173 sizeof(TO), sizeof(TI));
174 }
175 *auxaccum += value;
176}
177
178template<>
179inline void MixAccum<float, int16_t>(float *auxaccum, int16_t value) {
Andy Hung116a4982017-11-30 10:15:08 -0800180 static constexpr float norm = 1. / (1 << 15);
Andy Hung296b7412014-06-17 15:25:47 -0700181 *auxaccum += norm * value;
182}
183
184template<>
185inline void MixAccum<float, int32_t>(float *auxaccum, int32_t value) {
Andy Hung116a4982017-11-30 10:15:08 -0800186 static constexpr float norm = 1. / (1 << 27);
Andy Hung296b7412014-06-17 15:25:47 -0700187 *auxaccum += norm * value;
188}
189
190template<>
191inline void MixAccum<int32_t, int16_t>(int32_t *auxaccum, int16_t value) {
192 *auxaccum += value << 12;
193}
194
195template<>
196inline 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
204template <typename TO, typename TI, typename TV, typename TA>
205inline 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 */
214enum {
215 MIXTYPE_MULTI,
216 MIXTYPE_MONOEXPAND,
217 MIXTYPE_MULTI_SAVEONLY,
Andy Hunge93b6b72014-07-17 21:30:53 -0700218 MIXTYPE_MULTI_MONOVOL,
219 MIXTYPE_MULTI_SAVEONLY_MONOVOL,
Judy Hsiao19e533c2019-08-14 16:52:51 +0800220 MIXTYPE_MULTI_STEREOVOL,
221 MIXTYPE_MULTI_SAVEONLY_STEREOVOL,
Andy Hung296b7412014-06-17 15:25:47 -0700222};
223
224/*
Judy Hsiao19e533c2019-08-14 16:52:51 +0800225 * TODO: We should work on non-interleaved streams - the
226 * complexity of working on interleaved streams is now getting
227 * too high, and likely limits compiler optimization.
228 */
229template <int MIXTYPE, int NCHAN,
230 typename TO, typename TI, typename TV,
231 typename F>
232void stereoVolumeHelper(TO*& out, const TI*& in, const TV *vol, F f) {
233 static_assert(NCHAN > 0 && NCHAN <= 8);
234 static_assert(MIXTYPE == MIXTYPE_MULTI_STEREOVOL
235 || MIXTYPE == MIXTYPE_MULTI_SAVEONLY_STEREOVOL);
236 auto proc = [](auto& a, const auto& b) {
237 if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL) {
238 a += b;
239 } else {
240 a = b;
241 }
242 };
243 // HALs should only expose the canonical channel masks.
244 proc(*out++, f(*in++, vol[0])); // front left
245 if constexpr (NCHAN == 1) return;
246 proc(*out++, f(*in++, vol[1])); // front right
247 if constexpr (NCHAN == 2) return;
248 if constexpr (NCHAN == 4) {
249 proc(*out++, f(*in++, vol[0])); // back left
250 proc(*out++, f(*in++, vol[1])); // back right
251 return;
252 }
253
254 // TODO: Precompute center volume if not ramping.
255 std::decay_t<TV> center;
256 if constexpr (std::is_floating_point_v<TV>) {
257 center = (vol[0] + vol[1]) * 0.5; // do not use divide
258 } else {
259 center = (vol[0] >> 1) + (vol[1] >> 1); // rounds to 0.
260 }
261 proc(*out++, f(*in++, center)); // center (or 2.1 LFE)
262 if constexpr (NCHAN == 3) return;
263 if constexpr (NCHAN == 5) {
264 proc(*out++, f(*in++, vol[0])); // back left
265 proc(*out++, f(*in++, vol[1])); // back right
266 return;
267 }
268
269 proc(*out++, f(*in++, center)); // lfe
270 proc(*out++, f(*in++, vol[0])); // back left
271 proc(*out++, f(*in++, vol[1])); // back right
272 if constexpr (NCHAN == 6) return;
273 if constexpr (NCHAN == 7) {
274 proc(*out++, f(*in++, center)); // back center
275 return;
276 }
277 // NCHAN == 8
278 proc(*out++, f(*in++, vol[0])); // side left
279 proc(*out++, f(*in++, vol[1])); // side right
280}
281
282/*
Andy Hung296b7412014-06-17 15:25:47 -0700283 * The volumeRampMulti and volumeRamp functions take a MIXTYPE
284 * which indicates the per-frame mixing and accumulation strategy.
285 *
286 * MIXTYPE_MULTI:
287 * NCHAN represents number of input and output channels.
288 * TO: int32_t (Q4.27) or float
289 * TI: int32_t (Q4.27) or int16_t (Q0.15) or float
Andy Hung116a4982017-11-30 10:15:08 -0800290 * TA: int32_t (Q4.27) or float
Andy Hung296b7412014-06-17 15:25:47 -0700291 * TV: int32_t (U4.28) or int16_t (U4.12) or float
292 * vol: represents a volume array.
293 *
294 * This accumulates into the out pointer.
295 *
296 * MIXTYPE_MONOEXPAND:
297 * Single input channel. NCHAN represents number of output channels.
298 * TO: int32_t (Q4.27) or float
299 * TI: int32_t (Q4.27) or int16_t (Q0.15) or float
Andy Hung116a4982017-11-30 10:15:08 -0800300 * TA: int32_t (Q4.27) or float
301 * TV/TAV: int32_t (U4.28) or int16_t (U4.12) or float
Andy Hung296b7412014-06-17 15:25:47 -0700302 * Input channel count is 1.
303 * vol: represents volume array.
304 *
305 * This accumulates into the out pointer.
306 *
307 * MIXTYPE_MULTI_SAVEONLY:
308 * NCHAN represents number of input and output channels.
309 * TO: int16_t (Q.15) or float
310 * TI: int32_t (Q4.27) or int16_t (Q0.15) or float
Andy Hung116a4982017-11-30 10:15:08 -0800311 * TA: int32_t (Q4.27) or float
312 * TV/TAV: int32_t (U4.28) or int16_t (U4.12) or float
Andy Hung296b7412014-06-17 15:25:47 -0700313 * vol: represents a volume array.
314 *
315 * MIXTYPE_MULTI_SAVEONLY does not accumulate into the out pointer.
Andy Hunge93b6b72014-07-17 21:30:53 -0700316 *
317 * MIXTYPE_MULTI_MONOVOL:
318 * Same as MIXTYPE_MULTI, but uses only volume[0].
319 *
320 * MIXTYPE_MULTI_SAVEONLY_MONOVOL:
321 * Same as MIXTYPE_MULTI_SAVEONLY, but uses only volume[0].
322 *
Judy Hsiao19e533c2019-08-14 16:52:51 +0800323 * MIXTYPE_MULTI_STEREOVOL:
324 * Same as MIXTYPE_MULTI, but uses only volume[0] and volume[1].
325 *
326 * MIXTYPE_MULTI_SAVEONLY_STEREOVOL:
327 * Same as MIXTYPE_MULTI_SAVEONLY, but uses only volume[0] and volume[1].
328 *
Andy Hung296b7412014-06-17 15:25:47 -0700329 */
330
331template <int MIXTYPE, int NCHAN,
332 typename TO, typename TI, typename TV, typename TA, typename TAV>
333inline void volumeRampMulti(TO* out, size_t frameCount,
334 const TI* in, TA* aux, TV *vol, const TV *volinc, TAV *vola, TAV volainc)
335{
336#ifdef ALOGVV
337 ALOGVV("volumeRampMulti, MIXTYPE:%d\n", MIXTYPE);
338#endif
339 if (aux != NULL) {
340 do {
341 TA auxaccum = 0;
Judy Hsiao19e533c2019-08-14 16:52:51 +0800342 if constexpr (MIXTYPE == MIXTYPE_MULTI) {
Andy Hung296b7412014-06-17 15:25:47 -0700343 for (int i = 0; i < NCHAN; ++i) {
344 *out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum);
345 vol[i] += volinc[i];
346 }
Judy Hsiao19e533c2019-08-14 16:52:51 +0800347 } else if constexpr (MIXTYPE == MIXTYPE_MONOEXPAND) {
Andy Hung296b7412014-06-17 15:25:47 -0700348 for (int i = 0; i < NCHAN; ++i) {
349 *out++ += MixMulAux<TO, TI, TV, TA>(*in, vol[i], &auxaccum);
350 vol[i] += volinc[i];
351 }
352 in++;
Judy Hsiao19e533c2019-08-14 16:52:51 +0800353 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY) {
Andy Hunge93b6b72014-07-17 21:30:53 -0700354 for (int i = 0; i < NCHAN; ++i) {
355 *out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum);
356 vol[i] += volinc[i];
357 }
Judy Hsiao19e533c2019-08-14 16:52:51 +0800358 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_MONOVOL) {
Andy Hunge93b6b72014-07-17 21:30:53 -0700359 for (int i = 0; i < NCHAN; ++i) {
360 *out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[0], &auxaccum);
361 }
362 vol[0] += volinc[0];
Judy Hsiao19e533c2019-08-14 16:52:51 +0800363 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY_MONOVOL) {
Andy Hunge93b6b72014-07-17 21:30:53 -0700364 for (int i = 0; i < NCHAN; ++i) {
365 *out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[0], &auxaccum);
366 }
367 vol[0] += volinc[0];
Judy Hsiao19e533c2019-08-14 16:52:51 +0800368 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL
369 || MIXTYPE == MIXTYPE_MULTI_SAVEONLY_STEREOVOL) {
370 stereoVolumeHelper<MIXTYPE, NCHAN>(
371 out, in, vol, [&auxaccum] (auto &a, const auto &b) {
372 return MixMulAux<TO, TI, TV, TA>(a, b, &auxaccum);
373 });
374 vol[0] += volinc[0];
375 vol[1] += volinc[1];
376 } else /* constexpr */ {
377 static_assert(dependent_false<MIXTYPE>, "invalid mixtype");
Andy Hung296b7412014-06-17 15:25:47 -0700378 }
379 auxaccum /= NCHAN;
380 *aux++ += MixMul<TA, TA, TAV>(auxaccum, *vola);
381 vola[0] += volainc;
382 } while (--frameCount);
383 } else {
384 do {
Judy Hsiao19e533c2019-08-14 16:52:51 +0800385 if constexpr (MIXTYPE == MIXTYPE_MULTI) {
Andy Hung296b7412014-06-17 15:25:47 -0700386 for (int i = 0; i < NCHAN; ++i) {
387 *out++ += MixMul<TO, TI, TV>(*in++, vol[i]);
388 vol[i] += volinc[i];
389 }
Judy Hsiao19e533c2019-08-14 16:52:51 +0800390 } else if constexpr (MIXTYPE == MIXTYPE_MONOEXPAND) {
Andy Hung296b7412014-06-17 15:25:47 -0700391 for (int i = 0; i < NCHAN; ++i) {
392 *out++ += MixMul<TO, TI, TV>(*in, vol[i]);
393 vol[i] += volinc[i];
394 }
395 in++;
Judy Hsiao19e533c2019-08-14 16:52:51 +0800396 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY) {
Andy Hunge93b6b72014-07-17 21:30:53 -0700397 for (int i = 0; i < NCHAN; ++i) {
398 *out++ = MixMul<TO, TI, TV>(*in++, vol[i]);
399 vol[i] += volinc[i];
400 }
Judy Hsiao19e533c2019-08-14 16:52:51 +0800401 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_MONOVOL) {
Andy Hunge93b6b72014-07-17 21:30:53 -0700402 for (int i = 0; i < NCHAN; ++i) {
403 *out++ += MixMul<TO, TI, TV>(*in++, vol[0]);
404 }
405 vol[0] += volinc[0];
Judy Hsiao19e533c2019-08-14 16:52:51 +0800406 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY_MONOVOL) {
Andy Hunge93b6b72014-07-17 21:30:53 -0700407 for (int i = 0; i < NCHAN; ++i) {
408 *out++ = MixMul<TO, TI, TV>(*in++, vol[0]);
409 }
410 vol[0] += volinc[0];
Judy Hsiao19e533c2019-08-14 16:52:51 +0800411 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL
412 || MIXTYPE == MIXTYPE_MULTI_SAVEONLY_STEREOVOL) {
413 stereoVolumeHelper<MIXTYPE, NCHAN>(out, in, vol, [] (auto &a, const auto &b) {
414 return MixMul<TO, TI, TV>(a, b);
415 });
416 vol[0] += volinc[0];
417 vol[1] += volinc[1];
418 } else /* constexpr */ {
419 static_assert(dependent_false<MIXTYPE>, "invalid mixtype");
Andy Hung296b7412014-06-17 15:25:47 -0700420 }
421 } while (--frameCount);
422 }
423}
424
425template <int MIXTYPE, int NCHAN,
426 typename TO, typename TI, typename TV, typename TA, typename TAV>
427inline void volumeMulti(TO* out, size_t frameCount,
428 const TI* in, TA* aux, const TV *vol, TAV vola)
429{
430#ifdef ALOGVV
431 ALOGVV("volumeMulti MIXTYPE:%d\n", MIXTYPE);
432#endif
433 if (aux != NULL) {
434 do {
435 TA auxaccum = 0;
Judy Hsiao19e533c2019-08-14 16:52:51 +0800436 if constexpr (MIXTYPE == MIXTYPE_MULTI) {
Andy Hung296b7412014-06-17 15:25:47 -0700437 for (int i = 0; i < NCHAN; ++i) {
438 *out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum);
439 }
Judy Hsiao19e533c2019-08-14 16:52:51 +0800440 } else if constexpr (MIXTYPE == MIXTYPE_MONOEXPAND) {
Andy Hung296b7412014-06-17 15:25:47 -0700441 for (int i = 0; i < NCHAN; ++i) {
442 *out++ += MixMulAux<TO, TI, TV, TA>(*in, vol[i], &auxaccum);
443 }
444 in++;
Judy Hsiao19e533c2019-08-14 16:52:51 +0800445 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY) {
Andy Hunge93b6b72014-07-17 21:30:53 -0700446 for (int i = 0; i < NCHAN; ++i) {
447 *out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum);
448 }
Judy Hsiao19e533c2019-08-14 16:52:51 +0800449 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_MONOVOL) {
Andy Hunge93b6b72014-07-17 21:30:53 -0700450 for (int i = 0; i < NCHAN; ++i) {
451 *out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[0], &auxaccum);
452 }
Judy Hsiao19e533c2019-08-14 16:52:51 +0800453 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY_MONOVOL) {
Andy Hunge93b6b72014-07-17 21:30:53 -0700454 for (int i = 0; i < NCHAN; ++i) {
455 *out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[0], &auxaccum);
456 }
Judy Hsiao19e533c2019-08-14 16:52:51 +0800457 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL
458 || MIXTYPE == MIXTYPE_MULTI_SAVEONLY_STEREOVOL) {
459 stereoVolumeHelper<MIXTYPE, NCHAN>(
460 out, in, vol, [&auxaccum] (auto &a, const auto &b) {
461 return MixMulAux<TO, TI, TV, TA>(a, b, &auxaccum);
462 });
463 } else /* constexpr */ {
464 static_assert(dependent_false<MIXTYPE>, "invalid mixtype");
Andy Hung296b7412014-06-17 15:25:47 -0700465 }
466 auxaccum /= NCHAN;
467 *aux++ += MixMul<TA, TA, TAV>(auxaccum, vola);
468 } while (--frameCount);
469 } else {
470 do {
Judy Hsiao19e533c2019-08-14 16:52:51 +0800471 if constexpr (MIXTYPE == MIXTYPE_MULTI) {
Andy Hung296b7412014-06-17 15:25:47 -0700472 for (int i = 0; i < NCHAN; ++i) {
473 *out++ += MixMul<TO, TI, TV>(*in++, vol[i]);
474 }
Judy Hsiao19e533c2019-08-14 16:52:51 +0800475 } else if constexpr (MIXTYPE == MIXTYPE_MONOEXPAND) {
Andy Hung296b7412014-06-17 15:25:47 -0700476 for (int i = 0; i < NCHAN; ++i) {
477 *out++ += MixMul<TO, TI, TV>(*in, vol[i]);
478 }
479 in++;
Judy Hsiao19e533c2019-08-14 16:52:51 +0800480 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY) {
Andy Hunge93b6b72014-07-17 21:30:53 -0700481 for (int i = 0; i < NCHAN; ++i) {
482 *out++ = MixMul<TO, TI, TV>(*in++, vol[i]);
483 }
Judy Hsiao19e533c2019-08-14 16:52:51 +0800484 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_MONOVOL) {
Andy Hunge93b6b72014-07-17 21:30:53 -0700485 for (int i = 0; i < NCHAN; ++i) {
486 *out++ += MixMul<TO, TI, TV>(*in++, vol[0]);
487 }
Judy Hsiao19e533c2019-08-14 16:52:51 +0800488 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY_MONOVOL) {
Andy Hunge93b6b72014-07-17 21:30:53 -0700489 for (int i = 0; i < NCHAN; ++i) {
490 *out++ = MixMul<TO, TI, TV>(*in++, vol[0]);
491 }
Judy Hsiao19e533c2019-08-14 16:52:51 +0800492 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL
493 || MIXTYPE == MIXTYPE_MULTI_SAVEONLY_STEREOVOL) {
494 stereoVolumeHelper<MIXTYPE, NCHAN>(out, in, vol, [] (auto &a, const auto &b) {
495 return MixMul<TO, TI, TV>(a, b);
496 });
497 } else /* constexpr */ {
498 static_assert(dependent_false<MIXTYPE>, "invalid mixtype");
Andy Hung296b7412014-06-17 15:25:47 -0700499 }
500 } while (--frameCount);
501 }
502}
503
504};
505
506#endif /* ANDROID_AUDIO_MIXER_OPS_H */