EffectDownmix.c revision f28c8792f64e10c3c477d86bf4804a8566ff524e
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 <cutils/log.h>
20#include <stdlib.h>
21#include <string.h>
22#include <stdbool.h>
23#include "EffectDownmix.h"
24
25#define MINUS_3_DB_IN_Q19_12 2896 // -3dB = 0.707 * 2^12 = 2896
26
27// effect_handle_t interface implementation for downmix effect
28const struct effect_interface_s gDownmixInterface = {
29        Downmix_Process,
30        Downmix_Command,
31        Downmix_GetDescriptor,
32        NULL /* no process_reverse function, no reference stream needed */
33};
34
35audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
36    tag : AUDIO_EFFECT_LIBRARY_TAG,
37    version : EFFECT_LIBRARY_API_VERSION,
38    name : "Downmix Library",
39    implementor : "The Android Open Source Project",
40    query_num_effects : DownmixLib_QueryNumberEffects,
41    query_effect : DownmixLib_QueryEffect,
42    create_effect : DownmixLib_Create,
43    release_effect : DownmixLib_Release,
44    get_descriptor : DownmixLib_GetDescriptor,
45};
46
47
48// AOSP insert downmix UUID: 93f04452-e4fe-41cc-91f9-e475b6d1d69f
49static const effect_descriptor_t gDownmixDescriptor = {
50        EFFECT_UIID_DOWNMIX__, //type
51        {0x93f04452, 0xe4fe, 0x41cc, 0x91f9, {0xe4, 0x75, 0xb6, 0xd1, 0xd6, 0x9f}}, // uuid
52        EFFECT_CONTROL_API_VERSION,
53        EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST,
54        0, //FIXME what value should be reported? // cpu load
55        0, //FIXME what value should be reported? // memory usage
56        "Multichannel Downmix To Stereo", // human readable effect name
57        "The Android Open Source Project" // human readable effect implementor name
58};
59
60// gDescriptors contains pointers to all defined effect descriptor in this library
61static const effect_descriptor_t * const gDescriptors[] = {
62        &gDownmixDescriptor
63};
64
65// number of effects in this library
66const int kNbEffects = sizeof(gDescriptors) / sizeof(const effect_descriptor_t *);
67
68
69/*----------------------------------------------------------------------------
70 * Effect API implementation
71 *--------------------------------------------------------------------------*/
72
73/*--- Effect Library Interface Implementation ---*/
74
75int32_t DownmixLib_QueryNumberEffects(uint32_t *pNumEffects) {
76    ALOGV("DownmixLib_QueryNumberEffects()");
77    *pNumEffects = kNbEffects;
78    return 0;
79}
80
81int32_t DownmixLib_QueryEffect(uint32_t index, effect_descriptor_t *pDescriptor) {
82    ALOGV("DownmixLib_QueryEffect() index=%d", index);
83    if (pDescriptor == NULL) {
84        return -EINVAL;
85    }
86    if (index >= (uint32_t)kNbEffects) {
87        return -EINVAL;
88    }
89    memcpy(pDescriptor, gDescriptors[index], sizeof(effect_descriptor_t));
90    return 0;
91}
92
93
94int32_t DownmixLib_Create(const effect_uuid_t *uuid,
95        int32_t sessionId,
96        int32_t ioId,
97        effect_handle_t *pHandle) {
98    int ret;
99    int i;
100    downmix_module_t *module;
101    const effect_descriptor_t *desc;
102
103    ALOGV("DownmixLib_Create()");
104
105    if (pHandle == NULL || uuid == NULL) {
106        return -EINVAL;
107    }
108
109    for (i = 0 ; i < kNbEffects ; i++) {
110        desc = gDescriptors[i];
111        if (memcmp(uuid, &desc->uuid, sizeof(effect_uuid_t)) == 0) {
112            break;
113        }
114    }
115
116    if (i == kNbEffects) {
117        return -ENOENT;
118    }
119
120    module = malloc(sizeof(downmix_module_t));
121
122    module->itfe = &gDownmixInterface;
123
124    module->context.state = DOWNMIX_STATE_UNINITIALIZED;
125
126    ret = Downmix_Init(module);
127    if (ret < 0) {
128        ALOGW("DownmixLib_Create() init failed");
129        free(module);
130        return ret;
131    }
132
133    *pHandle = (effect_handle_t) module;
134
135    ALOGV("DownmixLib_Create() %p , size %d", module, sizeof(downmix_module_t));
136
137    return 0;
138}
139
140
141int32_t DownmixLib_Release(effect_handle_t handle) {
142    downmix_module_t *pDwmModule = (downmix_module_t *)handle;
143
144    ALOGV("DownmixLib_Release() %p", handle);
145    if (handle == NULL) {
146        return -EINVAL;
147    }
148
149    pDwmModule->context.state = DOWNMIX_STATE_UNINITIALIZED;
150
151    free(pDwmModule);
152    return 0;
153}
154
155
156int32_t DownmixLib_GetDescriptor(const effect_uuid_t *uuid, effect_descriptor_t *pDescriptor) {
157    ALOGV("DownmixLib_GetDescriptor()");
158    int i;
159
160    if (pDescriptor == NULL || uuid == NULL){
161        ALOGE("DownmixLib_Create() called with NULL pointer");
162        return -EINVAL;
163    }
164    ALOGV("DownmixLib_GetDescriptor() nb effects=%d", kNbEffects);
165    for (i = 0; i < kNbEffects; i++) {
166        ALOGV("DownmixLib_GetDescriptor() i=%d", i);
167        if (memcmp(uuid, &gDescriptors[i]->uuid, sizeof(effect_uuid_t)) == 0) {
168            memcpy(pDescriptor, gDescriptors[i], sizeof(effect_descriptor_t));
169            ALOGV("EffectGetDescriptor - UUID matched downmix type %d, UUID = %x",
170                 i, gDescriptors[i]->uuid.timeLow);
171            return 0;
172        }
173    }
174
175    return -EINVAL;
176}
177
178
179/*--- Effect Control Interface Implementation ---*/
180
181static int Downmix_Process(effect_handle_t self,
182        audio_buffer_t *inBuffer, audio_buffer_t *outBuffer) {
183
184    downmix_object_t *pDownmixer;
185    int16_t *pSrc, *pDst;
186    downmix_module_t *pDwmModule = (downmix_module_t *)self;
187
188    if (pDwmModule == NULL) {
189        return -EINVAL;
190    }
191
192    if (inBuffer == NULL || inBuffer->raw == NULL ||
193        outBuffer == NULL || outBuffer->raw == NULL ||
194        inBuffer->frameCount != outBuffer->frameCount) {
195        return -EINVAL;
196    }
197
198    pDownmixer = (downmix_object_t*) &pDwmModule->context;
199
200    if (pDownmixer->state == DOWNMIX_STATE_UNINITIALIZED) {
201        ALOGE("Downmix_Process error: trying to use an uninitialized downmixer");
202        return -EINVAL;
203    } else if (pDownmixer->state == DOWNMIX_STATE_INITIALIZED) {
204        ALOGE("Downmix_Process error: trying to use a non-configured downmixer");
205        return -ENODATA;
206    }
207
208    pSrc = inBuffer->s16;
209    pDst = outBuffer->s16;
210    size_t numFrames = outBuffer->frameCount;
211
212    const bool accumulate =
213            (pDwmModule->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE);
214
215    switch(pDownmixer->type) {
216
217      case DOWNMIX_TYPE_STRIP:
218          if (accumulate) {
219              while (numFrames) {
220                  pDst[0] = clamp16(pDst[0] + pSrc[0]);
221                  pDst[1] = clamp16(pDst[1] + pSrc[1]);
222                  pSrc += pDownmixer->input_channel_count;
223                  pDst += 2;
224                  numFrames--;
225              }
226          } else {
227              while (numFrames) {
228                  pDst[0] = pSrc[0];
229                  pDst[1] = pSrc[1];
230                  pSrc += pDownmixer->input_channel_count;
231                  pDst += 2;
232                  numFrames--;
233              }
234          }
235          break;
236
237      case DOWNMIX_TYPE_FOLD:
238        // optimize for the common formats
239        switch(pDwmModule->config.inputCfg.channels) {
240        case AUDIO_CHANNEL_OUT_QUAD:
241            Downmix_foldFromQuad(pSrc, pDst, numFrames, accumulate);
242            break;
243        case AUDIO_CHANNEL_OUT_SURROUND:
244            Downmix_foldFromSurround(pSrc, pDst, numFrames, accumulate);
245            break;
246        case AUDIO_CHANNEL_OUT_5POINT1:
247            Downmix_foldFrom5Point1(pSrc, pDst, numFrames, accumulate);
248            break;
249        case AUDIO_CHANNEL_OUT_7POINT1:
250            Downmix_foldFrom7Point1(pSrc, pDst, numFrames, accumulate);
251            break;
252        default:
253            // FIXME implement generic downmix
254            ALOGE("Multichannel configurations other than quad, 4.0, 5.1 and 7.1 are not supported");
255            break;
256        }
257        break;
258
259      default:
260        return -EINVAL;
261    }
262
263    return 0;
264}
265
266
267static int Downmix_Command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
268        void *pCmdData, uint32_t *replySize, void *pReplyData) {
269
270    downmix_module_t *pDwmModule = (downmix_module_t *) self;
271    downmix_object_t *pDownmixer;
272    int retsize;
273
274    if (pDwmModule == NULL || pDwmModule->context.state == DOWNMIX_STATE_UNINITIALIZED) {
275        return -EINVAL;
276    }
277
278    pDownmixer = (downmix_object_t*) &pDwmModule->context;
279
280    ALOGV("Downmix_Command command %d cmdSize %d",cmdCode, cmdSize);
281
282    switch (cmdCode) {
283    case EFFECT_CMD_INIT:
284        if (pReplyData == NULL || *replySize != sizeof(int)) {
285            return -EINVAL;
286        }
287        *(int *) pReplyData = Downmix_Init(pDwmModule);
288        break;
289
290    case EFFECT_CMD_SET_CONFIG:
291        if (pCmdData == NULL || cmdSize != sizeof(effect_config_t)
292                || pReplyData == NULL || *replySize != sizeof(int)) {
293            return -EINVAL;
294        }
295        *(int *) pReplyData = Downmix_Configure(pDwmModule,
296                (effect_config_t *)pCmdData, false);
297        break;
298
299    case EFFECT_CMD_RESET:
300        Downmix_Reset(pDownmixer, false);
301        break;
302
303    case EFFECT_CMD_GET_PARAM:
304        ALOGV("Downmix_Command EFFECT_CMD_GET_PARAM pCmdData %p, *replySize %d, pReplyData: %p",
305                pCmdData, *replySize, pReplyData);
306        if (pCmdData == NULL || cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t)) ||
307                pReplyData == NULL ||
308                *replySize < (int) sizeof(effect_param_t) + 2 * sizeof(int32_t)) {
309            return -EINVAL;
310        }
311        effect_param_t *rep = (effect_param_t *) pReplyData;
312        memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + sizeof(int32_t));
313        ALOGV("Downmix_Command EFFECT_CMD_GET_PARAM param %d, replySize %d",
314                *(int32_t *)rep->data, rep->vsize);
315        rep->status = Downmix_getParameter(pDownmixer, *(int32_t *)rep->data, &rep->vsize,
316                rep->data + sizeof(int32_t));
317        *replySize = sizeof(effect_param_t) + sizeof(int32_t) + rep->vsize;
318        break;
319
320    case EFFECT_CMD_SET_PARAM:
321        ALOGV("Downmix_Command EFFECT_CMD_SET_PARAM cmdSize %d pCmdData %p, *replySize %d, " \
322                "pReplyData %p", cmdSize, pCmdData, *replySize, pReplyData);
323        if (pCmdData == NULL || (cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t)))
324                || pReplyData == NULL || *replySize != (int)sizeof(int32_t)) {
325            return -EINVAL;
326        }
327        effect_param_t *cmd = (effect_param_t *) pCmdData;
328        *(int *)pReplyData = Downmix_setParameter(pDownmixer, *(int32_t *)cmd->data,
329                cmd->vsize, cmd->data + sizeof(int32_t));
330        break;
331
332    case EFFECT_CMD_SET_PARAM_DEFERRED:
333        //FIXME implement
334        ALOGW("Downmix_Command command EFFECT_CMD_SET_PARAM_DEFERRED not supported, FIXME");
335        break;
336
337    case EFFECT_CMD_SET_PARAM_COMMIT:
338        //FIXME implement
339        ALOGW("Downmix_Command command EFFECT_CMD_SET_PARAM_COMMIT not supported, FIXME");
340        break;
341
342    case EFFECT_CMD_ENABLE:
343        if (pReplyData == NULL || *replySize != sizeof(int)) {
344            return -EINVAL;
345        }
346        if (pDownmixer->state != DOWNMIX_STATE_INITIALIZED) {
347            return -ENOSYS;
348        }
349        pDownmixer->state = DOWNMIX_STATE_ACTIVE;
350        ALOGV("EFFECT_CMD_ENABLE() OK");
351        *(int *)pReplyData = 0;
352        break;
353
354    case EFFECT_CMD_DISABLE:
355        if (pReplyData == NULL || *replySize != sizeof(int)) {
356            return -EINVAL;
357        }
358        if (pDownmixer->state != DOWNMIX_STATE_ACTIVE) {
359            return -ENOSYS;
360        }
361        pDownmixer->state = DOWNMIX_STATE_INITIALIZED;
362        ALOGV("EFFECT_CMD_DISABLE() OK");
363        *(int *)pReplyData = 0;
364        break;
365
366    case EFFECT_CMD_SET_DEVICE:
367        if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) {
368            return -EINVAL;
369        }
370        // FIXME change type if playing on headset vs speaker
371        ALOGV("Downmix_Command EFFECT_CMD_SET_DEVICE: 0x%08x", *(uint32_t *)pCmdData);
372        break;
373
374    case EFFECT_CMD_SET_VOLUME: {
375        // audio output is always stereo => 2 channel volumes
376        if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t) * 2) {
377            return -EINVAL;
378        }
379        // FIXME change volume
380        ALOGW("Downmix_Command command EFFECT_CMD_SET_VOLUME not supported, FIXME");
381        float left = (float)(*(uint32_t *)pCmdData) / (1 << 24);
382        float right = (float)(*((uint32_t *)pCmdData + 1)) / (1 << 24);
383        ALOGV("Downmix_Command EFFECT_CMD_SET_VOLUME: left %f, right %f ", left, right);
384        break;
385    }
386
387    case EFFECT_CMD_SET_AUDIO_MODE:
388        if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) {
389            return -EINVAL;
390        }
391        ALOGV("Downmix_Command EFFECT_CMD_SET_AUDIO_MODE: %d", *(uint32_t *)pCmdData);
392        break;
393
394    case EFFECT_CMD_SET_CONFIG_REVERSE:
395    case EFFECT_CMD_SET_INPUT_DEVICE:
396        // these commands are ignored by a downmix effect
397        break;
398
399    default:
400        ALOGW("Downmix_Command invalid command %d",cmdCode);
401        return -EINVAL;
402    }
403
404    return 0;
405}
406
407
408int Downmix_GetDescriptor(effect_handle_t self, effect_descriptor_t *pDescriptor)
409{
410    downmix_module_t *pDwnmxModule = (downmix_module_t *) self;
411
412    if (pDwnmxModule == NULL ||
413            pDwnmxModule->context.state == DOWNMIX_STATE_UNINITIALIZED) {
414        return -EINVAL;
415    }
416
417    memcpy(pDescriptor, &gDownmixDescriptor, sizeof(effect_descriptor_t));
418
419    return 0;
420}
421
422
423/*----------------------------------------------------------------------------
424 * Downmix internal functions
425 *--------------------------------------------------------------------------*/
426
427/*----------------------------------------------------------------------------
428 * Downmix_Init()
429 *----------------------------------------------------------------------------
430 * Purpose:
431 * Initialize downmix context and apply default parameters
432 *
433 * Inputs:
434 *  pDwmModule    pointer to downmix effect module
435 *
436 * Outputs:
437 *
438 * Returns:
439 *  0             indicates success
440 *
441 * Side Effects:
442 *  updates:
443 *           pDwmModule->context.type
444 *           pDwmModule->context.apply_volume_correction
445 *           pDwmModule->config.inputCfg
446 *           pDwmModule->config.outputCfg
447 *           pDwmModule->config.inputCfg.samplingRate
448 *           pDwmModule->config.outputCfg.samplingRate
449 *           pDwmModule->context.state
450 *  doesn't set:
451 *           pDwmModule->itfe
452 *
453 *----------------------------------------------------------------------------
454 */
455
456int Downmix_Init(downmix_module_t *pDwmModule) {
457
458    ALOGV("Downmix_Init module %p", pDwmModule);
459    int ret = 0;
460
461    memset(&pDwmModule->context, 0, sizeof(downmix_object_t));
462
463    pDwmModule->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
464    pDwmModule->config.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
465    pDwmModule->config.inputCfg.channels = AUDIO_CHANNEL_OUT_7POINT1;
466    pDwmModule->config.inputCfg.bufferProvider.getBuffer = NULL;
467    pDwmModule->config.inputCfg.bufferProvider.releaseBuffer = NULL;
468    pDwmModule->config.inputCfg.bufferProvider.cookie = NULL;
469    pDwmModule->config.inputCfg.mask = EFFECT_CONFIG_ALL;
470
471    pDwmModule->config.inputCfg.samplingRate = 44100;
472    pDwmModule->config.outputCfg.samplingRate = pDwmModule->config.inputCfg.samplingRate;
473
474    // set a default value for the access mode, but should be overwritten by caller
475    pDwmModule->config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
476    pDwmModule->config.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
477    pDwmModule->config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
478    pDwmModule->config.outputCfg.bufferProvider.getBuffer = NULL;
479    pDwmModule->config.outputCfg.bufferProvider.releaseBuffer = NULL;
480    pDwmModule->config.outputCfg.bufferProvider.cookie = NULL;
481    pDwmModule->config.outputCfg.mask = EFFECT_CONFIG_ALL;
482
483    ret = Downmix_Configure(pDwmModule, &pDwmModule->config, true);
484    if (ret != 0) {
485        ALOGV("Downmix_Init error %d on module %p", ret, pDwmModule);
486    } else {
487        pDwmModule->context.state = DOWNMIX_STATE_INITIALIZED;
488    }
489
490    return ret;
491}
492
493
494/*----------------------------------------------------------------------------
495 * Downmix_Configure()
496 *----------------------------------------------------------------------------
497 * Purpose:
498 *  Set input and output audio configuration.
499 *
500 * Inputs:
501 *  pDwmModule  pointer to downmix effect module
502 *  pConfig     pointer to effect_config_t structure containing input
503 *                  and output audio parameters configuration
504 *  init        true if called from init function
505 *
506 * Outputs:
507 *
508 * Returns:
509 *  0           indicates success
510 *
511 * Side Effects:
512 *
513 *----------------------------------------------------------------------------
514 */
515
516int Downmix_Configure(downmix_module_t *pDwmModule, effect_config_t *pConfig, bool init) {
517
518    downmix_object_t *pDownmixer = &pDwmModule->context;
519
520    // Check configuration compatibility with build options, and effect capabilities
521    if (pConfig->inputCfg.samplingRate != pConfig->outputCfg.samplingRate
522        || pConfig->outputCfg.channels != DOWNMIX_OUTPUT_CHANNELS
523        || pConfig->inputCfg.format != AUDIO_FORMAT_PCM_16_BIT
524        || pConfig->outputCfg.format != AUDIO_FORMAT_PCM_16_BIT) {
525        ALOGE("Downmix_Configure error: invalid config");
526        return -EINVAL;
527    }
528
529    memcpy(&pDwmModule->config, pConfig, sizeof(effect_config_t));
530
531    if (init) {
532        pDownmixer->type = DOWNMIX_TYPE_FOLD;
533        pDownmixer->apply_volume_correction = false;
534        pDownmixer->input_channel_count = 8; // matches default input of AUDIO_CHANNEL_OUT_7POINT1
535    } else {
536        // when configuring the effect, do not allow a blank channel mask
537        if (pConfig->inputCfg.channels == 0) {
538            ALOGE("Downmix_Configure error: input channel mask can't be 0");
539            return -EINVAL;
540        }
541        pDownmixer->input_channel_count = popcount(pConfig->inputCfg.channels);
542    }
543
544    Downmix_Reset(pDownmixer, init);
545
546    return 0;
547}
548
549
550/*----------------------------------------------------------------------------
551 * Downmix_Reset()
552 *----------------------------------------------------------------------------
553 * Purpose:
554 *  Reset internal states.
555 *
556 * Inputs:
557 *  pDownmixer   pointer to downmix context
558 *  init         true if called from init function
559 *
560 * Outputs:
561*
562 * Returns:
563 *  0            indicates success
564 *
565 * Side Effects:
566 *
567 *----------------------------------------------------------------------------
568 */
569
570int Downmix_Reset(downmix_object_t *pDownmixer, bool init) {
571    // nothing to do here
572    return 0;
573}
574
575
576/*----------------------------------------------------------------------------
577 * Downmix_setParameter()
578 *----------------------------------------------------------------------------
579 * Purpose:
580 * Set a Downmix parameter
581 *
582 * Inputs:
583 *  pDownmixer    handle to instance data
584 *  param         parameter
585 *  pValue        pointer to parameter value
586 *  size          value size
587 *
588 * Outputs:
589 *
590 * Returns:
591 *  0             indicates success
592 *
593 * Side Effects:
594 *
595 *----------------------------------------------------------------------------
596 */
597int Downmix_setParameter(downmix_object_t *pDownmixer, int32_t param, size_t size, void *pValue) {
598
599    int16_t value16;
600    ALOGV("Downmix_setParameter, context %p, param %d, value16 %d, value32 %d",
601            pDownmixer, param, *(int16_t *)pValue, *(int32_t *)pValue);
602
603    switch (param) {
604
605      case DOWNMIX_PARAM_TYPE:
606        if (size != sizeof(downmix_type_t)) {
607            ALOGE("Downmix_setParameter(DOWNMIX_PARAM_TYPE) invalid size %d, should be %d",
608                    size, sizeof(downmix_type_t));
609            return -EINVAL;
610        }
611        value16 = *(int16_t *)pValue;
612        ALOGV("set DOWNMIX_PARAM_TYPE, type %d", value16);
613        if (!((value16 > DOWNMIX_TYPE_INVALID) && (value16 <= DOWNMIX_TYPE_LAST))) {
614            ALOGE("Downmix_setParameter invalid DOWNMIX_PARAM_TYPE value %d", value16);
615            return -EINVAL;
616        } else {
617            pDownmixer->type = (downmix_type_t) value16;
618        break;
619
620      default:
621        ALOGE("Downmix_setParameter unknown parameter %d", param);
622        return -EINVAL;
623    }
624}
625
626    return 0;
627} /* end Downmix_setParameter */
628
629
630/*----------------------------------------------------------------------------
631 * Downmix_getParameter()
632 *----------------------------------------------------------------------------
633 * Purpose:
634 * Get a Downmix parameter
635 *
636 * Inputs:
637 *  pDownmixer    handle to instance data
638 *  param         parameter
639 *  pValue        pointer to variable to hold retrieved value
640 *  pSize         pointer to value size: maximum size as input
641 *
642 * Outputs:
643 *  *pValue updated with parameter value
644 *  *pSize updated with actual value size
645 *
646 * Returns:
647 *  0             indicates success
648 *
649 * Side Effects:
650 *
651 *----------------------------------------------------------------------------
652 */
653int Downmix_getParameter(downmix_object_t *pDownmixer, int32_t param, size_t *pSize, void *pValue) {
654    int16_t *pValue16;
655
656    switch (param) {
657
658    case DOWNMIX_PARAM_TYPE:
659      if (*pSize < sizeof(int16_t)) {
660          ALOGE("Downmix_getParameter invalid parameter size %d for DOWNMIX_PARAM_TYPE", *pSize);
661          return -EINVAL;
662      }
663      pValue16 = (int16_t *)pValue;
664      *pValue16 = (int16_t) pDownmixer->type;
665      *pSize = sizeof(int16_t);
666      ALOGV("Downmix_getParameter DOWNMIX_PARAM_TYPE is %d", *pValue16);
667      break;
668
669    default:
670      ALOGE("Downmix_getParameter unknown parameter %d", param);
671      return -EINVAL;
672    }
673
674    return 0;
675} /* end Downmix_getParameter */
676
677
678/*----------------------------------------------------------------------------
679 * Downmix_foldFromQuad()
680 *----------------------------------------------------------------------------
681 * Purpose:
682 * downmix a quad signal to stereo
683 *
684 * Inputs:
685 *  pSrc       quad audio samples to downmix
686 *  numFrames  the number of quad frames to downmix
687 *
688 * Outputs:
689 *  pDst       downmixed stereo audio samples
690 *
691 *----------------------------------------------------------------------------
692 */
693void Downmix_foldFromQuad(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
694    // sample at index 0 is FL
695    // sample at index 1 is FR
696    // sample at index 2 is RL
697    // sample at index 3 is RR
698    if (accumulate) {
699        while (numFrames) {
700            // FL + RL
701            pDst[0] = clamp16(pDst[0] + pSrc[0] + pSrc[2]);
702            // FR + RR
703            pDst[1] = clamp16(pDst[1] + pSrc[1] + pSrc[3]);
704            pSrc += 4;
705            pDst += 2;
706            numFrames--;
707        }
708    } else { // same code as above but without adding and clamping pDst[i] to itself
709        while (numFrames) {
710            // FL + RL
711            pDst[0] = clamp16(pSrc[0] + pSrc[2]);
712            // FR + RR
713            pDst[1] = clamp16(pSrc[1] + pSrc[3]);
714            pSrc += 4;
715            pDst += 2;
716            numFrames--;
717        }
718    }
719}
720
721
722/*----------------------------------------------------------------------------
723 * Downmix_foldFromSurround()
724 *----------------------------------------------------------------------------
725 * Purpose:
726 * downmix a "surround sound" (mono rear) signal to stereo
727 *
728 * Inputs:
729 *  pSrc       surround signal to downmix
730 *  numFrames  the number of surround frames to downmix
731 *
732 * Outputs:
733 *  pDst       downmixed stereo audio samples
734 *
735 *----------------------------------------------------------------------------
736 */
737void Downmix_foldFromSurround(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
738    int32_t lt, rt, centerPlusRearContrib; // samples in Q19.12 format
739    // sample at index 0 is FL
740    // sample at index 1 is FR
741    // sample at index 2 is FC
742    // sample at index 3 is RC
743    if (accumulate) {
744        while (numFrames) {
745            // centerPlusRearContrib = FC(-3dB) + RC(-3dB)
746            centerPlusRearContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12) + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
747            // FL + centerPlusRearContrib
748            lt = (pSrc[0] << 12) + centerPlusRearContrib;
749            // FR + centerPlusRearContrib
750            rt = (pSrc[1] << 12) + centerPlusRearContrib;
751            pDst[0] = clamp16(pDst[0] + (lt >> 12));
752            pDst[1] = clamp16(pDst[1] + (rt >> 12));
753            pSrc += 4;
754            pDst += 2;
755            numFrames--;
756        }
757    } else { // same code as above but without adding and clamping pDst[i] to itself
758        while (numFrames) {
759            // centerPlusRearContrib = FC(-3dB) + RC(-3dB)
760            centerPlusRearContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12) + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
761            // FL + centerPlusRearContrib
762            lt = (pSrc[0] << 12) + centerPlusRearContrib;
763            // FR + centerPlusRearContrib
764            rt = (pSrc[1] << 12) + centerPlusRearContrib;
765            pDst[0] = clamp16(lt >> 12);
766            pDst[1] = clamp16(rt >> 12);
767            pSrc += 4;
768            pDst += 2;
769            numFrames--;
770        }
771    }
772}
773
774
775/*----------------------------------------------------------------------------
776 * Downmix_foldFrom5Point1()
777 *----------------------------------------------------------------------------
778 * Purpose:
779 * downmix a 5.1 signal to stereo
780 *
781 * Inputs:
782 *  pSrc       5.1 audio samples to downmix
783 *  numFrames  the number of 5.1 frames to downmix
784 *
785 * Outputs:
786 *  pDst       downmixed stereo audio samples
787 *
788 *----------------------------------------------------------------------------
789 */
790void Downmix_foldFrom5Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
791    int32_t lt, rt, centerPlusLfeContrib; // samples in Q19.12 format
792    // sample at index 0 is FL
793    // sample at index 1 is FR
794    // sample at index 2 is FC
795    // sample at index 3 is LFE
796    // sample at index 4 is RL
797    // sample at index 5 is RR
798    if (accumulate) {
799        while (numFrames) {
800            // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
801            centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
802                    + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
803            // FL + centerPlusLfeContrib + RL
804            lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[4] << 12);
805            // FR + centerPlusLfeContrib + RR
806            rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[5] << 12);
807            pDst[0] = clamp16(pDst[0] + (lt >> 12));
808            pDst[1] = clamp16(pDst[1] + (rt >> 12));
809            pSrc += 6;
810            pDst += 2;
811            numFrames--;
812        }
813    } else { // same code as above but without adding and clamping pDst[i] to itself
814        while (numFrames) {
815            // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
816            centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
817                    + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
818            // FL + centerPlusLfeContrib + RL
819            lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[4] << 12);
820            // FR + centerPlusLfeContrib + RR
821            rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[5] << 12);
822            pDst[0] = clamp16(lt >> 12);
823            pDst[1] = clamp16(rt >> 12);
824            pSrc += 6;
825            pDst += 2;
826            numFrames--;
827        }
828    }
829}
830
831
832/*----------------------------------------------------------------------------
833 * Downmix_foldFrom7Point1()
834 *----------------------------------------------------------------------------
835 * Purpose:
836 * downmix a 7.1 signal to stereo
837 *
838 * Inputs:
839 *  pSrc       7.1 audio samples to downmix
840 *  numFrames  the number of 7.1 frames to downmix
841 *
842 * Outputs:
843 *  pDst       downmixed stereo audio samples
844 *
845 *----------------------------------------------------------------------------
846 */
847void Downmix_foldFrom7Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
848    int32_t lt, rt, centerPlusLfeContrib; // samples in Q19.12 format
849    // sample at index 0 is FL
850    // sample at index 1 is FR
851    // sample at index 2 is FC
852    // sample at index 3 is LFE
853    // sample at index 4 is RL
854    // sample at index 5 is RR
855    // sample at index 6 is SL
856    // sample at index 7 is SR
857    if (accumulate) {
858        while (numFrames) {
859            // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
860            centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
861                    + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
862            // FL + centerPlusLfeContrib + SL + RL
863            lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[6] << 12) + (pSrc[4] << 12);
864            // FR + centerPlusLfeContrib + SR + RR
865            rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[7] << 12) + (pSrc[5] << 12);
866            pDst[0] = clamp16(lt >> 12);
867            pDst[1] = clamp16(rt >> 12);
868            pSrc += 8;
869            pDst += 2;
870            numFrames--;
871    }
872    } else { // same code as above but without adding and clamping pDst[i] to itself
873        while (numFrames) {
874            // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
875            centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
876                    + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
877            // FL + centerPlusLfeContrib + SL + RL
878            lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[6] << 12) + (pSrc[4] << 12);
879            // FR + centerPlusLfeContrib + SR + RR
880            rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[7] << 12) + (pSrc[5] << 12);
881            pDst[0] = clamp16(pDst[0] + (lt >> 12));
882            pDst[1] = clamp16(pDst[1] + (rt >> 12));
883            pSrc += 8;
884            pDst += 2;
885            numFrames--;
886        }
887    }
888}
889
890