Report explicit full speed setting
[xiph/unicode.git] / rtrecord / main.c
blob6d797adbcf9f5eb5fb7ec98ba8b160f392607b5e
1 /*
3 * rtrecord
4 *
5 * Copyright (C) 2006 Red Hat Inc.
7 * rtrecord is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2, or (at your option)
10 * any later version.
12 * rtrecord is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with rtrecord; see the file COPYING. If not, write to the
19 * Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
25 #define _GNU_SOURCE
26 #define _LARGEFILE_SOURCE
27 #define _LARGEFILE64_SOURCE
28 #define _FILE_OFFSET_BITS 64
30 #ifdef HAVE_CONFIG_H
31 # include <config.h>
32 #endif
34 #ifndef _REENTRANT
35 # define _REENTRANT
36 #endif
38 #include <stdio.h>
39 #include <unistd.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <getopt.h>
43 #include <time.h>
44 #include <math.h>
45 #include <signal.h>
46 #include <alsa/asoundlib.h>
47 #include <semaphore.h>
48 #include <pthread.h>
49 #include <sys/mman.h>
51 #include "rtrecord.h"
53 int channels=2;
54 int rate=44100;
55 int width=16;
56 char *device="hw:0,0";
57 snd_pcm_t *devhandle=NULL;
58 int weight=0;
59 int format = SND_PCM_FORMAT_S16_LE;
60 int samplesize = 2;
61 int framesize = 0;
62 int quiet = 0;
63 int ttyfd;
64 int ttypipe[2];
65 int exiting = 0;
66 int paused = 0;
67 int realtime=1;
68 int lockbuffer=1;
69 float smooth = .5;
71 pthread_t main_thread_id;
72 pthread_t record_thread_id;
73 pthread_t disk_thread_id;
74 pthread_t tty_thread_id;
75 pthread_t ui_thread_id;
76 sem_t ui_sem;
77 sem_t buffer_sem;
78 sem_t setup_sem;
80 unsigned char *diskbuffer=0;
81 sig_atomic_t diskbuffer_head=0;
82 sig_atomic_t diskbuffer_tail=0;
84 int diskbuffer_size;
85 int diskbuffer_frames;
87 char *out_name=0;
88 FILE *out_FILE=0;
90 atomic_mark dma_mark = {0,0};
91 atomic_mark disk_mark = {0,0};
93 long dmabuffer_frames = 0;
94 buffer_meta dma_data[2] = {
95 {0,0,0,0},
96 {0,0,0,0},
98 channel_meta *pcm_data[2];
100 void exit_handler(int sig){
101 signal(sig,SIG_IGN);
102 write(ttypipe[1],"q",1);
103 exiting = 1;
106 /* used to redirect the real tty input into the pipe that ncurses
107 thinks is the tty */
108 void *tty_thread(void *dummy){
109 char buf;
111 while(!exiting){
112 int ret=read(ttyfd,&buf,1);
113 if(ret==1){
114 write(ttypipe[1],&buf,1);
117 return NULL;
120 /* used to request a ui update (vi the ui sempahore) such that there
121 is absolutely no possibility of blocking if the other UI code is
122 otherwise unresponsive; just writing to the ttypipe could backfire
123 if the pipe fills */
124 void *ui_thread(void *dummy){
125 while(!exiting){
126 write(ttypipe[1],"",1);
127 sem_wait(&ui_sem);
129 return NULL;
132 static void record_setup(void){
133 snd_pcm_hw_params_t *hw;
134 snd_pcm_uframes_t frames;
135 int ret;
137 if ((ret = snd_pcm_hw_params_malloc (&hw)) < 0) {
138 fprintf (stderr, "capture cannot allocate hardware parameter structure (%s)\n",
139 snd_strerror (ret));
140 exit (1);
143 if ((ret = snd_pcm_hw_params_any (devhandle, hw)) < 0) {
144 fprintf (stderr, "capture cannot initialize hardware parameter structure (%s)\n",
145 snd_strerror (ret));
146 exit (1);
149 if ((ret = snd_pcm_hw_params_set_access (devhandle, hw, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
150 fprintf (stderr, "capture cannot set access type (%s)\n",
151 snd_strerror (ret));
152 exit (1);
155 if ((ret = snd_pcm_hw_params_set_format (devhandle, hw, format)) < 0) {
156 fprintf (stderr, "capture cannot set sample format (%s)\n",
157 snd_strerror (ret));
158 exit (1);
161 if ((ret = snd_pcm_hw_params_set_rate (devhandle, hw, rate, 0)) < 0) {
162 fprintf (stderr, "capture cannot set sample rate (%s)\n",
163 snd_strerror (ret));
164 exit (1);
167 if ((ret = snd_pcm_hw_params_set_channels (devhandle, hw, channels)) < 0) {
168 fprintf (stderr, "capture cannot set channel count (%s)\n",
169 snd_strerror (ret));
170 exit (1);
173 if ((ret = snd_pcm_hw_params (devhandle, hw)) < 0) {
174 fprintf (stderr, "capture cannot set parameters (%s)\n",
175 snd_strerror (ret));
176 exit (1);
180 if((ret = snd_pcm_hw_params_get_buffer_size(hw,&frames)) < 0){
181 fprintf (stderr, "capture cannot query buffer size (%s)\n",
182 snd_strerror (ret));
183 exit (1);
186 dmabuffer_frames = frames;
189 snd_pcm_hw_params_free (hw);
191 if ((ret = snd_pcm_prepare (devhandle)) < 0) {
192 fprintf (stderr, "capture cannot prepare audio interface for use (%s)\n",
193 snd_strerror (ret));
194 exit (1);
199 /* realtime 'pounce' thread that grabs data from the audio device (and
200 the little bitty hardware buffer) and stuffs the data, for the time
201 being, into a relatively huge ringbuffer that the disk thread can
202 service at its leisure */
203 void *record_thread(void *dummy){
204 /* sound device startup */
205 int ret;
206 snd_pcm_sframes_t sframes;
207 int fps16 = (rate>>4);
209 if(realtime){
210 struct sched_param param;
211 param.sched_priority=89;
212 if(pthread_setschedparam(pthread_self(), SCHED_FIFO, &param)){
213 fprintf(stderr,"\nERROR: Could not set realtime priority for capture thread.\n\n");
214 fprintf(stderr,
215 "To run without realtime scheduling, use the --no-realtime or -n option, or\n"
216 "consider running 'record', equivalent to 'rtrecord --no-lock --no-realtime'\n"
217 "Be warned that recording without realtime scheduling may be prone to skips\n"
218 "and pops depending on machine load and specific hardware; using realtime\n"
219 "is strongly suggested for any mission critical recording.\n\n"
220 "To run with realtime scheduling as a non-root user, make the rtrecord\n"
221 "executable setuid root.\n\n");
222 exit(1);
226 sem_post(&setup_sem);
228 while(!exiting){
229 int frames = 0;
230 int head = diskbuffer_head;
232 while(frames<fps16){
233 int to_read = diskbuffer_frames - head;
234 if(to_read > fps16-frames) to_read = fps16 - frames;
236 /* update high water mark of dma buffer */
237 if(snd_pcm_delay (devhandle, &sframes)==0){
238 int watermark = dmabuffer_frames-sframes;
239 if(dma_data[dma_mark.bank].dmabuffer_min>watermark)
240 dma_data[dma_mark.bank].dmabuffer_min=watermark;
243 /* read -- we always have space due to invariant */
244 ret = snd_pcm_readi (devhandle, diskbuffer + head*framesize,
245 to_read);
247 if(ret<0){
248 // most likely an underrun
249 switch(ret){
250 case -EAGAIN:
251 break;
252 case -EPIPE: case -ESTRPIPE:
253 // underrun; set starve and reset soft device
254 dma_data[dma_mark.bank].dmabuffer_overrun=1;
255 snd_pcm_drop(devhandle);
256 snd_pcm_prepare(devhandle);
257 frames=0;
259 break;
260 case -EBADFD:
261 default:
262 // Something went very wrong. Don't hose the machine here in a realtime thread
263 frames = fps16;
264 exiting=1;
265 fprintf (stderr, "unrecoverable capture read error: (%s)\n",
266 snd_strerror (ret));
267 break;
269 }else{
270 frames+=ret;
271 head+=ret;
272 if(head>=diskbuffer_frames) head = 0;
276 if(!exiting){
277 /* advance the head index, but only if there will be enough
278 space left to write the next frame. if there isn't enough
279 space left, leave the head as-is and set 'underrun'. This
280 wastes a small amount of buffer space but eliminates the
281 need for the sample thread to conditionally block on the disk
282 thread. */
283 int tail = diskbuffer_tail;
284 int remaining = diskbuffer_frames - (tail <= head ?
285 (head - tail):
286 (head + diskbuffer_frames - tail));
288 if(remaining >= fps16){
290 /* explicit invariant: this is always full_frame aligned
291 (equal numbers of frames in each channel) */
293 if(dma_data[dma_mark.bank].diskbuffer_min>remaining)
294 dma_data[dma_mark.bank].diskbuffer_min=remaining;
295 if(dma_mark.read == dma_mark.bank){
296 dma_mark.bank = !dma_mark.bank;
298 /* initialize fresh bank */
299 dma_data[dma_mark.bank].diskbuffer_min = diskbuffer_frames;
300 dma_data[dma_mark.bank].dmabuffer_min = dmabuffer_frames;
301 dma_data[dma_mark.bank].diskbuffer_overrun=0;
302 dma_data[dma_mark.bank].dmabuffer_overrun=0;
306 diskbuffer_head = head;
307 sem_post(&buffer_sem);
309 }else{
310 /* not enough space for next frame; discard what we just read
311 and mark underrun */
312 dma_data[dma_mark.bank].diskbuffer_overrun=1;
313 dma_data[dma_mark.bank].diskbuffer_min=0;
314 if(dma_mark.read == dma_mark.bank){
315 dma_mark.bank = !dma_mark.bank;
317 /* initialize fresh bank */
318 dma_data[dma_mark.bank].diskbuffer_min = diskbuffer_frames;
319 dma_data[dma_mark.bank].dmabuffer_min = dmabuffer_frames;
320 dma_data[dma_mark.bank].diskbuffer_overrun=0;
321 dma_data[dma_mark.bank].dmabuffer_overrun=0;
328 snd_pcm_close(devhandle);
329 return(NULL);
332 void PutNumLE(long num,FILE *f,int bytes){
333 int i=0;
334 while(bytes--){
335 fputc((num>>(i<<3))&0xff,f);
336 i++;
340 /* mutant WAV header with undeclared chunklength */
341 void wav_header(FILE *f,long channels,long rate,long bits){
342 fseek(f,0,SEEK_SET);
343 fprintf(f,"RIFF");
344 PutNumLE(0xffffffff,f,4);
345 fprintf(f,"WAVEfmt ");
346 PutNumLE(16,f,4);
347 PutNumLE(1,f,2);
348 PutNumLE(channels,f,2);
349 PutNumLE(rate,f,4);
350 PutNumLE(rate*channels*((bits-1)/8+1),f,4);
351 PutNumLE(((bits-1)/8+1)*channels,f,2);
352 PutNumLE(bits,f,2);
353 fprintf(f,"data");
354 PutNumLE(0xffffffff,f,4);
357 float aweight_w1=0.f;
358 float aweight_w2=0.f;
359 float aweight_w3=0.f;
360 float aweight_w4=0.f;
361 float aweight_g=0.f;
363 typedef struct {
365 float _z1a;
366 float _z1b;
367 float _z2;
368 float _z3;
369 float _z4a;
370 float _z4b;
372 } afilter;
374 /* A-weighting code borrowed from LADSPA A-weighting plugin */
375 afilter *aweight_filter=0;
377 #define AW_F1 20.5990
378 #define AW_F2 107.652
379 #define AW_F3 737.862
380 #define AW_F4 12194.2
382 void aweight_init (int rate, int channels){
383 double f;
385 aweight_filter = calloc(channels, sizeof(*aweight_filter));
387 switch (rate) {
388 case 44100:
389 aweight_w4 = 0.846;
390 break;
391 case 48000:
392 aweight_w4 = 0.817;
393 break;
394 case 88200:
395 aweight_w4 = 0.587;
396 break;
397 case 96000:
398 aweight_w4 = 0.555;
399 break;
400 default:
401 fprintf(stderr,"A-weighting only available with following sample-rates:\n"
402 " 44100\n"
403 " 48000\n"
404 " 88200\n"
405 " 96000\n\n");
406 exit(1);
409 aweight_g = 1.2502f;
411 f = AW_F1 / rate;
412 aweight_w1 = 2 * M_PI * f;
413 aweight_g *= 2 / (2 - aweight_w1);
414 aweight_g *= 2 / (2 - aweight_w1); // twice !!
415 aweight_w1 *= 1 - 3 * f;
417 f = AW_F2 / rate;
418 aweight_w2 = 2 * M_PI * f;
419 aweight_g *= 2 / (2 - aweight_w2);
420 aweight_w2 *= 1 - 3 * f;
422 f = AW_F3 / rate;
423 aweight_w3 = 2 * M_PI * f;
424 aweight_g *= 2 / (2 - aweight_w3);
425 aweight_w3 *= 1 - 3 * f;
429 float aweight_process (float x, afilter *f){
431 // highpass sections
432 f->_z1a += aweight_w1 * (x - f->_z1a + 1e-40f);
433 x -= f->_z1a;
434 f->_z1b += aweight_w1 * (x - f->_z1b + 1e-40f);
435 x -= f->_z1b;
436 f->_z2 += aweight_w2 * (x - f->_z2 + 1e-40f);
437 x -= f->_z2;
438 f->_z3 += aweight_w3 * (x - f->_z3 + 1e-40f);
439 x -= f->_z3;
441 // lowpass sections
442 f->_z4a += aweight_w4 * (x - f->_z4a);
443 x = 0.25 * f->_z4b;
444 f->_z4b += aweight_w4 * (f->_z4a - f->_z4b);
445 x += 0.75 * f->_z4b;
447 return aweight_g * x;
450 void *disk_thread(void *dummy){
452 /* write out a 'streaming' WAV header */
453 if(out_FILE)
454 wav_header(out_FILE, channels, rate, width);
456 /* become consumer to the record thread's producer */
457 while(!exiting){
458 int head = diskbuffer_head;
459 int tail = diskbuffer_tail;
460 int full_frames,i,j;
461 unsigned char *ptr;
462 if(tail > head) head = diskbuffer_frames;
463 full_frames = (head - tail);
465 /* scan audio, compiling metadata */
466 ptr = diskbuffer + tail * framesize;
467 for(i=0;i<full_frames;i++){
468 for(j=0;j<channels;j++){
469 int32_t val=0;
470 float wval;
472 switch(samplesize){
473 case 4:
474 val |= (int)((unsigned int)*ptr);
475 val |= (int)(((unsigned int)*(ptr+1))<<8);
476 val |= (int)(((unsigned int)*(ptr+2))<<16);
477 val |= (int)(((unsigned int)*(ptr+3))<<24);
478 if(val == 0x7fffffff)pcm_data[disk_mark.bank][j].pcm_clip = 1;
479 break;
480 case 3:
481 val |= (int)(((unsigned int)*ptr)<<8);
482 val |= (int)(((unsigned int)*(ptr+1))<<16);
483 val |= (int)(((unsigned int)*(ptr+2))<<24);
484 if(val == 0x7fffff00)pcm_data[disk_mark.bank][j].pcm_clip = 1;
485 break;
486 case 2:
487 val |= (int)(((unsigned int)*ptr)<<16);
488 val |= (int)(((unsigned int)*(ptr+1))<<24);
489 if(val == 0x7fff0000)pcm_data[disk_mark.bank][j].pcm_clip = 1;
490 break;
491 case 1:
492 val |= (int)(((unsigned int)*ptr)<<24);
493 if(val == 0x7f000000)pcm_data[disk_mark.bank][j].pcm_clip = 1;
494 break;
497 if(val == (int32_t)0x80000000)pcm_data[disk_mark.bank][j].pcm_clip = 1;
499 /* find peak val per channel */
500 if(pcm_data[disk_mark.bank][j].peak < abs(val))
501 pcm_data[disk_mark.bank][j].peak = abs(val);
503 /* find rms/A-weight per-channel */
504 wval = val*.00000000046566128730;
505 if(weight){
506 wval = aweight_process(wval,&aweight_filter[j]);
507 val = wval / .00000000046566128730;
510 pcm_data[disk_mark.bank][j].weight_num += wval*wval;
511 pcm_data[disk_mark.bank][j].weight_denom += 1.;
513 /* write audio out */
514 if(out_FILE && !paused)
515 fwrite(ptr,1,samplesize,out_FILE);
517 ptr+=samplesize;
521 if(full_frames){
522 if(disk_mark.read == disk_mark.bank){
523 disk_mark.bank = !disk_mark.bank;
525 /* initialize new bank */
526 for(j=0;j<channels;j++){
527 pcm_data[disk_mark.bank][j].peak = 0;
528 pcm_data[disk_mark.bank][j].weight_num = 0.;
529 pcm_data[disk_mark.bank][j].weight_denom = 0.;
530 pcm_data[disk_mark.bank][j].pcm_clip = 0;
535 /* atomic tail update */
536 tail = head;
537 if(tail == diskbuffer_frames) tail = 0;
538 diskbuffer_tail = tail;
540 sem_post(&ui_sem);
541 if(diskbuffer_head == diskbuffer_tail)
542 sem_wait(&buffer_sem);
544 return(NULL);
547 const char *optstring = "ac:d:r:w:qb:ns:hu";
548 struct option options [] = {
549 {"help",no_argument,NULL,'h'},
550 {"no-realtime",no_argument,NULL,'n'},
551 {"no-lock",no_argument,NULL,'u'},
552 {"a-weight",no_argument,NULL,'a'},
553 {"quiet",no_argument,NULL,'q'},
554 {"buffer",optional_argument,NULL,'b'},
555 {"channels",optional_argument,NULL,'c'},
556 {"width",optional_argument,NULL,'w'},
557 {"rate",optional_argument,NULL,'r'},
558 {"device",optional_argument,NULL,'d'},
559 {"smooth",optional_argument,NULL,'s'},
560 {NULL,0,NULL,0}
563 static void usage(void){
564 fprintf(stderr,
565 "Usage: rtrecord [options] [output_file] \n\n"
566 "Options: \n\n"
567 " -a --a-weight A-weight the RMS level display\n"
568 " default is unweighted\n\n"
569 " -b --buffer <seconds> Request <seconds> of disk buffer\n"
570 " default is 10 seconds\n\n"
571 " -c --channels <n> Request recording of <n> channel audio\n"
572 " default is 2 channels (stereo)\n\n"
573 " -d --device <devname> Record from specified audio device\n"
574 " default is hw:0,0\n\n"
575 " -h --help This help message\n\n"
576 " -n --no-realtime Do not use realtime scheduling\n\n"
577 " -r --rate <n> Request sampling rate of <n> samples per\n"
578 " second. Default is 44100\n\n"
579 " -s --smooth <n> Smooth the VU readouts by the specified\n"
580 " factor; 0 is unsmoothed, 10 is\n"
581 " very smooth; default is 1\n\n"
582 " -q --quiet Produce no terminal output; run silently\n\n"
583 " -u --no-lock Do not try to lock the recording buffer\n"
584 " into physical memory\n\n"
585 " -w --width <n> Request a sample width of <n> bits\n"
586 " default is 16\n\n"
587 "Keys:\n\n"
588 " p Pause recoding. Pause does not interrupt sampling; it \n"
589 " merely suspends saving/piping recoded data out.\n\n"
590 " q Quit\n\n"
591 " space clear clipping and overrun flags\n\n"
592 "rtrecord outputs only uncompressed RIFF WAV format audio. When no\n"
593 "output file is specified and stdout is not redirected away from\n"
594 "the tty, no output is produced; rtrecord will only sample and update\n"
595 "the terminal VU meters.\n\n");
596 exit(1);
599 int main(int argc,char *argv[]){
600 int c,long_option_index,ret,seconds=10;
601 pthread_t dummy;
603 /* before anything else, drop filesystem privs! */
604 setfsuid(getuid());
606 if(!strcmp(argv[0],"record") ||
607 (strlen(argv[0])>=7 && !strcmp(argv[0]+strlen(argv[0])-7,"/record"))){
608 /* an alias for a version that requires no resources normally
609 limited to root, eg, don't lock the buffer, don't ask for
610 realtime in the record thread */
611 lockbuffer=0;
612 realtime=0;
615 while((c=getopt_long(argc,argv,optstring,options,&long_option_index))!=EOF){
616 switch(c){
617 case 'c':
618 channels = atoi(optarg);
620 if(channels<=0){
621 fprintf(stderr,"Number of channels must be greater than zero\n");
622 exit(1);
624 break;
625 case 'b':
626 seconds = atoi(optarg);
628 if(seconds<=0){
629 fprintf(stderr,"Disk buffer must be at least 1 second.\n");
630 exit(1);
632 break;
634 case 'd':
635 device = strdup(optarg);
636 break;
638 case 'a':
639 weight = 1;
640 break;
642 case 'n':
643 realtime=0;
644 break;
646 case 'q':
647 quiet = 1;
648 break;
650 case 'u':
651 lockbuffer = 0;
652 break;
654 case 'r':
655 rate = atoi(optarg);
656 if(rate<=0){
657 fprintf(stderr,"Sampling rate must be greater than zero\n");
658 exit(1);
660 break;
661 case 's':
663 int s = atoi(optarg);
664 if(s<0){
665 fprintf(stderr,"Minimum 'smooth' value is 0\n");
666 exit(1);
668 if(s>10){
669 fprintf(stderr,"Maximum 'smooth' value is 10\n");
670 exit(1);
672 smooth=1./(s+1);
674 break;
676 case 'w':
677 width = atoi(optarg);
678 switch(width){
679 case 8:
680 format = SND_PCM_FORMAT_S8;
681 samplesize = 1;
682 break;
683 case 16:
684 format = SND_PCM_FORMAT_S16_LE;
685 samplesize = 2;
686 break;
687 case 24:
688 format = SND_PCM_FORMAT_S24_3LE;
689 samplesize = 3;
690 break;
691 case 32:
692 format = SND_PCM_FORMAT_S32_LE;
693 samplesize = 4;
694 break;
695 default:
696 fprintf(stderr,"Supported linear PCM sampling widths: 8, 16, 24 and 32 bits\n");
697 exit(1);
699 break;
700 default:
701 usage();
705 if(optind<argc){
706 /* assume that anything following the options must be a filename */
707 out_name = strdup(argv[optind]);
708 out_FILE = fopen(out_name,"wb");
709 if(!out_FILE){
710 fprintf(stderr,"Unable to open file %s for writing.\n",out_name);
711 exit(1);
713 }else{
714 if(!isatty(STDOUT_FILENO)){
715 /* use stdout for writing, except that curses is fairly hardwired
716 to want stdout, so dup this first... */
717 int newfd = dup(STDOUT_FILENO);
718 out_name="stdout";
719 out_FILE = fdopen(newfd,"wb");
720 }else{
721 out_name="(none)";
722 out_FILE = 0;
726 /* if stdout isn't a terminal, and we're not running silent... */
727 if(!quiet && (!isatty(STDOUT_FILENO))){
728 /* if stderr is the terminal, let's use that. */
729 if(isatty(STDERR_FILENO)){
730 /* curses is fairly hardwired to want stdout, so remap stderr to stdout */
731 dup2(STDERR_FILENO,STDOUT_FILENO);
732 }else{
733 fprintf(stderr,"No terminal available for panel output; assuming -q\n");
734 quiet=1;
738 if(weight){
739 aweight_init(rate,channels);
743 /* Open hardware sample device */
744 if ((ret = snd_pcm_open (&devhandle, device, SND_PCM_STREAM_CAPTURE, 0)) < 0) {
745 fprintf(stderr,"unable to open audio device %s for record: %s.\n",device,snd_strerror(ret));
746 exit(1);
748 record_setup();
750 /* set up tty input subversion so that ncurses can be fully
751 functional entirely withing one thread */
752 ttyfd=open("/dev/tty",O_RDONLY);
753 if(ttyfd<0){
754 fprintf(stderr,"Unable to open /dev/tty:\n"
755 " %s\n",strerror(errno));
756 exit(1);
758 if(pipe(ttypipe)){
759 fprintf(stderr,"Unable to open tty pipe:\n"
760 " %s\n",strerror(errno));
761 exit(1);
763 dup2(ttypipe[0],0);
765 framesize = samplesize * channels;
766 diskbuffer_frames = (rate*seconds/getpagesize()+1)*getpagesize();
767 diskbuffer_size = diskbuffer_frames*framesize;
769 if(lockbuffer){
770 /* set up record buffer, lock it in-core */
771 diskbuffer = mmap(0, diskbuffer_size, PROT_READ|PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_LOCKED, 0, 0);
772 if(diskbuffer == MAP_FAILED){
773 if(errno == EAGAIN){
774 fprintf(stderr,"\nERROR: Unable to mmap locked buffer for capture;\n",strerror(errno));
775 fprintf(stderr,"The configured per-user resource limits currently disallow\n"
776 "locking enough memory in-core for a recording ringbuffer.\n\n"
777 "To run with an unlocked ringbuffer, use the -u option; an unlocked\n"
778 "ringbuffer may be slightly less reliable on a loaded machine.\n\n"
779 "To allow running with a locked ringbuffer, consider unlimiting\n"
780 "\"max locked memory\" for selected users or installing rtrecord\n"
781 "setuid root.\n\n");
782 }else{
783 fprintf(stderr,"Unable to mmap locked buffer for capture:\n %s\n",strerror(errno));
786 exit(1);
788 }else{
789 diskbuffer = malloc(diskbuffer_size);
792 /* set up shared metadata feedback structs */
793 pcm_data[0] = calloc(channels,sizeof(**pcm_data));
794 pcm_data[1] = calloc(channels,sizeof(**pcm_data));
796 sem_init(&ui_sem,0,0);
797 sem_init(&buffer_sem,0,0);
798 sem_init(&setup_sem,0,0);
800 /* spawn a small army of threads */
801 pthread_create(&disk_thread_id,NULL,disk_thread,NULL);
802 pthread_create(&record_thread_id,NULL,record_thread,NULL);
804 /* wait for realtime thread to declare it has started up */
805 sem_wait(&setup_sem);
807 /* drop privs, then continue starting threads */
808 setuid(getuid());
810 pthread_create(&tty_thread_id,NULL,tty_thread,NULL);
811 pthread_create(&ui_thread_id,NULL,ui_thread,NULL);
812 main_thread_id=pthread_self();
814 signal(SIGINT,exit_handler);
815 signal(SIGSEGV,exit_handler);
816 signal(SIGPIPE,exit_handler);
819 if(!quiet)
820 terminal_init_panel();
822 /* setup complete; perform work */
823 terminal_main_loop(quiet);
825 /* remove the panel if possible */
826 if(!quiet)
827 terminal_remove_panel();
829 exiting=1;
831 /* wake the disk thread if its asleep */
832 sem_post(&buffer_sem);
834 /* wake the ui thread if its asleep */
835 sem_post(&ui_sem);
837 /* waking the tty thread cleanly would require another fd and a
838 select(); much easier to just uncleanly cancel it */
839 pthread_cancel(tty_thread_id);
841 pthread_join(ui_thread_id, NULL);
842 pthread_join(tty_thread_id, NULL);
843 pthread_join(record_thread_id, NULL);
844 pthread_join(disk_thread_id, NULL);
845 sem_destroy(&ui_sem);
846 sem_destroy(&buffer_sem);
848 close(ttypipe[0]);
849 close(ttypipe[1]);
850 if(out_FILE)
851 fclose(out_FILE);
852 close(ttyfd);
854 free(pcm_data[0]);
855 free(pcm_data[1]);
856 return(0);