audio_power_monitor_unittest.cc revision ca12bfac764ba476d6cd062bf1dde12cc64c3f40
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// An observer that receives power measurements.  Each power measurement should
86// should make progress towards the goal value.
87class MeasurementObserver {
88 public:
89  MeasurementObserver(float goal_power_measurement, bool goal_clipped)
90      : goal_power_measurement_(goal_power_measurement),
91        goal_clipped_(goal_clipped), measurement_count_(0),
92        last_power_measurement_(AudioPowerMonitor::zero_power()),
93        last_clipped_(false) {}
94
95  int measurement_count() const {
96    return measurement_count_;
97  }
98
99  float last_power_measurement() const {
100    return last_power_measurement_;
101  }
102
103  bool last_clipped() const {
104    return last_clipped_;
105  }
106
107  void OnPowerMeasured(float cur_power_measurement, bool clipped) {
108    if (measurement_count_ == 0) {
109      measurements_should_increase_ =
110          (cur_power_measurement < goal_power_measurement_);
111    } else {
112      SCOPED_TRACE(::testing::Message()
113                   << "Power: goal=" << goal_power_measurement_
114                   << "; last=" << last_power_measurement_
115                   << "; cur=" << cur_power_measurement);
116
117      if (last_power_measurement_ != goal_power_measurement_) {
118        if (measurements_should_increase_) {
119          EXPECT_LE(last_power_measurement_, cur_power_measurement)
120              << "Measurements should be monotonically increasing.";
121        } else {
122          EXPECT_GE(last_power_measurement_, cur_power_measurement)
123              << "Measurements should be monotonically decreasing.";
124        }
125      } else {
126        EXPECT_EQ(last_power_measurement_, cur_power_measurement)
127            << "Measurements are numerically unstable at goal value.";
128      }
129    }
130
131    last_power_measurement_ = cur_power_measurement;
132    last_clipped_ = clipped;
133    ++measurement_count_;
134  }
135
136 private:
137  const float goal_power_measurement_;
138  const bool goal_clipped_;
139  int measurement_count_;
140  bool measurements_should_increase_;
141  float last_power_measurement_;
142  bool last_clipped_;
143
144  DISALLOW_COPY_AND_ASSIGN(MeasurementObserver);
145};
146
147}  // namespace
148
149class AudioPowerMonitorTest : public ::testing::TestWithParam<TestScenario> {
150 public:
151  AudioPowerMonitorTest()
152      : power_monitor_(
153            kSampleRate,
154            base::TimeDelta::FromMilliseconds(kTimeConstantMillis),
155            base::TimeDelta::FromMilliseconds(kMeasurementPeriodMillis),
156            &message_loop_,
157            base::Bind(&AudioPowerMonitorTest::OnPowerMeasured,
158                       base::Unretained(this))) {}
159
160  void FeedAndCheckExpectedPowerIsMeasured(
161      const AudioBus& bus, float power, bool clipped) {
162    // Feed the AudioPowerMonitor.  It should post tasks to |message_loop_|.
163    static const int kNumFeedIters = 100;
164    for (int i = 0; i < kNumFeedIters; ++i)
165      power_monitor_.Scan(bus, bus.frames());
166
167    // Set up an observer and run all the enqueued tasks.
168    MeasurementObserver observer(power, clipped);
169    current_observer_ = &observer;
170    message_loop_.RunUntilIdle();
171    current_observer_ = NULL;
172
173    // Check that the results recorded by the observer are the same whole-number
174    // dBFS.
175    EXPECT_EQ(static_cast<int>(power),
176              static_cast<int>(observer.last_power_measurement()));
177    EXPECT_EQ(clipped, observer.last_clipped());
178  }
179
180 private:
181  void OnPowerMeasured(float power, bool clipped) {
182    CHECK(current_observer_);
183    current_observer_->OnPowerMeasured(power, clipped);
184  }
185
186  base::MessageLoop message_loop_;
187  AudioPowerMonitor power_monitor_;
188  MeasurementObserver* current_observer_;
189
190  DISALLOW_COPY_AND_ASSIGN(AudioPowerMonitorTest);
191};
192
193TEST_P(AudioPowerMonitorTest, MeasuresPowerOfSignal) {
194  const TestScenario& scenario = GetParam();
195
196  scoped_ptr<AudioBus> zeroed_bus =
197      AudioBus::Create(scenario.data().channels(), scenario.data().frames());
198  zeroed_bus->Zero();
199
200  // Send a "zero power" audio signal, then this scenario's audio signal, then
201  // the "zero power" audio signal again; testing that the power monitor
202  // measurements match expected values.
203  FeedAndCheckExpectedPowerIsMeasured(
204      *zeroed_bus, AudioPowerMonitor::zero_power(), false);
205  FeedAndCheckExpectedPowerIsMeasured(
206      scenario.data(), scenario.expected_power(), scenario.expected_clipped());
207  FeedAndCheckExpectedPowerIsMeasured(
208      *zeroed_bus, AudioPowerMonitor::zero_power(), false);
209}
210
211static const float kMonoSilentNoise[] = {
212  0.01f, -0.01f
213};
214
215static const float kMonoMaxAmplitude[] = {
216  1.0f
217};
218
219static const float kMonoMaxAmplitude2[] = {
220  -1.0f, 1.0f
221};
222
223static const float kMonoHalfMaxAmplitude[] = {
224  0.5f, -0.5f, 0.5f, -0.5f
225};
226
227static const float kMonoAmplitudeClipped[] = {
228  2.0f, -2.0f
229};
230
231static const float kMonoMaxAmplitudeWithClip[] = {
232  2.0f, 0.0, 0.0f, 0.0f
233};
234
235static const float kMonoMaxAmplitudeWithClip2[] = {
236  4.0f, 0.0, 0.0f, 0.0f
237};
238
239static const float kStereoSilentNoise[] = {
240  // left channel
241  0.005f, -0.005f,
242  // right channel
243  0.005f, -0.005f
244};
245
246static const float kStereoMaxAmplitude[] = {
247  // left channel
248  1.0f, -1.0f,
249  // right channel
250  -1.0f, 1.0f
251};
252
253static const float kRightChannelMaxAmplitude[] = {
254  // left channel
255  0.0f, 0.0f, 0.0f, 0.0f,
256  // right channel
257  -1.0f, 1.0f, -1.0f, 1.0f
258};
259
260static const float kLeftChannelHalfMaxAmplitude[] = {
261  // left channel
262  0.5f, -0.5f, 0.5f, -0.5f,
263  // right channel
264  0.0f, 0.0f, 0.0f, 0.0f,
265};
266
267static const float kStereoMixed[] = {
268  // left channel
269  0.5f, -0.5f, 0.5f, -0.5f,
270  // right channel
271  -1.0f, 1.0f, -1.0f, 1.0f
272};
273
274static const float kStereoMixed2[] = {
275  // left channel
276  1.0f, -1.0f, 0.75f, -0.75f, 0.5f, -0.5f, 0.25f, -0.25f,
277  // right channel
278  0.25f, -0.25f, 0.5f, -0.5f, 0.75f, -0.75f, 1.0f, -1.0f
279};
280
281INSTANTIATE_TEST_CASE_P(
282    Scenarios, AudioPowerMonitorTest,
283    ::testing::Values(
284         TestScenario(kMonoSilentNoise, 1, 2, -40, false),
285         TestScenario(kMonoMaxAmplitude, 1, 1,
286                      AudioPowerMonitor::max_power(), false),
287         TestScenario(kMonoMaxAmplitude2, 1, 2,
288                      AudioPowerMonitor::max_power(), false),
289         TestScenario(kMonoHalfMaxAmplitude, 1, 4, -6, false),
290         TestScenario(kMonoAmplitudeClipped, 1, 2,
291                      AudioPowerMonitor::max_power(), true),
292         TestScenario(kMonoMaxAmplitudeWithClip, 1, 4,
293                      AudioPowerMonitor::max_power(), true),
294         TestScenario(kMonoMaxAmplitudeWithClip2, 1, 4,
295                      AudioPowerMonitor::max_power(), true),
296         TestScenario(kMonoSilentNoise, 1, 2,
297                      AudioPowerMonitor::zero_power(), true).
298             WithABadSample(std::numeric_limits<float>::infinity()),
299         TestScenario(kMonoHalfMaxAmplitude, 1, 4,
300                      AudioPowerMonitor::zero_power(), false).
301             WithABadSample(std::numeric_limits<float>::quiet_NaN()),
302         TestScenario(kStereoSilentNoise, 2, 2, -46, false),
303         TestScenario(kStereoMaxAmplitude, 2, 2,
304                      AudioPowerMonitor::max_power(), false),
305         TestScenario(kRightChannelMaxAmplitude, 2, 4, -3, false),
306         TestScenario(kLeftChannelHalfMaxAmplitude, 2, 4, -9, false),
307         TestScenario(kStereoMixed, 2, 4, -2, false),
308         TestScenario(kStereoMixed2, 2, 8, -3, false)));
309
310}  // namespace media
311