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