Mercurial > hg > audiostuff
comparison spandsp-0.0.6pre17/tests/fax_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 * fax_tests.c | |
5 * | |
6 * Written by Steve Underwood <steveu@coppice.org> | |
7 * | |
8 * Copyright (C) 2003 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: fax_tests.c,v 1.102 2009/05/30 15:23:13 steveu Exp $ | |
26 */ | |
27 | |
28 /*! \page fax_tests_page FAX tests | |
29 \section fax_tests_page_sec_1 What does it do? | |
30 \section fax_tests_page_sec_2 How does it work? | |
31 */ | |
32 | |
33 #if defined(HAVE_CONFIG_H) | |
34 #include "config.h" | |
35 #endif | |
36 | |
37 #include <stdlib.h> | |
38 #include <stdio.h> | |
39 #include <unistd.h> | |
40 #include <string.h> | |
41 #include <assert.h> | |
42 #include <sndfile.h> | |
43 | |
44 //#if defined(WITH_SPANDSP_INTERNALS) | |
45 #define SPANDSP_EXPOSE_INTERNAL_STRUCTURES | |
46 //#endif | |
47 | |
48 #include "spandsp.h" | |
49 #include "spandsp-sim.h" | |
50 | |
51 #include "fax_utils.h" | |
52 | |
53 #define SAMPLES_PER_CHUNK 160 | |
54 | |
55 #define INPUT_TIFF_FILE_NAME "../test-data/itu/fax/itutests.tif" | |
56 | |
57 #define OUTPUT_FILE_NAME_WAVE "fax_tests.wav" | |
58 | |
59 #define FAX_MACHINES 2 | |
60 | |
61 struct machine_s | |
62 { | |
63 int chan; | |
64 int16_t amp[SAMPLES_PER_CHUNK]; | |
65 int len; | |
66 fax_state_t *fax; | |
67 awgn_state_t *awgn; | |
68 int done; | |
69 int succeeded; | |
70 char tag[50]; | |
71 int error_delay; | |
72 int total_audio_time; | |
73 } machines[FAX_MACHINES]; | |
74 | |
75 int use_receiver_not_ready = FALSE; | |
76 int test_local_interrupt = FALSE; | |
77 int t30_state_to_wreck = -1; | |
78 | |
79 static int phase_b_handler(t30_state_t *s, void *user_data, int result) | |
80 { | |
81 int i; | |
82 char tag[20]; | |
83 | |
84 i = (int) (intptr_t) user_data; | |
85 snprintf(tag, sizeof(tag), "%c: Phase B:", i); | |
86 printf("%c: Phase B handler on channel %c - (0x%X) %s\n", i, i, result, t30_frametype(result)); | |
87 log_rx_parameters(s, tag); | |
88 return T30_ERR_OK; | |
89 } | |
90 /*- End of function --------------------------------------------------------*/ | |
91 | |
92 static int phase_d_handler(t30_state_t *s, void *user_data, int result) | |
93 { | |
94 int i; | |
95 char tag[20]; | |
96 | |
97 i = (int) (intptr_t) user_data; | |
98 snprintf(tag, sizeof(tag), "%c: Phase D:", i); | |
99 printf("%c: Phase D handler on channel %c - (0x%X) %s\n", i, i, result, t30_frametype(result)); | |
100 log_transfer_statistics(s, tag); | |
101 log_tx_parameters(s, tag); | |
102 log_rx_parameters(s, tag); | |
103 | |
104 if (use_receiver_not_ready) | |
105 t30_set_receiver_not_ready(s, 3); | |
106 | |
107 if (test_local_interrupt) | |
108 { | |
109 if (i == 0) | |
110 { | |
111 printf("%d: Initiating interrupt request\n", i); | |
112 t30_local_interrupt_request(s, TRUE); | |
113 } | |
114 else | |
115 { | |
116 switch (result) | |
117 { | |
118 case T30_PIP: | |
119 case T30_PRI_MPS: | |
120 case T30_PRI_EOM: | |
121 case T30_PRI_EOP: | |
122 printf("%d: Accepting interrupt request\n", i); | |
123 t30_local_interrupt_request(s, TRUE); | |
124 break; | |
125 case T30_PIN: | |
126 break; | |
127 } | |
128 } | |
129 } | |
130 return T30_ERR_OK; | |
131 } | |
132 /*- End of function --------------------------------------------------------*/ | |
133 | |
134 static void phase_e_handler(t30_state_t *s, void *user_data, int result) | |
135 { | |
136 int i; | |
137 t30_stats_t t; | |
138 char tag[20]; | |
139 | |
140 i = (intptr_t) user_data; | |
141 snprintf(tag, sizeof(tag), "%c: Phase E:", i); | |
142 printf("%c: Phase E handler on channel %c - (%d) %s\n", i, i, result, t30_completion_code_to_str(result)); | |
143 log_transfer_statistics(s, tag); | |
144 log_tx_parameters(s, tag); | |
145 log_rx_parameters(s, tag); | |
146 t30_get_transfer_statistics(s, &t); | |
147 machines[i - 'A'].succeeded = (result == T30_ERR_OK) && (t.pages_tx == 12 || t.pages_rx == 12); | |
148 machines[i - 'A'].done = TRUE; | |
149 } | |
150 /*- End of function --------------------------------------------------------*/ | |
151 | |
152 static void real_time_frame_handler(t30_state_t *s, | |
153 void *user_data, | |
154 int direction, | |
155 const uint8_t *msg, | |
156 int len) | |
157 { | |
158 int i; | |
159 | |
160 i = (intptr_t) user_data; | |
161 printf("%c: Real time frame handler on channel %c - %s, %s, length = %d\n", | |
162 i, | |
163 i, | |
164 (direction) ? "line->T.30" : "T.30->line", | |
165 t30_frametype(msg[2]), | |
166 len); | |
167 } | |
168 /*- End of function --------------------------------------------------------*/ | |
169 | |
170 static int document_handler(t30_state_t *s, void *user_data, int event) | |
171 { | |
172 int i; | |
173 | |
174 i = (intptr_t) user_data; | |
175 printf("%c: Document handler on channel %c - event %d\n", i, i, event); | |
176 return FALSE; | |
177 } | |
178 /*- End of function --------------------------------------------------------*/ | |
179 | |
180 int main(int argc, char *argv[]) | |
181 { | |
182 SNDFILE *wave_handle; | |
183 SNDFILE *input_wave_handle; | |
184 int i; | |
185 int j; | |
186 int k; | |
187 struct machine_s *mc; | |
188 int outframes; | |
189 char buf[128 + 1]; | |
190 int16_t silence[SAMPLES_PER_CHUNK]; | |
191 int16_t out_amp[2*SAMPLES_PER_CHUNK]; | |
192 int alldone; | |
193 const char *input_tiff_file_name; | |
194 const char *input_audio_file_name; | |
195 int log_audio; | |
196 int use_ecm; | |
197 int use_tep; | |
198 int use_transmit_on_idle; | |
199 int use_line_hits; | |
200 int polled_mode; | |
201 int reverse_flow; | |
202 int use_page_limits; | |
203 int supported_modems; | |
204 int signal_level; | |
205 int noise_level; | |
206 float signal_scaling; | |
207 time_t start_time; | |
208 time_t end_time; | |
209 char *page_header_info; | |
210 int opt; | |
211 t30_state_t *t30; | |
212 logging_state_t *logging; | |
213 | |
214 log_audio = FALSE; | |
215 input_tiff_file_name = INPUT_TIFF_FILE_NAME; | |
216 input_audio_file_name = NULL; | |
217 use_ecm = FALSE; | |
218 use_line_hits = FALSE; | |
219 use_tep = FALSE; | |
220 polled_mode = FALSE; | |
221 page_header_info = NULL; | |
222 reverse_flow = FALSE; | |
223 use_transmit_on_idle = TRUE; | |
224 use_receiver_not_ready = FALSE; | |
225 use_page_limits = FALSE; | |
226 signal_level = 0; | |
227 noise_level = -99; | |
228 supported_modems = T30_SUPPORT_V27TER | T30_SUPPORT_V29 | T30_SUPPORT_V17; | |
229 while ((opt = getopt(argc, argv, "ehH:i:I:lm:n:prRs:tTw:")) != -1) | |
230 { | |
231 switch (opt) | |
232 { | |
233 case 'e': | |
234 use_ecm = TRUE; | |
235 break; | |
236 case 'h': | |
237 use_line_hits = TRUE; | |
238 break; | |
239 case 'H': | |
240 page_header_info = optarg; | |
241 break; | |
242 case 'i': | |
243 input_tiff_file_name = optarg; | |
244 break; | |
245 case 'I': | |
246 input_audio_file_name = optarg; | |
247 break; | |
248 case 'l': | |
249 log_audio = TRUE; | |
250 break; | |
251 case 'm': | |
252 supported_modems = atoi(optarg); | |
253 break; | |
254 case 'n': | |
255 noise_level = atoi(optarg); | |
256 break; | |
257 case 'p': | |
258 polled_mode = TRUE; | |
259 break; | |
260 case 'r': | |
261 reverse_flow = TRUE; | |
262 break; | |
263 case 'R': | |
264 use_receiver_not_ready = TRUE; | |
265 break; | |
266 case 's': | |
267 signal_level = atoi(optarg); | |
268 break; | |
269 case 't': | |
270 use_tep = TRUE; | |
271 break; | |
272 case 'T': | |
273 use_page_limits = TRUE; | |
274 break; | |
275 case 'w': | |
276 t30_state_to_wreck = atoi(optarg); | |
277 break; | |
278 default: | |
279 //usage(); | |
280 exit(2); | |
281 break; | |
282 } | |
283 } | |
284 | |
285 input_wave_handle = NULL; | |
286 if (input_audio_file_name) | |
287 { | |
288 if ((input_wave_handle = sf_open_telephony_read(input_audio_file_name, 1)) == NULL) | |
289 { | |
290 fprintf(stderr, " Cannot open audio file '%s'\n", input_audio_file_name); | |
291 exit(2); | |
292 } | |
293 } | |
294 | |
295 wave_handle = NULL; | |
296 if (log_audio) | |
297 { | |
298 if ((wave_handle = sf_open_telephony_write(OUTPUT_FILE_NAME_WAVE, 2)) == NULL) | |
299 { | |
300 fprintf(stderr, " Cannot create audio file '%s'\n", OUTPUT_FILE_NAME_WAVE); | |
301 exit(2); | |
302 } | |
303 } | |
304 | |
305 memset(silence, 0, sizeof(silence)); | |
306 for (j = 0; j < FAX_MACHINES; j++) | |
307 { | |
308 machines[j].chan = j; | |
309 mc = &machines[j]; | |
310 | |
311 i = mc->chan + 1; | |
312 sprintf(buf, "%d%d%d%d%d%d%d%d", i, i, i, i, i, i, i, i); | |
313 if (reverse_flow) | |
314 mc->fax = fax_init(NULL, (mc->chan & 1) ? TRUE : FALSE); | |
315 else | |
316 mc->fax = fax_init(NULL, (mc->chan & 1) ? FALSE : TRUE); | |
317 mc->awgn = NULL; | |
318 signal_scaling = 1.0f; | |
319 if (noise_level > -99) | |
320 { | |
321 mc->awgn = awgn_init_dbm0(NULL, 1234567, noise_level); | |
322 signal_scaling = powf(10.0f, signal_level/20.0f); | |
323 printf("Signal scaling %f\n", signal_scaling); | |
324 } | |
325 fax_set_transmit_on_idle(mc->fax, use_transmit_on_idle); | |
326 fax_set_tep_mode(mc->fax, use_tep); | |
327 t30 = fax_get_t30_state(mc->fax); | |
328 t30_set_tx_ident(t30, buf); | |
329 t30_set_tx_sub_address(t30, "Sub-address"); | |
330 t30_set_tx_sender_ident(t30, "Sender ID"); | |
331 t30_set_tx_password(t30, "Password"); | |
332 t30_set_tx_polled_sub_address(t30, "Polled sub-address"); | |
333 t30_set_tx_selective_polling_address(t30, "Selective polling address"); | |
334 t30_set_tx_page_header_info(t30, page_header_info); | |
335 t30_set_tx_nsf(t30, (const uint8_t *) "\x50\x00\x00\x00Spandsp\x00", 12); | |
336 t30_set_ecm_capability(t30, use_ecm); | |
337 t30_set_supported_t30_features(t30, | |
338 T30_SUPPORT_IDENTIFICATION | |
339 | T30_SUPPORT_SELECTIVE_POLLING | |
340 | T30_SUPPORT_SUB_ADDRESSING); | |
341 | |
342 if ((mc->chan & 1)) | |
343 t30_set_minimum_scan_line_time(t30, 40); | |
344 t30_set_supported_image_sizes(t30, | |
345 T30_SUPPORT_US_LETTER_LENGTH | |
346 | T30_SUPPORT_US_LEGAL_LENGTH | |
347 | T30_SUPPORT_UNLIMITED_LENGTH | |
348 | T30_SUPPORT_215MM_WIDTH | |
349 | T30_SUPPORT_255MM_WIDTH | |
350 | T30_SUPPORT_303MM_WIDTH); | |
351 t30_set_supported_resolutions(t30, | |
352 T30_SUPPORT_STANDARD_RESOLUTION | |
353 | T30_SUPPORT_FINE_RESOLUTION | |
354 | T30_SUPPORT_SUPERFINE_RESOLUTION | |
355 | T30_SUPPORT_R8_RESOLUTION | |
356 | T30_SUPPORT_R16_RESOLUTION | |
357 | T30_SUPPORT_300_300_RESOLUTION | |
358 | T30_SUPPORT_400_400_RESOLUTION | |
359 | T30_SUPPORT_600_600_RESOLUTION | |
360 | T30_SUPPORT_1200_1200_RESOLUTION | |
361 | T30_SUPPORT_300_600_RESOLUTION | |
362 | T30_SUPPORT_400_800_RESOLUTION | |
363 | T30_SUPPORT_600_1200_RESOLUTION); | |
364 t30_set_supported_modems(t30, supported_modems); | |
365 if (use_ecm) | |
366 t30_set_supported_compressions(t30, T30_SUPPORT_T4_1D_COMPRESSION | T30_SUPPORT_T4_2D_COMPRESSION | T30_SUPPORT_T6_COMPRESSION); | |
367 if ((mc->chan & 1)) | |
368 { | |
369 if (polled_mode) | |
370 { | |
371 if (use_page_limits) | |
372 t30_set_tx_file(t30, input_tiff_file_name, 3, 6); | |
373 else | |
374 t30_set_tx_file(t30, input_tiff_file_name, -1, -1); | |
375 } | |
376 else | |
377 { | |
378 sprintf(buf, "fax_tests_%d.tif", (mc->chan + 1)/2); | |
379 t30_set_rx_file(t30, buf, -1); | |
380 t30_set_rx_encoding(t30, T4_COMPRESSION_ITU_T6); | |
381 } | |
382 } | |
383 else | |
384 { | |
385 if (polled_mode) | |
386 { | |
387 sprintf(buf, "fax_tests_%d.tif", (mc->chan + 1)/2); | |
388 t30_set_rx_file(t30, buf, -1); | |
389 t30_set_rx_encoding(t30, T4_COMPRESSION_ITU_T6); | |
390 } | |
391 else | |
392 { | |
393 if (use_page_limits) | |
394 t30_set_tx_file(t30, input_tiff_file_name, 3, 6); | |
395 else | |
396 t30_set_tx_file(t30, input_tiff_file_name, -1, -1); | |
397 } | |
398 } | |
399 t30_set_phase_b_handler(t30, phase_b_handler, (void *) (intptr_t) mc->chan + 'A'); | |
400 t30_set_phase_d_handler(t30, phase_d_handler, (void *) (intptr_t) mc->chan + 'A'); | |
401 t30_set_phase_e_handler(t30, phase_e_handler, (void *) (intptr_t) mc->chan + 'A'); | |
402 t30_set_real_time_frame_handler(t30, real_time_frame_handler, (void *) (intptr_t) mc->chan + 'A'); | |
403 t30_set_document_handler(t30, document_handler, (void *) (intptr_t) mc->chan + 'A'); | |
404 sprintf(mc->tag, "FAX-%d", j + 1); | |
405 | |
406 logging = t30_get_logging_state(t30); | |
407 span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME | SPAN_LOG_FLOW); | |
408 span_log_set_tag(logging, mc->tag); | |
409 span_log_set_level(&t30->t4.logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME | SPAN_LOG_FLOW); | |
410 span_log_set_tag(&t30->t4.logging, mc->tag); | |
411 | |
412 logging = fax_get_logging_state(mc->fax); | |
413 span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME | SPAN_LOG_FLOW); | |
414 span_log_set_tag(logging, mc->tag); | |
415 | |
416 memset(mc->amp, 0, sizeof(mc->amp)); | |
417 mc->total_audio_time = 0; | |
418 mc->done = FALSE; | |
419 } | |
420 time(&start_time); | |
421 for (;;) | |
422 { | |
423 alldone = TRUE; | |
424 for (j = 0; j < FAX_MACHINES; j++) | |
425 { | |
426 mc = &machines[j]; | |
427 | |
428 if ((j & 1) == 0 && input_audio_file_name) | |
429 { | |
430 mc->len = sf_readf_short(input_wave_handle, mc->amp, SAMPLES_PER_CHUNK); | |
431 if (mc->len == 0) | |
432 break; | |
433 } | |
434 else | |
435 { | |
436 mc->len = fax_tx(mc->fax, mc->amp, SAMPLES_PER_CHUNK); | |
437 if (mc->awgn) | |
438 { | |
439 for (k = 0; k < mc->len; k++) | |
440 mc->amp[k] = ((int16_t) (mc->amp[k]*signal_scaling)) + awgn(mc->awgn); | |
441 } | |
442 } | |
443 mc->total_audio_time += SAMPLES_PER_CHUNK; | |
444 if (!use_transmit_on_idle) | |
445 { | |
446 /* The receive side always expects a full block of samples, but the | |
447 transmit side may not be sending any when it doesn't need to. We | |
448 may need to pad with some silence. */ | |
449 if (mc->len < SAMPLES_PER_CHUNK) | |
450 { | |
451 memset(mc->amp + mc->len, 0, sizeof(int16_t)*(SAMPLES_PER_CHUNK - mc->len)); | |
452 mc->len = SAMPLES_PER_CHUNK; | |
453 } | |
454 } | |
455 t30 = fax_get_t30_state(mc->fax); | |
456 logging = t30_get_logging_state(t30); | |
457 span_log_bump_samples(logging, mc->len); | |
458 logging = fax_get_logging_state(mc->fax); | |
459 span_log_bump_samples(logging, mc->len); | |
460 | |
461 if (log_audio) | |
462 { | |
463 for (k = 0; k < mc->len; k++) | |
464 out_amp[2*k + j] = mc->amp[k]; | |
465 } | |
466 if (machines[j ^ 1].len < SAMPLES_PER_CHUNK) | |
467 memset(machines[j ^ 1].amp + machines[j ^ 1].len, 0, sizeof(int16_t)*(SAMPLES_PER_CHUNK - machines[j ^ 1].len)); | |
468 t30 = fax_get_t30_state(mc->fax); | |
469 #if defined(WITH_SPANDSP_INTERNALS) | |
470 if (use_line_hits) | |
471 { | |
472 /* TODO: This applies very crude line hits. improve it */ | |
473 if (t30->state == 22) | |
474 { | |
475 if (++mc->error_delay == 100) | |
476 { | |
477 fprintf(stderr, "HIT %d!\n", j); | |
478 mc->error_delay = 0; | |
479 for (k = 0; k < 5; k++) | |
480 mc->amp[k] = 0; | |
481 } | |
482 } | |
483 } | |
484 if (t30->state == t30_state_to_wreck) | |
485 memset(machines[j ^ 1].amp, 0, sizeof(int16_t)*SAMPLES_PER_CHUNK); | |
486 #endif | |
487 if (fax_rx(mc->fax, machines[j ^ 1].amp, SAMPLES_PER_CHUNK)) | |
488 break; | |
489 if (!mc->done) | |
490 alldone = FALSE; | |
491 } | |
492 | |
493 if (log_audio) | |
494 { | |
495 outframes = sf_writef_short(wave_handle, out_amp, SAMPLES_PER_CHUNK); | |
496 if (outframes != SAMPLES_PER_CHUNK) | |
497 break; | |
498 } | |
499 | |
500 if (alldone || j < FAX_MACHINES) | |
501 break; | |
502 } | |
503 time(&end_time); | |
504 for (j = 0; j < FAX_MACHINES; j++) | |
505 { | |
506 mc = &machines[j]; | |
507 fax_release(mc->fax); | |
508 } | |
509 if (log_audio) | |
510 { | |
511 if (sf_close(wave_handle)) | |
512 { | |
513 fprintf(stderr, " Cannot close audio file '%s'\n", OUTPUT_FILE_NAME_WAVE); | |
514 exit(2); | |
515 } | |
516 } | |
517 if (input_audio_file_name) | |
518 { | |
519 if (sf_close(input_wave_handle)) | |
520 { | |
521 fprintf(stderr, " Cannot close audio file '%s'\n", input_audio_file_name); | |
522 exit(2); | |
523 } | |
524 } | |
525 printf("Total audio time = %ds (wall time %ds)\n", machines[0].total_audio_time/8000, (int) (end_time - start_time)); | |
526 return 0; | |
527 } | |
528 /*- End of function --------------------------------------------------------*/ | |
529 /*- End of file ------------------------------------------------------------*/ |