DynamicsProcessing: Add test for threshold and ratio parameters of MBCBandConfig

This test compresses only one band at a time and validates that the
output magnitude of the compressed band is less than that of the other
band.

Bug: 305866207
Test: atest VtsHalDynamicsProcessingTargetTest
Change-Id: Iaeeb3968c47e2478fd5e528ae36d84c4dcc625a2
diff --git a/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp b/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp
index 09b6621..1e6f186 100644
--- a/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp
+++ b/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp
@@ -506,6 +506,27 @@
     limiterConfigList.push_back(cfg);
 }
 
+DynamicsProcessing::MbcBandConfig createMbcBandConfig(int channel, int band, float cutoffFreqHz,
+                                                      float attackTimeMs, float releaseTimeMs,
+                                                      float ratio, float thresholdDb,
+                                                      float kneeWidthDb, float noiseGate,
+                                                      float expanderRatio, float preGainDb,
+                                                      float postGainDb) {
+    return DynamicsProcessing::MbcBandConfig{.channel = channel,
+                                             .band = band,
+                                             .enable = true,
+                                             .cutoffFrequencyHz = cutoffFreqHz,
+                                             .attackTimeMs = attackTimeMs,
+                                             .releaseTimeMs = releaseTimeMs,
+                                             .ratio = ratio,
+                                             .thresholdDb = thresholdDb,
+                                             .kneeWidthDb = kneeWidthDb,
+                                             .noiseGateThresholdDb = noiseGate,
+                                             .expanderRatio = expanderRatio,
+                                             .preGainDb = preGainDb,
+                                             .postGainDb = postGainDb};
+}
+
 /**
  * Test DynamicsProcessing Engine Configuration
  */
@@ -818,7 +839,7 @@
             fillLimiterConfig(mLimiterConfigList, i, true, kDefaultLinkerGroup, kDefaultAttackTime,
                               kDefaultReleaseTime, kDefaultRatio, threshold, kDefaultPostGain);
         }
-        EXPECT_NO_FATAL_FAILURE(setLimiterParamsAndProcess(mInput, output));
+        ASSERT_NO_FATAL_FAILURE(setLimiterParamsAndProcess(mInput, output));
         if (!isAllParamsValid()) {
             continue;
         }
@@ -827,7 +848,7 @@
             EXPECT_NEAR(mInputDb, outputDb, kToleranceDb);
         } else {
             float calculatedThreshold = 0;
-            EXPECT_NO_FATAL_FAILURE(computeThreshold(kDefaultRatio, outputDb, calculatedThreshold));
+            ASSERT_NO_FATAL_FAILURE(computeThreshold(kDefaultRatio, outputDb, calculatedThreshold));
             ASSERT_GT(calculatedThreshold, previousThreshold);
             previousThreshold = calculatedThreshold;
         }
@@ -844,7 +865,7 @@
             fillLimiterConfig(mLimiterConfigList, i, true, kDefaultLinkerGroup, kDefaultAttackTime,
                               kDefaultReleaseTime, ratio, kDefaultThreshold, kDefaultPostGain);
         }
-        EXPECT_NO_FATAL_FAILURE(setLimiterParamsAndProcess(mInput, output));
+        ASSERT_NO_FATAL_FAILURE(setLimiterParamsAndProcess(mInput, output));
         if (!isAllParamsValid()) {
             continue;
         }
@@ -854,7 +875,7 @@
             EXPECT_NEAR(mInputDb, outputDb, kToleranceDb);
         } else {
             float calculatedRatio = 0;
-            EXPECT_NO_FATAL_FAILURE(computeRatio(kDefaultThreshold, outputDb, calculatedRatio));
+            ASSERT_NO_FATAL_FAILURE(computeRatio(kDefaultThreshold, outputDb, calculatedRatio));
             ASSERT_GT(calculatedRatio, previousRatio);
             previousRatio = calculatedRatio;
         }
@@ -870,7 +891,7 @@
             fillLimiterConfig(mLimiterConfigList, i, true, kDefaultLinkerGroup, kDefaultAttackTime,
                               kDefaultReleaseTime, kDefaultRatio, -1, postGainDb);
         }
