blob: 0b40f328101a34f1103ece406841bc551289f87e [file] [log] [blame]
/*
* 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 <string>
#include <unordered_set>
#include <gtest/gtest.h>
#include <android/media/audio/common/AudioLatencyMode.h>
#include <android/media/audio/common/HeadTracking.h>
#include <android/media/audio/common/Spatialization.h>
#include <com_android_media_audio.h>
#include <utils/Log.h>
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<EffectsFactoryHalInterface> effectsFactoryHal
= EffectsFactoryHalInterface::create();
mSpatializer = Spatializer::create(&mTestCallback, effectsFactoryHal);
if (mSpatializer == nullptr) {
GTEST_SKIP() << "Skipping Spatializer tests: no spatializer";
}
std::vector<Spatialization::Level> 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<audio_latency_mode_t> sA2DPLatencyModes = {
AUDIO_LATENCY_MODE_LOW,
AUDIO_LATENCY_MODE_FREE
};
const static inline std::vector<audio_latency_mode_t> 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<HeadTracking::Mode> 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<Spatializer> mSpatializer;
};
TEST_F(SpatializerTest, SupportedA2dpLatencyTest) {
if (!setpUpForHeadtracking()) {
GTEST_SKIP() << "Skipping SupportedA2dpLatencyTest: head tracking not supported";
}
std::vector<audio_latency_mode_t> latencies = sA2DPLatencyModes;
mSpatializer->onSupportedLatencyModesChangedMsg(sTestOutput, std::move(latencies));
std::vector<audio_latency_mode_t> 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<audio_latency_mode_t> latencies = sBLELatencyModes;
mSpatializer->onSupportedLatencyModesChangedMsg(sTestOutput, std::move(latencies));
std::vector<audio_latency_mode_t> 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<audio_latency_mode_t> orderedLowLatencyModes =
mSpatializer->getOrderedLowLatencyModes();
std::vector<audio_latency_mode_t> 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<audio_latency_mode_t>::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<audio_latency_mode_t> 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);
}