blob: aede7b7a2c7cb64e3177c4bd51a816d26fcaa026 [file] [log] [blame]
niklase@google.com470e71d2011-07-07 08:21:25 +00001/*
2 * Copyright (c) 2011 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 <stdio.h>
12#include <string.h>
13#ifdef WEBRTC_ANDROID
14#include <sys/stat.h>
15#endif
16
niklase@google.com470e71d2011-07-07 08:21:25 +000017#include "gtest/gtest.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000018
19#include "audio_processing.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000020#include "cpu_features_wrapper.h"
ajm@google.com808e0e02011-08-03 21:08:51 +000021#include "module_common_types.h"
andrew@webrtc.org3119ecf2011-11-01 17:00:18 +000022#include "scoped_ptr.h"
ajm@google.com808e0e02011-08-03 21:08:51 +000023#include "tick_util.h"
leozwang@google.com81520b72011-08-05 22:20:03 +000024#ifdef WEBRTC_ANDROID
andrew@webrtc.org4d5d5c12011-10-19 01:40:33 +000025#include "external/webrtc/src/modules/audio_processing/debug.pb.h"
leozwang@google.com81520b72011-08-05 22:20:03 +000026#else
ajm@google.com808e0e02011-08-03 21:08:51 +000027#include "webrtc/audio_processing/debug.pb.h"
leozwang@google.com81520b72011-08-05 22:20:03 +000028#endif
niklase@google.com470e71d2011-07-07 08:21:25 +000029
30using webrtc::AudioFrame;
niklase@google.com470e71d2011-07-07 08:21:25 +000031using webrtc::AudioProcessing;
andrew@webrtc.org94c74132011-09-19 15:17:57 +000032using webrtc::EchoCancellation;
niklase@google.com470e71d2011-07-07 08:21:25 +000033using webrtc::GainControl;
34using webrtc::NoiseSuppression;
andrew@webrtc.org3119ecf2011-11-01 17:00:18 +000035using webrtc::scoped_array;
ajm@google.com808e0e02011-08-03 21:08:51 +000036using webrtc::TickInterval;
37using webrtc::TickTime;
andrew@webrtc.org3119ecf2011-11-01 17:00:18 +000038
ajm@google.com808e0e02011-08-03 21:08:51 +000039using webrtc::audioproc::Event;
40using webrtc::audioproc::Init;
41using webrtc::audioproc::ReverseStream;
42using webrtc::audioproc::Stream;
43
44namespace {
45// Returns true on success, false on error or end-of-file.
46bool ReadMessageFromFile(FILE* file,
47 ::google::protobuf::MessageLite* msg) {
48 // The "wire format" for the size is little-endian.
49 // Assume process_test is running on a little-endian machine.
andrew@webrtc.orgcb181212011-10-26 00:27:17 +000050 int32_t size = 0;
ajm@google.com808e0e02011-08-03 21:08:51 +000051 if (fread(&size, sizeof(int32_t), 1, file) != 1) {
52 return false;
53 }
54 if (size <= 0) {
55 return false;
56 }
andrew@webrtc.org3119ecf2011-11-01 17:00:18 +000057 const size_t usize = static_cast<size_t>(size);
ajm@google.com808e0e02011-08-03 21:08:51 +000058
andrew@webrtc.org3119ecf2011-11-01 17:00:18 +000059 scoped_array<char> array(new char[usize]);
60 if (fread(array.get(), sizeof(char), usize, file) != usize) {
ajm@google.com808e0e02011-08-03 21:08:51 +000061 return false;
62 }
63
64 msg->Clear();
andrew@webrtc.org3119ecf2011-11-01 17:00:18 +000065 return msg->ParseFromArray(array.get(), usize);
ajm@google.com808e0e02011-08-03 21:08:51 +000066}
niklase@google.com470e71d2011-07-07 08:21:25 +000067
andrew@webrtc.org94c74132011-09-19 15:17:57 +000068void PrintStat(const AudioProcessing::Statistic& stat) {
69 printf("%d, %d, %d\n", stat.average,
70 stat.maximum,
71 stat.minimum);
72}
73
niklase@google.com470e71d2011-07-07 08:21:25 +000074void usage() {
75 printf(
ajm@google.com808e0e02011-08-03 21:08:51 +000076 "Usage: process_test [options] [-pb PROTOBUF_FILE]\n"
77 " [-ir REVERSE_FILE] [-i PRIMARY_FILE] [-o OUT_FILE]\n");
niklase@google.com470e71d2011-07-07 08:21:25 +000078 printf(
79 "process_test is a test application for AudioProcessing.\n\n"
ajm@google.com808e0e02011-08-03 21:08:51 +000080 "When a protobuf debug file is available, specify it with -pb.\n"
81 "Alternately, when -ir or -i is used, the specified files will be\n"
82 "processed directly in a simulation mode. Otherwise the full set of\n"
83 "legacy test files is expected to be present in the working directory.\n");
niklase@google.com470e71d2011-07-07 08:21:25 +000084 printf("\n");
85 printf("Options\n");
ajm@google.com808e0e02011-08-03 21:08:51 +000086 printf("General configuration (only used for the simulation mode):\n");
niklase@google.com470e71d2011-07-07 08:21:25 +000087 printf(" -fs SAMPLE_RATE_HZ\n");
88 printf(" -ch CHANNELS_IN CHANNELS_OUT\n");
89 printf(" -rch REVERSE_CHANNELS\n");
90 printf("\n");
91 printf("Component configuration:\n");
92 printf(
93 "All components are disabled by default. Each block below begins with a\n"
94 "flag to enable the component with default settings. The subsequent flags\n"
95 "in the block are used to provide configuration settings.\n");
96 printf("\n -aec Echo cancellation\n");
97 printf(" --drift_compensation\n");
98 printf(" --no_drift_compensation\n");
andrew@webrtc.org94c74132011-09-19 15:17:57 +000099 printf(" --no_echo_metrics\n");
bjornv@google.com1ba3dbe2011-10-03 08:18:10 +0000100 printf(" --no_delay_logging\n");
niklase@google.com470e71d2011-07-07 08:21:25 +0000101 printf("\n -aecm Echo control mobile\n");
bjornv@google.com238a0222011-07-15 14:51:52 +0000102 printf(" --aecm_echo_path_in_file FILE\n");
103 printf(" --aecm_echo_path_out_file FILE\n");
niklase@google.com470e71d2011-07-07 08:21:25 +0000104 printf("\n -agc Gain control\n");
105 printf(" --analog\n");
106 printf(" --adaptive_digital\n");
107 printf(" --fixed_digital\n");
108 printf(" --target_level LEVEL\n");
109 printf(" --compression_gain GAIN\n");
110 printf(" --limiter\n");
111 printf(" --no_limiter\n");
112 printf("\n -hpf High pass filter\n");
113 printf("\n -ns Noise suppression\n");
114 printf(" --ns_low\n");
115 printf(" --ns_moderate\n");
116 printf(" --ns_high\n");
117 printf(" --ns_very_high\n");
118 printf("\n -vad Voice activity detection\n");
ajm@google.com808e0e02011-08-03 21:08:51 +0000119 printf(" --vad_out_file FILE\n");
niklase@google.com470e71d2011-07-07 08:21:25 +0000120 printf("\n");
121 printf("Modifiers:\n");
andrew@webrtc.orgcb181212011-10-26 00:27:17 +0000122 printf(" --noasm Disable SSE optimization.\n");
andrew@webrtc.org4b13fc92011-11-09 19:27:11 +0000123 printf(" --delay DELAY Add DELAY ms to input value.\n");
andrew@webrtc.orgcb181212011-10-26 00:27:17 +0000124 printf(" --perf Measure performance.\n");
125 printf(" --quiet Suppress text output.\n");
126 printf(" --no_progress Suppress progress.\n");
127 printf(" --debug_file FILE Dump a debug recording.\n");
128 printf(" --version Print version information and exit.\n");
niklase@google.com470e71d2011-07-07 08:21:25 +0000129}
130
131// void function for gtest.
132void void_main(int argc, char* argv[]) {
133 if (argc > 1 && strcmp(argv[1], "--help") == 0) {
134 usage();
135 return;
136 }
137
138 if (argc < 2) {
139 printf("Did you mean to run without arguments?\n");
140 printf("Try `process_test --help' for more information.\n\n");
141 }
142
143 AudioProcessing* apm = AudioProcessing::Create(0);
144 ASSERT_TRUE(apm != NULL);
145
146 WebRtc_Word8 version[1024];
147 WebRtc_UWord32 version_bytes_remaining = sizeof(version);
148 WebRtc_UWord32 version_position = 0;
149
ajm@google.com808e0e02011-08-03 21:08:51 +0000150 const char* pb_filename = NULL;
niklase@google.com470e71d2011-07-07 08:21:25 +0000151 const char* far_filename = NULL;
152 const char* near_filename = NULL;
153 const char* out_filename = NULL;
154 const char* vad_out_filename = NULL;
bjornv@google.comc4b939c2011-07-13 08:09:56 +0000155 const char* aecm_echo_path_in_filename = NULL;
156 const char* aecm_echo_path_out_filename = NULL;
niklase@google.com470e71d2011-07-07 08:21:25 +0000157
158 int32_t sample_rate_hz = 16000;
159 int32_t device_sample_rate_hz = 16000;
160
161 int num_capture_input_channels = 1;
162 int num_capture_output_channels = 1;
163 int num_render_channels = 1;
164
165 int samples_per_channel = sample_rate_hz / 100;
166
167 bool simulating = false;
168 bool perf_testing = false;
169 bool verbose = true;
170 bool progress = true;
andrew@webrtc.org4b13fc92011-11-09 19:27:11 +0000171 int extra_delay_ms = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000172 //bool interleaved = true;
173
174 for (int i = 1; i < argc; i++) {
andrew@webrtc.org94c74132011-09-19 15:17:57 +0000175 if (strcmp(argv[i], "-pb") == 0) {
ajm@google.com808e0e02011-08-03 21:08:51 +0000176 i++;
177 ASSERT_LT(i, argc) << "Specify protobuf filename after -pb";
178 pb_filename = argv[i];
179
180 } else if (strcmp(argv[i], "-ir") == 0) {
niklase@google.com470e71d2011-07-07 08:21:25 +0000181 i++;
182 ASSERT_LT(i, argc) << "Specify filename after -ir";
183 far_filename = argv[i];
184 simulating = true;
185
186 } else if (strcmp(argv[i], "-i") == 0) {
187 i++;
188 ASSERT_LT(i, argc) << "Specify filename after -i";
189 near_filename = argv[i];
190 simulating = true;
191
192 } else if (strcmp(argv[i], "-o") == 0) {
193 i++;
194 ASSERT_LT(i, argc) << "Specify filename after -o";
195 out_filename = argv[i];
196
197 } else if (strcmp(argv[i], "-fs") == 0) {
198 i++;
199 ASSERT_LT(i, argc) << "Specify sample rate after -fs";
200 ASSERT_EQ(1, sscanf(argv[i], "%d", &sample_rate_hz));
201 samples_per_channel = sample_rate_hz / 100;
202
203 ASSERT_EQ(apm->kNoError,
204 apm->set_sample_rate_hz(sample_rate_hz));
205
206 } else if (strcmp(argv[i], "-ch") == 0) {
207 i++;
208 ASSERT_LT(i + 1, argc) << "Specify number of channels after -ch";
209 ASSERT_EQ(1, sscanf(argv[i], "%d", &num_capture_input_channels));
210 i++;
211 ASSERT_EQ(1, sscanf(argv[i], "%d", &num_capture_output_channels));
212
213 ASSERT_EQ(apm->kNoError,
214 apm->set_num_channels(num_capture_input_channels,
215 num_capture_output_channels));
216
217 } else if (strcmp(argv[i], "-rch") == 0) {
218 i++;
219 ASSERT_LT(i, argc) << "Specify number of channels after -rch";
220 ASSERT_EQ(1, sscanf(argv[i], "%d", &num_render_channels));
221
222 ASSERT_EQ(apm->kNoError,
223 apm->set_num_reverse_channels(num_render_channels));
224
225 } else if (strcmp(argv[i], "-aec") == 0) {
226 ASSERT_EQ(apm->kNoError, apm->echo_cancellation()->Enable(true));
bjornv@google.com1ba3dbe2011-10-03 08:18:10 +0000227 ASSERT_EQ(apm->kNoError,
228 apm->echo_cancellation()->enable_metrics(true));
229 ASSERT_EQ(apm->kNoError,
230 apm->echo_cancellation()->enable_delay_logging(true));
niklase@google.com470e71d2011-07-07 08:21:25 +0000231
niklase@google.com470e71d2011-07-07 08:21:25 +0000232 } else if (strcmp(argv[i], "--drift_compensation") == 0) {
233 ASSERT_EQ(apm->kNoError, apm->echo_cancellation()->Enable(true));
234 // TODO(ajm): this is enabled in the VQE test app by default. Investigate
235 // why it can give better performance despite passing zeros.
236 ASSERT_EQ(apm->kNoError,
237 apm->echo_cancellation()->enable_drift_compensation(true));
238 } else if (strcmp(argv[i], "--no_drift_compensation") == 0) {
239 ASSERT_EQ(apm->kNoError, apm->echo_cancellation()->Enable(true));
240 ASSERT_EQ(apm->kNoError,
241 apm->echo_cancellation()->enable_drift_compensation(false));
242
andrew@webrtc.org94c74132011-09-19 15:17:57 +0000243 } else if (strcmp(argv[i], "--no_echo_metrics") == 0) {
244 ASSERT_EQ(apm->kNoError, apm->echo_cancellation()->Enable(true));
245 ASSERT_EQ(apm->kNoError,
246 apm->echo_cancellation()->enable_metrics(false));
247
bjornv@google.com1ba3dbe2011-10-03 08:18:10 +0000248 } else if (strcmp(argv[i], "--no_delay_logging") == 0) {
249 ASSERT_EQ(apm->kNoError, apm->echo_cancellation()->Enable(true));
250 ASSERT_EQ(apm->kNoError,
251 apm->echo_cancellation()->enable_delay_logging(false));
252
niklase@google.com470e71d2011-07-07 08:21:25 +0000253 } else if (strcmp(argv[i], "-aecm") == 0) {
254 ASSERT_EQ(apm->kNoError, apm->echo_control_mobile()->Enable(true));
255
bjornv@google.comc4b939c2011-07-13 08:09:56 +0000256 } else if (strcmp(argv[i], "--aecm_echo_path_in_file") == 0) {
257 i++;
258 ASSERT_LT(i, argc) << "Specify filename after --aecm_echo_path_in_file";
259 aecm_echo_path_in_filename = argv[i];
260
261 } else if (strcmp(argv[i], "--aecm_echo_path_out_file") == 0) {
262 i++;
263 ASSERT_LT(i, argc) << "Specify filename after --aecm_echo_path_out_file";
264 aecm_echo_path_out_filename = argv[i];
265
niklase@google.com470e71d2011-07-07 08:21:25 +0000266 } else if (strcmp(argv[i], "-agc") == 0) {
267 ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
268
269 } else if (strcmp(argv[i], "--analog") == 0) {
270 ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
271 ASSERT_EQ(apm->kNoError,
272 apm->gain_control()->set_mode(GainControl::kAdaptiveAnalog));
273
274 } else if (strcmp(argv[i], "--adaptive_digital") == 0) {
275 ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
276 ASSERT_EQ(apm->kNoError,
277 apm->gain_control()->set_mode(GainControl::kAdaptiveDigital));
278
279 } else if (strcmp(argv[i], "--fixed_digital") == 0) {
280 ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
281 ASSERT_EQ(apm->kNoError,
282 apm->gain_control()->set_mode(GainControl::kFixedDigital));
283
284 } else if (strcmp(argv[i], "--target_level") == 0) {
285 i++;
286 int level;
287 ASSERT_EQ(1, sscanf(argv[i], "%d", &level));
288
289 ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
290 ASSERT_EQ(apm->kNoError,
291 apm->gain_control()->set_target_level_dbfs(level));
292
293 } else if (strcmp(argv[i], "--compression_gain") == 0) {
294 i++;
295 int gain;
296 ASSERT_EQ(1, sscanf(argv[i], "%d", &gain));
297
298 ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
299 ASSERT_EQ(apm->kNoError,
300 apm->gain_control()->set_compression_gain_db(gain));
301
302 } else if (strcmp(argv[i], "--limiter") == 0) {
303 ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
304 ASSERT_EQ(apm->kNoError,
305 apm->gain_control()->enable_limiter(true));
306
307 } else if (strcmp(argv[i], "--no_limiter") == 0) {
308 ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
309 ASSERT_EQ(apm->kNoError,
310 apm->gain_control()->enable_limiter(false));
311
312 } else if (strcmp(argv[i], "-hpf") == 0) {
313 ASSERT_EQ(apm->kNoError, apm->high_pass_filter()->Enable(true));
314
315 } else if (strcmp(argv[i], "-ns") == 0) {
316 ASSERT_EQ(apm->kNoError, apm->noise_suppression()->Enable(true));
317
318 } else if (strcmp(argv[i], "--ns_low") == 0) {
319 ASSERT_EQ(apm->kNoError, apm->noise_suppression()->Enable(true));
320 ASSERT_EQ(apm->kNoError,
321 apm->noise_suppression()->set_level(NoiseSuppression::kLow));
322
323 } else if (strcmp(argv[i], "--ns_moderate") == 0) {
324 ASSERT_EQ(apm->kNoError, apm->noise_suppression()->Enable(true));
325 ASSERT_EQ(apm->kNoError,
326 apm->noise_suppression()->set_level(NoiseSuppression::kModerate));
327
328 } else if (strcmp(argv[i], "--ns_high") == 0) {
329 ASSERT_EQ(apm->kNoError, apm->noise_suppression()->Enable(true));
330 ASSERT_EQ(apm->kNoError,
331 apm->noise_suppression()->set_level(NoiseSuppression::kHigh));
332
333 } else if (strcmp(argv[i], "--ns_very_high") == 0) {
334 ASSERT_EQ(apm->kNoError, apm->noise_suppression()->Enable(true));
335 ASSERT_EQ(apm->kNoError,
336 apm->noise_suppression()->set_level(NoiseSuppression::kVeryHigh));
337
338 } else if (strcmp(argv[i], "-vad") == 0) {
339 ASSERT_EQ(apm->kNoError, apm->voice_detection()->Enable(true));
340
341 } else if (strcmp(argv[i], "--vad_out_file") == 0) {
342 i++;
343 ASSERT_LT(i, argc) << "Specify filename after --vad_out_file";
344 vad_out_filename = argv[i];
345
andrew@webrtc.org94c74132011-09-19 15:17:57 +0000346 } else if (strcmp(argv[i], "--noasm") == 0) {
347 WebRtc_GetCPUInfo = WebRtc_GetCPUInfoNoASM;
348 // We need to reinitialize here if components have already been enabled.
349 ASSERT_EQ(apm->kNoError, apm->Initialize());
350
andrew@webrtc.org4b13fc92011-11-09 19:27:11 +0000351 } else if (strcmp(argv[i], "--delay") == 0) {
352 i++;
353 ASSERT_EQ(1, sscanf(argv[i], "%d", &extra_delay_ms));
354
niklase@google.com470e71d2011-07-07 08:21:25 +0000355 } else if (strcmp(argv[i], "--perf") == 0) {
356 perf_testing = true;
357
358 } else if (strcmp(argv[i], "--quiet") == 0) {
359 verbose = false;
360 progress = false;
361
362 } else if (strcmp(argv[i], "--no_progress") == 0) {
363 progress = false;
364
365 } else if (strcmp(argv[i], "--version") == 0) {
366 ASSERT_EQ(apm->kNoError, apm->Version(version,
367 version_bytes_remaining,
368 version_position));
369 printf("%s\n", version);
370 return;
371
andrew@webrtc.orgcb181212011-10-26 00:27:17 +0000372 } else if (strcmp(argv[i], "--debug_file") == 0) {
ajm@google.com808e0e02011-08-03 21:08:51 +0000373 i++;
andrew@webrtc.orgcb181212011-10-26 00:27:17 +0000374 ASSERT_LT(i, argc) << "Specify filename after --debug_file";
ajm@google.com808e0e02011-08-03 21:08:51 +0000375 ASSERT_EQ(apm->kNoError, apm->StartDebugRecording(argv[i]));
niklase@google.com470e71d2011-07-07 08:21:25 +0000376 } else {
377 FAIL() << "Unrecognized argument " << argv[i];
378 }
379 }
ajm@google.com808e0e02011-08-03 21:08:51 +0000380 // If we're reading a protobuf file, ensure a simulation hasn't also
381 // been requested (which makes no sense...)
382 ASSERT_FALSE(pb_filename && simulating);
niklase@google.com470e71d2011-07-07 08:21:25 +0000383
384 if (verbose) {
385 printf("Sample rate: %d Hz\n", sample_rate_hz);
386 printf("Primary channels: %d (in), %d (out)\n",
387 num_capture_input_channels,
388 num_capture_output_channels);
389 printf("Reverse channels: %d \n", num_render_channels);
390 }
391
392 const char far_file_default[] = "apm_far.pcm";
393 const char near_file_default[] = "apm_near.pcm";
394 const char out_file_default[] = "out.pcm";
395 const char event_filename[] = "apm_event.dat";
396 const char delay_filename[] = "apm_delay.dat";
397 const char drift_filename[] = "apm_drift.dat";
398 const char vad_file_default[] = "vad_out.dat";
399
400 if (!simulating) {
401 far_filename = far_file_default;
402 near_filename = near_file_default;
403 }
404
ajm@google.com808e0e02011-08-03 21:08:51 +0000405 if (!out_filename) {
niklase@google.com470e71d2011-07-07 08:21:25 +0000406 out_filename = out_file_default;
407 }
408
ajm@google.com808e0e02011-08-03 21:08:51 +0000409 if (!vad_out_filename) {
niklase@google.com470e71d2011-07-07 08:21:25 +0000410 vad_out_filename = vad_file_default;
411 }
412
ajm@google.com808e0e02011-08-03 21:08:51 +0000413 FILE* pb_file = NULL;
niklase@google.com470e71d2011-07-07 08:21:25 +0000414 FILE* far_file = NULL;
415 FILE* near_file = NULL;
416 FILE* out_file = NULL;
417 FILE* event_file = NULL;
418 FILE* delay_file = NULL;
419 FILE* drift_file = NULL;
420 FILE* vad_out_file = NULL;
bjornv@google.comc4b939c2011-07-13 08:09:56 +0000421 FILE* aecm_echo_path_in_file = NULL;
422 FILE* aecm_echo_path_out_file = NULL;
niklase@google.com470e71d2011-07-07 08:21:25 +0000423
ajm@google.com808e0e02011-08-03 21:08:51 +0000424 if (pb_filename) {
425 pb_file = fopen(pb_filename, "rb");
426 ASSERT_TRUE(NULL != pb_file) << "Unable to open protobuf file "
427 << pb_filename;
428 } else {
429 if (far_filename) {
430 far_file = fopen(far_filename, "rb");
431 ASSERT_TRUE(NULL != far_file) << "Unable to open far-end audio file "
432 << far_filename;
433 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000434
ajm@google.com808e0e02011-08-03 21:08:51 +0000435 near_file = fopen(near_filename, "rb");
436 ASSERT_TRUE(NULL != near_file) << "Unable to open near-end audio file "
437 << near_filename;
438 if (!simulating) {
439 event_file = fopen(event_filename, "rb");
440 ASSERT_TRUE(NULL != event_file) << "Unable to open event file "
441 << event_filename;
442
443 delay_file = fopen(delay_filename, "rb");
444 ASSERT_TRUE(NULL != delay_file) << "Unable to open buffer file "
445 << delay_filename;
446
447 drift_file = fopen(drift_filename, "rb");
448 ASSERT_TRUE(NULL != drift_file) << "Unable to open drift file "
449 << drift_filename;
450 }
451 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000452
453 out_file = fopen(out_filename, "wb");
454 ASSERT_TRUE(NULL != out_file) << "Unable to open output audio file "
455 << out_filename;
456
ajm@google.com808e0e02011-08-03 21:08:51 +0000457 int near_size_samples = 0;
458 if (pb_file) {
459 struct stat st;
460 stat(pb_filename, &st);
461 // Crude estimate, but should be good enough.
462 near_size_samples = st.st_size / 3 / sizeof(int16_t);
463 } else {
464 struct stat st;
465 stat(near_filename, &st);
466 near_size_samples = st.st_size / sizeof(int16_t);
niklase@google.com470e71d2011-07-07 08:21:25 +0000467 }
468
469 if (apm->voice_detection()->is_enabled()) {
470 vad_out_file = fopen(vad_out_filename, "wb");
471 ASSERT_TRUE(NULL != vad_out_file) << "Unable to open VAD output file "
472 << vad_out_file;
473 }
474
bjornv@google.comc4b939c2011-07-13 08:09:56 +0000475 if (aecm_echo_path_in_filename != NULL) {
476 aecm_echo_path_in_file = fopen(aecm_echo_path_in_filename, "rb");
477 ASSERT_TRUE(NULL != aecm_echo_path_in_file) << "Unable to open file "
478 << aecm_echo_path_in_filename;
479
ajm@google.com22e65152011-07-18 18:03:01 +0000480 const size_t path_size =
481 apm->echo_control_mobile()->echo_path_size_bytes();
andrew@webrtc.org3119ecf2011-11-01 17:00:18 +0000482 scoped_array<char> echo_path(new char[path_size]);
483 ASSERT_EQ(path_size, fread(echo_path.get(),
484 sizeof(char),
bjornv@google.comc4b939c2011-07-13 08:09:56 +0000485 path_size,
486 aecm_echo_path_in_file));
487 EXPECT_EQ(apm->kNoError,
andrew@webrtc.org3119ecf2011-11-01 17:00:18 +0000488 apm->echo_control_mobile()->SetEchoPath(echo_path.get(),
489 path_size));
bjornv@google.comc4b939c2011-07-13 08:09:56 +0000490 fclose(aecm_echo_path_in_file);
491 aecm_echo_path_in_file = NULL;
492 }
493
494 if (aecm_echo_path_out_filename != NULL) {
495 aecm_echo_path_out_file = fopen(aecm_echo_path_out_filename, "wb");
496 ASSERT_TRUE(NULL != aecm_echo_path_out_file) << "Unable to open file "
497 << aecm_echo_path_out_filename;
bjornv@google.comc4b939c2011-07-13 08:09:56 +0000498 }
499
niklase@google.com470e71d2011-07-07 08:21:25 +0000500 size_t read_count = 0;
501 int reverse_count = 0;
502 int primary_count = 0;
503 int near_read_samples = 0;
504 TickInterval acc_ticks;
505
506 AudioFrame far_frame;
507 far_frame._frequencyInHz = sample_rate_hz;
508
509 AudioFrame near_frame;
510 near_frame._frequencyInHz = sample_rate_hz;
511
512 int delay_ms = 0;
513 int drift_samples = 0;
514 int capture_level = 127;
515 int8_t stream_has_voice = 0;
516
517 TickTime t0 = TickTime::Now();
518 TickTime t1 = t0;
519 WebRtc_Word64 max_time_us = 0;
520 WebRtc_Word64 max_time_reverse_us = 0;
521 WebRtc_Word64 min_time_us = 1e6;
522 WebRtc_Word64 min_time_reverse_us = 1e6;
523
ajm@google.com808e0e02011-08-03 21:08:51 +0000524 // TODO(ajm): Ideally we would refactor this block into separate functions,
525 // but for now we want to share the variables.
526 if (pb_file) {
527 Event event_msg;
528 while (ReadMessageFromFile(pb_file, &event_msg)) {
529 std::ostringstream trace_stream;
530 trace_stream << "Processed frames: " << reverse_count << " (reverse), "
531 << primary_count << " (primary)";
532 SCOPED_TRACE(trace_stream.str());
niklase@google.com470e71d2011-07-07 08:21:25 +0000533
ajm@google.com808e0e02011-08-03 21:08:51 +0000534 if (event_msg.type() == Event::INIT) {
535 ASSERT_TRUE(event_msg.has_init());
536 const Init msg = event_msg.init();
niklase@google.com470e71d2011-07-07 08:21:25 +0000537
ajm@google.com808e0e02011-08-03 21:08:51 +0000538 ASSERT_TRUE(msg.has_sample_rate());
539 ASSERT_EQ(apm->kNoError,
540 apm->set_sample_rate_hz(msg.sample_rate()));
541
542 ASSERT_TRUE(msg.has_device_sample_rate());
543 ASSERT_EQ(apm->kNoError,
544 apm->echo_cancellation()->set_device_sample_rate_hz(
545 msg.device_sample_rate()));
546
547 ASSERT_TRUE(msg.has_num_input_channels());
548 ASSERT_TRUE(msg.has_num_output_channels());
549 ASSERT_EQ(apm->kNoError,
550 apm->set_num_channels(msg.num_input_channels(),
551 msg.num_output_channels()));
552
553 ASSERT_TRUE(msg.has_num_reverse_channels());
554 ASSERT_EQ(apm->kNoError,
555 apm->set_num_reverse_channels(msg.num_reverse_channels()));
556
557 samples_per_channel = msg.sample_rate() / 100;
558 far_frame._frequencyInHz = msg.sample_rate();
559 far_frame._payloadDataLengthInSamples =
560 msg.num_reverse_channels() * samples_per_channel;
561 near_frame._frequencyInHz = msg.sample_rate();
562
563 if (verbose) {
564 printf("Init at frame: %d (primary), %d (reverse)\n",
565 primary_count, reverse_count);
566 printf(" Sample rate: %d Hz\n", sample_rate_hz);
567 }
568
569 } else if (event_msg.type() == Event::REVERSE_STREAM) {
570 ASSERT_TRUE(event_msg.has_reverse_stream());
571 const ReverseStream msg = event_msg.reverse_stream();
572 reverse_count++;
573
574 ASSERT_TRUE(msg.has_data());
575 ASSERT_EQ(sizeof(int16_t) * far_frame._payloadDataLengthInSamples,
576 msg.data().size());
577 memcpy(far_frame._payloadData, msg.data().data(), msg.data().size());
578
579 if (perf_testing) {
580 t0 = TickTime::Now();
581 }
582
583 ASSERT_EQ(apm->kNoError,
584 apm->AnalyzeReverseStream(&far_frame));
585
586 if (perf_testing) {
587 t1 = TickTime::Now();
588 TickInterval tick_diff = t1 - t0;
589 acc_ticks += tick_diff;
590 if (tick_diff.Microseconds() > max_time_reverse_us) {
591 max_time_reverse_us = tick_diff.Microseconds();
592 }
593 if (tick_diff.Microseconds() < min_time_reverse_us) {
594 min_time_reverse_us = tick_diff.Microseconds();
595 }
596 }
597
598 } else if (event_msg.type() == Event::STREAM) {
599 ASSERT_TRUE(event_msg.has_stream());
600 const Stream msg = event_msg.stream();
601 primary_count++;
602
603 near_frame._audioChannel = apm->num_input_channels();
604 near_frame._payloadDataLengthInSamples =
605 apm->num_input_channels() * samples_per_channel;
606
607 ASSERT_TRUE(msg.has_input_data());
608 ASSERT_EQ(sizeof(int16_t) * near_frame._payloadDataLengthInSamples,
609 msg.input_data().size());
610 memcpy(near_frame._payloadData,
611 msg.input_data().data(),
612 msg.input_data().size());
613
614 near_read_samples += near_frame._payloadDataLengthInSamples;
615 if (progress && primary_count % 100 == 0) {
616 printf("%.0f%% complete\r",
617 (near_read_samples * 100.0) / near_size_samples);
618 fflush(stdout);
619 }
620
621 if (perf_testing) {
622 t0 = TickTime::Now();
623 }
624
625 ASSERT_EQ(apm->kNoError,
626 apm->gain_control()->set_stream_analog_level(msg.level()));
627 ASSERT_EQ(apm->kNoError,
andrew@webrtc.org4b13fc92011-11-09 19:27:11 +0000628 apm->set_stream_delay_ms(msg.delay() + extra_delay_ms));
ajm@google.com808e0e02011-08-03 21:08:51 +0000629 ASSERT_EQ(apm->kNoError,
630 apm->echo_cancellation()->set_stream_drift_samples(msg.drift()));
631
632 int err = apm->ProcessStream(&near_frame);
633 if (err == apm->kBadStreamParameterWarning) {
634 printf("Bad parameter warning. %s\n", trace_stream.str().c_str());
635 }
636 ASSERT_TRUE(err == apm->kNoError ||
637 err == apm->kBadStreamParameterWarning);
638
639 capture_level = apm->gain_control()->stream_analog_level();
640
641 stream_has_voice =
642 static_cast<int8_t>(apm->voice_detection()->stream_has_voice());
643 if (vad_out_file != NULL) {
644 ASSERT_EQ(1u, fwrite(&stream_has_voice,
645 sizeof(stream_has_voice),
646 1,
647 vad_out_file));
648 }
649
650 if (apm->gain_control()->mode() != GainControl::kAdaptiveAnalog) {
651 ASSERT_EQ(msg.level(), capture_level);
652 }
653
654 if (perf_testing) {
655 t1 = TickTime::Now();
656 TickInterval tick_diff = t1 - t0;
657 acc_ticks += tick_diff;
658 if (tick_diff.Microseconds() > max_time_us) {
659 max_time_us = tick_diff.Microseconds();
660 }
661 if (tick_diff.Microseconds() < min_time_us) {
662 min_time_us = tick_diff.Microseconds();
663 }
664 }
665
666 ASSERT_EQ(near_frame._payloadDataLengthInSamples,
667 fwrite(near_frame._payloadData,
668 sizeof(int16_t),
669 near_frame._payloadDataLengthInSamples,
670 out_file));
671 }
672 }
673
674 ASSERT_TRUE(feof(pb_file));
ajm@google.com808e0e02011-08-03 21:08:51 +0000675
676 } else {
bjornv@google.coma2c6ea02011-09-27 08:04:45 +0000677 enum Events {
678 kInitializeEvent,
679 kRenderEvent,
680 kCaptureEvent,
681 kResetEventDeprecated
682 };
683 int16_t event = 0;
ajm@google.com808e0e02011-08-03 21:08:51 +0000684 while (simulating || feof(event_file) == 0) {
685 std::ostringstream trace_stream;
686 trace_stream << "Processed frames: " << reverse_count << " (reverse), "
687 << primary_count << " (primary)";
688 SCOPED_TRACE(trace_stream.str());
689
690 if (simulating) {
691 if (far_file == NULL) {
niklase@google.com470e71d2011-07-07 08:21:25 +0000692 event = kCaptureEvent;
693 } else {
ajm@google.com808e0e02011-08-03 21:08:51 +0000694 if (event == kRenderEvent) {
695 event = kCaptureEvent;
696 } else {
697 event = kRenderEvent;
698 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000699 }
700 } else {
ajm@google.com808e0e02011-08-03 21:08:51 +0000701 read_count = fread(&event, sizeof(event), 1, event_file);
702 if (read_count != 1) {
703 break;
niklase@google.com470e71d2011-07-07 08:21:25 +0000704 }
705 }
706
ajm@google.com808e0e02011-08-03 21:08:51 +0000707 if (event == kInitializeEvent || event == kResetEventDeprecated) {
708 ASSERT_EQ(1u,
709 fread(&sample_rate_hz, sizeof(sample_rate_hz), 1, event_file));
710 samples_per_channel = sample_rate_hz / 100;
niklase@google.com470e71d2011-07-07 08:21:25 +0000711
ajm@google.com808e0e02011-08-03 21:08:51 +0000712 ASSERT_EQ(1u,
713 fread(&device_sample_rate_hz,
714 sizeof(device_sample_rate_hz),
715 1,
716 event_file));
717
718 ASSERT_EQ(apm->kNoError,
719 apm->set_sample_rate_hz(sample_rate_hz));
720
721 ASSERT_EQ(apm->kNoError,
722 apm->echo_cancellation()->set_device_sample_rate_hz(
723 device_sample_rate_hz));
724
725 far_frame._frequencyInHz = sample_rate_hz;
726 near_frame._frequencyInHz = sample_rate_hz;
727
728 if (verbose) {
729 printf("Init at frame: %d (primary), %d (reverse)\n",
730 primary_count, reverse_count);
731 printf(" Sample rate: %d Hz\n", sample_rate_hz);
732 }
733
734 } else if (event == kRenderEvent) {
735 reverse_count++;
736 far_frame._audioChannel = num_render_channels;
737 far_frame._payloadDataLengthInSamples =
738 num_render_channels * samples_per_channel;
739
740 read_count = fread(far_frame._payloadData,
741 sizeof(WebRtc_Word16),
742 far_frame._payloadDataLengthInSamples,
743 far_file);
744
745 if (simulating) {
746 if (read_count != far_frame._payloadDataLengthInSamples) {
andrew@webrtc.org94c74132011-09-19 15:17:57 +0000747 // Read an equal amount from the near file to avoid errors due to
748 // not reaching end-of-file.
749 EXPECT_EQ(0, fseek(near_file, read_count * sizeof(WebRtc_Word16),
750 SEEK_CUR));
ajm@google.com808e0e02011-08-03 21:08:51 +0000751 break; // This is expected.
752 }
753 } else {
754 ASSERT_EQ(read_count,
755 far_frame._payloadDataLengthInSamples);
756 }
757
758 if (perf_testing) {
759 t0 = TickTime::Now();
760 }
761
762 ASSERT_EQ(apm->kNoError,
763 apm->AnalyzeReverseStream(&far_frame));
764
765 if (perf_testing) {
766 t1 = TickTime::Now();
767 TickInterval tick_diff = t1 - t0;
768 acc_ticks += tick_diff;
769 if (tick_diff.Microseconds() > max_time_reverse_us) {
770 max_time_reverse_us = tick_diff.Microseconds();
771 }
772 if (tick_diff.Microseconds() < min_time_reverse_us) {
773 min_time_reverse_us = tick_diff.Microseconds();
774 }
775 }
776
777 } else if (event == kCaptureEvent) {
778 primary_count++;
779 near_frame._audioChannel = num_capture_input_channels;
780 near_frame._payloadDataLengthInSamples =
781 num_capture_input_channels * samples_per_channel;
782
783 read_count = fread(near_frame._payloadData,
784 sizeof(WebRtc_Word16),
785 near_frame._payloadDataLengthInSamples,
786 near_file);
787
788 near_read_samples += read_count;
789 if (progress && primary_count % 100 == 0) {
790 printf("%.0f%% complete\r",
791 (near_read_samples * 100.0) / near_size_samples);
792 fflush(stdout);
793 }
794 if (simulating) {
795 if (read_count != near_frame._payloadDataLengthInSamples) {
796 break; // This is expected.
797 }
798
799 delay_ms = 0;
800 drift_samples = 0;
801 } else {
802 ASSERT_EQ(read_count,
803 near_frame._payloadDataLengthInSamples);
804
805 // TODO(ajm): sizeof(delay_ms) for current files?
806 ASSERT_EQ(1u,
807 fread(&delay_ms, 2, 1, delay_file));
808 ASSERT_EQ(1u,
809 fread(&drift_samples, sizeof(drift_samples), 1, drift_file));
810 }
811
812 if (perf_testing) {
813 t0 = TickTime::Now();
814 }
815
816 // TODO(ajm): fake an analog gain while simulating.
817
818 int capture_level_in = capture_level;
819 ASSERT_EQ(apm->kNoError,
820 apm->gain_control()->set_stream_analog_level(capture_level));
821 ASSERT_EQ(apm->kNoError,
andrew@webrtc.org4b13fc92011-11-09 19:27:11 +0000822 apm->set_stream_delay_ms(delay_ms + extra_delay_ms));
ajm@google.com808e0e02011-08-03 21:08:51 +0000823 ASSERT_EQ(apm->kNoError,
824 apm->echo_cancellation()->set_stream_drift_samples(drift_samples));
825
826 int err = apm->ProcessStream(&near_frame);
827 if (err == apm->kBadStreamParameterWarning) {
828 printf("Bad parameter warning. %s\n", trace_stream.str().c_str());
829 }
830 ASSERT_TRUE(err == apm->kNoError ||
831 err == apm->kBadStreamParameterWarning);
832
833 capture_level = apm->gain_control()->stream_analog_level();
834
835 stream_has_voice =
836 static_cast<int8_t>(apm->voice_detection()->stream_has_voice());
837 if (vad_out_file != NULL) {
838 ASSERT_EQ(1u, fwrite(&stream_has_voice,
839 sizeof(stream_has_voice),
840 1,
841 vad_out_file));
842 }
843
844 if (apm->gain_control()->mode() != GainControl::kAdaptiveAnalog) {
845 ASSERT_EQ(capture_level_in, capture_level);
846 }
847
848 if (perf_testing) {
849 t1 = TickTime::Now();
850 TickInterval tick_diff = t1 - t0;
851 acc_ticks += tick_diff;
852 if (tick_diff.Microseconds() > max_time_us) {
853 max_time_us = tick_diff.Microseconds();
854 }
855 if (tick_diff.Microseconds() < min_time_us) {
856 min_time_us = tick_diff.Microseconds();
857 }
858 }
859
860 ASSERT_EQ(near_frame._payloadDataLengthInSamples,
861 fwrite(near_frame._payloadData,
niklase@google.com470e71d2011-07-07 08:21:25 +0000862 sizeof(WebRtc_Word16),
863 near_frame._payloadDataLengthInSamples,
ajm@google.com808e0e02011-08-03 21:08:51 +0000864 out_file));
niklase@google.com470e71d2011-07-07 08:21:25 +0000865 }
ajm@google.com808e0e02011-08-03 21:08:51 +0000866 else {
867 FAIL() << "Event " << event << " is unrecognized";
niklase@google.com470e71d2011-07-07 08:21:25 +0000868 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000869 }
870 }
andrew@webrtc.org94c74132011-09-19 15:17:57 +0000871 printf("100%% complete\r");
niklase@google.com470e71d2011-07-07 08:21:25 +0000872
bjornv@google.comc4b939c2011-07-13 08:09:56 +0000873 if (aecm_echo_path_out_file != NULL) {
ajm@google.com22e65152011-07-18 18:03:01 +0000874 const size_t path_size =
875 apm->echo_control_mobile()->echo_path_size_bytes();
andrew@webrtc.org3119ecf2011-11-01 17:00:18 +0000876 scoped_array<char> echo_path(new char[path_size]);
877 apm->echo_control_mobile()->GetEchoPath(echo_path.get(), path_size);
878 ASSERT_EQ(path_size, fwrite(echo_path.get(),
879 sizeof(char),
bjornv@google.comc4b939c2011-07-13 08:09:56 +0000880 path_size,
881 aecm_echo_path_out_file));
882 fclose(aecm_echo_path_out_file);
883 aecm_echo_path_out_file = NULL;
884 }
885
niklase@google.com470e71d2011-07-07 08:21:25 +0000886 if (verbose) {
887 printf("\nProcessed frames: %d (primary), %d (reverse)\n",
888 primary_count, reverse_count);
andrew@webrtc.org94c74132011-09-19 15:17:57 +0000889
890 if (apm->echo_cancellation()->are_metrics_enabled()) {
891 EchoCancellation::Metrics metrics;
892 apm->echo_cancellation()->GetMetrics(&metrics);
893 printf("\n--Echo metrics--\n");
894 printf("(avg, max, min)\n");
895 printf("ERL: ");
896 PrintStat(metrics.echo_return_loss);
897 printf("ERLE: ");
898 PrintStat(metrics.echo_return_loss_enhancement);
899 printf("ANLP: ");
900 PrintStat(metrics.a_nlp);
901 }
bjornv@google.com1ba3dbe2011-10-03 08:18:10 +0000902 if (apm->echo_cancellation()->is_delay_logging_enabled()) {
903 int median = 0;
904 int std = 0;
905 apm->echo_cancellation()->GetDelayMetrics(&median, &std);
906 printf("\n--Delay metrics--\n");
907 printf("Median: %3d\n", median);
908 printf("Standard deviation: %3d\n", std);
909 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000910 }
911
ajm@google.com808e0e02011-08-03 21:08:51 +0000912 if (!pb_file) {
913 int8_t temp_int8;
914 if (far_file) {
915 read_count = fread(&temp_int8, sizeof(temp_int8), 1, far_file);
916 EXPECT_NE(0, feof(far_file)) << "Far-end file not fully processed";
917 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000918
ajm@google.com808e0e02011-08-03 21:08:51 +0000919 read_count = fread(&temp_int8, sizeof(temp_int8), 1, near_file);
920 EXPECT_NE(0, feof(near_file)) << "Near-end file not fully processed";
921
922 if (!simulating) {
923 read_count = fread(&temp_int8, sizeof(temp_int8), 1, event_file);
924 EXPECT_NE(0, feof(event_file)) << "Event file not fully processed";
925 read_count = fread(&temp_int8, sizeof(temp_int8), 1, delay_file);
926 EXPECT_NE(0, feof(delay_file)) << "Delay file not fully processed";
927 read_count = fread(&temp_int8, sizeof(temp_int8), 1, drift_file);
928 EXPECT_NE(0, feof(drift_file)) << "Drift file not fully processed";
929 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000930 }
931
932 if (perf_testing) {
933 if (primary_count > 0) {
934 WebRtc_Word64 exec_time = acc_ticks.Milliseconds();
935 printf("\nTotal time: %.3f s, file time: %.2f s\n",
936 exec_time * 0.001, primary_count * 0.01);
937 printf("Time per frame: %.3f ms (average), %.3f ms (max),"
938 " %.3f ms (min)\n",
939 (exec_time * 1.0) / primary_count,
940 (max_time_us + max_time_reverse_us) / 1000.0,
941 (min_time_us + min_time_reverse_us) / 1000.0);
942 } else {
943 printf("Warning: no capture frames\n");
944 }
945 }
946
947 AudioProcessing::Destroy(apm);
948 apm = NULL;
949}
ajm@google.com808e0e02011-08-03 21:08:51 +0000950} // namespace
niklase@google.com470e71d2011-07-07 08:21:25 +0000951
952int main(int argc, char* argv[])
953{
954 void_main(argc, argv);
955
andrew@webrtc.org64235092011-08-19 21:22:08 +0000956 // Optional, but removes memory leak noise from Valgrind.
957 google::protobuf::ShutdownProtobufLibrary();
niklase@google.com470e71d2011-07-07 08:21:25 +0000958 return 0;
959}