blob: 5b748453542ee949e3dc905d596f0cfd6cd924d8 [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 Salyzyn60d02072016-09-29 08:48:48 -070019
Mark Salyzyn7cb0e732014-04-18 13:48:25 -070020#include <inttypes.h>
Mark Salyzyn60d02072016-09-29 08:48:48 -070021#include <stdbool.h>
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -080022#include <stdlib.h>
23#include <string.h>
Mark Salyzyn60d02072016-09-29 08:48:48 -070024
Mark Salyzyneb165612017-01-10 09:08:19 -080025#include <log/log.h>
Mark Salyzyn60d02072016-09-29 08:48:48 -070026
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -080027#include "EffectDownmix.h"
28
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -070029// Do not submit with DOWNMIX_TEST_CHANNEL_INDEX defined, strictly for testing
30//#define DOWNMIX_TEST_CHANNEL_INDEX 0
31// Do not submit with DOWNMIX_ALWAYS_USE_GENERIC_DOWNMIXER defined, strictly for testing
32//#define DOWNMIX_ALWAYS_USE_GENERIC_DOWNMIXER 0
33
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -080034#define MINUS_3_DB_IN_Q19_12 2896 // -3dB = 0.707 * 2^12 = 2896
35
Glenn Kasten029a64e2014-04-15 09:58:55 -070036// subset of possible audio_channel_mask_t values, and AUDIO_CHANNEL_OUT_* renamed to CHANNEL_MASK_*
Jean-Michel Trividb46b482012-04-23 11:29:26 -070037typedef enum {
Glenn Kasten029a64e2014-04-15 09:58:55 -070038 CHANNEL_MASK_QUAD_BACK = AUDIO_CHANNEL_OUT_QUAD_BACK,
39 CHANNEL_MASK_QUAD_SIDE = AUDIO_CHANNEL_OUT_QUAD_SIDE,
40 CHANNEL_MASK_5POINT1_BACK = AUDIO_CHANNEL_OUT_5POINT1_BACK,
41 CHANNEL_MASK_5POINT1_SIDE = AUDIO_CHANNEL_OUT_5POINT1_SIDE,
42 CHANNEL_MASK_7POINT1 = AUDIO_CHANNEL_OUT_7POINT1,
Jean-Michel Trividb46b482012-04-23 11:29:26 -070043} downmix_input_channel_mask_t;
44
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -080045// effect_handle_t interface implementation for downmix effect
46const struct effect_interface_s gDownmixInterface = {
47 Downmix_Process,
48 Downmix_Command,
49 Downmix_GetDescriptor,
50 NULL /* no process_reverse function, no reference stream needed */
51};
52
Marco Nelissen7f16b192012-10-25 16:05:57 -070053// This is the only symbol that needs to be exported
54__attribute__ ((visibility ("default")))
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -080055audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
synergydevc9d8ea72013-10-19 22:51:33 -070056 .tag = AUDIO_EFFECT_LIBRARY_TAG,
57 .version = EFFECT_LIBRARY_API_VERSION,
58 .name = "Downmix Library",
59 .implementor = "The Android Open Source Project",
60 .create_effect = DownmixLib_Create,
61 .release_effect = DownmixLib_Release,
62 .get_descriptor = DownmixLib_GetDescriptor,
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -080063};
64
65
66// AOSP insert downmix UUID: 93f04452-e4fe-41cc-91f9-e475b6d1d69f
67static const effect_descriptor_t gDownmixDescriptor = {
68 EFFECT_UIID_DOWNMIX__, //type
69 {0x93f04452, 0xe4fe, 0x41cc, 0x91f9, {0xe4, 0x75, 0xb6, 0xd1, 0xd6, 0x9f}}, // uuid
70 EFFECT_CONTROL_API_VERSION,
71 EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST,
72 0, //FIXME what value should be reported? // cpu load
73 0, //FIXME what value should be reported? // memory usage
74 "Multichannel Downmix To Stereo", // human readable effect name
75 "The Android Open Source Project" // human readable effect implementor name
76};
77
78// gDescriptors contains pointers to all defined effect descriptor in this library
79static const effect_descriptor_t * const gDescriptors[] = {
80 &gDownmixDescriptor
81};
82
83// number of effects in this library
84const int kNbEffects = sizeof(gDescriptors) / sizeof(const effect_descriptor_t *);
85
86
87/*----------------------------------------------------------------------------
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -070088 * Test code
89 *--------------------------------------------------------------------------*/
90#ifdef DOWNMIX_TEST_CHANNEL_INDEX
91// strictly for testing, logs the indices of the channels for a given mask,
92// uses the same code as Downmix_foldGeneric()
93void Downmix_testIndexComputation(uint32_t mask) {
Mark Salyzyn7cb0e732014-04-18 13:48:25 -070094 ALOGI("Testing index computation for 0x%" PRIx32 ":", mask);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -070095 // check against unsupported channels
96 if (mask & kUnsupported) {
97 ALOGE("Unsupported channels (top or front left/right of center)");
98 return;
99 }
100 // verify has FL/FR
101 if ((mask & AUDIO_CHANNEL_OUT_STEREO) != AUDIO_CHANNEL_OUT_STEREO) {
102 ALOGE("Front channels must be present");
103 return;
104 }
105 // verify uses SIDE as a pair (ok if not using SIDE at all)
106 bool hasSides = false;
107 if ((mask & kSides) != 0) {
108 if ((mask & kSides) != kSides) {
109 ALOGE("Side channels must be used as a pair");
110 return;
111 }
112 hasSides = true;
113 }
114 // verify uses BACK as a pair (ok if not using BACK at all)
115 bool hasBacks = false;
116 if ((mask & kBacks) != 0) {
117 if ((mask & kBacks) != kBacks) {
118 ALOGE("Back channels must be used as a pair");
119 return;
120 }
121 hasBacks = true;
122 }
123
Andy Hunge5412692014-05-16 11:25:07 -0700124 const int numChan = audio_channel_count_from_out_mask(mask);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700125 const bool hasFC = ((mask & AUDIO_CHANNEL_OUT_FRONT_CENTER) == AUDIO_CHANNEL_OUT_FRONT_CENTER);
126 const bool hasLFE =
127 ((mask & AUDIO_CHANNEL_OUT_LOW_FREQUENCY) == AUDIO_CHANNEL_OUT_LOW_FREQUENCY);
128 const bool hasBC = ((mask & AUDIO_CHANNEL_OUT_BACK_CENTER) == AUDIO_CHANNEL_OUT_BACK_CENTER);
129 // compute at what index each channel is: samples will be in the following order:
130 // FL FR FC LFE BL BR BC SL SR
131 // when a channel is not present, its index is set to the same as the index of the preceding
132 // channel
133 const int indexFC = hasFC ? 2 : 1; // front center
134 const int indexLFE = hasLFE ? indexFC + 1 : indexFC; // low frequency
135 const int indexBL = hasBacks ? indexLFE + 1 : indexLFE; // back left
136 const int indexBR = hasBacks ? indexBL + 1 : indexBL; // back right
137 const int indexBC = hasBC ? indexBR + 1 : indexBR; // back center
138 const int indexSL = hasSides ? indexBC + 1 : indexBC; // side left
139 const int indexSR = hasSides ? indexSL + 1 : indexSL; // side right
140
141 ALOGI(" FL FR FC LFE BL BR BC SL SR");
142 ALOGI(" %d %d %d %d %d %d %d %d %d",
143 0, 1, indexFC, indexLFE, indexBL, indexBR, indexBC, indexSL, indexSR);
144}
145#endif
146
Haynes Mathew Georgeee507ef2015-07-15 11:06:05 -0700147static bool Downmix_validChannelMask(uint32_t mask)
148{
149 if (!mask) {
150 return false;
151 }
152 // check against unsupported channels
153 if (mask & kUnsupported) {
154 ALOGE("Unsupported channels (top or front left/right of center)");
155 return false;
156 }
157 // verify has FL/FR
158 if ((mask & AUDIO_CHANNEL_OUT_STEREO) != AUDIO_CHANNEL_OUT_STEREO) {
159 ALOGE("Front channels must be present");
160 return false;
161 }
162 // verify uses SIDE as a pair (ok if not using SIDE at all)
163 if ((mask & kSides) != 0) {
164 if ((mask & kSides) != kSides) {
165 ALOGE("Side channels must be used as a pair");
166 return false;
167 }
168 }
169 // verify uses BACK as a pair (ok if not using BACK at all)
170 if ((mask & kBacks) != 0) {
171 if ((mask & kBacks) != kBacks) {
172 ALOGE("Back channels must be used as a pair");
173 return false;
174 }
175 }
176 return true;
177}
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700178
179/*----------------------------------------------------------------------------
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800180 * Effect API implementation
181 *--------------------------------------------------------------------------*/
182
183/*--- Effect Library Interface Implementation ---*/
184
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800185int32_t DownmixLib_Create(const effect_uuid_t *uuid,
Eric Laurent0f714a42015-06-19 15:33:57 -0700186 int32_t sessionId __unused,
187 int32_t ioId __unused,
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800188 effect_handle_t *pHandle) {
189 int ret;
190 int i;
191 downmix_module_t *module;
192 const effect_descriptor_t *desc;
193
194 ALOGV("DownmixLib_Create()");
195
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700196#ifdef DOWNMIX_TEST_CHANNEL_INDEX
197 // should work (won't log an error)
198 ALOGI("DOWNMIX_TEST_CHANNEL_INDEX: should work:");
199 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT |
200 AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_BACK_CENTER);
201 Downmix_testIndexComputation(CHANNEL_MASK_QUAD_SIDE | CHANNEL_MASK_QUAD_BACK);
202 Downmix_testIndexComputation(CHANNEL_MASK_5POINT1_SIDE | AUDIO_CHANNEL_OUT_BACK_CENTER);
203 Downmix_testIndexComputation(CHANNEL_MASK_5POINT1_BACK | AUDIO_CHANNEL_OUT_BACK_CENTER);
204 // shouldn't work (will log an error, won't display channel indices)
205 ALOGI("DOWNMIX_TEST_CHANNEL_INDEX: should NOT work:");
206 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT |
207 AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_BACK_LEFT);
208 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT |
209 AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_SIDE_LEFT);
210 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT |
211 AUDIO_CHANNEL_OUT_BACK_LEFT | AUDIO_CHANNEL_OUT_BACK_RIGHT);
212 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT |
213 AUDIO_CHANNEL_OUT_SIDE_LEFT | AUDIO_CHANNEL_OUT_SIDE_RIGHT);
214#endif
215
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800216 if (pHandle == NULL || uuid == NULL) {
217 return -EINVAL;
218 }
219
220 for (i = 0 ; i < kNbEffects ; i++) {
221 desc = gDescriptors[i];
222 if (memcmp(uuid, &desc->uuid, sizeof(effect_uuid_t)) == 0) {
223 break;
224 }
225 }
226
227 if (i == kNbEffects) {
228 return -ENOENT;
229 }
230
231 module = malloc(sizeof(downmix_module_t));
232
233 module->itfe = &gDownmixInterface;
234
235 module->context.state = DOWNMIX_STATE_UNINITIALIZED;
236
237 ret = Downmix_Init(module);
238 if (ret < 0) {
239 ALOGW("DownmixLib_Create() init failed");
240 free(module);
241 return ret;
242 }
243
244 *pHandle = (effect_handle_t) module;
245
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700246 ALOGV("DownmixLib_Create() %p , size %zu", module, sizeof(downmix_module_t));
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800247
248 return 0;
249}
250
251
252int32_t DownmixLib_Release(effect_handle_t handle) {
253 downmix_module_t *pDwmModule = (downmix_module_t *)handle;
254
255 ALOGV("DownmixLib_Release() %p", handle);
256 if (handle == NULL) {
257 return -EINVAL;
258 }
259
260 pDwmModule->context.state = DOWNMIX_STATE_UNINITIALIZED;
261
262 free(pDwmModule);
263 return 0;
264}
265
266
267int32_t DownmixLib_GetDescriptor(const effect_uuid_t *uuid, effect_descriptor_t *pDescriptor) {
268 ALOGV("DownmixLib_GetDescriptor()");
269 int i;
270
271 if (pDescriptor == NULL || uuid == NULL){
272 ALOGE("DownmixLib_Create() called with NULL pointer");
273 return -EINVAL;
274 }
275 ALOGV("DownmixLib_GetDescriptor() nb effects=%d", kNbEffects);
276 for (i = 0; i < kNbEffects; i++) {
277 ALOGV("DownmixLib_GetDescriptor() i=%d", i);
278 if (memcmp(uuid, &gDescriptors[i]->uuid, sizeof(effect_uuid_t)) == 0) {
279 memcpy(pDescriptor, gDescriptors[i], sizeof(effect_descriptor_t));
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700280 ALOGV("EffectGetDescriptor - UUID matched downmix type %d, UUID = %" PRIx32,
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800281 i, gDescriptors[i]->uuid.timeLow);
282 return 0;
283 }
284 }
285
286 return -EINVAL;
287}
288
289
290/*--- Effect Control Interface Implementation ---*/
291
292static int Downmix_Process(effect_handle_t self,
293 audio_buffer_t *inBuffer, audio_buffer_t *outBuffer) {
294
295 downmix_object_t *pDownmixer;
296 int16_t *pSrc, *pDst;
297 downmix_module_t *pDwmModule = (downmix_module_t *)self;
298
299 if (pDwmModule == NULL) {
300 return -EINVAL;
301 }
302
303 if (inBuffer == NULL || inBuffer->raw == NULL ||
304 outBuffer == NULL || outBuffer->raw == NULL ||
305 inBuffer->frameCount != outBuffer->frameCount) {
306 return -EINVAL;
307 }
308
309 pDownmixer = (downmix_object_t*) &pDwmModule->context;
310
311 if (pDownmixer->state == DOWNMIX_STATE_UNINITIALIZED) {
312 ALOGE("Downmix_Process error: trying to use an uninitialized downmixer");
313 return -EINVAL;
314 } else if (pDownmixer->state == DOWNMIX_STATE_INITIALIZED) {
315 ALOGE("Downmix_Process error: trying to use a non-configured downmixer");
316 return -ENODATA;
317 }
318
319 pSrc = inBuffer->s16;
320 pDst = outBuffer->s16;
321 size_t numFrames = outBuffer->frameCount;
322
323 const bool accumulate =
324 (pDwmModule->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700325 const uint32_t downmixInputChannelMask = pDwmModule->config.inputCfg.channels;
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800326
327 switch(pDownmixer->type) {
328
329 case DOWNMIX_TYPE_STRIP:
330 if (accumulate) {
331 while (numFrames) {
332 pDst[0] = clamp16(pDst[0] + pSrc[0]);
333 pDst[1] = clamp16(pDst[1] + pSrc[1]);
334 pSrc += pDownmixer->input_channel_count;
335 pDst += 2;
336 numFrames--;
337 }
338 } else {
339 while (numFrames) {
340 pDst[0] = pSrc[0];
341 pDst[1] = pSrc[1];
342 pSrc += pDownmixer->input_channel_count;
343 pDst += 2;
344 numFrames--;
345 }
346 }
347 break;
348
349 case DOWNMIX_TYPE_FOLD:
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700350#ifdef DOWNMIX_ALWAYS_USE_GENERIC_DOWNMIXER
351 // bypass the optimized downmix routines for the common formats
352 if (!Downmix_foldGeneric(
353 downmixInputChannelMask, pSrc, pDst, numFrames, accumulate)) {
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700354 ALOGE("Multichannel configuration 0x%" PRIx32 " is not supported", downmixInputChannelMask);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700355 return -EINVAL;
356 }
357 break;
358#endif
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800359 // optimize for the common formats
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700360 switch((downmix_input_channel_mask_t)downmixInputChannelMask) {
Jean-Michel Trividb46b482012-04-23 11:29:26 -0700361 case CHANNEL_MASK_QUAD_BACK:
362 case CHANNEL_MASK_QUAD_SIDE:
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800363 Downmix_foldFromQuad(pSrc, pDst, numFrames, accumulate);
364 break;
Jean-Michel Trividb46b482012-04-23 11:29:26 -0700365 case CHANNEL_MASK_5POINT1_BACK:
366 case CHANNEL_MASK_5POINT1_SIDE:
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800367 Downmix_foldFrom5Point1(pSrc, pDst, numFrames, accumulate);
368 break;
Glenn Kasten029a64e2014-04-15 09:58:55 -0700369 case CHANNEL_MASK_7POINT1:
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800370 Downmix_foldFrom7Point1(pSrc, pDst, numFrames, accumulate);
371 break;
372 default:
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700373 if (!Downmix_foldGeneric(
374 downmixInputChannelMask, pSrc, pDst, numFrames, accumulate)) {
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700375 ALOGE("Multichannel configuration 0x%" PRIx32 " is not supported", downmixInputChannelMask);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700376 return -EINVAL;
377 }
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800378 break;
379 }
380 break;
381
382 default:
383 return -EINVAL;
384 }
385
386 return 0;
387}
388
389
390static int Downmix_Command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
391 void *pCmdData, uint32_t *replySize, void *pReplyData) {
392
393 downmix_module_t *pDwmModule = (downmix_module_t *) self;
394 downmix_object_t *pDownmixer;
395 int retsize;
396
397 if (pDwmModule == NULL || pDwmModule->context.state == DOWNMIX_STATE_UNINITIALIZED) {
398 return -EINVAL;
399 }
400
401 pDownmixer = (downmix_object_t*) &pDwmModule->context;
402
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700403 ALOGV("Downmix_Command command %" PRIu32 " cmdSize %" PRIu32, cmdCode, cmdSize);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800404
405 switch (cmdCode) {
406 case EFFECT_CMD_INIT:
Eric Laurent0f714a42015-06-19 15:33:57 -0700407 if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800408 return -EINVAL;
409 }
410 *(int *) pReplyData = Downmix_Init(pDwmModule);
411 break;
412
413 case EFFECT_CMD_SET_CONFIG:
414 if (pCmdData == NULL || cmdSize != sizeof(effect_config_t)
Eric Laurent0f714a42015-06-19 15:33:57 -0700415 || pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800416 return -EINVAL;
417 }
418 *(int *) pReplyData = Downmix_Configure(pDwmModule,
419 (effect_config_t *)pCmdData, false);
420 break;
421
422 case EFFECT_CMD_RESET:
423 Downmix_Reset(pDownmixer, false);
424 break;
425
426 case EFFECT_CMD_GET_PARAM:
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700427 ALOGV("Downmix_Command EFFECT_CMD_GET_PARAM pCmdData %p, *replySize %" PRIu32 ", pReplyData: %p",
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800428 pCmdData, *replySize, pReplyData);
429 if (pCmdData == NULL || cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t)) ||
Eric Laurent0f714a42015-06-19 15:33:57 -0700430 pReplyData == NULL || replySize == NULL ||
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800431 *replySize < (int) sizeof(effect_param_t) + 2 * sizeof(int32_t)) {
432 return -EINVAL;
433 }
434 effect_param_t *rep = (effect_param_t *) pReplyData;
435 memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + sizeof(int32_t));
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700436 ALOGV("Downmix_Command EFFECT_CMD_GET_PARAM param %" PRId32 ", replySize %" PRIu32,
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800437 *(int32_t *)rep->data, rep->vsize);
438 rep->status = Downmix_getParameter(pDownmixer, *(int32_t *)rep->data, &rep->vsize,
439 rep->data + sizeof(int32_t));
440 *replySize = sizeof(effect_param_t) + sizeof(int32_t) + rep->vsize;
441 break;
442
443 case EFFECT_CMD_SET_PARAM:
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700444 ALOGV("Downmix_Command EFFECT_CMD_SET_PARAM cmdSize %d pCmdData %p, *replySize %" PRIu32
445 ", pReplyData %p", cmdSize, pCmdData, *replySize, pReplyData);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800446 if (pCmdData == NULL || (cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t)))
Eric Laurent0f714a42015-06-19 15:33:57 -0700447 || pReplyData == NULL || replySize == NULL || *replySize != (int)sizeof(int32_t)) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800448 return -EINVAL;
449 }
450 effect_param_t *cmd = (effect_param_t *) pCmdData;
451 *(int *)pReplyData = Downmix_setParameter(pDownmixer, *(int32_t *)cmd->data,
452 cmd->vsize, cmd->data + sizeof(int32_t));
453 break;
454
455 case EFFECT_CMD_SET_PARAM_DEFERRED:
456 //FIXME implement
457 ALOGW("Downmix_Command command EFFECT_CMD_SET_PARAM_DEFERRED not supported, FIXME");
458 break;
459
460 case EFFECT_CMD_SET_PARAM_COMMIT:
461 //FIXME implement
462 ALOGW("Downmix_Command command EFFECT_CMD_SET_PARAM_COMMIT not supported, FIXME");
463 break;
464
465 case EFFECT_CMD_ENABLE:
Eric Laurent0f714a42015-06-19 15:33:57 -0700466 if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800467 return -EINVAL;
468 }
469 if (pDownmixer->state != DOWNMIX_STATE_INITIALIZED) {
470 return -ENOSYS;
471 }
472 pDownmixer->state = DOWNMIX_STATE_ACTIVE;
473 ALOGV("EFFECT_CMD_ENABLE() OK");
474 *(int *)pReplyData = 0;
475 break;
476
477 case EFFECT_CMD_DISABLE:
Eric Laurent0f714a42015-06-19 15:33:57 -0700478 if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800479 return -EINVAL;
480 }
481 if (pDownmixer->state != DOWNMIX_STATE_ACTIVE) {
482 return -ENOSYS;
483 }
484 pDownmixer->state = DOWNMIX_STATE_INITIALIZED;
485 ALOGV("EFFECT_CMD_DISABLE() OK");
486 *(int *)pReplyData = 0;
487 break;
488
489 case EFFECT_CMD_SET_DEVICE:
490 if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) {
491 return -EINVAL;
492 }
493 // FIXME change type if playing on headset vs speaker
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700494 ALOGV("Downmix_Command EFFECT_CMD_SET_DEVICE: 0x%08" PRIx32, *(uint32_t *)pCmdData);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800495 break;
496
497 case EFFECT_CMD_SET_VOLUME: {
498 // audio output is always stereo => 2 channel volumes
499 if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t) * 2) {
500 return -EINVAL;
501 }
502 // FIXME change volume
503 ALOGW("Downmix_Command command EFFECT_CMD_SET_VOLUME not supported, FIXME");
504 float left = (float)(*(uint32_t *)pCmdData) / (1 << 24);
505 float right = (float)(*((uint32_t *)pCmdData + 1)) / (1 << 24);
506 ALOGV("Downmix_Command EFFECT_CMD_SET_VOLUME: left %f, right %f ", left, right);
507 break;
508 }
509
510 case EFFECT_CMD_SET_AUDIO_MODE:
511 if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) {
512 return -EINVAL;
513 }
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700514 ALOGV("Downmix_Command EFFECT_CMD_SET_AUDIO_MODE: %" PRIu32, *(uint32_t *)pCmdData);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800515 break;
516
517 case EFFECT_CMD_SET_CONFIG_REVERSE:
518 case EFFECT_CMD_SET_INPUT_DEVICE:
519 // these commands are ignored by a downmix effect
520 break;
521
522 default:
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700523 ALOGW("Downmix_Command invalid command %" PRIu32, cmdCode);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800524 return -EINVAL;
525 }
526
527 return 0;
528}
529
530
531int Downmix_GetDescriptor(effect_handle_t self, effect_descriptor_t *pDescriptor)
532{
533 downmix_module_t *pDwnmxModule = (downmix_module_t *) self;
534
535 if (pDwnmxModule == NULL ||
536 pDwnmxModule->context.state == DOWNMIX_STATE_UNINITIALIZED) {
537 return -EINVAL;
538 }
539
540 memcpy(pDescriptor, &gDownmixDescriptor, sizeof(effect_descriptor_t));
541
542 return 0;
543}
544
545
546/*----------------------------------------------------------------------------
547 * Downmix internal functions
548 *--------------------------------------------------------------------------*/
549
550/*----------------------------------------------------------------------------
551 * Downmix_Init()
552 *----------------------------------------------------------------------------
553 * Purpose:
554 * Initialize downmix context and apply default parameters
555 *
556 * Inputs:
557 * pDwmModule pointer to downmix effect module
558 *
559 * Outputs:
560 *
561 * Returns:
562 * 0 indicates success
563 *
564 * Side Effects:
565 * updates:
566 * pDwmModule->context.type
567 * pDwmModule->context.apply_volume_correction
568 * pDwmModule->config.inputCfg
569 * pDwmModule->config.outputCfg
570 * pDwmModule->config.inputCfg.samplingRate
571 * pDwmModule->config.outputCfg.samplingRate
572 * pDwmModule->context.state
573 * doesn't set:
574 * pDwmModule->itfe
575 *
576 *----------------------------------------------------------------------------
577 */
578
579int Downmix_Init(downmix_module_t *pDwmModule) {
580
581 ALOGV("Downmix_Init module %p", pDwmModule);
582 int ret = 0;
583
584 memset(&pDwmModule->context, 0, sizeof(downmix_object_t));
585
586 pDwmModule->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
587 pDwmModule->config.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
588 pDwmModule->config.inputCfg.channels = AUDIO_CHANNEL_OUT_7POINT1;
589 pDwmModule->config.inputCfg.bufferProvider.getBuffer = NULL;
590 pDwmModule->config.inputCfg.bufferProvider.releaseBuffer = NULL;
591 pDwmModule->config.inputCfg.bufferProvider.cookie = NULL;
592 pDwmModule->config.inputCfg.mask = EFFECT_CONFIG_ALL;
593
594 pDwmModule->config.inputCfg.samplingRate = 44100;
595 pDwmModule->config.outputCfg.samplingRate = pDwmModule->config.inputCfg.samplingRate;
596
597 // set a default value for the access mode, but should be overwritten by caller
598 pDwmModule->config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
599 pDwmModule->config.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
600 pDwmModule->config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
601 pDwmModule->config.outputCfg.bufferProvider.getBuffer = NULL;
602 pDwmModule->config.outputCfg.bufferProvider.releaseBuffer = NULL;
603 pDwmModule->config.outputCfg.bufferProvider.cookie = NULL;
604 pDwmModule->config.outputCfg.mask = EFFECT_CONFIG_ALL;
605
606 ret = Downmix_Configure(pDwmModule, &pDwmModule->config, true);
607 if (ret != 0) {
608 ALOGV("Downmix_Init error %d on module %p", ret, pDwmModule);
609 } else {
610 pDwmModule->context.state = DOWNMIX_STATE_INITIALIZED;
611 }
612
613 return ret;
614}
615
616
617/*----------------------------------------------------------------------------
618 * Downmix_Configure()
619 *----------------------------------------------------------------------------
620 * Purpose:
621 * Set input and output audio configuration.
622 *
623 * Inputs:
624 * pDwmModule pointer to downmix effect module
625 * pConfig pointer to effect_config_t structure containing input
626 * and output audio parameters configuration
627 * init true if called from init function
628 *
629 * Outputs:
630 *
631 * Returns:
632 * 0 indicates success
633 *
634 * Side Effects:
635 *
636 *----------------------------------------------------------------------------
637 */
638
639int Downmix_Configure(downmix_module_t *pDwmModule, effect_config_t *pConfig, bool init) {
640
641 downmix_object_t *pDownmixer = &pDwmModule->context;
642
643 // Check configuration compatibility with build options, and effect capabilities
644 if (pConfig->inputCfg.samplingRate != pConfig->outputCfg.samplingRate
645 || pConfig->outputCfg.channels != DOWNMIX_OUTPUT_CHANNELS
646 || pConfig->inputCfg.format != AUDIO_FORMAT_PCM_16_BIT
647 || pConfig->outputCfg.format != AUDIO_FORMAT_PCM_16_BIT) {
648 ALOGE("Downmix_Configure error: invalid config");
649 return -EINVAL;
650 }
651
Chih-Wei Huanga93c8c92013-01-10 20:52:57 +0800652 if (&pDwmModule->config != pConfig) {
653 memcpy(&pDwmModule->config, pConfig, sizeof(effect_config_t));
654 }
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800655
656 if (init) {
657 pDownmixer->type = DOWNMIX_TYPE_FOLD;
658 pDownmixer->apply_volume_correction = false;
659 pDownmixer->input_channel_count = 8; // matches default input of AUDIO_CHANNEL_OUT_7POINT1
660 } else {
Haynes Mathew Georgeee507ef2015-07-15 11:06:05 -0700661 // when configuring the effect, do not allow a blank or unsupported channel mask
662 if (!Downmix_validChannelMask(pConfig->inputCfg.channels)) {
663 ALOGE("Downmix_Configure error: input channel mask(0x%x) not supported",
664 pConfig->inputCfg.channels);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800665 return -EINVAL;
666 }
Andy Hunge5412692014-05-16 11:25:07 -0700667 pDownmixer->input_channel_count =
668 audio_channel_count_from_out_mask(pConfig->inputCfg.channels);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800669 }
670
671 Downmix_Reset(pDownmixer, init);
672
673 return 0;
674}
675
676
677/*----------------------------------------------------------------------------
678 * Downmix_Reset()
679 *----------------------------------------------------------------------------
680 * Purpose:
681 * Reset internal states.
682 *
683 * Inputs:
684 * pDownmixer pointer to downmix context
685 * init true if called from init function
686 *
687 * Outputs:
688*
689 * Returns:
690 * 0 indicates success
691 *
692 * Side Effects:
693 *
694 *----------------------------------------------------------------------------
695 */
696
Eric Laurent0f714a42015-06-19 15:33:57 -0700697int Downmix_Reset(downmix_object_t *pDownmixer __unused, bool init __unused) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800698 // nothing to do here
699 return 0;
700}
701
702
703/*----------------------------------------------------------------------------
704 * Downmix_setParameter()
705 *----------------------------------------------------------------------------
706 * Purpose:
707 * Set a Downmix parameter
708 *
709 * Inputs:
710 * pDownmixer handle to instance data
711 * param parameter
712 * pValue pointer to parameter value
713 * size value size
714 *
715 * Outputs:
716 *
717 * Returns:
718 * 0 indicates success
719 *
720 * Side Effects:
721 *
722 *----------------------------------------------------------------------------
723 */
Ashok Bhatb302bd52014-02-18 11:40:00 +0000724int Downmix_setParameter(downmix_object_t *pDownmixer, int32_t param, uint32_t size, void *pValue) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800725
726 int16_t value16;
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700727 ALOGV("Downmix_setParameter, context %p, param %" PRId32 ", value16 %" PRId16 ", value32 %" PRId32,
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800728 pDownmixer, param, *(int16_t *)pValue, *(int32_t *)pValue);
729
730 switch (param) {
731
732 case DOWNMIX_PARAM_TYPE:
733 if (size != sizeof(downmix_type_t)) {
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700734 ALOGE("Downmix_setParameter(DOWNMIX_PARAM_TYPE) invalid size %" PRIu32 ", should be %zu",
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800735 size, sizeof(downmix_type_t));
736 return -EINVAL;
737 }
738 value16 = *(int16_t *)pValue;
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700739 ALOGV("set DOWNMIX_PARAM_TYPE, type %" PRId16, value16);
Jean-Michel Trivi7d5b2622012-04-04 18:54:36 -0700740 if (!((value16 > DOWNMIX_TYPE_INVALID) && (value16 <= DOWNMIX_TYPE_LAST))) {
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700741 ALOGE("Downmix_setParameter invalid DOWNMIX_PARAM_TYPE value %" PRId16, value16);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800742 return -EINVAL;
743 } else {
744 pDownmixer->type = (downmix_type_t) value16;
745 break;
746
747 default:
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700748 ALOGE("Downmix_setParameter unknown parameter %" PRId32, param);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800749 return -EINVAL;
750 }
751}
752
753 return 0;
754} /* end Downmix_setParameter */
755
756
757/*----------------------------------------------------------------------------
758 * Downmix_getParameter()
759 *----------------------------------------------------------------------------
760 * Purpose:
761 * Get a Downmix parameter
762 *
763 * Inputs:
764 * pDownmixer handle to instance data
765 * param parameter
766 * pValue pointer to variable to hold retrieved value
767 * pSize pointer to value size: maximum size as input
768 *
769 * Outputs:
770 * *pValue updated with parameter value
771 * *pSize updated with actual value size
772 *
773 * Returns:
774 * 0 indicates success
775 *
776 * Side Effects:
777 *
778 *----------------------------------------------------------------------------
779 */
Ashok Bhatb302bd52014-02-18 11:40:00 +0000780int Downmix_getParameter(downmix_object_t *pDownmixer, int32_t param, uint32_t *pSize, void *pValue) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800781 int16_t *pValue16;
782
783 switch (param) {
784
785 case DOWNMIX_PARAM_TYPE:
786 if (*pSize < sizeof(int16_t)) {
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700787 ALOGE("Downmix_getParameter invalid parameter size %" PRIu32 " for DOWNMIX_PARAM_TYPE", *pSize);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800788 return -EINVAL;
789 }
790 pValue16 = (int16_t *)pValue;
791 *pValue16 = (int16_t) pDownmixer->type;
792 *pSize = sizeof(int16_t);
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700793 ALOGV("Downmix_getParameter DOWNMIX_PARAM_TYPE is %" PRId16, *pValue16);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800794 break;
795
796 default:
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700797 ALOGE("Downmix_getParameter unknown parameter %" PRId16, param);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800798 return -EINVAL;
799 }
800
801 return 0;
802} /* end Downmix_getParameter */
803
804
805/*----------------------------------------------------------------------------
806 * Downmix_foldFromQuad()
807 *----------------------------------------------------------------------------
808 * Purpose:
809 * downmix a quad signal to stereo
810 *
811 * Inputs:
812 * pSrc quad audio samples to downmix
813 * numFrames the number of quad frames to downmix
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700814 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
815 * or overwrite pDst (when false)
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800816 *
817 * Outputs:
818 * pDst downmixed stereo audio samples
819 *
820 *----------------------------------------------------------------------------
821 */
822void Downmix_foldFromQuad(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
823 // sample at index 0 is FL
824 // sample at index 1 is FR
825 // sample at index 2 is RL
826 // sample at index 3 is RR
827 if (accumulate) {
828 while (numFrames) {
829 // FL + RL
Jean-Michel Triviaea27152012-05-24 10:11:07 -0700830 pDst[0] = clamp16(pDst[0] + ((pSrc[0] + pSrc[2]) >> 1));
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800831 // FR + RR
Jean-Michel Triviaea27152012-05-24 10:11:07 -0700832 pDst[1] = clamp16(pDst[1] + ((pSrc[1] + pSrc[3]) >> 1));
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800833 pSrc += 4;
834 pDst += 2;
835 numFrames--;
836 }
837 } else { // same code as above but without adding and clamping pDst[i] to itself
838 while (numFrames) {
839 // FL + RL
Jean-Michel Triviaea27152012-05-24 10:11:07 -0700840 pDst[0] = clamp16((pSrc[0] + pSrc[2]) >> 1);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800841 // FR + RR
Jean-Michel Triviaea27152012-05-24 10:11:07 -0700842 pDst[1] = clamp16((pSrc[1] + pSrc[3]) >> 1);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800843 pSrc += 4;
844 pDst += 2;
845 numFrames--;
846 }
847 }
848}
849
850
851/*----------------------------------------------------------------------------
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800852 * Downmix_foldFrom5Point1()
853 *----------------------------------------------------------------------------
854 * Purpose:
855 * downmix a 5.1 signal to stereo
856 *
857 * Inputs:
858 * pSrc 5.1 audio samples to downmix
859 * numFrames the number of 5.1 frames to downmix
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700860 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
861 * or overwrite pDst (when false)
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800862 *
863 * Outputs:
864 * pDst downmixed stereo audio samples
865 *
866 *----------------------------------------------------------------------------
867 */
868void Downmix_foldFrom5Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
869 int32_t lt, rt, centerPlusLfeContrib; // samples in Q19.12 format
870 // sample at index 0 is FL
871 // sample at index 1 is FR
872 // sample at index 2 is FC
873 // sample at index 3 is LFE
874 // sample at index 4 is RL
875 // sample at index 5 is RR
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700876 // code is mostly duplicated between the two values of accumulate to avoid repeating the test
877 // for every sample
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800878 if (accumulate) {
879 while (numFrames) {
880 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
881 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
882 + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
883 // FL + centerPlusLfeContrib + RL
884 lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[4] << 12);
885 // FR + centerPlusLfeContrib + RR
886 rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[5] << 12);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700887 // accumulate in destination
Jean-Michel Triviaea27152012-05-24 10:11:07 -0700888 pDst[0] = clamp16(pDst[0] + (lt >> 13));
889 pDst[1] = clamp16(pDst[1] + (rt >> 13));
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800890 pSrc += 6;
891 pDst += 2;
892 numFrames--;
893 }
894 } else { // same code as above but without adding and clamping pDst[i] to itself
895 while (numFrames) {
896 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
897 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
898 + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
899 // FL + centerPlusLfeContrib + RL
900 lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[4] << 12);
901 // FR + centerPlusLfeContrib + RR
902 rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[5] << 12);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700903 // store in destination
Jean-Michel Triviaea27152012-05-24 10:11:07 -0700904 pDst[0] = clamp16(lt >> 13); // differs from when accumulate is true above
905 pDst[1] = clamp16(rt >> 13); // differs from when accumulate is true above
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800906 pSrc += 6;
907 pDst += 2;
908 numFrames--;
909 }
910 }
911}
912
913
914/*----------------------------------------------------------------------------
915 * Downmix_foldFrom7Point1()
916 *----------------------------------------------------------------------------
917 * Purpose:
918 * downmix a 7.1 signal to stereo
919 *
920 * Inputs:
921 * pSrc 7.1 audio samples to downmix
922 * numFrames the number of 7.1 frames to downmix
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700923 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
924 * or overwrite pDst (when false)
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800925 *
926 * Outputs:
927 * pDst downmixed stereo audio samples
928 *
929 *----------------------------------------------------------------------------
930 */
931void Downmix_foldFrom7Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
932 int32_t lt, rt, centerPlusLfeContrib; // samples in Q19.12 format
933 // sample at index 0 is FL
934 // sample at index 1 is FR
935 // sample at index 2 is FC
936 // sample at index 3 is LFE
937 // sample at index 4 is RL
938 // sample at index 5 is RR
939 // sample at index 6 is SL
940 // sample at index 7 is SR
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700941 // code is mostly duplicated between the two values of accumulate to avoid repeating the test
942 // for every sample
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800943 if (accumulate) {
944 while (numFrames) {
945 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
946 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
947 + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
948 // FL + centerPlusLfeContrib + SL + RL
949 lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[6] << 12) + (pSrc[4] << 12);
950 // FR + centerPlusLfeContrib + SR + RR
951 rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[7] << 12) + (pSrc[5] << 12);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700952 //accumulate in destination
Jean-Michel Triviaea27152012-05-24 10:11:07 -0700953 pDst[0] = clamp16(pDst[0] + (lt >> 13));
954 pDst[1] = clamp16(pDst[1] + (rt >> 13));
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800955 pSrc += 8;
956 pDst += 2;
957 numFrames--;
958 }
959 } else { // same code as above but without adding and clamping pDst[i] to itself
960 while (numFrames) {
961 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
962 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
963 + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
964 // FL + centerPlusLfeContrib + SL + RL
965 lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[6] << 12) + (pSrc[4] << 12);
966 // FR + centerPlusLfeContrib + SR + RR
967 rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[7] << 12) + (pSrc[5] << 12);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700968 // store in destination
Jean-Michel Triviaea27152012-05-24 10:11:07 -0700969 pDst[0] = clamp16(lt >> 13); // differs from when accumulate is true above
970 pDst[1] = clamp16(rt >> 13); // differs from when accumulate is true above
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800971 pSrc += 8;
972 pDst += 2;
973 numFrames--;
974 }
975 }
976}
977
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700978
979/*----------------------------------------------------------------------------
980 * Downmix_foldGeneric()
981 *----------------------------------------------------------------------------
982 * Purpose:
983 * downmix to stereo a multichannel signal whose format is:
984 * - has FL/FR
985 * - if using AUDIO_CHANNEL_OUT_SIDE*, it contains both left and right
986 * - if using AUDIO_CHANNEL_OUT_BACK*, it contains both left and right
987 * - doesn't use any of the AUDIO_CHANNEL_OUT_TOP* channels
988 * - doesn't use any of the AUDIO_CHANNEL_OUT_FRONT_*_OF_CENTER channels
989 * Only handles channel masks not enumerated in downmix_input_channel_mask_t
990 *
991 * Inputs:
992 * mask the channel mask of pSrc
993 * pSrc multichannel audio buffer to downmix
994 * numFrames the number of multichannel frames to downmix
995 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
996 * or overwrite pDst (when false)
997 *
998 * Outputs:
999 * pDst downmixed stereo audio samples
1000 *
1001 * Returns: false if multichannel format is not supported
1002 *
1003 *----------------------------------------------------------------------------
1004 */
1005bool Downmix_foldGeneric(
1006 uint32_t mask, int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
Haynes Mathew Georgeee507ef2015-07-15 11:06:05 -07001007
1008 if (!Downmix_validChannelMask(mask)) {
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -07001009 return false;
1010 }
Haynes Mathew Georgeee507ef2015-07-15 11:06:05 -07001011
1012 const bool hasSides = (mask & kSides) != 0;
1013 const bool hasBacks = (mask & kBacks) != 0;
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -07001014
Andy Hunge5412692014-05-16 11:25:07 -07001015 const int numChan = audio_channel_count_from_out_mask(mask);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -07001016 const bool hasFC = ((mask & AUDIO_CHANNEL_OUT_FRONT_CENTER) == AUDIO_CHANNEL_OUT_FRONT_CENTER);
1017 const bool hasLFE =
1018 ((mask & AUDIO_CHANNEL_OUT_LOW_FREQUENCY) == AUDIO_CHANNEL_OUT_LOW_FREQUENCY);
1019 const bool hasBC = ((mask & AUDIO_CHANNEL_OUT_BACK_CENTER) == AUDIO_CHANNEL_OUT_BACK_CENTER);
1020 // compute at what index each channel is: samples will be in the following order:
1021 // FL FR FC LFE BL BR BC SL SR
1022 // when a channel is not present, its index is set to the same as the index of the preceding
1023 // channel
1024 const int indexFC = hasFC ? 2 : 1; // front center
1025 const int indexLFE = hasLFE ? indexFC + 1 : indexFC; // low frequency
1026 const int indexBL = hasBacks ? indexLFE + 1 : indexLFE; // back left
1027 const int indexBR = hasBacks ? indexBL + 1 : indexBL; // back right
1028 const int indexBC = hasBC ? indexBR + 1 : indexBR; // back center
1029 const int indexSL = hasSides ? indexBC + 1 : indexBC; // side left
1030 const int indexSR = hasSides ? indexSL + 1 : indexSL; // side right
1031
1032 int32_t lt, rt, centersLfeContrib; // samples in Q19.12 format
1033 // code is mostly duplicated between the two values of accumulate to avoid repeating the test
1034 // for every sample
1035 if (accumulate) {
1036 while (numFrames) {
1037 // compute contribution of FC, BC and LFE
1038 centersLfeContrib = 0;
1039 if (hasFC) { centersLfeContrib += pSrc[indexFC]; }
1040 if (hasLFE) { centersLfeContrib += pSrc[indexLFE]; }
1041 if (hasBC) { centersLfeContrib += pSrc[indexBC]; }
1042 centersLfeContrib *= MINUS_3_DB_IN_Q19_12;
1043 // always has FL/FR
1044 lt = (pSrc[0] << 12);
1045 rt = (pSrc[1] << 12);
1046 // mix in sides and backs
1047 if (hasSides) {
1048 lt += pSrc[indexSL] << 12;
1049 rt += pSrc[indexSR] << 12;
1050 }
1051 if (hasBacks) {
1052 lt += pSrc[indexBL] << 12;
1053 rt += pSrc[indexBR] << 12;
1054 }
1055 lt += centersLfeContrib;
1056 rt += centersLfeContrib;
1057 // accumulate in destination
Jean-Michel Triviaea27152012-05-24 10:11:07 -07001058 pDst[0] = clamp16(pDst[0] + (lt >> 13));
1059 pDst[1] = clamp16(pDst[1] + (rt >> 13));
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -07001060 pSrc += numChan;
1061 pDst += 2;
1062 numFrames--;
1063 }
1064 } else {
1065 while (numFrames) {
1066 // compute contribution of FC, BC and LFE
1067 centersLfeContrib = 0;
1068 if (hasFC) { centersLfeContrib += pSrc[indexFC]; }
1069 if (hasLFE) { centersLfeContrib += pSrc[indexLFE]; }
1070 if (hasBC) { centersLfeContrib += pSrc[indexBC]; }
1071 centersLfeContrib *= MINUS_3_DB_IN_Q19_12;
1072 // always has FL/FR
1073 lt = (pSrc[0] << 12);
1074 rt = (pSrc[1] << 12);
1075 // mix in sides and backs
1076 if (hasSides) {
1077 lt += pSrc[indexSL] << 12;
1078 rt += pSrc[indexSR] << 12;
1079 }
1080 if (hasBacks) {
1081 lt += pSrc[indexBL] << 12;
1082 rt += pSrc[indexBR] << 12;
1083 }
1084 lt += centersLfeContrib;
1085 rt += centersLfeContrib;
1086 // store in destination
Jean-Michel Triviaea27152012-05-24 10:11:07 -07001087 pDst[0] = clamp16(lt >> 13); // differs from when accumulate is true above
1088 pDst[1] = clamp16(rt >> 13); // differs from when accumulate is true above
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -07001089 pSrc += numChan;
1090 pDst += 2;
1091 numFrames--;
1092 }
1093 }
1094 return true;
1095}