libeffects: Add multichannel support to Reverb

Test: SoloTester and Clarity app
Test: Standalone application test
Bug: 129491957
Change-Id: Ifac02eb2a7ac3eba5fae9318c3ae1e1c7df4f681
diff --git a/media/libeffects/lvm/tests/build_and_run_all_unit_tests_reverb.sh b/media/libeffects/lvm/tests/build_and_run_all_unit_tests_reverb.sh
index 5a972db..0c3b0b5 100755
--- a/media/libeffects/lvm/tests/build_and_run_all_unit_tests_reverb.sh
+++ b/media/libeffects/lvm/tests/build_and_run_all_unit_tests_reverb.sh
@@ -41,47 +41,65 @@
     192000
 )
 
+flags_arr=(
+    "--M --fch 1"
+    "--fch 2"
+)
+
 # run reverb at different configs, saving only the stereo channel
 # pair.
 error_count=0
+testcase_count=0
 for cmd in "${cmds[@]}"
 do
     $cmd
-    for preset_val in {0..6}
+    for flags in "${flags_arr[@]}"
     do
-        for fs in ${fs_arr[*]}
+        for preset_val in {0..6}
         do
-            for chMask in {1..22}
+            for fs in ${fs_arr[*]}
             do
-                adb shell LD_LIBRARY_PATH=/system/vendor/lib/soundfx $testdir/reverb_test \
-                    --input $testdir/sinesweepraw.raw \
-                    --output $testdir/sinesweep_$((chMask))_$((fs)).raw \
-                    --chMask $chMask --fs $fs --preset $preset_val
+                for chMask in {0..22}
+                do
+                    adb shell LD_LIBRARY_PATH=/system/vendor/lib/soundfx $testdir/reverb_test \
+                        --input $testdir/sinesweepraw.raw \
+                        --output $testdir/sinesweep_$((chMask))_$((fs)).raw \
+                        --chMask $chMask $flags --fs $fs --preset $preset_val
 
-                shell_ret=$?
-                if [ $shell_ret -ne 0 ]; then
-                    echo "error: $shell_ret"
-                    ((++error_count))
-                fi
+                    shell_ret=$?
+                    if [ $shell_ret -ne 0 ]; then
+                        echo "error: $shell_ret"
+                        ((++error_count))
+                    fi
 
-                # two channel files should be identical to higher channel
-                # computation (first 2 channels).
-                if [[ "$chMask" -gt 1 ]]
-                then
-                    adb shell cmp $testdir/sinesweep_1_$((fs)).raw \
-                        $testdir/sinesweep_$((chMask))_$((fs)).raw
-                fi
-                # cmp returns EXIT_FAILURE on mismatch.
-                shell_ret=$?
-                if [ $shell_ret -ne 0 ]; then
-                    echo "error: $shell_ret"
-                    ((++error_count))
-                fi
+                    if [[ "$chMask" -gt 0 ]] && [[ $flags != *"--fch 2"* ]]
+                    then
+                        # single channel files should be identical to higher channel
+                        # computation (first channel).
+                        adb shell cmp $testdir/sinesweep_0_$((fs)).raw \
+                            $testdir/sinesweep_$((chMask))_$((fs)).raw
+                    elif [[ "$chMask" -gt 1 ]]
+                    then
+                        # two channel files should be identical to higher channel
+                        # computation (first 2 channels).
+                        adb shell cmp $testdir/sinesweep_1_$((fs)).raw \
+                            $testdir/sinesweep_$((chMask))_$((fs)).raw
+                    fi
+
+                    # cmp returns EXIT_FAILURE on mismatch.
+                    shell_ret=$?
+                    if [ $shell_ret -ne 0 ]; then
+                        echo "error: $shell_ret"
+                        ((++error_count))
+                    fi
+                    ((++testcase_count))
+                done
             done
         done
     done
 done
 
 adb shell rm -r $testdir
+echo "$testcase_count tests performed"
 echo "$error_count errors"
 exit $error_count
diff --git a/media/libeffects/lvm/tests/reverb_test.cpp b/media/libeffects/lvm/tests/reverb_test.cpp
index a9cf348..f403229 100644
--- a/media/libeffects/lvm/tests/reverb_test.cpp
+++ b/media/libeffects/lvm/tests/reverb_test.cpp
@@ -297,6 +297,9 @@
   config.inputCfg.samplingRate = config.outputCfg.samplingRate = revConfigParams.sampleRate;
   config.inputCfg.channels = config.outputCfg.channels = revConfigParams.chMask;
   config.inputCfg.format = config.outputCfg.format = AUDIO_FORMAT_PCM_FLOAT;
