/* * fifo.c - a buffering program intended primarily for audio CD playback * * compile with: gcc -o fifo fifo.c * * run with argument -h or --help to read usage message */ #include #include #include #include #include #include #include #include #include static fd_set rset, wset; static struct timeval tout; void waitinput(); void waitoutput(); void waitio(); int main(int argc, char **argv) { char *unitstr, *buf, *top, *readptr, *writeptr; unsigned long bufsize, chunksize, unit, space, avail; long xferred; int eoi; if( argc > 3 || (argc==2 && !isdigit(*argv[1])) || (argc==3 && !isdigit(*argv[2])) ) { fprintf(stderr, "usage: | fifo [[k|M|G|s] [[k|M|G|s]]] | \n" "For example: cdparanoia 1- - | fifo | aplay -\n" "fifo is a FIFO application intended primarily for audio CD playback. It reads\n" "data from its standard input and outputs it unchanged to standard output. On\n" "program startup, its buffer is filled completely before outputting the first\n" "byte of data, to allow the CD ROM drive to spin up. Then output and input are\n" "performed in turn, with each output restricted to the chunk size to prevent the\n" "drive from spinning down intermediately. The buffer and chunk size can be\n" "given in bytes, kiB, MiB, GiB or seconds of CD-quality audio (16 bit stereo at\n" "44.1 kHz). The default buffer size is 1 MiB, the default chunk size is half\n" "the buffer size.\n"); return -1; } if( argc == 2 ) { bufsize= strtoul(argv[1], &unitstr, 0); if( *unitstr=='k' || *unitstr=='K' ) unit= 1024; else if( *unitstr=='m' || *unitstr=='M' ) unit= 1024*1024; else if( *unitstr=='g' || *unitstr=='G' ) unit= 1024*1024*1024; else if( *unitstr=='s' || *unitstr=='S' ) unit= 4*44100; // 16 bit stereo at 44.1 kHz else unit= 1; if( bufsize > ULONG_MAX / unit ) { fprintf(stderr, "fifo: Buffer size exceeds maximum of %lu bytes. Setting to maximum.\n", ULONG_MAX ); bufsize= ULONG_MAX; } else bufsize *= unit; } else bufsize= 1024*1024; if( argc == 3 ) { chunksize= strtoul(argv[2], &unitstr, 0); if( *unitstr=='k' || *unitstr=='K' ) unit= 1024; else if( *unitstr=='m' || *unitstr=='M' ) unit= 1024*1024; else if( *unitstr=='g' || *unitstr=='G' ) unit= 1024*1024*1024; else if( *unitstr=='s' || *unitstr=='S' ) unit= 4*44100; // 16 bit stereo at 44.1 kHz else unit= 1; if( chunksize > bufsize ) { fprintf(stderr, "fifo: Chunk size exceeds buffer size (%lu bytes). Setting to buffer size.\n", ULONG_MAX ); chunksize= bufsize; } else chunksize *= unit; } else chunksize= bufsize/2; buf= malloc(bufsize); if( !buf ) { fprintf(stderr, "fifo: Could not allocate buffer (size %lu bytes).\n", bufsize); return -1; } readptr= writeptr= buf; top= buf+bufsize; FD_ZERO(&rset); FD_ZERO(&wset); eoi= 0; // first fill the ring buffer while( !eoi && writeptr< top-1 ) { waitinput(); xferred= read(0, writeptr, top-writeptr-1); if( xferred > 0 ) writeptr += xferred; else eoi= 1; // end of input } while( 13 ) { space= readptr > writeptr? readptr-1-writeptr : readptr-1+bufsize-writeptr; avail= readptr > writeptr? writeptr+bufsize-readptr : writeptr-readptr; if( space && !eoi ) if( avail ) waitio(); else waitinput(); else if( avail ) waitoutput(); else break; // all data transferred if( readptr > writeptr ) avail= top-readptr; // restrict to contiguous region // downstream reads take precedence now (a FIFO read is a write() operation // from the FIFO's point of view) if( FD_ISSET(1, &wset) ) { xferred= write(1, readptr, avail>chunksize? chunksize : avail); if( xferred > 0 ) { readptr += xferred; if( readptr==top ) readptr= buf; space= readptr > writeptr? readptr-1-writeptr : top-writeptr; } else break; // downstream disconnect or error } if( readptr==writeptr ) readptr= writeptr= buf; // maximise contiguous space if( writeptr >= readptr ) space= readptr==buf? top-1-writeptr : top-writeptr; // restrict to contiguous region // now for writes to the FIFO, ie reads from upstream if( FD_ISSET(0, &rset) ) { xferred= read(0, writeptr, space); if( xferred > 0 ) { writeptr += xferred; if( writeptr==top ) writeptr= buf; } else eoi= 1; // end of input } } free(buf); return 0; } void waitinput() { int result; FD_SET(0, &rset); result= select(1, &rset, NULL, NULL, NULL); if( result==-1 ) { perror("fifo: select()"); exit(-1); } FD_CLR(1, &wset); // to simplify main program checks return; } void waitoutput() { int result; FD_SET(1, &wset); result= select(2, NULL, &wset, NULL, NULL); if( result==-1 ) { perror("fifo: select()"); exit(-1); } FD_CLR(0, &rset); // to simplify main program checks return; } void waitio() { int result; FD_SET(0, &rset); FD_SET(1, &wset); result= select(2, &rset, &wset, NULL, NULL); if( result==-1 ) { perror("fifo: select()"); exit(-1); } return; }