1 /*
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"
18 //#define LOG_NDEBUG 0
19 #include <log/log.h>
20
21 #include "EffectDownmix.h"
22 #include <audio_utils/ChannelMix.h>
23
24 // 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
29 #define MINUS_3_DB_IN_FLOAT M_SQRT1_2 // -3dB = 0.70710678
30
31 typedef enum {
32 DOWNMIX_STATE_UNINITIALIZED,
33 DOWNMIX_STATE_INITIALIZED,
34 DOWNMIX_STATE_ACTIVE,
35 } downmix_state_t;
36
37 /* parameters for each downmixer */
38 struct downmix_object_t {
39 downmix_state_t state;
40 downmix_type_t type;
41 bool apply_volume_correction;
42 uint8_t input_channel_count;
43 android::audio_utils::channels::ChannelMix channelMix;
44 };
45
46 typedef 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;
51
52
53 // Audio Effect API
54 static int32_t DownmixLib_Create(const effect_uuid_t *uuid,
55 int32_t sessionId,
56 int32_t ioId,
57 effect_handle_t *pHandle);
58 static int32_t DownmixLib_Release(effect_handle_t handle);
59 static int32_t DownmixLib_GetDescriptor(const effect_uuid_t *uuid,
60 effect_descriptor_t *pDescriptor);
61 static int32_t Downmix_Process(effect_handle_t self,
62 audio_buffer_t *inBuffer,
63 audio_buffer_t *outBuffer);
64 static 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);
70 static int32_t Downmix_GetDescriptor(effect_handle_t self,
71 effect_descriptor_t *pDescriptor);
72
73 // Internal methods
74 static int Downmix_Init(downmix_module_t *pDwmModule);
75 static int Downmix_Configure(downmix_module_t *pDwmModule, effect_config_t *pConfig, bool init);
76 static int Downmix_Reset(downmix_object_t *pDownmixer, bool init);
77 static int Downmix_setParameter(
78 downmix_object_t *pDownmixer, int32_t param, uint32_t size, void *pValue);
79 static int Downmix_getParameter(
80 downmix_object_t *pDownmixer, int32_t param, uint32_t *pSize, void *pValue);
81
82 // effect_handle_t interface implementation for downmix effect
83 const 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
90 // This is the only symbol that needs to be exported
91 __attribute__ ((visibility ("default")))
92 audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
93 .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,
100 };
101
102 // AOSP insert downmix UUID: 93f04452-e4fe-41cc-91f9-e475b6d1d69f
103 static 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
115 static const effect_descriptor_t * const gDescriptors[] = {
116 &gDownmixDescriptor
117 };
118
119 // number of effects in this library
120 const int kNbEffects = sizeof(gDescriptors) / sizeof(const effect_descriptor_t *);
121
clamp_float(float value)122 static inline float clamp_float(float value) {
123 return fmin(fmax(value, -1.f), 1.f);
124 }
125
126 /*----------------------------------------------------------------------------
127 * 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()
Downmix_testIndexComputation(uint32_t mask)132 void Downmix_testIndexComputation(uint32_t mask) {
133 ALOGI("Testing index computation for %#x:", mask);
134 // 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
163 const int numChan = audio_channel_count_from_out_mask(mask);
164 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
Downmix_validChannelMask(uint32_t mask)186 static bool Downmix_validChannelMask(uint32_t mask)
187 {
188 if (!mask) {
189 return false;
190 }
191 // check against unsupported channels (up to FCC_26)
192 constexpr uint32_t MAXIMUM_CHANNEL_MASK = AUDIO_CHANNEL_OUT_22POINT2
193 | AUDIO_CHANNEL_OUT_FRONT_WIDE_LEFT | AUDIO_CHANNEL_OUT_FRONT_WIDE_RIGHT;
194 if (mask & ~MAXIMUM_CHANNEL_MASK) {
195 ALOGE("Unsupported channels in %#x", mask & ~MAXIMUM_CHANNEL_MASK);
196 return false;
197 }
198 return true;
199 }
200
201 /*----------------------------------------------------------------------------
202 * Effect API implementation
203 *--------------------------------------------------------------------------*/
204
205 /*--- Effect Library Interface Implementation ---*/
206
DownmixLib_Create(const effect_uuid_t * uuid,int32_t,int32_t,effect_handle_t * pHandle)207 static int32_t DownmixLib_Create(const effect_uuid_t *uuid,
208 int32_t /* sessionId */,
209 int32_t /* ioId */,
210 effect_handle_t *pHandle) {
211 int ret;
212 int i;
213 downmix_module_t *module;
214 const effect_descriptor_t *desc;
215
216 ALOGV("DownmixLib_Create()");
217
218 #ifdef DOWNMIX_TEST_CHANNEL_INDEX
219 // should work (won't log an error)
220 ALOGI("DOWNMIX_TEST_CHANNEL_INDEX: should work:");
221 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT |
222 AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_BACK_CENTER);
223 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_QUAD_SIDE | AUDIO_CHANNEL_OUT_QUAD_BACK);
224 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_5POINT1_SIDE | AUDIO_CHANNEL_OUT_BACK_CENTER);
225 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_5POINT1_BACK | AUDIO_CHANNEL_OUT_BACK_CENTER);
226 // shouldn't work (will log an error, won't display channel indices)
227 ALOGI("DOWNMIX_TEST_CHANNEL_INDEX: should NOT work:");
228 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT |
229 AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_BACK_LEFT);
230 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT |
231 AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_SIDE_LEFT);
232 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT |
233 AUDIO_CHANNEL_OUT_BACK_LEFT | AUDIO_CHANNEL_OUT_BACK_RIGHT);
234 Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT |
235 AUDIO_CHANNEL_OUT_SIDE_LEFT | AUDIO_CHANNEL_OUT_SIDE_RIGHT);
236 #endif
237
238 if (pHandle == NULL || uuid == NULL) {
239 return -EINVAL;
240 }
241
242 for (i = 0 ; i < kNbEffects ; i++) {
243 desc = gDescriptors[i];
244 if (memcmp(uuid, &desc->uuid, sizeof(effect_uuid_t)) == 0) {
245 break;
246 }
247 }
248
249 if (i == kNbEffects) {
250 return -ENOENT;
251 }
252
253 module = new downmix_module_t{};
254
255 module->itfe = &gDownmixInterface;
256
257 module->context.state = DOWNMIX_STATE_UNINITIALIZED;
258
259 ret = Downmix_Init(module);
260 if (ret < 0) {
261 ALOGW("DownmixLib_Create() init failed");
262 free(module);
263 return ret;
264 }
265
266 *pHandle = (effect_handle_t) module;
267
268 ALOGV("DownmixLib_Create() %p , size %zu", module, sizeof(downmix_module_t));
269
270 return 0;
271 }
272
DownmixLib_Release(effect_handle_t handle)273 static int32_t DownmixLib_Release(effect_handle_t handle) {
274 downmix_module_t *pDwmModule = (downmix_module_t *)handle;
275
276 ALOGV("DownmixLib_Release() %p", handle);
277 if (handle == NULL) {
278 return -EINVAL;
279 }
280
281 pDwmModule->context.state = DOWNMIX_STATE_UNINITIALIZED;
282
283 delete pDwmModule;
284 return 0;
285 }
286
DownmixLib_GetDescriptor(const effect_uuid_t * uuid,effect_descriptor_t * pDescriptor)287 static int32_t DownmixLib_GetDescriptor(
288 const effect_uuid_t *uuid, effect_descriptor_t *pDescriptor) {
289 ALOGV("DownmixLib_GetDescriptor()");
290 int i;
291
292 if (pDescriptor == NULL || uuid == NULL){
293 ALOGE("DownmixLib_Create() called with NULL pointer");
294 return -EINVAL;
295 }
296 ALOGV("DownmixLib_GetDescriptor() nb effects=%d", kNbEffects);
297 for (i = 0; i < kNbEffects; i++) {
298 ALOGV("DownmixLib_GetDescriptor() i=%d", i);
299 if (memcmp(uuid, &gDescriptors[i]->uuid, sizeof(effect_uuid_t)) == 0) {
300 memcpy(pDescriptor, gDescriptors[i], sizeof(effect_descriptor_t));
301 ALOGV("EffectGetDescriptor - UUID matched downmix type %d, UUID = %#x",
302 i, gDescriptors[i]->uuid.timeLow);
303 return 0;
304 }
305 }
306
307 return -EINVAL;
308 }
309
310 /*--- Effect Control Interface Implementation ---*/
311
Downmix_Process(effect_handle_t self,audio_buffer_t * inBuffer,audio_buffer_t * outBuffer)312 static int32_t Downmix_Process(effect_handle_t self,
313 audio_buffer_t *inBuffer, audio_buffer_t *outBuffer) {
314
315 downmix_object_t *pDownmixer;
316 const float *pSrc;
317 float *pDst;
318 downmix_module_t *pDwmModule = (downmix_module_t *)self;
319
320 if (pDwmModule == NULL) {
321 return -EINVAL;
322 }
323
324 if (inBuffer == NULL || inBuffer->raw == NULL ||
325 outBuffer == NULL || outBuffer->raw == NULL ||
326 inBuffer->frameCount != outBuffer->frameCount) {
327 return -EINVAL;
328 }
329
330 pDownmixer = (downmix_object_t*) &pDwmModule->context;
331
332 if (pDownmixer->state == DOWNMIX_STATE_UNINITIALIZED) {
333 ALOGE("Downmix_Process error: trying to use an uninitialized downmixer");
334 return -EINVAL;
335 } else if (pDownmixer->state == DOWNMIX_STATE_INITIALIZED) {
336 ALOGE("Downmix_Process error: trying to use a non-configured downmixer");
337 return -ENODATA;
338 }
339
340 pSrc = inBuffer->f32;
341 pDst = outBuffer->f32;
342 size_t numFrames = outBuffer->frameCount;
343
344 const bool accumulate =
345 (pDwmModule->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE);
346 const audio_channel_mask_t downmixInputChannelMask =
347 (audio_channel_mask_t)pDwmModule->config.inputCfg.channels;
348
349 switch(pDownmixer->type) {
350
351 case DOWNMIX_TYPE_STRIP:
352 if (accumulate) {
353 while (numFrames) {
354 pDst[0] = clamp_float(pDst[0] + pSrc[0]);
355 pDst[1] = clamp_float(pDst[1] + pSrc[1]);
356 pSrc += pDownmixer->input_channel_count;
357 pDst += 2;
358 numFrames--;
359 }
360 } else {
361 while (numFrames) {
362 pDst[0] = pSrc[0];
363 pDst[1] = pSrc[1];
364 pSrc += pDownmixer->input_channel_count;
365 pDst += 2;
366 numFrames--;
367 }
368 }
369 break;
370
371 case DOWNMIX_TYPE_FOLD: {
372 if (!pDownmixer->channelMix.process(
373 pSrc, pDst, numFrames, accumulate, downmixInputChannelMask)) {
374 ALOGE("Multichannel configuration %#x is not supported",
375 downmixInputChannelMask);
376 return -EINVAL;
377 }
378 }
379 break;
380
381 default:
382 return -EINVAL;
383 }
384
385 return 0;
386 }
387
Downmix_Command(effect_handle_t self,uint32_t cmdCode,uint32_t cmdSize,void * pCmdData,uint32_t * replySize,void * pReplyData)388 static int32_t Downmix_Command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
389 void *pCmdData, uint32_t *replySize, void *pReplyData) {
390
391 downmix_module_t *pDwmModule = (downmix_module_t *) self;
392 downmix_object_t *pDownmixer;
393
394 if (pDwmModule == NULL || pDwmModule->context.state == DOWNMIX_STATE_UNINITIALIZED) {
395 return -EINVAL;
396 }
397
398 pDownmixer = (downmix_object_t*) &pDwmModule->context;
399
400 ALOGV("Downmix_Command command %u cmdSize %u", cmdCode, cmdSize);
401
402 switch (cmdCode) {
403 case EFFECT_CMD_INIT:
404 if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
405 return -EINVAL;
406 }
407 *(int *) pReplyData = Downmix_Init(pDwmModule);
408 break;
409
410 case EFFECT_CMD_SET_CONFIG:
411 if (pCmdData == NULL || cmdSize != sizeof(effect_config_t)
412 || pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
413 return -EINVAL;
414 }
415 *(int *) pReplyData = Downmix_Configure(pDwmModule,
416 (effect_config_t *)pCmdData, false);
417 break;
418
419 case EFFECT_CMD_RESET:
420 Downmix_Reset(pDownmixer, false);
421 break;
422
423 case EFFECT_CMD_GET_PARAM: {
424 ALOGV("Downmix_Command EFFECT_CMD_GET_PARAM pCmdData %p, *replySize %u, pReplyData: %p",
425 pCmdData, *replySize, pReplyData);
426 if (pCmdData == NULL || cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t)) ||
427 pReplyData == NULL || replySize == NULL ||
428 *replySize < (int) sizeof(effect_param_t) + 2 * sizeof(int32_t)) {
429 return -EINVAL;
430 }
431 effect_param_t *rep = (effect_param_t *) pReplyData;
432 memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + sizeof(int32_t));
433 ALOGV("Downmix_Command EFFECT_CMD_GET_PARAM param %d, replySize %u",
434 *(int32_t *)rep->data, rep->vsize);
435 rep->status = Downmix_getParameter(pDownmixer, *(int32_t *)rep->data, &rep->vsize,
436 rep->data + sizeof(int32_t));
437 *replySize = sizeof(effect_param_t) + sizeof(int32_t) + rep->vsize;
438 break;
439 }
440 case EFFECT_CMD_SET_PARAM: {
441 ALOGV("Downmix_Command EFFECT_CMD_SET_PARAM cmdSize %d pCmdData %p, *replySize %u"
442 ", pReplyData %p", cmdSize, pCmdData, *replySize, pReplyData);
443 if (pCmdData == NULL || (cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t)))
444 || pReplyData == NULL || replySize == NULL || *replySize != (int)sizeof(int32_t)) {
445 return -EINVAL;
446 }
447 effect_param_t *cmd = (effect_param_t *) pCmdData;
448 if (cmd->psize != sizeof(int32_t)) {
449 android_errorWriteLog(0x534e4554, "63662938");
450 return -EINVAL;
451 }
452 *(int *)pReplyData = Downmix_setParameter(pDownmixer, *(int32_t *)cmd->data,
453 cmd->vsize, cmd->data + sizeof(int32_t));
454 break;
455 }
456
457 case EFFECT_CMD_SET_PARAM_DEFERRED:
458 //FIXME implement
459 ALOGW("Downmix_Command command EFFECT_CMD_SET_PARAM_DEFERRED not supported, FIXME");
460 break;
461
462 case EFFECT_CMD_SET_PARAM_COMMIT:
463 //FIXME implement
464 ALOGW("Downmix_Command command EFFECT_CMD_SET_PARAM_COMMIT not supported, FIXME");
465 break;
466
467 case EFFECT_CMD_ENABLE:
468 if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
469 return -EINVAL;
470 }
471 if (pDownmixer->state != DOWNMIX_STATE_INITIALIZED) {
472 return -ENOSYS;
473 }
474 pDownmixer->state = DOWNMIX_STATE_ACTIVE;
475 ALOGV("EFFECT_CMD_ENABLE() OK");
476 *(int *)pReplyData = 0;
477 break;
478
479 case EFFECT_CMD_DISABLE:
480 if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
481 return -EINVAL;
482 }
483 if (pDownmixer->state != DOWNMIX_STATE_ACTIVE) {
484 return -ENOSYS;
485 }
486 pDownmixer->state = DOWNMIX_STATE_INITIALIZED;
487 ALOGV("EFFECT_CMD_DISABLE() OK");
488 *(int *)pReplyData = 0;
489 break;
490
491 case EFFECT_CMD_SET_DEVICE:
492 if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) {
493 return -EINVAL;
494 }
495 // FIXME change type if playing on headset vs speaker
496 ALOGV("Downmix_Command EFFECT_CMD_SET_DEVICE: %#x", *(uint32_t *)pCmdData);
497 break;
498
499 case EFFECT_CMD_SET_VOLUME: {
500 // audio output is always stereo => 2 channel volumes
501 if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t) * 2) {
502 return -EINVAL;
503 }
504 // FIXME change volume
505 ALOGW("Downmix_Command command EFFECT_CMD_SET_VOLUME not supported, FIXME");
506 float left = (float)(*(uint32_t *)pCmdData) / (1 << 24);
507 float right = (float)(*((uint32_t *)pCmdData + 1)) / (1 << 24);
508 ALOGV("Downmix_Command EFFECT_CMD_SET_VOLUME: left %f, right %f ", left, right);
509 break;
510 }
511
512 case EFFECT_CMD_SET_AUDIO_MODE:
513 if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) {
514 return -EINVAL;
515 }
516 ALOGV("Downmix_Command EFFECT_CMD_SET_AUDIO_MODE: %u", *(uint32_t *)pCmdData);
517 break;
518
519 case EFFECT_CMD_SET_CONFIG_REVERSE:
520 case EFFECT_CMD_SET_INPUT_DEVICE:
521 // these commands are ignored by a downmix effect
522 break;
523
524 default:
525 ALOGW("Downmix_Command invalid command %u", cmdCode);
526 return -EINVAL;
527 }
528
529 return 0;
530 }
531
Downmix_GetDescriptor(effect_handle_t self,effect_descriptor_t * pDescriptor)532 static int32_t Downmix_GetDescriptor(effect_handle_t self, effect_descriptor_t *pDescriptor)
533 {
534 downmix_module_t *pDwnmxModule = (downmix_module_t *) self;
535
536 if (pDwnmxModule == NULL ||
537 pDwnmxModule->context.state == DOWNMIX_STATE_UNINITIALIZED) {
538 return -EINVAL;
539 }
540
541 memcpy(pDescriptor, &gDownmixDescriptor, sizeof(effect_descriptor_t));
542
543 return 0;
544 }
545
546
547 /*----------------------------------------------------------------------------
548 * Downmix internal functions
549 *--------------------------------------------------------------------------*/
550
551 /*----------------------------------------------------------------------------
552 * Downmix_Init()
553 *----------------------------------------------------------------------------
554 * Purpose:
555 * Initialize downmix context and apply default parameters
556 *
557 * Inputs:
558 * pDwmModule pointer to downmix effect module
559 *
560 * Outputs:
561 *
562 * Returns:
563 * 0 indicates success
564 *
565 * Side Effects:
566 * updates:
567 * pDwmModule->context.type
568 * pDwmModule->context.apply_volume_correction
569 * pDwmModule->config.inputCfg
570 * pDwmModule->config.outputCfg
571 * pDwmModule->config.inputCfg.samplingRate
572 * pDwmModule->config.outputCfg.samplingRate
573 * pDwmModule->context.state
574 * doesn't set:
575 * pDwmModule->itfe
576 *
577 *----------------------------------------------------------------------------
578 */
579
Downmix_Init(downmix_module_t * pDwmModule)580 static int Downmix_Init(downmix_module_t *pDwmModule) {
581
582 ALOGV("Downmix_Init module %p", pDwmModule);
583 int ret = 0;
584
585 memset(&pDwmModule->context, 0, sizeof(downmix_object_t));
586
587 pDwmModule->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
588 pDwmModule->config.inputCfg.format = AUDIO_FORMAT_PCM_FLOAT;
589 pDwmModule->config.inputCfg.channels = AUDIO_CHANNEL_OUT_7POINT1;
590 pDwmModule->config.inputCfg.bufferProvider.getBuffer = NULL;
591 pDwmModule->config.inputCfg.bufferProvider.releaseBuffer = NULL;
592 pDwmModule->config.inputCfg.bufferProvider.cookie = NULL;
593 pDwmModule->config.inputCfg.mask = EFFECT_CONFIG_ALL;
594
595 pDwmModule->config.inputCfg.samplingRate = 44100;
596 pDwmModule->config.outputCfg.samplingRate = pDwmModule->config.inputCfg.samplingRate;
597
598 // set a default value for the access mode, but should be overwritten by caller
599 pDwmModule->config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
600 pDwmModule->config.outputCfg.format = AUDIO_FORMAT_PCM_FLOAT;
601 pDwmModule->config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
602 pDwmModule->config.outputCfg.bufferProvider.getBuffer = NULL;
603 pDwmModule->config.outputCfg.bufferProvider.releaseBuffer = NULL;
604 pDwmModule->config.outputCfg.bufferProvider.cookie = NULL;
605 pDwmModule->config.outputCfg.mask = EFFECT_CONFIG_ALL;
606
607 ret = Downmix_Configure(pDwmModule, &pDwmModule->config, true);
608 if (ret != 0) {
609 ALOGV("Downmix_Init error %d on module %p", ret, pDwmModule);
610 } else {
611 pDwmModule->context.state = DOWNMIX_STATE_INITIALIZED;
612 }
613
614 return ret;
615 }
616
617
618 /*----------------------------------------------------------------------------
619 * Downmix_Configure()
620 *----------------------------------------------------------------------------
621 * Purpose:
622 * Set input and output audio configuration.
623 *
624 * Inputs:
625 * pDwmModule pointer to downmix effect module
626 * pConfig pointer to effect_config_t structure containing input
627 * and output audio parameters configuration
628 * init true if called from init function
629 *
630 * Outputs:
631 *
632 * Returns:
633 * 0 indicates success
634 *
635 * Side Effects:
636 *
637 *----------------------------------------------------------------------------
638 */
639
Downmix_Configure(downmix_module_t * pDwmModule,effect_config_t * pConfig,bool init)640 static int Downmix_Configure(downmix_module_t *pDwmModule, effect_config_t *pConfig, bool init) {
641
642 downmix_object_t *pDownmixer = &pDwmModule->context;
643
644 // Check configuration compatibility with build options, and effect capabilities
645 if (pConfig->inputCfg.samplingRate != pConfig->outputCfg.samplingRate
646 || pConfig->outputCfg.channels != AUDIO_CHANNEL_OUT_STEREO
647 || pConfig->inputCfg.format != AUDIO_FORMAT_PCM_FLOAT
648 || pConfig->outputCfg.format != AUDIO_FORMAT_PCM_FLOAT) {
649 ALOGE("Downmix_Configure error: invalid config");
650 return -EINVAL;
651 }
652 // when configuring the effect, do not allow a blank or unsupported channel mask
653 if (!Downmix_validChannelMask(pConfig->inputCfg.channels)) {
654 ALOGE("Downmix_Configure error: input channel mask(0x%x) not supported",
655 pConfig->inputCfg.channels);
656 return -EINVAL;
657 }
658
659 if (&pDwmModule->config != pConfig) {
660 memcpy(&pDwmModule->config, pConfig, sizeof(effect_config_t));
661 }
662
663 if (init) {
664 pDownmixer->type = DOWNMIX_TYPE_FOLD;
665 pDownmixer->apply_volume_correction = false;
666 pDownmixer->input_channel_count = 8; // matches default input of AUDIO_CHANNEL_OUT_7POINT1
667 } else {
668 pDownmixer->input_channel_count =
669 audio_channel_count_from_out_mask(pConfig->inputCfg.channels);
670 }
671
672 Downmix_Reset(pDownmixer, init);
673
674 return 0;
675 }
676
677
678 /*----------------------------------------------------------------------------
679 * Downmix_Reset()
680 *----------------------------------------------------------------------------
681 * Purpose:
682 * Reset internal states.
683 *
684 * Inputs:
685 * pDownmixer pointer to downmix context
686 * init true if called from init function
687 *
688 * Outputs:
689 *
690 * Returns:
691 * 0 indicates success
692 *
693 * Side Effects:
694 *
695 *----------------------------------------------------------------------------
696 */
697
Downmix_Reset(downmix_object_t *,bool)698 static int Downmix_Reset(downmix_object_t* /* pDownmixer */, bool /* init */) {
699 // nothing to do here
700 return 0;
701 }
702
703
704 /*----------------------------------------------------------------------------
705 * Downmix_setParameter()
706 *----------------------------------------------------------------------------
707 * Purpose:
708 * Set a Downmix parameter
709 *
710 * Inputs:
711 * pDownmixer handle to instance data
712 * param parameter
713 * pValue pointer to parameter value
714 * size value size
715 *
716 * Outputs:
717 *
718 * Returns:
719 * 0 indicates success
720 *
721 * Side Effects:
722 *
723 *----------------------------------------------------------------------------
724 */
Downmix_setParameter(downmix_object_t * pDownmixer,int32_t param,uint32_t size,void * pValue)725 static int Downmix_setParameter(
726 downmix_object_t *pDownmixer, int32_t param, uint32_t size, void *pValue) {
727
728 int16_t value16;
729 ALOGV("Downmix_setParameter, context %p, param %d, value16 %d, value32 %d",
730 pDownmixer, param, *(int16_t *)pValue, *(int32_t *)pValue);
731
732 switch (param) {
733
734 case DOWNMIX_PARAM_TYPE:
735 if (size != sizeof(downmix_type_t)) {
736 ALOGE("Downmix_setParameter(DOWNMIX_PARAM_TYPE) invalid size %u, should be %zu",
737 size, sizeof(downmix_type_t));
738 return -EINVAL;
739 }
740 value16 = *(int16_t *)pValue;
741 ALOGV("set DOWNMIX_PARAM_TYPE, type %d", value16);
742 if (!((value16 > DOWNMIX_TYPE_INVALID) && (value16 <= DOWNMIX_TYPE_LAST))) {
743 ALOGE("Downmix_setParameter invalid DOWNMIX_PARAM_TYPE value %d", value16);
744 return -EINVAL;
745 } else {
746 pDownmixer->type = (downmix_type_t) value16;
747 break;
748
749 default:
750 ALOGE("Downmix_setParameter unknown parameter %d", param);
751 return -EINVAL;
752 }
753 }
754
755 return 0;
756 } /* end Downmix_setParameter */
757
758 /*----------------------------------------------------------------------------
759 * Downmix_getParameter()
760 *----------------------------------------------------------------------------
761 * Purpose:
762 * Get a Downmix parameter
763 *
764 * Inputs:
765 * pDownmixer handle to instance data
766 * param parameter
767 * pValue pointer to variable to hold retrieved value
768 * pSize pointer to value size: maximum size as input
769 *
770 * Outputs:
771 * *pValue updated with parameter value
772 * *pSize updated with actual value size
773 *
774 * Returns:
775 * 0 indicates success
776 *
777 * Side Effects:
778 *
779 *----------------------------------------------------------------------------
780 */
Downmix_getParameter(downmix_object_t * pDownmixer,int32_t param,uint32_t * pSize,void * pValue)781 static int Downmix_getParameter(
782 downmix_object_t *pDownmixer, int32_t param, uint32_t *pSize, void *pValue) {
783 int16_t *pValue16;
784
785 switch (param) {
786
787 case DOWNMIX_PARAM_TYPE:
788 if (*pSize < sizeof(int16_t)) {
789 ALOGE("Downmix_getParameter invalid parameter size %u for DOWNMIX_PARAM_TYPE", *pSize);
790 return -EINVAL;
791 }
792 pValue16 = (int16_t *)pValue;
793 *pValue16 = (int16_t) pDownmixer->type;
794 *pSize = sizeof(int16_t);
795 ALOGV("Downmix_getParameter DOWNMIX_PARAM_TYPE is %d", *pValue16);
796 break;
797
798 default:
799 ALOGE("Downmix_getParameter unknown parameter %d", param);
800 return -EINVAL;
801 }
802
803 return 0;
804 } /* end Downmix_getParameter */
805
806