blob: 67489de08e39ddeff404b5ecf63862357b3905c6 [file] [log] [blame]
henrik.lundin76622ce2016-11-25 05:30:47 -08001/*
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 */
Yves Gerey14696c22019-04-11 13:51:10 +020010// MSVC++ requires this to be set before any other includes to get M_PI.
11#define _USE_MATH_DEFINES
henrik.lundin76622ce2016-11-25 05:30:47 -080012#include <cmath>
13#include <memory>
14#include <vector>
15
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020016#include "api/array_view.h"
17#include "modules/audio_processing/rms_level.h"
18#include "rtc_base/checks.h"
Karl Wiberge40468b2017-11-22 10:42:26 +010019#include "rtc_base/numerics/safe_conversions.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020020#include "test/gtest.h"
henrik.lundin76622ce2016-11-25 05:30:47 -080021
22namespace webrtc {
23namespace {
24constexpr int kSampleRateHz = 48000;
25constexpr size_t kBlockSizeSamples = kSampleRateHz / 100;
26
henrik.lundin50499422016-11-29 04:26:24 -080027std::unique_ptr<RmsLevel> RunTest(rtc::ArrayView<const int16_t> input) {
28 std::unique_ptr<RmsLevel> level(new RmsLevel);
henrik.lundin76622ce2016-11-25 05:30:47 -080029 for (size_t n = 0; n + kBlockSizeSamples <= input.size();
30 n += kBlockSizeSamples) {
henrik.lundin50499422016-11-29 04:26:24 -080031 level->Analyze(input.subview(n, kBlockSizeSamples));
henrik.lundin76622ce2016-11-25 05:30:47 -080032 }
33 return level;
34}
35
36std::vector<int16_t> CreateSinusoid(int frequency_hz,
37 int amplitude,
38 size_t num_samples) {
39 std::vector<int16_t> x(num_samples);
40 for (size_t n = 0; n < num_samples; ++n) {
41 x[n] = rtc::saturated_cast<int16_t>(
42 amplitude * std::sin(2 * M_PI * n * frequency_hz / kSampleRateHz));
43 }
44 return x;
45}
46} // namespace
47
48TEST(RmsLevelTest, Run1000HzFullScale) {
49 auto x = CreateSinusoid(1000, INT16_MAX, kSampleRateHz);
50 auto level = RunTest(x);
henrik.lundin50499422016-11-29 04:26:24 -080051 EXPECT_EQ(3, level->Average()); // -3 dBFS
52}
53
54TEST(RmsLevelTest, Run1000HzFullScaleAverageAndPeak) {
55 auto x = CreateSinusoid(1000, INT16_MAX, kSampleRateHz);
56 auto level = RunTest(x);
57 auto stats = level->AverageAndPeak();
58 EXPECT_EQ(3, stats.average); // -3 dBFS
59 EXPECT_EQ(3, stats.peak);
henrik.lundin76622ce2016-11-25 05:30:47 -080060}
61
62TEST(RmsLevelTest, Run1000HzHalfScale) {
63 auto x = CreateSinusoid(1000, INT16_MAX / 2, kSampleRateHz);
64 auto level = RunTest(x);
henrik.lundin50499422016-11-29 04:26:24 -080065 EXPECT_EQ(9, level->Average()); // -9 dBFS
henrik.lundin76622ce2016-11-25 05:30:47 -080066}
67
68TEST(RmsLevelTest, RunZeros) {
69 std::vector<int16_t> x(kSampleRateHz, 0); // 1 second of pure silence.
70 auto level = RunTest(x);
henrik.lundin50499422016-11-29 04:26:24 -080071 EXPECT_EQ(127, level->Average());
72}
73
74TEST(RmsLevelTest, RunZerosAverageAndPeak) {
75 std::vector<int16_t> x(kSampleRateHz, 0); // 1 second of pure silence.
76 auto level = RunTest(x);
77 auto stats = level->AverageAndPeak();
78 EXPECT_EQ(127, stats.average);
79 EXPECT_EQ(127, stats.peak);
henrik.lundin76622ce2016-11-25 05:30:47 -080080}
81
82TEST(RmsLevelTest, NoSamples) {
henrik.lundin50499422016-11-29 04:26:24 -080083 RmsLevel level;
84 EXPECT_EQ(127, level.Average()); // Return minimum if no samples are given.
85}
86
87TEST(RmsLevelTest, NoSamplesAverageAndPeak) {
88 RmsLevel level;
89 auto stats = level.AverageAndPeak();
90 EXPECT_EQ(127, stats.average);
91 EXPECT_EQ(127, stats.peak);
henrik.lundin76622ce2016-11-25 05:30:47 -080092}
93
94TEST(RmsLevelTest, PollTwice) {
95 auto x = CreateSinusoid(1000, INT16_MAX, kSampleRateHz);
96 auto level = RunTest(x);
henrik.lundin50499422016-11-29 04:26:24 -080097 level->Average();
98 EXPECT_EQ(127, level->Average()); // Stats should be reset at this point.
henrik.lundin76622ce2016-11-25 05:30:47 -080099}
100
101TEST(RmsLevelTest, Reset) {
102 auto x = CreateSinusoid(1000, INT16_MAX, kSampleRateHz);
103 auto level = RunTest(x);
104 level->Reset();
henrik.lundin50499422016-11-29 04:26:24 -0800105 EXPECT_EQ(127, level->Average()); // Stats should be reset at this point.
henrik.lundin76622ce2016-11-25 05:30:47 -0800106}
107
108// Inserts 1 second of full-scale sinusoid, followed by 1 second of muted.
109TEST(RmsLevelTest, ProcessMuted) {
110 auto x = CreateSinusoid(1000, INT16_MAX, kSampleRateHz);
111 auto level = RunTest(x);
henrik.lundin50499422016-11-29 04:26:24 -0800112 const size_t kBlocksPerSecond = rtc::CheckedDivExact(
113 static_cast<size_t>(kSampleRateHz), kBlockSizeSamples);
114 for (size_t i = 0; i < kBlocksPerSecond; ++i) {
115 level->AnalyzeMuted(kBlockSizeSamples);
116 }
117 EXPECT_EQ(6, level->Average()); // Average RMS halved due to the silence.
118}
119
120// Inserts 1 second of half-scale sinusoid, follwed by 10 ms of full-scale, and
121// finally 1 second of half-scale again. Expect the average to be -9 dBFS due
122// to the vast majority of the signal being half-scale, and the peak to be
123// -3 dBFS.
124TEST(RmsLevelTest, RunHalfScaleAndInsertFullScale) {
125 auto half_scale = CreateSinusoid(1000, INT16_MAX / 2, kSampleRateHz);
126 auto full_scale = CreateSinusoid(1000, INT16_MAX, kSampleRateHz / 100);
127 auto x = half_scale;
128 x.insert(x.end(), full_scale.begin(), full_scale.end());
129 x.insert(x.end(), half_scale.begin(), half_scale.end());
130 ASSERT_EQ(static_cast<size_t>(2 * kSampleRateHz + kSampleRateHz / 100),
131 x.size());
132 auto level = RunTest(x);
133 auto stats = level->AverageAndPeak();
134 EXPECT_EQ(9, stats.average);
135 EXPECT_EQ(3, stats.peak);
136}
137
138TEST(RmsLevelTest, ResetOnBlockSizeChange) {
139 auto x = CreateSinusoid(1000, INT16_MAX, kSampleRateHz);
140 auto level = RunTest(x);
141 // Create a new signal with half amplitude, but double block length.
142 auto y = CreateSinusoid(1000, INT16_MAX / 2, kBlockSizeSamples * 2);
143 level->Analyze(y);
144 auto stats = level->AverageAndPeak();
145 // Expect all stats to only be influenced by the last signal (y), since the
146 // changed block size should reset the stats.
147 EXPECT_EQ(9, stats.average);
148 EXPECT_EQ(9, stats.peak);
henrik.lundin76622ce2016-11-25 05:30:47 -0800149}
150
151} // namespace webrtc