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