Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(478)

Side by Side Diff: media/audio/audio_power_monitor_unittest.cc

Issue 14600025: Replace AudioSilenceDetector with an AudioPowerMonitor. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Replace RMS scheme with 1st-order low-pass filter, per crogers@. Simpler, single-threaded unit tes… Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "media/audio/audio_power_monitor.h"
6
7 #include <limits>
8
9 #include "base/bind.h"
10 #include "base/bind_helpers.h"
11 #include "base/message_loop.h"
12 #include "base/time/time.h"
13 #include "media/base/audio_bus.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15
16 namespace media {
17
18 static const int kSampleRate = 48000;
19 static const int kFramesPerBuffer = 128;
20
21 static const int kTimeConstantMillis = 5;
22 static const int kMeasurementPeriodMillis = 20;
23
24 namespace {
25
26 // Container for each parameterized test's data (input and expected results).
27 class TestScenario {
28 public:
29 TestScenario(const float* data, int num_channels, int num_frames,
30 float expected_power, bool expected_clipped)
31 : expected_power_(expected_power), expected_clipped_(expected_clipped) {
32 CreatePopulatedBuffer(data, num_channels, num_frames);
33 }
34
35 // Copy constructor for ::testing::Values(...).
36 TestScenario(const TestScenario& other)
37 : expected_power_(other.expected_power_),
38 expected_clipped_(other.expected_clipped_) {
39 bus_ = AudioBus::Create(other.bus_->channels(), other.bus_->frames());
40 other.bus_->CopyTo(bus_.get());
41 }
42
43 const AudioBus& data() const {
44 return *bus_;
45 }
46
47 float expected_power() const {
48 return expected_power_;
49 }
50
51 bool expected_clipped() const {
52 return expected_clipped_;
53 }
54
55 private:
56 // Creates an AudioBus, sized and populated with kFramesPerBuffer frames of
57 // data. The given test |data| is repeated to fill the buffer.
58 void CreatePopulatedBuffer(
59 const float* data, int num_channels, int num_frames) {
60 bus_ = AudioBus::Create(num_channels, kFramesPerBuffer);
61 for (int ch = 0; ch < num_channels; ++ch) {
62 for (int frames = 0; frames < kFramesPerBuffer; frames += num_frames) {
63 const int num_to_copy = std::min(num_frames, kFramesPerBuffer - frames);
64 memcpy(bus_->channel(ch) + frames, data + num_frames * ch,
65 sizeof(float) * num_to_copy);
66 }
67 }
68 }
69
70 const float expected_power_;
71 const bool expected_clipped_;
72 scoped_ptr<AudioBus> bus_;
73
74 DISALLOW_ASSIGN(TestScenario);
75 };
76
77 // An observer that receives power measurements. Each power measurement should
78 // should make progress towards the goal value.
79 class MeasurementObserver {
80 public:
81 MeasurementObserver(float goal_power_measurement, bool goal_clipped)
82 : goal_power_measurement_(goal_power_measurement),
83 goal_clipped_(goal_clipped), measurement_count_(0) {}
84
85 int measurement_count() const {
86 return measurement_count_;
87 }
88
89 float last_power_measurement() const {
90 return last_power_measurement_;
91 }
92
93 bool last_clipped() const {
94 return last_clipped_;
95 }
96
97 void OnPowerMeasured(float cur_power_measurement, bool clipped) {
98 if (measurement_count_ == 0) {
99 measurements_should_increase_ =
100 (cur_power_measurement < goal_power_measurement_);
101 } else {
102 SCOPED_TRACE(::testing::Message()
103 << "Power: goal=" << goal_power_measurement_
104 << "; last=" << last_power_measurement_
105 << "; cur=" << cur_power_measurement);
106
107 if (last_power_measurement_ != goal_power_measurement_) {
108 if (measurements_should_increase_) {
109 EXPECT_LE(last_power_measurement_, cur_power_measurement)
110 << "Measurements should be monotonically increasing.";
111 } else {
112 EXPECT_GE(last_power_measurement_, cur_power_measurement)
113 << "Measurements should be monotonically decreasing.";
114 }
115 } else {
116 EXPECT_EQ(last_power_measurement_, cur_power_measurement)
117 << "Measurements are numerically unstable at goal value.";
118 }
119 }
120
121 last_power_measurement_ = cur_power_measurement;
122 last_clipped_ = clipped;
123 ++measurement_count_;
124 }
125
126 private:
127 const float goal_power_measurement_;
128 const bool goal_clipped_;
129 int measurement_count_;
130 bool measurements_should_increase_;
131 float last_power_measurement_;
132 bool last_clipped_;
133
134 DISALLOW_COPY_AND_ASSIGN(MeasurementObserver);
135 };
136
137 } // namespace
138
139 class AudioPowerMonitorTest : public ::testing::TestWithParam<TestScenario> {
140 public:
141 AudioPowerMonitorTest()
142 : power_monitor_(
143 kSampleRate,
144 base::TimeDelta::FromMilliseconds(kTimeConstantMillis),
145 base::TimeDelta::FromMilliseconds(kMeasurementPeriodMillis),
146 &message_loop_,
147 base::Bind(&AudioPowerMonitorTest::OnPowerMeasured,
148 base::Unretained(this))) {}
149
150 void FeedAndCheckExpectedPowerIsMeasured(
151 const AudioBus& bus, float power, bool clipped) {
152 // Feed the AudioPowerMonitor. It should post tasks to |message_loop_|.
153 static const int kNumFeedIters = 100;
154 for (int i = 0; i < kNumFeedIters; ++i)
155 power_monitor_.Scan(bus, bus.frames());
156
157 // Set up an observer and run all the enqueued tasks.
158 MeasurementObserver observer(power, clipped);
159 current_observer_ = &observer;
160 message_loop_.RunUntilIdle();
161 current_observer_ = NULL;
162
163 // Check that the results recorded by the observer are the same whole-number
164 // dBFS.
165 if (observer.measurement_count() > 0) {
166 EXPECT_EQ(static_cast<int>(power),
167 static_cast<int>(observer.last_power_measurement()));
168 EXPECT_EQ(clipped, observer.last_clipped());
169 } else {
170 // Edge case: AudioPowerMonitor reported no measurements. This infers it
171 // decided not to report any redundant measurements, which we assume is an
172 // unclipped "zero power" result.
173 EXPECT_EQ(static_cast<int>(-std::numeric_limits<float>::infinity()),
174 static_cast<int>(power));
175 EXPECT_EQ(false, clipped);
176 }
177 }
178
179 private:
180 void OnPowerMeasured(float power, bool clipped) {
181 CHECK(current_observer_);
182 current_observer_->OnPowerMeasured(power, clipped);
183 }
184
185 base::MessageLoop message_loop_;
186 AudioPowerMonitor power_monitor_;
187 MeasurementObserver* current_observer_;
188
189 DISALLOW_COPY_AND_ASSIGN(AudioPowerMonitorTest);
190 };
191
192 TEST_P(AudioPowerMonitorTest, MeasuresPowerOfSignal) {
193 const TestScenario& scenario = GetParam();
194
195 scoped_ptr<AudioBus> zeroed_bus =
196 AudioBus::Create(scenario.data().channels(), scenario.data().frames());
197 zeroed_bus->Zero();
198
199 // Send a "zero power" audio signal, then this scenario's audio signal, then
200 // the "zero power" audio signal again; testing that the power monitor
201 // measurements match expected values.
202 FeedAndCheckExpectedPowerIsMeasured(
203 *zeroed_bus, AudioPowerMonitor::kZeroPowerDBFS, false);
204 FeedAndCheckExpectedPowerIsMeasured(
205 scenario.data(), scenario.expected_power(), scenario.expected_clipped());
206 FeedAndCheckExpectedPowerIsMeasured(
207 *zeroed_bus, AudioPowerMonitor::kZeroPowerDBFS, false);
208 }
209
210 static const float kMonoSilentNoise[] = {
211 0.01f, -0.01f
212 };
213
214 static const float kMonoMaxAmplitude[] = {
215 1.0f
216 };
217
218 static const float kMonoMaxAmplitude2[] = {
219 -1.0f, 1.0f
220 };
221
222 static const float kMonoHalfMaxAmplitude[] = {
223 0.5f, -0.5f, 0.5f, -0.5f
224 };
225
226 static const float kMonoAmplitudeClipped[] = {
227 2.0f, -2.0f
228 };
229
230 static const float kMonoMaxAmplitudeWithClip[] = {
231 2.0f, 0.0, 0.0f, 0.0f
232 };
233
234 static const float kMonoMaxAmplitudeWithClip2[] = {
235 4.0f, 0.0, 0.0f, 0.0f
236 };
237
238 static const float kMonoContainsInfinity[] = {
239 0.0f, 0.0f, 0.0f, std::numeric_limits<float>::infinity()
DaleCurtis 2013/07/02 22:26:34 Static initializer.
miu 2013/07/09 00:59:56 Done.
240 };
241
242 static const float kMonoContainsNaN[] = {
243 0.5f, -0.5f, 0.5f, std::numeric_limits<float>::quiet_NaN()
DaleCurtis 2013/07/02 22:26:34 Ditto.
miu 2013/07/09 00:59:56 Done.
244 };
245
246 static const float kStereoSilentNoise[] = {
247 // left channel
248 0.005f, -0.005f,
249 // right channel
250 0.005f, -0.005f
251 };
252
253 static const float kStereoMaxAmplitude[] = {
254 // left channel
255 1.0f, -1.0f,
256 // right channel
257 -1.0f, 1.0f
258 };
259
260 static const float kRightChannelMaxAmplitude[] = {
261 // left channel
262 0.0f, 0.0f, 0.0f, 0.0f,
263 // right channel
264 -1.0f, 1.0f, -1.0f, 1.0f
265 };
266
267 static const float kLeftChannelHalfMaxAmplitude[] = {
268 // left channel
269 0.5f, -0.5f, 0.5f, -0.5f,
270 // right channel
271 0.0f, 0.0f, 0.0f, 0.0f,
272 };
273
274 static const float kStereoMixed[] = {
275 // left channel
276 0.5f, -0.5f, 0.5f, -0.5f,
277 // right channel
278 -1.0f, 1.0f, -1.0f, 1.0f
279 };
280
281 static const float kStereoMixed2[] = {
282 // left channel
283 1.0f, -1.0f, 0.75f, -0.75f, 0.5f, -0.5f, 0.25f, -0.25f,
284 // right channel
285 0.25f, -0.25f, 0.5f, -0.5f, 0.75f, -0.75f, 1.0f, -1.0f
286 };
287
288 INSTANTIATE_TEST_CASE_P(
289 Scenarios, AudioPowerMonitorTest,
290 ::testing::Values(
291 TestScenario(kMonoSilentNoise, 1, 1, -40, false),
292 TestScenario(kMonoMaxAmplitude, 1, 1,
293 AudioPowerMonitor::kMaxPowerDBFS, false),
294 TestScenario(kMonoMaxAmplitude2, 1, 2,
295 AudioPowerMonitor::kMaxPowerDBFS, false),
296 TestScenario(kMonoHalfMaxAmplitude, 1, 4, -6, false),
297 TestScenario(kMonoAmplitudeClipped, 1, 2,
298 AudioPowerMonitor::kMaxPowerDBFS, true),
299 TestScenario(kMonoMaxAmplitudeWithClip, 1, 4,
300 AudioPowerMonitor::kMaxPowerDBFS, true),
301 TestScenario(kMonoMaxAmplitudeWithClip2, 1, 4,
302 AudioPowerMonitor::kMaxPowerDBFS, true),
303 TestScenario(kMonoContainsInfinity, 1, 4,
304 AudioPowerMonitor::kZeroPowerDBFS, true),
305 TestScenario(kMonoContainsNaN, 1, 4,
306 AudioPowerMonitor::kZeroPowerDBFS, false),
307 TestScenario(kStereoSilentNoise, 2, 1, -46, false),
308 TestScenario(kStereoMaxAmplitude, 2, 2,
309 AudioPowerMonitor::kMaxPowerDBFS, false),
310 TestScenario(kRightChannelMaxAmplitude, 2, 4, -3, false),
311 TestScenario(kLeftChannelHalfMaxAmplitude, 2, 4, -9, false),
312 TestScenario(kStereoMixed, 2, 4, -2, false),
313 TestScenario(kStereoMixed2, 2, 8, -3, false)));
314
315 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698