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