blob: 2988c6797e4b7ff8093fd6a6c1a2d422760ce965 [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
Andy Hunge059b8f2021-06-08 17:10:54 -070020#include <audio_utils/channels.h>
21#include <audio_utils/primitives.h>
Andy Hung936845a2021-06-08 00:09:06 -070022#include <system/audio.h>
23
Andy Hung296b7412014-06-17 15:25:47 -070024namespace android {
25
Judy Hsiao19e533c2019-08-14 16:52:51 +080026// Hack to make static_assert work in a constexpr
27// https://en.cppreference.com/w/cpp/language/if
28template <int N>
29inline constexpr bool dependent_false = false;
Andy Hung296b7412014-06-17 15:25:47 -070030
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.
Chih-Hung Hsieh65025402014-12-11 13:06:46 -080048 *
49 * A generic version is NOT defined to catch any mistake of using it.
Andy Hung296b7412014-06-17 15:25:47 -070050 */
51
52template <typename TO, typename TI, typename TV>
Chih-Hung Hsieh65025402014-12-11 13:06:46 -080053TO MixMul(TI value, TV volume);
Andy Hung296b7412014-06-17 15:25:47 -070054
55template <>
56inline int32_t MixMul<int32_t, int16_t, int16_t>(int16_t value, int16_t volume) {
57 return value * volume;
58}
59
60template <>
61inline int32_t MixMul<int32_t, int32_t, int16_t>(int32_t value, int16_t volume) {
62 return (value >> 12) * volume;
63}
64
65template <>
66inline int32_t MixMul<int32_t, int16_t, int32_t>(int16_t value, int32_t volume) {
67 return value * (volume >> 16);
68}
69
70template <>
71inline int32_t MixMul<int32_t, int32_t, int32_t>(int32_t value, int32_t volume) {
72 return (value >> 12) * (volume >> 16);
73}
74
75template <>
76inline 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
81template <>
82inline 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
87template <>
88inline 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
92template <>
93inline 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
97template <>
98inline 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
103template <>
104inline 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
109template <>
110inline 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
114template <>
115inline 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
119template <>
120inline 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
124template <>
125inline 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
Andy Hung5e58b0a2014-06-23 19:07:29 -0700129/* 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 */
133template <>
134inline float MixMul<float, float, float>(float value, float volume) {
135 return value * volume;
136}
137
138template <>
139inline 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
144template <>
145inline 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
150template <>
151inline 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
157template <>
158inline 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");
Andy Hung83ffcfb2015-06-18 17:34:40 -0700160 return clamp16_from_float(MixMul<float, int16_t, float>(value, volume));
Andy Hung5e58b0a2014-06-23 19:07:29 -0700161}
162
163template <>
164inline int16_t MixMul<int16_t, float, float>(float value, float volume) {
Andy Hung83ffcfb2015-06-18 17:34:40 -0700165 return clamp16_from_float(value * volume);
Andy Hung5e58b0a2014-06-23 19:07:29 -0700166}
167
Andy Hung296b7412014-06-17 15:25:47 -0700168/*
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
173template <typename TO, typename TI>
174inline void MixAccum(TO *auxaccum, TI value) {
Judy Hsiao19e533c2019-08-14 16:52:51 +0800175 if (!std::is_same_v<TO, TI>) {
Glenn Kastena4daf0b2014-07-28 16:34:45 -0700176 LOG_ALWAYS_FATAL("MixAccum type not properly specialized: %zu %zu\n",
Andy Hung296b7412014-06-17 15:25:47 -0700177 sizeof(TO), sizeof(TI));
178 }
179 *auxaccum += value;
180}
181
182template<>
183inline void MixAccum<float, int16_t>(float *auxaccum, int16_t value) {
Andy Hung116a4982017-11-30 10:15:08 -0800184 static constexpr float norm = 1. / (1 << 15);
Andy Hung296b7412014-06-17 15:25:47 -0700185 *auxaccum += norm * value;
186}
187
188template<>
189inline void MixAccum<float, int32_t>(float *auxaccum, int32_t value) {
Andy Hung116a4982017-11-30 10:15:08 -0800190 static constexpr float norm = 1. / (1 << 27);
Andy Hung296b7412014-06-17 15:25:47 -0700191 *auxaccum += norm * value;
192}
193
194template<>
195inline void MixAccum<int32_t, int16_t>(int32_t *auxaccum, int16_t value) {
196 *auxaccum += value << 12;
197}
198
199template<>
200inline 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
208template <typename TO, typename TI, typename TV, typename TA>
209inline 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 */
218enum {
219 MIXTYPE_MULTI,
220 MIXTYPE_MONOEXPAND,
221 MIXTYPE_MULTI_SAVEONLY,
Andy Hunge93b6b72014-07-17 21:30:53 -0700222 MIXTYPE_MULTI_MONOVOL,
223 MIXTYPE_MULTI_SAVEONLY_MONOVOL,
Judy Hsiao19e533c2019-08-14 16:52:51 +0800224 MIXTYPE_MULTI_STEREOVOL,
225 MIXTYPE_MULTI_SAVEONLY_STEREOVOL,
Judy Hsiaoc5cf9e22019-08-15 11:32:02 +0800226 MIXTYPE_STEREOEXPAND,
Andy Hung296b7412014-06-17 15:25:47 -0700227};
228
229/*
Judy Hsiao19e533c2019-08-14 16:52:51 +0800230 * 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 */
Andy Hunge059b8f2021-06-08 17:10:54 -0700234
235// compile-time function.
236constexpr 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 */
250template <int MIXTYPE, audio_channel_mask_t MASK,
Judy Hsiao19e533c2019-08-14 16:52:51 +0800251 typename TO, typename TI, typename TV,
252 typename F>
Andy Hunge059b8f2021-06-08 17:10:54 -0700253void stereoVolumeHelperWithChannelMask(TO*& out, const TI*& in, const TV *vol, F f) {
Judy Hsiao19e533c2019-08-14 16:52:51 +0800254 auto proc = [](auto& a, const auto& b) {
Andy Hunge0c3d792020-06-01 09:41:24 -0700255 if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL
Andy Hung2d8397a2020-07-14 00:56:03 -0700256 || MIXTYPE == MIXTYPE_STEREOEXPAND
257 || MIXTYPE == MIXTYPE_MONOEXPAND) {
Judy Hsiao19e533c2019-08-14 16:52:51 +0800258 a += b;
259 } else {
260 a = b;
261 }
262 };
Judy Hsiaoc5cf9e22019-08-15 11:32:02 +0800263 auto inp = [&in]() -> const TI& {
Andy Hung2d8397a2020-07-14 00:56:03 -0700264 if constexpr (MIXTYPE == MIXTYPE_STEREOEXPAND
265 || MIXTYPE == MIXTYPE_MONOEXPAND) {
Andy Hunge059b8f2021-06-08 17:10:54 -0700266 return *in; // note STEREOEXPAND assumes replicated L/R channels (see doc below).
Judy Hsiaoc5cf9e22019-08-15 11:32:02 +0800267 } else {
268 return *in++;
269 }
270 };
271
Judy Hsiao19e533c2019-08-14 16:52:51 +0800272 std::decay_t<TV> center;
Andy Hunge059b8f2021-06-08 17:10:54 -0700273 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.
Andy Hung1b998522021-06-07 16:43:58 -0700279 }
280 }
Andy Hunge059b8f2021-06-08 17:10:54 -0700281
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 static_assert(FCC_LIMIT <= FCC_24); // Note: this may need to change.
332#pragma pop_macro("DO_CHANNEL_POSITION")
333}
334
335// These are the channel position masks we expect from the HAL.
336// See audio_channel_out_mask_from_count() but this is constexpr
337constexpr inline audio_channel_mask_t canonicalChannelMaskFromCount(size_t channelCount) {
338 constexpr audio_channel_mask_t canonical[] = {
339 [0] = AUDIO_CHANNEL_NONE,
340 [1] = AUDIO_CHANNEL_OUT_MONO,
341 [2] = AUDIO_CHANNEL_OUT_STEREO,
342 [3] = AUDIO_CHANNEL_OUT_2POINT1,
343 [4] = AUDIO_CHANNEL_OUT_QUAD,
344 [5] = AUDIO_CHANNEL_OUT_PENTA,
345 [6] = AUDIO_CHANNEL_OUT_5POINT1,
346 [7] = AUDIO_CHANNEL_OUT_6POINT1,
347 [8] = AUDIO_CHANNEL_OUT_7POINT1,
348 [12] = AUDIO_CHANNEL_OUT_7POINT1POINT4,
349 [24] = AUDIO_CHANNEL_OUT_22POINT2,
350 };
351 return channelCount < std::size(canonical) ? canonical[channelCount] : AUDIO_CHANNEL_NONE;
352}
353
354template <int MIXTYPE, int NCHAN,
355 typename TO, typename TI, typename TV,
356 typename F>
357void stereoVolumeHelper(TO*& out, const TI*& in, const TV *vol, F f) {
358 static_assert(NCHAN > 0 && NCHAN <= FCC_LIMIT);
359 static_assert(MIXTYPE == MIXTYPE_MULTI_STEREOVOL
360 || MIXTYPE == MIXTYPE_MULTI_SAVEONLY_STEREOVOL
361 || MIXTYPE == MIXTYPE_STEREOEXPAND
362 || MIXTYPE == MIXTYPE_MONOEXPAND);
363 constexpr audio_channel_mask_t MASK{canonicalChannelMaskFromCount(NCHAN)};
364 if constexpr (MASK == AUDIO_CHANNEL_NONE) {
365 ALOGE("%s: Invalid position count %d", __func__, NCHAN);
366 return; // not a valid system mask, ignore.
367 }
368 stereoVolumeHelperWithChannelMask<MIXTYPE, MASK, TO, TI, TV, F>(out, in, vol, f);
Judy Hsiao19e533c2019-08-14 16:52:51 +0800369}
370
371/*
Andy Hung296b7412014-06-17 15:25:47 -0700372 * The volumeRampMulti and volumeRamp functions take a MIXTYPE
373 * which indicates the per-frame mixing and accumulation strategy.
374 *
375 * MIXTYPE_MULTI:
376 * NCHAN represents number of input and output channels.
377 * TO: int32_t (Q4.27) or float
378 * TI: int32_t (Q4.27) or int16_t (Q0.15) or float
Andy Hung116a4982017-11-30 10:15:08 -0800379 * TA: int32_t (Q4.27) or float
Andy Hung296b7412014-06-17 15:25:47 -0700380 * TV: int32_t (U4.28) or int16_t (U4.12) or float
381 * vol: represents a volume array.
382 *
383 * This accumulates into the out pointer.
384 *
385 * MIXTYPE_MONOEXPAND:
386 * Single input channel. NCHAN represents number of output channels.
387 * TO: int32_t (Q4.27) or float
388 * TI: int32_t (Q4.27) or int16_t (Q0.15) or float
Andy Hung116a4982017-11-30 10:15:08 -0800389 * TA: int32_t (Q4.27) or float
390 * TV/TAV: int32_t (U4.28) or int16_t (U4.12) or float
Andy Hung296b7412014-06-17 15:25:47 -0700391 * Input channel count is 1.
392 * vol: represents volume array.
Andy Hung2d8397a2020-07-14 00:56:03 -0700393 * This uses stereo balanced volume vol[0] and vol[1].
394 * Before R, this was a full volume array but was called only for channels <= 2.
Andy Hung296b7412014-06-17 15:25:47 -0700395 *
396 * This accumulates into the out pointer.
397 *
398 * MIXTYPE_MULTI_SAVEONLY:
399 * NCHAN represents number of input and output channels.
400 * TO: int16_t (Q.15) or float
401 * TI: int32_t (Q4.27) or int16_t (Q0.15) or float
Andy Hung116a4982017-11-30 10:15:08 -0800402 * TA: int32_t (Q4.27) or float
403 * TV/TAV: int32_t (U4.28) or int16_t (U4.12) or float
Andy Hung296b7412014-06-17 15:25:47 -0700404 * vol: represents a volume array.
405 *
406 * MIXTYPE_MULTI_SAVEONLY does not accumulate into the out pointer.
Andy Hunge93b6b72014-07-17 21:30:53 -0700407 *
408 * MIXTYPE_MULTI_MONOVOL:
409 * Same as MIXTYPE_MULTI, but uses only volume[0].
410 *
411 * MIXTYPE_MULTI_SAVEONLY_MONOVOL:
412 * Same as MIXTYPE_MULTI_SAVEONLY, but uses only volume[0].
413 *
Judy Hsiao19e533c2019-08-14 16:52:51 +0800414 * MIXTYPE_MULTI_STEREOVOL:
415 * Same as MIXTYPE_MULTI, but uses only volume[0] and volume[1].
416 *
417 * MIXTYPE_MULTI_SAVEONLY_STEREOVOL:
418 * Same as MIXTYPE_MULTI_SAVEONLY, but uses only volume[0] and volume[1].
419 *
Judy Hsiaoc5cf9e22019-08-15 11:32:02 +0800420 * MIXTYPE_STEREOEXPAND:
421 * Stereo input channel. NCHAN represents number of output channels.
422 * Expand size 2 array "in" and "vol" to multi-channel output. Note
423 * that the 2 array is assumed to have replicated L+R.
424 *
Andy Hung296b7412014-06-17 15:25:47 -0700425 */
426
427template <int MIXTYPE, int NCHAN,
428 typename TO, typename TI, typename TV, typename TA, typename TAV>
429inline void volumeRampMulti(TO* out, size_t frameCount,
430 const TI* in, TA* aux, TV *vol, const TV *volinc, TAV *vola, TAV volainc)
431{
432#ifdef ALOGVV
433 ALOGVV("volumeRampMulti, MIXTYPE:%d\n", MIXTYPE);
434#endif
435 if (aux != NULL) {
436 do {
437 TA auxaccum = 0;
Judy Hsiao19e533c2019-08-14 16:52:51 +0800438 if constexpr (MIXTYPE == MIXTYPE_MULTI) {
Andy Hung2d8397a2020-07-14 00:56:03 -0700439 static_assert(NCHAN <= 2);
Andy Hung296b7412014-06-17 15:25:47 -0700440 for (int i = 0; i < NCHAN; ++i) {
441 *out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum);
442 vol[i] += volinc[i];
443 }
Judy Hsiao19e533c2019-08-14 16:52:51 +0800444 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY) {
Andy Hung2d8397a2020-07-14 00:56:03 -0700445 static_assert(NCHAN <= 2);
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 vol[i] += volinc[i];
449 }
Judy Hsiao19e533c2019-08-14 16:52:51 +0800450 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_MONOVOL) {
Andy Hunge93b6b72014-07-17 21:30:53 -0700451 for (int i = 0; i < NCHAN; ++i) {
452 *out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[0], &auxaccum);
453 }
454 vol[0] += volinc[0];
Judy Hsiao19e533c2019-08-14 16:52:51 +0800455 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY_MONOVOL) {
Andy Hunge93b6b72014-07-17 21:30:53 -0700456 for (int i = 0; i < NCHAN; ++i) {
457 *out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[0], &auxaccum);
458 }
459 vol[0] += volinc[0];
Judy Hsiao19e533c2019-08-14 16:52:51 +0800460 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL
Judy Hsiaoc5cf9e22019-08-15 11:32:02 +0800461 || MIXTYPE == MIXTYPE_MULTI_SAVEONLY_STEREOVOL
Andy Hung2d8397a2020-07-14 00:56:03 -0700462 || MIXTYPE == MIXTYPE_MONOEXPAND
Judy Hsiaoc5cf9e22019-08-15 11:32:02 +0800463 || MIXTYPE == MIXTYPE_STEREOEXPAND) {
Judy Hsiao19e533c2019-08-14 16:52:51 +0800464 stereoVolumeHelper<MIXTYPE, NCHAN>(
465 out, in, vol, [&auxaccum] (auto &a, const auto &b) {
466 return MixMulAux<TO, TI, TV, TA>(a, b, &auxaccum);
467 });
Andy Hung2d8397a2020-07-14 00:56:03 -0700468 if constexpr (MIXTYPE == MIXTYPE_MONOEXPAND) in += 1;
Judy Hsiaoc5cf9e22019-08-15 11:32:02 +0800469 if constexpr (MIXTYPE == MIXTYPE_STEREOEXPAND) in += 2;
Judy Hsiao19e533c2019-08-14 16:52:51 +0800470 vol[0] += volinc[0];
471 vol[1] += volinc[1];
472 } else /* constexpr */ {
473 static_assert(dependent_false<MIXTYPE>, "invalid mixtype");
Andy Hung296b7412014-06-17 15:25:47 -0700474 }
475 auxaccum /= NCHAN;
476 *aux++ += MixMul<TA, TA, TAV>(auxaccum, *vola);
477 vola[0] += volainc;
478 } while (--frameCount);
479 } else {
480 do {
Judy Hsiao19e533c2019-08-14 16:52:51 +0800481 if constexpr (MIXTYPE == MIXTYPE_MULTI) {
Andy Hung2d8397a2020-07-14 00:56:03 -0700482 static_assert(NCHAN <= 2);
Andy Hung296b7412014-06-17 15:25:47 -0700483 for (int i = 0; i < NCHAN; ++i) {
484 *out++ += MixMul<TO, TI, TV>(*in++, vol[i]);
485 vol[i] += volinc[i];
486 }
Judy Hsiao19e533c2019-08-14 16:52:51 +0800487 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY) {
Andy Hung2d8397a2020-07-14 00:56:03 -0700488 static_assert(NCHAN <= 2);
Andy Hunge93b6b72014-07-17 21:30:53 -0700489 for (int i = 0; i < NCHAN; ++i) {
490 *out++ = MixMul<TO, TI, TV>(*in++, vol[i]);
491 vol[i] += volinc[i];
492 }
Judy Hsiao19e533c2019-08-14 16:52:51 +0800493 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_MONOVOL) {
Andy Hunge93b6b72014-07-17 21:30:53 -0700494 for (int i = 0; i < NCHAN; ++i) {
495 *out++ += MixMul<TO, TI, TV>(*in++, vol[0]);
496 }
497 vol[0] += volinc[0];
Judy Hsiao19e533c2019-08-14 16:52:51 +0800498 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY_MONOVOL) {
Andy Hunge93b6b72014-07-17 21:30:53 -0700499 for (int i = 0; i < NCHAN; ++i) {
500 *out++ = MixMul<TO, TI, TV>(*in++, vol[0]);
501 }
502 vol[0] += volinc[0];
Judy Hsiao19e533c2019-08-14 16:52:51 +0800503 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL
Judy Hsiaoc5cf9e22019-08-15 11:32:02 +0800504 || MIXTYPE == MIXTYPE_MULTI_SAVEONLY_STEREOVOL
Andy Hung2d8397a2020-07-14 00:56:03 -0700505 || MIXTYPE == MIXTYPE_MONOEXPAND
Judy Hsiaoc5cf9e22019-08-15 11:32:02 +0800506 || MIXTYPE == MIXTYPE_STEREOEXPAND) {
Judy Hsiao19e533c2019-08-14 16:52:51 +0800507 stereoVolumeHelper<MIXTYPE, NCHAN>(out, in, vol, [] (auto &a, const auto &b) {
508 return MixMul<TO, TI, TV>(a, b);
509 });
Andy Hung2d8397a2020-07-14 00:56:03 -0700510 if constexpr (MIXTYPE == MIXTYPE_MONOEXPAND) in += 1;
Judy Hsiaoc5cf9e22019-08-15 11:32:02 +0800511 if constexpr (MIXTYPE == MIXTYPE_STEREOEXPAND) in += 2;
Judy Hsiao19e533c2019-08-14 16:52:51 +0800512 vol[0] += volinc[0];
513 vol[1] += volinc[1];
514 } else /* constexpr */ {
515 static_assert(dependent_false<MIXTYPE>, "invalid mixtype");
Andy Hung296b7412014-06-17 15:25:47 -0700516 }
517 } while (--frameCount);
518 }
519}
520
521template <int MIXTYPE, int NCHAN,
522 typename TO, typename TI, typename TV, typename TA, typename TAV>
523inline void volumeMulti(TO* out, size_t frameCount,
524 const TI* in, TA* aux, const TV *vol, TAV vola)
525{
526#ifdef ALOGVV
527 ALOGVV("volumeMulti MIXTYPE:%d\n", MIXTYPE);
528#endif
529 if (aux != NULL) {
530 do {
531 TA auxaccum = 0;
Judy Hsiao19e533c2019-08-14 16:52:51 +0800532 if constexpr (MIXTYPE == MIXTYPE_MULTI) {
Andy Hung2d8397a2020-07-14 00:56:03 -0700533 static_assert(NCHAN <= 2);
Andy Hung296b7412014-06-17 15:25:47 -0700534 for (int i = 0; i < NCHAN; ++i) {
535 *out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum);
536 }
Judy Hsiao19e533c2019-08-14 16:52:51 +0800537 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY) {
Andy Hung2d8397a2020-07-14 00:56:03 -0700538 static_assert(NCHAN <= 2);
Andy Hunge93b6b72014-07-17 21:30:53 -0700539 for (int i = 0; i < NCHAN; ++i) {
540 *out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum);
541 }
Judy Hsiao19e533c2019-08-14 16:52:51 +0800542 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_MONOVOL) {
Andy Hunge93b6b72014-07-17 21:30:53 -0700543 for (int i = 0; i < NCHAN; ++i) {
544 *out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[0], &auxaccum);
545 }
Judy Hsiao19e533c2019-08-14 16:52:51 +0800546 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY_MONOVOL) {
Andy Hunge93b6b72014-07-17 21:30:53 -0700547 for (int i = 0; i < NCHAN; ++i) {
548 *out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[0], &auxaccum);
549 }
Judy Hsiao19e533c2019-08-14 16:52:51 +0800550 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL
Judy Hsiaoc5cf9e22019-08-15 11:32:02 +0800551 || MIXTYPE == MIXTYPE_MULTI_SAVEONLY_STEREOVOL
Andy Hung2d8397a2020-07-14 00:56:03 -0700552 || MIXTYPE == MIXTYPE_MONOEXPAND
Judy Hsiaoc5cf9e22019-08-15 11:32:02 +0800553 || MIXTYPE == MIXTYPE_STEREOEXPAND) {
Judy Hsiao19e533c2019-08-14 16:52:51 +0800554 stereoVolumeHelper<MIXTYPE, NCHAN>(
555 out, in, vol, [&auxaccum] (auto &a, const auto &b) {
556 return MixMulAux<TO, TI, TV, TA>(a, b, &auxaccum);
557 });
Andy Hung2d8397a2020-07-14 00:56:03 -0700558 if constexpr (MIXTYPE == MIXTYPE_MONOEXPAND) in += 1;
Judy Hsiaoc5cf9e22019-08-15 11:32:02 +0800559 if constexpr (MIXTYPE == MIXTYPE_STEREOEXPAND) in += 2;
Judy Hsiao19e533c2019-08-14 16:52:51 +0800560 } else /* constexpr */ {
561 static_assert(dependent_false<MIXTYPE>, "invalid mixtype");
Andy Hung296b7412014-06-17 15:25:47 -0700562 }
563 auxaccum /= NCHAN;
564 *aux++ += MixMul<TA, TA, TAV>(auxaccum, vola);
565 } while (--frameCount);
566 } else {
567 do {
Andy Hung2d8397a2020-07-14 00:56:03 -0700568 // ALOGD("Mixtype:%d NCHAN:%d", MIXTYPE, NCHAN);
Judy Hsiao19e533c2019-08-14 16:52:51 +0800569 if constexpr (MIXTYPE == MIXTYPE_MULTI) {
Andy Hung2d8397a2020-07-14 00:56:03 -0700570 static_assert(NCHAN <= 2);
Andy Hung296b7412014-06-17 15:25:47 -0700571 for (int i = 0; i < NCHAN; ++i) {
572 *out++ += MixMul<TO, TI, TV>(*in++, vol[i]);
573 }
Judy Hsiao19e533c2019-08-14 16:52:51 +0800574 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY) {
Andy Hung2d8397a2020-07-14 00:56:03 -0700575 static_assert(NCHAN <= 2);
Andy Hunge93b6b72014-07-17 21:30:53 -0700576 for (int i = 0; i < NCHAN; ++i) {
577 *out++ = MixMul<TO, TI, TV>(*in++, vol[i]);
578 }
Judy Hsiao19e533c2019-08-14 16:52:51 +0800579 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_MONOVOL) {
Andy Hunge93b6b72014-07-17 21:30:53 -0700580 for (int i = 0; i < NCHAN; ++i) {
581 *out++ += MixMul<TO, TI, TV>(*in++, vol[0]);
582 }
Judy Hsiao19e533c2019-08-14 16:52:51 +0800583 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY_MONOVOL) {
Andy Hunge93b6b72014-07-17 21:30:53 -0700584 for (int i = 0; i < NCHAN; ++i) {
585 *out++ = MixMul<TO, TI, TV>(*in++, vol[0]);
586 }
Judy Hsiao19e533c2019-08-14 16:52:51 +0800587 } else if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL
Judy Hsiaoc5cf9e22019-08-15 11:32:02 +0800588 || MIXTYPE == MIXTYPE_MULTI_SAVEONLY_STEREOVOL
Andy Hung2d8397a2020-07-14 00:56:03 -0700589 || MIXTYPE == MIXTYPE_MONOEXPAND
Judy Hsiaoc5cf9e22019-08-15 11:32:02 +0800590 || MIXTYPE == MIXTYPE_STEREOEXPAND) {
Judy Hsiao19e533c2019-08-14 16:52:51 +0800591 stereoVolumeHelper<MIXTYPE, NCHAN>(out, in, vol, [] (auto &a, const auto &b) {
592 return MixMul<TO, TI, TV>(a, b);
593 });
Andy Hung2d8397a2020-07-14 00:56:03 -0700594 if constexpr (MIXTYPE == MIXTYPE_MONOEXPAND) in += 1;
Judy Hsiaoc5cf9e22019-08-15 11:32:02 +0800595 if constexpr (MIXTYPE == MIXTYPE_STEREOEXPAND) in += 2;
Judy Hsiao19e533c2019-08-14 16:52:51 +0800596 } else /* constexpr */ {
597 static_assert(dependent_false<MIXTYPE>, "invalid mixtype");
Andy Hung296b7412014-06-17 15:25:47 -0700598 }
599 } while (--frameCount);
600 }
601}
602
603};
604
605#endif /* ANDROID_AUDIO_MIXER_OPS_H */