Merge "EffectDownmix: Move processing to audio_utils ChannelMix" into sc-v2-dev
diff --git a/media/libeffects/downmix/EffectDownmix.cpp b/media/libeffects/downmix/EffectDownmix.cpp
index f500bc3..90bb410 100644
--- a/media/libeffects/downmix/EffectDownmix.cpp
+++ b/media/libeffects/downmix/EffectDownmix.cpp
@@ -19,7 +19,7 @@
 #include <log/log.h>
 
 #include "EffectDownmix.h"
-#include <math.h>
+#include <audio_utils/ChannelMix.h>
 
 // Do not submit with DOWNMIX_TEST_CHANNEL_INDEX defined, strictly for testing
 //#define DOWNMIX_TEST_CHANNEL_INDEX 0
@@ -35,12 +35,13 @@
 } downmix_state_t;
 
 /* parameters for each downmixer */
-typedef struct {
+struct downmix_object_t {
     downmix_state_t state;
     downmix_type_t type;
     bool apply_volume_correction;
     uint8_t input_channel_count;
-} downmix_object_t;
+    android::audio_utils::channels::ChannelMix channelMix;
+};
 
 typedef struct downmix_module_s {
     const struct effect_interface_s *itfe;
@@ -77,11 +78,6 @@
         downmix_object_t *pDownmixer, int32_t param, uint32_t size, void *pValue);
 static int Downmix_getParameter(
         downmix_object_t *pDownmixer, int32_t param, uint32_t *pSize, void *pValue);
