blob: cb4be750320b9f2eacf8eb1474b449aee88acce8 [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
Haynes Mathew Georgeee507ef2015-07-15 11:06:05 -0700144static bool Downmix_validChannelMask(uint32_t mask)
145{
146 if (!mask) {
147 return false;
148 }
149 // check against unsupported channels
150 if (mask & kUnsupported) {
151 ALOGE("Unsupported channels (top or front left/right of center)");
152 return false;
153 }
154 // verify has FL/FR
155 if ((mask & AUDIO_CHANNEL_OUT_STEREO) != AUDIO_CHANNEL_OUT_STEREO) {
156 ALOGE("Front channels must be present");
157 return false;
158 }
159 // verify uses SIDE as a pair (ok if not using SIDE at all)
160 if ((mask & kSides) != 0) {
161 if ((mask & kSides) != kSides) {
162 ALOGE("Side channels must be used as a pair");
163 return false;
164 }
165 }
166 // verify uses BACK as a pair (ok if not using BACK at all)
167 if ((mask & kBacks) != 0) {
168 if ((mask & kBacks) != kBacks) {
169 ALOGE("Back channels must be used as a pair");
170 return false;
171 }
172 }
173 return true;
174}
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700175
176/*----------------------------------------------------------------------------
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800177 * Effect API implementation
178 *--------------------------------------------------------------------------*/
179
180/*--- Effect Library Interface Implementation ---*/
181
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800182int32_t DownmixLib_Create(const effect_uuid_t *uuid,
Eric Laurent0f714a42015-06-19 15:33:57 -0700183 int32_t sessionId __unused,
184 int32_t ioId __unused,
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800185 effect_handle_t *pHandle) {
186 int ret;
187 int i;
188 downmix_module_t *module;
189 const effect_descriptor_t *desc;
190
191 ALOGV("DownmixLib_Create()");
192
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700193#ifdef DOWNMIX_TEST_CHANNEL_INDEX
194 // should work (won't log an error)
195 ALOGI("DOWNMIX_TEST_CHANNEL_INDEX: should work:");
196 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT |
197 AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_BACK_CENTER);
198 Downmix_testIndexComputation(CHANNEL_MASK_QUAD_SIDE | CHANNEL_MASK_QUAD_BACK);
199 Downmix_testIndexComputation(CHANNEL_MASK_5POINT1_SIDE | AUDIO_CHANNEL_OUT_BACK_CENTER);
200 Downmix_testIndexComputation(CHANNEL_MASK_5POINT1_BACK | AUDIO_CHANNEL_OUT_BACK_CENTER);
201 // shouldn't work (will log an error, won't display channel indices)
202 ALOGI("DOWNMIX_TEST_CHANNEL_INDEX: should NOT work:");
203 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT |
204 AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_BACK_LEFT);
205 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT |
206 AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_SIDE_LEFT);
207 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT |
208 AUDIO_CHANNEL_OUT_BACK_LEFT | AUDIO_CHANNEL_OUT_BACK_RIGHT);
209 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT |
210 AUDIO_CHANNEL_OUT_SIDE_LEFT | AUDIO_CHANNEL_OUT_SIDE_RIGHT);
211#endif
212
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800213 if (pHandle == NULL || uuid == NULL) {
214 return -EINVAL;
215 }
216
217 for (i = 0 ; i < kNbEffects ; i++) {
218 desc = gDescriptors[i];
219 if (memcmp(uuid, &desc->uuid, sizeof(effect_uuid_t)) == 0) {
220 break;
221 }
222 }
223
224 if (i == kNbEffects) {
225 return -ENOENT;
226 }
227
228 module = malloc(sizeof(downmix_module_t));
229
230 module->itfe = &gDownmixInterface;
231
232 module->context.state = DOWNMIX_STATE_UNINITIALIZED;
233
234 ret = Downmix_Init(module);
235 if (ret < 0) {
236 ALOGW("DownmixLib_Create() init failed");
237 free(module);
238 return ret;
239 }
240
241 *pHandle = (effect_handle_t) module;
242
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700243 ALOGV("DownmixLib_Create() %p , size %zu", module, sizeof(downmix_module_t));
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800244
245 return 0;
246}
247
248
249int32_t DownmixLib_Release(effect_handle_t handle) {
250 downmix_module_t *pDwmModule = (downmix_module_t *)handle;
251
252 ALOGV("DownmixLib_Release() %p", handle);
253 if (handle == NULL) {
254 return -EINVAL;
255 }
256
257 pDwmModule->context.state = DOWNMIX_STATE_UNINITIALIZED;
258
259 free(pDwmModule);
260 return 0;
261}
262
263
264int32_t DownmixLib_GetDescriptor(const effect_uuid_t *uuid, effect_descriptor_t *pDescriptor) {
265 ALOGV("DownmixLib_GetDescriptor()");
266 int i;
267
268 if (pDescriptor == NULL || uuid == NULL){
269 ALOGE("DownmixLib_Create() called with NULL pointer");
270 return -EINVAL;
271 }
272 ALOGV("DownmixLib_GetDescriptor() nb effects=%d", kNbEffects);
273 for (i = 0; i < kNbEffects; i++) {
274 ALOGV("DownmixLib_GetDescriptor() i=%d", i);
275 if (memcmp(uuid, &gDescriptors[i]->uuid, sizeof(effect_uuid_t)) == 0) {
276 memcpy(pDescriptor, gDescriptors[i], sizeof(effect_descriptor_t));
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700277 ALOGV("EffectGetDescriptor - UUID matched downmix type %d, UUID = %" PRIx32,
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800278 i, gDescriptors[i]->uuid.timeLow);
279 return 0;
280 }
281 }
282
283 return -EINVAL;
284}
285
286
287/*--- Effect Control Interface Implementation ---*/
288
289static int Downmix_Process(effect_handle_t self,
290 audio_buffer_t *inBuffer, audio_buffer_t *outBuffer) {
291
292 downmix_object_t *pDownmixer;
293 int16_t *pSrc, *pDst;
294 downmix_module_t *pDwmModule = (downmix_module_t *)self;
295
296 if (pDwmModule == NULL) {
297 return -EINVAL;
298 }
299
300 if (inBuffer == NULL || inBuffer->raw == NULL ||
301 outBuffer == NULL || outBuffer->raw == NULL ||
302 inBuffer->frameCount != outBuffer->frameCount) {
303 return -EINVAL;
304 }
305
306 pDownmixer = (downmix_object_t*) &pDwmModule->context;
307
308 if (pDownmixer->state == DOWNMIX_STATE_UNINITIALIZED) {
309 ALOGE("Downmix_Process error: trying to use an uninitialized downmixer");
310 return -EINVAL;
311 } else if (pDownmixer->state == DOWNMIX_STATE_INITIALIZED) {
312 ALOGE("Downmix_Process error: trying to use a non-configured downmixer");
313 return -ENODATA;
314 }
315
316 pSrc = inBuffer->s16;
317 pDst = outBuffer->s16;
318 size_t numFrames = outBuffer->frameCount;
319
320 const bool accumulate =
321 (pDwmModule->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700322 const uint32_t downmixInputChannelMask = pDwmModule->config.inputCfg.channels;
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800323
324 switch(pDownmixer->type) {
325
326 case DOWNMIX_TYPE_STRIP:
327 if (accumulate) {
328 while (numFrames) {
329 pDst[0] = clamp16(pDst[0] + pSrc[0]);
330 pDst[1] = clamp16(pDst[1] + pSrc[1]);
331 pSrc += pDownmixer->input_channel_count;
332 pDst += 2;
333 numFrames--;
334 }
335 } else {
336 while (numFrames) {
337 pDst[0] = pSrc[0];
338 pDst[1] = pSrc[1];
339 pSrc += pDownmixer->input_channel_count;
340 pDst += 2;
341 numFrames--;
342 }
343 }
344 break;
345
346 case DOWNMIX_TYPE_FOLD:
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700347#ifdef DOWNMIX_ALWAYS_USE_GENERIC_DOWNMIXER
348 // bypass the optimized downmix routines for the common formats
349 if (!Downmix_foldGeneric(
350 downmixInputChannelMask, pSrc, pDst, numFrames, accumulate)) {
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700351 ALOGE("Multichannel configuration 0x%" PRIx32 " is not supported", downmixInputChannelMask);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700352 return -EINVAL;
353 }
354 break;
355#endif
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800356 // optimize for the common formats
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700357 switch((downmix_input_channel_mask_t)downmixInputChannelMask) {
Jean-Michel Trividb46b482012-04-23 11:29:26 -0700358 case CHANNEL_MASK_QUAD_BACK:
359 case CHANNEL_MASK_QUAD_SIDE:
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800360 Downmix_foldFromQuad(pSrc, pDst, numFrames, accumulate);
361 break;
Jean-Michel Trividb46b482012-04-23 11:29:26 -0700362 case CHANNEL_MASK_5POINT1_BACK:
363 case CHANNEL_MASK_5POINT1_SIDE:
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800364 Downmix_foldFrom5Point1(pSrc, pDst, numFrames, accumulate);
365 break;
Glenn Kasten029a64e2014-04-15 09:58:55 -0700366 case CHANNEL_MASK_7POINT1:
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800367 Downmix_foldFrom7Point1(pSrc, pDst, numFrames, accumulate);
368 break;
369 default:
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700370 if (!Downmix_foldGeneric(
371 downmixInputChannelMask, pSrc, pDst, numFrames, accumulate)) {
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700372 ALOGE("Multichannel configuration 0x%" PRIx32 " is not supported", downmixInputChannelMask);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700373 return -EINVAL;
374 }
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800375 break;
376 }
377 break;
378
379 default:
380 return -EINVAL;
381 }
382
383 return 0;
384}
385
386
387static int Downmix_Command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
388 void *pCmdData, uint32_t *replySize, void *pReplyData) {
389
390 downmix_module_t *pDwmModule = (downmix_module_t *) self;
391 downmix_object_t *pDownmixer;
392 int retsize;
393
394 if (pDwmModule == NULL || pDwmModule->context.state == DOWNMIX_STATE_UNINITIALIZED) {
395 return -EINVAL;
396 }
397
398 pDownmixer = (downmix_object_t*) &pDwmModule->context;
399
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700400 ALOGV("Downmix_Command command %" PRIu32 " cmdSize %" PRIu32, cmdCode, cmdSize);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800401
402 switch (cmdCode) {
403 case EFFECT_CMD_INIT:
Eric Laurent0f714a42015-06-19 15:33:57 -0700404 if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800405 return -EINVAL;
406 }
407 *(int *) pReplyData = Downmix_Init(pDwmModule);
408 break;
409
410 case EFFECT_CMD_SET_CONFIG:
411 if (pCmdData == NULL || cmdSize != sizeof(effect_config_t)
Eric Laurent0f714a42015-06-19 15:33:57 -0700412 || pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800413 return -EINVAL;
414 }
415 *(int *) pReplyData = Downmix_Configure(pDwmModule,
416 (effect_config_t *)pCmdData, false);
417 break;
418
419 case EFFECT_CMD_RESET:
420 Downmix_Reset(pDownmixer, false);
421 break;
422
423 case EFFECT_CMD_GET_PARAM:
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700424 ALOGV("Downmix_Command EFFECT_CMD_GET_PARAM pCmdData %p, *replySize %" PRIu32 ", pReplyData: %p",
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800425 pCmdData, *replySize, pReplyData);
426 if (pCmdData == NULL || cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t)) ||
Eric Laurent0f714a42015-06-19 15:33:57 -0700427 pReplyData == NULL || replySize == NULL ||
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800428 *replySize < (int) sizeof(effect_param_t) + 2 * sizeof(int32_t)) {
429 return -EINVAL;
430 }
431 effect_param_t *rep = (effect_param_t *) pReplyData;
432 memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + sizeof(int32_t));
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700433 ALOGV("Downmix_Command EFFECT_CMD_GET_PARAM param %" PRId32 ", replySize %" PRIu32,
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800434 *(int32_t *)rep->data, rep->vsize);
435 rep->status = Downmix_getParameter(pDownmixer, *(int32_t *)rep->data, &rep->vsize,
436 rep->data + sizeof(int32_t));
437 *replySize = sizeof(effect_param_t) + sizeof(int32_t) + rep->vsize;
438 break;
439
440 case EFFECT_CMD_SET_PARAM:
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700441 ALOGV("Downmix_Command EFFECT_CMD_SET_PARAM cmdSize %d pCmdData %p, *replySize %" PRIu32
442 ", pReplyData %p", cmdSize, pCmdData, *replySize, pReplyData);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800443 if (pCmdData == NULL || (cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t)))
Eric Laurent0f714a42015-06-19 15:33:57 -0700444 || pReplyData == NULL || replySize == NULL || *replySize != (int)sizeof(int32_t)) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800445 return -EINVAL;
446 }
447 effect_param_t *cmd = (effect_param_t *) pCmdData;
Mikhail Naganov804632a2017-07-24 17:25:47 -0700448 if (cmd->psize != sizeof(int32_t)) {
449 android_errorWriteLog(0x534e4554, "63662938");
450 return -EINVAL;
451 }
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800452 *(int *)pReplyData = Downmix_setParameter(pDownmixer, *(int32_t *)cmd->data,
453 cmd->vsize, cmd->data + sizeof(int32_t));
454 break;
455
456 case EFFECT_CMD_SET_PARAM_DEFERRED:
457 //FIXME implement
458 ALOGW("Downmix_Command command EFFECT_CMD_SET_PARAM_DEFERRED not supported, FIXME");
459 break;
460
461 case EFFECT_CMD_SET_PARAM_COMMIT:
462 //FIXME implement
463 ALOGW("Downmix_Command command EFFECT_CMD_SET_PARAM_COMMIT not supported, FIXME");
464 break;
465
466 case EFFECT_CMD_ENABLE:
Eric Laurent0f714a42015-06-19 15:33:57 -0700467 if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800468 return -EINVAL;
469 }
470 if (pDownmixer->state != DOWNMIX_STATE_INITIALIZED) {
471 return -ENOSYS;
472 }
473 pDownmixer->state = DOWNMIX_STATE_ACTIVE;
474 ALOGV("EFFECT_CMD_ENABLE() OK");
475 *(int *)pReplyData = 0;
476 break;
477
478 case EFFECT_CMD_DISABLE:
Eric Laurent0f714a42015-06-19 15:33:57 -0700479 if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800480 return -EINVAL;
481 }
482 if (pDownmixer->state != DOWNMIX_STATE_ACTIVE) {
483 return -ENOSYS;
484 }
485 pDownmixer->state = DOWNMIX_STATE_INITIALIZED;
486 ALOGV("EFFECT_CMD_DISABLE() OK");
487 *(int *)pReplyData = 0;
488 break;
489
490 case EFFECT_CMD_SET_DEVICE:
491 if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) {
492 return -EINVAL;
493 }
494 // FIXME change type if playing on headset vs speaker
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700495 ALOGV("Downmix_Command EFFECT_CMD_SET_DEVICE: 0x%08" PRIx32, *(uint32_t *)pCmdData);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800496 break;
497
498 case EFFECT_CMD_SET_VOLUME: {
499 // audio output is always stereo => 2 channel volumes
500 if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t) * 2) {
501 return -EINVAL;
502 }
503 // FIXME change volume
504 ALOGW("Downmix_Command command EFFECT_CMD_SET_VOLUME not supported, FIXME");
505 float left = (float)(*(uint32_t *)pCmdData) / (1 << 24);
506 float right = (float)(*((uint32_t *)pCmdData + 1)) / (1 << 24);
507 ALOGV("Downmix_Command EFFECT_CMD_SET_VOLUME: left %f, right %f ", left, right);
508 break;
509 }
510
511 case EFFECT_CMD_SET_AUDIO_MODE:
512 if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) {
513 return -EINVAL;
514 }
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700515 ALOGV("Downmix_Command EFFECT_CMD_SET_AUDIO_MODE: %" PRIu32, *(uint32_t *)pCmdData);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800516 break;
517
518 case EFFECT_CMD_SET_CONFIG_REVERSE:
519 case EFFECT_CMD_SET_INPUT_DEVICE:
520 // these commands are ignored by a downmix effect
521 break;
522
523 default:
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700524 ALOGW("Downmix_Command invalid command %" PRIu32, cmdCode);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800525 return -EINVAL;
526 }
527
528 return 0;
529}
530
531
532int Downmix_GetDescriptor(effect_handle_t self, effect_descriptor_t *pDescriptor)
533{
534 downmix_module_t *pDwnmxModule = (downmix_module_t *) self;
535
536 if (pDwnmxModule == NULL ||
537 pDwnmxModule->context.state == DOWNMIX_STATE_UNINITIALIZED) {
538 return -EINVAL;
539 }
540
541 memcpy(pDescriptor, &gDownmixDescriptor, sizeof(effect_descriptor_t));
542
543 return 0;
544}
545
546
547/*----------------------------------------------------------------------------
548 * Downmix internal functions
549 *--------------------------------------------------------------------------*/
550
551/*----------------------------------------------------------------------------
552 * Downmix_Init()
553 *----------------------------------------------------------------------------
554 * Purpose:
555 * Initialize downmix context and apply default parameters
556 *
557 * Inputs:
558 * pDwmModule pointer to downmix effect module
559 *
560 * Outputs:
561 *
562 * Returns:
563 * 0 indicates success
564 *
565 * Side Effects:
566 * updates:
567 * pDwmModule->context.type
568 * pDwmModule->context.apply_volume_correction
569 * pDwmModule->config.inputCfg
570 * pDwmModule->config.outputCfg
571 * pDwmModule->config.inputCfg.samplingRate
572 * pDwmModule->config.outputCfg.samplingRate
573 * pDwmModule->context.state
574 * doesn't set:
575 * pDwmModule->itfe
576 *
577 *----------------------------------------------------------------------------
578 */
579
580int Downmix_Init(downmix_module_t *pDwmModule) {
581
582 ALOGV("Downmix_Init module %p", pDwmModule);
583 int ret = 0;
584
585 memset(&pDwmModule->context, 0, sizeof(downmix_object_t));
586
587 pDwmModule->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
588 pDwmModule->config.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
589 pDwmModule->config.inputCfg.channels = AUDIO_CHANNEL_OUT_7POINT1;
590 pDwmModule->config.inputCfg.bufferProvider.getBuffer = NULL;
591 pDwmModule->config.inputCfg.bufferProvider.releaseBuffer = NULL;
592 pDwmModule->config.inputCfg.bufferProvider.cookie = NULL;
593 pDwmModule->config.inputCfg.mask = EFFECT_CONFIG_ALL;
594
595 pDwmModule->config.inputCfg.samplingRate = 44100;
596 pDwmModule->config.outputCfg.samplingRate = pDwmModule->config.inputCfg.samplingRate;
597
598 // set a default value for the access mode, but should be overwritten by caller
599 pDwmModule->config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
600 pDwmModule->config.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
601 pDwmModule->config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
602 pDwmModule->config.outputCfg.bufferProvider.getBuffer = NULL;
603 pDwmModule->config.outputCfg.bufferProvider.releaseBuffer = NULL;
604 pDwmModule->config.outputCfg.bufferProvider.cookie = NULL;
605 pDwmModule->config.outputCfg.mask = EFFECT_CONFIG_ALL;
606
607 ret = Downmix_Configure(pDwmModule, &pDwmModule->config, true);
608 if (ret != 0) {
609 ALOGV("Downmix_Init error %d on module %p", ret, pDwmModule);
610 } else {
611 pDwmModule->context.state = DOWNMIX_STATE_INITIALIZED;
612 }
613
614 return ret;
615}
616
617
618/*----------------------------------------------------------------------------
619 * Downmix_Configure()
620 *----------------------------------------------------------------------------
621 * Purpose:
622 * Set input and output audio configuration.
623 *
624 * Inputs:
625 * pDwmModule pointer to downmix effect module
626 * pConfig pointer to effect_config_t structure containing input
627 * and output audio parameters configuration
628 * init true if called from init function
629 *
630 * Outputs:
631 *
632 * Returns:
633 * 0 indicates success
634 *
635 * Side Effects:
636 *
637 *----------------------------------------------------------------------------
638 */
639
640int Downmix_Configure(downmix_module_t *pDwmModule, effect_config_t *pConfig, bool init) {
641
642 downmix_object_t *pDownmixer = &pDwmModule->context;
643
644 // Check configuration compatibility with build options, and effect capabilities
645 if (pConfig->inputCfg.samplingRate != pConfig->outputCfg.samplingRate
646 || pConfig->outputCfg.channels != DOWNMIX_OUTPUT_CHANNELS
647 || pConfig->inputCfg.format != AUDIO_FORMAT_PCM_16_BIT
648 || pConfig->outputCfg.format != AUDIO_FORMAT_PCM_16_BIT) {
649 ALOGE("Downmix_Configure error: invalid config");
650 return -EINVAL;
651 }
652
Chih-Wei Huanga93c8c92013-01-10 20:52:57 +0800653 if (&pDwmModule->config != pConfig) {
654 memcpy(&pDwmModule->config, pConfig, sizeof(effect_config_t));
655 }
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800656
657 if (init) {
658 pDownmixer->type = DOWNMIX_TYPE_FOLD;
659 pDownmixer->apply_volume_correction = false;
660 pDownmixer->input_channel_count = 8; // matches default input of AUDIO_CHANNEL_OUT_7POINT1
661 } else {
Haynes Mathew Georgeee507ef2015-07-15 11:06:05 -0700662 // when configuring the effect, do not allow a blank or unsupported channel mask
663 if (!Downmix_validChannelMask(pConfig->inputCfg.channels)) {
664 ALOGE("Downmix_Configure error: input channel mask(0x%x) not supported",
665 pConfig->inputCfg.channels);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800666 return -EINVAL;
667 }
Andy Hunge5412692014-05-16 11:25:07 -0700668 pDownmixer->input_channel_count =
669 audio_channel_count_from_out_mask(pConfig->inputCfg.channels);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800670 }
671
672 Downmix_Reset(pDownmixer, init);
673
674 return 0;
675}
676
677
678/*----------------------------------------------------------------------------
679 * Downmix_Reset()
680 *----------------------------------------------------------------------------
681 * Purpose:
682 * Reset internal states.
683 *
684 * Inputs:
685 * pDownmixer pointer to downmix context
686 * init true if called from init function
687 *
688 * Outputs:
689*
690 * Returns:
691 * 0 indicates success
692 *
693 * Side Effects:
694 *
695 *----------------------------------------------------------------------------
696 */
697
Eric Laurent0f714a42015-06-19 15:33:57 -0700698int Downmix_Reset(downmix_object_t *pDownmixer __unused, bool init __unused) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800699 // nothing to do here
700 return 0;
701}
702
703
704/*----------------------------------------------------------------------------
705 * Downmix_setParameter()
706 *----------------------------------------------------------------------------
707 * Purpose:
708 * Set a Downmix parameter
709 *
710 * Inputs:
711 * pDownmixer handle to instance data
712 * param parameter
713 * pValue pointer to parameter value
714 * size value size
715 *
716 * Outputs:
717 *
718 * Returns:
719 * 0 indicates success
720 *
721 * Side Effects:
722 *
723 *----------------------------------------------------------------------------
724 */
Ashok Bhatb302bd52014-02-18 11:40:00 +0000725int Downmix_setParameter(downmix_object_t *pDownmixer, int32_t param, uint32_t size, void *pValue) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800726
727 int16_t value16;
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700728 ALOGV("Downmix_setParameter, context %p, param %" PRId32 ", value16 %" PRId16 ", value32 %" PRId32,
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800729 pDownmixer, param, *(int16_t *)pValue, *(int32_t *)pValue);
730
731 switch (param) {
732
733 case DOWNMIX_PARAM_TYPE:
734 if (size != sizeof(downmix_type_t)) {
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700735 ALOGE("Downmix_setParameter(DOWNMIX_PARAM_TYPE) invalid size %" PRIu32 ", should be %zu",
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800736 size, sizeof(downmix_type_t));
737 return -EINVAL;
738 }
739 value16 = *(int16_t *)pValue;
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700740 ALOGV("set DOWNMIX_PARAM_TYPE, type %" PRId16, value16);
Jean-Michel Trivi7d5b2622012-04-04 18:54:36 -0700741 if (!((value16 > DOWNMIX_TYPE_INVALID) && (value16 <= DOWNMIX_TYPE_LAST))) {
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700742 ALOGE("Downmix_setParameter invalid DOWNMIX_PARAM_TYPE value %" PRId16, value16);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800743 return -EINVAL;
744 } else {
745 pDownmixer->type = (downmix_type_t) value16;
746 break;
747
748 default:
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700749 ALOGE("Downmix_setParameter unknown parameter %" PRId32, param);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800750 return -EINVAL;
751 }
752}
753
754 return 0;
755} /* end Downmix_setParameter */
756
757
758/*----------------------------------------------------------------------------
759 * Downmix_getParameter()
760 *----------------------------------------------------------------------------
761 * Purpose:
762 * Get a Downmix parameter
763 *
764 * Inputs:
765 * pDownmixer handle to instance data
766 * param parameter
767 * pValue pointer to variable to hold retrieved value
768 * pSize pointer to value size: maximum size as input
769 *
770 * Outputs:
771 * *pValue updated with parameter value
772 * *pSize updated with actual value size
773 *
774 * Returns:
775 * 0 indicates success
776 *
777 * Side Effects:
778 *
779 *----------------------------------------------------------------------------
780 */
Ashok Bhatb302bd52014-02-18 11:40:00 +0000781int Downmix_getParameter(downmix_object_t *pDownmixer, int32_t param, uint32_t *pSize, void *pValue) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800782 int16_t *pValue16;
783
784 switch (param) {
785
786 case DOWNMIX_PARAM_TYPE:
787 if (*pSize < sizeof(int16_t)) {
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700788 ALOGE("Downmix_getParameter invalid parameter size %" PRIu32 " for DOWNMIX_PARAM_TYPE", *pSize);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800789 return -EINVAL;
790 }
791 pValue16 = (int16_t *)pValue;
792 *pValue16 = (int16_t) pDownmixer->type;
793 *pSize = sizeof(int16_t);
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700794 ALOGV("Downmix_getParameter DOWNMIX_PARAM_TYPE is %" PRId16, *pValue16);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800795 break;
796
797 default:
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700798 ALOGE("Downmix_getParameter unknown parameter %" PRId16, param);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800799 return -EINVAL;
800 }
801
802 return 0;
803} /* end Downmix_getParameter */
804
805
806/*----------------------------------------------------------------------------
807 * Downmix_foldFromQuad()
808 *----------------------------------------------------------------------------
809 * Purpose:
810 * downmix a quad signal to stereo
811 *
812 * Inputs:
813 * pSrc quad audio samples to downmix
814 * numFrames the number of quad frames to downmix
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700815 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
816 * or overwrite pDst (when false)
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800817 *
818 * Outputs:
819 * pDst downmixed stereo audio samples
820 *
821 *----------------------------------------------------------------------------
822 */
823void Downmix_foldFromQuad(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
824 // sample at index 0 is FL
825 // sample at index 1 is FR
826 // sample at index 2 is RL
827 // sample at index 3 is RR
828 if (accumulate) {
829 while (numFrames) {
830 // FL + RL
Jean-Michel Triviaea27152012-05-24 10:11:07 -0700831 pDst[0] = clamp16(pDst[0] + ((pSrc[0] + pSrc[2]) >> 1));
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800832 // FR + RR
Jean-Michel Triviaea27152012-05-24 10:11:07 -0700833 pDst[1] = clamp16(pDst[1] + ((pSrc[1] + pSrc[3]) >> 1));
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800834 pSrc += 4;
835 pDst += 2;
836 numFrames--;
837 }
838 } else { // same code as above but without adding and clamping pDst[i] to itself
839 while (numFrames) {
840 // FL + RL
Jean-Michel Triviaea27152012-05-24 10:11:07 -0700841 pDst[0] = clamp16((pSrc[0] + pSrc[2]) >> 1);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800842 // FR + RR
Jean-Michel Triviaea27152012-05-24 10:11:07 -0700843 pDst[1] = clamp16((pSrc[1] + pSrc[3]) >> 1);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800844 pSrc += 4;
845 pDst += 2;
846 numFrames--;
847 }
848 }
849}
850
851
852/*----------------------------------------------------------------------------
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800853 * Downmix_foldFrom5Point1()
854 *----------------------------------------------------------------------------
855 * Purpose:
856 * downmix a 5.1 signal to stereo
857 *
858 * Inputs:
859 * pSrc 5.1 audio samples to downmix
860 * numFrames the number of 5.1 frames to downmix
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700861 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
862 * or overwrite pDst (when false)
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800863 *
864 * Outputs:
865 * pDst downmixed stereo audio samples
866 *
867 *----------------------------------------------------------------------------
868 */
869void Downmix_foldFrom5Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
870 int32_t lt, rt, centerPlusLfeContrib; // samples in Q19.12 format
871 // sample at index 0 is FL
872 // sample at index 1 is FR
873 // sample at index 2 is FC
874 // sample at index 3 is LFE
875 // sample at index 4 is RL
876 // sample at index 5 is RR
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700877 // code is mostly duplicated between the two values of accumulate to avoid repeating the test
878 // for every sample
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800879 if (accumulate) {
880 while (numFrames) {
881 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
882 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
883 + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
884 // FL + centerPlusLfeContrib + RL
885 lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[4] << 12);
886 // FR + centerPlusLfeContrib + RR
887 rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[5] << 12);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700888 // accumulate in destination
Jean-Michel Triviaea27152012-05-24 10:11:07 -0700889 pDst[0] = clamp16(pDst[0] + (lt >> 13));
890 pDst[1] = clamp16(pDst[1] + (rt >> 13));
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800891 pSrc += 6;
892 pDst += 2;
893 numFrames--;
894 }
895 } else { // same code as above but without adding and clamping pDst[i] to itself
896 while (numFrames) {
897 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
898 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
899 + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
900 // FL + centerPlusLfeContrib + RL
901 lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[4] << 12);
902 // FR + centerPlusLfeContrib + RR
903 rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[5] << 12);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700904 // store in destination
Jean-Michel Triviaea27152012-05-24 10:11:07 -0700905 pDst[0] = clamp16(lt >> 13); // differs from when accumulate is true above
906 pDst[1] = clamp16(rt >> 13); // differs from when accumulate is true above
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800907 pSrc += 6;
908 pDst += 2;
909 numFrames--;
910 }
911 }
912}
913
914
915/*----------------------------------------------------------------------------
916 * Downmix_foldFrom7Point1()
917 *----------------------------------------------------------------------------
918 * Purpose:
919 * downmix a 7.1 signal to stereo
920 *
921 * Inputs:
922 * pSrc 7.1 audio samples to downmix
923 * numFrames the number of 7.1 frames to downmix
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700924 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
925 * or overwrite pDst (when false)
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800926 *
927 * Outputs:
928 * pDst downmixed stereo audio samples
929 *
930 *----------------------------------------------------------------------------
931 */
932void Downmix_foldFrom7Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
933 int32_t lt, rt, centerPlusLfeContrib; // samples in Q19.12 format
934 // sample at index 0 is FL
935 // sample at index 1 is FR
936 // sample at index 2 is FC
937 // sample at index 3 is LFE
938 // sample at index 4 is RL
939 // sample at index 5 is RR
940 // sample at index 6 is SL
941 // sample at index 7 is SR
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700942 // code is mostly duplicated between the two values of accumulate to avoid repeating the test
943 // for every sample
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800944 if (accumulate) {
945 while (numFrames) {
946 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
947 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
948 + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
949 // FL + centerPlusLfeContrib + SL + RL
950 lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[6] << 12) + (pSrc[4] << 12);
951 // FR + centerPlusLfeContrib + SR + RR
952 rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[7] << 12) + (pSrc[5] << 12);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700953 //accumulate in destination
Jean-Michel Triviaea27152012-05-24 10:11:07 -0700954 pDst[0] = clamp16(pDst[0] + (lt >> 13));
955 pDst[1] = clamp16(pDst[1] + (rt >> 13));
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800956 pSrc += 8;
957 pDst += 2;
958 numFrames--;
959 }
960 } else { // same code as above but without adding and clamping pDst[i] to itself
961 while (numFrames) {
962 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
963 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
964 + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
965 // FL + centerPlusLfeContrib + SL + RL
966 lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[6] << 12) + (pSrc[4] << 12);
967 // FR + centerPlusLfeContrib + SR + RR
968 rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[7] << 12) + (pSrc[5] << 12);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700969 // store in destination
Jean-Michel Triviaea27152012-05-24 10:11:07 -0700970 pDst[0] = clamp16(lt >> 13); // differs from when accumulate is true above
971 pDst[1] = clamp16(rt >> 13); // differs from when accumulate is true above
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800972 pSrc += 8;
973 pDst += 2;
974 numFrames--;
975 }
976 }
977}
978
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700979
980/*----------------------------------------------------------------------------
981 * Downmix_foldGeneric()
982 *----------------------------------------------------------------------------
983 * Purpose:
984 * downmix to stereo a multichannel signal whose format is:
985 * - has FL/FR
986 * - if using AUDIO_CHANNEL_OUT_SIDE*, it contains both left and right
987 * - if using AUDIO_CHANNEL_OUT_BACK*, it contains both left and right
988 * - doesn't use any of the AUDIO_CHANNEL_OUT_TOP* channels
989 * - doesn't use any of the AUDIO_CHANNEL_OUT_FRONT_*_OF_CENTER channels
990 * Only handles channel masks not enumerated in downmix_input_channel_mask_t
991 *
992 * Inputs:
993 * mask the channel mask of pSrc
994 * pSrc multichannel audio buffer to downmix
995 * numFrames the number of multichannel frames to downmix
996 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
997 * or overwrite pDst (when false)
998 *
999 * Outputs:
1000 * pDst downmixed stereo audio samples
1001 *
1002 * Returns: false if multichannel format is not supported
1003 *
1004 *----------------------------------------------------------------------------
1005 */
1006bool Downmix_foldGeneric(
1007 uint32_t mask, int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
Haynes Mathew Georgeee507ef2015-07-15 11:06:05 -07001008
1009 if (!Downmix_validChannelMask(mask)) {
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -07001010 return false;
1011 }
Haynes Mathew Georgeee507ef2015-07-15 11:06:05 -07001012
1013 const bool hasSides = (mask & kSides) != 0;
1014 const bool hasBacks = (mask & kBacks) != 0;
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -07001015
Andy Hunge5412692014-05-16 11:25:07 -07001016 const int numChan = audio_channel_count_from_out_mask(mask);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -07001017 const bool hasFC = ((mask & AUDIO_CHANNEL_OUT_FRONT_CENTER) == AUDIO_CHANNEL_OUT_FRONT_CENTER);
1018 const bool hasLFE =
1019 ((mask & AUDIO_CHANNEL_OUT_LOW_FREQUENCY) == AUDIO_CHANNEL_OUT_LOW_FREQUENCY);
1020 const bool hasBC = ((mask & AUDIO_CHANNEL_OUT_BACK_CENTER) == AUDIO_CHANNEL_OUT_BACK_CENTER);
1021 // compute at what index each channel is: samples will be in the following order:
1022 // FL FR FC LFE BL BR BC SL SR
1023 // when a channel is not present, its index is set to the same as the index of the preceding
1024 // channel
1025 const int indexFC = hasFC ? 2 : 1; // front center
1026 const int indexLFE = hasLFE ? indexFC + 1 : indexFC; // low frequency
1027 const int indexBL = hasBacks ? indexLFE + 1 : indexLFE; // back left
1028 const int indexBR = hasBacks ? indexBL + 1 : indexBL; // back right
1029 const int indexBC = hasBC ? indexBR + 1 : indexBR; // back center
1030 const int indexSL = hasSides ? indexBC + 1 : indexBC; // side left
1031 const int indexSR = hasSides ? indexSL + 1 : indexSL; // side right
1032
1033 int32_t lt, rt, centersLfeContrib; // samples in Q19.12 format
1034 // code is mostly duplicated between the two values of accumulate to avoid repeating the test
1035 // for every sample
1036 if (accumulate) {
1037 while (numFrames) {
1038 // compute contribution of FC, BC and LFE
1039 centersLfeContrib = 0;
1040 if (hasFC) { centersLfeContrib += pSrc[indexFC]; }
1041 if (hasLFE) { centersLfeContrib += pSrc[indexLFE]; }
1042 if (hasBC) { centersLfeContrib += pSrc[indexBC]; }
1043 centersLfeContrib *= MINUS_3_DB_IN_Q19_12;
1044 // always has FL/FR
1045 lt = (pSrc[0] << 12);
1046 rt = (pSrc[1] << 12);
1047 // mix in sides and backs
1048 if (hasSides) {
1049 lt += pSrc[indexSL] << 12;
1050 rt += pSrc[indexSR] << 12;
1051 }
1052 if (hasBacks) {
1053 lt += pSrc[indexBL] << 12;
1054 rt += pSrc[indexBR] << 12;
1055 }
1056 lt += centersLfeContrib;
1057 rt += centersLfeContrib;
1058 // accumulate in destination
Jean-Michel Triviaea27152012-05-24 10:11:07 -07001059 pDst[0] = clamp16(pDst[0] + (lt >> 13));
1060 pDst[1] = clamp16(pDst[1] + (rt >> 13));
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -07001061 pSrc += numChan;
1062 pDst += 2;
1063 numFrames--;
1064 }
1065 } else {
1066 while (numFrames) {
1067 // compute contribution of FC, BC and LFE
1068 centersLfeContrib = 0;
1069 if (hasFC) { centersLfeContrib += pSrc[indexFC]; }
1070 if (hasLFE) { centersLfeContrib += pSrc[indexLFE]; }
1071 if (hasBC) { centersLfeContrib += pSrc[indexBC]; }
1072 centersLfeContrib *= MINUS_3_DB_IN_Q19_12;
1073 // always has FL/FR
1074 lt = (pSrc[0] << 12);
1075 rt = (pSrc[1] << 12);
1076 // mix in sides and backs
1077 if (hasSides) {
1078 lt += pSrc[indexSL] << 12;
1079 rt += pSrc[indexSR] << 12;
1080 }
1081 if (hasBacks) {
1082 lt += pSrc[indexBL] << 12;
1083 rt += pSrc[indexBR] << 12;
1084 }
1085 lt += centersLfeContrib;
1086 rt += centersLfeContrib;
1087 // store in destination
Jean-Michel Triviaea27152012-05-24 10:11:07 -07001088 pDst[0] = clamp16(lt >> 13); // differs from when accumulate is true above
1089 pDst[1] = clamp16(rt >> 13); // differs from when accumulate is true above
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -07001090 pSrc += numChan;
1091 pDst += 2;
1092 numFrames--;
1093 }
1094 }
1095 return true;
1096}