rms_level_unittest.cc (6718B)
1 /* 2 * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 #include "modules/audio_processing/rms_level.h" 11 12 #include <cmath> 13 #include <cstddef> 14 #include <cstdint> 15 #include <memory> 16 #include <numbers> 17 #include <vector> 18 19 #include "api/array_view.h" 20 #include "rtc_base/checks.h" 21 #include "rtc_base/numerics/safe_conversions.h" 22 #include "test/gtest.h" 23 24 namespace webrtc { 25 namespace { 26 constexpr int kSampleRateHz = 48000; 27 constexpr size_t kBlockSizeSamples = kSampleRateHz / 100; 28 29 std::unique_ptr<RmsLevel> RunTest(ArrayView<const int16_t> input) { 30 std::unique_ptr<RmsLevel> level(new RmsLevel); 31 for (size_t n = 0; n + kBlockSizeSamples <= input.size(); 32 n += kBlockSizeSamples) { 33 level->Analyze(input.subview(n, kBlockSizeSamples)); 34 } 35 return level; 36 } 37 38 std::unique_ptr<RmsLevel> RunTest(ArrayView<const float> input) { 39 std::unique_ptr<RmsLevel> level(new RmsLevel); 40 for (size_t n = 0; n + kBlockSizeSamples <= input.size(); 41 n += kBlockSizeSamples) { 42 level->Analyze(input.subview(n, kBlockSizeSamples)); 43 } 44 return level; 45 } 46 47 std::vector<int16_t> CreateInt16Sinusoid(int frequency_hz, 48 int amplitude, 49 size_t num_samples) { 50 std::vector<int16_t> x(num_samples); 51 for (size_t n = 0; n < num_samples; ++n) { 52 x[n] = saturated_cast<int16_t>( 53 amplitude * 54 std::sin(2 * std::numbers::pi * n * frequency_hz / kSampleRateHz)); 55 } 56 return x; 57 } 58 59 std::vector<float> CreateFloatSinusoid(int frequency_hz, 60 int amplitude, 61 size_t num_samples) { 62 std::vector<int16_t> x16 = 63 CreateInt16Sinusoid(frequency_hz, amplitude, num_samples); 64 std::vector<float> x(x16.size()); 65 for (size_t n = 0; n < x.size(); ++n) { 66 x[n] = x16[n]; 67 } 68 return x; 69 } 70 71 } // namespace 72 73 TEST(RmsLevelTest, VerifyIndentityBetweenFloatAndFix) { 74 auto x_f = CreateFloatSinusoid(1000, INT16_MAX, kSampleRateHz); 75 auto x_i = CreateFloatSinusoid(1000, INT16_MAX, kSampleRateHz); 76 auto level_f = RunTest(x_f); 77 auto level_i = RunTest(x_i); 78 int avg_i = level_i->Average(); 79 int avg_f = level_f->Average(); 80 EXPECT_EQ(3, avg_i); // -3 dBFS 81 EXPECT_EQ(avg_f, avg_i); 82 } 83 84 TEST(RmsLevelTest, Run1000HzFullScale) { 85 auto x = CreateInt16Sinusoid(1000, INT16_MAX, kSampleRateHz); 86 auto level = RunTest(x); 87 EXPECT_EQ(3, level->Average()); // -3 dBFS 88 } 89 90 TEST(RmsLevelTest, Run1000HzFullScaleAverageAndPeak) { 91 auto x = CreateInt16Sinusoid(1000, INT16_MAX, kSampleRateHz); 92 auto level = RunTest(x); 93 auto stats = level->AverageAndPeak(); 94 EXPECT_EQ(3, stats.average); // -3 dBFS 95 EXPECT_EQ(3, stats.peak); 96 } 97 98 TEST(RmsLevelTest, Run1000HzHalfScale) { 99 auto x = CreateInt16Sinusoid(1000, INT16_MAX / 2, kSampleRateHz); 100 auto level = RunTest(x); 101 EXPECT_EQ(9, level->Average()); // -9 dBFS 102 } 103 104 TEST(RmsLevelTest, RunZeros) { 105 std::vector<int16_t> x(kSampleRateHz, 0); // 1 second of pure silence. 106 auto level = RunTest(x); 107 EXPECT_EQ(127, level->Average()); 108 } 109 110 TEST(RmsLevelTest, RunZerosAverageAndPeak) { 111 std::vector<int16_t> x(kSampleRateHz, 0); // 1 second of pure silence. 112 auto level = RunTest(x); 113 auto stats = level->AverageAndPeak(); 114 EXPECT_EQ(127, stats.average); 115 EXPECT_EQ(127, stats.peak); 116 } 117 118 TEST(RmsLevelTest, NoSamples) { 119 RmsLevel level; 120 EXPECT_EQ(127, level.Average()); // Return minimum if no samples are given. 121 } 122 123 TEST(RmsLevelTest, NoSamplesAverageAndPeak) { 124 RmsLevel level; 125 auto stats = level.AverageAndPeak(); 126 EXPECT_EQ(127, stats.average); 127 EXPECT_EQ(127, stats.peak); 128 } 129 130 TEST(RmsLevelTest, PollTwice) { 131 auto x = CreateInt16Sinusoid(1000, INT16_MAX, kSampleRateHz); 132 auto level = RunTest(x); 133 level->Average(); 134 EXPECT_EQ(127, level->Average()); // Stats should be reset at this point. 135 } 136 137 TEST(RmsLevelTest, Reset) { 138 auto x = CreateInt16Sinusoid(1000, INT16_MAX, kSampleRateHz); 139 auto level = RunTest(x); 140 level->Reset(); 141 EXPECT_EQ(127, level->Average()); // Stats should be reset at this point. 142 } 143 144 // Inserts 1 second of full-scale sinusoid, followed by 1 second of muted. 145 TEST(RmsLevelTest, ProcessMuted) { 146 auto x = CreateInt16Sinusoid(1000, INT16_MAX, kSampleRateHz); 147 auto level = RunTest(x); 148 const size_t kBlocksPerSecond = 149 CheckedDivExact(static_cast<size_t>(kSampleRateHz), kBlockSizeSamples); 150 for (size_t i = 0; i < kBlocksPerSecond; ++i) { 151 level->AnalyzeMuted(kBlockSizeSamples); 152 } 153 EXPECT_EQ(6, level->Average()); // Average RMS halved due to the silence. 154 } 155 156 // Digital silence must yield 127 and anything else should yield 126 or lower. 157 TEST(RmsLevelTest, OnlyDigitalSilenceIs127) { 158 std::vector<int16_t> test_buffer(kSampleRateHz, 0); 159 auto level = RunTest(test_buffer); 160 EXPECT_EQ(127, level->Average()); 161 // Change one sample to something other than 0 to make the buffer not strictly 162 // represent digital silence. 163 test_buffer[0] = 1; 164 level = RunTest(test_buffer); 165 EXPECT_LT(level->Average(), 127); 166 } 167 168 // Inserts 1 second of half-scale sinusoid, follwed by 10 ms of full-scale, and 169 // finally 1 second of half-scale again. Expect the average to be -9 dBFS due 170 // to the vast majority of the signal being half-scale, and the peak to be 171 // -3 dBFS. 172 TEST(RmsLevelTest, RunHalfScaleAndInsertFullScale) { 173 auto half_scale = CreateInt16Sinusoid(1000, INT16_MAX / 2, kSampleRateHz); 174 auto full_scale = CreateInt16Sinusoid(1000, INT16_MAX, kSampleRateHz / 100); 175 auto x = half_scale; 176 x.insert(x.end(), full_scale.begin(), full_scale.end()); 177 x.insert(x.end(), half_scale.begin(), half_scale.end()); 178 ASSERT_EQ(static_cast<size_t>(2 * kSampleRateHz + kSampleRateHz / 100), 179 x.size()); 180 auto level = RunTest(x); 181 auto stats = level->AverageAndPeak(); 182 EXPECT_EQ(9, stats.average); 183 EXPECT_EQ(3, stats.peak); 184 } 185 186 TEST(RmsLevelTest, ResetOnBlockSizeChange) { 187 auto x = CreateInt16Sinusoid(1000, INT16_MAX, kSampleRateHz); 188 auto level = RunTest(x); 189 // Create a new signal with half amplitude, but double block length. 190 auto y = CreateInt16Sinusoid(1000, INT16_MAX / 2, kBlockSizeSamples * 2); 191 level->Analyze(y); 192 auto stats = level->AverageAndPeak(); 193 // Expect all stats to only be influenced by the last signal (y), since the 194 // changed block size should reset the stats. 195 EXPECT_EQ(9, stats.average); 196 EXPECT_EQ(9, stats.peak); 197 } 198 199 } // namespace webrtc