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 #define MINUS_3_DB_IN_Q19_12 2896 // -3dB = 0.707 * 2^12 = 2896
35
36 #ifdef BUILD_FLOAT
37 #define MINUS_3_DB_IN_FLOAT 0.70710678f // -3dB = 0.70710678f
38 #endif
39
40 // subset of possible audio_channel_mask_t values, and AUDIO_CHANNEL_OUT_* renamed to CHANNEL_MASK_*
41 typedef enum {
42 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,
47 } downmix_input_channel_mask_t;
48
49 // effect_handle_t interface implementation for downmix effect
50 const 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
57 // This is the only symbol that needs to be exported
58 __attribute__ ((visibility ("default")))
59 audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
60 .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,
67 };
68
69
70 // AOSP insert downmix UUID: 93f04452-e4fe-41cc-91f9-e475b6d1d69f
71 static 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
83 static const effect_descriptor_t * const gDescriptors[] = {
84 &gDownmixDescriptor
85 };
86
87 // number of effects in this library
88 const int kNbEffects = sizeof(gDescriptors) / sizeof(const effect_descriptor_t *);
89 #ifdef BUILD_FLOAT
clamp_float(LVM_FLOAT a)90 static 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
102 /*----------------------------------------------------------------------------
103 * 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()
Downmix_testIndexComputation(uint32_t mask)108 void Downmix_testIndexComputation(uint32_t mask) {
109 ALOGI("Testing index computation for 0x%" PRIx32 ":", mask);
110 // 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
139 const int numChan = audio_channel_count_from_out_mask(mask);
140 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
Downmix_validChannelMask(uint32_t mask)162 static 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 }
193
194 /*----------------------------------------------------------------------------
195 * Effect API implementation
196 *--------------------------------------------------------------------------*/
197
198 /*--- Effect Library Interface Implementation ---*/
199
DownmixLib_Create(const effect_uuid_t * uuid,int32_t sessionId __unused,int32_t ioId __unused,effect_handle_t * pHandle)200 int32_t DownmixLib_Create(const effect_uuid_t *uuid,
201 int32_t sessionId __unused,
202 int32_t ioId __unused,
203 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
211 #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
231 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
261 ALOGV("DownmixLib_Create() %p , size %zu", module, sizeof(downmix_module_t));
262
263 return 0;
264 }
265
266
DownmixLib_Release(effect_handle_t handle)267 int32_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
DownmixLib_GetDescriptor(const effect_uuid_t * uuid,effect_descriptor_t * pDescriptor)282 int32_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));
295 ALOGV("EffectGetDescriptor - UUID matched downmix type %d, UUID = %" PRIx32,
296 i, gDescriptors[i]->uuid.timeLow);
297 return 0;
298 }
299 }
300
301 return -EINVAL;
302 }
303
304 #ifndef BUILD_FLOAT
305 /*--- Effect Control Interface Implementation ---*/
306
Downmix_Process(effect_handle_t self,audio_buffer_t * inBuffer,audio_buffer_t * outBuffer)307 static 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);
340 const uint32_t downmixInputChannelMask = pDwmModule->config.inputCfg.channels;
341
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:
365 #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)) {
369 ALOGE("Multichannel configuration 0x%" PRIx32 " is not supported", downmixInputChannelMask);
370 return -EINVAL;
371 }
372 break;
373 #endif
374 // optimize for the common formats
375 switch((downmix_input_channel_mask_t)downmixInputChannelMask) {
376 case CHANNEL_MASK_QUAD_BACK:
377 case CHANNEL_MASK_QUAD_SIDE:
378 Downmix_foldFromQuad(pSrc, pDst, numFrames, accumulate);
379 break;
380 case CHANNEL_MASK_5POINT1_BACK:
381 case CHANNEL_MASK_5POINT1_SIDE:
382 Downmix_foldFrom5Point1(pSrc, pDst, numFrames, accumulate);
383 break;
384 case CHANNEL_MASK_7POINT1:
385 Downmix_foldFrom7Point1(pSrc, pDst, numFrames, accumulate);
386 break;
387 default:
388 if (!Downmix_foldGeneric(
389 downmixInputChannelMask, pSrc, pDst, numFrames, accumulate)) {
390 ALOGE("Multichannel configuration 0x%" PRIx32 " is not supported", downmixInputChannelMask);
391 return -EINVAL;
392 }
393 break;
394 }
395 break;
396
397 default:
398 return -EINVAL;
399 }
400
401 return 0;
402 }
403 #else /*BUILD_FLOAT*/
404 /*--- Effect Control Interface Implementation ---*/
405
Downmix_Process(effect_handle_t self,audio_buffer_t * inBuffer,audio_buffer_t * outBuffer)406 static 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
505
Downmix_Command(effect_handle_t self,uint32_t cmdCode,uint32_t cmdSize,void * pCmdData,uint32_t * replySize,void * pReplyData)506 static 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;
511
512 if (pDwmModule == NULL || pDwmModule->context.state == DOWNMIX_STATE_UNINITIALIZED) {
513 return -EINVAL;
514 }
515
516 pDownmixer = (downmix_object_t*) &pDwmModule->context;
517
518 ALOGV("Downmix_Command command %" PRIu32 " cmdSize %" PRIu32, cmdCode, cmdSize);
519
520 switch (cmdCode) {
521 case EFFECT_CMD_INIT:
522 if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
523 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)
530 || pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
531 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:
542 ALOGV("Downmix_Command EFFECT_CMD_GET_PARAM pCmdData %p, *replySize %" PRIu32 ", pReplyData: %p",
543 pCmdData, *replySize, pReplyData);
544 if (pCmdData == NULL || cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t)) ||
545 pReplyData == NULL || replySize == NULL ||
546 *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));
551 ALOGV("Downmix_Command EFFECT_CMD_GET_PARAM param %" PRId32 ", replySize %" PRIu32,
552 *(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:
559 ALOGV("Downmix_Command EFFECT_CMD_SET_PARAM cmdSize %d pCmdData %p, *replySize %" PRIu32
560 ", pReplyData %p", cmdSize, pCmdData, *replySize, pReplyData);
561 if (pCmdData == NULL || (cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t)))
562 || pReplyData == NULL || replySize == NULL || *replySize != (int)sizeof(int32_t)) {
563 return -EINVAL;
564 }
565 effect_param_t *cmd = (effect_param_t *) pCmdData;
566 if (cmd->psize != sizeof(int32_t)) {
567 android_errorWriteLog(0x534e4554, "63662938");
568 return -EINVAL;
569 }
570 *(int *)pReplyData = Downmix_setParameter(pDownmixer, *(int32_t *)cmd->data,
571 cmd->vsize, cmd->data + sizeof(int32_t));
572 break;
573
574 case EFFECT_CMD_SET_PARAM_DEFERRED:
575 //FIXME implement
576 ALOGW("Downmix_Command command EFFECT_CMD_SET_PARAM_DEFERRED not supported, FIXME");
577 break;
578
579 case EFFECT_CMD_SET_PARAM_COMMIT:
580 //FIXME implement
581 ALOGW("Downmix_Command command EFFECT_CMD_SET_PARAM_COMMIT not supported, FIXME");
582 break;
583
584 case EFFECT_CMD_ENABLE:
585 if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
586 return -EINVAL;
587 }
588 if (pDownmixer->state != DOWNMIX_STATE_INITIALIZED) {
589 return -ENOSYS;
590 }
591 pDownmixer->state = DOWNMIX_STATE_ACTIVE;
592 ALOGV("EFFECT_CMD_ENABLE() OK");
593 *(int *)pReplyData = 0;
594 break;
595
596 case EFFECT_CMD_DISABLE:
597 if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
598 return -EINVAL;
599 }
600 if (pDownmixer->state != DOWNMIX_STATE_ACTIVE) {
601 return -ENOSYS;
602 }
603 pDownmixer->state = DOWNMIX_STATE_INITIALIZED;
604 ALOGV("EFFECT_CMD_DISABLE() OK");
605 *(int *)pReplyData = 0;
606 break;
607
608 case EFFECT_CMD_SET_DEVICE:
609 if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) {
610 return -EINVAL;
611 }
612 // FIXME change type if playing on headset vs speaker
613 ALOGV("Downmix_Command EFFECT_CMD_SET_DEVICE: 0x%08" PRIx32, *(uint32_t *)pCmdData);
614 break;
615
616 case EFFECT_CMD_SET_VOLUME: {
617 // audio output is always stereo => 2 channel volumes
618 if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t) * 2) {
619 return -EINVAL;
620 }
621 // FIXME change volume
622 ALOGW("Downmix_Command command EFFECT_CMD_SET_VOLUME not supported, FIXME");
623 float left = (float)(*(uint32_t *)pCmdData) / (1 << 24);
624 float right = (float)(*((uint32_t *)pCmdData + 1)) / (1 << 24);
625 ALOGV("Downmix_Command EFFECT_CMD_SET_VOLUME: left %f, right %f ", left, right);
626 break;
627 }
628
629 case EFFECT_CMD_SET_AUDIO_MODE:
630 if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) {
631 return -EINVAL;
632 }
633 ALOGV("Downmix_Command EFFECT_CMD_SET_AUDIO_MODE: %" PRIu32, *(uint32_t *)pCmdData);
634 break;
635
636 case EFFECT_CMD_SET_CONFIG_REVERSE:
637 case EFFECT_CMD_SET_INPUT_DEVICE:
638 // these commands are ignored by a downmix effect
639 break;
640
641 default:
642 ALOGW("Downmix_Command invalid command %" PRIu32, cmdCode);
643 return -EINVAL;
644 }
645
646 return 0;
647 }
648
649
Downmix_GetDescriptor(effect_handle_t self,effect_descriptor_t * pDescriptor)650 int Downmix_GetDescriptor(effect_handle_t self, effect_descriptor_t *pDescriptor)
651 {
652 downmix_module_t *pDwnmxModule = (downmix_module_t *) self;
653
654 if (pDwnmxModule == NULL ||
655 pDwnmxModule->context.state == DOWNMIX_STATE_UNINITIALIZED) {
656 return -EINVAL;
657 }
658
659 memcpy(pDescriptor, &gDownmixDescriptor, sizeof(effect_descriptor_t));
660
661 return 0;
662 }
663
664
665 /*----------------------------------------------------------------------------
666 * Downmix internal functions
667 *--------------------------------------------------------------------------*/
668
669 /*----------------------------------------------------------------------------
670 * Downmix_Init()
671 *----------------------------------------------------------------------------
672 * Purpose:
673 * Initialize downmix context and apply default parameters
674 *
675 * Inputs:
676 * pDwmModule pointer to downmix effect module
677 *
678 * Outputs:
679 *
680 * Returns:
681 * 0 indicates success
682 *
683 * Side Effects:
684 * updates:
685 * pDwmModule->context.type
686 * pDwmModule->context.apply_volume_correction
687 * pDwmModule->config.inputCfg
688 * pDwmModule->config.outputCfg
689 * pDwmModule->config.inputCfg.samplingRate
690 * pDwmModule->config.outputCfg.samplingRate
691 * pDwmModule->context.state
692 * doesn't set:
693 * pDwmModule->itfe
694 *
695 *----------------------------------------------------------------------------
696 */
697
Downmix_Init(downmix_module_t * pDwmModule)698 int Downmix_Init(downmix_module_t *pDwmModule) {
699
700 ALOGV("Downmix_Init module %p", pDwmModule);
701 int ret = 0;
702
703 memset(&pDwmModule->context, 0, sizeof(downmix_object_t));
704
705 pDwmModule->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
706 pDwmModule->config.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
707 pDwmModule->config.inputCfg.channels = AUDIO_CHANNEL_OUT_7POINT1;
708 pDwmModule->config.inputCfg.bufferProvider.getBuffer = NULL;
709 pDwmModule->config.inputCfg.bufferProvider.releaseBuffer = NULL;
710 pDwmModule->config.inputCfg.bufferProvider.cookie = NULL;
711 pDwmModule->config.inputCfg.mask = EFFECT_CONFIG_ALL;
712
713 pDwmModule->config.inputCfg.samplingRate = 44100;
714 pDwmModule->config.outputCfg.samplingRate = pDwmModule->config.inputCfg.samplingRate;
715
716 // set a default value for the access mode, but should be overwritten by caller
717 pDwmModule->config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
718 pDwmModule->config.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
719 pDwmModule->config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
720 pDwmModule->config.outputCfg.bufferProvider.getBuffer = NULL;
721 pDwmModule->config.outputCfg.bufferProvider.releaseBuffer = NULL;
722 pDwmModule->config.outputCfg.bufferProvider.cookie = NULL;
723 pDwmModule->config.outputCfg.mask = EFFECT_CONFIG_ALL;
724
725 ret = Downmix_Configure(pDwmModule, &pDwmModule->config, true);
726 if (ret != 0) {
727 ALOGV("Downmix_Init error %d on module %p", ret, pDwmModule);
728 } else {
729 pDwmModule->context.state = DOWNMIX_STATE_INITIALIZED;
730 }
731
732 return ret;
733 }
734
735
736 /*----------------------------------------------------------------------------
737 * Downmix_Configure()
738 *----------------------------------------------------------------------------
739 * Purpose:
740 * Set input and output audio configuration.
741 *
742 * Inputs:
743 * pDwmModule pointer to downmix effect module
744 * pConfig pointer to effect_config_t structure containing input
745 * and output audio parameters configuration
746 * init true if called from init function
747 *
748 * Outputs:
749 *
750 * Returns:
751 * 0 indicates success
752 *
753 * Side Effects:
754 *
755 *----------------------------------------------------------------------------
756 */
757
Downmix_Configure(downmix_module_t * pDwmModule,effect_config_t * pConfig,bool init)758 int Downmix_Configure(downmix_module_t *pDwmModule, effect_config_t *pConfig, bool init) {
759
760 downmix_object_t *pDownmixer = &pDwmModule->context;
761
762 // Check configuration compatibility with build options, and effect capabilities
763 if (pConfig->inputCfg.samplingRate != pConfig->outputCfg.samplingRate
764 || pConfig->outputCfg.channels != DOWNMIX_OUTPUT_CHANNELS
765 || pConfig->inputCfg.format != AUDIO_FORMAT_PCM_16_BIT
766 || pConfig->outputCfg.format != AUDIO_FORMAT_PCM_16_BIT) {
767 ALOGE("Downmix_Configure error: invalid config");
768 return -EINVAL;
769 }
770
771 if (&pDwmModule->config != pConfig) {
772 memcpy(&pDwmModule->config, pConfig, sizeof(effect_config_t));
773 }
774
775 if (init) {
776 pDownmixer->type = DOWNMIX_TYPE_FOLD;
777 pDownmixer->apply_volume_correction = false;
778 pDownmixer->input_channel_count = 8; // matches default input of AUDIO_CHANNEL_OUT_7POINT1
779 } else {
780 // when configuring the effect, do not allow a blank or unsupported channel mask
781 if (!Downmix_validChannelMask(pConfig->inputCfg.channels)) {
782 ALOGE("Downmix_Configure error: input channel mask(0x%x) not supported",
783 pConfig->inputCfg.channels);
784 return -EINVAL;
785 }
786 pDownmixer->input_channel_count =
787 audio_channel_count_from_out_mask(pConfig->inputCfg.channels);
788 }
789
790 Downmix_Reset(pDownmixer, init);
791
792 return 0;
793 }
794
795
796 /*----------------------------------------------------------------------------
797 * Downmix_Reset()
798 *----------------------------------------------------------------------------
799 * Purpose:
800 * Reset internal states.
801 *
802 * Inputs:
803 * pDownmixer pointer to downmix context
804 * init true if called from init function
805 *
806 * Outputs:
807 *
808 * Returns:
809 * 0 indicates success
810 *
811 * Side Effects:
812 *
813 *----------------------------------------------------------------------------
814 */
815
Downmix_Reset(downmix_object_t * pDownmixer __unused,bool init __unused)816 int Downmix_Reset(downmix_object_t *pDownmixer __unused, bool init __unused) {
817 // nothing to do here
818 return 0;
819 }
820
821
822 /*----------------------------------------------------------------------------
823 * Downmix_setParameter()
824 *----------------------------------------------------------------------------
825 * Purpose:
826 * Set a Downmix parameter
827 *
828 * Inputs:
829 * pDownmixer handle to instance data
830 * param parameter
831 * pValue pointer to parameter value
832 * size value size
833 *
834 * Outputs:
835 *
836 * Returns:
837 * 0 indicates success
838 *
839 * Side Effects:
840 *
841 *----------------------------------------------------------------------------
842 */
Downmix_setParameter(downmix_object_t * pDownmixer,int32_t param,uint32_t size,void * pValue)843 int Downmix_setParameter(downmix_object_t *pDownmixer, int32_t param, uint32_t size, void *pValue) {
844
845 int16_t value16;
846 ALOGV("Downmix_setParameter, context %p, param %" PRId32 ", value16 %" PRId16 ", value32 %" PRId32,
847 pDownmixer, param, *(int16_t *)pValue, *(int32_t *)pValue);
848
849 switch (param) {
850
851 case DOWNMIX_PARAM_TYPE:
852 if (size != sizeof(downmix_type_t)) {
853 ALOGE("Downmix_setParameter(DOWNMIX_PARAM_TYPE) invalid size %" PRIu32 ", should be %zu",
854 size, sizeof(downmix_type_t));
855 return -EINVAL;
856 }
857 value16 = *(int16_t *)pValue;
858 ALOGV("set DOWNMIX_PARAM_TYPE, type %" PRId16, value16);
859 if (!((value16 > DOWNMIX_TYPE_INVALID) && (value16 <= DOWNMIX_TYPE_LAST))) {
860 ALOGE("Downmix_setParameter invalid DOWNMIX_PARAM_TYPE value %" PRId16, value16);
861 return -EINVAL;
862 } else {
863 pDownmixer->type = (downmix_type_t) value16;
864 break;
865
866 default:
867 ALOGE("Downmix_setParameter unknown parameter %" PRId32, param);
868 return -EINVAL;
869 }
870 }
871
872 return 0;
873 } /* end Downmix_setParameter */
874
875
876 /*----------------------------------------------------------------------------
877 * Downmix_getParameter()
878 *----------------------------------------------------------------------------
879 * Purpose:
880 * Get a Downmix parameter
881 *
882 * Inputs:
883 * pDownmixer handle to instance data
884 * param parameter
885 * pValue pointer to variable to hold retrieved value
886 * pSize pointer to value size: maximum size as input
887 *
888 * Outputs:
889 * *pValue updated with parameter value
890 * *pSize updated with actual value size
891 *
892 * Returns:
893 * 0 indicates success
894 *
895 * Side Effects:
896 *
897 *----------------------------------------------------------------------------
898 */
Downmix_getParameter(downmix_object_t * pDownmixer,int32_t param,uint32_t * pSize,void * pValue)899 int Downmix_getParameter(downmix_object_t *pDownmixer, int32_t param, uint32_t *pSize, void *pValue) {
900 int16_t *pValue16;
901
902 switch (param) {
903
904 case DOWNMIX_PARAM_TYPE:
905 if (*pSize < sizeof(int16_t)) {
906 ALOGE("Downmix_getParameter invalid parameter size %" PRIu32 " for DOWNMIX_PARAM_TYPE", *pSize);
907 return -EINVAL;
908 }
909 pValue16 = (int16_t *)pValue;
910 *pValue16 = (int16_t) pDownmixer->type;
911 *pSize = sizeof(int16_t);
912 ALOGV("Downmix_getParameter DOWNMIX_PARAM_TYPE is %" PRId16, *pValue16);
913 break;
914
915 default:
916 ALOGE("Downmix_getParameter unknown parameter %" PRId16, param);
917 return -EINVAL;
918 }
919
920 return 0;
921 } /* end Downmix_getParameter */
922
923
924 /*----------------------------------------------------------------------------
925 * Downmix_foldFromQuad()
926 *----------------------------------------------------------------------------
927 * Purpose:
928 * downmix a quad signal to stereo
929 *
930 * Inputs:
931 * pSrc quad audio samples to downmix
932 * numFrames the number of quad frames to downmix
933 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
934 * or overwrite pDst (when false)
935 *
936 * Outputs:
937 * pDst downmixed stereo audio samples
938 *
939 *----------------------------------------------------------------------------
940 */
941 #ifndef BUILD_FLOAT
Downmix_foldFromQuad(int16_t * pSrc,int16_t * pDst,size_t numFrames,bool accumulate)942 void Downmix_foldFromQuad(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
943 // sample at index 0 is FL
944 // sample at index 1 is FR
945 // sample at index 2 is RL
946 // sample at index 3 is RR
947 if (accumulate) {
948 while (numFrames) {
949 // FL + RL
950 pDst[0] = clamp16(pDst[0] + ((pSrc[0] + pSrc[2]) >> 1));
951 // FR + RR
952 pDst[1] = clamp16(pDst[1] + ((pSrc[1] + pSrc[3]) >> 1));
953 pSrc += 4;
954 pDst += 2;
955 numFrames--;
956 }
957 } else { // same code as above but without adding and clamping pDst[i] to itself
958 while (numFrames) {
959 // FL + RL
960 pDst[0] = clamp16((pSrc[0] + pSrc[2]) >> 1);
961 // FR + RR
962 pDst[1] = clamp16((pSrc[1] + pSrc[3]) >> 1);
963 pSrc += 4;
964 pDst += 2;
965 numFrames--;
966 }
967 }
968 }
969 #else
Downmix_foldFromQuad(LVM_FLOAT * pSrc,LVM_FLOAT * pDst,size_t numFrames,bool accumulate)970 void Downmix_foldFromQuad(LVM_FLOAT *pSrc, LVM_FLOAT *pDst, size_t numFrames, bool accumulate) {
971 // sample at index 0 is FL
972 // sample at index 1 is FR
973 // sample at index 2 is RL
974 // sample at index 3 is RR
975 if (accumulate) {
976 while (numFrames) {
977 // FL + RL
978 pDst[0] = clamp_float(pDst[0] + ((pSrc[0] + pSrc[2]) / 2.0f));
979 // FR + RR
980 pDst[1] = clamp_float(pDst[1] + ((pSrc[1] + pSrc[3]) / 2.0f));
981 pSrc += 4;
982 pDst += 2;
983 numFrames--;
984 }
985 } else { // same code as above but without adding and clamping pDst[i] to itself
986 while (numFrames) {
987 // FL + RL
988 pDst[0] = clamp_float((pSrc[0] + pSrc[2]) / 2.0f);
989 // FR + RR
990 pDst[1] = clamp_float((pSrc[1] + pSrc[3]) / 2.0f);
991 pSrc += 4;
992 pDst += 2;
993 numFrames--;
994 }
995 }
996 }
997 #endif
998
999 /*----------------------------------------------------------------------------
1000 * Downmix_foldFrom5Point1()
1001 *----------------------------------------------------------------------------
1002 * Purpose:
1003 * downmix a 5.1 signal to stereo
1004 *
1005 * Inputs:
1006 * pSrc 5.1 audio samples to downmix
1007 * numFrames the number of 5.1 frames to downmix
1008 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
1009 * or overwrite pDst (when false)
1010 *
1011 * Outputs:
1012 * pDst downmixed stereo audio samples
1013 *
1014 *----------------------------------------------------------------------------
1015 */
1016 #ifndef BUILD_FLOAT
Downmix_foldFrom5Point1(int16_t * pSrc,int16_t * pDst,size_t numFrames,bool accumulate)1017 void Downmix_foldFrom5Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
1018 int32_t lt, rt, centerPlusLfeContrib; // samples in Q19.12 format
1019 // sample at index 0 is FL
1020 // sample at index 1 is FR
1021 // sample at index 2 is FC
1022 // sample at index 3 is LFE
1023 // sample at index 4 is RL
1024 // sample at index 5 is RR
1025 // code is mostly duplicated between the two values of accumulate to avoid repeating the test
1026 // for every sample
1027 if (accumulate) {
1028 while (numFrames) {
1029 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
1030 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
1031 + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
1032 // FL + centerPlusLfeContrib + RL
1033 lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[4] << 12);
1034 // FR + centerPlusLfeContrib + RR
1035 rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[5] << 12);
1036 // accumulate in destination
1037 pDst[0] = clamp16(pDst[0] + (lt >> 13));
1038 pDst[1] = clamp16(pDst[1] + (rt >> 13));
1039 pSrc += 6;
1040 pDst += 2;
1041 numFrames--;
1042 }
1043 } else { // same code as above but without adding and clamping pDst[i] to itself
1044 while (numFrames) {
1045 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
1046 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
1047 + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
1048 // FL + centerPlusLfeContrib + RL
1049 lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[4] << 12);
1050 // FR + centerPlusLfeContrib + RR
1051 rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[5] << 12);
1052 // store in destination
1053 pDst[0] = clamp16(lt >> 13); // differs from when accumulate is true above
1054 pDst[1] = clamp16(rt >> 13); // differs from when accumulate is true above
1055 pSrc += 6;
1056 pDst += 2;
1057 numFrames--;
1058 }
1059 }
1060 }
1061 #else
Downmix_foldFrom5Point1(LVM_FLOAT * pSrc,LVM_FLOAT * pDst,size_t numFrames,bool accumulate)1062 void Downmix_foldFrom5Point1(LVM_FLOAT *pSrc, LVM_FLOAT *pDst, size_t numFrames, bool accumulate) {
1063 LVM_FLOAT lt, rt, centerPlusLfeContrib; // samples in Q19.12 format
1064 // sample at index 0 is FL
1065 // sample at index 1 is FR
1066 // sample at index 2 is FC
1067 // sample at index 3 is LFE
1068 // sample at index 4 is RL
1069 // sample at index 5 is RR
1070 // code is mostly duplicated between the two values of accumulate to avoid repeating the test
1071 // for every sample
1072 if (accumulate) {
1073 while (numFrames) {
1074 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
1075 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_FLOAT)
1076 + (pSrc[3] * MINUS_3_DB_IN_FLOAT);
1077 // FL + centerPlusLfeContrib + RL
1078 lt = pSrc[0] + centerPlusLfeContrib + pSrc[4];
1079 // FR + centerPlusLfeContrib + RR
1080 rt = pSrc[1] + centerPlusLfeContrib + pSrc[5];
1081 // accumulate in destination
1082 pDst[0] = clamp_float(pDst[0] + (lt / 2.0f));
1083 pDst[1] = clamp_float(pDst[1] + (rt / 2.0f));
1084 pSrc += 6;
1085 pDst += 2;
1086 numFrames--;
1087 }
1088 } else { // same code as above but without adding and clamping pDst[i] to itself
1089 while (numFrames) {
1090 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
1091 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_FLOAT)
1092 + (pSrc[3] * MINUS_3_DB_IN_FLOAT);
1093 // FL + centerPlusLfeContrib + RL
1094 lt = pSrc[0] + centerPlusLfeContrib + pSrc[4];
1095 // FR + centerPlusLfeContrib + RR
1096 rt = pSrc[1] + centerPlusLfeContrib + pSrc[5];
1097 // store in destination
1098 pDst[0] = clamp_float(lt / 2.0f); // differs from when accumulate is true above
1099 pDst[1] = clamp_float(rt / 2.0f); // differs from when accumulate is true above
1100 pSrc += 6;
1101 pDst += 2;
1102 numFrames--;
1103 }
1104 }
1105 }
1106 #endif
1107
1108 /*----------------------------------------------------------------------------
1109 * Downmix_foldFrom7Point1()
1110 *----------------------------------------------------------------------------
1111 * Purpose:
1112 * downmix a 7.1 signal to stereo
1113 *
1114 * Inputs:
1115 * pSrc 7.1 audio samples to downmix
1116 * numFrames the number of 7.1 frames to downmix
1117 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
1118 * or overwrite pDst (when false)
1119 *
1120 * Outputs:
1121 * pDst downmixed stereo audio samples
1122 *
1123 *----------------------------------------------------------------------------
1124 */
1125 #ifndef BUILD_FLOAT
Downmix_foldFrom7Point1(int16_t * pSrc,int16_t * pDst,size_t numFrames,bool accumulate)1126 void Downmix_foldFrom7Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
1127 int32_t lt, rt, centerPlusLfeContrib; // samples in Q19.12 format
1128 // sample at index 0 is FL
1129 // sample at index 1 is FR
1130 // sample at index 2 is FC
1131 // sample at index 3 is LFE
1132 // sample at index 4 is RL
1133 // sample at index 5 is RR
1134 // sample at index 6 is SL
1135 // sample at index 7 is SR
1136 // code is mostly duplicated between the two values of accumulate to avoid repeating the test
1137 // for every sample
1138 if (accumulate) {
1139 while (numFrames) {
1140 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
1141 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
1142 + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
1143 // FL + centerPlusLfeContrib + SL + RL
1144 lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[6] << 12) + (pSrc[4] << 12);
1145 // FR + centerPlusLfeContrib + SR + RR
1146 rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[7] << 12) + (pSrc[5] << 12);
1147 //accumulate in destination
1148 pDst[0] = clamp16(pDst[0] + (lt >> 13));
1149 pDst[1] = clamp16(pDst[1] + (rt >> 13));
1150 pSrc += 8;
1151 pDst += 2;
1152 numFrames--;
1153 }
1154 } else { // same code as above but without adding and clamping pDst[i] to itself
1155 while (numFrames) {
1156 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
1157 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
1158 + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
1159 // FL + centerPlusLfeContrib + SL + RL
1160 lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[6] << 12) + (pSrc[4] << 12);
1161 // FR + centerPlusLfeContrib + SR + RR
1162 rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[7] << 12) + (pSrc[5] << 12);
1163 // store in destination
1164 pDst[0] = clamp16(lt >> 13); // differs from when accumulate is true above
1165 pDst[1] = clamp16(rt >> 13); // differs from when accumulate is true above
1166 pSrc += 8;
1167 pDst += 2;
1168 numFrames--;
1169 }
1170 }
1171 }
1172 #else
Downmix_foldFrom7Point1(LVM_FLOAT * pSrc,LVM_FLOAT * pDst,size_t numFrames,bool accumulate)1173 void Downmix_foldFrom7Point1(LVM_FLOAT *pSrc, LVM_FLOAT *pDst, size_t numFrames, bool accumulate) {
1174 LVM_FLOAT lt, rt, centerPlusLfeContrib; // samples in Q19.12 format
1175 // sample at index 0 is FL
1176 // sample at index 1 is FR
1177 // sample at index 2 is FC
1178 // sample at index 3 is LFE
1179 // sample at index 4 is RL
1180 // sample at index 5 is RR
1181 // sample at index 6 is SL
1182 // sample at index 7 is SR
1183 // code is mostly duplicated between the two values of accumulate to avoid repeating the test
1184 // for every sample
1185 if (accumulate) {
1186 while (numFrames) {
1187 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
1188 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
1189 + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
1190 // FL + centerPlusLfeContrib + SL + RL
1191 lt = pSrc[0] + centerPlusLfeContrib + pSrc[6] + pSrc[4];
1192 // FR + centerPlusLfeContrib + SR + RR
1193 rt = pSrc[1] + centerPlusLfeContrib + pSrc[7] + pSrc[5];
1194 //accumulate in destination
1195 pDst[0] = clamp_float(pDst[0] + (lt / 2.0f));
1196 pDst[1] = clamp_float(pDst[1] + (rt / 2.0f));
1197 pSrc += 8;
1198 pDst += 2;
1199 numFrames--;
1200 }
1201 } else { // same code as above but without adding and clamping pDst[i] to itself
1202 while (numFrames) {
1203 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
1204 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_FLOAT)
1205 + (pSrc[3] * MINUS_3_DB_IN_FLOAT);
1206 // FL + centerPlusLfeContrib + SL + RL
1207 lt = pSrc[0] + centerPlusLfeContrib + pSrc[6] + pSrc[4];
1208 // FR + centerPlusLfeContrib + SR + RR
1209 rt = pSrc[1] + centerPlusLfeContrib + pSrc[7] + pSrc[5];
1210 // store in destination
1211 pDst[0] = clamp_float(lt / 2.0f); // differs from when accumulate is true above
1212 pDst[1] = clamp_float(rt / 2.0f); // differs from when accumulate is true above
1213 pSrc += 8;
1214 pDst += 2;
1215 numFrames--;
1216 }
1217 }
1218 }
1219 #endif
1220 /*----------------------------------------------------------------------------
1221 * Downmix_foldGeneric()
1222 *----------------------------------------------------------------------------
1223 * Purpose:
1224 * downmix to stereo a multichannel signal whose format is:
1225 * - has FL/FR
1226 * - if using AUDIO_CHANNEL_OUT_SIDE*, it contains both left and right
1227 * - if using AUDIO_CHANNEL_OUT_BACK*, it contains both left and right
1228 * - doesn't use any of the AUDIO_CHANNEL_OUT_TOP* channels
1229 * - doesn't use any of the AUDIO_CHANNEL_OUT_FRONT_*_OF_CENTER channels
1230 * Only handles channel masks not enumerated in downmix_input_channel_mask_t
1231 *
1232 * Inputs:
1233 * mask the channel mask of pSrc
1234 * pSrc multichannel audio buffer to downmix
1235 * numFrames the number of multichannel frames to downmix
1236 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
1237 * or overwrite pDst (when false)
1238 *
1239 * Outputs:
1240 * pDst downmixed stereo audio samples
1241 *
1242 * Returns: false if multichannel format is not supported
1243 *
1244 *----------------------------------------------------------------------------
1245 */
1246 #ifndef BUILD_FLOAT
Downmix_foldGeneric(uint32_t mask,int16_t * pSrc,int16_t * pDst,size_t numFrames,bool accumulate)1247 bool Downmix_foldGeneric(
1248 uint32_t mask, int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
1249
1250 if (!Downmix_validChannelMask(mask)) {
1251 return false;
1252 }
1253
1254 const bool hasSides = (mask & kSides) != 0;
1255 const bool hasBacks = (mask & kBacks) != 0;
1256
1257 const int numChan = audio_channel_count_from_out_mask(mask);
1258 const bool hasFC = ((mask & AUDIO_CHANNEL_OUT_FRONT_CENTER) == AUDIO_CHANNEL_OUT_FRONT_CENTER);
1259 const bool hasLFE =
1260 ((mask & AUDIO_CHANNEL_OUT_LOW_FREQUENCY) == AUDIO_CHANNEL_OUT_LOW_FREQUENCY);
1261 const bool hasBC = ((mask & AUDIO_CHANNEL_OUT_BACK_CENTER) == AUDIO_CHANNEL_OUT_BACK_CENTER);
1262 // compute at what index each channel is: samples will be in the following order:
1263 // FL FR FC LFE BL BR BC SL SR
1264 // when a channel is not present, its index is set to the same as the index of the preceding
1265 // channel
1266 const int indexFC = hasFC ? 2 : 1; // front center
1267 const int indexLFE = hasLFE ? indexFC + 1 : indexFC; // low frequency
1268 const int indexBL = hasBacks ? indexLFE + 1 : indexLFE; // back left
1269 const int indexBR = hasBacks ? indexBL + 1 : indexBL; // back right
1270 const int indexBC = hasBC ? indexBR + 1 : indexBR; // back center
1271 const int indexSL = hasSides ? indexBC + 1 : indexBC; // side left
1272 const int indexSR = hasSides ? indexSL + 1 : indexSL; // side right
1273
1274 int32_t lt, rt, centersLfeContrib; // samples in Q19.12 format
1275 // code is mostly duplicated between the two values of accumulate to avoid repeating the test
1276 // for every sample
1277 if (accumulate) {
1278 while (numFrames) {
1279 // compute contribution of FC, BC and LFE
1280 centersLfeContrib = 0;
1281 if (hasFC) { centersLfeContrib += pSrc[indexFC]; }
1282 if (hasLFE) { centersLfeContrib += pSrc[indexLFE]; }
1283 if (hasBC) { centersLfeContrib += pSrc[indexBC]; }
1284 centersLfeContrib *= MINUS_3_DB_IN_Q19_12;
1285 // always has FL/FR
1286 lt = (pSrc[0] << 12);
1287 rt = (pSrc[1] << 12);
1288 // mix in sides and backs
1289 if (hasSides) {
1290 lt += pSrc[indexSL] << 12;
1291 rt += pSrc[indexSR] << 12;
1292 }
1293 if (hasBacks) {
1294 lt += pSrc[indexBL] << 12;
1295 rt += pSrc[indexBR] << 12;
1296 }
1297 lt += centersLfeContrib;
1298 rt += centersLfeContrib;
1299 // accumulate in destination
1300 pDst[0] = clamp16(pDst[0] + (lt >> 13));
1301 pDst[1] = clamp16(pDst[1] + (rt >> 13));
1302 pSrc += numChan;
1303 pDst += 2;
1304 numFrames--;
1305 }
1306 } else {
1307 while (numFrames) {
1308 // compute contribution of FC, BC and LFE
1309 centersLfeContrib = 0;
1310 if (hasFC) { centersLfeContrib += pSrc[indexFC]; }
1311 if (hasLFE) { centersLfeContrib += pSrc[indexLFE]; }
1312 if (hasBC) { centersLfeContrib += pSrc[indexBC]; }
1313 centersLfeContrib *= MINUS_3_DB_IN_Q19_12;
1314 // always has FL/FR
1315 lt = (pSrc[0] << 12);
1316 rt = (pSrc[1] << 12);
1317 // mix in sides and backs
1318 if (hasSides) {
1319 lt += pSrc[indexSL] << 12;
1320 rt += pSrc[indexSR] << 12;
1321 }
1322 if (hasBacks) {
1323 lt += pSrc[indexBL] << 12;
1324 rt += pSrc[indexBR] << 12;
1325 }
1326 lt += centersLfeContrib;
1327 rt += centersLfeContrib;
1328 // store in destination
1329 pDst[0] = clamp16(lt >> 13); // differs from when accumulate is true above
1330 pDst[1] = clamp16(rt >> 13); // differs from when accumulate is true above
1331 pSrc += numChan;
1332 pDst += 2;
1333 numFrames--;
1334 }
1335 }
1336 return true;
1337 }
1338 #else
Downmix_foldGeneric(uint32_t mask,LVM_FLOAT * pSrc,LVM_FLOAT * pDst,size_t numFrames,bool accumulate)1339 bool Downmix_foldGeneric(
1340 uint32_t mask, LVM_FLOAT *pSrc, LVM_FLOAT *pDst, size_t numFrames, bool accumulate) {
1341
1342 if (!Downmix_validChannelMask(mask)) {
1343 return false;
1344 }
1345
1346 const bool hasSides = (mask & kSides) != 0;
1347 const bool hasBacks = (mask & kBacks) != 0;
1348
1349 const int numChan = audio_channel_count_from_out_mask(mask);
1350 const bool hasFC = ((mask & AUDIO_CHANNEL_OUT_FRONT_CENTER) == AUDIO_CHANNEL_OUT_FRONT_CENTER);
1351 const bool hasLFE =
1352 ((mask & AUDIO_CHANNEL_OUT_LOW_FREQUENCY) == AUDIO_CHANNEL_OUT_LOW_FREQUENCY);
1353 const bool hasBC = ((mask & AUDIO_CHANNEL_OUT_BACK_CENTER) == AUDIO_CHANNEL_OUT_BACK_CENTER);
1354 // compute at what index each channel is: samples will be in the following order:
1355 // FL FR FC LFE BL BR BC SL SR
1356 // when a channel is not present, its index is set to the same as the index of the preceding
1357 // channel
1358 const int indexFC = hasFC ? 2 : 1; // front center
1359 const int indexLFE = hasLFE ? indexFC + 1 : indexFC; // low frequency
1360 const int indexBL = hasBacks ? indexLFE + 1 : indexLFE; // back left
1361 const int indexBR = hasBacks ? indexBL + 1 : indexBL; // back right
1362 const int indexBC = hasBC ? indexBR + 1 : indexBR; // back center
1363 const int indexSL = hasSides ? indexBC + 1 : indexBC; // side left
1364 const int indexSR = hasSides ? indexSL + 1 : indexSL; // side right
1365
1366 LVM_FLOAT lt, rt, centersLfeContrib;
1367 // code is mostly duplicated between the two values of accumulate to avoid repeating the test
1368 // for every sample
1369 if (accumulate) {
1370 while (numFrames) {
1371 // compute contribution of FC, BC and LFE
1372 centersLfeContrib = 0;
1373 if (hasFC) { centersLfeContrib += pSrc[indexFC]; }
1374 if (hasLFE) { centersLfeContrib += pSrc[indexLFE]; }
1375 if (hasBC) { centersLfeContrib += pSrc[indexBC]; }
1376 centersLfeContrib *= MINUS_3_DB_IN_FLOAT;
1377 // always has FL/FR
1378 lt = pSrc[0];
1379 rt = pSrc[1];
1380 // mix in sides and backs
1381 if (hasSides) {
1382 lt += pSrc[indexSL];
1383 rt += pSrc[indexSR];
1384 }
1385 if (hasBacks) {
1386 lt += pSrc[indexBL];
1387 rt += pSrc[indexBR];
1388 }
1389 lt += centersLfeContrib;
1390 rt += centersLfeContrib;
1391 // accumulate in destination
1392 pDst[0] = clamp_float(pDst[0] + (lt / 2.0f));
1393 pDst[1] = clamp_float(pDst[1] + (rt / 2.0f));
1394 pSrc += numChan;
1395 pDst += 2;
1396 numFrames--;
1397 }
1398 } else {
1399 while (numFrames) {
1400 // compute contribution of FC, BC and LFE
1401 centersLfeContrib = 0;
1402 if (hasFC) { centersLfeContrib += pSrc[indexFC]; }
1403 if (hasLFE) { centersLfeContrib += pSrc[indexLFE]; }
1404 if (hasBC) { centersLfeContrib += pSrc[indexBC]; }
1405 centersLfeContrib *= MINUS_3_DB_IN_FLOAT;
1406 // always has FL/FR
1407 lt = pSrc[0];
1408 rt = pSrc[1];
1409 // mix in sides and backs
1410 if (hasSides) {
1411 lt += pSrc[indexSL];
1412 rt += pSrc[indexSR];
1413 }
1414 if (hasBacks) {
1415 lt += pSrc[indexBL];
1416 rt += pSrc[indexBR];
1417 }
1418 lt += centersLfeContrib;
1419 rt += centersLfeContrib;
1420 // store in destination
1421 pDst[0] = clamp_float(lt / 2.0f); // differs from when accumulate is true above
1422 pDst[1] = clamp_float(rt / 2.0f); // differs from when accumulate is true above
1423 pSrc += numChan;
1424 pDst += 2;
1425 numFrames--;
1426 }
1427 }
1428 return true;
1429 }
1430 #endif