blob: 22a0b7cbc4839afd28f85704b5f501bb6ba0ac0c [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
Jean-Michel Trividb46b482012-04-23 11:29:26 -070065
Andy Hung47068792021-05-18 16:28:32 -070066// Audio Effect API
67static int32_t DownmixLib_Create(const effect_uuid_t *uuid,
68 int32_t sessionId,
69 int32_t ioId,
70 effect_handle_t *pHandle);
71static int32_t DownmixLib_Release(effect_handle_t handle);
72static int32_t DownmixLib_GetDescriptor(const effect_uuid_t *uuid,
73 effect_descriptor_t *pDescriptor);
74static int32_t Downmix_Process(effect_handle_t self,
75 audio_buffer_t *inBuffer,
76 audio_buffer_t *outBuffer);
77static int32_t Downmix_Command(effect_handle_t self,
78 uint32_t cmdCode,
79 uint32_t cmdSize,
80 void *pCmdData,
81 uint32_t *replySize,
82 void *pReplyData);
83static int32_t Downmix_GetDescriptor(effect_handle_t self,
84 effect_descriptor_t *pDescriptor);
85
86// Internal methods
87static int Downmix_Init(downmix_module_t *pDwmModule);
88static int Downmix_Configure(downmix_module_t *pDwmModule, effect_config_t *pConfig, bool init);
89static int Downmix_Reset(downmix_object_t *pDownmixer, bool init);
90static int Downmix_setParameter(
91 downmix_object_t *pDownmixer, int32_t param, uint32_t size, void *pValue);
92static int Downmix_getParameter(
93 downmix_object_t *pDownmixer, int32_t param, uint32_t *pSize, void *pValue);
94static void Downmix_foldFromQuad(float *pSrc, float *pDst, size_t numFrames, bool accumulate);
95static void Downmix_foldFrom5Point1(float *pSrc, float *pDst, size_t numFrames, bool accumulate);
96static void Downmix_foldFrom7Point1(float *pSrc, float *pDst, size_t numFrames, bool accumulate);
97static bool Downmix_foldGeneric(
98 uint32_t mask, float *pSrc, float *pDst, size_t numFrames, bool accumulate);
99
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800100// effect_handle_t interface implementation for downmix effect
101const struct effect_interface_s gDownmixInterface = {
102 Downmix_Process,
103 Downmix_Command,
104 Downmix_GetDescriptor,
105 NULL /* no process_reverse function, no reference stream needed */
106};
107
Marco Nelissen7f16b192012-10-25 16:05:57 -0700108// This is the only symbol that needs to be exported
109__attribute__ ((visibility ("default")))
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800110audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
synergydevc9d8ea72013-10-19 22:51:33 -0700111 .tag = AUDIO_EFFECT_LIBRARY_TAG,
112 .version = EFFECT_LIBRARY_API_VERSION,
113 .name = "Downmix Library",
114 .implementor = "The Android Open Source Project",
115 .create_effect = DownmixLib_Create,
116 .release_effect = DownmixLib_Release,
117 .get_descriptor = DownmixLib_GetDescriptor,
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800118};
119
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800120// AOSP insert downmix UUID: 93f04452-e4fe-41cc-91f9-e475b6d1d69f
121static const effect_descriptor_t gDownmixDescriptor = {
122 EFFECT_UIID_DOWNMIX__, //type
123 {0x93f04452, 0xe4fe, 0x41cc, 0x91f9, {0xe4, 0x75, 0xb6, 0xd1, 0xd6, 0x9f}}, // uuid
124 EFFECT_CONTROL_API_VERSION,
125 EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST,
126 0, //FIXME what value should be reported? // cpu load
127 0, //FIXME what value should be reported? // memory usage
128 "Multichannel Downmix To Stereo", // human readable effect name
129 "The Android Open Source Project" // human readable effect implementor name
130};
131
132// gDescriptors contains pointers to all defined effect descriptor in this library
133static const effect_descriptor_t * const gDescriptors[] = {
134 &gDownmixDescriptor
135};
136
137// number of effects in this library
138const int kNbEffects = sizeof(gDescriptors) / sizeof(const effect_descriptor_t *);
Andy Hungca1c4e42021-05-06 22:32:02 -0700139
Ramesh Katurid7d01342016-05-02 15:03:47 +0530140static LVM_FLOAT clamp_float(LVM_FLOAT a) {
141 if (a > 1.0f) {
142 return 1.0f;
143 }
144 else if (a < -1.0f) {
145 return -1.0f;
146 }
147 else {
148 return a;
149 }
150}
Andy Hungca1c4e42021-05-06 22:32:02 -0700151
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800152/*----------------------------------------------------------------------------
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700153 * Test code
154 *--------------------------------------------------------------------------*/
155#ifdef DOWNMIX_TEST_CHANNEL_INDEX
156// strictly for testing, logs the indices of the channels for a given mask,
157// uses the same code as Downmix_foldGeneric()
158void Downmix_testIndexComputation(uint32_t mask) {
Andy Hung47068792021-05-18 16:28:32 -0700159 ALOGI("Testing index computation for %#x:", mask);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700160 // check against unsupported channels
161 if (mask & kUnsupported) {
162 ALOGE("Unsupported channels (top or front left/right of center)");
163 return;
164 }
165 // verify has FL/FR
166 if ((mask & AUDIO_CHANNEL_OUT_STEREO) != AUDIO_CHANNEL_OUT_STEREO) {
167 ALOGE("Front channels must be present");
168 return;
169 }
170 // verify uses SIDE as a pair (ok if not using SIDE at all)
171 bool hasSides = false;
172 if ((mask & kSides) != 0) {
173 if ((mask & kSides) != kSides) {
174 ALOGE("Side channels must be used as a pair");
175 return;
176 }
177 hasSides = true;
178 }
179 // verify uses BACK as a pair (ok if not using BACK at all)
180 bool hasBacks = false;
181 if ((mask & kBacks) != 0) {
182 if ((mask & kBacks) != kBacks) {
183 ALOGE("Back channels must be used as a pair");
184 return;
185 }
186 hasBacks = true;
187 }
188
Andy Hunge5412692014-05-16 11:25:07 -0700189 const int numChan = audio_channel_count_from_out_mask(mask);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700190 const bool hasFC = ((mask & AUDIO_CHANNEL_OUT_FRONT_CENTER) == AUDIO_CHANNEL_OUT_FRONT_CENTER);
191 const bool hasLFE =
192 ((mask & AUDIO_CHANNEL_OUT_LOW_FREQUENCY) == AUDIO_CHANNEL_OUT_LOW_FREQUENCY);
193 const bool hasBC = ((mask & AUDIO_CHANNEL_OUT_BACK_CENTER) == AUDIO_CHANNEL_OUT_BACK_CENTER);
194 // compute at what index each channel is: samples will be in the following order:
195 // FL FR FC LFE BL BR BC SL SR
196 // when a channel is not present, its index is set to the same as the index of the preceding
197 // channel
198 const int indexFC = hasFC ? 2 : 1; // front center
199 const int indexLFE = hasLFE ? indexFC + 1 : indexFC; // low frequency
200 const int indexBL = hasBacks ? indexLFE + 1 : indexLFE; // back left
201 const int indexBR = hasBacks ? indexBL + 1 : indexBL; // back right
202 const int indexBC = hasBC ? indexBR + 1 : indexBR; // back center
203 const int indexSL = hasSides ? indexBC + 1 : indexBC; // side left
204 const int indexSR = hasSides ? indexSL + 1 : indexSL; // side right
205
206 ALOGI(" FL FR FC LFE BL BR BC SL SR");
207 ALOGI(" %d %d %d %d %d %d %d %d %d",
208 0, 1, indexFC, indexLFE, indexBL, indexBR, indexBC, indexSL, indexSR);
209}
210#endif
211
Haynes Mathew Georgeee507ef2015-07-15 11:06:05 -0700212static bool Downmix_validChannelMask(uint32_t mask)
213{
214 if (!mask) {
215 return false;
216 }
217 // check against unsupported channels
218 if (mask & kUnsupported) {
219 ALOGE("Unsupported channels (top or front left/right of center)");
220 return false;
221 }
222 // verify has FL/FR
223 if ((mask & AUDIO_CHANNEL_OUT_STEREO) != AUDIO_CHANNEL_OUT_STEREO) {
224 ALOGE("Front channels must be present");
225 return false;
226 }
227 // verify uses SIDE as a pair (ok if not using SIDE at all)
228 if ((mask & kSides) != 0) {
229 if ((mask & kSides) != kSides) {
230 ALOGE("Side channels must be used as a pair");
231 return false;
232 }
233 }
234 // verify uses BACK as a pair (ok if not using BACK at all)
235 if ((mask & kBacks) != 0) {
236 if ((mask & kBacks) != kBacks) {
237 ALOGE("Back channels must be used as a pair");
238 return false;
239 }
240 }
241 return true;
242}
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700243
244/*----------------------------------------------------------------------------
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800245 * Effect API implementation
246 *--------------------------------------------------------------------------*/
247
248/*--- Effect Library Interface Implementation ---*/
249
Andy Hung47068792021-05-18 16:28:32 -0700250static int32_t DownmixLib_Create(const effect_uuid_t *uuid,
Andy Hung679c4712021-05-19 14:14:54 -0700251 int32_t /* sessionId */,
252 int32_t /* ioId */,
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800253 effect_handle_t *pHandle) {
254 int ret;
255 int i;
256 downmix_module_t *module;
257 const effect_descriptor_t *desc;
258
259 ALOGV("DownmixLib_Create()");
260
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700261#ifdef DOWNMIX_TEST_CHANNEL_INDEX
262 // should work (won't log an error)
263 ALOGI("DOWNMIX_TEST_CHANNEL_INDEX: should work:");
264 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT |
265 AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_BACK_CENTER);
Andy Hung862b1ab2021-05-19 11:37:08 -0700266 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_QUAD_SIDE | AUDIO_CHANNEL_OUT_QUAD_BACK);
267 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_5POINT1_SIDE | AUDIO_CHANNEL_OUT_BACK_CENTER);
268 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_5POINT1_BACK | AUDIO_CHANNEL_OUT_BACK_CENTER);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700269 // shouldn't work (will log an error, won't display channel indices)
270 ALOGI("DOWNMIX_TEST_CHANNEL_INDEX: should NOT work:");
271 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT |
272 AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_BACK_LEFT);
273 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT |
274 AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_SIDE_LEFT);
275 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT |
276 AUDIO_CHANNEL_OUT_BACK_LEFT | AUDIO_CHANNEL_OUT_BACK_RIGHT);
277 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT |
278 AUDIO_CHANNEL_OUT_SIDE_LEFT | AUDIO_CHANNEL_OUT_SIDE_RIGHT);
279#endif
280
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800281 if (pHandle == NULL || uuid == NULL) {
282 return -EINVAL;
283 }
284
285 for (i = 0 ; i < kNbEffects ; i++) {
286 desc = gDescriptors[i];
287 if (memcmp(uuid, &desc->uuid, sizeof(effect_uuid_t)) == 0) {
288 break;
289 }
290 }
291
292 if (i == kNbEffects) {
293 return -ENOENT;
294 }
295
Andy Hung19748032021-05-19 17:34:16 -0700296 module = new downmix_module_t{};
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800297
298 module->itfe = &gDownmixInterface;
299
300 module->context.state = DOWNMIX_STATE_UNINITIALIZED;
301
302 ret = Downmix_Init(module);
303 if (ret < 0) {
304 ALOGW("DownmixLib_Create() init failed");
305 free(module);
306 return ret;
307 }
308
309 *pHandle = (effect_handle_t) module;
310
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700311 ALOGV("DownmixLib_Create() %p , size %zu", module, sizeof(downmix_module_t));
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800312
313 return 0;
314}
315
Andy Hung47068792021-05-18 16:28:32 -0700316static int32_t DownmixLib_Release(effect_handle_t handle) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800317 downmix_module_t *pDwmModule = (downmix_module_t *)handle;
318
319 ALOGV("DownmixLib_Release() %p", handle);
320 if (handle == NULL) {
321 return -EINVAL;
322 }
323
324 pDwmModule->context.state = DOWNMIX_STATE_UNINITIALIZED;
325
Andy Hung19748032021-05-19 17:34:16 -0700326 delete pDwmModule;
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800327 return 0;
328}
329
Andy Hung47068792021-05-18 16:28:32 -0700330static int32_t DownmixLib_GetDescriptor(
331 const effect_uuid_t *uuid, effect_descriptor_t *pDescriptor) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800332 ALOGV("DownmixLib_GetDescriptor()");
333 int i;
334
335 if (pDescriptor == NULL || uuid == NULL){
336 ALOGE("DownmixLib_Create() called with NULL pointer");
337 return -EINVAL;
338 }
339 ALOGV("DownmixLib_GetDescriptor() nb effects=%d", kNbEffects);
340 for (i = 0; i < kNbEffects; i++) {
341 ALOGV("DownmixLib_GetDescriptor() i=%d", i);
342 if (memcmp(uuid, &gDescriptors[i]->uuid, sizeof(effect_uuid_t)) == 0) {
343 memcpy(pDescriptor, gDescriptors[i], sizeof(effect_descriptor_t));
Andy Hung47068792021-05-18 16:28:32 -0700344 ALOGV("EffectGetDescriptor - UUID matched downmix type %d, UUID = %#x",
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800345 i, gDescriptors[i]->uuid.timeLow);
346 return 0;
347 }
348 }
349
350 return -EINVAL;
351}
352
Ramesh Katurid7d01342016-05-02 15:03:47 +0530353/*--- Effect Control Interface Implementation ---*/
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800354
Andy Hung47068792021-05-18 16:28:32 -0700355static int32_t Downmix_Process(effect_handle_t self,
Ramesh Katurid7d01342016-05-02 15:03:47 +0530356 audio_buffer_t *inBuffer, audio_buffer_t *outBuffer) {
357
358 downmix_object_t *pDownmixer;
359 LVM_FLOAT *pSrc, *pDst;
360 downmix_module_t *pDwmModule = (downmix_module_t *)self;
361
362 if (pDwmModule == NULL) {
363 return -EINVAL;
364 }
365
366 if (inBuffer == NULL || inBuffer->raw == NULL ||
367 outBuffer == NULL || outBuffer->raw == NULL ||
368 inBuffer->frameCount != outBuffer->frameCount) {
369 return -EINVAL;
370 }
371
372 pDownmixer = (downmix_object_t*) &pDwmModule->context;
373
374 if (pDownmixer->state == DOWNMIX_STATE_UNINITIALIZED) {
375 ALOGE("Downmix_Process error: trying to use an uninitialized downmixer");
376 return -EINVAL;
377 } else if (pDownmixer->state == DOWNMIX_STATE_INITIALIZED) {
378 ALOGE("Downmix_Process error: trying to use a non-configured downmixer");
379 return -ENODATA;
380 }
381
382 pSrc = (LVM_FLOAT *) inBuffer->s16;
383 pDst = (LVM_FLOAT *) outBuffer->s16;
384 size_t numFrames = outBuffer->frameCount;
385
386 const bool accumulate =
387 (pDwmModule->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE);
388 const uint32_t downmixInputChannelMask = pDwmModule->config.inputCfg.channels;
389
390 switch(pDownmixer->type) {
391
392 case DOWNMIX_TYPE_STRIP:
393 if (accumulate) {
394 while (numFrames) {
395 pDst[0] = clamp_float(pDst[0] + pSrc[0]);
396 pDst[1] = clamp_float(pDst[1] + pSrc[1]);
397 pSrc += pDownmixer->input_channel_count;
398 pDst += 2;
399 numFrames--;
400 }
401 } else {
402 while (numFrames) {
403 pDst[0] = pSrc[0];
404 pDst[1] = pSrc[1];
405 pSrc += pDownmixer->input_channel_count;
406 pDst += 2;
407 numFrames--;
408 }
409 }
410 break;
411
412 case DOWNMIX_TYPE_FOLD:
413#ifdef DOWNMIX_ALWAYS_USE_GENERIC_DOWNMIXER
414 // bypass the optimized downmix routines for the common formats
415 if (!Downmix_foldGeneric(
416 downmixInputChannelMask, pSrc, pDst, numFrames, accumulate)) {
Andy Hung47068792021-05-18 16:28:32 -0700417 ALOGE("Multichannel configuration %#x is not supported",
Ramesh Katurid7d01342016-05-02 15:03:47 +0530418 downmixInputChannelMask);
419 return -EINVAL;
420 }
421 break;
422#endif
423 // optimize for the common formats
Andy Hung862b1ab2021-05-19 11:37:08 -0700424 switch (downmixInputChannelMask) {
425 case AUDIO_CHANNEL_OUT_QUAD_BACK:
426 case AUDIO_CHANNEL_OUT_QUAD_SIDE:
Ramesh Katurid7d01342016-05-02 15:03:47 +0530427 Downmix_foldFromQuad(pSrc, pDst, numFrames, accumulate);
428 break;
Andy Hung862b1ab2021-05-19 11:37:08 -0700429 case AUDIO_CHANNEL_OUT_5POINT1_BACK:
430 case AUDIO_CHANNEL_OUT_5POINT1_SIDE:
Ramesh Katurid7d01342016-05-02 15:03:47 +0530431 Downmix_foldFrom5Point1(pSrc, pDst, numFrames, accumulate);
432 break;
Andy Hung862b1ab2021-05-19 11:37:08 -0700433 case AUDIO_CHANNEL_OUT_7POINT1:
Ramesh Katurid7d01342016-05-02 15:03:47 +0530434 Downmix_foldFrom7Point1(pSrc, pDst, numFrames, accumulate);
435 break;
436 default:
437 if (!Downmix_foldGeneric(
438 downmixInputChannelMask, pSrc, pDst, numFrames, accumulate)) {
Andy Hung47068792021-05-18 16:28:32 -0700439 ALOGE("Multichannel configuration %#x is not supported",
Ramesh Katurid7d01342016-05-02 15:03:47 +0530440 downmixInputChannelMask);
441 return -EINVAL;
442 }
443 break;
444 }
445 break;
446
447 default:
448 return -EINVAL;
449 }
450
451 return 0;
452}
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800453
Andy Hung47068792021-05-18 16:28:32 -0700454static int32_t Downmix_Command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800455 void *pCmdData, uint32_t *replySize, void *pReplyData) {
456
457 downmix_module_t *pDwmModule = (downmix_module_t *) self;
458 downmix_object_t *pDownmixer;
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800459
460 if (pDwmModule == NULL || pDwmModule->context.state == DOWNMIX_STATE_UNINITIALIZED) {
461 return -EINVAL;
462 }
463
464 pDownmixer = (downmix_object_t*) &pDwmModule->context;
465
Andy Hung47068792021-05-18 16:28:32 -0700466 ALOGV("Downmix_Command command %u cmdSize %u", cmdCode, cmdSize);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800467
468 switch (cmdCode) {
469 case EFFECT_CMD_INIT:
Eric Laurent0f714a42015-06-19 15:33:57 -0700470 if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800471 return -EINVAL;
472 }
473 *(int *) pReplyData = Downmix_Init(pDwmModule);
474 break;
475
476 case EFFECT_CMD_SET_CONFIG:
477 if (pCmdData == NULL || cmdSize != sizeof(effect_config_t)
Eric Laurent0f714a42015-06-19 15:33:57 -0700478 || pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800479 return -EINVAL;
480 }
481 *(int *) pReplyData = Downmix_Configure(pDwmModule,
482 (effect_config_t *)pCmdData, false);
483 break;
484
485 case EFFECT_CMD_RESET:
486 Downmix_Reset(pDownmixer, false);
487 break;
488
Andy Hung19748032021-05-19 17:34:16 -0700489 case EFFECT_CMD_GET_PARAM: {
Andy Hung47068792021-05-18 16:28:32 -0700490 ALOGV("Downmix_Command EFFECT_CMD_GET_PARAM pCmdData %p, *replySize %u, pReplyData: %p",
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800491 pCmdData, *replySize, pReplyData);
492 if (pCmdData == NULL || cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t)) ||
Eric Laurent0f714a42015-06-19 15:33:57 -0700493 pReplyData == NULL || replySize == NULL ||
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800494 *replySize < (int) sizeof(effect_param_t) + 2 * sizeof(int32_t)) {
495 return -EINVAL;
496 }
497 effect_param_t *rep = (effect_param_t *) pReplyData;
498 memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + sizeof(int32_t));
Andy Hung47068792021-05-18 16:28:32 -0700499 ALOGV("Downmix_Command EFFECT_CMD_GET_PARAM param %d, replySize %u",
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800500 *(int32_t *)rep->data, rep->vsize);
501 rep->status = Downmix_getParameter(pDownmixer, *(int32_t *)rep->data, &rep->vsize,
502 rep->data + sizeof(int32_t));
503 *replySize = sizeof(effect_param_t) + sizeof(int32_t) + rep->vsize;
504 break;
Andy Hung19748032021-05-19 17:34:16 -0700505 }
506 case EFFECT_CMD_SET_PARAM: {
Andy Hung47068792021-05-18 16:28:32 -0700507 ALOGV("Downmix_Command EFFECT_CMD_SET_PARAM cmdSize %d pCmdData %p, *replySize %u"
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700508 ", pReplyData %p", cmdSize, pCmdData, *replySize, pReplyData);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800509 if (pCmdData == NULL || (cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t)))
Eric Laurent0f714a42015-06-19 15:33:57 -0700510 || pReplyData == NULL || replySize == NULL || *replySize != (int)sizeof(int32_t)) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800511 return -EINVAL;
512 }
513 effect_param_t *cmd = (effect_param_t *) pCmdData;
Mikhail Naganov804632a2017-07-24 17:25:47 -0700514 if (cmd->psize != sizeof(int32_t)) {
515 android_errorWriteLog(0x534e4554, "63662938");
516 return -EINVAL;
517 }
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800518 *(int *)pReplyData = Downmix_setParameter(pDownmixer, *(int32_t *)cmd->data,
519 cmd->vsize, cmd->data + sizeof(int32_t));
520 break;
Andy Hung19748032021-05-19 17:34:16 -0700521 }
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800522
523 case EFFECT_CMD_SET_PARAM_DEFERRED:
524 //FIXME implement
525 ALOGW("Downmix_Command command EFFECT_CMD_SET_PARAM_DEFERRED not supported, FIXME");
526 break;
527
528 case EFFECT_CMD_SET_PARAM_COMMIT:
529 //FIXME implement
530 ALOGW("Downmix_Command command EFFECT_CMD_SET_PARAM_COMMIT not supported, FIXME");
531 break;
532
533 case EFFECT_CMD_ENABLE:
Eric Laurent0f714a42015-06-19 15:33:57 -0700534 if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800535 return -EINVAL;
536 }
537 if (pDownmixer->state != DOWNMIX_STATE_INITIALIZED) {
538 return -ENOSYS;
539 }
540 pDownmixer->state = DOWNMIX_STATE_ACTIVE;
541 ALOGV("EFFECT_CMD_ENABLE() OK");
542 *(int *)pReplyData = 0;
543 break;
544
545 case EFFECT_CMD_DISABLE:
Eric Laurent0f714a42015-06-19 15:33:57 -0700546 if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800547 return -EINVAL;
548 }
549 if (pDownmixer->state != DOWNMIX_STATE_ACTIVE) {
550 return -ENOSYS;
551 }
552 pDownmixer->state = DOWNMIX_STATE_INITIALIZED;
553 ALOGV("EFFECT_CMD_DISABLE() OK");
554 *(int *)pReplyData = 0;
555 break;
556
557 case EFFECT_CMD_SET_DEVICE:
558 if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) {
559 return -EINVAL;
560 }
561 // FIXME change type if playing on headset vs speaker
Andy Hung47068792021-05-18 16:28:32 -0700562 ALOGV("Downmix_Command EFFECT_CMD_SET_DEVICE: %#x", *(uint32_t *)pCmdData);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800563 break;
564
565 case EFFECT_CMD_SET_VOLUME: {
566 // audio output is always stereo => 2 channel volumes
567 if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t) * 2) {
568 return -EINVAL;
569 }
570 // FIXME change volume
571 ALOGW("Downmix_Command command EFFECT_CMD_SET_VOLUME not supported, FIXME");
572 float left = (float)(*(uint32_t *)pCmdData) / (1 << 24);
573 float right = (float)(*((uint32_t *)pCmdData + 1)) / (1 << 24);
574 ALOGV("Downmix_Command EFFECT_CMD_SET_VOLUME: left %f, right %f ", left, right);
575 break;
576 }
577
578 case EFFECT_CMD_SET_AUDIO_MODE:
579 if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) {
580 return -EINVAL;
581 }
Andy Hung47068792021-05-18 16:28:32 -0700582 ALOGV("Downmix_Command EFFECT_CMD_SET_AUDIO_MODE: %u", *(uint32_t *)pCmdData);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800583 break;
584
585 case EFFECT_CMD_SET_CONFIG_REVERSE:
586 case EFFECT_CMD_SET_INPUT_DEVICE:
587 // these commands are ignored by a downmix effect
588 break;
589
590 default:
Andy Hung47068792021-05-18 16:28:32 -0700591 ALOGW("Downmix_Command invalid command %u", cmdCode);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800592 return -EINVAL;
593 }
594
595 return 0;
596}
597
Andy Hung47068792021-05-18 16:28:32 -0700598static int32_t Downmix_GetDescriptor(effect_handle_t self, effect_descriptor_t *pDescriptor)
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800599{
600 downmix_module_t *pDwnmxModule = (downmix_module_t *) self;
601
602 if (pDwnmxModule == NULL ||
603 pDwnmxModule->context.state == DOWNMIX_STATE_UNINITIALIZED) {
604 return -EINVAL;
605 }
606
607 memcpy(pDescriptor, &gDownmixDescriptor, sizeof(effect_descriptor_t));
608
609 return 0;
610}
611
612
613/*----------------------------------------------------------------------------
614 * Downmix internal functions
615 *--------------------------------------------------------------------------*/
616
617/*----------------------------------------------------------------------------
618 * Downmix_Init()
619 *----------------------------------------------------------------------------
620 * Purpose:
621 * Initialize downmix context and apply default parameters
622 *
623 * Inputs:
624 * pDwmModule pointer to downmix effect module
625 *
626 * Outputs:
627 *
628 * Returns:
629 * 0 indicates success
630 *
631 * Side Effects:
632 * updates:
633 * pDwmModule->context.type
634 * pDwmModule->context.apply_volume_correction
635 * pDwmModule->config.inputCfg
636 * pDwmModule->config.outputCfg
637 * pDwmModule->config.inputCfg.samplingRate
638 * pDwmModule->config.outputCfg.samplingRate
639 * pDwmModule->context.state
640 * doesn't set:
641 * pDwmModule->itfe
642 *
643 *----------------------------------------------------------------------------
644 */
645
Andy Hung47068792021-05-18 16:28:32 -0700646static int Downmix_Init(downmix_module_t *pDwmModule) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800647
648 ALOGV("Downmix_Init module %p", pDwmModule);
649 int ret = 0;
650
651 memset(&pDwmModule->context, 0, sizeof(downmix_object_t));
652
653 pDwmModule->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
Andy Hung47068792021-05-18 16:28:32 -0700654 pDwmModule->config.inputCfg.format = AUDIO_FORMAT_PCM_FLOAT;
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800655 pDwmModule->config.inputCfg.channels = AUDIO_CHANNEL_OUT_7POINT1;
656 pDwmModule->config.inputCfg.bufferProvider.getBuffer = NULL;
657 pDwmModule->config.inputCfg.bufferProvider.releaseBuffer = NULL;
658 pDwmModule->config.inputCfg.bufferProvider.cookie = NULL;
659 pDwmModule->config.inputCfg.mask = EFFECT_CONFIG_ALL;
660
661 pDwmModule->config.inputCfg.samplingRate = 44100;
662 pDwmModule->config.outputCfg.samplingRate = pDwmModule->config.inputCfg.samplingRate;
663
664 // set a default value for the access mode, but should be overwritten by caller
665 pDwmModule->config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
Andy Hung47068792021-05-18 16:28:32 -0700666 pDwmModule->config.outputCfg.format = AUDIO_FORMAT_PCM_FLOAT;
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800667 pDwmModule->config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
668 pDwmModule->config.outputCfg.bufferProvider.getBuffer = NULL;
669 pDwmModule->config.outputCfg.bufferProvider.releaseBuffer = NULL;
670 pDwmModule->config.outputCfg.bufferProvider.cookie = NULL;
671 pDwmModule->config.outputCfg.mask = EFFECT_CONFIG_ALL;
672
673 ret = Downmix_Configure(pDwmModule, &pDwmModule->config, true);
674 if (ret != 0) {
675 ALOGV("Downmix_Init error %d on module %p", ret, pDwmModule);
676 } else {
677 pDwmModule->context.state = DOWNMIX_STATE_INITIALIZED;
678 }
679
680 return ret;
681}
682
683
684/*----------------------------------------------------------------------------
685 * Downmix_Configure()
686 *----------------------------------------------------------------------------
687 * Purpose:
688 * Set input and output audio configuration.
689 *
690 * Inputs:
691 * pDwmModule pointer to downmix effect module
692 * pConfig pointer to effect_config_t structure containing input
693 * and output audio parameters configuration
694 * init true if called from init function
695 *
696 * Outputs:
697 *
698 * Returns:
699 * 0 indicates success
700 *
701 * Side Effects:
702 *
703 *----------------------------------------------------------------------------
704 */
705
Andy Hung47068792021-05-18 16:28:32 -0700706static int Downmix_Configure(downmix_module_t *pDwmModule, effect_config_t *pConfig, bool init) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800707
708 downmix_object_t *pDownmixer = &pDwmModule->context;
709
710 // Check configuration compatibility with build options, and effect capabilities
711 if (pConfig->inputCfg.samplingRate != pConfig->outputCfg.samplingRate
Andy Hung47068792021-05-18 16:28:32 -0700712 || pConfig->outputCfg.channels != AUDIO_CHANNEL_OUT_STEREO
713 || pConfig->inputCfg.format != AUDIO_FORMAT_PCM_FLOAT
714 || pConfig->outputCfg.format != AUDIO_FORMAT_PCM_FLOAT) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800715 ALOGE("Downmix_Configure error: invalid config");
716 return -EINVAL;
717 }
718
Chih-Wei Huanga93c8c92013-01-10 20:52:57 +0800719 if (&pDwmModule->config != pConfig) {
720 memcpy(&pDwmModule->config, pConfig, sizeof(effect_config_t));
721 }
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800722
723 if (init) {
724 pDownmixer->type = DOWNMIX_TYPE_FOLD;
725 pDownmixer->apply_volume_correction = false;
726 pDownmixer->input_channel_count = 8; // matches default input of AUDIO_CHANNEL_OUT_7POINT1
727 } else {
Haynes Mathew Georgeee507ef2015-07-15 11:06:05 -0700728 // when configuring the effect, do not allow a blank or unsupported channel mask
729 if (!Downmix_validChannelMask(pConfig->inputCfg.channels)) {
730 ALOGE("Downmix_Configure error: input channel mask(0x%x) not supported",
731 pConfig->inputCfg.channels);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800732 return -EINVAL;
733 }
Andy Hunge5412692014-05-16 11:25:07 -0700734 pDownmixer->input_channel_count =
735 audio_channel_count_from_out_mask(pConfig->inputCfg.channels);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800736 }
737
738 Downmix_Reset(pDownmixer, init);
739
740 return 0;
741}
742
743
744/*----------------------------------------------------------------------------
745 * Downmix_Reset()
746 *----------------------------------------------------------------------------
747 * Purpose:
748 * Reset internal states.
749 *
750 * Inputs:
751 * pDownmixer pointer to downmix context
752 * init true if called from init function
753 *
754 * Outputs:
755*
756 * Returns:
757 * 0 indicates success
758 *
759 * Side Effects:
760 *
761 *----------------------------------------------------------------------------
762 */
763
Andy Hung679c4712021-05-19 14:14:54 -0700764static int Downmix_Reset(downmix_object_t* /* pDownmixer */, bool /* init */) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800765 // nothing to do here
766 return 0;
767}
768
769
770/*----------------------------------------------------------------------------
771 * Downmix_setParameter()
772 *----------------------------------------------------------------------------
773 * Purpose:
774 * Set a Downmix parameter
775 *
776 * Inputs:
777 * pDownmixer handle to instance data
778 * param parameter
779 * pValue pointer to parameter value
780 * size value size
781 *
782 * Outputs:
783 *
784 * Returns:
785 * 0 indicates success
786 *
787 * Side Effects:
788 *
789 *----------------------------------------------------------------------------
790 */
Andy Hung47068792021-05-18 16:28:32 -0700791static int Downmix_setParameter(
792 downmix_object_t *pDownmixer, int32_t param, uint32_t size, void *pValue) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800793
794 int16_t value16;
Andy Hung47068792021-05-18 16:28:32 -0700795 ALOGV("Downmix_setParameter, context %p, param %d, value16 %d, value32 %d",
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800796 pDownmixer, param, *(int16_t *)pValue, *(int32_t *)pValue);
797
798 switch (param) {
799
800 case DOWNMIX_PARAM_TYPE:
801 if (size != sizeof(downmix_type_t)) {
Andy Hung47068792021-05-18 16:28:32 -0700802 ALOGE("Downmix_setParameter(DOWNMIX_PARAM_TYPE) invalid size %u, should be %zu",
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800803 size, sizeof(downmix_type_t));
804 return -EINVAL;
805 }
806 value16 = *(int16_t *)pValue;
Andy Hung47068792021-05-18 16:28:32 -0700807 ALOGV("set DOWNMIX_PARAM_TYPE, type %d", value16);
Jean-Michel Trivi7d5b2622012-04-04 18:54:36 -0700808 if (!((value16 > DOWNMIX_TYPE_INVALID) && (value16 <= DOWNMIX_TYPE_LAST))) {
Andy Hung47068792021-05-18 16:28:32 -0700809 ALOGE("Downmix_setParameter invalid DOWNMIX_PARAM_TYPE value %d", value16);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800810 return -EINVAL;
811 } else {
812 pDownmixer->type = (downmix_type_t) value16;
813 break;
814
815 default:
Andy Hung47068792021-05-18 16:28:32 -0700816 ALOGE("Downmix_setParameter unknown parameter %d", param);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800817 return -EINVAL;
818 }
819}
820
821 return 0;
822} /* end Downmix_setParameter */
823
824
825/*----------------------------------------------------------------------------
826 * Downmix_getParameter()
827 *----------------------------------------------------------------------------
828 * Purpose:
829 * Get a Downmix parameter
830 *
831 * Inputs:
832 * pDownmixer handle to instance data
833 * param parameter
834 * pValue pointer to variable to hold retrieved value
835 * pSize pointer to value size: maximum size as input
836 *
837 * Outputs:
838 * *pValue updated with parameter value
839 * *pSize updated with actual value size
840 *
841 * Returns:
842 * 0 indicates success
843 *
844 * Side Effects:
845 *
846 *----------------------------------------------------------------------------
847 */
Andy Hung47068792021-05-18 16:28:32 -0700848static int Downmix_getParameter(
849 downmix_object_t *pDownmixer, int32_t param, uint32_t *pSize, void *pValue) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800850 int16_t *pValue16;
851
852 switch (param) {
853
854 case DOWNMIX_PARAM_TYPE:
855 if (*pSize < sizeof(int16_t)) {
Andy Hung47068792021-05-18 16:28:32 -0700856 ALOGE("Downmix_getParameter invalid parameter size %u for DOWNMIX_PARAM_TYPE", *pSize);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800857 return -EINVAL;
858 }
859 pValue16 = (int16_t *)pValue;
860 *pValue16 = (int16_t) pDownmixer->type;
861 *pSize = sizeof(int16_t);
Andy Hung47068792021-05-18 16:28:32 -0700862 ALOGV("Downmix_getParameter DOWNMIX_PARAM_TYPE is %d", *pValue16);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800863 break;
864
865 default:
Andy Hung47068792021-05-18 16:28:32 -0700866 ALOGE("Downmix_getParameter unknown parameter %d", param);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800867 return -EINVAL;
868 }
869
870 return 0;
871} /* end Downmix_getParameter */
872
873
874/*----------------------------------------------------------------------------
875 * Downmix_foldFromQuad()
876 *----------------------------------------------------------------------------
877 * Purpose:
878 * downmix a quad signal to stereo
879 *
880 * Inputs:
881 * pSrc quad audio samples to downmix
882 * numFrames the number of quad frames to downmix
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700883 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
884 * or overwrite pDst (when false)
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800885 *
886 * Outputs:
887 * pDst downmixed stereo audio samples
888 *
889 *----------------------------------------------------------------------------
890 */
Ramesh Katurid7d01342016-05-02 15:03:47 +0530891void Downmix_foldFromQuad(LVM_FLOAT *pSrc, LVM_FLOAT *pDst, size_t numFrames, bool accumulate) {
892 // sample at index 0 is FL
893 // sample at index 1 is FR
894 // sample at index 2 is RL
895 // sample at index 3 is RR
896 if (accumulate) {
897 while (numFrames) {
898 // FL + RL
899 pDst[0] = clamp_float(pDst[0] + ((pSrc[0] + pSrc[2]) / 2.0f));
900 // FR + RR
901 pDst[1] = clamp_float(pDst[1] + ((pSrc[1] + pSrc[3]) / 2.0f));
902 pSrc += 4;
903 pDst += 2;
904 numFrames--;
905 }
906 } else { // same code as above but without adding and clamping pDst[i] to itself
907 while (numFrames) {
908 // FL + RL
909 pDst[0] = clamp_float((pSrc[0] + pSrc[2]) / 2.0f);
910 // FR + RR
911 pDst[1] = clamp_float((pSrc[1] + pSrc[3]) / 2.0f);
912 pSrc += 4;
913 pDst += 2;
914 numFrames--;
915 }
916 }
917}
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800918
919/*----------------------------------------------------------------------------
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800920 * Downmix_foldFrom5Point1()
921 *----------------------------------------------------------------------------
922 * Purpose:
923 * downmix a 5.1 signal to stereo
924 *
925 * Inputs:
926 * pSrc 5.1 audio samples to downmix
927 * numFrames the number of 5.1 frames to downmix
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700928 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
929 * or overwrite pDst (when false)
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800930 *
931 * Outputs:
932 * pDst downmixed stereo audio samples
933 *
934 *----------------------------------------------------------------------------
935 */
Ramesh Katurid7d01342016-05-02 15:03:47 +0530936void Downmix_foldFrom5Point1(LVM_FLOAT *pSrc, LVM_FLOAT *pDst, size_t numFrames, bool accumulate) {
937 LVM_FLOAT lt, rt, centerPlusLfeContrib; // samples in Q19.12 format
938 // sample at index 0 is FL
939 // sample at index 1 is FR
940 // sample at index 2 is FC
941 // sample at index 3 is LFE
942 // sample at index 4 is RL
943 // sample at index 5 is RR
944 // code is mostly duplicated between the two values of accumulate to avoid repeating the test
945 // for every sample
946 if (accumulate) {
947 while (numFrames) {
948 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
949 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_FLOAT)
950 + (pSrc[3] * MINUS_3_DB_IN_FLOAT);
951 // FL + centerPlusLfeContrib + RL
952 lt = pSrc[0] + centerPlusLfeContrib + pSrc[4];
953 // FR + centerPlusLfeContrib + RR
954 rt = pSrc[1] + centerPlusLfeContrib + pSrc[5];
955 // accumulate in destination
956 pDst[0] = clamp_float(pDst[0] + (lt / 2.0f));
957 pDst[1] = clamp_float(pDst[1] + (rt / 2.0f));
958 pSrc += 6;
959 pDst += 2;
960 numFrames--;
961 }
962 } else { // same code as above but without adding and clamping pDst[i] to itself
963 while (numFrames) {
964 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
965 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_FLOAT)
966 + (pSrc[3] * MINUS_3_DB_IN_FLOAT);
967 // FL + centerPlusLfeContrib + RL
968 lt = pSrc[0] + centerPlusLfeContrib + pSrc[4];
969 // FR + centerPlusLfeContrib + RR
970 rt = pSrc[1] + centerPlusLfeContrib + pSrc[5];
971 // store in destination
972 pDst[0] = clamp_float(lt / 2.0f); // differs from when accumulate is true above
973 pDst[1] = clamp_float(rt / 2.0f); // differs from when accumulate is true above
974 pSrc += 6;
975 pDst += 2;
976 numFrames--;
977 }
978 }
979}
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800980
981/*----------------------------------------------------------------------------
982 * Downmix_foldFrom7Point1()
983 *----------------------------------------------------------------------------
984 * Purpose:
985 * downmix a 7.1 signal to stereo
986 *
987 * Inputs:
988 * pSrc 7.1 audio samples to downmix
989 * numFrames the number of 7.1 frames to downmix
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700990 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
991 * or overwrite pDst (when false)
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800992 *
993 * Outputs:
994 * pDst downmixed stereo audio samples
995 *
996 *----------------------------------------------------------------------------
997 */
Ramesh Katurid7d01342016-05-02 15:03:47 +0530998void Downmix_foldFrom7Point1(LVM_FLOAT *pSrc, LVM_FLOAT *pDst, size_t numFrames, bool accumulate) {
999 LVM_FLOAT lt, rt, centerPlusLfeContrib; // samples in Q19.12 format
1000 // sample at index 0 is FL
1001 // sample at index 1 is FR
1002 // sample at index 2 is FC
1003 // sample at index 3 is LFE
1004 // sample at index 4 is RL
1005 // sample at index 5 is RR
1006 // sample at index 6 is SL
1007 // sample at index 7 is SR
1008 // code is mostly duplicated between the two values of accumulate to avoid repeating the test
1009 // for every sample
1010 if (accumulate) {
1011 while (numFrames) {
1012 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
Andy Hunged1172f2018-12-21 16:20:03 -08001013 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_FLOAT)
1014 + (pSrc[3] * MINUS_3_DB_IN_FLOAT);
Ramesh Katurid7d01342016-05-02 15:03:47 +05301015 // FL + centerPlusLfeContrib + SL + RL
1016 lt = pSrc[0] + centerPlusLfeContrib + pSrc[6] + pSrc[4];
1017 // FR + centerPlusLfeContrib + SR + RR
1018 rt = pSrc[1] + centerPlusLfeContrib + pSrc[7] + pSrc[5];
1019 //accumulate in destination
1020 pDst[0] = clamp_float(pDst[0] + (lt / 2.0f));
1021 pDst[1] = clamp_float(pDst[1] + (rt / 2.0f));
1022 pSrc += 8;
1023 pDst += 2;
1024 numFrames--;
1025 }
1026 } else { // same code as above but without adding and clamping pDst[i] to itself
1027 while (numFrames) {
1028 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
1029 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_FLOAT)
1030 + (pSrc[3] * MINUS_3_DB_IN_FLOAT);
1031 // FL + centerPlusLfeContrib + SL + RL
1032 lt = pSrc[0] + centerPlusLfeContrib + pSrc[6] + pSrc[4];
1033 // FR + centerPlusLfeContrib + SR + RR
1034 rt = pSrc[1] + centerPlusLfeContrib + pSrc[7] + pSrc[5];
1035 // store in destination
1036 pDst[0] = clamp_float(lt / 2.0f); // differs from when accumulate is true above
1037 pDst[1] = clamp_float(rt / 2.0f); // differs from when accumulate is true above
1038 pSrc += 8;
1039 pDst += 2;
1040 numFrames--;
1041 }
1042 }
1043}
Andy Hungca1c4e42021-05-06 22:32:02 -07001044
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -07001045/*----------------------------------------------------------------------------
1046 * Downmix_foldGeneric()
1047 *----------------------------------------------------------------------------
1048 * Purpose:
1049 * downmix to stereo a multichannel signal whose format is:
1050 * - has FL/FR
1051 * - if using AUDIO_CHANNEL_OUT_SIDE*, it contains both left and right
1052 * - if using AUDIO_CHANNEL_OUT_BACK*, it contains both left and right
1053 * - doesn't use any of the AUDIO_CHANNEL_OUT_TOP* channels
1054 * - doesn't use any of the AUDIO_CHANNEL_OUT_FRONT_*_OF_CENTER channels
1055 * Only handles channel masks not enumerated in downmix_input_channel_mask_t
1056 *
1057 * Inputs:
1058 * mask the channel mask of pSrc
1059 * pSrc multichannel audio buffer to downmix
1060 * numFrames the number of multichannel frames to downmix
1061 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
1062 * or overwrite pDst (when false)
1063 *
1064 * Outputs:
1065 * pDst downmixed stereo audio samples
1066 *
1067 * Returns: false if multichannel format is not supported
1068 *
1069 *----------------------------------------------------------------------------
1070 */
Ramesh Katurid7d01342016-05-02 15:03:47 +05301071bool Downmix_foldGeneric(
1072 uint32_t mask, LVM_FLOAT *pSrc, LVM_FLOAT *pDst, size_t numFrames, bool accumulate) {
1073
1074 if (!Downmix_validChannelMask(mask)) {
1075 return false;
1076 }
1077
1078 const bool hasSides = (mask & kSides) != 0;
1079 const bool hasBacks = (mask & kBacks) != 0;
1080
1081 const int numChan = audio_channel_count_from_out_mask(mask);
1082 const bool hasFC = ((mask & AUDIO_CHANNEL_OUT_FRONT_CENTER) == AUDIO_CHANNEL_OUT_FRONT_CENTER);
1083 const bool hasLFE =
1084 ((mask & AUDIO_CHANNEL_OUT_LOW_FREQUENCY) == AUDIO_CHANNEL_OUT_LOW_FREQUENCY);
1085 const bool hasBC = ((mask & AUDIO_CHANNEL_OUT_BACK_CENTER) == AUDIO_CHANNEL_OUT_BACK_CENTER);
1086 // compute at what index each channel is: samples will be in the following order:
1087 // FL FR FC LFE BL BR BC SL SR
1088 // when a channel is not present, its index is set to the same as the index of the preceding
1089 // channel
1090 const int indexFC = hasFC ? 2 : 1; // front center
1091 const int indexLFE = hasLFE ? indexFC + 1 : indexFC; // low frequency
1092 const int indexBL = hasBacks ? indexLFE + 1 : indexLFE; // back left
1093 const int indexBR = hasBacks ? indexBL + 1 : indexBL; // back right
1094 const int indexBC = hasBC ? indexBR + 1 : indexBR; // back center
1095 const int indexSL = hasSides ? indexBC + 1 : indexBC; // side left
1096 const int indexSR = hasSides ? indexSL + 1 : indexSL; // side right
1097
1098 LVM_FLOAT lt, rt, centersLfeContrib;
1099 // code is mostly duplicated between the two values of accumulate to avoid repeating the test
1100 // for every sample
1101 if (accumulate) {
1102 while (numFrames) {
1103 // compute contribution of FC, BC and LFE
1104 centersLfeContrib = 0;
1105 if (hasFC) { centersLfeContrib += pSrc[indexFC]; }
1106 if (hasLFE) { centersLfeContrib += pSrc[indexLFE]; }
1107 if (hasBC) { centersLfeContrib += pSrc[indexBC]; }
1108 centersLfeContrib *= MINUS_3_DB_IN_FLOAT;
1109 // always has FL/FR
1110 lt = pSrc[0];
1111 rt = pSrc[1];
1112 // mix in sides and backs
1113 if (hasSides) {
1114 lt += pSrc[indexSL];
1115 rt += pSrc[indexSR];
1116 }
1117 if (hasBacks) {
1118 lt += pSrc[indexBL];
1119 rt += pSrc[indexBR];
1120 }
1121 lt += centersLfeContrib;
1122 rt += centersLfeContrib;
1123 // accumulate in destination
1124 pDst[0] = clamp_float(pDst[0] + (lt / 2.0f));
1125 pDst[1] = clamp_float(pDst[1] + (rt / 2.0f));
1126 pSrc += numChan;
1127 pDst += 2;
1128 numFrames--;
1129 }
1130 } else {
1131 while (numFrames) {
1132 // compute contribution of FC, BC and LFE
1133 centersLfeContrib = 0;
1134 if (hasFC) { centersLfeContrib += pSrc[indexFC]; }
1135 if (hasLFE) { centersLfeContrib += pSrc[indexLFE]; }
1136 if (hasBC) { centersLfeContrib += pSrc[indexBC]; }
1137 centersLfeContrib *= MINUS_3_DB_IN_FLOAT;
1138 // always has FL/FR
1139 lt = pSrc[0];
1140 rt = pSrc[1];
1141 // mix in sides and backs
1142 if (hasSides) {
1143 lt += pSrc[indexSL];
1144 rt += pSrc[indexSR];
1145 }
1146 if (hasBacks) {
1147 lt += pSrc[indexBL];
1148 rt += pSrc[indexBR];
1149 }
1150 lt += centersLfeContrib;
1151 rt += centersLfeContrib;
1152 // store in destination
1153 pDst[0] = clamp_float(lt / 2.0f); // differs from when accumulate is true above
1154 pDst[1] = clamp_float(rt / 2.0f); // differs from when accumulate is true above
1155 pSrc += numChan;
1156 pDst += 2;
1157 numFrames--;
1158 }
1159 }
1160 return true;
1161}