Loading audio/aidl/vts/Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ cc_defaults { "android.hardware.common.fmq-V1-ndk", "libaudioaidlcommon", "libaidlcommonsupport", "libpffft", ], header_libs: [ "libaudioaidl_headers", Loading @@ -36,6 +37,7 @@ cc_defaults { "-Wextra", "-Werror", "-Wthread-safety", "-Wno-error=unused-parameter", ], test_config_template: "VtsHalAudioTargetTestTemplate.xml", test_suites: [ Loading audio/aidl/vts/EffectHelper.h +42 −0 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ #include "EffectFactoryHelper.h" #include "TestUtils.h" #include "pffft.hpp" using namespace android; using aidl::android::hardware::audio::effect::CommandId; Loading Loading @@ -329,4 +330,45 @@ class EffectHelper { ASSERT_NO_FATAL_FAILURE(command(mEffect, CommandId::RESET)); ASSERT_NO_FATAL_FAILURE(expectState(mEffect, State::IDLE)); } // Find FFT bin indices for testFrequencies and get bin center frequencies void roundToFreqCenteredToFftBin(std::vector<int>& testFrequencies, std::vector<int>& binOffsets, const float kBinWidth) { for (size_t i = 0; i < testFrequencies.size(); i++) { binOffsets[i] = std::round(testFrequencies[i] / kBinWidth); testFrequencies[i] = std::round(binOffsets[i] * kBinWidth); } } // Generate multitone input between -1 to +1 using testFrequencies void generateMultiTone(const std::vector<int>& testFrequencies, std::vector<float>& input, const int samplingFrequency) { for (size_t i = 0; i < input.size(); i++) { input[i] = 0; for (size_t j = 0; j < testFrequencies.size(); j++) { input[i] += sin(2 * M_PI * testFrequencies[j] * i / samplingFrequency); } input[i] /= testFrequencies.size(); } } // Use FFT transform to convert the buffer to frequency domain // Compute its magnitude at binOffsets std::vector<float> calculateMagnitude(const std::vector<float>& buffer, const std::vector<int>& binOffsets, const int nPointFFT) { std::vector<float> fftInput(nPointFFT); PFFFT_Setup* inputHandle = pffft_new_setup(nPointFFT, PFFFT_REAL); pffft_transform_ordered(inputHandle, buffer.data(), fftInput.data(), nullptr, PFFFT_FORWARD); pffft_destroy_setup(inputHandle); std::vector<float> bufferMag(binOffsets.size()); for (size_t i = 0; i < binOffsets.size(); i++) { size_t k = binOffsets[i]; bufferMag[i] = sqrt((fftInput[k * 2] * fftInput[k * 2]) + (fftInput[k * 2 + 1] * fftInput[k * 2 + 1])); } return bufferMag; } }; audio/aidl/vts/VtsHalVolumeTargetTest.cpp +235 −72 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ using namespace android; using aidl::android::hardware::audio::common::getChannelCount; using aidl::android::hardware::audio::effect::Descriptor; using aidl::android::hardware::audio::effect::getEffectTypeUuidVolume; using aidl::android::hardware::audio::effect::IEffect; Loading @@ -29,110 +30,258 @@ using aidl::android::hardware::audio::effect::Parameter; using aidl::android::hardware::audio::effect::Volume; using android::hardware::audio::common::testing::detail::TestExecutionTracer; /** * Here we focus on specific parameter checking, general IEffect interfaces testing performed in * VtsAudioEffectTargetTest. */ enum ParamName { PARAM_INSTANCE_NAME, PARAM_LEVEL, PARAM_MUTE }; using VolumeParamTestParam = std::tuple<std::pair<std::shared_ptr<IFactory>, Descriptor>, int, bool>; class VolumeParamTest : public ::testing::TestWithParam<VolumeParamTestParam>, public EffectHelper { class VolumeControlHelper : public EffectHelper { public: VolumeParamTest() : mParamLevel(std::get<PARAM_LEVEL>(GetParam())), mParamMute(std::get<PARAM_MUTE>(GetParam())) { std::tie(mFactory, mDescriptor) = std::get<PARAM_INSTANCE_NAME>(GetParam()); } void SetUp() override { void SetUpVolumeControl() { ASSERT_NE(nullptr, mFactory); ASSERT_NO_FATAL_FAILURE(create(mFactory, mEffect, mDescriptor)); initFrameCount(); Parameter::Specific specific = getDefaultParamSpecific(); Parameter::Common common = EffectHelper::createParamCommon( 0 /* session */, 1 /* ioHandle */, 44100 /* iSampleRate */, 44100 /* oSampleRate */, kInputFrameCount /* iFrameCount */, kOutputFrameCount /* oFrameCount */); IEffect::OpenEffectReturn ret; ASSERT_NO_FATAL_FAILURE(open(mEffect, common, specific, &ret, EX_NONE)); 0 /* session */, 1 /* ioHandle */, kSamplingFrequency /* iSampleRate */, kSamplingFrequency /* oSampleRate */, mInputFrameCount /* iFrameCount */, mInputFrameCount /* oFrameCount */); ASSERT_NO_FATAL_FAILURE(open(mEffect, common, specific, &mOpenEffectReturn, EX_NONE)); ASSERT_NE(nullptr, mEffect); } void TearDown() override { void TearDownVolumeControl() { ASSERT_NO_FATAL_FAILURE(close(mEffect)); ASSERT_NO_FATAL_FAILURE(destroy(mFactory, mEffect)); mOpenEffectReturn = IEffect::OpenEffectReturn{}; } Parameter::Specific getDefaultParamSpecific() { Volume vol = Volume::make<Volume::levelDb>(-9600); Volume vol = Volume::make<Volume::levelDb>(kMinLevel); Parameter::Specific specific = Parameter::Specific::make<Parameter::Specific::volume>(vol); return specific; } static const long kInputFrameCount = 0x100, kOutputFrameCount = 0x100; Parameter createVolumeParam(int param, Volume::Tag volTag) { return Parameter::make<Parameter::specific>( Parameter::Specific::make<Parameter::Specific::volume>( (volTag == Volume::mute) ? Volume::make<Volume::mute>(param) : Volume::make<Volume::levelDb>(param))); } void initFrameCount() { int channelCount = getChannelCount( AudioChannelLayout::make<AudioChannelLayout::layoutMask>(kDefaultChannelLayout)); mInputFrameCount = kBufferSize / channelCount; mOutputFrameCount = kBufferSize / channelCount; } bool isLevelValid(int level) { auto vol = Volume::make<Volume::levelDb>(level); return isParameterValid<Volume, Range::volume>(vol, mDescriptor); } void setAndVerifyParameters(Volume::Tag volTag, int param, binder_exception_t expected) { auto expectedParam = createVolumeParam(param, volTag); EXPECT_STATUS(expected, mEffect->setParameter(expectedParam)) << expectedParam.toString(); if (expected == EX_NONE) { Volume::Id volId = Volume::Id::make<Volume::Id::commonTag>(volTag); auto id = Parameter::Id::make<Parameter::Id::volumeTag>(volId); // get parameter Parameter getParam; // if set success, then get should match EXPECT_STATUS(expected, mEffect->getParameter(id, &getParam)); EXPECT_EQ(expectedParam, getParam) << "\nexpectedParam:" << expectedParam.toString() << "\ngetParam:" << getParam.toString(); } } static constexpr int kSamplingFrequency = 44100; static constexpr int kDurationMilliSec = 2000; static constexpr int kBufferSize = kSamplingFrequency * kDurationMilliSec / 1000; static constexpr int kMinLevel = -96; static constexpr int kDefaultChannelLayout = AudioChannelLayout::LAYOUT_STEREO; long mInputFrameCount, mOutputFrameCount; std::shared_ptr<IFactory> mFactory; std::shared_ptr<IEffect> mEffect; IEffect::OpenEffectReturn mOpenEffectReturn; Descriptor mDescriptor; }; /** * Here we focus on specific parameter checking, general IEffect interfaces testing performed in * VtsAudioEffectTargetTest. */ enum ParamName { PARAM_INSTANCE_NAME, PARAM_LEVEL, PARAM_MUTE }; using VolumeParamTestParam = std::tuple<std::pair<std::shared_ptr<IFactory>, Descriptor>, int, bool>; class VolumeParamTest : public ::testing::TestWithParam<VolumeParamTestParam>, public VolumeControlHelper { public: VolumeParamTest() : mParamLevel(std::get<PARAM_LEVEL>(GetParam())), mParamMute(std::get<PARAM_MUTE>(GetParam())) { std::tie(mFactory, mDescriptor) = std::get<PARAM_INSTANCE_NAME>(GetParam()); } void SetUp() override { ASSERT_NO_FATAL_FAILURE(SetUpVolumeControl()); } void TearDown() override { TearDownVolumeControl(); } int mParamLevel = 0; bool mParamMute = false; }; void SetAndGetParameters() { for (auto& it : mTags) { auto& tag = it.first; auto& vol = it.second; // validate parameter Descriptor desc; ASSERT_STATUS(EX_NONE, mEffect->getDescriptor(&desc)); const bool valid = isParameterValid<Volume, Range::volume>(it.second, desc); const binder_exception_t expected = valid ? EX_NONE : EX_ILLEGAL_ARGUMENT; // set parameter Parameter expectParam; Parameter::Specific specific; specific.set<Parameter::Specific::volume>(vol); expectParam.set<Parameter::specific>(specific); EXPECT_STATUS(expected, mEffect->setParameter(expectParam)) << expectParam.toString(); // only get if parameter is in range and set success if (expected == EX_NONE) { Parameter getParam; Parameter::Id id; Volume::Id volId; volId.set<Volume::Id::commonTag>(tag); id.set<Parameter::Id::volumeTag>(volId); EXPECT_STATUS(EX_NONE, mEffect->getParameter(id, &getParam)); TEST_P(VolumeParamTest, SetAndGetParams) { ASSERT_NO_FATAL_FAILURE( setAndVerifyParameters(Volume::levelDb, mParamLevel, isLevelValid(mParamLevel) ? EX_NONE : EX_ILLEGAL_ARGUMENT)); ASSERT_NO_FATAL_FAILURE(setAndVerifyParameters(Volume::mute, mParamMute, EX_NONE)); } EXPECT_EQ(expectParam, getParam) << "\nexpect:" << expectParam.toString() << "\ngetParam:" << getParam.toString(); using VolumeDataTestParam = std::pair<std::shared_ptr<IFactory>, Descriptor>; class VolumeDataTest : public ::testing::TestWithParam<VolumeDataTestParam>, public VolumeControlHelper { public: VolumeDataTest() { std::tie(mFactory, mDescriptor) = GetParam(); mInput.resize(kBufferSize); mInputMag.resize(mTestFrequencies.size()); mBinOffsets.resize(mTestFrequencies.size()); roundToFreqCenteredToFftBin(mTestFrequencies, mBinOffsets, kBinWidth); generateMultiTone(mTestFrequencies, mInput, kSamplingFrequency); mInputMag = calculateMagnitude(mInput, mBinOffsets, kNPointFFT); } std::vector<int> calculatePercentageDiff(const std::vector<float>& outputMag) { std::vector<int> percentages(mTestFrequencies.size()); for (size_t i = 0; i < mInputMag.size(); i++) { float diff = mInputMag[i] - outputMag[i]; percentages[i] = std::round(diff / mInputMag[i] * 100); } return percentages; } void addLevelParam(int level) { Volume vol; vol.set<Volume::levelDb>(level); mTags.push_back({Volume::levelDb, vol}); // Convert Decibel value to Percentage int percentageDb(float level) { return std::round((1 - (pow(10, level / 20))) * 100); } void SetUp() override { ASSERT_NO_FATAL_FAILURE(SetUpVolumeControl()); } void TearDown() override { TearDownVolumeControl(); } static constexpr int kMaxAudioSample = 1; static constexpr int kTransitionDuration = 300; static constexpr int kNPointFFT = 32768; static constexpr float kBinWidth = (float)kSamplingFrequency / kNPointFFT; static constexpr size_t offset = kSamplingFrequency * kTransitionDuration / 1000; static constexpr float kBaseLevel = 0; std::vector<int> mTestFrequencies = {100, 1000}; std::vector<float> mInput; std::vector<float> mInputMag; std::vector<int> mBinOffsets; }; TEST_P(VolumeDataTest, ApplyLevelMuteUnmute) { std::vector<float> output(kBufferSize); std::vector<int> diffs(mTestFrequencies.size()); std::vector<float> outputMag(mTestFrequencies.size()); if (!isLevelValid(kBaseLevel)) { GTEST_SKIP() << "Volume Level not supported, skipping the test\n"; } void addMuteParam(bool mute) { Volume vol; vol.set<Volume::mute>(mute); mTags.push_back({Volume::mute, vol}); // Apply Volume Level ASSERT_NO_FATAL_FAILURE(setAndVerifyParameters(Volume::levelDb, kBaseLevel, EX_NONE)); ASSERT_NO_FATAL_FAILURE(processAndWriteToOutput(mInput, output, mEffect, &mOpenEffectReturn)); outputMag = calculateMagnitude(output, mBinOffsets, kNPointFFT); diffs = calculatePercentageDiff(outputMag); for (size_t i = 0; i < diffs.size(); i++) { ASSERT_EQ(diffs[i], percentageDb(kBaseLevel)); } private: std::vector<std::pair<Volume::Tag, Volume>> mTags; void CleanUp() { mTags.clear(); } }; // Apply Mute ASSERT_NO_FATAL_FAILURE(setAndVerifyParameters(Volume::mute, true /*mute*/, EX_NONE)); ASSERT_NO_FATAL_FAILURE(processAndWriteToOutput(mInput, output, mEffect, &mOpenEffectReturn)); std::vector<float> subOutputMute(output.begin() + offset, output.end()); outputMag = calculateMagnitude(subOutputMute, mBinOffsets, kNPointFFT); diffs = calculatePercentageDiff(outputMag); for (size_t i = 0; i < diffs.size(); i++) { ASSERT_EQ(diffs[i], percentageDb(kMinLevel /*Mute*/)); } // Verifying Fade out outputMag = calculateMagnitude(output, mBinOffsets, kNPointFFT); diffs = calculatePercentageDiff(outputMag); TEST_P(VolumeParamTest, SetAndGetLevel) { EXPECT_NO_FATAL_FAILURE(addLevelParam(mParamLevel)); SetAndGetParameters(); for (size_t i = 0; i < diffs.size(); i++) { ASSERT_LT(diffs[i], percentageDb(kMinLevel /*Mute*/)); } TEST_P(VolumeParamTest, SetAndGetMute) { EXPECT_NO_FATAL_FAILURE(addMuteParam(mParamMute)); SetAndGetParameters(); // Apply Unmute ASSERT_NO_FATAL_FAILURE(setAndVerifyParameters(Volume::mute, false /*unmute*/, EX_NONE)); ASSERT_NO_FATAL_FAILURE(processAndWriteToOutput(mInput, output, mEffect, &mOpenEffectReturn)); std::vector<float> subOutputUnmute(output.begin() + offset, output.end()); outputMag = calculateMagnitude(subOutputUnmute, mBinOffsets, kNPointFFT); diffs = calculatePercentageDiff(outputMag); for (size_t i = 0; i < diffs.size(); i++) { ASSERT_EQ(diffs[i], percentageDb(kBaseLevel)); } // Verifying Fade in outputMag = calculateMagnitude(output, mBinOffsets, kNPointFFT); diffs = calculatePercentageDiff(outputMag); for (size_t i = 0; i < diffs.size(); i++) { ASSERT_GT(diffs[i], percentageDb(kBaseLevel)); } } TEST_P(VolumeDataTest, DecreasingLevels) { std::vector<int> decreasingLevels = {-24, -48, -96}; std::vector<float> baseOutput(kBufferSize); std::vector<int> baseDiffs(mTestFrequencies.size()); std::vector<float> outputMag(mTestFrequencies.size()); if (!isLevelValid(kBaseLevel)) { GTEST_SKIP() << "Volume Level not supported, skipping the test\n"; } ASSERT_NO_FATAL_FAILURE(setAndVerifyParameters(Volume::levelDb, kBaseLevel, EX_NONE)); ASSERT_NO_FATAL_FAILURE( processAndWriteToOutput(mInput, baseOutput, mEffect, &mOpenEffectReturn)); outputMag = calculateMagnitude(baseOutput, mBinOffsets, kNPointFFT); baseDiffs = calculatePercentageDiff(outputMag); for (int level : decreasingLevels) { std::vector<float> output(kBufferSize); std::vector<int> diffs(mTestFrequencies.size()); // Skipping the further steps for unnsupported level values if (!isLevelValid(level)) { continue; } ASSERT_NO_FATAL_FAILURE(setAndVerifyParameters(Volume::levelDb, level, EX_NONE)); ASSERT_NO_FATAL_FAILURE( processAndWriteToOutput(mInput, output, mEffect, &mOpenEffectReturn)); outputMag = calculateMagnitude(output, mBinOffsets, kNPointFFT); diffs = calculatePercentageDiff(outputMag); // Decrease in volume level results in greater magnitude difference for (size_t i = 0; i < diffs.size(); i++) { ASSERT_GT(diffs[i], baseDiffs[i]); } baseDiffs = diffs; } } std::vector<std::pair<std::shared_ptr<IFactory>, Descriptor>> kDescPair; Loading @@ -157,6 +306,20 @@ INSTANTIATE_TEST_SUITE_P( GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(VolumeParamTest); INSTANTIATE_TEST_SUITE_P(VolumeTest, VolumeDataTest, testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors( IFactory::descriptor, getEffectTypeUuidVolume())), [](const testing::TestParamInfo<VolumeDataTest::ParamType>& 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(VolumeDataTest); int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer()); Loading Loading
audio/aidl/vts/Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ cc_defaults { "android.hardware.common.fmq-V1-ndk", "libaudioaidlcommon", "libaidlcommonsupport", "libpffft", ], header_libs: [ "libaudioaidl_headers", Loading @@ -36,6 +37,7 @@ cc_defaults { "-Wextra", "-Werror", "-Wthread-safety", "-Wno-error=unused-parameter", ], test_config_template: "VtsHalAudioTargetTestTemplate.xml", test_suites: [ Loading
audio/aidl/vts/EffectHelper.h +42 −0 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ #include "EffectFactoryHelper.h" #include "TestUtils.h" #include "pffft.hpp" using namespace android; using aidl::android::hardware::audio::effect::CommandId; Loading Loading @@ -329,4 +330,45 @@ class EffectHelper { ASSERT_NO_FATAL_FAILURE(command(mEffect, CommandId::RESET)); ASSERT_NO_FATAL_FAILURE(expectState(mEffect, State::IDLE)); } // Find FFT bin indices for testFrequencies and get bin center frequencies void roundToFreqCenteredToFftBin(std::vector<int>& testFrequencies, std::vector<int>& binOffsets, const float kBinWidth) { for (size_t i = 0; i < testFrequencies.size(); i++) { binOffsets[i] = std::round(testFrequencies[i] / kBinWidth); testFrequencies[i] = std::round(binOffsets[i] * kBinWidth); } } // Generate multitone input between -1 to +1 using testFrequencies void generateMultiTone(const std::vector<int>& testFrequencies, std::vector<float>& input, const int samplingFrequency) { for (size_t i = 0; i < input.size(); i++) { input[i] = 0; for (size_t j = 0; j < testFrequencies.size(); j++) { input[i] += sin(2 * M_PI * testFrequencies[j] * i / samplingFrequency); } input[i] /= testFrequencies.size(); } } // Use FFT transform to convert the buffer to frequency domain // Compute its magnitude at binOffsets std::vector<float> calculateMagnitude(const std::vector<float>& buffer, const std::vector<int>& binOffsets, const int nPointFFT) { std::vector<float> fftInput(nPointFFT); PFFFT_Setup* inputHandle = pffft_new_setup(nPointFFT, PFFFT_REAL); pffft_transform_ordered(inputHandle, buffer.data(), fftInput.data(), nullptr, PFFFT_FORWARD); pffft_destroy_setup(inputHandle); std::vector<float> bufferMag(binOffsets.size()); for (size_t i = 0; i < binOffsets.size(); i++) { size_t k = binOffsets[i]; bufferMag[i] = sqrt((fftInput[k * 2] * fftInput[k * 2]) + (fftInput[k * 2 + 1] * fftInput[k * 2 + 1])); } return bufferMag; } };
audio/aidl/vts/VtsHalVolumeTargetTest.cpp +235 −72 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ using namespace android; using aidl::android::hardware::audio::common::getChannelCount; using aidl::android::hardware::audio::effect::Descriptor; using aidl::android::hardware::audio::effect::getEffectTypeUuidVolume; using aidl::android::hardware::audio::effect::IEffect; Loading @@ -29,110 +30,258 @@ using aidl::android::hardware::audio::effect::Parameter; using aidl::android::hardware::audio::effect::Volume; using android::hardware::audio::common::testing::detail::TestExecutionTracer; /** * Here we focus on specific parameter checking, general IEffect interfaces testing performed in * VtsAudioEffectTargetTest. */ enum ParamName { PARAM_INSTANCE_NAME, PARAM_LEVEL, PARAM_MUTE }; using VolumeParamTestParam = std::tuple<std::pair<std::shared_ptr<IFactory>, Descriptor>, int, bool>; class VolumeParamTest : public ::testing::TestWithParam<VolumeParamTestParam>, public EffectHelper { class VolumeControlHelper : public EffectHelper { public: VolumeParamTest() : mParamLevel(std::get<PARAM_LEVEL>(GetParam())), mParamMute(std::get<PARAM_MUTE>(GetParam())) { std::tie(mFactory, mDescriptor) = std::get<PARAM_INSTANCE_NAME>(GetParam()); } void SetUp() override { void SetUpVolumeControl() { ASSERT_NE(nullptr, mFactory); ASSERT_NO_FATAL_FAILURE(create(mFactory, mEffect, mDescriptor)); initFrameCount(); Parameter::Specific specific = getDefaultParamSpecific(); Parameter::Common common = EffectHelper::createParamCommon( 0 /* session */, 1 /* ioHandle */, 44100 /* iSampleRate */, 44100 /* oSampleRate */, kInputFrameCount /* iFrameCount */, kOutputFrameCount /* oFrameCount */); IEffect::OpenEffectReturn ret; ASSERT_NO_FATAL_FAILURE(open(mEffect, common, specific, &ret, EX_NONE)); 0 /* session */, 1 /* ioHandle */, kSamplingFrequency /* iSampleRate */, kSamplingFrequency /* oSampleRate */, mInputFrameCount /* iFrameCount */, mInputFrameCount /* oFrameCount */); ASSERT_NO_FATAL_FAILURE(open(mEffect, common, specific, &mOpenEffectReturn, EX_NONE)); ASSERT_NE(nullptr, mEffect); } void TearDown() override { void TearDownVolumeControl() { ASSERT_NO_FATAL_FAILURE(close(mEffect)); ASSERT_NO_FATAL_FAILURE(destroy(mFactory, mEffect)); mOpenEffectReturn = IEffect::OpenEffectReturn{}; } Parameter::Specific getDefaultParamSpecific() { Volume vol = Volume::make<Volume::levelDb>(-9600); Volume vol = Volume::make<Volume::levelDb>(kMinLevel); Parameter::Specific specific = Parameter::Specific::make<Parameter::Specific::volume>(vol); return specific; } static const long kInputFrameCount = 0x100, kOutputFrameCount = 0x100; Parameter createVolumeParam(int param, Volume::Tag volTag) { return Parameter::make<Parameter::specific>( Parameter::Specific::make<Parameter::Specific::volume>( (volTag == Volume::mute) ? Volume::make<Volume::mute>(param) : Volume::make<Volume::levelDb>(param))); } void initFrameCount() { int channelCount = getChannelCount( AudioChannelLayout::make<AudioChannelLayout::layoutMask>(kDefaultChannelLayout)); mInputFrameCount = kBufferSize / channelCount; mOutputFrameCount = kBufferSize / channelCount; } bool isLevelValid(int level) { auto vol = Volume::make<Volume::levelDb>(level); return isParameterValid<Volume, Range::volume>(vol, mDescriptor); } void setAndVerifyParameters(Volume::Tag volTag, int param, binder_exception_t expected) { auto expectedParam = createVolumeParam(param, volTag); EXPECT_STATUS(expected, mEffect->setParameter(expectedParam)) << expectedParam.toString(); if (expected == EX_NONE) { Volume::Id volId = Volume::Id::make<Volume::Id::commonTag>(volTag); auto id = Parameter::Id::make<Parameter::Id::volumeTag>(volId); // get parameter Parameter getParam; // if set success, then get should match EXPECT_STATUS(expected, mEffect->getParameter(id, &getParam)); EXPECT_EQ(expectedParam, getParam) << "\nexpectedParam:" << expectedParam.toString() << "\ngetParam:" << getParam.toString(); } } static constexpr int kSamplingFrequency = 44100; static constexpr int kDurationMilliSec = 2000; static constexpr int kBufferSize = kSamplingFrequency * kDurationMilliSec / 1000; static constexpr int kMinLevel = -96; static constexpr int kDefaultChannelLayout = AudioChannelLayout::LAYOUT_STEREO; long mInputFrameCount, mOutputFrameCount; std::shared_ptr<IFactory> mFactory; std::shared_ptr<IEffect> mEffect; IEffect::OpenEffectReturn mOpenEffectReturn; Descriptor mDescriptor; }; /** * Here we focus on specific parameter checking, general IEffect interfaces testing performed in * VtsAudioEffectTargetTest. */ enum ParamName { PARAM_INSTANCE_NAME, PARAM_LEVEL, PARAM_MUTE }; using VolumeParamTestParam = std::tuple<std::pair<std::shared_ptr<IFactory>, Descriptor>, int, bool>; class VolumeParamTest : public ::testing::TestWithParam<VolumeParamTestParam>, public VolumeControlHelper { public: VolumeParamTest() : mParamLevel(std::get<PARAM_LEVEL>(GetParam())), mParamMute(std::get<PARAM_MUTE>(GetParam())) { std::tie(mFactory, mDescriptor) = std::get<PARAM_INSTANCE_NAME>(GetParam()); } void SetUp() override { ASSERT_NO_FATAL_FAILURE(SetUpVolumeControl()); } void TearDown() override { TearDownVolumeControl(); } int mParamLevel = 0; bool mParamMute = false; }; void SetAndGetParameters() { for (auto& it : mTags) { auto& tag = it.first; auto& vol = it.second; // validate parameter Descriptor desc; ASSERT_STATUS(EX_NONE, mEffect->getDescriptor(&desc)); const bool valid = isParameterValid<Volume, Range::volume>(it.second, desc); const binder_exception_t expected = valid ? EX_NONE : EX_ILLEGAL_ARGUMENT; // set parameter Parameter expectParam; Parameter::Specific specific; specific.set<Parameter::Specific::volume>(vol); expectParam.set<Parameter::specific>(specific); EXPECT_STATUS(expected, mEffect->setParameter(expectParam)) << expectParam.toString(); // only get if parameter is in range and set success if (expected == EX_NONE) { Parameter getParam; Parameter::Id id; Volume::Id volId; volId.set<Volume::Id::commonTag>(tag); id.set<Parameter::Id::volumeTag>(volId); EXPECT_STATUS(EX_NONE, mEffect->getParameter(id, &getParam)); TEST_P(VolumeParamTest, SetAndGetParams) { ASSERT_NO_FATAL_FAILURE( setAndVerifyParameters(Volume::levelDb, mParamLevel, isLevelValid(mParamLevel) ? EX_NONE : EX_ILLEGAL_ARGUMENT)); ASSERT_NO_FATAL_FAILURE(setAndVerifyParameters(Volume::mute, mParamMute, EX_NONE)); } EXPECT_EQ(expectParam, getParam) << "\nexpect:" << expectParam.toString() << "\ngetParam:" << getParam.toString(); using VolumeDataTestParam = std::pair<std::shared_ptr<IFactory>, Descriptor>; class VolumeDataTest : public ::testing::TestWithParam<VolumeDataTestParam>, public VolumeControlHelper { public: VolumeDataTest() { std::tie(mFactory, mDescriptor) = GetParam(); mInput.resize(kBufferSize); mInputMag.resize(mTestFrequencies.size()); mBinOffsets.resize(mTestFrequencies.size()); roundToFreqCenteredToFftBin(mTestFrequencies, mBinOffsets, kBinWidth); generateMultiTone(mTestFrequencies, mInput, kSamplingFrequency); mInputMag = calculateMagnitude(mInput, mBinOffsets, kNPointFFT); } std::vector<int> calculatePercentageDiff(const std::vector<float>& outputMag) { std::vector<int> percentages(mTestFrequencies.size()); for (size_t i = 0; i < mInputMag.size(); i++) { float diff = mInputMag[i] - outputMag[i]; percentages[i] = std::round(diff / mInputMag[i] * 100); } return percentages; } void addLevelParam(int level) { Volume vol; vol.set<Volume::levelDb>(level); mTags.push_back({Volume::levelDb, vol}); // Convert Decibel value to Percentage int percentageDb(float level) { return std::round((1 - (pow(10, level / 20))) * 100); } void SetUp() override { ASSERT_NO_FATAL_FAILURE(SetUpVolumeControl()); } void TearDown() override { TearDownVolumeControl(); } static constexpr int kMaxAudioSample = 1; static constexpr int kTransitionDuration = 300; static constexpr int kNPointFFT = 32768; static constexpr float kBinWidth = (float)kSamplingFrequency / kNPointFFT; static constexpr size_t offset = kSamplingFrequency * kTransitionDuration / 1000; static constexpr float kBaseLevel = 0; std::vector<int> mTestFrequencies = {100, 1000}; std::vector<float> mInput; std::vector<float> mInputMag; std::vector<int> mBinOffsets; }; TEST_P(VolumeDataTest, ApplyLevelMuteUnmute) { std::vector<float> output(kBufferSize); std::vector<int> diffs(mTestFrequencies.size()); std::vector<float> outputMag(mTestFrequencies.size()); if (!isLevelValid(kBaseLevel)) { GTEST_SKIP() << "Volume Level not supported, skipping the test\n"; } void addMuteParam(bool mute) { Volume vol; vol.set<Volume::mute>(mute); mTags.push_back({Volume::mute, vol}); // Apply Volume Level ASSERT_NO_FATAL_FAILURE(setAndVerifyParameters(Volume::levelDb, kBaseLevel, EX_NONE)); ASSERT_NO_FATAL_FAILURE(processAndWriteToOutput(mInput, output, mEffect, &mOpenEffectReturn)); outputMag = calculateMagnitude(output, mBinOffsets, kNPointFFT); diffs = calculatePercentageDiff(outputMag); for (size_t i = 0; i < diffs.size(); i++) { ASSERT_EQ(diffs[i], percentageDb(kBaseLevel)); } private: std::vector<std::pair<Volume::Tag, Volume>> mTags; void CleanUp() { mTags.clear(); } }; // Apply Mute ASSERT_NO_FATAL_FAILURE(setAndVerifyParameters(Volume::mute, true /*mute*/, EX_NONE)); ASSERT_NO_FATAL_FAILURE(processAndWriteToOutput(mInput, output, mEffect, &mOpenEffectReturn)); std::vector<float> subOutputMute(output.begin() + offset, output.end()); outputMag = calculateMagnitude(subOutputMute, mBinOffsets, kNPointFFT); diffs = calculatePercentageDiff(outputMag); for (size_t i = 0; i < diffs.size(); i++) { ASSERT_EQ(diffs[i], percentageDb(kMinLevel /*Mute*/)); } // Verifying Fade out outputMag = calculateMagnitude(output, mBinOffsets, kNPointFFT); diffs = calculatePercentageDiff(outputMag); TEST_P(VolumeParamTest, SetAndGetLevel) { EXPECT_NO_FATAL_FAILURE(addLevelParam(mParamLevel)); SetAndGetParameters(); for (size_t i = 0; i < diffs.size(); i++) { ASSERT_LT(diffs[i], percentageDb(kMinLevel /*Mute*/)); } TEST_P(VolumeParamTest, SetAndGetMute) { EXPECT_NO_FATAL_FAILURE(addMuteParam(mParamMute)); SetAndGetParameters(); // Apply Unmute ASSERT_NO_FATAL_FAILURE(setAndVerifyParameters(Volume::mute, false /*unmute*/, EX_NONE)); ASSERT_NO_FATAL_FAILURE(processAndWriteToOutput(mInput, output, mEffect, &mOpenEffectReturn)); std::vector<float> subOutputUnmute(output.begin() + offset, output.end()); outputMag = calculateMagnitude(subOutputUnmute, mBinOffsets, kNPointFFT); diffs = calculatePercentageDiff(outputMag); for (size_t i = 0; i < diffs.size(); i++) { ASSERT_EQ(diffs[i], percentageDb(kBaseLevel)); } // Verifying Fade in outputMag = calculateMagnitude(output, mBinOffsets, kNPointFFT); diffs = calculatePercentageDiff(outputMag); for (size_t i = 0; i < diffs.size(); i++) { ASSERT_GT(diffs[i], percentageDb(kBaseLevel)); } } TEST_P(VolumeDataTest, DecreasingLevels) { std::vector<int> decreasingLevels = {-24, -48, -96}; std::vector<float> baseOutput(kBufferSize); std::vector<int> baseDiffs(mTestFrequencies.size()); std::vector<float> outputMag(mTestFrequencies.size()); if (!isLevelValid(kBaseLevel)) { GTEST_SKIP() << "Volume Level not supported, skipping the test\n"; } ASSERT_NO_FATAL_FAILURE(setAndVerifyParameters(Volume::levelDb, kBaseLevel, EX_NONE)); ASSERT_NO_FATAL_FAILURE( processAndWriteToOutput(mInput, baseOutput, mEffect, &mOpenEffectReturn)); outputMag = calculateMagnitude(baseOutput, mBinOffsets, kNPointFFT); baseDiffs = calculatePercentageDiff(outputMag); for (int level : decreasingLevels) { std::vector<float> output(kBufferSize); std::vector<int> diffs(mTestFrequencies.size()); // Skipping the further steps for unnsupported level values if (!isLevelValid(level)) { continue; } ASSERT_NO_FATAL_FAILURE(setAndVerifyParameters(Volume::levelDb, level, EX_NONE)); ASSERT_NO_FATAL_FAILURE( processAndWriteToOutput(mInput, output, mEffect, &mOpenEffectReturn)); outputMag = calculateMagnitude(output, mBinOffsets, kNPointFFT); diffs = calculatePercentageDiff(outputMag); // Decrease in volume level results in greater magnitude difference for (size_t i = 0; i < diffs.size(); i++) { ASSERT_GT(diffs[i], baseDiffs[i]); } baseDiffs = diffs; } } std::vector<std::pair<std::shared_ptr<IFactory>, Descriptor>> kDescPair; Loading @@ -157,6 +306,20 @@ INSTANTIATE_TEST_SUITE_P( GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(VolumeParamTest); INSTANTIATE_TEST_SUITE_P(VolumeTest, VolumeDataTest, testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors( IFactory::descriptor, getEffectTypeUuidVolume())), [](const testing::TestParamInfo<VolumeDataTest::ParamType>& 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(VolumeDataTest); int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer()); Loading