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