OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. | |
3 * | |
4 * Use of this source code is governed by a BSD-style license | |
5 * that can be found in the LICENSE file in the root of the source | |
6 * tree. An additional intellectual property rights grant can be found | |
7 * in the file PATENTS. All contributing project authors may | |
8 * be found in the AUTHORS file in the root of the source tree. | |
9 */ | |
10 | |
11 #include <math.h> | |
12 #include <stdio.h> | |
13 | |
14 #include <algorithm> | |
15 #include <limits> | |
16 #include <memory> | |
17 #include <queue> | |
18 | |
19 #include "webrtc/base/arraysize.h" | |
20 #include "webrtc/common_audio/include/audio_util.h" | |
21 #include "webrtc/common_audio/resampler/include/push_resampler.h" | |
22 #include "webrtc/common_audio/resampler/push_sinc_resampler.h" | |
23 #include "webrtc/common_audio/signal_processing/include/signal_processing_librar
y.h" | |
24 #include "webrtc/modules/audio_processing/beamformer/mock_nonlinear_beamformer.h
" | |
25 #include "webrtc/modules/audio_processing/common.h" | |
26 #include "webrtc/modules/audio_processing/include/audio_processing.h" | |
27 #include "webrtc/modules/audio_processing/test/protobuf_utils.h" | |
28 #include "webrtc/modules/audio_processing/test/test_utils.h" | |
29 #include "webrtc/modules/include/module_common_types.h" | |
30 #include "webrtc/system_wrappers/include/event_wrapper.h" | |
31 #include "webrtc/system_wrappers/include/trace.h" | |
32 #include "webrtc/test/testsupport/fileutils.h" | |
33 #ifdef WEBRTC_ANDROID_PLATFORM_BUILD | |
34 #include "gtest/gtest.h" | |
35 #include "external/webrtc/webrtc/modules/audio_processing/test/unittest.pb.h" | |
36 #else | |
37 #include "testing/gtest/include/gtest/gtest.h" | |
38 #include "webrtc/modules/audio_processing/unittest.pb.h" | |
39 #endif | |
40 | |
41 namespace webrtc { | |
42 namespace { | |
43 | |
44 // TODO(ekmeyerson): Switch to using StreamConfig and ProcessingConfig where | |
45 // applicable. | |
46 | |
47 // TODO(bjornv): This is not feasible until the functionality has been | |
48 // re-implemented; see comment at the bottom of this file. For now, the user has | |
49 // to hard code the |write_ref_data| value. | |
50 // When false, this will compare the output data with the results stored to | |
51 // file. This is the typical case. When the file should be updated, it can | |
52 // be set to true with the command-line switch --write_ref_data. | |
53 bool write_ref_data = false; | |
54 const google::protobuf::int32 kChannels[] = {1, 2}; | |
55 const int kSampleRates[] = {8000, 16000, 32000, 48000}; | |
56 | |
57 #if defined(WEBRTC_AUDIOPROC_FIXED_PROFILE) | |
58 // Android doesn't support 48kHz. | |
59 const int kProcessSampleRates[] = {8000, 16000, 32000}; | |
60 #elif defined(WEBRTC_AUDIOPROC_FLOAT_PROFILE) | |
61 const int kProcessSampleRates[] = {8000, 16000, 32000, 48000}; | |
62 #endif | |
63 | |
64 enum StreamDirection { kForward = 0, kReverse }; | |
65 | |
66 void ConvertToFloat(const int16_t* int_data, ChannelBuffer<float>* cb) { | |
67 ChannelBuffer<int16_t> cb_int(cb->num_frames(), | |
68 cb->num_channels()); | |
69 Deinterleave(int_data, | |
70 cb->num_frames(), | |
71 cb->num_channels(), | |
72 cb_int.channels()); | |
73 for (size_t i = 0; i < cb->num_channels(); ++i) { | |
74 S16ToFloat(cb_int.channels()[i], | |
75 cb->num_frames(), | |
76 cb->channels()[i]); | |
77 } | |
78 } | |
79 | |
80 void ConvertToFloat(const AudioFrame& frame, ChannelBuffer<float>* cb) { | |
81 ConvertToFloat(frame.data_, cb); | |
82 } | |
83 | |
84 // Number of channels including the keyboard channel. | |
85 size_t TotalChannelsFromLayout(AudioProcessing::ChannelLayout layout) { | |
86 switch (layout) { | |
87 case AudioProcessing::kMono: | |
88 return 1; | |
89 case AudioProcessing::kMonoAndKeyboard: | |
90 case AudioProcessing::kStereo: | |
91 return 2; | |
92 case AudioProcessing::kStereoAndKeyboard: | |
93 return 3; | |
94 } | |
95 assert(false); | |
96 return 0; | |
97 } | |
98 | |
99 int TruncateToMultipleOf10(int value) { | |
100 return (value / 10) * 10; | |
101 } | |
102 | |
103 void MixStereoToMono(const float* stereo, float* mono, | |
104 size_t samples_per_channel) { | |
105 for (size_t i = 0; i < samples_per_channel; ++i) | |
106 mono[i] = (stereo[i * 2] + stereo[i * 2 + 1]) / 2; | |
107 } | |
108 | |
109 void MixStereoToMono(const int16_t* stereo, int16_t* mono, | |
110 size_t samples_per_channel) { | |
111 for (size_t i = 0; i < samples_per_channel; ++i) | |
112 mono[i] = (stereo[i * 2] + stereo[i * 2 + 1]) >> 1; | |
113 } | |
114 | |
115 void CopyLeftToRightChannel(int16_t* stereo, size_t samples_per_channel) { | |
116 for (size_t i = 0; i < samples_per_channel; i++) { | |
117 stereo[i * 2 + 1] = stereo[i * 2]; | |
118 } | |
119 } | |
120 | |
121 void VerifyChannelsAreEqual(int16_t* stereo, size_t samples_per_channel) { | |
122 for (size_t i = 0; i < samples_per_channel; i++) { | |
123 EXPECT_EQ(stereo[i * 2 + 1], stereo[i * 2]); | |
124 } | |
125 } | |
126 | |
127 void SetFrameTo(AudioFrame* frame, int16_t value) { | |
128 for (size_t i = 0; i < frame->samples_per_channel_ * frame->num_channels_; | |
129 ++i) { | |
130 frame->data_[i] = value; | |
131 } | |
132 } | |
133 | |
134 void SetFrameTo(AudioFrame* frame, int16_t left, int16_t right) { | |
135 ASSERT_EQ(2u, frame->num_channels_); | |
136 for (size_t i = 0; i < frame->samples_per_channel_ * 2; i += 2) { | |
137 frame->data_[i] = left; | |
138 frame->data_[i + 1] = right; | |
139 } | |
140 } | |
141 | |
142 void ScaleFrame(AudioFrame* frame, float scale) { | |
143 for (size_t i = 0; i < frame->samples_per_channel_ * frame->num_channels_; | |
144 ++i) { | |
145 frame->data_[i] = FloatS16ToS16(frame->data_[i] * scale); | |
146 } | |
147 } | |
148 | |
149 bool FrameDataAreEqual(const AudioFrame& frame1, const AudioFrame& frame2) { | |
150 if (frame1.samples_per_channel_ != frame2.samples_per_channel_) { | |
151 return false; | |
152 } | |
153 if (frame1.num_channels_ != frame2.num_channels_) { | |
154 return false; | |
155 } | |
156 if (memcmp(frame1.data_, frame2.data_, | |
157 frame1.samples_per_channel_ * frame1.num_channels_ * | |
158 sizeof(int16_t))) { | |
159 return false; | |
160 } | |
161 return true; | |
162 } | |
163 | |
164 void EnableAllAPComponents(AudioProcessing* ap) { | |
165 #if defined(WEBRTC_AUDIOPROC_FIXED_PROFILE) | |
166 EXPECT_NOERR(ap->echo_control_mobile()->Enable(true)); | |
167 | |
168 EXPECT_NOERR(ap->gain_control()->set_mode(GainControl::kAdaptiveDigital)); | |
169 EXPECT_NOERR(ap->gain_control()->Enable(true)); | |
170 #elif defined(WEBRTC_AUDIOPROC_FLOAT_PROFILE) | |
171 EXPECT_NOERR(ap->echo_cancellation()->enable_drift_compensation(true)); | |
172 EXPECT_NOERR(ap->echo_cancellation()->enable_metrics(true)); | |
173 EXPECT_NOERR(ap->echo_cancellation()->enable_delay_logging(true)); | |
174 EXPECT_NOERR(ap->echo_cancellation()->Enable(true)); | |
175 | |
176 EXPECT_NOERR(ap->gain_control()->set_mode(GainControl::kAdaptiveAnalog)); | |
177 EXPECT_NOERR(ap->gain_control()->set_analog_level_limits(0, 255)); | |
178 EXPECT_NOERR(ap->gain_control()->Enable(true)); | |
179 #endif | |
180 | |
181 EXPECT_NOERR(ap->high_pass_filter()->Enable(true)); | |
182 EXPECT_NOERR(ap->level_estimator()->Enable(true)); | |
183 EXPECT_NOERR(ap->noise_suppression()->Enable(true)); | |
184 | |
185 EXPECT_NOERR(ap->voice_detection()->Enable(true)); | |
186 } | |
187 | |
188 // These functions are only used by ApmTest.Process. | |
189 template <class T> | |
190 T AbsValue(T a) { | |
191 return a > 0 ? a: -a; | |
192 } | |
193 | |
194 int16_t MaxAudioFrame(const AudioFrame& frame) { | |
195 const size_t length = frame.samples_per_channel_ * frame.num_channels_; | |
196 int16_t max_data = AbsValue(frame.data_[0]); | |
197 for (size_t i = 1; i < length; i++) { | |
198 max_data = std::max(max_data, AbsValue(frame.data_[i])); | |
199 } | |
200 | |
201 return max_data; | |
202 } | |
203 | |
204 #if defined(WEBRTC_AUDIOPROC_FLOAT_PROFILE) | |
205 void TestStats(const AudioProcessing::Statistic& test, | |
206 const audioproc::Test::Statistic& reference) { | |
207 EXPECT_NEAR(reference.instant(), test.instant, 2); | |
208 EXPECT_NEAR(reference.average(), test.average, 2); | |
209 EXPECT_NEAR(reference.maximum(), test.maximum, 3); | |
210 EXPECT_NEAR(reference.minimum(), test.minimum, 2); | |
211 } | |
212 | |
213 void WriteStatsMessage(const AudioProcessing::Statistic& output, | |
214 audioproc::Test::Statistic* msg) { | |
215 msg->set_instant(output.instant); | |
216 msg->set_average(output.average); | |
217 msg->set_maximum(output.maximum); | |
218 msg->set_minimum(output.minimum); | |
219 } | |
220 #endif | |
221 | |
222 void OpenFileAndWriteMessage(const std::string filename, | |
223 const ::google::protobuf::MessageLite& msg) { | |
224 #if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID) | |
225 FILE* file = fopen(filename.c_str(), "wb"); | |
226 ASSERT_TRUE(file != NULL); | |
227 | |
228 int32_t size = msg.ByteSize(); | |
229 ASSERT_GT(size, 0); | |
230 std::unique_ptr<uint8_t[]> array(new uint8_t[size]); | |
231 ASSERT_TRUE(msg.SerializeToArray(array.get(), size)); | |
232 | |
233 ASSERT_EQ(1u, fwrite(&size, sizeof(size), 1, file)); | |
234 ASSERT_EQ(static_cast<size_t>(size), | |
235 fwrite(array.get(), sizeof(array[0]), size, file)); | |
236 fclose(file); | |
237 #else | |
238 std::cout << "Warning: Writing new reference is only allowed on Linux!" | |
239 << std::endl; | |
240 #endif | |
241 } | |
242 | |
243 std::string ResourceFilePath(std::string name, int sample_rate_hz) { | |
244 std::ostringstream ss; | |
245 // Resource files are all stereo. | |
246 ss << name << sample_rate_hz / 1000 << "_stereo"; | |
247 return test::ResourcePath(ss.str(), "pcm"); | |
248 } | |
249 | |
250 // Temporary filenames unique to this process. Used to be able to run these | |
251 // tests in parallel as each process needs to be running in isolation they can't | |
252 // have competing filenames. | |
253 std::map<std::string, std::string> temp_filenames; | |
254 | |
255 std::string OutputFilePath(std::string name, | |
256 int input_rate, | |
257 int output_rate, | |
258 int reverse_input_rate, | |
259 int reverse_output_rate, | |
260 size_t num_input_channels, | |
261 size_t num_output_channels, | |
262 size_t num_reverse_input_channels, | |
263 size_t num_reverse_output_channels, | |
264 StreamDirection file_direction) { | |
265 std::ostringstream ss; | |
266 ss << name << "_i" << num_input_channels << "_" << input_rate / 1000 << "_ir" | |
267 << num_reverse_input_channels << "_" << reverse_input_rate / 1000 << "_"; | |
268 if (num_output_channels == 1) { | |
269 ss << "mono"; | |
270 } else if (num_output_channels == 2) { | |
271 ss << "stereo"; | |
272 } else { | |
273 assert(false); | |
274 } | |
275 ss << output_rate / 1000; | |
276 if (num_reverse_output_channels == 1) { | |
277 ss << "_rmono"; | |
278 } else if (num_reverse_output_channels == 2) { | |
279 ss << "_rstereo"; | |
280 } else { | |
281 assert(false); | |
282 } | |
283 ss << reverse_output_rate / 1000; | |
284 ss << "_d" << file_direction << "_pcm"; | |
285 | |
286 std::string filename = ss.str(); | |
287 if (temp_filenames[filename].empty()) | |
288 temp_filenames[filename] = test::TempFilename(test::OutputPath(), filename); | |
289 return temp_filenames[filename]; | |
290 } | |
291 | |
292 void ClearTempFiles() { | |
293 for (auto& kv : temp_filenames) | |
294 remove(kv.second.c_str()); | |
295 } | |
296 | |
297 void OpenFileAndReadMessage(const std::string filename, | |
298 ::google::protobuf::MessageLite* msg) { | |
299 FILE* file = fopen(filename.c_str(), "rb"); | |
300 ASSERT_TRUE(file != NULL); | |
301 ReadMessageFromFile(file, msg); | |
302 fclose(file); | |
303 } | |
304 | |
305 // Reads a 10 ms chunk of int16 interleaved audio from the given (assumed | |
306 // stereo) file, converts to deinterleaved float (optionally downmixing) and | |
307 // returns the result in |cb|. Returns false if the file ended (or on error) and | |
308 // true otherwise. | |
309 // | |
310 // |int_data| and |float_data| are just temporary space that must be | |
311 // sufficiently large to hold the 10 ms chunk. | |
312 bool ReadChunk(FILE* file, int16_t* int_data, float* float_data, | |
313 ChannelBuffer<float>* cb) { | |
314 // The files always contain stereo audio. | |
315 size_t frame_size = cb->num_frames() * 2; | |
316 size_t read_count = fread(int_data, sizeof(int16_t), frame_size, file); | |
317 if (read_count != frame_size) { | |
318 // Check that the file really ended. | |
319 assert(feof(file)); | |
320 return false; // This is expected. | |
321 } | |
322 | |
323 S16ToFloat(int_data, frame_size, float_data); | |
324 if (cb->num_channels() == 1) { | |
325 MixStereoToMono(float_data, cb->channels()[0], cb->num_frames()); | |
326 } else { | |
327 Deinterleave(float_data, cb->num_frames(), 2, | |
328 cb->channels()); | |
329 } | |
330 | |
331 return true; | |
332 } | |
333 | |
334 class ApmTest : public ::testing::Test { | |
335 protected: | |
336 ApmTest(); | |
337 virtual void SetUp(); | |
338 virtual void TearDown(); | |
339 | |
340 static void SetUpTestCase() { | |
341 Trace::CreateTrace(); | |
342 } | |
343 | |
344 static void TearDownTestCase() { | |
345 Trace::ReturnTrace(); | |
346 ClearTempFiles(); | |
347 } | |
348 | |
349 // Used to select between int and float interface tests. | |
350 enum Format { | |
351 kIntFormat, | |
352 kFloatFormat | |
353 }; | |
354 | |
355 void Init(int sample_rate_hz, | |
356 int output_sample_rate_hz, | |
357 int reverse_sample_rate_hz, | |
358 size_t num_input_channels, | |
359 size_t num_output_channels, | |
360 size_t num_reverse_channels, | |
361 bool open_output_file); | |
362 void Init(AudioProcessing* ap); | |
363 void EnableAllComponents(); | |
364 bool ReadFrame(FILE* file, AudioFrame* frame); | |
365 bool ReadFrame(FILE* file, AudioFrame* frame, ChannelBuffer<float>* cb); | |
366 void ReadFrameWithRewind(FILE* file, AudioFrame* frame); | |
367 void ReadFrameWithRewind(FILE* file, AudioFrame* frame, | |
368 ChannelBuffer<float>* cb); | |
369 void ProcessWithDefaultStreamParameters(AudioFrame* frame); | |
370 void ProcessDelayVerificationTest(int delay_ms, int system_delay_ms, | |
371 int delay_min, int delay_max); | |
372 void TestChangingChannelsInt16Interface( | |
373 size_t num_channels, | |
374 AudioProcessing::Error expected_return); | |
375 void TestChangingForwardChannels(size_t num_in_channels, | |
376 size_t num_out_channels, | |
377 AudioProcessing::Error expected_return); | |
378 void TestChangingReverseChannels(size_t num_rev_channels, | |
379 AudioProcessing::Error expected_return); | |
380 void RunQuantizedVolumeDoesNotGetStuckTest(int sample_rate); | |
381 void RunManualVolumeChangeIsPossibleTest(int sample_rate); | |
382 void StreamParametersTest(Format format); | |
383 int ProcessStreamChooser(Format format); | |
384 int AnalyzeReverseStreamChooser(Format format); | |
385 void ProcessDebugDump(const std::string& in_filename, | |
386 const std::string& out_filename, | |
387 Format format, | |
388 int max_size_bytes); | |
389 void VerifyDebugDumpTest(Format format); | |
390 | |
391 const std::string output_path_; | |
392 const std::string ref_path_; | |
393 const std::string ref_filename_; | |
394 std::unique_ptr<AudioProcessing> apm_; | |
395 AudioFrame* frame_; | |
396 AudioFrame* revframe_; | |
397 std::unique_ptr<ChannelBuffer<float> > float_cb_; | |
398 std::unique_ptr<ChannelBuffer<float> > revfloat_cb_; | |
399 int output_sample_rate_hz_; | |
400 size_t num_output_channels_; | |
401 FILE* far_file_; | |
402 FILE* near_file_; | |
403 FILE* out_file_; | |
404 }; | |
405 | |
406 ApmTest::ApmTest() | |
407 : output_path_(test::OutputPath()), | |
408 ref_path_(test::ProjectRootPath() + "data/audio_processing/"), | |
409 #if defined(WEBRTC_AUDIOPROC_FIXED_PROFILE) | |
410 ref_filename_(ref_path_ + "output_data_fixed.pb"), | |
411 #elif defined(WEBRTC_AUDIOPROC_FLOAT_PROFILE) | |
412 #if defined(WEBRTC_MAC) | |
413 // A different file for Mac is needed because on this platform the AEC | |
414 // constant |kFixedDelayMs| value is 20 and not 50 as it is on the rest. | |
415 ref_filename_(ref_path_ + "output_data_mac.pb"), | |
416 #else | |
417 ref_filename_(ref_path_ + "output_data_float.pb"), | |
418 #endif | |
419 #endif | |
420 frame_(NULL), | |
421 revframe_(NULL), | |
422 output_sample_rate_hz_(0), | |
423 num_output_channels_(0), | |
424 far_file_(NULL), | |
425 near_file_(NULL), | |
426 out_file_(NULL) { | |
427 Config config; | |
428 config.Set<ExperimentalAgc>(new ExperimentalAgc(false)); | |
429 apm_.reset(AudioProcessing::Create(config)); | |
430 } | |
431 | |
432 void ApmTest::SetUp() { | |
433 ASSERT_TRUE(apm_.get() != NULL); | |
434 | |
435 frame_ = new AudioFrame(); | |
436 revframe_ = new AudioFrame(); | |
437 | |
438 Init(32000, 32000, 32000, 2, 2, 2, false); | |
439 } | |
440 | |
441 void ApmTest::TearDown() { | |
442 if (frame_) { | |
443 delete frame_; | |
444 } | |
445 frame_ = NULL; | |
446 | |
447 if (revframe_) { | |
448 delete revframe_; | |
449 } | |
450 revframe_ = NULL; | |
451 | |
452 if (far_file_) { | |
453 ASSERT_EQ(0, fclose(far_file_)); | |
454 } | |
455 far_file_ = NULL; | |
456 | |
457 if (near_file_) { | |
458 ASSERT_EQ(0, fclose(near_file_)); | |
459 } | |
460 near_file_ = NULL; | |
461 | |
462 if (out_file_) { | |
463 ASSERT_EQ(0, fclose(out_file_)); | |
464 } | |
465 out_file_ = NULL; | |
466 } | |
467 | |
468 void ApmTest::Init(AudioProcessing* ap) { | |
469 ASSERT_EQ(kNoErr, | |
470 ap->Initialize( | |
471 {{{frame_->sample_rate_hz_, frame_->num_channels_}, | |
472 {output_sample_rate_hz_, num_output_channels_}, | |
473 {revframe_->sample_rate_hz_, revframe_->num_channels_}, | |
474 {revframe_->sample_rate_hz_, revframe_->num_channels_}}})); | |
475 } | |
476 | |
477 void ApmTest::Init(int sample_rate_hz, | |
478 int output_sample_rate_hz, | |
479 int reverse_sample_rate_hz, | |
480 size_t num_input_channels, | |
481 size_t num_output_channels, | |
482 size_t num_reverse_channels, | |
483 bool open_output_file) { | |
484 SetContainerFormat(sample_rate_hz, num_input_channels, frame_, &float_cb_); | |
485 output_sample_rate_hz_ = output_sample_rate_hz; | |
486 num_output_channels_ = num_output_channels; | |
487 | |
488 SetContainerFormat(reverse_sample_rate_hz, num_reverse_channels, revframe_, | |
489 &revfloat_cb_); | |
490 Init(apm_.get()); | |
491 | |
492 if (far_file_) { | |
493 ASSERT_EQ(0, fclose(far_file_)); | |
494 } | |
495 std::string filename = ResourceFilePath("far", sample_rate_hz); | |
496 far_file_ = fopen(filename.c_str(), "rb"); | |
497 ASSERT_TRUE(far_file_ != NULL) << "Could not open file " << | |
498 filename << "\n"; | |
499 | |
500 if (near_file_) { | |
501 ASSERT_EQ(0, fclose(near_file_)); | |
502 } | |
503 filename = ResourceFilePath("near", sample_rate_hz); | |
504 near_file_ = fopen(filename.c_str(), "rb"); | |
505 ASSERT_TRUE(near_file_ != NULL) << "Could not open file " << | |
506 filename << "\n"; | |
507 | |
508 if (open_output_file) { | |
509 if (out_file_) { | |
510 ASSERT_EQ(0, fclose(out_file_)); | |
511 } | |
512 filename = OutputFilePath( | |
513 "out", sample_rate_hz, output_sample_rate_hz, reverse_sample_rate_hz, | |
514 reverse_sample_rate_hz, num_input_channels, num_output_channels, | |
515 num_reverse_channels, num_reverse_channels, kForward); | |
516 out_file_ = fopen(filename.c_str(), "wb"); | |
517 ASSERT_TRUE(out_file_ != NULL) << "Could not open file " << | |
518 filename << "\n"; | |
519 } | |
520 } | |
521 | |
522 void ApmTest::EnableAllComponents() { | |
523 EnableAllAPComponents(apm_.get()); | |
524 } | |
525 | |
526 bool ApmTest::ReadFrame(FILE* file, AudioFrame* frame, | |
527 ChannelBuffer<float>* cb) { | |
528 // The files always contain stereo audio. | |
529 size_t frame_size = frame->samples_per_channel_ * 2; | |
530 size_t read_count = fread(frame->data_, | |
531 sizeof(int16_t), | |
532 frame_size, | |
533 file); | |
534 if (read_count != frame_size) { | |
535 // Check that the file really ended. | |
536 EXPECT_NE(0, feof(file)); | |
537 return false; // This is expected. | |
538 } | |
539 | |
540 if (frame->num_channels_ == 1) { | |
541 MixStereoToMono(frame->data_, frame->data_, | |
542 frame->samples_per_channel_); | |
543 } | |
544 | |
545 if (cb) { | |
546 ConvertToFloat(*frame, cb); | |
547 } | |
548 return true; | |
549 } | |
550 | |
551 bool ApmTest::ReadFrame(FILE* file, AudioFrame* frame) { | |
552 return ReadFrame(file, frame, NULL); | |
553 } | |
554 | |
555 // If the end of the file has been reached, rewind it and attempt to read the | |
556 // frame again. | |
557 void ApmTest::ReadFrameWithRewind(FILE* file, AudioFrame* frame, | |
558 ChannelBuffer<float>* cb) { | |
559 if (!ReadFrame(near_file_, frame_, cb)) { | |
560 rewind(near_file_); | |
561 ASSERT_TRUE(ReadFrame(near_file_, frame_, cb)); | |
562 } | |
563 } | |
564 | |
565 void ApmTest::ReadFrameWithRewind(FILE* file, AudioFrame* frame) { | |
566 ReadFrameWithRewind(file, frame, NULL); | |
567 } | |
568 | |
569 void ApmTest::ProcessWithDefaultStreamParameters(AudioFrame* frame) { | |
570 EXPECT_EQ(apm_->kNoError, apm_->set_stream_delay_ms(0)); | |
571 apm_->echo_cancellation()->set_stream_drift_samples(0); | |
572 EXPECT_EQ(apm_->kNoError, | |
573 apm_->gain_control()->set_stream_analog_level(127)); | |
574 EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame)); | |
575 } | |
576 | |
577 int ApmTest::ProcessStreamChooser(Format format) { | |
578 if (format == kIntFormat) { | |
579 return apm_->ProcessStream(frame_); | |
580 } | |
581 return apm_->ProcessStream(float_cb_->channels(), | |
582 frame_->samples_per_channel_, | |
583 frame_->sample_rate_hz_, | |
584 LayoutFromChannels(frame_->num_channels_), | |
585 output_sample_rate_hz_, | |
586 LayoutFromChannels(num_output_channels_), | |
587 float_cb_->channels()); | |
588 } | |
589 | |
590 int ApmTest::AnalyzeReverseStreamChooser(Format format) { | |
591 if (format == kIntFormat) { | |
592 return apm_->ProcessReverseStream(revframe_); | |
593 } | |
594 return apm_->AnalyzeReverseStream( | |
595 revfloat_cb_->channels(), | |
596 revframe_->samples_per_channel_, | |
597 revframe_->sample_rate_hz_, | |
598 LayoutFromChannels(revframe_->num_channels_)); | |
599 } | |
600 | |
601 void ApmTest::ProcessDelayVerificationTest(int delay_ms, int system_delay_ms, | |
602 int delay_min, int delay_max) { | |
603 // The |revframe_| and |frame_| should include the proper frame information, | |
604 // hence can be used for extracting information. | |
605 AudioFrame tmp_frame; | |
606 std::queue<AudioFrame*> frame_queue; | |
607 bool causal = true; | |
608 | |
609 tmp_frame.CopyFrom(*revframe_); | |
610 SetFrameTo(&tmp_frame, 0); | |
611 | |
612 EXPECT_EQ(apm_->kNoError, apm_->Initialize()); | |
613 // Initialize the |frame_queue| with empty frames. | |
614 int frame_delay = delay_ms / 10; | |
615 while (frame_delay < 0) { | |
616 AudioFrame* frame = new AudioFrame(); | |
617 frame->CopyFrom(tmp_frame); | |
618 frame_queue.push(frame); | |
619 frame_delay++; | |
620 causal = false; | |
621 } | |
622 while (frame_delay > 0) { | |
623 AudioFrame* frame = new AudioFrame(); | |
624 frame->CopyFrom(tmp_frame); | |
625 frame_queue.push(frame); | |
626 frame_delay--; | |
627 } | |
628 // Run for 4.5 seconds, skipping statistics from the first 2.5 seconds. We | |
629 // need enough frames with audio to have reliable estimates, but as few as | |
630 // possible to keep processing time down. 4.5 seconds seemed to be a good | |
631 // compromise for this recording. | |
632 for (int frame_count = 0; frame_count < 450; ++frame_count) { | |
633 AudioFrame* frame = new AudioFrame(); | |
634 frame->CopyFrom(tmp_frame); | |
635 // Use the near end recording, since that has more speech in it. | |
636 ASSERT_TRUE(ReadFrame(near_file_, frame)); | |
637 frame_queue.push(frame); | |
638 AudioFrame* reverse_frame = frame; | |
639 AudioFrame* process_frame = frame_queue.front(); | |
640 if (!causal) { | |
641 reverse_frame = frame_queue.front(); | |
642 // When we call ProcessStream() the frame is modified, so we can't use the | |
643 // pointer directly when things are non-causal. Use an intermediate frame | |
644 // and copy the data. | |
645 process_frame = &tmp_frame; | |
646 process_frame->CopyFrom(*frame); | |
647 } | |
648 EXPECT_EQ(apm_->kNoError, apm_->ProcessReverseStream(reverse_frame)); | |
649 EXPECT_EQ(apm_->kNoError, apm_->set_stream_delay_ms(system_delay_ms)); | |
650 EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(process_frame)); | |
651 frame = frame_queue.front(); | |
652 frame_queue.pop(); | |
653 delete frame; | |
654 | |
655 if (frame_count == 250) { | |
656 int median; | |
657 int std; | |
658 float poor_fraction; | |
659 // Discard the first delay metrics to avoid convergence effects. | |
660 EXPECT_EQ(apm_->kNoError, | |
661 apm_->echo_cancellation()->GetDelayMetrics(&median, &std, | |
662 &poor_fraction)); | |
663 } | |
664 } | |
665 | |
666 rewind(near_file_); | |
667 while (!frame_queue.empty()) { | |
668 AudioFrame* frame = frame_queue.front(); | |
669 frame_queue.pop(); | |
670 delete frame; | |
671 } | |
672 // Calculate expected delay estimate and acceptable regions. Further, | |
673 // limit them w.r.t. AEC delay estimation support. | |
674 const size_t samples_per_ms = | |
675 std::min(static_cast<size_t>(16), frame_->samples_per_channel_ / 10); | |
676 int expected_median = std::min(std::max(delay_ms - system_delay_ms, | |
677 delay_min), delay_max); | |
678 int expected_median_high = std::min( | |
679 std::max(expected_median + static_cast<int>(96 / samples_per_ms), | |
680 delay_min), | |
681 delay_max); | |
682 int expected_median_low = std::min( | |
683 std::max(expected_median - static_cast<int>(96 / samples_per_ms), | |
684 delay_min), | |
685 delay_max); | |
686 // Verify delay metrics. | |
687 int median; | |
688 int std; | |
689 float poor_fraction; | |
690 EXPECT_EQ(apm_->kNoError, | |
691 apm_->echo_cancellation()->GetDelayMetrics(&median, &std, | |
692 &poor_fraction)); | |
693 EXPECT_GE(expected_median_high, median); | |
694 EXPECT_LE(expected_median_low, median); | |
695 } | |
696 | |
697 void ApmTest::StreamParametersTest(Format format) { | |
698 // No errors when the components are disabled. | |
699 EXPECT_EQ(apm_->kNoError, ProcessStreamChooser(format)); | |
700 | |
701 // -- Missing AGC level -- | |
702 EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(true)); | |
703 EXPECT_EQ(apm_->kStreamParameterNotSetError, | |
704 ProcessStreamChooser(format)); | |
705 | |
706 // Resets after successful ProcessStream(). | |
707 EXPECT_EQ(apm_->kNoError, | |
708 apm_->gain_control()->set_stream_analog_level(127)); | |
709 EXPECT_EQ(apm_->kNoError, ProcessStreamChooser(format)); | |
710 EXPECT_EQ(apm_->kStreamParameterNotSetError, | |
711 ProcessStreamChooser(format)); | |
712 | |
713 // Other stream parameters set correctly. | |
714 EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->Enable(true)); | |
715 EXPECT_EQ(apm_->kNoError, | |
716 apm_->echo_cancellation()->enable_drift_compensation(true)); | |
717 EXPECT_EQ(apm_->kNoError, apm_->set_stream_delay_ms(100)); | |
718 apm_->echo_cancellation()->set_stream_drift_samples(0); | |
719 EXPECT_EQ(apm_->kStreamParameterNotSetError, | |
720 ProcessStreamChooser(format)); | |
721 EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(false)); | |
722 EXPECT_EQ(apm_->kNoError, | |
723 apm_->echo_cancellation()->enable_drift_compensation(false)); | |
724 | |
725 // -- Missing delay -- | |
726 EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->Enable(true)); | |
727 EXPECT_EQ(apm_->kNoError, ProcessStreamChooser(format)); | |
728 EXPECT_EQ(apm_->kStreamParameterNotSetError, | |
729 ProcessStreamChooser(format)); | |
730 | |
731 // Resets after successful ProcessStream(). | |
732 EXPECT_EQ(apm_->kNoError, apm_->set_stream_delay_ms(100)); | |
733 EXPECT_EQ(apm_->kNoError, ProcessStreamChooser(format)); | |
734 EXPECT_EQ(apm_->kStreamParameterNotSetError, | |
735 ProcessStreamChooser(format)); | |
736 | |
737 // Other stream parameters set correctly. | |
738 EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(true)); | |
739 EXPECT_EQ(apm_->kNoError, | |
740 apm_->echo_cancellation()->enable_drift_compensation(true)); | |
741 apm_->echo_cancellation()->set_stream_drift_samples(0); | |
742 EXPECT_EQ(apm_->kNoError, | |
743 apm_->gain_control()->set_stream_analog_level(127)); | |
744 EXPECT_EQ(apm_->kStreamParameterNotSetError, | |
745 ProcessStreamChooser(format)); | |
746 EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(false)); | |
747 | |
748 // -- Missing drift -- | |
749 EXPECT_EQ(apm_->kStreamParameterNotSetError, | |
750 ProcessStreamChooser(format)); | |
751 | |
752 // Resets after successful ProcessStream(). | |
753 EXPECT_EQ(apm_->kNoError, apm_->set_stream_delay_ms(100)); | |
754 apm_->echo_cancellation()->set_stream_drift_samples(0); | |
755 EXPECT_EQ(apm_->kNoError, ProcessStreamChooser(format)); | |
756 EXPECT_EQ(apm_->kStreamParameterNotSetError, | |
757 ProcessStreamChooser(format)); | |
758 | |
759 // Other stream parameters set correctly. | |
760 EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(true)); | |
761 EXPECT_EQ(apm_->kNoError, apm_->set_stream_delay_ms(100)); | |
762 EXPECT_EQ(apm_->kNoError, | |
763 apm_->gain_control()->set_stream_analog_level(127)); | |
764 EXPECT_EQ(apm_->kStreamParameterNotSetError, | |
765 ProcessStreamChooser(format)); | |
766 | |
767 // -- No stream parameters -- | |
768 EXPECT_EQ(apm_->kNoError, | |
769 AnalyzeReverseStreamChooser(format)); | |
770 EXPECT_EQ(apm_->kStreamParameterNotSetError, | |
771 ProcessStreamChooser(format)); | |
772 | |
773 // -- All there -- | |
774 EXPECT_EQ(apm_->kNoError, apm_->set_stream_delay_ms(100)); | |
775 apm_->echo_cancellation()->set_stream_drift_samples(0); | |
776 EXPECT_EQ(apm_->kNoError, | |
777 apm_->gain_control()->set_stream_analog_level(127)); | |
778 EXPECT_EQ(apm_->kNoError, ProcessStreamChooser(format)); | |
779 } | |
780 | |
781 TEST_F(ApmTest, StreamParametersInt) { | |
782 StreamParametersTest(kIntFormat); | |
783 } | |
784 | |
785 TEST_F(ApmTest, StreamParametersFloat) { | |
786 StreamParametersTest(kFloatFormat); | |
787 } | |
788 | |
789 TEST_F(ApmTest, DefaultDelayOffsetIsZero) { | |
790 EXPECT_EQ(0, apm_->delay_offset_ms()); | |
791 EXPECT_EQ(apm_->kNoError, apm_->set_stream_delay_ms(50)); | |
792 EXPECT_EQ(50, apm_->stream_delay_ms()); | |
793 } | |
794 | |
795 TEST_F(ApmTest, DelayOffsetWithLimitsIsSetProperly) { | |
796 // High limit of 500 ms. | |
797 apm_->set_delay_offset_ms(100); | |
798 EXPECT_EQ(100, apm_->delay_offset_ms()); | |
799 EXPECT_EQ(apm_->kBadStreamParameterWarning, apm_->set_stream_delay_ms(450)); | |
800 EXPECT_EQ(500, apm_->stream_delay_ms()); | |
801 EXPECT_EQ(apm_->kNoError, apm_->set_stream_delay_ms(100)); | |
802 EXPECT_EQ(200, apm_->stream_delay_ms()); | |
803 | |
804 // Low limit of 0 ms. | |
805 apm_->set_delay_offset_ms(-50); | |
806 EXPECT_EQ(-50, apm_->delay_offset_ms()); | |
807 EXPECT_EQ(apm_->kBadStreamParameterWarning, apm_->set_stream_delay_ms(20)); | |
808 EXPECT_EQ(0, apm_->stream_delay_ms()); | |
809 EXPECT_EQ(apm_->kNoError, apm_->set_stream_delay_ms(100)); | |
810 EXPECT_EQ(50, apm_->stream_delay_ms()); | |
811 } | |
812 | |
813 void ApmTest::TestChangingChannelsInt16Interface( | |
814 size_t num_channels, | |
815 AudioProcessing::Error expected_return) { | |
816 frame_->num_channels_ = num_channels; | |
817 EXPECT_EQ(expected_return, apm_->ProcessStream(frame_)); | |
818 EXPECT_EQ(expected_return, apm_->ProcessReverseStream(frame_)); | |
819 } | |
820 | |
821 void ApmTest::TestChangingForwardChannels( | |
822 size_t num_in_channels, | |
823 size_t num_out_channels, | |
824 AudioProcessing::Error expected_return) { | |
825 const StreamConfig input_stream = {frame_->sample_rate_hz_, num_in_channels}; | |
826 const StreamConfig output_stream = {output_sample_rate_hz_, num_out_channels}; | |
827 | |
828 EXPECT_EQ(expected_return, | |
829 apm_->ProcessStream(float_cb_->channels(), input_stream, | |
830 output_stream, float_cb_->channels())); | |
831 } | |
832 | |
833 void ApmTest::TestChangingReverseChannels( | |
834 size_t num_rev_channels, | |
835 AudioProcessing::Error expected_return) { | |
836 const ProcessingConfig processing_config = { | |
837 {{frame_->sample_rate_hz_, apm_->num_input_channels()}, | |
838 {output_sample_rate_hz_, apm_->num_output_channels()}, | |
839 {frame_->sample_rate_hz_, num_rev_channels}, | |
840 {frame_->sample_rate_hz_, num_rev_channels}}}; | |
841 | |
842 EXPECT_EQ( | |
843 expected_return, | |
844 apm_->ProcessReverseStream( | |
845 float_cb_->channels(), processing_config.reverse_input_stream(), | |
846 processing_config.reverse_output_stream(), float_cb_->channels())); | |
847 } | |
848 | |
849 TEST_F(ApmTest, ChannelsInt16Interface) { | |
850 // Testing number of invalid and valid channels. | |
851 Init(16000, 16000, 16000, 4, 4, 4, false); | |
852 | |
853 TestChangingChannelsInt16Interface(0, apm_->kBadNumberChannelsError); | |
854 | |
855 for (size_t i = 1; i < 4; i++) { | |
856 TestChangingChannelsInt16Interface(i, kNoErr); | |
857 EXPECT_EQ(i, apm_->num_input_channels()); | |
858 // We always force the number of reverse channels used for processing to 1. | |
859 EXPECT_EQ(1u, apm_->num_reverse_channels()); | |
860 } | |
861 } | |
862 | |
863 TEST_F(ApmTest, Channels) { | |
864 // Testing number of invalid and valid channels. | |
865 Init(16000, 16000, 16000, 4, 4, 4, false); | |
866 | |
867 TestChangingForwardChannels(0, 1, apm_->kBadNumberChannelsError); | |
868 TestChangingReverseChannels(0, apm_->kBadNumberChannelsError); | |
869 | |
870 for (size_t i = 1; i < 4; ++i) { | |
871 for (size_t j = 0; j < 1; ++j) { | |
872 // Output channels much be one or match input channels. | |
873 if (j == 1 || i == j) { | |
874 TestChangingForwardChannels(i, j, kNoErr); | |
875 TestChangingReverseChannels(i, kNoErr); | |
876 | |
877 EXPECT_EQ(i, apm_->num_input_channels()); | |
878 EXPECT_EQ(j, apm_->num_output_channels()); | |
879 // The number of reverse channels used for processing to is always 1. | |
880 EXPECT_EQ(1u, apm_->num_reverse_channels()); | |
881 } else { | |
882 TestChangingForwardChannels(i, j, | |
883 AudioProcessing::kBadNumberChannelsError); | |
884 } | |
885 } | |
886 } | |
887 } | |
888 | |
889 TEST_F(ApmTest, SampleRatesInt) { | |
890 // Testing invalid sample rates | |
891 SetContainerFormat(10000, 2, frame_, &float_cb_); | |
892 EXPECT_EQ(apm_->kBadSampleRateError, ProcessStreamChooser(kIntFormat)); | |
893 // Testing valid sample rates | |
894 int fs[] = {8000, 16000, 32000, 48000}; | |
895 for (size_t i = 0; i < arraysize(fs); i++) { | |
896 SetContainerFormat(fs[i], 2, frame_, &float_cb_); | |
897 EXPECT_NOERR(ProcessStreamChooser(kIntFormat)); | |
898 } | |
899 } | |
900 | |
901 TEST_F(ApmTest, EchoCancellation) { | |
902 EXPECT_EQ(apm_->kNoError, | |
903 apm_->echo_cancellation()->enable_drift_compensation(true)); | |
904 EXPECT_TRUE(apm_->echo_cancellation()->is_drift_compensation_enabled()); | |
905 EXPECT_EQ(apm_->kNoError, | |
906 apm_->echo_cancellation()->enable_drift_compensation(false)); | |
907 EXPECT_FALSE(apm_->echo_cancellation()->is_drift_compensation_enabled()); | |
908 | |
909 EchoCancellation::SuppressionLevel level[] = { | |
910 EchoCancellation::kLowSuppression, | |
911 EchoCancellation::kModerateSuppression, | |
912 EchoCancellation::kHighSuppression, | |
913 }; | |
914 for (size_t i = 0; i < arraysize(level); i++) { | |
915 EXPECT_EQ(apm_->kNoError, | |
916 apm_->echo_cancellation()->set_suppression_level(level[i])); | |
917 EXPECT_EQ(level[i], | |
918 apm_->echo_cancellation()->suppression_level()); | |
919 } | |
920 | |
921 EchoCancellation::Metrics metrics; | |
922 EXPECT_EQ(apm_->kNotEnabledError, | |
923 apm_->echo_cancellation()->GetMetrics(&metrics)); | |
924 | |
925 EXPECT_EQ(apm_->kNoError, | |
926 apm_->echo_cancellation()->enable_metrics(true)); | |
927 EXPECT_TRUE(apm_->echo_cancellation()->are_metrics_enabled()); | |
928 EXPECT_EQ(apm_->kNoError, | |
929 apm_->echo_cancellation()->enable_metrics(false)); | |
930 EXPECT_FALSE(apm_->echo_cancellation()->are_metrics_enabled()); | |
931 | |
932 int median = 0; | |
933 int std = 0; | |
934 float poor_fraction = 0; | |
935 EXPECT_EQ(apm_->kNotEnabledError, | |
936 apm_->echo_cancellation()->GetDelayMetrics(&median, &std, | |
937 &poor_fraction)); | |
938 | |
939 EXPECT_EQ(apm_->kNoError, | |
940 apm_->echo_cancellation()->enable_delay_logging(true)); | |
941 EXPECT_TRUE(apm_->echo_cancellation()->is_delay_logging_enabled()); | |
942 EXPECT_EQ(apm_->kNoError, | |
943 apm_->echo_cancellation()->enable_delay_logging(false)); | |
944 EXPECT_FALSE(apm_->echo_cancellation()->is_delay_logging_enabled()); | |
945 | |
946 EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->Enable(true)); | |
947 EXPECT_TRUE(apm_->echo_cancellation()->is_enabled()); | |
948 EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->Enable(false)); | |
949 EXPECT_FALSE(apm_->echo_cancellation()->is_enabled()); | |
950 | |
951 EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->Enable(true)); | |
952 EXPECT_TRUE(apm_->echo_cancellation()->is_enabled()); | |
953 EXPECT_TRUE(apm_->echo_cancellation()->aec_core() != NULL); | |
954 EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->Enable(false)); | |
955 EXPECT_FALSE(apm_->echo_cancellation()->is_enabled()); | |
956 EXPECT_FALSE(apm_->echo_cancellation()->aec_core() != NULL); | |
957 } | |
958 | |
959 TEST_F(ApmTest, DISABLED_EchoCancellationReportsCorrectDelays) { | |
960 // TODO(bjornv): Fix this test to work with DA-AEC. | |
961 // Enable AEC only. | |
962 EXPECT_EQ(apm_->kNoError, | |
963 apm_->echo_cancellation()->enable_drift_compensation(false)); | |
964 EXPECT_EQ(apm_->kNoError, | |
965 apm_->echo_cancellation()->enable_metrics(false)); | |
966 EXPECT_EQ(apm_->kNoError, | |
967 apm_->echo_cancellation()->enable_delay_logging(true)); | |
968 EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->Enable(true)); | |
969 Config config; | |
970 config.Set<DelayAgnostic>(new DelayAgnostic(false)); | |
971 apm_->SetExtraOptions(config); | |
972 | |
973 // Internally in the AEC the amount of lookahead the delay estimation can | |
974 // handle is 15 blocks and the maximum delay is set to 60 blocks. | |
975 const int kLookaheadBlocks = 15; | |
976 const int kMaxDelayBlocks = 60; | |
977 // The AEC has a startup time before it actually starts to process. This | |
978 // procedure can flush the internal far-end buffer, which of course affects | |
979 // the delay estimation. Therefore, we set a system_delay high enough to | |
980 // avoid that. The smallest system_delay you can report without flushing the | |
981 // buffer is 66 ms in 8 kHz. | |
982 // | |
983 // It is known that for 16 kHz (and 32 kHz) sampling frequency there is an | |
984 // additional stuffing of 8 ms on the fly, but it seems to have no impact on | |
985 // delay estimation. This should be noted though. In case of test failure, | |
986 // this could be the cause. | |
987 const int kSystemDelayMs = 66; | |
988 // Test a couple of corner cases and verify that the estimated delay is | |
989 // within a valid region (set to +-1.5 blocks). Note that these cases are | |
990 // sampling frequency dependent. | |
991 for (size_t i = 0; i < arraysize(kProcessSampleRates); i++) { | |
992 Init(kProcessSampleRates[i], | |
993 kProcessSampleRates[i], | |
994 kProcessSampleRates[i], | |
995 2, | |
996 2, | |
997 2, | |
998 false); | |
999 // Sampling frequency dependent variables. | |
1000 const int num_ms_per_block = | |
1001 std::max(4, static_cast<int>(640 / frame_->samples_per_channel_)); | |
1002 const int delay_min_ms = -kLookaheadBlocks * num_ms_per_block; | |
1003 const int delay_max_ms = (kMaxDelayBlocks - 1) * num_ms_per_block; | |
1004 | |
1005 // 1) Verify correct delay estimate at lookahead boundary. | |
1006 int delay_ms = TruncateToMultipleOf10(kSystemDelayMs + delay_min_ms); | |
1007 ProcessDelayVerificationTest(delay_ms, kSystemDelayMs, delay_min_ms, | |
1008 delay_max_ms); | |
1009 // 2) A delay less than maximum lookahead should give an delay estimate at | |
1010 // the boundary (= -kLookaheadBlocks * num_ms_per_block). | |
1011 delay_ms -= 20; | |
1012 ProcessDelayVerificationTest(delay_ms, kSystemDelayMs, delay_min_ms, | |
1013 delay_max_ms); | |
1014 // 3) Three values around zero delay. Note that we need to compensate for | |
1015 // the fake system_delay. | |
1016 delay_ms = TruncateToMultipleOf10(kSystemDelayMs - 10); | |
1017 ProcessDelayVerificationTest(delay_ms, kSystemDelayMs, delay_min_ms, | |
1018 delay_max_ms); | |
1019 delay_ms = TruncateToMultipleOf10(kSystemDelayMs); | |
1020 ProcessDelayVerificationTest(delay_ms, kSystemDelayMs, delay_min_ms, | |
1021 delay_max_ms); | |
1022 delay_ms = TruncateToMultipleOf10(kSystemDelayMs + 10); | |
1023 ProcessDelayVerificationTest(delay_ms, kSystemDelayMs, delay_min_ms, | |
1024 delay_max_ms); | |
1025 // 4) Verify correct delay estimate at maximum delay boundary. | |
1026 delay_ms = TruncateToMultipleOf10(kSystemDelayMs + delay_max_ms); | |
1027 ProcessDelayVerificationTest(delay_ms, kSystemDelayMs, delay_min_ms, | |
1028 delay_max_ms); | |
1029 // 5) A delay above the maximum delay should give an estimate at the | |
1030 // boundary (= (kMaxDelayBlocks - 1) * num_ms_per_block). | |
1031 delay_ms += 20; | |
1032 ProcessDelayVerificationTest(delay_ms, kSystemDelayMs, delay_min_ms, | |
1033 delay_max_ms); | |
1034 } | |
1035 } | |
1036 | |
1037 TEST_F(ApmTest, EchoControlMobile) { | |
1038 // Turn AECM on (and AEC off) | |
1039 Init(16000, 16000, 16000, 2, 2, 2, false); | |
1040 EXPECT_EQ(apm_->kNoError, apm_->echo_control_mobile()->Enable(true)); | |
1041 EXPECT_TRUE(apm_->echo_control_mobile()->is_enabled()); | |
1042 | |
1043 // Toggle routing modes | |
1044 EchoControlMobile::RoutingMode mode[] = { | |
1045 EchoControlMobile::kQuietEarpieceOrHeadset, | |
1046 EchoControlMobile::kEarpiece, | |
1047 EchoControlMobile::kLoudEarpiece, | |
1048 EchoControlMobile::kSpeakerphone, | |
1049 EchoControlMobile::kLoudSpeakerphone, | |
1050 }; | |
1051 for (size_t i = 0; i < arraysize(mode); i++) { | |
1052 EXPECT_EQ(apm_->kNoError, | |
1053 apm_->echo_control_mobile()->set_routing_mode(mode[i])); | |
1054 EXPECT_EQ(mode[i], | |
1055 apm_->echo_control_mobile()->routing_mode()); | |
1056 } | |
1057 // Turn comfort noise off/on | |
1058 EXPECT_EQ(apm_->kNoError, | |
1059 apm_->echo_control_mobile()->enable_comfort_noise(false)); | |
1060 EXPECT_FALSE(apm_->echo_control_mobile()->is_comfort_noise_enabled()); | |
1061 EXPECT_EQ(apm_->kNoError, | |
1062 apm_->echo_control_mobile()->enable_comfort_noise(true)); | |
1063 EXPECT_TRUE(apm_->echo_control_mobile()->is_comfort_noise_enabled()); | |
1064 // Set and get echo path | |
1065 const size_t echo_path_size = | |
1066 apm_->echo_control_mobile()->echo_path_size_bytes(); | |
1067 std::unique_ptr<char[]> echo_path_in(new char[echo_path_size]); | |
1068 std::unique_ptr<char[]> echo_path_out(new char[echo_path_size]); | |
1069 EXPECT_EQ(apm_->kNullPointerError, | |
1070 apm_->echo_control_mobile()->SetEchoPath(NULL, echo_path_size)); | |
1071 EXPECT_EQ(apm_->kNullPointerError, | |
1072 apm_->echo_control_mobile()->GetEchoPath(NULL, echo_path_size)); | |
1073 EXPECT_EQ(apm_->kBadParameterError, | |
1074 apm_->echo_control_mobile()->GetEchoPath(echo_path_out.get(), 1)); | |
1075 EXPECT_EQ(apm_->kNoError, | |
1076 apm_->echo_control_mobile()->GetEchoPath(echo_path_out.get(), | |
1077 echo_path_size)); | |
1078 for (size_t i = 0; i < echo_path_size; i++) { | |
1079 echo_path_in[i] = echo_path_out[i] + 1; | |
1080 } | |
1081 EXPECT_EQ(apm_->kBadParameterError, | |
1082 apm_->echo_control_mobile()->SetEchoPath(echo_path_in.get(), 1)); | |
1083 EXPECT_EQ(apm_->kNoError, | |
1084 apm_->echo_control_mobile()->SetEchoPath(echo_path_in.get(), | |
1085 echo_path_size)); | |
1086 EXPECT_EQ(apm_->kNoError, | |
1087 apm_->echo_control_mobile()->GetEchoPath(echo_path_out.get(), | |
1088 echo_path_size)); | |
1089 for (size_t i = 0; i < echo_path_size; i++) { | |
1090 EXPECT_EQ(echo_path_in[i], echo_path_out[i]); | |
1091 } | |
1092 | |
1093 // Process a few frames with NS in the default disabled state. This exercises | |
1094 // a different codepath than with it enabled. | |
1095 EXPECT_EQ(apm_->kNoError, apm_->set_stream_delay_ms(0)); | |
1096 EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); | |
1097 EXPECT_EQ(apm_->kNoError, apm_->set_stream_delay_ms(0)); | |
1098 EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); | |
1099 | |
1100 // Turn AECM off | |
1101 EXPECT_EQ(apm_->kNoError, apm_->echo_control_mobile()->Enable(false)); | |
1102 EXPECT_FALSE(apm_->echo_control_mobile()->is_enabled()); | |
1103 } | |
1104 | |
1105 TEST_F(ApmTest, GainControl) { | |
1106 // Testing gain modes | |
1107 EXPECT_EQ(apm_->kNoError, | |
1108 apm_->gain_control()->set_mode( | |
1109 apm_->gain_control()->mode())); | |
1110 | |
1111 GainControl::Mode mode[] = { | |
1112 GainControl::kAdaptiveAnalog, | |
1113 GainControl::kAdaptiveDigital, | |
1114 GainControl::kFixedDigital | |
1115 }; | |
1116 for (size_t i = 0; i < arraysize(mode); i++) { | |
1117 EXPECT_EQ(apm_->kNoError, | |
1118 apm_->gain_control()->set_mode(mode[i])); | |
1119 EXPECT_EQ(mode[i], apm_->gain_control()->mode()); | |
1120 } | |
1121 // Testing invalid target levels | |
1122 EXPECT_EQ(apm_->kBadParameterError, | |
1123 apm_->gain_control()->set_target_level_dbfs(-3)); | |
1124 EXPECT_EQ(apm_->kBadParameterError, | |
1125 apm_->gain_control()->set_target_level_dbfs(-40)); | |
1126 // Testing valid target levels | |
1127 EXPECT_EQ(apm_->kNoError, | |
1128 apm_->gain_control()->set_target_level_dbfs( | |
1129 apm_->gain_control()->target_level_dbfs())); | |
1130 | |
1131 int level_dbfs[] = {0, 6, 31}; | |
1132 for (size_t i = 0; i < arraysize(level_dbfs); i++) { | |
1133 EXPECT_EQ(apm_->kNoError, | |
1134 apm_->gain_control()->set_target_level_dbfs(level_dbfs[i])); | |
1135 EXPECT_EQ(level_dbfs[i], apm_->gain_control()->target_level_dbfs()); | |
1136 } | |
1137 | |
1138 // Testing invalid compression gains | |
1139 EXPECT_EQ(apm_->kBadParameterError, | |
1140 apm_->gain_control()->set_compression_gain_db(-1)); | |
1141 EXPECT_EQ(apm_->kBadParameterError, | |
1142 apm_->gain_control()->set_compression_gain_db(100)); | |
1143 | |
1144 // Testing valid compression gains | |
1145 EXPECT_EQ(apm_->kNoError, | |
1146 apm_->gain_control()->set_compression_gain_db( | |
1147 apm_->gain_control()->compression_gain_db())); | |
1148 | |
1149 int gain_db[] = {0, 10, 90}; | |
1150 for (size_t i = 0; i < arraysize(gain_db); i++) { | |
1151 EXPECT_EQ(apm_->kNoError, | |
1152 apm_->gain_control()->set_compression_gain_db(gain_db[i])); | |
1153 EXPECT_EQ(gain_db[i], apm_->gain_control()->compression_gain_db()); | |
1154 } | |
1155 | |
1156 // Testing limiter off/on | |
1157 EXPECT_EQ(apm_->kNoError, apm_->gain_control()->enable_limiter(false)); | |
1158 EXPECT_FALSE(apm_->gain_control()->is_limiter_enabled()); | |
1159 EXPECT_EQ(apm_->kNoError, apm_->gain_control()->enable_limiter(true)); | |
1160 EXPECT_TRUE(apm_->gain_control()->is_limiter_enabled()); | |
1161 | |
1162 // Testing invalid level limits | |
1163 EXPECT_EQ(apm_->kBadParameterError, | |
1164 apm_->gain_control()->set_analog_level_limits(-1, 512)); | |
1165 EXPECT_EQ(apm_->kBadParameterError, | |
1166 apm_->gain_control()->set_analog_level_limits(100000, 512)); | |
1167 EXPECT_EQ(apm_->kBadParameterError, | |
1168 apm_->gain_control()->set_analog_level_limits(512, -1)); | |
1169 EXPECT_EQ(apm_->kBadParameterError, | |
1170 apm_->gain_control()->set_analog_level_limits(512, 100000)); | |
1171 EXPECT_EQ(apm_->kBadParameterError, | |
1172 apm_->gain_control()->set_analog_level_limits(512, 255)); | |
1173 | |
1174 // Testing valid level limits | |
1175 EXPECT_EQ(apm_->kNoError, | |
1176 apm_->gain_control()->set_analog_level_limits( | |
1177 apm_->gain_control()->analog_level_minimum(), | |
1178 apm_->gain_control()->analog_level_maximum())); | |
1179 | |
1180 int min_level[] = {0, 255, 1024}; | |
1181 for (size_t i = 0; i < arraysize(min_level); i++) { | |
1182 EXPECT_EQ(apm_->kNoError, | |
1183 apm_->gain_control()->set_analog_level_limits(min_level[i], 1024)); | |
1184 EXPECT_EQ(min_level[i], apm_->gain_control()->analog_level_minimum()); | |
1185 } | |
1186 | |
1187 int max_level[] = {0, 1024, 65535}; | |
1188 for (size_t i = 0; i < arraysize(min_level); i++) { | |
1189 EXPECT_EQ(apm_->kNoError, | |
1190 apm_->gain_control()->set_analog_level_limits(0, max_level[i])); | |
1191 EXPECT_EQ(max_level[i], apm_->gain_control()->analog_level_maximum()); | |
1192 } | |
1193 | |
1194 // TODO(ajm): stream_is_saturated() and stream_analog_level() | |
1195 | |
1196 // Turn AGC off | |
1197 EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(false)); | |
1198 EXPECT_FALSE(apm_->gain_control()->is_enabled()); | |
1199 } | |
1200 | |
1201 void ApmTest::RunQuantizedVolumeDoesNotGetStuckTest(int sample_rate) { | |
1202 Init(sample_rate, sample_rate, sample_rate, 2, 2, 2, false); | |
1203 EXPECT_EQ(apm_->kNoError, | |
1204 apm_->gain_control()->set_mode(GainControl::kAdaptiveAnalog)); | |
1205 EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(true)); | |
1206 | |
1207 int out_analog_level = 0; | |
1208 for (int i = 0; i < 2000; ++i) { | |
1209 ReadFrameWithRewind(near_file_, frame_); | |
1210 // Ensure the audio is at a low level, so the AGC will try to increase it. | |
1211 ScaleFrame(frame_, 0.25); | |
1212 | |
1213 // Always pass in the same volume. | |
1214 EXPECT_EQ(apm_->kNoError, | |
1215 apm_->gain_control()->set_stream_analog_level(100)); | |
1216 EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); | |
1217 out_analog_level = apm_->gain_control()->stream_analog_level(); | |
1218 } | |
1219 | |
1220 // Ensure the AGC is still able to reach the maximum. | |
1221 EXPECT_EQ(255, out_analog_level); | |
1222 } | |
1223 | |
1224 // Verifies that despite volume slider quantization, the AGC can continue to | |
1225 // increase its volume. | |
1226 TEST_F(ApmTest, QuantizedVolumeDoesNotGetStuck) { | |
1227 for (size_t i = 0; i < arraysize(kSampleRates); ++i) { | |
1228 RunQuantizedVolumeDoesNotGetStuckTest(kSampleRates[i]); | |
1229 } | |
1230 } | |
1231 | |
1232 void ApmTest::RunManualVolumeChangeIsPossibleTest(int sample_rate) { | |
1233 Init(sample_rate, sample_rate, sample_rate, 2, 2, 2, false); | |
1234 EXPECT_EQ(apm_->kNoError, | |
1235 apm_->gain_control()->set_mode(GainControl::kAdaptiveAnalog)); | |
1236 EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(true)); | |
1237 | |
1238 int out_analog_level = 100; | |
1239 for (int i = 0; i < 1000; ++i) { | |
1240 ReadFrameWithRewind(near_file_, frame_); | |
1241 // Ensure the audio is at a low level, so the AGC will try to increase it. | |
1242 ScaleFrame(frame_, 0.25); | |
1243 | |
1244 EXPECT_EQ(apm_->kNoError, | |
1245 apm_->gain_control()->set_stream_analog_level(out_analog_level)); | |
1246 EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); | |
1247 out_analog_level = apm_->gain_control()->stream_analog_level(); | |
1248 } | |
1249 | |
1250 // Ensure the volume was raised. | |
1251 EXPECT_GT(out_analog_level, 100); | |
1252 int highest_level_reached = out_analog_level; | |
1253 // Simulate a user manual volume change. | |
1254 out_analog_level = 100; | |
1255 | |
1256 for (int i = 0; i < 300; ++i) { | |
1257 ReadFrameWithRewind(near_file_, frame_); | |
1258 ScaleFrame(frame_, 0.25); | |
1259 | |
1260 EXPECT_EQ(apm_->kNoError, | |
1261 apm_->gain_control()->set_stream_analog_level(out_analog_level)); | |
1262 EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); | |
1263 out_analog_level = apm_->gain_control()->stream_analog_level(); | |
1264 // Check that AGC respected the manually adjusted volume. | |
1265 EXPECT_LT(out_analog_level, highest_level_reached); | |
1266 } | |
1267 // Check that the volume was still raised. | |
1268 EXPECT_GT(out_analog_level, 100); | |
1269 } | |
1270 | |
1271 TEST_F(ApmTest, ManualVolumeChangeIsPossible) { | |
1272 for (size_t i = 0; i < arraysize(kSampleRates); ++i) { | |
1273 RunManualVolumeChangeIsPossibleTest(kSampleRates[i]); | |
1274 } | |
1275 } | |
1276 | |
1277 #if !defined(WEBRTC_ANDROID) && !defined(WEBRTC_IOS) | |
1278 TEST_F(ApmTest, AgcOnlyAdaptsWhenTargetSignalIsPresent) { | |
1279 const int kSampleRateHz = 16000; | |
1280 const size_t kSamplesPerChannel = | |
1281 static_cast<size_t>(AudioProcessing::kChunkSizeMs * kSampleRateHz / 1000); | |
1282 const size_t kNumInputChannels = 2; | |
1283 const size_t kNumOutputChannels = 1; | |
1284 const size_t kNumChunks = 700; | |
1285 const float kScaleFactor = 0.25f; | |
1286 Config config; | |
1287 std::vector<webrtc::Point> geometry; | |
1288 geometry.push_back(webrtc::Point(0.f, 0.f, 0.f)); | |
1289 geometry.push_back(webrtc::Point(0.05f, 0.f, 0.f)); | |
1290 config.Set<Beamforming>(new Beamforming(true, geometry)); | |
1291 testing::NiceMock<MockNonlinearBeamformer>* beamformer = | |
1292 new testing::NiceMock<MockNonlinearBeamformer>(geometry); | |
1293 std::unique_ptr<AudioProcessing> apm( | |
1294 AudioProcessing::Create(config, beamformer)); | |
1295 EXPECT_EQ(kNoErr, apm->gain_control()->Enable(true)); | |
1296 ChannelBuffer<float> src_buf(kSamplesPerChannel, kNumInputChannels); | |
1297 ChannelBuffer<float> dest_buf(kSamplesPerChannel, kNumOutputChannels); | |
1298 const size_t max_length = kSamplesPerChannel * std::max(kNumInputChannels, | |
1299 kNumOutputChannels); | |
1300 std::unique_ptr<int16_t[]> int_data(new int16_t[max_length]); | |
1301 std::unique_ptr<float[]> float_data(new float[max_length]); | |
1302 std::string filename = ResourceFilePath("far", kSampleRateHz); | |
1303 FILE* far_file = fopen(filename.c_str(), "rb"); | |
1304 ASSERT_TRUE(far_file != NULL) << "Could not open file " << filename << "\n"; | |
1305 const int kDefaultVolume = apm->gain_control()->stream_analog_level(); | |
1306 const int kDefaultCompressionGain = | |
1307 apm->gain_control()->compression_gain_db(); | |
1308 bool is_target = false; | |
1309 EXPECT_CALL(*beamformer, is_target_present()) | |
1310 .WillRepeatedly(testing::ReturnPointee(&is_target)); | |
1311 for (size_t i = 0; i < kNumChunks; ++i) { | |
1312 ASSERT_TRUE(ReadChunk(far_file, | |
1313 int_data.get(), | |
1314 float_data.get(), | |
1315 &src_buf)); | |
1316 for (size_t j = 0; j < kNumInputChannels; ++j) { | |
1317 for (size_t k = 0; k < kSamplesPerChannel; ++k) { | |
1318 src_buf.channels()[j][k] *= kScaleFactor; | |
1319 } | |
1320 } | |
1321 EXPECT_EQ(kNoErr, | |
1322 apm->ProcessStream(src_buf.channels(), | |
1323 src_buf.num_frames(), | |
1324 kSampleRateHz, | |
1325 LayoutFromChannels(src_buf.num_channels()), | |
1326 kSampleRateHz, | |
1327 LayoutFromChannels(dest_buf.num_channels()), | |
1328 dest_buf.channels())); | |
1329 } | |
1330 EXPECT_EQ(kDefaultVolume, | |
1331 apm->gain_control()->stream_analog_level()); | |
1332 EXPECT_EQ(kDefaultCompressionGain, | |
1333 apm->gain_control()->compression_gain_db()); | |
1334 rewind(far_file); | |
1335 is_target = true; | |
1336 for (size_t i = 0; i < kNumChunks; ++i) { | |
1337 ASSERT_TRUE(ReadChunk(far_file, | |
1338 int_data.get(), | |
1339 float_data.get(), | |
1340 &src_buf)); | |
1341 for (size_t j = 0; j < kNumInputChannels; ++j) { | |
1342 for (size_t k = 0; k < kSamplesPerChannel; ++k) { | |
1343 src_buf.channels()[j][k] *= kScaleFactor; | |
1344 } | |
1345 } | |
1346 EXPECT_EQ(kNoErr, | |
1347 apm->ProcessStream(src_buf.channels(), | |
1348 src_buf.num_frames(), | |
1349 kSampleRateHz, | |
1350 LayoutFromChannels(src_buf.num_channels()), | |
1351 kSampleRateHz, | |
1352 LayoutFromChannels(dest_buf.num_channels()), | |
1353 dest_buf.channels())); | |
1354 } | |
1355 EXPECT_LT(kDefaultVolume, | |
1356 apm->gain_control()->stream_analog_level()); | |
1357 EXPECT_LT(kDefaultCompressionGain, | |
1358 apm->gain_control()->compression_gain_db()); | |
1359 ASSERT_EQ(0, fclose(far_file)); | |
1360 } | |
1361 #endif | |
1362 | |
1363 TEST_F(ApmTest, NoiseSuppression) { | |
1364 // Test valid suppression levels. | |
1365 NoiseSuppression::Level level[] = { | |
1366 NoiseSuppression::kLow, | |
1367 NoiseSuppression::kModerate, | |
1368 NoiseSuppression::kHigh, | |
1369 NoiseSuppression::kVeryHigh | |
1370 }; | |
1371 for (size_t i = 0; i < arraysize(level); i++) { | |
1372 EXPECT_EQ(apm_->kNoError, | |
1373 apm_->noise_suppression()->set_level(level[i])); | |
1374 EXPECT_EQ(level[i], apm_->noise_suppression()->level()); | |
1375 } | |
1376 | |
1377 // Turn NS on/off | |
1378 EXPECT_EQ(apm_->kNoError, apm_->noise_suppression()->Enable(true)); | |
1379 EXPECT_TRUE(apm_->noise_suppression()->is_enabled()); | |
1380 EXPECT_EQ(apm_->kNoError, apm_->noise_suppression()->Enable(false)); | |
1381 EXPECT_FALSE(apm_->noise_suppression()->is_enabled()); | |
1382 } | |
1383 | |
1384 TEST_F(ApmTest, HighPassFilter) { | |
1385 // Turn HP filter on/off | |
1386 EXPECT_EQ(apm_->kNoError, apm_->high_pass_filter()->Enable(true)); | |
1387 EXPECT_TRUE(apm_->high_pass_filter()->is_enabled()); | |
1388 EXPECT_EQ(apm_->kNoError, apm_->high_pass_filter()->Enable(false)); | |
1389 EXPECT_FALSE(apm_->high_pass_filter()->is_enabled()); | |
1390 } | |
1391 | |
1392 TEST_F(ApmTest, LevelEstimator) { | |
1393 // Turn level estimator on/off | |
1394 EXPECT_EQ(apm_->kNoError, apm_->level_estimator()->Enable(false)); | |
1395 EXPECT_FALSE(apm_->level_estimator()->is_enabled()); | |
1396 | |
1397 EXPECT_EQ(apm_->kNotEnabledError, apm_->level_estimator()->RMS()); | |
1398 | |
1399 EXPECT_EQ(apm_->kNoError, apm_->level_estimator()->Enable(true)); | |
1400 EXPECT_TRUE(apm_->level_estimator()->is_enabled()); | |
1401 | |
1402 // Run this test in wideband; in super-wb, the splitting filter distorts the | |
1403 // audio enough to cause deviation from the expectation for small values. | |
1404 frame_->samples_per_channel_ = 160; | |
1405 frame_->num_channels_ = 2; | |
1406 frame_->sample_rate_hz_ = 16000; | |
1407 | |
1408 // Min value if no frames have been processed. | |
1409 EXPECT_EQ(127, apm_->level_estimator()->RMS()); | |
1410 | |
1411 // Min value on zero frames. | |
1412 SetFrameTo(frame_, 0); | |
1413 EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); | |
1414 EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); | |
1415 EXPECT_EQ(127, apm_->level_estimator()->RMS()); | |
1416 | |
1417 // Try a few RMS values. | |
1418 // (These also test that the value resets after retrieving it.) | |
1419 SetFrameTo(frame_, 32767); | |
1420 EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); | |
1421 EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); | |
1422 EXPECT_EQ(0, apm_->level_estimator()->RMS()); | |
1423 | |
1424 SetFrameTo(frame_, 30000); | |
1425 EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); | |
1426 EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); | |
1427 EXPECT_EQ(1, apm_->level_estimator()->RMS()); | |
1428 | |
1429 SetFrameTo(frame_, 10000); | |
1430 EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); | |
1431 EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); | |
1432 EXPECT_EQ(10, apm_->level_estimator()->RMS()); | |
1433 | |
1434 SetFrameTo(frame_, 10); | |
1435 EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); | |
1436 EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); | |
1437 EXPECT_EQ(70, apm_->level_estimator()->RMS()); | |
1438 | |
1439 // Verify reset after enable/disable. | |
1440 SetFrameTo(frame_, 32767); | |
1441 EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); | |
1442 EXPECT_EQ(apm_->kNoError, apm_->level_estimator()->Enable(false)); | |
1443 EXPECT_EQ(apm_->kNoError, apm_->level_estimator()->Enable(true)); | |
1444 SetFrameTo(frame_, 1); | |
1445 EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); | |
1446 EXPECT_EQ(90, apm_->level_estimator()->RMS()); | |
1447 | |
1448 // Verify reset after initialize. | |
1449 SetFrameTo(frame_, 32767); | |
1450 EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); | |
1451 EXPECT_EQ(apm_->kNoError, apm_->Initialize()); | |
1452 SetFrameTo(frame_, 1); | |
1453 EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); | |
1454 EXPECT_EQ(90, apm_->level_estimator()->RMS()); | |
1455 } | |
1456 | |
1457 TEST_F(ApmTest, VoiceDetection) { | |
1458 // Test external VAD | |
1459 EXPECT_EQ(apm_->kNoError, | |
1460 apm_->voice_detection()->set_stream_has_voice(true)); | |
1461 EXPECT_TRUE(apm_->voice_detection()->stream_has_voice()); | |
1462 EXPECT_EQ(apm_->kNoError, | |
1463 apm_->voice_detection()->set_stream_has_voice(false)); | |
1464 EXPECT_FALSE(apm_->voice_detection()->stream_has_voice()); | |
1465 | |
1466 // Test valid likelihoods | |
1467 VoiceDetection::Likelihood likelihood[] = { | |
1468 VoiceDetection::kVeryLowLikelihood, | |
1469 VoiceDetection::kLowLikelihood, | |
1470 VoiceDetection::kModerateLikelihood, | |
1471 VoiceDetection::kHighLikelihood | |
1472 }; | |
1473 for (size_t i = 0; i < arraysize(likelihood); i++) { | |
1474 EXPECT_EQ(apm_->kNoError, | |
1475 apm_->voice_detection()->set_likelihood(likelihood[i])); | |
1476 EXPECT_EQ(likelihood[i], apm_->voice_detection()->likelihood()); | |
1477 } | |
1478 | |
1479 /* TODO(bjornv): Enable once VAD supports other frame lengths than 10 ms | |
1480 // Test invalid frame sizes | |
1481 EXPECT_EQ(apm_->kBadParameterError, | |
1482 apm_->voice_detection()->set_frame_size_ms(12)); | |
1483 | |
1484 // Test valid frame sizes | |
1485 for (int i = 10; i <= 30; i += 10) { | |
1486 EXPECT_EQ(apm_->kNoError, | |
1487 apm_->voice_detection()->set_frame_size_ms(i)); | |
1488 EXPECT_EQ(i, apm_->voice_detection()->frame_size_ms()); | |
1489 } | |
1490 */ | |
1491 | |
1492 // Turn VAD on/off | |
1493 EXPECT_EQ(apm_->kNoError, apm_->voice_detection()->Enable(true)); | |
1494 EXPECT_TRUE(apm_->voice_detection()->is_enabled()); | |
1495 EXPECT_EQ(apm_->kNoError, apm_->voice_detection()->Enable(false)); | |
1496 EXPECT_FALSE(apm_->voice_detection()->is_enabled()); | |
1497 | |
1498 // Test that AudioFrame activity is maintained when VAD is disabled. | |
1499 EXPECT_EQ(apm_->kNoError, apm_->voice_detection()->Enable(false)); | |
1500 AudioFrame::VADActivity activity[] = { | |
1501 AudioFrame::kVadActive, | |
1502 AudioFrame::kVadPassive, | |
1503 AudioFrame::kVadUnknown | |
1504 }; | |
1505 for (size_t i = 0; i < arraysize(activity); i++) { | |
1506 frame_->vad_activity_ = activity[i]; | |
1507 EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); | |
1508 EXPECT_EQ(activity[i], frame_->vad_activity_); | |
1509 } | |
1510 | |
1511 // Test that AudioFrame activity is set when VAD is enabled. | |
1512 EXPECT_EQ(apm_->kNoError, apm_->voice_detection()->Enable(true)); | |
1513 frame_->vad_activity_ = AudioFrame::kVadUnknown; | |
1514 EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); | |
1515 EXPECT_NE(AudioFrame::kVadUnknown, frame_->vad_activity_); | |
1516 | |
1517 // TODO(bjornv): Add tests for streamed voice; stream_has_voice() | |
1518 } | |
1519 | |
1520 TEST_F(ApmTest, AllProcessingDisabledByDefault) { | |
1521 EXPECT_FALSE(apm_->echo_cancellation()->is_enabled()); | |
1522 EXPECT_FALSE(apm_->echo_control_mobile()->is_enabled()); | |
1523 EXPECT_FALSE(apm_->gain_control()->is_enabled()); | |
1524 EXPECT_FALSE(apm_->high_pass_filter()->is_enabled()); | |
1525 EXPECT_FALSE(apm_->level_estimator()->is_enabled()); | |
1526 EXPECT_FALSE(apm_->noise_suppression()->is_enabled()); | |
1527 EXPECT_FALSE(apm_->voice_detection()->is_enabled()); | |
1528 } | |
1529 | |
1530 TEST_F(ApmTest, NoProcessingWhenAllComponentsDisabled) { | |
1531 for (size_t i = 0; i < arraysize(kSampleRates); i++) { | |
1532 Init(kSampleRates[i], kSampleRates[i], kSampleRates[i], 2, 2, 2, false); | |
1533 SetFrameTo(frame_, 1000, 2000); | |
1534 AudioFrame frame_copy; | |
1535 frame_copy.CopyFrom(*frame_); | |
1536 for (int j = 0; j < 1000; j++) { | |
1537 EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); | |
1538 EXPECT_TRUE(FrameDataAreEqual(*frame_, frame_copy)); | |
1539 EXPECT_EQ(apm_->kNoError, apm_->ProcessReverseStream(frame_)); | |
1540 EXPECT_TRUE(FrameDataAreEqual(*frame_, frame_copy)); | |
1541 } | |
1542 } | |
1543 } | |
1544 | |
1545 TEST_F(ApmTest, NoProcessingWhenAllComponentsDisabledFloat) { | |
1546 // Test that ProcessStream copies input to output even with no processing. | |
1547 const size_t kSamples = 80; | |
1548 const int sample_rate = 8000; | |
1549 const float src[kSamples] = { | |
1550 -1.0f, 0.0f, 1.0f | |
1551 }; | |
1552 float dest[kSamples] = {}; | |
1553 | |
1554 auto src_channels = &src[0]; | |
1555 auto dest_channels = &dest[0]; | |
1556 | |
1557 apm_.reset(AudioProcessing::Create()); | |
1558 EXPECT_NOERR(apm_->ProcessStream( | |
1559 &src_channels, kSamples, sample_rate, LayoutFromChannels(1), | |
1560 sample_rate, LayoutFromChannels(1), &dest_channels)); | |
1561 | |
1562 for (size_t i = 0; i < kSamples; ++i) { | |
1563 EXPECT_EQ(src[i], dest[i]); | |
1564 } | |
1565 | |
1566 // Same for ProcessReverseStream. | |
1567 float rev_dest[kSamples] = {}; | |
1568 auto rev_dest_channels = &rev_dest[0]; | |
1569 | |
1570 StreamConfig input_stream = {sample_rate, 1}; | |
1571 StreamConfig output_stream = {sample_rate, 1}; | |
1572 EXPECT_NOERR(apm_->ProcessReverseStream(&src_channels, input_stream, | |
1573 output_stream, &rev_dest_channels)); | |
1574 | |
1575 for (size_t i = 0; i < kSamples; ++i) { | |
1576 EXPECT_EQ(src[i], rev_dest[i]); | |
1577 } | |
1578 } | |
1579 | |
1580 TEST_F(ApmTest, IdenticalInputChannelsResultInIdenticalOutputChannels) { | |
1581 EnableAllComponents(); | |
1582 | |
1583 for (size_t i = 0; i < arraysize(kProcessSampleRates); i++) { | |
1584 Init(kProcessSampleRates[i], | |
1585 kProcessSampleRates[i], | |
1586 kProcessSampleRates[i], | |
1587 2, | |
1588 2, | |
1589 2, | |
1590 false); | |
1591 int analog_level = 127; | |
1592 ASSERT_EQ(0, feof(far_file_)); | |
1593 ASSERT_EQ(0, feof(near_file_)); | |
1594 while (ReadFrame(far_file_, revframe_) && ReadFrame(near_file_, frame_)) { | |
1595 CopyLeftToRightChannel(revframe_->data_, revframe_->samples_per_channel_); | |
1596 | |
1597 ASSERT_EQ(kNoErr, apm_->ProcessReverseStream(revframe_)); | |
1598 | |
1599 CopyLeftToRightChannel(frame_->data_, frame_->samples_per_channel_); | |
1600 frame_->vad_activity_ = AudioFrame::kVadUnknown; | |
1601 | |
1602 ASSERT_EQ(kNoErr, apm_->set_stream_delay_ms(0)); | |
1603 apm_->echo_cancellation()->set_stream_drift_samples(0); | |
1604 ASSERT_EQ(kNoErr, | |
1605 apm_->gain_control()->set_stream_analog_level(analog_level)); | |
1606 ASSERT_EQ(kNoErr, apm_->ProcessStream(frame_)); | |
1607 analog_level = apm_->gain_control()->stream_analog_level(); | |
1608 | |
1609 VerifyChannelsAreEqual(frame_->data_, frame_->samples_per_channel_); | |
1610 } | |
1611 rewind(far_file_); | |
1612 rewind(near_file_); | |
1613 } | |
1614 } | |
1615 | |
1616 TEST_F(ApmTest, SplittingFilter) { | |
1617 // Verify the filter is not active through undistorted audio when: | |
1618 // 1. No components are enabled... | |
1619 SetFrameTo(frame_, 1000); | |
1620 AudioFrame frame_copy; | |
1621 frame_copy.CopyFrom(*frame_); | |
1622 EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); | |
1623 EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); | |
1624 EXPECT_TRUE(FrameDataAreEqual(*frame_, frame_copy)); | |
1625 | |
1626 // 2. Only the level estimator is enabled... | |
1627 SetFrameTo(frame_, 1000); | |
1628 frame_copy.CopyFrom(*frame_); | |
1629 EXPECT_EQ(apm_->kNoError, apm_->level_estimator()->Enable(true)); | |
1630 EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); | |
1631 EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); | |
1632 EXPECT_TRUE(FrameDataAreEqual(*frame_, frame_copy)); | |
1633 EXPECT_EQ(apm_->kNoError, apm_->level_estimator()->Enable(false)); | |
1634 | |
1635 // 3. Only VAD is enabled... | |
1636 SetFrameTo(frame_, 1000); | |
1637 frame_copy.CopyFrom(*frame_); | |
1638 EXPECT_EQ(apm_->kNoError, apm_->voice_detection()->Enable(true)); | |
1639 EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); | |
1640 EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); | |
1641 EXPECT_TRUE(FrameDataAreEqual(*frame_, frame_copy)); | |
1642 EXPECT_EQ(apm_->kNoError, apm_->voice_detection()->Enable(false)); | |
1643 | |
1644 // 4. Both VAD and the level estimator are enabled... | |
1645 SetFrameTo(frame_, 1000); | |
1646 frame_copy.CopyFrom(*frame_); | |
1647 EXPECT_EQ(apm_->kNoError, apm_->level_estimator()->Enable(true)); | |
1648 EXPECT_EQ(apm_->kNoError, apm_->voice_detection()->Enable(true)); | |
1649 EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); | |
1650 EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); | |
1651 EXPECT_TRUE(FrameDataAreEqual(*frame_, frame_copy)); | |
1652 EXPECT_EQ(apm_->kNoError, apm_->level_estimator()->Enable(false)); | |
1653 EXPECT_EQ(apm_->kNoError, apm_->voice_detection()->Enable(false)); | |
1654 | |
1655 // 5. Not using super-wb. | |
1656 frame_->samples_per_channel_ = 160; | |
1657 frame_->num_channels_ = 2; | |
1658 frame_->sample_rate_hz_ = 16000; | |
1659 // Enable AEC, which would require the filter in super-wb. We rely on the | |
1660 // first few frames of data being unaffected by the AEC. | |
1661 // TODO(andrew): This test, and the one below, rely rather tenuously on the | |
1662 // behavior of the AEC. Think of something more robust. | |
1663 EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->Enable(true)); | |
1664 // Make sure we have extended filter enabled. This makes sure nothing is | |
1665 // touched until we have a farend frame. | |
1666 Config config; | |
1667 config.Set<ExtendedFilter>(new ExtendedFilter(true)); | |
1668 apm_->SetExtraOptions(config); | |
1669 SetFrameTo(frame_, 1000); | |
1670 frame_copy.CopyFrom(*frame_); | |
1671 EXPECT_EQ(apm_->kNoError, apm_->set_stream_delay_ms(0)); | |
1672 apm_->echo_cancellation()->set_stream_drift_samples(0); | |
1673 EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); | |
1674 EXPECT_EQ(apm_->kNoError, apm_->set_stream_delay_ms(0)); | |
1675 apm_->echo_cancellation()->set_stream_drift_samples(0); | |
1676 EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); | |
1677 EXPECT_TRUE(FrameDataAreEqual(*frame_, frame_copy)); | |
1678 | |
1679 // Check the test is valid. We should have distortion from the filter | |
1680 // when AEC is enabled (which won't affect the audio). | |
1681 frame_->samples_per_channel_ = 320; | |
1682 frame_->num_channels_ = 2; | |
1683 frame_->sample_rate_hz_ = 32000; | |
1684 SetFrameTo(frame_, 1000); | |
1685 frame_copy.CopyFrom(*frame_); | |
1686 EXPECT_EQ(apm_->kNoError, apm_->set_stream_delay_ms(0)); | |
1687 apm_->echo_cancellation()->set_stream_drift_samples(0); | |
1688 EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); | |
1689 EXPECT_FALSE(FrameDataAreEqual(*frame_, frame_copy)); | |
1690 } | |
1691 | |
1692 #ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP | |
1693 void ApmTest::ProcessDebugDump(const std::string& in_filename, | |
1694 const std::string& out_filename, | |
1695 Format format, | |
1696 int max_size_bytes) { | |
1697 FILE* in_file = fopen(in_filename.c_str(), "rb"); | |
1698 ASSERT_TRUE(in_file != NULL); | |
1699 audioproc::Event event_msg; | |
1700 bool first_init = true; | |
1701 | |
1702 while (ReadMessageFromFile(in_file, &event_msg)) { | |
1703 if (event_msg.type() == audioproc::Event::INIT) { | |
1704 const audioproc::Init msg = event_msg.init(); | |
1705 int reverse_sample_rate = msg.sample_rate(); | |
1706 if (msg.has_reverse_sample_rate()) { | |
1707 reverse_sample_rate = msg.reverse_sample_rate(); | |
1708 } | |
1709 int output_sample_rate = msg.sample_rate(); | |
1710 if (msg.has_output_sample_rate()) { | |
1711 output_sample_rate = msg.output_sample_rate(); | |
1712 } | |
1713 | |
1714 Init(msg.sample_rate(), | |
1715 output_sample_rate, | |
1716 reverse_sample_rate, | |
1717 msg.num_input_channels(), | |
1718 msg.num_output_channels(), | |
1719 msg.num_reverse_channels(), | |
1720 false); | |
1721 if (first_init) { | |
1722 // StartDebugRecording() writes an additional init message. Don't start | |
1723 // recording until after the first init to avoid the extra message. | |
1724 EXPECT_NOERR( | |
1725 apm_->StartDebugRecording(out_filename.c_str(), max_size_bytes)); | |
1726 first_init = false; | |
1727 } | |
1728 | |
1729 } else if (event_msg.type() == audioproc::Event::REVERSE_STREAM) { | |
1730 const audioproc::ReverseStream msg = event_msg.reverse_stream(); | |
1731 | |
1732 if (msg.channel_size() > 0) { | |
1733 ASSERT_EQ(revframe_->num_channels_, | |
1734 static_cast<size_t>(msg.channel_size())); | |
1735 for (int i = 0; i < msg.channel_size(); ++i) { | |
1736 memcpy(revfloat_cb_->channels()[i], | |
1737 msg.channel(i).data(), | |
1738 msg.channel(i).size()); | |
1739 } | |
1740 } else { | |
1741 memcpy(revframe_->data_, msg.data().data(), msg.data().size()); | |
1742 if (format == kFloatFormat) { | |
1743 // We're using an int16 input file; convert to float. | |
1744 ConvertToFloat(*revframe_, revfloat_cb_.get()); | |
1745 } | |
1746 } | |
1747 AnalyzeReverseStreamChooser(format); | |
1748 | |
1749 } else if (event_msg.type() == audioproc::Event::STREAM) { | |
1750 const audioproc::Stream msg = event_msg.stream(); | |
1751 // ProcessStream could have changed this for the output frame. | |
1752 frame_->num_channels_ = apm_->num_input_channels(); | |
1753 | |
1754 EXPECT_NOERR(apm_->gain_control()->set_stream_analog_level(msg.level())); | |
1755 EXPECT_NOERR(apm_->set_stream_delay_ms(msg.delay())); | |
1756 apm_->echo_cancellation()->set_stream_drift_samples(msg.drift()); | |
1757 if (msg.has_keypress()) { | |
1758 apm_->set_stream_key_pressed(msg.keypress()); | |
1759 } else { | |
1760 apm_->set_stream_key_pressed(true); | |
1761 } | |
1762 | |
1763 if (msg.input_channel_size() > 0) { | |
1764 ASSERT_EQ(frame_->num_channels_, | |
1765 static_cast<size_t>(msg.input_channel_size())); | |
1766 for (int i = 0; i < msg.input_channel_size(); ++i) { | |
1767 memcpy(float_cb_->channels()[i], | |
1768 msg.input_channel(i).data(), | |
1769 msg.input_channel(i).size()); | |
1770 } | |
1771 } else { | |
1772 memcpy(frame_->data_, msg.input_data().data(), msg.input_data().size()); | |
1773 if (format == kFloatFormat) { | |
1774 // We're using an int16 input file; convert to float. | |
1775 ConvertToFloat(*frame_, float_cb_.get()); | |
1776 } | |
1777 } | |
1778 ProcessStreamChooser(format); | |
1779 } | |
1780 } | |
1781 EXPECT_NOERR(apm_->StopDebugRecording()); | |
1782 fclose(in_file); | |
1783 } | |
1784 | |
1785 void ApmTest::VerifyDebugDumpTest(Format format) { | |
1786 const std::string in_filename = test::ResourcePath("ref03", "aecdump"); | |
1787 std::string format_string; | |
1788 switch (format) { | |
1789 case kIntFormat: | |
1790 format_string = "_int"; | |
1791 break; | |
1792 case kFloatFormat: | |
1793 format_string = "_float"; | |
1794 break; | |
1795 } | |
1796 const std::string ref_filename = test::TempFilename( | |
1797 test::OutputPath(), std::string("ref") + format_string + "_aecdump"); | |
1798 const std::string out_filename = test::TempFilename( | |
1799 test::OutputPath(), std::string("out") + format_string + "_aecdump"); | |
1800 const std::string limited_filename = test::TempFilename( | |
1801 test::OutputPath(), std::string("limited") + format_string + "_aecdump"); | |
1802 const size_t logging_limit_bytes = 100000; | |
1803 // We expect at least this many bytes in the created logfile. | |
1804 const size_t logging_expected_bytes = 95000; | |
1805 EnableAllComponents(); | |
1806 ProcessDebugDump(in_filename, ref_filename, format, -1); | |
1807 ProcessDebugDump(ref_filename, out_filename, format, -1); | |
1808 ProcessDebugDump(ref_filename, limited_filename, format, logging_limit_bytes); | |
1809 | |
1810 FILE* ref_file = fopen(ref_filename.c_str(), "rb"); | |
1811 FILE* out_file = fopen(out_filename.c_str(), "rb"); | |
1812 FILE* limited_file = fopen(limited_filename.c_str(), "rb"); | |
1813 ASSERT_TRUE(ref_file != NULL); | |
1814 ASSERT_TRUE(out_file != NULL); | |
1815 ASSERT_TRUE(limited_file != NULL); | |
1816 std::unique_ptr<uint8_t[]> ref_bytes; | |
1817 std::unique_ptr<uint8_t[]> out_bytes; | |
1818 std::unique_ptr<uint8_t[]> limited_bytes; | |
1819 | |
1820 size_t ref_size = ReadMessageBytesFromFile(ref_file, &ref_bytes); | |
1821 size_t out_size = ReadMessageBytesFromFile(out_file, &out_bytes); | |
1822 size_t limited_size = ReadMessageBytesFromFile(limited_file, &limited_bytes); | |
1823 size_t bytes_read = 0; | |
1824 size_t bytes_read_limited = 0; | |
1825 while (ref_size > 0 && out_size > 0) { | |
1826 bytes_read += ref_size; | |
1827 bytes_read_limited += limited_size; | |
1828 EXPECT_EQ(ref_size, out_size); | |
1829 EXPECT_GE(ref_size, limited_size); | |
1830 EXPECT_EQ(0, memcmp(ref_bytes.get(), out_bytes.get(), ref_size)); | |
1831 EXPECT_EQ(0, memcmp(ref_bytes.get(), limited_bytes.get(), limited_size)); | |
1832 ref_size = ReadMessageBytesFromFile(ref_file, &ref_bytes); | |
1833 out_size = ReadMessageBytesFromFile(out_file, &out_bytes); | |
1834 limited_size = ReadMessageBytesFromFile(limited_file, &limited_bytes); | |
1835 } | |
1836 EXPECT_GT(bytes_read, 0u); | |
1837 EXPECT_GT(bytes_read_limited, logging_expected_bytes); | |
1838 EXPECT_LE(bytes_read_limited, logging_limit_bytes); | |
1839 EXPECT_NE(0, feof(ref_file)); | |
1840 EXPECT_NE(0, feof(out_file)); | |
1841 EXPECT_NE(0, feof(limited_file)); | |
1842 ASSERT_EQ(0, fclose(ref_file)); | |
1843 ASSERT_EQ(0, fclose(out_file)); | |
1844 ASSERT_EQ(0, fclose(limited_file)); | |
1845 remove(ref_filename.c_str()); | |
1846 remove(out_filename.c_str()); | |
1847 remove(limited_filename.c_str()); | |
1848 } | |
1849 | |
1850 TEST_F(ApmTest, VerifyDebugDumpInt) { | |
1851 VerifyDebugDumpTest(kIntFormat); | |
1852 } | |
1853 | |
1854 TEST_F(ApmTest, VerifyDebugDumpFloat) { | |
1855 VerifyDebugDumpTest(kFloatFormat); | |
1856 } | |
1857 #endif | |
1858 | |
1859 // TODO(andrew): expand test to verify output. | |
1860 TEST_F(ApmTest, DebugDump) { | |
1861 const std::string filename = | |
1862 test::TempFilename(test::OutputPath(), "debug_aec"); | |
1863 EXPECT_EQ(apm_->kNullPointerError, | |
1864 apm_->StartDebugRecording(static_cast<const char*>(NULL), -1)); | |
1865 | |
1866 #ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP | |
1867 // Stopping without having started should be OK. | |
1868 EXPECT_EQ(apm_->kNoError, apm_->StopDebugRecording()); | |
1869 | |
1870 EXPECT_EQ(apm_->kNoError, apm_->StartDebugRecording(filename.c_str(), -1)); | |
1871 EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); | |
1872 EXPECT_EQ(apm_->kNoError, apm_->ProcessReverseStream(revframe_)); | |
1873 EXPECT_EQ(apm_->kNoError, apm_->StopDebugRecording()); | |
1874 | |
1875 // Verify the file has been written. | |
1876 FILE* fid = fopen(filename.c_str(), "r"); | |
1877 ASSERT_TRUE(fid != NULL); | |
1878 | |
1879 // Clean it up. | |
1880 ASSERT_EQ(0, fclose(fid)); | |
1881 ASSERT_EQ(0, remove(filename.c_str())); | |
1882 #else | |
1883 EXPECT_EQ(apm_->kUnsupportedFunctionError, | |
1884 apm_->StartDebugRecording(filename.c_str(), -1)); | |
1885 EXPECT_EQ(apm_->kUnsupportedFunctionError, apm_->StopDebugRecording()); | |
1886 | |
1887 // Verify the file has NOT been written. | |
1888 ASSERT_TRUE(fopen(filename.c_str(), "r") == NULL); | |
1889 #endif // WEBRTC_AUDIOPROC_DEBUG_DUMP | |
1890 } | |
1891 | |
1892 // TODO(andrew): expand test to verify output. | |
1893 TEST_F(ApmTest, DebugDumpFromFileHandle) { | |
1894 FILE* fid = NULL; | |
1895 EXPECT_EQ(apm_->kNullPointerError, apm_->StartDebugRecording(fid, -1)); | |
1896 const std::string filename = | |
1897 test::TempFilename(test::OutputPath(), "debug_aec"); | |
1898 fid = fopen(filename.c_str(), "w"); | |
1899 ASSERT_TRUE(fid); | |
1900 | |
1901 #ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP | |
1902 // Stopping without having started should be OK. | |
1903 EXPECT_EQ(apm_->kNoError, apm_->StopDebugRecording()); | |
1904 | |
1905 EXPECT_EQ(apm_->kNoError, apm_->StartDebugRecording(fid, -1)); | |
1906 EXPECT_EQ(apm_->kNoError, apm_->ProcessReverseStream(revframe_)); | |
1907 EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); | |
1908 EXPECT_EQ(apm_->kNoError, apm_->StopDebugRecording()); | |
1909 | |
1910 // Verify the file has been written. | |
1911 fid = fopen(filename.c_str(), "r"); | |
1912 ASSERT_TRUE(fid != NULL); | |
1913 | |
1914 // Clean it up. | |
1915 ASSERT_EQ(0, fclose(fid)); | |
1916 ASSERT_EQ(0, remove(filename.c_str())); | |
1917 #else | |
1918 EXPECT_EQ(apm_->kUnsupportedFunctionError, | |
1919 apm_->StartDebugRecording(fid, -1)); | |
1920 EXPECT_EQ(apm_->kUnsupportedFunctionError, apm_->StopDebugRecording()); | |
1921 | |
1922 ASSERT_EQ(0, fclose(fid)); | |
1923 #endif // WEBRTC_AUDIOPROC_DEBUG_DUMP | |
1924 } | |
1925 | |
1926 TEST_F(ApmTest, FloatAndIntInterfacesGiveSimilarResults) { | |
1927 audioproc::OutputData ref_data; | |
1928 OpenFileAndReadMessage(ref_filename_, &ref_data); | |
1929 | |
1930 Config config; | |
1931 config.Set<ExperimentalAgc>(new ExperimentalAgc(false)); | |
1932 std::unique_ptr<AudioProcessing> fapm(AudioProcessing::Create(config)); | |
1933 EnableAllComponents(); | |
1934 EnableAllAPComponents(fapm.get()); | |
1935 for (int i = 0; i < ref_data.test_size(); i++) { | |
1936 printf("Running test %d of %d...\n", i + 1, ref_data.test_size()); | |
1937 | |
1938 audioproc::Test* test = ref_data.mutable_test(i); | |
1939 // TODO(ajm): Restore downmixing test cases. | |
1940 if (test->num_input_channels() != test->num_output_channels()) | |
1941 continue; | |
1942 | |
1943 const size_t num_render_channels = | |
1944 static_cast<size_t>(test->num_reverse_channels()); | |
1945 const size_t num_input_channels = | |
1946 static_cast<size_t>(test->num_input_channels()); | |
1947 const size_t num_output_channels = | |
1948 static_cast<size_t>(test->num_output_channels()); | |
1949 const size_t samples_per_channel = static_cast<size_t>( | |
1950 test->sample_rate() * AudioProcessing::kChunkSizeMs / 1000); | |
1951 | |
1952 Init(test->sample_rate(), test->sample_rate(), test->sample_rate(), | |
1953 num_input_channels, num_output_channels, num_render_channels, true); | |
1954 Init(fapm.get()); | |
1955 | |
1956 ChannelBuffer<int16_t> output_cb(samples_per_channel, num_input_channels); | |
1957 ChannelBuffer<int16_t> output_int16(samples_per_channel, | |
1958 num_input_channels); | |
1959 | |
1960 int analog_level = 127; | |
1961 size_t num_bad_chunks = 0; | |
1962 while (ReadFrame(far_file_, revframe_, revfloat_cb_.get()) && | |
1963 ReadFrame(near_file_, frame_, float_cb_.get())) { | |
1964 frame_->vad_activity_ = AudioFrame::kVadUnknown; | |
1965 | |
1966 EXPECT_NOERR(apm_->ProcessReverseStream(revframe_)); | |
1967 EXPECT_NOERR(fapm->AnalyzeReverseStream( | |
1968 revfloat_cb_->channels(), | |
1969 samples_per_channel, | |
1970 test->sample_rate(), | |
1971 LayoutFromChannels(num_render_channels))); | |
1972 | |
1973 EXPECT_NOERR(apm_->set_stream_delay_ms(0)); | |
1974 EXPECT_NOERR(fapm->set_stream_delay_ms(0)); | |
1975 apm_->echo_cancellation()->set_stream_drift_samples(0); | |
1976 fapm->echo_cancellation()->set_stream_drift_samples(0); | |
1977 EXPECT_NOERR(apm_->gain_control()->set_stream_analog_level(analog_level)); | |
1978 EXPECT_NOERR(fapm->gain_control()->set_stream_analog_level(analog_level)); | |
1979 | |
1980 EXPECT_NOERR(apm_->ProcessStream(frame_)); | |
1981 Deinterleave(frame_->data_, samples_per_channel, num_output_channels, | |
1982 output_int16.channels()); | |
1983 | |
1984 EXPECT_NOERR(fapm->ProcessStream( | |
1985 float_cb_->channels(), | |
1986 samples_per_channel, | |
1987 test->sample_rate(), | |
1988 LayoutFromChannels(num_input_channels), | |
1989 test->sample_rate(), | |
1990 LayoutFromChannels(num_output_channels), | |
1991 float_cb_->channels())); | |
1992 for (size_t j = 0; j < num_output_channels; ++j) { | |
1993 FloatToS16(float_cb_->channels()[j], | |
1994 samples_per_channel, | |
1995 output_cb.channels()[j]); | |
1996 float variance = 0; | |
1997 float snr = ComputeSNR(output_int16.channels()[j], | |
1998 output_cb.channels()[j], | |
1999 samples_per_channel, &variance); | |
2000 | |
2001 const float kVarianceThreshold = 20; | |
2002 const float kSNRThreshold = 20; | |
2003 | |
2004 // Skip frames with low energy. | |
2005 if (sqrt(variance) > kVarianceThreshold && snr < kSNRThreshold) { | |
2006 ++num_bad_chunks; | |
2007 } | |
2008 } | |
2009 | |
2010 analog_level = fapm->gain_control()->stream_analog_level(); | |
2011 EXPECT_EQ(apm_->gain_control()->stream_analog_level(), | |
2012 fapm->gain_control()->stream_analog_level()); | |
2013 EXPECT_EQ(apm_->echo_cancellation()->stream_has_echo(), | |
2014 fapm->echo_cancellation()->stream_has_echo()); | |
2015 EXPECT_NEAR(apm_->noise_suppression()->speech_probability(), | |
2016 fapm->noise_suppression()->speech_probability(), | |
2017 0.01); | |
2018 | |
2019 // Reset in case of downmixing. | |
2020 frame_->num_channels_ = static_cast<size_t>(test->num_input_channels()); | |
2021 } | |
2022 | |
2023 #if defined(WEBRTC_AUDIOPROC_FLOAT_PROFILE) | |
2024 const size_t kMaxNumBadChunks = 0; | |
2025 #elif defined(WEBRTC_AUDIOPROC_FIXED_PROFILE) | |
2026 // There are a few chunks in the fixed-point profile that give low SNR. | |
2027 // Listening confirmed the difference is acceptable. | |
2028 const size_t kMaxNumBadChunks = 60; | |
2029 #endif | |
2030 EXPECT_LE(num_bad_chunks, kMaxNumBadChunks); | |
2031 | |
2032 rewind(far_file_); | |
2033 rewind(near_file_); | |
2034 } | |
2035 } | |
2036 | |
2037 // TODO(andrew): Add a test to process a few frames with different combinations | |
2038 // of enabled components. | |
2039 | |
2040 TEST_F(ApmTest, Process) { | |
2041 GOOGLE_PROTOBUF_VERIFY_VERSION; | |
2042 audioproc::OutputData ref_data; | |
2043 | |
2044 if (!write_ref_data) { | |
2045 OpenFileAndReadMessage(ref_filename_, &ref_data); | |
2046 } else { | |
2047 // Write the desired tests to the protobuf reference file. | |
2048 for (size_t i = 0; i < arraysize(kChannels); i++) { | |
2049 for (size_t j = 0; j < arraysize(kChannels); j++) { | |
2050 for (size_t l = 0; l < arraysize(kProcessSampleRates); l++) { | |
2051 audioproc::Test* test = ref_data.add_test(); | |
2052 test->set_num_reverse_channels(kChannels[i]); | |
2053 test->set_num_input_channels(kChannels[j]); | |
2054 test->set_num_output_channels(kChannels[j]); | |
2055 test->set_sample_rate(kProcessSampleRates[l]); | |
2056 test->set_use_aec_extended_filter(false); | |
2057 } | |
2058 } | |
2059 } | |
2060 #if defined(WEBRTC_AUDIOPROC_FLOAT_PROFILE) | |
2061 // To test the extended filter mode. | |
2062 audioproc::Test* test = ref_data.add_test(); | |
2063 test->set_num_reverse_channels(2); | |
2064 test->set_num_input_channels(2); | |
2065 test->set_num_output_channels(2); | |
2066 test->set_sample_rate(AudioProcessing::kSampleRate32kHz); | |
2067 test->set_use_aec_extended_filter(true); | |
2068 #endif | |
2069 } | |
2070 | |
2071 for (int i = 0; i < ref_data.test_size(); i++) { | |
2072 printf("Running test %d of %d...\n", i + 1, ref_data.test_size()); | |
2073 | |
2074 audioproc::Test* test = ref_data.mutable_test(i); | |
2075 // TODO(ajm): We no longer allow different input and output channels. Skip | |
2076 // these tests for now, but they should be removed from the set. | |
2077 if (test->num_input_channels() != test->num_output_channels()) | |
2078 continue; | |
2079 | |
2080 Config config; | |
2081 config.Set<ExperimentalAgc>(new ExperimentalAgc(false)); | |
2082 config.Set<ExtendedFilter>( | |
2083 new ExtendedFilter(test->use_aec_extended_filter())); | |
2084 apm_.reset(AudioProcessing::Create(config)); | |
2085 | |
2086 EnableAllComponents(); | |
2087 | |
2088 Init(test->sample_rate(), | |
2089 test->sample_rate(), | |
2090 test->sample_rate(), | |
2091 static_cast<size_t>(test->num_input_channels()), | |
2092 static_cast<size_t>(test->num_output_channels()), | |
2093 static_cast<size_t>(test->num_reverse_channels()), | |
2094 true); | |
2095 | |
2096 int frame_count = 0; | |
2097 int has_echo_count = 0; | |
2098 int has_voice_count = 0; | |
2099 int is_saturated_count = 0; | |
2100 int analog_level = 127; | |
2101 int analog_level_average = 0; | |
2102 int max_output_average = 0; | |
2103 float ns_speech_prob_average = 0.0f; | |
2104 | |
2105 while (ReadFrame(far_file_, revframe_) && ReadFrame(near_file_, frame_)) { | |
2106 EXPECT_EQ(apm_->kNoError, apm_->ProcessReverseStream(revframe_)); | |
2107 | |
2108 frame_->vad_activity_ = AudioFrame::kVadUnknown; | |
2109 | |
2110 EXPECT_EQ(apm_->kNoError, apm_->set_stream_delay_ms(0)); | |
2111 apm_->echo_cancellation()->set_stream_drift_samples(0); | |
2112 EXPECT_EQ(apm_->kNoError, | |
2113 apm_->gain_control()->set_stream_analog_level(analog_level)); | |
2114 | |
2115 EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); | |
2116 | |
2117 // Ensure the frame was downmixed properly. | |
2118 EXPECT_EQ(static_cast<size_t>(test->num_output_channels()), | |
2119 frame_->num_channels_); | |
2120 | |
2121 max_output_average += MaxAudioFrame(*frame_); | |
2122 | |
2123 if (apm_->echo_cancellation()->stream_has_echo()) { | |
2124 has_echo_count++; | |
2125 } | |
2126 | |
2127 analog_level = apm_->gain_control()->stream_analog_level(); | |
2128 analog_level_average += analog_level; | |
2129 if (apm_->gain_control()->stream_is_saturated()) { | |
2130 is_saturated_count++; | |
2131 } | |
2132 if (apm_->voice_detection()->stream_has_voice()) { | |
2133 has_voice_count++; | |
2134 EXPECT_EQ(AudioFrame::kVadActive, frame_->vad_activity_); | |
2135 } else { | |
2136 EXPECT_EQ(AudioFrame::kVadPassive, frame_->vad_activity_); | |
2137 } | |
2138 | |
2139 ns_speech_prob_average += apm_->noise_suppression()->speech_probability(); | |
2140 | |
2141 size_t frame_size = frame_->samples_per_channel_ * frame_->num_channels_; | |
2142 size_t write_count = fwrite(frame_->data_, | |
2143 sizeof(int16_t), | |
2144 frame_size, | |
2145 out_file_); | |
2146 ASSERT_EQ(frame_size, write_count); | |
2147 | |
2148 // Reset in case of downmixing. | |
2149 frame_->num_channels_ = static_cast<size_t>(test->num_input_channels()); | |
2150 frame_count++; | |
2151 } | |
2152 max_output_average /= frame_count; | |
2153 analog_level_average /= frame_count; | |
2154 ns_speech_prob_average /= frame_count; | |
2155 | |
2156 #if defined(WEBRTC_AUDIOPROC_FLOAT_PROFILE) | |
2157 EchoCancellation::Metrics echo_metrics; | |
2158 EXPECT_EQ(apm_->kNoError, | |
2159 apm_->echo_cancellation()->GetMetrics(&echo_metrics)); | |
2160 int median = 0; | |
2161 int std = 0; | |
2162 float fraction_poor_delays = 0; | |
2163 EXPECT_EQ(apm_->kNoError, | |
2164 apm_->echo_cancellation()->GetDelayMetrics( | |
2165 &median, &std, &fraction_poor_delays)); | |
2166 | |
2167 int rms_level = apm_->level_estimator()->RMS(); | |
2168 EXPECT_LE(0, rms_level); | |
2169 EXPECT_GE(127, rms_level); | |
2170 #endif | |
2171 | |
2172 if (!write_ref_data) { | |
2173 const int kIntNear = 1; | |
2174 // When running the test on a N7 we get a {2, 6} difference of | |
2175 // |has_voice_count| and |max_output_average| is up to 18 higher. | |
2176 // All numbers being consistently higher on N7 compare to ref_data. | |
2177 // TODO(bjornv): If we start getting more of these offsets on Android we | |
2178 // should consider a different approach. Either using one slack for all, | |
2179 // or generate a separate android reference. | |
2180 #if defined(WEBRTC_ANDROID) | |
2181 const int kHasVoiceCountOffset = 3; | |
2182 const int kHasVoiceCountNear = 3; | |
2183 const int kMaxOutputAverageOffset = 9; | |
2184 const int kMaxOutputAverageNear = 9; | |
2185 #else | |
2186 const int kHasVoiceCountOffset = 0; | |
2187 const int kHasVoiceCountNear = kIntNear; | |
2188 const int kMaxOutputAverageOffset = 0; | |
2189 const int kMaxOutputAverageNear = kIntNear; | |
2190 #endif | |
2191 EXPECT_NEAR(test->has_echo_count(), has_echo_count, kIntNear); | |
2192 EXPECT_NEAR(test->has_voice_count(), | |
2193 has_voice_count - kHasVoiceCountOffset, | |
2194 kHasVoiceCountNear); | |
2195 EXPECT_NEAR(test->is_saturated_count(), is_saturated_count, kIntNear); | |
2196 | |
2197 EXPECT_NEAR(test->analog_level_average(), analog_level_average, kIntNear); | |
2198 EXPECT_NEAR(test->max_output_average(), | |
2199 max_output_average - kMaxOutputAverageOffset, | |
2200 kMaxOutputAverageNear); | |
2201 | |
2202 #if defined(WEBRTC_AUDIOPROC_FLOAT_PROFILE) | |
2203 audioproc::Test::EchoMetrics reference = test->echo_metrics(); | |
2204 TestStats(echo_metrics.residual_echo_return_loss, | |
2205 reference.residual_echo_return_loss()); | |
2206 TestStats(echo_metrics.echo_return_loss, | |
2207 reference.echo_return_loss()); | |
2208 TestStats(echo_metrics.echo_return_loss_enhancement, | |
2209 reference.echo_return_loss_enhancement()); | |
2210 TestStats(echo_metrics.a_nlp, | |
2211 reference.a_nlp()); | |
2212 | |
2213 const double kFloatNear = 0.0005; | |
2214 audioproc::Test::DelayMetrics reference_delay = test->delay_metrics(); | |
2215 EXPECT_NEAR(reference_delay.median(), median, kIntNear); | |
2216 EXPECT_NEAR(reference_delay.std(), std, kIntNear); | |
2217 EXPECT_NEAR(reference_delay.fraction_poor_delays(), fraction_poor_delays, | |
2218 kFloatNear); | |
2219 | |
2220 EXPECT_NEAR(test->rms_level(), rms_level, kIntNear); | |
2221 | |
2222 EXPECT_NEAR(test->ns_speech_probability_average(), | |
2223 ns_speech_prob_average, | |
2224 kFloatNear); | |
2225 #endif | |
2226 } else { | |
2227 test->set_has_echo_count(has_echo_count); | |
2228 test->set_has_voice_count(has_voice_count); | |
2229 test->set_is_saturated_count(is_saturated_count); | |
2230 | |
2231 test->set_analog_level_average(analog_level_average); | |
2232 test->set_max_output_average(max_output_average); | |
2233 | |
2234 #if defined(WEBRTC_AUDIOPROC_FLOAT_PROFILE) | |
2235 audioproc::Test::EchoMetrics* message = test->mutable_echo_metrics(); | |
2236 WriteStatsMessage(echo_metrics.residual_echo_return_loss, | |
2237 message->mutable_residual_echo_return_loss()); | |
2238 WriteStatsMessage(echo_metrics.echo_return_loss, | |
2239 message->mutable_echo_return_loss()); | |
2240 WriteStatsMessage(echo_metrics.echo_return_loss_enhancement, | |
2241 message->mutable_echo_return_loss_enhancement()); | |
2242 WriteStatsMessage(echo_metrics.a_nlp, | |
2243 message->mutable_a_nlp()); | |
2244 | |
2245 audioproc::Test::DelayMetrics* message_delay = | |
2246 test->mutable_delay_metrics(); | |
2247 message_delay->set_median(median); | |
2248 message_delay->set_std(std); | |
2249 message_delay->set_fraction_poor_delays(fraction_poor_delays); | |
2250 | |
2251 test->set_rms_level(rms_level); | |
2252 | |
2253 EXPECT_LE(0.0f, ns_speech_prob_average); | |
2254 EXPECT_GE(1.0f, ns_speech_prob_average); | |
2255 test->set_ns_speech_probability_average(ns_speech_prob_average); | |
2256 #endif | |
2257 } | |
2258 | |
2259 rewind(far_file_); | |
2260 rewind(near_file_); | |
2261 } | |
2262 | |
2263 if (write_ref_data) { | |
2264 OpenFileAndWriteMessage(ref_filename_, ref_data); | |
2265 } | |
2266 } | |
2267 | |
2268 TEST_F(ApmTest, NoErrorsWithKeyboardChannel) { | |
2269 struct ChannelFormat { | |
2270 AudioProcessing::ChannelLayout in_layout; | |
2271 AudioProcessing::ChannelLayout out_layout; | |
2272 }; | |
2273 ChannelFormat cf[] = { | |
2274 {AudioProcessing::kMonoAndKeyboard, AudioProcessing::kMono}, | |
2275 {AudioProcessing::kStereoAndKeyboard, AudioProcessing::kMono}, | |
2276 {AudioProcessing::kStereoAndKeyboard, AudioProcessing::kStereo}, | |
2277 }; | |
2278 | |
2279 std::unique_ptr<AudioProcessing> ap(AudioProcessing::Create()); | |
2280 // Enable one component just to ensure some processing takes place. | |
2281 ap->noise_suppression()->Enable(true); | |
2282 for (size_t i = 0; i < arraysize(cf); ++i) { | |
2283 const int in_rate = 44100; | |
2284 const int out_rate = 48000; | |
2285 ChannelBuffer<float> in_cb(SamplesFromRate(in_rate), | |
2286 TotalChannelsFromLayout(cf[i].in_layout)); | |
2287 ChannelBuffer<float> out_cb(SamplesFromRate(out_rate), | |
2288 ChannelsFromLayout(cf[i].out_layout)); | |
2289 | |
2290 // Run over a few chunks. | |
2291 for (int j = 0; j < 10; ++j) { | |
2292 EXPECT_NOERR(ap->ProcessStream( | |
2293 in_cb.channels(), | |
2294 in_cb.num_frames(), | |
2295 in_rate, | |
2296 cf[i].in_layout, | |
2297 out_rate, | |
2298 cf[i].out_layout, | |
2299 out_cb.channels())); | |
2300 } | |
2301 } | |
2302 } | |
2303 | |
2304 // Compares the reference and test arrays over a region around the expected | |
2305 // delay. Finds the highest SNR in that region and adds the variance and squared | |
2306 // error results to the supplied accumulators. | |
2307 void UpdateBestSNR(const float* ref, | |
2308 const float* test, | |
2309 size_t length, | |
2310 int expected_delay, | |
2311 double* variance_acc, | |
2312 double* sq_error_acc) { | |
2313 double best_snr = std::numeric_limits<double>::min(); | |
2314 double best_variance = 0; | |
2315 double best_sq_error = 0; | |
2316 // Search over a region of eight samples around the expected delay. | |
2317 for (int delay = std::max(expected_delay - 4, 0); delay <= expected_delay + 4; | |
2318 ++delay) { | |
2319 double sq_error = 0; | |
2320 double variance = 0; | |
2321 for (size_t i = 0; i < length - delay; ++i) { | |
2322 double error = test[i + delay] - ref[i]; | |
2323 sq_error += error * error; | |
2324 variance += ref[i] * ref[i]; | |
2325 } | |
2326 | |
2327 if (sq_error == 0) { | |
2328 *variance_acc += variance; | |
2329 return; | |
2330 } | |
2331 double snr = variance / sq_error; | |
2332 if (snr > best_snr) { | |
2333 best_snr = snr; | |
2334 best_variance = variance; | |
2335 best_sq_error = sq_error; | |
2336 } | |
2337 } | |
2338 | |
2339 *variance_acc += best_variance; | |
2340 *sq_error_acc += best_sq_error; | |
2341 } | |
2342 | |
2343 // Used to test a multitude of sample rate and channel combinations. It works | |
2344 // by first producing a set of reference files (in SetUpTestCase) that are | |
2345 // assumed to be correct, as the used parameters are verified by other tests | |
2346 // in this collection. Primarily the reference files are all produced at | |
2347 // "native" rates which do not involve any resampling. | |
2348 | |
2349 // Each test pass produces an output file with a particular format. The output | |
2350 // is matched against the reference file closest to its internal processing | |
2351 // format. If necessary the output is resampled back to its process format. | |
2352 // Due to the resampling distortion, we don't expect identical results, but | |
2353 // enforce SNR thresholds which vary depending on the format. 0 is a special | |
2354 // case SNR which corresponds to inf, or zero error. | |
2355 typedef std::tr1::tuple<int, int, int, int, double, double> | |
2356 AudioProcessingTestData; | |
2357 class AudioProcessingTest | |
2358 : public testing::TestWithParam<AudioProcessingTestData> { | |
2359 public: | |
2360 AudioProcessingTest() | |
2361 : input_rate_(std::tr1::get<0>(GetParam())), | |
2362 output_rate_(std::tr1::get<1>(GetParam())), | |
2363 reverse_input_rate_(std::tr1::get<2>(GetParam())), | |
2364 reverse_output_rate_(std::tr1::get<3>(GetParam())), | |
2365 expected_snr_(std::tr1::get<4>(GetParam())), | |
2366 expected_reverse_snr_(std::tr1::get<5>(GetParam())) {} | |
2367 | |
2368 virtual ~AudioProcessingTest() {} | |
2369 | |
2370 static void SetUpTestCase() { | |
2371 // Create all needed output reference files. | |
2372 const int kNativeRates[] = {8000, 16000, 32000, 48000}; | |
2373 const size_t kNumChannels[] = {1, 2}; | |
2374 for (size_t i = 0; i < arraysize(kNativeRates); ++i) { | |
2375 for (size_t j = 0; j < arraysize(kNumChannels); ++j) { | |
2376 for (size_t k = 0; k < arraysize(kNumChannels); ++k) { | |
2377 // The reference files always have matching input and output channels. | |
2378 ProcessFormat(kNativeRates[i], kNativeRates[i], kNativeRates[i], | |
2379 kNativeRates[i], kNumChannels[j], kNumChannels[j], | |
2380 kNumChannels[k], kNumChannels[k], "ref"); | |
2381 } | |
2382 } | |
2383 } | |
2384 } | |
2385 | |
2386 static void TearDownTestCase() { | |
2387 ClearTempFiles(); | |
2388 } | |
2389 | |
2390 // Runs a process pass on files with the given parameters and dumps the output | |
2391 // to a file specified with |output_file_prefix|. Both forward and reverse | |
2392 // output streams are dumped. | |
2393 static void ProcessFormat(int input_rate, | |
2394 int output_rate, | |
2395 int reverse_input_rate, | |
2396 int reverse_output_rate, | |
2397 size_t num_input_channels, | |
2398 size_t num_output_channels, | |
2399 size_t num_reverse_input_channels, | |
2400 size_t num_reverse_output_channels, | |
2401 std::string output_file_prefix) { | |
2402 Config config; | |
2403 config.Set<ExperimentalAgc>(new ExperimentalAgc(false)); | |
2404 std::unique_ptr<AudioProcessing> ap(AudioProcessing::Create(config)); | |
2405 EnableAllAPComponents(ap.get()); | |
2406 | |
2407 ProcessingConfig processing_config = { | |
2408 {{input_rate, num_input_channels}, | |
2409 {output_rate, num_output_channels}, | |
2410 {reverse_input_rate, num_reverse_input_channels}, | |
2411 {reverse_output_rate, num_reverse_output_channels}}}; | |
2412 ap->Initialize(processing_config); | |
2413 | |
2414 FILE* far_file = | |
2415 fopen(ResourceFilePath("far", reverse_input_rate).c_str(), "rb"); | |
2416 FILE* near_file = fopen(ResourceFilePath("near", input_rate).c_str(), "rb"); | |
2417 FILE* out_file = | |
2418 fopen(OutputFilePath(output_file_prefix, input_rate, output_rate, | |
2419 reverse_input_rate, reverse_output_rate, | |
2420 num_input_channels, num_output_channels, | |
2421 num_reverse_input_channels, | |
2422 num_reverse_output_channels, kForward).c_str(), | |
2423 "wb"); | |
2424 FILE* rev_out_file = | |
2425 fopen(OutputFilePath(output_file_prefix, input_rate, output_rate, | |
2426 reverse_input_rate, reverse_output_rate, | |
2427 num_input_channels, num_output_channels, | |
2428 num_reverse_input_channels, | |
2429 num_reverse_output_channels, kReverse).c_str(), | |
2430 "wb"); | |
2431 ASSERT_TRUE(far_file != NULL); | |
2432 ASSERT_TRUE(near_file != NULL); | |
2433 ASSERT_TRUE(out_file != NULL); | |
2434 ASSERT_TRUE(rev_out_file != NULL); | |
2435 | |
2436 ChannelBuffer<float> fwd_cb(SamplesFromRate(input_rate), | |
2437 num_input_channels); | |
2438 ChannelBuffer<float> rev_cb(SamplesFromRate(reverse_input_rate), | |
2439 num_reverse_input_channels); | |
2440 ChannelBuffer<float> out_cb(SamplesFromRate(output_rate), | |
2441 num_output_channels); | |
2442 ChannelBuffer<float> rev_out_cb(SamplesFromRate(reverse_output_rate), | |
2443 num_reverse_output_channels); | |
2444 | |
2445 // Temporary buffers. | |
2446 const int max_length = | |
2447 2 * std::max(std::max(out_cb.num_frames(), rev_out_cb.num_frames()), | |
2448 std::max(fwd_cb.num_frames(), rev_cb.num_frames())); | |
2449 std::unique_ptr<float[]> float_data(new float[max_length]); | |
2450 std::unique_ptr<int16_t[]> int_data(new int16_t[max_length]); | |
2451 | |
2452 int analog_level = 127; | |
2453 while (ReadChunk(far_file, int_data.get(), float_data.get(), &rev_cb) && | |
2454 ReadChunk(near_file, int_data.get(), float_data.get(), &fwd_cb)) { | |
2455 EXPECT_NOERR(ap->ProcessReverseStream( | |
2456 rev_cb.channels(), processing_config.reverse_input_stream(), | |
2457 processing_config.reverse_output_stream(), rev_out_cb.channels())); | |
2458 | |
2459 EXPECT_NOERR(ap->set_stream_delay_ms(0)); | |
2460 ap->echo_cancellation()->set_stream_drift_samples(0); | |
2461 EXPECT_NOERR(ap->gain_control()->set_stream_analog_level(analog_level)); | |
2462 | |
2463 EXPECT_NOERR(ap->ProcessStream( | |
2464 fwd_cb.channels(), | |
2465 fwd_cb.num_frames(), | |
2466 input_rate, | |
2467 LayoutFromChannels(num_input_channels), | |
2468 output_rate, | |
2469 LayoutFromChannels(num_output_channels), | |
2470 out_cb.channels())); | |
2471 | |
2472 // Dump forward output to file. | |
2473 Interleave(out_cb.channels(), out_cb.num_frames(), out_cb.num_channels(), | |
2474 float_data.get()); | |
2475 size_t out_length = out_cb.num_channels() * out_cb.num_frames(); | |
2476 | |
2477 ASSERT_EQ(out_length, | |
2478 fwrite(float_data.get(), sizeof(float_data[0]), | |
2479 out_length, out_file)); | |
2480 | |
2481 // Dump reverse output to file. | |
2482 Interleave(rev_out_cb.channels(), rev_out_cb.num_frames(), | |
2483 rev_out_cb.num_channels(), float_data.get()); | |
2484 size_t rev_out_length = | |
2485 rev_out_cb.num_channels() * rev_out_cb.num_frames(); | |
2486 | |
2487 ASSERT_EQ(rev_out_length, | |
2488 fwrite(float_data.get(), sizeof(float_data[0]), rev_out_length, | |
2489 rev_out_file)); | |
2490 | |
2491 analog_level = ap->gain_control()->stream_analog_level(); | |
2492 } | |
2493 fclose(far_file); | |
2494 fclose(near_file); | |
2495 fclose(out_file); | |
2496 fclose(rev_out_file); | |
2497 } | |
2498 | |
2499 protected: | |
2500 int input_rate_; | |
2501 int output_rate_; | |
2502 int reverse_input_rate_; | |
2503 int reverse_output_rate_; | |
2504 double expected_snr_; | |
2505 double expected_reverse_snr_; | |
2506 }; | |
2507 | |
2508 TEST_P(AudioProcessingTest, Formats) { | |
2509 struct ChannelFormat { | |
2510 int num_input; | |
2511 int num_output; | |
2512 int num_reverse_input; | |
2513 int num_reverse_output; | |
2514 }; | |
2515 ChannelFormat cf[] = { | |
2516 {1, 1, 1, 1}, | |
2517 {1, 1, 2, 1}, | |
2518 {2, 1, 1, 1}, | |
2519 {2, 1, 2, 1}, | |
2520 {2, 2, 1, 1}, | |
2521 {2, 2, 2, 2}, | |
2522 }; | |
2523 | |
2524 for (size_t i = 0; i < arraysize(cf); ++i) { | |
2525 ProcessFormat(input_rate_, output_rate_, reverse_input_rate_, | |
2526 reverse_output_rate_, cf[i].num_input, cf[i].num_output, | |
2527 cf[i].num_reverse_input, cf[i].num_reverse_output, "out"); | |
2528 | |
2529 // Verify output for both directions. | |
2530 std::vector<StreamDirection> stream_directions; | |
2531 stream_directions.push_back(kForward); | |
2532 stream_directions.push_back(kReverse); | |
2533 for (StreamDirection file_direction : stream_directions) { | |
2534 const int in_rate = file_direction ? reverse_input_rate_ : input_rate_; | |
2535 const int out_rate = file_direction ? reverse_output_rate_ : output_rate_; | |
2536 const int out_num = | |
2537 file_direction ? cf[i].num_reverse_output : cf[i].num_output; | |
2538 const double expected_snr = | |
2539 file_direction ? expected_reverse_snr_ : expected_snr_; | |
2540 | |
2541 const int min_ref_rate = std::min(in_rate, out_rate); | |
2542 int ref_rate; | |
2543 | |
2544 if (min_ref_rate > 32000) { | |
2545 ref_rate = 48000; | |
2546 } else if (min_ref_rate > 16000) { | |
2547 ref_rate = 32000; | |
2548 } else if (min_ref_rate > 8000) { | |
2549 ref_rate = 16000; | |
2550 } else { | |
2551 ref_rate = 8000; | |
2552 } | |
2553 #ifdef WEBRTC_ARCH_ARM_FAMILY | |
2554 if (file_direction == kForward) { | |
2555 ref_rate = std::min(ref_rate, 32000); | |
2556 } | |
2557 #endif | |
2558 FILE* out_file = fopen( | |
2559 OutputFilePath("out", input_rate_, output_rate_, reverse_input_rate_, | |
2560 reverse_output_rate_, cf[i].num_input, | |
2561 cf[i].num_output, cf[i].num_reverse_input, | |
2562 cf[i].num_reverse_output, file_direction).c_str(), | |
2563 "rb"); | |
2564 // The reference files always have matching input and output channels. | |
2565 FILE* ref_file = fopen( | |
2566 OutputFilePath("ref", ref_rate, ref_rate, ref_rate, ref_rate, | |
2567 cf[i].num_output, cf[i].num_output, | |
2568 cf[i].num_reverse_output, cf[i].num_reverse_output, | |
2569 file_direction).c_str(), | |
2570 "rb"); | |
2571 ASSERT_TRUE(out_file != NULL); | |
2572 ASSERT_TRUE(ref_file != NULL); | |
2573 | |
2574 const size_t ref_length = SamplesFromRate(ref_rate) * out_num; | |
2575 const size_t out_length = SamplesFromRate(out_rate) * out_num; | |
2576 // Data from the reference file. | |
2577 std::unique_ptr<float[]> ref_data(new float[ref_length]); | |
2578 // Data from the output file. | |
2579 std::unique_ptr<float[]> out_data(new float[out_length]); | |
2580 // Data from the resampled output, in case the reference and output rates | |
2581 // don't match. | |
2582 std::unique_ptr<float[]> cmp_data(new float[ref_length]); | |
2583 | |
2584 PushResampler<float> resampler; | |
2585 resampler.InitializeIfNeeded(out_rate, ref_rate, out_num); | |
2586 | |
2587 // Compute the resampling delay of the output relative to the reference, | |
2588 // to find the region over which we should search for the best SNR. | |
2589 float expected_delay_sec = 0; | |
2590 if (in_rate != ref_rate) { | |
2591 // Input resampling delay. | |
2592 expected_delay_sec += | |
2593 PushSincResampler::AlgorithmicDelaySeconds(in_rate); | |
2594 } | |
2595 if (out_rate != ref_rate) { | |
2596 // Output resampling delay. | |
2597 expected_delay_sec += | |
2598 PushSincResampler::AlgorithmicDelaySeconds(ref_rate); | |
2599 // Delay of converting the output back to its processing rate for | |
2600 // testing. | |
2601 expected_delay_sec += | |
2602 PushSincResampler::AlgorithmicDelaySeconds(out_rate); | |
2603 } | |
2604 int expected_delay = | |
2605 floor(expected_delay_sec * ref_rate + 0.5f) * out_num; | |
2606 | |
2607 double variance = 0; | |
2608 double sq_error = 0; | |
2609 while (fread(out_data.get(), sizeof(out_data[0]), out_length, out_file) && | |
2610 fread(ref_data.get(), sizeof(ref_data[0]), ref_length, ref_file)) { | |
2611 float* out_ptr = out_data.get(); | |
2612 if (out_rate != ref_rate) { | |
2613 // Resample the output back to its internal processing rate if | |
2614 // necssary. | |
2615 ASSERT_EQ(ref_length, | |
2616 static_cast<size_t>(resampler.Resample( | |
2617 out_ptr, out_length, cmp_data.get(), ref_length))); | |
2618 out_ptr = cmp_data.get(); | |
2619 } | |
2620 | |
2621 // Update the |sq_error| and |variance| accumulators with the highest | |
2622 // SNR of reference vs output. | |
2623 UpdateBestSNR(ref_data.get(), out_ptr, ref_length, expected_delay, | |
2624 &variance, &sq_error); | |
2625 } | |
2626 | |
2627 std::cout << "(" << input_rate_ << ", " << output_rate_ << ", " | |
2628 << reverse_input_rate_ << ", " << reverse_output_rate_ << ", " | |
2629 << cf[i].num_input << ", " << cf[i].num_output << ", " | |
2630 << cf[i].num_reverse_input << ", " << cf[i].num_reverse_output | |
2631 << ", " << file_direction << "): "; | |
2632 if (sq_error > 0) { | |
2633 double snr = 10 * log10(variance / sq_error); | |
2634 EXPECT_GE(snr, expected_snr); | |
2635 EXPECT_NE(0, expected_snr); | |
2636 std::cout << "SNR=" << snr << " dB" << std::endl; | |
2637 } else { | |
2638 std::cout << "SNR=inf dB" << std::endl; | |
2639 } | |
2640 | |
2641 fclose(out_file); | |
2642 fclose(ref_file); | |
2643 } | |
2644 } | |
2645 } | |
2646 | |
2647 #if defined(WEBRTC_AUDIOPROC_FLOAT_PROFILE) | |
2648 INSTANTIATE_TEST_CASE_P( | |
2649 CommonFormats, | |
2650 AudioProcessingTest, | |
2651 testing::Values(std::tr1::make_tuple(48000, 48000, 48000, 48000, 0, 0), | |
2652 std::tr1::make_tuple(48000, 48000, 32000, 48000, 35, 30), | |
2653 std::tr1::make_tuple(48000, 48000, 16000, 48000, 35, 20), | |
2654 std::tr1::make_tuple(48000, 44100, 48000, 44100, 20, 20), | |
2655 std::tr1::make_tuple(48000, 44100, 32000, 44100, 20, 15), | |
2656 std::tr1::make_tuple(48000, 44100, 16000, 44100, 20, 15), | |
2657 std::tr1::make_tuple(48000, 32000, 48000, 32000, 30, 35), | |
2658 std::tr1::make_tuple(48000, 32000, 32000, 32000, 30, 0), | |
2659 std::tr1::make_tuple(48000, 32000, 16000, 32000, 30, 20), | |
2660 std::tr1::make_tuple(48000, 16000, 48000, 16000, 25, 20), | |
2661 std::tr1::make_tuple(48000, 16000, 32000, 16000, 25, 20), | |
2662 std::tr1::make_tuple(48000, 16000, 16000, 16000, 25, 0), | |
2663 | |
2664 std::tr1::make_tuple(44100, 48000, 48000, 48000, 30, 0), | |
2665 std::tr1::make_tuple(44100, 48000, 32000, 48000, 30, 30), | |
2666 std::tr1::make_tuple(44100, 48000, 16000, 48000, 30, 20), | |
2667 std::tr1::make_tuple(44100, 44100, 48000, 44100, 20, 20), | |
2668 std::tr1::make_tuple(44100, 44100, 32000, 44100, 20, 15), | |
2669 std::tr1::make_tuple(44100, 44100, 16000, 44100, 20, 15), | |
2670 std::tr1::make_tuple(44100, 32000, 48000, 32000, 30, 35), | |
2671 std::tr1::make_tuple(44100, 32000, 32000, 32000, 30, 0), | |
2672 std::tr1::make_tuple(44100, 32000, 16000, 32000, 30, 20), | |
2673 std::tr1::make_tuple(44100, 16000, 48000, 16000, 25, 20), | |
2674 std::tr1::make_tuple(44100, 16000, 32000, 16000, 25, 20), | |
2675 std::tr1::make_tuple(44100, 16000, 16000, 16000, 25, 0), | |
2676 | |
2677 std::tr1::make_tuple(32000, 48000, 48000, 48000, 30, 0), | |
2678 std::tr1::make_tuple(32000, 48000, 32000, 48000, 35, 30), | |
2679 std::tr1::make_tuple(32000, 48000, 16000, 48000, 30, 20), | |
2680 std::tr1::make_tuple(32000, 44100, 48000, 44100, 20, 20), | |
2681 std::tr1::make_tuple(32000, 44100, 32000, 44100, 20, 15), | |
2682 std::tr1::make_tuple(32000, 44100, 16000, 44100, 20, 15), | |
2683 std::tr1::make_tuple(32000, 32000, 48000, 32000, 40, 35), | |
2684 std::tr1::make_tuple(32000, 32000, 32000, 32000, 0, 0), | |
2685 std::tr1::make_tuple(32000, 32000, 16000, 32000, 40, 20), | |
2686 std::tr1::make_tuple(32000, 16000, 48000, 16000, 25, 20), | |
2687 std::tr1::make_tuple(32000, 16000, 32000, 16000, 25, 20), | |
2688 std::tr1::make_tuple(32000, 16000, 16000, 16000, 25, 0), | |
2689 | |
2690 std::tr1::make_tuple(16000, 48000, 48000, 48000, 25, 0), | |
2691 std::tr1::make_tuple(16000, 48000, 32000, 48000, 25, 30), | |
2692 std::tr1::make_tuple(16000, 48000, 16000, 48000, 25, 20), | |
2693 std::tr1::make_tuple(16000, 44100, 48000, 44100, 15, 20), | |
2694 std::tr1::make_tuple(16000, 44100, 32000, 44100, 15, 15), | |
2695 std::tr1::make_tuple(16000, 44100, 16000, 44100, 15, 15), | |
2696 std::tr1::make_tuple(16000, 32000, 48000, 32000, 25, 35), | |
2697 std::tr1::make_tuple(16000, 32000, 32000, 32000, 25, 0), | |
2698 std::tr1::make_tuple(16000, 32000, 16000, 32000, 25, 20), | |
2699 std::tr1::make_tuple(16000, 16000, 48000, 16000, 40, 20), | |
2700 std::tr1::make_tuple(16000, 16000, 32000, 16000, 40, 20), | |
2701 std::tr1::make_tuple(16000, 16000, 16000, 16000, 0, 0))); | |
2702 | |
2703 #elif defined(WEBRTC_AUDIOPROC_FIXED_PROFILE) | |
2704 INSTANTIATE_TEST_CASE_P( | |
2705 CommonFormats, | |
2706 AudioProcessingTest, | |
2707 testing::Values(std::tr1::make_tuple(48000, 48000, 48000, 48000, 20, 0), | |
2708 std::tr1::make_tuple(48000, 48000, 32000, 48000, 20, 30), | |
2709 std::tr1::make_tuple(48000, 48000, 16000, 48000, 20, 20), | |
2710 std::tr1::make_tuple(48000, 44100, 48000, 44100, 15, 20), | |
2711 std::tr1::make_tuple(48000, 44100, 32000, 44100, 15, 15), | |
2712 std::tr1::make_tuple(48000, 44100, 16000, 44100, 15, 15), | |
2713 std::tr1::make_tuple(48000, 32000, 48000, 32000, 20, 35), | |
2714 std::tr1::make_tuple(48000, 32000, 32000, 32000, 20, 0), | |
2715 std::tr1::make_tuple(48000, 32000, 16000, 32000, 20, 20), | |
2716 std::tr1::make_tuple(48000, 16000, 48000, 16000, 20, 20), | |
2717 std::tr1::make_tuple(48000, 16000, 32000, 16000, 20, 20), | |
2718 std::tr1::make_tuple(48000, 16000, 16000, 16000, 20, 0), | |
2719 | |
2720 std::tr1::make_tuple(44100, 48000, 48000, 48000, 15, 0), | |
2721 std::tr1::make_tuple(44100, 48000, 32000, 48000, 15, 30), | |
2722 std::tr1::make_tuple(44100, 48000, 16000, 48000, 15, 20), | |
2723 std::tr1::make_tuple(44100, 44100, 48000, 44100, 15, 20), | |
2724 std::tr1::make_tuple(44100, 44100, 32000, 44100, 15, 15), | |
2725 std::tr1::make_tuple(44100, 44100, 16000, 44100, 15, 15), | |
2726 std::tr1::make_tuple(44100, 32000, 48000, 32000, 20, 35), | |
2727 std::tr1::make_tuple(44100, 32000, 32000, 32000, 20, 0), | |
2728 std::tr1::make_tuple(44100, 32000, 16000, 32000, 20, 20), | |
2729 std::tr1::make_tuple(44100, 16000, 48000, 16000, 20, 20), | |
2730 std::tr1::make_tuple(44100, 16000, 32000, 16000, 20, 20), | |
2731 std::tr1::make_tuple(44100, 16000, 16000, 16000, 20, 0), | |
2732 | |
2733 std::tr1::make_tuple(32000, 48000, 48000, 48000, 35, 0), | |
2734 std::tr1::make_tuple(32000, 48000, 32000, 48000, 65, 30), | |
2735 std::tr1::make_tuple(32000, 48000, 16000, 48000, 40, 20), | |
2736 std::tr1::make_tuple(32000, 44100, 48000, 44100, 20, 20), | |
2737 std::tr1::make_tuple(32000, 44100, 32000, 44100, 20, 15), | |
2738 std::tr1::make_tuple(32000, 44100, 16000, 44100, 20, 15), | |
2739 std::tr1::make_tuple(32000, 32000, 48000, 32000, 35, 35), | |
2740 std::tr1::make_tuple(32000, 32000, 32000, 32000, 0, 0), | |
2741 std::tr1::make_tuple(32000, 32000, 16000, 32000, 40, 20), | |
2742 std::tr1::make_tuple(32000, 16000, 48000, 16000, 20, 20), | |
2743 std::tr1::make_tuple(32000, 16000, 32000, 16000, 20, 20), | |
2744 std::tr1::make_tuple(32000, 16000, 16000, 16000, 20, 0), | |
2745 | |
2746 std::tr1::make_tuple(16000, 48000, 48000, 48000, 25, 0), | |
2747 std::tr1::make_tuple(16000, 48000, 32000, 48000, 25, 30), | |
2748 std::tr1::make_tuple(16000, 48000, 16000, 48000, 25, 20), | |
2749 std::tr1::make_tuple(16000, 44100, 48000, 44100, 15, 20), | |
2750 std::tr1::make_tuple(16000, 44100, 32000, 44100, 15, 15), | |
2751 std::tr1::make_tuple(16000, 44100, 16000, 44100, 15, 15), | |
2752 std::tr1::make_tuple(16000, 32000, 48000, 32000, 25, 35), | |
2753 std::tr1::make_tuple(16000, 32000, 32000, 32000, 25, 0), | |
2754 std::tr1::make_tuple(16000, 32000, 16000, 32000, 25, 20), | |
2755 std::tr1::make_tuple(16000, 16000, 48000, 16000, 35, 20), | |
2756 std::tr1::make_tuple(16000, 16000, 32000, 16000, 35, 20), | |
2757 std::tr1::make_tuple(16000, 16000, 16000, 16000, 0, 0))); | |
2758 #endif | |
2759 | |
2760 } // namespace | |
2761 } // namespace webrtc | |
OLD | NEW |