-static void Downmix_foldFromQuad(float *pSrc, float *pDst, size_t numFrames, bool accumulate);
-static void Downmix_foldFrom5Point1(float *pSrc, float *pDst, size_t numFrames, bool accumulate);
-static void Downmix_foldFrom7Point1(float *pSrc, float *pDst, size_t numFrames, bool accumulate);
-static bool Downmix_foldGeneric(
-        uint32_t mask, float *pSrc, float *pDst, size_t numFrames, bool accumulate);
 
 // effect_handle_t interface implementation for downmix effect
 const struct effect_interface_s gDownmixInterface = {
@@ -315,7 +311,8 @@
         audio_buffer_t *inBuffer, audio_buffer_t *outBuffer) {
 
     downmix_object_t *pDownmixer;
-    float *pSrc, *pDst;
+    const float *pSrc;
+    float *pDst;
     downmix_module_t *pDwmModule = (downmix_module_t *)self;
 
     if (pDwmModule == NULL) {
@@ -344,7 +341,8 @@
 
     const bool accumulate =
             (pDwmModule->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE);
-    const uint32_t downmixInputChannelMask = pDwmModule->config.inputCfg.channels;
+    const audio_channel_mask_t downmixInputChannelMask =
+            (audio_channel_mask_t)pDwmModule->config.inputCfg.channels;
 
     switch(pDownmixer->type) {
 
@@ -368,38 +366,13 @@
           }
           break;
 
-      case DOWNMIX_TYPE_FOLD:
-#ifdef DOWNMIX_ALWAYS_USE_GENERIC_DOWNMIXER
-          // bypass the optimized downmix routines for the common formats
-          if (!Downmix_foldGeneric(
-                  downmixInputChannelMask, pSrc, pDst, numFrames, accumulate)) {
-              ALOGE("Multichannel configuration %#x is not supported",
-                    downmixInputChannelMask);
-              return -EINVAL;
-          }
-          break;
-#endif
-        // optimize for the common formats
-        switch (downmixInputChannelMask) {
-        case AUDIO_CHANNEL_OUT_QUAD_BACK:
-        case AUDIO_CHANNEL_OUT_QUAD_SIDE:
-            Downmix_foldFromQuad(pSrc, pDst, numFrames, accumulate);
-            break;
-        case AUDIO_CHANNEL_OUT_5POINT1_BACK:
-        case AUDIO_CHANNEL_OUT_5POINT1_SIDE:
-            Downmix_foldFrom5Point1(pSrc, pDst, numFrames, accumulate);
-            break;
-        case AUDIO_CHANNEL_OUT_7POINT1:
-            Downmix_foldFrom7Point1(pSrc, pDst, numFrames, accumulate);
-            break;
-        default:
-            if (!Downmix_foldGeneric(
-                    downmixInputChannelMask, pSrc, pDst, numFrames, accumulate)) {
+      case DOWNMIX_TYPE_FOLD: {
+            if (!pDownmixer->channelMix.process(
+                    pSrc, pDst, numFrames, accumulate, downmixInputChannelMask)) {
                 ALOGE("Multichannel configuration %#x is not supported",
                       downmixInputChannelMask);
                 return -EINVAL;
             }
-            break;
         }
         break;
 
@@ -780,7 +753,6 @@
     return 0;
 } /* end Downmix_setParameter */
 
-
 /*----------------------------------------------------------------------------
  * Downmix_getParameter()
  *----------------------------------------------------------------------------
@@ -829,299 +801,3 @@
     return 0;
 } /* end Downmix_getParameter */
 
-
-/*----------------------------------------------------------------------------
- * Downmix_foldFromQuad()
- *----------------------------------------------------------------------------
- * Purpose:
- * downmix a quad signal to stereo
- *
- * Inputs:
- *  pSrc       quad audio samples to downmix
- *  numFrames  the number of quad frames to downmix
- *  accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
- *               or overwrite pDst (when false)
- *
- * Outputs:
- *  pDst       downmixed stereo audio samples
- *
- *----------------------------------------------------------------------------
- */
-void Downmix_foldFromQuad(float *pSrc, float *pDst, size_t numFrames, bool accumulate) {
-    // sample at index 0 is FL
-    // sample at index 1 is FR
-    // sample at index 2 is RL
-    // sample at index 3 is RR
-    if (accumulate) {
-        while (numFrames) {
-            // FL + RL
-            pDst[0] = clamp_float(pDst[0] + ((pSrc[0] + pSrc[2]) / 2.0f));
-            // FR + RR
-            pDst[1] = clamp_float(pDst[1] + ((pSrc[1] + pSrc[3]) / 2.0f));
-            pSrc += 4;
-            pDst += 2;
-            numFrames--;
-        }
-    } else { // same code as above but without adding and clamping pDst[i] to itself
-        while (numFrames) {
-            // FL + RL
-            pDst[0] = clamp_float((pSrc[0] + pSrc[2]) / 2.0f);
-            // FR + RR
-            pDst[1] = clamp_float((pSrc[1] + pSrc[3]) / 2.0f);
-            pSrc += 4;
-            pDst += 2;
-            numFrames--;
-        }
-    }
-}
-
-/*----------------------------------------------------------------------------
- * Downmix_foldFrom5Point1()
- *----------------------------------------------------------------------------
- * Purpose:
- * downmix a 5.1 signal to stereo
- *
- * Inputs:
- *  pSrc       5.1 audio samples to downmix
- *  numFrames  the number of 5.1 frames to downmix
- *  accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
- *               or overwrite pDst (when false)
- *
- * Outputs:
- *  pDst       downmixed stereo audio samples
- *
- *----------------------------------------------------------------------------
- */
-void Downmix_foldFrom5Point1(float *pSrc, float *pDst, size_t numFrames, bool accumulate) {
-    float lt, rt, centerPlusLfeContrib; // samples in Q19.12 format
-    // sample at index 0 is FL
-    // sample at index 1 is FR
-    // sample at index 2 is FC
-    // sample at index 3 is LFE
-    // sample at index 4 is RL
-    // sample at index 5 is RR
-    // code is mostly duplicated between the two values of accumulate to avoid repeating the test
-    // for every sample
-    if (accumulate) {
-        while (numFrames) {
-            // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
-            centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_FLOAT)
-                    + (pSrc[3] * MINUS_3_DB_IN_FLOAT);
-            // FL + centerPlusLfeContrib + RL
-            lt = pSrc[0] + centerPlusLfeContrib + pSrc[4];
-            // FR + centerPlusLfeContrib + RR
-            rt = pSrc[1] + centerPlusLfeContrib + pSrc[5];
-            // accumulate in destination
-            pDst[0] = clamp_float(pDst[0] + (lt / 2.0f));
-            pDst[1] = clamp_float(pDst[1] + (rt / 2.0f));
-            pSrc += 6;
-            pDst += 2;
-            numFrames--;
-        }
-    } else { // same code as above but without adding and clamping pDst[i] to itself
-        while (numFrames) {
-            // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
-            centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_FLOAT)
-                    + (pSrc[3] * MINUS_3_DB_IN_FLOAT);
-            // FL + centerPlusLfeContrib + RL
-            lt = pSrc[0] + centerPlusLfeContrib + pSrc[4];
-            // FR + centerPlusLfeContrib + RR
-            rt = pSrc[1] + centerPlusLfeContrib + pSrc[5];
-            // store in destination
-            pDst[0] = clamp_float(lt / 2.0f); // differs from when accumulate is true above
-            pDst[1] = clamp_float(rt / 2.0f); // differs from when accumulate is true above
-            pSrc += 6;
-            pDst += 2;
-            numFrames--;
-        }
-    }
-}
-
-/*----------------------------------------------------------------------------
- * Downmix_foldFrom7Point1()
- *----------------------------------------------------------------------------
- * Purpose:
- * downmix a 7.1 signal to stereo
- *
- * Inputs:
- *  pSrc       7.1 audio samples to downmix
- *  numFrames  the number of 7.1 frames to downmix
- *  accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
- *               or overwrite pDst (when false)
- *
- * Outputs:
- *  pDst       downmixed stereo audio samples
- *
- *----------------------------------------------------------------------------
- */
-void Downmix_foldFrom7Point1(float *pSrc, float *pDst, size_t numFrames, bool accumulate) {
-    float lt, rt, centerPlusLfeContrib; // samples in Q19.12 format
-    // sample at index 0 is FL
-    // sample at index 1 is FR
-    // sample at index 2 is FC
-    // sample at index 3 is LFE
-    // sample at index 4 is RL
-    // sample at index 5 is RR
-    // sample at index 6 is SL
-    // sample at index 7 is SR
-    // code is mostly duplicated between the two values of accumulate to avoid repeating the test
-    // for every sample
-    if (accumulate) {
-        while (numFrames) {
-            // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
-            centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_FLOAT)
-                    + (pSrc[3] * MINUS_3_DB_IN_FLOAT);
-            // FL + centerPlusLfeContrib + SL + RL
-            lt = pSrc[0] + centerPlusLfeContrib + pSrc[6] + pSrc[4];
-            // FR + centerPlusLfeContrib + SR + RR
-            rt = pSrc[1] + centerPlusLfeContrib + pSrc[7] + pSrc[5];
-            //accumulate in destination
-            pDst[0] = clamp_float(pDst[0] + (lt / 2.0f));
-            pDst[1] = clamp_float(pDst[1] + (rt / 2.0f));
-            pSrc += 8;
-            pDst += 2;
-            numFrames--;
-        }
-    } else { // same code as above but without adding and clamping pDst[i] to itself
-        while (numFrames) {
-            // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
-            centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_FLOAT)
-                    + (pSrc[3] * MINUS_3_DB_IN_FLOAT);
-            // FL + centerPlusLfeContrib + SL + RL
-            lt = pSrc[0] + centerPlusLfeContrib + pSrc[6] + pSrc[4];
-            // FR + centerPlusLfeContrib + SR + RR
-            rt = pSrc[1] + centerPlusLfeContrib + pSrc[7] + pSrc[5];
-            // store in destination
-            pDst[0] = clamp_float(lt / 2.0f); // differs from when accumulate is true above
-            pDst[1] = clamp_float(rt / 2.0f); // differs from when accumulate is true above
-            pSrc += 8;
-            pDst += 2;
-            numFrames--;
-        }
-    }
-}
-
-/*----------------------------------------------------------------------------
- * Downmix_foldGeneric()
- *----------------------------------------------------------------------------
- * Purpose:
- * downmix to stereo a multichannel signal of arbitrary channel position mask.
- *
- * Inputs:
- *  mask       the channel mask of pSrc
- *  pSrc       multichannel audio buffer to downmix
- *  numFrames  the number of multichannel frames to downmix
- *  accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
- *               or overwrite pDst (when false)
- *
- * Outputs:
- *  pDst       downmixed stereo audio samples
- *
- * Returns: false if multichannel format is not supported
- *
- *----------------------------------------------------------------------------
- */
-bool Downmix_foldGeneric(
-        uint32_t mask, float *pSrc, float *pDst, size_t numFrames, bool accumulate) {
-
-    if (!Downmix_validChannelMask(mask)) {
-        return false;
-    }
-    const int numChan = audio_channel_count_from_out_mask(mask);
-
-    // compute at what index each channel is: samples will be in the following order:
-    //   FL  FR  FC    LFE   BL  BR  BC    SL  SR
-    //
-    //  (transfer matrix)
-    //   FL  FR  FC    LFE   BL  BR  BC    SL  SR
-    //   0.5     0.353 0.353 0.5     0.353 0.5
-    //       0.5 0.353 0.353     0.5 0.353     0.5
-
-    // derive the indices for the transfer matrix columns that have non-zero values.
-    int indexFL = -1;
-    int indexFR = -1;
-    int indexFC = -1;
-    int indexLFE = -1;
-    int indexBL = -1;
-    int indexBR = -1;
-    int indexBC = -1;
-    int indexSL = -1;
-    int indexSR = -1;
-    int index = 0;
-    for (unsigned tmp = mask;
-         (tmp & (AUDIO_CHANNEL_OUT_7POINT1 | AUDIO_CHANNEL_OUT_BACK_CENTER)) != 0;
-         ++index) {
-        const unsigned lowestBit = tmp & -(signed)tmp;
-        switch (lowestBit) {
-        case AUDIO_CHANNEL_OUT_FRONT_LEFT:
-            indexFL = index;
-            break;
-        case AUDIO_CHANNEL_OUT_FRONT_RIGHT:
-            indexFR = index;
-            break;
-        case AUDIO_CHANNEL_OUT_FRONT_CENTER:
-            indexFC = index;
-            break;
-        case AUDIO_CHANNEL_OUT_LOW_FREQUENCY:
-            indexLFE = index;
-            break;
-        case AUDIO_CHANNEL_OUT_BACK_LEFT:
-            indexBL = index;
-            break;
-        case AUDIO_CHANNEL_OUT_BACK_RIGHT:
-            indexBR = index;
-            break;
-        case AUDIO_CHANNEL_OUT_BACK_CENTER:
-            indexBC = index;
-            break;
-        case AUDIO_CHANNEL_OUT_SIDE_LEFT:
-            indexSL = index;
-            break;
-        case AUDIO_CHANNEL_OUT_SIDE_RIGHT:
-            indexSR = index;
-            break;
-        }
-        tmp ^= lowestBit;
-    }
-
-    // With good branch prediction, this should run reasonably fast.
-    // Also consider using a transfer matrix form.
-    while (numFrames) {
-        // compute contribution of FC, BC and LFE
-        float centersLfeContrib = 0;
-        if (indexFC >= 0) centersLfeContrib = pSrc[indexFC];
-        if (indexLFE >= 0) centersLfeContrib += pSrc[indexLFE];
-        if (indexBC >= 0) centersLfeContrib += pSrc[indexBC];
-        centersLfeContrib *= MINUS_3_DB_IN_FLOAT;
-
-        float ch[2];
-        ch[0] = centersLfeContrib;
-        ch[1] = centersLfeContrib;
-
-        // mix in left / right channels
-        if (indexFL >= 0) ch[0] += pSrc[indexFL];
-        if (indexFR >= 0) ch[1] += pSrc[indexFR];
-
-        if (indexSL >= 0) ch[0] += pSrc[indexSL];
-        if (indexSR >= 0) ch[1] += pSrc[indexSR]; // note pair checks enforce this if indexSL != 0
-
-        if (indexBL >= 0) ch[0] += pSrc[indexBL];
-        if (indexBR >= 0) ch[1] += pSrc[indexBR]; // note pair checks enforce this if indexBL != 0
-
-        // scale to prevent overflow.
-        ch[0] *= 0.5f;
-        ch[1] *= 0.5f;
-
-        if (accumulate) {
-            ch[0] += pDst[0];
-            ch[1] += pDst[1];
-        }
-
-        pDst[0] = clamp_float(ch[0]);
-        pDst[1] = clamp_float(ch[1]);
-        pSrc += numChan;
-        pDst += 2;
-        numFrames--;
-    }
-    return true;
-}
diff --git a/media/libeffects/downmix/benchmark/downmix_benchmark.cpp b/media/libeffects/downmix/benchmark/downmix_benchmark.cpp
index ee169c2..d4adf5e 100644
--- a/media/libeffects/downmix/benchmark/downmix_benchmark.cpp
+++ b/media/libeffects/downmix/benchmark/downmix_benchmark.cpp
@@ -35,16 +35,14 @@
     AUDIO_CHANNEL_OUT_STEREO,
     AUDIO_CHANNEL_OUT_2POINT1,
     AUDIO_CHANNEL_OUT_2POINT0POINT2,