-        EXPECT_NO_FATAL_FAILURE(setLimiterParamsAndProcess(mInput, output));
+        ASSERT_NO_FATAL_FAILURE(setLimiterParamsAndProcess(mInput, output));
         if (!isAllParamsValid()) {
             continue;
         }
@@ -891,7 +912,7 @@
                               5 /*attack time*/, 5 /*release time*/, 10 /*ratio*/,
                               -10 /*threshold*/, 5 /*postgain*/);
         }
-        EXPECT_NO_FATAL_FAILURE(setLimiterParamsAndProcess(mInput, output));
+        ASSERT_NO_FATAL_FAILURE(setLimiterParamsAndProcess(mInput, output));
         if (!isAllParamsValid()) {
             continue;
         }
@@ -1150,25 +1171,21 @@
 
 void fillMbcBandConfig(std::vector<DynamicsProcessing::MbcBandConfig>& cfgs,
                        const TestParamsMbcBandConfig& params) {
-    const std::vector<std::pair<int, float>> cutOffFreqs = std::get<MBC_BAND_CUTOFF_FREQ>(params);
-    const std::array<float, MBC_ADD_MAX_NUM> additional = std::get<MBC_BAND_ADDITIONAL>(params);
-    int bandCount = cutOffFreqs.size();
-    cfgs.resize(bandCount);
-    for (int i = 0; i < bandCount; i++) {
-        cfgs[i] = DynamicsProcessing::MbcBandConfig{
-                .channel = std::get<MBC_BAND_CHANNEL>(params),
-                .band = cutOffFreqs[i].first,
-                .enable = true /*Mbc Band Enable*/,
-                .cutoffFrequencyHz = cutOffFreqs[i].second,
-                .attackTimeMs = additional[MBC_ADD_ATTACK_TIME],
-                .releaseTimeMs = additional[MBC_ADD_RELEASE_TIME],
-                .ratio = additional[MBC_ADD_RATIO],
-                .thresholdDb = additional[MBC_ADD_THRESHOLD],
-                .kneeWidthDb = additional[MBC_ADD_KNEE_WIDTH],
-                .noiseGateThresholdDb = additional[MBC_ADD_NOISE_GATE_THRESHOLD],
-                .expanderRatio = additional[MBC_ADD_EXPENDER_RATIO],
-                .preGainDb = additional[MBC_ADD_PRE_GAIN],
-                .postGainDb = additional[MBC_ADD_POST_GAIN]};
+    const auto& cutOffFreqs = std::get<MBC_BAND_CUTOFF_FREQ>(params);
+    const auto& additional = std::get<MBC_BAND_ADDITIONAL>(params);
+
+    cfgs.resize(cutOffFreqs.size());
+
+    for (size_t i = 0; i < cutOffFreqs.size(); ++i) {
+        cfgs[i] = createMbcBandConfig(std::get<MBC_BAND_CHANNEL>(params),
+                                      cutOffFreqs[i].first,   // band channel
+                                      cutOffFreqs[i].second,  // band cutoff frequency
+                                      additional[MBC_ADD_ATTACK_TIME],
+                                      additional[MBC_ADD_RELEASE_TIME], additional[MBC_ADD_RATIO],
+                                      additional[MBC_ADD_THRESHOLD], additional[MBC_ADD_KNEE_WIDTH],
+                                      additional[MBC_ADD_NOISE_GATE_THRESHOLD],
+                                      additional[MBC_ADD_EXPENDER_RATIO],
+                                      additional[MBC_ADD_PRE_GAIN], additional[MBC_ADD_POST_GAIN]);
     }
 }
 
@@ -1222,6 +1239,160 @@
         });
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DynamicsProcessingTestMbcBandConfig);
 
