blob: 366a78b6e731c11dda45f7202a8327d44adbdb7c [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
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -070025// Do not submit with DOWNMIX_TEST_CHANNEL_INDEX defined, strictly for testing
26//#define DOWNMIX_TEST_CHANNEL_INDEX 0
27// Do not submit with DOWNMIX_ALWAYS_USE_GENERIC_DOWNMIXER defined, strictly for testing
28//#define DOWNMIX_ALWAYS_USE_GENERIC_DOWNMIXER 0
29
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -080030#define MINUS_3_DB_IN_Q19_12 2896 // -3dB = 0.707 * 2^12 = 2896
31
Jean-Michel Trividb46b482012-04-23 11:29:26 -070032typedef enum {
33 CHANNEL_MASK_SURROUND = AUDIO_CHANNEL_OUT_SURROUND,
34 CHANNEL_MASK_QUAD_BACK = AUDIO_CHANNEL_OUT_QUAD,
35 // like AUDIO_CHANNEL_OUT_QUAD with *_SIDE_* instead of *_BACK_*, same channel order
36 CHANNEL_MASK_QUAD_SIDE =
37 AUDIO_CHANNEL_OUT_FRONT_LEFT |
38 AUDIO_CHANNEL_OUT_FRONT_RIGHT |
39 AUDIO_CHANNEL_OUT_SIDE_LEFT |
40 AUDIO_CHANNEL_OUT_SIDE_RIGHT,
41 CHANNEL_MASK_5POINT1_BACK = AUDIO_CHANNEL_OUT_5POINT1,
42 // like AUDIO_CHANNEL_OUT_5POINT1 with *_SIDE_* instead of *_BACK_*, same channel order
43 CHANNEL_MASK_5POINT1_SIDE =
44 AUDIO_CHANNEL_OUT_FRONT_LEFT |
45 AUDIO_CHANNEL_OUT_FRONT_RIGHT |
46 AUDIO_CHANNEL_OUT_FRONT_CENTER |
47 AUDIO_CHANNEL_OUT_LOW_FREQUENCY |
48 AUDIO_CHANNEL_OUT_SIDE_LEFT |
49 AUDIO_CHANNEL_OUT_SIDE_RIGHT,
50 CHANNEL_MASK_7POINT1_SIDE_BACK = AUDIO_CHANNEL_OUT_7POINT1,
51} downmix_input_channel_mask_t;
52
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -080053// effect_handle_t interface implementation for downmix effect
54const struct effect_interface_s gDownmixInterface = {
55 Downmix_Process,
56 Downmix_Command,
57 Downmix_GetDescriptor,
58 NULL /* no process_reverse function, no reference stream needed */
59};
60
61audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
62 tag : AUDIO_EFFECT_LIBRARY_TAG,
63 version : EFFECT_LIBRARY_API_VERSION,
64 name : "Downmix Library",
65 implementor : "The Android Open Source Project",
66 query_num_effects : DownmixLib_QueryNumberEffects,
67 query_effect : DownmixLib_QueryEffect,
68 create_effect : DownmixLib_Create,
69 release_effect : DownmixLib_Release,
70 get_descriptor : DownmixLib_GetDescriptor,
71};
72
73
74// AOSP insert downmix UUID: 93f04452-e4fe-41cc-91f9-e475b6d1d69f
75static const effect_descriptor_t gDownmixDescriptor = {
76 EFFECT_UIID_DOWNMIX__, //type
77 {0x93f04452, 0xe4fe, 0x41cc, 0x91f9, {0xe4, 0x75, 0xb6, 0xd1, 0xd6, 0x9f}}, // uuid
78 EFFECT_CONTROL_API_VERSION,
79 EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST,
80 0, //FIXME what value should be reported? // cpu load
81 0, //FIXME what value should be reported? // memory usage
82 "Multichannel Downmix To Stereo", // human readable effect name
83 "The Android Open Source Project" // human readable effect implementor name
84};
85
86// gDescriptors contains pointers to all defined effect descriptor in this library
87static const effect_descriptor_t * const gDescriptors[] = {
88 &gDownmixDescriptor
89};
90
91// number of effects in this library
92const int kNbEffects = sizeof(gDescriptors) / sizeof(const effect_descriptor_t *);
93
94
95/*----------------------------------------------------------------------------
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -070096 * Test code
97 *--------------------------------------------------------------------------*/
98#ifdef DOWNMIX_TEST_CHANNEL_INDEX
99// strictly for testing, logs the indices of the channels for a given mask,
100// uses the same code as Downmix_foldGeneric()
101void Downmix_testIndexComputation(uint32_t mask) {
102 ALOGI("Testing index computation for 0x%x:", mask);
103 // check against unsupported channels
104 if (mask & kUnsupported) {
105 ALOGE("Unsupported channels (top or front left/right of center)");
106 return;
107 }
108 // verify has FL/FR
109 if ((mask & AUDIO_CHANNEL_OUT_STEREO) != AUDIO_CHANNEL_OUT_STEREO) {
110 ALOGE("Front channels must be present");
111 return;
112 }
113 // verify uses SIDE as a pair (ok if not using SIDE at all)
114 bool hasSides = false;
115 if ((mask & kSides) != 0) {
116 if ((mask & kSides) != kSides) {
117 ALOGE("Side channels must be used as a pair");
118 return;
119 }
120 hasSides = true;
121 }
122 // verify uses BACK as a pair (ok if not using BACK at all)
123 bool hasBacks = false;
124 if ((mask & kBacks) != 0) {
125 if ((mask & kBacks) != kBacks) {
126 ALOGE("Back channels must be used as a pair");
127 return;
128 }
129 hasBacks = true;
130 }
131
132 const int numChan = popcount(mask);
133 const bool hasFC = ((mask & AUDIO_CHANNEL_OUT_FRONT_CENTER) == AUDIO_CHANNEL_OUT_FRONT_CENTER);
134 const bool hasLFE =
135 ((mask & AUDIO_CHANNEL_OUT_LOW_FREQUENCY) == AUDIO_CHANNEL_OUT_LOW_FREQUENCY);
136 const bool hasBC = ((mask & AUDIO_CHANNEL_OUT_BACK_CENTER) == AUDIO_CHANNEL_OUT_BACK_CENTER);
137 // compute at what index each channel is: samples will be in the following order:
138 // FL FR FC LFE BL BR BC SL SR
139 // when a channel is not present, its index is set to the same as the index of the preceding
140 // channel
141 const int indexFC = hasFC ? 2 : 1; // front center
142 const int indexLFE = hasLFE ? indexFC + 1 : indexFC; // low frequency
143 const int indexBL = hasBacks ? indexLFE + 1 : indexLFE; // back left
144 const int indexBR = hasBacks ? indexBL + 1 : indexBL; // back right
145 const int indexBC = hasBC ? indexBR + 1 : indexBR; // back center
146 const int indexSL = hasSides ? indexBC + 1 : indexBC; // side left
147 const int indexSR = hasSides ? indexSL + 1 : indexSL; // side right
148
149 ALOGI(" FL FR FC LFE BL BR BC SL SR");
150 ALOGI(" %d %d %d %d %d %d %d %d %d",
151 0, 1, indexFC, indexLFE, indexBL, indexBR, indexBC, indexSL, indexSR);
152}
153#endif
154
155
156/*----------------------------------------------------------------------------
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800157 * Effect API implementation
158 *--------------------------------------------------------------------------*/
159
160/*--- Effect Library Interface Implementation ---*/
161
162int32_t DownmixLib_QueryNumberEffects(uint32_t *pNumEffects) {
163 ALOGV("DownmixLib_QueryNumberEffects()");
164 *pNumEffects = kNbEffects;
165 return 0;
166}
167
168int32_t DownmixLib_QueryEffect(uint32_t index, effect_descriptor_t *pDescriptor) {
169 ALOGV("DownmixLib_QueryEffect() index=%d", index);
170 if (pDescriptor == NULL) {
171 return -EINVAL;
172 }
173 if (index >= (uint32_t)kNbEffects) {
174 return -EINVAL;
175 }
176 memcpy(pDescriptor, gDescriptors[index], sizeof(effect_descriptor_t));
177 return 0;
178}
179
180
181int32_t DownmixLib_Create(const effect_uuid_t *uuid,
182 int32_t sessionId,
183 int32_t ioId,
184 effect_handle_t *pHandle) {
185 int ret;
186 int i;
187 downmix_module_t *module;
188 const effect_descriptor_t *desc;
189
190 ALOGV("DownmixLib_Create()");
191
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700192#ifdef DOWNMIX_TEST_CHANNEL_INDEX
193 // should work (won't log an error)
194 ALOGI("DOWNMIX_TEST_CHANNEL_INDEX: should work:");
195 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT |
196 AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_BACK_CENTER);
197 Downmix_testIndexComputation(CHANNEL_MASK_QUAD_SIDE | CHANNEL_MASK_QUAD_BACK);
198 Downmix_testIndexComputation(CHANNEL_MASK_5POINT1_SIDE | AUDIO_CHANNEL_OUT_BACK_CENTER);
199 Downmix_testIndexComputation(CHANNEL_MASK_5POINT1_BACK | AUDIO_CHANNEL_OUT_BACK_CENTER);
200 // shouldn't work (will log an error, won't display channel indices)
201 ALOGI("DOWNMIX_TEST_CHANNEL_INDEX: should NOT work:");
202 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT |
203 AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_BACK_LEFT);
204 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT |
205 AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_SIDE_LEFT);
206 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT |
207 AUDIO_CHANNEL_OUT_BACK_LEFT | AUDIO_CHANNEL_OUT_BACK_RIGHT);
208 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT |
209 AUDIO_CHANNEL_OUT_SIDE_LEFT | AUDIO_CHANNEL_OUT_SIDE_RIGHT);
210#endif
211
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800212 if (pHandle == NULL || uuid == NULL) {
213 return -EINVAL;
214 }
215
216 for (i = 0 ; i < kNbEffects ; i++) {
217 desc = gDescriptors[i];
218 if (memcmp(uuid, &desc->uuid, sizeof(effect_uuid_t)) == 0) {
219 break;
220 }
221 }
222
223 if (i == kNbEffects) {
224 return -ENOENT;
225 }
226
227 module = malloc(sizeof(downmix_module_t));
228
229 module->itfe = &gDownmixInterface;
230
231 module->context.state = DOWNMIX_STATE_UNINITIALIZED;
232
233 ret = Downmix_Init(module);
234 if (ret < 0) {
235 ALOGW("DownmixLib_Create() init failed");
236 free(module);
237 return ret;
238 }
239
240 *pHandle = (effect_handle_t) module;
241
242 ALOGV("DownmixLib_Create() %p , size %d", module, sizeof(downmix_module_t));
243
244 return 0;
245}
246
247
248int32_t DownmixLib_Release(effect_handle_t handle) {
249 downmix_module_t *pDwmModule = (downmix_module_t *)handle;
250
251 ALOGV("DownmixLib_Release() %p", handle);
252 if (handle == NULL) {
253 return -EINVAL;
254 }
255
256 pDwmModule->context.state = DOWNMIX_STATE_UNINITIALIZED;
257
258 free(pDwmModule);
259 return 0;
260}
261
262
263int32_t DownmixLib_GetDescriptor(const effect_uuid_t *uuid, effect_descriptor_t *pDescriptor) {
264 ALOGV("DownmixLib_GetDescriptor()");
265 int i;
266
267 if (pDescriptor == NULL || uuid == NULL){
268 ALOGE("DownmixLib_Create() called with NULL pointer");
269 return -EINVAL;
270 }
271 ALOGV("DownmixLib_GetDescriptor() nb effects=%d", kNbEffects);
272 for (i = 0; i < kNbEffects; i++) {
273 ALOGV("DownmixLib_GetDescriptor() i=%d", i);
274 if (memcmp(uuid, &gDescriptors[i]->uuid, sizeof(effect_uuid_t)) == 0) {
275 memcpy(pDescriptor, gDescriptors[i], sizeof(effect_descriptor_t));
276 ALOGV("EffectGetDescriptor - UUID matched downmix type %d, UUID = %x",
277 i, gDescriptors[i]->uuid.timeLow);
278 return 0;
279 }
280 }
281
282 return -EINVAL;
283}
284
285
286/*--- Effect Control Interface Implementation ---*/
287
288static int Downmix_Process(effect_handle_t self,
289 audio_buffer_t *inBuffer, audio_buffer_t *outBuffer) {
290
291 downmix_object_t *pDownmixer;
292 int16_t *pSrc, *pDst;
293 downmix_module_t *pDwmModule = (downmix_module_t *)self;
294
295 if (pDwmModule == NULL) {
296 return -EINVAL;
297 }
298
299 if (inBuffer == NULL || inBuffer->raw == NULL ||
300 outBuffer == NULL || outBuffer->raw == NULL ||
301 inBuffer->frameCount != outBuffer->frameCount) {
302 return -EINVAL;
303 }
304
305 pDownmixer = (downmix_object_t*) &pDwmModule->context;
306
307 if (pDownmixer->state == DOWNMIX_STATE_UNINITIALIZED) {
308 ALOGE("Downmix_Process error: trying to use an uninitialized downmixer");
309 return -EINVAL;
310 } else if (pDownmixer->state == DOWNMIX_STATE_INITIALIZED) {
311 ALOGE("Downmix_Process error: trying to use a non-configured downmixer");
312 return -ENODATA;
313 }
314
315 pSrc = inBuffer->s16;
316 pDst = outBuffer->s16;
317 size_t numFrames = outBuffer->frameCount;
318
319 const bool accumulate =
320 (pDwmModule->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700321 const uint32_t downmixInputChannelMask = pDwmModule->config.inputCfg.channels;
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800322
323 switch(pDownmixer->type) {
324
325 case DOWNMIX_TYPE_STRIP:
326 if (accumulate) {
327 while (numFrames) {
328 pDst[0] = clamp16(pDst[0] + pSrc[0]);
329 pDst[1] = clamp16(pDst[1] + pSrc[1]);
330 pSrc += pDownmixer->input_channel_count;
331 pDst += 2;
332 numFrames--;
333 }
334 } else {
335 while (numFrames) {
336 pDst[0] = pSrc[0];
337 pDst[1] = pSrc[1];
338 pSrc += pDownmixer->input_channel_count;
339 pDst += 2;
340 numFrames--;
341 }
342 }
343 break;
344
345 case DOWNMIX_TYPE_FOLD:
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700346#ifdef DOWNMIX_ALWAYS_USE_GENERIC_DOWNMIXER
347 // bypass the optimized downmix routines for the common formats
348 if (!Downmix_foldGeneric(
349 downmixInputChannelMask, pSrc, pDst, numFrames, accumulate)) {
350 ALOGE("Multichannel configuration 0x%x is not supported", downmixInputChannelMask);
351 return -EINVAL;
352 }
353 break;
354#endif
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800355 // optimize for the common formats
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700356 switch((downmix_input_channel_mask_t)downmixInputChannelMask) {
Jean-Michel Trividb46b482012-04-23 11:29:26 -0700357 case CHANNEL_MASK_QUAD_BACK:
358 case CHANNEL_MASK_QUAD_SIDE:
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800359 Downmix_foldFromQuad(pSrc, pDst, numFrames, accumulate);
360 break;
Jean-Michel Trividb46b482012-04-23 11:29:26 -0700361 case CHANNEL_MASK_SURROUND:
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800362 Downmix_foldFromSurround(pSrc, pDst, numFrames, accumulate);
363 break;
Jean-Michel Trividb46b482012-04-23 11:29:26 -0700364 case CHANNEL_MASK_5POINT1_BACK:
365 case CHANNEL_MASK_5POINT1_SIDE:
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800366 Downmix_foldFrom5Point1(pSrc, pDst, numFrames, accumulate);
367 break;
Jean-Michel Trividb46b482012-04-23 11:29:26 -0700368 case CHANNEL_MASK_7POINT1_SIDE_BACK:
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800369 Downmix_foldFrom7Point1(pSrc, pDst, numFrames, accumulate);
370 break;
371 default:
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700372 if (!Downmix_foldGeneric(
373 downmixInputChannelMask, pSrc, pDst, numFrames, accumulate)) {
374 ALOGE("Multichannel configuration 0x%x is not supported", downmixInputChannelMask);
375 return -EINVAL;
376 }
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800377 break;
378 }
379 break;
380
381 default:
382 return -EINVAL;
383 }
384
385 return 0;
386}
387
388
389static int Downmix_Command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
390 void *pCmdData, uint32_t *replySize, void *pReplyData) {
391
392 downmix_module_t *pDwmModule = (downmix_module_t *) self;
393 downmix_object_t *pDownmixer;
394 int retsize;
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 %d cmdSize %d",cmdCode, cmdSize);
403
404 switch (cmdCode) {
405 case EFFECT_CMD_INIT:
406 if (pReplyData == 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 != 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 %d, pReplyData: %p",
427 pCmdData, *replySize, pReplyData);
428 if (pCmdData == NULL || cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t)) ||
429 pReplyData == 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 %d, replySize %d",
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 %d, " \
444 "pReplyData %p", cmdSize, pCmdData, *replySize, pReplyData);
445 if (pCmdData == NULL || (cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t)))
446 || pReplyData == 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 != 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 != 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%08x", *(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: %d", *(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 %d",cmdCode);
523 return -EINVAL;
524 }
525
526 return 0;
527}
528
529
530int 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
578int 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
638int 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
Chih-Wei Huanga93c8c92013-01-10 20:52:57 +0800651 if (&pDwmModule->config != pConfig) {
652 memcpy(&pDwmModule->config, pConfig, sizeof(effect_config_t));
653 }
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800654
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 channel mask
661 if (pConfig->inputCfg.channels == 0) {
662 ALOGE("Downmix_Configure error: input channel mask can't be 0");
663 return -EINVAL;
664 }
665 pDownmixer->input_channel_count = popcount(pConfig->inputCfg.channels);
666 }
667
668 Downmix_Reset(pDownmixer, init);
669
670 return 0;
671}
672
673
674/*----------------------------------------------------------------------------
675 * Downmix_Reset()
676 *----------------------------------------------------------------------------
677 * Purpose:
678 * Reset internal states.
679 *
680 * Inputs:
681 * pDownmixer pointer to downmix context
682 * init true if called from init function
683 *
684 * Outputs:
685*
686 * Returns:
687 * 0 indicates success
688 *
689 * Side Effects:
690 *
691 *----------------------------------------------------------------------------
692 */
693
694int Downmix_Reset(downmix_object_t *pDownmixer, bool init) {
695 // nothing to do here
696 return 0;
697}
698
699
700/*----------------------------------------------------------------------------
701 * Downmix_setParameter()
702 *----------------------------------------------------------------------------
703 * Purpose:
704 * Set a Downmix parameter
705 *
706 * Inputs:
707 * pDownmixer handle to instance data
708 * param parameter
709 * pValue pointer to parameter value
710 * size value size
711 *
712 * Outputs:
713 *
714 * Returns:
715 * 0 indicates success
716 *
717 * Side Effects:
718 *
719 *----------------------------------------------------------------------------
720 */
721int Downmix_setParameter(downmix_object_t *pDownmixer, int32_t param, size_t size, void *pValue) {
722
723 int16_t value16;
724 ALOGV("Downmix_setParameter, context %p, param %d, value16 %d, value32 %d",
725 pDownmixer, param, *(int16_t *)pValue, *(int32_t *)pValue);
726
727 switch (param) {
728
729 case DOWNMIX_PARAM_TYPE:
730 if (size != sizeof(downmix_type_t)) {
731 ALOGE("Downmix_setParameter(DOWNMIX_PARAM_TYPE) invalid size %d, should be %d",
732 size, sizeof(downmix_type_t));
733 return -EINVAL;
734 }
735 value16 = *(int16_t *)pValue;
736 ALOGV("set DOWNMIX_PARAM_TYPE, type %d", value16);
Jean-Michel Trivi7d5b2622012-04-04 18:54:36 -0700737 if (!((value16 > DOWNMIX_TYPE_INVALID) && (value16 <= DOWNMIX_TYPE_LAST))) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800738 ALOGE("Downmix_setParameter invalid DOWNMIX_PARAM_TYPE value %d", value16);
739 return -EINVAL;
740 } else {
741 pDownmixer->type = (downmix_type_t) value16;
742 break;
743
744 default:
745 ALOGE("Downmix_setParameter unknown parameter %d", param);
746 return -EINVAL;
747 }
748}
749
750 return 0;
751} /* end Downmix_setParameter */
752
753
754/*----------------------------------------------------------------------------
755 * Downmix_getParameter()
756 *----------------------------------------------------------------------------
757 * Purpose:
758 * Get a Downmix parameter
759 *
760 * Inputs:
761 * pDownmixer handle to instance data
762 * param parameter
763 * pValue pointer to variable to hold retrieved value
764 * pSize pointer to value size: maximum size as input
765 *
766 * Outputs:
767 * *pValue updated with parameter value
768 * *pSize updated with actual value size
769 *
770 * Returns:
771 * 0 indicates success
772 *
773 * Side Effects:
774 *
775 *----------------------------------------------------------------------------
776 */
777int Downmix_getParameter(downmix_object_t *pDownmixer, int32_t param, size_t *pSize, void *pValue) {
778 int16_t *pValue16;
779
780 switch (param) {
781
782 case DOWNMIX_PARAM_TYPE:
783 if (*pSize < sizeof(int16_t)) {
784 ALOGE("Downmix_getParameter invalid parameter size %d for DOWNMIX_PARAM_TYPE", *pSize);
785 return -EINVAL;
786 }
787 pValue16 = (int16_t *)pValue;
788 *pValue16 = (int16_t) pDownmixer->type;
789 *pSize = sizeof(int16_t);
790 ALOGV("Downmix_getParameter DOWNMIX_PARAM_TYPE is %d", *pValue16);
791 break;
792
793 default:
794 ALOGE("Downmix_getParameter unknown parameter %d", param);
795 return -EINVAL;
796 }
797
798 return 0;
799} /* end Downmix_getParameter */
800
801
802/*----------------------------------------------------------------------------
803 * Downmix_foldFromQuad()
804 *----------------------------------------------------------------------------
805 * Purpose:
806 * downmix a quad signal to stereo
807 *
808 * Inputs:
809 * pSrc quad audio samples to downmix
810 * numFrames the number of quad frames to downmix
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700811 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
812 * or overwrite pDst (when false)
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800813 *
814 * Outputs:
815 * pDst downmixed stereo audio samples
816 *
817 *----------------------------------------------------------------------------
818 */
819void Downmix_foldFromQuad(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
820 // sample at index 0 is FL
821 // sample at index 1 is FR
822 // sample at index 2 is RL
823 // sample at index 3 is RR
824 if (accumulate) {
825 while (numFrames) {
826 // FL + RL
Jean-Michel Triviaea27152012-05-24 10:11:07 -0700827 pDst[0] = clamp16(pDst[0] + ((pSrc[0] + pSrc[2]) >> 1));
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800828 // FR + RR
Jean-Michel Triviaea27152012-05-24 10:11:07 -0700829 pDst[1] = clamp16(pDst[1] + ((pSrc[1] + pSrc[3]) >> 1));
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800830 pSrc += 4;
831 pDst += 2;
832 numFrames--;
833 }
834 } else { // same code as above but without adding and clamping pDst[i] to itself
835 while (numFrames) {
836 // FL + RL
Jean-Michel Triviaea27152012-05-24 10:11:07 -0700837 pDst[0] = clamp16((pSrc[0] + pSrc[2]) >> 1);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800838 // FR + RR
Jean-Michel Triviaea27152012-05-24 10:11:07 -0700839 pDst[1] = clamp16((pSrc[1] + pSrc[3]) >> 1);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800840 pSrc += 4;
841 pDst += 2;
842 numFrames--;
843 }
844 }
845}
846
847
848/*----------------------------------------------------------------------------
849 * Downmix_foldFromSurround()
850 *----------------------------------------------------------------------------
851 * Purpose:
852 * downmix a "surround sound" (mono rear) signal to stereo
853 *
854 * Inputs:
855 * pSrc surround signal to downmix
856 * numFrames the number of surround frames to downmix
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700857 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
858 * or overwrite pDst (when false)
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800859 *
860 * Outputs:
861 * pDst downmixed stereo audio samples
862 *
863 *----------------------------------------------------------------------------
864 */
865void Downmix_foldFromSurround(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
866 int32_t lt, rt, centerPlusRearContrib; // samples in Q19.12 format
867 // sample at index 0 is FL
868 // sample at index 1 is FR
869 // sample at index 2 is FC
870 // sample at index 3 is RC
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700871 // code is mostly duplicated between the two values of accumulate to avoid repeating the test
872 // for every sample
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800873 if (accumulate) {
874 while (numFrames) {
875 // centerPlusRearContrib = FC(-3dB) + RC(-3dB)
876 centerPlusRearContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12) + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
877 // FL + centerPlusRearContrib
878 lt = (pSrc[0] << 12) + centerPlusRearContrib;
879 // FR + centerPlusRearContrib
880 rt = (pSrc[1] << 12) + centerPlusRearContrib;
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700881 // accumulate in destination
Jean-Michel Triviaea27152012-05-24 10:11:07 -0700882 pDst[0] = clamp16(pDst[0] + (lt >> 13));
883 pDst[1] = clamp16(pDst[1] + (rt >> 13));
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800884 pSrc += 4;
885 pDst += 2;
886 numFrames--;
887 }
888 } else { // same code as above but without adding and clamping pDst[i] to itself
889 while (numFrames) {
890 // centerPlusRearContrib = FC(-3dB) + RC(-3dB)
891 centerPlusRearContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12) + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
892 // FL + centerPlusRearContrib
893 lt = (pSrc[0] << 12) + centerPlusRearContrib;
894 // FR + centerPlusRearContrib
895 rt = (pSrc[1] << 12) + centerPlusRearContrib;
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700896 // store in destination
Jean-Michel Triviaea27152012-05-24 10:11:07 -0700897 pDst[0] = clamp16(lt >> 13); // differs from when accumulate is true above
898 pDst[1] = clamp16(rt >> 13); // differs from when accumulate is true above
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800899 pSrc += 4;
900 pDst += 2;
901 numFrames--;
902 }
903 }
904}
905
906
907/*----------------------------------------------------------------------------
908 * Downmix_foldFrom5Point1()
909 *----------------------------------------------------------------------------
910 * Purpose:
911 * downmix a 5.1 signal to stereo
912 *
913 * Inputs:
914 * pSrc 5.1 audio samples to downmix
915 * numFrames the number of 5.1 frames to downmix
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700916 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
917 * or overwrite pDst (when false)
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800918 *
919 * Outputs:
920 * pDst downmixed stereo audio samples
921 *
922 *----------------------------------------------------------------------------
923 */
924void Downmix_foldFrom5Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
925 int32_t lt, rt, centerPlusLfeContrib; // samples in Q19.12 format
926 // sample at index 0 is FL
927 // sample at index 1 is FR
928 // sample at index 2 is FC
929 // sample at index 3 is LFE
930 // sample at index 4 is RL
931 // sample at index 5 is RR
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700932 // code is mostly duplicated between the two values of accumulate to avoid repeating the test
933 // for every sample
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800934 if (accumulate) {
935 while (numFrames) {
936 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
937 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
938 + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
939 // FL + centerPlusLfeContrib + RL
940 lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[4] << 12);
941 // FR + centerPlusLfeContrib + RR
942 rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[5] << 12);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700943 // accumulate in destination
Jean-Michel Triviaea27152012-05-24 10:11:07 -0700944 pDst[0] = clamp16(pDst[0] + (lt >> 13));
945 pDst[1] = clamp16(pDst[1] + (rt >> 13));
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800946 pSrc += 6;
947 pDst += 2;
948 numFrames--;
949 }
950 } else { // same code as above but without adding and clamping pDst[i] to itself
951 while (numFrames) {
952 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
953 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
954 + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
955 // FL + centerPlusLfeContrib + RL
956 lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[4] << 12);
957 // FR + centerPlusLfeContrib + RR
958 rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[5] << 12);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700959 // store in destination
Jean-Michel Triviaea27152012-05-24 10:11:07 -0700960 pDst[0] = clamp16(lt >> 13); // differs from when accumulate is true above
961 pDst[1] = clamp16(rt >> 13); // differs from when accumulate is true above
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800962 pSrc += 6;
963 pDst += 2;
964 numFrames--;
965 }
966 }
967}
968
969
970/*----------------------------------------------------------------------------
971 * Downmix_foldFrom7Point1()
972 *----------------------------------------------------------------------------
973 * Purpose:
974 * downmix a 7.1 signal to stereo
975 *
976 * Inputs:
977 * pSrc 7.1 audio samples to downmix
978 * numFrames the number of 7.1 frames to downmix
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700979 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
980 * or overwrite pDst (when false)
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800981 *
982 * Outputs:
983 * pDst downmixed stereo audio samples
984 *
985 *----------------------------------------------------------------------------
986 */
987void Downmix_foldFrom7Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
988 int32_t lt, rt, centerPlusLfeContrib; // samples in Q19.12 format
989 // sample at index 0 is FL
990 // sample at index 1 is FR
991 // sample at index 2 is FC
992 // sample at index 3 is LFE
993 // sample at index 4 is RL
994 // sample at index 5 is RR
995 // sample at index 6 is SL
996 // sample at index 7 is SR
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700997 // code is mostly duplicated between the two values of accumulate to avoid repeating the test
998 // for every sample
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800999 if (accumulate) {
1000 while (numFrames) {
1001 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
1002 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
1003 + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
1004 // FL + centerPlusLfeContrib + SL + RL
1005 lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[6] << 12) + (pSrc[4] << 12);
1006 // FR + centerPlusLfeContrib + SR + RR
1007 rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[7] << 12) + (pSrc[5] << 12);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -07001008 //accumulate in destination
Jean-Michel Triviaea27152012-05-24 10:11:07 -07001009 pDst[0] = clamp16(pDst[0] + (lt >> 13));
1010 pDst[1] = clamp16(pDst[1] + (rt >> 13));
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -08001011 pSrc += 8;
1012 pDst += 2;
1013 numFrames--;
1014 }
1015 } else { // same code as above but without adding and clamping pDst[i] to itself
1016 while (numFrames) {
1017 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
1018 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
1019 + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
1020 // FL + centerPlusLfeContrib + SL + RL
1021 lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[6] << 12) + (pSrc[4] << 12);
1022 // FR + centerPlusLfeContrib + SR + RR
1023 rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[7] << 12) + (pSrc[5] << 12);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -07001024 // store in destination
Jean-Michel Triviaea27152012-05-24 10:11:07 -07001025 pDst[0] = clamp16(lt >> 13); // differs from when accumulate is true above
1026 pDst[1] = clamp16(rt >> 13); // differs from when accumulate is true above
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -08001027 pSrc += 8;
1028 pDst += 2;
1029 numFrames--;
1030 }
1031 }
1032}
1033
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -07001034
1035/*----------------------------------------------------------------------------
1036 * Downmix_foldGeneric()
1037 *----------------------------------------------------------------------------
1038 * Purpose:
1039 * downmix to stereo a multichannel signal whose format is:
1040 * - has FL/FR
1041 * - if using AUDIO_CHANNEL_OUT_SIDE*, it contains both left and right
1042 * - if using AUDIO_CHANNEL_OUT_BACK*, it contains both left and right
1043 * - doesn't use any of the AUDIO_CHANNEL_OUT_TOP* channels
1044 * - doesn't use any of the AUDIO_CHANNEL_OUT_FRONT_*_OF_CENTER channels
1045 * Only handles channel masks not enumerated in downmix_input_channel_mask_t
1046 *
1047 * Inputs:
1048 * mask the channel mask of pSrc
1049 * pSrc multichannel audio buffer to downmix
1050 * numFrames the number of multichannel frames to downmix
1051 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
1052 * or overwrite pDst (when false)
1053 *
1054 * Outputs:
1055 * pDst downmixed stereo audio samples
1056 *
1057 * Returns: false if multichannel format is not supported
1058 *
1059 *----------------------------------------------------------------------------
1060 */
1061bool Downmix_foldGeneric(
1062 uint32_t mask, int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
1063 // check against unsupported channels
1064 if (mask & kUnsupported) {
1065 ALOGE("Unsupported channels (top or front left/right of center)");
1066 return false;
1067 }
1068 // verify has FL/FR
1069 if ((mask & AUDIO_CHANNEL_OUT_STEREO) != AUDIO_CHANNEL_OUT_STEREO) {
1070 ALOGE("Front channels must be present");
1071 return false;
1072 }
1073 // verify uses SIDE as a pair (ok if not using SIDE at all)
1074 bool hasSides = false;
1075 if ((mask & kSides) != 0) {
1076 if ((mask & kSides) != kSides) {
1077 ALOGE("Side channels must be used as a pair");
1078 return false;
1079 }
1080 hasSides = true;
1081 }
1082 // verify uses BACK as a pair (ok if not using BACK at all)
1083 bool hasBacks = false;
1084 if ((mask & kBacks) != 0) {
1085 if ((mask & kBacks) != kBacks) {
1086 ALOGE("Back channels must be used as a pair");
1087 return false;
1088 }
1089 hasBacks = true;
1090 }
1091
1092 const int numChan = popcount(mask);
1093 const bool hasFC = ((mask & AUDIO_CHANNEL_OUT_FRONT_CENTER) == AUDIO_CHANNEL_OUT_FRONT_CENTER);
1094 const bool hasLFE =
1095 ((mask & AUDIO_CHANNEL_OUT_LOW_FREQUENCY) == AUDIO_CHANNEL_OUT_LOW_FREQUENCY);
1096 const bool hasBC = ((mask & AUDIO_CHANNEL_OUT_BACK_CENTER) == AUDIO_CHANNEL_OUT_BACK_CENTER);
1097 // compute at what index each channel is: samples will be in the following order:
1098 // FL FR FC LFE BL BR BC SL SR
1099 // when a channel is not present, its index is set to the same as the index of the preceding
1100 // channel
1101 const int indexFC = hasFC ? 2 : 1; // front center
1102 const int indexLFE = hasLFE ? indexFC + 1 : indexFC; // low frequency
1103 const int indexBL = hasBacks ? indexLFE + 1 : indexLFE; // back left
1104 const int indexBR = hasBacks ? indexBL + 1 : indexBL; // back right
1105 const int indexBC = hasBC ? indexBR + 1 : indexBR; // back center
1106 const int indexSL = hasSides ? indexBC + 1 : indexBC; // side left
1107 const int indexSR = hasSides ? indexSL + 1 : indexSL; // side right
1108
1109 int32_t lt, rt, centersLfeContrib; // samples in Q19.12 format
1110 // code is mostly duplicated between the two values of accumulate to avoid repeating the test
1111 // for every sample
1112 if (accumulate) {
1113 while (numFrames) {
1114 // compute contribution of FC, BC and LFE
1115 centersLfeContrib = 0;
1116 if (hasFC) { centersLfeContrib += pSrc[indexFC]; }
1117 if (hasLFE) { centersLfeContrib += pSrc[indexLFE]; }
1118 if (hasBC) { centersLfeContrib += pSrc[indexBC]; }
1119 centersLfeContrib *= MINUS_3_DB_IN_Q19_12;
1120 // always has FL/FR
1121 lt = (pSrc[0] << 12);
1122 rt = (pSrc[1] << 12);
1123 // mix in sides and backs
1124 if (hasSides) {
1125 lt += pSrc[indexSL] << 12;
1126 rt += pSrc[indexSR] << 12;
1127 }
1128 if (hasBacks) {
1129 lt += pSrc[indexBL] << 12;
1130 rt += pSrc[indexBR] << 12;
1131 }
1132 lt += centersLfeContrib;
1133 rt += centersLfeContrib;
1134 // accumulate in destination
Jean-Michel Triviaea27152012-05-24 10:11:07 -07001135 pDst[0] = clamp16(pDst[0] + (lt >> 13));
1136 pDst[1] = clamp16(pDst[1] + (rt >> 13));
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -07001137 pSrc += numChan;
1138 pDst += 2;
1139 numFrames--;
1140 }
1141 } else {
1142 while (numFrames) {
1143 // compute contribution of FC, BC and LFE
1144 centersLfeContrib = 0;
1145 if (hasFC) { centersLfeContrib += pSrc[indexFC]; }
1146 if (hasLFE) { centersLfeContrib += pSrc[indexLFE]; }
1147 if (hasBC) { centersLfeContrib += pSrc[indexBC]; }
1148 centersLfeContrib *= MINUS_3_DB_IN_Q19_12;
1149 // always has FL/FR
1150 lt = (pSrc[0] << 12);
1151 rt = (pSrc[1] << 12);
1152 // mix in sides and backs
1153 if (hasSides) {
1154 lt += pSrc[indexSL] << 12;
1155 rt += pSrc[indexSR] << 12;
1156 }
1157 if (hasBacks) {
1158 lt += pSrc[indexBL] << 12;
1159 rt += pSrc[indexBR] << 12;
1160 }
1161 lt += centersLfeContrib;
1162 rt += centersLfeContrib;
1163 // store in destination
Jean-Michel Triviaea27152012-05-24 10:11:07 -07001164 pDst[0] = clamp16(lt >> 13); // differs from when accumulate is true above
1165 pDst[1] = clamp16(rt >> 13); // differs from when accumulate is true above
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -07001166 pSrc += numChan;
1167 pDst += 2;
1168 numFrames--;
1169 }
1170 }
1171 return true;
1172}