summaryrefslogtreecommitdiff
path: root/usr.bin/aucat/aucat.c
diff options
context:
space:
mode:
authorAlexandre Ratchov <ratchov@cvs.openbsd.org>2008-10-26 08:49:45 +0000
committerAlexandre Ratchov <ratchov@cvs.openbsd.org>2008-10-26 08:49:45 +0000
commit13e276c156d9b9f3a5064700b447d8e90d89bebf (patch)
tree7c47aad8fcd6da2de5156ec12853b26ee468547e /usr.bin/aucat/aucat.c
parentc1f6af90f771854093903e82e7de930b96a15d25 (diff)
add minimal server capability to aucat(1). When started in server
mode, it listens on an unix socket and mixes/demultiplexes any number of full-duplex streams, doing necessary format conversions and resampling on the fly. programs can use the new libsa(3) library to play and record audio. The library provides a very simple API to connect to the audio server; if aucat(1) isn't running, it uses the audio(4) driver transparently instead.
Diffstat (limited to 'usr.bin/aucat/aucat.c')
-rw-r--r--usr.bin/aucat/aucat.c286
1 files changed, 144 insertions, 142 deletions
diff --git a/usr.bin/aucat/aucat.c b/usr.bin/aucat/aucat.c
index 9ad9eec6373..ae74bc87932 100644
--- a/usr.bin/aucat/aucat.c
+++ b/usr.bin/aucat/aucat.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: aucat.c,v 1.27 2008/08/25 11:56:12 sobrado Exp $ */
+/* $OpenBSD: aucat.c,v 1.28 2008/10/26 08:49:43 ratchov Exp $ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
*
@@ -48,6 +48,7 @@
#include <sys/types.h>
#include <sys/queue.h>
+#include <signal.h>
#include <err.h>
#include <fcntl.h>
#include <signal.h>
@@ -61,73 +62,44 @@
#include "aparams.h"
#include "aproc.h"
#include "abuf.h"
-#include "file.h"
+#include "wav.h"
+#include "listen.h"
#include "dev.h"
-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 *);
+/*
+ * 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;
+}
/*
- * List of allowed encodings and their names.
+ * increase debug level on SIGUSR1
*/
-struct enc {
- char *name;
- struct aparams par;
-} enc_list[] = {
- /* name bps, bits, le, sign, msb, unused */
- { "s8", { 1, 8, 1, 1, 1, 0, 0, 0 } },
- { "u8", { 1, 8, 1, 0, 1, 0, 0, 0 } },
- { "s16le", { 2, 16, 1, 1, 1, 0, 0, 0 } },
- { "u16le", { 2, 16, 1, 0, 1, 0, 0, 0 } },
- { "s16be", { 2, 16, 0, 1, 1, 0, 0, 0 } },
- { "u16be", { 2, 16, 0, 0, 1, 0, 0, 0 } },
- { "s24le", { 4, 24, 1, 1, 1, 0, 0, 0 } },
- { "u24le", { 4, 24, 1, 0, 1, 0, 0, 0 } },
- { "s24be", { 4, 24, 0, 1, 1, 0, 0, 0 } },
- { "u24be", { 4, 24, 0, 0, 1, 0, 0, 0 } },
- { "s32le", { 4, 32, 1, 1, 1, 0, 0, 0 } },
- { "u32le", { 4, 32, 1, 0, 1, 0, 0, 0 } },
- { "s32be", { 4, 32, 0, 1, 1, 0, 0, 0 } },
- { "u32be", { 4, 32, 0, 0, 1, 0, 0, 0 } },
- { "s24le3", { 3, 24, 1, 1, 1, 0, 0, 0 } },
- { "u24le3", { 3, 24, 1, 0, 1, 0, 0, 0 } },
- { "s24be3", { 3, 24, 0, 1, 1, 0, 0, 0 } },
- { "u24be3", { 3, 24, 0, 0, 1, 0, 0, 0 } },
- { "s20le3", { 3, 20, 1, 1, 1, 0, 0, 0 } },
- { "u20le3", { 3, 20, 1, 0, 1, 0, 0, 0 } },
- { "s20be3", { 3, 20, 0, 1, 1, 0, 0, 0 } },
- { "u20be3", { 3, 20, 0, 0, 1, 0, 0, 0 } },
- { "s18le3", { 3, 18, 1, 1, 1, 0, 0, 0 } },
- { "u18le3", { 3, 18, 1, 0, 1, 0, 0, 0 } },
- { "s18be3", { 3, 18, 0, 1, 1, 0, 0, 0 } },
- { "u18be3", { 3, 18, 0, 0, 1, 0, 0, 0 } },
- { NULL, { 0, 0, 0, 0, 0, 0, 0, 0 } }
-};
+void
+sigusr1(int s)
+{
+ if (debug_level < 4)
+ debug_level++;
+}
/*
- * Search an encoding in the above table. On success fill encoding
- * part of "par" and return 1, otherwise return 0.
+ * decrease debug level on SIGUSR2
*/
-unsigned
-enc_lookup(char *name, struct aparams *par)
+void
+sigusr2(int s)
{
- struct enc *e;
-
- for (e = enc_list; e->name != NULL; e++) {
- if (strcmp(e->name, name) == 0) {
- par->bps = e->par.bps;
- par->bits = e->par.bits;
- par->sig = e->par.sig;
- par->le = e->par.le;
- par->msb = e->par.msb;
- return 1;
- }
- }
- return 0;
+ if (debug_level > 0)
+ debug_level--;
}
void
@@ -136,10 +108,10 @@ usage(void)
extern char *__progname;
fprintf(stderr,
- "usage: %s [-qu] [-C min:max] [-c min:max] [-E enc] [-e enc] "
- "[-f device]\n"
- "\t[-H fmt] [-h fmt] [-i file] [-o file] [-R rate] [-r rate]\n"
- "\t[-X policy] [-x policy]\n",
+ "usage: %s [-lu] [-C min:max] [-c min:max] [-d level] "
+ "[-e enc]\n"
+ "\t[-f device] [-h fmt] [-i file] [-o file] "
+ "[-r rate] [-x policy]\n",
__progname);
}
@@ -147,8 +119,7 @@ void
opt_ch(struct aparams *par)
{
if (sscanf(optarg, "%u:%u", &par->cmin, &par->cmax) != 2 ||
- par->cmin > CHAN_MAX || par->cmax > CHAN_MAX ||
- par->cmin > par->cmax)
+ par->cmax < par->cmin || par->cmax > NCHAN_MAX - 1)
err(1, "%s: bad channel range", optarg);
}
@@ -163,8 +134,11 @@ opt_rate(struct aparams *par)
void
opt_enc(struct aparams *par)
{
- if (!enc_lookup(optarg, par))
- err(1, "%s: bad encoding", optarg);
+ int len;
+
+ len = aparams_strtoenc(par, optarg);
+ if (len == 0 || optarg[len] != '\0')
+ errx(1, "%s: bad encoding", optarg);
}
int
@@ -246,7 +220,7 @@ void
newinput(struct farg *fa)
{
int fd;
- struct file *f;
+ struct wav *f;
struct aproc *proc;
struct abuf *buf;
unsigned nfr;
@@ -261,18 +235,16 @@ newinput(struct farg *fa)
if (fd < 0)
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, &f->hpar, &f->rbytes))
- exit(1);
- }
- nfr = dev_onfr * f->hpar.rate / dev_opar.rate;
- buf = abuf_new(nfr, aparams_bpf(&f->hpar));
- proc = rpipe_new(f);
+ /*
+ * XXX : we should round rate, right ?
+ */
+ f = wav_new_in(&wav_ops, fd, fa->name, &fa->par, fa->hdr);
+ nfr = dev_bufsz * fa->par.rate / dev_rate;
+ buf = abuf_new(nfr, aparams_bpf(&fa->par));
+ proc = rpipe_new((struct file *)f);
aproc_setout(proc, buf);
- dev_attach(fa->name, buf, &f->hpar, fa->xrun, NULL, NULL, 0);
+ abuf_fill(buf); /* XXX: move this in dev_attach() ? */
+ dev_attach(fa->name, buf, &fa->par, fa->xrun, NULL, NULL, 0);
}
/*
@@ -282,7 +254,7 @@ void
newoutput(struct farg *fa)
{
int fd;
- struct file *f;
+ struct wav *f;
struct aproc *proc;
struct abuf *buf;
unsigned nfr;
@@ -298,31 +270,31 @@ newoutput(struct farg *fa)
if (fd < 0)
err(1, "%s", fa->name);
}
- f = file_new(fd, fa->name);
- f->hdr = fa->hdr;
- f->hpar = fa->par;
- if (f->hdr == HDR_WAV) {
- f->wbytes = WAV_DATAMAX;
- if (!wav_writehdr(fd, &f->hpar))
- exit(1);
- }
- nfr = dev_infr * f->hpar.rate / dev_ipar.rate;
- proc = wpipe_new(f);
- buf = abuf_new(nfr, aparams_bpf(&f->hpar));
+ /*
+ * XXX : we should round rate, right ?
+ */
+ f = wav_new_out(&wav_ops, fd, fa->name, &fa->par, fa->hdr);
+ nfr = dev_bufsz * fa->par.rate / dev_rate;
+ proc = wpipe_new((struct file *)f);
+ buf = abuf_new(nfr, aparams_bpf(&fa->par));
aproc_setin(proc, buf);
- dev_attach(fa->name, NULL, NULL, 0, buf, &f->hpar, fa->xrun);
+ dev_attach(fa->name, NULL, NULL, 0, buf, &fa->par, fa->xrun);
}
int
main(int argc, char **argv)
{
- int c, u_flag, ohdr, ihdr, ixrun, oxrun;
+ int c, u_flag, l_flag, hdr, xrun;
struct farg *fa;
struct farglist ifiles, ofiles;
struct aparams ipar, opar, dipar, dopar;
- unsigned ivol, ovol;
- char *devpath, *dbgenv;
+ struct sigaction sa;
+ unsigned ivol, ovol, bufsz = 0;
+ char *devpath, *dbgenv, *listenpath;
const char *errstr;
+ extern char *malloc_options;
+
+ malloc_options = "FGJ";
dbgenv = getenv("AUCAT_DEBUG");
if (dbgenv) {
@@ -335,27 +307,22 @@ main(int argc, char **argv)
aparams_init(&opar, 0, 1, 44100);
u_flag = 0;
+ l_flag = 0;
devpath = NULL;
SLIST_INIT(&ifiles);
SLIST_INIT(&ofiles);
- ihdr = ohdr = HDR_AUTO;
- ixrun = oxrun = XRUN_IGNORE;
+ hdr = HDR_AUTO;
+ xrun = XRUN_IGNORE;
ivol = ovol = MIDI_TO_ADATA(127);
- while ((c = getopt(argc, argv, "c:C:e:E:r:R:h:H:x:X:i:o:f:qu"))
+ while ((c = getopt(argc, argv, "b:c:C:e:r:h:x:i:o:f:lqu"))
!= -1) {
switch (c) {
case 'h':
- ihdr = opt_hdr();
- break;
- case 'H':
- ohdr = opt_hdr();
+ hdr = opt_hdr();
break;
case 'x':
- ixrun = opt_xrun();
- break;
- case 'X':
- oxrun = opt_xrun();
+ xrun = opt_xrun();
break;
case 'c':
opt_ch(&ipar);
@@ -365,35 +332,37 @@ main(int argc, char **argv)
break;
case 'e':
opt_enc(&ipar);
- break;
- case 'E':
- opt_enc(&opar);
+ aparams_copyenc(&opar, &ipar);
break;
case 'r':
opt_rate(&ipar);
- break;
- case 'R':
- opt_rate(&opar);
+ opar.rate = ipar.rate;
break;
case 'i':
- opt_file(&ifiles, &ipar, 127, ihdr, ixrun, optarg);
+ opt_file(&ifiles, &ipar, 127, hdr, xrun, optarg);
break;
case 'o':
- opt_file(&ofiles, &opar, 127, ohdr, oxrun, optarg);
+ opt_file(&ofiles, &opar, 127, hdr, xrun, optarg);
break;
case 'f':
if (devpath)
err(1, "only one -f allowed");
devpath = optarg;
- dipar = ipar;
- dopar = opar;
+ dipar = opar;
+ dopar = ipar;
break;
- case 'q':
- quiet_flag = 1;
+ case 'l':
+ l_flag = 1;
break;
case 'u':
u_flag = 1;
break;
+ case 'b':
+ if (sscanf(optarg, "%u", &bufsz) != 1) {
+ fprintf(stderr, "%s: bad buf size\n", optarg);
+ exit(1);
+ }
+ break;
default:
usage();
exit(1);
@@ -403,14 +372,12 @@ main(int argc, char **argv)
argv += optind;
if (!devpath) {
- devpath = getenv("AUDIODEVICE");
- if (devpath == NULL)
- devpath = DEFAULT_DEVICE;
dipar = ipar;
dopar = opar;
}
- if (SLIST_EMPTY(&ifiles) && SLIST_EMPTY(&ofiles) && argc > 0) {
+ if (!l_flag && SLIST_EMPTY(&ifiles) &&
+ SLIST_EMPTY(&ofiles) && argc > 0) {
/*
* Legacy mode: if no -i or -o options are provided, and
* there are arguments then assume the arguments are files
@@ -426,15 +393,17 @@ main(int argc, char **argv)
exit(1);
}
+ if (l_flag && (!SLIST_EMPTY(&ofiles) || !SLIST_EMPTY(&ifiles)))
+ errx(1, "can't use -l with -o or -i");
- if (!u_flag) {
+ if (!u_flag && !l_flag) {
/*
* 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);
+ aparams_init(&dipar, NCHAN_MAX - 1, 0, RATE_MAX);
+ aparams_init(&dopar, NCHAN_MAX - 1, 0, RATE_MIN);
SLIST_FOREACH(fa, &ifiles, entry) {
if (dopar.cmin > fa->par.cmin)
dopar.cmin = fa->par.cmin;
@@ -452,15 +421,37 @@ main(int argc, char **argv)
dipar.rate = fa->par.rate;
}
}
- file_start();
+
+ quit_flag = 0;
+ sigfillset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+ sa.sa_handler = sigint;
+ if (sigaction(SIGINT, &sa, NULL) < 0)
+ DPRINTF("sigaction(int) failed\n");
+#ifdef DEBUG
+ sa.sa_handler = sigusr1;
+ if (sigaction(SIGUSR1, &sa, NULL) < 0)
+ DPRINTF("sigaction(usr1) failed\n");
+ sa.sa_handler = sigusr2;
+ if (sigaction(SIGUSR2, &sa, NULL) < 0)
+ DPRINTF("sigaction(usr2) failed1n");
+#endif
+ filelist_init();
/*
- * Open the device, dev_init() will return new parameters
- * that must be used by all inputs and outputs.
+ * Open the device.
*/
dev_init(devpath,
- (!SLIST_EMPTY(&ofiles)) ? &dipar : NULL,
- (!SLIST_EMPTY(&ifiles)) ? &dopar : NULL);
+ (l_flag || !SLIST_EMPTY(&ofiles)) ? &dipar : NULL,
+ (l_flag || !SLIST_EMPTY(&ifiles)) ? &dopar : NULL,
+ bufsz);
+
+ if (l_flag) {
+ listenpath = getenv("AUCAT_SOCKET");
+ if (!listenpath)
+ listenpath = DEFAULT_SOCKET;
+ (void)listen_new(&listen_ops, listenpath);
+ }
/*
* Create buffers for all input and output pipes.
@@ -479,24 +470,35 @@ main(int argc, char **argv)
}
/*
- * Normalize input levels
+ * automatically terminate when there no are streams
*/
- if (dev_mix)
- mix_setmaster(dev_mix);
+ if (!l_flag) {
+ if (dev_mix)
+ dev_mix->u.mix.flags |= MIX_AUTOQUIT;
+ if (dev_sub)
+ dev_sub->u.sub.flags |= SUB_AUTOQUIT;
+ }
/*
- * start audio
+ * loop, start audio
*/
- if (!quiet_flag)
- fprintf(stderr, "starting device...\n");
- dev_start();
- if (!quiet_flag)
- fprintf(stderr, "process started...\n");
- dev_run(1);
- if (!quiet_flag)
- fprintf(stderr, "stopping device...\n");
+ for (;;) {
+ if (quit_flag) {
+ if (l_flag)
+ filelist_unlisten();
+ break;
+ }
+ if (!file_poll())
+ break;
+ }
+
dev_done();
+ filelist_done();
- file_stop();
+ sigfillset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+ sa.sa_handler = SIG_DFL;
+ if (sigaction(SIGINT, &sa, NULL) < 0)
+ DPRINTF("dev_done: sigaction failed\n");
return 0;
}