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