+  if (AUDIO_CHANNEL_OUT_MONO == revConfigParams.chMask) {
+    config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
+  }
   if (int status =
           reverbCreateEffect(&effectHandle, &config, sessionId, ioId, revConfigParams.auxiliary);
       status != 0) {
@@ -332,15 +335,15 @@
    * Mono input will be converted to 2 channels internally in the process call
    * by copying the same data into the second channel.
    * Hence when channelCount is 1, output buffer should be allocated for
-   * 2 channels. The memAllocChCount takes care of allocation of sufficient
+   * 2 channels. The outChannelCount takes care of allocation of sufficient
    * memory for the output buffer.
    */
-  const int memAllocChCount = (channelCount == 1 ? 2 : channelCount);
+  const int outChannelCount = (channelCount == 1 ? 2 : channelCount);
 
   std::vector<short> in(frameLength * maxChannelCount);
   std::vector<short> out(frameLength * maxChannelCount);
   std::vector<float> floatIn(frameLength * channelCount);
-  std::vector<float> floatOut(frameLength * memAllocChCount);
+  std::vector<float> floatOut(frameLength * outChannelCount);
 
   int frameCounter = 0;
 
@@ -374,11 +377,11 @@
 #else
     memcpy(floatOut.data(), floatIn.data(), frameLength * frameSize);
 #endif
-    memcpy_to_i16_from_float(out.data(), floatOut.data(), frameLength * channelCount);
+    memcpy_to_i16_from_float(out.data(), floatOut.data(), frameLength * outChannelCount);
 
-    if (ioChannelCount != channelCount) {
-      adjust_channels(out.data(), channelCount, out.data(), ioChannelCount, sizeof(short),
-                      frameLength * channelCount * sizeof(short));
+    if (ioChannelCount != outChannelCount) {
+      adjust_channels(out.data(), outChannelCount, out.data(), ioChannelCount, sizeof(short),
+                      frameLength * outChannelCount * sizeof(short));
     }
     (void)fwrite(out.data(), ioFrameSize, frameLength, outputFp.get());
     frameCounter += frameLength;
diff --git a/media/libeffects/lvm/wrapper/Android.bp b/media/libeffects/lvm/wrapper/Android.bp
index afc4220..021020c 100644
--- a/media/libeffects/lvm/wrapper/Android.bp
+++ b/media/libeffects/lvm/wrapper/Android.bp
@@ -53,6 +53,7 @@
 
     cppflags: [
         "-fvisibility=hidden",
+        "-DSUPPORT_MC",
 
         "-Wall",
         "-Werror",
diff --git a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
index 39f5bb6..b95494d 100644
--- a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
+++ b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
@@ -259,6 +259,9 @@
 
     int channels = audio_channel_count_from_out_mask(pContext->config.inputCfg.channels);
 
+#ifdef SUPPORT_MC
+    channels = (pContext->auxiliary == true)? channels : FCC_2;
+#endif
     // Allocate memory for reverb process (*2 is for STEREO)
     pContext->bufferSizeIn = LVREV_MAX_FRAME_SIZE * sizeof(process_buffer_t) * channels;
     pContext->bufferSizeOut = LVREV_MAX_FRAME_SIZE * sizeof(process_buffer_t) * FCC_2;
@@ -343,11 +346,18 @@
     int channels = audio_channel_count_from_out_mask(pContext->config.inputCfg.channels);
     LVREV_ReturnStatus_en   LvmStatus = LVREV_SUCCESS;              /* Function call status */
 
-    // Check that the input is either mono or stereo
+    // Reverb only effects the stereo channels in multichannel source.
+#ifdef SUPPORT_MC
+    if (channels < 1 || channels > LVM_MAX_CHANNELS) {
+        ALOGE("\tLVREV_ERROR : process invalid PCM channels %d", channels);
+        return -EINVAL;
+    }
+#else
     if (!(channels == 1 || channels == FCC_2) ) {
         ALOGE("\tLVREV_ERROR : process invalid PCM format");
         return -EINVAL;
     }
+#endif
 
     size_t inSize = frameCount * sizeof(process_buffer_t) * channels;
     size_t outSize = frameCount * sizeof(process_buffer_t) * FCC_2;
@@ -380,12 +390,28 @@
         static_assert(std::is_same<decltype(*pIn), decltype(*pContext->InFrames)>::value,
                 "pIn and InFrames must be same type");
         memcpy(pContext->InFrames, pIn, frameCount * channels * sizeof(*pIn));
+    } else {
+        // mono input is duplicated
+#ifdef SUPPORT_MC
+        if (channels >= FCC_2) {
+            for (int i = 0; i < frameCount; i++) {
+                pContext->InFrames[FCC_2 * i] =
+                            (process_buffer_t)pIn[channels * i] * REVERB_SEND_LEVEL;
+                pContext->InFrames[FCC_2 * i + 1] =
+                            (process_buffer_t)pIn[channels * i + 1] * REVERB_SEND_LEVEL;
+            }
         } else {
-        // insert reverb input is always stereo
+            for (int i = 0; i < frameCount; i++) {
+                pContext->InFrames[FCC_2 * i] = pContext->InFrames[FCC_2 * i + 1] =
+                            (process_buffer_t)pIn[i] * REVERB_SEND_LEVEL;
+            }
+        }
+#else
         for (int i = 0; i < frameCount; i++) {
             pContext->InFrames[2 * i] = (process_buffer_t)pIn[2 * i] * REVERB_SEND_LEVEL;
             pContext->InFrames[2 * i + 1] = (process_buffer_t)pIn[2 * i + 1] * REVERB_SEND_LEVEL;
         }
+#endif
     }
 
     if (pContext->preset && pContext->curPreset == REVERB_PRESET_NONE) {
@@ -412,10 +438,26 @@
     if (pContext->auxiliary) {
         // nothing to do here
     } else {
+#ifdef SUPPORT_MC
+        if (channels >= FCC_2) {
+            for (int i = 0; i < frameCount; i++) {
+                // Mix with dry input
+                pContext->OutFrames[FCC_2 * i] += pIn[channels * i];
+                pContext->OutFrames[FCC_2 * i + 1] += pIn[channels * i + 1];
+            }
+        } else {
+            for (int i = 0; i < frameCount; i++) {
+                // Mix with dry input
+                pContext->OutFrames[FCC_2 * i] += pIn[i];
+                pContext->OutFrames[FCC_2 * i + 1] += pIn[i];
+            }
+        }
+#else
         for (int i = 0; i < frameCount * FCC_2; i++) { // always stereo here
             // Mix with dry input
             pContext->OutFrames[i] += pIn[i];
         }
+#endif
         // apply volume with ramp if needed
         if ((pContext->leftVolume != pContext->prevLeftVolume ||
                 pContext->rightVolume != pContext->prevRightVolume) &&
@@ -450,6 +492,35 @@
         }
     }
 
+#ifdef SUPPORT_MC
+    if (channels > 2) {
+        //Accumulate if required
+        if (pContext->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) {
+            for (int i = 0; i < frameCount; i++) {
+                pOut[channels * i] += pContext->OutFrames[FCC_2 * i];
+                pOut[channels * i + 1] += pContext->OutFrames[FCC_2 * i + 1];
+            }
+        } else {
+            for (int i = 0; i < frameCount; i++) {
+                pOut[channels * i] = pContext->OutFrames[FCC_2 * i];
+                pOut[channels * i + 1] = pContext->OutFrames[FCC_2 * i + 1];
+            }
+        }
+        for (int i = 0; i < frameCount; i++) {
+            for (int j = FCC_2; j < channels; j++) {
+                pOut[channels * i + j] = pIn[channels * i + j];
+            }
+        }
+    } else {
+        if (pContext->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) {
+            for (int i = 0; i < frameCount * FCC_2; i++) {
+                pOut[i] += pContext->OutFrames[i];
+            }
+        } else {
+            memcpy(pOut, pContext->OutFrames, frameCount * sizeof(*pOut) * FCC_2);
+        }
+    }
+#else
 
     // Accumulate if required
     if (pContext->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE){
@@ -462,6 +533,7 @@
         memcpy(pOut, pContext->OutFrames, frameCount * sizeof(*pOut) * FCC_2);
     }
 
+#endif
     return 0;
 }    /* end process */
 
@@ -525,9 +597,18 @@
 
     CHECK_ARG(pConfig->inputCfg.samplingRate == pConfig->outputCfg.samplingRate);
     CHECK_ARG(pConfig->inputCfg.format == pConfig->outputCfg.format);
+#ifdef SUPPORT_MC
+    int inputChannels = audio_channel_count_from_out_mask(pConfig->inputCfg.channels);
+    CHECK_ARG((pContext->auxiliary && pConfig->inputCfg.channels == AUDIO_CHANNEL_OUT_MONO) ||
+              ((!pContext->auxiliary) &&
+              (inputChannels <= LVM_MAX_CHANNELS)));
+    int outputChannels = audio_channel_count_from_out_mask(pConfig->outputCfg.channels);
+    CHECK_ARG(outputChannels >= FCC_2 && outputChannels <= LVM_MAX_CHANNELS);
+#else
     CHECK_ARG((pContext->auxiliary && pConfig->inputCfg.channels == AUDIO_CHANNEL_OUT_MONO) ||
               ((!pContext->auxiliary) && pConfig->inputCfg.channels == AUDIO_CHANNEL_OUT_STEREO));
     CHECK_ARG(pConfig->outputCfg.channels == AUDIO_CHANNEL_OUT_STEREO);
+#endif
     CHECK_ARG(pConfig->outputCfg.accessMode == EFFECT_BUFFER_ACCESS_WRITE
               || pConfig->outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE);
     CHECK_ARG(pConfig->inputCfg.format == EFFECT_BUFFER_FORMAT);
@@ -749,6 +830,11 @@
         params.SourceFormat   = LVM_STEREO;
     }
 
+#ifdef SUPPORT_MC
+    if ((pContext->auxiliary == false) && (params.SourceFormat == LVM_MONO)) {
+        params.SourceFormat   = LVM_STEREO;
+    }
+#endif
     /* Reverb parameters */
     params.Level          = 0;
     params.LPF            = 23999;