1 #include "config.h"
2 
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <unistd.h>
7 #include <fcntl.h>
8 #include <errno.h>
9 #include <locale.h>
10 #include <curses.h>
11 #include <signal.h>
12 #include <inttypes.h>
13 #include <sys/time.h>
14 #include <sys/signal.h>
15 #include <sys/ioctl.h>
16 #include <sys/types.h>
17 #ifdef HAVE_SOUNDCARD_H
18 # include <soundcard.h>
19 #endif
20 #ifdef HAVE_SYS_SOUNDCARD_H
21 # include <sys/soundcard.h>
22 #endif
23 
24 /* -------------------------------------------------------------------- */
25 
26 static void
tty_raw(void)27 tty_raw(void)
28 {
29     initscr();
30     cbreak();
31     noecho();
32     keypad(stdscr,1);
33     refresh();
34 }
35 
36 static void
tty_restore(void)37 tty_restore(void)
38 {
39     endwin();
40 }
41 
42 /* -------------------------------------------------------------------- */
43 
44 static int           sound_fd;
45 static int           sound_rcount;
46 static unsigned int  sound_blksize;
47 static int16_t       *sound_buffer;
48 static int           maxl,maxr;
49 static int           secl,secr;
50 static int           *histl,*histr,histn,histi;
51 static float         peak_seconds = 1.5;
52 static char          *audio_dev = "/dev/dsp";
53 
54 static int
sound_open(int rate)55 sound_open(int rate)
56 {
57     int frag,afmt,channels,trigger,srate;
58 
59     if (-1 == (sound_fd = open(audio_dev, O_RDONLY))) {
60 	fprintf(stderr,"open %s: %s\n",audio_dev,strerror(errno));
61 	exit(1);
62     }
63 
64     frag = 0x7fff000d; /* 8k */
65     if (-1 == ioctl(sound_fd, SNDCTL_DSP_SETFRAGMENT, &frag))
66 	perror("ioctl SNDCTL_DSP_SETFRAGMENT");
67 
68     /* format */
69     afmt = AFMT_S16_LE;
70     if (-1 == ioctl(sound_fd, SNDCTL_DSP_SETFMT, &afmt)) {
71 	perror("ioctl SNDCTL_DSP_SETFMT");
72 	exit(1);
73     }
74     if (afmt != AFMT_S16_LE) {
75 	fprintf(stderr,"can't set sound format to 16 bit (le)\n");
76 	exit(1);
77     }
78 
79     /* channels */
80     channels = 2;
81     if (-1 == ioctl(sound_fd, SNDCTL_DSP_CHANNELS, &channels)) {
82 	perror("ioctl SNDCTL_DSP_CHANNELS");
83 	exit(1);
84     }
85     if (channels != 2) {
86 	fprintf(stderr,"can't record in stereo\n");
87 	exit(1);
88     }
89 
90     /* rate */
91     srate = rate;
92     if (-1 == ioctl(sound_fd, SNDCTL_DSP_SPEED, &srate)) {
93 	perror("ioctl SNDCTL_DSP_SPEED");
94 	exit(1);
95     }
96     /* accept +/- 1% */
97     if (srate < rate *  99 / 100 ||
98 	srate > rate * 101 / 100) {
99 	fprintf(stderr,"can't set sample rate to %d (got %d)\n",
100 		rate,srate);
101 	exit(1);
102     }
103 
104     /* get block size */
105     if (-1 == ioctl(sound_fd, SNDCTL_DSP_GETBLKSIZE,  &sound_blksize)) {
106 	perror("ioctl SNDCTL_DSP_GETBLKSIZE");
107 	exit(1);
108     }
109     if (0 == sound_blksize)
110 	sound_blksize = 4096;
111     sound_buffer = malloc(sound_blksize);
112 
113     /* peak level history */
114     histn = peak_seconds * rate * 4 / sound_blksize;
115     histl = malloc(histn * sizeof(int));
116     histr = malloc(histn * sizeof(int));
117     memset(histl,0,histn * sizeof(int));
118     memset(histr,0,histn * sizeof(int));
119 
120     /* trigger record */
121     trigger = ~PCM_ENABLE_INPUT;
122     ioctl(sound_fd,SNDCTL_DSP_SETTRIGGER,&trigger);
123     trigger = PCM_ENABLE_INPUT;
124     ioctl(sound_fd,SNDCTL_DSP_SETTRIGGER,&trigger);
125 
126     return sound_fd;
127 }
128 
129 static int
sound_read(void)130 sound_read(void)
131 {
132     unsigned int have;
133     int     i,rc;
134     int16_t *v;
135 
136     /* read */
137     for (have = 0;have < sound_blksize;) {
138 	rc = read(sound_fd,sound_buffer+have,sound_blksize-have);
139 	switch (rc) {
140 	case -1:
141 	    if (EINTR != errno) {
142 		perror("read sound");
143 		exit(1);
144 	    }
145 	    break;
146 	case 0:
147 	    fprintf(stderr,"Huh? got 0 bytes from sound device?\n");
148 	    exit(1);
149 	default:
150 	    have += rc;
151 
152 	}
153     }
154 
155     /* look for peaks */
156     maxl = 0;
157     maxr = 0;
158     for (i = sound_blksize>>2, v=sound_buffer; i > 0; i--) {
159 	if (abs(*v) > maxl)
160 	    maxl = abs(*v);
161 	v++;
162 	if (abs(*v) > maxr)
163 	    maxr = abs(*v);
164 	v++;
165     }
166 
167     /* max for the last second */
168     histl[histi] = maxl;
169     histr[histi] = maxr;
170     histi++;
171     if (histn == histi)
172 	histi = 0;
173 
174     for (secl = 0, secr = 0, i = 0; i < histn; i++) {
175 	if (secl < histl[i])
176 	    secl = histl[i];
177 	if (secr < histr[i])
178 	    secr = histr[i];
179     }
180     sound_rcount++;
181     return 0;
182 }
183 
184 /* -------------------------------------------------------------------- */
185 
186 char *names[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
187 char *config_names[SOUND_MIXER_NRDEVICES][4];
188 
189 static int  mix;
190 static int  dev = -1;
191 static int  volume;
192 static char *mixer_dev = "/dev/mixer";
193 
194 static int
mixer_open(char * filename,char * device)195 mixer_open(char *filename, char *device)
196 {
197     int i, devmask;
198 
199     if (-1 == (mix = open(filename,O_RDONLY))) {
200 	fprintf(stderr,"open %s: %s\n",filename,strerror(errno));
201 	exit(1);
202     }
203     if (-1 == ioctl(mix,MIXER_READ(SOUND_MIXER_DEVMASK),&devmask)) {
204 	perror("mixer read devmask");
205 	exit(1);
206     }
207     for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
208 	if ((1<<i) & devmask && strcasecmp(names[i],device) == 0) {
209 	    if (-1 == ioctl(mix,MIXER_READ(i),&volume)) {
210 		perror("mixer read volume");
211 		exit(1);
212 	    } else {
213 		dev = i;
214 	    }
215 	}
216     }
217     if (-1 == dev) {
218 	fprintf(stderr,"mixer: havn't found device '%s'\nmixer: available: ",device);
219 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
220 	    if ((1<<i) & devmask)
221 		fprintf(stderr," '%s'",names[i]);
222 	fprintf(stderr,"\n");
223 	exit(1);
224     }
225     return (-1 != dev) ? 0 : -1;
226 }
227 
228 static void
mixer_close(void)229 mixer_close(void)
230 {
231     close(mix);
232     dev = -1;
233 }
234 
235 static int
mixer_get_volume(void)236 mixer_get_volume(void)
237 {
238     return (-1 == dev) ? -1 : (volume & 0x7f);
239 }
240 
241 static int
mixer_set_volume(int val)242 mixer_set_volume(int val)
243 {
244     if (-1 == dev)
245 	return -1;
246     val   &= 0x7f;
247     volume = val | (val << 8);;
248     if (-1 == ioctl(mix,MIXER_WRITE(dev),&volume)) {
249 	perror("mixer write volume");
250 	return -1;
251     }
252     return 0;
253 }
254 
255 /* ---------------------------------------------------------------------- */
256 /* *.wav I/O stolen from cdda2wav */
257 
258 /* Copyright (C) by Heiko Eissfeldt */
259 
260 typedef uint8_t   BYTE;
261 typedef uint16_t  WORD;
262 typedef uint32_t  DWORD;
263 typedef uint32_t  FOURCC;	/* a four character code */
264 
265 /* flags for 'wFormatTag' field of WAVEFORMAT */
266 #define WAVE_FORMAT_PCM 1
267 
268 /* MMIO macros */
269 #define mmioFOURCC(ch0, ch1, ch2, ch3) \
270   ((DWORD)(BYTE)(ch0) | ((DWORD)(BYTE)(ch1) << 8) | \
271   ((DWORD)(BYTE)(ch2) << 16) | ((DWORD)(BYTE)(ch3) << 24))
272 
273 #define FOURCC_RIFF	mmioFOURCC ('R', 'I', 'F', 'F')
274 #define FOURCC_LIST	mmioFOURCC ('L', 'I', 'S', 'T')
275 #define FOURCC_WAVE	mmioFOURCC ('W', 'A', 'V', 'E')
276 #define FOURCC_FMT	mmioFOURCC ('f', 'm', 't', ' ')
277 #define FOURCC_DATA	mmioFOURCC ('d', 'a', 't', 'a')
278 
279 typedef struct CHUNKHDR {
280     FOURCC ckid;		/* chunk ID */
281     DWORD dwSize; 		/* chunk size */
282 } CHUNKHDR;
283 
284 /* simplified Header for standard WAV files */
285 typedef struct WAVEHDR {
286     CHUNKHDR chkRiff;
287     FOURCC fccWave;
288     CHUNKHDR chkFmt;
289     WORD wFormatTag;	   /* format type */
290     WORD nChannels;	   /* number of channels (i.e. mono, stereo, etc.) */
291     DWORD nSamplesPerSec;  /* sample rate */
292     DWORD nAvgBytesPerSec; /* for buffer estimation */
293     WORD nBlockAlign;	   /* block size of data */
294     WORD wBitsPerSample;
295     CHUNKHDR chkData;
296 } WAVEHDR;
297 
298 #define IS_STD_WAV_HEADER(waveHdr) ( \
299   waveHdr.chkRiff.ckid == FOURCC_RIFF && \
300   waveHdr.fccWave == FOURCC_WAVE && \
301   waveHdr.chkFmt.ckid == FOURCC_FMT && \
302   waveHdr.chkData.ckid == FOURCC_DATA && \
303   waveHdr.wFormatTag == WAVE_FORMAT_PCM)
304 
305 #define cpu_to_le32(x) (x)
306 #define cpu_to_le16(x) (x)
307 #define le32_to_cpu(x) (x)
308 #define le16_to_cpu(x) (x)
309 
310 /* -------------------------------------------------------------------- */
311 
312 static WAVEHDR  fileheader;
313 static size_t   wav_size;
314 static size_t   done_size;
315 
316 static void
wav_init_header(int rate)317 wav_init_header(int rate)
318 {
319     /* stolen from cdda2wav */
320     int nBitsPerSample = 16;
321     int channels = 2;
322 
323     unsigned long nBlockAlign = channels * ((nBitsPerSample + 7) / 8);
324     unsigned long nAvgBytesPerSec = nBlockAlign * rate;
325     unsigned long temp = /* data length */ 0 +
326 	sizeof(WAVEHDR) - sizeof(CHUNKHDR);
327 
328     fileheader.chkRiff.ckid    = cpu_to_le32(FOURCC_RIFF);
329     fileheader.fccWave         = cpu_to_le32(FOURCC_WAVE);
330     fileheader.chkFmt.ckid     = cpu_to_le32(FOURCC_FMT);
331     fileheader.chkFmt.dwSize   = cpu_to_le32(16);
332     fileheader.wFormatTag      = cpu_to_le16(WAVE_FORMAT_PCM);
333     fileheader.nChannels       = cpu_to_le16(channels);
334     fileheader.nSamplesPerSec  = cpu_to_le32(rate);
335     fileheader.nAvgBytesPerSec = cpu_to_le32(nAvgBytesPerSec);
336     fileheader.nBlockAlign     = cpu_to_le16(nBlockAlign);
337     fileheader.wBitsPerSample  = cpu_to_le16(nBitsPerSample);
338     fileheader.chkData.ckid    = cpu_to_le32(FOURCC_DATA);
339     fileheader.chkRiff.dwSize  = cpu_to_le32(temp);
340     fileheader.chkData.dwSize  = cpu_to_le32(0 /* data length */);
341 }
342 
343 static void
wav_start_write(int fd,int rate)344 wav_start_write(int fd,int rate)
345 {
346     wav_init_header(rate);
347     lseek(fd,0,SEEK_SET);
348     write(fd,&fileheader,sizeof(WAVEHDR));
349     wav_size = 0;
350 }
351 
352 static int
wav_write_audio(int fd,void * data,int len)353 wav_write_audio(int fd, void *data, int len)
354 {
355     int rc;
356 
357     rc = write(fd,data,len);
358     if (len == rc) {
359 	wav_size += len;
360 	return 0;
361     } else
362 	return -1;
363 }
364 
365 static void
wav_stop_write(int fd)366 wav_stop_write(int fd)
367 {
368     unsigned long temp = wav_size + sizeof(WAVEHDR) - sizeof(CHUNKHDR);
369 
370     fileheader.chkRiff.dwSize = cpu_to_le32(temp);
371     fileheader.chkData.dwSize = cpu_to_le32(wav_size);
372     lseek(fd,0,SEEK_SET);
373     write(fd,&fileheader,sizeof(WAVEHDR));
374     done_size += wav_size;
375 }
376 
377 /* -------------------------------------------------------------------- */
378 
379 static char full[] =
380 "##################################################"
381 "##################################################"
382 "##################################################"
383 "##################################################";
384 
385 static char empty[] =
386 "--------------------------------------------------"
387 "--------------------------------------------------"
388 "--------------------------------------------------"
389 "--------------------------------------------------";
390 
391 static char blank[] =
392 "                                                  "
393 "                                                  "
394 "                                                  "
395 "                                                  ";
396 
397 static char alive[] = "-\\|/";
398 //static char alive[] = ".oOo";
399 #define ALIVE(count)  alive[count % (sizeof(alive)/sizeof(alive[0])-1)]
400 
401 static void
print_bar(int line,char * name,int val1,int val2,int max)402 print_bar(int line, char *name, int val1, int val2, int max)
403 {
404     int total,len;
405 
406     total = COLS-16;
407     len   = val1*total/max;
408 
409     mvprintw(line,0,"%-6s: %5d  ",name,(val2 != -1) ? val2 : val1);
410     printw("%*.*s",len,len,full);
411     printw("%*.*s",total-len,total-len,empty);
412     if (val2 != -1)
413 	mvprintw(line,14+val2*total/max,"|");
414 }
415 
416 /* -------------------------------------------------------------------- */
417 
418 enum MODE {
419     NCURSES = 1,
420     CONSOLE = 2,
421 };
422 enum MODE mode = NCURSES;
423 int       stop,verbose;
424 char      *filename = "record";
425 int       rate = 44100;
426 
427 static void
ctrlc(int signal)428 ctrlc(int signal)
429 {
430     if (verbose)
431 	fprintf(stderr,"\n%s - exiting\n",
432 		sys_siglist[signal]);
433     stop = 1;
434 }
435 
436 static int
record_start(char * outfile,int * nr)437 record_start(char *outfile, int *nr)
438 {
439     int wav;
440 
441     do {
442 	sprintf(outfile,"%s%03d.wav",filename,(*nr)++);
443 	wav = open(outfile, O_WRONLY | O_EXCL | O_CREAT, 0666);
444     } while ((-1 == wav) && (EEXIST == errno));
445     if (-1 == wav) {
446 	perror("open");
447 	exit(1);
448     }
449     wav_start_write(wav,rate);
450     return wav;
451 }
452 
453 static void
record_stop(int fd)454 record_stop(int fd)
455 {
456     wav_stop_write(fd);
457     close(fd);
458     switch (mode) {
459     case CONSOLE:
460 	if (verbose)
461 	    printf("\n");
462 	break;
463     case NCURSES:
464 	mvprintw(3,0,"%*.*s",COLS-1,COLS-1,blank);
465 	break;
466     }
467 }
468 
469 static size_t
parse_size(const char * arg)470 parse_size(const char *arg)
471 {
472     int value;
473     char mul[4];
474     off_t retval = -1;
475 
476     if (2 != sscanf(arg,"%d%3s",&value,mul))
477 	return 0;
478     if (0 == strcasecmp(mul,"g") ||
479 	0 == strcasecmp(mul,"gb"))
480 	retval = (off_t)value * 1024 * 1024 * 1024;
481     if (0 == strcasecmp(mul,"m") ||
482 	0 == strcasecmp(mul,"mb"))
483 	retval = (off_t)value * 1024 * 1024;
484     if (0 == strcasecmp(mul,"k") ||
485 	0 == strcasecmp(mul,"kb"))
486 	retval = (off_t)value * 1024;
487     return retval;
488 }
489 
490 static char*
str_mb(off_t value)491 str_mb(off_t value)
492 {
493     static char buf[32];
494 
495     if (value > (1 << 30)) {
496 	value = (value * 10) >> 30;
497 	sprintf(buf,"%d.%d GB",(int)(value/10),(int)(value%10));
498 	return buf;
499     }
500     if (value > (1 << 20)) {
501 	value = (value * 10) >> 20;
502 	sprintf(buf,"%d.%d MB",(int)(value/10),(int)(value%10));
503 	return buf;
504     }
505     value >>= 10;
506     sprintf(buf,"%3d kB",(int)value);
507     return buf;
508 }
509 
510 /* -------------------------------------------------------------------- */
511 
512 char      *progname;
513 char      *input = "line";
514 char      *str_maxsize = "2GB";
515 int       level_trigger;
516 
517 static void
usage(FILE * fp)518 usage(FILE *fp)
519 {
520     fprintf(fp,
521 	    "\n"
522 	    "%s records sound in CD-Quality (44100/16bit/stereo).\n"
523 	    "It has a nice ascii-art input-level meter.  It is a\n"
524 	    "interactive curses application.  You'll need a fast\n"
525 	    "terminal, don't try this on a 9600 bps vt100...\n"
526 	    "\n"
527 	    "%s has several options:\n"
528 	    "  -h        this text\n"
529 	    "  -o file   output file basename [%s], a number and the .wav\n"
530 	    "            extention are added by %s.\n"
531 	    "  -i ctrl   mixer control [%s].  This should be the one\n"
532 	    "            where you can adjust the record level for\n"
533 	    "            your audio source, \"line\", \"mic\" and \"igain\"\n"
534 	    "            are good candidates.\n"
535 	    "  -m dev    set mixer device [%s]\n"
536 	    "  -d dev    set dsp device   [%s]\n"
537 	    "  -r rate   set sample rate  [%d]\n"
538 	    "  -p sec    peak seconds     [%.1f]\n"
539 	    "\n"
540 	    "for non-interactive usage only:\n"
541 	    "  -c        enable console (non-interactive) mode\n"
542 	    "  -v        be verbose (show progress)\n"
543 	    "  -t mm:ss  limit the time to record.  By default it records\n"
544 	    "            until stopped by a signal (^C)\n"
545 	    "  -s size   set max file size [%s]. You have to give number\n"
546 	    "            and unit without space inbetween, i.e. \"100mb\".\n"
547 	    "  -n num    limit amount of files recorded, quits when\n"
548 	    "            reached.\n"
549 	    "  -l        signal level triggered recording.\n"
550 	    "  -L level  same as above + specify trigger level [%d]\n"
551 	    "\n",
552 	    progname,progname,filename,progname,
553 	    input,mixer_dev,audio_dev,
554 	    rate,peak_seconds,str_maxsize,
555 	    level_trigger ? level_trigger : 1000);
556 }
557 
558 int
main(int argc,char * argv[])559 main(int argc, char *argv[])
560 {
561     int             c,key,vol,delay,auto_adjust;
562     int             record,nr,wav=0;
563     char            *outfile;
564     fd_set          s;
565     int             sec,maxhour,maxmin,maxsec;
566     int             maxfiles = 0;
567     size_t          maxsize;
568 
569     /* init some vars */
570     progname = strrchr(argv[0],'/');
571     progname = progname ? progname+1 : argv[0];
572     maxsec  = 0;
573     delay   = 0;
574     auto_adjust   = 1;
575     record = 0;
576     nr = 0;
577 
578     setlocale(LC_ALL,"");
579 
580     /* parse options */
581     for (;;) {
582 	if (-1 == (c = getopt(argc, argv, "vhlci:o:d:m:r:t:s:L:p:n:")))
583 	    break;
584 	switch (c) {
585 	case 'v':
586 	    verbose = 1;
587 	    break;
588 	case 'l':
589 	    level_trigger = 1000;
590 	    break;
591 	case 'L':
592 	    level_trigger = atoi(optarg);
593 	    break;
594 	case 'i':
595 	    input = optarg;
596 	    break;
597 	case 'o':
598 	    filename = optarg;
599 	    break;
600 	case 'd':
601 	    audio_dev = optarg;
602 	    break;
603 	case 'm':
604 	    mixer_dev = optarg;
605 	    break;
606 	case 'c':
607 	    mode = CONSOLE;
608 	    break;
609 	case 'r':
610 	    rate = atoi(optarg);
611 	    break;
612 	case 'p':
613 	    peak_seconds = atof(optarg);
614 	    break;
615 	case 't':
616 	    if (3 != sscanf(optarg,"%d:%d:%d",&maxhour,&maxmin,&maxsec)) {
617 		maxhour = 0;
618 		if (2 != sscanf(optarg,"%d:%d",&maxmin,&maxsec)) {
619 		    fprintf(stderr,"time parse error\n");
620 		    exit(1);
621 		}
622 	    }
623 	    maxsec += maxmin  * 60;
624 	    maxsec += maxhour * 60 * 60;
625 	    break;
626 	case 's':
627 	    str_maxsize = optarg;
628 	    break;
629 	case 'n':
630 	    maxfiles = atoi(optarg);
631 	    break;
632 	case 'h':
633 	    usage(stdout);
634 	    exit(0);
635 	default:
636 	    usage(stderr);
637 	    exit(1);
638 	}
639     }
640     maxsize = parse_size(str_maxsize);
641     if (0 == maxsize) {
642 	fprintf(stderr,"maxsize parse error [%s]\n",str_maxsize);
643 	exit(1);
644     }
645 
646     mixer_open(mixer_dev,input);
647     sound_open(rate);
648     outfile = malloc(strlen(filename)+16);
649 
650     if (mode == NCURSES) {
651 	tty_raw();
652 	atexit(tty_restore);
653     }
654 
655     signal(SIGINT,ctrlc);
656     signal(SIGQUIT,ctrlc);
657     signal(SIGTERM,ctrlc);
658     signal(SIGHUP,ctrlc);
659 
660     if (mode == NCURSES) {
661 	mvprintw( 5,0,"record to   %s*.wav",filename);
662 	mvprintw( 7,0,"left/right  adjust mixer level for \"%s\"",input);
663 	mvprintw( 8,0,"space       starts/stops recording");
664 	/* line 9 is printed later */
665 	mvprintw(10,0,"            auto-adjust reduces the record level on overruns");
666 	mvprintw(11,0,"'N'         next file (same as space twice, but without break)");
667 	mvprintw(12,0,"'Q'         quit");
668 	mvprintw(LINES-3,0,"--");
669 	mvprintw(LINES-2,0,"(c) 1999-2003 Gerd Knorr <kraxel@bytesex.org>");
670 
671 	for (;!stop;) {
672 	    refresh();
673 	    FD_ZERO(&s);
674 	    FD_SET(0,&s);
675 	    FD_SET(sound_fd,&s);
676 	    if (-1 == select(sound_fd+1,&s,NULL,NULL,NULL)) {
677 		if (EINTR == errno)
678 		    continue;
679 		perror("select");
680 		break;
681 	    }
682 
683 	    if (FD_ISSET(sound_fd,&s)) {
684 		/* sound */
685 		if (-1 == sound_read())
686 		    break;
687 		if (delay)
688 		    delay--;
689 		if (auto_adjust && (0 == delay) &&
690 		    (maxl >= 32767 || maxr >= 32767)) {
691 		    /* auto-adjust */
692 		    vol = mixer_get_volume();
693 		    vol--;
694 		    if (vol < 0)
695 			vol = 0;
696 		    mixer_set_volume(vol);
697 		    delay = 3;
698 		}
699 		print_bar(0,input,mixer_get_volume(),-1,100);
700 		print_bar(1,"left",maxl,secl,32768);
701 		print_bar(2,"right",maxr,secr,32768);
702 		mvprintw(9,0,"'A'         toggle auto-adjust [%s] ",
703 			 auto_adjust ? "on" : "off");
704 		if (record) {
705 		    wav_write_audio(wav,sound_buffer,sound_blksize);
706 		    sec = wav_size / (rate*4);
707 		    mvprintw(3,0,"%s: %3d:%02d (%s) ",outfile,
708 			     sec/60,sec%60,str_mb(wav_size));
709 		} else {
710 		    mvprintw(3,0,"%c",ALIVE(sound_rcount));
711 		}
712 	    }
713 
714 	    if (FD_ISSET(0,&s)) {
715 		/* tty in */
716 		switch (key = getch()) {
717 		case 'Q':
718 		case 'q':
719 		    stop = 1;
720 		    break;
721 		case 'A':
722 		case 'a':
723 		    auto_adjust = !auto_adjust;
724 		    break;
725 		case 'N':
726 		case 'n':
727 		    if (record) {
728 			record_stop(wav);
729 			wav = record_start(outfile,&nr);
730 		    }
731 		    break;
732 		case ' ':
733 		    if (!filename)
734 			break;
735 		    if (!record) {
736 			/* start */
737 			wav = record_start(outfile,&nr);
738 			record=1;
739 			auto_adjust=0;
740 		    } else {
741 			/* stop */
742 			record_stop(wav);
743 			record=0;
744 		    }
745 		    break;
746 		case KEY_RIGHT:
747 		    vol = mixer_get_volume();
748 		    vol++;
749 		    if (vol > 100)
750 			vol = 100;
751 		    mixer_set_volume(vol);
752 		    break;
753 		case KEY_LEFT:
754 		    vol = mixer_get_volume();
755 		    vol--;
756 		    if (vol < 0)
757 			vol = 0;
758 		    mixer_set_volume(vol);
759 		    break;
760 		}
761 	    }
762 	}
763     }
764 
765     if (mode == CONSOLE) {
766 	if (!level_trigger) {
767 	    wav = record_start(outfile,&nr);
768 	    record=1;
769 	}
770 
771 	for (;!stop;) {
772 	    if (-1 == sound_read())
773 		break;
774 	    if (level_trigger) {
775 		if (!record &&
776 		    (maxl > level_trigger ||
777 		     maxr > level_trigger)) {
778 		    wav = record_start(outfile,&nr);
779 		    record=1;
780 		}
781 		if (record &&
782 		    secl < level_trigger &&
783 		    secr < level_trigger) {
784 		    record_stop(wav);
785 		    record=0;
786 		    if (maxfiles && nr == maxfiles)
787 			break;
788 		}
789 	    }
790 	    if (!record) {
791 		printf("waiting for signal %c [%d/%d]...  \r",
792 		       ALIVE(sound_rcount), maxl,maxr);
793 		fflush(stdout);
794 		continue;
795 	    }
796 
797 	    sec = (done_size + wav_size) / (rate*4);
798 	    if (maxsec && sec >= maxsec)
799 		break;
800 	    if (wav_size + sound_blksize + sizeof(WAVEHDR) > maxsize) {
801 		record_stop(wav);
802 		wav = record_start(outfile,&nr);
803 	    }
804 	    wav_write_audio(wav,sound_buffer,sound_blksize);
805 	    if (verbose) {
806 		int total = 10;
807 		int len   = (maxl+maxr)*total/32768/2;
808 		printf("|%*.*s%*.*s|  %s  %d:%02d",
809 		       len,len,full, total-len,total-len,empty,
810 		       outfile,sec/60,sec%60);
811 		if (maxsec)
812 		    printf("/%d:%02d",maxsec/60,maxsec%60);
813 		printf(" (%s",str_mb(wav_size));
814 		if (done_size)
815 		    printf(", %s total",str_mb(done_size + wav_size));
816 		printf(")      \r");
817 		fflush(stdout);
818 	    }
819 	}
820     }
821 
822     if (record)
823 	record_stop(wav);
824     mixer_close();
825     exit(0);
826 }
827