xpp: fix manpage of astribank_hexload
[dahdi/tools.git] / dahdi_monitor.c
1 /*
2  * Monitor a DAHDI Channel
3  *
4  * Written by Mark Spencer <markster@digium.com>
5  * Based on previous works, designs, and architectures conceived and
6  * written by Jim Dixon <jim@lambdatel.com>.
7  *
8  * Copyright (C) 2001 Jim Dixon / Zapata Telephony.
9  * Copyright (C) 2001-2008 Digium, Inc.
10  *
11  * All rights reserved.
12  *
13  */
14
15 /*
16  * See http://www.asterisk.org for more information about
17  * the Asterisk project. Please do not directly contact
18  * any of the maintainers of this project for assistance;
19  * the project provides a web site, mailing lists and IRC
20  * channels for your use.
21  *
22  * This program is free software, distributed under the terms of
23  * the GNU General Public License Version 2 as published by the
24  * Free Software Foundation. See the LICENSE file included with
25  * this program for more details.
26  */
27
28 #include <stdio.h>
29 #include <getopt.h>
30 #include <string.h>
31 #include <stdarg.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <sys/ioctl.h>
35 #include <sys/time.h>
36 #include <fcntl.h>
37 #include <errno.h>
38 #include <ctype.h>
39 #include <signal.h>
40
41 #include <dahdi/user.h>
42 #include "dahdi_tools_version.h"
43 #include "wavformat.h"
44 #include "autoconfig.h"
45
46 #ifdef HAVE_SYS_SOUNDCARD_H
47 # include <sys/soundcard.h>
48 #else
49 # ifdef HAVE_LINUX_SOUNDCARD_H
50 #  include <linux/soundcard.h>
51 # else
52 #  error "Your installation appears to be missing soundcard.h which is needed to continue."
53 # endif
54 #endif
55
56 /*
57 * defines for file handle numbers
58 */
59 #define MON_BRX            0    /*!< both channels if multichannel==1 or receive otherwise */
60 #define MON_TX             1    /*!< transmit channel */
61 #define MON_PRE_BRX        2    /*!< same as MON_BRX but before echo cancellation */
62 #define MON_PRE_TX         3    /*!< same as MON_TX but before echo cancellation */
63 #define MON_STEREO     4        /*!< stereo mix of rx/tx streams */
64 #define MON_PRE_STEREO 5        /*!< stereo mix of rx/tx before echo can.  This is exactly what is fed into the echo can */
65
66 #define BLOCK_SIZE 240
67
68 #define BUFFERS 4
69
70 #define FRAG_SIZE 8
71
72 #define MAX_OFH 6
73
74 /* Put the ofh (output file handles) outside the main loop in case we ever add a
75  * signal handler.
76  */
77 static FILE *ofh[MAX_OFH];
78 static int run = 1;
79
80 static int stereo;
81 static int verbose;
82
83 /* handler to catch ctrl-c */
84 void cleanup_and_exit(int signal)
85 {
86         fprintf(stderr, "cntrl-c pressed\n");
87         run = 0; /* stop reading */
88 }
89
90 int filename_is_wav(char *filename)
91 {
92         if (NULL != strstr(filename, ".wav"))
93                 return 1;
94         return 0;
95 }
96
97 /*
98  * Fill the wav header with default info
99  * num_chans - 0 = mono; 1 = stereo
100  */
101 void wavheader_init(struct wavheader *wavheader, int num_chans)
102 {
103         memset(wavheader, 0, sizeof(struct wavheader));
104
105         memcpy(&wavheader->riff_chunk_id, "RIFF", 4);
106         memcpy(&wavheader->riff_type, "WAVE", 4);
107
108         memcpy(&wavheader->fmt_chunk_id, "fmt ", 4);
109         wavheader->fmt_data_size = 16;
110         wavheader->fmt_compression_code = 1;
111         wavheader->fmt_num_channels = num_chans;
112         wavheader->fmt_sample_rate = 8000;
113         wavheader->fmt_avg_bytes_per_sec = 16000;
114         wavheader->fmt_block_align = 2;
115         wavheader->fmt_significant_bps = 16;
116
117         memcpy(&wavheader->data_chunk_id, "data", 4);
118 }
119
120 int audio_open(void)
121 {
122         int fd;
123         int speed = 8000;
124         int fmt = AFMT_S16_LE;
125         int fragsize = (BUFFERS << 16) | (FRAG_SIZE);
126         struct audio_buf_info ispace, ospace;
127         fd = open("/dev/dsp", O_WRONLY);
128         if (fd < 0) {
129                 fprintf(stderr, "Unable to open /dev/dsp: %s\n", strerror(errno));
130                 return -1;
131         }
132         /* Step 1: Signed linear */
133         if (ioctl(fd, SNDCTL_DSP_SETFMT, &fmt) < 0) {
134                 fprintf(stderr, "ioctl(SETFMT) failed: %s\n", strerror(errno));
135                 close(fd);
136                 return -1;
137         }
138         /* Step 2: Make non-stereo */
139         if (ioctl(fd, SNDCTL_DSP_STEREO, &stereo) < 0) {
140                 fprintf(stderr, "ioctl(STEREO) failed: %s\n", strerror(errno));
141                 close(fd);
142                 return -1;
143         }
144         if (stereo != 0) {
145                 fprintf(stderr, "Can't turn stereo off :(\n");
146         }
147         /* Step 3: Make 8000 Hz */
148         if (ioctl(fd, SNDCTL_DSP_SPEED, &speed) < 0) {
149                 fprintf(stderr, "ioctl(SPEED) failed: %s\n", strerror(errno));
150                 close(fd);
151                 return -1;
152         }
153         if (speed != 8000) {
154                 fprintf(stderr, "Warning: Requested 8000 Hz, got %d\n", speed);
155         }
156         if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &fragsize)) {
157                 fprintf(stderr, "Sound card won't let me set fragment size to %u %u-byte buffers (%x)\n"
158                                                 "so sound may be choppy: %s.\n", BUFFERS, (1 << FRAG_SIZE), fragsize, strerror(errno));
159         }
160         bzero(&ispace, sizeof(ispace));
161         bzero(&ospace, sizeof(ospace));
162
163         if (ioctl(fd, SNDCTL_DSP_GETISPACE, &ispace)) {
164                 /* They don't support block size stuff, so just return but notify the user */
165                 fprintf(stderr, "Sound card won't let me know the input buffering...\n");
166         }
167         if (ioctl(fd, SNDCTL_DSP_GETOSPACE, &ospace)) {
168                 /* They don't support block size stuff, so just return but notify the user */
169                 fprintf(stderr, "Sound card won't let me know the output buffering...\n");
170         }
171         fprintf(stderr, "New input space:  %d of %d %d byte fragments (%d bytes left)\n",
172                 ispace.fragments, ispace.fragstotal, ispace.fragsize, ispace.bytes);
173         fprintf(stderr, "New output space:  %d of %d %d byte fragments (%d bytes left)\n",
174                 ospace.fragments, ospace.fragstotal, ospace.fragsize, ospace.bytes);
175         return fd;
176 }
177
178 int pseudo_open(void)
179 {
180         int fd;
181         int x = 1;
182         fd = open("/dev/dahdi/pseudo", O_RDWR);
183         if (fd < 0) {
184                 fprintf(stderr, "Unable to open pseudo channel: %s\n", strerror(errno));
185                 return -1;
186         }
187         if (ioctl(fd, DAHDI_SETLINEAR, &x)) {
188                 fprintf(stderr, "Unable to set linear mode: %s\n", strerror(errno));
189                 close(fd);
190                 return -1;
191         }
192         x = BLOCK_SIZE;
193         if (ioctl(fd, DAHDI_SET_BLOCKSIZE, &x)) {
194                 fprintf(stderr, "unable to set sane block size: %s\n", strerror(errno));
195                 close(fd);
196                 return -1;
197         }
198         return fd;
199 }
200
201 #define barlen 35
202 #define baroptimal 3250
203 //define barlevel 200
204 #define barlevel ((baroptimal/barlen)*2)
205 #define maxlevel (barlen*barlevel)
206
207 void draw_barheader()
208 {
209         char bar[barlen + 4];
210
211         memset(bar, '-', sizeof(bar));
212         memset(bar, '<', 1);
213         memset(bar + barlen + 2, '>', 1);
214         memset(bar + barlen + 3, '\0', 1);
215
216         memcpy(bar + (barlen / 2), "(RX)", 4);
217         printf("%s", bar);
218
219         memcpy(bar + (barlen / 2), "(TX)", 4);
220         printf(" %s\n", bar);
221 }
222
223 void draw_bar(int avg, int max)
224 {
225         char bar[barlen+5];
226
227         memset(bar, ' ', sizeof(bar));
228
229         max /= barlevel;
230         avg /= barlevel;
231         if (avg > barlen)
232                 avg = barlen;
233         if (max > barlen)
234                 max = barlen;
235
236         if (avg > 0)
237                 memset(bar, '#', avg);
238         if (max > 0)
239                 memset(bar + max, '*', 1);
240
241         bar[barlen+1] = '\0';
242         printf("%s", bar);
243         fflush(stdout);
244 }
245
246 void visualize(short *tx, short *rx, int cnt)
247 {
248         int x;
249         float txavg = 0;
250         float rxavg = 0;
251         static int txmax = 0;
252         static int rxmax = 0;
253         static int sametxmax = 0;
254         static int samerxmax = 0;
255         static int txbest = 0;
256         static int rxbest = 0;
257         float ms;
258         static struct timeval last;
259         struct timeval tv;
260
261         gettimeofday(&tv, NULL);
262         ms = (tv.tv_sec - last.tv_sec) * 1000.0 + (tv.tv_usec - last.tv_usec) / 1000.0;
263         for (x = 0; x < cnt; x++) {
264                 txavg += abs(tx[x]);
265                 rxavg += abs(rx[x]);
266         }
267         txavg = abs(txavg / cnt);
268         rxavg = abs(rxavg / cnt);
269
270         if (txavg > txbest)
271                 txbest = txavg;
272         if (rxavg > rxbest)
273                 rxbest = rxavg;
274
275         /* Update no more than 10 times a second */
276         if (ms < 100)
277                 return;
278
279         /* Save as max levels, if greater */
280         if (txbest > txmax) {
281                 txmax = txbest;
282                 sametxmax = 0;
283         }
284         if (rxbest > rxmax) {
285                 rxmax = rxbest;
286                 samerxmax = 0;
287         }
288
289         memcpy(&last, &tv, sizeof(last));
290
291         /* Clear screen */
292         printf("\r ");
293         draw_bar(rxbest, rxmax);
294         printf("   ");
295         draw_bar(txbest, txmax);
296         if (verbose)
297                 printf("   Rx: %5d (%5d) Tx: %5d (%5d)", rxbest, rxmax, txbest, txmax);
298         txbest = 0;
299         rxbest = 0;
300
301         /* If we have had the same max hits for x times, clear the values */
302         sametxmax++;
303         samerxmax++;
304         if (sametxmax > 6) {
305                 txmax = 0;
306                 sametxmax = 0;
307         }
308         if (samerxmax > 6) {
309                 rxmax = 0;
310                 samerxmax = 0;
311         }
312 }
313
314 int main(int argc, char *argv[])
315 {
316         int afd = -1;
317         int pfd[4] = {-1, -1, -1, -1};
318         short buf_brx[BLOCK_SIZE * 2];
319         short buf_tx[BLOCK_SIZE * 4];
320         short stereobuf[BLOCK_SIZE * 4];
321         int res_brx, res_tx;
322         int visual = 0;
323         int multichannel = 0;
324         int ossoutput = 0;
325         int preecho = 0;
326         int savefile = 0;
327         int stereo_output = 0;
328         int limit = 0;
329         int readcount = 0;
330         int x, chan;
331         struct dahdi_confinfo zc;
332         int opt;
333         extern char *optarg;
334         struct wavheader wavheaders[MAX_OFH]; /* we have one for each potential filehandle */
335         unsigned int bytes_written[MAX_OFH] = {0};
336         int file_is_wav[MAX_OFH] = {0};
337         int i;
338
339         if ((argc < 2) || (atoi(argv[1]) < 1)) {
340                 fprintf(stderr, "Usage: dahdi_monitor <channel num> [-v[v]] [-m] [-o] [-l limit] [-f FILE | -s FILE | -r FILE1 -t FILE2] [-F FILE | -S FILE | -R FILE1 -T FILE2]\n");
341                 fprintf(stderr, "Options:\n");
342                 fprintf(stderr, "        -v: Visual mode.  Implies -m.\n");
343                 fprintf(stderr, "        -vv: Visual/Verbose mode.  Implies -m.\n");
344                 fprintf(stderr, "        -l LIMIT: Stop after reading LIMIT bytes\n");
345                 fprintf(stderr, "        -m: Separate rx/tx streams.\n");
346                 fprintf(stderr, "        -o: Output audio via OSS.  Note: Only 'normal' combined rx/tx streams are output via OSS.\n");
347                 fprintf(stderr, "        -f FILE: Save combined rx/tx stream to mono FILE. Cannot be used with -m.\n");
348                 fprintf(stderr, "        -r FILE: Save rx stream to FILE. Implies -m.\n");
349                 fprintf(stderr, "        -t FILE: Save tx stream to FILE. Implies -m.\n");
350                 fprintf(stderr, "        -s FILE: Save stereo rx/tx stream to FILE. Implies -m.\n");
351                 fprintf(stderr, "        -F FILE: Save combined pre-echocanceled rx/tx stream to FILE. Cannot be used with -m.\n");
352                 fprintf(stderr, "        -R FILE: Save pre-echocanceled rx stream to FILE. Implies -m.\n");
353                 fprintf(stderr, "        -T FILE: Save pre-echocanceled tx stream to FILE. Implies -m.\n");
354                 fprintf(stderr, "        -S FILE: Save pre-echocanceled stereo rx/tx stream to FILE. Implies -m.\n");
355                 fprintf(stderr, "Examples:\n");
356                 fprintf(stderr, "Save a stream to a file\n");
357                 fprintf(stderr, "        dahdi_monitor 1 -f stream.raw\n");
358                 fprintf(stderr, "Visualize an rx/tx stream and save them to separate files.\n");
359                 fprintf(stderr, "        dahdi_monitor 1 -v -r streamrx.raw -t streamtx.raw\n");
360                 fprintf(stderr, "Play a combined rx/tx stream via OSS and save it to a file\n");
361                 fprintf(stderr, "        dahdi_monitor 1 -o -f stream.raw\n");
362                 fprintf(stderr, "Save a combined normal rx/tx stream and a combined 'preecho' rx/tx stream to files\n");
363                 fprintf(stderr, "        dahdi_monitor 1 -f stream.raw -F streampreecho.raw\n");
364                 fprintf(stderr, "Save a normal rx/tx stream and a 'preecho' rx/tx stream to separate files\n");
365                 fprintf(stderr, "        dahdi_monitor 1 -m -r streamrx.raw -t streamtx.raw -R streampreechorx.raw -T streampreechotx.raw\n");
366                 exit(1);
367         }
368
369         chan = atoi(argv[1]);
370
371         while ((opt = getopt(argc, argv, "vmol:f:r:t:s:F:R:T:S:")) != -1) {
372                 switch (opt) {
373                 case '?':
374                         exit(EXIT_FAILURE);
375                 case 'v':
376                         if (visual)
377                                 verbose = 1;
378                         visual = 1;
379                         multichannel = 1;
380                         break;
381                 case 'm':
382                         multichannel = 1;
383                         break;
384                 case 'o':
385                         ossoutput = 1;
386                         break;
387                 case 'l':
388                         if (sscanf(optarg, "%d", &limit) != 1 || limit < 0)
389                                 limit = 0;
390                         fprintf(stderr, "Will stop reading after %d bytes\n", limit);
391                         break;
392                 case 'f':
393                         if (multichannel) {
394                                 fprintf(stderr, "'%c' mode cannot be used when multichannel mode is enabled.\n", opt);
395                                 exit(EXIT_FAILURE);
396                         }
397                         if (ofh[MON_BRX]) {
398                                 fprintf(stderr, "Cannot specify option '%c' more than once.\n", opt);
399                                 exit(EXIT_FAILURE);
400                         }
401                         if ((ofh[MON_BRX] = fopen(optarg, "w")) == NULL) {
402                                 fprintf(stderr, "Could not open %s for writing: %s\n", optarg, strerror(errno));
403                                 exit(EXIT_FAILURE);
404                         }
405                         fprintf(stderr, "Writing combined stream to %s\n", optarg);
406                         file_is_wav[MON_BRX] = filename_is_wav(optarg);
407                         if (file_is_wav[MON_BRX]) {
408                                 wavheader_init(&wavheaders[MON_BRX], 1);
409                                 if (fwrite(&wavheaders[MON_BRX], 1, sizeof(struct wavheader), ofh[MON_BRX]) != sizeof(struct wavheader)) {
410                                         fprintf(stderr, "Could not write wav header to %s: %s\n", optarg, strerror(errno));
411                                         exit(EXIT_FAILURE);
412                                 }
413                         }
414                         savefile = 1;
415                         break;
416                 case 'F':
417                         if (multichannel) {
418                                 fprintf(stderr, "'%c' mode cannot be used when multichannel mode is enabled.\n", opt);
419                                 exit(EXIT_FAILURE);
420                         }
421                         if (ofh[MON_PRE_BRX]) {
422                                 fprintf(stderr, "Cannot specify option '%c' more than once.\n", opt);
423                                 exit(EXIT_FAILURE);
424                         }
425                         if ((ofh[MON_PRE_BRX] = fopen(optarg, "w")) == NULL) {
426                                 fprintf(stderr, "Could not open %s for writing: %s\n", optarg, strerror(errno));
427                                 exit(EXIT_FAILURE);
428                         }
429                         fprintf(stderr, "Writing pre-echo combined stream to %s\n", optarg);
430                         preecho = 1;
431                         savefile = 1;
432                         break;
433                 case 'r':
434                         if (!multichannel && ofh[MON_BRX]) {
435                                 fprintf(stderr, "'%c' mode cannot be used when combined mode is enabled.\n", opt);
436                                 exit(EXIT_FAILURE);
437                         }
438                         if (ofh[MON_BRX]) {
439                                 fprintf(stderr, "Cannot specify option '%c' more than once.\n", opt);
440                                 exit(EXIT_FAILURE);
441                         }
442                         if ((ofh[MON_BRX] = fopen(optarg, "w")) == NULL) {
443                                 fprintf(stderr, "Could not open %s for writing: %s\n", optarg, strerror(errno));
444                                 exit(EXIT_FAILURE);
445                         }
446                         fprintf(stderr, "Writing receive stream to %s\n", optarg);
447                         file_is_wav[MON_BRX] = filename_is_wav(optarg);
448                         if (file_is_wav[MON_BRX]) {
449                                 wavheader_init(&wavheaders[MON_BRX], 1);
450                                 if (fwrite(&wavheaders[MON_BRX], 1, sizeof(struct wavheader), ofh[MON_BRX]) != sizeof(struct wavheader)) {
451                                         fprintf(stderr, "Could not write wav header to %s: %s\n", optarg, strerror(errno));
452                                         exit(EXIT_FAILURE);
453                                 }
454                         }
455                         multichannel = 1;
456                         savefile = 1;
457                         break;
458                 case 'R':
459                         if (!multichannel && ofh[MON_PRE_BRX]) {
460                                 fprintf(stderr, "'%c' mode cannot be used when combined mode is enabled.\n", opt);
461                                 exit(EXIT_FAILURE);
462                         }
463                         if (ofh[MON_PRE_BRX]) {
464                                 fprintf(stderr, "Cannot specify option '%c' more than once.\n", opt);
465                                 exit(EXIT_FAILURE);
466                         }
467                         if ((ofh[MON_PRE_BRX] = fopen(optarg, "w")) == NULL) {
468                                 fprintf(stderr, "Could not open %s for writing: %s\n", optarg, strerror(errno));
469                                 exit(EXIT_FAILURE);
470                         }
471                         fprintf(stderr, "Writing pre-echo receive stream to %s\n", optarg);
472                         file_is_wav[MON_PRE_BRX] = filename_is_wav(optarg);
473                         if (file_is_wav[MON_PRE_BRX]) {
474                                 wavheader_init(&wavheaders[MON_PRE_BRX], 1);
475                                 if (fwrite(&wavheaders[MON_PRE_BRX], 1, sizeof(struct wavheader), ofh[MON_PRE_BRX]) != sizeof(struct wavheader)) {
476                                         fprintf(stderr, "Could not write wav header to %s: %s\n", optarg, strerror(errno));
477                                         exit(EXIT_FAILURE);
478                                 }
479                         }
480                         preecho = 1;
481                         multichannel = 1;
482                         savefile = 1;
483                         break;
484                 case 't':
485                         if (!multichannel && ofh[MON_BRX]) {
486                                 fprintf(stderr, "'%c' mode cannot be used when combined mode is enabled.\n", opt);
487                                 exit(EXIT_FAILURE);
488                         }
489                         if (ofh[MON_TX]) {
490                                 fprintf(stderr, "Cannot specify option '%c' more than once.\n", opt);
491                                 exit(EXIT_FAILURE);
492                         }
493                         if ((ofh[MON_TX] = fopen(optarg, "w")) == NULL) {
494                                 fprintf(stderr, "Could not open %s for writing: %s\n", optarg, strerror(errno));
495                                 exit(EXIT_FAILURE);
496                         }
497                         fprintf(stderr, "Writing transmit stream to %s\n", optarg);
498                         file_is_wav[MON_TX] = filename_is_wav(optarg);
499                         if (file_is_wav[MON_TX]) {
500                                 wavheader_init(&wavheaders[MON_TX], 1);
501                                 if (fwrite(&wavheaders[MON_TX], 1, sizeof(struct wavheader), ofh[MON_TX]) != sizeof(struct wavheader)) {
502                                         fprintf(stderr, "Could not write wav header to %s: %s\n", optarg, strerror(errno));
503                                         exit(EXIT_FAILURE);
504                                 }
505                         }
506                         multichannel = 1;
507                         savefile = 1;
508                         break;
509                 case 'T':
510                         if (!multichannel && ofh[MON_PRE_BRX]) {
511                                 fprintf(stderr, "'%c' mode cannot be used when combined mode is enabled.\n", opt);
512                                 exit(EXIT_FAILURE);
513                         }
514                         if (ofh[MON_PRE_TX]) {
515                                 fprintf(stderr, "Cannot specify option '%c' more than once.\n", opt);
516                                 exit(EXIT_FAILURE);
517                         }
518                         if ((ofh[MON_PRE_TX] = fopen(optarg, "w")) == NULL) {
519                                 fprintf(stderr, "Could not open %s for writing: %s\n", optarg, strerror(errno));
520                                 exit(EXIT_FAILURE);
521                         }
522                         fprintf(stderr, "Writing pre-echo transmit stream to %s\n", optarg);
523                         file_is_wav[MON_PRE_TX] = filename_is_wav(optarg);
524                         if (file_is_wav[MON_PRE_TX]) {
525                                 wavheader_init(&wavheaders[MON_PRE_TX], 1);
526                                 if (fwrite(&wavheaders[MON_PRE_TX], 1, sizeof(struct wavheader), ofh[MON_PRE_TX]) != sizeof(struct wavheader)) {
527                                         fprintf(stderr, "Could not write wav header to %s: %s\n", optarg, strerror(errno));
528                                         exit(EXIT_FAILURE);
529                                 }
530                         }
531                         preecho = 1;
532                         multichannel = 1;
533                         savefile = 1;
534                         break;
535                 case 's':
536                         if (!multichannel && ofh[MON_BRX]) {
537                                 fprintf(stderr, "'%c' mode cannot be used when combined mode is enabled.\n", opt);
538                                 exit(EXIT_FAILURE);
539                         }
540                         if (ofh[MON_STEREO]) {
541                                 fprintf(stderr, "Cannot specify option '%c' more than once.\n", opt);
542                                 exit(EXIT_FAILURE);
543                         }
544                         if ((ofh[MON_STEREO] = fopen(optarg, "w")) == NULL) {
545                                 fprintf(stderr, "Could not open %s for writing: %s\n", optarg, strerror(errno));
546                                 exit(EXIT_FAILURE);
547                         }
548                         fprintf(stderr, "Writing stereo stream to %s\n", optarg);
549                         file_is_wav[MON_STEREO] = filename_is_wav(optarg);
550                         if (file_is_wav[MON_STEREO]) {
551                                 wavheader_init(&wavheaders[MON_STEREO], 2);
552                                 if (fwrite(&wavheaders[MON_STEREO], 1, sizeof(struct wavheader), ofh[MON_STEREO]) != sizeof(struct wavheader)) {
553                                         fprintf(stderr, "Could not write wav header to %s: %s\n", optarg, strerror(errno));
554                                         exit(EXIT_FAILURE);
555                                 }
556                         }
557                         multichannel = 1;
558                         savefile = 1;
559                         stereo_output = 1;
560                         break;
561                 case 'S':
562                         if (!multichannel && ofh[MON_PRE_BRX]) {
563                                 fprintf(stderr, "'%c' mode cannot be used when combined mode is enabled.\n", opt);
564                                 exit(EXIT_FAILURE);
565                         }
566                         if (ofh[MON_PRE_STEREO]) {
567                                 fprintf(stderr, "Cannot specify option '%c' more than once.\n", opt);
568                                 exit(EXIT_FAILURE);
569                         }
570                         if ((ofh[MON_PRE_STEREO] = fopen(optarg, "w")) == NULL) {
571                                 fprintf(stderr, "Could not open %s for writing: %s\n", optarg, strerror(errno));
572                                 exit(EXIT_FAILURE);
573                         }
574                         fprintf(stderr, "Writing pre-echo stereo stream to %s\n", optarg);
575                         file_is_wav[MON_PRE_STEREO] = filename_is_wav(optarg);
576                         if (file_is_wav[MON_PRE_STEREO]) {
577                                 wavheader_init(&wavheaders[MON_PRE_STEREO], 2);
578                                 if (fwrite(&wavheaders[MON_PRE_STEREO], 1, sizeof(struct wavheader), ofh[MON_PRE_STEREO]) != sizeof(struct wavheader)) {
579                                         fprintf(stderr, "Could not write wav header to %s: %s\n", optarg, strerror(errno));
580                                         exit(EXIT_FAILURE);
581                                 }
582                         }
583                         preecho = 1;
584                         multichannel = 1;
585                         savefile = 1;
586                         stereo_output = 1;
587                         break;
588                 }
589         }
590
591         if (ossoutput) {
592                 if (multichannel) {
593                         printf("Multi-channel audio is enabled.  OSS output will be disabled.\n");
594                         ossoutput = 0;
595                 } else {
596                         /* Open audio */
597                         if ((afd = audio_open()) < 0) {
598                                 printf("Cannot open audio ...\n");
599                                 ossoutput = 0;
600                         }
601                 }
602         }
603         if (!ossoutput && !multichannel && !savefile) {
604                 fprintf(stderr, "Nothing to do with the stream(s) ...\n");
605                 exit(1);
606         }
607
608         /* Open Pseudo device */
609         if ((pfd[MON_BRX] = pseudo_open()) < 0)
610                 exit(1);
611         if (multichannel && ((pfd[MON_TX] = pseudo_open()) < 0))
612                 exit(1);
613         if (preecho) {
614                 if ((pfd[MON_PRE_BRX] = pseudo_open()) < 0)
615                         exit(1);
616                 if (multichannel && ((pfd[MON_PRE_TX] = pseudo_open()) < 0))
617                         exit(1);
618         }
619         /* Conference them */
620         if (multichannel) {
621                 memset(&zc, 0, sizeof(zc));
622                 zc.chan = 0;
623                 zc.confno = chan;
624                 /* Two pseudo's, one for tx, one for rx */
625                 zc.confmode = DAHDI_CONF_MONITOR;
626                 if (ioctl(pfd[MON_BRX], DAHDI_SETCONF, &zc) < 0) {
627                         fprintf(stderr, "Unable to monitor: %s\n", strerror(errno));
628                         exit(1);
629                 }
630                 memset(&zc, 0, sizeof(zc));
631                 zc.chan = 0;
632                 zc.confno = chan;
633                 zc.confmode = DAHDI_CONF_MONITORTX;
634                 if (ioctl(pfd[MON_TX], DAHDI_SETCONF, &zc) < 0) {
635                         fprintf(stderr, "Unable to monitor: %s\n", strerror(errno));
636                         exit(1);
637                 }
638                 if (preecho) {
639                         memset(&zc, 0, sizeof(zc));
640                         zc.chan = 0;
641                         zc.confno = chan;
642                         /* Two pseudo's, one for tx, one for rx */
643                         zc.confmode = DAHDI_CONF_MONITOR_RX_PREECHO;
644                         if (ioctl(pfd[MON_PRE_BRX], DAHDI_SETCONF, &zc) < 0) {
645                                 fprintf(stderr, "Unable to monitor: %s\n", strerror(errno));
646                                 exit(1);
647                         }
648                         memset(&zc, 0, sizeof(zc));
649                         zc.chan = 0;
650                         zc.confno = chan;
651                         zc.confmode = DAHDI_CONF_MONITOR_TX_PREECHO;
652                         if (ioctl(pfd[MON_PRE_TX], DAHDI_SETCONF, &zc) < 0) {
653                                 fprintf(stderr, "Unable to monitor: %s\n", strerror(errno));
654                                 exit(1);
655                         }
656                 }
657         } else {
658                 memset(&zc, 0, sizeof(zc));
659                 zc.chan = 0;
660                 zc.confno = chan;
661                 zc.confmode = DAHDI_CONF_MONITORBOTH;
662                 if (ioctl(pfd[MON_BRX], DAHDI_SETCONF, &zc) < 0) {
663                         fprintf(stderr, "Unable to monitor: %s\n", strerror(errno));
664                         exit(1);
665                 }
666                 if (preecho) {
667                         memset(&zc, 0, sizeof(zc));
668                         zc.chan = 0;
669                         zc.confno = chan;
670                         zc.confmode = DAHDI_CONF_MONITORBOTH_PREECHO;
671                         if (ioctl(pfd[MON_PRE_BRX], DAHDI_SETCONF, &zc) < 0) {
672                                 fprintf(stderr, "Unable to monitor: %s\n", strerror(errno));
673                                 exit(1);
674                         }
675                 }
676         }
677         if (signal(SIGINT, cleanup_and_exit) == SIG_ERR) {
678                 fprintf(stderr, "Error registering signal handler: %s\n", strerror(errno));
679         }
680         if (visual) {
681                 printf("\nVisual Audio Levels.\n");
682                 printf("--------------------\n");
683                 printf(" Use chan_dahdi.conf file to adjust the gains if needed.\n\n");
684                 printf("( # = Audio Level  * = Max Audio Hit )\n");
685                 draw_barheader();
686         }
687         /* Now, copy from pseudo to audio */
688         while (run) {
689                 res_brx = read(pfd[MON_BRX], buf_brx, sizeof(buf_brx));
690                 if (res_brx < 1)
691                         break;
692                 readcount += res_brx;
693                 if (ofh[MON_BRX])
694                         bytes_written[MON_BRX] += fwrite(buf_brx, 1, res_brx, ofh[MON_BRX]);
695
696                 if (multichannel) {
697                         res_tx = read(pfd[MON_TX], buf_tx, res_brx);
698                         if (res_tx < 1)
699                                 break;
700                         if (ofh[MON_TX])
701                                 bytes_written[MON_TX] += fwrite(buf_tx, 1, res_tx, ofh[MON_TX]);
702
703                         if (stereo_output && ofh[MON_STEREO]) {
704                                 for (x = 0; x < res_tx; x++) {
705                                         stereobuf[x*2] = buf_brx[x];
706                                         stereobuf[x*2+1] = buf_tx[x];
707                                 }
708                                 bytes_written[MON_STEREO] += fwrite(stereobuf, 1, res_tx*2, ofh[MON_STEREO]);
709                         }
710
711                         if (visual) {
712                                 if (res_brx == res_tx)
713                                         visualize((short *)buf_tx, (short *)buf_brx, res_brx/2);
714                                 else
715                                         printf("Huh?  res_tx = %d, res_brx = %d?\n", res_tx, res_brx);
716                         }
717                 }
718
719                 if (preecho) {
720                         res_brx = read(pfd[MON_PRE_BRX], buf_brx, sizeof(buf_brx));
721                         if (res_brx < 1)
722                                 break;
723                         if (ofh[MON_PRE_BRX])
724                                 bytes_written[MON_PRE_BRX] += fwrite(buf_brx, 1, res_brx, ofh[MON_PRE_BRX]);
725
726                         if (multichannel) {
727                                 res_tx = read(pfd[MON_PRE_TX], buf_tx, res_brx);
728                                 if (res_tx < 1)
729                                         break;
730                                 if (ofh[MON_PRE_TX])
731                                         bytes_written[MON_PRE_TX] += fwrite(buf_tx, 1, res_tx, ofh[MON_PRE_TX]);
732
733                                 if (stereo_output && ofh[MON_PRE_STEREO]) {
734                                         for (x = 0; x < res_brx; x++) {
735                                                 stereobuf[x*2] = buf_brx[x];
736                                                 stereobuf[x*2+1] = buf_tx[x];
737                                         }
738                                         bytes_written[MON_PRE_STEREO] += fwrite(stereobuf, 1, res_brx * 2, ofh[MON_PRE_STEREO]);
739                                 }
740                         }
741                 }
742
743                 if (ossoutput && afd) {
744                         if (stereo) {
745                                 for (x = 0; x < res_brx; x++) {
746                                         buf_tx[x << 1] = buf_tx[(x << 1) + 1] = buf_brx[x];
747                                 }
748                                 x = write(afd, buf_tx, res_brx << 1);
749                         } else {
750                                 x = write(afd, buf_brx, res_brx);
751                         }
752                 }
753
754                 if (limit && readcount >= limit) {
755                         /* bail if we've read too much */
756                         break;
757                 }
758         }
759         /* write filesize info */
760         for (i = 0; i < MAX_OFH; i++) {
761                 if (NULL == ofh[i])
762                         continue;
763                 if (!(file_is_wav[i]))
764                         continue;
765
766                 rewind(ofh[i]);
767
768                 if (fread(&wavheaders[i], 1, sizeof(struct wavheader), ofh[i]) != sizeof(struct wavheader)) {
769                         fprintf(stderr, "Failed to read in a full wav header.  Expect bad things.\n");
770                 }
771
772                 wavheaders[i].riff_chunk_size = (bytes_written[i]) + sizeof(struct wavheader) - 8; /* filesize - 8 */
773                 wavheaders[i].data_data_size = bytes_written[i];
774
775                 rewind(ofh[i]);
776                 if (fwrite(&wavheaders[i], 1, sizeof(struct wavheader), ofh[i]) != sizeof(struct wavheader)) {
777                         fprintf(stderr, "Failed to write out a full wav header.\n");
778                 }
779                 fclose(ofh[i]);
780         }
781         printf("done cleaning up ... exiting.\n");
782         return 0;
783 }