/* * Copyright (C) 2022 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_NDEBUG 0 #define LOG_TAG "audio_utils_mel_aggregator_tests" #include #include #include namespace android::audio_utils { namespace { constexpr int32_t kTestPortId = 1; constexpr float kFloatError = 0.1f; constexpr float kMelFloatError = 0.0001f; /** Value used for CSD calculation. 3 MELs with this value will cause a change of 1% in CSD. */ constexpr float kCustomMelDbA = 107.f; using ::testing::ElementsAre; using ::testing::Pointwise; using ::testing::FloatNear; TEST(MelAggregatorTest, ResetAggregator) { MelAggregator aggregator{100}; aggregator.aggregateAndAddNewMelRecord(MelRecord(1, {10.f, 10.f}, 0)); aggregator.reset(1.f, {CsdRecord(1, 1, 1.f, 1.f)}); EXPECT_EQ(aggregator.getCachedMelRecordsSize(), size_t{0}); EXPECT_EQ(aggregator.getCsd(), 1.f); EXPECT_EQ(aggregator.getCsdRecordsSize(), size_t{1}); } TEST(MelAggregatorTest, AggregateValuesFromDifferentStreams) { MelAggregator aggregator{/* csdWindowSeconds */ 100}; aggregator.aggregateAndAddNewMelRecord(MelRecord(kTestPortId, {10.f, 10.f}, /* timestamp */0)); aggregator.aggregateAndAddNewMelRecord(MelRecord(kTestPortId, {10.f, 10.f}, /* timestamp */0)); ASSERT_EQ(aggregator.getCachedMelRecordsSize(), size_t{1}); aggregator.foreachCachedMel([](const MelRecord &record) { EXPECT_EQ(record.portId, kTestPortId); EXPECT_THAT(record.mels, Pointwise(FloatNear(kFloatError), {13.f, 13.f})); }); } TEST(MelAggregatorTest, AggregateWithOlderValues) { MelAggregator aggregator{/* csdWindowSeconds */ 100}; aggregator.aggregateAndAddNewMelRecord(MelRecord(kTestPortId, {1.f, 1.f}, /* timestamp */1)); // second mel array contains values that are older than the first entry aggregator.aggregateAndAddNewMelRecord(MelRecord(kTestPortId, {2.f, 2.f, 2.f}, /* timestamp */0)); ASSERT_EQ(aggregator.getCachedMelRecordsSize(), size_t{1}); aggregator.foreachCachedMel([](const MelRecord &record) { EXPECT_EQ(record.portId, kTestPortId); EXPECT_THAT(record.mels, Pointwise(FloatNear(kFloatError), {2.f, 4.5f, 4.5f})); }); } TEST(MelAggregatorTest, AggregateWithNewerValues) { MelAggregator aggregator{/* csdWindowSeconds */ 100}; aggregator.aggregateAndAddNewMelRecord(MelRecord(kTestPortId, {1.f, 1.f}, /* timestamp */1)); // second mel array contains values that are older than the first entry aggregator.aggregateAndAddNewMelRecord(MelRecord(kTestPortId, {2.f, 2.f}, /* timestamp */2)); ASSERT_EQ(aggregator.getCachedMelRecordsSize(), size_t{1}); aggregator.foreachCachedMel([](const MelRecord &record) { EXPECT_EQ(record.portId, kTestPortId); EXPECT_THAT(record.mels, Pointwise(FloatNear(kFloatError), {1.f, 4.5f, 2.f})); }); } TEST(MelAggregatorTest, AggregateWithNonOverlappingValues) { MelAggregator aggregator{/* csdWindowSeconds */ 100}; aggregator.aggregateAndAddNewMelRecord(MelRecord(kTestPortId, {1.f, 1.f}, /* timestamp */0)); // second mel array contains values that are older than the first entry aggregator.aggregateAndAddNewMelRecord(MelRecord(kTestPortId, {1.f, 1.f}, /* timestamp */2)); ASSERT_EQ(aggregator.getCachedMelRecordsSize(), size_t{2}); aggregator.foreachCachedMel([](const MelRecord &record) { EXPECT_EQ(record.portId, kTestPortId); EXPECT_THAT(record.mels, Pointwise(FloatNear(kFloatError), {1.f, 1.f})); }); } TEST(MelAggregatorTest, CheckMelIntervalSplit) { MelAggregator aggregator{/* csdWindowSeconds */ 100}; aggregator.aggregateAndAddNewMelRecord(MelRecord(kTestPortId, {3.f, 3.f}, /* timestamp */1)); aggregator.aggregateAndAddNewMelRecord(MelRecord(kTestPortId, {3.f, 3.f, 3.f, 3.f}, /* timestamp */0)); ASSERT_EQ(aggregator.getCachedMelRecordsSize(), size_t{1}); aggregator.foreachCachedMel([](const MelRecord &record) { EXPECT_EQ(record.portId, kTestPortId); EXPECT_THAT(record.mels, Pointwise(FloatNear(kFloatError), {3.f, 6.f, 6.f, 3.f})); }); } TEST(MelAggregatorTest, CsdRollingWindowDiscardsOldElements) { MelAggregator aggregator{/* csdWindowSeconds */ 3}; aggregator.aggregateAndAddNewMelRecord(MelRecord(kTestPortId, std::vector(3, kCustomMelDbA), /* timestamp */0)); float csdValue = aggregator.getCsd(); auto records = aggregator.aggregateAndAddNewMelRecord( MelRecord(kTestPortId, std::vector(3, kCustomMelDbA), /* timestamp */3)); EXPECT_EQ(records.size(), size_t{2}); // new record and record to remove EXPECT_TRUE(records[0].value * records[1].value < 0.f); EXPECT_EQ(csdValue, aggregator.getCsd()); EXPECT_EQ(aggregator.getCsdRecordsSize(), size_t{1}); } TEST(MelAggregatorTest, CsdReaches100PercWith107dB) { MelAggregator aggregator{/* csdWindowSeconds */ 300}; // 287s of 107dB should produce at least 100% CSD auto records = aggregator.aggregateAndAddNewMelRecord( MelRecord(kTestPortId, std::vector(288, kCustomMelDbA), /* timestamp */0)); // each record should have a CSD value between 1% and 2% EXPECT_GE(records.size(), size_t{50}); EXPECT_GE(aggregator.getCsd(), 1.f); } TEST(MelAggregatorTest, CsdReaches100PercWith80dB) { constexpr int64_t seconds40h = 40*3600; MelAggregator aggregator{seconds40h}; // 40h of 80dB should produce (near) exactly 100% CSD auto records = aggregator.aggregateAndAddNewMelRecord( MelRecord(kTestPortId, std::vector(seconds40h, 80.0f), /* timestamp */0)); // each record should have a CSD value between 1% and 2% EXPECT_GE(records.size(), size_t{50}); EXPECT_NEAR(aggregator.getCsd(), 1.f, kMelFloatError); } } // namespace } // namespace android