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 |