auto_assign_spans may be true even if not '1'
[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                         file_is_wav[MON_PRE_BRX] = filename_is_wav(optarg);
431                         if (file_is_wav[MON_PRE_BRX]) {
432                                 wavheader_init(&wavheaders[MON_PRE_BRX], 1);
433                                 if (fwrite(&wavheaders[MON_PRE_BRX], 1, sizeof(struct wavheader), ofh[MON_PRE_BRX]) != sizeof(struct wavheader)) {
434                                         fprintf(stderr, "Could not write wav header to %s: %s\n", optarg, strerror(errno));
435                                         exit(EXIT_FAILURE);
436                                 }
437                         }
438                         preecho = 1;
439                         savefile = 1;
440                         break;
441                 case 'r':
442                         if (!multichannel && ofh[MON_BRX]) {
443                                 fprintf(stderr, "'%c' mode cannot be used when combined mode is enabled.\n", opt);
444                                 exit(EXIT_FAILURE);
445                         }
446                         if (ofh[MON_BRX]) {
447                                 fprintf(stderr, "Cannot specify option '%c' more than once.\n", opt);
448                                 exit(EXIT_FAILURE);
449                         }
450                         if ((ofh[MON_BRX] = fopen(optarg, "w")) == NULL) {
451                                 fprintf(stderr, "Could not open %s for writing: %s\n", optarg, strerror(errno));
452                                 exit(EXIT_FAILURE);
453                         }
454                         fprintf(stderr, "Writing receive stream to %s\n", optarg);
455                         file_is_wav[MON_BRX] = filename_is_wav(optarg);
456                         if (file_is_wav[MON_BRX]) {
457                                 wavheader_init(&wavheaders[MON_BRX], 1);
458                                 if (fwrite(&wavheaders[MON_BRX], 1, sizeof(struct wavheader), ofh[MON_BRX]) != sizeof(struct wavheader)) {
459                                         fprintf(stderr, "Could not write wav header to %s: %s\n", optarg, strerror(errno));
460                                         exit(EXIT_FAILURE);
461                                 }
462                         }
463                         multichannel = 1;
464                         savefile = 1;
465                         break;
466                 case 'R':
467                         if (!multichannel && ofh[MON_PRE_BRX]) {
468                                 fprintf(stderr, "'%c' mode cannot be used when combined mode is enabled.\n", opt);
469                                 exit(EXIT_FAILURE);
470                         }
471                         if (ofh[MON_PRE_BRX]) {
472                                 fprintf(stderr, "Cannot specify option '%c' more than once.\n", opt);
473                                 exit(EXIT_FAILURE);
474                         }
475                         if ((ofh[MON_PRE_BRX] = fopen(optarg, "w")) == NULL) {
476                                 fprintf(stderr, "Could not open %s for writing: %s\n", optarg, strerror(errno));
477                                 exit(EXIT_FAILURE);
478                         }
479                         fprintf(stderr, "Writing pre-echo receive stream to %s\n", optarg);
480                         file_is_wav[MON_PRE_BRX] = filename_is_wav(optarg);
481                         if (file_is_wav[MON_PRE_BRX]) {
482                                 wavheader_init(&wavheaders[MON_PRE_BRX], 1);
483                                 if (fwrite(&wavheaders[MON_PRE_BRX], 1, sizeof(struct wavheader), ofh[MON_PRE_BRX]) != sizeof(struct wavheader)) {
484                                         fprintf(stderr, "Could not write wav header to %s: %s\n", optarg, strerror(errno));
485                                         exit(EXIT_FAILURE);
486                                 }
487                         }
488                         preecho = 1;
489                         multichannel = 1;
490                         savefile = 1;
491                         break;
492                 case 't':
493                         if (!multichannel && ofh[MON_BRX]) {
494                                 fprintf(stderr, "'%c' mode cannot be used when combined mode is enabled.\n", opt);
495                                 exit(EXIT_FAILURE);
496                         }
497                         if (ofh[MON_TX]) {
498                                 fprintf(stderr, "Cannot specify option '%c' more than once.\n", opt);
499                                 exit(EXIT_FAILURE);
500                         }
501                         if ((ofh[MON_TX] = fopen(optarg, "w")) == NULL) {
502                                 fprintf(stderr, "Could not open %s for writing: %s\n", optarg, strerror(errno));
503                                 exit(EXIT_FAILURE);
504                         }
505                         fprintf(stderr, "Writing transmit stream to %s\n", optarg);
506                         file_is_wav[MON_TX] = filename_is_wav(optarg);
507                         if (file_is_wav[MON_TX]) {
508                                 wavheader_init(&wavheaders[MON_TX], 1);
509                                 if (fwrite(&wavheaders[MON_TX], 1, sizeof(struct wavheader), ofh[MON_TX]) != sizeof(struct wavheader)) {
510                                         fprintf(stderr, "Could not write wav header to %s: %s\n", optarg, strerror(errno));
511                                         exit(EXIT_FAILURE);
512                                 }
513                         }
514                         multichannel = 1;
515                         savefile = 1;
516                         break;
517                 case 'T':
518                         if (!multichannel && ofh[MON_PRE_BRX]) {
519                                 fprintf(stderr, "'%c' mode cannot be used when combined mode is enabled.\n", opt);
520                                 exit(EXIT_FAILURE);
521                         }
522                         if (ofh[MON_PRE_TX]) {
523                                 fprintf(stderr, "Cannot specify option '%c' more than once.\n", opt);
524                                 exit(EXIT_FAILURE);
525                         }
526                         if ((ofh[MON_PRE_TX] = fopen(optarg, "w")) == NULL) {
527                                 fprintf(stderr, "Could not open %s for writing: %s\n", optarg, strerror(errno));
528                                 exit(EXIT_FAILURE);
529                         }
530                         fprintf(stderr, "Writing pre-echo transmit stream to %s\n", optarg);
531                         file_is_wav[MON_PRE_TX] = filename_is_wav(optarg);
532                         if (file_is_wav[MON_PRE_TX]) {
533                                 wavheader_init(&wavheaders[MON_PRE_TX], 1);
534                                 if (fwrite(&wavheaders[MON_PRE_TX], 1, sizeof(struct wavheader), ofh[MON_PRE_TX]) != sizeof(struct wavheader)) {
535                                         fprintf(stderr, "Could not write wav header to %s: %s\n", optarg, strerror(errno));
536                                         exit(EXIT_FAILURE);
537                                 }
538                         }
539                         preecho = 1;
540                         multichannel = 1;
541                         savefile = 1;
542                         break;
543                 case 's':
544                         if (!multichannel && ofh[MON_BRX]) {
545                                 fprintf(stderr, "'%c' mode cannot be used when combined mode is enabled.\n", opt);
546                                 exit(EXIT_FAILURE);
547                         }
548                         if (ofh[MON_STEREO]) {
549                                 fprintf(stderr, "Cannot specify option '%c' more than once.\n", opt);
550                                 exit(EXIT_FAILURE);
551                         }
552                         if ((ofh[MON_STEREO] = fopen(optarg, "w")) == NULL) {
553                                 fprintf(stderr, "Could not open %s for writing: %s\n", optarg, strerror(errno));
554                                 exit(EXIT_FAILURE);
555                         }
556                         fprintf(stderr, "Writing stereo stream to %s\n", optarg);
557                         file_is_wav[MON_STEREO] = filename_is_wav(optarg);
558                         if (file_is_wav[MON_STEREO]) {
559                                 wavheader_init(&wavheaders[MON_STEREO], 2);
560                                 if (fwrite(&wavheaders[MON_STEREO], 1, sizeof(struct wavheader), ofh[MON_STEREO]) != sizeof(struct wavheader)) {
561                                         fprintf(stderr, "Could not write wav header to %s: %s\n", optarg, strerror(errno));
562                                         exit(EXIT_FAILURE);
563                                 }
564                         }
565                         multichannel = 1;
566                         savefile = 1;
567                         stereo_output = 1;
568                         break;
569                 case 'S':
570                         if (!multichannel && ofh[MON_PRE_BRX]) {
571                                 fprintf(stderr, "'%c' mode cannot be used when combined mode is enabled.\n", opt);
572                                 exit(EXIT_FAILURE);
573                         }
574                         if (ofh[MON_PRE_STEREO]) {
575                                 fprintf(stderr, "Cannot specify option '%c' more than once.\n", opt);
576                                 exit(EXIT_FAILURE);
577                         }
578                         if ((ofh[MON_PRE_STEREO] = fopen(optarg, "w")) == NULL) {
579                                 fprintf(stderr, "Could not open %s for writing: %s\n", optarg, strerror(errno));
580                                 exit(EXIT_FAILURE);
581                         }
582                         fprintf(stderr, "Writing pre-echo stereo stream to %s\n", optarg);
583                         file_is_wav[MON_PRE_STEREO] = filename_is_wav(optarg);
584                         if (file_is_wav[MON_PRE_STEREO]) {
585                                 wavheader_init(&wavheaders[MON_PRE_STEREO], 2);
586                                 if (fwrite(&wavheaders[MON_PRE_STEREO], 1, sizeof(struct wavheader), ofh[MON_PRE_STEREO]) != sizeof(struct wavheader)) {
587                                         fprintf(stderr, "Could not write wav header to %s: %s\n", optarg, strerror(errno));
588                                         exit(EXIT_FAILURE);
589                                 }
590                         }
591                         preecho = 1;
592                         multichannel = 1;
593                         savefile = 1;
594                         stereo_output = 1;
595                         break;
596                 }
597         }
598
599         if (ossoutput) {
600                 if (multichannel) {
601                         printf("Multi-channel audio is enabled.  OSS output will be disabled.\n");
602                         ossoutput = 0;
603                 } else {
604                         /* Open audio */
605                         if ((afd = audio_open()) < 0) {
606                                 printf("Cannot open audio ...\n");
607                                 ossoutput = 0;
608                         }
609                 }
610         }
611         if (!ossoutput && !multichannel && !savefile) {
612                 fprintf(stderr, "Nothing to do with the stream(s) ...\n");
613                 exit(1);
614         }
615
616         /* Open Pseudo device */
617         if ((pfd[MON_BRX] = pseudo_open()) < 0)
618                 exit(1);
619         if (multichannel && ((pfd[MON_TX] = pseudo_open()) < 0))
620                 exit(1);
621         if (preecho) {
622                 if ((pfd[MON_PRE_BRX] = pseudo_open()) < 0)
623                         exit(1);
624                 if (multichannel && ((pfd[MON_PRE_TX] = pseudo_open()) < 0))
625                         exit(1);
626         }
627         /* Conference them */
628         if (multichannel) {
629                 memset(&zc, 0, sizeof(zc));
630                 zc.chan = 0;
631                 zc.confno = chan;
632                 /* Two pseudo's, one for tx, one for rx */
633                 zc.confmode = DAHDI_CONF_MONITOR;
634                 if (ioctl(pfd[MON_BRX], DAHDI_SETCONF, &zc) < 0) {
635                         fprintf(stderr, "Unable to monitor: %s\n", strerror(errno));
636                         exit(1);
637                 }
638                 memset(&zc, 0, sizeof(zc));
639                 zc.chan = 0;
640                 zc.confno = chan;
641                 zc.confmode = DAHDI_CONF_MONITORTX;
642                 if (ioctl(pfd[MON_TX], DAHDI_SETCONF, &zc) < 0) {
643                         fprintf(stderr, "Unable to monitor: %s\n", strerror(errno));
644                         exit(1);
645                 }
646                 if (preecho) {
647                         memset(&zc, 0, sizeof(zc));
648                         zc.chan = 0;
649                         zc.confno = chan;
650                         /* Two pseudo's, one for tx, one for rx */
651                         zc.confmode = DAHDI_CONF_MONITOR_RX_PREECHO;
652                         if (ioctl(pfd[MON_PRE_BRX], DAHDI_SETCONF, &zc) < 0) {
653                                 fprintf(stderr, "Unable to monitor: %s\n", strerror(errno));
654                                 exit(1);
655                         }
656                         memset(&zc, 0, sizeof(zc));
657                         zc.chan = 0;
658                         zc.confno = chan;
659                         zc.confmode = DAHDI_CONF_MONITOR_TX_PREECHO;
660                         if (ioctl(pfd[MON_PRE_TX], DAHDI_SETCONF, &zc) < 0) {
661                                 fprintf(stderr, "Unable to monitor: %s\n", strerror(errno));
662                                 exit(1);
663                         }
664                 }
665         } else {
666                 memset(&zc, 0, sizeof(zc));
667                 zc.chan = 0;
668                 zc.confno = chan;
669                 zc.confmode = DAHDI_CONF_MONITORBOTH;
670                 if (ioctl(pfd[MON_BRX], DAHDI_SETCONF, &zc) < 0) {
671                         fprintf(stderr, "Unable to monitor: %s\n", strerror(errno));
672                         exit(1);
673                 }
674                 if (preecho) {
675                         memset(&zc, 0, sizeof(zc));
676                         zc.chan = 0;
677                         zc.confno = chan;
678                         zc.confmode = DAHDI_CONF_MONITORBOTH_PREECHO;
679                         if (ioctl(pfd[MON_PRE_BRX], DAHDI_SETCONF, &zc) < 0) {
680                                 fprintf(stderr, "Unable to monitor: %s\n", strerror(errno));
681                                 exit(1);
682                         }
683                 }
684         }
685         if (signal(SIGINT, cleanup_and_exit) == SIG_ERR) {
686                 fprintf(stderr, "Error registering signal handler: %s\n", strerror(errno));
687         }
688         if (visual) {
689                 printf("\nVisual Audio Levels.\n");
690                 printf("--------------------\n");
691                 printf(" Use chan_dahdi.conf file to adjust the gains if needed.\n\n");
692                 printf("( # = Audio Level  * = Max Audio Hit )\n");
693                 draw_barheader();
694         }
695         /* Now, copy from pseudo to audio */
696         while (run) {
697                 res_brx = read(pfd[MON_BRX], buf_brx, sizeof(buf_brx));
698                 if (res_brx < 1)
699                         break;
700                 readcount += res_brx;
701                 if (ofh[MON_BRX])
702                         bytes_written[MON_BRX] += fwrite(buf_brx, 1, res_brx, ofh[MON_BRX]);
703
704                 if (multichannel) {
705                         res_tx = read(pfd[MON_TX], buf_tx, res_brx);
706                         if (res_tx < 1)
707                                 break;
708                         if (ofh[MON_TX])
709                                 bytes_written[MON_TX] += fwrite(buf_tx, 1, res_tx, ofh[MON_TX]);
710
711                         if (stereo_output && ofh[MON_STEREO]) {
712                                 for (x = 0; x < res_tx; x++) {
713                                         stereobuf[x*2] = buf_brx[x];
714                                         stereobuf[x*2+1] = buf_tx[x];
715                                 }
716                                 bytes_written[MON_STEREO] += fwrite(stereobuf, 1, res_tx*2, ofh[MON_STEREO]);
717                         }
718
719                         if (visual) {
720                                 if (res_brx == res_tx)
721                                         visualize((short *)buf_tx, (short *)buf_brx, res_brx/2);
722                                 else
723                                         printf("Huh?  res_tx = %d, res_brx = %d?\n", res_tx, res_brx);
724                         }
725                 }
726
727                 if (preecho) {
728                         res_brx = read(pfd[MON_PRE_BRX], buf_brx, sizeof(buf_brx));
729                         if (res_brx < 1)
730                                 break;
731                         if (ofh[MON_PRE_BRX])
732                                 bytes_written[MON_PRE_BRX] += fwrite(buf_brx, 1, res_brx, ofh[MON_PRE_BRX]);
733
734                         if (multichannel) {
735                                 res_tx = read(pfd[MON_PRE_TX], buf_tx, res_brx);
736                                 if (res_tx < 1)
737                                         break;
738                                 if (ofh[MON_PRE_TX])
739                                         bytes_written[MON_PRE_TX] += fwrite(buf_tx, 1, res_tx, ofh[MON_PRE_TX]);
740
741                                 if (stereo_output && ofh[MON_PRE_STEREO]) {
742                                         for (x = 0; x < res_brx; x++) {
743                                                 stereobuf[x*2] = buf_brx[x];
744                                                 stereobuf[x*2+1] = buf_tx[x];
745                                         }
746                                         bytes_written[MON_PRE_STEREO] += fwrite(stereobuf, 1, res_brx * 2, ofh[MON_PRE_STEREO]);
747                                 }
748                         }
749                 }
750
751                 if (ossoutput && afd) {
752                         if (stereo) {
753                                 for (x = 0; x < res_brx; x++) {
754                                         buf_tx[x << 1] = buf_tx[(x << 1) + 1] = buf_brx[x];
755                                 }
756                                 x = write(afd, buf_tx, res_brx << 1);
757                         } else {
758                                 x = write(afd, buf_brx, res_brx);
759                         }
760                 }
761
762                 if (limit && readcount >= limit) {
763                         /* bail if we've read too much */
764                         break;
765                 }
766         }
767         /* write filesize info */
768         for (i = 0; i < MAX_OFH; i++) {
769                 if (NULL == ofh[i])
770                         continue;
771                 if (!(file_is_wav[i]))
772                         continue;
773
774                 wavheaders[i].riff_chunk_size = (bytes_written[i]) + sizeof(struct wavheader) - 8; /* filesize - 8 */
775                 wavheaders[i].data_data_size = bytes_written[i];
776
777                 rewind(ofh[i]);
778                 if (fwrite(&wavheaders[i], 1, sizeof(struct wavheader), ofh[i]) != sizeof(struct wavheader)) {
779                         fprintf(stderr, "Failed to write out a full wav header.\n");
780                 }
781                 fclose(ofh[i]);
782         }
783         printf("done cleaning up ... exiting.\n");
784         return 0;
785 }