blob: 519f4a88bb24ae4599f884912e85dd532e582629 [file] [log] [blame]
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -08001/*
2 * Copyright (C) 2012 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#define LOG_TAG "EffectDownmix"
Jean-Michel Trivif28c8792012-04-18 18:38:42 -070018//#define LOG_NDEBUG 0
Mark Salyzyn60d02072016-09-29 08:48:48 -070019
Mark Salyzyn7cb0e732014-04-18 13:48:25 -070020#include <inttypes.h>
Mark Salyzyn60d02072016-09-29 08:48:48 -070021#include <stdbool.h>
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -080022#include <stdlib.h>
23#include <string.h>
Mark Salyzyn60d02072016-09-29 08:48:48 -070024
Mark Salyzyneb165612017-01-10 09:08:19 -080025#include <log/log.h>
Mark Salyzyn60d02072016-09-29 08:48:48 -070026
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -080027#include "EffectDownmix.h"
28
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -070029// Do not submit with DOWNMIX_TEST_CHANNEL_INDEX defined, strictly for testing
30//#define DOWNMIX_TEST_CHANNEL_INDEX 0
31// Do not submit with DOWNMIX_ALWAYS_USE_GENERIC_DOWNMIXER defined, strictly for testing
32//#define DOWNMIX_ALWAYS_USE_GENERIC_DOWNMIXER 0
33
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -080034#define MINUS_3_DB_IN_Q19_12 2896 // -3dB = 0.707 * 2^12 = 2896
35
Ramesh Katurid7d01342016-05-02 15:03:47 +053036#ifdef BUILD_FLOAT
37#define MINUS_3_DB_IN_FLOAT 0.70710678f // -3dB = 0.70710678f
38#endif
39
Glenn Kasten029a64e2014-04-15 09:58:55 -070040// subset of possible audio_channel_mask_t values, and AUDIO_CHANNEL_OUT_* renamed to CHANNEL_MASK_*
Jean-Michel Trividb46b482012-04-23 11:29:26 -070041typedef enum {
Glenn Kasten029a64e2014-04-15 09:58:55 -070042 CHANNEL_MASK_QUAD_BACK = AUDIO_CHANNEL_OUT_QUAD_BACK,
43 CHANNEL_MASK_QUAD_SIDE = AUDIO_CHANNEL_OUT_QUAD_SIDE,
44 CHANNEL_MASK_5POINT1_BACK = AUDIO_CHANNEL_OUT_5POINT1_BACK,
45 CHANNEL_MASK_5POINT1_SIDE = AUDIO_CHANNEL_OUT_5POINT1_SIDE,
46 CHANNEL_MASK_7POINT1 = AUDIO_CHANNEL_OUT_7POINT1,
Jean-Michel Trividb46b482012-04-23 11:29:26 -070047} downmix_input_channel_mask_t;
48
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -080049// effect_handle_t interface implementation for downmix effect
50const struct effect_interface_s gDownmixInterface = {
51 Downmix_Process,
52 Downmix_Command,
53 Downmix_GetDescriptor,
54 NULL /* no process_reverse function, no reference stream needed */
55};
56
Marco Nelissen7f16b192012-10-25 16:05:57 -070057// This is the only symbol that needs to be exported
58__attribute__ ((visibility ("default")))
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -080059audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
synergydevc9d8ea72013-10-19 22:51:33 -070060 .tag = AUDIO_EFFECT_LIBRARY_TAG,
61 .version = EFFECT_LIBRARY_API_VERSION,
62 .name = "Downmix Library",
63 .implementor = "The Android Open Source Project",
64 .create_effect = DownmixLib_Create,
65 .release_effect = DownmixLib_Release,
66 .get_descriptor = DownmixLib_GetDescriptor,
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -080067};
68
69
70// AOSP insert downmix UUID: 93f04452-e4fe-41cc-91f9-e475b6d1d69f
71static const effect_descriptor_t gDownmixDescriptor = {
72 EFFECT_UIID_DOWNMIX__, //type
73 {0x93f04452, 0xe4fe, 0x41cc, 0x91f9, {0xe4, 0x75, 0xb6, 0xd1, 0xd6, 0x9f}}, // uuid
74 EFFECT_CONTROL_API_VERSION,
75 EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST,
76 0, //FIXME what value should be reported? // cpu load
77 0, //FIXME what value should be reported? // memory usage
78 "Multichannel Downmix To Stereo", // human readable effect name
79 "The Android Open Source Project" // human readable effect implementor name
80};
81
82// gDescriptors contains pointers to all defined effect descriptor in this library
83static const effect_descriptor_t * const gDescriptors[] = {
84 &gDownmixDescriptor
85};
86
87// number of effects in this library
88const int kNbEffects = sizeof(gDescriptors) / sizeof(const effect_descriptor_t *);
Ramesh Katurid7d01342016-05-02 15:03:47 +053089#ifdef BUILD_FLOAT
90static LVM_FLOAT clamp_float(LVM_FLOAT a) {
91 if (a > 1.0f) {
92 return 1.0f;
93 }
94 else if (a < -1.0f) {
95 return -1.0f;
96 }
97 else {
98 return a;
99 }
100}
101#endif
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800102/*----------------------------------------------------------------------------
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700103 * Test code
104 *--------------------------------------------------------------------------*/
105#ifdef DOWNMIX_TEST_CHANNEL_INDEX
106// strictly for testing, logs the indices of the channels for a given mask,
107// uses the same code as Downmix_foldGeneric()
108void Downmix_testIndexComputation(uint32_t mask) {
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700109 ALOGI("Testing index computation for 0x%" PRIx32 ":", mask);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700110 // check against unsupported channels
111 if (mask & kUnsupported) {
112 ALOGE("Unsupported channels (top or front left/right of center)");
113 return;
114 }
115 // verify has FL/FR
116 if ((mask & AUDIO_CHANNEL_OUT_STEREO) != AUDIO_CHANNEL_OUT_STEREO) {
117 ALOGE("Front channels must be present");
118 return;
119 }
120 // verify uses SIDE as a pair (ok if not using SIDE at all)
121 bool hasSides = false;
122 if ((mask & kSides) != 0) {
123 if ((mask & kSides) != kSides) {
124 ALOGE("Side channels must be used as a pair");
125 return;
126 }
127 hasSides = true;
128 }
129 // verify uses BACK as a pair (ok if not using BACK at all)
130 bool hasBacks = false;
131 if ((mask & kBacks) != 0) {
132 if ((mask & kBacks) != kBacks) {
133 ALOGE("Back channels must be used as a pair");
134 return;
135 }
136 hasBacks = true;
137 }
138
Andy Hunge5412692014-05-16 11:25:07 -0700139 const int numChan = audio_channel_count_from_out_mask(mask);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700140 const bool hasFC = ((mask & AUDIO_CHANNEL_OUT_FRONT_CENTER) == AUDIO_CHANNEL_OUT_FRONT_CENTER);
141 const bool hasLFE =
142 ((mask & AUDIO_CHANNEL_OUT_LOW_FREQUENCY) == AUDIO_CHANNEL_OUT_LOW_FREQUENCY);
143 const bool hasBC = ((mask & AUDIO_CHANNEL_OUT_BACK_CENTER) == AUDIO_CHANNEL_OUT_BACK_CENTER);
144 // compute at what index each channel is: samples will be in the following order:
145 // FL FR FC LFE BL BR BC SL SR
146 // when a channel is not present, its index is set to the same as the index of the preceding
147 // channel
148 const int indexFC = hasFC ? 2 : 1; // front center
149 const int indexLFE = hasLFE ? indexFC + 1 : indexFC; // low frequency
150 const int indexBL = hasBacks ? indexLFE + 1 : indexLFE; // back left
151 const int indexBR = hasBacks ? indexBL + 1 : indexBL; // back right
152 const int indexBC = hasBC ? indexBR + 1 : indexBR; // back center
153 const int indexSL = hasSides ? indexBC + 1 : indexBC; // side left
154 const int indexSR = hasSides ? indexSL + 1 : indexSL; // side right
155
156 ALOGI(" FL FR FC LFE BL BR BC SL SR");
157 ALOGI(" %d %d %d %d %d %d %d %d %d",
158 0, 1, indexFC, indexLFE, indexBL, indexBR, indexBC, indexSL, indexSR);
159}
160#endif
161
Haynes Mathew Georgeee507ef2015-07-15 11:06:05 -0700162static bool Downmix_validChannelMask(uint32_t mask)
163{
164 if (!mask) {
165 return false;
166 }
167 // check against unsupported channels
168 if (mask & kUnsupported) {
169 ALOGE("Unsupported channels (top or front left/right of center)");
170 return false;
171 }
172 // verify has FL/FR
173 if ((mask & AUDIO_CHANNEL_OUT_STEREO) != AUDIO_CHANNEL_OUT_STEREO) {
174 ALOGE("Front channels must be present");
175 return false;
176 }
177 // verify uses SIDE as a pair (ok if not using SIDE at all)
178 if ((mask & kSides) != 0) {
179 if ((mask & kSides) != kSides) {
180 ALOGE("Side channels must be used as a pair");
181 return false;
182 }
183 }
184 // verify uses BACK as a pair (ok if not using BACK at all)
185 if ((mask & kBacks) != 0) {
186 if ((mask & kBacks) != kBacks) {
187 ALOGE("Back channels must be used as a pair");
188 return false;
189 }
190 }
191 return true;
192}
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700193
194/*----------------------------------------------------------------------------
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800195 * Effect API implementation
196 *--------------------------------------------------------------------------*/
197
198/*--- Effect Library Interface Implementation ---*/
199
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800200int32_t DownmixLib_Create(const effect_uuid_t *uuid,
Eric Laurent0f714a42015-06-19 15:33:57 -0700201 int32_t sessionId __unused,
202 int32_t ioId __unused,
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800203 effect_handle_t *pHandle) {
204 int ret;
205 int i;
206 downmix_module_t *module;
207 const effect_descriptor_t *desc;
208
209 ALOGV("DownmixLib_Create()");
210
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700211#ifdef DOWNMIX_TEST_CHANNEL_INDEX
212 // should work (won't log an error)
213 ALOGI("DOWNMIX_TEST_CHANNEL_INDEX: should work:");
214 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT |
215 AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_BACK_CENTER);
216 Downmix_testIndexComputation(CHANNEL_MASK_QUAD_SIDE | CHANNEL_MASK_QUAD_BACK);
217 Downmix_testIndexComputation(CHANNEL_MASK_5POINT1_SIDE | AUDIO_CHANNEL_OUT_BACK_CENTER);
218 Downmix_testIndexComputation(CHANNEL_MASK_5POINT1_BACK | AUDIO_CHANNEL_OUT_BACK_CENTER);
219 // shouldn't work (will log an error, won't display channel indices)
220 ALOGI("DOWNMIX_TEST_CHANNEL_INDEX: should NOT work:");
221 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT |
222 AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_BACK_LEFT);
223 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT |
224 AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_SIDE_LEFT);
225 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT |
226 AUDIO_CHANNEL_OUT_BACK_LEFT | AUDIO_CHANNEL_OUT_BACK_RIGHT);
227 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT |
228 AUDIO_CHANNEL_OUT_SIDE_LEFT | AUDIO_CHANNEL_OUT_SIDE_RIGHT);
229#endif
230
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800231 if (pHandle == NULL || uuid == NULL) {
232 return -EINVAL;
233 }
234
235 for (i = 0 ; i < kNbEffects ; i++) {
236 desc = gDescriptors[i];
237 if (memcmp(uuid, &desc->uuid, sizeof(effect_uuid_t)) == 0) {
238 break;
239 }
240 }
241
242 if (i == kNbEffects) {
243 return -ENOENT;
244 }
245
246 module = malloc(sizeof(downmix_module_t));
247
248 module->itfe = &gDownmixInterface;
249
250 module->context.state = DOWNMIX_STATE_UNINITIALIZED;
251
252 ret = Downmix_Init(module);
253 if (ret < 0) {
254 ALOGW("DownmixLib_Create() init failed");
255 free(module);
256 return ret;
257 }
258
259 *pHandle = (effect_handle_t) module;
260
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700261 ALOGV("DownmixLib_Create() %p , size %zu", module, sizeof(downmix_module_t));
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800262
263 return 0;
264}
265
266
267int32_t DownmixLib_Release(effect_handle_t handle) {
268 downmix_module_t *pDwmModule = (downmix_module_t *)handle;
269
270 ALOGV("DownmixLib_Release() %p", handle);
271 if (handle == NULL) {
272 return -EINVAL;
273 }
274
275 pDwmModule->context.state = DOWNMIX_STATE_UNINITIALIZED;
276
277 free(pDwmModule);
278 return 0;
279}
280
281
282int32_t DownmixLib_GetDescriptor(const effect_uuid_t *uuid, effect_descriptor_t *pDescriptor) {
283 ALOGV("DownmixLib_GetDescriptor()");
284 int i;
285
286 if (pDescriptor == NULL || uuid == NULL){
287 ALOGE("DownmixLib_Create() called with NULL pointer");
288 return -EINVAL;
289 }
290 ALOGV("DownmixLib_GetDescriptor() nb effects=%d", kNbEffects);
291 for (i = 0; i < kNbEffects; i++) {
292 ALOGV("DownmixLib_GetDescriptor() i=%d", i);
293 if (memcmp(uuid, &gDescriptors[i]->uuid, sizeof(effect_uuid_t)) == 0) {
294 memcpy(pDescriptor, gDescriptors[i], sizeof(effect_descriptor_t));
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700295 ALOGV("EffectGetDescriptor - UUID matched downmix type %d, UUID = %" PRIx32,
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800296 i, gDescriptors[i]->uuid.timeLow);
297 return 0;
298 }
299 }
300
301 return -EINVAL;
302}
303
Ramesh Katurid7d01342016-05-02 15:03:47 +0530304#ifndef BUILD_FLOAT
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800305/*--- Effect Control Interface Implementation ---*/
306
307static int Downmix_Process(effect_handle_t self,
308 audio_buffer_t *inBuffer, audio_buffer_t *outBuffer) {
309
310 downmix_object_t *pDownmixer;
311 int16_t *pSrc, *pDst;
312 downmix_module_t *pDwmModule = (downmix_module_t *)self;
313
314 if (pDwmModule == NULL) {
315 return -EINVAL;
316 }
317
318 if (inBuffer == NULL || inBuffer->raw == NULL ||
319 outBuffer == NULL || outBuffer->raw == NULL ||
320 inBuffer->frameCount != outBuffer->frameCount) {
321 return -EINVAL;
322 }
323
324 pDownmixer = (downmix_object_t*) &pDwmModule->context;
325
326 if (pDownmixer->state == DOWNMIX_STATE_UNINITIALIZED) {
327 ALOGE("Downmix_Process error: trying to use an uninitialized downmixer");
328 return -EINVAL;
329 } else if (pDownmixer->state == DOWNMIX_STATE_INITIALIZED) {
330 ALOGE("Downmix_Process error: trying to use a non-configured downmixer");
331 return -ENODATA;
332 }
333
334 pSrc = inBuffer->s16;
335 pDst = outBuffer->s16;
336 size_t numFrames = outBuffer->frameCount;
337
338 const bool accumulate =
339 (pDwmModule->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700340 const uint32_t downmixInputChannelMask = pDwmModule->config.inputCfg.channels;
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800341
342 switch(pDownmixer->type) {
343
344 case DOWNMIX_TYPE_STRIP:
345 if (accumulate) {
346 while (numFrames) {
347 pDst[0] = clamp16(pDst[0] + pSrc[0]);
348 pDst[1] = clamp16(pDst[1] + pSrc[1]);
349 pSrc += pDownmixer->input_channel_count;
350 pDst += 2;
351 numFrames--;
352 }
353 } else {
354 while (numFrames) {
355 pDst[0] = pSrc[0];
356 pDst[1] = pSrc[1];
357 pSrc += pDownmixer->input_channel_count;
358 pDst += 2;
359 numFrames--;
360 }
361 }
362 break;
363
364 case DOWNMIX_TYPE_FOLD:
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700365#ifdef DOWNMIX_ALWAYS_USE_GENERIC_DOWNMIXER
366 // bypass the optimized downmix routines for the common formats
367 if (!Downmix_foldGeneric(
368 downmixInputChannelMask, pSrc, pDst, numFrames, accumulate)) {
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700369 ALOGE("Multichannel configuration 0x%" PRIx32 " is not supported", downmixInputChannelMask);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700370 return -EINVAL;
371 }
372 break;
373#endif
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800374 // optimize for the common formats
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700375 switch((downmix_input_channel_mask_t)downmixInputChannelMask) {
Jean-Michel Trividb46b482012-04-23 11:29:26 -0700376 case CHANNEL_MASK_QUAD_BACK:
377 case CHANNEL_MASK_QUAD_SIDE:
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800378 Downmix_foldFromQuad(pSrc, pDst, numFrames, accumulate);
379 break;
Jean-Michel Trividb46b482012-04-23 11:29:26 -0700380 case CHANNEL_MASK_5POINT1_BACK:
381 case CHANNEL_MASK_5POINT1_SIDE:
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800382 Downmix_foldFrom5Point1(pSrc, pDst, numFrames, accumulate);
383 break;
Glenn Kasten029a64e2014-04-15 09:58:55 -0700384 case CHANNEL_MASK_7POINT1:
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800385 Downmix_foldFrom7Point1(pSrc, pDst, numFrames, accumulate);
386 break;
387 default:
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700388 if (!Downmix_foldGeneric(
389 downmixInputChannelMask, pSrc, pDst, numFrames, accumulate)) {
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700390 ALOGE("Multichannel configuration 0x%" PRIx32 " is not supported", downmixInputChannelMask);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700391 return -EINVAL;
392 }
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800393 break;
394 }
395 break;
396
397 default:
398 return -EINVAL;
399 }
400
401 return 0;
402}
Ramesh Katurid7d01342016-05-02 15:03:47 +0530403#else /*BUILD_FLOAT*/
404/*--- Effect Control Interface Implementation ---*/
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800405
Ramesh Katurid7d01342016-05-02 15:03:47 +0530406static int Downmix_Process(effect_handle_t self,
407 audio_buffer_t *inBuffer, audio_buffer_t *outBuffer) {
408
409 downmix_object_t *pDownmixer;
410 LVM_FLOAT *pSrc, *pDst;
411 downmix_module_t *pDwmModule = (downmix_module_t *)self;
412
413 if (pDwmModule == NULL) {
414 return -EINVAL;
415 }
416
417 if (inBuffer == NULL || inBuffer->raw == NULL ||
418 outBuffer == NULL || outBuffer->raw == NULL ||
419 inBuffer->frameCount != outBuffer->frameCount) {
420 return -EINVAL;
421 }
422
423 pDownmixer = (downmix_object_t*) &pDwmModule->context;
424
425 if (pDownmixer->state == DOWNMIX_STATE_UNINITIALIZED) {
426 ALOGE("Downmix_Process error: trying to use an uninitialized downmixer");
427 return -EINVAL;
428 } else if (pDownmixer->state == DOWNMIX_STATE_INITIALIZED) {
429 ALOGE("Downmix_Process error: trying to use a non-configured downmixer");
430 return -ENODATA;
431 }
432
433 pSrc = (LVM_FLOAT *) inBuffer->s16;
434 pDst = (LVM_FLOAT *) outBuffer->s16;
435 size_t numFrames = outBuffer->frameCount;
436
437 const bool accumulate =
438 (pDwmModule->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE);
439 const uint32_t downmixInputChannelMask = pDwmModule->config.inputCfg.channels;
440
441 switch(pDownmixer->type) {
442
443 case DOWNMIX_TYPE_STRIP:
444 if (accumulate) {
445 while (numFrames) {
446 pDst[0] = clamp_float(pDst[0] + pSrc[0]);
447 pDst[1] = clamp_float(pDst[1] + pSrc[1]);
448 pSrc += pDownmixer->input_channel_count;
449 pDst += 2;
450 numFrames--;
451 }
452 } else {
453 while (numFrames) {
454 pDst[0] = pSrc[0];
455 pDst[1] = pSrc[1];
456 pSrc += pDownmixer->input_channel_count;
457 pDst += 2;
458 numFrames--;
459 }
460 }
461 break;
462
463 case DOWNMIX_TYPE_FOLD:
464#ifdef DOWNMIX_ALWAYS_USE_GENERIC_DOWNMIXER
465 // bypass the optimized downmix routines for the common formats
466 if (!Downmix_foldGeneric(
467 downmixInputChannelMask, pSrc, pDst, numFrames, accumulate)) {
468 ALOGE("Multichannel configuration 0x%" PRIx32 " is not supported",
469 downmixInputChannelMask);
470 return -EINVAL;
471 }
472 break;
473#endif
474 // optimize for the common formats
475 switch((downmix_input_channel_mask_t)downmixInputChannelMask) {
476 case CHANNEL_MASK_QUAD_BACK:
477 case CHANNEL_MASK_QUAD_SIDE:
478 Downmix_foldFromQuad(pSrc, pDst, numFrames, accumulate);
479 break;
480 case CHANNEL_MASK_5POINT1_BACK:
481 case CHANNEL_MASK_5POINT1_SIDE:
482 Downmix_foldFrom5Point1(pSrc, pDst, numFrames, accumulate);
483 break;
484 case CHANNEL_MASK_7POINT1:
485 Downmix_foldFrom7Point1(pSrc, pDst, numFrames, accumulate);
486 break;
487 default:
488 if (!Downmix_foldGeneric(
489 downmixInputChannelMask, pSrc, pDst, numFrames, accumulate)) {
490 ALOGE("Multichannel configuration 0x%" PRIx32 " is not supported",
491 downmixInputChannelMask);
492 return -EINVAL;
493 }
494 break;
495 }
496 break;
497
498 default:
499 return -EINVAL;
500 }
501
502 return 0;
503}
504#endif
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800505
506static int Downmix_Command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
507 void *pCmdData, uint32_t *replySize, void *pReplyData) {
508
509 downmix_module_t *pDwmModule = (downmix_module_t *) self;
510 downmix_object_t *pDownmixer;
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800511
512 if (pDwmModule == NULL || pDwmModule->context.state == DOWNMIX_STATE_UNINITIALIZED) {
513 return -EINVAL;
514 }
515
516 pDownmixer = (downmix_object_t*) &pDwmModule->context;
517
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700518 ALOGV("Downmix_Command command %" PRIu32 " cmdSize %" PRIu32, cmdCode, cmdSize);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800519
520 switch (cmdCode) {
521 case EFFECT_CMD_INIT:
Eric Laurent0f714a42015-06-19 15:33:57 -0700522 if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800523 return -EINVAL;
524 }
525 *(int *) pReplyData = Downmix_Init(pDwmModule);
526 break;
527
528 case EFFECT_CMD_SET_CONFIG:
529 if (pCmdData == NULL || cmdSize != sizeof(effect_config_t)
Eric Laurent0f714a42015-06-19 15:33:57 -0700530 || pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800531 return -EINVAL;
532 }
533 *(int *) pReplyData = Downmix_Configure(pDwmModule,
534 (effect_config_t *)pCmdData, false);
535 break;
536
537 case EFFECT_CMD_RESET:
538 Downmix_Reset(pDownmixer, false);
539 break;
540
541 case EFFECT_CMD_GET_PARAM:
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700542 ALOGV("Downmix_Command EFFECT_CMD_GET_PARAM pCmdData %p, *replySize %" PRIu32 ", pReplyData: %p",
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800543 pCmdData, *replySize, pReplyData);
544 if (pCmdData == NULL || cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t)) ||
Eric Laurent0f714a42015-06-19 15:33:57 -0700545 pReplyData == NULL || replySize == NULL ||
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800546 *replySize < (int) sizeof(effect_param_t) + 2 * sizeof(int32_t)) {
547 return -EINVAL;
548 }
549 effect_param_t *rep = (effect_param_t *) pReplyData;
550 memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + sizeof(int32_t));
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700551 ALOGV("Downmix_Command EFFECT_CMD_GET_PARAM param %" PRId32 ", replySize %" PRIu32,
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800552 *(int32_t *)rep->data, rep->vsize);
553 rep->status = Downmix_getParameter(pDownmixer, *(int32_t *)rep->data, &rep->vsize,
554 rep->data + sizeof(int32_t));
555 *replySize = sizeof(effect_param_t) + sizeof(int32_t) + rep->vsize;
556 break;
557
558 case EFFECT_CMD_SET_PARAM:
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700559 ALOGV("Downmix_Command EFFECT_CMD_SET_PARAM cmdSize %d pCmdData %p, *replySize %" PRIu32
560 ", pReplyData %p", cmdSize, pCmdData, *replySize, pReplyData);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800561 if (pCmdData == NULL || (cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t)))
Eric Laurent0f714a42015-06-19 15:33:57 -0700562 || pReplyData == NULL || replySize == NULL || *replySize != (int)sizeof(int32_t)) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800563 return -EINVAL;
564 }
565 effect_param_t *cmd = (effect_param_t *) pCmdData;
566 *(int *)pReplyData = Downmix_setParameter(pDownmixer, *(int32_t *)cmd->data,
567 cmd->vsize, cmd->data + sizeof(int32_t));
568 break;
569
570 case EFFECT_CMD_SET_PARAM_DEFERRED:
571 //FIXME implement
572 ALOGW("Downmix_Command command EFFECT_CMD_SET_PARAM_DEFERRED not supported, FIXME");
573 break;
574
575 case EFFECT_CMD_SET_PARAM_COMMIT:
576 //FIXME implement
577 ALOGW("Downmix_Command command EFFECT_CMD_SET_PARAM_COMMIT not supported, FIXME");
578 break;
579
580 case EFFECT_CMD_ENABLE:
Eric Laurent0f714a42015-06-19 15:33:57 -0700581 if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800582 return -EINVAL;
583 }
584 if (pDownmixer->state != DOWNMIX_STATE_INITIALIZED) {
585 return -ENOSYS;
586 }
587 pDownmixer->state = DOWNMIX_STATE_ACTIVE;
588 ALOGV("EFFECT_CMD_ENABLE() OK");
589 *(int *)pReplyData = 0;
590 break;
591
592 case EFFECT_CMD_DISABLE:
Eric Laurent0f714a42015-06-19 15:33:57 -0700593 if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800594 return -EINVAL;
595 }
596 if (pDownmixer->state != DOWNMIX_STATE_ACTIVE) {
597 return -ENOSYS;
598 }
599 pDownmixer->state = DOWNMIX_STATE_INITIALIZED;
600 ALOGV("EFFECT_CMD_DISABLE() OK");
601 *(int *)pReplyData = 0;
602 break;
603
604 case EFFECT_CMD_SET_DEVICE:
605 if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) {
606 return -EINVAL;
607 }
608 // FIXME change type if playing on headset vs speaker
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700609 ALOGV("Downmix_Command EFFECT_CMD_SET_DEVICE: 0x%08" PRIx32, *(uint32_t *)pCmdData);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800610 break;
611
612 case EFFECT_CMD_SET_VOLUME: {
613 // audio output is always stereo => 2 channel volumes
614 if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t) * 2) {
615 return -EINVAL;
616 }
617 // FIXME change volume
618 ALOGW("Downmix_Command command EFFECT_CMD_SET_VOLUME not supported, FIXME");
619 float left = (float)(*(uint32_t *)pCmdData) / (1 << 24);
620 float right = (float)(*((uint32_t *)pCmdData + 1)) / (1 << 24);
621 ALOGV("Downmix_Command EFFECT_CMD_SET_VOLUME: left %f, right %f ", left, right);
622 break;
623 }
624
625 case EFFECT_CMD_SET_AUDIO_MODE:
626 if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) {
627 return -EINVAL;
628 }
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700629 ALOGV("Downmix_Command EFFECT_CMD_SET_AUDIO_MODE: %" PRIu32, *(uint32_t *)pCmdData);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800630 break;
631
632 case EFFECT_CMD_SET_CONFIG_REVERSE:
633 case EFFECT_CMD_SET_INPUT_DEVICE:
634 // these commands are ignored by a downmix effect
635 break;
636
637 default:
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700638 ALOGW("Downmix_Command invalid command %" PRIu32, cmdCode);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800639 return -EINVAL;
640 }
641
642 return 0;
643}
644
645
646int Downmix_GetDescriptor(effect_handle_t self, effect_descriptor_t *pDescriptor)
647{
648 downmix_module_t *pDwnmxModule = (downmix_module_t *) self;
649
650 if (pDwnmxModule == NULL ||
651 pDwnmxModule->context.state == DOWNMIX_STATE_UNINITIALIZED) {
652 return -EINVAL;
653 }
654
655 memcpy(pDescriptor, &gDownmixDescriptor, sizeof(effect_descriptor_t));
656
657 return 0;
658}
659
660
661/*----------------------------------------------------------------------------
662 * Downmix internal functions
663 *--------------------------------------------------------------------------*/
664
665/*----------------------------------------------------------------------------
666 * Downmix_Init()
667 *----------------------------------------------------------------------------
668 * Purpose:
669 * Initialize downmix context and apply default parameters
670 *
671 * Inputs:
672 * pDwmModule pointer to downmix effect module
673 *
674 * Outputs:
675 *
676 * Returns:
677 * 0 indicates success
678 *
679 * Side Effects:
680 * updates:
681 * pDwmModule->context.type
682 * pDwmModule->context.apply_volume_correction
683 * pDwmModule->config.inputCfg
684 * pDwmModule->config.outputCfg
685 * pDwmModule->config.inputCfg.samplingRate
686 * pDwmModule->config.outputCfg.samplingRate
687 * pDwmModule->context.state
688 * doesn't set:
689 * pDwmModule->itfe
690 *
691 *----------------------------------------------------------------------------
692 */
693
694int Downmix_Init(downmix_module_t *pDwmModule) {
695
696 ALOGV("Downmix_Init module %p", pDwmModule);
697 int ret = 0;
698
699 memset(&pDwmModule->context, 0, sizeof(downmix_object_t));
700
701 pDwmModule->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
702 pDwmModule->config.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
703 pDwmModule->config.inputCfg.channels = AUDIO_CHANNEL_OUT_7POINT1;
704 pDwmModule->config.inputCfg.bufferProvider.getBuffer = NULL;
705 pDwmModule->config.inputCfg.bufferProvider.releaseBuffer = NULL;
706 pDwmModule->config.inputCfg.bufferProvider.cookie = NULL;
707 pDwmModule->config.inputCfg.mask = EFFECT_CONFIG_ALL;
708
709 pDwmModule->config.inputCfg.samplingRate = 44100;
710 pDwmModule->config.outputCfg.samplingRate = pDwmModule->config.inputCfg.samplingRate;
711
712 // set a default value for the access mode, but should be overwritten by caller
713 pDwmModule->config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
714 pDwmModule->config.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
715 pDwmModule->config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
716 pDwmModule->config.outputCfg.bufferProvider.getBuffer = NULL;
717 pDwmModule->config.outputCfg.bufferProvider.releaseBuffer = NULL;
718 pDwmModule->config.outputCfg.bufferProvider.cookie = NULL;
719 pDwmModule->config.outputCfg.mask = EFFECT_CONFIG_ALL;
720
721 ret = Downmix_Configure(pDwmModule, &pDwmModule->config, true);
722 if (ret != 0) {
723 ALOGV("Downmix_Init error %d on module %p", ret, pDwmModule);
724 } else {
725 pDwmModule->context.state = DOWNMIX_STATE_INITIALIZED;
726 }
727
728 return ret;
729}
730
731
732/*----------------------------------------------------------------------------
733 * Downmix_Configure()
734 *----------------------------------------------------------------------------
735 * Purpose:
736 * Set input and output audio configuration.
737 *
738 * Inputs:
739 * pDwmModule pointer to downmix effect module
740 * pConfig pointer to effect_config_t structure containing input
741 * and output audio parameters configuration
742 * init true if called from init function
743 *
744 * Outputs:
745 *
746 * Returns:
747 * 0 indicates success
748 *
749 * Side Effects:
750 *
751 *----------------------------------------------------------------------------
752 */
753
754int Downmix_Configure(downmix_module_t *pDwmModule, effect_config_t *pConfig, bool init) {
755
756 downmix_object_t *pDownmixer = &pDwmModule->context;
757
758 // Check configuration compatibility with build options, and effect capabilities
759 if (pConfig->inputCfg.samplingRate != pConfig->outputCfg.samplingRate
760 || pConfig->outputCfg.channels != DOWNMIX_OUTPUT_CHANNELS
761 || pConfig->inputCfg.format != AUDIO_FORMAT_PCM_16_BIT
762 || pConfig->outputCfg.format != AUDIO_FORMAT_PCM_16_BIT) {
763 ALOGE("Downmix_Configure error: invalid config");
764 return -EINVAL;
765 }
766
Chih-Wei Huanga93c8c92013-01-10 20:52:57 +0800767 if (&pDwmModule->config != pConfig) {
768 memcpy(&pDwmModule->config, pConfig, sizeof(effect_config_t));
769 }
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800770
771 if (init) {
772 pDownmixer->type = DOWNMIX_TYPE_FOLD;
773 pDownmixer->apply_volume_correction = false;
774 pDownmixer->input_channel_count = 8; // matches default input of AUDIO_CHANNEL_OUT_7POINT1
775 } else {
Haynes Mathew Georgeee507ef2015-07-15 11:06:05 -0700776 // when configuring the effect, do not allow a blank or unsupported channel mask
777 if (!Downmix_validChannelMask(pConfig->inputCfg.channels)) {
778 ALOGE("Downmix_Configure error: input channel mask(0x%x) not supported",
779 pConfig->inputCfg.channels);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800780 return -EINVAL;
781 }
Andy Hunge5412692014-05-16 11:25:07 -0700782 pDownmixer->input_channel_count =
783 audio_channel_count_from_out_mask(pConfig->inputCfg.channels);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800784 }
785
786 Downmix_Reset(pDownmixer, init);
787
788 return 0;
789}
790
791
792/*----------------------------------------------------------------------------
793 * Downmix_Reset()
794 *----------------------------------------------------------------------------
795 * Purpose:
796 * Reset internal states.
797 *
798 * Inputs:
799 * pDownmixer pointer to downmix context
800 * init true if called from init function
801 *
802 * Outputs:
803*
804 * Returns:
805 * 0 indicates success
806 *
807 * Side Effects:
808 *
809 *----------------------------------------------------------------------------
810 */
811
Eric Laurent0f714a42015-06-19 15:33:57 -0700812int Downmix_Reset(downmix_object_t *pDownmixer __unused, bool init __unused) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800813 // nothing to do here
814 return 0;
815}
816
817
818/*----------------------------------------------------------------------------
819 * Downmix_setParameter()
820 *----------------------------------------------------------------------------
821 * Purpose:
822 * Set a Downmix parameter
823 *
824 * Inputs:
825 * pDownmixer handle to instance data
826 * param parameter
827 * pValue pointer to parameter value
828 * size value size
829 *
830 * Outputs:
831 *
832 * Returns:
833 * 0 indicates success
834 *
835 * Side Effects:
836 *
837 *----------------------------------------------------------------------------
838 */
Ashok Bhatb302bd52014-02-18 11:40:00 +0000839int Downmix_setParameter(downmix_object_t *pDownmixer, int32_t param, uint32_t size, void *pValue) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800840
841 int16_t value16;
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700842 ALOGV("Downmix_setParameter, context %p, param %" PRId32 ", value16 %" PRId16 ", value32 %" PRId32,
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800843 pDownmixer, param, *(int16_t *)pValue, *(int32_t *)pValue);
844
845 switch (param) {
846
847 case DOWNMIX_PARAM_TYPE:
848 if (size != sizeof(downmix_type_t)) {
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700849 ALOGE("Downmix_setParameter(DOWNMIX_PARAM_TYPE) invalid size %" PRIu32 ", should be %zu",
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800850 size, sizeof(downmix_type_t));
851 return -EINVAL;
852 }
853 value16 = *(int16_t *)pValue;
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700854 ALOGV("set DOWNMIX_PARAM_TYPE, type %" PRId16, value16);
Jean-Michel Trivi7d5b2622012-04-04 18:54:36 -0700855 if (!((value16 > DOWNMIX_TYPE_INVALID) && (value16 <= DOWNMIX_TYPE_LAST))) {
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700856 ALOGE("Downmix_setParameter invalid DOWNMIX_PARAM_TYPE value %" PRId16, value16);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800857 return -EINVAL;
858 } else {
859 pDownmixer->type = (downmix_type_t) value16;
860 break;
861
862 default:
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700863 ALOGE("Downmix_setParameter unknown parameter %" PRId32, param);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800864 return -EINVAL;
865 }
866}
867
868 return 0;
869} /* end Downmix_setParameter */
870
871
872/*----------------------------------------------------------------------------
873 * Downmix_getParameter()
874 *----------------------------------------------------------------------------
875 * Purpose:
876 * Get a Downmix parameter
877 *
878 * Inputs:
879 * pDownmixer handle to instance data
880 * param parameter
881 * pValue pointer to variable to hold retrieved value
882 * pSize pointer to value size: maximum size as input
883 *
884 * Outputs:
885 * *pValue updated with parameter value
886 * *pSize updated with actual value size
887 *
888 * Returns:
889 * 0 indicates success
890 *
891 * Side Effects:
892 *
893 *----------------------------------------------------------------------------
894 */
Ashok Bhatb302bd52014-02-18 11:40:00 +0000895int Downmix_getParameter(downmix_object_t *pDownmixer, int32_t param, uint32_t *pSize, void *pValue) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800896 int16_t *pValue16;
897
898 switch (param) {
899
900 case DOWNMIX_PARAM_TYPE:
901 if (*pSize < sizeof(int16_t)) {
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700902 ALOGE("Downmix_getParameter invalid parameter size %" PRIu32 " for DOWNMIX_PARAM_TYPE", *pSize);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800903 return -EINVAL;
904 }
905 pValue16 = (int16_t *)pValue;
906 *pValue16 = (int16_t) pDownmixer->type;
907 *pSize = sizeof(int16_t);
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700908 ALOGV("Downmix_getParameter DOWNMIX_PARAM_TYPE is %" PRId16, *pValue16);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800909 break;
910
911 default:
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700912 ALOGE("Downmix_getParameter unknown parameter %" PRId16, param);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800913 return -EINVAL;
914 }
915
916 return 0;
917} /* end Downmix_getParameter */
918
919
920/*----------------------------------------------------------------------------
921 * Downmix_foldFromQuad()
922 *----------------------------------------------------------------------------
923 * Purpose:
924 * downmix a quad signal to stereo
925 *
926 * Inputs:
927 * pSrc quad audio samples to downmix
928 * numFrames the number of quad frames to downmix
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700929 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
930 * or overwrite pDst (when false)
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800931 *
932 * Outputs:
933 * pDst downmixed stereo audio samples
934 *
935 *----------------------------------------------------------------------------
936 */
Ramesh Katurid7d01342016-05-02 15:03:47 +0530937#ifndef BUILD_FLOAT
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800938void Downmix_foldFromQuad(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
939 // sample at index 0 is FL
940 // sample at index 1 is FR
941 // sample at index 2 is RL
942 // sample at index 3 is RR
943 if (accumulate) {
944 while (numFrames) {
945 // FL + RL
Jean-Michel Triviaea27152012-05-24 10:11:07 -0700946 pDst[0] = clamp16(pDst[0] + ((pSrc[0] + pSrc[2]) >> 1));
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800947 // FR + RR
Jean-Michel Triviaea27152012-05-24 10:11:07 -0700948 pDst[1] = clamp16(pDst[1] + ((pSrc[1] + pSrc[3]) >> 1));
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800949 pSrc += 4;
950 pDst += 2;
951 numFrames--;
952 }
953 } else { // same code as above but without adding and clamping pDst[i] to itself
954 while (numFrames) {
955 // FL + RL
Jean-Michel Triviaea27152012-05-24 10:11:07 -0700956 pDst[0] = clamp16((pSrc[0] + pSrc[2]) >> 1);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800957 // FR + RR
Jean-Michel Triviaea27152012-05-24 10:11:07 -0700958 pDst[1] = clamp16((pSrc[1] + pSrc[3]) >> 1);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800959 pSrc += 4;
960 pDst += 2;
961 numFrames--;
962 }
963 }
964}
Ramesh Katurid7d01342016-05-02 15:03:47 +0530965#else
966void Downmix_foldFromQuad(LVM_FLOAT *pSrc, LVM_FLOAT *pDst, size_t numFrames, bool accumulate) {
967 // sample at index 0 is FL
968 // sample at index 1 is FR
969 // sample at index 2 is RL
970 // sample at index 3 is RR
971 if (accumulate) {
972 while (numFrames) {
973 // FL + RL
974 pDst[0] = clamp_float(pDst[0] + ((pSrc[0] + pSrc[2]) / 2.0f));
975 // FR + RR
976 pDst[1] = clamp_float(pDst[1] + ((pSrc[1] + pSrc[3]) / 2.0f));
977 pSrc += 4;
978 pDst += 2;
979 numFrames--;
980 }
981 } else { // same code as above but without adding and clamping pDst[i] to itself
982 while (numFrames) {
983 // FL + RL
984 pDst[0] = clamp_float((pSrc[0] + pSrc[2]) / 2.0f);
985 // FR + RR
986 pDst[1] = clamp_float((pSrc[1] + pSrc[3]) / 2.0f);
987 pSrc += 4;
988 pDst += 2;
989 numFrames--;
990 }
991 }
992}
993#endif
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800994
995/*----------------------------------------------------------------------------
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800996 * Downmix_foldFrom5Point1()
997 *----------------------------------------------------------------------------
998 * Purpose:
999 * downmix a 5.1 signal to stereo
1000 *
1001 * Inputs:
1002 * pSrc 5.1 audio samples to downmix
1003 * numFrames the number of 5.1 frames to downmix
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -07001004 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
1005 * or overwrite pDst (when false)
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -08001006 *
1007 * Outputs:
1008 * pDst downmixed stereo audio samples
1009 *
1010 *----------------------------------------------------------------------------
1011 */
Ramesh Katurid7d01342016-05-02 15:03:47 +05301012#ifndef BUILD_FLOAT
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -08001013void Downmix_foldFrom5Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
1014 int32_t lt, rt, centerPlusLfeContrib; // samples in Q19.12 format
1015 // sample at index 0 is FL
1016 // sample at index 1 is FR
1017 // sample at index 2 is FC
1018 // sample at index 3 is LFE
1019 // sample at index 4 is RL
1020 // sample at index 5 is RR
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -07001021 // code is mostly duplicated between the two values of accumulate to avoid repeating the test
1022 // for every sample
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -08001023 if (accumulate) {
1024 while (numFrames) {
1025 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
1026 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
1027 + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
1028 // FL + centerPlusLfeContrib + RL
1029 lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[4] << 12);
1030 // FR + centerPlusLfeContrib + RR
1031 rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[5] << 12);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -07001032 // accumulate in destination
Jean-Michel Triviaea27152012-05-24 10:11:07 -07001033 pDst[0] = clamp16(pDst[0] + (lt >> 13));
1034 pDst[1] = clamp16(pDst[1] + (rt >> 13));
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -08001035 pSrc += 6;
1036 pDst += 2;
1037 numFrames--;
1038 }
1039 } else { // same code as above but without adding and clamping pDst[i] to itself
1040 while (numFrames) {
1041 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
1042 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
1043 + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
1044 // FL + centerPlusLfeContrib + RL
1045 lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[4] << 12);
1046 // FR + centerPlusLfeContrib + RR
1047 rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[5] << 12);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -07001048 // store in destination
Jean-Michel Triviaea27152012-05-24 10:11:07 -07001049 pDst[0] = clamp16(lt >> 13); // differs from when accumulate is true above
1050 pDst[1] = clamp16(rt >> 13); // differs from when accumulate is true above
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -08001051 pSrc += 6;
1052 pDst += 2;
1053 numFrames--;
1054 }
1055 }
1056}
Ramesh Katurid7d01342016-05-02 15:03:47 +05301057#else
1058void Downmix_foldFrom5Point1(LVM_FLOAT *pSrc, LVM_FLOAT *pDst, size_t numFrames, bool accumulate) {
1059 LVM_FLOAT lt, rt, centerPlusLfeContrib; // samples in Q19.12 format
1060 // sample at index 0 is FL
1061 // sample at index 1 is FR
1062 // sample at index 2 is FC
1063 // sample at index 3 is LFE
1064 // sample at index 4 is RL
1065 // sample at index 5 is RR
1066 // code is mostly duplicated between the two values of accumulate to avoid repeating the test
1067 // for every sample
1068 if (accumulate) {
1069 while (numFrames) {
1070 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
1071 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_FLOAT)
1072 + (pSrc[3] * MINUS_3_DB_IN_FLOAT);
1073 // FL + centerPlusLfeContrib + RL
1074 lt = pSrc[0] + centerPlusLfeContrib + pSrc[4];
1075 // FR + centerPlusLfeContrib + RR
1076 rt = pSrc[1] + centerPlusLfeContrib + pSrc[5];
1077 // accumulate in destination
1078 pDst[0] = clamp_float(pDst[0] + (lt / 2.0f));
1079 pDst[1] = clamp_float(pDst[1] + (rt / 2.0f));
1080 pSrc += 6;
1081 pDst += 2;
1082 numFrames--;
1083 }
1084 } else { // same code as above but without adding and clamping pDst[i] to itself
1085 while (numFrames) {
1086 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
1087 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_FLOAT)
1088 + (pSrc[3] * MINUS_3_DB_IN_FLOAT);
1089 // FL + centerPlusLfeContrib + RL
1090 lt = pSrc[0] + centerPlusLfeContrib + pSrc[4];
1091 // FR + centerPlusLfeContrib + RR
1092 rt = pSrc[1] + centerPlusLfeContrib + pSrc[5];
1093 // store in destination
1094 pDst[0] = clamp_float(lt / 2.0f); // differs from when accumulate is true above
1095 pDst[1] = clamp_float(rt / 2.0f); // differs from when accumulate is true above
1096 pSrc += 6;
1097 pDst += 2;
1098 numFrames--;
1099 }
1100 }
1101}
1102#endif
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -08001103
1104/*----------------------------------------------------------------------------
1105 * Downmix_foldFrom7Point1()
1106 *----------------------------------------------------------------------------
1107 * Purpose:
1108 * downmix a 7.1 signal to stereo
1109 *
1110 * Inputs:
1111 * pSrc 7.1 audio samples to downmix
1112 * numFrames the number of 7.1 frames to downmix
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -07001113 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
1114 * or overwrite pDst (when false)
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -08001115 *
1116 * Outputs:
1117 * pDst downmixed stereo audio samples
1118 *
1119 *----------------------------------------------------------------------------
1120 */
Ramesh Katurid7d01342016-05-02 15:03:47 +05301121#ifndef BUILD_FLOAT
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -08001122void Downmix_foldFrom7Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
1123 int32_t lt, rt, centerPlusLfeContrib; // samples in Q19.12 format
1124 // sample at index 0 is FL
1125 // sample at index 1 is FR
1126 // sample at index 2 is FC
1127 // sample at index 3 is LFE
1128 // sample at index 4 is RL
1129 // sample at index 5 is RR
1130 // sample at index 6 is SL
1131 // sample at index 7 is SR
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -07001132 // code is mostly duplicated between the two values of accumulate to avoid repeating the test
1133 // for every sample
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -08001134 if (accumulate) {
1135 while (numFrames) {
1136 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
1137 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
1138 + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
1139 // FL + centerPlusLfeContrib + SL + RL
1140 lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[6] << 12) + (pSrc[4] << 12);
1141 // FR + centerPlusLfeContrib + SR + RR
1142 rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[7] << 12) + (pSrc[5] << 12);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -07001143 //accumulate in destination
Jean-Michel Triviaea27152012-05-24 10:11:07 -07001144 pDst[0] = clamp16(pDst[0] + (lt >> 13));
1145 pDst[1] = clamp16(pDst[1] + (rt >> 13));
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -08001146 pSrc += 8;
1147 pDst += 2;
1148 numFrames--;
1149 }
1150 } else { // same code as above but without adding and clamping pDst[i] to itself
1151 while (numFrames) {
1152 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
1153 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
1154 + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
1155 // FL + centerPlusLfeContrib + SL + RL
1156 lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[6] << 12) + (pSrc[4] << 12);
1157 // FR + centerPlusLfeContrib + SR + RR
1158 rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[7] << 12) + (pSrc[5] << 12);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -07001159 // store in destination
Jean-Michel Triviaea27152012-05-24 10:11:07 -07001160 pDst[0] = clamp16(lt >> 13); // differs from when accumulate is true above
1161 pDst[1] = clamp16(rt >> 13); // differs from when accumulate is true above
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -08001162 pSrc += 8;
1163 pDst += 2;
1164 numFrames--;
1165 }
1166 }
1167}
Ramesh Katurid7d01342016-05-02 15:03:47 +05301168#else
1169void Downmix_foldFrom7Point1(LVM_FLOAT *pSrc, LVM_FLOAT *pDst, size_t numFrames, bool accumulate) {
1170 LVM_FLOAT lt, rt, centerPlusLfeContrib; // samples in Q19.12 format
1171 // sample at index 0 is FL
1172 // sample at index 1 is FR
1173 // sample at index 2 is FC
1174 // sample at index 3 is LFE
1175 // sample at index 4 is RL
1176 // sample at index 5 is RR
1177 // sample at index 6 is SL
1178 // sample at index 7 is SR
1179 // code is mostly duplicated between the two values of accumulate to avoid repeating the test
1180 // for every sample
1181 if (accumulate) {
1182 while (numFrames) {
1183 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
1184 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
1185 + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
1186 // FL + centerPlusLfeContrib + SL + RL
1187 lt = pSrc[0] + centerPlusLfeContrib + pSrc[6] + pSrc[4];
1188 // FR + centerPlusLfeContrib + SR + RR
1189 rt = pSrc[1] + centerPlusLfeContrib + pSrc[7] + pSrc[5];
1190 //accumulate in destination
1191 pDst[0] = clamp_float(pDst[0] + (lt / 2.0f));
1192 pDst[1] = clamp_float(pDst[1] + (rt / 2.0f));
1193 pSrc += 8;
1194 pDst += 2;
1195 numFrames--;
1196 }
1197 } else { // same code as above but without adding and clamping pDst[i] to itself
1198 while (numFrames) {
1199 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
1200 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_FLOAT)
1201 + (pSrc[3] * MINUS_3_DB_IN_FLOAT);
1202 // FL + centerPlusLfeContrib + SL + RL
1203 lt = pSrc[0] + centerPlusLfeContrib + pSrc[6] + pSrc[4];
1204 // FR + centerPlusLfeContrib + SR + RR
1205 rt = pSrc[1] + centerPlusLfeContrib + pSrc[7] + pSrc[5];
1206 // store in destination
1207 pDst[0] = clamp_float(lt / 2.0f); // differs from when accumulate is true above
1208 pDst[1] = clamp_float(rt / 2.0f); // differs from when accumulate is true above
1209 pSrc += 8;
1210 pDst += 2;
1211 numFrames--;
1212 }
1213 }
1214}
1215#endif
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -07001216/*----------------------------------------------------------------------------
1217 * Downmix_foldGeneric()
1218 *----------------------------------------------------------------------------
1219 * Purpose:
1220 * downmix to stereo a multichannel signal whose format is:
1221 * - has FL/FR
1222 * - if using AUDIO_CHANNEL_OUT_SIDE*, it contains both left and right
1223 * - if using AUDIO_CHANNEL_OUT_BACK*, it contains both left and right
1224 * - doesn't use any of the AUDIO_CHANNEL_OUT_TOP* channels
1225 * - doesn't use any of the AUDIO_CHANNEL_OUT_FRONT_*_OF_CENTER channels
1226 * Only handles channel masks not enumerated in downmix_input_channel_mask_t
1227 *
1228 * Inputs:
1229 * mask the channel mask of pSrc
1230 * pSrc multichannel audio buffer to downmix
1231 * numFrames the number of multichannel frames to downmix
1232 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
1233 * or overwrite pDst (when false)
1234 *
1235 * Outputs:
1236 * pDst downmixed stereo audio samples
1237 *
1238 * Returns: false if multichannel format is not supported
1239 *
1240 *----------------------------------------------------------------------------
1241 */
Ramesh Katurid7d01342016-05-02 15:03:47 +05301242#ifndef BUILD_FLOAT
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -07001243bool Downmix_foldGeneric(
1244 uint32_t mask, int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
Haynes Mathew Georgeee507ef2015-07-15 11:06:05 -07001245
1246 if (!Downmix_validChannelMask(mask)) {
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -07001247 return false;
1248 }
Haynes Mathew Georgeee507ef2015-07-15 11:06:05 -07001249
1250 const bool hasSides = (mask & kSides) != 0;
1251 const bool hasBacks = (mask & kBacks) != 0;
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -07001252
Andy Hunge5412692014-05-16 11:25:07 -07001253 const int numChan = audio_channel_count_from_out_mask(mask);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -07001254 const bool hasFC = ((mask & AUDIO_CHANNEL_OUT_FRONT_CENTER) == AUDIO_CHANNEL_OUT_FRONT_CENTER);
1255 const bool hasLFE =
1256 ((mask & AUDIO_CHANNEL_OUT_LOW_FREQUENCY) == AUDIO_CHANNEL_OUT_LOW_FREQUENCY);
1257 const bool hasBC = ((mask & AUDIO_CHANNEL_OUT_BACK_CENTER) == AUDIO_CHANNEL_OUT_BACK_CENTER);
1258 // compute at what index each channel is: samples will be in the following order:
1259 // FL FR FC LFE BL BR BC SL SR
1260 // when a channel is not present, its index is set to the same as the index of the preceding
1261 // channel
1262 const int indexFC = hasFC ? 2 : 1; // front center
1263 const int indexLFE = hasLFE ? indexFC + 1 : indexFC; // low frequency
1264 const int indexBL = hasBacks ? indexLFE + 1 : indexLFE; // back left
1265 const int indexBR = hasBacks ? indexBL + 1 : indexBL; // back right
1266 const int indexBC = hasBC ? indexBR + 1 : indexBR; // back center
1267 const int indexSL = hasSides ? indexBC + 1 : indexBC; // side left
1268 const int indexSR = hasSides ? indexSL + 1 : indexSL; // side right
1269
1270 int32_t lt, rt, centersLfeContrib; // samples in Q19.12 format
1271 // code is mostly duplicated between the two values of accumulate to avoid repeating the test
1272 // for every sample
1273 if (accumulate) {
1274 while (numFrames) {
1275 // compute contribution of FC, BC and LFE
1276 centersLfeContrib = 0;
1277 if (hasFC) { centersLfeContrib += pSrc[indexFC]; }
1278 if (hasLFE) { centersLfeContrib += pSrc[indexLFE]; }
1279 if (hasBC) { centersLfeContrib += pSrc[indexBC]; }
1280 centersLfeContrib *= MINUS_3_DB_IN_Q19_12;
1281 // always has FL/FR
1282 lt = (pSrc[0] << 12);
1283 rt = (pSrc[1] << 12);
1284 // mix in sides and backs
1285 if (hasSides) {
1286 lt += pSrc[indexSL] << 12;
1287 rt += pSrc[indexSR] << 12;
1288 }
1289 if (hasBacks) {
1290 lt += pSrc[indexBL] << 12;
1291 rt += pSrc[indexBR] << 12;
1292 }
1293 lt += centersLfeContrib;
1294 rt += centersLfeContrib;
1295 // accumulate in destination
Jean-Michel Triviaea27152012-05-24 10:11:07 -07001296 pDst[0] = clamp16(pDst[0] + (lt >> 13));
1297 pDst[1] = clamp16(pDst[1] + (rt >> 13));
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -07001298 pSrc += numChan;
1299 pDst += 2;
1300 numFrames--;
1301 }
1302 } else {
1303 while (numFrames) {
1304 // compute contribution of FC, BC and LFE
1305 centersLfeContrib = 0;
1306 if (hasFC) { centersLfeContrib += pSrc[indexFC]; }
1307 if (hasLFE) { centersLfeContrib += pSrc[indexLFE]; }
1308 if (hasBC) { centersLfeContrib += pSrc[indexBC]; }
1309 centersLfeContrib *= MINUS_3_DB_IN_Q19_12;
1310 // always has FL/FR
1311 lt = (pSrc[0] << 12);
1312 rt = (pSrc[1] << 12);
1313 // mix in sides and backs
1314 if (hasSides) {
1315 lt += pSrc[indexSL] << 12;
1316 rt += pSrc[indexSR] << 12;
1317 }
1318 if (hasBacks) {
1319 lt += pSrc[indexBL] << 12;
1320 rt += pSrc[indexBR] << 12;
1321 }
1322 lt += centersLfeContrib;
1323 rt += centersLfeContrib;
1324 // store in destination
Jean-Michel Triviaea27152012-05-24 10:11:07 -07001325 pDst[0] = clamp16(lt >> 13); // differs from when accumulate is true above
1326 pDst[1] = clamp16(rt >> 13); // differs from when accumulate is true above
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -07001327 pSrc += numChan;
1328 pDst += 2;
1329 numFrames--;
1330 }
1331 }
1332 return true;
1333}
Ramesh Katurid7d01342016-05-02 15:03:47 +05301334#else
1335bool Downmix_foldGeneric(
1336 uint32_t mask, LVM_FLOAT *pSrc, LVM_FLOAT *pDst, size_t numFrames, bool accumulate) {
1337
1338 if (!Downmix_validChannelMask(mask)) {
1339 return false;
1340 }
1341
1342 const bool hasSides = (mask & kSides) != 0;
1343 const bool hasBacks = (mask & kBacks) != 0;
1344
1345 const int numChan = audio_channel_count_from_out_mask(mask);
1346 const bool hasFC = ((mask & AUDIO_CHANNEL_OUT_FRONT_CENTER) == AUDIO_CHANNEL_OUT_FRONT_CENTER);
1347 const bool hasLFE =
1348 ((mask & AUDIO_CHANNEL_OUT_LOW_FREQUENCY) == AUDIO_CHANNEL_OUT_LOW_FREQUENCY);
1349 const bool hasBC = ((mask & AUDIO_CHANNEL_OUT_BACK_CENTER) == AUDIO_CHANNEL_OUT_BACK_CENTER);
1350 // compute at what index each channel is: samples will be in the following order:
1351 // FL FR FC LFE BL BR BC SL SR
1352 // when a channel is not present, its index is set to the same as the index of the preceding
1353 // channel
1354 const int indexFC = hasFC ? 2 : 1; // front center
1355 const int indexLFE = hasLFE ? indexFC + 1 : indexFC; // low frequency
1356 const int indexBL = hasBacks ? indexLFE + 1 : indexLFE; // back left
1357 const int indexBR = hasBacks ? indexBL + 1 : indexBL; // back right
1358 const int indexBC = hasBC ? indexBR + 1 : indexBR; // back center
1359 const int indexSL = hasSides ? indexBC + 1 : indexBC; // side left
1360 const int indexSR = hasSides ? indexSL + 1 : indexSL; // side right
1361
1362 LVM_FLOAT lt, rt, centersLfeContrib;
1363 // code is mostly duplicated between the two values of accumulate to avoid repeating the test
1364 // for every sample
1365 if (accumulate) {
1366 while (numFrames) {
1367 // compute contribution of FC, BC and LFE
1368 centersLfeContrib = 0;
1369 if (hasFC) { centersLfeContrib += pSrc[indexFC]; }
1370 if (hasLFE) { centersLfeContrib += pSrc[indexLFE]; }
1371 if (hasBC) { centersLfeContrib += pSrc[indexBC]; }
1372 centersLfeContrib *= MINUS_3_DB_IN_FLOAT;
1373 // always has FL/FR
1374 lt = pSrc[0];
1375 rt = pSrc[1];
1376 // mix in sides and backs
1377 if (hasSides) {
1378 lt += pSrc[indexSL];
1379 rt += pSrc[indexSR];
1380 }
1381 if (hasBacks) {
1382 lt += pSrc[indexBL];
1383 rt += pSrc[indexBR];
1384 }
1385 lt += centersLfeContrib;
1386 rt += centersLfeContrib;
1387 // accumulate in destination
1388 pDst[0] = clamp_float(pDst[0] + (lt / 2.0f));
1389 pDst[1] = clamp_float(pDst[1] + (rt / 2.0f));
1390 pSrc += numChan;
1391 pDst += 2;
1392 numFrames--;
1393 }
1394 } else {
1395 while (numFrames) {
1396 // compute contribution of FC, BC and LFE
1397 centersLfeContrib = 0;
1398 if (hasFC) { centersLfeContrib += pSrc[indexFC]; }
1399 if (hasLFE) { centersLfeContrib += pSrc[indexLFE]; }
1400 if (hasBC) { centersLfeContrib += pSrc[indexBC]; }
1401 centersLfeContrib *= MINUS_3_DB_IN_FLOAT;
1402 // always has FL/FR
1403 lt = pSrc[0];
1404 rt = pSrc[1];
1405 // mix in sides and backs
1406 if (hasSides) {
1407 lt += pSrc[indexSL];
1408 rt += pSrc[indexSR];
1409 }
1410 if (hasBacks) {
1411 lt += pSrc[indexBL];
1412 rt += pSrc[indexBR];
1413 }
1414 lt += centersLfeContrib;
1415 rt += centersLfeContrib;
1416 // store in destination
1417 pDst[0] = clamp_float(lt / 2.0f); // differs from when accumulate is true above
1418 pDst[1] = clamp_float(rt / 2.0f); // differs from when accumulate is true above
1419 pSrc += numChan;
1420 pDst += 2;
1421 numFrames--;
1422 }
1423 }
1424 return true;
1425}
1426#endif