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