blob: e5f19749c2569a5efab953d94b1ac22393768bfe [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 Salyzyneb165612017-01-10 09:08:19 -080019#include <log/log.h>
Mark Salyzyn60d02072016-09-29 08:48:48 -070020
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -080021#include "EffectDownmix.h"
Andy Hung47068792021-05-18 16:28:32 -070022#include <math.h>
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -080023
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -070024// Do not submit with DOWNMIX_TEST_CHANNEL_INDEX defined, strictly for testing
25//#define DOWNMIX_TEST_CHANNEL_INDEX 0
26// Do not submit with DOWNMIX_ALWAYS_USE_GENERIC_DOWNMIXER defined, strictly for testing
27//#define DOWNMIX_ALWAYS_USE_GENERIC_DOWNMIXER 0
28
Andy Hung47068792021-05-18 16:28:32 -070029#define MINUS_3_DB_IN_FLOAT M_SQRT1_2 // -3dB = 0.70710678
30
31#define LVM_FLOAT float
32
33const uint32_t kSides = AUDIO_CHANNEL_OUT_SIDE_LEFT | AUDIO_CHANNEL_OUT_SIDE_RIGHT;
34const uint32_t kBacks = AUDIO_CHANNEL_OUT_BACK_LEFT | AUDIO_CHANNEL_OUT_BACK_RIGHT;
35const uint32_t kUnsupported =
36 AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER | AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER |
37 AUDIO_CHANNEL_OUT_TOP_CENTER |
38 AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT |
39 AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER |
40 AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT |
41 AUDIO_CHANNEL_OUT_TOP_BACK_LEFT |
42 AUDIO_CHANNEL_OUT_TOP_BACK_CENTER |
43 AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT;
44
45typedef enum {
46 DOWNMIX_STATE_UNINITIALIZED,
47 DOWNMIX_STATE_INITIALIZED,
48 DOWNMIX_STATE_ACTIVE,
49} downmix_state_t;
50
51/* parameters for each downmixer */
52typedef struct {
53 downmix_state_t state;
54 downmix_type_t type;
55 bool apply_volume_correction;
56 uint8_t input_channel_count;
57} downmix_object_t;
58
59typedef struct downmix_module_s {
60 const struct effect_interface_s *itfe;
61 effect_config_t config;
62 downmix_object_t context;
63} downmix_module_t;
Ramesh Katurid7d01342016-05-02 15:03:47 +053064
Glenn Kasten029a64e2014-04-15 09:58:55 -070065// subset of possible audio_channel_mask_t values, and AUDIO_CHANNEL_OUT_* renamed to CHANNEL_MASK_*
Jean-Michel Trividb46b482012-04-23 11:29:26 -070066typedef enum {
Glenn Kasten029a64e2014-04-15 09:58:55 -070067 CHANNEL_MASK_QUAD_BACK = AUDIO_CHANNEL_OUT_QUAD_BACK,
68 CHANNEL_MASK_QUAD_SIDE = AUDIO_CHANNEL_OUT_QUAD_SIDE,
69 CHANNEL_MASK_5POINT1_BACK = AUDIO_CHANNEL_OUT_5POINT1_BACK,
70 CHANNEL_MASK_5POINT1_SIDE = AUDIO_CHANNEL_OUT_5POINT1_SIDE,
71 CHANNEL_MASK_7POINT1 = AUDIO_CHANNEL_OUT_7POINT1,
Jean-Michel Trividb46b482012-04-23 11:29:26 -070072} downmix_input_channel_mask_t;
73
Andy Hung47068792021-05-18 16:28:32 -070074// Audio Effect API
75static int32_t DownmixLib_Create(const effect_uuid_t *uuid,
76 int32_t sessionId,
77 int32_t ioId,
78 effect_handle_t *pHandle);
79static int32_t DownmixLib_Release(effect_handle_t handle);
80static int32_t DownmixLib_GetDescriptor(const effect_uuid_t *uuid,
81 effect_descriptor_t *pDescriptor);
82static int32_t Downmix_Process(effect_handle_t self,
83 audio_buffer_t *inBuffer,
84 audio_buffer_t *outBuffer);
85static int32_t Downmix_Command(effect_handle_t self,
86 uint32_t cmdCode,
87 uint32_t cmdSize,
88 void *pCmdData,
89 uint32_t *replySize,
90 void *pReplyData);
91static int32_t Downmix_GetDescriptor(effect_handle_t self,
92 effect_descriptor_t *pDescriptor);
93
94// Internal methods
95static int Downmix_Init(downmix_module_t *pDwmModule);
96static int Downmix_Configure(downmix_module_t *pDwmModule, effect_config_t *pConfig, bool init);
97static int Downmix_Reset(downmix_object_t *pDownmixer, bool init);
98static int Downmix_setParameter(
99 downmix_object_t *pDownmixer, int32_t param, uint32_t size, void *pValue);
100static int Downmix_getParameter(
101 downmix_object_t *pDownmixer, int32_t param, uint32_t *pSize, void *pValue);
102static void Downmix_foldFromQuad(float *pSrc, float *pDst, size_t numFrames, bool accumulate);
103static void Downmix_foldFrom5Point1(float *pSrc, float *pDst, size_t numFrames, bool accumulate);
104static void Downmix_foldFrom7Point1(float *pSrc, float *pDst, size_t numFrames, bool accumulate);
105static bool Downmix_foldGeneric(
106 uint32_t mask, float *pSrc, float *pDst, size_t numFrames, bool accumulate);
107
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800108// effect_handle_t interface implementation for downmix effect
109const struct effect_interface_s gDownmixInterface = {
110 Downmix_Process,
111 Downmix_Command,
112 Downmix_GetDescriptor,
113 NULL /* no process_reverse function, no reference stream needed */
114};
115
Marco Nelissen7f16b192012-10-25 16:05:57 -0700116// This is the only symbol that needs to be exported
117__attribute__ ((visibility ("default")))
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800118audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
synergydevc9d8ea72013-10-19 22:51:33 -0700119 .tag = AUDIO_EFFECT_LIBRARY_TAG,
120 .version = EFFECT_LIBRARY_API_VERSION,
121 .name = "Downmix Library",
122 .implementor = "The Android Open Source Project",
123 .create_effect = DownmixLib_Create,
124 .release_effect = DownmixLib_Release,
125 .get_descriptor = DownmixLib_GetDescriptor,
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800126};
127
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800128// AOSP insert downmix UUID: 93f04452-e4fe-41cc-91f9-e475b6d1d69f
129static const effect_descriptor_t gDownmixDescriptor = {
130 EFFECT_UIID_DOWNMIX__, //type
131 {0x93f04452, 0xe4fe, 0x41cc, 0x91f9, {0xe4, 0x75, 0xb6, 0xd1, 0xd6, 0x9f}}, // uuid
132 EFFECT_CONTROL_API_VERSION,
133 EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST,
134 0, //FIXME what value should be reported? // cpu load
135 0, //FIXME what value should be reported? // memory usage
136 "Multichannel Downmix To Stereo", // human readable effect name
137 "The Android Open Source Project" // human readable effect implementor name
138};
139
140// gDescriptors contains pointers to all defined effect descriptor in this library
141static const effect_descriptor_t * const gDescriptors[] = {
142 &gDownmixDescriptor
143};
144
145// number of effects in this library
146const int kNbEffects = sizeof(gDescriptors) / sizeof(const effect_descriptor_t *);
Andy Hungca1c4e42021-05-06 22:32:02 -0700147
Ramesh Katurid7d01342016-05-02 15:03:47 +0530148static LVM_FLOAT clamp_float(LVM_FLOAT a) {
149 if (a > 1.0f) {
150 return 1.0f;
151 }
152 else if (a < -1.0f) {
153 return -1.0f;
154 }
155 else {
156 return a;
157 }
158}
Andy Hungca1c4e42021-05-06 22:32:02 -0700159
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800160/*----------------------------------------------------------------------------
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700161 * Test code
162 *--------------------------------------------------------------------------*/
163#ifdef DOWNMIX_TEST_CHANNEL_INDEX
164// strictly for testing, logs the indices of the channels for a given mask,
165// uses the same code as Downmix_foldGeneric()
166void Downmix_testIndexComputation(uint32_t mask) {
Andy Hung47068792021-05-18 16:28:32 -0700167 ALOGI("Testing index computation for %#x:", mask);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700168 // check against unsupported channels
169 if (mask & kUnsupported) {
170 ALOGE("Unsupported channels (top or front left/right of center)");
171 return;
172 }
173 // verify has FL/FR
174 if ((mask & AUDIO_CHANNEL_OUT_STEREO) != AUDIO_CHANNEL_OUT_STEREO) {
175 ALOGE("Front channels must be present");
176 return;
177 }
178 // verify uses SIDE as a pair (ok if not using SIDE at all)
179 bool hasSides = false;
180 if ((mask & kSides) != 0) {
181 if ((mask & kSides) != kSides) {
182 ALOGE("Side channels must be used as a pair");
183 return;
184 }
185 hasSides = true;
186 }
187 // verify uses BACK as a pair (ok if not using BACK at all)
188 bool hasBacks = false;
189 if ((mask & kBacks) != 0) {
190 if ((mask & kBacks) != kBacks) {
191 ALOGE("Back channels must be used as a pair");
192 return;
193 }
194 hasBacks = true;
195 }
196
Andy Hunge5412692014-05-16 11:25:07 -0700197 const int numChan = audio_channel_count_from_out_mask(mask);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700198 const bool hasFC = ((mask & AUDIO_CHANNEL_OUT_FRONT_CENTER) == AUDIO_CHANNEL_OUT_FRONT_CENTER);
199 const bool hasLFE =
200 ((mask & AUDIO_CHANNEL_OUT_LOW_FREQUENCY) == AUDIO_CHANNEL_OUT_LOW_FREQUENCY);
201 const bool hasBC = ((mask & AUDIO_CHANNEL_OUT_BACK_CENTER) == AUDIO_CHANNEL_OUT_BACK_CENTER);
202 // compute at what index each channel is: samples will be in the following order:
203 // FL FR FC LFE BL BR BC SL SR
204 // when a channel is not present, its index is set to the same as the index of the preceding
205 // channel
206 const int indexFC = hasFC ? 2 : 1; // front center
207 const int indexLFE = hasLFE ? indexFC + 1 : indexFC; // low frequency
208 const int indexBL = hasBacks ? indexLFE + 1 : indexLFE; // back left
209 const int indexBR = hasBacks ? indexBL + 1 : indexBL; // back right
210 const int indexBC = hasBC ? indexBR + 1 : indexBR; // back center
211 const int indexSL = hasSides ? indexBC + 1 : indexBC; // side left
212 const int indexSR = hasSides ? indexSL + 1 : indexSL; // side right
213
214 ALOGI(" FL FR FC LFE BL BR BC SL SR");
215 ALOGI(" %d %d %d %d %d %d %d %d %d",
216 0, 1, indexFC, indexLFE, indexBL, indexBR, indexBC, indexSL, indexSR);
217}
218#endif
219
Haynes Mathew Georgeee507ef2015-07-15 11:06:05 -0700220static bool Downmix_validChannelMask(uint32_t mask)
221{
222 if (!mask) {
223 return false;
224 }
225 // check against unsupported channels
226 if (mask & kUnsupported) {
227 ALOGE("Unsupported channels (top or front left/right of center)");
228 return false;
229 }
230 // verify has FL/FR
231 if ((mask & AUDIO_CHANNEL_OUT_STEREO) != AUDIO_CHANNEL_OUT_STEREO) {
232 ALOGE("Front channels must be present");
233 return false;
234 }
235 // verify uses SIDE as a pair (ok if not using SIDE at all)
236 if ((mask & kSides) != 0) {
237 if ((mask & kSides) != kSides) {
238 ALOGE("Side channels must be used as a pair");
239 return false;
240 }
241 }
242 // verify uses BACK as a pair (ok if not using BACK at all)
243 if ((mask & kBacks) != 0) {
244 if ((mask & kBacks) != kBacks) {
245 ALOGE("Back channels must be used as a pair");
246 return false;
247 }
248 }
249 return true;
250}
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700251
252/*----------------------------------------------------------------------------
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800253 * Effect API implementation
254 *--------------------------------------------------------------------------*/
255
256/*--- Effect Library Interface Implementation ---*/
257
Andy Hung47068792021-05-18 16:28:32 -0700258static int32_t DownmixLib_Create(const effect_uuid_t *uuid,
Andy Hung679c4712021-05-19 14:14:54 -0700259 int32_t /* sessionId */,
260 int32_t /* ioId */,
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800261 effect_handle_t *pHandle) {
262 int ret;
263 int i;
264 downmix_module_t *module;
265 const effect_descriptor_t *desc;
266
267 ALOGV("DownmixLib_Create()");
268
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700269#ifdef DOWNMIX_TEST_CHANNEL_INDEX
270 // should work (won't log an error)
271 ALOGI("DOWNMIX_TEST_CHANNEL_INDEX: should work:");
272 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT |
273 AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_BACK_CENTER);
274 Downmix_testIndexComputation(CHANNEL_MASK_QUAD_SIDE | CHANNEL_MASK_QUAD_BACK);
275 Downmix_testIndexComputation(CHANNEL_MASK_5POINT1_SIDE | AUDIO_CHANNEL_OUT_BACK_CENTER);
276 Downmix_testIndexComputation(CHANNEL_MASK_5POINT1_BACK | AUDIO_CHANNEL_OUT_BACK_CENTER);
277 // shouldn't work (will log an error, won't display channel indices)
278 ALOGI("DOWNMIX_TEST_CHANNEL_INDEX: should NOT work:");
279 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT |
280 AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_BACK_LEFT);
281 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT |
282 AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_SIDE_LEFT);
283 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT |
284 AUDIO_CHANNEL_OUT_BACK_LEFT | AUDIO_CHANNEL_OUT_BACK_RIGHT);
285 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT |
286 AUDIO_CHANNEL_OUT_SIDE_LEFT | AUDIO_CHANNEL_OUT_SIDE_RIGHT);
287#endif
288
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800289 if (pHandle == NULL || uuid == NULL) {
290 return -EINVAL;
291 }
292
293 for (i = 0 ; i < kNbEffects ; i++) {
294 desc = gDescriptors[i];
295 if (memcmp(uuid, &desc->uuid, sizeof(effect_uuid_t)) == 0) {
296 break;
297 }
298 }
299
300 if (i == kNbEffects) {
301 return -ENOENT;
302 }
303
Andy Hung19748032021-05-19 17:34:16 -0700304 module = new downmix_module_t{};
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800305
306 module->itfe = &gDownmixInterface;
307
308 module->context.state = DOWNMIX_STATE_UNINITIALIZED;
309
310 ret = Downmix_Init(module);
311 if (ret < 0) {
312 ALOGW("DownmixLib_Create() init failed");
313 free(module);
314 return ret;
315 }
316
317 *pHandle = (effect_handle_t) module;
318
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700319 ALOGV("DownmixLib_Create() %p , size %zu", module, sizeof(downmix_module_t));
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800320
321 return 0;
322}
323
Andy Hung47068792021-05-18 16:28:32 -0700324static int32_t DownmixLib_Release(effect_handle_t handle) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800325 downmix_module_t *pDwmModule = (downmix_module_t *)handle;
326
327 ALOGV("DownmixLib_Release() %p", handle);
328 if (handle == NULL) {
329 return -EINVAL;
330 }
331
332 pDwmModule->context.state = DOWNMIX_STATE_UNINITIALIZED;
333
Andy Hung19748032021-05-19 17:34:16 -0700334 delete pDwmModule;
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800335 return 0;
336}
337
Andy Hung47068792021-05-18 16:28:32 -0700338static int32_t DownmixLib_GetDescriptor(
339 const effect_uuid_t *uuid, effect_descriptor_t *pDescriptor) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800340 ALOGV("DownmixLib_GetDescriptor()");
341 int i;
342
343 if (pDescriptor == NULL || uuid == NULL){
344 ALOGE("DownmixLib_Create() called with NULL pointer");
345 return -EINVAL;
346 }
347 ALOGV("DownmixLib_GetDescriptor() nb effects=%d", kNbEffects);
348 for (i = 0; i < kNbEffects; i++) {
349 ALOGV("DownmixLib_GetDescriptor() i=%d", i);
350 if (memcmp(uuid, &gDescriptors[i]->uuid, sizeof(effect_uuid_t)) == 0) {
351 memcpy(pDescriptor, gDescriptors[i], sizeof(effect_descriptor_t));
Andy Hung47068792021-05-18 16:28:32 -0700352 ALOGV("EffectGetDescriptor - UUID matched downmix type %d, UUID = %#x",
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800353 i, gDescriptors[i]->uuid.timeLow);
354 return 0;
355 }
356 }
357
358 return -EINVAL;
359}
360
Ramesh Katurid7d01342016-05-02 15:03:47 +0530361/*--- Effect Control Interface Implementation ---*/
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800362
Andy Hung47068792021-05-18 16:28:32 -0700363static int32_t Downmix_Process(effect_handle_t self,
Ramesh Katurid7d01342016-05-02 15:03:47 +0530364 audio_buffer_t *inBuffer, audio_buffer_t *outBuffer) {
365
366 downmix_object_t *pDownmixer;
367 LVM_FLOAT *pSrc, *pDst;
368 downmix_module_t *pDwmModule = (downmix_module_t *)self;
369
370 if (pDwmModule == NULL) {
371 return -EINVAL;
372 }
373
374 if (inBuffer == NULL || inBuffer->raw == NULL ||
375 outBuffer == NULL || outBuffer->raw == NULL ||
376 inBuffer->frameCount != outBuffer->frameCount) {
377 return -EINVAL;
378 }
379
380 pDownmixer = (downmix_object_t*) &pDwmModule->context;
381
382 if (pDownmixer->state == DOWNMIX_STATE_UNINITIALIZED) {
383 ALOGE("Downmix_Process error: trying to use an uninitialized downmixer");
384 return -EINVAL;
385 } else if (pDownmixer->state == DOWNMIX_STATE_INITIALIZED) {
386 ALOGE("Downmix_Process error: trying to use a non-configured downmixer");
387 return -ENODATA;
388 }
389
390 pSrc = (LVM_FLOAT *) inBuffer->s16;
391 pDst = (LVM_FLOAT *) outBuffer->s16;
392 size_t numFrames = outBuffer->frameCount;
393
394 const bool accumulate =
395 (pDwmModule->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE);
396 const uint32_t downmixInputChannelMask = pDwmModule->config.inputCfg.channels;
397
398 switch(pDownmixer->type) {
399
400 case DOWNMIX_TYPE_STRIP:
401 if (accumulate) {
402 while (numFrames) {
403 pDst[0] = clamp_float(pDst[0] + pSrc[0]);
404 pDst[1] = clamp_float(pDst[1] + pSrc[1]);
405 pSrc += pDownmixer->input_channel_count;
406 pDst += 2;
407 numFrames--;
408 }
409 } else {
410 while (numFrames) {
411 pDst[0] = pSrc[0];
412 pDst[1] = pSrc[1];
413 pSrc += pDownmixer->input_channel_count;
414 pDst += 2;
415 numFrames--;
416 }
417 }
418 break;
419
420 case DOWNMIX_TYPE_FOLD:
421#ifdef DOWNMIX_ALWAYS_USE_GENERIC_DOWNMIXER
422 // bypass the optimized downmix routines for the common formats
423 if (!Downmix_foldGeneric(
424 downmixInputChannelMask, pSrc, pDst, numFrames, accumulate)) {
Andy Hung47068792021-05-18 16:28:32 -0700425 ALOGE("Multichannel configuration %#x is not supported",
Ramesh Katurid7d01342016-05-02 15:03:47 +0530426 downmixInputChannelMask);
427 return -EINVAL;
428 }
429 break;
430#endif
431 // optimize for the common formats
432 switch((downmix_input_channel_mask_t)downmixInputChannelMask) {
433 case CHANNEL_MASK_QUAD_BACK:
434 case CHANNEL_MASK_QUAD_SIDE:
435 Downmix_foldFromQuad(pSrc, pDst, numFrames, accumulate);
436 break;
437 case CHANNEL_MASK_5POINT1_BACK:
438 case CHANNEL_MASK_5POINT1_SIDE:
439 Downmix_foldFrom5Point1(pSrc, pDst, numFrames, accumulate);
440 break;
441 case CHANNEL_MASK_7POINT1:
442 Downmix_foldFrom7Point1(pSrc, pDst, numFrames, accumulate);
443 break;
444 default:
445 if (!Downmix_foldGeneric(
446 downmixInputChannelMask, pSrc, pDst, numFrames, accumulate)) {
Andy Hung47068792021-05-18 16:28:32 -0700447 ALOGE("Multichannel configuration %#x is not supported",
Ramesh Katurid7d01342016-05-02 15:03:47 +0530448 downmixInputChannelMask);
449 return -EINVAL;
450 }
451 break;
452 }
453 break;
454
455 default:
456 return -EINVAL;
457 }
458
459 return 0;
460}
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800461
Andy Hung47068792021-05-18 16:28:32 -0700462static int32_t Downmix_Command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800463 void *pCmdData, uint32_t *replySize, void *pReplyData) {
464
465 downmix_module_t *pDwmModule = (downmix_module_t *) self;
466 downmix_object_t *pDownmixer;
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800467
468 if (pDwmModule == NULL || pDwmModule->context.state == DOWNMIX_STATE_UNINITIALIZED) {
469 return -EINVAL;
470 }
471
472 pDownmixer = (downmix_object_t*) &pDwmModule->context;
473
Andy Hung47068792021-05-18 16:28:32 -0700474 ALOGV("Downmix_Command command %u cmdSize %u", cmdCode, cmdSize);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800475
476 switch (cmdCode) {
477 case EFFECT_CMD_INIT:
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 *(int *) pReplyData = Downmix_Init(pDwmModule);
482 break;
483
484 case EFFECT_CMD_SET_CONFIG:
485 if (pCmdData == NULL || cmdSize != sizeof(effect_config_t)
Eric Laurent0f714a42015-06-19 15:33:57 -0700486 || pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800487 return -EINVAL;
488 }
489 *(int *) pReplyData = Downmix_Configure(pDwmModule,
490 (effect_config_t *)pCmdData, false);
491 break;
492
493 case EFFECT_CMD_RESET:
494 Downmix_Reset(pDownmixer, false);
495 break;
496
Andy Hung19748032021-05-19 17:34:16 -0700497 case EFFECT_CMD_GET_PARAM: {
Andy Hung47068792021-05-18 16:28:32 -0700498 ALOGV("Downmix_Command EFFECT_CMD_GET_PARAM pCmdData %p, *replySize %u, pReplyData: %p",
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800499 pCmdData, *replySize, pReplyData);
500 if (pCmdData == NULL || cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t)) ||
Eric Laurent0f714a42015-06-19 15:33:57 -0700501 pReplyData == NULL || replySize == NULL ||
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800502 *replySize < (int) sizeof(effect_param_t) + 2 * sizeof(int32_t)) {
503 return -EINVAL;
504 }
505 effect_param_t *rep = (effect_param_t *) pReplyData;
506 memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + sizeof(int32_t));
Andy Hung47068792021-05-18 16:28:32 -0700507 ALOGV("Downmix_Command EFFECT_CMD_GET_PARAM param %d, replySize %u",
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800508 *(int32_t *)rep->data, rep->vsize);
509 rep->status = Downmix_getParameter(pDownmixer, *(int32_t *)rep->data, &rep->vsize,
510 rep->data + sizeof(int32_t));
511 *replySize = sizeof(effect_param_t) + sizeof(int32_t) + rep->vsize;
512 break;
Andy Hung19748032021-05-19 17:34:16 -0700513 }
514 case EFFECT_CMD_SET_PARAM: {
Andy Hung47068792021-05-18 16:28:32 -0700515 ALOGV("Downmix_Command EFFECT_CMD_SET_PARAM cmdSize %d pCmdData %p, *replySize %u"
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700516 ", pReplyData %p", cmdSize, pCmdData, *replySize, pReplyData);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800517 if (pCmdData == NULL || (cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t)))
Eric Laurent0f714a42015-06-19 15:33:57 -0700518 || pReplyData == NULL || replySize == NULL || *replySize != (int)sizeof(int32_t)) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800519 return -EINVAL;
520 }
521 effect_param_t *cmd = (effect_param_t *) pCmdData;
Mikhail Naganov804632a2017-07-24 17:25:47 -0700522 if (cmd->psize != sizeof(int32_t)) {
523 android_errorWriteLog(0x534e4554, "63662938");
524 return -EINVAL;
525 }
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800526 *(int *)pReplyData = Downmix_setParameter(pDownmixer, *(int32_t *)cmd->data,
527 cmd->vsize, cmd->data + sizeof(int32_t));
528 break;
Andy Hung19748032021-05-19 17:34:16 -0700529 }
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800530
531 case EFFECT_CMD_SET_PARAM_DEFERRED:
532 //FIXME implement
533 ALOGW("Downmix_Command command EFFECT_CMD_SET_PARAM_DEFERRED not supported, FIXME");
534 break;
535
536 case EFFECT_CMD_SET_PARAM_COMMIT:
537 //FIXME implement
538 ALOGW("Downmix_Command command EFFECT_CMD_SET_PARAM_COMMIT not supported, FIXME");
539 break;
540
541 case EFFECT_CMD_ENABLE:
Eric Laurent0f714a42015-06-19 15:33:57 -0700542 if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800543 return -EINVAL;
544 }
545 if (pDownmixer->state != DOWNMIX_STATE_INITIALIZED) {
546 return -ENOSYS;
547 }
548 pDownmixer->state = DOWNMIX_STATE_ACTIVE;
549 ALOGV("EFFECT_CMD_ENABLE() OK");
550 *(int *)pReplyData = 0;
551 break;
552
553 case EFFECT_CMD_DISABLE:
Eric Laurent0f714a42015-06-19 15:33:57 -0700554 if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800555 return -EINVAL;
556 }
557 if (pDownmixer->state != DOWNMIX_STATE_ACTIVE) {
558 return -ENOSYS;
559 }
560 pDownmixer->state = DOWNMIX_STATE_INITIALIZED;
561 ALOGV("EFFECT_CMD_DISABLE() OK");
562 *(int *)pReplyData = 0;
563 break;
564
565 case EFFECT_CMD_SET_DEVICE:
566 if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) {
567 return -EINVAL;
568 }
569 // FIXME change type if playing on headset vs speaker
Andy Hung47068792021-05-18 16:28:32 -0700570 ALOGV("Downmix_Command EFFECT_CMD_SET_DEVICE: %#x", *(uint32_t *)pCmdData);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800571 break;
572
573 case EFFECT_CMD_SET_VOLUME: {
574 // audio output is always stereo => 2 channel volumes
575 if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t) * 2) {
576 return -EINVAL;
577 }
578 // FIXME change volume
579 ALOGW("Downmix_Command command EFFECT_CMD_SET_VOLUME not supported, FIXME");
580 float left = (float)(*(uint32_t *)pCmdData) / (1 << 24);
581 float right = (float)(*((uint32_t *)pCmdData + 1)) / (1 << 24);
582 ALOGV("Downmix_Command EFFECT_CMD_SET_VOLUME: left %f, right %f ", left, right);
583 break;
584 }
585
586 case EFFECT_CMD_SET_AUDIO_MODE:
587 if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) {
588 return -EINVAL;
589 }
Andy Hung47068792021-05-18 16:28:32 -0700590 ALOGV("Downmix_Command EFFECT_CMD_SET_AUDIO_MODE: %u", *(uint32_t *)pCmdData);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800591 break;
592
593 case EFFECT_CMD_SET_CONFIG_REVERSE:
594 case EFFECT_CMD_SET_INPUT_DEVICE:
595 // these commands are ignored by a downmix effect
596 break;
597
598 default:
Andy Hung47068792021-05-18 16:28:32 -0700599 ALOGW("Downmix_Command invalid command %u", cmdCode);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800600 return -EINVAL;
601 }
602
603 return 0;
604}
605
Andy Hung47068792021-05-18 16:28:32 -0700606static int32_t Downmix_GetDescriptor(effect_handle_t self, effect_descriptor_t *pDescriptor)
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800607{
608 downmix_module_t *pDwnmxModule = (downmix_module_t *) self;
609
610 if (pDwnmxModule == NULL ||
611 pDwnmxModule->context.state == DOWNMIX_STATE_UNINITIALIZED) {
612 return -EINVAL;
613 }
614
615 memcpy(pDescriptor, &gDownmixDescriptor, sizeof(effect_descriptor_t));
616
617 return 0;
618}
619
620
621/*----------------------------------------------------------------------------
622 * Downmix internal functions
623 *--------------------------------------------------------------------------*/
624
625/*----------------------------------------------------------------------------
626 * Downmix_Init()
627 *----------------------------------------------------------------------------
628 * Purpose:
629 * Initialize downmix context and apply default parameters
630 *
631 * Inputs:
632 * pDwmModule pointer to downmix effect module
633 *
634 * Outputs:
635 *
636 * Returns:
637 * 0 indicates success
638 *
639 * Side Effects:
640 * updates:
641 * pDwmModule->context.type
642 * pDwmModule->context.apply_volume_correction
643 * pDwmModule->config.inputCfg
644 * pDwmModule->config.outputCfg
645 * pDwmModule->config.inputCfg.samplingRate
646 * pDwmModule->config.outputCfg.samplingRate
647 * pDwmModule->context.state
648 * doesn't set:
649 * pDwmModule->itfe
650 *
651 *----------------------------------------------------------------------------
652 */
653
Andy Hung47068792021-05-18 16:28:32 -0700654static int Downmix_Init(downmix_module_t *pDwmModule) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800655
656 ALOGV("Downmix_Init module %p", pDwmModule);
657 int ret = 0;
658
659 memset(&pDwmModule->context, 0, sizeof(downmix_object_t));
660
661 pDwmModule->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
Andy Hung47068792021-05-18 16:28:32 -0700662 pDwmModule->config.inputCfg.format = AUDIO_FORMAT_PCM_FLOAT;
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800663 pDwmModule->config.inputCfg.channels = AUDIO_CHANNEL_OUT_7POINT1;
664 pDwmModule->config.inputCfg.bufferProvider.getBuffer = NULL;
665 pDwmModule->config.inputCfg.bufferProvider.releaseBuffer = NULL;
666 pDwmModule->config.inputCfg.bufferProvider.cookie = NULL;
667 pDwmModule->config.inputCfg.mask = EFFECT_CONFIG_ALL;
668
669 pDwmModule->config.inputCfg.samplingRate = 44100;
670 pDwmModule->config.outputCfg.samplingRate = pDwmModule->config.inputCfg.samplingRate;
671
672 // set a default value for the access mode, but should be overwritten by caller
673 pDwmModule->config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
Andy Hung47068792021-05-18 16:28:32 -0700674 pDwmModule->config.outputCfg.format = AUDIO_FORMAT_PCM_FLOAT;
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800675 pDwmModule->config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
676 pDwmModule->config.outputCfg.bufferProvider.getBuffer = NULL;
677 pDwmModule->config.outputCfg.bufferProvider.releaseBuffer = NULL;
678 pDwmModule->config.outputCfg.bufferProvider.cookie = NULL;
679 pDwmModule->config.outputCfg.mask = EFFECT_CONFIG_ALL;
680
681 ret = Downmix_Configure(pDwmModule, &pDwmModule->config, true);
682 if (ret != 0) {
683 ALOGV("Downmix_Init error %d on module %p", ret, pDwmModule);
684 } else {
685 pDwmModule->context.state = DOWNMIX_STATE_INITIALIZED;
686 }
687
688 return ret;
689}
690
691
692/*----------------------------------------------------------------------------
693 * Downmix_Configure()
694 *----------------------------------------------------------------------------
695 * Purpose:
696 * Set input and output audio configuration.
697 *
698 * Inputs:
699 * pDwmModule pointer to downmix effect module
700 * pConfig pointer to effect_config_t structure containing input
701 * and output audio parameters configuration
702 * init true if called from init function
703 *
704 * Outputs:
705 *
706 * Returns:
707 * 0 indicates success
708 *
709 * Side Effects:
710 *
711 *----------------------------------------------------------------------------
712 */
713
Andy Hung47068792021-05-18 16:28:32 -0700714static int Downmix_Configure(downmix_module_t *pDwmModule, effect_config_t *pConfig, bool init) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800715
716 downmix_object_t *pDownmixer = &pDwmModule->context;
717
718 // Check configuration compatibility with build options, and effect capabilities
719 if (pConfig->inputCfg.samplingRate != pConfig->outputCfg.samplingRate
Andy Hung47068792021-05-18 16:28:32 -0700720 || pConfig->outputCfg.channels != AUDIO_CHANNEL_OUT_STEREO
721 || pConfig->inputCfg.format != AUDIO_FORMAT_PCM_FLOAT
722 || pConfig->outputCfg.format != AUDIO_FORMAT_PCM_FLOAT) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800723 ALOGE("Downmix_Configure error: invalid config");
724 return -EINVAL;
725 }
726
Chih-Wei Huanga93c8c92013-01-10 20:52:57 +0800727 if (&pDwmModule->config != pConfig) {
728 memcpy(&pDwmModule->config, pConfig, sizeof(effect_config_t));
729 }
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800730
731 if (init) {
732 pDownmixer->type = DOWNMIX_TYPE_FOLD;
733 pDownmixer->apply_volume_correction = false;
734 pDownmixer->input_channel_count = 8; // matches default input of AUDIO_CHANNEL_OUT_7POINT1
735 } else {
Haynes Mathew Georgeee507ef2015-07-15 11:06:05 -0700736 // when configuring the effect, do not allow a blank or unsupported channel mask
737 if (!Downmix_validChannelMask(pConfig->inputCfg.channels)) {
738 ALOGE("Downmix_Configure error: input channel mask(0x%x) not supported",
739 pConfig->inputCfg.channels);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800740 return -EINVAL;
741 }
Andy Hunge5412692014-05-16 11:25:07 -0700742 pDownmixer->input_channel_count =
743 audio_channel_count_from_out_mask(pConfig->inputCfg.channels);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800744 }
745
746 Downmix_Reset(pDownmixer, init);
747
748 return 0;
749}
750
751
752/*----------------------------------------------------------------------------
753 * Downmix_Reset()
754 *----------------------------------------------------------------------------
755 * Purpose:
756 * Reset internal states.
757 *
758 * Inputs:
759 * pDownmixer pointer to downmix context
760 * init true if called from init function
761 *
762 * Outputs:
763*
764 * Returns:
765 * 0 indicates success
766 *
767 * Side Effects:
768 *
769 *----------------------------------------------------------------------------
770 */
771
Andy Hung679c4712021-05-19 14:14:54 -0700772static int Downmix_Reset(downmix_object_t* /* pDownmixer */, bool /* init */) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800773 // nothing to do here
774 return 0;
775}
776
777
778/*----------------------------------------------------------------------------
779 * Downmix_setParameter()
780 *----------------------------------------------------------------------------
781 * Purpose:
782 * Set a Downmix parameter
783 *
784 * Inputs:
785 * pDownmixer handle to instance data
786 * param parameter
787 * pValue pointer to parameter value
788 * size value size
789 *
790 * Outputs:
791 *
792 * Returns:
793 * 0 indicates success
794 *
795 * Side Effects:
796 *
797 *----------------------------------------------------------------------------
798 */
Andy Hung47068792021-05-18 16:28:32 -0700799static int Downmix_setParameter(
800 downmix_object_t *pDownmixer, int32_t param, uint32_t size, void *pValue) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800801
802 int16_t value16;
Andy Hung47068792021-05-18 16:28:32 -0700803 ALOGV("Downmix_setParameter, context %p, param %d, value16 %d, value32 %d",
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800804 pDownmixer, param, *(int16_t *)pValue, *(int32_t *)pValue);
805
806 switch (param) {
807
808 case DOWNMIX_PARAM_TYPE:
809 if (size != sizeof(downmix_type_t)) {
Andy Hung47068792021-05-18 16:28:32 -0700810 ALOGE("Downmix_setParameter(DOWNMIX_PARAM_TYPE) invalid size %u, should be %zu",
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800811 size, sizeof(downmix_type_t));
812 return -EINVAL;
813 }
814 value16 = *(int16_t *)pValue;
Andy Hung47068792021-05-18 16:28:32 -0700815 ALOGV("set DOWNMIX_PARAM_TYPE, type %d", value16);
Jean-Michel Trivi7d5b2622012-04-04 18:54:36 -0700816 if (!((value16 > DOWNMIX_TYPE_INVALID) && (value16 <= DOWNMIX_TYPE_LAST))) {
Andy Hung47068792021-05-18 16:28:32 -0700817 ALOGE("Downmix_setParameter invalid DOWNMIX_PARAM_TYPE value %d", value16);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800818 return -EINVAL;
819 } else {
820 pDownmixer->type = (downmix_type_t) value16;
821 break;
822
823 default:
Andy Hung47068792021-05-18 16:28:32 -0700824 ALOGE("Downmix_setParameter unknown parameter %d", param);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800825 return -EINVAL;
826 }
827}
828
829 return 0;
830} /* end Downmix_setParameter */
831
832
833/*----------------------------------------------------------------------------
834 * Downmix_getParameter()
835 *----------------------------------------------------------------------------
836 * Purpose:
837 * Get a Downmix parameter
838 *
839 * Inputs:
840 * pDownmixer handle to instance data
841 * param parameter
842 * pValue pointer to variable to hold retrieved value
843 * pSize pointer to value size: maximum size as input
844 *
845 * Outputs:
846 * *pValue updated with parameter value
847 * *pSize updated with actual value size
848 *
849 * Returns:
850 * 0 indicates success
851 *
852 * Side Effects:
853 *
854 *----------------------------------------------------------------------------
855 */
Andy Hung47068792021-05-18 16:28:32 -0700856static int Downmix_getParameter(
857 downmix_object_t *pDownmixer, int32_t param, uint32_t *pSize, void *pValue) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800858 int16_t *pValue16;
859
860 switch (param) {
861
862 case DOWNMIX_PARAM_TYPE:
863 if (*pSize < sizeof(int16_t)) {
Andy Hung47068792021-05-18 16:28:32 -0700864 ALOGE("Downmix_getParameter invalid parameter size %u for DOWNMIX_PARAM_TYPE", *pSize);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800865 return -EINVAL;
866 }
867 pValue16 = (int16_t *)pValue;
868 *pValue16 = (int16_t) pDownmixer->type;
869 *pSize = sizeof(int16_t);
Andy Hung47068792021-05-18 16:28:32 -0700870 ALOGV("Downmix_getParameter DOWNMIX_PARAM_TYPE is %d", *pValue16);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800871 break;
872
873 default:
Andy Hung47068792021-05-18 16:28:32 -0700874 ALOGE("Downmix_getParameter unknown parameter %d", param);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800875 return -EINVAL;
876 }
877
878 return 0;
879} /* end Downmix_getParameter */
880
881
882/*----------------------------------------------------------------------------
883 * Downmix_foldFromQuad()
884 *----------------------------------------------------------------------------
885 * Purpose:
886 * downmix a quad signal to stereo
887 *
888 * Inputs:
889 * pSrc quad audio samples to downmix
890 * numFrames the number of quad frames to downmix
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700891 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
892 * or overwrite pDst (when false)
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800893 *
894 * Outputs:
895 * pDst downmixed stereo audio samples
896 *
897 *----------------------------------------------------------------------------
898 */
Ramesh Katurid7d01342016-05-02 15:03:47 +0530899void Downmix_foldFromQuad(LVM_FLOAT *pSrc, LVM_FLOAT *pDst, size_t numFrames, bool accumulate) {
900 // sample at index 0 is FL
901 // sample at index 1 is FR
902 // sample at index 2 is RL
903 // sample at index 3 is RR
904 if (accumulate) {
905 while (numFrames) {
906 // FL + RL
907 pDst[0] = clamp_float(pDst[0] + ((pSrc[0] + pSrc[2]) / 2.0f));
908 // FR + RR
909 pDst[1] = clamp_float(pDst[1] + ((pSrc[1] + pSrc[3]) / 2.0f));
910 pSrc += 4;
911 pDst += 2;
912 numFrames--;
913 }
914 } else { // same code as above but without adding and clamping pDst[i] to itself
915 while (numFrames) {
916 // FL + RL
917 pDst[0] = clamp_float((pSrc[0] + pSrc[2]) / 2.0f);
918 // FR + RR
919 pDst[1] = clamp_float((pSrc[1] + pSrc[3]) / 2.0f);
920 pSrc += 4;
921 pDst += 2;
922 numFrames--;
923 }
924 }
925}
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800926
927/*----------------------------------------------------------------------------
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800928 * Downmix_foldFrom5Point1()
929 *----------------------------------------------------------------------------
930 * Purpose:
931 * downmix a 5.1 signal to stereo
932 *
933 * Inputs:
934 * pSrc 5.1 audio samples to downmix
935 * numFrames the number of 5.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_foldFrom5Point1(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 // code is mostly duplicated between the two values of accumulate to avoid repeating the test
953 // for every sample
954 if (accumulate) {
955 while (numFrames) {
956 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
957 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_FLOAT)
958 + (pSrc[3] * MINUS_3_DB_IN_FLOAT);
959 // FL + centerPlusLfeContrib + RL
960 lt = pSrc[0] + centerPlusLfeContrib + pSrc[4];
961 // FR + centerPlusLfeContrib + RR
962 rt = pSrc[1] + centerPlusLfeContrib + pSrc[5];
963 // accumulate in destination
964 pDst[0] = clamp_float(pDst[0] + (lt / 2.0f));
965 pDst[1] = clamp_float(pDst[1] + (rt / 2.0f));
966 pSrc += 6;
967 pDst += 2;
968 numFrames--;
969 }
970 } else { // same code as above but without adding and clamping pDst[i] to itself
971 while (numFrames) {
972 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
973 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_FLOAT)
974 + (pSrc[3] * MINUS_3_DB_IN_FLOAT);
975 // FL + centerPlusLfeContrib + RL
976 lt = pSrc[0] + centerPlusLfeContrib + pSrc[4];
977 // FR + centerPlusLfeContrib + RR
978 rt = pSrc[1] + centerPlusLfeContrib + pSrc[5];
979 // store in destination
980 pDst[0] = clamp_float(lt / 2.0f); // differs from when accumulate is true above
981 pDst[1] = clamp_float(rt / 2.0f); // differs from when accumulate is true above
982 pSrc += 6;
983 pDst += 2;
984 numFrames--;
985 }
986 }
987}
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800988
989/*----------------------------------------------------------------------------
990 * Downmix_foldFrom7Point1()
991 *----------------------------------------------------------------------------
992 * Purpose:
993 * downmix a 7.1 signal to stereo
994 *
995 * Inputs:
996 * pSrc 7.1 audio samples to downmix
997 * numFrames the number of 7.1 frames to downmix
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700998 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
999 * or overwrite pDst (when false)
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -08001000 *
1001 * Outputs:
1002 * pDst downmixed stereo audio samples
1003 *
1004 *----------------------------------------------------------------------------
1005 */
Ramesh Katurid7d01342016-05-02 15:03:47 +05301006void Downmix_foldFrom7Point1(LVM_FLOAT *pSrc, LVM_FLOAT *pDst, size_t numFrames, bool accumulate) {
1007 LVM_FLOAT lt, rt, centerPlusLfeContrib; // samples in Q19.12 format
1008 // sample at index 0 is FL
1009 // sample at index 1 is FR
1010 // sample at index 2 is FC
1011 // sample at index 3 is LFE
1012 // sample at index 4 is RL
1013 // sample at index 5 is RR
1014 // sample at index 6 is SL
1015 // sample at index 7 is SR
1016 // code is mostly duplicated between the two values of accumulate to avoid repeating the test
1017 // for every sample
1018 if (accumulate) {
1019 while (numFrames) {
1020 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
Andy Hunged1172f2018-12-21 16:20:03 -08001021 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_FLOAT)
1022 + (pSrc[3] * MINUS_3_DB_IN_FLOAT);
Ramesh Katurid7d01342016-05-02 15:03:47 +05301023 // FL + centerPlusLfeContrib + SL + RL
1024 lt = pSrc[0] + centerPlusLfeContrib + pSrc[6] + pSrc[4];
1025 // FR + centerPlusLfeContrib + SR + RR
1026 rt = pSrc[1] + centerPlusLfeContrib + pSrc[7] + pSrc[5];
1027 //accumulate in destination
1028 pDst[0] = clamp_float(pDst[0] + (lt / 2.0f));
1029 pDst[1] = clamp_float(pDst[1] + (rt / 2.0f));
1030 pSrc += 8;
1031 pDst += 2;
1032 numFrames--;
1033 }
1034 } else { // same code as above but without adding and clamping pDst[i] to itself
1035 while (numFrames) {
1036 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
1037 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_FLOAT)
1038 + (pSrc[3] * MINUS_3_DB_IN_FLOAT);
1039 // FL + centerPlusLfeContrib + SL + RL
1040 lt = pSrc[0] + centerPlusLfeContrib + pSrc[6] + pSrc[4];
1041 // FR + centerPlusLfeContrib + SR + RR
1042 rt = pSrc[1] + centerPlusLfeContrib + pSrc[7] + pSrc[5];
1043 // store in destination
1044 pDst[0] = clamp_float(lt / 2.0f); // differs from when accumulate is true above
1045 pDst[1] = clamp_float(rt / 2.0f); // differs from when accumulate is true above
1046 pSrc += 8;
1047 pDst += 2;
1048 numFrames--;
1049 }
1050 }
1051}
Andy Hungca1c4e42021-05-06 22:32:02 -07001052
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -07001053/*----------------------------------------------------------------------------
1054 * Downmix_foldGeneric()
1055 *----------------------------------------------------------------------------
1056 * Purpose:
1057 * downmix to stereo a multichannel signal whose format is:
1058 * - has FL/FR
1059 * - if using AUDIO_CHANNEL_OUT_SIDE*, it contains both left and right
1060 * - if using AUDIO_CHANNEL_OUT_BACK*, it contains both left and right
1061 * - doesn't use any of the AUDIO_CHANNEL_OUT_TOP* channels
1062 * - doesn't use any of the AUDIO_CHANNEL_OUT_FRONT_*_OF_CENTER channels
1063 * Only handles channel masks not enumerated in downmix_input_channel_mask_t
1064 *
1065 * Inputs:
1066 * mask the channel mask of pSrc
1067 * pSrc multichannel audio buffer to downmix
1068 * numFrames the number of multichannel frames to downmix
1069 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
1070 * or overwrite pDst (when false)
1071 *
1072 * Outputs:
1073 * pDst downmixed stereo audio samples
1074 *
1075 * Returns: false if multichannel format is not supported
1076 *
1077 *----------------------------------------------------------------------------
1078 */
Ramesh Katurid7d01342016-05-02 15:03:47 +05301079bool Downmix_foldGeneric(
1080 uint32_t mask, LVM_FLOAT *pSrc, LVM_FLOAT *pDst, size_t numFrames, bool accumulate) {
1081
1082 if (!Downmix_validChannelMask(mask)) {
1083 return false;
1084 }
1085
1086 const bool hasSides = (mask & kSides) != 0;
1087 const bool hasBacks = (mask & kBacks) != 0;
1088
1089 const int numChan = audio_channel_count_from_out_mask(mask);
1090 const bool hasFC = ((mask & AUDIO_CHANNEL_OUT_FRONT_CENTER) == AUDIO_CHANNEL_OUT_FRONT_CENTER);
1091 const bool hasLFE =
1092 ((mask & AUDIO_CHANNEL_OUT_LOW_FREQUENCY) == AUDIO_CHANNEL_OUT_LOW_FREQUENCY);
1093 const bool hasBC = ((mask & AUDIO_CHANNEL_OUT_BACK_CENTER) == AUDIO_CHANNEL_OUT_BACK_CENTER);
1094 // compute at what index each channel is: samples will be in the following order:
1095 // FL FR FC LFE BL BR BC SL SR
1096 // when a channel is not present, its index is set to the same as the index of the preceding
1097 // channel
1098 const int indexFC = hasFC ? 2 : 1; // front center
1099 const int indexLFE = hasLFE ? indexFC + 1 : indexFC; // low frequency
1100 const int indexBL = hasBacks ? indexLFE + 1 : indexLFE; // back left
1101 const int indexBR = hasBacks ? indexBL + 1 : indexBL; // back right
1102 const int indexBC = hasBC ? indexBR + 1 : indexBR; // back center
1103 const int indexSL = hasSides ? indexBC + 1 : indexBC; // side left
1104 const int indexSR = hasSides ? indexSL + 1 : indexSL; // side right
1105
1106 LVM_FLOAT lt, rt, centersLfeContrib;
1107 // code is mostly duplicated between the two values of accumulate to avoid repeating the test
1108 // for every sample
1109 if (accumulate) {
1110 while (numFrames) {
1111 // compute contribution of FC, BC and LFE
1112 centersLfeContrib = 0;
1113 if (hasFC) { centersLfeContrib += pSrc[indexFC]; }
1114 if (hasLFE) { centersLfeContrib += pSrc[indexLFE]; }
1115 if (hasBC) { centersLfeContrib += pSrc[indexBC]; }
1116 centersLfeContrib *= MINUS_3_DB_IN_FLOAT;
1117 // always has FL/FR
1118 lt = pSrc[0];
1119 rt = pSrc[1];
1120 // mix in sides and backs
1121 if (hasSides) {
1122 lt += pSrc[indexSL];
1123 rt += pSrc[indexSR];
1124 }
1125 if (hasBacks) {
1126 lt += pSrc[indexBL];
1127 rt += pSrc[indexBR];
1128 }
1129 lt += centersLfeContrib;
1130 rt += centersLfeContrib;
1131 // accumulate in destination
1132 pDst[0] = clamp_float(pDst[0] + (lt / 2.0f));
1133 pDst[1] = clamp_float(pDst[1] + (rt / 2.0f));
1134 pSrc += numChan;
1135 pDst += 2;
1136 numFrames--;
1137 }
1138 } else {
1139 while (numFrames) {
1140 // compute contribution of FC, BC and LFE
1141 centersLfeContrib = 0;
1142 if (hasFC) { centersLfeContrib += pSrc[indexFC]; }
1143 if (hasLFE) { centersLfeContrib += pSrc[indexLFE]; }
1144 if (hasBC) { centersLfeContrib += pSrc[indexBC]; }
1145 centersLfeContrib *= MINUS_3_DB_IN_FLOAT;
1146 // always has FL/FR
1147 lt = pSrc[0];
1148 rt = pSrc[1];
1149 // mix in sides and backs
1150 if (hasSides) {
1151 lt += pSrc[indexSL];
1152 rt += pSrc[indexSR];
1153 }
1154 if (hasBacks) {
1155 lt += pSrc[indexBL];
1156 rt += pSrc[indexBR];
1157 }
1158 lt += centersLfeContrib;
1159 rt += centersLfeContrib;
1160 // store in destination
1161 pDst[0] = clamp_float(lt / 2.0f); // differs from when accumulate is true above
1162 pDst[1] = clamp_float(rt / 2.0f); // differs from when accumulate is true above
1163 pSrc += numChan;
1164 pDst += 2;
1165 numFrames--;
1166 }
1167 }
1168 return true;
1169}