blob: 1d298379fa6ba188c52941a4753167d8a02d0d01 [file] [log] [blame]
/*
* 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 <audio_utils/MelAggregator.h>
#include <gtest/gtest.h>
#include <gmock/gmock.h>
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<float>(3, kCustomMelDbA),
/* timestamp */0));
float csdValue = aggregator.getCsd();
auto records = aggregator.aggregateAndAddNewMelRecord(
MelRecord(kTestPortId, std::vector<float>(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<float>(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<float>(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