summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexandre Ratchov <ratchov@cvs.openbsd.org>2010-05-02 11:54:28 +0000
committerAlexandre Ratchov <ratchov@cvs.openbsd.org>2010-05-02 11:54:28 +0000
commit4b106b8af0d7d4217dd1937dd7396d46b5d73a08 (patch)
tree2d2c8d1faabfdb0c3e5b09c6572ea7165aa34e04
parent2cbb14893098445118dbd76eb3a6da56a4848f23 (diff)
Clean up device handling code to clarify different initialization phases
and different device states. Split initialization in two phases: first global variables are initialized then the audio hardware is opened. Allow devices that don't support full-duplex to work in play-only or record-only mode, even if ``-m play'' or ``-m rec'' are not specified.
-rw-r--r--usr.bin/aucat/aucat.c80
-rw-r--r--usr.bin/aucat/dev.c631
-rw-r--r--usr.bin/aucat/dev.h32
-rw-r--r--usr.bin/aucat/headers.c4
-rw-r--r--usr.bin/aucat/midi.c5
-rw-r--r--usr.bin/aucat/opt.h3
-rw-r--r--usr.bin/aucat/siofile.c30
-rw-r--r--usr.bin/aucat/siofile.h4
-rw-r--r--usr.bin/aucat/sock.c23
-rw-r--r--usr.bin/aucat/wav.c24
10 files changed, 556 insertions, 280 deletions
diff --git a/usr.bin/aucat/aucat.c b/usr.bin/aucat/aucat.c
index e7767c597cb..79273f9607f 100644
--- a/usr.bin/aucat/aucat.c
+++ b/usr.bin/aucat/aucat.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: aucat.c,v 1.89 2010/05/02 11:12:31 ratchov Exp $ */
+/* $OpenBSD: aucat.c,v 1.90 2010/05/02 11:54:26 ratchov Exp $ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
*
@@ -157,7 +157,7 @@ opt_mmc(void)
}
int
-opt_join(void)
+opt_onoff(void)
{
if (strcmp("off", optarg) == 0)
return 0;
@@ -468,7 +468,7 @@ aucat_main(int argc, char **argv)
xrun = opt_xrun();
break;
case 'j':
- join = opt_join();
+ join = opt_onoff();
break;
case 't':
mmc = opt_mmc();
@@ -645,20 +645,20 @@ aucat_main(int argc, char **argv)
filelist_init();
/*
- * Open the device. Give half of the buffer to the device,
- * the other half is for the socket/files.
+ * Open the device
*/
if (n_flag) {
if (mode & MODE_MON)
errx(1, "monitoring not allowed in loopback mode");
- dev_loopinit(&dipar, &dopar, bufsz);
+ dev_init_loop(&dipar, &dopar, bufsz);
} else {
if ((mode & MODE_MON) && !(mode & MODE_PLAY))
errx(1, "no playback stream to monitor");
- if (!dev_init(devpath, mode, &dipar, &dopar, bufsz, round)) {
- errx(1, "%s: can't open device",
- devpath ? devpath : "<default>");
- }
+ dev_init_sio(devpath, mode, &dipar, &dopar, bufsz, round);
+ }
+ if (!dev_ref()) {
+ errx(1, "%s: can't open device",
+ devpath ? devpath : "<default>");
}
/*
@@ -710,53 +710,28 @@ aucat_main(int argc, char **argv)
*/
ctl_start(dev_midi);
}
- if (l_flag)
- dev_prime();
/*
* Loop, start audio.
*/
for (;;) {
- if (quit_flag) {
+ if (quit_flag)
break;
- }
- if ((APROC_OK(dev_mix) && LIST_EMPTY(&dev_mix->outs)) ||
- (APROC_OK(dev_sub) && LIST_EMPTY(&dev_sub->ins))) {
- fprintf(stderr, "device disappeared, terminating\n");
+ if (!dev_run())
break;
- }
if (!l_flag && ctl_idle(dev_midi))
break;
if (!file_poll())
break;
- if ((!APROC_OK(dev_mix) || dev_mix->u.mix.idle > 2 * dev_bufsz) &&
- (!APROC_OK(dev_sub) || dev_sub->u.sub.idle > 2 * dev_bufsz) &&
- (!APROC_OK(dev_submon) || dev_submon->u.sub.idle > 2 * dev_bufsz) &&
- (!APROC_OK(dev_midi) || dev_midi->u.ctl.tstate != CTL_RUN)) {
- if (dev_pstate == DEV_RUN) {
- dev_pstate = DEV_INIT;
- dev_stop();
- dev_clear();
- /*
- * priming buffer in non-server mode is not
- * ok, because it will insert silence and
- * break synchronization
- */
- if (l_flag)
- dev_prime();
- }
- }
- /*
- * move device state machine
- * XXX: move this to dev.c
- */
- if (dev_pstate == DEV_START) {
- dev_pstate = DEV_RUN;
- dev_start();
- }
}
+ dev_unref();
stopall();
dev_done();
+ /*
+ * give a chance to drain
+ */
+ while (file_poll())
+ ; /* nothing */
filelist_done();
if (l_flag) {
if (rmdir(base) < 0 && errno != ENOTEMPTY && errno != EPERM)
@@ -848,7 +823,9 @@ midicat_main(int argc, char **argv)
setsig();
filelist_init();
- dev_thruinit();
+ dev_init_thru();
+ if (0 && !dev_ref())
+ errx(1, "couldn't opem midi thru box");
if (!l_flag && APROC_OK(dev_midi))
dev_midi->flags |= APROC_QUIT;
if ((!SLIST_EMPTY(&ifiles) || !SLIST_EMPTY(&ofiles)) &&
@@ -919,14 +896,22 @@ midicat_main(int argc, char **argv)
* loop, start processing
*/
for (;;) {
- if (quit_flag) {
+ if (quit_flag)
+ break;
+ if (!dev_run())
break;
- }
if (!file_poll())
break;
}
stopall();
+ if (0)
+ dev_unref();
dev_done();
+ /*
+ * drain
+ */
+ while (file_poll())
+ ; /* nothing */
filelist_done();
if (l_flag) {
if (rmdir(base) < 0 && errno != ENOTEMPTY && errno != EPERM)
@@ -942,6 +927,9 @@ main(int argc, char **argv)
{
char *prog;
+#ifdef DEBUG
+ atexit(dbg_flush);
+#endif
prog = strrchr(argv[0], '/');
if (prog == NULL)
prog = argv[0];
diff --git a/usr.bin/aucat/dev.c b/usr.bin/aucat/dev.c
index e963cef8d5b..fbcb42cb637 100644
--- a/usr.bin/aucat/dev.c
+++ b/usr.bin/aucat/dev.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: dev.c,v 1.50 2010/05/02 11:12:31 ratchov Exp $ */
+/* $OpenBSD: dev.c,v 1.51 2010/05/02 11:54:26 ratchov Exp $ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
*
@@ -14,7 +14,75 @@
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-
+/*
+ * Device abstraction module
+ *
+ * This module exposes a ``enhanced device'' that uses aproc
+ * structures framework; it does conversions on the fly and can
+ * handle multiple streams. The enhanced device starts and stops
+ * automatically, when streams are attached, and provides
+ * primitives for MIDI control
+ *
+ * From the main loop, the device is used as follows:
+ *
+ * 1. create the device using dev_init_xxx()
+ * 2. call dev_run() in the event loop
+ * 3. destroy the device using dev_done()
+ * 4. continue running the event loop to drain
+ *
+ * The device is used as follows from aproc context:
+ *
+ * 1. open the device with dev_ref()
+ * 2. negociate parameters (mode, rate, ...)
+ * 3. create your stream (ie allocate and fill abufs)
+ * 4. attach your stream atomically:
+ * - first call dev_wakeup() to ensure device is not suspended
+ * - possibly fetch dynamic parameters (eg. dev_getpos())
+ * - attach your buffers with dev_attach()
+ * 5. close your stream, ie abuf_eof() or abuf_hup()
+ * 6. close the device with dev_unref()
+ *
+ * The device has the following states:
+ *
+ * CLOSED sio_open() is not called, it's not ready and
+ * no streams can be attached; dev_ref() must
+ * be called to open the device
+ *
+ * INIT device is opened, processing chain is ready, but
+ * DMA is not started yet. Streams can attach,
+ * in which case device will automatically switch
+ * to the START state
+ *
+ * START at least one stream is attached, play buffers
+ * are primed (if necessary) DMA is ready and
+ * will start immeadiately (next cycle)
+ *
+ * RUN DMA is started. New streams can attach. If the
+ * device is idle (all streams are closed and
+ * finished draining), then the device
+ * automatically switches to INIT or CLOSED
+ */
+/*
+ * TODO:
+ *
+ * priming buffer is not ok, because it will insert silence and
+ * break synchronization to other programs.
+ *
+ * priming buffer in server mode is required, because f->bufsz may
+ * be smaller than the server buffer and may cause underrun in the
+ * dev_bufsz part of the buffer, in turn causing apps to break. It
+ * doesn't hurt because we care only in synchronization between
+ * clients.
+ *
+ * Priming is not required in non-server mode, because streams
+ * actually start when they are in the READY state, and their
+ * buffer is large enough to never cause underruns of dev_bufsz.
+ *
+ * Fix sock.c to allocate dev_bufsz, but to use only appbufsz --
+ * or whatever -- but to avoid underruns in dev_bufsz. Then remove
+ * this ugly hack.
+ *
+ */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
@@ -32,248 +100,252 @@
#include "dbg.h"
#endif
-unsigned dev_pstate;
-unsigned dev_bufsz, dev_round, dev_rate;
-struct aparams dev_ipar, dev_opar;
-struct aproc *dev_mix, *dev_sub, *dev_rec, *dev_play, *dev_submon, *dev_mon;
-struct aproc *dev_midi;
+/*
+ * state of the device
+ */
+#define DEV_CLOSED 0 /* closed */
+#define DEV_INIT 1 /* stopped */
+#define DEV_START 2 /* ready to start */
+#define DEV_RUN 3 /* started */
/*
- * Create a MIDI thru box as the MIDI end of the device
+ * desired parameters
*/
-void
-dev_thruinit(void)
-{
- dev_midi = thru_new("thru");
- dev_midi->refs++;
-}
+unsigned dev_reqmode; /* mode */
+struct aparams dev_reqipar, dev_reqopar; /* parameters */
+unsigned dev_reqbufsz; /* buffer size */
+unsigned dev_reqround; /* block size */
/*
- * Open a MIDI device and connect it to the thru box
+ * actual parameters and runtime state
*/
-int
-dev_thruadd(char *name, int in, int out)
-{
- struct file *f;
- struct abuf *rbuf = NULL, *wbuf = NULL;
- struct aproc *rproc, *wproc;
+char *dev_path; /* sio path */
+unsigned dev_refcnt = 0; /* number of openers */
+unsigned dev_pstate; /* on of DEV_xxx */
+unsigned dev_mode; /* bitmap of MODE_xxx */
+unsigned dev_bufsz, dev_round, dev_rate;
+struct aparams dev_ipar, dev_opar;
+struct aproc *dev_mix, *dev_sub, *dev_rec, *dev_play, *dev_submon, *dev_mon;
+struct aproc *dev_midi;
- f = (struct file *)miofile_new(&miofile_ops, name, in, out);
- if (f == NULL)
- return 0;
- if (in) {
- rproc = rfile_new(f);
- rbuf = abuf_new(MIDI_BUFSZ, &aparams_none);
- aproc_setout(rproc, rbuf);
- }
- if (out) {
- wproc = wfile_new(f);
- wbuf = abuf_new(MIDI_BUFSZ, &aparams_none);
- aproc_setin(wproc, wbuf);
- }
- dev_midiattach(rbuf, wbuf);
- return 1;
-}
+void dev_start(void);
+void dev_stop(void);
+void dev_clear(void);
+void dev_prime(void);
/*
- * Attach a bi-directional MIDI stream to the MIDI device
+ * Create a sndio device
*/
void
-dev_midiattach(struct abuf *ibuf, struct abuf *obuf)
+dev_init_sio(char *path, unsigned mode,
+ struct aparams *dipar, struct aparams *dopar,
+ unsigned bufsz, unsigned round)
{
- if (ibuf)
- aproc_setin(dev_midi, ibuf);
- if (obuf) {
- aproc_setout(dev_midi, obuf);
- if (ibuf) {
- ibuf->duplex = obuf;
- obuf->duplex = ibuf;
- }
- }
+ dev_path = path;
+ dev_reqmode = mode;
+ if (mode & MODE_PLAY)
+ dev_reqopar = *dopar;
+ if (mode & MODE_RECMASK)
+ dev_reqipar = *dipar;
+ dev_reqbufsz = bufsz;
+ dev_reqround = round;
+ dev_pstate = DEV_CLOSED;
}
/*
- * Same as dev_init(), but create a fake device that records what is
- * played.
+ * Create a loopback synchronous device
*/
void
-dev_loopinit(struct aparams *dipar, struct aparams *dopar, unsigned bufsz)
+dev_init_loop(struct aparams *dipar, struct aparams *dopar, unsigned bufsz)
{
- struct abuf *buf;
struct aparams par;
unsigned cmin, cmax, rate;
- /*
- * in principle we don't need control, but the start-stop mechanism
- * depend on it and it's simpler to reuse this mechanism rather than
- * dealing with lots of special cases
- */
- dev_midi = ctl_new("ctl");
- dev_midi->refs++;
-
cmin = (dipar->cmin < dopar->cmin) ? dipar->cmin : dopar->cmin;
cmax = (dipar->cmax > dopar->cmax) ? dipar->cmax : dopar->cmax;
rate = (dipar->rate > dopar->rate) ? dipar->rate : dopar->rate;
aparams_init(&par, cmin, cmax, rate);
- dev_ipar = par;
- dev_opar = par;
- dev_round = (bufsz + 1) / 2;
- dev_bufsz = dev_round * 2;
- dev_rate = rate;
- dev_rec = NULL;
- dev_play = NULL;
- dev_mon = NULL;
- dev_submon = NULL;
- dev_pstate = DEV_INIT;
-
- buf = abuf_new(dev_bufsz, &par);
- dev_mix = mix_new("mix", dev_bufsz, 1);
- dev_mix->refs++;
- dev_sub = sub_new("sub", dev_bufsz, 1);
- dev_sub->refs++;
- aproc_setout(dev_mix, buf);
- aproc_setin(dev_sub, buf);
-
- dev_mix->flags |= APROC_QUIT;
- dev_sub->flags |= APROC_QUIT;
-
- *dipar = dev_ipar;
- *dopar = dev_opar;
+ dev_reqipar = par;
+ dev_reqopar = par;
+ dev_rate = rate;
+ dev_reqround = (bufsz + 1) / 2;
+ dev_reqbufsz = dev_reqround * 2;
+ dev_reqmode = MODE_PLAY | MODE_REC | MODE_LOOP;
+ dev_pstate = DEV_CLOSED;
}
-unsigned
-dev_roundof(unsigned newrate)
+/*
+ * Create a MIDI thru box device
+ */
+void
+dev_init_thru(void)
{
- return (dev_round * newrate + dev_rate / 2) / dev_rate;
+ dev_reqmode = 0;
+ dev_pstate = DEV_CLOSED;
}
/*
- * Open the device with the given hardware parameters and create a mixer
- * and a multiplexer connected to it with all necessary conversions
- * setup.
+ * Open the device with the dev_reqxxx capabilities. Setup a mixer, demuxer,
+ * monitor, midi control, and any necessary conversions.
*/
int
-dev_init(char *devpath, unsigned mode,
- struct aparams *dipar, struct aparams *dopar, unsigned bufsz, unsigned round)
+dev_open(void)
{
struct file *f;
- struct aparams ipar, opar;
+ struct aparams par;
struct aproc *conv;
struct abuf *buf;
-
- dev_midi = ctl_new("ctl");
- dev_midi->refs++;
+ unsigned siomode;
+
+ dev_mode = dev_reqmode;
+ dev_round = dev_reqround;
+ dev_bufsz = dev_reqbufsz;
+ dev_ipar = dev_reqipar;
+ dev_opar = dev_reqopar;
+ dev_rec = NULL;
+ dev_play = NULL;
+ dev_mon = NULL;
+ dev_submon = NULL;
+ dev_rate = 0;
/*
- * Ask for 1/4 of the buffer for the kernel ring and
- * limit the block size to 1/4 of the requested buffer.
+ * If needed, open the device (ie create dev_rec and dev_play)
*/
- dev_round = round;
- dev_bufsz = bufsz;
- f = (struct file *)siofile_new(&siofile_ops, devpath,
- mode & (MODE_PLAY | MODE_REC), dipar, dopar,
- &dev_bufsz, &dev_round);
- if (f == NULL)
- return 0;
- if (mode & MODE_REC) {
+ if ((dev_mode & (MODE_PLAY | MODE_REC)) && !(dev_mode & MODE_LOOP)) {
+ siomode = dev_mode & (MODE_PLAY | MODE_REC);
+ f = (struct file *)siofile_new(&siofile_ops,
+ dev_path,
+ &siomode,
+ &dev_ipar,
+ &dev_opar,
+ &dev_bufsz,
+ &dev_round);
+ if (f == NULL) {
#ifdef DEBUG
- if (debug_level >= 2) {
- dbg_puts("hw recording ");
- aparams_dbg(dipar);
- dbg_puts("\n");
+ if (debug_level >= 1) {
+ dbg_puts(dev_path ? dev_path : "default");
+ dbg_puts(": failed to open audio device\n");
+ }
+#endif
+ return 0;
}
+ if (!(siomode & MODE_PLAY))
+ dev_mode &= ~(MODE_PLAY | MODE_MON);
+ if (!(siomode & MODE_REC))
+ dev_mode &= ~MODE_REC;
+ if ((dev_mode & (MODE_PLAY | MODE_REC)) == 0) {
+#ifdef DEBUG
+ if (debug_level >= 1) {
+ dbg_puts(dev_path ? dev_path : "default");
+ dbg_puts(": mode not supported by device\n");
+ }
#endif
- dev_rate = dipar->rate;
- }
- if (mode & MODE_PLAY) {
+ return 0;
+ }
+ dev_rate = dev_mode & MODE_REC ? dev_ipar.rate : dev_opar.rate;
#ifdef DEBUG
if (debug_level >= 2) {
- dbg_puts("hw playing ");
- aparams_dbg(dopar);
- dbg_puts("\n");
+ if (dev_mode & MODE_REC) {
+ dbg_puts("hw recording ");
+ aparams_dbg(&dev_ipar);
+ dbg_puts("\n");
+ }
+ if (dev_mode & MODE_PLAY) {
+ dbg_puts("hw playing ");
+ aparams_dbg(&dev_opar);
+ dbg_puts("\n");
+ }
}
#endif
- dev_rate = dopar->rate;
+ if (dev_mode & MODE_REC) {
+ dev_rec = rsio_new(f);
+ dev_rec->refs++;
+ }
+ if (dev_mode & MODE_PLAY) {
+ dev_play = wsio_new(f);
+ dev_play->refs++;
+ }
}
/*
- * Create record chain.
+ * Create the midi control end, or a simple thru box
+ * if there's no device
*/
- if (mode & MODE_REC) {
- aparams_init(&ipar, dipar->cmin, dipar->cmax, dipar->rate);
+ dev_midi = (dev_mode == 0) ? thru_new("thru") : ctl_new("ctl");
+ dev_midi->refs++;
+
+ /*
+ * Create mixer, demuxer and monitor
+ */
+ if (dev_mode & MODE_PLAY) {
+ dev_mix = mix_new("play", dev_bufsz, dev_round);
+ dev_mix->refs++;
+ dev_mix->u.mix.ctl = dev_midi;
+ }
+ if (dev_mode & MODE_REC) {
+ dev_sub = sub_new("rec", dev_bufsz, dev_round);
+ dev_sub->refs++;
+ /*
+ * If not playing, use the record end as clock source
+ */
+ if (!(dev_mode & MODE_PLAY))
+ dev_sub->u.sub.ctl = dev_midi;
+ }
+ if (dev_mode & MODE_LOOP) {
+ /*
+ * connect mixer out to demuxer in
+ */
+ buf = abuf_new(dev_bufsz, &dev_opar);
+ aproc_setout(dev_mix, buf);
+ aproc_setin(dev_sub, buf);
+
+ dev_mix->flags |= APROC_QUIT;
+ dev_sub->flags |= APROC_QUIT;
+ dev_rate = dev_opar.rate;
+ }
+ if (dev_rec) {
+ aparams_init(&par, dev_ipar.cmin, dev_ipar.cmax, dev_rate);
+
/*
- * Create the read end.
+ * Create device <-> demuxer buffer
*/
- dev_rec = rsio_new(f);
- dev_rec->refs++;
- buf = abuf_new(dev_bufsz, dipar);
+ buf = abuf_new(dev_bufsz, &dev_ipar);
aproc_setout(dev_rec, buf);
/*
- * Append a converter, if needed.
+ * Insert a converter, if needed.
*/
- if (!aparams_eqenc(dipar, &ipar)) {
- conv = dec_new("rec", dipar);
+ if (!aparams_eqenc(&dev_ipar, &par)) {
+ conv = dec_new("rec", &dev_ipar);
aproc_setin(conv, buf);
- buf = abuf_new(dev_round, &ipar);
+ buf = abuf_new(dev_round, &par);
aproc_setout(conv, buf);
}
- dev_ipar = ipar;
-
- /*
- * Append a "sub" to which clients will connect.
- * Link it to the controller only in record-only mode
- */
- dev_sub = sub_new("rec", dev_bufsz, dev_round);
- dev_sub->refs++;
- if (!(mode & MODE_PLAY))
- dev_sub->u.sub.ctl = dev_midi;
+ dev_ipar = par;
aproc_setin(dev_sub, buf);
- } else {
- dev_rec = NULL;
- dev_sub = NULL;
}
+ if (dev_play) {
+ aparams_init(&par, dev_opar.cmin, dev_opar.cmax, dev_rate);
- /*
- * Create play chain.
- */
- if (mode & MODE_PLAY) {
- aparams_init(&opar, dopar->cmin, dopar->cmax, dopar->rate);
/*
- * Create the write end.
+ * Create device <-> mixer buffer
*/
- dev_play = wsio_new(f);
- dev_play->refs++;
- buf = abuf_new(dev_bufsz, dopar);
+ buf = abuf_new(dev_bufsz, &dev_opar);
aproc_setin(dev_play, buf);
/*
* Append a converter, if needed.
*/
- if (!aparams_eqenc(&opar, dopar)) {
- conv = enc_new("play", dopar);
+ if (!aparams_eqenc(&par, &dev_opar)) {
+ conv = enc_new("play", &dev_opar);
aproc_setout(conv, buf);
- buf = abuf_new(dev_round, &opar);
+ buf = abuf_new(dev_round, &par);
aproc_setin(conv, buf);
}
- dev_opar = opar;
-
- /*
- * Append a "mix" to which clients will connect.
- */
- dev_mix = mix_new("play", dev_bufsz, dev_round);
- dev_mix->refs++;
- dev_mix->u.mix.ctl = dev_midi;
+ dev_opar = par;
aproc_setout(dev_mix, buf);
- } else {
- dev_play = NULL;
- dev_mix = NULL;
}
-
- /*
- * Create monitoring chain
- */
- if (mode & MODE_MON) {
+ if (dev_mode & MODE_MON) {
dev_mon = mon_new("mon", dev_bufsz);
dev_mon->refs++;
buf = abuf_new(dev_bufsz, &dev_opar);
@@ -281,30 +353,26 @@ dev_init(char *devpath, unsigned mode,
/*
* Append a "sub" to which clients will connect.
- * Link it to the controller only in record-only mode
*/
dev_submon = sub_new("mon", dev_bufsz, dev_round);
dev_submon->refs++;
aproc_setin(dev_submon, buf);
/*
- * Attack to the mixer
+ * Attach to the mixer
*/
dev_mix->u.mix.mon = dev_mon;
dev_mon->refs++;
- } else {
- dev_submon = NULL;
- if (APROC_OK(dev_mix))
- dev_mix->u.mix.mon = NULL;
}
-
#ifdef DEBUG
- if (debug_level >= 2) {
- dbg_puts("device block size is ");
- dbg_putu(dev_round);
- dbg_puts(" frames, using ");
- dbg_putu(dev_bufsz / dev_round);
- dbg_puts(" blocks\n");
+ if (debug_level >= 2) {
+ if (dev_mode & (MODE_PLAY | MODE_RECMASK)) {
+ dbg_puts("device block size is ");
+ dbg_putu(dev_round);
+ dbg_puts(" frames, using ");
+ dbg_putu(dev_bufsz / dev_round);
+ dbg_puts(" blocks\n");
+ }
}
#endif
dev_pstate = DEV_INIT;
@@ -316,7 +384,7 @@ dev_init(char *devpath, unsigned mode,
* once both play chain and record chain are gone.
*/
void
-dev_done(void)
+dev_close(void)
{
struct file *f;
@@ -342,8 +410,9 @@ dev_done(void)
}
#ifdef DEBUG
if (debug_level >= 2)
- dbg_puts("closing audio device\n");
+ dbg_puts("closing device\n");
#endif
+
if (dev_mix) {
/*
* Put the mixer in ``autoquit'' state and generate
@@ -357,6 +426,10 @@ dev_done(void)
*/
if (APROC_OK(dev_mix))
mix_quit(dev_mix);
+
+ /*
+ * XXX: handle this in mix_done()
+ */
if (APROC_OK(dev_mix->u.mix.mon)) {
dev_mix->u.mix.mon->refs--;
aproc_del(dev_mix->u.mix.mon);
@@ -434,12 +507,71 @@ dev_done(void)
aproc_del(dev_midi);
dev_midi = NULL;
}
- for (;;) {
- if (!file_poll())
- break;
+ dev_pstate = DEV_CLOSED;
+}
+
+/*
+ * Free the device
+ */
+void
+dev_done(void)
+{
+ if (dev_pstate != DEV_CLOSED)
+ dev_close();
+}
+
+/*
+ * Open a MIDI device and connect it to the thru box
+ */
+int
+dev_thruadd(char *name, int in, int out)
+{
+ struct file *f;
+ struct abuf *rbuf = NULL, *wbuf = NULL;
+ struct aproc *rproc, *wproc;
+
+ if (!dev_ref())
+ return 0;
+ f = (struct file *)miofile_new(&miofile_ops, name, in, out);
+ if (f == NULL)
+ return 0;
+ if (in) {
+ rproc = rfile_new(f);
+ rbuf = abuf_new(MIDI_BUFSZ, &aparams_none);
+ aproc_setout(rproc, rbuf);
+ }
+ if (out) {
+ wproc = wfile_new(f);
+ wbuf = abuf_new(MIDI_BUFSZ, &aparams_none);
+ aproc_setin(wproc, wbuf);
+ }
+ dev_midiattach(rbuf, wbuf);
+ return 1;
+}
+
+/*
+ * Attach a bi-directional MIDI stream to the MIDI device
+ */
+void
+dev_midiattach(struct abuf *ibuf, struct abuf *obuf)
+{
+ if (ibuf)
+ aproc_setin(dev_midi, ibuf);
+ if (obuf) {
+ aproc_setout(dev_midi, obuf);
+ if (ibuf) {
+ ibuf->duplex = obuf;
+ obuf->duplex = ibuf;
+ }
}
}
+unsigned
+dev_roundof(unsigned newrate)
+{
+ return (dev_round * newrate + dev_rate / 2) / dev_rate;
+}
+
/*
* Start the (paused) device. By default it's paused.
*/
@@ -450,8 +582,9 @@ dev_start(void)
#ifdef DEBUG
if (debug_level >= 2)
- dbg_puts("starting audio device\n");
+ dbg_puts("starting device\n");
#endif
+ dev_pstate = DEV_RUN;
if (APROC_OK(dev_mix))
dev_mix->flags |= APROC_DROP;
if (APROC_OK(dev_sub))
@@ -476,6 +609,7 @@ dev_stop(void)
{
struct file *f;
+ dev_pstate = DEV_INIT;
if (APROC_OK(dev_play) && dev_play->u.io.file) {
f = dev_play->u.io.file;
f->ops->stop(f);
@@ -491,8 +625,112 @@ dev_stop(void)
dev_submon->flags &= ~APROC_DROP;
#ifdef DEBUG
if (debug_level >= 2)
- dbg_puts("audio device stopped\n");
+ dbg_puts("device stopped\n");
+#endif
+}
+
+int
+dev_ref(void)
+{
+#ifdef DEBUG
+ if (debug_level >= 3)
+ dbg_puts("device requested\n");
+#endif
+ if (dev_pstate == DEV_CLOSED && !dev_open())
+ return 0;
+ dev_refcnt++;
+ return 1;
+}
+
+void
+dev_unref(void)
+{
+#ifdef DEBUG
+ if (debug_level >= 3)
+ dbg_puts("device released\n");
+#endif
+ dev_refcnt--;
+ if (dev_refcnt == 0 && dev_pstate == DEV_INIT)
+ dev_close();
+}
+
+/*
+ * There are actions (like start/stop/close ... ) that may trigger aproc
+ * operations, a thus cannot be started from aproc context.
+ * To avoid problems, aprocs only change the s!tate of the device,
+ * and actual operations are triggered from the main loop,
+ * outside the aproc code path.
+ *
+ * The following routine invokes pending actions, returns 0
+ * on fatal error
+ */
+int
+dev_run(void)
+{
+ if (dev_pstate == DEV_CLOSED)
+ return 1;
+ /*
+ * check if device isn't gone
+ */
+ if (((dev_mode & MODE_PLAY) && !APROC_OK(dev_mix)) ||
+ ((dev_mode & MODE_REC) && !APROC_OK(dev_sub)) ||
+ ((dev_mode & MODE_MON) && !APROC_OK(dev_submon))) {
+#ifdef DEBUG
+ if (debug_level >= 1)
+ dbg_puts("device disappeared\n");
+#endif
+ dev_close();
+ return 0;
+ }
+ switch (dev_pstate) {
+ case DEV_INIT:
+ /* nothing */
+ break;
+ case DEV_START:
+ dev_start();
+ /* PASSTHROUGH */
+ case DEV_RUN:
+ /*
+ * if the device is not used, then stop it
+ */
+ if ((!APROC_OK(dev_mix) ||
+ dev_mix->u.mix.idle > 2 * dev_bufsz) &&
+ (!APROC_OK(dev_sub) ||
+ dev_sub->u.sub.idle > 2 * dev_bufsz) &&
+ (!APROC_OK(dev_submon) ||
+ dev_submon->u.sub.idle > 2 * dev_bufsz) &&
+ (!APROC_OK(dev_midi) ||
+ dev_midi->u.ctl.tstate != CTL_RUN)) {
+#ifdef DEBUG
+ if (debug_level >= 3)
+ dbg_puts("device idle, suspending\n");
#endif
+ dev_stop();
+ if (dev_refcnt == 0)
+ dev_close();
+ else
+ dev_clear();
+ }
+ break;
+ }
+ return 1;
+}
+
+/*
+ * If the device is paused, then resume it. If the caller is using
+ * full-duplex and its buffers are small, the ``prime'' flag
+ * could be set to initialize device buffers with silence
+ *
+ * This routine can be called from aproc context.
+ */
+void
+dev_wakeup(int prime)
+{
+ if (dev_pstate == DEV_INIT) {
+ if (prime)
+ dev_prime();
+ dev_pstate = DEV_START;
+ }
}
/*
@@ -592,13 +830,14 @@ dev_sync(unsigned mode, struct abuf *ibuf, struct abuf *obuf)
if (debug_level >= 3) {
dbg_puts("syncing device, delta = ");
dbg_putu(delta);
- dbg_puts(" ");
if (APROC_OK(dev_mix)) {
+ dbg_puts(", ");
aproc_dbg(dev_mix);
dbg_puts(": abspos = ");
dbg_putu(dev_mix->u.mix.abspos);
}
if (APROC_OK(dev_sub)) {
+ dbg_puts(", ");
aproc_dbg(dev_sub);
dbg_puts(": abspos = ");
dbg_putu(dev_sub->u.sub.abspos);
@@ -690,7 +929,6 @@ dev_attach(char *name, unsigned mode,
return;
}
#endif
-
if (mode & MODE_PLAY) {
ipar = *sipar;
pbuf = LIST_FIRST(&dev_mix->outs);
@@ -828,12 +1066,6 @@ dev_attach(char *name, unsigned mode,
obuf->duplex = ibuf;
}
dev_sync(mode, ibuf, obuf);
-
- /*
- * Start device if not already started
- */
- if (dev_pstate == DEV_INIT)
- dev_pstate = DEV_START;
}
/*
@@ -918,6 +1150,11 @@ dev_clear(void)
void
dev_prime(void)
{
+
+#ifdef DEBUG
+ if (debug_level >= 3)
+ dbg_puts("priming device\n");
+#endif
if (APROC_OK(dev_mix)) {
#ifdef DEBUG
if (!LIST_EMPTY(&dev_mix->ins)) {
diff --git a/usr.bin/aucat/dev.h b/usr.bin/aucat/dev.h
index ec114833c00..ef9d1475dca 100644
--- a/usr.bin/aucat/dev.h
+++ b/usr.bin/aucat/dev.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: dev.h,v 1.21 2010/04/21 06:13:07 ratchov Exp $ */
+/* $OpenBSD: dev.h,v 1.22 2010/05/02 11:54:26 ratchov Exp $ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
*
@@ -21,37 +21,29 @@ struct aproc;
struct aparams;
struct abuf;
-#define DEV_INIT 0
-#define DEV_START 1
-#define DEV_RUN 2
-#define DEV_STOP 3
-
-extern unsigned dev_pstate;
extern unsigned dev_bufsz, dev_round, dev_rate;
extern struct aparams dev_ipar, dev_opar;
extern struct aproc *dev_mix, *dev_sub, *dev_midi, *dev_submon, *dev_mon;
-void dev_thruinit(void);
+int dev_run(void);
+int dev_open(void);
+void dev_close(void);
+int dev_ref(void);
+void dev_unref(void);
+void dev_done(void);
+void dev_wakeup(int);
+void dev_init_thru(void);
+void dev_init_loop(struct aparams *, struct aparams *, unsigned);
+void dev_init_sio(char *, unsigned,
+ struct aparams *, struct aparams *, unsigned, unsigned);
int dev_thruadd(char *, int, int);
void dev_midiattach(struct abuf *, struct abuf *);
unsigned dev_roundof(unsigned);
-void dev_loopinit(struct aparams *, struct aparams *, unsigned);
-int dev_init(char *, unsigned,
- struct aparams *, struct aparams *, unsigned, unsigned);
-void dev_start(void);
-void dev_stop(void);
-void dev_run(int);
-void dev_done(void);
-int dev_getep(unsigned, struct abuf **, struct abuf **);
-void dev_sync(unsigned, struct abuf *, struct abuf *);
-unsigned dev_getmode(void);
int dev_getpos(void);
void dev_attach(char *, unsigned,
struct abuf *, struct aparams *, unsigned,
struct abuf *, struct aparams *, unsigned,
unsigned, int);
void dev_setvol(struct abuf *, int);
-void dev_clear(void);
-void dev_prime(void);
#endif /* !define(DEV_H) */
diff --git a/usr.bin/aucat/headers.c b/usr.bin/aucat/headers.c
index 80efddbcfbc..b9d0a6efe57 100644
--- a/usr.bin/aucat/headers.c
+++ b/usr.bin/aucat/headers.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: headers.c,v 1.13 2010/04/06 20:07:01 ratchov Exp $ */
+/* $OpenBSD: headers.c,v 1.14 2010/05/02 11:54:26 ratchov Exp $ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
*
@@ -166,7 +166,7 @@ wav_readhdr(int fd, struct aparams *par, off_t *startpos, off_t *datasz, short *
break;
} else {
#ifdef DEBUG
- if (debug_level >= 1)
+ if (debug_level >= 2)
warnx("ignoring chuck <%.4s>\n", chunk.id);
#endif
}
diff --git a/usr.bin/aucat/midi.c b/usr.bin/aucat/midi.c
index ccd99e92ede..95f550f3439 100644
--- a/usr.bin/aucat/midi.c
+++ b/usr.bin/aucat/midi.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: midi.c,v 1.20 2010/04/24 06:18:23 ratchov Exp $ */
+/* $OpenBSD: midi.c,v 1.21 2010/05/02 11:54:26 ratchov Exp $ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
*
@@ -696,8 +696,7 @@ ctl_trystart(struct aproc *p, int caller)
dbg_puts(" mtc fps\n");
}
#endif
- if (dev_pstate == DEV_INIT)
- dev_pstate = DEV_START;
+ dev_wakeup(1);
ctl_full(p);
return 1;
}
diff --git a/usr.bin/aucat/opt.h b/usr.bin/aucat/opt.h
index 17638290833..752e7dd5eae 100644
--- a/usr.bin/aucat/opt.h
+++ b/usr.bin/aucat/opt.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: opt.h,v 1.6 2010/04/21 06:13:07 ratchov Exp $ */
+/* $OpenBSD: opt.h,v 1.7 2010/05/02 11:54:26 ratchov Exp $ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
*
@@ -34,6 +34,7 @@ struct opt {
#define MODE_MIDIIN 0x4 /* allowed to read midi */
#define MODE_MIDIOUT 0x8 /* allowed to write midi */
#define MODE_MON 0x10 /* allowed to monitor */
+#define MODE_LOOP 0x20 /* deviceless mode */
#define MODE_RECMASK (MODE_REC | MODE_MON)
unsigned mode; /* bitmap of above */
};
diff --git a/usr.bin/aucat/siofile.c b/usr.bin/aucat/siofile.c
index 72577eff1b2..780356126d8 100644
--- a/usr.bin/aucat/siofile.c
+++ b/usr.bin/aucat/siofile.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: siofile.c,v 1.4 2010/04/06 20:07:01 ratchov Exp $ */
+/* $OpenBSD: siofile.c,v 1.5 2010/05/02 11:54:26 ratchov Exp $ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
*
@@ -194,17 +194,38 @@ siofile_cb(void *addr, int delta)
* Open the device.
*/
struct siofile *
-siofile_new(struct fileops *ops, char *path, unsigned mode,
+siofile_new(struct fileops *ops, char *path, unsigned *rmode,
struct aparams *ipar, struct aparams *opar,
unsigned *bufsz, unsigned *round)
{
struct sio_par par;
struct sio_hdl *hdl;
struct siofile *f;
+ unsigned mode = *rmode;
hdl = sio_open(path, mode, 1);
- if (hdl == NULL)
- return NULL;
+ if (hdl == NULL) {
+ if (mode != (SIO_PLAY | SIO_REC))
+ return NULL;
+ hdl = sio_open(path, SIO_PLAY, 1);
+ if (hdl != NULL)
+ mode = SIO_PLAY;
+ else {
+ hdl = sio_open(path, SIO_REC, 1);
+ if (hdl != NULL)
+ mode = SIO_REC;
+ else
+ return NULL;
+ }
+#ifdef DEBUG
+ if (debug_level >= 1) {
+ dbg_puts("warning, device opened in ");
+ dbg_puts(mode == SIO_PLAY ? "play-only" : "rec-only");
+ dbg_puts(" mode\n");
+ }
+#endif
+ }
+
sio_initpar(&par);
if (mode & SIO_REC) {
par.bits = ipar->bits;
@@ -248,6 +269,7 @@ siofile_new(struct fileops *ops, char *path, unsigned mode,
opar->rate = par.rate;
opar->cmax = opar->cmin + par.pchan - 1;
}
+ *rmode = mode;
*bufsz = par.bufsz;
*round = par.round;
if (path == NULL)
diff --git a/usr.bin/aucat/siofile.h b/usr.bin/aucat/siofile.h
index 7f72255a347..c2242887b2e 100644
--- a/usr.bin/aucat/siofile.h
+++ b/usr.bin/aucat/siofile.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: siofile.h,v 1.4 2010/04/06 20:07:01 ratchov Exp $ */
+/* $OpenBSD: siofile.h,v 1.5 2010/05/02 11:54:26 ratchov Exp $ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
*
@@ -22,7 +22,7 @@ struct siofile;
struct aparams;
struct aproc;
-struct siofile *siofile_new(struct fileops *, char *, unsigned,
+struct siofile *siofile_new(struct fileops *, char *, unsigned *,
struct aparams *, struct aparams *, unsigned *, unsigned *);
struct aproc *rsio_new(struct file *f);
struct aproc *wsio_new(struct file *f);
diff --git a/usr.bin/aucat/sock.c b/usr.bin/aucat/sock.c
index 751bf6cb4f0..f3b50405dc2 100644
--- a/usr.bin/aucat/sock.c
+++ b/usr.bin/aucat/sock.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sock.c,v 1.45 2010/04/24 06:18:23 ratchov Exp $ */
+/* $OpenBSD: sock.c,v 1.46 2010/05/02 11:54:27 ratchov Exp $ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
*
@@ -95,6 +95,7 @@ sock_close(struct file *f)
{
sock_sesrefs--;
pipe_close(f);
+ dev_unref();
}
void
@@ -333,8 +334,14 @@ sock_new(struct fileops *ops, int fd)
sock_sesrefs++;
f = (struct sock *)pipe_new(ops, fd, "sock");
- if (f == NULL)
+ if (f == NULL) {
+ close(fd);
+ return NULL;
+ }
+ if (!dev_ref()) {
+ close(fd);
return NULL;
+ }
f->pstate = SOCK_HELLO;
f->mode = 0;
f->opt = opt_byname("default");
@@ -532,6 +539,12 @@ sock_attach(struct sock *f, int force)
return;
/*
+ * start the device (dev_getpos() and dev_attach() must
+ * be called on a started device
+ */
+ dev_wakeup(1);
+
+ /*
* get the current position, the origin is when
* the first sample is played/recorded
*/
@@ -1516,7 +1529,11 @@ sock_buildmsg(struct sock *f)
#ifdef DEBUG
if (ibuf->used > f->wmax && debug_level >= 3) {
sock_dbg(f);
- dbg_puts(": attempt to send past current position\n");
+ dbg_puts(": attempt to send past current position: used = ");
+ dbg_putu(ibuf->used);
+ dbg_puts(" wmax = ");
+ dbg_putu(f->wmax);
+ dbg_puts("\n");
}
#endif
max = AMSG_DATAMAX / ibuf->bpf;
diff --git a/usr.bin/aucat/wav.c b/usr.bin/aucat/wav.c
index afe5cee7ecb..84a374cf421 100644
--- a/usr.bin/aucat/wav.c
+++ b/usr.bin/aucat/wav.c
@@ -295,6 +295,7 @@ wav_close(struct file *file)
}
}
pipe_close(file);
+ dev_unref();
}
/*
@@ -317,6 +318,13 @@ wav_attach(struct wav *f, int force)
dbg_puts(": attaching\n");
}
#endif
+
+ /*
+ * start the device (dev_getpos() and dev_attach() must
+ * be called on a started device
+ */
+ dev_wakeup(0);
+
dev_attach(f->pipe.file.name, f->mode,
rbuf, &f->hpar, f->join ? dev_opar.cmax - dev_opar.cmin + 1 : 0,
wbuf, &f->hpar, f->join ? dev_ipar.cmax - dev_ipar.cmin + 1 : 0,
@@ -648,8 +656,14 @@ wav_new_in(struct fileops *ops, unsigned mode, char *name, unsigned hdr,
perror(name);
}
f = (struct wav *)pipe_new(ops, fd, name);
- if (f == NULL)
+ if (f == NULL) {
+ close(fd);
+ return NULL;
+ }
+ if (!dev_ref()) {
+ close(fd);
return NULL;
+ }
if (hdr == HDR_WAV) {
if (!wav_readhdr(f->pipe.fd, par, &f->startpos, &f->rbytes, &f->map)) {
file_del((struct file *)f);
@@ -720,8 +734,14 @@ wav_new_out(struct fileops *ops, unsigned mode, char *name, unsigned hdr,
}
}
f = (struct wav *)pipe_new(ops, fd, name);
- if (f == NULL)
+ if (f == NULL) {
+ close(fd);
return NULL;
+ }
+ if (!dev_ref()) {
+ close(fd);
+ return NULL;
+ }
if (hdr == HDR_WAV) {
par->le = 1;
par->sig = (par->bits <= 8) ? 0 : 1;