| 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_silence_detector.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/bind_helpers.h" | |
| 9 #include "base/message_loop.h" | |
| 10 #include "base/synchronization/waitable_event.h" | |
| 11 #include "base/threading/thread.h" | |
| 12 #include "base/time.h" | |
| 13 #include "media/base/audio_bus.h" | |
| 14 #include "testing/gmock/include/gmock/gmock.h" | |
| 15 #include "testing/gtest/include/gtest/gtest.h" | |
| 16 | |
| 17 using ::testing::_; | |
| 18 using ::testing::InvokeWithoutArgs; | |
| 19 using ::testing::TestWithParam; | |
| 20 using ::testing::Values; | |
| 21 | |
| 22 namespace media { | |
| 23 | |
| 24 static const int kSampleRate = 48000; | |
| 25 static const int kFramesPerBuffer = 128; | |
| 26 | |
| 27 namespace { | |
| 28 | |
| 29 class MockObserver { | |
| 30 public: | |
| 31 MOCK_METHOD1(OnAudible, void(bool)); | |
| 32 }; | |
| 33 | |
| 34 struct TestScenario { | |
| 35 const float* data; | |
| 36 int num_channels; | |
| 37 int num_frames; | |
| 38 float value_range; | |
| 39 | |
| 40 TestScenario(const float* d, int c, int f, float v) | |
| 41 : data(d), num_channels(c), num_frames(f), value_range(v) {} | |
| 42 }; | |
| 43 | |
| 44 } // namespace | |
| 45 | |
| 46 class AudioSilenceDetectorTest : public TestWithParam<TestScenario> { | |
| 47 public: | |
| 48 AudioSilenceDetectorTest() | |
| 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 ~AudioSilenceDetectorTest() { | |
| 56 SyncWithAudioThread(); | |
| 57 } | |
| 58 | |
| 59 AudioSilenceDetector* silence_detector() const { | |
| 60 return silence_detector_.get(); | |
| 61 } | |
| 62 | |
| 63 void StartSilenceDetector(float threshold, MockObserver* observer) { | |
| 64 audio_message_loop_->PostTask( | |
| 65 FROM_HERE, | |
| 66 base::Bind(&AudioSilenceDetectorTest::StartDetectorOnAudioThread, | |
| 67 base::Unretained(this), threshold, observer)); | |
| 68 SyncWithAudioThread(); | |
| 69 } | |
| 70 | |
| 71 void StopSilenceDetector() { | |
| 72 audio_message_loop_->PostTask( | |
| 73 FROM_HERE, | |
| 74 base::Bind(&AudioSilenceDetectorTest::StopDetectorOnAudioThread, | |
| 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); | |
| 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 void SignalNotificationReceived() { | |
| 95 notification_received_.Signal(); | |
| 96 } | |
| 97 | |
| 98 void WaitForNotificationReceived() { | |
| 99 notification_received_.Wait(); | |
| 100 } | |
| 101 | |
| 102 // Post a task on the audio thread and block until it is executed. This | |
| 103 // provides a barrier: All previous tasks pending on the audio thread have | |
| 104 // completed before this method returns. | |
| 105 void SyncWithAudioThread() { | |
| 106 base::WaitableEvent done(false, false); | |
| 107 audio_message_loop_->PostTask( | |
| 108 FROM_HERE, | |
| 109 base::Bind(&base::WaitableEvent::Signal, base::Unretained(&done))); | |
| 110 done.Wait(); | |
| 111 } | |
| 112 | |
| 113 private: | |
| 114 void StartDetectorOnAudioThread(float threshold, MockObserver* observer) { | |
| 115 const AudioSilenceDetector::AudibleCallback notify_is_audible = | |
| 116 base::Bind(&MockObserver::OnAudible, base::Unretained(observer)); | |
| 117 silence_detector_.reset(new AudioSilenceDetector( | |
| 118 kSampleRate, base::TimeDelta::FromMilliseconds(1), threshold)); | |
| 119 silence_detector_->Start(notify_is_audible); | |
| 120 } | |
| 121 | |
| 122 void StopDetectorOnAudioThread() { | |
| 123 silence_detector_->Stop(true); | |
| 124 silence_detector_.reset(); | |
| 125 } | |
| 126 | |
| 127 scoped_ptr<base::Thread> audio_manager_thread_; | |
| 128 scoped_refptr<base::MessageLoopProxy> audio_message_loop_; | |
| 129 | |
| 130 base::WaitableEvent notification_received_; | |
| 131 | |
| 132 scoped_ptr<AudioSilenceDetector> silence_detector_; | |
| 133 | |
| 134 DISALLOW_COPY_AND_ASSIGN(AudioSilenceDetectorTest); | |
| 135 }; | |
| 136 | |
| 137 TEST_P(AudioSilenceDetectorTest, TriggersAtVariousThresholds) { | |
| 138 static const float kThresholdsToTry[] = | |
| 139 { 0.0f, 0.125f, 0.25f, 0.5f, 0.75f, 1.0f }; | |
| 140 | |
| 141 const TestScenario& scenario = GetParam(); | |
| 142 | |
| 143 for (size_t i = 0; i < arraysize(kThresholdsToTry); ++i) { | |
| 144 MockObserver observer; | |
| 145 | |
| 146 // Start the detector. This should cause a single callback to indicate | |
| 147 // we're starting out in silence. | |
| 148 EXPECT_CALL(observer, OnAudible(false)) | |
| 149 .WillOnce(InvokeWithoutArgs( | |
| 150 this, &AudioSilenceDetectorTest::SignalNotificationReceived)) | |
| 151 .RetiresOnSaturation(); | |
| 152 StartSilenceDetector(kThresholdsToTry[i], &observer); | |
| 153 WaitForNotificationReceived(); | |
| 154 | |
| 155 // Send more data to the silence detector. For some test scenarios, the | |
| 156 // sound data will trigger a callback. | |
| 157 const bool expect_a_callback = (kThresholdsToTry[i] < scenario.value_range); | |
| 158 if (expect_a_callback) { | |
| 159 EXPECT_CALL(observer, OnAudible(true)) | |
| 160 .WillOnce(InvokeWithoutArgs( | |
| 161 this, &AudioSilenceDetectorTest::SignalNotificationReceived)) | |
| 162 .RetiresOnSaturation(); | |
| 163 } | |
| 164 scoped_ptr<AudioBus> bus = CreatePopulatedBuffer( | |
| 165 scenario.data, scenario.num_channels, scenario.num_frames); | |
| 166 silence_detector()->Scan(bus.get(), bus->frames()); | |
| 167 if (expect_a_callback) | |
| 168 WaitForNotificationReceived(); | |
| 169 | |
| 170 // Stop the detector. This should cause another callback to indicate we're | |
| 171 // ending in silence. | |
| 172 EXPECT_CALL(observer, OnAudible(false)) | |
| 173 .WillOnce(InvokeWithoutArgs( | |
| 174 this, &AudioSilenceDetectorTest::SignalNotificationReceived)) | |
| 175 .RetiresOnSaturation(); | |
| 176 StopSilenceDetector(); | |
| 177 WaitForNotificationReceived(); | |
| 178 | |
| 179 SyncWithAudioThread(); | |
| 180 } | |
| 181 } | |
| 182 | |
| 183 static const float kAllZeros[] = { | |
| 184 // left channel | |
| 185 0.0f, | |
| 186 // right channel | |
| 187 0.0f | |
| 188 }; | |
| 189 | |
| 190 static const float kAllOnes[] = { | |
| 191 // left channel | |
| 192 1.0f, | |
| 193 // right channel | |
| 194 1.0f | |
| 195 }; | |
| 196 | |
| 197 static const float kSilentLeftChannel[] = { | |
| 198 // left channel | |
| 199 0.5f, 0.5f, 0.5f, 0.5f, | |
| 200 // right channel | |
| 201 0.0f, 0.25f, 0.0f, 0.0f | |
| 202 }; | |
| 203 | |
| 204 static const float kSilentRightChannel[] = { | |
| 205 // left channel | |
| 206 1.0f, 1.0f, 0.75f, 0.5f, 1.0f, | |
| 207 // right channel | |
| 208 0.0f, 0.0f, 0.0f, 0.0f, 0.0f | |
| 209 }; | |
| 210 | |
| 211 static const float kAtDifferentVolumesAndBias[] = { | |
| 212 // left channel | |
| 213 1.0f, 0.9f, 0.8f, 0.7f, 0.6f, 0.5f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, | |
| 214 // right channel | |
| 215 0.0f, 0.2f, 0.4f, 0.6f, 0.4f, 0.2f, 0.0f, 0.2f, 0.4f, 0.6f, 0.4f, 0.2f | |
| 216 }; | |
| 217 | |
| 218 INSTANTIATE_TEST_CASE_P( | |
| 219 Scenarios, AudioSilenceDetectorTest, | |
| 220 Values( | |
| 221 TestScenario(kAllZeros, 2, 1, 0.0f), | |
| 222 TestScenario(kAllOnes, 2, 1, 0.0f), | |
| 223 TestScenario(kSilentLeftChannel, 2, 4, 0.25f), | |
| 224 TestScenario(kSilentRightChannel, 2, 5, 0.5f), | |
| 225 TestScenario(kAtDifferentVolumesAndBias, 2, 12, 0.6f))); | |
| 226 | |
| 227 } // namespace media | |
| OLD | NEW |