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#include <inttypes.h>
21#include <stdlib.h>
22#include <string.h>
23#include <stdbool.h>
24#include "EffectDownmix.h"
25
26// Do not submit with DOWNMIX_TEST_CHANNEL_INDEX defined, strictly for testing
27//#define DOWNMIX_TEST_CHANNEL_INDEX 0
28// Do not submit with DOWNMIX_ALWAYS_USE_GENERIC_DOWNMIXER defined, strictly for testing
29//#define DOWNMIX_ALWAYS_USE_GENERIC_DOWNMIXER 0
30
31#define MINUS_3_DB_IN_Q19_12 2896 // -3dB = 0.707 * 2^12 = 2896
32
33// subset of possible audio_channel_mask_t values, and AUDIO_CHANNEL_OUT_* renamed to CHANNEL_MASK_*
34typedef enum {
35    CHANNEL_MASK_QUAD_BACK = AUDIO_CHANNEL_OUT_QUAD_BACK,
36    CHANNEL_MASK_QUAD_SIDE = AUDIO_CHANNEL_OUT_QUAD_SIDE,
37    CHANNEL_MASK_5POINT1_BACK = AUDIO_CHANNEL_OUT_5POINT1_BACK,
38    CHANNEL_MASK_5POINT1_SIDE = AUDIO_CHANNEL_OUT_5POINT1_SIDE,
39    CHANNEL_MASK_7POINT1 = AUDIO_CHANNEL_OUT_7POINT1,
40} downmix_input_channel_mask_t;
41
42// effect_handle_t interface implementation for downmix effect
43const struct effect_interface_s gDownmixInterface = {
44        Downmix_Process,
45        Downmix_Command,
46        Downmix_GetDescriptor,
47        NULL /* no process_reverse function, no reference stream needed */
48};
49
50// This is the only symbol that needs to be exported
51__attribute__ ((visibility ("default")))
52audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
53    .tag = AUDIO_EFFECT_LIBRARY_TAG,
54    .version = EFFECT_LIBRARY_API_VERSION,
55    .name = "Downmix Library",
56    .implementor = "The Android Open Source Project",
57    .create_effect = DownmixLib_Create,
58    .release_effect = DownmixLib_Release,
59    .get_descriptor = DownmixLib_GetDescriptor,
60};
61
62
63// AOSP insert downmix UUID: 93f04452-e4fe-41cc-91f9-e475b6d1d69f
64static const effect_descriptor_t gDownmixDescriptor = {
65        EFFECT_UIID_DOWNMIX__, //type
66        {0x93f04452, 0xe4fe, 0x41cc, 0x91f9, {0xe4, 0x75, 0xb6, 0xd1, 0xd6, 0x9f}}, // uuid
67        EFFECT_CONTROL_API_VERSION,
68        EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST,
69        0, //FIXME what value should be reported? // cpu load
70        0, //FIXME what value should be reported? // memory usage
71        "Multichannel Downmix To Stereo", // human readable effect name
72        "The Android Open Source Project" // human readable effect implementor name
73};
74
75// gDescriptors contains pointers to all defined effect descriptor in this library
76static const effect_descriptor_t * const gDescriptors[] = {
77        &gDownmixDescriptor
78};
79
80// number of effects in this library
81const int kNbEffects = sizeof(gDescriptors) / sizeof(const effect_descriptor_t *);
82
83
84/*----------------------------------------------------------------------------
85 * Test code
86 *--------------------------------------------------------------------------*/
87#ifdef DOWNMIX_TEST_CHANNEL_INDEX
88// strictly for testing, logs the indices of the channels for a given mask,
89// uses the same code as Downmix_foldGeneric()
90void Downmix_testIndexComputation(uint32_t mask) {
91    ALOGI("Testing index computation for 0x%" PRIx32 ":", mask);
92    // check against unsupported channels
93    if (mask & kUnsupported) {
94        ALOGE("Unsupported channels (top or front left/right of center)");
95        return;
96    }
97    // verify has FL/FR
98    if ((mask & AUDIO_CHANNEL_OUT_STEREO) != AUDIO_CHANNEL_OUT_STEREO) {
99        ALOGE("Front channels must be present");
100        return;
101    }
102    // verify uses SIDE as a pair (ok if not using SIDE at all)
103    bool hasSides = false;
104    if ((mask & kSides) != 0) {
105        if ((mask & kSides) != kSides) {
106            ALOGE("Side channels must be used as a pair");
107            return;
108        }
109        hasSides = true;
110    }
111    // verify uses BACK as a pair (ok if not using BACK at all)
112    bool hasBacks = false;
113    if ((mask & kBacks) != 0) {
114        if ((mask & kBacks) != kBacks) {
115            ALOGE("Back channels must be used as a pair");
116            return;
117        }
118        hasBacks = true;
119    }
120
121    const int numChan = audio_channel_count_from_out_mask(mask);
122    const bool hasFC = ((mask & AUDIO_CHANNEL_OUT_FRONT_CENTER) == AUDIO_CHANNEL_OUT_FRONT_CENTER);
123    const bool hasLFE =
124            ((mask & AUDIO_CHANNEL_OUT_LOW_FREQUENCY) == AUDIO_CHANNEL_OUT_LOW_FREQUENCY);
125    const bool hasBC = ((mask & AUDIO_CHANNEL_OUT_BACK_CENTER) == AUDIO_CHANNEL_OUT_BACK_CENTER);
126    // compute at what index each channel is: samples will be in the following order:
127    //   FL FR FC LFE BL BR BC SL SR
128    // when a channel is not present, its index is set to the same as the index of the preceding
129    // channel
130    const int indexFC  = hasFC    ? 2            : 1;        // front center
131    const int indexLFE = hasLFE   ? indexFC + 1  : indexFC;  // low frequency
132    const int indexBL  = hasBacks ? indexLFE + 1 : indexLFE; // back left
133    const int indexBR  = hasBacks ? indexBL + 1  : indexBL;  // back right
134    const int indexBC  = hasBC    ? indexBR + 1  : indexBR;  // back center
135    const int indexSL  = hasSides ? indexBC + 1  : indexBC;  // side left
136    const int indexSR  = hasSides ? indexSL + 1  : indexSL;  // side right
137
138    ALOGI("  FL FR FC LFE BL BR BC SL SR");
139    ALOGI("   %d  %d  %d   %d  %d  %d  %d  %d  %d",
140            0, 1, indexFC, indexLFE, indexBL, indexBR, indexBC, indexSL, indexSR);
141}
142#endif
143
144
145/*----------------------------------------------------------------------------
146 * Effect API implementation
147 *--------------------------------------------------------------------------*/
148
149/*--- Effect Library Interface Implementation ---*/
150
151int32_t DownmixLib_Create(const effect_uuid_t *uuid,
152        int32_t sessionId __unused,
153        int32_t ioId __unused,
154        effect_handle_t *pHandle) {
155    int ret;
156    int i;
157    downmix_module_t *module;
158    const effect_descriptor_t *desc;
159
160    ALOGV("DownmixLib_Create()");
161
162#ifdef DOWNMIX_TEST_CHANNEL_INDEX
163    // should work (won't log an error)
164    ALOGI("DOWNMIX_TEST_CHANNEL_INDEX: should work:");
165    Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT |
166                    AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_BACK_CENTER);
167    Downmix_testIndexComputation(CHANNEL_MASK_QUAD_SIDE | CHANNEL_MASK_QUAD_BACK);
168    Downmix_testIndexComputation(CHANNEL_MASK_5POINT1_SIDE | AUDIO_CHANNEL_OUT_BACK_CENTER);
169    Downmix_testIndexComputation(CHANNEL_MASK_5POINT1_BACK | AUDIO_CHANNEL_OUT_BACK_CENTER);
170    // shouldn't work (will log an error, won't display channel indices)
171    ALOGI("DOWNMIX_TEST_CHANNEL_INDEX: should NOT work:");
172    Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT |
173                        AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_BACK_LEFT);
174    Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT |
175                            AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_SIDE_LEFT);
176    Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT |
177                        AUDIO_CHANNEL_OUT_BACK_LEFT | AUDIO_CHANNEL_OUT_BACK_RIGHT);
178    Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT |
179                            AUDIO_CHANNEL_OUT_SIDE_LEFT | AUDIO_CHANNEL_OUT_SIDE_RIGHT);
180#endif
181
182    if (pHandle == NULL || uuid == NULL) {
183        return -EINVAL;
184    }
185
186    for (i = 0 ; i < kNbEffects ; i++) {
187        desc = gDescriptors[i];
188        if (memcmp(uuid, &desc->uuid, sizeof(effect_uuid_t)) == 0) {
189            break;
190        }
191    }
192
193    if (i == kNbEffects) {
194        return -ENOENT;
195    }
196
197    module = malloc(sizeof(downmix_module_t));
198
199    module->itfe = &gDownmixInterface;
200
201    module->context.state = DOWNMIX_STATE_UNINITIALIZED;
202
203    ret = Downmix_Init(module);
204    if (ret < 0) {
205        ALOGW("DownmixLib_Create() init failed");
206        free(module);
207        return ret;
208    }
209
210    *pHandle = (effect_handle_t) module;
211
212    ALOGV("DownmixLib_Create() %p , size %zu", module, sizeof(downmix_module_t));
213
214    return 0;
215}
216
217
218int32_t DownmixLib_Release(effect_handle_t handle) {
219    downmix_module_t *pDwmModule = (downmix_module_t *)handle;
220
221    ALOGV("DownmixLib_Release() %p", handle);
222    if (handle == NULL) {
223        return -EINVAL;
224    }
225
226    pDwmModule->context.state = DOWNMIX_STATE_UNINITIALIZED;
227
228    free(pDwmModule);
229    return 0;
230}
231
232
233int32_t DownmixLib_GetDescriptor(const effect_uuid_t *uuid, effect_descriptor_t *pDescriptor) {
234    ALOGV("DownmixLib_GetDescriptor()");
235    int i;
236
237    if (pDescriptor == NULL || uuid == NULL){
238        ALOGE("DownmixLib_Create() called with NULL pointer");
239        return -EINVAL;
240    }
241    ALOGV("DownmixLib_GetDescriptor() nb effects=%d", kNbEffects);
242    for (i = 0; i < kNbEffects; i++) {
243        ALOGV("DownmixLib_GetDescriptor() i=%d", i);
244        if (memcmp(uuid, &gDescriptors[i]->uuid, sizeof(effect_uuid_t)) == 0) {
245            memcpy(pDescriptor, gDescriptors[i], sizeof(effect_descriptor_t));
246            ALOGV("EffectGetDescriptor - UUID matched downmix type %d, UUID = %" PRIx32,
247                 i, gDescriptors[i]->uuid.timeLow);
248            return 0;
249        }
250    }
251
252    return -EINVAL;
253}
254
255
256/*--- Effect Control Interface Implementation ---*/
257
258static int Downmix_Process(effect_handle_t self,
259        audio_buffer_t *inBuffer, audio_buffer_t *outBuffer) {
260
261    downmix_object_t *pDownmixer;
262    int16_t *pSrc, *pDst;
263    downmix_module_t *pDwmModule = (downmix_module_t *)self;
264
265    if (pDwmModule == NULL) {
266        return -EINVAL;
267    }
268
269    if (inBuffer == NULL || inBuffer->raw == NULL ||
270        outBuffer == NULL || outBuffer->raw == NULL ||
271        inBuffer->frameCount != outBuffer->frameCount) {
272        return -EINVAL;
273    }
274
275    pDownmixer = (downmix_object_t*) &pDwmModule->context;
276
277    if (pDownmixer->state == DOWNMIX_STATE_UNINITIALIZED) {
278        ALOGE("Downmix_Process error: trying to use an uninitialized downmixer");
279        return -EINVAL;
280    } else if (pDownmixer->state == DOWNMIX_STATE_INITIALIZED) {
281        ALOGE("Downmix_Process error: trying to use a non-configured downmixer");
282        return -ENODATA;
283    }
284
285    pSrc = inBuffer->s16;
286    pDst = outBuffer->s16;
287    size_t numFrames = outBuffer->frameCount;
288
289    const bool accumulate =
290            (pDwmModule->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE);
291    const uint32_t downmixInputChannelMask = pDwmModule->config.inputCfg.channels;
292
293    switch(pDownmixer->type) {
294
295      case DOWNMIX_TYPE_STRIP:
296          if (accumulate) {
297              while (numFrames) {
298                  pDst[0] = clamp16(pDst[0] + pSrc[0]);
299                  pDst[1] = clamp16(pDst[1] + pSrc[1]);
300                  pSrc += pDownmixer->input_channel_count;
301                  pDst += 2;
302                  numFrames--;
303              }
304          } else {
305              while (numFrames) {
306                  pDst[0] = pSrc[0];
307                  pDst[1] = pSrc[1];
308                  pSrc += pDownmixer->input_channel_count;
309                  pDst += 2;
310                  numFrames--;
311              }
312          }
313          break;
314
315      case DOWNMIX_TYPE_FOLD:
316#ifdef DOWNMIX_ALWAYS_USE_GENERIC_DOWNMIXER
317          // bypass the optimized downmix routines for the common formats
318          if (!Downmix_foldGeneric(
319                  downmixInputChannelMask, pSrc, pDst, numFrames, accumulate)) {
320              ALOGE("Multichannel configuration 0x%" PRIx32 " is not supported", downmixInputChannelMask);
321              return -EINVAL;
322          }
323          break;
324#endif
325        // optimize for the common formats
326        switch((downmix_input_channel_mask_t)downmixInputChannelMask) {
327        case CHANNEL_MASK_QUAD_BACK:
328        case CHANNEL_MASK_QUAD_SIDE:
329            Downmix_foldFromQuad(pSrc, pDst, numFrames, accumulate);
330            break;
331        case CHANNEL_MASK_5POINT1_BACK:
332        case CHANNEL_MASK_5POINT1_SIDE:
333            Downmix_foldFrom5Point1(pSrc, pDst, numFrames, accumulate);
334            break;
335        case CHANNEL_MASK_7POINT1:
336            Downmix_foldFrom7Point1(pSrc, pDst, numFrames, accumulate);
337            break;
338        default:
339            if (!Downmix_foldGeneric(
340                    downmixInputChannelMask, pSrc, pDst, numFrames, accumulate)) {
341                ALOGE("Multichannel configuration 0x%" PRIx32 " is not supported", downmixInputChannelMask);
342                return -EINVAL;
343            }
344            break;
345        }
346        break;
347
348      default:
349        return -EINVAL;
350    }
351
352    return 0;
353}
354
355
356static int Downmix_Command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
357        void *pCmdData, uint32_t *replySize, void *pReplyData) {
358
359    downmix_module_t *pDwmModule = (downmix_module_t *) self;
360    downmix_object_t *pDownmixer;
361    int retsize;
362
363    if (pDwmModule == NULL || pDwmModule->context.state == DOWNMIX_STATE_UNINITIALIZED) {
364        return -EINVAL;
365    }
366
367    pDownmixer = (downmix_object_t*) &pDwmModule->context;
368
369    ALOGV("Downmix_Command command %" PRIu32 " cmdSize %" PRIu32, cmdCode, cmdSize);
370
371    switch (cmdCode) {
372    case EFFECT_CMD_INIT:
373        if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
374            return -EINVAL;
375        }
376        *(int *) pReplyData = Downmix_Init(pDwmModule);
377        break;
378
379    case EFFECT_CMD_SET_CONFIG:
380        if (pCmdData == NULL || cmdSize != sizeof(effect_config_t)
381                || pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
382            return -EINVAL;
383        }
384        *(int *) pReplyData = Downmix_Configure(pDwmModule,
385                (effect_config_t *)pCmdData, false);
386        break;
387
388    case EFFECT_CMD_RESET:
389        Downmix_Reset(pDownmixer, false);
390        break;
391
392    case EFFECT_CMD_GET_PARAM:
393        ALOGV("Downmix_Command EFFECT_CMD_GET_PARAM pCmdData %p, *replySize %" PRIu32 ", pReplyData: %p",
394                pCmdData, *replySize, pReplyData);
395        if (pCmdData == NULL || cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t)) ||
396                pReplyData == NULL || replySize == NULL ||
397                *replySize < (int) sizeof(effect_param_t) + 2 * sizeof(int32_t)) {
398            return -EINVAL;
399        }
400        effect_param_t *rep = (effect_param_t *) pReplyData;
401        memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + sizeof(int32_t));
402        ALOGV("Downmix_Command EFFECT_CMD_GET_PARAM param %" PRId32 ", replySize %" PRIu32,
403                *(int32_t *)rep->data, rep->vsize);
404        rep->status = Downmix_getParameter(pDownmixer, *(int32_t *)rep->data, &rep->vsize,
405                rep->data + sizeof(int32_t));
406        *replySize = sizeof(effect_param_t) + sizeof(int32_t) + rep->vsize;
407        break;
408
409    case EFFECT_CMD_SET_PARAM:
410        ALOGV("Downmix_Command EFFECT_CMD_SET_PARAM cmdSize %d pCmdData %p, *replySize %" PRIu32
411                ", pReplyData %p", cmdSize, pCmdData, *replySize, pReplyData);
412        if (pCmdData == NULL || (cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t)))
413                || pReplyData == NULL || replySize == NULL || *replySize != (int)sizeof(int32_t)) {
414            return -EINVAL;
415        }
416        effect_param_t *cmd = (effect_param_t *) pCmdData;
417        *(int *)pReplyData = Downmix_setParameter(pDownmixer, *(int32_t *)cmd->data,
418                cmd->vsize, cmd->data + sizeof(int32_t));
419        break;
420
421    case EFFECT_CMD_SET_PARAM_DEFERRED:
422        //FIXME implement
423        ALOGW("Downmix_Command command EFFECT_CMD_SET_PARAM_DEFERRED not supported, FIXME");
424        break;
425
426    case EFFECT_CMD_SET_PARAM_COMMIT:
427        //FIXME implement
428        ALOGW("Downmix_Command command EFFECT_CMD_SET_PARAM_COMMIT not supported, FIXME");
429        break;
430
431    case EFFECT_CMD_ENABLE:
432        if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
433            return -EINVAL;
434        }
435        if (pDownmixer->state != DOWNMIX_STATE_INITIALIZED) {
436            return -ENOSYS;
437        }
438        pDownmixer->state = DOWNMIX_STATE_ACTIVE;
439        ALOGV("EFFECT_CMD_ENABLE() OK");
440        *(int *)pReplyData = 0;
441        break;
442
443    case EFFECT_CMD_DISABLE:
444        if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
445            return -EINVAL;
446        }
447        if (pDownmixer->state != DOWNMIX_STATE_ACTIVE) {
448            return -ENOSYS;
449        }
450        pDownmixer->state = DOWNMIX_STATE_INITIALIZED;
451        ALOGV("EFFECT_CMD_DISABLE() OK");
452        *(int *)pReplyData = 0;
453        break;
454
455    case EFFECT_CMD_SET_DEVICE:
456        if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) {
457            return -EINVAL;
458        }
459        // FIXME change type if playing on headset vs speaker
460        ALOGV("Downmix_Command EFFECT_CMD_SET_DEVICE: 0x%08" PRIx32, *(uint32_t *)pCmdData);
461        break;
462
463    case EFFECT_CMD_SET_VOLUME: {
464        // audio output is always stereo => 2 channel volumes
465        if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t) * 2) {
466            return -EINVAL;
467        }
468        // FIXME change volume
469        ALOGW("Downmix_Command command EFFECT_CMD_SET_VOLUME not supported, FIXME");
470        float left = (float)(*(uint32_t *)pCmdData) / (1 << 24);
471        float right = (float)(*((uint32_t *)pCmdData + 1)) / (1 << 24);
472        ALOGV("Downmix_Command EFFECT_CMD_SET_VOLUME: left %f, right %f ", left, right);
473        break;
474    }
475
476    case EFFECT_CMD_SET_AUDIO_MODE:
477        if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) {
478            return -EINVAL;
479        }
480        ALOGV("Downmix_Command EFFECT_CMD_SET_AUDIO_MODE: %" PRIu32, *(uint32_t *)pCmdData);
481        break;
482
483    case EFFECT_CMD_SET_CONFIG_REVERSE:
484    case EFFECT_CMD_SET_INPUT_DEVICE:
485        // these commands are ignored by a downmix effect
486        break;
487
488    default:
489        ALOGW("Downmix_Command invalid command %" PRIu32, cmdCode);
490        return -EINVAL;
491    }
492
493    return 0;
494}
495
496
497int Downmix_GetDescriptor(effect_handle_t self, effect_descriptor_t *pDescriptor)
498{
499    downmix_module_t *pDwnmxModule = (downmix_module_t *) self;
500
501    if (pDwnmxModule == NULL ||
502            pDwnmxModule->context.state == DOWNMIX_STATE_UNINITIALIZED) {
503        return -EINVAL;
504    }
505
506    memcpy(pDescriptor, &gDownmixDescriptor, sizeof(effect_descriptor_t));
507
508    return 0;
509}
510
511
512/*----------------------------------------------------------------------------
513 * Downmix internal functions
514 *--------------------------------------------------------------------------*/
515
516/*----------------------------------------------------------------------------
517 * Downmix_Init()
518 *----------------------------------------------------------------------------
519 * Purpose:
520 * Initialize downmix context and apply default parameters
521 *
522 * Inputs:
523 *  pDwmModule    pointer to downmix effect module
524 *
525 * Outputs:
526 *
527 * Returns:
528 *  0             indicates success
529 *
530 * Side Effects:
531 *  updates:
532 *           pDwmModule->context.type
533 *           pDwmModule->context.apply_volume_correction
534 *           pDwmModule->config.inputCfg
535 *           pDwmModule->config.outputCfg
536 *           pDwmModule->config.inputCfg.samplingRate
537 *           pDwmModule->config.outputCfg.samplingRate
538 *           pDwmModule->context.state
539 *  doesn't set:
540 *           pDwmModule->itfe
541 *
542 *----------------------------------------------------------------------------
543 */
544
545int Downmix_Init(downmix_module_t *pDwmModule) {
546
547    ALOGV("Downmix_Init module %p", pDwmModule);
548    int ret = 0;
549
550    memset(&pDwmModule->context, 0, sizeof(downmix_object_t));
551
552    pDwmModule->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
553    pDwmModule->config.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
554    pDwmModule->config.inputCfg.channels = AUDIO_CHANNEL_OUT_7POINT1;
555    pDwmModule->config.inputCfg.bufferProvider.getBuffer = NULL;
556    pDwmModule->config.inputCfg.bufferProvider.releaseBuffer = NULL;
557    pDwmModule->config.inputCfg.bufferProvider.cookie = NULL;
558    pDwmModule->config.inputCfg.mask = EFFECT_CONFIG_ALL;
559
560    pDwmModule->config.inputCfg.samplingRate = 44100;
561    pDwmModule->config.outputCfg.samplingRate = pDwmModule->config.inputCfg.samplingRate;
562
563    // set a default value for the access mode, but should be overwritten by caller
564    pDwmModule->config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
565    pDwmModule->config.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
566    pDwmModule->config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
567    pDwmModule->config.outputCfg.bufferProvider.getBuffer = NULL;
568    pDwmModule->config.outputCfg.bufferProvider.releaseBuffer = NULL;
569    pDwmModule->config.outputCfg.bufferProvider.cookie = NULL;
570    pDwmModule->config.outputCfg.mask = EFFECT_CONFIG_ALL;
571
572    ret = Downmix_Configure(pDwmModule, &pDwmModule->config, true);
573    if (ret != 0) {
574        ALOGV("Downmix_Init error %d on module %p", ret, pDwmModule);
575    } else {
576        pDwmModule->context.state = DOWNMIX_STATE_INITIALIZED;
577    }
578
579    return ret;
580}
581
582
583/*----------------------------------------------------------------------------
584 * Downmix_Configure()
585 *----------------------------------------------------------------------------
586 * Purpose:
587 *  Set input and output audio configuration.
588 *
589 * Inputs:
590 *  pDwmModule  pointer to downmix effect module
591 *  pConfig     pointer to effect_config_t structure containing input
592 *                  and output audio parameters configuration
593 *  init        true if called from init function
594 *
595 * Outputs:
596 *
597 * Returns:
598 *  0           indicates success
599 *
600 * Side Effects:
601 *
602 *----------------------------------------------------------------------------
603 */
604
605int Downmix_Configure(downmix_module_t *pDwmModule, effect_config_t *pConfig, bool init) {
606
607    downmix_object_t *pDownmixer = &pDwmModule->context;
608
609    // Check configuration compatibility with build options, and effect capabilities
610    if (pConfig->inputCfg.samplingRate != pConfig->outputCfg.samplingRate
611        || pConfig->outputCfg.channels != DOWNMIX_OUTPUT_CHANNELS
612        || pConfig->inputCfg.format != AUDIO_FORMAT_PCM_16_BIT
613        || pConfig->outputCfg.format != AUDIO_FORMAT_PCM_16_BIT) {
614        ALOGE("Downmix_Configure error: invalid config");
615        return -EINVAL;
616    }
617
618    if (&pDwmModule->config != pConfig) {
619        memcpy(&pDwmModule->config, pConfig, sizeof(effect_config_t));
620    }
621
622    if (init) {
623        pDownmixer->type = DOWNMIX_TYPE_FOLD;
624        pDownmixer->apply_volume_correction = false;
625        pDownmixer->input_channel_count = 8; // matches default input of AUDIO_CHANNEL_OUT_7POINT1
626    } else {
627        // when configuring the effect, do not allow a blank channel mask
628        if (pConfig->inputCfg.channels == 0) {
629            ALOGE("Downmix_Configure error: input channel mask can't be 0");
630            return -EINVAL;
631        }
632        pDownmixer->input_channel_count =
633                audio_channel_count_from_out_mask(pConfig->inputCfg.channels);
634    }
635
636    Downmix_Reset(pDownmixer, init);
637
638    return 0;
639}
640
641
642/*----------------------------------------------------------------------------
643 * Downmix_Reset()
644 *----------------------------------------------------------------------------
645 * Purpose:
646 *  Reset internal states.
647 *
648 * Inputs:
649 *  pDownmixer   pointer to downmix context
650 *  init         true if called from init function
651 *
652 * Outputs:
653*
654 * Returns:
655 *  0            indicates success
656 *
657 * Side Effects:
658 *
659 *----------------------------------------------------------------------------
660 */
661
662int Downmix_Reset(downmix_object_t *pDownmixer __unused, bool init __unused) {
663    // nothing to do here
664    return 0;
665}
666
667
668/*----------------------------------------------------------------------------
669 * Downmix_setParameter()
670 *----------------------------------------------------------------------------
671 * Purpose:
672 * Set a Downmix parameter
673 *
674 * Inputs:
675 *  pDownmixer    handle to instance data
676 *  param         parameter
677 *  pValue        pointer to parameter value
678 *  size          value size
679 *
680 * Outputs:
681 *
682 * Returns:
683 *  0             indicates success
684 *
685 * Side Effects:
686 *
687 *----------------------------------------------------------------------------
688 */
689int Downmix_setParameter(downmix_object_t *pDownmixer, int32_t param, uint32_t size, void *pValue) {
690
691    int16_t value16;
692    ALOGV("Downmix_setParameter, context %p, param %" PRId32 ", value16 %" PRId16 ", value32 %" PRId32,
693            pDownmixer, param, *(int16_t *)pValue, *(int32_t *)pValue);
694
695    switch (param) {
696
697      case DOWNMIX_PARAM_TYPE:
698        if (size != sizeof(downmix_type_t)) {
699            ALOGE("Downmix_setParameter(DOWNMIX_PARAM_TYPE) invalid size %" PRIu32 ", should be %zu",
700                    size, sizeof(downmix_type_t));
701            return -EINVAL;
702        }
703        value16 = *(int16_t *)pValue;
704        ALOGV("set DOWNMIX_PARAM_TYPE, type %" PRId16, value16);
705        if (!((value16 > DOWNMIX_TYPE_INVALID) && (value16 <= DOWNMIX_TYPE_LAST))) {
706            ALOGE("Downmix_setParameter invalid DOWNMIX_PARAM_TYPE value %" PRId16, value16);
707            return -EINVAL;
708        } else {
709            pDownmixer->type = (downmix_type_t) value16;
710        break;
711
712      default:
713        ALOGE("Downmix_setParameter unknown parameter %" PRId32, param);
714        return -EINVAL;
715    }
716}
717
718    return 0;
719} /* end Downmix_setParameter */
720
721
722/*----------------------------------------------------------------------------
723 * Downmix_getParameter()
724 *----------------------------------------------------------------------------
725 * Purpose:
726 * Get a Downmix parameter
727 *
728 * Inputs:
729 *  pDownmixer    handle to instance data
730 *  param         parameter
731 *  pValue        pointer to variable to hold retrieved value
732 *  pSize         pointer to value size: maximum size as input
733 *
734 * Outputs:
735 *  *pValue updated with parameter value
736 *  *pSize updated with actual value size
737 *
738 * Returns:
739 *  0             indicates success
740 *
741 * Side Effects:
742 *
743 *----------------------------------------------------------------------------
744 */
745int Downmix_getParameter(downmix_object_t *pDownmixer, int32_t param, uint32_t *pSize, void *pValue) {
746    int16_t *pValue16;
747
748    switch (param) {
749
750    case DOWNMIX_PARAM_TYPE:
751      if (*pSize < sizeof(int16_t)) {
752          ALOGE("Downmix_getParameter invalid parameter size %" PRIu32 " for DOWNMIX_PARAM_TYPE", *pSize);
753          return -EINVAL;
754      }
755      pValue16 = (int16_t *)pValue;
756      *pValue16 = (int16_t) pDownmixer->type;
757      *pSize = sizeof(int16_t);
758      ALOGV("Downmix_getParameter DOWNMIX_PARAM_TYPE is %" PRId16, *pValue16);
759      break;
760
761    default:
762      ALOGE("Downmix_getParameter unknown parameter %" PRId16, param);
763      return -EINVAL;
764    }
765
766    return 0;
767} /* end Downmix_getParameter */
768
769
770/*----------------------------------------------------------------------------
771 * Downmix_foldFromQuad()
772 *----------------------------------------------------------------------------
773 * Purpose:
774 * downmix a quad signal to stereo
775 *
776 * Inputs:
777 *  pSrc       quad audio samples to downmix
778 *  numFrames  the number of quad frames to downmix
779 *  accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
780 *               or overwrite pDst (when false)
781 *
782 * Outputs:
783 *  pDst       downmixed stereo audio samples
784 *
785 *----------------------------------------------------------------------------
786 */
787void Downmix_foldFromQuad(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
788    // sample at index 0 is FL
789    // sample at index 1 is FR
790    // sample at index 2 is RL
791    // sample at index 3 is RR
792    if (accumulate) {
793        while (numFrames) {
794            // FL + RL
795            pDst[0] = clamp16(pDst[0] + ((pSrc[0] + pSrc[2]) >> 1));
796            // FR + RR
797            pDst[1] = clamp16(pDst[1] + ((pSrc[1] + pSrc[3]) >> 1));
798            pSrc += 4;
799            pDst += 2;
800            numFrames--;
801        }
802    } else { // same code as above but without adding and clamping pDst[i] to itself
803        while (numFrames) {
804            // FL + RL
805            pDst[0] = clamp16((pSrc[0] + pSrc[2]) >> 1);
806            // FR + RR
807            pDst[1] = clamp16((pSrc[1] + pSrc[3]) >> 1);
808            pSrc += 4;
809            pDst += 2;
810            numFrames--;
811        }
812    }
813}
814
815
816/*----------------------------------------------------------------------------
817 * Downmix_foldFrom5Point1()
818 *----------------------------------------------------------------------------
819 * Purpose:
820 * downmix a 5.1 signal to stereo
821 *
822 * Inputs:
823 *  pSrc       5.1 audio samples to downmix
824 *  numFrames  the number of 5.1 frames to downmix
825 *  accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
826 *               or overwrite pDst (when false)
827 *
828 * Outputs:
829 *  pDst       downmixed stereo audio samples
830 *
831 *----------------------------------------------------------------------------
832 */
833void Downmix_foldFrom5Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
834    int32_t lt, rt, centerPlusLfeContrib; // samples in Q19.12 format
835    // sample at index 0 is FL
836    // sample at index 1 is FR
837    // sample at index 2 is FC
838    // sample at index 3 is LFE
839    // sample at index 4 is RL
840    // sample at index 5 is RR
841    // code is mostly duplicated between the two values of accumulate to avoid repeating the test
842    // for every sample
843    if (accumulate) {
844        while (numFrames) {
845            // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
846            centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
847                    + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
848            // FL + centerPlusLfeContrib + RL
849            lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[4] << 12);
850            // FR + centerPlusLfeContrib + RR
851            rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[5] << 12);
852            // accumulate in destination
853            pDst[0] = clamp16(pDst[0] + (lt >> 13));
854            pDst[1] = clamp16(pDst[1] + (rt >> 13));
855            pSrc += 6;
856            pDst += 2;
857            numFrames--;
858        }
859    } else { // same code as above but without adding and clamping pDst[i] to itself
860        while (numFrames) {
861            // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
862            centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
863                    + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
864            // FL + centerPlusLfeContrib + RL
865            lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[4] << 12);
866            // FR + centerPlusLfeContrib + RR
867            rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[5] << 12);
868            // store in destination
869            pDst[0] = clamp16(lt >> 13); // differs from when accumulate is true above
870            pDst[1] = clamp16(rt >> 13); // differs from when accumulate is true above
871            pSrc += 6;
872            pDst += 2;
873            numFrames--;
874        }
875    }
876}
877
878
879/*----------------------------------------------------------------------------
880 * Downmix_foldFrom7Point1()
881 *----------------------------------------------------------------------------
882 * Purpose:
883 * downmix a 7.1 signal to stereo
884 *
885 * Inputs:
886 *  pSrc       7.1 audio samples to downmix
887 *  numFrames  the number of 7.1 frames to downmix
888 *  accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
889 *               or overwrite pDst (when false)
890 *
891 * Outputs:
892 *  pDst       downmixed stereo audio samples
893 *
894 *----------------------------------------------------------------------------
895 */
896void Downmix_foldFrom7Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
897    int32_t lt, rt, centerPlusLfeContrib; // samples in Q19.12 format
898    // sample at index 0 is FL
899    // sample at index 1 is FR
900    // sample at index 2 is FC
901    // sample at index 3 is LFE
902    // sample at index 4 is RL
903    // sample at index 5 is RR
904    // sample at index 6 is SL
905    // sample at index 7 is SR
906    // code is mostly duplicated between the two values of accumulate to avoid repeating the test
907    // for every sample
908    if (accumulate) {
909        while (numFrames) {
910            // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
911            centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
912                    + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
913            // FL + centerPlusLfeContrib + SL + RL
914            lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[6] << 12) + (pSrc[4] << 12);
915            // FR + centerPlusLfeContrib + SR + RR
916            rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[7] << 12) + (pSrc[5] << 12);
917            //accumulate in destination
918            pDst[0] = clamp16(pDst[0] + (lt >> 13));
919            pDst[1] = clamp16(pDst[1] + (rt >> 13));
920            pSrc += 8;
921            pDst += 2;
922            numFrames--;
923    }
924    } else { // same code as above but without adding and clamping pDst[i] to itself
925        while (numFrames) {
926            // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
927            centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
928                    + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
929            // FL + centerPlusLfeContrib + SL + RL
930            lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[6] << 12) + (pSrc[4] << 12);
931            // FR + centerPlusLfeContrib + SR + RR
932            rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[7] << 12) + (pSrc[5] << 12);
933            // store in destination
934            pDst[0] = clamp16(lt >> 13); // differs from when accumulate is true above
935            pDst[1] = clamp16(rt >> 13); // differs from when accumulate is true above
936            pSrc += 8;
937            pDst += 2;
938            numFrames--;
939        }
940    }
941}
942
943
944/*----------------------------------------------------------------------------
945 * Downmix_foldGeneric()
946 *----------------------------------------------------------------------------
947 * Purpose:
948 * downmix to stereo a multichannel signal whose format is:
949 *  - has FL/FR
950 *  - if using AUDIO_CHANNEL_OUT_SIDE*, it contains both left and right
951 *  - if using AUDIO_CHANNEL_OUT_BACK*, it contains both left and right
952 *  - doesn't use any of the AUDIO_CHANNEL_OUT_TOP* channels
953 *  - doesn't use any of the AUDIO_CHANNEL_OUT_FRONT_*_OF_CENTER channels
954 * Only handles channel masks not enumerated in downmix_input_channel_mask_t
955 *
956 * Inputs:
957 *  mask       the channel mask of pSrc
958 *  pSrc       multichannel audio buffer to downmix
959 *  numFrames  the number of multichannel frames to downmix
960 *  accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
961 *               or overwrite pDst (when false)
962 *
963 * Outputs:
964 *  pDst       downmixed stereo audio samples
965 *
966 * Returns: false if multichannel format is not supported
967 *
968 *----------------------------------------------------------------------------
969 */
970bool Downmix_foldGeneric(
971        uint32_t mask, int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
972    // check against unsupported channels
973    if (mask & kUnsupported) {
974        ALOGE("Unsupported channels (top or front left/right of center)");
975        return false;
976    }
977    // verify has FL/FR
978    if ((mask & AUDIO_CHANNEL_OUT_STEREO) != AUDIO_CHANNEL_OUT_STEREO) {
979        ALOGE("Front channels must be present");
980        return false;
981    }
982    // verify uses SIDE as a pair (ok if not using SIDE at all)
983    bool hasSides = false;
984    if ((mask & kSides) != 0) {
985        if ((mask & kSides) != kSides) {
986            ALOGE("Side channels must be used as a pair");
987            return false;
988        }
989        hasSides = true;
990    }
991    // verify uses BACK as a pair (ok if not using BACK at all)
992    bool hasBacks = false;
993    if ((mask & kBacks) != 0) {
994        if ((mask & kBacks) != kBacks) {
995            ALOGE("Back channels must be used as a pair");
996            return false;
997        }
998        hasBacks = true;
999    }
1000
1001    const int numChan = audio_channel_count_from_out_mask(mask);
1002    const bool hasFC = ((mask & AUDIO_CHANNEL_OUT_FRONT_CENTER) == AUDIO_CHANNEL_OUT_FRONT_CENTER);
1003    const bool hasLFE =
1004            ((mask & AUDIO_CHANNEL_OUT_LOW_FREQUENCY) == AUDIO_CHANNEL_OUT_LOW_FREQUENCY);
1005    const bool hasBC = ((mask & AUDIO_CHANNEL_OUT_BACK_CENTER) == AUDIO_CHANNEL_OUT_BACK_CENTER);
1006    // compute at what index each channel is: samples will be in the following order:
1007    //   FL FR FC LFE BL BR BC SL SR
1008    // when a channel is not present, its index is set to the same as the index of the preceding
1009    // channel
1010    const int indexFC  = hasFC    ? 2            : 1;        // front center
1011    const int indexLFE = hasLFE   ? indexFC + 1  : indexFC;  // low frequency
1012    const int indexBL  = hasBacks ? indexLFE + 1 : indexLFE; // back left
1013    const int indexBR  = hasBacks ? indexBL + 1  : indexBL;  // back right
1014    const int indexBC  = hasBC    ? indexBR + 1  : indexBR;  // back center
1015    const int indexSL  = hasSides ? indexBC + 1  : indexBC;  // side left
1016    const int indexSR  = hasSides ? indexSL + 1  : indexSL;  // side right
1017
1018    int32_t lt, rt, centersLfeContrib; // samples in Q19.12 format
1019    // code is mostly duplicated between the two values of accumulate to avoid repeating the test
1020    // for every sample
1021    if (accumulate) {
1022        while (numFrames) {
1023            // compute contribution of FC, BC and LFE
1024            centersLfeContrib = 0;
1025            if (hasFC)  { centersLfeContrib += pSrc[indexFC]; }
1026            if (hasLFE) { centersLfeContrib += pSrc[indexLFE]; }
1027            if (hasBC)  { centersLfeContrib += pSrc[indexBC]; }
1028            centersLfeContrib *= MINUS_3_DB_IN_Q19_12;
1029            // always has FL/FR
1030            lt = (pSrc[0] << 12);
1031            rt = (pSrc[1] << 12);
1032            // mix in sides and backs
1033            if (hasSides) {
1034                lt += pSrc[indexSL] << 12;
1035                rt += pSrc[indexSR] << 12;
1036            }
1037            if (hasBacks) {
1038                lt += pSrc[indexBL] << 12;
1039                rt += pSrc[indexBR] << 12;
1040            }
1041            lt += centersLfeContrib;
1042            rt += centersLfeContrib;
1043            // accumulate in destination
1044            pDst[0] = clamp16(pDst[0] + (lt >> 13));
1045            pDst[1] = clamp16(pDst[1] + (rt >> 13));
1046            pSrc += numChan;
1047            pDst += 2;
1048            numFrames--;
1049        }
1050    } else {
1051        while (numFrames) {
1052            // compute contribution of FC, BC and LFE
1053            centersLfeContrib = 0;
1054            if (hasFC)  { centersLfeContrib += pSrc[indexFC]; }
1055            if (hasLFE) { centersLfeContrib += pSrc[indexLFE]; }
1056            if (hasBC)  { centersLfeContrib += pSrc[indexBC]; }
1057            centersLfeContrib *= MINUS_3_DB_IN_Q19_12;
1058            // always has FL/FR
1059            lt = (pSrc[0] << 12);
1060            rt = (pSrc[1] << 12);
1061            // mix in sides and backs
1062            if (hasSides) {
1063                lt += pSrc[indexSL] << 12;
1064                rt += pSrc[indexSR] << 12;
1065            }
1066            if (hasBacks) {
1067                lt += pSrc[indexBL] << 12;
1068                rt += pSrc[indexBR] << 12;
1069            }
1070            lt += centersLfeContrib;
1071            rt += centersLfeContrib;
1072            // store in destination
1073            pDst[0] = clamp16(lt >> 13); // differs from when accumulate is true above
1074            pDst[1] = clamp16(rt >> 13); // differs from when accumulate is true above
1075            pSrc += numChan;
1076            pDst += 2;
1077            numFrames--;
1078        }
1079    }
1080    return true;
1081}
1082