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