1 /*
2  *  Copyright (c) 2021 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/capture_levels_adjuster/audio_samples_scaler.h"
11 
12 #include <tuple>
13 
14 #include "modules/audio_processing/test/audio_buffer_tools.h"
15 #include "rtc_base/strings/string_builder.h"
16 #include "test/gtest.h"
17 
18 namespace webrtc {
19 namespace {
20 
SampleValueForChannel(int channel)21 float SampleValueForChannel(int channel) {
22   constexpr float kSampleBaseValue = 100.f;
23   constexpr float kSampleChannelOffset = 1.f;
24   return kSampleBaseValue + channel * kSampleChannelOffset;
25 }
26 
PopulateBuffer(AudioBuffer & audio_buffer)27 void PopulateBuffer(AudioBuffer& audio_buffer) {
28   for (size_t ch = 0; ch < audio_buffer.num_channels(); ++ch) {
29     test::FillBufferChannel(SampleValueForChannel(ch), ch, audio_buffer);
30   }
31 }
32 
33 constexpr int kNumFramesToProcess = 10;
34 
35 class AudioSamplesScalerTest
36     : public ::testing::Test,
37       public ::testing::WithParamInterface<std::tuple<int, int, float>> {
38  protected:
sample_rate_hz() const39   int sample_rate_hz() const { return std::get<0>(GetParam()); }
num_channels() const40   int num_channels() const { return std::get<1>(GetParam()); }
initial_gain() const41   float initial_gain() const { return std::get<2>(GetParam()); }
42 };
43 
44 INSTANTIATE_TEST_SUITE_P(
45     AudioSamplesScalerTestSuite,
46     AudioSamplesScalerTest,
47     ::testing::Combine(::testing::Values(16000, 32000, 48000),
48                        ::testing::Values(1, 2, 4),
49                        ::testing::Values(0.1f, 1.f, 2.f, 4.f)));
50 
TEST_P(AudioSamplesScalerTest,InitialGainIsRespected)51 TEST_P(AudioSamplesScalerTest, InitialGainIsRespected) {
52   AudioSamplesScaler scaler(initial_gain());
53 
54   AudioBuffer audio_buffer(sample_rate_hz(), num_channels(), sample_rate_hz(),
55                            num_channels(), sample_rate_hz(), num_channels());
56 
57   for (int frame = 0; frame < kNumFramesToProcess; ++frame) {
58     PopulateBuffer(audio_buffer);
59     scaler.Process(audio_buffer);
60     for (int ch = 0; ch < num_channels(); ++ch) {
61       for (size_t i = 0; i < audio_buffer.num_frames(); ++i) {
62         EXPECT_FLOAT_EQ(audio_buffer.channels_const()[ch][i],
63                         initial_gain() * SampleValueForChannel(ch));
64       }
65     }
66   }
67 }
68 
TEST_P(AudioSamplesScalerTest,VerifyGainAdjustment)69 TEST_P(AudioSamplesScalerTest, VerifyGainAdjustment) {
70   const float higher_gain = initial_gain();
71   const float lower_gain = higher_gain / 2.f;
72 
73   AudioSamplesScaler scaler(lower_gain);
74 
75   AudioBuffer audio_buffer(sample_rate_hz(), num_channels(), sample_rate_hz(),
76                            num_channels(), sample_rate_hz(), num_channels());
77 
78   // Allow the intial, lower, gain to take effect.
79   PopulateBuffer(audio_buffer);
80 
81   scaler.Process(audio_buffer);
82 
83   // Set the new, higher, gain.
84   scaler.SetGain(higher_gain);
85 
86   // Ensure that the new, higher, gain is achieved gradually over one frame.
87   PopulateBuffer(audio_buffer);
88 
89   scaler.Process(audio_buffer);
90   for (int ch = 0; ch < num_channels(); ++ch) {
91     for (size_t i = 0; i < audio_buffer.num_frames() - 1; ++i) {
92       EXPECT_LT(audio_buffer.channels_const()[ch][i],
93                 higher_gain * SampleValueForChannel(ch));
94       EXPECT_LE(audio_buffer.channels_const()[ch][i],
95                 audio_buffer.channels_const()[ch][i + 1]);
96     }
97     EXPECT_LE(audio_buffer.channels_const()[ch][audio_buffer.num_frames() - 1],
98               higher_gain * SampleValueForChannel(ch));
99   }
100 
101   // Ensure that the new, higher, gain is achieved and stay unchanged.
102   for (int frame = 0; frame < kNumFramesToProcess; ++frame) {
103     PopulateBuffer(audio_buffer);
104     scaler.Process(audio_buffer);
105 
106     for (int ch = 0; ch < num_channels(); ++ch) {
107       for (size_t i = 0; i < audio_buffer.num_frames(); ++i) {
108         EXPECT_FLOAT_EQ(audio_buffer.channels_const()[ch][i],
109                         higher_gain * SampleValueForChannel(ch));
110       }
111     }
112   }
113 
114   // Set the new, lower, gain.
115   scaler.SetGain(lower_gain);
116 
117   // Ensure that the new, lower, gain is achieved gradually over one frame.
118   PopulateBuffer(audio_buffer);
119   scaler.Process(audio_buffer);
120   for (int ch = 0; ch < num_channels(); ++ch) {
121     for (size_t i = 0; i < audio_buffer.num_frames() - 1; ++i) {
122       EXPECT_GT(audio_buffer.channels_const()[ch][i],
123                 lower_gain * SampleValueForChannel(ch));
124       EXPECT_GE(audio_buffer.channels_const()[ch][i],
125                 audio_buffer.channels_const()[ch][i + 1]);
126     }
127     EXPECT_GE(audio_buffer.channels_const()[ch][audio_buffer.num_frames() - 1],
128               lower_gain * SampleValueForChannel(ch));
129   }
130 
131   // Ensure that the new, lower, gain is achieved and stay unchanged.
132   for (int frame = 0; frame < kNumFramesToProcess; ++frame) {
133     PopulateBuffer(audio_buffer);
134     scaler.Process(audio_buffer);
135 
136     for (int ch = 0; ch < num_channels(); ++ch) {
137       for (size_t i = 0; i < audio_buffer.num_frames(); ++i) {
138         EXPECT_FLOAT_EQ(audio_buffer.channels_const()[ch][i],
139                         lower_gain * SampleValueForChannel(ch));
140       }
141     }
142   }
143 }
144 
TEST(AudioSamplesScaler,UpwardsClamping)145 TEST(AudioSamplesScaler, UpwardsClamping) {
146   constexpr int kSampleRateHz = 48000;
147   constexpr int kNumChannels = 1;
148   constexpr float kGain = 10.f;
149   constexpr float kMaxClampedSampleValue = 32767.f;
150   static_assert(kGain > 1.f, "");
151 
152   AudioSamplesScaler scaler(kGain);
153 
154   AudioBuffer audio_buffer(kSampleRateHz, kNumChannels, kSampleRateHz,
155                            kNumChannels, kSampleRateHz, kNumChannels);
156 
157   for (int frame = 0; frame < kNumFramesToProcess; ++frame) {
158     for (size_t ch = 0; ch < audio_buffer.num_channels(); ++ch) {
159       test::FillBufferChannel(
160           kMaxClampedSampleValue - audio_buffer.num_channels() + 1.f + ch, ch,
161           audio_buffer);
162     }
163 
164     scaler.Process(audio_buffer);
165     for (int ch = 0; ch < kNumChannels; ++ch) {
166       for (size_t i = 0; i < audio_buffer.num_frames(); ++i) {
167         EXPECT_FLOAT_EQ(audio_buffer.channels_const()[ch][i],
168                         kMaxClampedSampleValue);
169       }
170     }
171   }
172 }
173 
TEST(AudioSamplesScaler,DownwardsClamping)174 TEST(AudioSamplesScaler, DownwardsClamping) {
175   constexpr int kSampleRateHz = 48000;
176   constexpr int kNumChannels = 1;
177   constexpr float kGain = 10.f;
178   constexpr float kMinClampedSampleValue = -32768.f;
179   static_assert(kGain > 1.f, "");
180 
181   AudioSamplesScaler scaler(kGain);
182 
183   AudioBuffer audio_buffer(kSampleRateHz, kNumChannels, kSampleRateHz,
184                            kNumChannels, kSampleRateHz, kNumChannels);
185 
186   for (int frame = 0; frame < kNumFramesToProcess; ++frame) {
187     for (size_t ch = 0; ch < audio_buffer.num_channels(); ++ch) {
188       test::FillBufferChannel(
189           kMinClampedSampleValue + audio_buffer.num_channels() - 1.f + ch, ch,
190           audio_buffer);
191     }
192 
193     scaler.Process(audio_buffer);
194     for (int ch = 0; ch < kNumChannels; ++ch) {
195       for (size_t i = 0; i < audio_buffer.num_frames(); ++i) {
196         EXPECT_FLOAT_EQ(audio_buffer.channels_const()[ch][i],
197                         kMinClampedSampleValue);
198       }
199     }
200   }
201 }
202 
203 }  // namespace
204 }  // namespace webrtc
205