diff options
-rw-r--r-- | usr.bin/aucat/Makefile | 6 | ||||
-rw-r--r-- | usr.bin/aucat/aproc.c | 29 | ||||
-rw-r--r-- | usr.bin/aucat/aproc.h | 5 | ||||
-rw-r--r-- | usr.bin/aucat/aucat.c | 374 | ||||
-rw-r--r-- | usr.bin/aucat/dev.c | 485 | ||||
-rw-r--r-- | usr.bin/aucat/dev.h | 45 | ||||
-rw-r--r-- | usr.bin/aucat/dev_sun.c | 74 | ||||
-rw-r--r-- | usr.bin/aucat/file.c | 34 | ||||
-rw-r--r-- | usr.bin/aucat/file.h | 13 | ||||
-rw-r--r-- | usr.bin/aucat/legacy.c | 7 |
10 files changed, 727 insertions, 345 deletions
diff --git a/usr.bin/aucat/Makefile b/usr.bin/aucat/Makefile index a9992a57427..88c9a50e5b0 100644 --- a/usr.bin/aucat/Makefile +++ b/usr.bin/aucat/Makefile @@ -1,8 +1,8 @@ -# $OpenBSD: Makefile,v 1.2 2008/05/23 07:15:46 ratchov Exp $ +# $OpenBSD: Makefile,v 1.3 2008/08/14 09:58:55 ratchov Exp $ PROG= aucat -SRCS= aucat.c abuf.c aparams.c aproc.c dev_sun.c file.c headers.c \ - legacy.c +SRCS= aucat.c abuf.c aparams.c aproc.c dev.c file.c headers.c \ + dev_sun.c legacy.c CFLAGS+= -DDEBUG -Wall -Wstrict-prototypes -Werror -Wundef .include <bsd.prog.mk> diff --git a/usr.bin/aucat/aproc.c b/usr.bin/aucat/aproc.c index aa358a71052..8fa5c7adfb4 100644 --- a/usr.bin/aucat/aproc.c +++ b/usr.bin/aucat/aproc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: aproc.c,v 1.9 2008/08/14 09:47:51 ratchov Exp $ */ +/* $OpenBSD: aproc.c,v 1.10 2008/08/14 09:58:55 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -467,6 +467,33 @@ mix_new(void) return p; } +void +mix_pushzero(struct aproc *p) +{ + struct abuf *obuf = LIST_FIRST(&p->obuflist); + + abuf_wcommit(obuf, obuf->mixtodo); + obuf->mixtodo = 0; + abuf_flush(obuf); + mix_bzero(p); +} + +/* + * Normalize input levels + */ +void +mix_setmaster(struct aproc *p) +{ + unsigned n; + struct abuf *buf; + + n = 0; + LIST_FOREACH(buf, &p->ibuflist, ient) + n++; + LIST_FOREACH(buf, &p->ibuflist, ient) + buf->mixvol = ADATA_UNIT / n; +} + /* * Copy data from ibuf to obuf. */ diff --git a/usr.bin/aucat/aproc.h b/usr.bin/aucat/aproc.h index 26cd5a77136..c3cb00786cb 100644 --- a/usr.bin/aucat/aproc.h +++ b/usr.bin/aucat/aproc.h @@ -1,4 +1,4 @@ -/* $OpenBSD: aproc.h,v 1.4 2008/08/14 09:47:51 ratchov Exp $ */ +/* $OpenBSD: aproc.h,v 1.5 2008/08/14 09:58:55 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -171,4 +171,7 @@ struct aproc *mix_new(void); struct aproc *sub_new(void); struct aproc *conv_new(char *, struct aparams *, struct aparams *); +void mix_pushzero(struct aproc *); +void mix_setmaster(struct aproc *); + #endif /* !defined(FIFO_H) */ diff --git a/usr.bin/aucat/aucat.c b/usr.bin/aucat/aucat.c index 1128439b939..17c8d87500b 100644 --- a/usr.bin/aucat/aucat.c +++ b/usr.bin/aucat/aucat.c @@ -1,4 +1,4 @@ -/* $OpenBSD: aucat.c,v 1.25 2008/06/02 17:09:51 ratchov Exp $ */ +/* $OpenBSD: aucat.c,v 1.26 2008/08/14 09:58:55 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -17,10 +17,6 @@ /* * TODO: * - * (not yet)add a silent/quiet/verbose/whatever flag, but be sure - * that by default the user is notified when one of the following - * (cpu consuming) aproc is created: mix, sub, conv - * * (hard) use parsable encoding names instead of the lookup * table. For instance, [s|u]bits[le|be][/bytes{msb|lsb}], example * s8, s16le, s24le/3msb. This would give names that correspond to @@ -34,8 +30,6 @@ * s24le/3msb,{3-6},48000 so we don't have to use three -e, -r, -c * flags, but only one -p flag that specify one or more parameters. * - * (hard) dont create mix (sub) if there's only one input (output) - * * (hard) if all inputs are over, the mixer terminates and closes * the write end of the device. It should continue writing zeros * until the recording is over (or be able to stop write end of @@ -48,8 +42,6 @@ * they provide are not used on the output). Similarly ignore * outputs that are zero filled (because channels they consume are * not provided). - * - * (easy) do we need -d flag ? */ #include <sys/param.h> @@ -72,15 +64,12 @@ #include "file.h" #include "dev.h" -/* - * Format for file headers. - */ -#define HDR_AUTO 0 /* guess by looking at the file name */ -#define HDR_RAW 1 /* no headers, ie openbsd native ;-) */ -#define HDR_WAV 2 /* microsoft riff wave */ +int debug_level = 0, quiet_flag = 0; +volatile int quit_flag = 0, pause_flag = 0; -int debug_level = 0; -volatile int quit_flag = 0; +void suspend(struct file *); +void fill(struct file *); +void flush(struct file *); /* * List of allowed encodings and their names. @@ -212,9 +201,6 @@ struct farg { char *name; /* optarg pointer (no need to copy it */ int hdr; /* header format */ int xrun; /* overrun/underrun policy */ - int fd; /* file descriptor for I/O */ - struct aproc *proc; /* rpipe_xxx our wpipe_xxx */ - struct abuf *buf; }; SLIST_HEAD(farglist, farg); @@ -250,7 +236,6 @@ opt_file(struct farglist *list, fa->par = *par; fa->vol = vol; fa->name = optarg; - fa->proc = NULL; SLIST_INSERT_HEAD(list, fa, entry); } @@ -258,12 +243,13 @@ opt_file(struct farglist *list, * Open an input file and setup converter if necessary. */ void -newinput(struct farg *fa, struct aparams *npar, unsigned nfr, int quiet_flag) +newinput(struct farg *fa) { int fd; struct file *f; - struct aproc *p, *c; - struct abuf *buf, *nbuf; + struct aproc *proc; + struct abuf *buf; + unsigned nfr; if (strcmp(fa->name, "-") == 0) { fd = STDIN_FILENO; @@ -276,40 +262,30 @@ newinput(struct farg *fa, struct aparams *npar, unsigned nfr, int quiet_flag) err(1, "%s", fa->name); } f = file_new(fd, fa->name); + f->hdr = 0; + f->hpar = fa->par; if (fa->hdr == HDR_WAV) { - if (!wav_readhdr(fd, &fa->par, &f->rbytes)) + if (!wav_readhdr(fd, &f->hpar, &f->rbytes)) exit(1); } - buf = abuf_new(nfr, aparams_bpf(&fa->par)); - p = rpipe_new(f); - aproc_setout(p, buf); - if (!aparams_eq(&fa->par, npar)) { - if (!quiet_flag) { - fprintf(stderr, "%s: ", fa->name); - aparams_print2(&fa->par, npar); - fprintf(stderr, "\n"); - } - nbuf = abuf_new(nfr, aparams_bpf(npar)); - c = conv_new(fa->name, &fa->par, npar); - aproc_setin(c, buf); - aproc_setout(c, nbuf); - fa->buf = nbuf; - } else - fa->buf = buf; - fa->proc = p; - fa->fd = fd; + nfr = dev_onfr * f->hpar.rate / dev_opar.rate; + buf = abuf_new(nfr, aparams_bpf(&f->hpar)); + proc = rpipe_new(f); + aproc_setout(proc, buf); + dev_attach(fa->name, buf, &f->hpar, fa->xrun, NULL, NULL, 0); } /* * Open an output file and setup converter if necessary. */ void -newoutput(struct farg *fa, struct aparams *npar, unsigned nfr, int quiet_flag) +newoutput(struct farg *fa) { int fd; struct file *f; - struct aproc *p, *c; - struct abuf *buf, *nbuf; + struct aproc *proc; + struct abuf *buf; + unsigned nfr; if (strcmp(fa->name, "-") == 0) { fd = STDOUT_FILENO; @@ -323,57 +299,30 @@ newoutput(struct farg *fa, struct aparams *npar, unsigned nfr, int quiet_flag) err(1, "%s", fa->name); } f = file_new(fd, fa->name); - if (fa->hdr == HDR_WAV) { + f->hdr = fa->hdr; + f->hpar = fa->par; + if (f->hdr == HDR_WAV) { f->wbytes = WAV_DATAMAX; - if (!wav_writehdr(fd, &fa->par)) + if (!wav_writehdr(fd, &f->hpar)) exit(1); } - buf = abuf_new(nfr, aparams_bpf(&fa->par)); - p = wpipe_new(f); - aproc_setin(p, buf); - if (!aparams_eq(&fa->par, npar)) { - if (!quiet_flag) { - fprintf(stderr, "%s: ", fa->name); - aparams_print2(npar, &fa->par); - fprintf(stderr, "\n"); - } - c = conv_new(fa->name, npar, &fa->par); - nbuf = abuf_new(nfr, aparams_bpf(npar)); - aproc_setin(c, nbuf); - aproc_setout(c, buf); - fa->buf = nbuf; - } else - fa->buf = buf; - fa->proc = p; - fa->fd = fd; -} - -void -sighdl(int s) -{ - if (quit_flag) - _exit(1); - quit_flag = 1; + nfr = dev_infr * f->hpar.rate / dev_ipar.rate; + proc = wpipe_new(f); + buf = abuf_new(nfr, aparams_bpf(&f->hpar)); + aproc_setin(proc, buf); + dev_attach(fa->name, NULL, NULL, 0, buf, &f->hpar, fa->xrun); } int main(int argc, char **argv) { - sigset_t sigset; - struct sigaction sa; - int c, u_flag, quiet_flag, ohdr, ihdr, ixrun, oxrun; + int c, u_flag, ohdr, ihdr, ixrun, oxrun; struct farg *fa; struct farglist ifiles, ofiles; - struct aparams ipar, opar, dipar, dopar, cipar, copar; + struct aparams ipar, opar, dipar, dopar; unsigned ivol, ovol; - unsigned dinfr, donfr, cinfr, confr; char *devpath, *dbgenv; - unsigned n; - struct aproc *rec, *play, *mix, *sub, *conv; - struct file *dev, *f; - struct abuf *buf, *cbuf; const char *errstr; - int fd; dbgenv = getenv("AUCAT_DEBUG"); if (dbgenv) { @@ -385,7 +334,7 @@ main(int argc, char **argv) aparams_init(&ipar, 0, 1, 44100); aparams_init(&opar, 0, 1, 44100); - u_flag = quiet_flag = 0; + u_flag = 0; devpath = NULL; SLIST_INIT(&ifiles); SLIST_INIT(&ofiles); @@ -477,240 +426,77 @@ main(int argc, char **argv) exit(1); } - sigfillset(&sa.sa_mask); - sa.sa_flags = SA_RESTART; - sa.sa_handler = sighdl; - if (sigaction(SIGINT, &sa, NULL) < 0) - err(1, "sigaction"); - - sigemptyset(&sigset); - (void)sigaddset(&sigset, SIGTSTP); - (void)sigaddset(&sigset, SIGCONT); - if (sigprocmask(SIG_BLOCK, &sigset, NULL)) - err(1, "sigprocmask"); - - file_start(); - play = rec = mix = sub = NULL; - - aparams_init(&cipar, CHAN_MAX, 0, RATE_MIN); - aparams_init(&copar, CHAN_MAX, 0, RATE_MAX); - - /* - * Iterate over all inputs and outputs and find the maximum - * sample rate and channel number. - */ - SLIST_FOREACH(fa, &ifiles, entry) { - if (cipar.cmin > fa->par.cmin) - cipar.cmin = fa->par.cmin; - if (cipar.cmax < fa->par.cmax) - cipar.cmax = fa->par.cmax; - if (cipar.rate < fa->par.rate) - cipar.rate = fa->par.rate; - } - SLIST_FOREACH(fa, &ofiles, entry) { - if (copar.cmin > fa->par.cmin) - copar.cmin = fa->par.cmin; - if (copar.cmax < fa->par.cmax) - copar.cmax = fa->par.cmax; - if (copar.rate > fa->par.rate) - copar.rate = fa->par.rate; - } - /* - * Open the device and increase the maximum sample rate. - * channel number to include those used by the device - */ if (!u_flag) { - dipar = copar; - dopar = cipar; - } - fd = dev_init(devpath, - !SLIST_EMPTY(&ofiles) ? &dipar : NULL, - !SLIST_EMPTY(&ifiles) ? &dopar : NULL, &dinfr, &donfr); - if (fd < 0) - exit(1); - if (!SLIST_EMPTY(&ofiles)) { - if (!quiet_flag) { - fprintf(stderr, "%s: recording ", devpath); - aparams_print(&dipar); - fprintf(stderr, "\n"); + /* + * Calculate "best" device parameters. Iterate over all + * inputs and outputs and find the maximum sample rate + * and channel number. + */ + aparams_init(&dipar, CHAN_MAX, 0, RATE_MAX); + aparams_init(&dopar, CHAN_MAX, 0, RATE_MIN); + SLIST_FOREACH(fa, &ifiles, entry) { + if (dopar.cmin > fa->par.cmin) + dopar.cmin = fa->par.cmin; + if (dopar.cmax < fa->par.cmax) + dopar.cmax = fa->par.cmax; + if (dopar.rate < fa->par.rate) + dopar.rate = fa->par.rate; } - if (copar.cmin > dipar.cmin) - copar.cmin = dipar.cmin; - if (copar.cmax < dipar.cmax) - copar.cmax = dipar.cmax; - if (copar.rate > dipar.rate) - copar.rate = dipar.rate; - dinfr *= DEFAULT_NBLK; - DPRINTF("%s: using %ums rec buffer\n", devpath, - 1000 * dinfr / dipar.rate); - } - if (!SLIST_EMPTY(&ifiles)) { - if (!quiet_flag) { - fprintf(stderr, "%s: playing ", devpath); - aparams_print(&dopar); - fprintf(stderr, "\n"); + SLIST_FOREACH(fa, &ofiles, entry) { + if (dipar.cmin > fa->par.cmin) + dipar.cmin = fa->par.cmin; + if (dipar.cmax < fa->par.cmax) + dipar.cmax = fa->par.cmax; + if (dipar.rate > fa->par.rate) + dipar.rate = fa->par.rate; } - if (cipar.cmin > dopar.cmin) - cipar.cmin = dopar.cmin; - if (cipar.cmax < dopar.cmax) - cipar.cmax = dopar.cmax; - if (cipar.rate < dopar.rate) - cipar.rate = dopar.rate; - donfr *= DEFAULT_NBLK; - DPRINTF("%s: using %ums play buffer\n", devpath, - 1000 * donfr / dopar.rate); - } - - /* - * Create buffers for the device. - */ - dev = file_new(fd, devpath); - if (!SLIST_EMPTY(&ofiles)) { - rec = rpipe_new(dev); - sub = sub_new(); - } - if (!SLIST_EMPTY(&ifiles)) { - play = wpipe_new(dev); - mix = mix_new(); } + file_start(); /* - * Calculate sizes of buffers using "common" parameters, to - * have roughly the same duration as device buffers. + * Open the device, dev_init() will return new parameters + * that must be used by all inputs and outputs. */ - cinfr = donfr * cipar.rate / dopar.rate; - confr = dinfr * copar.rate / dipar.rate; + dev_init(devpath, + (!SLIST_EMPTY(&ofiles)) ? &dipar : NULL, + (!SLIST_EMPTY(&ifiles)) ? &dopar : NULL); /* * Create buffers for all input and output pipes. */ - SLIST_FOREACH(fa, &ifiles, entry) { - newinput(fa, &cipar, cinfr, quiet_flag); - if (mix) { - aproc_setin(mix, fa->buf); - fa->buf->xrun = fa->xrun; - } - if (!quiet_flag) { - fprintf(stderr, "%s: reading ", fa->name); - aparams_print(&fa->par); - fprintf(stderr, "\n"); - } + while (!SLIST_EMPTY(&ifiles)) { + fa = SLIST_FIRST(&ifiles); + SLIST_REMOVE_HEAD(&ifiles, entry); + newinput(fa); + free(fa); } - SLIST_FOREACH(fa, &ofiles, entry) { - newoutput(fa, &copar, confr, quiet_flag); - if (sub) { - aproc_setout(sub, fa->buf); - fa->buf->xrun = fa->xrun; - } - if (!quiet_flag) { - fprintf(stderr, "%s: writing ", fa->name); - aparams_print(&fa->par); - fprintf(stderr, "\n"); - } + while (!SLIST_EMPTY(&ofiles)) { + fa = SLIST_FIRST(&ofiles); + SLIST_REMOVE_HEAD(&ofiles, entry); + newoutput(fa); + free(fa); } /* - * Connect the multiplexer to the device input. + * Normalize input levels */ - if (sub) { - buf = abuf_new(dinfr, aparams_bpf(&dipar)); - aproc_setout(rec, buf); - if (!aparams_eq(&copar, &dipar)) { - if (!quiet_flag) { - fprintf(stderr, "%s: ", devpath); - aparams_print2(&dipar, &copar); - fprintf(stderr, "\n"); - } - conv = conv_new("subconv", &dipar, &copar); - cbuf = abuf_new(confr, aparams_bpf(&copar)); - aproc_setin(conv, buf); - aproc_setout(conv, cbuf); - aproc_setin(sub, cbuf); - } else - aproc_setin(sub, buf); - } - - /* - * Normalize input levels and connect the mixer to the device - * output. - */ - if (mix) { - n = 0; - SLIST_FOREACH(fa, &ifiles, entry) - n++; - SLIST_FOREACH(fa, &ifiles, entry) - fa->buf->mixvol /= n; - buf = abuf_new(donfr, aparams_bpf(&dopar)); - aproc_setin(play, buf); - if (!aparams_eq(&cipar, &dopar)) { - if (!quiet_flag) { - fprintf(stderr, "%s: ", devpath); - aparams_print2(&cipar, &dopar); - fprintf(stderr, "\n"); - } - conv = conv_new("mixconv", &cipar, &dopar); - cbuf = abuf_new(cinfr, aparams_bpf(&cipar)); - aproc_setout(conv, buf); - aproc_setin(conv, cbuf); - aproc_setout(mix, cbuf); - } else - aproc_setout(mix, buf); - } + if (dev_mix) + mix_setmaster(dev_mix); /* * start audio */ - if (play != NULL) { - if (!quiet_flag) - fprintf(stderr, "filling buffers...\n"); - buf = LIST_FIRST(&play->ibuflist); - while (!quit_flag) { - /* no more devices to poll */ - if (!file_poll()) - break; - /* eof */ - if (dev->state & FILE_EOF) - break; - /* device is blocked and play buffer is full */ - if ((dev->events & POLLOUT) && !ABUF_WOK(buf)) - break; - } - } if (!quiet_flag) fprintf(stderr, "starting device...\n"); - dev_start(dev->fd); - if (mix) - mix->u.mix.flags |= MIX_DROP; - if (sub) - sub->u.sub.flags |= SUB_DROP; - while (!quit_flag) { - if (!file_poll()) - break; - } - + dev_start(); if (!quiet_flag) - fprintf(stderr, "draining buffers...\n"); + fprintf(stderr, "process started...\n"); + dev_run(1); + if (!quiet_flag) + fprintf(stderr, "stopping device...\n"); + dev_done(); - /* - * generate EOF on all files that do input, so - * once buffers are drained, everything will be cleaned - */ - LIST_FOREACH(f, &file_list, entry) { - if ((f->events) & POLLIN || (f->state & FILE_ROK)) - file_eof(f); - } - for (;;) { - if (!file_poll()) - break; - } - SLIST_FOREACH(fa, &ofiles, entry) { - if (fa->hdr == HDR_WAV) - wav_writehdr(fa->fd, &fa->par); - close(fa->fd); - DPRINTF("%s: closed\n", fa->name); - } - dev_stop(dev->fd); file_stop(); return 0; } diff --git a/usr.bin/aucat/dev.c b/usr.bin/aucat/dev.c new file mode 100644 index 00000000000..808450aa0c7 --- /dev/null +++ b/usr.bin/aucat/dev.c @@ -0,0 +1,485 @@ +/* $OpenBSD: dev.c,v 1.1 2008/08/14 09:58:55 ratchov Exp $ */ +/* + * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <signal.h> +#include <err.h> + +#include "dev.h" +#include "abuf.h" +#include "aproc.h" +#include "file.h" +#include "conf.h" + +int quit_flag, pause_flag; +unsigned dev_infr, dev_onfr; +struct aparams dev_ipar, dev_opar; +struct aproc *dev_mix, *dev_sub, *dev_rec, *dev_play; +struct file *dev_file; +struct devops *devops = &devops_sun; + +/* + * SIGINT handler, it raises the quit flag. If the flag is already set, + * that means that the last SIGINT was not handled, because the process + * is blocked somewhere, so exit + */ +void +sigint(int s) +{ + if (quit_flag) + _exit(1); + quit_flag = 1; +} + +/* + * called when the user hits ctrl-z + */ +void +sigtstp(int s) +{ + pause_flag = 1; +} + +/* + * SIGCONT is send when resumed after SIGTSTP or SIGSTOP. If the pause + * flag is not set, that means that the process was not suspended by + * dev_suspend(), which means that we lost the sync; since we cannot + * resync, just exit + */ +void +sigcont(int s) +{ + static char msg[] = "can't resume afer SIGSTOP, terminating...\n"; + + if (!pause_flag) { + write(STDERR_FILENO, msg, sizeof(msg) - 1); + _exit(1); + } +} + +/* + * suicide with SIGTSTP (tty stop) as if the user had hit ctrl-z + */ +void +dev_suspend(void) +{ + struct sigaction sa; + + sigfillset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + sa.sa_handler = SIG_DFL; + if (sigaction(SIGTSTP, &sa, NULL) < 0) + err(1, "sigaction"); + DPRINTF("suspended by tty\n"); + kill(getpid(), SIGTSTP); + pause_flag = 0; + sa.sa_handler = sigtstp; + if (sigaction(SIGTSTP, &sa, NULL) < 0) + err(1, "sigaction"); + DPRINTF("resumed after suspend\n"); +} + +/* + * fill playback buffer, so when device is started there + * are samples to play + */ +void +dev_fill(void) +{ + struct abuf *buf; + + + /* + * if there are no inputs, zero fill the mixer + */ + if (dev_mix && LIST_EMPTY(&dev_mix->ibuflist)) + mix_pushzero(dev_mix); + DPRINTF("filling play buffers...\n"); + for (;;) { + if (!dev_file->wproc) { + DPRINTF("fill: no writer\n"); + break; + } + if (dev_file->events & POLLOUT) { + /* + * kernel buffers are full, but continue + * until the play buffer is full too. + */ + buf = LIST_FIRST(&dev_file->wproc->ibuflist); + if (!ABUF_WOK(buf)) + break; /* buffer full */ + if (!buf->wproc) + break; /* will never be filled */ + } + if (!file_poll()) + break; + if (pause_flag) + dev_suspend(); + } +} + +/* + * flush recorded samples once the device is stopped so + * they aren't lost + */ +void +dev_flush(void) +{ + struct abuf *buf; + + DPRINTF("flushing record buffers...\n"); + for (;;) { + if (!dev_file->rproc) { + DPRINTF("flush: no more reader\n"); + break; + } + if (dev_file->events & POLLIN) { + /* + * we drained kernel buffers, but continue + * until the record buffer is empty. + */ + buf = LIST_FIRST(&dev_file->rproc->obuflist); + if (!ABUF_ROK(buf)) + break; /* buffer empty */ + if (!buf->rproc) + break; /* will never be drained */ + } + if (!file_poll()) + break; + if (pause_flag) + dev_suspend(); + } +} + + +/* + * open the device with the given hardware parameters and create a mixer + * and a multiplexer connected to it with all necessary conversions + * setup + */ +void +dev_init(char *devpath, struct aparams *dipar, struct aparams *dopar) +{ + int fd; + struct sigaction sa; + unsigned infr, onfr; + struct aparams ipar, opar; + struct aproc *conv; + struct abuf *buf; + + quit_flag = 0; + pause_flag = 0; + + sigfillset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + sa.sa_handler = sigint; + if (sigaction(SIGINT, &sa, NULL) < 0) + err(1, "sigaction"); + sa.sa_handler = sigtstp; + if (sigaction(SIGTSTP, &sa, NULL) < 0) + err(1, "sigaction"); + sa.sa_handler = sigcont; + if (sigaction(SIGCONT, &sa, NULL) < 0) + err(1, "sigaction"); + + fd = devops->open(devpath, dipar, dopar, &infr, &onfr); + if (fd < 0) + exit(1); + dev_file = file_new(fd, devpath); + + /* + * create record chain + */ + if (dipar) { + aparams_init(&ipar, dipar->cmin, dipar->cmax, dipar->rate); + infr *= DEFAULT_NBLK; + + /* + * create the read end + */ + dev_rec = rpipe_new(dev_file); + buf = abuf_new(infr, aparams_bpf(dipar)); + aproc_setout(dev_rec, buf); + + /* + * append a converter, if needed + */ + if (!aparams_eq(dipar, &ipar)) { + if (debug_level > 0) { + fprintf(stderr, "%s: ", devpath); + aparams_print2(dipar, &ipar); + fprintf(stderr, "\n"); + } + conv = conv_new("subconv", dipar, &ipar); + aproc_setin(conv, buf); + buf = abuf_new(infr, aparams_bpf(&ipar)); + aproc_setout(conv, buf); + } + dev_ipar = ipar; + dev_infr = infr; + + /* + * append a "sub" to which clients will connect + */ + dev_sub = sub_new(); + aproc_setin(dev_sub, buf); + } else { + dev_rec = NULL; + dev_sub = NULL; + } + + /* + * create play chain + */ + if (dopar) { + aparams_init(&opar, dopar->cmin, dopar->cmax, dopar->rate); + onfr *= DEFAULT_NBLK; + + /* + * create the write end + */ + dev_play = wpipe_new(dev_file); + buf = abuf_new(onfr, aparams_bpf(dopar)); + aproc_setin(dev_play, buf); + + /* + * append a converter, if needed + */ + if (!aparams_eq(&opar, dopar)) { + if (debug_level > 0) { + fprintf(stderr, "%s: ", devpath); + aparams_print2(&opar, dopar); + fprintf(stderr, "\n"); + } + conv = conv_new("mixconv", &opar, dopar); + aproc_setout(conv, buf); + buf = abuf_new(onfr, aparams_bpf(&opar)); + aproc_setin(conv, buf); + *dopar = opar; + } + dev_opar = opar; + dev_onfr = onfr; + + /* + * append a "mix" to which clients will connect + */ + dev_mix = mix_new(); + aproc_setout(dev_mix, buf); + } else { + dev_play = NULL; + dev_mix = NULL; + } +} + +/* + * cleanly stop and drain everything and close the device + * once both play chain and record chain are gone + */ +void +dev_done(void) +{ + struct sigaction sa; + struct file *f; + + /* + * generate EOF on all inputs (including device), so once + * buffers are drained, everything will be cleaned + */ + LIST_FOREACH(f, &file_list, entry) { + if (f->rproc) + file_eof(f); + } + /* + * destroy automatically mixe instead + * of generating silence + */ + if (dev_mix) + dev_mix->u.mix.flags |= MIX_AUTOQUIT; + if (dev_sub) + dev_sub->u.sub.flags |= SUB_AUTOQUIT; + /* + * drain buffers of terminated inputs. + */ + for (;;) { + if (!file_poll()) + break; + } + devops->close(dev_file->fd); + + sigfillset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + sa.sa_handler = SIG_DFL; + if (sigaction(SIGINT, &sa, NULL) < 0) + err(1, "sigaction"); + if (sigaction(SIGTSTP, &sa, NULL) < 0) + err(1, "sigaction"); + if (sigaction(SIGCONT, &sa, NULL) < 0) + err(1, "sigaction"); +} + +/* + * start the (paused) device. By default it's paused + */ +void +dev_start(void) +{ + dev_fill(); + if (dev_mix) + dev_mix->u.mix.flags |= MIX_DROP; + if (dev_sub) + dev_sub->u.sub.flags |= SUB_DROP; + devops->start(dev_file->fd); +} + +/* + * pause the device + */ +void +dev_stop(void) +{ + devops->stop(dev_file->fd); + if (dev_mix) + dev_mix->u.mix.flags &= ~MIX_DROP; + if (dev_sub) + dev_sub->u.sub.flags &= ~SUB_DROP; + dev_flush(); +} + +/* + * loop until there's either input or output to process + */ +void +dev_run(int autoquit) +{ + while (!quit_flag) { + if ((!dev_mix || LIST_EMPTY(&dev_mix->ibuflist)) && + (!dev_sub || LIST_EMPTY(&dev_sub->obuflist)) && autoquit) + break; + if (!file_poll()) + break; + if (pause_flag) { + devops->stop(dev_file->fd); + dev_flush(); + dev_suspend(); + dev_fill(); + devops->start(dev_file->fd); + } + } +} + +/* + * attach the given input and output buffers to the mixer and the + * multiplexer respectively. The operation is done synchronously, so + * both buffers enter in sync. If buffers do not match play + * and rec + */ +void +dev_attach(char *name, + struct abuf *ibuf, struct aparams *ipar, unsigned underrun, + struct abuf *obuf, struct aparams *opar, unsigned overrun) +{ + int delta; + struct abuf *pbuf = NULL, *rbuf = NULL; + struct aproc *conv; + + if (ibuf) { + pbuf = LIST_FIRST(&dev_mix->obuflist); + if (!aparams_eq(ipar, &dev_opar)) { + if (debug_level > 1) { + fprintf(stderr, "dev_attach: %s: ", name); + aparams_print2(ipar, &dev_opar); + fprintf(stderr, "\n"); + } + conv = conv_new(name, ipar, &dev_opar); + aproc_setin(conv, ibuf); + ibuf = abuf_new(dev_onfr, aparams_bpf(&dev_opar)); + aproc_setout(conv, ibuf); + } + aproc_setin(dev_mix, ibuf); + ibuf->xrun = underrun; + mix_setmaster(dev_mix); + } + if (obuf) { + rbuf = LIST_FIRST(&dev_sub->ibuflist); + if (!aparams_eq(opar, &dev_ipar)) { + if (debug_level > 1) { + fprintf(stderr, "dev_attach: %s: ", name); + aparams_print2(&dev_ipar, opar); + fprintf(stderr, "\n"); + } + conv = conv_new(name, &dev_ipar, opar); + aproc_setout(conv, obuf); + obuf = abuf_new(dev_infr, aparams_bpf(&dev_ipar)); + aproc_setin(conv, obuf); + } + aproc_setout(dev_sub, obuf); + obuf->xrun = overrun; + } + + /* + * calculate delta, the number of frames the play chain is ahead + * of the record chain. It's necessary to schedule silences (or + * drops) in order to start playback and record in sync. + */ + if (ibuf && obuf) { + delta = + rbuf->bpf * (pbuf->abspos + pbuf->used) - + pbuf->bpf * rbuf->abspos; + delta /= pbuf->bpf * rbuf->bpf; + DPRINTF("dev_attach: ppos = %u, pused = %u, rpos = %u\n", + pbuf->abspos, pbuf->used, rbuf->abspos); + } else + delta = 0; + DPRINTF("dev_attach: delta = %u\n", delta); + + if (delta > 0) { + /* + * if the play chain is ahead (most cases) drop some of + * the recorded input, to get both in sync + */ + obuf->drop += delta * obuf->bpf; + } else if (delta < 0) { + /* + * if record chain is ahead (should never happen, + * right?) then insert silence to play + */ + ibuf->silence += -delta * ibuf->bpf; + } + if (ibuf && (dev_mix->u.mix.flags & MIX_DROP)) { + DPRINTF("lmkqsjdlkqsjklqsd\n"); + /* + * fill the play buffer with silence to avoid underruns, + * drop samples on the input to keep play/record in sync + * after the silence insertion + */ + ibuf->silence += dev_onfr * ibuf->bpf; + if (obuf) + obuf->drop += dev_onfr * obuf->bpf; + /* + * force data to propagate + */ + abuf_run(ibuf); + DPRINTF("dev_attach: ibuf: used = %u, silence = %u\n", + ibuf->used, ibuf->silence); + } + if (obuf && (dev_sub->u.mix.flags & SUB_DROP)) { + abuf_run(obuf); + DPRINTF("dev_attach: ibuf: used = %u, drop = %u\n", + obuf->used, obuf->drop); + } +} diff --git a/usr.bin/aucat/dev.h b/usr.bin/aucat/dev.h index 940e2b52bb4..e5b50af53a3 100644 --- a/usr.bin/aucat/dev.h +++ b/usr.bin/aucat/dev.h @@ -1,4 +1,4 @@ -/* $OpenBSD: dev.h,v 1.1 2008/05/23 07:15:46 ratchov Exp $ */ +/* $OpenBSD: dev.h,v 1.2 2008/08/14 09:58:55 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -17,19 +17,42 @@ #ifndef DEV_H #define DEV_H -#include <sys/types.h> -#include <sys/ioctl.h> -#include <sys/audioio.h> -#include <string.h> +struct aproc; +struct aparams; +struct file; +struct abuf; -int dev_init(char *, struct aparams *, struct aparams *, - unsigned *, unsigned *); -void dev_done(int); -void dev_start(int); -void dev_stop(int); +extern unsigned dev_infr, dev_onfr; +extern struct aparams dev_ipar, dev_opar; +extern struct aproc *dev_mix, *dev_sub, *dev_rec, *dev_play; +extern struct file *dev_file; +void dev_fill(void); +void dev_flush(void); +void dev_init(char *, struct aparams *, struct aparams *); +void dev_start(void); +void dev_stop(void); +void dev_run(int); +void dev_done(void); +void dev_attach(char *, + struct abuf *, struct aparams *, unsigned, + struct abuf *, struct aparams *, unsigned); + +struct devops { + int (*open)(char *, struct aparams *, struct aparams *, + unsigned *, unsigned *); + void (*close)(int); + void (*start)(int); + void (*stop)(int); +}; + +extern struct devops *devops, devops_sun; + +/* + * Sun API specific functions + */ +struct audio_prinfo; int sun_infotopar(struct audio_prinfo *, struct aparams *); void sun_partoinfo(struct audio_prinfo *, struct aparams *); - #endif /* !define(DEV_H) */ diff --git a/usr.bin/aucat/dev_sun.c b/usr.bin/aucat/dev_sun.c index e0e36b07d0e..2e97f6def0e 100644 --- a/usr.bin/aucat/dev_sun.c +++ b/usr.bin/aucat/dev_sun.c @@ -1,4 +1,4 @@ -/* $OpenBSD: dev_sun.c,v 1.4 2008/06/03 14:36:20 drahn Exp $ */ +/* $OpenBSD: dev_sun.c,v 1.5 2008/08/14 09:58:55 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -15,11 +15,16 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/audioio.h> + #include <err.h> #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> +#include <string.h> #include <unistd.h> #include "conf.h" @@ -27,7 +32,7 @@ #include "dev.h" /* - * convert sun device parameters to struct params + * Convert sun device parameters to struct aparams */ int sun_infotopar(struct audio_prinfo *ai, struct aparams *par) @@ -75,7 +80,7 @@ sun_infotopar(struct audio_prinfo *ai, struct aparams *par) } /* - * Convert struct params to sun device parameters. + * Convert struct aparams to sun device parameters. */ void sun_partoinfo(struct audio_prinfo *ai, struct aparams *par) @@ -98,11 +103,11 @@ sun_partoinfo(struct audio_prinfo *ai, struct aparams *par) * Open the device and pause it, so later play and record * can be started simultaneously. * - * int "infr" and "onfd" we return the input and the output + * in "infr" and "onfd" we return the input and the output * block sizes respectively. */ int -dev_init(char *path, struct aparams *ipar, struct aparams *opar, +sun_open(char *path, struct aparams *ipar, struct aparams *opar, unsigned *infr, unsigned *onfr) { int fd; @@ -173,7 +178,7 @@ dev_init(char *path, struct aparams *ipar, struct aparams *opar, return -1; } if (ioctl(fd, AUDIO_GETINFO, &aui) < 0) { - warn("dev_init: getinfo"); + warn("sun_open: getinfo"); close(fd); return -1; } @@ -206,18 +211,21 @@ dev_init(char *path, struct aparams *ipar, struct aparams *opar, return fd; } +/* + * Drain and close the device + */ void -dev_done(int fd) +sun_close(int fd) { close(fd); - DPRINTF("dev_done: closed\n"); + DPRINTF("sun_close: closed\n"); } /* - * Start play/record. + * Start play/record simultaneously. Play buffers must be filled. */ void -dev_start(int fd) +sun_start(int fd) { audio_info_t aui; @@ -228,37 +236,47 @@ dev_start(int fd) AUDIO_INITINFO(&aui); aui.play.pause = aui.record.pause = 0; if (ioctl(fd, AUDIO_SETINFO, &aui) < 0) - err(1, "dev_start: setinfo"); + err(1, "sun_start: setinfo"); - DPRINTF("dev_start: play/rec started\n"); + DPRINTF("sun_start: play/rec started\n"); } /* - * Stop play/record and clear kernel buffers so that dev_start() can be called - * again. + * Drain play buffers and then stop play/record simultaneously. */ void -dev_stop(int fd) +sun_stop(int fd) { audio_info_t aui; - unsigned mode; - - if (ioctl(fd, AUDIO_DRAIN) < 0) - err(1, "dev_stop: drain"); /* - * The only way to clear kernel buffers and to pause the device - * simultaneously is to set the mode again (to the same value). + * Sun API doesn't not allows us to drain and stop without + * loosing the sync between playback and record. So, for now we + * just pause the device until this problem is worked around. + * + * there are three possible workarounds: + * + * 1) stop depending on this, ie. make the rest of the code + * able to resynchronize playback to record. Then just + * close/reset the device to stop it. + * + * 2) send "hiwat" blocks of silence and schedule the + * very same amount of silence to drop. + * + * 3) modify the AUDIO_DRAIN ioctl(2) not to loose sync + * */ - if (ioctl(fd, AUDIO_GETINFO, &aui) < 0) - err(1, "dev_stop: getinfo"); - - mode = aui.mode; AUDIO_INITINFO(&aui); - aui.mode = mode; aui.play.pause = aui.record.pause = 1; if (ioctl(fd, AUDIO_SETINFO, &aui) < 0) - err(1, "dev_stop: setinfo"); + err(1, "sun_stop: setinfo"); - DPRINTF("dev_stop: play/rec stopped\n"); + DPRINTF("sun_stop: play/rec stopped\n"); } + +struct devops devops_sun = { + sun_open, + sun_close, + sun_start, + sun_stop +}; diff --git a/usr.bin/aucat/file.c b/usr.bin/aucat/file.c index 7aae942925d..70995e64fa2 100644 --- a/usr.bin/aucat/file.c +++ b/usr.bin/aucat/file.c @@ -1,4 +1,4 @@ -/* $OpenBSD: file.c,v 1.2 2008/08/14 09:48:50 ratchov Exp $ */ +/* $OpenBSD: file.c,v 1.3 2008/08/14 09:58:55 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -31,17 +31,36 @@ #include <signal.h> #include <stdio.h> #include <stdlib.h> +#include <string.h> #include <unistd.h> #include "conf.h" #include "file.h" #include "aproc.h" #include "abuf.h" +#include "dev.h" #define MAXFDS 100 struct filelist file_list; +void +file_dprint(int n, struct file *f) +{ + if (debug_level >= n) { + fprintf(stderr, "%s <", f->name); + if (f->state & FILE_ROK) + fprintf(stderr, "ROK"); + if (f->state & FILE_WOK) + fprintf(stderr, "WOK"); + if (f->state & FILE_EOF) + fprintf(stderr, "EOF"); + if (f->state & FILE_HUP) + fprintf(stderr, "HUP"); + fprintf(stderr, ">"); + } +} + struct file * file_new(int fd, char *name) { @@ -74,7 +93,14 @@ file_new(int fd, char *name) void file_del(struct file *f) { - DPRINTF("file_del: %s|%x\n", f->name, f->state); + DPRINTF("file_del: "); + file_dprint(1, f); + DPRINTF("\n"); + + if (f->hdr == HDR_WAV) + wav_writehdr(f->fd, &f->hpar); + close(f->fd); + free(f); } int @@ -176,8 +202,8 @@ file_poll(void) fnext = LIST_NEXT(f, entry); if (f->rproc == NULL && f->wproc == NULL) { LIST_REMOVE(f, entry); - DPRINTF("file_poll: %s: deleted\n", f->name); - free(f); + DPRINTF("file_poll: %s: removed\n", f->name); + file_del(f); } } if (LIST_EMPTY(&file_list)) { diff --git a/usr.bin/aucat/file.h b/usr.bin/aucat/file.h index 9e3adcfff25..338d7a3c426 100644 --- a/usr.bin/aucat/file.h +++ b/usr.bin/aucat/file.h @@ -1,4 +1,4 @@ -/* $OpenBSD: file.h,v 1.2 2008/06/02 17:05:12 ratchov Exp $ */ +/* $OpenBSD: file.h,v 1.3 2008/08/14 09:58:55 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -19,8 +19,8 @@ #include <sys/queue.h> #include <sys/types.h> - #include <poll.h> +#include "aparams.h" struct aparams; struct aproc; @@ -40,6 +40,15 @@ struct file { char *name; /* for debug purposes */ struct aproc *rproc, *wproc; /* reader and/or writer */ LIST_ENTRY(file) entry; + + /* + * disk-file specific stuff + */ +#define HDR_AUTO 0 /* guess by looking at the file name */ +#define HDR_RAW 1 /* no headers, ie openbsd native ;-) */ +#define HDR_WAV 2 /* microsoft riff wave */ + unsigned hdr; /* HDR_RAW or HDR_WAV */ + struct aparams hpar; /* parameters to write on the header */ }; LIST_HEAD(filelist,file); diff --git a/usr.bin/aucat/legacy.c b/usr.bin/aucat/legacy.c index 56f4be7e854..fcb705120db 100644 --- a/usr.bin/aucat/legacy.c +++ b/usr.bin/aucat/legacy.c @@ -1,4 +1,4 @@ -/* $OpenBSD: legacy.c,v 1.1 2008/05/23 07:15:46 ratchov Exp $ */ +/* $OpenBSD: legacy.c,v 1.2 2008/08/14 09:58:55 ratchov Exp $ */ /* * Copyright (c) 1997 Kenneth Stailey. All rights reserved. * @@ -28,7 +28,12 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/audioio.h> + #include <fcntl.h> +#include <string.h> #include <unistd.h> #include <err.h> |