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 13 namespace media { 14 15 static const int kSampleRate = 48000; 16 static const int kFramesPerBuffer = 128; 17 18 static const int kTimeConstantMillis = 5; 19 20 namespace { 21 22 // Container for each parameterized test's data (input and expected results). 23 class 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. 92 class 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 154 class 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 187 TEST_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 205 static const float kMonoSilentNoise[] = { 206 0.01f, -0.01f 207 }; 208 209 static const float kMonoMaxAmplitude[] = { 210 1.0f 211 }; 212 213 static const float kMonoMaxAmplitude2[] = { 214 -1.0f, 1.0f 215 }; 216 217 static const float kMonoHalfMaxAmplitude[] = { 218 0.5f, -0.5f, 0.5f, -0.5f 219 }; 220 221 static const float kMonoAmplitudeClipped[] = { 222 2.0f, -2.0f 223 }; 224 225 static const float kMonoMaxAmplitudeWithClip[] = { 226 2.0f, 0.0, 0.0f, 0.0f 227 }; 228 229 static const float kMonoMaxAmplitudeWithClip2[] = { 230 4.0f, 0.0, 0.0f, 0.0f 231 }; 232 233 static const float kStereoSilentNoise[] = { 234 // left channel 235 0.005f, -0.005f, 236 // right channel 237 0.005f, -0.005f 238 }; 239 240 static const float kStereoMaxAmplitude[] = { 241 // left channel 242 1.0f, -1.0f, 243 // right channel 244 -1.0f, 1.0f 245 }; 246 247 static 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 254 static 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 261 static 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 268 static 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 275 INSTANTIATE_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