diff --git a/audio/aidl/vts/Android.bp b/audio/aidl/vts/Android.bp index ad816c7056..191f928a53 100644 --- a/audio/aidl/vts/Android.bp +++ b/audio/aidl/vts/Android.bp @@ -86,6 +86,9 @@ cc_test { name: "VtsHalDownmixTargetTest", defaults: ["VtsHalAudioTargetTestDefaults"], srcs: ["VtsHalDownmixTargetTest.cpp"], + shared_libs: [ + "libaudioutils", + ], } cc_test { diff --git a/audio/aidl/vts/EffectHelper.h b/audio/aidl/vts/EffectHelper.h index 2c8edf28fb..d813554221 100644 --- a/audio/aidl/vts/EffectHelper.h +++ b/audio/aidl/vts/EffectHelper.h @@ -283,4 +283,32 @@ class EffectHelper { } return functor(result); } + + static void processAndWriteToOutput(std::vector& inputBuffer, + std::vector& outputBuffer, + const std::shared_ptr& mEffect, + IEffect::OpenEffectReturn* mOpenEffectReturn) { + // Initialize AidlMessagequeues + auto statusMQ = std::make_unique(mOpenEffectReturn->statusMQ); + ASSERT_TRUE(statusMQ->isValid()); + auto inputMQ = std::make_unique(mOpenEffectReturn->inputDataMQ); + ASSERT_TRUE(inputMQ->isValid()); + auto outputMQ = std::make_unique(mOpenEffectReturn->outputDataMQ); + ASSERT_TRUE(outputMQ->isValid()); + + // Enabling the process + ASSERT_NO_FATAL_FAILURE(command(mEffect, CommandId::START)); + ASSERT_NO_FATAL_FAILURE(expectState(mEffect, State::PROCESSING)); + + // Write from buffer to message queues and calling process + EXPECT_NO_FATAL_FAILURE(EffectHelper::writeToFmq(statusMQ, inputMQ, inputBuffer)); + + // Read the updated message queues into buffer + EXPECT_NO_FATAL_FAILURE(EffectHelper::readFromFmq(statusMQ, 1, outputMQ, + outputBuffer.size(), outputBuffer)); + + // Disable the process + ASSERT_NO_FATAL_FAILURE(command(mEffect, CommandId::RESET)); + ASSERT_NO_FATAL_FAILURE(expectState(mEffect, State::IDLE)); + } }; diff --git a/audio/aidl/vts/VtsHalDownmixTargetTest.cpp b/audio/aidl/vts/VtsHalDownmixTargetTest.cpp index c01a9a2d36..d7db567a0b 100644 --- a/audio/aidl/vts/VtsHalDownmixTargetTest.cpp +++ b/audio/aidl/vts/VtsHalDownmixTargetTest.cpp @@ -18,88 +18,114 @@ #define LOG_TAG "VtsHalDownmixTargetTest" #include +#include #include "EffectHelper.h" using namespace android; +using aidl::android::hardware::audio::common::getChannelCount; using aidl::android::hardware::audio::effect::Descriptor; using aidl::android::hardware::audio::effect::Downmix; using aidl::android::hardware::audio::effect::getEffectTypeUuidDownmix; using aidl::android::hardware::audio::effect::IEffect; using aidl::android::hardware::audio::effect::IFactory; using aidl::android::hardware::audio::effect::Parameter; +using android::audio_utils::channels::ChannelMix; 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_TYPE }; -using DownmixParamTestParam = - std::tuple, Descriptor>, Downmix::Type>; // Testing for enum values -const std::vector kTypeValues = {Downmix::Type::STRIP, Downmix::Type::FOLD}; +static const std::vector kTypeValues = {ndk::enum_range().begin(), + ndk::enum_range().end()}; -class DownmixParamTest : public ::testing::TestWithParam, - public EffectHelper { +// Testing for supported layouts from AudioChannelLayout.h +static const std::vector kLayoutValues = { + AudioChannelLayout::LAYOUT_STEREO, AudioChannelLayout::LAYOUT_2POINT1, + AudioChannelLayout::LAYOUT_TRI, AudioChannelLayout::LAYOUT_TRI_BACK, + AudioChannelLayout::LAYOUT_3POINT1, AudioChannelLayout::LAYOUT_2POINT0POINT2, + AudioChannelLayout::LAYOUT_2POINT1POINT2, AudioChannelLayout::LAYOUT_3POINT0POINT2, + AudioChannelLayout::LAYOUT_3POINT1POINT2, AudioChannelLayout::LAYOUT_QUAD, + AudioChannelLayout::LAYOUT_QUAD_SIDE, AudioChannelLayout::LAYOUT_SURROUND, + AudioChannelLayout::LAYOUT_PENTA, AudioChannelLayout::LAYOUT_5POINT1, + AudioChannelLayout::LAYOUT_5POINT1_SIDE, AudioChannelLayout::LAYOUT_5POINT1POINT2, + AudioChannelLayout::LAYOUT_5POINT1POINT4, AudioChannelLayout::LAYOUT_6POINT1, + AudioChannelLayout::LAYOUT_7POINT1, AudioChannelLayout::LAYOUT_7POINT1POINT2, + AudioChannelLayout::LAYOUT_7POINT1POINT4, AudioChannelLayout::LAYOUT_9POINT1POINT4, + AudioChannelLayout::LAYOUT_9POINT1POINT6, AudioChannelLayout::LAYOUT_13POINT_360RA, + AudioChannelLayout::LAYOUT_22POINT2}; + +static const std::vector kChannels = { + AudioChannelLayout::CHANNEL_FRONT_LEFT, + AudioChannelLayout::CHANNEL_FRONT_RIGHT, + AudioChannelLayout::CHANNEL_FRONT_CENTER, + AudioChannelLayout::CHANNEL_LOW_FREQUENCY, + AudioChannelLayout::CHANNEL_BACK_LEFT, + AudioChannelLayout::CHANNEL_BACK_RIGHT, + AudioChannelLayout::CHANNEL_BACK_CENTER, + AudioChannelLayout::CHANNEL_SIDE_LEFT, + AudioChannelLayout::CHANNEL_SIDE_RIGHT, + AudioChannelLayout::CHANNEL_FRONT_LEFT_OF_CENTER, + AudioChannelLayout::CHANNEL_FRONT_RIGHT_OF_CENTER, + AudioChannelLayout::CHANNEL_TOP_CENTER, + AudioChannelLayout::CHANNEL_TOP_FRONT_LEFT, + AudioChannelLayout::CHANNEL_TOP_FRONT_CENTER, + AudioChannelLayout::CHANNEL_TOP_FRONT_RIGHT, + AudioChannelLayout::CHANNEL_TOP_BACK_LEFT, + AudioChannelLayout::CHANNEL_TOP_BACK_CENTER, + AudioChannelLayout::CHANNEL_TOP_BACK_RIGHT, + AudioChannelLayout::CHANNEL_TOP_SIDE_LEFT, + AudioChannelLayout::CHANNEL_TOP_SIDE_RIGHT, + AudioChannelLayout::CHANNEL_BOTTOM_FRONT_LEFT, + AudioChannelLayout::CHANNEL_BOTTOM_FRONT_CENTER, + AudioChannelLayout::CHANNEL_BOTTOM_FRONT_RIGHT, + AudioChannelLayout::CHANNEL_LOW_FREQUENCY_2, + AudioChannelLayout::CHANNEL_FRONT_WIDE_LEFT, + AudioChannelLayout::CHANNEL_FRONT_WIDE_RIGHT, +}; + +class DownmixEffectHelper : public EffectHelper { public: - DownmixParamTest() : mParamType(std::get(GetParam())) { - std::tie(mFactory, mDescriptor) = std::get(GetParam()); - } - - void SetUp() override { + void SetUpDownmix(int32_t inputBufferLayout = AudioChannelLayout::LAYOUT_STEREO) { ASSERT_NE(nullptr, mFactory); ASSERT_NO_FATAL_FAILURE(create(mFactory, mEffect, mDescriptor)); + AudioChannelLayout inputChannelLayout = + AudioChannelLayout::make(inputBufferLayout); + 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)); + kInputFrameCount /* iFrameCount */, kOutputFrameCount /* oFrameCount */, + inputChannelLayout); + ASSERT_NO_FATAL_FAILURE(open(mEffect, common, specific, &mOpenEffectReturn, EX_NONE)); ASSERT_NE(nullptr, mEffect); } - void TearDown() override { + void TearDownDownmix() { ASSERT_NO_FATAL_FAILURE(close(mEffect)); ASSERT_NO_FATAL_FAILURE(destroy(mFactory, mEffect)); + mOpenEffectReturn = IEffect::OpenEffectReturn{}; } - static const long kInputFrameCount = 0x100, kOutputFrameCount = 0x100; - std::shared_ptr mFactory; - std::shared_ptr mEffect; - Descriptor mDescriptor; - Downmix::Type mParamType = Downmix::Type::STRIP; - - void SetAndGetDownmixParameters() { - for (auto& it : mTags) { - auto& tag = it.first; - auto& dm = it.second; - - // set parameter - Parameter expectParam; - Parameter::Specific specific; - specific.set(dm); - expectParam.set(specific); - // All values are valid, set parameter should succeed - EXPECT_STATUS(EX_NONE, mEffect->setParameter(expectParam)) << expectParam.toString(); - - // get parameter - Parameter getParam; - Parameter::Id id; - Downmix::Id dmId; - dmId.set(tag); - id.set(dmId); - EXPECT_STATUS(EX_NONE, mEffect->getParameter(id, &getParam)); - - EXPECT_EQ(expectParam, getParam); - } + Parameter createDownmixParam(Downmix::Type type) { + return Parameter::make( + Parameter::Specific::make( + Downmix::make(type))); + } + void setParameters(Downmix::Type type) { + // set parameter + auto param = createDownmixParam(type); + EXPECT_STATUS(EX_NONE, mEffect->setParameter(param)) << param.toString(); } - void addTypeParam(Downmix::Type type) { - Downmix dm; - dm.set(type); - mTags.push_back({Downmix::type, dm}); + void validateParameters(Downmix::Type type) { + auto leId = Downmix::Id::make(Downmix::Tag(Downmix::type)); + auto id = Parameter::Id::make(leId); + // get parameter + Parameter getParam; + EXPECT_STATUS(EX_NONE, mEffect->getParameter(id, &getParam)); + auto expectedParam = createDownmixParam(type); + EXPECT_EQ(expectedParam, getParam) << "\nexpectedParam:" << expectedParam.toString() + << "\ngetParam:" << getParam.toString(); } Parameter::Specific getDefaultParamSpecific() { @@ -108,14 +134,281 @@ class DownmixParamTest : public ::testing::TestWithParam, return specific; } - private: - std::vector> mTags; - void CleanUp() { mTags.clear(); } + void setDataTestParams(int32_t layoutType) { + mInputBuffer.resize(kBufferSize); + mOutputBuffer.resize(kBufferSize); + + // Get the number of channels used + mInputChannelCount = getChannelCount( + AudioChannelLayout::make(layoutType)); + + // In case of downmix, output is always configured to stereo layout. + mOutputBufferSize = (mInputBuffer.size() / mInputChannelCount) * kOutputChannelCount; + } + + // Generate mInputBuffer values between -kMaxDownmixSample to kMaxDownmixSample + void generateInputBuffer(size_t position, bool isStrip) { + size_t increment; + if (isStrip) + // Fill input at all the channels + increment = 1; + else + // Fill input at only one channel + increment = mInputChannelCount; + + for (size_t i = position; i < mInputBuffer.size(); i += increment) { + mInputBuffer[i] = + ((static_cast(std::rand()) / RAND_MAX) * 2 - 1) * kMaxDownmixSample; + } + } + + bool isLayoutValid(int32_t inputLayout) { + if (inputLayout & kMaxChannelMask) { + return false; + } + return true; + } + + static constexpr long kInputFrameCount = 100, kOutputFrameCount = 100; + std::shared_ptr mFactory; + Descriptor mDescriptor; + std::shared_ptr mEffect; + IEffect::OpenEffectReturn mOpenEffectReturn; + + std::vector mInputBuffer; + std::vector mOutputBuffer; + size_t mInputChannelCount; + size_t mOutputBufferSize; + static constexpr size_t kBufferSize = 128; + static constexpr float kMaxDownmixSample = 1; + static constexpr int kOutputChannelCount = 2; + // Mask for layouts greater than MAX_INPUT_CHANNELS_SUPPORTED + static constexpr int32_t kMaxChannelMask = + ~((1 << ChannelMix::MAX_INPUT_CHANNELS_SUPPORTED) - 1); +}; + +/** + * Here we focus on specific parameter checking, general IEffect interfaces testing performed in + * VtsAudioEffectTargetTest. + */ +enum ParamName { PARAM_INSTANCE_NAME, PARAM_TYPE }; + +using DownmixParamTestParam = + std::tuple, Descriptor>, Downmix::Type>; + +class DownmixParamTest : public ::testing::TestWithParam, + public DownmixEffectHelper { + public: + DownmixParamTest() : mParamType(std::get(GetParam())) { + std::tie(mFactory, mDescriptor) = std::get(GetParam()); + } + + void SetUp() override { SetUpDownmix(); } + + void TearDown() override { TearDownDownmix(); } + + const Downmix::Type mParamType; }; TEST_P(DownmixParamTest, SetAndGetType) { - EXPECT_NO_FATAL_FAILURE(addTypeParam(mParamType)); - SetAndGetDownmixParameters(); + ASSERT_NO_FATAL_FAILURE(setParameters(mParamType)); + ASSERT_NO_FATAL_FAILURE(validateParameters(mParamType)); +} + +enum FoldParamName { FOLD_INSTANCE_NAME, FOLD_INPUT_LAYOUT, FOLD_TEST_CHANNEL }; + +using DownmixDataTestParamFold = + std::tuple, Descriptor>, int32_t>; + +class DownmixFoldDataTest : public ::testing::TestWithParam, + public DownmixEffectHelper { + public: + DownmixFoldDataTest() : mInputChannelLayout(std::get(GetParam())) { + std::tie(mFactory, mDescriptor) = std::get(GetParam()); + } + + void SetUp() override { + SetUpDownmix(mInputChannelLayout); + if (!isLayoutValid(mInputChannelLayout)) { + GTEST_SKIP() << "Layout not supported \n"; + } + setDataTestParams(mInputChannelLayout); + } + + void TearDown() override { TearDownDownmix(); } + + void checkAtLeft(int32_t position) { + for (size_t i = 0, j = position; i < mOutputBufferSize; + i += kOutputChannelCount, j += mInputChannelCount) { + // Validate Left channel has audio + if (mInputBuffer[j] != 0) { + ASSERT_NE(mOutputBuffer[i], 0); + } else { + // No change in output when input is 0 + ASSERT_EQ(mOutputBuffer[i], mInputBuffer[j]); + } + // Validate Right channel has no audio + ASSERT_EQ(mOutputBuffer[i + 1], 0); + } + } + + void checkAtRight(int32_t position) { + for (size_t i = 0, j = position; i < mOutputBufferSize; + i += kOutputChannelCount, j += mInputChannelCount) { + // Validate Left channel has no audio + ASSERT_EQ(mOutputBuffer[i], 0); + // Validate Right channel has audio + if (mInputBuffer[j] != 0) { + ASSERT_NE(mOutputBuffer[i + 1], 0); + } else { + // No change in output when input is 0 + ASSERT_EQ(mOutputBuffer[i + 1], mInputBuffer[j]); + } + } + } + + void checkAtCenter(size_t position) { + for (size_t i = 0, j = position; i < mOutputBufferSize; + i += kOutputChannelCount, j += mInputChannelCount) { + // Validate both channels have audio + if (mInputBuffer[j] != 0) { + ASSERT_NE(mOutputBuffer[i], 0); + ASSERT_NE(mOutputBuffer[i + 1], 0); + + } else { + // No change in output when input is 0 + ASSERT_EQ(mOutputBuffer[i], mInputBuffer[j]); + ASSERT_EQ(mOutputBuffer[i + 1], mInputBuffer[j]); + } + } + } + + void validateOutput(int32_t channel, size_t position) { + switch (channel) { + case AudioChannelLayout::CHANNEL_FRONT_LEFT: + case AudioChannelLayout::CHANNEL_BACK_LEFT: + case AudioChannelLayout::CHANNEL_SIDE_LEFT: + case AudioChannelLayout::CHANNEL_TOP_FRONT_LEFT: + case AudioChannelLayout::CHANNEL_BOTTOM_FRONT_LEFT: + case AudioChannelLayout::CHANNEL_TOP_BACK_LEFT: + case AudioChannelLayout::CHANNEL_FRONT_WIDE_LEFT: + case AudioChannelLayout::CHANNEL_TOP_SIDE_LEFT: + checkAtLeft(position); + break; + + case AudioChannelLayout::CHANNEL_FRONT_RIGHT: + case AudioChannelLayout::CHANNEL_BACK_RIGHT: + case AudioChannelLayout::CHANNEL_SIDE_RIGHT: + case AudioChannelLayout::CHANNEL_TOP_FRONT_RIGHT: + case AudioChannelLayout::CHANNEL_BOTTOM_FRONT_RIGHT: + case AudioChannelLayout::CHANNEL_TOP_BACK_RIGHT: + case AudioChannelLayout::CHANNEL_FRONT_WIDE_RIGHT: + case AudioChannelLayout::CHANNEL_TOP_SIDE_RIGHT: + case AudioChannelLayout::CHANNEL_LOW_FREQUENCY_2: + checkAtRight(position); + break; + + case AudioChannelLayout::CHANNEL_FRONT_CENTER: + case AudioChannelLayout::CHANNEL_BACK_CENTER: + case AudioChannelLayout::CHANNEL_TOP_FRONT_CENTER: + case AudioChannelLayout::CHANNEL_BOTTOM_FRONT_CENTER: + case AudioChannelLayout::CHANNEL_FRONT_LEFT_OF_CENTER: + case AudioChannelLayout::CHANNEL_FRONT_RIGHT_OF_CENTER: + case AudioChannelLayout::CHANNEL_TOP_CENTER: + case AudioChannelLayout::CHANNEL_TOP_BACK_CENTER: + checkAtCenter(position); + break; + + case AudioChannelLayout::CHANNEL_LOW_FREQUENCY: + // If CHANNEL_LOW_FREQUENCY_2 is supported + if (mInputChannelLayout & AudioChannelLayout::CHANNEL_LOW_FREQUENCY_2) { + // Validate that only Left channel has audio + checkAtLeft(position); + } else { + // Validate that both channels have audio + checkAtCenter(position); + } + break; + } + } + + std::set getInputChannelLayouts() { + std::set supportedChannels; + for (int32_t channel : kChannels) { + if ((mInputChannelLayout & channel) == channel) { + supportedChannels.insert(channel); + } + } + return supportedChannels; + } + + int32_t mInputChannelLayout; +}; + +TEST_P(DownmixFoldDataTest, DownmixProcessData) { + // Set FOLD type parameter + ASSERT_NO_FATAL_FAILURE(setParameters(Downmix::Type::FOLD)); + + // Get all the channels from input layout + std::set supportedChannels = getInputChannelLayouts(); + + for (int32_t channel : supportedChannels) { + size_t position = std::distance(supportedChannels.begin(), supportedChannels.find(channel)); + generateInputBuffer(position, false /*isStripe*/); + ASSERT_NO_FATAL_FAILURE( + processAndWriteToOutput(mInputBuffer, mOutputBuffer, mEffect, &mOpenEffectReturn)); + validateOutput(channel, position); + std::fill(mInputBuffer.begin(), mInputBuffer.end(), 0); + } +} + +enum StripParamName { STRIP_INSTANCE_NAME, STRIP_INPUT_LAYOUT }; + +using DownmixStripDataTestParam = + std::tuple, Descriptor>, int32_t>; + +class DownmixStripDataTest : public ::testing::TestWithParam, + public DownmixEffectHelper { + public: + DownmixStripDataTest() : mInputChannelLayout(std::get(GetParam())) { + std::tie(mFactory, mDescriptor) = std::get(GetParam()); + } + + void SetUp() override { + SetUpDownmix(mInputChannelLayout); + if (!isLayoutValid(mInputChannelLayout)) { + GTEST_SKIP() << "Layout not supported \n"; + } + setDataTestParams(mInputChannelLayout); + } + + void TearDown() override { TearDownDownmix(); } + + void validateOutput() { + ASSERT_EQ(kBufferSize, mInputBuffer.size()); + ASSERT_GE(kBufferSize, mOutputBufferSize); + for (size_t i = 0, j = 0; i < kBufferSize && j < mOutputBufferSize; + i += mInputChannelCount, j += kOutputChannelCount) { + ASSERT_EQ(mOutputBuffer[j], mInputBuffer[i]); + ASSERT_EQ(mOutputBuffer[j + 1], mInputBuffer[i + 1]); + } + for (size_t i = mOutputBufferSize; i < kBufferSize; i++) { + ASSERT_EQ(mOutputBuffer[i], mInputBuffer[i]); + } + } + + int32_t mInputChannelLayout; +}; + +TEST_P(DownmixStripDataTest, DownmixProcessData) { + // Set STRIP type parameter + ASSERT_NO_FATAL_FAILURE(setParameters(Downmix::Type::STRIP)); + + // Generate input buffer, call process and compare outputs + generateInputBuffer(0 /*position*/, true /*isStripe*/); + ASSERT_NO_FATAL_FAILURE( + processAndWriteToOutput(mInputBuffer, mOutputBuffer, mEffect, &mOpenEffectReturn)); + validateOutput(); } INSTANTIATE_TEST_SUITE_P( @@ -134,6 +427,39 @@ INSTANTIATE_TEST_SUITE_P( GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DownmixParamTest); +INSTANTIATE_TEST_SUITE_P( + DownmixTest, DownmixFoldDataTest, + ::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors( + IFactory::descriptor, getEffectTypeUuidDownmix())), + testing::ValuesIn(kLayoutValues)), + [](const testing::TestParamInfo& info) { + auto descriptor = std::get(info.param).second; + std::string layout = std::to_string(std::get(info.param)); + std::string name = getPrefix(descriptor) + "_fold" + "_layout" + layout; + std::replace_if( + name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_'); + return name; + }); + +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DownmixFoldDataTest); + +INSTANTIATE_TEST_SUITE_P( + DownmixTest, DownmixStripDataTest, + ::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors( + IFactory::descriptor, getEffectTypeUuidDownmix())), + testing::ValuesIn(kLayoutValues)), + [](const testing::TestParamInfo& info) { + auto descriptor = std::get(info.param).second; + std::string layout = + std::to_string(static_cast(std::get(info.param))); + std::string name = getPrefix(descriptor) + "_strip" + "_layout" + layout; + std::replace_if( + name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_'); + return name; + }); + +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DownmixStripDataTest); + int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());