Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b39b1dcb authored by Mikhail Naganov's avatar Mikhail Naganov Committed by Gerrit Code Review
Browse files

Merge "DynamicsProcessing: Add test for threshold and ratio parameters of MBCBandConfig" into main

parents 8ca47767 c3252765
Loading
Loading
Loading
Loading
+196 −25
Original line number Diff line number Diff line
@@ -506,6 +506,27 @@ void fillLimiterConfig(std::vector<DynamicsProcessing::LimiterConfig>& limiterCo
    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 @@ TEST_P(DynamicsProcessingLimiterConfigDataTest, IncreasingThresholdDb) {
            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 @@ TEST_P(DynamicsProcessingLimiterConfigDataTest, IncreasingThresholdDb) {
            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 @@ TEST_P(DynamicsProcessingLimiterConfigDataTest, IncreasingRatio) {
            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 @@ TEST_P(DynamicsProcessingLimiterConfigDataTest, IncreasingRatio) {
            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 @@ TEST_P(DynamicsProcessingLimiterConfigDataTest, IncreasingPostGain) {
            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 @@ TEST_P(DynamicsProcessingLimiterConfigDataTest, LimiterEnableDisable) {
                              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 @@ using TestParamsMbcBandConfig =

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 @@ INSTANTIATE_TEST_SUITE_P(
        });
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());