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        if (cmd->psize != sizeof(int32_t)) {
449            android_errorWriteLog(0x534e4554, "63662938");
450            return -EINVAL;
451        }
452        *(int *)pReplyData = Downmix_setParameter(pDownmixer, *(int32_t *)cmd->data,
453                cmd->vsize, cmd->data + sizeof(int32_t));
454        break;
455
456    case EFFECT_CMD_SET_PARAM_DEFERRED:
457        //FIXME implement
458        ALOGW("Downmix_Command command EFFECT_CMD_SET_PARAM_DEFERRED not supported, FIXME");
459        break;
460
461    case EFFECT_CMD_SET_PARAM_COMMIT:
462        //FIXME implement
463        ALOGW("Downmix_Command command EFFECT_CMD_SET_PARAM_COMMIT not supported, FIXME");
464        break;
465
466    case EFFECT_CMD_ENABLE:
467        if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
468            return -EINVAL;
469        }
470        if (pDownmixer->state != DOWNMIX_STATE_INITIALIZED) {
471            return -ENOSYS;
472        }
473        pDownmixer->state = DOWNMIX_STATE_ACTIVE;
474        ALOGV("EFFECT_CMD_ENABLE() OK");
475        *(int *)pReplyData = 0;
476        break;
477
478    case EFFECT_CMD_DISABLE:
479        if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
480            return -EINVAL;
481        }
482        if (pDownmixer->state != DOWNMIX_STATE_ACTIVE) {
483            return -ENOSYS;
484        }
485        pDownmixer->state = DOWNMIX_STATE_INITIALIZED;
486        ALOGV("EFFECT_CMD_DISABLE() OK");
487        *(int *)pReplyData = 0;
488        break;
489
490    case EFFECT_CMD_SET_DEVICE:
491        if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) {
492            return -EINVAL;
493        }
494        // FIXME change type if playing on headset vs speaker
495        ALOGV("Downmix_Command EFFECT_CMD_SET_DEVICE: 0x%08" PRIx32, *(uint32_t *)pCmdData);
496        break;
497
498    case EFFECT_CMD_SET_VOLUME: {
499        // audio output is always stereo => 2 channel volumes
500        if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t) * 2) {
501            return -EINVAL;
502        }
503        // FIXME change volume
504        ALOGW("Downmix_Command command EFFECT_CMD_SET_VOLUME not supported, FIXME");
505        float left = (float)(*(uint32_t *)pCmdData) / (1 << 24);
506        float right = (float)(*((uint32_t *)pCmdData + 1)) / (1 << 24);
507        ALOGV("Downmix_Command EFFECT_CMD_SET_VOLUME: left %f, right %f ", left, right);
508        break;
509    }
510
511    case EFFECT_CMD_SET_AUDIO_MODE:
512        if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) {
513            return -EINVAL;
514        }
515        ALOGV("Downmix_Command EFFECT_CMD_SET_AUDIO_MODE: %" PRIu32, *(uint32_t *)pCmdData);
516        break;
517
518    case EFFECT_CMD_SET_CONFIG_REVERSE:
519    case EFFECT_CMD_SET_INPUT_DEVICE:
520        // these commands are ignored by a downmix effect
521        break;
522
523    default:
524        ALOGW("Downmix_Command invalid command %" PRIu32, cmdCode);
525        return -EINVAL;
526    }
527
528    return 0;
529}
530
531
532int Downmix_GetDescriptor(effect_handle_t self, effect_descriptor_t *pDescriptor)
533{
534    downmix_module_t *pDwnmxModule = (downmix_module_t *) self;
535
536    if (pDwnmxModule == NULL ||
537            pDwnmxModule->context.state == DOWNMIX_STATE_UNINITIALIZED) {
538        return -EINVAL;
539    }
540
541    memcpy(pDescriptor, &gDownmixDescriptor, sizeof(effect_descriptor_t));
542
543    return 0;
544}
545
546
547/*----------------------------------------------------------------------------
548 * Downmix internal functions
549 *--------------------------------------------------------------------------*/
550
551/*----------------------------------------------------------------------------
552 * Downmix_Init()
553 *----------------------------------------------------------------------------
554 * Purpose:
555 * Initialize downmix context and apply default parameters
556 *
557 * Inputs:
558 *  pDwmModule    pointer to downmix effect module
559 *
560 * Outputs:
561 *
562 * Returns:
563 *  0             indicates success
564 *
565 * Side Effects:
566 *  updates:
567 *           pDwmModule->context.type
568 *           pDwmModule->context.apply_volume_correction
569 *           pDwmModule->config.inputCfg
570 *           pDwmModule->config.outputCfg
571 *           pDwmModule->config.inputCfg.samplingRate
572 *           pDwmModule->config.outputCfg.samplingRate
573 *           pDwmModule->context.state
574 *  doesn't set:
575 *           pDwmModule->itfe
576 *
577 *----------------------------------------------------------------------------
578 */
579
580int Downmix_Init(downmix_module_t *pDwmModule) {
581
582    ALOGV("Downmix_Init module %p", pDwmModule);
583    int ret = 0;
584
585    memset(&pDwmModule->context, 0, sizeof(downmix_object_t));
586
587    pDwmModule->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
588    pDwmModule->config.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
589    pDwmModule->config.inputCfg.channels = AUDIO_CHANNEL_OUT_7POINT1;
590    pDwmModule->config.inputCfg.bufferProvider.getBuffer = NULL;
591    pDwmModule->config.inputCfg.bufferProvider.releaseBuffer = NULL;
592    pDwmModule->config.inputCfg.bufferProvider.cookie = NULL;
593    pDwmModule->config.inputCfg.mask = EFFECT_CONFIG_ALL;
594
595    pDwmModule->config.inputCfg.samplingRate = 44100;
596    pDwmModule->config.outputCfg.samplingRate = pDwmModule->config.inputCfg.samplingRate;
597
598    // set a default value for the access mode, but should be overwritten by caller
599    pDwmModule->config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
600    pDwmModule->config.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
601    pDwmModule->config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
602    pDwmModule->config.outputCfg.bufferProvider.getBuffer = NULL;
603    pDwmModule->config.outputCfg.bufferProvider.releaseBuffer = NULL;
604    pDwmModule->config.outputCfg.bufferProvider.cookie = NULL;
605    pDwmModule->config.outputCfg.mask = EFFECT_CONFIG_ALL;
606
607    ret = Downmix_Configure(pDwmModule, &pDwmModule->config, true);
608    if (ret != 0) {
609        ALOGV("Downmix_Init error %d on module %p", ret, pDwmModule);
610    } else {
611        pDwmModule->context.state = DOWNMIX_STATE_INITIALIZED;
612    }
613
614    return ret;
615}
616
617
618/*----------------------------------------------------------------------------
619 * Downmix_Configure()
620 *----------------------------------------------------------------------------
621 * Purpose:
622 *  Set input and output audio configuration.
623 *
624 * Inputs:
625 *  pDwmModule  pointer to downmix effect module
626 *  pConfig     pointer to effect_config_t structure containing input
627 *                  and output audio parameters configuration
628 *  init        true if called from init function
629 *
630 * Outputs:
631 *
632 * Returns:
633 *  0           indicates success
634 *
635 * Side Effects:
636 *
637 *----------------------------------------------------------------------------
638 */
639
640int Downmix_Configure(downmix_module_t *pDwmModule, effect_config_t *pConfig, bool init) {
641
642    downmix_object_t *pDownmixer = &pDwmModule->context;
643
644    // Check configuration compatibility with build options, and effect capabilities
645    if (pConfig->inputCfg.samplingRate != pConfig->outputCfg.samplingRate
646        || pConfig->outputCfg.channels != DOWNMIX_OUTPUT_CHANNELS
647        || pConfig->inputCfg.format != AUDIO_FORMAT_PCM_16_BIT
648        || pConfig->outputCfg.format != AUDIO_FORMAT_PCM_16_BIT) {
649        ALOGE("Downmix_Configure error: invalid config");
650        return -EINVAL;
651    }
652
653    if (&pDwmModule->config != pConfig) {
654        memcpy(&pDwmModule->config, pConfig, sizeof(effect_config_t));
655    }
656
657    if (init) {
658        pDownmixer->type = DOWNMIX_TYPE_FOLD;
659        pDownmixer->apply_volume_correction = false;
660        pDownmixer->input_channel_count = 8; // matches default input of AUDIO_CHANNEL_OUT_7POINT1
661    } else {
662        // when configuring the effect, do not allow a blank or unsupported channel mask
663        if (!Downmix_validChannelMask(pConfig->inputCfg.channels)) {
664            ALOGE("Downmix_Configure error: input channel mask(0x%x) not supported",
665                                                        pConfig->inputCfg.channels);
666            return -EINVAL;
667        }
668        pDownmixer->input_channel_count =
669                audio_channel_count_from_out_mask(pConfig->inputCfg.channels);
670    }
671
672    Downmix_Reset(pDownmixer, init);
673
674    return 0;
675}
676
677
678/*----------------------------------------------------------------------------
679 * Downmix_Reset()
680 *----------------------------------------------------------------------------
681 * Purpose:
682 *  Reset internal states.
683 *
684 * Inputs:
685 *  pDownmixer   pointer to downmix context
686 *  init         true if called from init function
687 *
688 * Outputs:
689*
690 * Returns:
691 *  0            indicates success
692 *
693 * Side Effects:
694 *
695 *----------------------------------------------------------------------------
696 */
697
698int Downmix_Reset(downmix_object_t *pDownmixer __unused, bool init __unused) {
699    // nothing to do here
700    return 0;
701}
702
703
704/*----------------------------------------------------------------------------
705 * Downmix_setParameter()
706 *----------------------------------------------------------------------------
707 * Purpose:
708 * Set a Downmix parameter
709 *
710 * Inputs:
711 *  pDownmixer    handle to instance data
712 *  param         parameter
713 *  pValue        pointer to parameter value
714 *  size          value size
715 *
716 * Outputs:
717 *
718 * Returns:
719 *  0             indicates success
720 *
721 * Side Effects:
722 *
723 *----------------------------------------------------------------------------
724 */
725int Downmix_setParameter(downmix_object_t *pDownmixer, int32_t param, uint32_t size, void *pValue) {
726
727    int16_t value16;
728    ALOGV("Downmix_setParameter, context %p, param %" PRId32 ", value16 %" PRId16 ", value32 %" PRId32,
729            pDownmixer, param, *(int16_t *)pValue, *(int32_t *)pValue);
730
731    switch (param) {
732
733      case DOWNMIX_PARAM_TYPE:
734        if (size != sizeof(downmix_type_t)) {
735            ALOGE("Downmix_setParameter(DOWNMIX_PARAM_TYPE) invalid size %" PRIu32 ", should be %zu",
736                    size, sizeof(downmix_type_t));
737            return -EINVAL;
738        }
739        value16 = *(int16_t *)pValue;
740        ALOGV("set DOWNMIX_PARAM_TYPE, type %" PRId16, value16);
741        if (!((value16 > DOWNMIX_TYPE_INVALID) && (value16 <= DOWNMIX_TYPE_LAST))) {
742            ALOGE("Downmix_setParameter invalid DOWNMIX_PARAM_TYPE value %" PRId16, value16);
743            return -EINVAL;
744        } else {
745            pDownmixer->type = (downmix_type_t) value16;
746        break;
747
748      default:
749        ALOGE("Downmix_setParameter unknown parameter %" PRId32, param);
750        return -EINVAL;
751    }
752}
753
754    return 0;
755} /* end Downmix_setParameter */
756
757
758/*----------------------------------------------------------------------------
759 * Downmix_getParameter()
760 *----------------------------------------------------------------------------
761 * Purpose:
762 * Get a Downmix parameter
763 *
764 * Inputs:
765 *  pDownmixer    handle to instance data
766 *  param         parameter
767 *  pValue        pointer to variable to hold retrieved value
768 *  pSize         pointer to value size: maximum size as input
769 *
770 * Outputs:
771 *  *pValue updated with parameter value
772 *  *pSize updated with actual value size
773 *
774 * Returns:
775 *  0             indicates success
776 *
777 * Side Effects:
778 *
779 *----------------------------------------------------------------------------
780 */
781int Downmix_getParameter(downmix_object_t *pDownmixer, int32_t param, uint32_t *pSize, void *pValue) {
782    int16_t *pValue16;
783
784    switch (param) {
785
786    case DOWNMIX_PARAM_TYPE:
787      if (*pSize < sizeof(int16_t)) {
788          ALOGE("Downmix_getParameter invalid parameter size %" PRIu32 " for DOWNMIX_PARAM_TYPE", *pSize);
789          return -EINVAL;
790      }
791      pValue16 = (int16_t *)pValue;
792      *pValue16 = (int16_t) pDownmixer->type;
793      *pSize = sizeof(int16_t);
794      ALOGV("Downmix_getParameter DOWNMIX_PARAM_TYPE is %" PRId16, *pValue16);
795      break;
796
797    default:
798      ALOGE("Downmix_getParameter unknown parameter %" PRId16, param);
799      return -EINVAL;
800    }
801
802    return 0;
803} /* end Downmix_getParameter */
804
805
806/*----------------------------------------------------------------------------
807 * Downmix_foldFromQuad()
808 *----------------------------------------------------------------------------
809 * Purpose:
810 * downmix a quad signal to stereo
811 *
812 * Inputs:
813 *  pSrc       quad audio samples to downmix
814 *  numFrames  the number of quad frames to downmix
815 *  accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
816 *               or overwrite pDst (when false)
817 *
818 * Outputs:
819 *  pDst       downmixed stereo audio samples
820 *
821 *----------------------------------------------------------------------------
822 */
823void Downmix_foldFromQuad(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
824    // sample at index 0 is FL
825    // sample at index 1 is FR
826    // sample at index 2 is RL
827    // sample at index 3 is RR
828    if (accumulate) {
829        while (numFrames) {
830            // FL + RL
831            pDst[0] = clamp16(pDst[0] + ((pSrc[0] + pSrc[2]) >> 1));
832            // FR + RR
833            pDst[1] = clamp16(pDst[1] + ((pSrc[1] + pSrc[3]) >> 1));
834            pSrc += 4;
835            pDst += 2;
836            numFrames--;
837        }
838    } else { // same code as above but without adding and clamping pDst[i] to itself
839        while (numFrames) {
840            // FL + RL
841            pDst[0] = clamp16((pSrc[0] + pSrc[2]) >> 1);
842            // FR + RR
843            pDst[1] = clamp16((pSrc[1] + pSrc[3]) >> 1);
844            pSrc += 4;
845            pDst += 2;
846            numFrames--;
847        }
848    }
849}
850
851
852/*----------------------------------------------------------------------------
853 * Downmix_foldFrom5Point1()
854 *----------------------------------------------------------------------------
855 * Purpose:
856 * downmix a 5.1 signal to stereo
857 *
858 * Inputs:
859 *  pSrc       5.1 audio samples to downmix
860 *  numFrames  the number of 5.1 frames to downmix
861 *  accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
862 *               or overwrite pDst (when false)
863 *
864 * Outputs:
865 *  pDst       downmixed stereo audio samples
866 *
867 *----------------------------------------------------------------------------
868 */
869void Downmix_foldFrom5Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
870    int32_t lt, rt, centerPlusLfeContrib; // samples in Q19.12 format
871    // sample at index 0 is FL
872    // sample at index 1 is FR
873    // sample at index 2 is FC
874    // sample at index 3 is LFE
875    // sample at index 4 is RL
876    // sample at index 5 is RR
877    // code is mostly duplicated between the two values of accumulate to avoid repeating the test
878    // for every sample
879    if (accumulate) {
880        while (numFrames) {
881            // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
882            centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
883                    + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
884            // FL + centerPlusLfeContrib + RL
885            lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[4] << 12);
886            // FR + centerPlusLfeContrib + RR
887            rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[5] << 12);
888            // accumulate in destination
889            pDst[0] = clamp16(pDst[0] + (lt >> 13));
890            pDst[1] = clamp16(pDst[1] + (rt >> 13));
891            pSrc += 6;
892            pDst += 2;
893            numFrames--;
894        }
895    } else { // same code as above but without adding and clamping pDst[i] to itself
896        while (numFrames) {
897            // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
898            centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
899                    + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
900            // FL + centerPlusLfeContrib + RL
901            lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[4] << 12);
902            // FR + centerPlusLfeContrib + RR
903            rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[5] << 12);
904            // store in destination
905            pDst[0] = clamp16(lt >> 13); // differs from when accumulate is true above
906            pDst[1] = clamp16(rt >> 13); // differs from when accumulate is true above
907            pSrc += 6;
908            pDst += 2;
909            numFrames--;
910        }
911    }
912}
913
914
915/*----------------------------------------------------------------------------
916 * Downmix_foldFrom7Point1()
917 *----------------------------------------------------------------------------
918 * Purpose:
919 * downmix a 7.1 signal to stereo
920 *
921 * Inputs:
922 *  pSrc       7.1 audio samples to downmix
923 *  numFrames  the number of 7.1 frames to downmix
924 *  accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
925 *               or overwrite pDst (when false)
926 *
927 * Outputs:
928 *  pDst       downmixed stereo audio samples
929 *
930 *----------------------------------------------------------------------------
931 */
932void Downmix_foldFrom7Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
933    int32_t lt, rt, centerPlusLfeContrib; // samples in Q19.12 format
934    // sample at index 0 is FL
935    // sample at index 1 is FR
936    // sample at index 2 is FC
937    // sample at index 3 is LFE
938    // sample at index 4 is RL
939    // sample at index 5 is RR
940    // sample at index 6 is SL
941    // sample at index 7 is SR
942    // code is mostly duplicated between the two values of accumulate to avoid repeating the test
943    // for every sample
944    if (accumulate) {
945        while (numFrames) {
946            // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
947            centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
948                    + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
949            // FL + centerPlusLfeContrib + SL + RL
950            lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[6] << 12) + (pSrc[4] << 12);
951            // FR + centerPlusLfeContrib + SR + RR
952            rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[7] << 12) + (pSrc[5] << 12);
953            //accumulate in destination
954            pDst[0] = clamp16(pDst[0] + (lt >> 13));
955            pDst[1] = clamp16(pDst[1] + (rt >> 13));
956            pSrc += 8;
957            pDst += 2;
958            numFrames--;
959    }
960    } else { // same code as above but without adding and clamping pDst[i] to itself
961        while (numFrames) {
962            // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
963            centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
964                    + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
965            // FL + centerPlusLfeContrib + SL + RL
966            lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[6] << 12) + (pSrc[4] << 12);
967            // FR + centerPlusLfeContrib + SR + RR
968            rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[7] << 12) + (pSrc[5] << 12);
969            // store in destination
970            pDst[0] = clamp16(lt >> 13); // differs from when accumulate is true above
971            pDst[1] = clamp16(rt >> 13); // differs from when accumulate is true above
972            pSrc += 8;
973            pDst += 2;
974            numFrames--;
975        }
976    }
977}
978
979
980/*----------------------------------------------------------------------------
981 * Downmix_foldGeneric()
982 *----------------------------------------------------------------------------
983 * Purpose:
984 * downmix to stereo a multichannel signal whose format is:
985 *  - has FL/FR
986 *  - if using AUDIO_CHANNEL_OUT_SIDE*, it contains both left and right
987 *  - if using AUDIO_CHANNEL_OUT_BACK*, it contains both left and right
988 *  - doesn't use any of the AUDIO_CHANNEL_OUT_TOP* channels
989 *  - doesn't use any of the AUDIO_CHANNEL_OUT_FRONT_*_OF_CENTER channels
990 * Only handles channel masks not enumerated in downmix_input_channel_mask_t
991 *
992 * Inputs:
993 *  mask       the channel mask of pSrc
994 *  pSrc       multichannel audio buffer to downmix
995 *  numFrames  the number of multichannel frames to downmix
996 *  accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
997 *               or overwrite pDst (when false)
998 *
999 * Outputs:
1000 *  pDst       downmixed stereo audio samples
1001 *
1002 * Returns: false if multichannel format is not supported
1003 *
1004 *----------------------------------------------------------------------------
1005 */
1006bool Downmix_foldGeneric(
1007        uint32_t mask, int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
1008
1009    if (!Downmix_validChannelMask(mask)) {
1010        return false;
1011    }
1012
1013    const bool hasSides = (mask & kSides) != 0;
1014    const bool hasBacks = (mask & kBacks) != 0;
1015
1016    const int numChan = audio_channel_count_from_out_mask(mask);
1017    const bool hasFC = ((mask & AUDIO_CHANNEL_OUT_FRONT_CENTER) == AUDIO_CHANNEL_OUT_FRONT_CENTER);
1018    const bool hasLFE =
1019            ((mask & AUDIO_CHANNEL_OUT_LOW_FREQUENCY) == AUDIO_CHANNEL_OUT_LOW_FREQUENCY);
1020    const bool hasBC = ((mask & AUDIO_CHANNEL_OUT_BACK_CENTER) == AUDIO_CHANNEL_OUT_BACK_CENTER);
1021    // compute at what index each channel is: samples will be in the following order:
1022    //   FL FR FC LFE BL BR BC SL SR
1023    // when a channel is not present, its index is set to the same as the index of the preceding
1024    // channel
1025    const int indexFC  = hasFC    ? 2            : 1;        // front center
1026    const int indexLFE = hasLFE   ? indexFC + 1  : indexFC;  // low frequency
1027    const int indexBL  = hasBacks ? indexLFE + 1 : indexLFE; // back left
1028    const int indexBR  = hasBacks ? indexBL + 1  : indexBL;  // back right
1029    const int indexBC  = hasBC    ? indexBR + 1  : indexBR;  // back center
1030    const int indexSL  = hasSides ? indexBC + 1  : indexBC;  // side left
1031    const int indexSR  = hasSides ? indexSL + 1  : indexSL;  // side right
1032
1033    int32_t lt, rt, centersLfeContrib; // samples in Q19.12 format
1034    // code is mostly duplicated between the two values of accumulate to avoid repeating the test
1035    // for every sample
1036    if (accumulate) {
1037        while (numFrames) {
1038            // compute contribution of FC, BC and LFE
1039            centersLfeContrib = 0;
1040            if (hasFC)  { centersLfeContrib += pSrc[indexFC]; }
1041            if (hasLFE) { centersLfeContrib += pSrc[indexLFE]; }
1042            if (hasBC)  { centersLfeContrib += pSrc[indexBC]; }
1043            centersLfeContrib *= MINUS_3_DB_IN_Q19_12;
1044            // always has FL/FR
1045            lt = (pSrc[0] << 12);
1046            rt = (pSrc[1] << 12);
1047            // mix in sides and backs
1048            if (hasSides) {
1049                lt += pSrc[indexSL] << 12;
1050                rt += pSrc[indexSR] << 12;
1051            }
1052            if (hasBacks) {
1053                lt += pSrc[indexBL] << 12;
1054                rt += pSrc[indexBR] << 12;
1055            }
1056            lt += centersLfeContrib;
1057            rt += centersLfeContrib;
1058            // accumulate in destination
1059            pDst[0] = clamp16(pDst[0] + (lt >> 13));
1060            pDst[1] = clamp16(pDst[1] + (rt >> 13));
1061            pSrc += numChan;
1062            pDst += 2;
1063            numFrames--;
1064        }
1065    } else {
1066        while (numFrames) {
1067            // compute contribution of FC, BC and LFE
1068            centersLfeContrib = 0;
1069            if (hasFC)  { centersLfeContrib += pSrc[indexFC]; }
1070            if (hasLFE) { centersLfeContrib += pSrc[indexLFE]; }
1071            if (hasBC)  { centersLfeContrib += pSrc[indexBC]; }
1072            centersLfeContrib *= MINUS_3_DB_IN_Q19_12;
1073            // always has FL/FR
1074            lt = (pSrc[0] << 12);
1075            rt = (pSrc[1] << 12);
1076            // mix in sides and backs
1077            if (hasSides) {
1078                lt += pSrc[indexSL] << 12;
1079                rt += pSrc[indexSR] << 12;
1080            }
1081            if (hasBacks) {
1082                lt += pSrc[indexBL] << 12;
1083                rt += pSrc[indexBR] << 12;
1084            }
1085            lt += centersLfeContrib;
1086            rt += centersLfeContrib;
1087            // store in destination
1088            pDst[0] = clamp16(lt >> 13); // differs from when accumulate is true above
1089            pDst[1] = clamp16(rt >> 13); // differs from when accumulate is true above
1090            pSrc += numChan;
1091            pDst += 2;
1092            numFrames--;
1093        }
1094    }
1095    return true;
1096}
1097