blob: a325172a9c72da6dcfee9d7f7a1baca2990d461e [file] [log] [blame]
Jean-Michel Trivi64143abe12012-03-02 10:59:56 -08001/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "EffectDownmix"
18#define LOG_NDEBUG 0
19#include <cutils/log.h>
20#include <stdlib.h>
21#include <string.h>
22#include <stdbool.h>
23#include "EffectDownmix.h"
24
25#define MINUS_3_DB_IN_Q19_12 2896 // -3dB = 0.707 * 2^12 = 2896
26
27// effect_handle_t interface implementation for downmix effect
28const struct effect_interface_s gDownmixInterface = {
29 Downmix_Process,
30 Downmix_Command,
31 Downmix_GetDescriptor,
32 NULL /* no process_reverse function, no reference stream needed */
33};
34
35audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
36 tag : AUDIO_EFFECT_LIBRARY_TAG,
37 version : EFFECT_LIBRARY_API_VERSION,
38 name : "Downmix Library",
39 implementor : "The Android Open Source Project",
40 query_num_effects : DownmixLib_QueryNumberEffects,
41 query_effect : DownmixLib_QueryEffect,
42 create_effect : DownmixLib_Create,
43 release_effect : DownmixLib_Release,
44 get_descriptor : DownmixLib_GetDescriptor,
45};
46
47
48// AOSP insert downmix UUID: 93f04452-e4fe-41cc-91f9-e475b6d1d69f
49static const effect_descriptor_t gDownmixDescriptor = {
50 EFFECT_UIID_DOWNMIX__, //type
51 {0x93f04452, 0xe4fe, 0x41cc, 0x91f9, {0xe4, 0x75, 0xb6, 0xd1, 0xd6, 0x9f}}, // uuid
52 EFFECT_CONTROL_API_VERSION,
53 EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST,
54 0, //FIXME what value should be reported? // cpu load
55 0, //FIXME what value should be reported? // memory usage
56 "Multichannel Downmix To Stereo", // human readable effect name
57 "The Android Open Source Project" // human readable effect implementor name
58};
59
60// gDescriptors contains pointers to all defined effect descriptor in this library
61static const effect_descriptor_t * const gDescriptors[] = {
62 &gDownmixDescriptor
63};
64
65// number of effects in this library
66const int kNbEffects = sizeof(gDescriptors) / sizeof(const effect_descriptor_t *);
67
68
69/*----------------------------------------------------------------------------
70 * Effect API implementation
71 *--------------------------------------------------------------------------*/
72
73/*--- Effect Library Interface Implementation ---*/
74
75int32_t DownmixLib_QueryNumberEffects(uint32_t *pNumEffects) {
76 ALOGV("DownmixLib_QueryNumberEffects()");
77 *pNumEffects = kNbEffects;
78 return 0;
79}
80
81int32_t DownmixLib_QueryEffect(uint32_t index, effect_descriptor_t *pDescriptor) {
82 ALOGV("DownmixLib_QueryEffect() index=%d", index);
83 if (pDescriptor == NULL) {
84 return -EINVAL;
85 }
86 if (index >= (uint32_t)kNbEffects) {
87 return -EINVAL;
88 }
89 memcpy(pDescriptor, gDescriptors[index], sizeof(effect_descriptor_t));
90 return 0;
91}
92
93
94int32_t DownmixLib_Create(const effect_uuid_t *uuid,
95 int32_t sessionId,
96 int32_t ioId,
97 effect_handle_t *pHandle) {
98 int ret;
99 int i;
100 downmix_module_t *module;
101 const effect_descriptor_t *desc;
102
103 ALOGV("DownmixLib_Create()");
104
105 if (pHandle == NULL || uuid == NULL) {
106 return -EINVAL;
107 }
108
109 for (i = 0 ; i < kNbEffects ; i++) {
110 desc = gDescriptors[i];
111 if (memcmp(uuid, &desc->uuid, sizeof(effect_uuid_t)) == 0) {
112 break;
113 }
114 }
115
116 if (i == kNbEffects) {
117 return -ENOENT;
118 }
119
120 module = malloc(sizeof(downmix_module_t));
121
122 module->itfe = &gDownmixInterface;
123
124 module->context.state = DOWNMIX_STATE_UNINITIALIZED;
125
126 ret = Downmix_Init(module);
127 if (ret < 0) {
128 ALOGW("DownmixLib_Create() init failed");
129 free(module);
130 return ret;
131 }
132
133 *pHandle = (effect_handle_t) module;
134
135 ALOGV("DownmixLib_Create() %p , size %d", module, sizeof(downmix_module_t));
136
137 return 0;
138}
139
140
141int32_t DownmixLib_Release(effect_handle_t handle) {
142 downmix_module_t *pDwmModule = (downmix_module_t *)handle;
143
144 ALOGV("DownmixLib_Release() %p", handle);
145 if (handle == NULL) {
146 return -EINVAL;
147 }
148
149 pDwmModule->context.state = DOWNMIX_STATE_UNINITIALIZED;
150
151 free(pDwmModule);
152 return 0;
153}
154
155
156int32_t DownmixLib_GetDescriptor(const effect_uuid_t *uuid, effect_descriptor_t *pDescriptor) {
157 ALOGV("DownmixLib_GetDescriptor()");
158 int i;
159
160 if (pDescriptor == NULL || uuid == NULL){
161 ALOGE("DownmixLib_Create() called with NULL pointer");
162 return -EINVAL;
163 }
164 ALOGV("DownmixLib_GetDescriptor() nb effects=%d", kNbEffects);
165 for (i = 0; i < kNbEffects; i++) {
166 ALOGV("DownmixLib_GetDescriptor() i=%d", i);
167 if (memcmp(uuid, &gDescriptors[i]->uuid, sizeof(effect_uuid_t)) == 0) {
168 memcpy(pDescriptor, gDescriptors[i], sizeof(effect_descriptor_t));
169 ALOGV("EffectGetDescriptor - UUID matched downmix type %d, UUID = %x",
170 i, gDescriptors[i]->uuid.timeLow);
171 return 0;
172 }
173 }
174
175 return -EINVAL;
176}
177
178
179/*--- Effect Control Interface Implementation ---*/
180
181static int Downmix_Process(effect_handle_t self,
182 audio_buffer_t *inBuffer, audio_buffer_t *outBuffer) {
183
184 downmix_object_t *pDownmixer;
185 int16_t *pSrc, *pDst;
186 downmix_module_t *pDwmModule = (downmix_module_t *)self;
187
188 if (pDwmModule == NULL) {
189 return -EINVAL;
190 }
191
192 if (inBuffer == NULL || inBuffer->raw == NULL ||
193 outBuffer == NULL || outBuffer->raw == NULL ||
194 inBuffer->frameCount != outBuffer->frameCount) {
195 return -EINVAL;
196 }
197
198 pDownmixer = (downmix_object_t*) &pDwmModule->context;
199
200 if (pDownmixer->state == DOWNMIX_STATE_UNINITIALIZED) {
201 ALOGE("Downmix_Process error: trying to use an uninitialized downmixer");
202 return -EINVAL;
203 } else if (pDownmixer->state == DOWNMIX_STATE_INITIALIZED) {
204 ALOGE("Downmix_Process error: trying to use a non-configured downmixer");
205 return -ENODATA;
206 }
207
208 pSrc = inBuffer->s16;
209 pDst = outBuffer->s16;
210 size_t numFrames = outBuffer->frameCount;
211
212 const bool accumulate =
213 (pDwmModule->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE);
214
215 switch(pDownmixer->type) {
216
217 case DOWNMIX_TYPE_STRIP:
218 if (accumulate) {
219 while (numFrames) {
220 pDst[0] = clamp16(pDst[0] + pSrc[0]);
221 pDst[1] = clamp16(pDst[1] + pSrc[1]);
222 pSrc += pDownmixer->input_channel_count;
223 pDst += 2;
224 numFrames--;
225 }
226 } else {
227 while (numFrames) {
228 pDst[0] = pSrc[0];
229 pDst[1] = pSrc[1];
230 pSrc += pDownmixer->input_channel_count;
231 pDst += 2;
232 numFrames--;
233 }
234 }
235 break;
236
237 case DOWNMIX_TYPE_FOLD:
238 // optimize for the common formats
239 switch(pDwmModule->config.inputCfg.channels) {
240 case AUDIO_CHANNEL_OUT_QUAD:
241 Downmix_foldFromQuad(pSrc, pDst, numFrames, accumulate);
242 break;
243 case AUDIO_CHANNEL_OUT_SURROUND:
244 Downmix_foldFromSurround(pSrc, pDst, numFrames, accumulate);
245 break;
246 case AUDIO_CHANNEL_OUT_5POINT1:
247 Downmix_foldFrom5Point1(pSrc, pDst, numFrames, accumulate);
248 break;
249 case AUDIO_CHANNEL_OUT_7POINT1:
250 Downmix_foldFrom7Point1(pSrc, pDst, numFrames, accumulate);
251 break;
252 default:
253 // FIXME implement generic downmix
254 ALOGE("Multichannel configurations other than quad, 4.0, 5.1 and 7.1 are not supported");
255 break;
256 }
257 break;
258
259 default:
260 return -EINVAL;
261 }
262
263 return 0;
264}
265
266
267static int Downmix_Command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
268 void *pCmdData, uint32_t *replySize, void *pReplyData) {
269
270 downmix_module_t *pDwmModule = (downmix_module_t *) self;
271 downmix_object_t *pDownmixer;
272 int retsize;
273
274 if (pDwmModule == NULL || pDwmModule->context.state == DOWNMIX_STATE_UNINITIALIZED) {
275 return -EINVAL;
276 }
277
278 pDownmixer = (downmix_object_t*) &pDwmModule->context;
279
280 ALOGV("Downmix_Command command %d cmdSize %d",cmdCode, cmdSize);
281
282 switch (cmdCode) {
283 case EFFECT_CMD_INIT:
284 if (pReplyData == NULL || *replySize != sizeof(int)) {
285 return -EINVAL;
286 }
287 *(int *) pReplyData = Downmix_Init(pDwmModule);
288 break;
289
290 case EFFECT_CMD_SET_CONFIG:
291 if (pCmdData == NULL || cmdSize != sizeof(effect_config_t)
292 || pReplyData == NULL || *replySize != sizeof(int)) {
293 return -EINVAL;
294 }
295 *(int *) pReplyData = Downmix_Configure(pDwmModule,
296 (effect_config_t *)pCmdData, false);
297 break;
298
299 case EFFECT_CMD_RESET:
300 Downmix_Reset(pDownmixer, false);
301 break;
302
303 case EFFECT_CMD_GET_PARAM:
304 ALOGV("Downmix_Command EFFECT_CMD_GET_PARAM pCmdData %p, *replySize %d, pReplyData: %p",
305 pCmdData, *replySize, pReplyData);
306 if (pCmdData == NULL || cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t)) ||
307 pReplyData == NULL ||
308 *replySize < (int) sizeof(effect_param_t) + 2 * sizeof(int32_t)) {
309 return -EINVAL;
310 }
311 effect_param_t *rep = (effect_param_t *) pReplyData;
312 memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + sizeof(int32_t));
313 ALOGV("Downmix_Command EFFECT_CMD_GET_PARAM param %d, replySize %d",
314 *(int32_t *)rep->data, rep->vsize);
315 rep->status = Downmix_getParameter(pDownmixer, *(int32_t *)rep->data, &rep->vsize,
316 rep->data + sizeof(int32_t));
317 *replySize = sizeof(effect_param_t) + sizeof(int32_t) + rep->vsize;
318 break;
319
320 case EFFECT_CMD_SET_PARAM:
321 ALOGV("Downmix_Command EFFECT_CMD_SET_PARAM cmdSize %d pCmdData %p, *replySize %d, " \
322 "pReplyData %p", cmdSize, pCmdData, *replySize, pReplyData);
323 if (pCmdData == NULL || (cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t)))
324 || pReplyData == NULL || *replySize != (int)sizeof(int32_t)) {
325 return -EINVAL;
326 }
327 effect_param_t *cmd = (effect_param_t *) pCmdData;
328 *(int *)pReplyData = Downmix_setParameter(pDownmixer, *(int32_t *)cmd->data,
329 cmd->vsize, cmd->data + sizeof(int32_t));
330 break;
331
332 case EFFECT_CMD_SET_PARAM_DEFERRED:
333 //FIXME implement
334 ALOGW("Downmix_Command command EFFECT_CMD_SET_PARAM_DEFERRED not supported, FIXME");
335 break;
336
337 case EFFECT_CMD_SET_PARAM_COMMIT:
338 //FIXME implement
339 ALOGW("Downmix_Command command EFFECT_CMD_SET_PARAM_COMMIT not supported, FIXME");
340 break;
341
342 case EFFECT_CMD_ENABLE:
343 if (pReplyData == NULL || *replySize != sizeof(int)) {
344 return -EINVAL;
345 }
346 if (pDownmixer->state != DOWNMIX_STATE_INITIALIZED) {
347 return -ENOSYS;
348 }
349 pDownmixer->state = DOWNMIX_STATE_ACTIVE;
350 ALOGV("EFFECT_CMD_ENABLE() OK");
351 *(int *)pReplyData = 0;
352 break;
353
354 case EFFECT_CMD_DISABLE:
355 if (pReplyData == NULL || *replySize != sizeof(int)) {
356 return -EINVAL;
357 }
358 if (pDownmixer->state != DOWNMIX_STATE_ACTIVE) {
359 return -ENOSYS;
360 }
361 pDownmixer->state = DOWNMIX_STATE_INITIALIZED;
362 ALOGV("EFFECT_CMD_DISABLE() OK");
363 *(int *)pReplyData = 0;
364 break;
365
366 case EFFECT_CMD_SET_DEVICE:
367 if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) {
368 return -EINVAL;
369 }
370 // FIXME change type if playing on headset vs speaker
371 ALOGV("Downmix_Command EFFECT_CMD_SET_DEVICE: 0x%08x", *(uint32_t *)pCmdData);
372 break;
373
374 case EFFECT_CMD_SET_VOLUME: {
375 // audio output is always stereo => 2 channel volumes
376 if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t) * 2) {
377 return -EINVAL;
378 }
379 // FIXME change volume
380 ALOGW("Downmix_Command command EFFECT_CMD_SET_VOLUME not supported, FIXME");
381 float left = (float)(*(uint32_t *)pCmdData) / (1 << 24);
382 float right = (float)(*((uint32_t *)pCmdData + 1)) / (1 << 24);
383 ALOGV("Downmix_Command EFFECT_CMD_SET_VOLUME: left %f, right %f ", left, right);
384 break;
385 }
386
387 case EFFECT_CMD_SET_AUDIO_MODE:
388 if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) {
389 return -EINVAL;
390 }
391 ALOGV("Downmix_Command EFFECT_CMD_SET_AUDIO_MODE: %d", *(uint32_t *)pCmdData);
392 break;
393
394 case EFFECT_CMD_SET_CONFIG_REVERSE:
395 case EFFECT_CMD_SET_INPUT_DEVICE:
396 // these commands are ignored by a downmix effect
397 break;
398
399 default:
400 ALOGW("Downmix_Command invalid command %d",cmdCode);
401 return -EINVAL;
402 }
403
404 return 0;
405}
406
407
408int Downmix_GetDescriptor(effect_handle_t self, effect_descriptor_t *pDescriptor)
409{
410 downmix_module_t *pDwnmxModule = (downmix_module_t *) self;
411
412 if (pDwnmxModule == NULL ||
413 pDwnmxModule->context.state == DOWNMIX_STATE_UNINITIALIZED) {
414 return -EINVAL;
415 }
416
417 memcpy(pDescriptor, &gDownmixDescriptor, sizeof(effect_descriptor_t));
418
419 return 0;
420}
421
422
423/*----------------------------------------------------------------------------
424 * Downmix internal functions
425 *--------------------------------------------------------------------------*/
426
427/*----------------------------------------------------------------------------
428 * Downmix_Init()
429 *----------------------------------------------------------------------------
430 * Purpose:
431 * Initialize downmix context and apply default parameters
432 *
433 * Inputs:
434 * pDwmModule pointer to downmix effect module
435 *
436 * Outputs:
437 *
438 * Returns:
439 * 0 indicates success
440 *
441 * Side Effects:
442 * updates:
443 * pDwmModule->context.type
444 * pDwmModule->context.apply_volume_correction
445 * pDwmModule->config.inputCfg
446 * pDwmModule->config.outputCfg
447 * pDwmModule->config.inputCfg.samplingRate
448 * pDwmModule->config.outputCfg.samplingRate
449 * pDwmModule->context.state
450 * doesn't set:
451 * pDwmModule->itfe
452 *
453 *----------------------------------------------------------------------------
454 */
455
456int Downmix_Init(downmix_module_t *pDwmModule) {
457
458 ALOGV("Downmix_Init module %p", pDwmModule);
459 int ret = 0;
460
461 memset(&pDwmModule->context, 0, sizeof(downmix_object_t));
462
463 pDwmModule->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
464 pDwmModule->config.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
465 pDwmModule->config.inputCfg.channels = AUDIO_CHANNEL_OUT_7POINT1;
466 pDwmModule->config.inputCfg.bufferProvider.getBuffer = NULL;
467 pDwmModule->config.inputCfg.bufferProvider.releaseBuffer = NULL;
468 pDwmModule->config.inputCfg.bufferProvider.cookie = NULL;
469 pDwmModule->config.inputCfg.mask = EFFECT_CONFIG_ALL;
470
471 pDwmModule->config.inputCfg.samplingRate = 44100;
472 pDwmModule->config.outputCfg.samplingRate = pDwmModule->config.inputCfg.samplingRate;
473
474 // set a default value for the access mode, but should be overwritten by caller
475 pDwmModule->config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
476 pDwmModule->config.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
477 pDwmModule->config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
478 pDwmModule->config.outputCfg.bufferProvider.getBuffer = NULL;
479 pDwmModule->config.outputCfg.bufferProvider.releaseBuffer = NULL;
480 pDwmModule->config.outputCfg.bufferProvider.cookie = NULL;
481 pDwmModule->config.outputCfg.mask = EFFECT_CONFIG_ALL;
482
483 ret = Downmix_Configure(pDwmModule, &pDwmModule->config, true);
484 if (ret != 0) {
485 ALOGV("Downmix_Init error %d on module %p", ret, pDwmModule);
486 } else {
487 pDwmModule->context.state = DOWNMIX_STATE_INITIALIZED;
488 }
489
490 return ret;
491}
492
493
494/*----------------------------------------------------------------------------
495 * Downmix_Configure()
496 *----------------------------------------------------------------------------
497 * Purpose:
498 * Set input and output audio configuration.
499 *
500 * Inputs:
501 * pDwmModule pointer to downmix effect module
502 * pConfig pointer to effect_config_t structure containing input
503 * and output audio parameters configuration
504 * init true if called from init function
505 *
506 * Outputs:
507 *
508 * Returns:
509 * 0 indicates success
510 *
511 * Side Effects:
512 *
513 *----------------------------------------------------------------------------
514 */
515
516int Downmix_Configure(downmix_module_t *pDwmModule, effect_config_t *pConfig, bool init) {
517
518 downmix_object_t *pDownmixer = &pDwmModule->context;
519
520 // Check configuration compatibility with build options, and effect capabilities
521 if (pConfig->inputCfg.samplingRate != pConfig->outputCfg.samplingRate
522 || pConfig->outputCfg.channels != DOWNMIX_OUTPUT_CHANNELS
523 || pConfig->inputCfg.format != AUDIO_FORMAT_PCM_16_BIT
524 || pConfig->outputCfg.format != AUDIO_FORMAT_PCM_16_BIT) {
525 ALOGE("Downmix_Configure error: invalid config");
526 return -EINVAL;
527 }
528
529 memcpy(&pDwmModule->config, pConfig, sizeof(effect_config_t));
530
531 if (init) {
532 pDownmixer->type = DOWNMIX_TYPE_FOLD;
533 pDownmixer->apply_volume_correction = false;
534 pDownmixer->input_channel_count = 8; // matches default input of AUDIO_CHANNEL_OUT_7POINT1
535 } else {
536 // when configuring the effect, do not allow a blank channel mask
537 if (pConfig->inputCfg.channels == 0) {
538 ALOGE("Downmix_Configure error: input channel mask can't be 0");
539 return -EINVAL;
540 }
541 pDownmixer->input_channel_count = popcount(pConfig->inputCfg.channels);
542 }
543
544 Downmix_Reset(pDownmixer, init);
545
546 return 0;
547}
548
549
550/*----------------------------------------------------------------------------
551 * Downmix_Reset()
552 *----------------------------------------------------------------------------
553 * Purpose:
554 * Reset internal states.
555 *
556 * Inputs:
557 * pDownmixer pointer to downmix context
558 * init true if called from init function
559 *
560 * Outputs:
561*
562 * Returns:
563 * 0 indicates success
564 *
565 * Side Effects:
566 *
567 *----------------------------------------------------------------------------
568 */
569
570int Downmix_Reset(downmix_object_t *pDownmixer, bool init) {
571 // nothing to do here
572 return 0;
573}
574
575
576/*----------------------------------------------------------------------------
577 * Downmix_setParameter()
578 *----------------------------------------------------------------------------
579 * Purpose:
580 * Set a Downmix parameter
581 *
582 * Inputs:
583 * pDownmixer handle to instance data
584 * param parameter
585 * pValue pointer to parameter value
586 * size value size
587 *
588 * Outputs:
589 *
590 * Returns:
591 * 0 indicates success
592 *
593 * Side Effects:
594 *
595 *----------------------------------------------------------------------------
596 */
597int Downmix_setParameter(downmix_object_t *pDownmixer, int32_t param, size_t size, void *pValue) {
598
599 int16_t value16;
600 ALOGV("Downmix_setParameter, context %p, param %d, value16 %d, value32 %d",
601 pDownmixer, param, *(int16_t *)pValue, *(int32_t *)pValue);
602
603 switch (param) {
604
605 case DOWNMIX_PARAM_TYPE:
606 if (size != sizeof(downmix_type_t)) {
607 ALOGE("Downmix_setParameter(DOWNMIX_PARAM_TYPE) invalid size %d, should be %d",
608 size, sizeof(downmix_type_t));
609 return -EINVAL;
610 }
611 value16 = *(int16_t *)pValue;
612 ALOGV("set DOWNMIX_PARAM_TYPE, type %d", value16);
613 if (!((value16 > DOWNMIX_TYPE_INVALID) && (value16 < DOWNMIX_TYPE_LAST))) {
614 ALOGE("Downmix_setParameter invalid DOWNMIX_PARAM_TYPE value %d", value16);
615 return -EINVAL;
616 } else {
617 pDownmixer->type = (downmix_type_t) value16;
618 break;
619
620 default:
621 ALOGE("Downmix_setParameter unknown parameter %d", param);
622 return -EINVAL;
623 }
624}
625
626 return 0;
627} /* end Downmix_setParameter */
628
629
630/*----------------------------------------------------------------------------
631 * Downmix_getParameter()
632 *----------------------------------------------------------------------------
633 * Purpose:
634 * Get a Downmix parameter
635 *
636 * Inputs:
637 * pDownmixer handle to instance data
638 * param parameter
639 * pValue pointer to variable to hold retrieved value
640 * pSize pointer to value size: maximum size as input
641 *
642 * Outputs:
643 * *pValue updated with parameter value
644 * *pSize updated with actual value size
645 *
646 * Returns:
647 * 0 indicates success
648 *
649 * Side Effects:
650 *
651 *----------------------------------------------------------------------------
652 */
653int Downmix_getParameter(downmix_object_t *pDownmixer, int32_t param, size_t *pSize, void *pValue) {
654 int16_t *pValue16;
655
656 switch (param) {
657
658 case DOWNMIX_PARAM_TYPE:
659 if (*pSize < sizeof(int16_t)) {
660 ALOGE("Downmix_getParameter invalid parameter size %d for DOWNMIX_PARAM_TYPE", *pSize);
661 return -EINVAL;
662 }
663 pValue16 = (int16_t *)pValue;
664 *pValue16 = (int16_t) pDownmixer->type;
665 *pSize = sizeof(int16_t);
666 ALOGV("Downmix_getParameter DOWNMIX_PARAM_TYPE is %d", *pValue16);
667 break;
668
669 default:
670 ALOGE("Downmix_getParameter unknown parameter %d", param);
671 return -EINVAL;
672 }
673
674 return 0;
675} /* end Downmix_getParameter */
676
677
678/*----------------------------------------------------------------------------
679 * Downmix_foldFromQuad()
680 *----------------------------------------------------------------------------
681 * Purpose:
682 * downmix a quad signal to stereo
683 *
684 * Inputs:
685 * pSrc quad audio samples to downmix
686 * numFrames the number of quad frames to downmix
687 *
688 * Outputs:
689 * pDst downmixed stereo audio samples
690 *
691 *----------------------------------------------------------------------------
692 */
693void Downmix_foldFromQuad(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
694 // sample at index 0 is FL
695 // sample at index 1 is FR
696 // sample at index 2 is RL
697 // sample at index 3 is RR
698 if (accumulate) {
699 while (numFrames) {
700 // FL + RL
701 pDst[0] = clamp16(pDst[0] + pSrc[0] + pSrc[2]);
702 // FR + RR
703 pDst[1] = clamp16(pDst[1] + pSrc[1] + pSrc[3]);
704 pSrc += 4;
705 pDst += 2;
706 numFrames--;
707 }
708 } else { // same code as above but without adding and clamping pDst[i] to itself
709 while (numFrames) {
710 // FL + RL
711 pDst[0] = clamp16(pSrc[0] + pSrc[2]);
712 // FR + RR
713 pDst[1] = clamp16(pSrc[1] + pSrc[3]);
714 pSrc += 4;
715 pDst += 2;
716 numFrames--;
717 }
718 }
719}
720
721
722/*----------------------------------------------------------------------------
723 * Downmix_foldFromSurround()
724 *----------------------------------------------------------------------------
725 * Purpose:
726 * downmix a "surround sound" (mono rear) signal to stereo
727 *
728 * Inputs:
729 * pSrc surround signal to downmix
730 * numFrames the number of surround frames to downmix
731 *
732 * Outputs:
733 * pDst downmixed stereo audio samples
734 *
735 *----------------------------------------------------------------------------
736 */
737void Downmix_foldFromSurround(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
738 int32_t lt, rt, centerPlusRearContrib; // samples in Q19.12 format
739 // sample at index 0 is FL
740 // sample at index 1 is FR
741 // sample at index 2 is FC
742 // sample at index 3 is RC
743 if (accumulate) {
744 while (numFrames) {
745 // centerPlusRearContrib = FC(-3dB) + RC(-3dB)
746 centerPlusRearContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12) + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
747 // FL + centerPlusRearContrib
748 lt = (pSrc[0] << 12) + centerPlusRearContrib;
749 // FR + centerPlusRearContrib
750 rt = (pSrc[1] << 12) + centerPlusRearContrib;
751 pDst[0] = clamp16(pDst[0] + (lt >> 12));
752 pDst[1] = clamp16(pDst[1] + (rt >> 12));
753 pSrc += 4;
754 pDst += 2;
755 numFrames--;
756 }
757 } else { // same code as above but without adding and clamping pDst[i] to itself
758 while (numFrames) {
759 // centerPlusRearContrib = FC(-3dB) + RC(-3dB)
760 centerPlusRearContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12) + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
761 // FL + centerPlusRearContrib
762 lt = (pSrc[0] << 12) + centerPlusRearContrib;
763 // FR + centerPlusRearContrib
764 rt = (pSrc[1] << 12) + centerPlusRearContrib;
765 pDst[0] = clamp16(lt >> 12);
766 pDst[1] = clamp16(rt >> 12);
767 pSrc += 4;
768 pDst += 2;
769 numFrames--;
770 }
771 }
772}
773
774
775/*----------------------------------------------------------------------------
776 * Downmix_foldFrom5Point1()
777 *----------------------------------------------------------------------------
778 * Purpose:
779 * downmix a 5.1 signal to stereo
780 *
781 * Inputs:
782 * pSrc 5.1 audio samples to downmix
783 * numFrames the number of 5.1 frames to downmix
784 *
785 * Outputs:
786 * pDst downmixed stereo audio samples
787 *
788 *----------------------------------------------------------------------------
789 */
790void Downmix_foldFrom5Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
791 int32_t lt, rt, centerPlusLfeContrib; // samples in Q19.12 format
792 // sample at index 0 is FL
793 // sample at index 1 is FR
794 // sample at index 2 is FC
795 // sample at index 3 is LFE
796 // sample at index 4 is RL
797 // sample at index 5 is RR
798 if (accumulate) {
799 while (numFrames) {
800 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
801 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
802 + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
803 // FL + centerPlusLfeContrib + RL
804 lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[4] << 12);
805 // FR + centerPlusLfeContrib + RR
806 rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[5] << 12);
807 pDst[0] = clamp16(pDst[0] + (lt >> 12));
808 pDst[1] = clamp16(pDst[1] + (rt >> 12));
809 pSrc += 6;
810 pDst += 2;
811 numFrames--;
812 }
813 } else { // same code as above but without adding and clamping pDst[i] to itself
814 while (numFrames) {
815 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
816 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
817 + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
818 // FL + centerPlusLfeContrib + RL
819 lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[4] << 12);
820 // FR + centerPlusLfeContrib + RR
821 rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[5] << 12);
822 pDst[0] = clamp16(lt >> 12);
823 pDst[1] = clamp16(rt >> 12);
824 pSrc += 6;
825 pDst += 2;
826 numFrames--;
827 }
828 }
829}
830
831
832/*----------------------------------------------------------------------------
833 * Downmix_foldFrom7Point1()
834 *----------------------------------------------------------------------------
835 * Purpose:
836 * downmix a 7.1 signal to stereo
837 *
838 * Inputs:
839 * pSrc 7.1 audio samples to downmix
840 * numFrames the number of 7.1 frames to downmix
841 *
842 * Outputs:
843 * pDst downmixed stereo audio samples
844 *
845 *----------------------------------------------------------------------------
846 */
847void Downmix_foldFrom7Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
848 int32_t lt, rt, centerPlusLfeContrib; // samples in Q19.12 format
849 // sample at index 0 is FL
850 // sample at index 1 is FR
851 // sample at index 2 is FC
852 // sample at index 3 is LFE
853 // sample at index 4 is RL
854 // sample at index 5 is RR
855 // sample at index 6 is SL
856 // sample at index 7 is SR
857 if (accumulate) {
858 while (numFrames) {
859 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
860 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
861 + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
862 // FL + centerPlusLfeContrib + SL + RL
863 lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[6] << 12) + (pSrc[4] << 12);
864 // FR + centerPlusLfeContrib + SR + RR
865 rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[7] << 12) + (pSrc[5] << 12);
866 pDst[0] = clamp16(lt >> 12);
867 pDst[1] = clamp16(rt >> 12);
868 pSrc += 8;
869 pDst += 2;
870 numFrames--;
871 }
872 } else { // same code as above but without adding and clamping pDst[i] to itself
873 while (numFrames) {
874 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
875 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
876 + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
877 // FL + centerPlusLfeContrib + SL + RL
878 lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[6] << 12) + (pSrc[4] << 12);
879 // FR + centerPlusLfeContrib + SR + RR
880 rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[7] << 12) + (pSrc[5] << 12);
881 pDst[0] = clamp16(pDst[0] + (lt >> 12));
882 pDst[1] = clamp16(pDst[1] + (rt >> 12));
883 pSrc += 8;
884 pDst += 2;
885 numFrames--;
886 }
887 }
888}
889