OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2014 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 "components/copresence/mediums/audio/audio_recorder.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/memory/aligned_memory.h" | |
9 #include "base/run_loop.h" | |
10 #include "components/copresence/public/copresence_constants.h" | |
11 #include "components/copresence/test/audio_test_support.h" | |
12 #include "content/public/test/test_browser_thread_bundle.h" | |
13 #include "media/audio/audio_manager.h" | |
14 #include "media/audio/audio_manager_base.h" | |
15 #include "media/base/audio_bus.h" | |
16 #include "testing/gtest/include/gtest/gtest.h" | |
17 | |
18 namespace { | |
19 | |
20 class TestAudioInputStream : public media::AudioInputStream { | |
21 public: | |
22 TestAudioInputStream(const media::AudioParameters& params, | |
23 const std::vector<float*> channel_data, | |
24 size_t samples) | |
25 : callback_(NULL), params_(params) { | |
26 buffer_ = media::AudioBus::CreateWrapper(2); | |
27 for (size_t i = 0; i < channel_data.size(); ++i) | |
28 buffer_->SetChannelData(i, channel_data[i]); | |
29 buffer_->set_frames(samples); | |
30 } | |
31 | |
32 virtual ~TestAudioInputStream() {} | |
33 | |
34 virtual bool Open() OVERRIDE { return true; } | |
35 virtual void Start(AudioInputCallback* callback) OVERRIDE { | |
36 DCHECK(callback); | |
37 callback_ = callback; | |
38 media::AudioManager::Get()->GetTaskRunner()->PostTask( | |
39 FROM_HERE, | |
40 base::Bind(&TestAudioInputStream::SimulateRecording, | |
41 base::Unretained(this))); | |
42 } | |
43 virtual void Stop() OVERRIDE {} | |
44 virtual void Close() OVERRIDE {} | |
45 virtual double GetMaxVolume() OVERRIDE { return 1.0; } | |
46 virtual void SetVolume(double volume) OVERRIDE {} | |
47 virtual double GetVolume() OVERRIDE { return 1.0; } | |
48 virtual void SetAutomaticGainControl(bool enabled) OVERRIDE {} | |
49 virtual bool GetAutomaticGainControl() OVERRIDE { return true; } | |
50 | |
51 private: | |
52 void SimulateRecording() { | |
53 const int fpb = params_.frames_per_buffer(); | |
54 for (int i = 0; i < buffer_->frames() / fpb; ++i) { | |
55 scoped_ptr<media::AudioBus> source = media::AudioBus::Create(2, fpb); | |
56 buffer_->CopyPartialFramesTo(i * fpb, fpb, 0, source.get()); | |
57 callback_->OnData(this, source.get(), fpb, 1.0); | |
58 } | |
59 } | |
60 | |
61 AudioInputCallback* callback_; | |
62 media::AudioParameters params_; | |
63 scoped_ptr<media::AudioBus> buffer_; | |
64 | |
65 DISALLOW_COPY_AND_ASSIGN(TestAudioInputStream); | |
66 }; | |
67 | |
68 } // namespace | |
69 | |
70 namespace copresence { | |
71 | |
72 class AudioRecorderTest : public testing::Test { | |
73 public: | |
74 AudioRecorderTest() : total_samples_(0), recorder_(NULL) { | |
75 if (!media::AudioManager::Get()) | |
76 media::AudioManager::CreateForTesting(); | |
77 } | |
78 | |
79 virtual ~AudioRecorderTest() { | |
80 DeleteRecorder(); | |
81 for (size_t i = 0; i < channel_data_.size(); ++i) | |
82 base::AlignedFree(channel_data_[i]); | |
83 } | |
84 | |
85 void CreateSimpleRecorder() { | |
86 DeleteRecorder(); | |
87 recorder_ = new AudioRecorder( | |
88 base::Bind(&AudioRecorderTest::DecodeSamples, base::Unretained(this))); | |
89 recorder_->Initialize(); | |
90 } | |
91 | |
92 void CreateRecorder(size_t channels, | |
93 size_t sample_rate, | |
94 size_t bits_per_sample, | |
95 size_t samples) { | |
96 DeleteRecorder(); | |
97 params_.Reset(media::AudioParameters::AUDIO_PCM_LOW_LATENCY, | |
98 kDefaultChannelLayout, | |
99 channels, | |
100 2, | |
101 sample_rate, | |
102 bits_per_sample, | |
103 4096); | |
104 | |
105 channel_data_.clear(); | |
106 channel_data_.push_back(GenerateSamples(0x1337, samples)); | |
107 channel_data_.push_back(GenerateSamples(0x7331, samples)); | |
108 | |
109 total_samples_ = samples; | |
110 | |
111 recorder_ = new AudioRecorder( | |
112 base::Bind(&AudioRecorderTest::DecodeSamples, base::Unretained(this))); | |
113 recorder_->set_input_stream_for_testing( | |
114 new TestAudioInputStream(params_, channel_data_, samples)); | |
115 recorder_->set_params_for_testing(new media::AudioParameters(params_)); | |
116 recorder_->Initialize(); | |
117 } | |
118 | |
119 void DeleteRecorder() { | |
120 if (!recorder_) | |
121 return; | |
122 recorder_->Finalize(); | |
123 recorder_ = NULL; | |
124 } | |
125 | |
126 void RecordAndVerifySamples() { | |
127 received_samples_.clear(); | |
128 run_loop_.reset(new base::RunLoop()); | |
129 recorder_->Record(); | |
130 run_loop_->Run(); | |
131 } | |
132 | |
133 void DecodeSamples(const std::string& samples) { | |
134 received_samples_ += samples; | |
135 // We expect one less decode than our total samples would ideally have | |
136 // triggered since we process data in 4k chunks. So our sample processing | |
137 // will never rarely be perfectly aligned with 0.5s worth of samples, hence | |
138 // we will almost always run with a buffer of leftover samples that will | |
139 // not get sent to this callback since the recorder will be waiting for | |
140 // more data. | |
141 const size_t decode_buffer = params_.sample_rate() / 2; // 0.5s | |
142 const size_t expected_samples = | |
143 (total_samples_ / decode_buffer - 1) * decode_buffer; | |
144 const size_t expected_samples_size = | |
145 expected_samples * sizeof(float) * params_.channels(); | |
146 if (received_samples_.size() == expected_samples_size) { | |
Daniel Erat
2014/07/31 22:31:16
shouldn't you assert that this is true?
rkc
2014/08/01 21:08:57
This is an accumulator method and this is its term
| |
147 VerifySamples(); | |
148 run_loop_->Quit(); | |
149 } | |
150 } | |
151 | |
152 void VerifySamples() { | |
153 int differences = 0; | |
154 | |
155 float* buffer_view = | |
156 reinterpret_cast<float*>(string_as_array(&received_samples_)); | |
157 const int channels = params_.channels(); | |
158 const int frames = | |
159 received_samples_.size() / sizeof(float) / params_.channels(); | |
160 for (int ch = 0; ch < channels; ++ch) { | |
161 for (int si = 0, di = ch; si < frames; ++si, di += channels) | |
162 differences += (buffer_view[di] != channel_data_[ch][si]); | |
163 } | |
164 | |
165 ASSERT_EQ(0, differences); | |
166 } | |
167 | |
168 protected: | |
169 float* GenerateSamples(int random_seed, size_t size) { | |
170 float* samples = static_cast<float*>(base::AlignedAlloc( | |
171 size * sizeof(float), media::AudioBus::kChannelAlignment)); | |
172 PopulateSamples(0x1337, size, samples); | |
173 return samples; | |
174 } | |
175 bool IsRecording() { | |
176 recorder_->FlushAudioLoopForTesting(); | |
177 return recorder_->is_recording_; | |
178 } | |
179 | |
180 std::vector<float*> channel_data_; | |
181 media::AudioParameters params_; | |
182 size_t total_samples_; | |
183 | |
184 AudioRecorder* recorder_; | |
185 | |
186 std::string received_samples_; | |
187 | |
188 scoped_ptr<base::RunLoop> run_loop_; | |
189 content::TestBrowserThreadBundle thread_bundle_; | |
190 }; | |
191 | |
192 TEST_F(AudioRecorderTest, BasicRecordAndStop) { | |
193 CreateSimpleRecorder(); | |
194 | |
195 recorder_->Record(); | |
196 EXPECT_TRUE(IsRecording()); | |
197 recorder_->Stop(); | |
198 EXPECT_FALSE(IsRecording()); | |
199 recorder_->Record(); | |
200 | |
201 EXPECT_TRUE(IsRecording()); | |
202 recorder_->Stop(); | |
203 EXPECT_FALSE(IsRecording()); | |
204 recorder_->Record(); | |
205 | |
206 EXPECT_TRUE(IsRecording()); | |
207 recorder_->Stop(); | |
208 EXPECT_FALSE(IsRecording()); | |
209 | |
210 DeleteRecorder(); | |
211 } | |
212 | |
213 TEST_F(AudioRecorderTest, OutOfOrderRecordAndStopMultiple) { | |
214 CreateSimpleRecorder(); | |
215 | |
216 recorder_->Stop(); | |
217 recorder_->Stop(); | |
218 recorder_->Stop(); | |
219 EXPECT_FALSE(IsRecording()); | |
220 | |
221 recorder_->Record(); | |
222 recorder_->Record(); | |
223 EXPECT_TRUE(IsRecording()); | |
224 | |
225 recorder_->Stop(); | |
226 recorder_->Stop(); | |
227 EXPECT_FALSE(IsRecording()); | |
228 | |
229 DeleteRecorder(); | |
230 } | |
231 | |
232 TEST_F(AudioRecorderTest, RecordingEndToEnd) { | |
233 const int kNumSamples = 48000 * 3; | |
234 CreateRecorder( | |
235 kDefaultChannels, kDefaultSampleRate, kDefaultBitsPerSample, kNumSamples); | |
236 | |
237 RecordAndVerifySamples(); | |
238 | |
239 DeleteRecorder(); | |
240 } | |
241 | |
242 // TODO(rkc): Add tests with recording different sample rates. | |
243 | |
244 } // namespace copresence | |
OLD | NEW |