• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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<AUDIO_CHANNEL_OUT_STEREO> 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         delete 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     pDwmModule->context = downmix_object_t{};  // zero initialize (contains class with vtable).
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