Mercurial > hg > audiostuff
comparison spandsp-0.0.6pre17/tests/echo_tests.c @ 4:26cd8f1ef0b1
import spandsp-0.0.6pre17
author | Peter Meerwald <pmeerw@cosy.sbg.ac.at> |
---|---|
date | Fri, 25 Jun 2010 15:50:58 +0200 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
3:c6c5a16ce2f2 | 4:26cd8f1ef0b1 |
---|---|
1 /* | |
2 * SpanDSP - a series of DSP components for telephony | |
3 * | |
4 * echo_tests.c | |
5 * | |
6 * Written by Steve Underwood <steveu@coppice.org> | |
7 * | |
8 * Copyright (C) 2001 Steve Underwood | |
9 * | |
10 * Based on a bit from here, a bit from there, eye of toad, | |
11 * ear of bat, etc - plus, of course, my own 2 cents. | |
12 * | |
13 * All rights reserved. | |
14 * | |
15 * This program is free software; you can redistribute it and/or modify | |
16 * it under the terms of the GNU General Public License version 2, as | |
17 * published by the Free Software Foundation. | |
18 * | |
19 * This program is distributed in the hope that it will be useful, | |
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
22 * GNU General Public License for more details. | |
23 * | |
24 * You should have received a copy of the GNU General Public License | |
25 * along with this program; if not, write to the Free Software | |
26 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
27 * | |
28 * $Id: echo_tests.c,v 1.39 2009/05/30 15:23:13 steveu Exp $ | |
29 */ | |
30 | |
31 /*! \page echo_can_tests_page Line echo cancellation for voice tests | |
32 | |
33 \section echo_can_tests_page_sec_1 What does it do? | |
34 The echo cancellation tests test the echo cancellor against the G.168 spec. Not | |
35 all the tests in G.168 are fully implemented at this time. | |
36 | |
37 \section echo_can_tests_page_sec_2 How does it work? | |
38 | |
39 \section echo_can_tests_page_sec_2 How do I use it? | |
40 | |
41 */ | |
42 | |
43 #if defined(HAVE_CONFIG_H) | |
44 #include "config.h" | |
45 #endif | |
46 | |
47 #if defined(HAVE_FL_FL_H) && defined(HAVE_FL_FL_CARTESIAN_H) && defined(HAVE_FL_FL_AUDIO_METER_H) | |
48 #define ENABLE_GUI | |
49 #endif | |
50 | |
51 #include <stdlib.h> | |
52 #include <stdio.h> | |
53 #include <unistd.h> | |
54 #include <string.h> | |
55 #include <strings.h> | |
56 #include <assert.h> | |
57 #include <sndfile.h> | |
58 | |
59 #define GEN_CONST | |
60 #include <math.h> | |
61 | |
62 //#if defined(WITH_SPANDSP_INTERNALS) | |
63 #define SPANDSP_EXPOSE_INTERNAL_STRUCTURES | |
64 //#endif | |
65 | |
66 #include "spandsp.h" | |
67 #include "spandsp/g168models.h" | |
68 #include "spandsp-sim.h" | |
69 #if defined(ENABLE_GUI) | |
70 #include "echo_monitor.h" | |
71 #endif | |
72 | |
73 #if !defined(NULL) | |
74 #define NULL (void *) 0 | |
75 #endif | |
76 | |
77 #define TEST_EC_TAPS 256 | |
78 | |
79 #define RESIDUE_FILE_NAME "residue_sound.wav" | |
80 | |
81 /* | |
82 The key signal names, as defined in G.168 | |
83 | |
84 +--------------+ +------------+ | |
85 | | Sin | | | |
86 Sgen -->--| Echoey |--->---| Echo |-->-- Sout | |
87 | | | | | |
88 | World | Rout | Canceller | | |
89 --<--| |---<---| |--<-- Rin | |
90 | | | | | |
91 +--------------+ +------------+ | |
92 | |
93 Echoey world model. Munge means linear->PCM->linear distortion. | |
94 +-------------------------+ | |
95 | | Sin | |
96 Sgen -->--|-->munge--->sum-->munge--|--->--- | |
97 | +--> | | |
98 | FIR | Rout | |
99 --<--|--------+--------munge<--|---<--- | |
100 | | | |
101 +-------------------------+ | |
102 */ | |
103 | |
104 typedef struct | |
105 { | |
106 const char *name; | |
107 int max; | |
108 int cur; | |
109 float gain; | |
110 SNDFILE *handle; | |
111 int16_t signal[SAMPLE_RATE]; | |
112 } signal_source_t; | |
113 | |
114 /* Level measurement device, specified in G.168 section 6.4.1.2.1 */ | |
115 typedef struct | |
116 { | |
117 int type; | |
118 fir_float_state_t *fir; | |
119 float history[35*8]; | |
120 int pos; | |
121 float factor; | |
122 float power; | |
123 float peak; | |
124 } level_measurement_device_t; | |
125 | |
126 typedef struct | |
127 { | |
128 int model_no; | |
129 float erl; | |
130 fir32_state_t impulse; | |
131 float gain; | |
132 int munging_codec; | |
133 } channel_model_state_t; | |
134 | |
135 channel_model_state_t chan_model; | |
136 | |
137 signal_source_t local_css; | |
138 signal_source_t far_css; | |
139 awgn_state_t local_noise_source; | |
140 awgn_state_t far_noise_source; | |
141 | |
142 SNDFILE *residue_handle; | |
143 int16_t residue_sound[SAMPLE_RATE]; | |
144 int residue_cur = 0; | |
145 | |
146 level_measurement_device_t *power_meter_1; | |
147 level_measurement_device_t *power_meter_2; | |
148 | |
149 int line_model_no; | |
150 int supp_line_model_no; | |
151 int munger; | |
152 | |
153 level_measurement_device_t *rin_power_meter; /* Also known as Lrin */ | |
154 level_measurement_device_t *rout_power_meter; | |
155 level_measurement_device_t *sin_power_meter; | |
156 level_measurement_device_t *sout_power_meter; /* Also known as Lret (pre NLP value is known as Lres) */ | |
157 level_measurement_device_t *sgen_power_meter; | |
158 | |
159 #define RESULT_CHANNELS 7 | |
160 SNDFILE *result_handle; | |
161 int16_t result_sound[SAMPLE_RATE*RESULT_CHANNELS]; | |
162 int result_cur; | |
163 | |
164 const char *test_name; | |
165 int quiet; | |
166 int use_gui; | |
167 | |
168 float erl; | |
169 | |
170 /* Dump estimated echo response */ | |
171 static void dump_ec_state(echo_can_state_t *ctx) | |
172 { | |
173 int i; | |
174 FILE *f; | |
175 | |
176 if ((f = fopen("echo_tests_state.txt", "wt")) == NULL) | |
177 return; | |
178 for (i = 0; i < TEST_EC_TAPS; i++) | |
179 fprintf(f, "%f\n", (float) ctx->fir_taps16[0][i]/(1 << 15)); | |
180 fclose(f); | |
181 } | |
182 /*- End of function --------------------------------------------------------*/ | |
183 | |
184 static inline void put_residue(int16_t amp) | |
185 { | |
186 int outframes; | |
187 | |
188 residue_sound[residue_cur++] = amp; | |
189 if (residue_cur >= SAMPLE_RATE) | |
190 { | |
191 outframes = sf_writef_short(residue_handle, residue_sound, residue_cur); | |
192 if (outframes != residue_cur) | |
193 { | |
194 fprintf(stderr, " Error writing residue sound\n"); | |
195 exit(2); | |
196 } | |
197 residue_cur = 0; | |
198 } | |
199 } | |
200 /*- End of function --------------------------------------------------------*/ | |
201 | |
202 static void signal_load(signal_source_t *sig, const char *name) | |
203 { | |
204 sig->handle = sf_open_telephony_read(name, 1); | |
205 sig->name = name; | |
206 sig->max = sf_readf_short(sig->handle, sig->signal, SAMPLE_RATE); | |
207 if (sig->max < 0) | |
208 { | |
209 fprintf(stderr, " Error reading sound file '%s'\n", sig->name); | |
210 exit(2); | |
211 } | |
212 } | |
213 /*- End of function --------------------------------------------------------*/ | |
214 | |
215 static void signal_free(signal_source_t *sig) | |
216 { | |
217 if (sf_close(sig->handle) != 0) | |
218 { | |
219 fprintf(stderr, " Cannot close sound file '%s'\n", sig->name); | |
220 exit(2); | |
221 } | |
222 } | |
223 /*- End of function --------------------------------------------------------*/ | |
224 | |
225 static void signal_restart(signal_source_t *sig, float gain) | |
226 { | |
227 sig->cur = 0; | |
228 sig->gain = powf(10.0f, gain/20.0f); | |
229 } | |
230 /*- End of function --------------------------------------------------------*/ | |
231 | |
232 static int16_t signal_amp(signal_source_t *sig) | |
233 { | |
234 int16_t tx; | |
235 | |
236 tx = sig->signal[sig->cur++]*sig->gain; | |
237 if (sig->cur >= sig->max) | |
238 sig->cur = 0; | |
239 return tx; | |
240 } | |
241 /*- End of function --------------------------------------------------------*/ | |
242 | |
243 static level_measurement_device_t *level_measurement_device_create(int type) | |
244 { | |
245 level_measurement_device_t *dev; | |
246 int i; | |
247 | |
248 dev = (level_measurement_device_t *) malloc(sizeof(level_measurement_device_t)); | |
249 dev->fir = (fir_float_state_t *) malloc(sizeof(fir_float_state_t)); | |
250 fir_float_create(dev->fir, | |
251 level_measurement_bp_coeffs, | |
252 sizeof(level_measurement_bp_coeffs)/sizeof(float)); | |
253 for (i = 0; i < 35*8; i++) | |
254 dev->history[i] = 0.0f; | |
255 dev->pos = 0; | |
256 dev->factor = expf(-1.0f/((float) SAMPLE_RATE*0.035f)); | |
257 dev->power = 0; | |
258 dev->type = type; | |
259 return dev; | |
260 } | |
261 /*- End of function --------------------------------------------------------*/ | |
262 | |
263 #if 0 | |
264 static void level_measurement_device_reset(level_measurement_device_t *dev) | |
265 { | |
266 int i; | |
267 | |
268 for (i = 0; i < 35*8; i++) | |
269 dev->history[i] = 0.0f; | |
270 dev->pos = 0; | |
271 dev->power = 0; | |
272 dev->peak = 0.0f; | |
273 } | |
274 /*- End of function --------------------------------------------------------*/ | |
275 | |
276 static int level_measurement_device_release(level_measurement_device_t *s) | |
277 { | |
278 fir_float_free(s->fir); | |
279 free(s->fir); | |
280 free(s); | |
281 return 0; | |
282 } | |
283 /*- End of function --------------------------------------------------------*/ | |
284 #endif | |
285 | |
286 static float level_measurement_device_get_peak(level_measurement_device_t *dev) | |
287 { | |
288 return dev->peak; | |
289 } | |
290 /*- End of function --------------------------------------------------------*/ | |
291 | |
292 static float level_measurement_device_reset_peak(level_measurement_device_t *dev) | |
293 { | |
294 float power; | |
295 | |
296 power = dev->peak; | |
297 dev->peak = -99.0f; | |
298 return power; | |
299 } | |
300 /*- End of function --------------------------------------------------------*/ | |
301 | |
302 static float level_measurement_device(level_measurement_device_t *dev, int16_t amp) | |
303 { | |
304 float signal; | |
305 float power; | |
306 | |
307 /* Level measurement device(s), specified in G.168 section 6.4.1.2.1 and 6.4.1.2.2 */ | |
308 signal = fir_float(dev->fir, amp); | |
309 signal *= signal; | |
310 if (dev->type == 0) | |
311 { | |
312 /* Level measurement device, specified in G.168 section 6.4.1.2.1 - | |
313 level measurement device. This version uses a single pole | |
314 estimator.*/ | |
315 dev->power = dev->power*dev->factor + signal*(1.0f - dev->factor); | |
316 signal = sqrtf(dev->power); | |
317 } | |
318 else | |
319 { | |
320 /* Level measurement device, specified in G.168 section 6.4.1.2.2 - | |
321 level measurement device for peaks. This version uses a sliding | |
322 window estimator. */ | |
323 dev->power += (signal - dev->history[dev->pos]); | |
324 dev->history[dev->pos++] = signal; | |
325 signal = sqrtf(dev->power/(35.8f*8.0f)); | |
326 } | |
327 if (signal <= 0.0f) | |
328 return -99.0f; | |
329 power = DBM0_MAX_POWER + 20.0f*log10f(signal/32767.0f); | |
330 if (power > dev->peak) | |
331 dev->peak = power; | |
332 return power; | |
333 } | |
334 /*- End of function --------------------------------------------------------*/ | |
335 | |
336 static void level_measurements_create(int type) | |
337 { | |
338 rin_power_meter = level_measurement_device_create(type); | |
339 rout_power_meter = level_measurement_device_create(type); | |
340 sin_power_meter = level_measurement_device_create(type); | |
341 sout_power_meter = level_measurement_device_create(type); | |
342 sgen_power_meter = level_measurement_device_create(type); | |
343 } | |
344 /*- End of function --------------------------------------------------------*/ | |
345 | |
346 static void level_measurements_update(int16_t rin, int16_t sin, int16_t rout, int16_t sout, int16_t sgen) | |
347 { | |
348 level_measurement_device(rin_power_meter, rin); | |
349 level_measurement_device(rout_power_meter, rout); | |
350 level_measurement_device(sin_power_meter, sin); | |
351 level_measurement_device(sout_power_meter, sout); | |
352 level_measurement_device(sgen_power_meter, sgen); | |
353 } | |
354 /*- End of function --------------------------------------------------------*/ | |
355 | |
356 static void level_measurements_reset_peaks(void) | |
357 { | |
358 level_measurement_device_reset_peak(rin_power_meter); | |
359 level_measurement_device_reset_peak(rout_power_meter); | |
360 level_measurement_device_reset_peak(sin_power_meter); | |
361 level_measurement_device_reset_peak(sout_power_meter); | |
362 level_measurement_device_reset_peak(sgen_power_meter); | |
363 } | |
364 /*- End of function --------------------------------------------------------*/ | |
365 | |
366 static void print_results(void) | |
367 { | |
368 if (!quiet) | |
369 printf("test model ERL time Max Rin Max Rout Max Sgen Max Sin Max Sout\n"); | |
370 printf("%-4s %-1d %-5.1f%6.2fs%9.2f%9.2f%9.2f%9.2f%9.2f\n", | |
371 test_name, | |
372 chan_model.model_no, | |
373 20.0f*log10f(-chan_model.erl), | |
374 0.0f, //test_clock, | |
375 level_measurement_device_get_peak(rin_power_meter), | |
376 level_measurement_device_get_peak(rout_power_meter), | |
377 level_measurement_device_get_peak(sgen_power_meter), | |
378 level_measurement_device_get_peak(sin_power_meter), | |
379 level_measurement_device_get_peak(sout_power_meter)); | |
380 } | |
381 /*- End of function --------------------------------------------------------*/ | |
382 | |
383 static int channel_model_create(channel_model_state_t *chan, int model, float erl, int codec) | |
384 { | |
385 static const int32_t line_model_clear_coeffs[] = | |
386 { | |
387 32768 | |
388 }; | |
389 static const int32_t *line_models[] = | |
390 { | |
391 line_model_clear_coeffs, | |
392 line_model_d2_coeffs, | |
393 line_model_d3_coeffs, | |
394 line_model_d4_coeffs, | |
395 line_model_d5_coeffs, | |
396 line_model_d6_coeffs, | |
397 line_model_d7_coeffs, | |
398 line_model_d8_coeffs, | |
399 line_model_d9_coeffs | |
400 }; | |
401 static const int line_model_sizes[] = | |
402 { | |
403 sizeof(line_model_clear_coeffs)/sizeof(line_model_clear_coeffs[0]), | |
404 sizeof(line_model_d2_coeffs)/sizeof(line_model_d2_coeffs[0]), | |
405 sizeof(line_model_d3_coeffs)/sizeof(line_model_d3_coeffs[0]), | |
406 sizeof(line_model_d4_coeffs)/sizeof(line_model_d4_coeffs[0]), | |
407 sizeof(line_model_d5_coeffs)/sizeof(line_model_d5_coeffs[0]), | |
408 sizeof(line_model_d6_coeffs)/sizeof(line_model_d6_coeffs[0]), | |
409 sizeof(line_model_d7_coeffs)/sizeof(line_model_d7_coeffs[0]), | |
410 sizeof(line_model_d8_coeffs)/sizeof(line_model_d8_coeffs[0]), | |
411 sizeof(line_model_d9_coeffs)/sizeof(line_model_d9_coeffs[0]) | |
412 }; | |
413 static const float ki[] = | |
414 { | |
415 3.05e-5f, | |
416 LINE_MODEL_D2_GAIN, | |
417 LINE_MODEL_D3_GAIN, | |
418 LINE_MODEL_D4_GAIN, | |
419 LINE_MODEL_D5_GAIN, | |
420 LINE_MODEL_D6_GAIN, | |
421 LINE_MODEL_D7_GAIN, | |
422 LINE_MODEL_D8_GAIN, | |
423 LINE_MODEL_D9_GAIN | |
424 }; | |
425 | |
426 if (model < 0 || model >= (int) (sizeof(line_model_sizes)/sizeof(line_model_sizes[0]))) | |
427 return -1; | |
428 fir32_create(&chan->impulse, line_models[model], line_model_sizes[model]); | |
429 chan->gain = 32768.0f*powf(10.0f, erl/20.0f)*ki[model]; | |
430 chan->munging_codec = codec; | |
431 chan->model_no = model; | |
432 chan->erl = erl; | |
433 return 0; | |
434 } | |
435 /*- End of function --------------------------------------------------------*/ | |
436 | |
437 static int16_t channel_model(channel_model_state_t *chan, int16_t rout, int16_t sgen) | |
438 { | |
439 int16_t echo; | |
440 int16_t sin; | |
441 | |
442 /* Channel modelling is merely simulating the effects of A-law or u-law distortion | |
443 and using one of the echo models from G.168. Simulating the codec is very important, | |
444 as this is usually the limiting factor in how much echo reduction is achieved. */ | |
445 | |
446 /* The far end signal will have been through codec munging. */ | |
447 switch (chan->munging_codec) | |
448 { | |
449 case G711_ALAW: | |
450 sgen = alaw_to_linear(linear_to_alaw(sgen)); | |
451 break; | |
452 case G711_ULAW: | |
453 sgen = ulaw_to_linear(linear_to_ulaw(sgen)); | |
454 break; | |
455 } | |
456 | |
457 /* The local tx signal will usually have gone through codec munging before | |
458 it reached the line's analogue area, where the echo occurs. */ | |
459 switch (chan->munging_codec) | |
460 { | |
461 case G711_ALAW: | |
462 rout = alaw_to_linear(linear_to_alaw(rout)); | |
463 break; | |
464 case G711_ULAW: | |
465 rout = ulaw_to_linear(linear_to_ulaw(rout)); | |
466 break; | |
467 } | |
468 /* Now we need to model the echo. We only model a single analogue segment, as per | |
469 the G.168 spec. However, there will generally be near end and far end analogue/echoey | |
470 segments in the real world, unless an end is purely digital. */ | |
471 echo = fir32(&chan->impulse, rout*chan->gain); | |
472 sin = saturate(echo + sgen); | |
473 | |
474 /* This mixed echo and far end signal will have been through codec munging | |
475 when it came back into the digital network. */ | |
476 switch (chan->munging_codec) | |
477 { | |
478 case G711_ALAW: | |
479 sin = alaw_to_linear(linear_to_alaw(sin)); | |
480 break; | |
481 case G711_ULAW: | |
482 sin = ulaw_to_linear(linear_to_ulaw(sin)); | |
483 break; | |
484 } | |
485 return sin; | |
486 } | |
487 /*- End of function --------------------------------------------------------*/ | |
488 | |
489 static void write_log_files(int16_t rout, int16_t sin) | |
490 { | |
491 #if 0 | |
492 fprintf(flevel, "%f\t%f\t%f\t%f\n", LRin, LSin, LSout, LSgen); | |
493 fprintf(fdump, "%d %d %d", ctx->tx, ctx->rx, ctx->clean); | |
494 fprintf(fdump, | |
495 " %d %d %d %d %d %d %d %d %d %d\n", | |
496 ctx->clean_nlp, | |
497 ctx->Ltx, | |
498 ctx->Lrx, | |
499 ctx->Lclean, | |
500 (ctx->nonupdate_dwell > 0), | |
501 ctx->adapt, | |
502 ctx->Lclean_bg, | |
503 ctx->Pstates, | |
504 ctx->Lbgn_upper, | |
505 ctx->Lbgn); | |
506 #endif | |
507 } | |
508 /*- End of function --------------------------------------------------------*/ | |
509 | |
510 static int16_t silence(void) | |
511 { | |
512 return 0; | |
513 } | |
514 /*- End of function --------------------------------------------------------*/ | |
515 | |
516 static int16_t local_css_signal(void) | |
517 { | |
518 return signal_amp(&local_css); | |
519 } | |
520 /*- End of function --------------------------------------------------------*/ | |
521 | |
522 static int16_t far_css_signal(void) | |
523 { | |
524 return signal_amp(&far_css); | |
525 } | |
526 /*- End of function --------------------------------------------------------*/ | |
527 | |
528 static int16_t local_noise_signal(void) | |
529 { | |
530 return awgn(&local_noise_source); | |
531 } | |
532 /*- End of function --------------------------------------------------------*/ | |
533 | |
534 static int16_t far_noise_signal(void) | |
535 { | |
536 return awgn(&far_noise_source); | |
537 } | |
538 /*- End of function --------------------------------------------------------*/ | |
539 | |
540 #if 0 | |
541 static int16_t local_hoth_noise_signal(void) | |
542 { | |
543 static float hoth_noise = 0.0; | |
544 | |
545 hoth_noise = hoth_noise*0.625 + awgn(&local_noise_source)*0.375; | |
546 return (int16_t) hoth_noise; | |
547 } | |
548 /*- End of function --------------------------------------------------------*/ | |
549 #endif | |
550 | |
551 static int16_t far_hoth_noise_signal(void) | |
552 { | |
553 static float hoth_noise = 0.0; | |
554 | |
555 hoth_noise = hoth_noise*0.625 + awgn(&far_noise_source)*0.375; | |
556 return (int16_t) hoth_noise; | |
557 } | |
558 /*- End of function --------------------------------------------------------*/ | |
559 | |
560 static void run_test(echo_can_state_t *ctx, int16_t (*tx_source)(void), int16_t (*rx_source)(void), int period) | |
561 { | |
562 int i; | |
563 int16_t rin; | |
564 int16_t rout; | |
565 int16_t sin; | |
566 int16_t sout; | |
567 int16_t sgen; | |
568 int outframes; | |
569 | |
570 for (i = 0; i < period*SAMPLE_RATE/1000; i++) | |
571 { | |
572 rin = tx_source(); | |
573 sgen = rx_source(); | |
574 | |
575 rout = echo_can_hpf_tx(ctx, rin); | |
576 sin = channel_model(&chan_model, rout, sgen); | |
577 sout = echo_can_update(ctx, rout, sin); | |
578 | |
579 level_measurements_update(rin, sin, rout, sout, sgen); | |
580 //residue = 100.0f*pp1/pp2; | |
581 //put_residue(residue); | |
582 | |
583 //put_residue(clean - rx); | |
584 #if defined(ENABLE_GUI) | |
585 if (use_gui) | |
586 echo_can_monitor_can_update(ctx->fir_taps16[0], TEST_EC_TAPS); | |
587 #endif | |
588 result_sound[result_cur++] = rin; | |
589 result_sound[result_cur++] = sgen; | |
590 result_sound[result_cur++] = sin; | |
591 result_sound[result_cur++] = rout; | |
592 result_sound[result_cur++] = sout; | |
593 result_sound[result_cur++] = sout - sgen; | |
594 result_sound[result_cur++] = 0; // TODO: insert the EC's internal status here | |
595 if (result_cur >= RESULT_CHANNELS*SAMPLE_RATE) | |
596 { | |
597 outframes = sf_writef_short(result_handle, result_sound, result_cur/RESULT_CHANNELS); | |
598 if (outframes != result_cur/RESULT_CHANNELS) | |
599 { | |
600 fprintf(stderr, " Error writing result sound\n"); | |
601 exit(2); | |
602 } | |
603 result_cur = 0; | |
604 } | |
605 } | |
606 #if defined(ENABLE_GUI) | |
607 if (use_gui) | |
608 echo_can_monitor_can_update(ctx->fir_taps16[0], TEST_EC_TAPS); | |
609 #endif | |
610 if (result_cur >= 0) | |
611 { | |
612 outframes = sf_writef_short(result_handle, result_sound, result_cur/RESULT_CHANNELS); | |
613 if (outframes != result_cur/RESULT_CHANNELS) | |
614 { | |
615 fprintf(stderr, " Error writing result sound\n"); | |
616 exit(2); | |
617 } | |
618 result_cur = 0; | |
619 } | |
620 } | |
621 /*- End of function --------------------------------------------------------*/ | |
622 | |
623 static void print_test_title(const char *title) | |
624 { | |
625 if (quiet == FALSE) | |
626 printf(title); | |
627 } | |
628 /*- End of function --------------------------------------------------------*/ | |
629 | |
630 static int perform_test_sanity(void) | |
631 { | |
632 echo_can_state_t *ctx; | |
633 int i; | |
634 int16_t rx; | |
635 int16_t tx; | |
636 int16_t clean; | |
637 int far_tx; | |
638 int16_t far_sound[SAMPLE_RATE]; | |
639 int16_t result_sound[64000]; | |
640 int result_cur; | |
641 int outframes; | |
642 int local_cur; | |
643 int far_cur; | |
644 //int32_t coeffs[200][128]; | |
645 //int coeff_index; | |
646 | |
647 print_test_title("Performing basic sanity test\n"); | |
648 ctx = echo_can_init(TEST_EC_TAPS, 0); | |
649 | |
650 local_cur = 0; | |
651 far_cur = 0; | |
652 result_cur = 0; | |
653 | |
654 echo_can_flush(ctx); | |
655 /* Converge the canceller */ | |
656 signal_restart(&local_css, 0.0f); | |
657 run_test(ctx, silence, silence, 200); | |
658 run_test(ctx, local_css_signal, silence, 5000); | |
659 echo_can_adaption_mode(ctx, ECHO_CAN_USE_ADAPTION | ECHO_CAN_USE_NLP | ECHO_CAN_USE_CNG); | |
660 run_test(ctx, local_css_signal, silence, 5000); | |
661 echo_can_adaption_mode(ctx, ECHO_CAN_USE_ADAPTION); | |
662 | |
663 for (i = 0; i < SAMPLE_RATE*10; i++) | |
664 { | |
665 tx = local_css_signal(); | |
666 #if 0 | |
667 if ((i/10000)%10 == 9) | |
668 { | |
669 /* Inject a burst of far sound */ | |
670 if (far_cur >= far_max) | |
671 { | |
672 far_max = sf_readf_short(farhandle, far_sound, SAMPLE_RATE); | |
673 if (far_max < 0) | |
674 { | |
675 fprintf(stderr, " Error reading far sound\n"); | |
676 exit(2); | |
677 } | |
678 if (far_max == 0) | |
679 break; | |
680 far_cur = 0; | |
681 } | |
682 far_tx = far_sound[far_cur++]; | |
683 } | |
684 else | |
685 { | |
686 far_tx = 0; | |
687 } | |
688 #else | |
689 far_sound[0] = 0; | |
690 far_tx = 0; | |
691 #endif | |
692 rx = channel_model(&chan_model, tx, far_tx); | |
693 //rx += awgn(&far_noise_source); | |
694 //tx += awgn(&far_noise_source); | |
695 clean = echo_can_update(ctx, tx, rx); | |
696 | |
697 #if defined(XYZZY) | |
698 if (i%SAMPLE_RATE == 0) | |
699 { | |
700 if (coeff_index < 200) | |
701 { | |
702 for (j = 0; j < ctx->taps; j++) | |
703 coeffs[coeff_index][j] = ctx->fir_taps32[j]; | |
704 coeff_index++; | |
705 } | |
706 } | |
707 #endif | |
708 result_sound[result_cur++] = tx; | |
709 result_sound[result_cur++] = rx; | |
710 result_sound[result_cur++] = clean - far_tx; | |
711 //result_sound[result_cur++] = ctx->tx_power[2]; | |
712 //result_sound[result_cur++] = ctx->tx_power[1]; | |
713 ////result_sound[result_cur++] = (ctx->tx_power[1] > 64) ? SAMPLE_RATE : -SAMPLE_RATE; | |
714 //result_sound[result_cur++] = ctx->tap_set*SAMPLE_RATE; | |
715 //result_sound[result_cur++] = (ctx->nonupdate_dwell > 0) ? SAMPLE_RATE : -SAMPLE_RATE; | |
716 //result_sound[result_cur++] = ctx->latest_correction >> 8; | |
717 //result_sound[result_cur++] = level_measurement_device(tx)/(16.0*65536.0); | |
718 //result_sound[result_cur++] = level_measurement_device(tx)/4096.0; | |
719 ////result_sound[result_cur++] = (ctx->tx_power[1] > ctx->rx_power[0]) ? SAMPLE_RATE : -SAMPLE_RATE; | |
720 //result_sound[result_cur++] = (ctx->tx_power[1] > ctx->rx_power[0]) ? SAMPLE_RATE : -SAMPLE_RATE; | |
721 //result_sound[result_cur++] = (ctx->narrowband_score)*5; // ? SAMPLE_RATE : -SAMPLE_RATE; | |
722 //result_sound[result_cur++] = ctx->tap_rotate_counter*10; | |
723 ////result_sound[result_cur++] = ctx->vad; | |
724 | |
725 put_residue(clean - far_tx); | |
726 if (result_cur >= RESULT_CHANNELS*SAMPLE_RATE) | |
727 { | |
728 outframes = sf_writef_short(result_handle, result_sound, result_cur/RESULT_CHANNELS); | |
729 if (outframes != result_cur/RESULT_CHANNELS) | |
730 { | |
731 fprintf(stderr, " Error writing result sound\n"); | |
732 exit(2); | |
733 } | |
734 result_cur = 0; | |
735 } | |
736 } | |
737 if (result_cur > 0) | |
738 { | |
739 outframes = sf_writef_short(result_handle, result_sound, result_cur/RESULT_CHANNELS); | |
740 if (outframes != result_cur/RESULT_CHANNELS) | |
741 { | |
742 fprintf(stderr, " Error writing result sound\n"); | |
743 exit(2); | |
744 } | |
745 } | |
746 #if defined(XYZZY) | |
747 for (j = 0; j < ctx->taps; j++) | |
748 { | |
749 for (i = 0; i < coeff_index; i++) | |
750 fprintf(stderr, "%d ", coeffs[i][j]); | |
751 fprintf(stderr, "\n"); | |
752 } | |
753 #endif | |
754 | |
755 echo_can_free(ctx); | |
756 return 0; | |
757 } | |
758 /*- End of function --------------------------------------------------------*/ | |
759 | |
760 static int perform_test_2a(void) | |
761 { | |
762 echo_can_state_t *ctx; | |
763 | |
764 /* Test 2 - Convergence and steady state residual and returned echo level test */ | |
765 /* Test 2A - Convergence and reconvergence test with NLP enabled */ | |
766 print_test_title("Performing test 2A - Convergence and reconvergence test with NLP enabled\n"); | |
767 ctx = echo_can_init(TEST_EC_TAPS, 0); | |
768 | |
769 echo_can_flush(ctx); | |
770 echo_can_adaption_mode(ctx, ECHO_CAN_USE_ADAPTION | ECHO_CAN_USE_NLP); | |
771 | |
772 /* Test 2A (a) - Convergence test with NLP enabled */ | |
773 | |
774 /* Converge the canceller. */ | |
775 run_test(ctx, silence, silence, 200); | |
776 signal_restart(&local_css, 0.0f); | |
777 run_test(ctx, local_css_signal, silence, 1000); | |
778 level_measurements_reset_peaks(); | |
779 run_test(ctx, local_css_signal, silence, 9000); | |
780 print_results(); | |
781 if (level_measurement_device_get_peak(sout_power_meter) > -65.0f) | |
782 printf("Test failed\n"); | |
783 else | |
784 printf("Test passed\n"); | |
785 | |
786 /* Test 2A (b) - Reconvergence test with NLP enabled */ | |
787 | |
788 /* Make an abrupt change of channel characteristic, to another of the channel echo models. */ | |
789 if (channel_model_create(&chan_model, supp_line_model_no, erl, munger)) | |
790 { | |
791 fprintf(stderr, " Failed to create line model\n"); | |
792 exit(2); | |
793 } | |
794 signal_restart(&local_css, 0.0f); | |
795 run_test(ctx, local_css_signal, silence, 1000); | |
796 level_measurements_reset_peaks(); | |
797 run_test(ctx, local_css_signal, silence, 9000); | |
798 print_results(); | |
799 if (level_measurement_device_get_peak(sout_power_meter) > -65.0f) | |
800 printf("Test failed\n"); | |
801 else | |
802 printf("Test passed\n"); | |
803 | |
804 echo_can_free(ctx); | |
805 return 0; | |
806 } | |
807 /*- End of function --------------------------------------------------------*/ | |
808 | |
809 static int perform_test_2b(void) | |
810 { | |
811 echo_can_state_t *ctx; | |
812 | |
813 /* Test 2 - Convergence and steady state residual and returned echo level test */ | |
814 /* Test 2B - Convergence and reconverge with NLP disabled */ | |
815 print_test_title("Performing test 2B - Convergence and reconverge with NLP disabled\n"); | |
816 ctx = echo_can_init(TEST_EC_TAPS, 0); | |
817 | |
818 echo_can_flush(ctx); | |
819 echo_can_adaption_mode(ctx, ECHO_CAN_USE_ADAPTION); | |
820 signal_restart(&local_css, 0.0f); | |
821 | |
822 /* Test 2B (a) - Convergence test with NLP disabled */ | |
823 | |
824 /* Converge the canceller */ | |
825 run_test(ctx, silence, silence, 200); | |
826 run_test(ctx, local_css_signal, silence, 1000); | |
827 level_measurements_reset_peaks(); | |
828 run_test(ctx, local_css_signal, silence, 9000); | |
829 print_results(); | |
830 level_measurements_reset_peaks(); | |
831 run_test(ctx, local_css_signal, silence, 170000); | |
832 print_results(); | |
833 if (level_measurement_device_get_peak(sout_power_meter) > -65.0) | |
834 printf("Test failed\n"); | |
835 else | |
836 printf("Test passed\n"); | |
837 | |
838 /* Test 2B (b) - Reconvergence test with NLP disabled */ | |
839 | |
840 /* Make an abrupt change of channel characteristic, to another of the channel echo models. */ | |
841 if (channel_model_create(&chan_model, supp_line_model_no, erl, munger)) | |
842 { | |
843 fprintf(stderr, " Failed to create line model\n"); | |
844 exit(2); | |
845 } | |
846 run_test(ctx, local_css_signal, silence, 1000); | |
847 level_measurements_reset_peaks(); | |
848 run_test(ctx, local_css_signal, silence, 9000); | |
849 print_results(); | |
850 level_measurements_reset_peaks(); | |
851 run_test(ctx, local_css_signal, silence, 170000); | |
852 print_results(); | |
853 if (level_measurement_device_get_peak(sout_power_meter) > -65.0) | |
854 printf("Test failed\n"); | |
855 else | |
856 printf("Test passed\n"); | |
857 | |
858 echo_can_free(ctx); | |
859 return 0; | |
860 } | |
861 /*- End of function --------------------------------------------------------*/ | |
862 | |
863 static int perform_test_2ca(void) | |
864 { | |
865 echo_can_state_t *ctx; | |
866 | |
867 /* Test 2 - Convergence and steady state residual and returned echo level test */ | |
868 /* Test 2C(a) - Convergence with background noise present */ | |
869 print_test_title("Performing test 2C(a) - Convergence with background noise present\n"); | |
870 ctx = echo_can_init(TEST_EC_TAPS, 0); | |
871 awgn_init_dbm0(&far_noise_source, 7162534, -50.0f); | |
872 | |
873 echo_can_flush(ctx); | |
874 echo_can_adaption_mode(ctx, ECHO_CAN_USE_ADAPTION); | |
875 | |
876 /* Converge a canceller */ | |
877 signal_restart(&local_css, 0.0f); | |
878 run_test(ctx, silence, silence, 200); | |
879 | |
880 awgn_init_dbm0(&far_noise_source, 7162534, -40.0f); | |
881 run_test(ctx, local_css_signal, far_hoth_noise_signal, 5000); | |
882 | |
883 /* Now freeze adaption, and measure the echo. */ | |
884 echo_can_adaption_mode(ctx, 0); | |
885 level_measurements_reset_peaks(); | |
886 run_test(ctx, local_css_signal, silence, 5000); | |
887 print_results(); | |
888 if (level_measurement_device_get_peak(sout_power_meter) > level_measurement_device_get_peak(sgen_power_meter)) | |
889 printf("Test failed\n"); | |
890 else | |
891 printf("Test passed\n"); | |
892 | |
893 echo_can_adaption_mode(ctx, ECHO_CAN_USE_ADAPTION); | |
894 echo_can_free(ctx); | |
895 return 0; | |
896 } | |
897 /*- End of function --------------------------------------------------------*/ | |
898 | |
899 static int perform_test_3a(void) | |
900 { | |
901 echo_can_state_t *ctx; | |
902 | |
903 /* Test 3 - Performance under double talk conditions */ | |
904 /* Test 3A - Double talk test with low cancelled-end levels */ | |
905 print_test_title("Performing test 3A - Double talk test with low cancelled-end levels\n"); | |
906 ctx = echo_can_init(TEST_EC_TAPS, 0); | |
907 | |
908 echo_can_flush(ctx); | |
909 echo_can_adaption_mode(ctx, ECHO_CAN_USE_ADAPTION); | |
910 | |
911 run_test(ctx, silence, silence, 200); | |
912 signal_restart(&local_css, 0.0f); | |
913 signal_restart(&far_css, -20.0f); | |
914 | |
915 /* Apply double talk, with a weak far end signal */ | |
916 run_test(ctx, local_css_signal, far_css_signal, 5000); | |
917 | |
918 /* Now freeze adaption. */ | |
919 echo_can_adaption_mode(ctx, 0); | |
920 run_test(ctx, local_css_signal, silence, 500); | |
921 | |
922 /* Now measure the echo */ | |
923 level_measurements_reset_peaks(); | |
924 run_test(ctx, local_css_signal, silence, 5000); | |
925 print_results(); | |
926 if (level_measurement_device_get_peak(sout_power_meter) > level_measurement_device_get_peak(sgen_power_meter)) | |
927 printf("Test failed\n"); | |
928 else | |
929 printf("Test passed\n"); | |
930 | |
931 echo_can_adaption_mode(ctx, ECHO_CAN_USE_ADAPTION); | |
932 echo_can_free(ctx); | |
933 return 0; | |
934 } | |
935 /*- End of function --------------------------------------------------------*/ | |
936 | |
937 static int perform_test_3ba(void) | |
938 { | |
939 echo_can_state_t *ctx; | |
940 | |
941 /* Test 3 - Performance under double talk conditions */ | |
942 /* Test 3B(a) - Double talk stability test with high cancelled-end levels */ | |
943 print_test_title("Performing test 3B(b) - Double talk stability test with high cancelled-end levels\n"); | |
944 ctx = echo_can_init(TEST_EC_TAPS, 0); | |
945 | |
946 echo_can_flush(ctx); | |
947 echo_can_adaption_mode(ctx, ECHO_CAN_USE_ADAPTION); | |
948 | |
949 run_test(ctx, silence, silence, 200); | |
950 signal_restart(&local_css, 0.0f); | |
951 signal_restart(&far_css, 0.0f); | |
952 | |
953 /* Converge the canceller */ | |
954 run_test(ctx, local_css_signal, silence, 5000); | |
955 | |
956 /* Apply double talk */ | |
957 run_test(ctx, local_css_signal, far_css_signal, 5000); | |
958 | |
959 /* Now freeze adaption. */ | |
960 echo_can_adaption_mode(ctx, 0); | |
961 run_test(ctx, local_css_signal, far_css_signal, 1000); | |
962 | |
963 /* Turn off the double talk. */ | |
964 run_test(ctx, local_css_signal, silence, 500); | |
965 | |
966 /* Now measure the echo */ | |
967 level_measurements_reset_peaks(); | |
968 run_test(ctx, local_css_signal, silence, 5000); | |
969 print_results(); | |
970 | |
971 echo_can_adaption_mode(ctx, ECHO_CAN_USE_ADAPTION); | |
972 echo_can_free(ctx); | |
973 return 0; | |
974 } | |
975 /*- End of function --------------------------------------------------------*/ | |
976 | |
977 static int perform_test_3bb(void) | |
978 { | |
979 echo_can_state_t *ctx; | |
980 | |
981 /* Test 3 - Performance under double talk conditions */ | |
982 /* Test 3B(b) - Double talk stability test with low cancelled-end levels */ | |
983 print_test_title("Performing test 3B(b) - Double talk stability test with low cancelled-end levels\n"); | |
984 ctx = echo_can_init(TEST_EC_TAPS, 0); | |
985 | |
986 echo_can_flush(ctx); | |
987 echo_can_adaption_mode(ctx, ECHO_CAN_USE_ADAPTION); | |
988 | |
989 run_test(ctx, silence, silence, 200); | |
990 signal_restart(&local_css, 0.0f); | |
991 signal_restart(&far_css, -15.0f); | |
992 | |
993 /* Converge the canceller */ | |
994 run_test(ctx, local_css_signal, silence, 5000); | |
995 | |
996 /* Apply double talk */ | |
997 run_test(ctx, local_css_signal, far_css_signal, 5000); | |
998 | |
999 /* Now freeze adaption. */ | |
1000 echo_can_adaption_mode(ctx, 0); | |
1001 run_test(ctx, local_css_signal, silence, 1000); | |
1002 | |
1003 /* Turn off the double talk. */ | |
1004 run_test(ctx, local_css_signal, silence, 500); | |
1005 | |
1006 /* Now measure the echo */ | |
1007 level_measurements_reset_peaks(); | |
1008 run_test(ctx, local_css_signal, silence, 5000); | |
1009 print_results(); | |
1010 | |
1011 echo_can_adaption_mode(ctx, ECHO_CAN_USE_ADAPTION); | |
1012 echo_can_free(ctx); | |
1013 return 0; | |
1014 } | |
1015 /*- End of function --------------------------------------------------------*/ | |
1016 | |
1017 static int perform_test_3c(void) | |
1018 { | |
1019 echo_can_state_t *ctx; | |
1020 | |
1021 /* Test 3 - Performance under double talk conditions */ | |
1022 /* Test 3C - Double talk test with simulated conversation */ | |
1023 print_test_title("Performing test 3C - Double talk test with simulated conversation\n"); | |
1024 ctx = echo_can_init(TEST_EC_TAPS, 0); | |
1025 | |
1026 echo_can_flush(ctx); | |
1027 echo_can_adaption_mode(ctx, ECHO_CAN_USE_ADAPTION); | |
1028 run_test(ctx, silence, silence, 200); | |
1029 | |
1030 signal_restart(&local_css, 0.0f); | |
1031 signal_restart(&far_css, -15.0f); | |
1032 | |
1033 /* Apply double talk */ | |
1034 run_test(ctx, local_css_signal, far_css_signal, 5600); | |
1035 | |
1036 /* Stop the far signal, and measure the echo. */ | |
1037 level_measurements_reset_peaks(); | |
1038 run_test(ctx, local_css_signal, silence, 1400); | |
1039 print_results(); | |
1040 | |
1041 /* Continue measuring the resulting echo */ | |
1042 run_test(ctx, local_css_signal, silence, 5000); | |
1043 | |
1044 /* Reapply double talk */ | |
1045 signal_restart(&far_css, 0.0f); | |
1046 run_test(ctx, local_css_signal, far_css_signal, 5600); | |
1047 | |
1048 /* Now the far signal only */ | |
1049 run_test(ctx, silence, far_css_signal, 5600); | |
1050 | |
1051 echo_can_free(ctx); | |
1052 return 0; | |
1053 } | |
1054 /*- End of function --------------------------------------------------------*/ | |
1055 | |
1056 static int perform_test_4(void) | |
1057 { | |
1058 echo_can_state_t *ctx; | |
1059 | |
1060 /* Test 4 - Leak rate test */ | |
1061 print_test_title("Performing test 4 - Leak rate test\n"); | |
1062 ctx = echo_can_init(TEST_EC_TAPS, 0); | |
1063 | |
1064 echo_can_flush(ctx); | |
1065 echo_can_adaption_mode(ctx, ECHO_CAN_USE_ADAPTION); | |
1066 | |
1067 run_test(ctx, silence, silence, 200); | |
1068 | |
1069 /* Converge the canceller */ | |
1070 signal_restart(&local_css, 0.0f); | |
1071 run_test(ctx, local_css_signal, silence, 5000); | |
1072 | |
1073 /* Put 2 minutes of silence through it */ | |
1074 run_test(ctx, silence, silence, 120000); | |
1075 | |
1076 /* Now freeze it, and check if it is still well adapted. */ | |
1077 echo_can_adaption_mode(ctx, 0); | |
1078 level_measurements_reset_peaks(); | |
1079 run_test(ctx, local_css_signal, silence, 5000); | |
1080 print_results(); | |
1081 | |
1082 echo_can_adaption_mode(ctx, ECHO_CAN_USE_ADAPTION); | |
1083 echo_can_free(ctx); | |
1084 return 0; | |
1085 } | |
1086 /*- End of function --------------------------------------------------------*/ | |
1087 | |
1088 static int perform_test_5(void) | |
1089 { | |
1090 echo_can_state_t *ctx; | |
1091 | |
1092 /* Test 5 - Infinite return loss convergence test */ | |
1093 print_test_title("Performing test 5 - Infinite return loss convergence test\n"); | |
1094 ctx = echo_can_init(TEST_EC_TAPS, 0); | |
1095 | |
1096 echo_can_flush(ctx); | |
1097 echo_can_adaption_mode(ctx, ECHO_CAN_USE_ADAPTION); | |
1098 | |
1099 /* Converge the canceller */ | |
1100 signal_restart(&local_css, 0.0f); | |
1101 run_test(ctx, local_css_signal, silence, 5000); | |
1102 | |
1103 /* Now stop echoing, and see we don't do anything unpleasant as the | |
1104 echo path is open looped. */ | |
1105 run_test(ctx, local_css_signal, silence, 5000); | |
1106 print_results(); | |
1107 | |
1108 echo_can_free(ctx); | |
1109 return 0; | |
1110 } | |
1111 /*- End of function --------------------------------------------------------*/ | |
1112 | |
1113 static int perform_test_6(void) | |
1114 { | |
1115 echo_can_state_t *ctx; | |
1116 int i; | |
1117 int j; | |
1118 int k; | |
1119 int16_t rx; | |
1120 int16_t tx; | |
1121 int16_t clean; | |
1122 int local_max; | |
1123 tone_gen_descriptor_t tone_desc; | |
1124 tone_gen_state_t tone_state; | |
1125 int16_t local_sound[40000]; | |
1126 | |
1127 /* Test 6 - Non-divergence on narrow-band signals */ | |
1128 print_test_title("Performing test 6 - Non-divergence on narrow-band signals\n"); | |
1129 ctx = echo_can_init(TEST_EC_TAPS, 0); | |
1130 | |
1131 echo_can_flush(ctx); | |
1132 echo_can_adaption_mode(ctx, ECHO_CAN_USE_ADAPTION); | |
1133 | |
1134 /* Converge the canceller */ | |
1135 signal_restart(&local_css, 0.0f); | |
1136 run_test(ctx, local_css_signal, silence, 5000); | |
1137 | |
1138 /* Now put 5s bursts of a list of tones through the converged canceller, and check | |
1139 that nothing unpleasant happens. */ | |
1140 for (k = 0; tones_6_4_2_7[k][0]; k++) | |
1141 { | |
1142 make_tone_gen_descriptor(&tone_desc, | |
1143 tones_6_4_2_7[k][0], | |
1144 -11, | |
1145 tones_6_4_2_7[k][1], | |
1146 -9, | |
1147 1, | |
1148 0, | |
1149 0, | |
1150 0, | |
1151 1); | |
1152 tone_gen_init(&tone_state, &tone_desc); | |
1153 j = 0; | |
1154 for (i = 0; i < 5; i++) | |
1155 { | |
1156 local_max = tone_gen(&tone_state, local_sound, SAMPLE_RATE); | |
1157 for (j = 0; j < SAMPLE_RATE; j++) | |
1158 { | |
1159 tx = local_sound[j]; | |
1160 rx = channel_model(&chan_model, tx, 0); | |
1161 clean = echo_can_update(ctx, tx, rx); | |
1162 put_residue(clean); | |
1163 } | |
1164 #if defined(ENABLE_GUI) | |
1165 if (use_gui) | |
1166 { | |
1167 echo_can_monitor_can_update(ctx->fir_taps16[0], TEST_EC_TAPS); | |
1168 echo_can_monitor_update_display(); | |
1169 usleep(100000); | |
1170 } | |
1171 #endif | |
1172 } | |
1173 } | |
1174 #if defined(ENABLE_GUI) | |
1175 if (use_gui) | |
1176 echo_can_monitor_can_update(ctx->fir_taps16[0], TEST_EC_TAPS); | |
1177 #endif | |
1178 echo_can_free(ctx); | |
1179 return 0; | |
1180 } | |
1181 /*- End of function --------------------------------------------------------*/ | |
1182 | |
1183 static int perform_test_7(void) | |
1184 { | |
1185 echo_can_state_t *ctx; | |
1186 int i; | |
1187 int j; | |
1188 int16_t rx; | |
1189 int16_t tx; | |
1190 int16_t clean; | |
1191 int local_max; | |
1192 tone_gen_descriptor_t tone_desc; | |
1193 tone_gen_state_t tone_state; | |
1194 int16_t local_sound[40000]; | |
1195 | |
1196 /* Test 7 - Stability */ | |
1197 print_test_title("Performing test 7 - Stability\n"); | |
1198 ctx = echo_can_init(TEST_EC_TAPS, 0); | |
1199 | |
1200 /* Put tones through an unconverged canceller, and check nothing unpleasant | |
1201 happens. */ | |
1202 echo_can_flush(ctx); | |
1203 echo_can_adaption_mode(ctx, ECHO_CAN_USE_ADAPTION); | |
1204 make_tone_gen_descriptor(&tone_desc, | |
1205 tones_6_4_2_7[0][0], | |
1206 -11, | |
1207 tones_6_4_2_7[0][1], | |
1208 -9, | |
1209 1, | |
1210 0, | |
1211 0, | |
1212 0, | |
1213 1); | |
1214 tone_gen_init(&tone_state, &tone_desc); | |
1215 j = 0; | |
1216 for (i = 0; i < 120; i++) | |
1217 { | |
1218 local_max = tone_gen(&tone_state, local_sound, SAMPLE_RATE); | |
1219 for (j = 0; j < SAMPLE_RATE; j++) | |
1220 { | |
1221 tx = local_sound[j]; | |
1222 rx = channel_model(&chan_model, tx, 0); | |
1223 clean = echo_can_update(ctx, tx, rx); | |
1224 put_residue(clean); | |
1225 } | |
1226 #if defined(ENABLE_GUI) | |
1227 if (use_gui) | |
1228 { | |
1229 echo_can_monitor_can_update(ctx->fir_taps16[0], TEST_EC_TAPS); | |
1230 echo_can_monitor_update_display(); | |
1231 usleep(100000); | |
1232 } | |
1233 #endif | |
1234 } | |
1235 #if defined(ENABLE_GUI) | |
1236 if (use_gui) | |
1237 echo_can_monitor_can_update(ctx->fir_taps16[0], TEST_EC_TAPS); | |
1238 #endif | |
1239 echo_can_free(ctx); | |
1240 return 0; | |
1241 } | |
1242 /*- End of function --------------------------------------------------------*/ | |
1243 | |
1244 static int perform_test_8(void) | |
1245 { | |
1246 echo_can_state_t *ctx; | |
1247 | |
1248 /* Test 8 - Non-convergence on No 5, 6, and 7 in-band signalling */ | |
1249 print_test_title("Performing test 8 - Non-convergence on No 5, 6, and 7 in-band signalling\n"); | |
1250 ctx = echo_can_init(TEST_EC_TAPS, 0); | |
1251 | |
1252 fprintf(stderr, "Test 8 not yet implemented\n"); | |
1253 | |
1254 echo_can_free(ctx); | |
1255 return 0; | |
1256 } | |
1257 /*- End of function --------------------------------------------------------*/ | |
1258 | |
1259 static int perform_test_9(void) | |
1260 { | |
1261 echo_can_state_t *ctx; | |
1262 awgn_state_t local_noise_source; | |
1263 awgn_state_t far_noise_source; | |
1264 | |
1265 /* Test 9 - Comfort noise test */ | |
1266 print_test_title("Performing test 9 - Comfort noise test\n"); | |
1267 ctx = echo_can_init(TEST_EC_TAPS, 0); | |
1268 awgn_init_dbm0(&far_noise_source, 7162534, -50.0f); | |
1269 | |
1270 echo_can_flush(ctx); | |
1271 echo_can_adaption_mode(ctx, | |
1272 ECHO_CAN_USE_ADAPTION | |
1273 | ECHO_CAN_USE_NLP | |
1274 | ECHO_CAN_USE_CNG); | |
1275 | |
1276 /* Test 9 part 1 - matching */ | |
1277 /* Converge the canceller */ | |
1278 signal_restart(&local_css, 0.0f); | |
1279 run_test(ctx, local_css_signal, silence, 5000); | |
1280 | |
1281 echo_can_adaption_mode(ctx, ECHO_CAN_USE_ADAPTION | ECHO_CAN_USE_NLP | ECHO_CAN_USE_CNG); | |
1282 awgn_init_dbm0(&far_noise_source, 7162534, -45.0f); | |
1283 run_test(ctx, silence, far_noise_signal, 30000); | |
1284 | |
1285 awgn_init_dbm0(&local_noise_source, 1234567, -10.0f); | |
1286 run_test(ctx, local_noise_signal, far_noise_signal, 2000); | |
1287 | |
1288 /* Test 9 part 2 - adjust down */ | |
1289 awgn_init_dbm0(&far_noise_source, 7162534, -55.0f); | |
1290 run_test(ctx, silence, far_noise_signal, 10000); | |
1291 run_test(ctx, local_noise_signal, far_noise_signal, 2000); | |
1292 | |
1293 echo_can_free(ctx); | |
1294 return 0; | |
1295 } | |
1296 /*- End of function --------------------------------------------------------*/ | |
1297 | |
1298 static int perform_test_10a(void) | |
1299 { | |
1300 echo_can_state_t *ctx; | |
1301 | |
1302 /* Test 10 - FAX test during call establishment phase */ | |
1303 /* Test 10A - Canceller operation on the calling station side */ | |
1304 print_test_title("Performing test 10A - Canceller operation on the calling station side\n"); | |
1305 ctx = echo_can_init(TEST_EC_TAPS, 0); | |
1306 | |
1307 fprintf(stderr, "Test 10A not yet implemented\n"); | |
1308 | |
1309 echo_can_free(ctx); | |
1310 return 0; | |
1311 } | |
1312 /*- End of function --------------------------------------------------------*/ | |
1313 | |
1314 static int perform_test_10b(void) | |
1315 { | |
1316 echo_can_state_t *ctx; | |
1317 | |
1318 /* Test 10 - FAX test during call establishment phase */ | |
1319 /* Test 10B - Canceller operation on the called station side */ | |
1320 print_test_title("Performing test 10B - Canceller operation on the called station side\n"); | |
1321 ctx = echo_can_init(TEST_EC_TAPS, 0); | |
1322 | |
1323 fprintf(stderr, "Test 10B not yet implemented\n"); | |
1324 | |
1325 echo_can_free(ctx); | |
1326 return 0; | |
1327 } | |
1328 /*- End of function --------------------------------------------------------*/ | |
1329 | |
1330 static int perform_test_10c(void) | |
1331 { | |
1332 echo_can_state_t *ctx; | |
1333 | |
1334 /* Test 10 - FAX test during call establishment phase */ | |
1335 /* Test 10C - Canceller operation on the calling station side during page | |
1336 transmission and page breaks (for further study) */ | |
1337 print_test_title("Performing test 10C - Canceller operation on the calling station side during page\n" | |
1338 "transmission and page breaks (for further study)\n"); | |
1339 ctx = echo_can_init(TEST_EC_TAPS, 0); | |
1340 | |
1341 fprintf(stderr, "Test 10C not yet implemented\n"); | |
1342 | |
1343 echo_can_free(ctx); | |
1344 return 0; | |
1345 } | |
1346 /*- End of function --------------------------------------------------------*/ | |
1347 | |
1348 static int perform_test_11(void) | |
1349 { | |
1350 echo_can_state_t *ctx; | |
1351 | |
1352 /* Test 11 - Tandem echo canceller test (for further study) */ | |
1353 print_test_title("Performing test 11 - Tandem echo canceller test (for further study)\n"); | |
1354 ctx = echo_can_init(TEST_EC_TAPS, 0); | |
1355 | |
1356 fprintf(stderr, "Test 11 not yet implemented\n"); | |
1357 | |
1358 echo_can_free(ctx); | |
1359 return 0; | |
1360 } | |
1361 /*- End of function --------------------------------------------------------*/ | |
1362 | |
1363 static int perform_test_12(void) | |
1364 { | |
1365 echo_can_state_t *ctx; | |
1366 | |
1367 /* Test 12 - Residual acoustic echo test (for further study) */ | |
1368 print_test_title("Performing test 12 - Residual acoustic echo test (for further study)\n"); | |
1369 ctx = echo_can_init(TEST_EC_TAPS, 0); | |
1370 | |
1371 fprintf(stderr, "Test 12 not yet implemented\n"); | |
1372 | |
1373 echo_can_free(ctx); | |
1374 return 0; | |
1375 } | |
1376 /*- End of function --------------------------------------------------------*/ | |
1377 | |
1378 static int perform_test_13(void) | |
1379 { | |
1380 echo_can_state_t *ctx; | |
1381 | |
1382 /* Test 13 - Performance with ITU-T low-bit rate coders in echo path | |
1383 (Optional, under study) */ | |
1384 print_test_title("Performing test 13 - Performance with ITU-T low-bit rate coders in echo path (Optional, under study)\n"); | |
1385 ctx = echo_can_init(TEST_EC_TAPS, 0); | |
1386 | |
1387 fprintf(stderr, "Test 13 not yet implemented\n"); | |
1388 | |
1389 echo_can_free(ctx); | |
1390 return 0; | |
1391 } | |
1392 /*- End of function --------------------------------------------------------*/ | |
1393 | |
1394 static int perform_test_14(void) | |
1395 { | |
1396 echo_can_state_t *ctx; | |
1397 | |
1398 /* Test 14 - Performance with V-series low-speed data modems */ | |
1399 print_test_title("Performing test 14 - Performance with V-series low-speed data modems\n"); | |
1400 ctx = echo_can_init(TEST_EC_TAPS, 0); | |
1401 | |
1402 fprintf(stderr, "Test 14 not yet implemented\n"); | |
1403 | |
1404 echo_can_free(ctx); | |
1405 return 0; | |
1406 } | |
1407 /*- End of function --------------------------------------------------------*/ | |
1408 | |
1409 static int perform_test_15(void) | |
1410 { | |
1411 echo_can_state_t *ctx; | |
1412 | |
1413 /* Test 15 - PCM offset test (Optional) */ | |
1414 print_test_title("Performing test 15 - PCM offset test (Optional)\n"); | |
1415 ctx = echo_can_init(TEST_EC_TAPS, 0); | |
1416 | |
1417 fprintf(stderr, "Test 15 not yet implemented\n"); | |
1418 | |
1419 echo_can_free(ctx); | |
1420 return 0; | |
1421 } | |
1422 /*- End of function --------------------------------------------------------*/ | |
1423 | |
1424 static int match_test_name(const char *name) | |
1425 { | |
1426 const struct | |
1427 { | |
1428 const char *name; | |
1429 int (*func)(void); | |
1430 } tests[] = | |
1431 { | |
1432 {"sanity", perform_test_sanity}, | |
1433 {"2a", perform_test_2a}, | |
1434 {"2b", perform_test_2b}, | |
1435 {"2ca", perform_test_2ca}, | |
1436 {"3a", perform_test_3a}, | |
1437 {"3ba", perform_test_3ba}, | |
1438 {"3bb", perform_test_3bb}, | |
1439 {"3c", perform_test_3c}, | |
1440 {"4", perform_test_4}, | |
1441 {"5", perform_test_5}, | |
1442 {"6", perform_test_6}, | |
1443 {"7", perform_test_7}, | |
1444 {"8", perform_test_8}, | |
1445 {"9", perform_test_9}, | |
1446 {"10a", perform_test_10a}, | |
1447 {"10b", perform_test_10b}, | |
1448 {"10c", perform_test_10c}, | |
1449 {"11", perform_test_11}, | |
1450 {"12", perform_test_12}, | |
1451 {"13", perform_test_13}, | |
1452 {"14", perform_test_14}, | |
1453 {"15", perform_test_15}, | |
1454 {NULL, NULL} | |
1455 }; | |
1456 int i; | |
1457 | |
1458 for (i = 0; tests[i].name; i++) | |
1459 { | |
1460 if (strcasecmp(name, tests[i].name) == 0) | |
1461 { | |
1462 test_name = name; | |
1463 tests[i].func(); | |
1464 return 0; | |
1465 } | |
1466 } | |
1467 printf("Unknown test name '%s' specified. The known test names are ", name); | |
1468 for (i = 0; tests[i].name; i++) | |
1469 { | |
1470 printf("%s", tests[i].name); | |
1471 if (tests[i + 1].name) | |
1472 printf(", "); | |
1473 } | |
1474 printf("\n"); | |
1475 return -1; | |
1476 } | |
1477 /*- End of function --------------------------------------------------------*/ | |
1478 | |
1479 static void simulate_ec(char *argv[], int two_channel_file, int mode) | |
1480 { | |
1481 echo_can_state_t *ctx; | |
1482 SNDFILE *txfile; | |
1483 SNDFILE *rxfile; | |
1484 SNDFILE *rxtxfile; | |
1485 SNDFILE *ecfile; | |
1486 int ntx; | |
1487 int nrx; | |
1488 int nec; | |
1489 int16_t buf[2]; | |
1490 int16_t rin; | |
1491 int16_t rout; | |
1492 int16_t sin; | |
1493 int16_t sout; | |
1494 int32_t samples; | |
1495 | |
1496 mode |= ECHO_CAN_USE_ADAPTION; | |
1497 txfile = NULL; | |
1498 rxfile = NULL; | |
1499 rxtxfile = NULL; | |
1500 ecfile = NULL; | |
1501 if (two_channel_file) | |
1502 { | |
1503 txfile = sf_open_telephony_read(argv[0], 1); | |
1504 rxfile = sf_open_telephony_read(argv[1], 1); | |
1505 ecfile = sf_open_telephony_write(argv[2], 1); | |
1506 } | |
1507 else | |
1508 { | |
1509 rxtxfile = sf_open_telephony_read(argv[0], 2); | |
1510 ecfile = sf_open_telephony_write(argv[1], 1); | |
1511 } | |
1512 | |
1513 ctx = echo_can_init(TEST_EC_TAPS, 0); | |
1514 echo_can_adaption_mode(ctx, mode); | |
1515 samples = 0; | |
1516 do | |
1517 { | |
1518 if (two_channel_file) | |
1519 { | |
1520 if ((ntx = sf_readf_short(rxtxfile, buf, 1)) < 0) | |
1521 { | |
1522 fprintf(stderr, " Error reading tx sound file\n"); | |
1523 exit(2); | |
1524 } | |
1525 rin = buf[0]; | |
1526 sin = buf[1]; | |
1527 nrx = ntx; | |
1528 } | |
1529 else | |
1530 { | |
1531 if ((ntx = sf_readf_short(txfile, &rin, 1)) < 0) | |
1532 { | |
1533 fprintf(stderr, " Error reading tx sound file\n"); | |
1534 exit(2); | |
1535 } | |
1536 if ((nrx = sf_readf_short(rxfile, &sin, 1)) < 0) | |
1537 { | |
1538 fprintf(stderr, " Error reading rx sound file\n"); | |
1539 exit(2); | |
1540 } | |
1541 } | |
1542 rout = echo_can_hpf_tx(ctx, rin); | |
1543 sout = echo_can_update(ctx, rout, sin); | |
1544 | |
1545 if ((nec = sf_writef_short(ecfile, &sout, 1)) != 1) | |
1546 { | |
1547 fprintf(stderr, " Error writing ec sound file\n"); | |
1548 exit(2); | |
1549 } | |
1550 level_measurements_update(rin, sin, rout, sout, 0); | |
1551 write_log_files(rin, sin); | |
1552 #if defined(ENABLE_GUI) | |
1553 if (use_gui) | |
1554 echo_can_monitor_can_update(ctx->fir_taps16[0], TEST_EC_TAPS); | |
1555 #endif | |
1556 } | |
1557 while (ntx && nrx); | |
1558 dump_ec_state(ctx); | |
1559 | |
1560 echo_can_free(ctx); | |
1561 | |
1562 if (two_channel_file) | |
1563 { | |
1564 sf_close(rxtxfile); | |
1565 } | |
1566 else | |
1567 { | |
1568 sf_close(txfile); | |
1569 sf_close(rxfile); | |
1570 } | |
1571 sf_close(ecfile); | |
1572 } | |
1573 /*- End of function --------------------------------------------------------*/ | |
1574 | |
1575 int main(int argc, char *argv[]) | |
1576 { | |
1577 int i; | |
1578 time_t now; | |
1579 int simulate; | |
1580 int cng; | |
1581 int hpf; | |
1582 int two_channel_file; | |
1583 int opt; | |
1584 int mode; | |
1585 | |
1586 /* Check which tests we should run */ | |
1587 if (argc < 2) | |
1588 fprintf(stderr, "Usage: echo tests [-g] [-m <model number>] [-s] <list of test numbers>\n"); | |
1589 line_model_no = 0; | |
1590 supp_line_model_no = 0; | |
1591 cng = FALSE; | |
1592 hpf = FALSE; | |
1593 use_gui = FALSE; | |
1594 simulate = FALSE; | |
1595 munger = -1; | |
1596 two_channel_file = FALSE; | |
1597 erl = -12.0f; | |
1598 | |
1599 while ((opt = getopt(argc, argv, "2ace:ghm:M:su")) != -1) | |
1600 { | |
1601 switch (opt) | |
1602 { | |
1603 case '2': | |
1604 two_channel_file = TRUE; | |
1605 break; | |
1606 case 'a': | |
1607 munger = G711_ALAW; | |
1608 break; | |
1609 case 'c': | |
1610 cng = TRUE; | |
1611 break; | |
1612 case 'e': | |
1613 /* Allow for ERL being entered as x or -x */ | |
1614 erl = -fabs(atof(optarg)); | |
1615 break; | |
1616 case 'g': | |
1617 #if defined(ENABLE_GUI) | |
1618 use_gui = TRUE; | |
1619 #else | |
1620 fprintf(stderr, "Graphical monitoring not available\n"); | |
1621 exit(2); | |
1622 #endif | |
1623 break; | |
1624 case 'h': | |
1625 hpf = TRUE; | |
1626 break; | |
1627 case 'm': | |
1628 line_model_no = atoi(optarg); | |
1629 break; | |
1630 case 'M': | |
1631 supp_line_model_no = atoi(optarg); | |
1632 break; | |
1633 case 's': | |
1634 simulate = TRUE; | |
1635 break; | |
1636 case 'u': | |
1637 munger = G711_ULAW; | |
1638 break; | |
1639 default: | |
1640 //usage(); | |
1641 exit(2); | |
1642 break; | |
1643 } | |
1644 } | |
1645 argc -= optind; | |
1646 argv += optind; | |
1647 | |
1648 #if defined(ENABLE_GUI) | |
1649 if (use_gui) | |
1650 start_echo_can_monitor(TEST_EC_TAPS); | |
1651 #endif | |
1652 if (simulate) | |
1653 { | |
1654 /* Process a pair of transmitted and received audio files, and produce | |
1655 an echo cancelled audio file. */ | |
1656 if (argc < ((two_channel_file) ? 2 : 3)) | |
1657 { | |
1658 printf("not enough arguments for a simulation\n"); | |
1659 exit(2); | |
1660 } | |
1661 mode = ECHO_CAN_USE_NLP; | |
1662 mode |= ((cng) ? ECHO_CAN_USE_CNG : ECHO_CAN_USE_CLIP); | |
1663 if (hpf) | |
1664 { | |
1665 mode |= ECHO_CAN_USE_TX_HPF; | |
1666 mode |= ECHO_CAN_USE_RX_HPF; | |
1667 } | |
1668 simulate_ec(argv, two_channel_file, mode); | |
1669 } | |
1670 else | |
1671 { | |
1672 /* Run some G.168 tests */ | |
1673 #if defined(ENABLE_GUI) | |
1674 if (use_gui) | |
1675 echo_can_monitor_line_model_update(chan_model.impulse.coeffs, chan_model.impulse.taps); | |
1676 #endif | |
1677 signal_load(&local_css, "sound_c1_8k.wav"); | |
1678 signal_load(&far_css, "sound_c3_8k.wav"); | |
1679 | |
1680 if ((residue_handle = sf_open_telephony_write(RESIDUE_FILE_NAME, 1)) == NULL) | |
1681 { | |
1682 fprintf(stderr, " Failed to open '%s'\n", RESIDUE_FILE_NAME); | |
1683 exit(2); | |
1684 } | |
1685 if (argc <= 0) | |
1686 { | |
1687 printf("No tests specified\n"); | |
1688 } | |
1689 else | |
1690 { | |
1691 time(&now); | |
1692 if ((result_handle = sf_open_telephony_write("echo_tests_result.wav", RESULT_CHANNELS)) == NULL) | |
1693 { | |
1694 fprintf(stderr, " Failed to open result file\n"); | |
1695 exit(2); | |
1696 } | |
1697 result_cur = 0; | |
1698 level_measurements_create(0); | |
1699 for (i = 0; i < argc; i++) | |
1700 { | |
1701 if (channel_model_create(&chan_model, line_model_no, erl, munger)) | |
1702 { | |
1703 fprintf(stderr, " Failed to create line model\n"); | |
1704 exit(2); | |
1705 } | |
1706 match_test_name(argv[i]); | |
1707 } | |
1708 if (sf_close(result_handle) != 0) | |
1709 { | |
1710 fprintf(stderr, " Cannot close speech file '%s'\n", "result_sound.wav"); | |
1711 exit(2); | |
1712 } | |
1713 printf("Run time %lds\n", time(NULL) - now); | |
1714 } | |
1715 signal_free(&local_css); | |
1716 signal_free(&far_css); | |
1717 if (sf_close(residue_handle) != 0) | |
1718 { | |
1719 fprintf(stderr, " Cannot close speech file '%s'\n", RESIDUE_FILE_NAME); | |
1720 exit(2); | |
1721 } | |
1722 } | |
1723 #if defined(ENABLE_GUI) | |
1724 if (use_gui) | |
1725 echo_can_monitor_wait_to_end(); | |
1726 #endif | |
1727 | |
1728 printf("Tests passed.\n"); | |
1729 return 0; | |
1730 } | |
1731 /*- End of function --------------------------------------------------------*/ | |
1732 /*- End of file ------------------------------------------------------------*/ |