OLD | NEW |
---|---|
(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 | |
OLD | NEW |