audio_power_monitor_unittest.cc revision c2db58bd994c04d98e4ee2cd7565b71548655fe3
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