-    AUDIO_CHANNEL_OUT_QUAD,
-    AUDIO_CHANNEL_OUT_QUAD_BACK,
+    AUDIO_CHANNEL_OUT_QUAD, // AUDIO_CHANNEL_OUT_QUAD_BACK
     AUDIO_CHANNEL_OUT_QUAD_SIDE,
     AUDIO_CHANNEL_OUT_SURROUND,
     AUDIO_CHANNEL_OUT_2POINT1POINT2,
     AUDIO_CHANNEL_OUT_3POINT0POINT2,
     AUDIO_CHANNEL_OUT_PENTA,
     AUDIO_CHANNEL_OUT_3POINT1POINT2,
-    AUDIO_CHANNEL_OUT_5POINT1,
-    AUDIO_CHANNEL_OUT_5POINT1_BACK,
+    AUDIO_CHANNEL_OUT_5POINT1, // AUDIO_CHANNEL_OUT_5POINT1_BACK
     AUDIO_CHANNEL_OUT_5POINT1_SIDE,
     AUDIO_CHANNEL_OUT_6POINT1,
     AUDIO_CHANNEL_OUT_5POINT1POINT2,
@@ -62,58 +60,32 @@
 static constexpr size_t kFrameCount = 1000;
 
 /*
-Pixel 3XL
-downmix_benchmark:
-  #BM_Downmix/0     4723 ns    4708 ns       148694
-  #BM_Downmix/1     4717 ns    4702 ns       148873
-  #BM_Downmix/2     4803 ns    4788 ns       145893
-  #BM_Downmix/3     5056 ns    5041 ns       139110
-  #BM_Downmix/4     4710 ns    4696 ns       149625
-  #BM_Downmix/5     1514 ns    1509 ns       463694
-  #BM_Downmix/6     1513 ns    1509 ns       463451
-  #BM_Downmix/7     1516 ns    1511 ns       463899
-  #BM_Downmix/8     4445 ns    4431 ns       157831
-  #BM_Downmix/9     5081 ns    5065 ns       138412
-  #BM_Downmix/10    4354 ns    4341 ns       161247
-  #BM_Downmix/11    4411 ns    4397 ns       158893
-  #BM_Downmix/12    4434 ns    4420 ns       157992
-  #BM_Downmix/13    4845 ns    4830 ns       144873
-  #BM_Downmix/14    4851 ns    4835 ns       144954
-  #BM_Downmix/15    4884 ns    4870 ns       144233
-  #BM_Downmix/16    5832 ns    5813 ns       120565
-  #BM_Downmix/17    5241 ns    5224 ns       133927
-  #BM_Downmix/18    5044 ns    5028 ns       139131
-  #BM_Downmix/19    5244 ns    5227 ns       132315
-  #BM_Downmix/20    5943 ns    5923 ns       117759
-  #BM_Downmix/21    5990 ns    5971 ns       117263
-  #BM_Downmix/22    4468 ns    4454 ns       156689
-  #BM_Downmix/23    7306 ns    7286 ns        95911
---
-downmix_benchmark: (generic fold)
-  #BM_Downmix/0     4722 ns    4707 ns       149847
-  #BM_Downmix/1     4714 ns    4698 ns       148748
-  #BM_Downmix/2     4794 ns    4779 ns       145661
-  #BM_Downmix/3     5053 ns    5035 ns       139172
-  #BM_Downmix/4     4695 ns    4678 ns       149762
-  #BM_Downmix/5     4381 ns    4368 ns       159675
-  #BM_Downmix/6     4387 ns    4373 ns       160267
-  #BM_Downmix/7     4732 ns    4717 ns       148514
-  #BM_Downmix/8     4430 ns    4415 ns       158133
-  #BM_Downmix/9     5101 ns    5084 ns       138353
-  #BM_Downmix/10    4356 ns    4343 ns       160821
-  #BM_Downmix/11    4397 ns    4383 ns       159995
-  #BM_Downmix/12    4438 ns    4424 ns       158117
-  #BM_Downmix/13    5243 ns    5226 ns       133863
-  #BM_Downmix/14    5259 ns    5242 ns       131855
-  #BM_Downmix/15    5245 ns    5228 ns       133686
-  #BM_Downmix/16    5829 ns    5809 ns       120543
-  #BM_Downmix/17    5245 ns    5228 ns       133533
-  #BM_Downmix/18    5935 ns    5916 ns       118282
-  #BM_Downmix/19    5263 ns    5245 ns       133657
-  #BM_Downmix/20    5998 ns    5978 ns       114693
-  #BM_Downmix/21    5989 ns    5969 ns       117450
-  #BM_Downmix/22    4442 ns    4431 ns       157913
-  #BM_Downmix/23    7309 ns    7290 ns        95797
+Pixel 4XL
+--------------------------------------------------------
+Benchmark              Time             CPU   Iterations
+--------------------------------------------------------
+BM_Downmix/0        3553 ns         3545 ns       197503 AUDIO_CHANNEL_OUT_MONO
+BM_Downmix/1        2846 ns         2840 ns       202849
+BM_Downmix/2        4436 ns         4426 ns       158176 AUDIO_CHANNEL_OUT_STEREO
+BM_Downmix/3        5320 ns         5307 ns       131870 AUDIO_CHANNEL_OUT_2POINT1
+BM_Downmix/4        4437 ns         4426 ns       159523 AUDIO_CHANNEL_OUT_2POINT0POINT2
+BM_Downmix/5        2493 ns         2487 ns       281496 AUDIO_CHANNEL_OUT_QUAD
+BM_Downmix/6        2493 ns         2487 ns       281456 AUDIO_CHANNEL_OUT_QUAD_SIDE
+BM_Downmix/7        6204 ns         6188 ns       115044 AUDIO_CHANNEL_OUT_SURROUND
+BM_Downmix/8        5320 ns         5307 ns       100000 AUDIO_CHANNEL_OUT_2POINT1POINT2
+BM_Downmix/9        5320 ns         5307 ns       100000 AUDIO_CHANNEL_OUT_3POINT0POINT2
+BM_Downmix/10       7088 ns         7071 ns       108264 AUDIO_CHANNEL_OUT_PENTA
+BM_Downmix/11       6203 ns         6188 ns       117021 AUDIO_CHANNEL_OUT_3POINT1POINT2
+BM_Downmix/12       3105 ns         3097 ns       226182 AUDIO_CHANNEL_OUT_5POINT1
+BM_Downmix/13       3112 ns         3105 ns       225488 AUDIO_CHANNEL_OUT_5POINT1_SIDE
+BM_Downmix/14       8855 ns         8831 ns        79265 AUDIO_CHANNEL_OUT_6POINT1
+BM_Downmix/15       7971 ns         7951 ns        90918 AUDIO_CHANNEL_OUT_5POINT1POINT2
+BM_Downmix/16       3547 ns         3539 ns       197780 AUDIO_CHANNEL_OUT_7POINT1
+BM_Downmix/17       7972 ns         7953 ns        90101 AUDIO_CHANNEL_OUT_5POINT1POINT4
+BM_Downmix/18       9737 ns         9714 ns        72773 AUDIO_CHANNEL_OUT_7POINT1POINT2
+BM_Downmix/19       9745 ns         9721 ns        72015 AUDIO_CHANNEL_OUT_7POINT1POINT4
+BM_Downmix/20       7070 ns         7053 ns       109476 AUDIO_CHANNEL_OUT_13POINT_360RA
+BM_Downmix/21      12413 ns        12381 ns        57455 AUDIO_CHANNEL_OUT_22POINT2
 */
 
 static void BM_Downmix(benchmark::State& state) {
@@ -125,7 +97,7 @@
     std::minstd_rand gen(channelMask);
     std::uniform_real_distribution<> dis(-1.0f, 1.0f);
     std::vector<float> input(kFrameCount * channelCount);
-    std::vector<float> output(kFrameCount * 2);
+    std::vector<float> output(kFrameCount * FCC_2);
     for (auto& in : input) {
         in = dis(gen);
     }
@@ -187,7 +159,8 @@
         benchmark::ClobberMemory();
     }
 
