blob: c825970ddc9daaf757399bb16dc5910c8f28cf48 [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
Marco Nelissen655604a2012-10-25 16:05:57 -070061// This is the only symbol that needs to be exported
62__attribute__ ((visibility ("default")))
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -080063audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
64 tag : AUDIO_EFFECT_LIBRARY_TAG,
65 version : EFFECT_LIBRARY_API_VERSION,
66 name : "Downmix Library",
67 implementor : "The Android Open Source Project",
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -080068 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
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800162int32_t DownmixLib_Create(const effect_uuid_t *uuid,
Eric Laurent29b83cb2015-06-19 15:33:57 -0700163 int32_t sessionId __unused,
164 int32_t ioId __unused,
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800165 effect_handle_t *pHandle) {
166 int ret;
167 int i;
168 downmix_module_t *module;
169 const effect_descriptor_t *desc;
170
171 ALOGV("DownmixLib_Create()");
172
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700173#ifdef DOWNMIX_TEST_CHANNEL_INDEX
174 // should work (won't log an error)
175 ALOGI("DOWNMIX_TEST_CHANNEL_INDEX: should work:");
176 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT |
177 AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_BACK_CENTER);
178 Downmix_testIndexComputation(CHANNEL_MASK_QUAD_SIDE | CHANNEL_MASK_QUAD_BACK);
179 Downmix_testIndexComputation(CHANNEL_MASK_5POINT1_SIDE | AUDIO_CHANNEL_OUT_BACK_CENTER);
180 Downmix_testIndexComputation(CHANNEL_MASK_5POINT1_BACK | AUDIO_CHANNEL_OUT_BACK_CENTER);
181 // shouldn't work (will log an error, won't display channel indices)
182 ALOGI("DOWNMIX_TEST_CHANNEL_INDEX: should NOT work:");
183 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT |
184 AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_BACK_LEFT);
185 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT |
186 AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_SIDE_LEFT);
187 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT |
188 AUDIO_CHANNEL_OUT_BACK_LEFT | AUDIO_CHANNEL_OUT_BACK_RIGHT);
189 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT |
190 AUDIO_CHANNEL_OUT_SIDE_LEFT | AUDIO_CHANNEL_OUT_SIDE_RIGHT);
191#endif
192
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800193 if (pHandle == NULL || uuid == NULL) {
194 return -EINVAL;
195 }
196
197 for (i = 0 ; i < kNbEffects ; i++) {
198 desc = gDescriptors[i];
199 if (memcmp(uuid, &desc->uuid, sizeof(effect_uuid_t)) == 0) {
200 break;
201 }
202 }
203
204 if (i == kNbEffects) {
205 return -ENOENT;
206 }
207
208 module = malloc(sizeof(downmix_module_t));
209
210 module->itfe = &gDownmixInterface;
211
212 module->context.state = DOWNMIX_STATE_UNINITIALIZED;
213
214 ret = Downmix_Init(module);
215 if (ret < 0) {
216 ALOGW("DownmixLib_Create() init failed");
217 free(module);
218 return ret;
219 }
220
221 *pHandle = (effect_handle_t) module;
222
223 ALOGV("DownmixLib_Create() %p , size %d", module, sizeof(downmix_module_t));
224
225 return 0;
226}
227
228
229int32_t DownmixLib_Release(effect_handle_t handle) {
230 downmix_module_t *pDwmModule = (downmix_module_t *)handle;
231
232 ALOGV("DownmixLib_Release() %p", handle);
233 if (handle == NULL) {
234 return -EINVAL;
235 }
236
237 pDwmModule->context.state = DOWNMIX_STATE_UNINITIALIZED;
238
239 free(pDwmModule);
240 return 0;
241}
242
243
244int32_t DownmixLib_GetDescriptor(const effect_uuid_t *uuid, effect_descriptor_t *pDescriptor) {
245 ALOGV("DownmixLib_GetDescriptor()");
246 int i;
247
248 if (pDescriptor == NULL || uuid == NULL){
249 ALOGE("DownmixLib_Create() called with NULL pointer");
250 return -EINVAL;
251 }
252 ALOGV("DownmixLib_GetDescriptor() nb effects=%d", kNbEffects);
253 for (i = 0; i < kNbEffects; i++) {
254 ALOGV("DownmixLib_GetDescriptor() i=%d", i);
255 if (memcmp(uuid, &gDescriptors[i]->uuid, sizeof(effect_uuid_t)) == 0) {
256 memcpy(pDescriptor, gDescriptors[i], sizeof(effect_descriptor_t));
257 ALOGV("EffectGetDescriptor - UUID matched downmix type %d, UUID = %x",
258 i, gDescriptors[i]->uuid.timeLow);
259 return 0;
260 }
261 }
262
263 return -EINVAL;
264}
265
266
267/*--- Effect Control Interface Implementation ---*/
268
269static int Downmix_Process(effect_handle_t self,
270 audio_buffer_t *inBuffer, audio_buffer_t *outBuffer) {
271
272 downmix_object_t *pDownmixer;
273 int16_t *pSrc, *pDst;
274 downmix_module_t *pDwmModule = (downmix_module_t *)self;
275
276 if (pDwmModule == NULL) {
277 return -EINVAL;
278 }
279
280 if (inBuffer == NULL || inBuffer->raw == NULL ||
281 outBuffer == NULL || outBuffer->raw == NULL ||
282 inBuffer->frameCount != outBuffer->frameCount) {
283 return -EINVAL;
284 }
285
286 pDownmixer = (downmix_object_t*) &pDwmModule->context;
287
288 if (pDownmixer->state == DOWNMIX_STATE_UNINITIALIZED) {
289 ALOGE("Downmix_Process error: trying to use an uninitialized downmixer");
290 return -EINVAL;
291 } else if (pDownmixer->state == DOWNMIX_STATE_INITIALIZED) {
292 ALOGE("Downmix_Process error: trying to use a non-configured downmixer");
293 return -ENODATA;
294 }
295
296 pSrc = inBuffer->s16;
297 pDst = outBuffer->s16;
298 size_t numFrames = outBuffer->frameCount;
299
300 const bool accumulate =
301 (pDwmModule->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700302 const uint32_t downmixInputChannelMask = pDwmModule->config.inputCfg.channels;
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800303
304 switch(pDownmixer->type) {
305
306 case DOWNMIX_TYPE_STRIP:
307 if (accumulate) {
308 while (numFrames) {
309 pDst[0] = clamp16(pDst[0] + pSrc[0]);
310 pDst[1] = clamp16(pDst[1] + pSrc[1]);
311 pSrc += pDownmixer->input_channel_count;
312 pDst += 2;
313 numFrames--;
314 }
315 } else {
316 while (numFrames) {
317 pDst[0] = pSrc[0];
318 pDst[1] = pSrc[1];
319 pSrc += pDownmixer->input_channel_count;
320 pDst += 2;
321 numFrames--;
322 }
323 }
324 break;
325
326 case DOWNMIX_TYPE_FOLD:
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700327#ifdef DOWNMIX_ALWAYS_USE_GENERIC_DOWNMIXER
328 // bypass the optimized downmix routines for the common formats
329 if (!Downmix_foldGeneric(
330 downmixInputChannelMask, pSrc, pDst, numFrames, accumulate)) {
331 ALOGE("Multichannel configuration 0x%x is not supported", downmixInputChannelMask);
332 return -EINVAL;
333 }
334 break;
335#endif
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800336 // optimize for the common formats
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700337 switch((downmix_input_channel_mask_t)downmixInputChannelMask) {
Jean-Michel Trividb46b482012-04-23 11:29:26 -0700338 case CHANNEL_MASK_QUAD_BACK:
339 case CHANNEL_MASK_QUAD_SIDE:
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800340 Downmix_foldFromQuad(pSrc, pDst, numFrames, accumulate);
341 break;
Jean-Michel Trividb46b482012-04-23 11:29:26 -0700342 case CHANNEL_MASK_SURROUND:
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800343 Downmix_foldFromSurround(pSrc, pDst, numFrames, accumulate);
344 break;
Jean-Michel Trividb46b482012-04-23 11:29:26 -0700345 case CHANNEL_MASK_5POINT1_BACK:
346 case CHANNEL_MASK_5POINT1_SIDE:
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800347 Downmix_foldFrom5Point1(pSrc, pDst, numFrames, accumulate);
348 break;
Jean-Michel Trividb46b482012-04-23 11:29:26 -0700349 case CHANNEL_MASK_7POINT1_SIDE_BACK:
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800350 Downmix_foldFrom7Point1(pSrc, pDst, numFrames, accumulate);
351 break;
352 default:
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700353 if (!Downmix_foldGeneric(
354 downmixInputChannelMask, pSrc, pDst, numFrames, accumulate)) {
355 ALOGE("Multichannel configuration 0x%x is not supported", downmixInputChannelMask);
356 return -EINVAL;
357 }
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800358 break;
359 }
360 break;
361
362 default:
363 return -EINVAL;
364 }
365
366 return 0;
367}
368
369
370static int Downmix_Command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
371 void *pCmdData, uint32_t *replySize, void *pReplyData) {
372
373 downmix_module_t *pDwmModule = (downmix_module_t *) self;
374 downmix_object_t *pDownmixer;
375 int retsize;
376
377 if (pDwmModule == NULL || pDwmModule->context.state == DOWNMIX_STATE_UNINITIALIZED) {
378 return -EINVAL;
379 }
380
381 pDownmixer = (downmix_object_t*) &pDwmModule->context;
382
383 ALOGV("Downmix_Command command %d cmdSize %d",cmdCode, cmdSize);
384
385 switch (cmdCode) {
386 case EFFECT_CMD_INIT:
Eric Laurent29b83cb2015-06-19 15:33:57 -0700387 if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800388 return -EINVAL;
389 }
390 *(int *) pReplyData = Downmix_Init(pDwmModule);
391 break;
392
393 case EFFECT_CMD_SET_CONFIG:
394 if (pCmdData == NULL || cmdSize != sizeof(effect_config_t)
Eric Laurent29b83cb2015-06-19 15:33:57 -0700395 || pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800396 return -EINVAL;
397 }
398 *(int *) pReplyData = Downmix_Configure(pDwmModule,
399 (effect_config_t *)pCmdData, false);
400 break;
401
402 case EFFECT_CMD_RESET:
403 Downmix_Reset(pDownmixer, false);
404 break;
405
406 case EFFECT_CMD_GET_PARAM:
407 ALOGV("Downmix_Command EFFECT_CMD_GET_PARAM pCmdData %p, *replySize %d, pReplyData: %p",
408 pCmdData, *replySize, pReplyData);
409 if (pCmdData == NULL || cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t)) ||
Eric Laurent29b83cb2015-06-19 15:33:57 -0700410 pReplyData == NULL || replySize == NULL ||
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800411 *replySize < (int) sizeof(effect_param_t) + 2 * sizeof(int32_t)) {
412 return -EINVAL;
413 }
414 effect_param_t *rep = (effect_param_t *) pReplyData;
415 memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + sizeof(int32_t));
416 ALOGV("Downmix_Command EFFECT_CMD_GET_PARAM param %d, replySize %d",
417 *(int32_t *)rep->data, rep->vsize);
418 rep->status = Downmix_getParameter(pDownmixer, *(int32_t *)rep->data, &rep->vsize,
419 rep->data + sizeof(int32_t));
420 *replySize = sizeof(effect_param_t) + sizeof(int32_t) + rep->vsize;
421 break;
422
423 case EFFECT_CMD_SET_PARAM:
424 ALOGV("Downmix_Command EFFECT_CMD_SET_PARAM cmdSize %d pCmdData %p, *replySize %d, " \
425 "pReplyData %p", cmdSize, pCmdData, *replySize, pReplyData);
426 if (pCmdData == NULL || (cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t)))
Eric Laurent29b83cb2015-06-19 15:33:57 -0700427 || pReplyData == NULL || replySize == NULL || *replySize != (int)sizeof(int32_t)) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800428 return -EINVAL;
429 }
430 effect_param_t *cmd = (effect_param_t *) pCmdData;
Mikhail Naganov804632a2017-07-24 17:25:47 -0700431 if (cmd->psize != sizeof(int32_t)) {
432 android_errorWriteLog(0x534e4554, "63662938");
433 return -EINVAL;
434 }
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800435 *(int *)pReplyData = Downmix_setParameter(pDownmixer, *(int32_t *)cmd->data,
436 cmd->vsize, cmd->data + sizeof(int32_t));
437 break;
438
439 case EFFECT_CMD_SET_PARAM_DEFERRED:
440 //FIXME implement
441 ALOGW("Downmix_Command command EFFECT_CMD_SET_PARAM_DEFERRED not supported, FIXME");
442 break;
443
444 case EFFECT_CMD_SET_PARAM_COMMIT:
445 //FIXME implement
446 ALOGW("Downmix_Command command EFFECT_CMD_SET_PARAM_COMMIT not supported, FIXME");
447 break;
448
449 case EFFECT_CMD_ENABLE:
Eric Laurent29b83cb2015-06-19 15:33:57 -0700450 if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800451 return -EINVAL;
452 }
453 if (pDownmixer->state != DOWNMIX_STATE_INITIALIZED) {
454 return -ENOSYS;
455 }
456 pDownmixer->state = DOWNMIX_STATE_ACTIVE;
457 ALOGV("EFFECT_CMD_ENABLE() OK");
458 *(int *)pReplyData = 0;
459 break;
460
461 case EFFECT_CMD_DISABLE:
Eric Laurent29b83cb2015-06-19 15:33:57 -0700462 if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800463 return -EINVAL;
464 }
465 if (pDownmixer->state != DOWNMIX_STATE_ACTIVE) {
466 return -ENOSYS;
467 }
468 pDownmixer->state = DOWNMIX_STATE_INITIALIZED;
469 ALOGV("EFFECT_CMD_DISABLE() OK");
470 *(int *)pReplyData = 0;
471 break;
472
473 case EFFECT_CMD_SET_DEVICE:
474 if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) {
475 return -EINVAL;
476 }
477 // FIXME change type if playing on headset vs speaker
478 ALOGV("Downmix_Command EFFECT_CMD_SET_DEVICE: 0x%08x", *(uint32_t *)pCmdData);
479 break;
480
481 case EFFECT_CMD_SET_VOLUME: {
482 // audio output is always stereo => 2 channel volumes
483 if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t) * 2) {
484 return -EINVAL;
485 }
486 // FIXME change volume
487 ALOGW("Downmix_Command command EFFECT_CMD_SET_VOLUME not supported, FIXME");
488 float left = (float)(*(uint32_t *)pCmdData) / (1 << 24);
489 float right = (float)(*((uint32_t *)pCmdData + 1)) / (1 << 24);
490 ALOGV("Downmix_Command EFFECT_CMD_SET_VOLUME: left %f, right %f ", left, right);
491 break;
492 }
493
494 case EFFECT_CMD_SET_AUDIO_MODE:
495 if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) {
496 return -EINVAL;
497 }
498 ALOGV("Downmix_Command EFFECT_CMD_SET_AUDIO_MODE: %d", *(uint32_t *)pCmdData);
499 break;
500
501 case EFFECT_CMD_SET_CONFIG_REVERSE:
502 case EFFECT_CMD_SET_INPUT_DEVICE:
503 // these commands are ignored by a downmix effect
504 break;
505
506 default:
507 ALOGW("Downmix_Command invalid command %d",cmdCode);
508 return -EINVAL;
509 }
510
511 return 0;
512}
513
514
515int Downmix_GetDescriptor(effect_handle_t self, effect_descriptor_t *pDescriptor)
516{
517 downmix_module_t *pDwnmxModule = (downmix_module_t *) self;
518
519 if (pDwnmxModule == NULL ||
520 pDwnmxModule->context.state == DOWNMIX_STATE_UNINITIALIZED) {
521 return -EINVAL;
522 }
523
524 memcpy(pDescriptor, &gDownmixDescriptor, sizeof(effect_descriptor_t));
525
526 return 0;
527}
528
529
530/*----------------------------------------------------------------------------
531 * Downmix internal functions
532 *--------------------------------------------------------------------------*/
533
534/*----------------------------------------------------------------------------
535 * Downmix_Init()
536 *----------------------------------------------------------------------------
537 * Purpose:
538 * Initialize downmix context and apply default parameters
539 *
540 * Inputs:
541 * pDwmModule pointer to downmix effect module
542 *
543 * Outputs:
544 *
545 * Returns:
546 * 0 indicates success
547 *
548 * Side Effects:
549 * updates:
550 * pDwmModule->context.type
551 * pDwmModule->context.apply_volume_correction
552 * pDwmModule->config.inputCfg
553 * pDwmModule->config.outputCfg
554 * pDwmModule->config.inputCfg.samplingRate
555 * pDwmModule->config.outputCfg.samplingRate
556 * pDwmModule->context.state
557 * doesn't set:
558 * pDwmModule->itfe
559 *
560 *----------------------------------------------------------------------------
561 */
562
563int Downmix_Init(downmix_module_t *pDwmModule) {
564
565 ALOGV("Downmix_Init module %p", pDwmModule);
566 int ret = 0;
567
568 memset(&pDwmModule->context, 0, sizeof(downmix_object_t));
569
570 pDwmModule->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
571 pDwmModule->config.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
572 pDwmModule->config.inputCfg.channels = AUDIO_CHANNEL_OUT_7POINT1;
573 pDwmModule->config.inputCfg.bufferProvider.getBuffer = NULL;
574 pDwmModule->config.inputCfg.bufferProvider.releaseBuffer = NULL;
575 pDwmModule->config.inputCfg.bufferProvider.cookie = NULL;
576 pDwmModule->config.inputCfg.mask = EFFECT_CONFIG_ALL;
577
578 pDwmModule->config.inputCfg.samplingRate = 44100;
579 pDwmModule->config.outputCfg.samplingRate = pDwmModule->config.inputCfg.samplingRate;
580
581 // set a default value for the access mode, but should be overwritten by caller
582 pDwmModule->config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
583 pDwmModule->config.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
584 pDwmModule->config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
585 pDwmModule->config.outputCfg.bufferProvider.getBuffer = NULL;
586 pDwmModule->config.outputCfg.bufferProvider.releaseBuffer = NULL;
587 pDwmModule->config.outputCfg.bufferProvider.cookie = NULL;
588 pDwmModule->config.outputCfg.mask = EFFECT_CONFIG_ALL;
589
590 ret = Downmix_Configure(pDwmModule, &pDwmModule->config, true);
591 if (ret != 0) {
592 ALOGV("Downmix_Init error %d on module %p", ret, pDwmModule);
593 } else {
594 pDwmModule->context.state = DOWNMIX_STATE_INITIALIZED;
595 }
596
597 return ret;
598}
599
600
601/*----------------------------------------------------------------------------
602 * Downmix_Configure()
603 *----------------------------------------------------------------------------
604 * Purpose:
605 * Set input and output audio configuration.
606 *
607 * Inputs:
608 * pDwmModule pointer to downmix effect module
609 * pConfig pointer to effect_config_t structure containing input
610 * and output audio parameters configuration
611 * init true if called from init function
612 *
613 * Outputs:
614 *
615 * Returns:
616 * 0 indicates success
617 *
618 * Side Effects:
619 *
620 *----------------------------------------------------------------------------
621 */
622
623int Downmix_Configure(downmix_module_t *pDwmModule, effect_config_t *pConfig, bool init) {
624
625 downmix_object_t *pDownmixer = &pDwmModule->context;
626
627 // Check configuration compatibility with build options, and effect capabilities
628 if (pConfig->inputCfg.samplingRate != pConfig->outputCfg.samplingRate
629 || pConfig->outputCfg.channels != DOWNMIX_OUTPUT_CHANNELS
630 || pConfig->inputCfg.format != AUDIO_FORMAT_PCM_16_BIT
631 || pConfig->outputCfg.format != AUDIO_FORMAT_PCM_16_BIT) {
632 ALOGE("Downmix_Configure error: invalid config");
633 return -EINVAL;
634 }
635
636 memcpy(&pDwmModule->config, pConfig, sizeof(effect_config_t));
637
638 if (init) {
639 pDownmixer->type = DOWNMIX_TYPE_FOLD;
640 pDownmixer->apply_volume_correction = false;
641 pDownmixer->input_channel_count = 8; // matches default input of AUDIO_CHANNEL_OUT_7POINT1
642 } else {
643 // when configuring the effect, do not allow a blank channel mask
644 if (pConfig->inputCfg.channels == 0) {
645 ALOGE("Downmix_Configure error: input channel mask can't be 0");
646 return -EINVAL;
647 }
648 pDownmixer->input_channel_count = popcount(pConfig->inputCfg.channels);
649 }
650
651 Downmix_Reset(pDownmixer, init);
652
653 return 0;
654}
655
656
657/*----------------------------------------------------------------------------
658 * Downmix_Reset()
659 *----------------------------------------------------------------------------
660 * Purpose:
661 * Reset internal states.
662 *
663 * Inputs:
664 * pDownmixer pointer to downmix context
665 * init true if called from init function
666 *
667 * Outputs:
668*
669 * Returns:
670 * 0 indicates success
671 *
672 * Side Effects:
673 *
674 *----------------------------------------------------------------------------
675 */
676
Eric Laurent29b83cb2015-06-19 15:33:57 -0700677int Downmix_Reset(downmix_object_t *pDownmixer __unused, bool init __unused) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800678 // nothing to do here
679 return 0;
680}
681
682
683/*----------------------------------------------------------------------------
684 * Downmix_setParameter()
685 *----------------------------------------------------------------------------
686 * Purpose:
687 * Set a Downmix parameter
688 *
689 * Inputs:
690 * pDownmixer handle to instance data
691 * param parameter
692 * pValue pointer to parameter value
693 * size value size
694 *
695 * Outputs:
696 *
697 * Returns:
698 * 0 indicates success
699 *
700 * Side Effects:
701 *
702 *----------------------------------------------------------------------------
703 */
704int Downmix_setParameter(downmix_object_t *pDownmixer, int32_t param, size_t size, void *pValue) {
705
706 int16_t value16;
707 ALOGV("Downmix_setParameter, context %p, param %d, value16 %d, value32 %d",
708 pDownmixer, param, *(int16_t *)pValue, *(int32_t *)pValue);
709
710 switch (param) {
711
712 case DOWNMIX_PARAM_TYPE:
713 if (size != sizeof(downmix_type_t)) {
714 ALOGE("Downmix_setParameter(DOWNMIX_PARAM_TYPE) invalid size %d, should be %d",
715 size, sizeof(downmix_type_t));
716 return -EINVAL;
717 }
718 value16 = *(int16_t *)pValue;
719 ALOGV("set DOWNMIX_PARAM_TYPE, type %d", value16);
Jean-Michel Trivi7d5b2622012-04-04 18:54:36 -0700720 if (!((value16 > DOWNMIX_TYPE_INVALID) && (value16 <= DOWNMIX_TYPE_LAST))) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800721 ALOGE("Downmix_setParameter invalid DOWNMIX_PARAM_TYPE value %d", value16);
722 return -EINVAL;
723 } else {
724 pDownmixer->type = (downmix_type_t) value16;
725 break;
726
727 default:
728 ALOGE("Downmix_setParameter unknown parameter %d", param);
729 return -EINVAL;
730 }
731}
732
733 return 0;
734} /* end Downmix_setParameter */
735
736
737/*----------------------------------------------------------------------------
738 * Downmix_getParameter()
739 *----------------------------------------------------------------------------
740 * Purpose:
741 * Get a Downmix parameter
742 *
743 * Inputs:
744 * pDownmixer handle to instance data
745 * param parameter
746 * pValue pointer to variable to hold retrieved value
747 * pSize pointer to value size: maximum size as input
748 *
749 * Outputs:
750 * *pValue updated with parameter value
751 * *pSize updated with actual value size
752 *
753 * Returns:
754 * 0 indicates success
755 *
756 * Side Effects:
757 *
758 *----------------------------------------------------------------------------
759 */
760int Downmix_getParameter(downmix_object_t *pDownmixer, int32_t param, size_t *pSize, void *pValue) {
761 int16_t *pValue16;
762
763 switch (param) {
764
765 case DOWNMIX_PARAM_TYPE:
766 if (*pSize < sizeof(int16_t)) {
767 ALOGE("Downmix_getParameter invalid parameter size %d for DOWNMIX_PARAM_TYPE", *pSize);
768 return -EINVAL;
769 }
770 pValue16 = (int16_t *)pValue;
771 *pValue16 = (int16_t) pDownmixer->type;
772 *pSize = sizeof(int16_t);
773 ALOGV("Downmix_getParameter DOWNMIX_PARAM_TYPE is %d", *pValue16);
774 break;
775
776 default:
777 ALOGE("Downmix_getParameter unknown parameter %d", param);
778 return -EINVAL;
779 }
780
781 return 0;
782} /* end Downmix_getParameter */
783
784
785/*----------------------------------------------------------------------------
786 * Downmix_foldFromQuad()
787 *----------------------------------------------------------------------------
788 * Purpose:
789 * downmix a quad signal to stereo
790 *
791 * Inputs:
792 * pSrc quad audio samples to downmix
793 * numFrames the number of quad frames to downmix
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700794 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
795 * or overwrite pDst (when false)
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800796 *
797 * Outputs:
798 * pDst downmixed stereo audio samples
799 *
800 *----------------------------------------------------------------------------
801 */
802void Downmix_foldFromQuad(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
803 // sample at index 0 is FL
804 // sample at index 1 is FR
805 // sample at index 2 is RL
806 // sample at index 3 is RR
807 if (accumulate) {
808 while (numFrames) {
809 // FL + RL
Jean-Michel Triviaea27152012-05-24 10:11:07 -0700810 pDst[0] = clamp16(pDst[0] + ((pSrc[0] + pSrc[2]) >> 1));
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800811 // FR + RR
Jean-Michel Triviaea27152012-05-24 10:11:07 -0700812 pDst[1] = clamp16(pDst[1] + ((pSrc[1] + pSrc[3]) >> 1));
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800813 pSrc += 4;
814 pDst += 2;
815 numFrames--;
816 }
817 } else { // same code as above but without adding and clamping pDst[i] to itself
818 while (numFrames) {
819 // FL + RL
Jean-Michel Triviaea27152012-05-24 10:11:07 -0700820 pDst[0] = clamp16((pSrc[0] + pSrc[2]) >> 1);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800821 // FR + RR
Jean-Michel Triviaea27152012-05-24 10:11:07 -0700822 pDst[1] = clamp16((pSrc[1] + pSrc[3]) >> 1);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800823 pSrc += 4;
824 pDst += 2;
825 numFrames--;
826 }
827 }
828}
829
830
831/*----------------------------------------------------------------------------
832 * Downmix_foldFromSurround()
833 *----------------------------------------------------------------------------
834 * Purpose:
835 * downmix a "surround sound" (mono rear) signal to stereo
836 *
837 * Inputs:
838 * pSrc surround signal to downmix
839 * numFrames the number of surround frames to downmix
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700840 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
841 * or overwrite pDst (when false)
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800842 *
843 * Outputs:
844 * pDst downmixed stereo audio samples
845 *
846 *----------------------------------------------------------------------------
847 */
848void Downmix_foldFromSurround(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
849 int32_t lt, rt, centerPlusRearContrib; // samples in Q19.12 format
850 // sample at index 0 is FL
851 // sample at index 1 is FR
852 // sample at index 2 is FC
853 // sample at index 3 is RC
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700854 // code is mostly duplicated between the two values of accumulate to avoid repeating the test
855 // for every sample
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800856 if (accumulate) {
857 while (numFrames) {
858 // centerPlusRearContrib = FC(-3dB) + RC(-3dB)
859 centerPlusRearContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12) + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
860 // FL + centerPlusRearContrib
861 lt = (pSrc[0] << 12) + centerPlusRearContrib;
862 // FR + centerPlusRearContrib
863 rt = (pSrc[1] << 12) + centerPlusRearContrib;
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700864 // accumulate in destination
Jean-Michel Triviaea27152012-05-24 10:11:07 -0700865 pDst[0] = clamp16(pDst[0] + (lt >> 13));
866 pDst[1] = clamp16(pDst[1] + (rt >> 13));
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800867 pSrc += 4;
868 pDst += 2;
869 numFrames--;
870 }
871 } else { // same code as above but without adding and clamping pDst[i] to itself
872 while (numFrames) {
873 // centerPlusRearContrib = FC(-3dB) + RC(-3dB)
874 centerPlusRearContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12) + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
875 // FL + centerPlusRearContrib
876 lt = (pSrc[0] << 12) + centerPlusRearContrib;
877 // FR + centerPlusRearContrib
878 rt = (pSrc[1] << 12) + centerPlusRearContrib;
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700879 // store in destination
Jean-Michel Triviaea27152012-05-24 10:11:07 -0700880 pDst[0] = clamp16(lt >> 13); // differs from when accumulate is true above
881 pDst[1] = clamp16(rt >> 13); // differs from when accumulate is true above
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800882 pSrc += 4;
883 pDst += 2;
884 numFrames--;
885 }
886 }
887}
888
889
890/*----------------------------------------------------------------------------
891 * Downmix_foldFrom5Point1()
892 *----------------------------------------------------------------------------
893 * Purpose:
894 * downmix a 5.1 signal to stereo
895 *
896 * Inputs:
897 * pSrc 5.1 audio samples to downmix
898 * numFrames the number of 5.1 frames to downmix
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700899 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
900 * or overwrite pDst (when false)
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800901 *
902 * Outputs:
903 * pDst downmixed stereo audio samples
904 *
905 *----------------------------------------------------------------------------
906 */
907void Downmix_foldFrom5Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
908 int32_t lt, rt, centerPlusLfeContrib; // samples in Q19.12 format
909 // sample at index 0 is FL
910 // sample at index 1 is FR
911 // sample at index 2 is FC
912 // sample at index 3 is LFE
913 // sample at index 4 is RL
914 // sample at index 5 is RR
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700915 // code is mostly duplicated between the two values of accumulate to avoid repeating the test
916 // for every sample
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800917 if (accumulate) {
918 while (numFrames) {
919 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
920 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
921 + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
922 // FL + centerPlusLfeContrib + RL
923 lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[4] << 12);
924 // FR + centerPlusLfeContrib + RR
925 rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[5] << 12);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700926 // accumulate in destination
Jean-Michel Triviaea27152012-05-24 10:11:07 -0700927 pDst[0] = clamp16(pDst[0] + (lt >> 13));
928 pDst[1] = clamp16(pDst[1] + (rt >> 13));
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800929 pSrc += 6;
930 pDst += 2;
931 numFrames--;
932 }
933 } else { // same code as above but without adding and clamping pDst[i] to itself
934 while (numFrames) {
935 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
936 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
937 + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
938 // FL + centerPlusLfeContrib + RL
939 lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[4] << 12);
940 // FR + centerPlusLfeContrib + RR
941 rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[5] << 12);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700942 // store in destination
Jean-Michel Triviaea27152012-05-24 10:11:07 -0700943 pDst[0] = clamp16(lt >> 13); // differs from when accumulate is true above
944 pDst[1] = clamp16(rt >> 13); // differs from when accumulate is true above
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800945 pSrc += 6;
946 pDst += 2;
947 numFrames--;
948 }
949 }
950}
951
952
953/*----------------------------------------------------------------------------
954 * Downmix_foldFrom7Point1()
955 *----------------------------------------------------------------------------
956 * Purpose:
957 * downmix a 7.1 signal to stereo
958 *
959 * Inputs:
960 * pSrc 7.1 audio samples to downmix
961 * numFrames the number of 7.1 frames to downmix
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700962 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
963 * or overwrite pDst (when false)
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800964 *
965 * Outputs:
966 * pDst downmixed stereo audio samples
967 *
968 *----------------------------------------------------------------------------
969 */
970void Downmix_foldFrom7Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
971 int32_t lt, rt, centerPlusLfeContrib; // samples in Q19.12 format
972 // sample at index 0 is FL
973 // sample at index 1 is FR
974 // sample at index 2 is FC
975 // sample at index 3 is LFE
976 // sample at index 4 is RL
977 // sample at index 5 is RR
978 // sample at index 6 is SL
979 // sample at index 7 is SR
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700980 // code is mostly duplicated between the two values of accumulate to avoid repeating the test
981 // for every sample
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800982 if (accumulate) {
983 while (numFrames) {
984 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
985 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
986 + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
987 // FL + centerPlusLfeContrib + SL + RL
988 lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[6] << 12) + (pSrc[4] << 12);
989 // FR + centerPlusLfeContrib + SR + RR
990 rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[7] << 12) + (pSrc[5] << 12);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700991 //accumulate in destination
Jean-Michel Triviaea27152012-05-24 10:11:07 -0700992 pDst[0] = clamp16(pDst[0] + (lt >> 13));
993 pDst[1] = clamp16(pDst[1] + (rt >> 13));
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800994 pSrc += 8;
995 pDst += 2;
996 numFrames--;
997 }
998 } else { // same code as above but without adding and clamping pDst[i] to itself
999 while (numFrames) {
1000 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
1001 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
1002 + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
1003 // FL + centerPlusLfeContrib + SL + RL
1004 lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[6] << 12) + (pSrc[4] << 12);
1005 // FR + centerPlusLfeContrib + SR + RR
1006 rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[7] << 12) + (pSrc[5] << 12);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -07001007 // store in destination
Jean-Michel Triviaea27152012-05-24 10:11:07 -07001008 pDst[0] = clamp16(lt >> 13); // differs from when accumulate is true above
1009 pDst[1] = clamp16(rt >> 13); // differs from when accumulate is true above
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -08001010 pSrc += 8;
1011 pDst += 2;
1012 numFrames--;
1013 }
1014 }
1015}
1016
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -07001017
1018/*----------------------------------------------------------------------------
1019 * Downmix_foldGeneric()
1020 *----------------------------------------------------------------------------
1021 * Purpose:
1022 * downmix to stereo a multichannel signal whose format is:
1023 * - has FL/FR
1024 * - if using AUDIO_CHANNEL_OUT_SIDE*, it contains both left and right
1025 * - if using AUDIO_CHANNEL_OUT_BACK*, it contains both left and right
1026 * - doesn't use any of the AUDIO_CHANNEL_OUT_TOP* channels
1027 * - doesn't use any of the AUDIO_CHANNEL_OUT_FRONT_*_OF_CENTER channels
1028 * Only handles channel masks not enumerated in downmix_input_channel_mask_t
1029 *
1030 * Inputs:
1031 * mask the channel mask of pSrc
1032 * pSrc multichannel audio buffer to downmix
1033 * numFrames the number of multichannel frames to downmix
1034 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
1035 * or overwrite pDst (when false)
1036 *
1037 * Outputs:
1038 * pDst downmixed stereo audio samples
1039 *
1040 * Returns: false if multichannel format is not supported
1041 *
1042 *----------------------------------------------------------------------------
1043 */
1044bool Downmix_foldGeneric(
1045 uint32_t mask, int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
1046 // check against unsupported channels
1047 if (mask & kUnsupported) {
1048 ALOGE("Unsupported channels (top or front left/right of center)");
1049 return false;
1050 }
1051 // verify has FL/FR
1052 if ((mask & AUDIO_CHANNEL_OUT_STEREO) != AUDIO_CHANNEL_OUT_STEREO) {
1053 ALOGE("Front channels must be present");
1054 return false;
1055 }
1056 // verify uses SIDE as a pair (ok if not using SIDE at all)
1057 bool hasSides = false;
1058 if ((mask & kSides) != 0) {
1059 if ((mask & kSides) != kSides) {
1060 ALOGE("Side channels must be used as a pair");
1061 return false;
1062 }
1063 hasSides = true;
1064 }
1065 // verify uses BACK as a pair (ok if not using BACK at all)
1066 bool hasBacks = false;
1067 if ((mask & kBacks) != 0) {
1068 if ((mask & kBacks) != kBacks) {
1069 ALOGE("Back channels must be used as a pair");
1070 return false;
1071 }
1072 hasBacks = true;
1073 }
1074
1075 const int numChan = popcount(mask);
1076 const bool hasFC = ((mask & AUDIO_CHANNEL_OUT_FRONT_CENTER) == AUDIO_CHANNEL_OUT_FRONT_CENTER);
1077 const bool hasLFE =
1078 ((mask & AUDIO_CHANNEL_OUT_LOW_FREQUENCY) == AUDIO_CHANNEL_OUT_LOW_FREQUENCY);
1079 const bool hasBC = ((mask & AUDIO_CHANNEL_OUT_BACK_CENTER) == AUDIO_CHANNEL_OUT_BACK_CENTER);
1080 // compute at what index each channel is: samples will be in the following order:
1081 // FL FR FC LFE BL BR BC SL SR
1082 // when a channel is not present, its index is set to the same as the index of the preceding
1083 // channel
1084 const int indexFC = hasFC ? 2 : 1; // front center
1085 const int indexLFE = hasLFE ? indexFC + 1 : indexFC; // low frequency
1086 const int indexBL = hasBacks ? indexLFE + 1 : indexLFE; // back left
1087 const int indexBR = hasBacks ? indexBL + 1 : indexBL; // back right
1088 const int indexBC = hasBC ? indexBR + 1 : indexBR; // back center
1089 const int indexSL = hasSides ? indexBC + 1 : indexBC; // side left
1090 const int indexSR = hasSides ? indexSL + 1 : indexSL; // side right
1091
1092 int32_t lt, rt, centersLfeContrib; // samples in Q19.12 format
1093 // code is mostly duplicated between the two values of accumulate to avoid repeating the test
1094 // for every sample
1095 if (accumulate) {
1096 while (numFrames) {
1097 // compute contribution of FC, BC and LFE
1098 centersLfeContrib = 0;
1099 if (hasFC) { centersLfeContrib += pSrc[indexFC]; }
1100 if (hasLFE) { centersLfeContrib += pSrc[indexLFE]; }
1101 if (hasBC) { centersLfeContrib += pSrc[indexBC]; }
1102 centersLfeContrib *= MINUS_3_DB_IN_Q19_12;
1103 // always has FL/FR
1104 lt = (pSrc[0] << 12);
1105 rt = (pSrc[1] << 12);
1106 // mix in sides and backs
1107 if (hasSides) {
1108 lt += pSrc[indexSL] << 12;
1109 rt += pSrc[indexSR] << 12;
1110 }
1111 if (hasBacks) {
1112 lt += pSrc[indexBL] << 12;
1113 rt += pSrc[indexBR] << 12;
1114 }
1115 lt += centersLfeContrib;
1116 rt += centersLfeContrib;
1117 // accumulate in destination
Jean-Michel Triviaea27152012-05-24 10:11:07 -07001118 pDst[0] = clamp16(pDst[0] + (lt >> 13));
1119 pDst[1] = clamp16(pDst[1] + (rt >> 13));
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -07001120 pSrc += numChan;
1121 pDst += 2;
1122 numFrames--;
1123 }
1124 } else {
1125 while (numFrames) {
1126 // compute contribution of FC, BC and LFE
1127 centersLfeContrib = 0;
1128 if (hasFC) { centersLfeContrib += pSrc[indexFC]; }
1129 if (hasLFE) { centersLfeContrib += pSrc[indexLFE]; }
1130 if (hasBC) { centersLfeContrib += pSrc[indexBC]; }
1131 centersLfeContrib *= MINUS_3_DB_IN_Q19_12;
1132 // always has FL/FR
1133 lt = (pSrc[0] << 12);
1134 rt = (pSrc[1] << 12);
1135 // mix in sides and backs
1136 if (hasSides) {
1137 lt += pSrc[indexSL] << 12;
1138 rt += pSrc[indexSR] << 12;
1139 }
1140 if (hasBacks) {
1141 lt += pSrc[indexBL] << 12;
1142 rt += pSrc[indexBR] << 12;
1143 }
1144 lt += centersLfeContrib;
1145 rt += centersLfeContrib;
1146 // store in destination
Jean-Michel Triviaea27152012-05-24 10:11:07 -07001147 pDst[0] = clamp16(lt >> 13); // differs from when accumulate is true above
1148 pDst[1] = clamp16(rt >> 13); // differs from when accumulate is true above
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -07001149 pSrc += numChan;
1150 pDst += 2;
1151 numFrames--;
1152 }
1153 }
1154 return true;
1155}