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