-    state.SetComplexityN(state.range(0));
+    state.SetComplexityN(channelCount);
+    state.SetLabel(audio_channel_out_mask_to_string(channelMask));
 
     if (int status = AUDIO_EFFECT_LIBRARY_INFO_SYM.release_effect(effectHandle); status != 0) {
         ALOGE("release_effect returned an error = %d\n", status);
diff --git a/media/libeffects/downmix/tests/downmix_tests.cpp b/media/libeffects/downmix/tests/downmix_tests.cpp
index d4b7a3a..d378044 100644
--- a/media/libeffects/downmix/tests/downmix_tests.cpp
+++ b/media/libeffects/downmix/tests/downmix_tests.cpp
@@ -33,16 +33,14 @@
     AUDIO_CHANNEL_OUT_STEREO,
     AUDIO_CHANNEL_OUT_2POINT1,
     AUDIO_CHANNEL_OUT_2POINT0POINT2,
-    AUDIO_CHANNEL_OUT_QUAD,
-    AUDIO_CHANNEL_OUT_QUAD_BACK,
+    AUDIO_CHANNEL_OUT_QUAD, // AUDIO_CHANNEL_OUT_QUAD_BACK
     AUDIO_CHANNEL_OUT_QUAD_SIDE,
     AUDIO_CHANNEL_OUT_SURROUND,
     AUDIO_CHANNEL_OUT_2POINT1POINT2,
     AUDIO_CHANNEL_OUT_3POINT0POINT2,
     AUDIO_CHANNEL_OUT_PENTA,
     AUDIO_CHANNEL_OUT_3POINT1POINT2,
-    AUDIO_CHANNEL_OUT_5POINT1,
-    AUDIO_CHANNEL_OUT_5POINT1_BACK,
+    AUDIO_CHANNEL_OUT_5POINT1, // AUDIO_CHANNEL_OUT_5POINT1_BACK
     AUDIO_CHANNEL_OUT_5POINT1_SIDE,
     AUDIO_CHANNEL_OUT_6POINT1,
     AUDIO_CHANNEL_OUT_5POINT1POINT2,
@@ -57,6 +55,33 @@
 static constexpr audio_channel_mask_t kConsideredChannels =
     (audio_channel_mask_t)(AUDIO_CHANNEL_OUT_7POINT1 | AUDIO_CHANNEL_OUT_BACK_CENTER);
 
+constexpr inline float kScaleFromChannelIdx[] = {
+    0.5f, // AUDIO_CHANNEL_OUT_FRONT_LEFT            = 0x1u,
+    0.5f, // AUDIO_CHANNEL_OUT_FRONT_RIGHT           = 0x2u,
+    0.5f * M_SQRT1_2, // AUDIO_CHANNEL_OUT_FRONT_CENTER          = 0x4u,
+    0.5f * M_SQRT1_2, // AUDIO_CHANNEL_OUT_LOW_FREQUENCY         = 0x8u,
+    0.5f, // AUDIO_CHANNEL_OUT_BACK_LEFT             = 0x10u,
+    0.5f, // AUDIO_CHANNEL_OUT_BACK_RIGHT            = 0x20u,
+    0,    // AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER  = 0x40u,
+    0,    // AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER = 0x80u,
+    0.5f * M_SQRT1_2, // AUDIO_CHANNEL_OUT_BACK_CENTER           = 0x100u,
+    0.5f, // AUDIO_CHANNEL_OUT_SIDE_LEFT             = 0x200u,
+    0.5f, // AUDIO_CHANNEL_OUT_SIDE_RIGHT            = 0x400u,
+    0, // AUDIO_CHANNEL_OUT_TOP_CENTER            = 0x800u,
+    0, // AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT        = 0x1000u,
+    0, // AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER      = 0x2000u,
+    0, // AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT       = 0x4000u,
+    0, // AUDIO_CHANNEL_OUT_TOP_BACK_LEFT         = 0x8000u,
+    0, // AUDIO_CHANNEL_OUT_TOP_BACK_CENTER       = 0x10000u,
+    0, // AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT        = 0x20000u,
+    0, // AUDIO_CHANNEL_OUT_TOP_SIDE_LEFT         = 0x40000u,
+    0, // AUDIO_CHANNEL_OUT_TOP_SIDE_RIGHT        = 0x80000u,
+    0, // AUDIO_CHANNEL_OUT_BOTTOM_FRONT_LEFT     = 0x100000u,
+    0, // AUDIO_CHANNEL_OUT_BOTTOM_FRONT_CENTER   = 0x200000u,
+    0, // AUDIO_CHANNEL_OUT_BOTTOM_FRONT_RIGHT    = 0x400000u,
+    0, // AUDIO_CHANNEL_OUT_LOW_FREQUENCY_2       = 0x800000u,
+};
+
 // Downmix doesn't change with sample rate
 static constexpr size_t kSampleRates[] = {
     48000,
@@ -93,8 +118,8 @@
     void testBalance(int sampleRate, audio_channel_mask_t channelMask) {
         using namespace ::android::audio_utils::channels;
 
-        size_t frames = 100;
-        unsigned outChannels = 2;
+        size_t frames = 100; // set to an even number (2, 4, 6 ... ) stream alternates +1, -1.
+        constexpr unsigned outChannels = 2;
         unsigned inChannels = audio_channel_count_from_out_mask(channelMask);
         std::vector<float> input(frames * inChannels);
         std::vector<float> output(frames * outChannels);
@@ -119,7 +144,7 @@
 
             auto stats = channelStatistics(output, 2 /* channels */);
             // printf("power: %s %s\n", stats[0].toString().c_str(), stats[1].toString().c_str());
-            double power[2] = { stats[0].getVariance(), stats[1].getVariance() };
+            double power[2] = { stats[0].getPopVariance(), stats[1].getPopVariance() };
 
             // Check symmetric power for pair channels on exchange of left/right position.
             // to do this, we save previous power measurements.
@@ -139,20 +164,21 @@
                 EXPECT_EQ(0.f, power[1]);
                 continue;
             }
-            constexpr float POWER_TOLERANCE = 0.01;  // for variance sum error.
+
+            constexpr float POWER_TOLERANCE = 0.001;
+            const float expectedPower = kScaleFromChannelIdx[index] * kScaleFromChannelIdx[index];
             switch (side) {
             case AUDIO_GEOMETRY_SIDE_LEFT:
-                EXPECT_NEAR(0.25f, power[0], POWER_TOLERANCE);
-                EXPECT_EQ(0.f, power[1]);
+                EXPECT_EQ(0.f, power[1]); // always true
+                EXPECT_NEAR(expectedPower, power[0], POWER_TOLERANCE);
                 break;
             case AUDIO_GEOMETRY_SIDE_RIGHT:
-                EXPECT_EQ(0.f, power[0]);
-                EXPECT_NEAR(0.25f, power[1], POWER_TOLERANCE);
+                EXPECT_EQ(0.f, power[0]); // always true
+                EXPECT_NEAR(expectedPower, power[1], POWER_TOLERANCE);
                 break;
             case AUDIO_GEOMETRY_SIDE_CENTER:
-                EXPECT_NEAR(0.125f, power[0], POWER_TOLERANCE);
-                EXPECT_NEAR(0.125f, power[1], POWER_TOLERANCE);
-                EXPECT_NEAR_EPSILON(power[0], power[1]);
+                EXPECT_NEAR_EPSILON(power[0], power[1]); // always true
+                EXPECT_NEAR(expectedPower, power[0], POWER_TOLERANCE);
                 break;
             }
         }
@@ -244,10 +270,11 @@
         ::testing::Combine(
                 ::testing::Range(0, (int)std::size(kSampleRates)),
                 ::testing::Range(0, (int)std::size(kChannelPositionMasks))
-                ));
-
-int main(int argc, /* const */ char** argv) {
-    ::testing::InitGoogleTest(&argc, argv);
-    int status = RUN_ALL_TESTS();
-    return status;
-}
+                ),
+        [](const testing::TestParamInfo<DownmixTest::ParamType>& info) {
+            const int index = std::get<1>(info.param);
+            const audio_channel_mask_t channelMask = kChannelPositionMasks[index];
+            const std::string name = std::string(audio_channel_out_mask_to_string(channelMask))
+                + "_" + std::to_string(std::get<0>(info.param)) + "_" + std::to_string(index);
+            return name;
+        });