OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 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 <cmath> |
| 8 #include <limits> |
| 9 |
| 10 #include "base/bind.h" |
| 11 #include "base/bind_helpers.h" |
| 12 #include "base/message_loop.h" |
| 13 #include "base/synchronization/waitable_event.h" |
| 14 #include "base/threading/thread.h" |
| 15 #include "base/time.h" |
| 16 #include "media/base/audio_bus.h" |
| 17 #include "testing/gmock/include/gmock/gmock.h" |
| 18 #include "testing/gtest/include/gtest/gtest.h" |
| 19 |
| 20 using ::testing::_; |
| 21 using ::testing::FloatEq; |
| 22 using ::testing::InvokeWithoutArgs; |
| 23 using ::testing::TestWithParam; |
| 24 using ::testing::Values; |
| 25 |
| 26 namespace media { |
| 27 |
| 28 static const int kSampleRate = 48000; |
| 29 static const int kFramesPerBuffer = 128; |
| 30 |
| 31 namespace { |
| 32 |
| 33 class MockObserver { |
| 34 public: |
| 35 MOCK_METHOD1(OnPowerMeasured, void(float)); |
| 36 }; |
| 37 |
| 38 struct TestScenario { |
| 39 const float* data; |
| 40 int num_channels; |
| 41 int num_frames; |
| 42 float power_in_dbfs; |
| 43 |
| 44 TestScenario(const float* d, int c, int f, float p) |
| 45 : data(d), num_channels(c), num_frames(f), power_in_dbfs(p) {} |
| 46 }; |
| 47 |
| 48 } // namespace |
| 49 |
| 50 class AudioPowerMonitorTest : public TestWithParam<TestScenario> { |
| 51 public: |
| 52 AudioPowerMonitorTest() |
| 53 : audio_manager_thread_(new base::Thread("AudioThread")), |
| 54 notification_received_(false, false) { |
| 55 audio_manager_thread_->Start(); |
| 56 audio_message_loop_ = audio_manager_thread_->message_loop_proxy(); |
| 57 } |
| 58 |
| 59 virtual ~AudioPowerMonitorTest() { |
| 60 SyncWithAudioThread(); |
| 61 } |
| 62 |
| 63 AudioPowerMonitor* power_monitor() const { |
| 64 return power_monitor_.get(); |
| 65 } |
| 66 |
| 67 void StartPowerMonitor(MockObserver* observer) { |
| 68 audio_message_loop_->PostTask( |
| 69 FROM_HERE, |
| 70 base::Bind(&AudioPowerMonitorTest::StartMonitorOnAudioThread, |
| 71 base::Unretained(this), observer)); |
| 72 SyncWithAudioThread(); |
| 73 } |
| 74 |
| 75 void StopPowerMonitor() { |
| 76 audio_message_loop_->PostTask( |
| 77 FROM_HERE, |
| 78 base::Bind(&AudioPowerMonitorTest::StopMonitorOnAudioThread, |
| 79 base::Unretained(this))); |
| 80 SyncWithAudioThread(); |
| 81 } |
| 82 |
| 83 // Creates an AudioBus, sized and populated with kFramesPerBuffer frames of |
| 84 // data. The given test |data| is repeated to fill the buffer. |
| 85 scoped_ptr<AudioBus> CreatePopulatedBuffer( |
| 86 const float* data, int num_channels, int num_frames) { |
| 87 scoped_ptr<AudioBus> bus = AudioBus::Create(num_channels, kFramesPerBuffer); |
| 88 for (int ch = 0; ch < num_channels; ++ch) { |
| 89 for (int frames = 0; frames < kFramesPerBuffer; frames += num_frames) { |
| 90 const int num_to_copy = std::min(num_frames, kFramesPerBuffer - frames); |
| 91 memcpy(bus->channel(ch) + frames, data + num_frames * ch, |
| 92 sizeof(float) * num_to_copy); |
| 93 } |
| 94 } |
| 95 return bus.Pass(); |
| 96 } |
| 97 |
| 98 scoped_ptr<AudioBus> CreateZeroedBuffer(int num_channels) { |
| 99 scoped_ptr<AudioBus> bus = AudioBus::Create(num_channels, kFramesPerBuffer); |
| 100 bus->Zero(); |
| 101 return bus.Pass(); |
| 102 } |
| 103 |
| 104 |
| 105 void SignalNotificationReceived() { |
| 106 notification_received_.Signal(); |
| 107 } |
| 108 |
| 109 void WaitForNotificationReceived() { |
| 110 notification_received_.Wait(); |
| 111 } |
| 112 |
| 113 // Post a task on the audio thread and block until it is executed. This |
| 114 // provides a barrier: All previous tasks pending on the audio thread have |
| 115 // completed before this method returns. |
| 116 void SyncWithAudioThread() { |
| 117 base::WaitableEvent done(false, false); |
| 118 audio_message_loop_->PostTask( |
| 119 FROM_HERE, |
| 120 base::Bind(&base::WaitableEvent::Signal, base::Unretained(&done))); |
| 121 done.Wait(); |
| 122 } |
| 123 |
| 124 private: |
| 125 void StartMonitorOnAudioThread(MockObserver* observer) { |
| 126 const AudioPowerMonitor::PowerMeasurementCallback callback = |
| 127 base::Bind(&MockObserver::OnPowerMeasured, base::Unretained(observer)); |
| 128 power_monitor_.reset(new AudioPowerMonitor( |
| 129 kSampleRate, base::TimeDelta::FromMilliseconds(1), |
| 130 MessageLoop::current(), callback)); |
| 131 } |
| 132 |
| 133 void StopMonitorOnAudioThread() { |
| 134 power_monitor_.reset(); |
| 135 } |
| 136 |
| 137 scoped_ptr<base::Thread> audio_manager_thread_; |
| 138 scoped_refptr<base::MessageLoopProxy> audio_message_loop_; |
| 139 |
| 140 base::WaitableEvent notification_received_; |
| 141 |
| 142 scoped_ptr<AudioPowerMonitor> power_monitor_; |
| 143 |
| 144 DISALLOW_COPY_AND_ASSIGN(AudioPowerMonitorTest); |
| 145 }; |
| 146 |
| 147 TEST_P(AudioPowerMonitorTest, MeasuresPowerOfSignal) { |
| 148 const TestScenario& scenario = GetParam(); |
| 149 |
| 150 MockObserver observer; |
| 151 StartPowerMonitor(&observer); |
| 152 |
| 153 // Send silence and expect a callback with a "zero power" result. |
| 154 EXPECT_CALL(observer, |
| 155 OnPowerMeasured(FloatEq(AudioPowerMonitor::kZeroPowerDBFS))) |
| 156 .WillOnce(InvokeWithoutArgs( |
| 157 this, &AudioPowerMonitorTest::SignalNotificationReceived)) |
| 158 .RetiresOnSaturation(); |
| 159 scoped_ptr<AudioBus> bus = CreateZeroedBuffer(scenario.num_channels); |
| 160 power_monitor()->Scan(*bus, bus->frames()); |
| 161 WaitForNotificationReceived(); |
| 162 |
| 163 // Send audio signal data to the power monitor and expect a callback with the |
| 164 // correct result. |
| 165 EXPECT_CALL(observer, OnPowerMeasured(FloatEq(scenario.power_in_dbfs))) |
| 166 .WillOnce(InvokeWithoutArgs( |
| 167 this, &AudioPowerMonitorTest::SignalNotificationReceived)) |
| 168 .RetiresOnSaturation(); |
| 169 bus = CreatePopulatedBuffer( |
| 170 scenario.data, scenario.num_channels, scenario.num_frames); |
| 171 power_monitor()->Scan(*bus, bus->frames()); |
| 172 WaitForNotificationReceived(); |
| 173 |
| 174 // Send silence again and expect a callback with a "zero power" result again. |
| 175 EXPECT_CALL(observer, |
| 176 OnPowerMeasured(FloatEq(AudioPowerMonitor::kZeroPowerDBFS))) |
| 177 .WillOnce(InvokeWithoutArgs( |
| 178 this, &AudioPowerMonitorTest::SignalNotificationReceived)) |
| 179 .RetiresOnSaturation(); |
| 180 bus = CreateZeroedBuffer(scenario.num_channels); |
| 181 power_monitor()->Scan(*bus, bus->frames()); |
| 182 WaitForNotificationReceived(); |
| 183 |
| 184 StopPowerMonitor(); |
| 185 } |
| 186 |
| 187 static const float kMonoSilence[] = { |
| 188 0.0f |
| 189 }; |
| 190 |
| 191 static const float kMonoMaxAmplitude[] = { |
| 192 1.0f |
| 193 }; |
| 194 |
| 195 static const float kMonoMaxAmplitude2[] = { |
| 196 -1.0f, 1.0f, |
| 197 }; |
| 198 |
| 199 static const float kMonoHalfMaxAmplitude[] = { |
| 200 0.5f, -0.5f, 0.5f, -0.5f |
| 201 }; |
| 202 |
| 203 static const float kMonoNeedsClipping[] = { |
| 204 5.0f, -0.5, 0.5f, -0.5f |
| 205 }; |
| 206 |
| 207 static const float kMonoContainsInfinity[] = { |
| 208 0.0f, 0.0f, 0.0f, std::numeric_limits<float>::infinity() |
| 209 }; |
| 210 |
| 211 static const float kMonoContainsNaN[] = { |
| 212 0.5f, -0.5f, 0.5f, std::numeric_limits<float>::quiet_NaN() |
| 213 }; |
| 214 |
| 215 static const float kStereoSilence[] = { |
| 216 // left channel |
| 217 0.0f, |
| 218 // right channel |
| 219 0.0f |
| 220 }; |
| 221 |
| 222 static const float kStereoMaxAmplitude[] = { |
| 223 // left channel |
| 224 1.0f, -1.0f, |
| 225 // right channel |
| 226 -1.0f, 1.0f |
| 227 }; |
| 228 |
| 229 static const float kRightChannelMaxAmplitude[] = { |
| 230 // left channel |
| 231 0.0f, 0.0f, 0.0f, 0.0f, |
| 232 // right channel |
| 233 -1.0f, 1.0f, -1.0f, 1.0f |
| 234 }; |
| 235 |
| 236 static const float kLeftChannelHalfMaxAmplitude[] = { |
| 237 // left channel |
| 238 0.5f, -0.5f, 0.5f, -0.5f, |
| 239 // right channel |
| 240 0.0f, 0.0f, 0.0f, 0.0f, |
| 241 }; |
| 242 |
| 243 static const float kStereoMixed[] = { |
| 244 // left channel |
| 245 0.5f, -0.5f, 0.5f, -0.5f, |
| 246 // right channel |
| 247 -1.0f, 1.0f, -1.0f, 1.0f |
| 248 }; |
| 249 |
| 250 static const float kStereoMixed2[] = { |
| 251 // left channel |
| 252 1.0f, -1.0f, 0.5f, -0.5f, 0.25f, -0.25f, 0.125f, -0.125f, |
| 253 // right channel |
| 254 0.125f, -0.125f, 0.25f, -0.25f, 0.5f, -0.5f, 1.0f, -1.0f |
| 255 }; |
| 256 |
| 257 INSTANTIATE_TEST_CASE_P( |
| 258 Scenarios, AudioPowerMonitorTest, |
| 259 Values( |
| 260 TestScenario(kMonoSilence, 1, 1, |
| 261 AudioPowerMonitor::kZeroPowerDBFS), |
| 262 TestScenario(kMonoMaxAmplitude, 1, 1, |
| 263 AudioPowerMonitor::kMaxPowerDBFS), |
| 264 TestScenario(kMonoMaxAmplitude2, 1, 2, |
| 265 AudioPowerMonitor::kMaxPowerDBFS), |
| 266 TestScenario(kMonoHalfMaxAmplitude, 1, 4, |
| 267 -6.0206f), |
| 268 TestScenario(kMonoNeedsClipping, 1, 4, |
| 269 AudioPowerMonitor::kMaxPowerDBFS), |
| 270 TestScenario(kMonoContainsInfinity, 1, 4, |
| 271 AudioPowerMonitor::kMaxPowerDBFS), |
| 272 TestScenario(kMonoContainsNaN, 1, 4, |
| 273 AudioPowerMonitor::kZeroPowerDBFS), |
| 274 TestScenario(kStereoSilence, 2, 1, |
| 275 AudioPowerMonitor::kZeroPowerDBFS), |
| 276 TestScenario(kStereoMaxAmplitude, 2, 2, |
| 277 AudioPowerMonitor::kMaxPowerDBFS), |
| 278 TestScenario(kRightChannelMaxAmplitude, 2, 4, |
| 279 -3.0103f), |
| 280 TestScenario(kLeftChannelHalfMaxAmplitude, 2, 4, |
| 281 -9.0309f), |
| 282 TestScenario(kStereoMixed, 2, 4, |
| 283 -2.0412f), |
| 284 TestScenario(kStereoMixed2, 2, 8, |
| 285 -4.78821f))); |
| 286 |
| 287 } // namespace media |
OLD | NEW |