blob: 1663d475563684c5b3b8d60432511bca7eea3dd2 [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
Jean-Michel Trividb46b482012-04-23 11:29:26 -070033typedef enum {
34 CHANNEL_MASK_SURROUND = AUDIO_CHANNEL_OUT_SURROUND,
35 CHANNEL_MASK_QUAD_BACK = AUDIO_CHANNEL_OUT_QUAD,
36 // like AUDIO_CHANNEL_OUT_QUAD with *_SIDE_* instead of *_BACK_*, same channel order
37 CHANNEL_MASK_QUAD_SIDE =
38 AUDIO_CHANNEL_OUT_FRONT_LEFT |
39 AUDIO_CHANNEL_OUT_FRONT_RIGHT |
40 AUDIO_CHANNEL_OUT_SIDE_LEFT |
41 AUDIO_CHANNEL_OUT_SIDE_RIGHT,
42 CHANNEL_MASK_5POINT1_BACK = AUDIO_CHANNEL_OUT_5POINT1,
43 // like AUDIO_CHANNEL_OUT_5POINT1 with *_SIDE_* instead of *_BACK_*, same channel order
44 CHANNEL_MASK_5POINT1_SIDE =
45 AUDIO_CHANNEL_OUT_FRONT_LEFT |
46 AUDIO_CHANNEL_OUT_FRONT_RIGHT |
47 AUDIO_CHANNEL_OUT_FRONT_CENTER |
48 AUDIO_CHANNEL_OUT_LOW_FREQUENCY |
49 AUDIO_CHANNEL_OUT_SIDE_LEFT |
50 AUDIO_CHANNEL_OUT_SIDE_RIGHT,
51 CHANNEL_MASK_7POINT1_SIDE_BACK = AUDIO_CHANNEL_OUT_7POINT1,
52} downmix_input_channel_mask_t;
53
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -080054// effect_handle_t interface implementation for downmix effect
55const struct effect_interface_s gDownmixInterface = {
56 Downmix_Process,
57 Downmix_Command,
58 Downmix_GetDescriptor,
59 NULL /* no process_reverse function, no reference stream needed */
60};
61
Marco Nelissen7f16b192012-10-25 16:05:57 -070062// This is the only symbol that needs to be exported
63__attribute__ ((visibility ("default")))
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -080064audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
synergydevc9d8ea72013-10-19 22:51:33 -070065 .tag = AUDIO_EFFECT_LIBRARY_TAG,
66 .version = EFFECT_LIBRARY_API_VERSION,
67 .name = "Downmix Library",
68 .implementor = "The Android Open Source Project",
69 .create_effect = DownmixLib_Create,
70 .release_effect = DownmixLib_Release,
71 .get_descriptor = DownmixLib_GetDescriptor,
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -080072};
73
74
75// AOSP insert downmix UUID: 93f04452-e4fe-41cc-91f9-e475b6d1d69f
76static const effect_descriptor_t gDownmixDescriptor = {
77 EFFECT_UIID_DOWNMIX__, //type
78 {0x93f04452, 0xe4fe, 0x41cc, 0x91f9, {0xe4, 0x75, 0xb6, 0xd1, 0xd6, 0x9f}}, // uuid
79 EFFECT_CONTROL_API_VERSION,
80 EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST,
81 0, //FIXME what value should be reported? // cpu load
82 0, //FIXME what value should be reported? // memory usage
83 "Multichannel Downmix To Stereo", // human readable effect name
84 "The Android Open Source Project" // human readable effect implementor name
85};
86
87// gDescriptors contains pointers to all defined effect descriptor in this library
88static const effect_descriptor_t * const gDescriptors[] = {
89 &gDownmixDescriptor
90};
91
92// number of effects in this library
93const int kNbEffects = sizeof(gDescriptors) / sizeof(const effect_descriptor_t *);
94
95
96/*----------------------------------------------------------------------------
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -070097 * Test code
98 *--------------------------------------------------------------------------*/
99#ifdef DOWNMIX_TEST_CHANNEL_INDEX
100// strictly for testing, logs the indices of the channels for a given mask,
101// uses the same code as Downmix_foldGeneric()
102void Downmix_testIndexComputation(uint32_t mask) {
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700103 ALOGI("Testing index computation for 0x%" PRIx32 ":", mask);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700104 // check against unsupported channels
105 if (mask & kUnsupported) {
106 ALOGE("Unsupported channels (top or front left/right of center)");
107 return;
108 }
109 // verify has FL/FR
110 if ((mask & AUDIO_CHANNEL_OUT_STEREO) != AUDIO_CHANNEL_OUT_STEREO) {
111 ALOGE("Front channels must be present");
112 return;
113 }
114 // verify uses SIDE as a pair (ok if not using SIDE at all)
115 bool hasSides = false;
116 if ((mask & kSides) != 0) {
117 if ((mask & kSides) != kSides) {
118 ALOGE("Side channels must be used as a pair");
119 return;
120 }
121 hasSides = true;
122 }
123 // verify uses BACK as a pair (ok if not using BACK at all)
124 bool hasBacks = false;
125 if ((mask & kBacks) != 0) {
126 if ((mask & kBacks) != kBacks) {
127 ALOGE("Back channels must be used as a pair");
128 return;
129 }
130 hasBacks = true;
131 }
132
133 const int numChan = popcount(mask);
134 const bool hasFC = ((mask & AUDIO_CHANNEL_OUT_FRONT_CENTER) == AUDIO_CHANNEL_OUT_FRONT_CENTER);
135 const bool hasLFE =
136 ((mask & AUDIO_CHANNEL_OUT_LOW_FREQUENCY) == AUDIO_CHANNEL_OUT_LOW_FREQUENCY);
137 const bool hasBC = ((mask & AUDIO_CHANNEL_OUT_BACK_CENTER) == AUDIO_CHANNEL_OUT_BACK_CENTER);
138 // compute at what index each channel is: samples will be in the following order:
139 // FL FR FC LFE BL BR BC SL SR
140 // when a channel is not present, its index is set to the same as the index of the preceding
141 // channel
142 const int indexFC = hasFC ? 2 : 1; // front center
143 const int indexLFE = hasLFE ? indexFC + 1 : indexFC; // low frequency
144 const int indexBL = hasBacks ? indexLFE + 1 : indexLFE; // back left
145 const int indexBR = hasBacks ? indexBL + 1 : indexBL; // back right
146 const int indexBC = hasBC ? indexBR + 1 : indexBR; // back center
147 const int indexSL = hasSides ? indexBC + 1 : indexBC; // side left
148 const int indexSR = hasSides ? indexSL + 1 : indexSL; // side right
149
150 ALOGI(" FL FR FC LFE BL BR BC SL SR");
151 ALOGI(" %d %d %d %d %d %d %d %d %d",
152 0, 1, indexFC, indexLFE, indexBL, indexBR, indexBC, indexSL, indexSR);
153}
154#endif
155
156
157/*----------------------------------------------------------------------------
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800158 * Effect API implementation
159 *--------------------------------------------------------------------------*/
160
161/*--- Effect Library Interface Implementation ---*/
162
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800163int32_t DownmixLib_Create(const effect_uuid_t *uuid,
164 int32_t sessionId,
165 int32_t ioId,
166 effect_handle_t *pHandle) {
167 int ret;
168 int i;
169 downmix_module_t *module;
170 const effect_descriptor_t *desc;
171
172 ALOGV("DownmixLib_Create()");
173
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700174#ifdef DOWNMIX_TEST_CHANNEL_INDEX
175 // should work (won't log an error)
176 ALOGI("DOWNMIX_TEST_CHANNEL_INDEX: should work:");
177 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT |
178 AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_BACK_CENTER);
179 Downmix_testIndexComputation(CHANNEL_MASK_QUAD_SIDE | CHANNEL_MASK_QUAD_BACK);
180 Downmix_testIndexComputation(CHANNEL_MASK_5POINT1_SIDE | AUDIO_CHANNEL_OUT_BACK_CENTER);
181 Downmix_testIndexComputation(CHANNEL_MASK_5POINT1_BACK | AUDIO_CHANNEL_OUT_BACK_CENTER);
182 // shouldn't work (will log an error, won't display channel indices)
183 ALOGI("DOWNMIX_TEST_CHANNEL_INDEX: should NOT work:");
184 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT |
185 AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_BACK_LEFT);
186 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT |
187 AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_SIDE_LEFT);
188 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT |
189 AUDIO_CHANNEL_OUT_BACK_LEFT | AUDIO_CHANNEL_OUT_BACK_RIGHT);
190 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT |
191 AUDIO_CHANNEL_OUT_SIDE_LEFT | AUDIO_CHANNEL_OUT_SIDE_RIGHT);
192#endif
193
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800194 if (pHandle == NULL || uuid == NULL) {
195 return -EINVAL;
196 }
197
198 for (i = 0 ; i < kNbEffects ; i++) {
199 desc = gDescriptors[i];
200 if (memcmp(uuid, &desc->uuid, sizeof(effect_uuid_t)) == 0) {
201 break;
202 }
203 }
204
205 if (i == kNbEffects) {
206 return -ENOENT;
207 }
208
209 module = malloc(sizeof(downmix_module_t));
210
211 module->itfe = &gDownmixInterface;
212
213 module->context.state = DOWNMIX_STATE_UNINITIALIZED;
214
215 ret = Downmix_Init(module);
216 if (ret < 0) {
217 ALOGW("DownmixLib_Create() init failed");
218 free(module);
219 return ret;
220 }
221
222 *pHandle = (effect_handle_t) module;
223
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700224 ALOGV("DownmixLib_Create() %p , size %zu", module, sizeof(downmix_module_t));
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800225
226 return 0;
227}
228
229
230int32_t DownmixLib_Release(effect_handle_t handle) {
231 downmix_module_t *pDwmModule = (downmix_module_t *)handle;
232
233 ALOGV("DownmixLib_Release() %p", handle);
234 if (handle == NULL) {
235 return -EINVAL;
236 }
237
238 pDwmModule->context.state = DOWNMIX_STATE_UNINITIALIZED;
239
240 free(pDwmModule);
241 return 0;
242}
243
244
245int32_t DownmixLib_GetDescriptor(const effect_uuid_t *uuid, effect_descriptor_t *pDescriptor) {
246 ALOGV("DownmixLib_GetDescriptor()");
247 int i;
248
249 if (pDescriptor == NULL || uuid == NULL){
250 ALOGE("DownmixLib_Create() called with NULL pointer");
251 return -EINVAL;
252 }
253 ALOGV("DownmixLib_GetDescriptor() nb effects=%d", kNbEffects);
254 for (i = 0; i < kNbEffects; i++) {
255 ALOGV("DownmixLib_GetDescriptor() i=%d", i);
256 if (memcmp(uuid, &gDescriptors[i]->uuid, sizeof(effect_uuid_t)) == 0) {
257 memcpy(pDescriptor, gDescriptors[i], sizeof(effect_descriptor_t));
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700258 ALOGV("EffectGetDescriptor - UUID matched downmix type %d, UUID = %" PRIx32,
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800259 i, gDescriptors[i]->uuid.timeLow);
260 return 0;
261 }
262 }
263
264 return -EINVAL;
265}
266
267
268/*--- Effect Control Interface Implementation ---*/
269
270static int Downmix_Process(effect_handle_t self,
271 audio_buffer_t *inBuffer, audio_buffer_t *outBuffer) {
272
273 downmix_object_t *pDownmixer;
274 int16_t *pSrc, *pDst;
275 downmix_module_t *pDwmModule = (downmix_module_t *)self;
276
277 if (pDwmModule == NULL) {
278 return -EINVAL;
279 }
280
281 if (inBuffer == NULL || inBuffer->raw == NULL ||
282 outBuffer == NULL || outBuffer->raw == NULL ||
283 inBuffer->frameCount != outBuffer->frameCount) {
284 return -EINVAL;
285 }
286
287 pDownmixer = (downmix_object_t*) &pDwmModule->context;
288
289 if (pDownmixer->state == DOWNMIX_STATE_UNINITIALIZED) {
290 ALOGE("Downmix_Process error: trying to use an uninitialized downmixer");
291 return -EINVAL;
292 } else if (pDownmixer->state == DOWNMIX_STATE_INITIALIZED) {
293 ALOGE("Downmix_Process error: trying to use a non-configured downmixer");
294 return -ENODATA;
295 }
296
297 pSrc = inBuffer->s16;
298 pDst = outBuffer->s16;
299 size_t numFrames = outBuffer->frameCount;
300
301 const bool accumulate =
302 (pDwmModule->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700303 const uint32_t downmixInputChannelMask = pDwmModule->config.inputCfg.channels;
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800304
305 switch(pDownmixer->type) {
306
307 case DOWNMIX_TYPE_STRIP:
308 if (accumulate) {
309 while (numFrames) {
310 pDst[0] = clamp16(pDst[0] + pSrc[0]);
311 pDst[1] = clamp16(pDst[1] + pSrc[1]);
312 pSrc += pDownmixer->input_channel_count;
313 pDst += 2;
314 numFrames--;
315 }
316 } else {
317 while (numFrames) {
318 pDst[0] = pSrc[0];
319 pDst[1] = pSrc[1];
320 pSrc += pDownmixer->input_channel_count;
321 pDst += 2;
322 numFrames--;
323 }
324 }
325 break;
326
327 case DOWNMIX_TYPE_FOLD:
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700328#ifdef DOWNMIX_ALWAYS_USE_GENERIC_DOWNMIXER
329 // bypass the optimized downmix routines for the common formats
330 if (!Downmix_foldGeneric(
331 downmixInputChannelMask, pSrc, pDst, numFrames, accumulate)) {
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700332 ALOGE("Multichannel configuration 0x%" PRIx32 " is not supported", downmixInputChannelMask);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700333 return -EINVAL;
334 }
335 break;
336#endif
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800337 // optimize for the common formats
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700338 switch((downmix_input_channel_mask_t)downmixInputChannelMask) {
Jean-Michel Trividb46b482012-04-23 11:29:26 -0700339 case CHANNEL_MASK_QUAD_BACK:
340 case CHANNEL_MASK_QUAD_SIDE:
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800341 Downmix_foldFromQuad(pSrc, pDst, numFrames, accumulate);
342 break;
Jean-Michel Trividb46b482012-04-23 11:29:26 -0700343 case CHANNEL_MASK_SURROUND:
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800344 Downmix_foldFromSurround(pSrc, pDst, numFrames, accumulate);
345 break;
Jean-Michel Trividb46b482012-04-23 11:29:26 -0700346 case CHANNEL_MASK_5POINT1_BACK:
347 case CHANNEL_MASK_5POINT1_SIDE:
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800348 Downmix_foldFrom5Point1(pSrc, pDst, numFrames, accumulate);
349 break;
Jean-Michel Trividb46b482012-04-23 11:29:26 -0700350 case CHANNEL_MASK_7POINT1_SIDE_BACK:
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800351 Downmix_foldFrom7Point1(pSrc, pDst, numFrames, accumulate);
352 break;
353 default:
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700354 if (!Downmix_foldGeneric(
355 downmixInputChannelMask, pSrc, pDst, numFrames, accumulate)) {
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700356 ALOGE("Multichannel configuration 0x%" PRIx32 " is not supported", downmixInputChannelMask);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700357 return -EINVAL;
358 }
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800359 break;
360 }
361 break;
362
363 default:
364 return -EINVAL;
365 }
366
367 return 0;
368}
369
370
371static int Downmix_Command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
372 void *pCmdData, uint32_t *replySize, void *pReplyData) {
373
374 downmix_module_t *pDwmModule = (downmix_module_t *) self;
375 downmix_object_t *pDownmixer;
376 int retsize;
377
378 if (pDwmModule == NULL || pDwmModule->context.state == DOWNMIX_STATE_UNINITIALIZED) {
379 return -EINVAL;
380 }
381
382 pDownmixer = (downmix_object_t*) &pDwmModule->context;
383
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700384 ALOGV("Downmix_Command command %" PRIu32 " cmdSize %" PRIu32, cmdCode, cmdSize);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800385
386 switch (cmdCode) {
387 case EFFECT_CMD_INIT:
388 if (pReplyData == NULL || *replySize != sizeof(int)) {
389 return -EINVAL;
390 }
391 *(int *) pReplyData = Downmix_Init(pDwmModule);
392 break;
393
394 case EFFECT_CMD_SET_CONFIG:
395 if (pCmdData == NULL || cmdSize != sizeof(effect_config_t)
396 || pReplyData == NULL || *replySize != sizeof(int)) {
397 return -EINVAL;
398 }
399 *(int *) pReplyData = Downmix_Configure(pDwmModule,
400 (effect_config_t *)pCmdData, false);
401 break;
402
403 case EFFECT_CMD_RESET:
404 Downmix_Reset(pDownmixer, false);
405 break;
406
407 case EFFECT_CMD_GET_PARAM:
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700408 ALOGV("Downmix_Command EFFECT_CMD_GET_PARAM pCmdData %p, *replySize %" PRIu32 ", pReplyData: %p",
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800409 pCmdData, *replySize, pReplyData);
410 if (pCmdData == NULL || cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t)) ||
411 pReplyData == NULL ||
412 *replySize < (int) sizeof(effect_param_t) + 2 * sizeof(int32_t)) {
413 return -EINVAL;
414 }
415 effect_param_t *rep = (effect_param_t *) pReplyData;
416 memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + sizeof(int32_t));
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700417 ALOGV("Downmix_Command EFFECT_CMD_GET_PARAM param %" PRId32 ", replySize %" PRIu32,
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800418 *(int32_t *)rep->data, rep->vsize);
419 rep->status = Downmix_getParameter(pDownmixer, *(int32_t *)rep->data, &rep->vsize,
420 rep->data + sizeof(int32_t));
421 *replySize = sizeof(effect_param_t) + sizeof(int32_t) + rep->vsize;
422 break;
423
424 case EFFECT_CMD_SET_PARAM:
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700425 ALOGV("Downmix_Command EFFECT_CMD_SET_PARAM cmdSize %d pCmdData %p, *replySize %" PRIu32
426 ", pReplyData %p", cmdSize, pCmdData, *replySize, pReplyData);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800427 if (pCmdData == NULL || (cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t)))
428 || pReplyData == NULL || *replySize != (int)sizeof(int32_t)) {
429 return -EINVAL;
430 }
431 effect_param_t *cmd = (effect_param_t *) pCmdData;
432 *(int *)pReplyData = Downmix_setParameter(pDownmixer, *(int32_t *)cmd->data,
433 cmd->vsize, cmd->data + sizeof(int32_t));
434 break;
435
436 case EFFECT_CMD_SET_PARAM_DEFERRED:
437 //FIXME implement
438 ALOGW("Downmix_Command command EFFECT_CMD_SET_PARAM_DEFERRED not supported, FIXME");
439 break;
440
441 case EFFECT_CMD_SET_PARAM_COMMIT:
442 //FIXME implement
443 ALOGW("Downmix_Command command EFFECT_CMD_SET_PARAM_COMMIT not supported, FIXME");
444 break;
445
446 case EFFECT_CMD_ENABLE:
447 if (pReplyData == NULL || *replySize != sizeof(int)) {
448 return -EINVAL;
449 }
450 if (pDownmixer->state != DOWNMIX_STATE_INITIALIZED) {
451 return -ENOSYS;
452 }
453 pDownmixer->state = DOWNMIX_STATE_ACTIVE;
454 ALOGV("EFFECT_CMD_ENABLE() OK");
455 *(int *)pReplyData = 0;
456 break;
457
458 case EFFECT_CMD_DISABLE:
459 if (pReplyData == NULL || *replySize != sizeof(int)) {
460 return -EINVAL;
461 }
462 if (pDownmixer->state != DOWNMIX_STATE_ACTIVE) {
463 return -ENOSYS;
464 }
465 pDownmixer->state = DOWNMIX_STATE_INITIALIZED;
466 ALOGV("EFFECT_CMD_DISABLE() OK");
467 *(int *)pReplyData = 0;
468 break;
469
470 case EFFECT_CMD_SET_DEVICE:
471 if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) {
472 return -EINVAL;
473 }
474 // FIXME change type if playing on headset vs speaker
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700475 ALOGV("Downmix_Command EFFECT_CMD_SET_DEVICE: 0x%08" PRIx32, *(uint32_t *)pCmdData);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800476 break;
477
478 case EFFECT_CMD_SET_VOLUME: {
479 // audio output is always stereo => 2 channel volumes
480 if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t) * 2) {
481 return -EINVAL;
482 }
483 // FIXME change volume
484 ALOGW("Downmix_Command command EFFECT_CMD_SET_VOLUME not supported, FIXME");
485 float left = (float)(*(uint32_t *)pCmdData) / (1 << 24);
486 float right = (float)(*((uint32_t *)pCmdData + 1)) / (1 << 24);
487 ALOGV("Downmix_Command EFFECT_CMD_SET_VOLUME: left %f, right %f ", left, right);
488 break;
489 }
490
491 case EFFECT_CMD_SET_AUDIO_MODE:
492 if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) {
493 return -EINVAL;
494 }
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700495 ALOGV("Downmix_Command EFFECT_CMD_SET_AUDIO_MODE: %" PRIu32, *(uint32_t *)pCmdData);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800496 break;
497
498 case EFFECT_CMD_SET_CONFIG_REVERSE:
499 case EFFECT_CMD_SET_INPUT_DEVICE:
500 // these commands are ignored by a downmix effect
501 break;
502
503 default:
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700504 ALOGW("Downmix_Command invalid command %" PRIu32, cmdCode);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800505 return -EINVAL;
506 }
507
508 return 0;
509}
510
511
512int Downmix_GetDescriptor(effect_handle_t self, effect_descriptor_t *pDescriptor)
513{
514 downmix_module_t *pDwnmxModule = (downmix_module_t *) self;
515
516 if (pDwnmxModule == NULL ||
517 pDwnmxModule->context.state == DOWNMIX_STATE_UNINITIALIZED) {
518 return -EINVAL;
519 }
520
521 memcpy(pDescriptor, &gDownmixDescriptor, sizeof(effect_descriptor_t));
522
523 return 0;
524}
525
526
527/*----------------------------------------------------------------------------
528 * Downmix internal functions
529 *--------------------------------------------------------------------------*/
530
531/*----------------------------------------------------------------------------
532 * Downmix_Init()
533 *----------------------------------------------------------------------------
534 * Purpose:
535 * Initialize downmix context and apply default parameters
536 *
537 * Inputs:
538 * pDwmModule pointer to downmix effect module
539 *
540 * Outputs:
541 *
542 * Returns:
543 * 0 indicates success
544 *
545 * Side Effects:
546 * updates:
547 * pDwmModule->context.type
548 * pDwmModule->context.apply_volume_correction
549 * pDwmModule->config.inputCfg
550 * pDwmModule->config.outputCfg
551 * pDwmModule->config.inputCfg.samplingRate
552 * pDwmModule->config.outputCfg.samplingRate
553 * pDwmModule->context.state
554 * doesn't set:
555 * pDwmModule->itfe
556 *
557 *----------------------------------------------------------------------------
558 */
559
560int Downmix_Init(downmix_module_t *pDwmModule) {
561
562 ALOGV("Downmix_Init module %p", pDwmModule);
563 int ret = 0;
564
565 memset(&pDwmModule->context, 0, sizeof(downmix_object_t));
566
567 pDwmModule->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
568 pDwmModule->config.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
569 pDwmModule->config.inputCfg.channels = AUDIO_CHANNEL_OUT_7POINT1;
570 pDwmModule->config.inputCfg.bufferProvider.getBuffer = NULL;
571 pDwmModule->config.inputCfg.bufferProvider.releaseBuffer = NULL;
572 pDwmModule->config.inputCfg.bufferProvider.cookie = NULL;
573 pDwmModule->config.inputCfg.mask = EFFECT_CONFIG_ALL;
574
575 pDwmModule->config.inputCfg.samplingRate = 44100;
576 pDwmModule->config.outputCfg.samplingRate = pDwmModule->config.inputCfg.samplingRate;
577
578 // set a default value for the access mode, but should be overwritten by caller
579 pDwmModule->config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
580 pDwmModule->config.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
581 pDwmModule->config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
582 pDwmModule->config.outputCfg.bufferProvider.getBuffer = NULL;
583 pDwmModule->config.outputCfg.bufferProvider.releaseBuffer = NULL;
584 pDwmModule->config.outputCfg.bufferProvider.cookie = NULL;
585 pDwmModule->config.outputCfg.mask = EFFECT_CONFIG_ALL;
586
587 ret = Downmix_Configure(pDwmModule, &pDwmModule->config, true);
588 if (ret != 0) {
589 ALOGV("Downmix_Init error %d on module %p", ret, pDwmModule);
590 } else {
591 pDwmModule->context.state = DOWNMIX_STATE_INITIALIZED;
592 }
593
594 return ret;
595}
596
597
598/*----------------------------------------------------------------------------
599 * Downmix_Configure()
600 *----------------------------------------------------------------------------
601 * Purpose:
602 * Set input and output audio configuration.
603 *
604 * Inputs:
605 * pDwmModule pointer to downmix effect module
606 * pConfig pointer to effect_config_t structure containing input
607 * and output audio parameters configuration
608 * init true if called from init function
609 *
610 * Outputs:
611 *
612 * Returns:
613 * 0 indicates success
614 *
615 * Side Effects:
616 *
617 *----------------------------------------------------------------------------
618 */
619
620int Downmix_Configure(downmix_module_t *pDwmModule, effect_config_t *pConfig, bool init) {
621
622 downmix_object_t *pDownmixer = &pDwmModule->context;
623
624 // Check configuration compatibility with build options, and effect capabilities
625 if (pConfig->inputCfg.samplingRate != pConfig->outputCfg.samplingRate
626 || pConfig->outputCfg.channels != DOWNMIX_OUTPUT_CHANNELS
627 || pConfig->inputCfg.format != AUDIO_FORMAT_PCM_16_BIT
628 || pConfig->outputCfg.format != AUDIO_FORMAT_PCM_16_BIT) {
629 ALOGE("Downmix_Configure error: invalid config");
630 return -EINVAL;
631 }
632
Chih-Wei Huanga93c8c92013-01-10 20:52:57 +0800633 if (&pDwmModule->config != pConfig) {
634 memcpy(&pDwmModule->config, pConfig, sizeof(effect_config_t));
635 }
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800636
637 if (init) {
638 pDownmixer->type = DOWNMIX_TYPE_FOLD;
639 pDownmixer->apply_volume_correction = false;
640 pDownmixer->input_channel_count = 8; // matches default input of AUDIO_CHANNEL_OUT_7POINT1
641 } else {
642 // when configuring the effect, do not allow a blank channel mask
643 if (pConfig->inputCfg.channels == 0) {
644 ALOGE("Downmix_Configure error: input channel mask can't be 0");
645 return -EINVAL;
646 }
647 pDownmixer->input_channel_count = popcount(pConfig->inputCfg.channels);
648 }
649
650 Downmix_Reset(pDownmixer, init);
651
652 return 0;
653}
654
655
656/*----------------------------------------------------------------------------
657 * Downmix_Reset()
658 *----------------------------------------------------------------------------
659 * Purpose:
660 * Reset internal states.
661 *
662 * Inputs:
663 * pDownmixer pointer to downmix context
664 * init true if called from init function
665 *
666 * Outputs:
667*
668 * Returns:
669 * 0 indicates success
670 *
671 * Side Effects:
672 *
673 *----------------------------------------------------------------------------
674 */
675
676int Downmix_Reset(downmix_object_t *pDownmixer, bool init) {
677 // nothing to do here
678 return 0;
679}
680
681
682/*----------------------------------------------------------------------------
683 * Downmix_setParameter()
684 *----------------------------------------------------------------------------
685 * Purpose:
686 * Set a Downmix parameter
687 *
688 * Inputs:
689 * pDownmixer handle to instance data
690 * param parameter
691 * pValue pointer to parameter value
692 * size value size
693 *
694 * Outputs:
695 *
696 * Returns:
697 * 0 indicates success
698 *
699 * Side Effects:
700 *
701 *----------------------------------------------------------------------------
702 */
Ashok Bhatb302bd52014-02-18 11:40:00 +0000703int Downmix_setParameter(downmix_object_t *pDownmixer, int32_t param, uint32_t size, void *pValue) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800704
705 int16_t value16;
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700706 ALOGV("Downmix_setParameter, context %p, param %" PRId32 ", value16 %" PRId16 ", value32 %" PRId32,
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800707 pDownmixer, param, *(int16_t *)pValue, *(int32_t *)pValue);
708
709 switch (param) {
710
711 case DOWNMIX_PARAM_TYPE:
712 if (size != sizeof(downmix_type_t)) {
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700713 ALOGE("Downmix_setParameter(DOWNMIX_PARAM_TYPE) invalid size %" PRIu32 ", should be %zu",
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800714 size, sizeof(downmix_type_t));
715 return -EINVAL;
716 }
717 value16 = *(int16_t *)pValue;
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700718 ALOGV("set DOWNMIX_PARAM_TYPE, type %" PRId16, value16);
Jean-Michel Trivi7d5b2622012-04-04 18:54:36 -0700719 if (!((value16 > DOWNMIX_TYPE_INVALID) && (value16 <= DOWNMIX_TYPE_LAST))) {
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700720 ALOGE("Downmix_setParameter invalid DOWNMIX_PARAM_TYPE value %" PRId16, value16);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800721 return -EINVAL;
722 } else {
723 pDownmixer->type = (downmix_type_t) value16;
724 break;
725
726 default:
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700727 ALOGE("Downmix_setParameter unknown parameter %" PRId32, param);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800728 return -EINVAL;
729 }
730}
731
732 return 0;
733} /* end Downmix_setParameter */
734
735
736/*----------------------------------------------------------------------------
737 * Downmix_getParameter()
738 *----------------------------------------------------------------------------
739 * Purpose:
740 * Get a Downmix parameter
741 *
742 * Inputs:
743 * pDownmixer handle to instance data
744 * param parameter
745 * pValue pointer to variable to hold retrieved value
746 * pSize pointer to value size: maximum size as input
747 *
748 * Outputs:
749 * *pValue updated with parameter value
750 * *pSize updated with actual value size
751 *
752 * Returns:
753 * 0 indicates success
754 *
755 * Side Effects:
756 *
757 *----------------------------------------------------------------------------
758 */
Ashok Bhatb302bd52014-02-18 11:40:00 +0000759int Downmix_getParameter(downmix_object_t *pDownmixer, int32_t param, uint32_t *pSize, void *pValue) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800760 int16_t *pValue16;
761
762 switch (param) {
763
764 case DOWNMIX_PARAM_TYPE:
765 if (*pSize < sizeof(int16_t)) {
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700766 ALOGE("Downmix_getParameter invalid parameter size %" PRIu32 " for DOWNMIX_PARAM_TYPE", *pSize);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800767 return -EINVAL;
768 }
769 pValue16 = (int16_t *)pValue;
770 *pValue16 = (int16_t) pDownmixer->type;
771 *pSize = sizeof(int16_t);
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700772 ALOGV("Downmix_getParameter DOWNMIX_PARAM_TYPE is %" PRId16, *pValue16);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800773 break;
774
775 default:
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700776 ALOGE("Downmix_getParameter unknown parameter %" PRId16, param);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800777 return -EINVAL;
778 }
779
780 return 0;
781} /* end Downmix_getParameter */
782
783
784/*----------------------------------------------------------------------------
785 * Downmix_foldFromQuad()
786 *----------------------------------------------------------------------------
787 * Purpose:
788 * downmix a quad signal to stereo
789 *
790 * Inputs:
791 * pSrc quad audio samples to downmix
792 * numFrames the number of quad frames to downmix
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700793 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
794 * or overwrite pDst (when false)
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800795 *
796 * Outputs:
797 * pDst downmixed stereo audio samples
798 *
799 *----------------------------------------------------------------------------
800 */
801void Downmix_foldFromQuad(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
802 // sample at index 0 is FL
803 // sample at index 1 is FR
804 // sample at index 2 is RL
805 // sample at index 3 is RR
806 if (accumulate) {
807 while (numFrames) {
808 // FL + RL
Jean-Michel Triviaea27152012-05-24 10:11:07 -0700809 pDst[0] = clamp16(pDst[0] + ((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(pDst[1] + ((pSrc[1] + pSrc[3]) >> 1));
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800812 pSrc += 4;
813 pDst += 2;
814 numFrames--;
815 }
816 } else { // same code as above but without adding and clamping pDst[i] to itself
817 while (numFrames) {
818 // FL + RL
Jean-Michel Triviaea27152012-05-24 10:11:07 -0700819 pDst[0] = clamp16((pSrc[0] + pSrc[2]) >> 1);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800820 // FR + RR
Jean-Michel Triviaea27152012-05-24 10:11:07 -0700821 pDst[1] = clamp16((pSrc[1] + pSrc[3]) >> 1);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800822 pSrc += 4;
823 pDst += 2;
824 numFrames--;
825 }
826 }
827}
828
829
830/*----------------------------------------------------------------------------
831 * Downmix_foldFromSurround()
832 *----------------------------------------------------------------------------
833 * Purpose:
834 * downmix a "surround sound" (mono rear) signal to stereo
835 *
836 * Inputs:
837 * pSrc surround signal to downmix
838 * numFrames the number of surround frames to downmix
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700839 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
840 * or overwrite pDst (when false)
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800841 *
842 * Outputs:
843 * pDst downmixed stereo audio samples
844 *
845 *----------------------------------------------------------------------------
846 */
847void Downmix_foldFromSurround(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
848 int32_t lt, rt, centerPlusRearContrib; // samples in Q19.12 format
849 // sample at index 0 is FL
850 // sample at index 1 is FR
851 // sample at index 2 is FC
852 // sample at index 3 is RC
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700853 // code is mostly duplicated between the two values of accumulate to avoid repeating the test
854 // for every sample
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800855 if (accumulate) {
856 while (numFrames) {
857 // centerPlusRearContrib = FC(-3dB) + RC(-3dB)
858 centerPlusRearContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12) + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
859 // FL + centerPlusRearContrib
860 lt = (pSrc[0] << 12) + centerPlusRearContrib;
861 // FR + centerPlusRearContrib
862 rt = (pSrc[1] << 12) + centerPlusRearContrib;
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700863 // accumulate in destination
Jean-Michel Triviaea27152012-05-24 10:11:07 -0700864 pDst[0] = clamp16(pDst[0] + (lt >> 13));
865 pDst[1] = clamp16(pDst[1] + (rt >> 13));
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800866 pSrc += 4;
867 pDst += 2;
868 numFrames--;
869 }
870 } else { // same code as above but without adding and clamping pDst[i] to itself
871 while (numFrames) {
872 // centerPlusRearContrib = FC(-3dB) + RC(-3dB)
873 centerPlusRearContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12) + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
874 // FL + centerPlusRearContrib
875 lt = (pSrc[0] << 12) + centerPlusRearContrib;
876 // FR + centerPlusRearContrib
877 rt = (pSrc[1] << 12) + centerPlusRearContrib;
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700878 // store in destination
Jean-Michel Triviaea27152012-05-24 10:11:07 -0700879 pDst[0] = clamp16(lt >> 13); // differs from when accumulate is true above
880 pDst[1] = clamp16(rt >> 13); // differs from when accumulate is true above
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800881 pSrc += 4;
882 pDst += 2;
883 numFrames--;
884 }
885 }
886}
887
888
889/*----------------------------------------------------------------------------
890 * Downmix_foldFrom5Point1()
891 *----------------------------------------------------------------------------
892 * Purpose:
893 * downmix a 5.1 signal to stereo
894 *
895 * Inputs:
896 * pSrc 5.1 audio samples to downmix
897 * numFrames the number of 5.1 frames to downmix
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700898 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
899 * or overwrite pDst (when false)
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800900 *
901 * Outputs:
902 * pDst downmixed stereo audio samples
903 *
904 *----------------------------------------------------------------------------
905 */
906void Downmix_foldFrom5Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
907 int32_t lt, rt, centerPlusLfeContrib; // samples in Q19.12 format
908 // sample at index 0 is FL
909 // sample at index 1 is FR
910 // sample at index 2 is FC
911 // sample at index 3 is LFE
912 // sample at index 4 is RL
913 // sample at index 5 is RR
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700914 // code is mostly duplicated between the two values of accumulate to avoid repeating the test
915 // for every sample
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800916 if (accumulate) {
917 while (numFrames) {
918 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
919 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
920 + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
921 // FL + centerPlusLfeContrib + RL
922 lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[4] << 12);
923 // FR + centerPlusLfeContrib + RR
924 rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[5] << 12);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700925 // accumulate in destination
Jean-Michel Triviaea27152012-05-24 10:11:07 -0700926 pDst[0] = clamp16(pDst[0] + (lt >> 13));
927 pDst[1] = clamp16(pDst[1] + (rt >> 13));
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800928 pSrc += 6;
929 pDst += 2;
930 numFrames--;
931 }
932 } else { // same code as above but without adding and clamping pDst[i] to itself
933 while (numFrames) {
934 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
935 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
936 + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
937 // FL + centerPlusLfeContrib + RL
938 lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[4] << 12);
939 // FR + centerPlusLfeContrib + RR
940 rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[5] << 12);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700941 // store in destination
Jean-Michel Triviaea27152012-05-24 10:11:07 -0700942 pDst[0] = clamp16(lt >> 13); // differs from when accumulate is true above
943 pDst[1] = clamp16(rt >> 13); // differs from when accumulate is true above
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800944 pSrc += 6;
945 pDst += 2;
946 numFrames--;
947 }
948 }
949}
950
951
952/*----------------------------------------------------------------------------
953 * Downmix_foldFrom7Point1()
954 *----------------------------------------------------------------------------
955 * Purpose:
956 * downmix a 7.1 signal to stereo
957 *
958 * Inputs:
959 * pSrc 7.1 audio samples to downmix
960 * numFrames the number of 7.1 frames to downmix
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700961 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
962 * or overwrite pDst (when false)
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800963 *
964 * Outputs:
965 * pDst downmixed stereo audio samples
966 *
967 *----------------------------------------------------------------------------
968 */
969void Downmix_foldFrom7Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
970 int32_t lt, rt, centerPlusLfeContrib; // samples in Q19.12 format
971 // sample at index 0 is FL
972 // sample at index 1 is FR
973 // sample at index 2 is FC
974 // sample at index 3 is LFE
975 // sample at index 4 is RL
976 // sample at index 5 is RR
977 // sample at index 6 is SL
978 // sample at index 7 is SR
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700979 // code is mostly duplicated between the two values of accumulate to avoid repeating the test
980 // for every sample
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800981 if (accumulate) {
982 while (numFrames) {
983 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
984 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
985 + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
986 // FL + centerPlusLfeContrib + SL + RL
987 lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[6] << 12) + (pSrc[4] << 12);
988 // FR + centerPlusLfeContrib + SR + RR
989 rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[7] << 12) + (pSrc[5] << 12);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700990 //accumulate in destination
Jean-Michel Triviaea27152012-05-24 10:11:07 -0700991 pDst[0] = clamp16(pDst[0] + (lt >> 13));
992 pDst[1] = clamp16(pDst[1] + (rt >> 13));
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800993 pSrc += 8;
994 pDst += 2;
995 numFrames--;
996 }
997 } else { // same code as above but without adding and clamping pDst[i] to itself
998 while (numFrames) {
999 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
1000 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
1001 + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
1002 // FL + centerPlusLfeContrib + SL + RL
1003 lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[6] << 12) + (pSrc[4] << 12);
1004 // FR + centerPlusLfeContrib + SR + RR
1005 rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[7] << 12) + (pSrc[5] << 12);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -07001006 // store in destination
Jean-Michel Triviaea27152012-05-24 10:11:07 -07001007 pDst[0] = clamp16(lt >> 13); // differs from when accumulate is true above
1008 pDst[1] = clamp16(rt >> 13); // differs from when accumulate is true above
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -08001009 pSrc += 8;
1010 pDst += 2;
1011 numFrames--;
1012 }
1013 }
1014}
1015
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -07001016
1017/*----------------------------------------------------------------------------
1018 * Downmix_foldGeneric()
1019 *----------------------------------------------------------------------------
1020 * Purpose:
1021 * downmix to stereo a multichannel signal whose format is:
1022 * - has FL/FR
1023 * - if using AUDIO_CHANNEL_OUT_SIDE*, it contains both left and right
1024 * - if using AUDIO_CHANNEL_OUT_BACK*, it contains both left and right
1025 * - doesn't use any of the AUDIO_CHANNEL_OUT_TOP* channels
1026 * - doesn't use any of the AUDIO_CHANNEL_OUT_FRONT_*_OF_CENTER channels
1027 * Only handles channel masks not enumerated in downmix_input_channel_mask_t
1028 *
1029 * Inputs:
1030 * mask the channel mask of pSrc
1031 * pSrc multichannel audio buffer to downmix
1032 * numFrames the number of multichannel frames to downmix
1033 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
1034 * or overwrite pDst (when false)
1035 *
1036 * Outputs:
1037 * pDst downmixed stereo audio samples
1038 *
1039 * Returns: false if multichannel format is not supported
1040 *
1041 *----------------------------------------------------------------------------
1042 */
1043bool Downmix_foldGeneric(
1044 uint32_t mask, int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
1045 // check against unsupported channels
1046 if (mask & kUnsupported) {
1047 ALOGE("Unsupported channels (top or front left/right of center)");
1048 return false;
1049 }
1050 // verify has FL/FR
1051 if ((mask & AUDIO_CHANNEL_OUT_STEREO) != AUDIO_CHANNEL_OUT_STEREO) {
1052 ALOGE("Front channels must be present");
1053 return false;
1054 }
1055 // verify uses SIDE as a pair (ok if not using SIDE at all)
1056 bool hasSides = false;
1057 if ((mask & kSides) != 0) {
1058 if ((mask & kSides) != kSides) {
1059 ALOGE("Side channels must be used as a pair");
1060 return false;
1061 }
1062 hasSides = true;
1063 }
1064 // verify uses BACK as a pair (ok if not using BACK at all)
1065 bool hasBacks = false;
1066 if ((mask & kBacks) != 0) {
1067 if ((mask & kBacks) != kBacks) {
1068 ALOGE("Back channels must be used as a pair");
1069 return false;
1070 }
1071 hasBacks = true;
1072 }
1073
1074 const int numChan = popcount(mask);
1075 const bool hasFC = ((mask & AUDIO_CHANNEL_OUT_FRONT_CENTER) == AUDIO_CHANNEL_OUT_FRONT_CENTER);
1076 const bool hasLFE =
1077 ((mask & AUDIO_CHANNEL_OUT_LOW_FREQUENCY) == AUDIO_CHANNEL_OUT_LOW_FREQUENCY);
1078 const bool hasBC = ((mask & AUDIO_CHANNEL_OUT_BACK_CENTER) == AUDIO_CHANNEL_OUT_BACK_CENTER);
1079 // compute at what index each channel is: samples will be in the following order:
1080 // FL FR FC LFE BL BR BC SL SR
1081 // when a channel is not present, its index is set to the same as the index of the preceding
1082 // channel
1083 const int indexFC = hasFC ? 2 : 1; // front center
1084 const int indexLFE = hasLFE ? indexFC + 1 : indexFC; // low frequency
1085 const int indexBL = hasBacks ? indexLFE + 1 : indexLFE; // back left
1086 const int indexBR = hasBacks ? indexBL + 1 : indexBL; // back right
1087 const int indexBC = hasBC ? indexBR + 1 : indexBR; // back center
1088 const int indexSL = hasSides ? indexBC + 1 : indexBC; // side left
1089 const int indexSR = hasSides ? indexSL + 1 : indexSL; // side right
1090
1091 int32_t lt, rt, centersLfeContrib; // samples in Q19.12 format
1092 // code is mostly duplicated between the two values of accumulate to avoid repeating the test
1093 // for every sample
1094 if (accumulate) {
1095 while (numFrames) {
1096 // compute contribution of FC, BC and LFE
1097 centersLfeContrib = 0;
1098 if (hasFC) { centersLfeContrib += pSrc[indexFC]; }
1099 if (hasLFE) { centersLfeContrib += pSrc[indexLFE]; }
1100 if (hasBC) { centersLfeContrib += pSrc[indexBC]; }
1101 centersLfeContrib *= MINUS_3_DB_IN_Q19_12;
1102 // always has FL/FR
1103 lt = (pSrc[0] << 12);
1104 rt = (pSrc[1] << 12);
1105 // mix in sides and backs
1106 if (hasSides) {
1107 lt += pSrc[indexSL] << 12;
1108 rt += pSrc[indexSR] << 12;
1109 }
1110 if (hasBacks) {
1111 lt += pSrc[indexBL] << 12;
1112 rt += pSrc[indexBR] << 12;
1113 }
1114 lt += centersLfeContrib;
1115 rt += centersLfeContrib;
1116 // accumulate in destination
Jean-Michel Triviaea27152012-05-24 10:11:07 -07001117 pDst[0] = clamp16(pDst[0] + (lt >> 13));
1118 pDst[1] = clamp16(pDst[1] + (rt >> 13));
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -07001119 pSrc += numChan;
1120 pDst += 2;
1121 numFrames--;
1122 }
1123 } else {
1124 while (numFrames) {
1125 // compute contribution of FC, BC and LFE
1126 centersLfeContrib = 0;
1127 if (hasFC) { centersLfeContrib += pSrc[indexFC]; }
1128 if (hasLFE) { centersLfeContrib += pSrc[indexLFE]; }
1129 if (hasBC) { centersLfeContrib += pSrc[indexBC]; }
1130 centersLfeContrib *= MINUS_3_DB_IN_Q19_12;
1131 // always has FL/FR
1132 lt = (pSrc[0] << 12);
1133 rt = (pSrc[1] << 12);
1134 // mix in sides and backs
1135 if (hasSides) {
1136 lt += pSrc[indexSL] << 12;
1137 rt += pSrc[indexSR] << 12;
1138 }
1139 if (hasBacks) {
1140 lt += pSrc[indexBL] << 12;
1141 rt += pSrc[indexBR] << 12;
1142 }
1143 lt += centersLfeContrib;
1144 rt += centersLfeContrib;
1145 // store in destination
Jean-Michel Triviaea27152012-05-24 10:11:07 -07001146 pDst[0] = clamp16(lt >> 13); // differs from when accumulate is true above
1147 pDst[1] = clamp16(rt >> 13); // differs from when accumulate is true above
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -07001148 pSrc += numChan;
1149 pDst += 2;
1150 numFrames--;
1151 }
1152 }
1153 return true;
1154}