AudioMixerOps.h revision 296b741e8eb38e749e3202182f703a2e30ee5f1f
1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#ifndef ANDROID_AUDIO_MIXER_OPS_H 18#define ANDROID_AUDIO_MIXER_OPS_H 19 20namespace android { 21 22/* Behavior of is_same<>::value is true if the types are identical, 23 * false otherwise. Identical to the STL std::is_same. 24 */ 25template<typename T, typename U> 26struct is_same 27{ 28 static const bool value = false; 29}; 30 31template<typename T> 32struct is_same<T, T> // partial specialization 33{ 34 static const bool value = true; 35}; 36 37 38/* MixMul is a multiplication operator to scale an audio input signal 39 * by a volume gain, with the formula: 40 * 41 * O(utput) = I(nput) * V(olume) 42 * 43 * The output, input, and volume may have different types. 44 * There are 27 variants, of which 14 are actually defined in an 45 * explicitly templated class. 46 * 47 * The following type variables and the underlying meaning: 48 * 49 * Output type TO: int32_t (Q4.27) or int16_t (Q.15) or float [-1,1] 50 * Input signal type TI: int32_t (Q4.27) or int16_t (Q.15) or float [-1,1] 51 * Volume type TV: int32_t (U4.28) or int16_t (U4.12) or float [-1,1] 52 * 53 * For high precision audio, only the <TO, TI, TV> = <float, float, float> 54 * needs to be accelerated. This is perhaps the easiest form to do quickly as well. 55 */ 56 57template <typename TO, typename TI, typename TV> 58inline TO MixMul(TI value, TV volume) { 59 COMPILE_TIME_ASSERT_FUNCTION_SCOPE(false); 60 // should not be here :-). 61 // To avoid mistakes, this template is always specialized. 62 return value * volume; 63} 64 65template <> 66inline int32_t MixMul<int32_t, int16_t, int16_t>(int16_t value, int16_t volume) { 67 return value * volume; 68} 69 70template <> 71inline int32_t MixMul<int32_t, int32_t, int16_t>(int32_t value, int16_t volume) { 72 return (value >> 12) * volume; 73} 74 75template <> 76inline int32_t MixMul<int32_t, int16_t, int32_t>(int16_t value, int32_t volume) { 77 return value * (volume >> 16); 78} 79 80template <> 81inline int32_t MixMul<int32_t, int32_t, int32_t>(int32_t value, int32_t volume) { 82 return (value >> 12) * (volume >> 16); 83} 84 85template <> 86inline float MixMul<float, float, int16_t>(float value, int16_t volume) { 87 static const float norm = 1. / (1 << 12); 88 return value * volume * norm; 89} 90 91template <> 92inline float MixMul<float, float, int32_t>(float value, int32_t volume) { 93 static const float norm = 1. / (1 << 28); 94 return value * volume * norm; 95} 96 97template <> 98inline int16_t MixMul<int16_t, float, int16_t>(float value, int16_t volume) { 99 return clamp16_from_float(MixMul<float, float, int16_t>(value, volume)); 100} 101 102template <> 103inline int16_t MixMul<int16_t, float, int32_t>(float value, int32_t volume) { 104 return clamp16_from_float(MixMul<float, float, int32_t>(value, volume)); 105} 106 107template <> 108inline float MixMul<float, int16_t, int16_t>(int16_t value, int16_t volume) { 109 static const float norm = 1. / (1 << (15 + 12)); 110 return static_cast<float>(value) * static_cast<float>(volume) * norm; 111} 112 113template <> 114inline float MixMul<float, int16_t, int32_t>(int16_t value, int32_t volume) { 115 static const float norm = 1. / (1ULL << (15 + 28)); 116 return static_cast<float>(value) * static_cast<float>(volume) * norm; 117} 118 119template <> 120inline int16_t MixMul<int16_t, int16_t, int16_t>(int16_t value, int16_t volume) { 121 return clamp16(MixMul<int32_t, int16_t, int16_t>(value, volume) >> 12); 122} 123 124template <> 125inline int16_t MixMul<int16_t, int32_t, int16_t>(int32_t value, int16_t volume) { 126 return clamp16(MixMul<int32_t, int32_t, int16_t>(value, volume) >> 12); 127} 128 129template <> 130inline int16_t MixMul<int16_t, int16_t, int32_t>(int16_t value, int32_t volume) { 131 return clamp16(MixMul<int32_t, int16_t, int32_t>(value, volume) >> 12); 132} 133 134template <> 135inline int16_t MixMul<int16_t, int32_t, int32_t>(int32_t value, int32_t volume) { 136 return clamp16(MixMul<int32_t, int32_t, int32_t>(value, volume) >> 12); 137} 138 139/* 140 * MixAccum is used to add into an accumulator register of a possibly different 141 * type. The TO and TI types are the same as MixMul. 142 */ 143 144template <typename TO, typename TI> 145inline void MixAccum(TO *auxaccum, TI value) { 146 if (!is_same<TO, TI>::value) { 147 LOG_ALWAYS_FATAL("MixAccum type not properly specialized: %d %d\n", 148 sizeof(TO), sizeof(TI)); 149 } 150 *auxaccum += value; 151} 152 153template<> 154inline void MixAccum<float, int16_t>(float *auxaccum, int16_t value) { 155 static const float norm = 1. / (1 << 15); 156 *auxaccum += norm * value; 157} 158 159template<> 160inline void MixAccum<float, int32_t>(float *auxaccum, int32_t value) { 161 static const float norm = 1. / (1 << 27); 162 *auxaccum += norm * value; 163} 164 165template<> 166inline void MixAccum<int32_t, int16_t>(int32_t *auxaccum, int16_t value) { 167 *auxaccum += value << 12; 168} 169 170template<> 171inline void MixAccum<int32_t, float>(int32_t *auxaccum, float value) { 172 *auxaccum += clampq4_27_from_float(value); 173} 174 175/* MixMulAux is just like MixMul except it combines with 176 * an accumulator operation MixAccum. 177 */ 178 179template <typename TO, typename TI, typename TV, typename TA> 180inline TO MixMulAux(TI value, TV volume, TA *auxaccum) { 181 MixAccum<TA, TI>(auxaccum, value); 182 return MixMul<TO, TI, TV>(value, volume); 183} 184 185/* MIXTYPE is used to determine how the samples in the input frame 186 * are mixed with volume gain into the output frame. 187 * See the volumeRampMulti functions below for more details. 188 */ 189enum { 190 MIXTYPE_MULTI, 191 MIXTYPE_MONOEXPAND, 192 MIXTYPE_MULTI_SAVEONLY, 193}; 194 195/* 196 * The volumeRampMulti and volumeRamp functions take a MIXTYPE 197 * which indicates the per-frame mixing and accumulation strategy. 198 * 199 * MIXTYPE_MULTI: 200 * NCHAN represents number of input and output channels. 201 * TO: int32_t (Q4.27) or float 202 * TI: int32_t (Q4.27) or int16_t (Q0.15) or float 203 * TV: int32_t (U4.28) or int16_t (U4.12) or float 204 * vol: represents a volume array. 205 * 206 * This accumulates into the out pointer. 207 * 208 * MIXTYPE_MONOEXPAND: 209 * Single input channel. NCHAN represents number of output channels. 210 * TO: int32_t (Q4.27) or float 211 * TI: int32_t (Q4.27) or int16_t (Q0.15) or float 212 * TV: int32_t (U4.28) or int16_t (U4.12) or float 213 * Input channel count is 1. 214 * vol: represents volume array. 215 * 216 * This accumulates into the out pointer. 217 * 218 * MIXTYPE_MULTI_SAVEONLY: 219 * NCHAN represents number of input and output channels. 220 * TO: int16_t (Q.15) or float 221 * TI: int32_t (Q4.27) or int16_t (Q0.15) or float 222 * TV: int32_t (U4.28) or int16_t (U4.12) or float 223 * vol: represents a volume array. 224 * 225 * MIXTYPE_MULTI_SAVEONLY does not accumulate into the out pointer. 226 */ 227 228template <int MIXTYPE, int NCHAN, 229 typename TO, typename TI, typename TV, typename TA, typename TAV> 230inline void volumeRampMulti(TO* out, size_t frameCount, 231 const TI* in, TA* aux, TV *vol, const TV *volinc, TAV *vola, TAV volainc) 232{ 233#ifdef ALOGVV 234 ALOGVV("volumeRampMulti, MIXTYPE:%d\n", MIXTYPE); 235#endif 236 if (aux != NULL) { 237 do { 238 TA auxaccum = 0; 239 switch (MIXTYPE) { 240 case MIXTYPE_MULTI: 241 for (int i = 0; i < NCHAN; ++i) { 242 *out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum); 243 vol[i] += volinc[i]; 244 } 245 break; 246 case MIXTYPE_MULTI_SAVEONLY: 247 for (int i = 0; i < NCHAN; ++i) { 248 *out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum); 249 vol[i] += volinc[i]; 250 } 251 break; 252 case MIXTYPE_MONOEXPAND: 253 for (int i = 0; i < NCHAN; ++i) { 254 *out++ += MixMulAux<TO, TI, TV, TA>(*in, vol[i], &auxaccum); 255 vol[i] += volinc[i]; 256 } 257 in++; 258 break; 259 default: 260 LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE); 261 break; 262 } 263 auxaccum /= NCHAN; 264 *aux++ += MixMul<TA, TA, TAV>(auxaccum, *vola); 265 vola[0] += volainc; 266 } while (--frameCount); 267 } else { 268 do { 269 switch (MIXTYPE) { 270 case MIXTYPE_MULTI: 271 for (int i = 0; i < NCHAN; ++i) { 272 *out++ += MixMul<TO, TI, TV>(*in++, vol[i]); 273 vol[i] += volinc[i]; 274 } 275 break; 276 case MIXTYPE_MULTI_SAVEONLY: 277 for (int i = 0; i < NCHAN; ++i) { 278 *out++ = MixMul<TO, TI, TV>(*in++, vol[i]); 279 vol[i] += volinc[i]; 280 } 281 break; 282 case MIXTYPE_MONOEXPAND: 283 for (int i = 0; i < NCHAN; ++i) { 284 *out++ += MixMul<TO, TI, TV>(*in, vol[i]); 285 vol[i] += volinc[i]; 286 } 287 in++; 288 break; 289 default: 290 LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE); 291 break; 292 } 293 } while (--frameCount); 294 } 295} 296 297template <int MIXTYPE, int NCHAN, 298 typename TO, typename TI, typename TV, typename TA, typename TAV> 299inline void volumeMulti(TO* out, size_t frameCount, 300 const TI* in, TA* aux, const TV *vol, TAV vola) 301{ 302#ifdef ALOGVV 303 ALOGVV("volumeMulti MIXTYPE:%d\n", MIXTYPE); 304#endif 305 if (aux != NULL) { 306 do { 307 TA auxaccum = 0; 308 switch (MIXTYPE) { 309 case MIXTYPE_MULTI: 310 for (int i = 0; i < NCHAN; ++i) { 311 *out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum); 312 } 313 break; 314 case MIXTYPE_MULTI_SAVEONLY: 315 for (int i = 0; i < NCHAN; ++i) { 316 *out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum); 317 } 318 break; 319 case MIXTYPE_MONOEXPAND: 320 for (int i = 0; i < NCHAN; ++i) { 321 *out++ += MixMulAux<TO, TI, TV, TA>(*in, vol[i], &auxaccum); 322 } 323 in++; 324 break; 325 default: 326 LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE); 327 break; 328 } 329 auxaccum /= NCHAN; 330 *aux++ += MixMul<TA, TA, TAV>(auxaccum, vola); 331 } while (--frameCount); 332 } else { 333 do { 334 switch (MIXTYPE) { 335 case MIXTYPE_MULTI: 336 for (int i = 0; i < NCHAN; ++i) { 337 *out++ += MixMul<TO, TI, TV>(*in++, vol[i]); 338 } 339 break; 340 case MIXTYPE_MULTI_SAVEONLY: 341 for (int i = 0; i < NCHAN; ++i) { 342 *out++ = MixMul<TO, TI, TV>(*in++, vol[i]); 343 } 344 break; 345 case MIXTYPE_MONOEXPAND: 346 for (int i = 0; i < NCHAN; ++i) { 347 *out++ += MixMul<TO, TI, TV>(*in, vol[i]); 348 } 349 in++; 350 break; 351 default: 352 LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE); 353 break; 354 } 355 } while (--frameCount); 356 } 357} 358 359}; 360 361#endif /* ANDROID_AUDIO_MIXER_OPS_H */ 362