+class DynamicsProcessingMbcBandConfigDataTest
+    : public ::testing::TestWithParam<std::pair<std::shared_ptr<IFactory>, Descriptor>>,
+      public DynamicsProcessingTestHelper {
+  public:
+    DynamicsProcessingMbcBandConfigDataTest()
+        : DynamicsProcessingTestHelper(GetParam(), AudioChannelLayout::LAYOUT_MONO) {
+        mInput.resize(kFrameCount * mChannelCount);
+        mBinOffsets.resize(mTestFrequencies.size());
+    }
+
+    void SetUp() override {
+        SetUpDynamicsProcessingEffect();
+        SKIP_TEST_IF_DATA_UNSUPPORTED(mDescriptor.common.flags);
+        ASSERT_NO_FATAL_FAILURE(generateSineWave(mTestFrequencies, mInput, 1.0, kSamplingFrequency,
+                                                 mChannelLayout));
+    }
+
+    void TearDown() override { TearDownDynamicsProcessingEffect(); }
+
+    void setMbcParamsAndProcess(std::vector<float>& output) {
+        for (int i = 0; i < mChannelCount; i++) {
+            mChannelConfig.push_back(DynamicsProcessing::ChannelConfig(i, true));
+        }
+        mEngineConfigPreset.mbcStage.bandCount = mCfgs.size();
+        addEngineConfig(mEngineConfigPreset);
+        addMbcChannelConfig(mChannelConfig);
+        addMbcBandConfigs(mCfgs);
+
+        if (isAllParamsValid()) {
+            ASSERT_NO_FATAL_FAILURE(SetAndGetDynamicsProcessingParameters());
+            ASSERT_NO_FATAL_FAILURE(
+                    processAndWriteToOutput(mInput, output, mEffect, &mOpenEffectReturn));
+        }
+    }
+
+    void fillMbcBandConfig(std::vector<DynamicsProcessing::MbcBandConfig>& cfgs, int channelIndex,
+                           float threshold, float ratio, float noiseGate, float expanderRatio,
+                           int bandIndex, int cutoffFreqHz) {
+        cfgs.push_back(createMbcBandConfig(
+                channelIndex, bandIndex, static_cast<float>(cutoffFreqHz), kDefaultAttackTime,
+                kDefaultReleaseTime, ratio, threshold, kDefaultKneeWidth, noiseGate, expanderRatio,
+                kDefaultPreGainDb, kDefaultPostGainDb));
+    }
+
+    void getMagnitudeValue(const std::vector<float>& output, std::vector<float>& bufferMag) {
+        std::vector<float> subOutput(output.begin() + kStartIndex, output.end());
+        EXPECT_NO_FATAL_FAILURE(
+                calculateMagnitudeMono(bufferMag, subOutput, mBinOffsets, kNPointFFT));
+    }
+
+    void validateOutput(const std::vector<float>& output, float threshold, float ratio,
+                        size_t bandIndex) {
+        float inputDb = calculateDb(mInput);
+        std::vector<float> outputMag(mBinOffsets.size());
+        EXPECT_NO_FATAL_FAILURE(getMagnitudeValue(output, outputMag));
+        if (threshold >= inputDb || ratio == 1) {
+            std::vector<float> inputMag(mBinOffsets.size());
+            EXPECT_NO_FATAL_FAILURE(getMagnitudeValue(mInput, inputMag));
+            for (size_t i = 0; i < inputMag.size(); i++) {
+                EXPECT_NEAR(calculateDb({inputMag[i] / mNormalizingFactor}),
+                            calculateDb({outputMag[i] / mNormalizingFactor}), kToleranceDb);
+            }
+        } else {
+            // Current band's magnitude is less than the other band's magnitude
+            EXPECT_LT(outputMag[bandIndex], outputMag[bandIndex ^ 1]);
+        }
+    }
+
+    void analyseMultiBandOutput(float threshold, float ratio) {
+        std::vector<float> output(mInput.size());
+        roundToFreqCenteredToFftBin(mTestFrequencies, mBinOffsets, kBinWidth);
+        std::vector<int> cutoffFreqHz = {200 /*0th band cutoff*/, 2000 /*1st band cutoff*/};
+        // Set MBC values for two bands
+        for (size_t i = 0; i < cutoffFreqHz.size(); i++) {
+            for (int channelIndex = 0; channelIndex < mChannelCount; channelIndex++) {
+                fillMbcBandConfig(mCfgs, channelIndex, threshold, ratio, kDefaultNoiseGateDb,
+                                  kDefaultExpanderRatio, i, cutoffFreqHz[i]);
+                fillMbcBandConfig(mCfgs, channelIndex, kDefaultThresholdDb, kDefaultRatio,
+                                  kDefaultNoiseGateDb, kDefaultExpanderRatio, i ^ 1,
+                                  cutoffFreqHz[i ^ 1]);
+            }
+            ASSERT_NO_FATAL_FAILURE(setMbcParamsAndProcess(output));
+
+            if (isAllParamsValid()) {
+                ASSERT_NO_FATAL_FAILURE(validateOutput(output, threshold, ratio, i));
+            }
+            cleanUpMbcConfig();
+        }
+    }
+
+    void cleanUpMbcConfig() {
+        CleanUp();
+        mCfgs.clear();
+        mChannelConfig.clear();
+    }
+
+    static constexpr int kNPointFFT = 1024;
+    static constexpr float kToleranceDb = 0.5;
+    static constexpr float kDefaultPostGainDb = 0;
+    static constexpr float kDefaultPreGainDb = 0;
+    static constexpr float kDefaultAttackTime = 0;
+    static constexpr float kDefaultReleaseTime = 0;
+    static constexpr float kDefaultKneeWidth = 0;
+    static constexpr float kDefaultThresholdDb = 0;
+    static constexpr float kDefaultNoiseGateDb = -10;
+    static constexpr float kDefaultExpanderRatio = 1;
+    static constexpr float kDefaultRatio = 1;
+    static constexpr float kBinWidth = (float)kSamplingFrequency / kNPointFFT;
+    std::vector<int> mTestFrequencies = {100, 1000};
+    // Calculating normalizing factor by dividing the number of FFT points by half and the number of
+    // test frequencies. The normalization accounts for the FFT splitting the signal into positive
+    // and negative frequencies. Additionally, during multi-tone input generation, sample values are
+    // normalized to the range [-1, 1] by dividing them by the number of test frequencies.
+    float mNormalizingFactor = (kNPointFFT / (2 * mTestFrequencies.size()));
+    std::vector<DynamicsProcessing::MbcBandConfig> mCfgs;
+    std::vector<DynamicsProcessing::ChannelConfig> mChannelConfig;
+    std::vector<int> mBinOffsets;
+    std::vector<float> mInput;
+};
+
+TEST_P(DynamicsProcessingMbcBandConfigDataTest, IncreasingThreshold) {
+    float ratio = 20;
+    std::vector<float> thresholdValues = {-200, -100, 0, 100, 200};
+
+    for (float threshold : thresholdValues) {
+        cleanUpMbcConfig();
+        ASSERT_NO_FATAL_FAILURE(analyseMultiBandOutput(threshold, ratio));
+    }
+}
+
+TEST_P(DynamicsProcessingMbcBandConfigDataTest, IncreasingRatio) {
+    float threshold = -20;
+    std::vector<float> ratioValues = {1, 10, 20, 30, 40, 50};
+
+    for (float ratio : ratioValues) {
+        cleanUpMbcConfig();
+        ASSERT_NO_FATAL_FAILURE(analyseMultiBandOutput(threshold, ratio));
+    }
+}
+
+INSTANTIATE_TEST_SUITE_P(DynamicsProcessingTest, DynamicsProcessingMbcBandConfigDataTest,
+                         testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
+                                 IFactory::descriptor, getEffectTypeUuidDynamicsProcessing())),
+                         [](const auto& info) {
+                             auto descriptor = info.param;
+                             std::string name = getPrefix(descriptor.second);
+                             std::replace_if(
+                                     name.begin(), name.end(),
+                                     [](const char c) { return !std::isalnum(c); }, '_');
+                             return name;
+                         });
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DynamicsProcessingMbcBandConfigDataTest);
+
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
     ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());