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