Mercurial > hg > audiostuff
comparison spandsp-0.0.6pre17/tests/modem_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 * modem_echo_tests.c | |
5 * | |
6 * Written by Steve Underwood <steveu@coppice.org> | |
7 * | |
8 * Copyright (C) 2004 Steve Underwood | |
9 * | |
10 * All rights reserved. | |
11 * | |
12 * This program is free software; you can redistribute it and/or modify | |
13 * it under the terms of the GNU General Public License version 2, as | |
14 * published by the Free Software Foundation. | |
15 * | |
16 * This program is distributed in the hope that it will be useful, | |
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
19 * GNU General Public License for more details. | |
20 * | |
21 * You should have received a copy of the GNU General Public License | |
22 * along with this program; if not, write to the Free Software | |
23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
24 * | |
25 * $Id: modem_echo_tests.c,v 1.32 2009/05/30 15:23:14 steveu Exp $ | |
26 */ | |
27 | |
28 /*! \page modem_echo_can_tests_page Line echo cancellation for modems tests | |
29 \section modem_echo_can_tests_page_sec_1 What does it do? | |
30 Currently the echo cancellation tests only provide simple exercising of the | |
31 cancellor in the way it might be used for line echo cancellation. The test code | |
32 is in echotests.c. | |
33 | |
34 The goal is to test the echo cancellor again the G.16X specs. Clearly, that also | |
35 means the goal for the cancellor itself is to comply with those specs. Right | |
36 now, the only aspect of these tests implemented is the line impulse response | |
37 models in g168tests.c. | |
38 | |
39 \section modem_echo_can_tests_page_sec_2 How does it work? | |
40 The current test consists of feeding an audio file of real speech to the echo | |
41 cancellor as the transmit signal. A very simple model of a telephone line is | |
42 used to simulate a simple echo from the transmit signal. A second audio file of | |
43 real speech is also used to simulate a signal received form the far end of the | |
44 line. This is gated so it is only placed for one second every 10 seconds, | |
45 simulating the double talk condition. The resulting echo cancelled signal can | |
46 either be store in a file for further analysis, or played back as the data is | |
47 processed. | |
48 | |
49 A number of modified versions of this test have been performed. The signal level | |
50 of the two speech sources has been varied. Several simple models of the | |
51 telephone line have been used. Although the current cancellor design has known | |
52 limitations, it seems stable for all these test conditions. No instability has | |
53 been observed in the current version due to arithmetic overflow when the speech | |
54 is very loud (with earlier versions, well, ....:) ). The lack of saturating | |
55 arithmetic in general purpose CPUs is a huge disadvantage here, as software | |
56 saturation logic would cause a major slow down. Floating point would be good, | |
57 but is not usable in the Linux kernel. Anyway, the bottom line seems to be the | |
58 current design is genuinely useful, if imperfect. | |
59 | |
60 \section modem_echo_can_tests_page_sec_2 How do I use it? | |
61 | |
62 Build the tests with the command "./build". Currently there is no proper make | |
63 setup, or way to build individual tests. "./build" will built all the tests | |
64 which currently exist for the DSP functions. The echo cancellation test assumes | |
65 there are two audio files containing mono, 16 bit signed PCM speech data, sampled | |
66 at 8kHz. These should be called local_sound.wav and far_sound.wav. A third wave | |
67 file will be produced. This very crudely starts with the first 256 bytes from | |
68 the local_sound.wav file, followed by the results of the echo cancellation. The | |
69 resulting audio is also played to the /dev/dsp device. A printf near the end of | |
70 echo_tests.c is commented out with a \#if. If this is enabled, detailed | |
71 information about the results of the echo cancellation will be written to | |
72 stdout. By saving this into a file, Grace (recommended), GnuPlot, or some other | |
73 plotting package may be used to graphically display the functioning of the | |
74 cancellor. | |
75 */ | |
76 | |
77 #if defined(HAVE_CONFIG_H) | |
78 #include "config.h" | |
79 #endif | |
80 | |
81 #if defined(HAVE_FL_FL_H) && defined(HAVE_FL_FL_CARTESIAN_H) && defined(HAVE_FL_FL_AUDIO_METER_H) | |
82 #define ENABLE_GUI | |
83 #endif | |
84 | |
85 #include <stdlib.h> | |
86 #include <stdio.h> | |
87 #include <fcntl.h> | |
88 #include <string.h> | |
89 #include <time.h> | |
90 #include <sndfile.h> | |
91 #if defined(HAVE_MATH_H) | |
92 #define GEN_CONST | |
93 #endif | |
94 | |
95 //#if defined(WITH_SPANDSP_INTERNALS) | |
96 #define SPANDSP_EXPOSE_INTERNAL_STRUCTURES | |
97 //#endif | |
98 | |
99 #include "spandsp.h" | |
100 #include "spandsp/g168models.h" | |
101 #include "spandsp-sim.h" | |
102 | |
103 #if defined(ENABLE_GUI) | |
104 #include "echo_monitor.h" | |
105 #endif | |
106 | |
107 #if !defined(NULL) | |
108 #define NULL (void *) 0 | |
109 #endif | |
110 | |
111 typedef struct | |
112 { | |
113 const char *name; | |
114 int max; | |
115 int cur; | |
116 SNDFILE *handle; | |
117 int16_t signal[8000]; | |
118 } signal_source_t; | |
119 | |
120 signal_source_t local_css; | |
121 | |
122 fir32_state_t line_model; | |
123 | |
124 SNDFILE *resulthandle; | |
125 int16_t residue_sound[8000]; | |
126 int residue_cur = 0; | |
127 int do_codec_munge = TRUE; | |
128 int use_gui = FALSE; | |
129 | |
130 static const int16_t tone_1khz[] = {10362, 7327, 0, -7327, -10362, -7327, 0, 7327}; | |
131 | |
132 static inline void put_residue(int16_t tx, int16_t residue) | |
133 { | |
134 int outframes; | |
135 | |
136 residue_sound[residue_cur++] = tx; | |
137 residue_sound[residue_cur++] = residue; | |
138 if (residue_cur >= 8000) | |
139 { | |
140 residue_cur >>= 1; | |
141 outframes = sf_writef_short(resulthandle, residue_sound, residue_cur); | |
142 if (outframes != residue_cur) | |
143 { | |
144 fprintf(stderr, " Error writing residue sound\n"); | |
145 exit(2); | |
146 } | |
147 residue_cur = 0; | |
148 } | |
149 } | |
150 /*- End of function --------------------------------------------------------*/ | |
151 | |
152 static void signal_load(signal_source_t *sig, const char *name) | |
153 { | |
154 sig->name = name; | |
155 if ((sig->handle = sf_open_telephony_read(sig->name, 1)) == NULL) | |
156 { | |
157 fprintf(stderr, " Cannot open sound file '%s'\n", sig->name); | |
158 exit(2); | |
159 } | |
160 sig->max = sf_readf_short(sig->handle, sig->signal, 8000); | |
161 if (sig->max < 0) | |
162 { | |
163 fprintf(stderr, " Error reading sound file '%s'\n", sig->name); | |
164 exit(2); | |
165 } | |
166 } | |
167 /*- End of function --------------------------------------------------------*/ | |
168 | |
169 static void signal_free(signal_source_t *sig) | |
170 { | |
171 if (sf_close(sig->handle) != 0) | |
172 { | |
173 fprintf(stderr, " Cannot close sound file '%s'\n", sig->name); | |
174 exit(2); | |
175 } | |
176 } | |
177 /*- End of function --------------------------------------------------------*/ | |
178 | |
179 static void signal_restart(signal_source_t *sig) | |
180 { | |
181 sig->cur = 0; | |
182 } | |
183 /*- End of function --------------------------------------------------------*/ | |
184 | |
185 static int16_t signal_amp(signal_source_t *sig) | |
186 { | |
187 int16_t tx; | |
188 | |
189 tx = sig->signal[sig->cur++]; | |
190 if (sig->cur >= sig->max) | |
191 sig->cur = 0; | |
192 return tx; | |
193 } | |
194 /*- End of function --------------------------------------------------------*/ | |
195 | |
196 static inline int16_t codec_munger(int16_t amp) | |
197 { | |
198 if (do_codec_munge) | |
199 return alaw_to_linear(linear_to_alaw(amp)); | |
200 return amp; | |
201 } | |
202 /*- End of function --------------------------------------------------------*/ | |
203 | |
204 static void channel_model_create(int model) | |
205 { | |
206 static const int32_t *line_models[] = | |
207 { | |
208 line_model_d2_coeffs, | |
209 line_model_d3_coeffs, | |
210 line_model_d4_coeffs, | |
211 line_model_d5_coeffs, | |
212 line_model_d6_coeffs, | |
213 line_model_d7_coeffs, | |
214 line_model_d8_coeffs, | |
215 line_model_d9_coeffs | |
216 }; | |
217 | |
218 static int line_model_sizes[] = | |
219 { | |
220 sizeof(line_model_d2_coeffs)/sizeof(int32_t), | |
221 sizeof(line_model_d3_coeffs)/sizeof(int32_t), | |
222 sizeof(line_model_d4_coeffs)/sizeof(int32_t), | |
223 sizeof(line_model_d5_coeffs)/sizeof(int32_t), | |
224 sizeof(line_model_d6_coeffs)/sizeof(int32_t), | |
225 sizeof(line_model_d7_coeffs)/sizeof(int32_t), | |
226 sizeof(line_model_d8_coeffs)/sizeof(int32_t), | |
227 sizeof(line_model_d9_coeffs)/sizeof(int32_t) | |
228 }; | |
229 | |
230 fir32_create(&line_model, line_models[model], line_model_sizes[model]); | |
231 } | |
232 /*- End of function --------------------------------------------------------*/ | |
233 | |
234 static int16_t channel_model(int16_t local, int16_t far) | |
235 { | |
236 int16_t echo; | |
237 int16_t rx; | |
238 | |
239 /* Channel modelling is merely simulating the effects of A-law distortion | |
240 and using one of the echo models from G.168 */ | |
241 | |
242 /* The local tx signal will have gone through an A-law munging before | |
243 it reached the line's analogue area where the echo occurs. */ | |
244 echo = fir32(&line_model, codec_munger(local/8)); | |
245 /* The far end signal will have been through an A-law munging, although | |
246 this should not affect things. */ | |
247 rx = echo + codec_munger(far); | |
248 /* This mixed echo and far end signal will have been through an A-law munging when it came back into | |
249 the digital network. */ | |
250 rx = codec_munger(rx); | |
251 return rx; | |
252 } | |
253 /*- End of function --------------------------------------------------------*/ | |
254 | |
255 int main(int argc, char *argv[]) | |
256 { | |
257 modem_echo_can_state_t *ctx; | |
258 //awgn_state_t local_noise_source; | |
259 awgn_state_t far_noise_source; | |
260 int i; | |
261 int clean; | |
262 int16_t rx; | |
263 int16_t tx; | |
264 int local_cur; | |
265 int far_cur; | |
266 int result_cur; | |
267 int line_model_no; | |
268 time_t now; | |
269 power_meter_t power_before; | |
270 power_meter_t power_after; | |
271 float unadapted_output_power; | |
272 float unadapted_echo_power; | |
273 float adapted_output_power; | |
274 float adapted_echo_power; | |
275 #if defined(ENABLE_GUI) | |
276 int16_t amp[2]; | |
277 #endif | |
278 | |
279 line_model_no = 0; | |
280 use_gui = FALSE; | |
281 for (i = 1; i < argc; i++) | |
282 { | |
283 if (strcmp(argv[i], "-g") == 0) | |
284 { | |
285 use_gui = TRUE; | |
286 continue; | |
287 } | |
288 line_model_no = atoi(argv[1]); | |
289 } | |
290 time(&now); | |
291 ctx = modem_echo_can_create(256); | |
292 awgn_init_dbm0(&far_noise_source, 7162534, -50.0f); | |
293 | |
294 signal_load(&local_css, "sound_c1_8k.wav"); | |
295 | |
296 if ((resulthandle = sf_open_telephony_write("modem_echo.wav", 2)) == NULL) | |
297 { | |
298 fprintf(stderr, " Failed to open result file\n"); | |
299 exit(2); | |
300 } | |
301 | |
302 #if defined(ENABLE_GUI) | |
303 if (use_gui) | |
304 start_echo_can_monitor(256); | |
305 #endif | |
306 | |
307 local_cur = 0; | |
308 far_cur = 0; | |
309 result_cur = 0; | |
310 channel_model_create(line_model_no); | |
311 #if defined(ENABLE_GUI) | |
312 if (use_gui) | |
313 echo_can_monitor_line_model_update(line_model.coeffs, line_model.taps); | |
314 #endif | |
315 | |
316 modem_echo_can_flush(ctx); | |
317 | |
318 power_meter_init(&power_before, 5); | |
319 power_meter_init(&power_after, 5); | |
320 | |
321 /* Measure the echo power before adaption */ | |
322 modem_echo_can_adaption_mode(ctx, FALSE); | |
323 for (i = 0; i < 8000*5; i++) | |
324 { | |
325 tx = tone_1khz[i & 7]; | |
326 rx = channel_model(tx, 0); | |
327 clean = modem_echo_can_update(ctx, tx, rx); | |
328 power_meter_update(&power_before, rx); | |
329 power_meter_update(&power_after, clean); | |
330 } | |
331 unadapted_output_power = power_meter_current_dbm0(&power_before); | |
332 unadapted_echo_power = power_meter_current_dbm0(&power_after); | |
333 printf("Pre-adaption: output power %10.5fdBm0, echo power %10.5fdBm0\n", unadapted_output_power, unadapted_echo_power); | |
334 | |
335 /* Converge the canceller */ | |
336 signal_restart(&local_css); | |
337 modem_echo_can_adaption_mode(ctx, TRUE); | |
338 for (i = 0; i < 800*2; i++) | |
339 { | |
340 clean = modem_echo_can_update(ctx, 0, 0); | |
341 put_residue(0, clean); | |
342 } | |
343 | |
344 for (i = 0; i < 8000*50; i++) | |
345 { | |
346 tx = signal_amp(&local_css); | |
347 rx = channel_model(tx, 0); | |
348 clean = modem_echo_can_update(ctx, tx, rx); | |
349 power_meter_update(&power_before, rx); | |
350 power_meter_update(&power_after, clean); | |
351 #if 0 | |
352 if (i%800 == 0) | |
353 printf("Powers %10.5fdBm0 %10.5fdBm0\n", power_meter_current_dbm0(&power_before), power_meter_current_dbm0(&power_after)); | |
354 #endif | |
355 put_residue(tx, clean); | |
356 #if defined(ENABLE_GUI) | |
357 if (use_gui) | |
358 { | |
359 echo_can_monitor_can_update(ctx->fir_taps16, 256); | |
360 amp[0] = tx; | |
361 echo_can_monitor_line_spectrum_update(amp, 1); | |
362 } | |
363 #endif | |
364 } | |
365 | |
366 /* Now lets see how well adapted we are */ | |
367 modem_echo_can_adaption_mode(ctx, FALSE); | |
368 for (i = 0; i < 8000*5; i++) | |
369 { | |
370 tx = tone_1khz[i & 7]; | |
371 rx = channel_model(tx, 0); | |
372 clean = modem_echo_can_update(ctx, tx, rx); | |
373 power_meter_update(&power_before, rx); | |
374 power_meter_update(&power_after, clean); | |
375 } | |
376 adapted_output_power = power_meter_current_dbm0(&power_before); | |
377 adapted_echo_power = power_meter_current_dbm0(&power_after); | |
378 printf("Post-adaption: output power %10.5fdBm0, echo power %10.5fdBm0\n", adapted_output_power, adapted_echo_power); | |
379 | |
380 if (fabsf(adapted_output_power - unadapted_output_power) > 0.1f | |
381 || | |
382 adapted_echo_power > unadapted_echo_power - 30.0f) | |
383 { | |
384 printf("Tests failed.\n"); | |
385 exit(2); | |
386 } | |
387 | |
388 modem_echo_can_free(ctx); | |
389 signal_free(&local_css); | |
390 | |
391 if (sf_close(resulthandle) != 0) | |
392 { | |
393 fprintf(stderr, " Cannot close speech file '%s'\n", "result_sound.wav"); | |
394 exit(2); | |
395 } | |
396 | |
397 #if defined(ENABLE_GUI) | |
398 if (use_gui) | |
399 echo_can_monitor_wait_to_end(); | |
400 #endif | |
401 | |
402 printf("Tests passed.\n"); | |
403 return 0; | |
404 } | |
405 /*- End of function --------------------------------------------------------*/ | |
406 /*- End of file ------------------------------------------------------------*/ |