audio_power_monitor_unittest.cc revision a3f7b4e666c476898878fa745f637129375cd889
1// Copyright 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/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
16namespace media {
17
18static const int kSampleRate = 48000;
19static const int kFramesPerBuffer = 128;
20
21static const int kTimeConstantMillis = 5;
22static const int kMeasurementPeriodMillis = 20;
23
24namespace {
25
26// Container for each parameterized test's data (input and expected results).
27class 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 and assignment operator for ::testing::Values(...).
36  TestScenario(const TestScenario& other) { *this = other; }
37  TestScenario& operator=(const TestScenario& other) {
38    this->expected_power_ = other.expected_power_;
39    this->expected_clipped_ = other.expected_clipped_;
40    this->bus_ = AudioBus::Create(other.bus_->channels(), other.bus_->frames());
41    other.bus_->CopyTo(this->bus_.get());
42    return *this;
43  }
44
45  // Returns this TestScenario, but with a bad sample value placed in the middle
46  // of channel 0.
47  TestScenario WithABadSample(float bad_value) const {
48    TestScenario result(*this);
49    result.bus_->channel(0)[result.bus_->frames() / 2] = bad_value;
50    return result;
51  }
52
53  const AudioBus& data() const {
54    return *bus_;
55  }
56
57  float expected_power() const {
58    return expected_power_;
59  }
60
61  bool expected_clipped() const {
62    return expected_clipped_;
63  }
64
65 private:
66  // Creates an AudioBus, sized and populated with kFramesPerBuffer frames of
67  // data.  The given test |data| is repeated to fill the buffer.
68  void CreatePopulatedBuffer(
69      const float* data, int num_channels, int num_frames) {
70    bus_ = AudioBus::Create(num_channels, kFramesPerBuffer);
71    for (int ch = 0; ch < num_channels; ++ch) {
72      for (int frames = 0; frames < kFramesPerBuffer; frames += num_frames) {
73        const int num_to_copy = std::min(num_frames, kFramesPerBuffer - frames);
74        memcpy(bus_->channel(ch) + frames, data + num_frames * ch,
75               sizeof(float) * num_to_copy);
76      }
77    }
78  }
79
80  float expected_power_;
81  bool expected_clipped_;
82  scoped_ptr<AudioBus> bus_;
83};
84
85// Value printer for TestScenario.  Required to prevent Valgrind "access to
86// uninitialized memory" errors (http://crbug.com/263315).
87::std::ostream& operator<<(::std::ostream& os, const TestScenario& ts) {
88  return os << "{" << ts.data().channels() << "-channel signal} --> {"
89            << ts.expected_power() << " dBFS, "
90            << (ts.expected_clipped() ? "clipped" : "not clipped")
91            << "}";
92}
93
94// An observer that receives power measurements.  Each power measurement should
95// should make progress towards the goal value.
96class MeasurementObserver {
97 public:
98  MeasurementObserver(float goal_power_measurement, bool goal_clipped)
99      : goal_power_measurement_(goal_power_measurement),
100        goal_clipped_(goal_clipped), measurement_count_(0),
101        last_power_measurement_(AudioPowerMonitor::zero_power()),
102        last_clipped_(false) {}
103
104  int measurement_count() const {
105    return measurement_count_;
106  }
107
108  float last_power_measurement() const {
109    return last_power_measurement_;
110  }
111
112  bool last_clipped() const {
113    return last_clipped_;
114  }
115
116  void OnPowerMeasured(float cur_power_measurement, bool clipped) {
117    if (measurement_count_ == 0) {
118      measurements_should_increase_ =
119          (cur_power_measurement < goal_power_measurement_);
120    } else {
121      SCOPED_TRACE(::testing::Message()
122                   << "Power: goal=" << goal_power_measurement_
123                   << "; last=" << last_power_measurement_
124                   << "; cur=" << cur_power_measurement);
125
126      if (last_power_measurement_ != goal_power_measurement_) {
127        if (measurements_should_increase_) {
128          EXPECT_LE(last_power_measurement_, cur_power_measurement)
129              << "Measurements should be monotonically increasing.";
130        } else {
131          EXPECT_GE(last_power_measurement_, cur_power_measurement)
132              << "Measurements should be monotonically decreasing.";
133        }
134      } else {
135        EXPECT_EQ(last_power_measurement_, cur_power_measurement)
136            << "Measurements are numerically unstable at goal value.";
137      }
138    }
139
140    last_power_measurement_ = cur_power_measurement;
141    last_clipped_ = clipped;
142    ++measurement_count_;
143  }
144
145 private:
146  const float goal_power_measurement_;
147  const bool goal_clipped_;
148  int measurement_count_;
149  bool measurements_should_increase_;
150  float last_power_measurement_;
151  bool last_clipped_;
152
153  DISALLOW_COPY_AND_ASSIGN(MeasurementObserver);
154};
155
156}  // namespace
157
158class AudioPowerMonitorTest : public ::testing::TestWithParam<TestScenario> {
159 public:
160  AudioPowerMonitorTest()
161      : power_monitor_(
162            kSampleRate,
163            base::TimeDelta::FromMilliseconds(kTimeConstantMillis),
164            base::TimeDelta::FromMilliseconds(kMeasurementPeriodMillis),
165            &message_loop_,
166            base::Bind(&AudioPowerMonitorTest::OnPowerMeasured,
167                       base::Unretained(this))) {}
168
169  void FeedAndCheckExpectedPowerIsMeasured(
170      const AudioBus& bus, float power, bool clipped) {
171    // Feed the AudioPowerMonitor.  It should post tasks to |message_loop_|.
172    static const int kNumFeedIters = 100;
173    for (int i = 0; i < kNumFeedIters; ++i)
174      power_monitor_.Scan(bus, bus.frames());
175
176    // Set up an observer and run all the enqueued tasks.
177    MeasurementObserver observer(power, clipped);
178    current_observer_ = &observer;
179    message_loop_.RunUntilIdle();
180    current_observer_ = NULL;
181
182    // Check that the results recorded by the observer are the same whole-number
183    // dBFS.
184    EXPECT_EQ(static_cast<int>(power),
185              static_cast<int>(observer.last_power_measurement()));
186    EXPECT_EQ(clipped, observer.last_clipped());
187  }
188
189 private:
190  void OnPowerMeasured(float power, bool clipped) {
191    CHECK(current_observer_);
192    current_observer_->OnPowerMeasured(power, clipped);
193  }
194
195  base::MessageLoop message_loop_;
196  AudioPowerMonitor power_monitor_;
197  MeasurementObserver* current_observer_;
198
199  DISALLOW_COPY_AND_ASSIGN(AudioPowerMonitorTest);
200};
201
202TEST_P(AudioPowerMonitorTest, MeasuresPowerOfSignal) {
203  const TestScenario& scenario = GetParam();
204
205  scoped_ptr<AudioBus> zeroed_bus =
206      AudioBus::Create(scenario.data().channels(), scenario.data().frames());
207  zeroed_bus->Zero();
208
209  // Send a "zero power" audio signal, then this scenario's audio signal, then
210  // the "zero power" audio signal again; testing that the power monitor
211  // measurements match expected values.
212  FeedAndCheckExpectedPowerIsMeasured(
213      *zeroed_bus, AudioPowerMonitor::zero_power(), false);
214  FeedAndCheckExpectedPowerIsMeasured(
215      scenario.data(), scenario.expected_power(), scenario.expected_clipped());
216  FeedAndCheckExpectedPowerIsMeasured(
217      *zeroed_bus, AudioPowerMonitor::zero_power(), false);
218}
219
220static const float kMonoSilentNoise[] = {
221  0.01f, -0.01f
222};
223
224static const float kMonoMaxAmplitude[] = {
225  1.0f
226};
227
228static const float kMonoMaxAmplitude2[] = {
229  -1.0f, 1.0f
230};
231
232static const float kMonoHalfMaxAmplitude[] = {
233  0.5f, -0.5f, 0.5f, -0.5f
234};
235
236static const float kMonoAmplitudeClipped[] = {
237  2.0f, -2.0f
238};
239
240static const float kMonoMaxAmplitudeWithClip[] = {
241  2.0f, 0.0, 0.0f, 0.0f
242};
243
244static const float kMonoMaxAmplitudeWithClip2[] = {
245  4.0f, 0.0, 0.0f, 0.0f
246};
247
248static const float kStereoSilentNoise[] = {
249  // left channel
250  0.005f, -0.005f,
251  // right channel
252  0.005f, -0.005f
253};
254
255static const float kStereoMaxAmplitude[] = {
256  // left channel
257  1.0f, -1.0f,
258  // right channel
259  -1.0f, 1.0f
260};
261
262static const float kRightChannelMaxAmplitude[] = {
263  // left channel
264  0.0f, 0.0f, 0.0f, 0.0f,
265  // right channel
266  -1.0f, 1.0f, -1.0f, 1.0f
267};
268
269static const float kLeftChannelHalfMaxAmplitude[] = {
270  // left channel
271  0.5f, -0.5f, 0.5f, -0.5f,
272  // right channel
273  0.0f, 0.0f, 0.0f, 0.0f,
274};
275
276static const float kStereoMixed[] = {
277  // left channel
278  0.5f, -0.5f, 0.5f, -0.5f,
279  // right channel
280  -1.0f, 1.0f, -1.0f, 1.0f
281};
282
283static const float kStereoMixed2[] = {
284  // left channel
285  1.0f, -1.0f, 0.75f, -0.75f, 0.5f, -0.5f, 0.25f, -0.25f,
286  // right channel
287  0.25f, -0.25f, 0.5f, -0.5f, 0.75f, -0.75f, 1.0f, -1.0f
288};
289
290INSTANTIATE_TEST_CASE_P(
291    Scenarios, AudioPowerMonitorTest,
292    ::testing::Values(
293         TestScenario(kMonoSilentNoise, 1, 2, -40, false),
294         TestScenario(kMonoMaxAmplitude, 1, 1,
295                      AudioPowerMonitor::max_power(), false),
296         TestScenario(kMonoMaxAmplitude2, 1, 2,
297                      AudioPowerMonitor::max_power(), false),
298         TestScenario(kMonoHalfMaxAmplitude, 1, 4, -6, false),
299         TestScenario(kMonoAmplitudeClipped, 1, 2,
300                      AudioPowerMonitor::max_power(), true),
301         TestScenario(kMonoMaxAmplitudeWithClip, 1, 4,
302                      AudioPowerMonitor::max_power(), true),
303         TestScenario(kMonoMaxAmplitudeWithClip2, 1, 4,
304                      AudioPowerMonitor::max_power(), true),
305         TestScenario(kMonoSilentNoise, 1, 2,
306                      AudioPowerMonitor::zero_power(), true).
307             WithABadSample(std::numeric_limits<float>::infinity()),
308         TestScenario(kMonoHalfMaxAmplitude, 1, 4,
309                      AudioPowerMonitor::zero_power(), false).
310             WithABadSample(std::numeric_limits<float>::quiet_NaN()),
311         TestScenario(kStereoSilentNoise, 2, 2, -46, false),
312         TestScenario(kStereoMaxAmplitude, 2, 2,
313                      AudioPowerMonitor::max_power(), false),
314         TestScenario(kRightChannelMaxAmplitude, 2, 4, -3, false),
315         TestScenario(kLeftChannelHalfMaxAmplitude, 2, 4, -9, false),
316         TestScenario(kStereoMixed, 2, 4, -2, false),
317         TestScenario(kStereoMixed2, 2, 8, -3, false)));
318
319}  // namespace media
320