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