/* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "Spatializer_Test" #include "Spatializer.h" #include #include #include #include #include #include #include #include using namespace android; using media::audio::common::HeadTracking; using media::audio::common::Spatialization; // Test Spatializer Helper Methods TEST(Spatializer, containsImmersiveChannelMask) { // Regardless of the implementation, we expect the following // behavior. // Pure non-immersive EXPECT_FALSE(Spatializer::containsImmersiveChannelMask( { AUDIO_CHANNEL_OUT_STEREO })); EXPECT_FALSE(Spatializer::containsImmersiveChannelMask( { AUDIO_CHANNEL_OUT_MONO, AUDIO_CHANNEL_OUT_STEREO })); EXPECT_FALSE(Spatializer::containsImmersiveChannelMask( { AUDIO_CHANNEL_OUT_MONO, AUDIO_CHANNEL_OUT_STEREO, AUDIO_CHANNEL_OUT_STEREO, AUDIO_CHANNEL_OUT_MONO })); // Pure immersive EXPECT_TRUE(Spatializer::containsImmersiveChannelMask( { AUDIO_CHANNEL_OUT_5POINT1 })); EXPECT_TRUE(Spatializer::containsImmersiveChannelMask( { AUDIO_CHANNEL_OUT_7POINT1 })); EXPECT_TRUE(Spatializer::containsImmersiveChannelMask( { AUDIO_CHANNEL_OUT_5POINT1, AUDIO_CHANNEL_OUT_7POINT1, AUDIO_CHANNEL_OUT_22POINT2 })); // Mixed immersive/non-immersive EXPECT_TRUE(Spatializer::containsImmersiveChannelMask( { AUDIO_CHANNEL_OUT_MONO, AUDIO_CHANNEL_OUT_7POINT1POINT4 })); EXPECT_TRUE(Spatializer::containsImmersiveChannelMask( { AUDIO_CHANNEL_OUT_MONO, AUDIO_CHANNEL_OUT_STEREO, AUDIO_CHANNEL_OUT_7POINT1 })); } class TestSpatializerPolicyCallback : public SpatializerPolicyCallback { public: void onCheckSpatializer() override {}; }; class SpatializerTest : public ::testing::Test { protected: void SetUp() override { const sp effectsFactoryHal = EffectsFactoryHalInterface::create(); mSpatializer = Spatializer::create(&mTestCallback, effectsFactoryHal); if (mSpatializer == nullptr) { GTEST_SKIP() << "Skipping Spatializer tests: no spatializer"; } std::vector levels; binder::Status status = mSpatializer->getSupportedLevels(&levels); ASSERT_TRUE(status.isOk()); for (auto level : levels) { if (level != Spatialization::Level::NONE) { mSpatializer->setLevel(level); break; } } mSpatializer->setOutput(sTestOutput); } void TearDown() override { if (mSpatializer == nullptr) { return; } mSpatializer->setLevel(Spatialization::Level::NONE); mSpatializer->setOutput(AUDIO_IO_HANDLE_NONE); mSpatializer->setDesiredHeadTrackingMode(HeadTracking::Mode::DISABLED); mSpatializer->setHeadSensor(SpatializerPoseController::INVALID_SENSOR); mSpatializer->updateActiveTracks({}); } static constexpr audio_io_handle_t sTestOutput= 1977; static constexpr int sTestSensorHandle = 1980; const static inline std::vector sA2DPLatencyModes = { AUDIO_LATENCY_MODE_LOW, AUDIO_LATENCY_MODE_FREE }; const static inline std::vector sBLELatencyModes = { AUDIO_LATENCY_MODE_LOW, AUDIO_LATENCY_MODE_DYNAMIC_SPATIAL_AUDIO_SOFTWARE, AUDIO_LATENCY_MODE_DYNAMIC_SPATIAL_AUDIO_HARDWARE, AUDIO_LATENCY_MODE_FREE }; bool setpUpForHeadtracking() { bool htSupported; mSpatializer->isHeadTrackingSupported(&htSupported); if (!htSupported) { return false; } std::vector htModes; mSpatializer->getSupportedHeadTrackingModes(&htModes); for (auto htMode : htModes) { if (htMode != HeadTracking::Mode::DISABLED) { mSpatializer->setDesiredHeadTrackingMode(htMode); break; } } mSpatializer->setHeadSensor(sTestSensorHandle); return true; } TestSpatializerPolicyCallback mTestCallback; sp mSpatializer; }; TEST_F(SpatializerTest, SupportedA2dpLatencyTest) { if (!setpUpForHeadtracking()) { GTEST_SKIP() << "Skipping SupportedA2dpLatencyTest: head tracking not supported"; } std::vector latencies = sA2DPLatencyModes; mSpatializer->onSupportedLatencyModesChangedMsg(sTestOutput, std::move(latencies)); std::vector supportedLatencies = mSpatializer->getSupportedLatencyModes(); ASSERT_TRUE(supportedLatencies == sA2DPLatencyModes); // Free mode must always be the last of the ordered list ASSERT_TRUE(supportedLatencies.back() == AUDIO_LATENCY_MODE_FREE); } TEST_F(SpatializerTest, SupportedBleLatencyTest) { if (!setpUpForHeadtracking()) { GTEST_SKIP() << "Skipping SupportedBleLatencyTest: head tracking not supported"; } if (!com::android::media::audio::dsa_over_bt_le_audio()) { GTEST_SKIP() << "Skipping SupportedBleLatencyTest: DSA over LE not enabled"; } std::vector latencies = sBLELatencyModes; mSpatializer->onSupportedLatencyModesChangedMsg(sTestOutput, std::move(latencies)); std::vector supportedLatencies = mSpatializer->getSupportedLatencyModes(); ASSERT_TRUE(supportedLatencies.back() == AUDIO_LATENCY_MODE_FREE); ASSERT_TRUE(std::find(supportedLatencies.begin(), supportedLatencies.end(), AUDIO_LATENCY_MODE_LOW) != supportedLatencies.end()); std::vector orderedLowLatencyModes = mSpatializer->getOrderedLowLatencyModes(); std::vector supportedLowLatencyModes; // remove free mode at the end of the supported list to only retain low latency modes std::copy(supportedLatencies.begin(), supportedLatencies.begin() + supportedLatencies.size() - 1, std::back_inserter(supportedLowLatencyModes)); // Verify that supported low latency modes are always in ordered latency modes list and // in the same order std::vector::iterator lastIt = orderedLowLatencyModes.begin(); for (auto latency : supportedLowLatencyModes) { auto it = std::find(orderedLowLatencyModes.begin(), orderedLowLatencyModes.end(), latency); ASSERT_NE(it, orderedLowLatencyModes.end()); ASSERT_LE(lastIt, it); lastIt = it; } } TEST_F(SpatializerTest, RequestedA2dpLatencyTest) { if (!setpUpForHeadtracking()) { GTEST_SKIP() << "Skipping RequestedA2dpLatencyTest: head tracking not supported"; } std::vector latencies = sA2DPLatencyModes; mSpatializer->onSupportedLatencyModesChangedMsg(sTestOutput, std::move(latencies)); // requested latency mode must be free if no spatialized tracks are active audio_latency_mode_t requestedLatencyMode = mSpatializer->getRequestedLatencyMode(); ASSERT_EQ(requestedLatencyMode, AUDIO_LATENCY_MODE_FREE); // requested latency mode must be low if at least one spatialized tracks is active mSpatializer->updateActiveTracks({AUDIO_CHANNEL_OUT_5POINT1}); requestedLatencyMode = mSpatializer->getRequestedLatencyMode(); ASSERT_EQ(requestedLatencyMode, AUDIO_LATENCY_MODE_LOW); // requested latency mode must be free after stopping the last spatialized tracks mSpatializer->updateActiveTracks({}); requestedLatencyMode = mSpatializer->getRequestedLatencyMode(); ASSERT_EQ(requestedLatencyMode, AUDIO_LATENCY_MODE_FREE); } TEST_F(SpatializerTest, RequestedBleLatencyTest) { if (!setpUpForHeadtracking()) { GTEST_SKIP() << "Skipping RequestedBleLatencyTest: head tracking not supported"; } if (!com::android::media::audio::dsa_over_bt_le_audio()) { GTEST_SKIP() << "Skipping RequestedBleLatencyTest: DSA over LE not enabled"; } mSpatializer->onSupportedLatencyModesChangedMsg(sTestOutput, { AUDIO_LATENCY_MODE_DYNAMIC_SPATIAL_AUDIO_SOFTWARE, AUDIO_LATENCY_MODE_FREE }); // requested latency mode must be free if no spatialized tracks are active audio_latency_mode_t requestedLatencyMode = mSpatializer->getRequestedLatencyMode(); ASSERT_EQ(requestedLatencyMode, AUDIO_LATENCY_MODE_FREE); // requested latency mode must be low software if at least one spatialized tracks is active // and the only supported low latency mode is low software mSpatializer->updateActiveTracks({AUDIO_CHANNEL_OUT_5POINT1}); requestedLatencyMode = mSpatializer->getRequestedLatencyMode(); ASSERT_EQ(requestedLatencyMode, AUDIO_LATENCY_MODE_DYNAMIC_SPATIAL_AUDIO_SOFTWARE); mSpatializer->onSupportedLatencyModesChangedMsg(sTestOutput, { AUDIO_LATENCY_MODE_DYNAMIC_SPATIAL_AUDIO_SOFTWARE, AUDIO_LATENCY_MODE_DYNAMIC_SPATIAL_AUDIO_HARDWARE, AUDIO_LATENCY_MODE_FREE }); requestedLatencyMode = mSpatializer->getRequestedLatencyMode(); HeadTracking::ConnectionMode connectionMode = mSpatializer->getHeadtrackingConnectionMode(); // If low hardware mode is used, the spatializer must use either use one of the sensor // connection tunneled modes. // Otherwise, low software mode must be used if (requestedLatencyMode == AUDIO_LATENCY_MODE_DYNAMIC_SPATIAL_AUDIO_HARDWARE) { ASSERT_TRUE(connectionMode == HeadTracking::ConnectionMode::DIRECT_TO_SENSOR_TUNNEL || connectionMode == HeadTracking::ConnectionMode::DIRECT_TO_SENSOR_SW); } else { ASSERT_EQ(requestedLatencyMode, AUDIO_LATENCY_MODE_DYNAMIC_SPATIAL_AUDIO_SOFTWARE); } // requested latency mode must be free after stopping the last spatialized tracks mSpatializer->updateActiveTracks({}); requestedLatencyMode = mSpatializer->getRequestedLatencyMode(); ASSERT_EQ(requestedLatencyMode, AUDIO_LATENCY_MODE_FREE); }