blob: 5ca5525dad42d0e6801e982cc47cb3b87de1cb1f [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
Ramesh Katurid7d01342016-05-02 15:03:47 +053034#define MINUS_3_DB_IN_FLOAT 0.70710678f // -3dB = 0.70710678f
Andy Hunged1172f2018-12-21 16:20:03 -080035const audio_format_t gTargetFormat = AUDIO_FORMAT_PCM_FLOAT;
Ramesh Katurid7d01342016-05-02 15:03:47 +053036
Glenn Kasten029a64e2014-04-15 09:58:55 -070037// subset of possible audio_channel_mask_t values, and AUDIO_CHANNEL_OUT_* renamed to CHANNEL_MASK_*
Jean-Michel Trividb46b482012-04-23 11:29:26 -070038typedef enum {
Glenn Kasten029a64e2014-04-15 09:58:55 -070039 CHANNEL_MASK_QUAD_BACK = AUDIO_CHANNEL_OUT_QUAD_BACK,
40 CHANNEL_MASK_QUAD_SIDE = AUDIO_CHANNEL_OUT_QUAD_SIDE,
41 CHANNEL_MASK_5POINT1_BACK = AUDIO_CHANNEL_OUT_5POINT1_BACK,
42 CHANNEL_MASK_5POINT1_SIDE = AUDIO_CHANNEL_OUT_5POINT1_SIDE,
43 CHANNEL_MASK_7POINT1 = AUDIO_CHANNEL_OUT_7POINT1,
Jean-Michel Trividb46b482012-04-23 11:29:26 -070044} downmix_input_channel_mask_t;
45
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -080046// effect_handle_t interface implementation for downmix effect
47const struct effect_interface_s gDownmixInterface = {
48 Downmix_Process,
49 Downmix_Command,
50 Downmix_GetDescriptor,
51 NULL /* no process_reverse function, no reference stream needed */
52};
53
Marco Nelissen7f16b192012-10-25 16:05:57 -070054// This is the only symbol that needs to be exported
55__attribute__ ((visibility ("default")))
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -080056audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
synergydevc9d8ea72013-10-19 22:51:33 -070057 .tag = AUDIO_EFFECT_LIBRARY_TAG,
58 .version = EFFECT_LIBRARY_API_VERSION,
59 .name = "Downmix Library",
60 .implementor = "The Android Open Source Project",
61 .create_effect = DownmixLib_Create,
62 .release_effect = DownmixLib_Release,
63 .get_descriptor = DownmixLib_GetDescriptor,
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -080064};
65
66
67// AOSP insert downmix UUID: 93f04452-e4fe-41cc-91f9-e475b6d1d69f
68static const effect_descriptor_t gDownmixDescriptor = {
69 EFFECT_UIID_DOWNMIX__, //type
70 {0x93f04452, 0xe4fe, 0x41cc, 0x91f9, {0xe4, 0x75, 0xb6, 0xd1, 0xd6, 0x9f}}, // uuid
71 EFFECT_CONTROL_API_VERSION,
72 EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST,
73 0, //FIXME what value should be reported? // cpu load
74 0, //FIXME what value should be reported? // memory usage
75 "Multichannel Downmix To Stereo", // human readable effect name
76 "The Android Open Source Project" // human readable effect implementor name
77};
78
79// gDescriptors contains pointers to all defined effect descriptor in this library
80static const effect_descriptor_t * const gDescriptors[] = {
81 &gDownmixDescriptor
82};
83
84// number of effects in this library
85const int kNbEffects = sizeof(gDescriptors) / sizeof(const effect_descriptor_t *);
Andy Hungca1c4e42021-05-06 22:32:02 -070086
Ramesh Katurid7d01342016-05-02 15:03:47 +053087static LVM_FLOAT clamp_float(LVM_FLOAT a) {
88 if (a > 1.0f) {
89 return 1.0f;
90 }
91 else if (a < -1.0f) {
92 return -1.0f;
93 }
94 else {
95 return a;
96 }
97}
Andy Hungca1c4e42021-05-06 22:32:02 -070098
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -080099/*----------------------------------------------------------------------------
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700100 * Test code
101 *--------------------------------------------------------------------------*/
102#ifdef DOWNMIX_TEST_CHANNEL_INDEX
103// strictly for testing, logs the indices of the channels for a given mask,
104// uses the same code as Downmix_foldGeneric()
105void Downmix_testIndexComputation(uint32_t mask) {
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700106 ALOGI("Testing index computation for 0x%" PRIx32 ":", mask);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700107 // check against unsupported channels
108 if (mask & kUnsupported) {
109 ALOGE("Unsupported channels (top or front left/right of center)");
110 return;
111 }
112 // verify has FL/FR
113 if ((mask & AUDIO_CHANNEL_OUT_STEREO) != AUDIO_CHANNEL_OUT_STEREO) {
114 ALOGE("Front channels must be present");
115 return;
116 }
117 // verify uses SIDE as a pair (ok if not using SIDE at all)
118 bool hasSides = false;
119 if ((mask & kSides) != 0) {
120 if ((mask & kSides) != kSides) {
121 ALOGE("Side channels must be used as a pair");
122 return;
123 }
124 hasSides = true;
125 }
126 // verify uses BACK as a pair (ok if not using BACK at all)
127 bool hasBacks = false;
128 if ((mask & kBacks) != 0) {
129 if ((mask & kBacks) != kBacks) {
130 ALOGE("Back channels must be used as a pair");
131 return;
132 }
133 hasBacks = true;
134 }
135
Andy Hunge5412692014-05-16 11:25:07 -0700136 const int numChan = audio_channel_count_from_out_mask(mask);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700137 const bool hasFC = ((mask & AUDIO_CHANNEL_OUT_FRONT_CENTER) == AUDIO_CHANNEL_OUT_FRONT_CENTER);
138 const bool hasLFE =
139 ((mask & AUDIO_CHANNEL_OUT_LOW_FREQUENCY) == AUDIO_CHANNEL_OUT_LOW_FREQUENCY);
140 const bool hasBC = ((mask & AUDIO_CHANNEL_OUT_BACK_CENTER) == AUDIO_CHANNEL_OUT_BACK_CENTER);
141 // compute at what index each channel is: samples will be in the following order:
142 // FL FR FC LFE BL BR BC SL SR
143 // when a channel is not present, its index is set to the same as the index of the preceding
144 // channel
145 const int indexFC = hasFC ? 2 : 1; // front center
146 const int indexLFE = hasLFE ? indexFC + 1 : indexFC; // low frequency
147 const int indexBL = hasBacks ? indexLFE + 1 : indexLFE; // back left
148 const int indexBR = hasBacks ? indexBL + 1 : indexBL; // back right
149 const int indexBC = hasBC ? indexBR + 1 : indexBR; // back center
150 const int indexSL = hasSides ? indexBC + 1 : indexBC; // side left
151 const int indexSR = hasSides ? indexSL + 1 : indexSL; // side right
152
153 ALOGI(" FL FR FC LFE BL BR BC SL SR");
154 ALOGI(" %d %d %d %d %d %d %d %d %d",
155 0, 1, indexFC, indexLFE, indexBL, indexBR, indexBC, indexSL, indexSR);
156}
157#endif
158
Haynes Mathew Georgeee507ef2015-07-15 11:06:05 -0700159static bool Downmix_validChannelMask(uint32_t mask)
160{
161 if (!mask) {
162 return false;
163 }
164 // check against unsupported channels
165 if (mask & kUnsupported) {
166 ALOGE("Unsupported channels (top or front left/right of center)");
167 return false;
168 }
169 // verify has FL/FR
170 if ((mask & AUDIO_CHANNEL_OUT_STEREO) != AUDIO_CHANNEL_OUT_STEREO) {
171 ALOGE("Front channels must be present");
172 return false;
173 }
174 // verify uses SIDE as a pair (ok if not using SIDE at all)
175 if ((mask & kSides) != 0) {
176 if ((mask & kSides) != kSides) {
177 ALOGE("Side channels must be used as a pair");
178 return false;
179 }
180 }
181 // verify uses BACK as a pair (ok if not using BACK at all)
182 if ((mask & kBacks) != 0) {
183 if ((mask & kBacks) != kBacks) {
184 ALOGE("Back channels must be used as a pair");
185 return false;
186 }
187 }
188 return true;
189}
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700190
191/*----------------------------------------------------------------------------
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800192 * Effect API implementation
193 *--------------------------------------------------------------------------*/
194
195/*--- Effect Library Interface Implementation ---*/
196
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800197int32_t DownmixLib_Create(const effect_uuid_t *uuid,
Eric Laurent0f714a42015-06-19 15:33:57 -0700198 int32_t sessionId __unused,
199 int32_t ioId __unused,
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800200 effect_handle_t *pHandle) {
201 int ret;
202 int i;
203 downmix_module_t *module;
204 const effect_descriptor_t *desc;
205
206 ALOGV("DownmixLib_Create()");
207
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700208#ifdef DOWNMIX_TEST_CHANNEL_INDEX
209 // should work (won't log an error)
210 ALOGI("DOWNMIX_TEST_CHANNEL_INDEX: should work:");
211 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT |
212 AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_BACK_CENTER);
213 Downmix_testIndexComputation(CHANNEL_MASK_QUAD_SIDE | CHANNEL_MASK_QUAD_BACK);
214 Downmix_testIndexComputation(CHANNEL_MASK_5POINT1_SIDE | AUDIO_CHANNEL_OUT_BACK_CENTER);
215 Downmix_testIndexComputation(CHANNEL_MASK_5POINT1_BACK | AUDIO_CHANNEL_OUT_BACK_CENTER);
216 // shouldn't work (will log an error, won't display channel indices)
217 ALOGI("DOWNMIX_TEST_CHANNEL_INDEX: should NOT work:");
218 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT |
219 AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_BACK_LEFT);
220 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT |
221 AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_SIDE_LEFT);
222 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT |
223 AUDIO_CHANNEL_OUT_BACK_LEFT | AUDIO_CHANNEL_OUT_BACK_RIGHT);
224 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT |
225 AUDIO_CHANNEL_OUT_SIDE_LEFT | AUDIO_CHANNEL_OUT_SIDE_RIGHT);
226#endif
227
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800228 if (pHandle == NULL || uuid == NULL) {
229 return -EINVAL;
230 }
231
232 for (i = 0 ; i < kNbEffects ; i++) {
233 desc = gDescriptors[i];
234 if (memcmp(uuid, &desc->uuid, sizeof(effect_uuid_t)) == 0) {
235 break;
236 }
237 }
238
239 if (i == kNbEffects) {
240 return -ENOENT;
241 }
242
243 module = malloc(sizeof(downmix_module_t));
244
245 module->itfe = &gDownmixInterface;
246
247 module->context.state = DOWNMIX_STATE_UNINITIALIZED;
248
249 ret = Downmix_Init(module);
250 if (ret < 0) {
251 ALOGW("DownmixLib_Create() init failed");
252 free(module);
253 return ret;
254 }
255
256 *pHandle = (effect_handle_t) module;
257
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700258 ALOGV("DownmixLib_Create() %p , size %zu", module, sizeof(downmix_module_t));
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800259
260 return 0;
261}
262
263
264int32_t DownmixLib_Release(effect_handle_t handle) {
265 downmix_module_t *pDwmModule = (downmix_module_t *)handle;
266
267 ALOGV("DownmixLib_Release() %p", handle);
268 if (handle == NULL) {
269 return -EINVAL;
270 }
271
272 pDwmModule->context.state = DOWNMIX_STATE_UNINITIALIZED;
273
274 free(pDwmModule);
275 return 0;
276}
277
278
279int32_t DownmixLib_GetDescriptor(const effect_uuid_t *uuid, effect_descriptor_t *pDescriptor) {
280 ALOGV("DownmixLib_GetDescriptor()");
281 int i;
282
283 if (pDescriptor == NULL || uuid == NULL){
284 ALOGE("DownmixLib_Create() called with NULL pointer");
285 return -EINVAL;
286 }
287 ALOGV("DownmixLib_GetDescriptor() nb effects=%d", kNbEffects);
288 for (i = 0; i < kNbEffects; i++) {
289 ALOGV("DownmixLib_GetDescriptor() i=%d", i);
290 if (memcmp(uuid, &gDescriptors[i]->uuid, sizeof(effect_uuid_t)) == 0) {
291 memcpy(pDescriptor, gDescriptors[i], sizeof(effect_descriptor_t));
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700292 ALOGV("EffectGetDescriptor - UUID matched downmix type %d, UUID = %" PRIx32,
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800293 i, gDescriptors[i]->uuid.timeLow);
294 return 0;
295 }
296 }
297
298 return -EINVAL;
299}
300
Ramesh Katurid7d01342016-05-02 15:03:47 +0530301/*--- Effect Control Interface Implementation ---*/
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800302
Ramesh Katurid7d01342016-05-02 15:03:47 +0530303static int Downmix_Process(effect_handle_t self,
304 audio_buffer_t *inBuffer, audio_buffer_t *outBuffer) {
305
306 downmix_object_t *pDownmixer;
307 LVM_FLOAT *pSrc, *pDst;
308 downmix_module_t *pDwmModule = (downmix_module_t *)self;
309
310 if (pDwmModule == NULL) {
311 return -EINVAL;
312 }
313
314 if (inBuffer == NULL || inBuffer->raw == NULL ||
315 outBuffer == NULL || outBuffer->raw == NULL ||
316 inBuffer->frameCount != outBuffer->frameCount) {
317 return -EINVAL;
318 }
319
320 pDownmixer = (downmix_object_t*) &pDwmModule->context;
321
322 if (pDownmixer->state == DOWNMIX_STATE_UNINITIALIZED) {
323 ALOGE("Downmix_Process error: trying to use an uninitialized downmixer");
324 return -EINVAL;
325 } else if (pDownmixer->state == DOWNMIX_STATE_INITIALIZED) {
326 ALOGE("Downmix_Process error: trying to use a non-configured downmixer");
327 return -ENODATA;
328 }
329
330 pSrc = (LVM_FLOAT *) inBuffer->s16;
331 pDst = (LVM_FLOAT *) outBuffer->s16;
332 size_t numFrames = outBuffer->frameCount;
333
334 const bool accumulate =
335 (pDwmModule->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE);
336 const uint32_t downmixInputChannelMask = pDwmModule->config.inputCfg.channels;
337
338 switch(pDownmixer->type) {
339
340 case DOWNMIX_TYPE_STRIP:
341 if (accumulate) {
342 while (numFrames) {
343 pDst[0] = clamp_float(pDst[0] + pSrc[0]);
344 pDst[1] = clamp_float(pDst[1] + pSrc[1]);
345 pSrc += pDownmixer->input_channel_count;
346 pDst += 2;
347 numFrames--;
348 }
349 } else {
350 while (numFrames) {
351 pDst[0] = pSrc[0];
352 pDst[1] = pSrc[1];
353 pSrc += pDownmixer->input_channel_count;
354 pDst += 2;
355 numFrames--;
356 }
357 }
358 break;
359
360 case DOWNMIX_TYPE_FOLD:
361#ifdef DOWNMIX_ALWAYS_USE_GENERIC_DOWNMIXER
362 // bypass the optimized downmix routines for the common formats
363 if (!Downmix_foldGeneric(
364 downmixInputChannelMask, pSrc, pDst, numFrames, accumulate)) {
365 ALOGE("Multichannel configuration 0x%" PRIx32 " is not supported",
366 downmixInputChannelMask);
367 return -EINVAL;
368 }
369 break;
370#endif
371 // optimize for the common formats
372 switch((downmix_input_channel_mask_t)downmixInputChannelMask) {
373 case CHANNEL_MASK_QUAD_BACK:
374 case CHANNEL_MASK_QUAD_SIDE:
375 Downmix_foldFromQuad(pSrc, pDst, numFrames, accumulate);
376 break;
377 case CHANNEL_MASK_5POINT1_BACK:
378 case CHANNEL_MASK_5POINT1_SIDE:
379 Downmix_foldFrom5Point1(pSrc, pDst, numFrames, accumulate);
380 break;
381 case CHANNEL_MASK_7POINT1:
382 Downmix_foldFrom7Point1(pSrc, pDst, numFrames, accumulate);
383 break;
384 default:
385 if (!Downmix_foldGeneric(
386 downmixInputChannelMask, pSrc, pDst, numFrames, accumulate)) {
387 ALOGE("Multichannel configuration 0x%" PRIx32 " is not supported",
388 downmixInputChannelMask);
389 return -EINVAL;
390 }
391 break;
392 }
393 break;
394
395 default:
396 return -EINVAL;
397 }
398
399 return 0;
400}
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800401
402static int Downmix_Command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
403 void *pCmdData, uint32_t *replySize, void *pReplyData) {
404
405 downmix_module_t *pDwmModule = (downmix_module_t *) self;
406 downmix_object_t *pDownmixer;
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800407
408 if (pDwmModule == NULL || pDwmModule->context.state == DOWNMIX_STATE_UNINITIALIZED) {
409 return -EINVAL;
410 }
411
412 pDownmixer = (downmix_object_t*) &pDwmModule->context;
413
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700414 ALOGV("Downmix_Command command %" PRIu32 " cmdSize %" PRIu32, cmdCode, cmdSize);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800415
416 switch (cmdCode) {
417 case EFFECT_CMD_INIT:
Eric Laurent0f714a42015-06-19 15:33:57 -0700418 if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800419 return -EINVAL;
420 }
421 *(int *) pReplyData = Downmix_Init(pDwmModule);
422 break;
423
424 case EFFECT_CMD_SET_CONFIG:
425 if (pCmdData == NULL || cmdSize != sizeof(effect_config_t)
Eric Laurent0f714a42015-06-19 15:33:57 -0700426 || pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800427 return -EINVAL;
428 }
429 *(int *) pReplyData = Downmix_Configure(pDwmModule,
430 (effect_config_t *)pCmdData, false);
431 break;
432
433 case EFFECT_CMD_RESET:
434 Downmix_Reset(pDownmixer, false);
435 break;
436
437 case EFFECT_CMD_GET_PARAM:
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700438 ALOGV("Downmix_Command EFFECT_CMD_GET_PARAM pCmdData %p, *replySize %" PRIu32 ", pReplyData: %p",
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800439 pCmdData, *replySize, pReplyData);
440 if (pCmdData == NULL || cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t)) ||
Eric Laurent0f714a42015-06-19 15:33:57 -0700441 pReplyData == NULL || replySize == NULL ||
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800442 *replySize < (int) sizeof(effect_param_t) + 2 * sizeof(int32_t)) {
443 return -EINVAL;
444 }
445 effect_param_t *rep = (effect_param_t *) pReplyData;
446 memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + sizeof(int32_t));
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700447 ALOGV("Downmix_Command EFFECT_CMD_GET_PARAM param %" PRId32 ", replySize %" PRIu32,
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800448 *(int32_t *)rep->data, rep->vsize);
449 rep->status = Downmix_getParameter(pDownmixer, *(int32_t *)rep->data, &rep->vsize,
450 rep->data + sizeof(int32_t));
451 *replySize = sizeof(effect_param_t) + sizeof(int32_t) + rep->vsize;
452 break;
453
454 case EFFECT_CMD_SET_PARAM:
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700455 ALOGV("Downmix_Command EFFECT_CMD_SET_PARAM cmdSize %d pCmdData %p, *replySize %" PRIu32
456 ", pReplyData %p", cmdSize, pCmdData, *replySize, pReplyData);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800457 if (pCmdData == NULL || (cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t)))
Eric Laurent0f714a42015-06-19 15:33:57 -0700458 || pReplyData == NULL || replySize == NULL || *replySize != (int)sizeof(int32_t)) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800459 return -EINVAL;
460 }
461 effect_param_t *cmd = (effect_param_t *) pCmdData;
Mikhail Naganov804632a2017-07-24 17:25:47 -0700462 if (cmd->psize != sizeof(int32_t)) {
463 android_errorWriteLog(0x534e4554, "63662938");
464 return -EINVAL;
465 }
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800466 *(int *)pReplyData = Downmix_setParameter(pDownmixer, *(int32_t *)cmd->data,
467 cmd->vsize, cmd->data + sizeof(int32_t));
468 break;
469
470 case EFFECT_CMD_SET_PARAM_DEFERRED:
471 //FIXME implement
472 ALOGW("Downmix_Command command EFFECT_CMD_SET_PARAM_DEFERRED not supported, FIXME");
473 break;
474
475 case EFFECT_CMD_SET_PARAM_COMMIT:
476 //FIXME implement
477 ALOGW("Downmix_Command command EFFECT_CMD_SET_PARAM_COMMIT not supported, FIXME");
478 break;
479
480 case EFFECT_CMD_ENABLE:
Eric Laurent0f714a42015-06-19 15:33:57 -0700481 if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800482 return -EINVAL;
483 }
484 if (pDownmixer->state != DOWNMIX_STATE_INITIALIZED) {
485 return -ENOSYS;
486 }
487 pDownmixer->state = DOWNMIX_STATE_ACTIVE;
488 ALOGV("EFFECT_CMD_ENABLE() OK");
489 *(int *)pReplyData = 0;
490 break;
491
492 case EFFECT_CMD_DISABLE:
Eric Laurent0f714a42015-06-19 15:33:57 -0700493 if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800494 return -EINVAL;
495 }
496 if (pDownmixer->state != DOWNMIX_STATE_ACTIVE) {
497 return -ENOSYS;
498 }
499 pDownmixer->state = DOWNMIX_STATE_INITIALIZED;
500 ALOGV("EFFECT_CMD_DISABLE() OK");
501 *(int *)pReplyData = 0;
502 break;
503
504 case EFFECT_CMD_SET_DEVICE:
505 if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) {
506 return -EINVAL;
507 }
508 // FIXME change type if playing on headset vs speaker
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700509 ALOGV("Downmix_Command EFFECT_CMD_SET_DEVICE: 0x%08" PRIx32, *(uint32_t *)pCmdData);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800510 break;
511
512 case EFFECT_CMD_SET_VOLUME: {
513 // audio output is always stereo => 2 channel volumes
514 if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t) * 2) {
515 return -EINVAL;
516 }
517 // FIXME change volume
518 ALOGW("Downmix_Command command EFFECT_CMD_SET_VOLUME not supported, FIXME");
519 float left = (float)(*(uint32_t *)pCmdData) / (1 << 24);
520 float right = (float)(*((uint32_t *)pCmdData + 1)) / (1 << 24);
521 ALOGV("Downmix_Command EFFECT_CMD_SET_VOLUME: left %f, right %f ", left, right);
522 break;
523 }
524
525 case EFFECT_CMD_SET_AUDIO_MODE:
526 if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) {
527 return -EINVAL;
528 }
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700529 ALOGV("Downmix_Command EFFECT_CMD_SET_AUDIO_MODE: %" PRIu32, *(uint32_t *)pCmdData);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800530 break;
531
532 case EFFECT_CMD_SET_CONFIG_REVERSE:
533 case EFFECT_CMD_SET_INPUT_DEVICE:
534 // these commands are ignored by a downmix effect
535 break;
536
537 default:
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700538 ALOGW("Downmix_Command invalid command %" PRIu32, cmdCode);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800539 return -EINVAL;
540 }
541
542 return 0;
543}
544
545
546int Downmix_GetDescriptor(effect_handle_t self, effect_descriptor_t *pDescriptor)
547{
548 downmix_module_t *pDwnmxModule = (downmix_module_t *) self;
549
550 if (pDwnmxModule == NULL ||
551 pDwnmxModule->context.state == DOWNMIX_STATE_UNINITIALIZED) {
552 return -EINVAL;
553 }
554
555 memcpy(pDescriptor, &gDownmixDescriptor, sizeof(effect_descriptor_t));
556
557 return 0;
558}
559
560
561/*----------------------------------------------------------------------------
562 * Downmix internal functions
563 *--------------------------------------------------------------------------*/
564
565/*----------------------------------------------------------------------------
566 * Downmix_Init()
567 *----------------------------------------------------------------------------
568 * Purpose:
569 * Initialize downmix context and apply default parameters
570 *
571 * Inputs:
572 * pDwmModule pointer to downmix effect module
573 *
574 * Outputs:
575 *
576 * Returns:
577 * 0 indicates success
578 *
579 * Side Effects:
580 * updates:
581 * pDwmModule->context.type
582 * pDwmModule->context.apply_volume_correction
583 * pDwmModule->config.inputCfg
584 * pDwmModule->config.outputCfg
585 * pDwmModule->config.inputCfg.samplingRate
586 * pDwmModule->config.outputCfg.samplingRate
587 * pDwmModule->context.state
588 * doesn't set:
589 * pDwmModule->itfe
590 *
591 *----------------------------------------------------------------------------
592 */
593
594int Downmix_Init(downmix_module_t *pDwmModule) {
595
596 ALOGV("Downmix_Init module %p", pDwmModule);
597 int ret = 0;
598
599 memset(&pDwmModule->context, 0, sizeof(downmix_object_t));
600
601 pDwmModule->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
Andy Hunged1172f2018-12-21 16:20:03 -0800602 pDwmModule->config.inputCfg.format = gTargetFormat;
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800603 pDwmModule->config.inputCfg.channels = AUDIO_CHANNEL_OUT_7POINT1;
604 pDwmModule->config.inputCfg.bufferProvider.getBuffer = NULL;
605 pDwmModule->config.inputCfg.bufferProvider.releaseBuffer = NULL;
606 pDwmModule->config.inputCfg.bufferProvider.cookie = NULL;
607 pDwmModule->config.inputCfg.mask = EFFECT_CONFIG_ALL;
608
609 pDwmModule->config.inputCfg.samplingRate = 44100;
610 pDwmModule->config.outputCfg.samplingRate = pDwmModule->config.inputCfg.samplingRate;
611
612 // set a default value for the access mode, but should be overwritten by caller
613 pDwmModule->config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
Andy Hunged1172f2018-12-21 16:20:03 -0800614 pDwmModule->config.outputCfg.format = gTargetFormat;
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800615 pDwmModule->config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
616 pDwmModule->config.outputCfg.bufferProvider.getBuffer = NULL;
617 pDwmModule->config.outputCfg.bufferProvider.releaseBuffer = NULL;
618 pDwmModule->config.outputCfg.bufferProvider.cookie = NULL;
619 pDwmModule->config.outputCfg.mask = EFFECT_CONFIG_ALL;
620
621 ret = Downmix_Configure(pDwmModule, &pDwmModule->config, true);
622 if (ret != 0) {
623 ALOGV("Downmix_Init error %d on module %p", ret, pDwmModule);
624 } else {
625 pDwmModule->context.state = DOWNMIX_STATE_INITIALIZED;
626 }
627
628 return ret;
629}
630
631
632/*----------------------------------------------------------------------------
633 * Downmix_Configure()
634 *----------------------------------------------------------------------------
635 * Purpose:
636 * Set input and output audio configuration.
637 *
638 * Inputs:
639 * pDwmModule pointer to downmix effect module
640 * pConfig pointer to effect_config_t structure containing input
641 * and output audio parameters configuration
642 * init true if called from init function
643 *
644 * Outputs:
645 *
646 * Returns:
647 * 0 indicates success
648 *
649 * Side Effects:
650 *
651 *----------------------------------------------------------------------------
652 */
653
654int Downmix_Configure(downmix_module_t *pDwmModule, effect_config_t *pConfig, bool init) {
655
656 downmix_object_t *pDownmixer = &pDwmModule->context;
657
658 // Check configuration compatibility with build options, and effect capabilities
659 if (pConfig->inputCfg.samplingRate != pConfig->outputCfg.samplingRate
660 || pConfig->outputCfg.channels != DOWNMIX_OUTPUT_CHANNELS
Andy Hunged1172f2018-12-21 16:20:03 -0800661 || pConfig->inputCfg.format != gTargetFormat
662 || pConfig->outputCfg.format != gTargetFormat) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800663 ALOGE("Downmix_Configure error: invalid config");
664 return -EINVAL;
665 }
666
Chih-Wei Huanga93c8c92013-01-10 20:52:57 +0800667 if (&pDwmModule->config != pConfig) {
668 memcpy(&pDwmModule->config, pConfig, sizeof(effect_config_t));
669 }
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800670
671 if (init) {
672 pDownmixer->type = DOWNMIX_TYPE_FOLD;
673 pDownmixer->apply_volume_correction = false;
674 pDownmixer->input_channel_count = 8; // matches default input of AUDIO_CHANNEL_OUT_7POINT1
675 } else {
Haynes Mathew Georgeee507ef2015-07-15 11:06:05 -0700676 // when configuring the effect, do not allow a blank or unsupported channel mask
677 if (!Downmix_validChannelMask(pConfig->inputCfg.channels)) {
678 ALOGE("Downmix_Configure error: input channel mask(0x%x) not supported",
679 pConfig->inputCfg.channels);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800680 return -EINVAL;
681 }
Andy Hunge5412692014-05-16 11:25:07 -0700682 pDownmixer->input_channel_count =
683 audio_channel_count_from_out_mask(pConfig->inputCfg.channels);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800684 }
685
686 Downmix_Reset(pDownmixer, init);
687
688 return 0;
689}
690
691
692/*----------------------------------------------------------------------------
693 * Downmix_Reset()
694 *----------------------------------------------------------------------------
695 * Purpose:
696 * Reset internal states.
697 *
698 * Inputs:
699 * pDownmixer pointer to downmix context
700 * init true if called from init function
701 *
702 * Outputs:
703*
704 * Returns:
705 * 0 indicates success
706 *
707 * Side Effects:
708 *
709 *----------------------------------------------------------------------------
710 */
711
Eric Laurent0f714a42015-06-19 15:33:57 -0700712int Downmix_Reset(downmix_object_t *pDownmixer __unused, bool init __unused) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800713 // nothing to do here
714 return 0;
715}
716
717
718/*----------------------------------------------------------------------------
719 * Downmix_setParameter()
720 *----------------------------------------------------------------------------
721 * Purpose:
722 * Set a Downmix parameter
723 *
724 * Inputs:
725 * pDownmixer handle to instance data
726 * param parameter
727 * pValue pointer to parameter value
728 * size value size
729 *
730 * Outputs:
731 *
732 * Returns:
733 * 0 indicates success
734 *
735 * Side Effects:
736 *
737 *----------------------------------------------------------------------------
738 */
Ashok Bhatb302bd52014-02-18 11:40:00 +0000739int Downmix_setParameter(downmix_object_t *pDownmixer, int32_t param, uint32_t size, void *pValue) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800740
741 int16_t value16;
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700742 ALOGV("Downmix_setParameter, context %p, param %" PRId32 ", value16 %" PRId16 ", value32 %" PRId32,
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800743 pDownmixer, param, *(int16_t *)pValue, *(int32_t *)pValue);
744
745 switch (param) {
746
747 case DOWNMIX_PARAM_TYPE:
748 if (size != sizeof(downmix_type_t)) {
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700749 ALOGE("Downmix_setParameter(DOWNMIX_PARAM_TYPE) invalid size %" PRIu32 ", should be %zu",
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800750 size, sizeof(downmix_type_t));
751 return -EINVAL;
752 }
753 value16 = *(int16_t *)pValue;
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700754 ALOGV("set DOWNMIX_PARAM_TYPE, type %" PRId16, value16);
Jean-Michel Trivi7d5b2622012-04-04 18:54:36 -0700755 if (!((value16 > DOWNMIX_TYPE_INVALID) && (value16 <= DOWNMIX_TYPE_LAST))) {
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700756 ALOGE("Downmix_setParameter invalid DOWNMIX_PARAM_TYPE value %" PRId16, value16);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800757 return -EINVAL;
758 } else {
759 pDownmixer->type = (downmix_type_t) value16;
760 break;
761
762 default:
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700763 ALOGE("Downmix_setParameter unknown parameter %" PRId32, param);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800764 return -EINVAL;
765 }
766}
767
768 return 0;
769} /* end Downmix_setParameter */
770
771
772/*----------------------------------------------------------------------------
773 * Downmix_getParameter()
774 *----------------------------------------------------------------------------
775 * Purpose:
776 * Get a Downmix parameter
777 *
778 * Inputs:
779 * pDownmixer handle to instance data
780 * param parameter
781 * pValue pointer to variable to hold retrieved value
782 * pSize pointer to value size: maximum size as input
783 *
784 * Outputs:
785 * *pValue updated with parameter value
786 * *pSize updated with actual value size
787 *
788 * Returns:
789 * 0 indicates success
790 *
791 * Side Effects:
792 *
793 *----------------------------------------------------------------------------
794 */
Ashok Bhatb302bd52014-02-18 11:40:00 +0000795int Downmix_getParameter(downmix_object_t *pDownmixer, int32_t param, uint32_t *pSize, void *pValue) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800796 int16_t *pValue16;
797
798 switch (param) {
799
800 case DOWNMIX_PARAM_TYPE:
801 if (*pSize < sizeof(int16_t)) {
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700802 ALOGE("Downmix_getParameter invalid parameter size %" PRIu32 " for DOWNMIX_PARAM_TYPE", *pSize);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800803 return -EINVAL;
804 }
805 pValue16 = (int16_t *)pValue;
806 *pValue16 = (int16_t) pDownmixer->type;
807 *pSize = sizeof(int16_t);
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700808 ALOGV("Downmix_getParameter DOWNMIX_PARAM_TYPE is %" PRId16, *pValue16);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800809 break;
810
811 default:
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700812 ALOGE("Downmix_getParameter unknown parameter %" PRId16, param);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800813 return -EINVAL;
814 }
815
816 return 0;
817} /* end Downmix_getParameter */
818
819
820/*----------------------------------------------------------------------------
821 * Downmix_foldFromQuad()
822 *----------------------------------------------------------------------------
823 * Purpose:
824 * downmix a quad signal to stereo
825 *
826 * Inputs:
827 * pSrc quad audio samples to downmix
828 * numFrames the number of quad frames to downmix
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700829 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
830 * or overwrite pDst (when false)
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800831 *
832 * Outputs:
833 * pDst downmixed stereo audio samples
834 *
835 *----------------------------------------------------------------------------
836 */
Ramesh Katurid7d01342016-05-02 15:03:47 +0530837void Downmix_foldFromQuad(LVM_FLOAT *pSrc, LVM_FLOAT *pDst, size_t numFrames, bool accumulate) {
838 // sample at index 0 is FL
839 // sample at index 1 is FR
840 // sample at index 2 is RL
841 // sample at index 3 is RR
842 if (accumulate) {
843 while (numFrames) {
844 // FL + RL
845 pDst[0] = clamp_float(pDst[0] + ((pSrc[0] + pSrc[2]) / 2.0f));
846 // FR + RR
847 pDst[1] = clamp_float(pDst[1] + ((pSrc[1] + pSrc[3]) / 2.0f));
848 pSrc += 4;
849 pDst += 2;
850 numFrames--;
851 }
852 } else { // same code as above but without adding and clamping pDst[i] to itself
853 while (numFrames) {
854 // FL + RL
855 pDst[0] = clamp_float((pSrc[0] + pSrc[2]) / 2.0f);
856 // FR + RR
857 pDst[1] = clamp_float((pSrc[1] + pSrc[3]) / 2.0f);
858 pSrc += 4;
859 pDst += 2;
860 numFrames--;
861 }
862 }
863}
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800864
865/*----------------------------------------------------------------------------
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800866 * Downmix_foldFrom5Point1()
867 *----------------------------------------------------------------------------
868 * Purpose:
869 * downmix a 5.1 signal to stereo
870 *
871 * Inputs:
872 * pSrc 5.1 audio samples to downmix
873 * numFrames the number of 5.1 frames to downmix
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700874 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
875 * or overwrite pDst (when false)
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800876 *
877 * Outputs:
878 * pDst downmixed stereo audio samples
879 *
880 *----------------------------------------------------------------------------
881 */
Ramesh Katurid7d01342016-05-02 15:03:47 +0530882void Downmix_foldFrom5Point1(LVM_FLOAT *pSrc, LVM_FLOAT *pDst, size_t numFrames, bool accumulate) {
883 LVM_FLOAT lt, rt, centerPlusLfeContrib; // samples in Q19.12 format
884 // sample at index 0 is FL
885 // sample at index 1 is FR
886 // sample at index 2 is FC
887 // sample at index 3 is LFE
888 // sample at index 4 is RL
889 // sample at index 5 is RR
890 // code is mostly duplicated between the two values of accumulate to avoid repeating the test
891 // for every sample
892 if (accumulate) {
893 while (numFrames) {
894 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
895 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_FLOAT)
896 + (pSrc[3] * MINUS_3_DB_IN_FLOAT);
897 // FL + centerPlusLfeContrib + RL
898 lt = pSrc[0] + centerPlusLfeContrib + pSrc[4];
899 // FR + centerPlusLfeContrib + RR
900 rt = pSrc[1] + centerPlusLfeContrib + pSrc[5];
901 // accumulate in destination
902 pDst[0] = clamp_float(pDst[0] + (lt / 2.0f));
903 pDst[1] = clamp_float(pDst[1] + (rt / 2.0f));
904 pSrc += 6;
905 pDst += 2;
906 numFrames--;
907 }
908 } else { // same code as above but without adding and clamping pDst[i] to itself
909 while (numFrames) {
910 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
911 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_FLOAT)
912 + (pSrc[3] * MINUS_3_DB_IN_FLOAT);
913 // FL + centerPlusLfeContrib + RL
914 lt = pSrc[0] + centerPlusLfeContrib + pSrc[4];
915 // FR + centerPlusLfeContrib + RR
916 rt = pSrc[1] + centerPlusLfeContrib + pSrc[5];
917 // store in destination
918 pDst[0] = clamp_float(lt / 2.0f); // differs from when accumulate is true above
919 pDst[1] = clamp_float(rt / 2.0f); // differs from when accumulate is true above
920 pSrc += 6;
921 pDst += 2;
922 numFrames--;
923 }
924 }
925}
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800926
927/*----------------------------------------------------------------------------
928 * Downmix_foldFrom7Point1()
929 *----------------------------------------------------------------------------
930 * Purpose:
931 * downmix a 7.1 signal to stereo
932 *
933 * Inputs:
934 * pSrc 7.1 audio samples to downmix
935 * numFrames the number of 7.1 frames to downmix
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700936 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
937 * or overwrite pDst (when false)
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800938 *
939 * Outputs:
940 * pDst downmixed stereo audio samples
941 *
942 *----------------------------------------------------------------------------
943 */
Ramesh Katurid7d01342016-05-02 15:03:47 +0530944void Downmix_foldFrom7Point1(LVM_FLOAT *pSrc, LVM_FLOAT *pDst, size_t numFrames, bool accumulate) {
945 LVM_FLOAT lt, rt, centerPlusLfeContrib; // samples in Q19.12 format
946 // sample at index 0 is FL
947 // sample at index 1 is FR
948 // sample at index 2 is FC
949 // sample at index 3 is LFE
950 // sample at index 4 is RL
951 // sample at index 5 is RR
952 // sample at index 6 is SL
953 // sample at index 7 is SR
954 // code is mostly duplicated between the two values of accumulate to avoid repeating the test
955 // for every sample
956 if (accumulate) {
957 while (numFrames) {
958 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
Andy Hunged1172f2018-12-21 16:20:03 -0800959 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_FLOAT)
960 + (pSrc[3] * MINUS_3_DB_IN_FLOAT);
Ramesh Katurid7d01342016-05-02 15:03:47 +0530961 // FL + centerPlusLfeContrib + SL + RL
962 lt = pSrc[0] + centerPlusLfeContrib + pSrc[6] + pSrc[4];
963 // FR + centerPlusLfeContrib + SR + RR
964 rt = pSrc[1] + centerPlusLfeContrib + pSrc[7] + pSrc[5];
965 //accumulate in destination
966 pDst[0] = clamp_float(pDst[0] + (lt / 2.0f));
967 pDst[1] = clamp_float(pDst[1] + (rt / 2.0f));
968 pSrc += 8;
969 pDst += 2;
970 numFrames--;
971 }
972 } else { // same code as above but without adding and clamping pDst[i] to itself
973 while (numFrames) {
974 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
975 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_FLOAT)
976 + (pSrc[3] * MINUS_3_DB_IN_FLOAT);
977 // FL + centerPlusLfeContrib + SL + RL
978 lt = pSrc[0] + centerPlusLfeContrib + pSrc[6] + pSrc[4];
979 // FR + centerPlusLfeContrib + SR + RR
980 rt = pSrc[1] + centerPlusLfeContrib + pSrc[7] + pSrc[5];
981 // store in destination
982 pDst[0] = clamp_float(lt / 2.0f); // differs from when accumulate is true above
983 pDst[1] = clamp_float(rt / 2.0f); // differs from when accumulate is true above
984 pSrc += 8;
985 pDst += 2;
986 numFrames--;
987 }
988 }
989}
Andy Hungca1c4e42021-05-06 22:32:02 -0700990
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700991/*----------------------------------------------------------------------------
992 * Downmix_foldGeneric()
993 *----------------------------------------------------------------------------
994 * Purpose:
995 * downmix to stereo a multichannel signal whose format is:
996 * - has FL/FR
997 * - if using AUDIO_CHANNEL_OUT_SIDE*, it contains both left and right
998 * - if using AUDIO_CHANNEL_OUT_BACK*, it contains both left and right
999 * - doesn't use any of the AUDIO_CHANNEL_OUT_TOP* channels
1000 * - doesn't use any of the AUDIO_CHANNEL_OUT_FRONT_*_OF_CENTER channels
1001 * Only handles channel masks not enumerated in downmix_input_channel_mask_t
1002 *
1003 * Inputs:
1004 * mask the channel mask of pSrc
1005 * pSrc multichannel audio buffer to downmix
1006 * numFrames the number of multichannel frames to downmix
1007 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
1008 * or overwrite pDst (when false)
1009 *
1010 * Outputs:
1011 * pDst downmixed stereo audio samples
1012 *
1013 * Returns: false if multichannel format is not supported
1014 *
1015 *----------------------------------------------------------------------------
1016 */
Ramesh Katurid7d01342016-05-02 15:03:47 +05301017bool Downmix_foldGeneric(
1018 uint32_t mask, LVM_FLOAT *pSrc, LVM_FLOAT *pDst, size_t numFrames, bool accumulate) {
1019
1020 if (!Downmix_validChannelMask(mask)) {
1021 return false;
1022 }
1023
1024 const bool hasSides = (mask & kSides) != 0;
1025 const bool hasBacks = (mask & kBacks) != 0;
1026
1027 const int numChan = audio_channel_count_from_out_mask(mask);
1028 const bool hasFC = ((mask & AUDIO_CHANNEL_OUT_FRONT_CENTER) == AUDIO_CHANNEL_OUT_FRONT_CENTER);
1029 const bool hasLFE =
1030 ((mask & AUDIO_CHANNEL_OUT_LOW_FREQUENCY) == AUDIO_CHANNEL_OUT_LOW_FREQUENCY);
1031 const bool hasBC = ((mask & AUDIO_CHANNEL_OUT_BACK_CENTER) == AUDIO_CHANNEL_OUT_BACK_CENTER);
1032 // compute at what index each channel is: samples will be in the following order:
1033 // FL FR FC LFE BL BR BC SL SR
1034 // when a channel is not present, its index is set to the same as the index of the preceding
1035 // channel
1036 const int indexFC = hasFC ? 2 : 1; // front center
1037 const int indexLFE = hasLFE ? indexFC + 1 : indexFC; // low frequency
1038 const int indexBL = hasBacks ? indexLFE + 1 : indexLFE; // back left
1039 const int indexBR = hasBacks ? indexBL + 1 : indexBL; // back right
1040 const int indexBC = hasBC ? indexBR + 1 : indexBR; // back center
1041 const int indexSL = hasSides ? indexBC + 1 : indexBC; // side left
1042 const int indexSR = hasSides ? indexSL + 1 : indexSL; // side right
1043
1044 LVM_FLOAT lt, rt, centersLfeContrib;
1045 // code is mostly duplicated between the two values of accumulate to avoid repeating the test
1046 // for every sample
1047 if (accumulate) {
1048 while (numFrames) {
1049 // compute contribution of FC, BC and LFE
1050 centersLfeContrib = 0;
1051 if (hasFC) { centersLfeContrib += pSrc[indexFC]; }
1052 if (hasLFE) { centersLfeContrib += pSrc[indexLFE]; }
1053 if (hasBC) { centersLfeContrib += pSrc[indexBC]; }
1054 centersLfeContrib *= MINUS_3_DB_IN_FLOAT;
1055 // always has FL/FR
1056 lt = pSrc[0];
1057 rt = pSrc[1];
1058 // mix in sides and backs
1059 if (hasSides) {
1060 lt += pSrc[indexSL];
1061 rt += pSrc[indexSR];
1062 }
1063 if (hasBacks) {
1064 lt += pSrc[indexBL];
1065 rt += pSrc[indexBR];
1066 }
1067 lt += centersLfeContrib;
1068 rt += centersLfeContrib;
1069 // accumulate in destination
1070 pDst[0] = clamp_float(pDst[0] + (lt / 2.0f));
1071 pDst[1] = clamp_float(pDst[1] + (rt / 2.0f));
1072 pSrc += numChan;
1073 pDst += 2;
1074 numFrames--;
1075 }
1076 } else {
1077 while (numFrames) {
1078 // compute contribution of FC, BC and LFE
1079 centersLfeContrib = 0;
1080 if (hasFC) { centersLfeContrib += pSrc[indexFC]; }
1081 if (hasLFE) { centersLfeContrib += pSrc[indexLFE]; }
1082 if (hasBC) { centersLfeContrib += pSrc[indexBC]; }
1083 centersLfeContrib *= MINUS_3_DB_IN_FLOAT;
1084 // always has FL/FR
1085 lt = pSrc[0];
1086 rt = pSrc[1];
1087 // mix in sides and backs
1088 if (hasSides) {
1089 lt += pSrc[indexSL];
1090 rt += pSrc[indexSR];
1091 }
1092 if (hasBacks) {
1093 lt += pSrc[indexBL];
1094 rt += pSrc[indexBR];
1095 }
1096 lt += centersLfeContrib;
1097 rt += centersLfeContrib;
1098 // store in destination
1099 pDst[0] = clamp_float(lt / 2.0f); // differs from when accumulate is true above
1100 pDst[1] = clamp_float(rt / 2.0f); // differs from when accumulate is true above
1101 pSrc += numChan;
1102 pDst += 2;
1103 numFrames--;
1104 }
1105 }
1106 return true;
1107}