blob: 90bb410f81d4aa17a6a8952e3c621101d183ff51 [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 Hunge807e6b2021-05-19 12:01:58 -070022#include <audio_utils/ChannelMix.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
Andy Hung47068792021-05-18 16:28:32 -070031typedef enum {
32 DOWNMIX_STATE_UNINITIALIZED,
33 DOWNMIX_STATE_INITIALIZED,
34 DOWNMIX_STATE_ACTIVE,
35} downmix_state_t;
36
37/* parameters for each downmixer */
Andy Hunge807e6b2021-05-19 12:01:58 -070038struct downmix_object_t {
Andy Hung47068792021-05-18 16:28:32 -070039 downmix_state_t state;
40 downmix_type_t type;
41 bool apply_volume_correction;
42 uint8_t input_channel_count;
Andy Hunge807e6b2021-05-19 12:01:58 -070043 android::audio_utils::channels::ChannelMix channelMix;
44};
Andy Hung47068792021-05-18 16:28:32 -070045
46typedef struct downmix_module_s {
47 const struct effect_interface_s *itfe;
48 effect_config_t config;
49 downmix_object_t context;
50} downmix_module_t;
Ramesh Katurid7d01342016-05-02 15:03:47 +053051
Jean-Michel Trividb46b482012-04-23 11:29:26 -070052
Andy Hung47068792021-05-18 16:28:32 -070053// Audio Effect API
54static int32_t DownmixLib_Create(const effect_uuid_t *uuid,
55 int32_t sessionId,
56 int32_t ioId,
57 effect_handle_t *pHandle);
58static int32_t DownmixLib_Release(effect_handle_t handle);
59static int32_t DownmixLib_GetDescriptor(const effect_uuid_t *uuid,
60 effect_descriptor_t *pDescriptor);
61static int32_t Downmix_Process(effect_handle_t self,
62 audio_buffer_t *inBuffer,
63 audio_buffer_t *outBuffer);
64static int32_t Downmix_Command(effect_handle_t self,
65 uint32_t cmdCode,
66 uint32_t cmdSize,
67 void *pCmdData,
68 uint32_t *replySize,
69 void *pReplyData);
70static int32_t Downmix_GetDescriptor(effect_handle_t self,
71 effect_descriptor_t *pDescriptor);
72
73// Internal methods
74static int Downmix_Init(downmix_module_t *pDwmModule);
75static int Downmix_Configure(downmix_module_t *pDwmModule, effect_config_t *pConfig, bool init);
76static int Downmix_Reset(downmix_object_t *pDownmixer, bool init);
77static int Downmix_setParameter(
78 downmix_object_t *pDownmixer, int32_t param, uint32_t size, void *pValue);
79static int Downmix_getParameter(
80 downmix_object_t *pDownmixer, int32_t param, uint32_t *pSize, void *pValue);
Andy Hung47068792021-05-18 16:28:32 -070081
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -080082// effect_handle_t interface implementation for downmix effect
83const struct effect_interface_s gDownmixInterface = {
84 Downmix_Process,
85 Downmix_Command,
86 Downmix_GetDescriptor,
87 NULL /* no process_reverse function, no reference stream needed */
88};
89
Marco Nelissen7f16b192012-10-25 16:05:57 -070090// This is the only symbol that needs to be exported
91__attribute__ ((visibility ("default")))
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -080092audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
synergydevc9d8ea72013-10-19 22:51:33 -070093 .tag = AUDIO_EFFECT_LIBRARY_TAG,
94 .version = EFFECT_LIBRARY_API_VERSION,
95 .name = "Downmix Library",
96 .implementor = "The Android Open Source Project",
97 .create_effect = DownmixLib_Create,
98 .release_effect = DownmixLib_Release,
99 .get_descriptor = DownmixLib_GetDescriptor,
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800100};
101
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800102// AOSP insert downmix UUID: 93f04452-e4fe-41cc-91f9-e475b6d1d69f
103static const effect_descriptor_t gDownmixDescriptor = {
104 EFFECT_UIID_DOWNMIX__, //type
105 {0x93f04452, 0xe4fe, 0x41cc, 0x91f9, {0xe4, 0x75, 0xb6, 0xd1, 0xd6, 0x9f}}, // uuid
106 EFFECT_CONTROL_API_VERSION,
107 EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST,
108 0, //FIXME what value should be reported? // cpu load
109 0, //FIXME what value should be reported? // memory usage
110 "Multichannel Downmix To Stereo", // human readable effect name
111 "The Android Open Source Project" // human readable effect implementor name
112};
113
114// gDescriptors contains pointers to all defined effect descriptor in this library
115static const effect_descriptor_t * const gDescriptors[] = {
116 &gDownmixDescriptor
117};
118
119// number of effects in this library
120const int kNbEffects = sizeof(gDescriptors) / sizeof(const effect_descriptor_t *);
Andy Hungca1c4e42021-05-06 22:32:02 -0700121
Andy Hungef25b6b2021-05-19 09:26:49 -0700122static inline float clamp_float(float value) {
123 return fmin(fmax(value, -1.f), 1.f);
Ramesh Katurid7d01342016-05-02 15:03:47 +0530124}
Andy Hungca1c4e42021-05-06 22:32:02 -0700125
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800126/*----------------------------------------------------------------------------
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700127 * Test code
128 *--------------------------------------------------------------------------*/
129#ifdef DOWNMIX_TEST_CHANNEL_INDEX
130// strictly for testing, logs the indices of the channels for a given mask,
131// uses the same code as Downmix_foldGeneric()
132void Downmix_testIndexComputation(uint32_t mask) {
Andy Hung47068792021-05-18 16:28:32 -0700133 ALOGI("Testing index computation for %#x:", mask);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700134 // check against unsupported channels
135 if (mask & kUnsupported) {
136 ALOGE("Unsupported channels (top or front left/right of center)");
137 return;
138 }
139 // verify has FL/FR
140 if ((mask & AUDIO_CHANNEL_OUT_STEREO) != AUDIO_CHANNEL_OUT_STEREO) {
141 ALOGE("Front channels must be present");
142 return;
143 }
144 // verify uses SIDE as a pair (ok if not using SIDE at all)
145 bool hasSides = false;
146 if ((mask & kSides) != 0) {
147 if ((mask & kSides) != kSides) {
148 ALOGE("Side channels must be used as a pair");
149 return;
150 }
151 hasSides = true;
152 }
153 // verify uses BACK as a pair (ok if not using BACK at all)
154 bool hasBacks = false;
155 if ((mask & kBacks) != 0) {
156 if ((mask & kBacks) != kBacks) {
157 ALOGE("Back channels must be used as a pair");
158 return;
159 }
160 hasBacks = true;
161 }
162
Andy Hunge5412692014-05-16 11:25:07 -0700163 const int numChan = audio_channel_count_from_out_mask(mask);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700164 const bool hasFC = ((mask & AUDIO_CHANNEL_OUT_FRONT_CENTER) == AUDIO_CHANNEL_OUT_FRONT_CENTER);
165 const bool hasLFE =
166 ((mask & AUDIO_CHANNEL_OUT_LOW_FREQUENCY) == AUDIO_CHANNEL_OUT_LOW_FREQUENCY);
167 const bool hasBC = ((mask & AUDIO_CHANNEL_OUT_BACK_CENTER) == AUDIO_CHANNEL_OUT_BACK_CENTER);
168 // compute at what index each channel is: samples will be in the following order:
169 // FL FR FC LFE BL BR BC SL SR
170 // when a channel is not present, its index is set to the same as the index of the preceding
171 // channel
172 const int indexFC = hasFC ? 2 : 1; // front center
173 const int indexLFE = hasLFE ? indexFC + 1 : indexFC; // low frequency
174 const int indexBL = hasBacks ? indexLFE + 1 : indexLFE; // back left
175 const int indexBR = hasBacks ? indexBL + 1 : indexBL; // back right
176 const int indexBC = hasBC ? indexBR + 1 : indexBR; // back center
177 const int indexSL = hasSides ? indexBC + 1 : indexBC; // side left
178 const int indexSR = hasSides ? indexSL + 1 : indexSL; // side right
179
180 ALOGI(" FL FR FC LFE BL BR BC SL SR");
181 ALOGI(" %d %d %d %d %d %d %d %d %d",
182 0, 1, indexFC, indexLFE, indexBL, indexBR, indexBC, indexSL, indexSR);
183}
184#endif
185
Haynes Mathew Georgeee507ef2015-07-15 11:06:05 -0700186static bool Downmix_validChannelMask(uint32_t mask)
187{
188 if (!mask) {
189 return false;
190 }
191 // check against unsupported channels
Andy Hungef25b6b2021-05-19 09:26:49 -0700192 if (mask & ~AUDIO_CHANNEL_OUT_22POINT2) {
193 ALOGE("Unsupported channels in %u", mask & ~AUDIO_CHANNEL_OUT_22POINT2);
Haynes Mathew Georgeee507ef2015-07-15 11:06:05 -0700194 return false;
195 }
Haynes Mathew Georgeee507ef2015-07-15 11:06:05 -0700196 return true;
197}
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700198
199/*----------------------------------------------------------------------------
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800200 * Effect API implementation
201 *--------------------------------------------------------------------------*/
202
203/*--- Effect Library Interface Implementation ---*/
204
Andy Hung47068792021-05-18 16:28:32 -0700205static int32_t DownmixLib_Create(const effect_uuid_t *uuid,
Andy Hung679c4712021-05-19 14:14:54 -0700206 int32_t /* sessionId */,
207 int32_t /* ioId */,
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800208 effect_handle_t *pHandle) {
209 int ret;
210 int i;
211 downmix_module_t *module;
212 const effect_descriptor_t *desc;
213
214 ALOGV("DownmixLib_Create()");
215
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700216#ifdef DOWNMIX_TEST_CHANNEL_INDEX
217 // should work (won't log an error)
218 ALOGI("DOWNMIX_TEST_CHANNEL_INDEX: should work:");
219 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT |
220 AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_BACK_CENTER);
Andy Hung862b1ab2021-05-19 11:37:08 -0700221 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_QUAD_SIDE | AUDIO_CHANNEL_OUT_QUAD_BACK);
222 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_5POINT1_SIDE | AUDIO_CHANNEL_OUT_BACK_CENTER);
223 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_5POINT1_BACK | AUDIO_CHANNEL_OUT_BACK_CENTER);
Jean-Michel Trivi6895dee2012-05-15 15:51:16 -0700224 // shouldn't work (will log an error, won't display channel indices)
225 ALOGI("DOWNMIX_TEST_CHANNEL_INDEX: should NOT work:");
226 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT |
227 AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_BACK_LEFT);
228 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT |
229 AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_SIDE_LEFT);
230 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT |
231 AUDIO_CHANNEL_OUT_BACK_LEFT | AUDIO_CHANNEL_OUT_BACK_RIGHT);
232 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT |
233 AUDIO_CHANNEL_OUT_SIDE_LEFT | AUDIO_CHANNEL_OUT_SIDE_RIGHT);
234#endif
235
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800236 if (pHandle == NULL || uuid == NULL) {
237 return -EINVAL;
238 }
239
240 for (i = 0 ; i < kNbEffects ; i++) {
241 desc = gDescriptors[i];
242 if (memcmp(uuid, &desc->uuid, sizeof(effect_uuid_t)) == 0) {
243 break;
244 }
245 }
246
247 if (i == kNbEffects) {
248 return -ENOENT;
249 }
250
Andy Hung19748032021-05-19 17:34:16 -0700251 module = new downmix_module_t{};
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800252
253 module->itfe = &gDownmixInterface;
254
255 module->context.state = DOWNMIX_STATE_UNINITIALIZED;
256
257 ret = Downmix_Init(module);
258 if (ret < 0) {
259 ALOGW("DownmixLib_Create() init failed");
260 free(module);
261 return ret;
262 }
263
264 *pHandle = (effect_handle_t) module;
265
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700266 ALOGV("DownmixLib_Create() %p , size %zu", module, sizeof(downmix_module_t));
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800267
268 return 0;
269}
270
Andy Hung47068792021-05-18 16:28:32 -0700271static int32_t DownmixLib_Release(effect_handle_t handle) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800272 downmix_module_t *pDwmModule = (downmix_module_t *)handle;
273
274 ALOGV("DownmixLib_Release() %p", handle);
275 if (handle == NULL) {
276 return -EINVAL;
277 }
278
279 pDwmModule->context.state = DOWNMIX_STATE_UNINITIALIZED;
280
Andy Hung19748032021-05-19 17:34:16 -0700281 delete pDwmModule;
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800282 return 0;
283}
284
Andy Hung47068792021-05-18 16:28:32 -0700285static int32_t DownmixLib_GetDescriptor(
286 const effect_uuid_t *uuid, effect_descriptor_t *pDescriptor) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800287 ALOGV("DownmixLib_GetDescriptor()");
288 int i;
289
290 if (pDescriptor == NULL || uuid == NULL){
291 ALOGE("DownmixLib_Create() called with NULL pointer");
292 return -EINVAL;
293 }
294 ALOGV("DownmixLib_GetDescriptor() nb effects=%d", kNbEffects);
295 for (i = 0; i < kNbEffects; i++) {
296 ALOGV("DownmixLib_GetDescriptor() i=%d", i);
297 if (memcmp(uuid, &gDescriptors[i]->uuid, sizeof(effect_uuid_t)) == 0) {
298 memcpy(pDescriptor, gDescriptors[i], sizeof(effect_descriptor_t));
Andy Hung47068792021-05-18 16:28:32 -0700299 ALOGV("EffectGetDescriptor - UUID matched downmix type %d, UUID = %#x",
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800300 i, gDescriptors[i]->uuid.timeLow);
301 return 0;
302 }
303 }
304
305 return -EINVAL;
306}
307
Ramesh Katurid7d01342016-05-02 15:03:47 +0530308/*--- Effect Control Interface Implementation ---*/
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800309
Andy Hung47068792021-05-18 16:28:32 -0700310static int32_t Downmix_Process(effect_handle_t self,
Ramesh Katurid7d01342016-05-02 15:03:47 +0530311 audio_buffer_t *inBuffer, audio_buffer_t *outBuffer) {
312
313 downmix_object_t *pDownmixer;
Andy Hunge807e6b2021-05-19 12:01:58 -0700314 const float *pSrc;
315 float *pDst;
Ramesh Katurid7d01342016-05-02 15:03:47 +0530316 downmix_module_t *pDwmModule = (downmix_module_t *)self;
317
318 if (pDwmModule == NULL) {
319 return -EINVAL;
320 }
321
322 if (inBuffer == NULL || inBuffer->raw == NULL ||
323 outBuffer == NULL || outBuffer->raw == NULL ||
324 inBuffer->frameCount != outBuffer->frameCount) {
325 return -EINVAL;
326 }
327
328 pDownmixer = (downmix_object_t*) &pDwmModule->context;
329
330 if (pDownmixer->state == DOWNMIX_STATE_UNINITIALIZED) {
331 ALOGE("Downmix_Process error: trying to use an uninitialized downmixer");
332 return -EINVAL;
333 } else if (pDownmixer->state == DOWNMIX_STATE_INITIALIZED) {
334 ALOGE("Downmix_Process error: trying to use a non-configured downmixer");
335 return -ENODATA;
336 }
337
Andy Hunga11b17f2021-05-19 11:44:13 -0700338 pSrc = inBuffer->f32;
339 pDst = outBuffer->f32;
Ramesh Katurid7d01342016-05-02 15:03:47 +0530340 size_t numFrames = outBuffer->frameCount;
341
342 const bool accumulate =
343 (pDwmModule->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE);
Andy Hunge807e6b2021-05-19 12:01:58 -0700344 const audio_channel_mask_t downmixInputChannelMask =
345 (audio_channel_mask_t)pDwmModule->config.inputCfg.channels;
Ramesh Katurid7d01342016-05-02 15:03:47 +0530346
347 switch(pDownmixer->type) {
348
349 case DOWNMIX_TYPE_STRIP:
350 if (accumulate) {
351 while (numFrames) {
352 pDst[0] = clamp_float(pDst[0] + pSrc[0]);
353 pDst[1] = clamp_float(pDst[1] + pSrc[1]);
354 pSrc += pDownmixer->input_channel_count;
355 pDst += 2;
356 numFrames--;
357 }
358 } else {
359 while (numFrames) {
360 pDst[0] = pSrc[0];
361 pDst[1] = pSrc[1];
362 pSrc += pDownmixer->input_channel_count;
363 pDst += 2;
364 numFrames--;
365 }
366 }
367 break;
368
Andy Hunge807e6b2021-05-19 12:01:58 -0700369 case DOWNMIX_TYPE_FOLD: {
370 if (!pDownmixer->channelMix.process(
371 pSrc, pDst, numFrames, accumulate, downmixInputChannelMask)) {
Andy Hung47068792021-05-18 16:28:32 -0700372 ALOGE("Multichannel configuration %#x is not supported",
Ramesh Katurid7d01342016-05-02 15:03:47 +0530373 downmixInputChannelMask);
374 return -EINVAL;
375 }
Ramesh Katurid7d01342016-05-02 15:03:47 +0530376 }
377 break;
378
379 default:
380 return -EINVAL;
381 }
382
383 return 0;
384}
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800385
Andy Hung47068792021-05-18 16:28:32 -0700386static int32_t Downmix_Command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800387 void *pCmdData, uint32_t *replySize, void *pReplyData) {
388
389 downmix_module_t *pDwmModule = (downmix_module_t *) self;
390 downmix_object_t *pDownmixer;
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800391
392 if (pDwmModule == NULL || pDwmModule->context.state == DOWNMIX_STATE_UNINITIALIZED) {
393 return -EINVAL;
394 }
395
396 pDownmixer = (downmix_object_t*) &pDwmModule->context;
397
Andy Hung47068792021-05-18 16:28:32 -0700398 ALOGV("Downmix_Command command %u cmdSize %u", cmdCode, cmdSize);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800399
400 switch (cmdCode) {
401 case EFFECT_CMD_INIT:
Eric Laurent0f714a42015-06-19 15:33:57 -0700402 if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800403 return -EINVAL;
404 }
405 *(int *) pReplyData = Downmix_Init(pDwmModule);
406 break;
407
408 case EFFECT_CMD_SET_CONFIG:
409 if (pCmdData == NULL || cmdSize != sizeof(effect_config_t)
Eric Laurent0f714a42015-06-19 15:33:57 -0700410 || pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800411 return -EINVAL;
412 }
413 *(int *) pReplyData = Downmix_Configure(pDwmModule,
414 (effect_config_t *)pCmdData, false);
415 break;
416
417 case EFFECT_CMD_RESET:
418 Downmix_Reset(pDownmixer, false);
419 break;
420
Andy Hung19748032021-05-19 17:34:16 -0700421 case EFFECT_CMD_GET_PARAM: {
Andy Hung47068792021-05-18 16:28:32 -0700422 ALOGV("Downmix_Command EFFECT_CMD_GET_PARAM pCmdData %p, *replySize %u, pReplyData: %p",
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800423 pCmdData, *replySize, pReplyData);
424 if (pCmdData == NULL || cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t)) ||
Eric Laurent0f714a42015-06-19 15:33:57 -0700425 pReplyData == NULL || replySize == NULL ||
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800426 *replySize < (int) sizeof(effect_param_t) + 2 * sizeof(int32_t)) {
427 return -EINVAL;
428 }
429 effect_param_t *rep = (effect_param_t *) pReplyData;
430 memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + sizeof(int32_t));
Andy Hung47068792021-05-18 16:28:32 -0700431 ALOGV("Downmix_Command EFFECT_CMD_GET_PARAM param %d, replySize %u",
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800432 *(int32_t *)rep->data, rep->vsize);
433 rep->status = Downmix_getParameter(pDownmixer, *(int32_t *)rep->data, &rep->vsize,
434 rep->data + sizeof(int32_t));
435 *replySize = sizeof(effect_param_t) + sizeof(int32_t) + rep->vsize;
436 break;
Andy Hung19748032021-05-19 17:34:16 -0700437 }
438 case EFFECT_CMD_SET_PARAM: {
Andy Hung47068792021-05-18 16:28:32 -0700439 ALOGV("Downmix_Command EFFECT_CMD_SET_PARAM cmdSize %d pCmdData %p, *replySize %u"
Mark Salyzyn7cb0e732014-04-18 13:48:25 -0700440 ", pReplyData %p", cmdSize, pCmdData, *replySize, pReplyData);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800441 if (pCmdData == NULL || (cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t)))
Eric Laurent0f714a42015-06-19 15:33:57 -0700442 || pReplyData == NULL || replySize == NULL || *replySize != (int)sizeof(int32_t)) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800443 return -EINVAL;
444 }
445 effect_param_t *cmd = (effect_param_t *) pCmdData;
Mikhail Naganov804632a2017-07-24 17:25:47 -0700446 if (cmd->psize != sizeof(int32_t)) {
447 android_errorWriteLog(0x534e4554, "63662938");
448 return -EINVAL;
449 }
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800450 *(int *)pReplyData = Downmix_setParameter(pDownmixer, *(int32_t *)cmd->data,
451 cmd->vsize, cmd->data + sizeof(int32_t));
452 break;
Andy Hung19748032021-05-19 17:34:16 -0700453 }
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800454
455 case EFFECT_CMD_SET_PARAM_DEFERRED:
456 //FIXME implement
457 ALOGW("Downmix_Command command EFFECT_CMD_SET_PARAM_DEFERRED not supported, FIXME");
458 break;
459
460 case EFFECT_CMD_SET_PARAM_COMMIT:
461 //FIXME implement
462 ALOGW("Downmix_Command command EFFECT_CMD_SET_PARAM_COMMIT not supported, FIXME");
463 break;
464
465 case EFFECT_CMD_ENABLE:
Eric Laurent0f714a42015-06-19 15:33:57 -0700466 if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800467 return -EINVAL;
468 }
469 if (pDownmixer->state != DOWNMIX_STATE_INITIALIZED) {
470 return -ENOSYS;
471 }
472 pDownmixer->state = DOWNMIX_STATE_ACTIVE;
473 ALOGV("EFFECT_CMD_ENABLE() OK");
474 *(int *)pReplyData = 0;
475 break;
476
477 case EFFECT_CMD_DISABLE:
Eric Laurent0f714a42015-06-19 15:33:57 -0700478 if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800479 return -EINVAL;
480 }
481 if (pDownmixer->state != DOWNMIX_STATE_ACTIVE) {
482 return -ENOSYS;
483 }
484 pDownmixer->state = DOWNMIX_STATE_INITIALIZED;
485 ALOGV("EFFECT_CMD_DISABLE() OK");
486 *(int *)pReplyData = 0;
487 break;
488
489 case EFFECT_CMD_SET_DEVICE:
490 if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) {
491 return -EINVAL;
492 }
493 // FIXME change type if playing on headset vs speaker
Andy Hung47068792021-05-18 16:28:32 -0700494 ALOGV("Downmix_Command EFFECT_CMD_SET_DEVICE: %#x", *(uint32_t *)pCmdData);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800495 break;
496
497 case EFFECT_CMD_SET_VOLUME: {
498 // audio output is always stereo => 2 channel volumes
499 if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t) * 2) {
500 return -EINVAL;
501 }
502 // FIXME change volume
503 ALOGW("Downmix_Command command EFFECT_CMD_SET_VOLUME not supported, FIXME");
504 float left = (float)(*(uint32_t *)pCmdData) / (1 << 24);
505 float right = (float)(*((uint32_t *)pCmdData + 1)) / (1 << 24);
506 ALOGV("Downmix_Command EFFECT_CMD_SET_VOLUME: left %f, right %f ", left, right);
507 break;
508 }
509
510 case EFFECT_CMD_SET_AUDIO_MODE:
511 if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) {
512 return -EINVAL;
513 }
Andy Hung47068792021-05-18 16:28:32 -0700514 ALOGV("Downmix_Command EFFECT_CMD_SET_AUDIO_MODE: %u", *(uint32_t *)pCmdData);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800515 break;
516
517 case EFFECT_CMD_SET_CONFIG_REVERSE:
518 case EFFECT_CMD_SET_INPUT_DEVICE:
519 // these commands are ignored by a downmix effect
520 break;
521
522 default:
Andy Hung47068792021-05-18 16:28:32 -0700523 ALOGW("Downmix_Command invalid command %u", cmdCode);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800524 return -EINVAL;
525 }
526
527 return 0;
528}
529
Andy Hung47068792021-05-18 16:28:32 -0700530static int32_t Downmix_GetDescriptor(effect_handle_t self, effect_descriptor_t *pDescriptor)
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800531{
532 downmix_module_t *pDwnmxModule = (downmix_module_t *) self;
533
534 if (pDwnmxModule == NULL ||
535 pDwnmxModule->context.state == DOWNMIX_STATE_UNINITIALIZED) {
536 return -EINVAL;
537 }
538
539 memcpy(pDescriptor, &gDownmixDescriptor, sizeof(effect_descriptor_t));
540
541 return 0;
542}
543
544
545/*----------------------------------------------------------------------------
546 * Downmix internal functions
547 *--------------------------------------------------------------------------*/
548
549/*----------------------------------------------------------------------------
550 * Downmix_Init()
551 *----------------------------------------------------------------------------
552 * Purpose:
553 * Initialize downmix context and apply default parameters
554 *
555 * Inputs:
556 * pDwmModule pointer to downmix effect module
557 *
558 * Outputs:
559 *
560 * Returns:
561 * 0 indicates success
562 *
563 * Side Effects:
564 * updates:
565 * pDwmModule->context.type
566 * pDwmModule->context.apply_volume_correction
567 * pDwmModule->config.inputCfg
568 * pDwmModule->config.outputCfg
569 * pDwmModule->config.inputCfg.samplingRate
570 * pDwmModule->config.outputCfg.samplingRate
571 * pDwmModule->context.state
572 * doesn't set:
573 * pDwmModule->itfe
574 *
575 *----------------------------------------------------------------------------
576 */
577
Andy Hung47068792021-05-18 16:28:32 -0700578static int Downmix_Init(downmix_module_t *pDwmModule) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800579
580 ALOGV("Downmix_Init module %p", pDwmModule);
581 int ret = 0;
582
583 memset(&pDwmModule->context, 0, sizeof(downmix_object_t));
584
585 pDwmModule->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
Andy Hung47068792021-05-18 16:28:32 -0700586 pDwmModule->config.inputCfg.format = AUDIO_FORMAT_PCM_FLOAT;
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800587 pDwmModule->config.inputCfg.channels = AUDIO_CHANNEL_OUT_7POINT1;
588 pDwmModule->config.inputCfg.bufferProvider.getBuffer = NULL;
589 pDwmModule->config.inputCfg.bufferProvider.releaseBuffer = NULL;
590 pDwmModule->config.inputCfg.bufferProvider.cookie = NULL;
591 pDwmModule->config.inputCfg.mask = EFFECT_CONFIG_ALL;
592
593 pDwmModule->config.inputCfg.samplingRate = 44100;
594 pDwmModule->config.outputCfg.samplingRate = pDwmModule->config.inputCfg.samplingRate;
595
596 // set a default value for the access mode, but should be overwritten by caller
597 pDwmModule->config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
Andy Hung47068792021-05-18 16:28:32 -0700598 pDwmModule->config.outputCfg.format = AUDIO_FORMAT_PCM_FLOAT;
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800599 pDwmModule->config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
600 pDwmModule->config.outputCfg.bufferProvider.getBuffer = NULL;
601 pDwmModule->config.outputCfg.bufferProvider.releaseBuffer = NULL;
602 pDwmModule->config.outputCfg.bufferProvider.cookie = NULL;
603 pDwmModule->config.outputCfg.mask = EFFECT_CONFIG_ALL;
604
605 ret = Downmix_Configure(pDwmModule, &pDwmModule->config, true);
606 if (ret != 0) {
607 ALOGV("Downmix_Init error %d on module %p", ret, pDwmModule);
608 } else {
609 pDwmModule->context.state = DOWNMIX_STATE_INITIALIZED;
610 }
611
612 return ret;
613}
614
615
616/*----------------------------------------------------------------------------
617 * Downmix_Configure()
618 *----------------------------------------------------------------------------
619 * Purpose:
620 * Set input and output audio configuration.
621 *
622 * Inputs:
623 * pDwmModule pointer to downmix effect module
624 * pConfig pointer to effect_config_t structure containing input
625 * and output audio parameters configuration
626 * init true if called from init function
627 *
628 * Outputs:
629 *
630 * Returns:
631 * 0 indicates success
632 *
633 * Side Effects:
634 *
635 *----------------------------------------------------------------------------
636 */
637
Andy Hung47068792021-05-18 16:28:32 -0700638static int Downmix_Configure(downmix_module_t *pDwmModule, effect_config_t *pConfig, bool init) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800639
640 downmix_object_t *pDownmixer = &pDwmModule->context;
641
642 // Check configuration compatibility with build options, and effect capabilities
643 if (pConfig->inputCfg.samplingRate != pConfig->outputCfg.samplingRate
Andy Hung47068792021-05-18 16:28:32 -0700644 || pConfig->outputCfg.channels != AUDIO_CHANNEL_OUT_STEREO
645 || pConfig->inputCfg.format != AUDIO_FORMAT_PCM_FLOAT
646 || pConfig->outputCfg.format != AUDIO_FORMAT_PCM_FLOAT) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800647 ALOGE("Downmix_Configure error: invalid config");
648 return -EINVAL;
649 }
650
Chih-Wei Huanga93c8c92013-01-10 20:52:57 +0800651 if (&pDwmModule->config != pConfig) {
652 memcpy(&pDwmModule->config, pConfig, sizeof(effect_config_t));
653 }
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800654
655 if (init) {
656 pDownmixer->type = DOWNMIX_TYPE_FOLD;
657 pDownmixer->apply_volume_correction = false;
658 pDownmixer->input_channel_count = 8; // matches default input of AUDIO_CHANNEL_OUT_7POINT1
659 } else {
Haynes Mathew Georgeee507ef2015-07-15 11:06:05 -0700660 // when configuring the effect, do not allow a blank or unsupported channel mask
661 if (!Downmix_validChannelMask(pConfig->inputCfg.channels)) {
662 ALOGE("Downmix_Configure error: input channel mask(0x%x) not supported",
663 pConfig->inputCfg.channels);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800664 return -EINVAL;
665 }
Andy Hunge5412692014-05-16 11:25:07 -0700666 pDownmixer->input_channel_count =
667 audio_channel_count_from_out_mask(pConfig->inputCfg.channels);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800668 }
669
670 Downmix_Reset(pDownmixer, init);
671
672 return 0;
673}
674
675
676/*----------------------------------------------------------------------------
677 * Downmix_Reset()
678 *----------------------------------------------------------------------------
679 * Purpose:
680 * Reset internal states.
681 *
682 * Inputs:
683 * pDownmixer pointer to downmix context
684 * init true if called from init function
685 *
686 * Outputs:
687*
688 * Returns:
689 * 0 indicates success
690 *
691 * Side Effects:
692 *
693 *----------------------------------------------------------------------------
694 */
695
Andy Hung679c4712021-05-19 14:14:54 -0700696static int Downmix_Reset(downmix_object_t* /* pDownmixer */, bool /* init */) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800697 // nothing to do here
698 return 0;
699}
700
701
702/*----------------------------------------------------------------------------
703 * Downmix_setParameter()
704 *----------------------------------------------------------------------------
705 * Purpose:
706 * Set a Downmix parameter
707 *
708 * Inputs:
709 * pDownmixer handle to instance data
710 * param parameter
711 * pValue pointer to parameter value
712 * size value size
713 *
714 * Outputs:
715 *
716 * Returns:
717 * 0 indicates success
718 *
719 * Side Effects:
720 *
721 *----------------------------------------------------------------------------
722 */
Andy Hung47068792021-05-18 16:28:32 -0700723static int Downmix_setParameter(
724 downmix_object_t *pDownmixer, int32_t param, uint32_t size, void *pValue) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800725
726 int16_t value16;
Andy Hung47068792021-05-18 16:28:32 -0700727 ALOGV("Downmix_setParameter, context %p, param %d, value16 %d, value32 %d",
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800728 pDownmixer, param, *(int16_t *)pValue, *(int32_t *)pValue);
729
730 switch (param) {
731
732 case DOWNMIX_PARAM_TYPE:
733 if (size != sizeof(downmix_type_t)) {
Andy Hung47068792021-05-18 16:28:32 -0700734 ALOGE("Downmix_setParameter(DOWNMIX_PARAM_TYPE) invalid size %u, should be %zu",
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800735 size, sizeof(downmix_type_t));
736 return -EINVAL;
737 }
738 value16 = *(int16_t *)pValue;
Andy Hung47068792021-05-18 16:28:32 -0700739 ALOGV("set DOWNMIX_PARAM_TYPE, type %d", value16);
Jean-Michel Trivi7d5b2622012-04-04 18:54:36 -0700740 if (!((value16 > DOWNMIX_TYPE_INVALID) && (value16 <= DOWNMIX_TYPE_LAST))) {
Andy Hung47068792021-05-18 16:28:32 -0700741 ALOGE("Downmix_setParameter invalid DOWNMIX_PARAM_TYPE value %d", value16);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800742 return -EINVAL;
743 } else {
744 pDownmixer->type = (downmix_type_t) value16;
745 break;
746
747 default:
Andy Hung47068792021-05-18 16:28:32 -0700748 ALOGE("Downmix_setParameter unknown parameter %d", param);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800749 return -EINVAL;
750 }
751}
752
753 return 0;
754} /* end Downmix_setParameter */
755
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800756/*----------------------------------------------------------------------------
757 * Downmix_getParameter()
758 *----------------------------------------------------------------------------
759 * Purpose:
760 * Get a Downmix parameter
761 *
762 * Inputs:
763 * pDownmixer handle to instance data
764 * param parameter
765 * pValue pointer to variable to hold retrieved value
766 * pSize pointer to value size: maximum size as input
767 *
768 * Outputs:
769 * *pValue updated with parameter value
770 * *pSize updated with actual value size
771 *
772 * Returns:
773 * 0 indicates success
774 *
775 * Side Effects:
776 *
777 *----------------------------------------------------------------------------
778 */
Andy Hung47068792021-05-18 16:28:32 -0700779static int Downmix_getParameter(
780 downmix_object_t *pDownmixer, int32_t param, uint32_t *pSize, void *pValue) {
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800781 int16_t *pValue16;
782
783 switch (param) {
784
785 case DOWNMIX_PARAM_TYPE:
786 if (*pSize < sizeof(int16_t)) {
Andy Hung47068792021-05-18 16:28:32 -0700787 ALOGE("Downmix_getParameter invalid parameter size %u for DOWNMIX_PARAM_TYPE", *pSize);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800788 return -EINVAL;
789 }
790 pValue16 = (int16_t *)pValue;
791 *pValue16 = (int16_t) pDownmixer->type;
792 *pSize = sizeof(int16_t);
Andy Hung47068792021-05-18 16:28:32 -0700793 ALOGV("Downmix_getParameter DOWNMIX_PARAM_TYPE is %d", *pValue16);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800794 break;
795
796 default:
Andy Hung47068792021-05-18 16:28:32 -0700797 ALOGE("Downmix_getParameter unknown parameter %d", param);
Jean-Michel Trivi04c1e532012-03-02 10:59:56 -0800798 return -EINVAL;
799 }
800
801 return 0;
802} /* end Downmix_getParameter */
803