diff options
author | Alexandre Ratchov <ratchov@cvs.openbsd.org> | 2009-11-03 21:31:38 +0000 |
---|---|---|
committer | Alexandre Ratchov <ratchov@cvs.openbsd.org> | 2009-11-03 21:31:38 +0000 |
commit | 092696bed12a81c8dd98058f7e53cf6d128ce418 (patch) | |
tree | 90a71f9d04dd7efee0999fdf0fe8467441c933aa /usr.bin/aucat | |
parent | 573dc0b34de27c7e2f07b0c917e352fd1d4d1a23 (diff) |
Allow any program using aucat to act as MMC slave and MTC master
transparently. Multiple audio applications can be started
synchronously from external software/hardware supporting the
standard Start/Stop/Relocate messages. The server clock is exposed
through MTC, allowing non-audio software/hardware to be
synchronized to audio applications.
Diffstat (limited to 'usr.bin/aucat')
-rw-r--r-- | usr.bin/aucat/aproc.c | 33 | ||||
-rw-r--r-- | usr.bin/aucat/aproc.h | 44 | ||||
-rw-r--r-- | usr.bin/aucat/aucat.1 | 110 | ||||
-rw-r--r-- | usr.bin/aucat/aucat.c | 70 | ||||
-rw-r--r-- | usr.bin/aucat/conf.h | 7 | ||||
-rw-r--r-- | usr.bin/aucat/dev.c | 106 | ||||
-rw-r--r-- | usr.bin/aucat/dev.h | 5 | ||||
-rw-r--r-- | usr.bin/aucat/midi.c | 314 | ||||
-rw-r--r-- | usr.bin/aucat/midi.h | 7 | ||||
-rw-r--r-- | usr.bin/aucat/opt.c | 6 | ||||
-rw-r--r-- | usr.bin/aucat/opt.h | 5 | ||||
-rw-r--r-- | usr.bin/aucat/sock.c | 43 |
12 files changed, 642 insertions, 108 deletions
diff --git a/usr.bin/aucat/aproc.c b/usr.bin/aucat/aproc.c index 44e5529b163..6e64b8c9229 100644 --- a/usr.bin/aucat/aproc.c +++ b/usr.bin/aucat/aproc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: aproc.c,v 1.37 2009/10/10 09:54:05 ratchov Exp $ */ +/* $OpenBSD: aproc.c,v 1.38 2009/11/03 21:31:37 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -43,6 +43,7 @@ #include "aproc.h" #include "conf.h" #include "file.h" +#include "midi.h" struct aproc * @@ -58,7 +59,7 @@ aproc_new(struct aproc_ops *ops, char *name) p->name = name; p->ops = ops; p->refs = 0; - p->zomb = 0; + p->flags = 0; return p; } @@ -67,7 +68,7 @@ aproc_del(struct aproc *p) { struct abuf *i; - if (!p->zomb) { + if (!(p->flags & APROC_ZOMB)) { if (p->ops->done) { p->ops->done(p); } @@ -79,7 +80,7 @@ aproc_del(struct aproc *p) i = LIST_FIRST(&p->obuflist); abuf_eof(i); } - p->zomb = 1; + p->flags |= APROC_ZOMB; } if (p->refs > 0) { return; @@ -483,7 +484,7 @@ mix_out(struct aproc *p, struct abuf *obuf) if (!abuf_fill(i)) continue; /* eof */ if (!ABUF_ROK(i)) { - if (p->u.mix.flags & MIX_DROP) { + if (p->flags & APROC_DROP) { if (!mix_xrun(i, obuf)) continue; } @@ -493,11 +494,11 @@ mix_out(struct aproc *p, struct abuf *obuf) odone = i->r.mix.done; } if (LIST_EMPTY(&p->ibuflist)) { - if (p->u.mix.flags & MIX_AUTOQUIT) { + if (p->flags & APROC_QUIT) { aproc_del(p); return 0; } - if (!(p->u.mix.flags & MIX_DROP)) + if (!(p->flags & APROC_DROP)) return 0; mix_bzero(obuf, obuf->len); odone = obuf->w.mix.todo; @@ -570,6 +571,8 @@ void mix_opos(struct aproc *p, struct abuf *obuf, int delta) { p->u.mix.lat -= delta; + if (p->u.mix.ctl) + ctl_ontick(p->u.mix.ctl, delta); aproc_opos(p, obuf, delta); } @@ -587,15 +590,15 @@ struct aproc_ops mix_ops = { }; struct aproc * -mix_new(char *name, int maxlat) +mix_new(char *name, int maxlat, struct aproc *ctl) { struct aproc *p; p = aproc_new(&mix_ops, name); - p->u.mix.flags = 0; p->u.mix.idle = 0; p->u.mix.lat = 0; p->u.mix.maxlat = maxlat; + p->u.mix.ctl = ctl; return p; } @@ -705,7 +708,7 @@ sub_in(struct aproc *p, struct abuf *ibuf) for (i = LIST_FIRST(&p->obuflist); i != NULL; i = inext) { inext = LIST_NEXT(i, oent); if (!ABUF_WOK(i)) { - if (p->u.sub.flags & SUB_DROP) { + if (p->flags & APROC_DROP) { if (!sub_xrun(ibuf, i)) continue; } @@ -717,11 +720,11 @@ sub_in(struct aproc *p, struct abuf *ibuf) continue; } if (LIST_EMPTY(&p->obuflist)) { - if (p->u.sub.flags & SUB_AUTOQUIT) { + if (p->flags & APROC_QUIT) { aproc_del(p); return 0; } - if (!(p->u.sub.flags & SUB_DROP)) + if (!(p->flags & APROC_DROP)) return 0; idone = ibuf->used; p->u.sub.idle += idone / ibuf->bpf; @@ -811,6 +814,8 @@ void sub_ipos(struct aproc *p, struct abuf *ibuf, int delta) { p->u.sub.lat += delta; + if (p->u.sub.ctl) + ctl_ontick(p->u.sub.ctl, delta); aproc_ipos(p, ibuf, delta); } @@ -828,15 +833,15 @@ struct aproc_ops sub_ops = { }; struct aproc * -sub_new(char *name, int maxlat) +sub_new(char *name, int maxlat, struct aproc *ctl) { struct aproc *p; p = aproc_new(&sub_ops, name); - p->u.sub.flags = 0; p->u.sub.idle = 0; p->u.sub.lat = 0; p->u.sub.maxlat = maxlat; + p->u.sub.ctl = ctl; return p; } diff --git a/usr.bin/aucat/aproc.h b/usr.bin/aucat/aproc.h index 95bbced80e4..106bf0c2cce 100644 --- a/usr.bin/aucat/aproc.h +++ b/usr.bin/aucat/aproc.h @@ -1,4 +1,4 @@ -/* $OpenBSD: aproc.h,v 1.26 2009/10/27 22:41:03 ratchov Exp $ */ +/* $OpenBSD: aproc.h,v 1.27 2009/11/03 21:31:37 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -123,26 +123,25 @@ struct aproc { LIST_HEAD(, abuf) ibuflist; /* list of inputs */ LIST_HEAD(, abuf) obuflist; /* list of outputs */ unsigned refs; /* extern references */ - unsigned zomb; /* destroyed but not freed */ +#define APROC_ZOMB 1 /* destroyed but not freed */ +#define APROC_QUIT 2 /* try to terminate if unused */ +#define APROC_DROP 4 /* xrun if capable */ + unsigned flags; union { /* follow type-specific data */ struct { /* file/device io */ struct file *file; /* file to read/write */ } io; struct { -#define MIX_DROP 1 -#define MIX_AUTOQUIT 2 - unsigned flags; /* bit mask of above */ unsigned idle; /* frames since idleing */ int lat; /* current latency */ - int maxlat; /* max latency allowed*/ + int maxlat; /* max latency allowed */ + struct aproc *ctl; } mix; struct { -#define SUB_DROP 1 -#define SUB_AUTOQUIT 2 unsigned idle; /* frames since idleing */ - unsigned flags; /* bit mask of above */ int lat; /* current latency */ - int maxlat; /* max latency allowed*/ + int maxlat; /* max latency allowed */ + struct aproc *ctl; } sub; struct { #define RESAMP_NCTX 2 @@ -166,22 +165,39 @@ struct aproc { struct { struct abuf *owner; /* current input stream */ struct timo timo; /* timout for throtteling */ -#define THRU_AUTOQUIT 1 - unsigned flags; /* bit mask of above */ } thru; struct { #define CTL_NSLOT 8 #define CTL_NAMEMAX 8 unsigned serial; +#define CTL_OFF 0 /* ignore MMC messages */ +#define CTL_STOP 1 /* stopped, can't start */ +#define CTL_START 2 /* attempting to start */ +#define CTL_RUN 3 /* started */ + unsigned tstate; + unsigned origin; /* MTC start time */ + unsigned fps; /* MTC frames per second */ +#define MTC_FPS_24 0 +#define MTC_FPS_25 1 +#define MTC_FPS_30 3 + unsigned fps_id; /* one of above */ + unsigned hr; /* MTC hours */ + unsigned min; /* MTC minutes */ + unsigned sec; /* MTC seconds */ + unsigned fr; /* MTC frames */ + unsigned qfr; /* MTC quarter frames */ + int delta; /* rel. to the last MTC tick */ struct ctl_slot { struct ctl_ops { void (*vol)(void *, unsigned); + void (*start)(void *); } *ops; void *arg; unsigned unit; char name[CTL_NAMEMAX]; unsigned serial; unsigned vol; + unsigned tstate; } slot[CTL_NSLOT]; } ctl; } u; @@ -208,8 +224,8 @@ int wpipe_out(struct aproc *, struct abuf *); void wpipe_eof(struct aproc *, struct abuf *); void wpipe_hup(struct aproc *, struct abuf *); -struct aproc *mix_new(char *, int); -struct aproc *sub_new(char *, int); +struct aproc *mix_new(char *, int, struct aproc *); +struct aproc *sub_new(char *, int, struct aproc *); struct aproc *resamp_new(char *, unsigned, unsigned); struct aproc *cmap_new(char *, struct aparams *, struct aparams *); struct aproc *enc_new(char *, struct aparams *); diff --git a/usr.bin/aucat/aucat.1 b/usr.bin/aucat/aucat.1 index d498b0fa61b..67dd9030f11 100644 --- a/usr.bin/aucat/aucat.1 +++ b/usr.bin/aucat/aucat.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: aucat.1,v 1.61 2009/10/22 15:02:12 sobrado Exp $ +.\" $OpenBSD: aucat.1,v 1.62 2009/11/03 21:31:37 ratchov Exp $ .\" .\" Copyright (c) 2006 Alexandre Ratchov <alex@caoua.org> .\" @@ -14,7 +14,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: October 22 2009 $ +.Dd $Mdocdate: November 3 2009 $ .Dt AUCAT 1 .Os .Sh NAME @@ -35,9 +35,11 @@ .Op Fl o Ar file .Op Fl r Ar rate .Op Fl s Ar name +.Op Fl t Ar mode .Op Fl U Ar unit .Op Fl v Ar volume .Op Fl x Ar policy +.Op Fl z Ar nframes .Ek .Sh DESCRIPTION The @@ -134,11 +136,22 @@ Defining multiple sub-devices allows splitting a physical audio device into logical devices having different properties (eg. channel ranges). The given .Ar name -correponds to the +corresponds to the .Dq option part of the .Xr sndio 7 device name string. +.It Fl t Ar mode +Select the way sub-devices are controlled by MMC (MIDI Machine Control) messages. +If the mode is +.Va off +(the default), then streams are not affected by MMC messages. +If the mode is +.Va slave , +then streams are started synchronously by MMC start messages; +additionally, the server clock is exposed as MTC (MIDI Time Code) +messages allowing MTC-capable software or hardware to be synchronized +to audio streams. .It Fl U Ar unit Unit number to use when running in server mode. Each @@ -185,6 +198,21 @@ once the stream is unblocked. If the policy is .Dq error then the stream is closed permanently. +.Pp +If a sub-device is created with +.Fl t Ar slave , +then, to ensure proper synchronization, the +.Dq ignore +action is disabled for any stream connected to it. +.It Fl z Ar nframes +The audio block size in frames. +This is the number of frames between audio clock ticks, i.e. the clock resolution. +If a sub-device is created with +.Fl t Ar slave, +and MTC (Midi Time Code) is used for synchronization, the clock +resolution must be 96, 100 or 120 ticks per second for maximum +accuracy. For instance, 120 ticks per second at 48000Hz corresponds +to 400 frame block size. .El .Pp If @@ -365,14 +393,86 @@ While running in server mode .Nm exposes a MIDI device with the same name as the default audio device. -It allows MIDI hardware or software to be used to control -the volume of played streams. +It allows MIDI hardware or software to control programs +using +.Nm +or to synchronize to them. +.Pp A MIDI channel is assigned to each stream, and the volume is changed using the standard volume controller (number 7). Similarly, when the audio application changes its volume, the same MIDI controller message is sent out; it can be used for instance for monitoring or as feedback for motorized faders. +.Pp +Clients connected to sub-devices created with +.Fl t Ar slave , +are controlled by the following MMC (MIDI Machine Control) messages: +.Bl -tag -width relocateXXX -offset -indent +.It stop +Put the sub-device in stopped mode (the default). +In this mode, any stream attempting to start playback or recording +is paused. +Streams that are already started are not affected until they stop +and try to start again. +.It relocate +Gives +.Nm +the time, relative to the beginning of the stream, at which playback +and recording must start. +It is not interpreted by +.Nm +itself. +The given time position, is sent to MIDI clients as an MTC +.Dq "full frame" +message forcing all MTC-slaves to relocate to the given +position (see below). +.It start +Put the sub-device in starting mode. +In this mode, the sub-device waits for all streams to become ready +to start, and then starts them synchronously. +Once started, new streams can be created, but they will be blocked +until the next stop-to-start transition. +.El +.Pp +Sub-devices created with +.Fl t Ar slave , +will export the server clock using MTC (MIDI Time Code), allowing non-audio +software or hardware to be synchronized to the audio stream. +The following sample rates +.Pq Fl r +and block sizes +.Pq Fl z +are recommended for maximum accuracy: +.Pp +.Bl -bullet -offset -indent -compact +.It +44100Hz, 441 frames +.It +48000Hz, 400 frames +.It +48000Hz, 480 frames +.It +48000Hz, 500 frames +.El +.Pp +For instance, the following command will create two devices: +the default +.Va aucat:0 +and a MIDI-controlled one +.Va aucat:0.mmc . +.Bd -literal -offset indent +$ aucat -l -r 48000 -z 400 -s default -t slave -s mmc +.Ed +.Pp +Streams connected to +.Va aucat:0 +behave normally, while streams connected to +.Va aucat:0.mmc +wait for the MMC start signal and start synchronously. +Regardless to which device a stream is connected, its playback volume +knob is exposed. +.Pp .Sh LEGACY MODE If neither .Fl i diff --git a/usr.bin/aucat/aucat.c b/usr.bin/aucat/aucat.c index 32c20733de3..74b85b292d5 100644 --- a/usr.bin/aucat/aucat.c +++ b/usr.bin/aucat/aucat.c @@ -1,4 +1,4 @@ -/* $OpenBSD: aucat.c,v 1.73 2009/10/27 22:21:37 ratchov Exp $ */ +/* $OpenBSD: aucat.c,v 1.74 2009/11/03 21:31:37 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -110,6 +110,16 @@ opt_hdr(void) } int +opt_mmc(void) +{ + if (strcmp("off", optarg) == 0) + return 0; + if (strcmp("slave", optarg) == 0) + return 1; + errx(1, "%s: bad MMC mode", optarg); +} + +int opt_xrun(void) { if (strcmp("ignore", optarg) == 0) @@ -144,6 +154,7 @@ struct farg { char *name; /* optarg pointer (no need to copy it */ int hdr; /* header format */ int xrun; /* overrun/underrun policy */ + int mmc; /* MMC mode */ }; SLIST_HEAD(farglist, farg); @@ -155,7 +166,7 @@ SLIST_HEAD(farglist, farg); void farg_add(struct farglist *list, struct aparams *ipar, struct aparams *opar, unsigned vol, - int hdr, int xrun, char *optarg) + int hdr, int xrun, int mmc, char *optarg) { struct farg *fa; size_t namelen; @@ -179,6 +190,7 @@ farg_add(struct farglist *list, fa->opar = *opar; fa->vol = vol; fa->name = optarg; + fa->mmc = mmc; SLIST_INSERT_HEAD(list, fa, entry); } @@ -347,7 +359,7 @@ aucat_usage(void) (void)fputs("usage: " PROG_AUCAT " [-dlnu] [-b nframes] " "[-C min:max] [-c min:max] [-e enc] [-f device]\n" "\t[-h fmt] [-i file] [-m mode] [-o file] [-r rate] [-s name]\n" - "\t[-U unit] [-v volume] [-x policy]\n", + "\t[-t mode] [-U unit] [-v volume] [-x policy] [-z nframes]\n", stderr); } @@ -359,9 +371,10 @@ aucat_main(int argc, char **argv) struct farglist ifiles, ofiles, sfiles; struct aparams ipar, opar, dipar, dopar; char base[PATH_MAX], path[PATH_MAX]; - unsigned bufsz, mode; + unsigned bufsz, round, mode; char *devpath; unsigned volctl; + int mmc; aparams_init(&ipar, 0, 1, 44100); aparams_init(&opar, 0, 1, 44100); @@ -370,6 +383,7 @@ aucat_main(int argc, char **argv) l_flag = 0; n_flag = 0; unit = -1; + mmc = 0; devpath = NULL; SLIST_INIT(&ifiles); SLIST_INIT(&ofiles); @@ -377,10 +391,11 @@ aucat_main(int argc, char **argv) hdr = HDR_AUTO; xrun = XRUN_IGNORE; volctl = MIDI_MAXCTL; - bufsz = 44100 * 4 / 15; /* XXX: use milliseconds, not frames */ mode = 0; + bufsz = 0; + round = 0; - while ((c = getopt(argc, argv, "dnb:c:C:e:r:h:x:v:i:o:f:m:lus:U:")) != -1) { + while ((c = getopt(argc, argv, "dnb:c:C:e:r:h:x:v:i:o:f:m:lus:U:t:z:")) != -1) { switch (c) { case 'd': d_flag = 1; @@ -397,6 +412,9 @@ aucat_main(int argc, char **argv) case 'x': xrun = opt_xrun(); break; + case 't': + mmc = opt_mmc(); + break; case 'c': opt_ch(&ipar); break; @@ -416,15 +434,15 @@ aucat_main(int argc, char **argv) break; case 'i': farg_add(&ifiles, &ipar, &opar, volctl, - hdr, xrun, optarg); + hdr, xrun, 0, optarg); break; case 'o': farg_add(&ofiles, &ipar, &opar, volctl, - hdr, xrun, optarg); + hdr, xrun, 0, optarg); break; case 's': farg_add(&sfiles, &ipar, &opar, volctl, - hdr, xrun, optarg); + hdr, xrun, mmc, optarg); break; case 'f': if (devpath) @@ -451,6 +469,13 @@ aucat_main(int argc, char **argv) exit(1); } break; + case 'z': + if (sscanf(optarg, "%u", &round) != 1 || round == 0) { + fprintf(stderr, "%s: bad block size\n", optarg); + exit(1); + } + break; + default: aucat_usage(); exit(1); @@ -507,7 +532,7 @@ aucat_main(int argc, char **argv) */ if (l_flag && SLIST_EMPTY(&sfiles)) { farg_add(&sfiles, &dopar, &dipar, - volctl, HDR_RAW, XRUN_IGNORE, DEFAULT_OPT); + volctl, HDR_RAW, XRUN_IGNORE, mmc, DEFAULT_OPT); } if (!u_flag) { @@ -529,6 +554,10 @@ aucat_main(int argc, char **argv) aparams_grow(&dipar, &fa->opar); } } + if (!round) + round = ((mode & MODE_REC) ? dipar.rate : dopar.rate) / 15; + if (!bufsz) + bufsz = ((mode & MODE_REC) ? dipar.rate : dopar.rate) * 4 / 15; if (l_flag) { getbasepath(base, sizeof(base)); @@ -548,7 +577,7 @@ aucat_main(int argc, char **argv) if (!dev_init(devpath, (mode & MODE_REC) ? &dipar : NULL, (mode & MODE_PLAY) ? &dopar : NULL, - bufsz)) { + bufsz, round)) { errx(1, "%s: can't open device", devpath ? devpath : "<default>"); } @@ -572,7 +601,8 @@ aucat_main(int argc, char **argv) while (!SLIST_EMPTY(&sfiles)) { fa = SLIST_FIRST(&sfiles); SLIST_REMOVE_HEAD(&sfiles, entry); - opt_new(fa->name, &fa->opar, &fa->ipar, MIDI_TO_ADATA(fa->vol)); + opt_new(fa->name, &fa->opar, &fa->ipar, + MIDI_TO_ADATA(fa->vol), fa->mmc); free(fa); } if (l_flag) { @@ -598,7 +628,8 @@ aucat_main(int argc, char **argv) if (!file_poll()) break; if ((!dev_mix || dev_mix->u.mix.idle > 2 * dev_bufsz) && - (!dev_sub || dev_sub->u.sub.idle > 2 * dev_bufsz)) { + (!dev_sub || dev_sub->u.sub.idle > 2 * dev_bufsz) && + ((dev_mix || dev_sub) && dev_midi->u.ctl.tstate != CTL_RUN)) { if (!l_flag) break; if (!suspend) { @@ -608,7 +639,8 @@ aucat_main(int argc, char **argv) } } if ((dev_mix && dev_mix->u.mix.idle == 0) || - (dev_sub && dev_sub->u.sub.idle == 0)) { + (dev_sub && dev_sub->u.sub.idle == 0) || + ((dev_mix || dev_sub) && dev_midi->u.ctl.tstate == CTL_RUN)) { if (suspend) { suspend = 0; dev_start(); @@ -662,15 +694,15 @@ midicat_main(int argc, char **argv) break; case 'i': farg_add(&ifiles, &aparams_none, &aparams_none, - 0, HDR_RAW, 0, optarg); + 0, HDR_RAW, 0, 0, optarg); break; case 'o': farg_add(&ofiles, &aparams_none, &aparams_none, - 0, HDR_RAW, 0, optarg); + 0, HDR_RAW, 0, 0, optarg); break; case 'f': farg_add(&dfiles, &aparams_none, &aparams_none, - 0, HDR_RAW, 0, optarg); + 0, HDR_RAW, 0, 0, optarg); break; case 'l': l_flag = 1; @@ -708,11 +740,11 @@ midicat_main(int argc, char **argv) dev_thruinit(); if (!l_flag) - dev_midi->u.thru.flags |= THRU_AUTOQUIT; + dev_midi->flags |= APROC_QUIT; if ((!SLIST_EMPTY(&ifiles) || !SLIST_EMPTY(&ofiles)) && SLIST_EMPTY(&dfiles)) { farg_add(&dfiles, &aparams_none, &aparams_none, - 0, HDR_RAW, 0, NULL); + 0, HDR_RAW, 0, 0, NULL); } while (!SLIST_EMPTY(&dfiles)) { fa = SLIST_FIRST(&dfiles); diff --git a/usr.bin/aucat/conf.h b/usr.bin/aucat/conf.h index 5a138919166..00515460cfc 100644 --- a/usr.bin/aucat/conf.h +++ b/usr.bin/aucat/conf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: conf.h,v 1.10 2009/09/27 11:51:20 ratchov Exp $ */ +/* $OpenBSD: conf.h,v 1.11 2009/11/03 21:31:37 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -43,4 +43,9 @@ */ #define MIDI_BUFSZ 3125 /* 1 second at 31.25kbit/s */ +/* + * units used for MTC clock. + */ +#define MTC_SEC 2400 /* 1 second is 2400 ticks */ + #endif /* !defined(CONF_H) */ diff --git a/usr.bin/aucat/dev.c b/usr.bin/aucat/dev.c index d1c317fd6b4..4c58cffcce3 100644 --- a/usr.bin/aucat/dev.c +++ b/usr.bin/aucat/dev.c @@ -1,4 +1,4 @@ -/* $OpenBSD: dev.c,v 1.35 2009/10/27 22:24:27 ratchov Exp $ */ +/* $OpenBSD: dev.c,v 1.36 2009/11/03 21:31:37 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -83,15 +83,15 @@ dev_loopinit(struct aparams *dipar, struct aparams *dopar, unsigned bufsz) dev_play = NULL; buf = abuf_new(dev_bufsz, &par); - dev_mix = mix_new("mix", dev_bufsz); + dev_mix = mix_new("mix", dev_bufsz, NULL); dev_mix->refs++; - dev_sub = sub_new("sub", dev_bufsz); + dev_sub = sub_new("sub", dev_bufsz, NULL); dev_sub->refs++; aproc_setout(dev_mix, buf); aproc_setin(dev_sub, buf); - dev_mix->u.mix.flags |= MIX_AUTOQUIT; - dev_sub->u.sub.flags |= SUB_AUTOQUIT; + dev_mix->flags |= APROC_QUIT; + dev_sub->flags |= APROC_QUIT; *dipar = dev_ipar; *dopar = dev_opar; @@ -110,7 +110,7 @@ dev_roundof(unsigned newrate) */ int dev_init(char *devpath, - struct aparams *dipar, struct aparams *dopar, unsigned bufsz) + struct aparams *dipar, struct aparams *dopar, unsigned bufsz, unsigned round) { struct file *f; struct aparams ipar, opar; @@ -118,12 +118,16 @@ dev_init(char *devpath, struct abuf *buf; unsigned nfr, ibufsz, obufsz; + dev_midi = ctl_new("ctl"); + dev_midi->refs++; + /* * Ask for 1/4 of the buffer for the kernel ring and * limit the block size to 1/4 of the requested buffer. */ - dev_bufsz = (bufsz + 3) / 4; - dev_round = (bufsz + 3) / 4; + dev_round = round; + dev_bufsz = (bufsz + 3) / 4 + (dev_round - 1); + dev_bufsz -= dev_bufsz % dev_round; f = (struct file *)safile_new(&safile_ops, devpath, dipar, dopar, &dev_bufsz, &dev_round); if (f == NULL) @@ -176,8 +180,9 @@ dev_init(char *devpath, /* * Append a "sub" to which clients will connect. + * Link it to the controller only in record-only mode */ - dev_sub = sub_new("rec", nfr); + dev_sub = sub_new("rec", nfr, dopar ? NULL : dev_midi); dev_sub->refs++; aproc_setin(dev_sub, buf); } else { @@ -214,7 +219,7 @@ dev_init(char *devpath, /* * Append a "mix" to which clients will connect. */ - dev_mix = mix_new("play", nfr); + dev_mix = mix_new("play", nfr, dev_midi); dev_mix->refs++; aproc_setout(dev_mix, buf); } else { @@ -222,8 +227,6 @@ dev_init(char *devpath, dev_mix = NULL; } dev_bufsz = (dopar) ? obufsz : ibufsz; - dev_midi = ctl_new("ctl"); - dev_midi->refs++; dev_start(); return 1; } @@ -237,18 +240,6 @@ dev_done(void) { struct file *f; - if (dev_midi) { - if (!dev_sub && !dev_mix) - dev_midi->u.thru.flags |= THRU_AUTOQUIT; - restart_midi: - LIST_FOREACH(f, &file_list, entry) { - if (f->rproc && - aproc_depend(dev_midi, f->rproc)) { - file_eof(f); - goto restart_midi; - } - } - } if (dev_mix) { /* * Put the mixer in ``autoquit'' state and generate @@ -260,7 +251,7 @@ dev_done(void) * reorder the file_list, we have to restart the loop * after each call to file_eof(). */ - dev_mix->u.mix.flags |= MIX_AUTOQUIT; + dev_mix->flags |= APROC_QUIT; restart_mix: LIST_FOREACH(f, &file_list, entry) { if (f->rproc != NULL && @@ -284,33 +275,44 @@ dev_done(void) } } } + if (dev_midi) { + dev_midi->flags |= APROC_QUIT; + restart_midi: + LIST_FOREACH(f, &file_list, entry) { + if (f->rproc && + aproc_depend(dev_midi, f->rproc)) { + file_eof(f); + goto restart_midi; + } + } + } if (dev_mix) { dev_mix->refs--; - if (dev_mix->zomb) + if (dev_mix->flags & APROC_ZOMB) aproc_del(dev_mix); dev_mix = NULL; } if (dev_play) { dev_play->refs--; - if (dev_play->zomb) + if (dev_play->flags & APROC_ZOMB) aproc_del(dev_play); dev_play = NULL; } if (dev_sub) { dev_sub->refs--; - if (dev_sub->zomb) + if (dev_sub->flags & APROC_ZOMB) aproc_del(dev_sub); dev_sub = NULL; } if (dev_rec) { dev_rec->refs--; - if (dev_rec->zomb) + if (dev_rec->flags & APROC_ZOMB) aproc_del(dev_rec); dev_rec = NULL; } if (dev_midi) { dev_midi->refs--; - if (dev_midi->zomb) + if (dev_midi->flags & APROC_ZOMB) aproc_del(dev_midi); dev_midi = NULL; } @@ -329,9 +331,9 @@ dev_start(void) struct file *f; if (dev_mix) - dev_mix->u.mix.flags |= MIX_DROP; + dev_mix->flags |= APROC_DROP; if (dev_sub) - dev_sub->u.sub.flags |= SUB_DROP; + dev_sub->flags |= APROC_DROP; if (dev_play && dev_play->u.io.file) { f = dev_play->u.io.file; f->ops->start(f); @@ -357,9 +359,9 @@ dev_stop(void) f->ops->stop(f); } if (dev_mix) - dev_mix->u.mix.flags &= ~MIX_DROP; + dev_mix->flags &= ~APROC_DROP; if (dev_sub) - dev_sub->u.sub.flags &= ~SUB_DROP; + dev_sub->flags &= ~APROC_DROP; } /* @@ -445,6 +447,42 @@ dev_sync(struct abuf *ibuf, struct abuf *obuf) } /* + * return the current latency (in frames), ie the latency that + * a stream would have if dev_attach() is called on it. + */ +int +dev_getpos(void) +{ + struct abuf *pbuf = NULL, *rbuf = NULL; + int plat = 0, rlat = 0; + int delta; + + if (dev_mix) { + pbuf = LIST_FIRST(&dev_mix->obuflist); + if (!pbuf) + return 0; + plat = -dev_mix->u.mix.lat; + } + if (dev_sub) { + rbuf = LIST_FIRST(&dev_sub->ibuflist); + if (!rbuf) + return 0; + rlat = -dev_sub->u.sub.lat; + } + if (dev_mix && dev_sub) { + delta = + rbuf->bpf * (pbuf->abspos + pbuf->used) - + pbuf->bpf * rbuf->abspos; + delta /= pbuf->bpf * rbuf->bpf; + if (delta > 0) + rlat -= delta; + else if (delta < 0) + plat += delta; + } + return dev_mix ? plat : rlat; +} + +/* * 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 diff --git a/usr.bin/aucat/dev.h b/usr.bin/aucat/dev.h index 1ea74b2d661..76c9929cf22 100644 --- a/usr.bin/aucat/dev.h +++ b/usr.bin/aucat/dev.h @@ -1,4 +1,4 @@ -/* $OpenBSD: dev.h,v 1.14 2009/10/10 13:55:37 ratchov Exp $ */ +/* $OpenBSD: dev.h,v 1.15 2009/11/03 21:31:37 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -29,13 +29,14 @@ void dev_thruinit(void); void dev_midiattach(struct abuf *, struct abuf *); unsigned dev_roundof(unsigned); void dev_loopinit(struct aparams *, struct aparams *, unsigned); -int dev_init(char *, struct aparams *, struct aparams *, unsigned); +int dev_init(char *, 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(struct abuf **, struct abuf **); void dev_sync(struct abuf *, struct abuf *); +int dev_getpos(void); void dev_attach(char *, struct abuf *, struct aparams *, unsigned, struct abuf *, struct aparams *, unsigned, int); diff --git a/usr.bin/aucat/midi.c b/usr.bin/aucat/midi.c index aea71324735..8c8a80698cf 100644 --- a/usr.bin/aucat/midi.c +++ b/usr.bin/aucat/midi.c @@ -1,4 +1,4 @@ -/* $OpenBSD: midi.c,v 1.12 2009/10/27 22:41:03 ratchov Exp $ */ +/* $OpenBSD: midi.c,v 1.13 2009/11/03 21:31:37 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -225,7 +225,7 @@ thru_out(struct aproc *p, struct abuf *obuf) void thru_eof(struct aproc *p, struct abuf *ibuf) { - if (!(p->u.thru.flags & THRU_AUTOQUIT)) + if (!(p->flags & APROC_QUIT)) return; if (LIST_EMPTY(&p->obuflist) || LIST_EMPTY(&p->ibuflist)) aproc_del(p); @@ -234,7 +234,7 @@ thru_eof(struct aproc *p, struct abuf *ibuf) void thru_hup(struct aproc *p, struct abuf *obuf) { - if (!(p->u.thru.flags & THRU_AUTOQUIT)) + if (!(p->flags & APROC_QUIT)) return; if (LIST_EMPTY(&p->obuflist) || LIST_EMPTY(&p->ibuflist)) aproc_del(p); @@ -339,6 +339,99 @@ ctl_sendmsg(struct aproc *p, struct abuf *ibuf, unsigned char *msg, unsigned len } /* + * send a quarter frame MTC message + */ +void +ctl_qfr(struct aproc *p) +{ + unsigned char buf[2]; + unsigned data; + + switch (p->u.ctl.qfr) { + case 0: + data = p->u.ctl.fr & 0xf; + break; + case 1: + data = p->u.ctl.fr >> 4; + break; + case 2: + data = p->u.ctl.sec & 0xf; + break; + case 3: + data = p->u.ctl.sec >> 4; + break; + case 4: + data = p->u.ctl.min & 0xf; + break; + case 5: + data = p->u.ctl.min >> 4; + break; + case 6: + data = p->u.ctl.hr & 0xf; + break; + case 7: + data = (p->u.ctl.hr >> 4) | (p->u.ctl.fps_id << 1); + /* + * tick messages are sent 2 frames ahead + */ + p->u.ctl.fr += 2; + if (p->u.ctl.fr < p->u.ctl.fps) + break; + p->u.ctl.fr -= p->u.ctl.fps; + p->u.ctl.sec++; + if (p->u.ctl.sec < 60) + break;; + p->u.ctl.sec = 0; + p->u.ctl.min++; + if (p->u.ctl.min < 60) + break; + p->u.ctl.min = 0; + p->u.ctl.hr++; + if (p->u.ctl.hr < 24) + break; + p->u.ctl.hr = 0; + break; + default: + /* NOTREACHED */ + data = 0; + } + buf[0] = 0xf1; + buf[1] = (p->u.ctl.qfr << 4) | data; + p->u.ctl.qfr++; + p->u.ctl.qfr &= 7; + ctl_sendmsg(p, NULL, buf, 2); +} + +/* + * send a full frame MTC message + */ +void +ctl_full(struct aproc *p) +{ + unsigned char buf[10]; + unsigned origin = p->u.ctl.origin; + unsigned fps = p->u.ctl.fps; + + p->u.ctl.hr = (origin / (3600 * MTC_SEC)) % 24; + p->u.ctl.min = (origin / (60 * MTC_SEC)) % 60; + p->u.ctl.sec = (origin / MTC_SEC) % 60; + p->u.ctl.fr = (origin / (MTC_SEC / fps)) % fps; + + buf[0] = 0xf0; + buf[1] = 0x7f; + buf[2] = 0x7f; + buf[3] = 0x01; + buf[4] = 0x01; + buf[5] = p->u.ctl.hr | (p->u.ctl.fps_id << 5); + buf[6] = p->u.ctl.min; + buf[7] = p->u.ctl.sec; + buf[8] = p->u.ctl.fr; + buf[9] = 0xf7; + p->u.ctl.qfr = 0; + ctl_sendmsg(p, NULL, buf, 10); +} + +/* * find the best matching free slot index (ie midi channel). * return -1, if there are no free slots anymore */ @@ -418,14 +511,62 @@ ctl_getidx(struct aproc *p, char *who) } /* + * check that all clients controlled by MMC are ready to start, + * if so, start them all but the caller + */ +int +ctl_trystart(struct aproc *p, int caller) +{ + unsigned i; + struct ctl_slot *s; + + if (p->u.ctl.tstate != CTL_START) { + return 0; + } + for (i = 0, s = p->u.ctl.slot; i < CTL_NSLOT; i++, s++) { + if (!s->ops || i == caller) + continue; + if (s->tstate != CTL_OFF && s->tstate != CTL_START) { + return 0; + } + } + for (i = 0, s = p->u.ctl.slot; i < CTL_NSLOT; i++, s++) { + if (!s->ops || i == caller) + continue; + if (s->tstate == CTL_START) { + s->tstate = CTL_RUN; + s->ops->start(s->arg); + } + } + if (caller >= 0) + p->u.ctl.slot[caller].tstate = CTL_RUN; + p->u.ctl.tstate = CTL_RUN; + p->u.ctl.delta = MTC_SEC * dev_getpos(); + if (dev_rate % (30 * 4 * dev_round)) { + p->u.ctl.fps_id = MTC_FPS_30; + p->u.ctl.fps = 30; + } else if (dev_rate % (25 * 4 * dev_round)) { + p->u.ctl.fps_id = MTC_FPS_25; + p->u.ctl.fps = 25; + } else { + p->u.ctl.fps_id = MTC_FPS_24; + p->u.ctl.fps = 24; + } + ctl_full(p); + return 1; +} + +/* * allocate a new slot and register the given call-backs */ int -ctl_slotnew(struct aproc *p, char *who, struct ctl_ops *ops, void *arg) +ctl_slotnew(struct aproc *p, char *who, struct ctl_ops *ops, void *arg, int tr) { int idx; struct ctl_slot *s; + if (p == NULL) + return -1; idx = ctl_getidx(p, who); if (idx < 0) return -1; @@ -433,6 +574,7 @@ ctl_slotnew(struct aproc *p, char *who, struct ctl_ops *ops, void *arg) s = p->u.ctl.slot + idx; s->ops = ops; s->arg = arg; + s->tstate = tr ? CTL_STOP : CTL_OFF; s->ops->vol(s->arg, s->vol); ctl_slotvol(p, idx, s->vol); return idx; @@ -444,7 +586,50 @@ ctl_slotnew(struct aproc *p, char *who, struct ctl_ops *ops, void *arg) void ctl_slotdel(struct aproc *p, int index) { + unsigned i; + struct ctl_slot *s; + + if (p == NULL) + return; p->u.ctl.slot[index].ops = NULL; + if (!(p->flags & APROC_QUIT)) + return; + for (i = 0, s = p->u.ctl.slot; i < CTL_NSLOT; i++, s++) { + if (s->ops) + return; + } + if (!LIST_EMPTY(&p->obuflist) || !LIST_EMPTY(&p->ibuflist)) + aproc_del(p); +} + +/* + * called at every clock tick by the mixer, delta is positive, unless + * there's an overrun/underrun + */ +void +ctl_ontick(struct aproc *p, int delta) +{ + int qfrlen; + + /* + * don't send ticks before the start signal + */ + if (p->u.ctl.tstate != CTL_RUN) + return; + + p->u.ctl.delta += delta * MTC_SEC; + + /* + * don't send ticks during the count-down + */ + if (p->u.ctl.delta < 0) + return; + + qfrlen = dev_rate * (MTC_SEC / (4 * p->u.ctl.fps)); + while (p->u.ctl.delta >= qfrlen) { + ctl_qfr(p); + p->u.ctl.delta -= qfrlen; + } } /* @@ -457,6 +642,8 @@ ctl_slotvol(struct aproc *p, int slot, unsigned vol) { unsigned char msg[3]; + if (p == NULL) + return; p->u.ctl.slot[slot].vol = vol; msg[0] = MIDI_CTL | slot; msg[1] = MIDI_CTLVOL; @@ -465,6 +652,51 @@ ctl_slotvol(struct aproc *p, int slot, unsigned vol) } /* + * notify the MMC layer that the stream is attempting + * to start. If other streams are not ready, 0 is returned meaning + * that the stream should wait. If other streams are ready, they + * are started, and the caller should start immediately. + */ +int +ctl_slotstart(struct aproc *p, int slot) +{ + struct ctl_slot *s = p->u.ctl.slot + slot; + + if (p == NULL) + return 1; + if (s->tstate == CTL_OFF || p->u.ctl.tstate == CTL_OFF) + return 1; + + /* + * if the server already started (the client missed the + * start rendez-vous) or the server is stopped, then + * tag the client as ``wanting to start'' + */ + s->tstate = CTL_START; + return ctl_trystart(p, slot); +} + +/* + * notify the MMC layer that the stream no longer is trying to + * start (or that it just stopped), meaning that its ``start'' call-back + * shouldn't be called anymore + */ +void +ctl_slotstop(struct aproc *p, int slot) +{ + struct ctl_slot *s = p->u.ctl.slot + slot; + + if (p == NULL) + return; + /* + * tag the stream as not trying to start, + * unless MMC is turned off + */ + if (s->tstate != CTL_OFF) + s->tstate = CTL_STOP; +} + +/* * handle a MIDI event received from ibuf */ void @@ -472,6 +704,7 @@ ctl_ev(struct aproc *p, struct abuf *ibuf) { unsigned chan; struct ctl_slot *slot; + unsigned fps; if ((ibuf->r.midi.msg[0] & MIDI_CMDMASK) == MIDI_CTL && ibuf->r.midi.msg[1] == MIDI_CTLVOL) { chan = ibuf->r.midi.msg[0] & MIDI_CHANMASK; @@ -484,6 +717,54 @@ ctl_ev(struct aproc *p, struct abuf *ibuf) slot->ops->vol(slot->arg, slot->vol); ctl_sendmsg(p, ibuf, ibuf->r.midi.msg, ibuf->r.midi.len); } + if (ibuf->r.midi.idx == 6 && + ibuf->r.midi.msg[0] == 0xf0 && + ibuf->r.midi.msg[1] == 0x7f && /* type is realtime */ + ibuf->r.midi.msg[3] == 0x06 && /* subtype is mmc */ + ibuf->r.midi.msg[5] == 0xf7) { /* subtype is mmc */ + switch (ibuf->r.midi.msg[4]) { + case 0x01: /* mmc stop */ + if (p->u.ctl.tstate == CTL_RUN || + p->u.ctl.tstate == CTL_START) + p->u.ctl.tstate = CTL_STOP; + break; + case 0x02: /* mmc start */ + if (p->u.ctl.tstate == CTL_STOP) { + p->u.ctl.tstate = CTL_START; + (void)ctl_trystart(p, -1); + } + break; + } + } + if (ibuf->r.midi.idx == 13 && + ibuf->r.midi.msg[0] == 0xf0 && + ibuf->r.midi.msg[1] == 0x7f && + ibuf->r.midi.msg[3] == 0x06 && + ibuf->r.midi.msg[4] == 0x44 && + ibuf->r.midi.msg[5] == 0x06 && + ibuf->r.midi.msg[6] == 0x01 && + ibuf->r.midi.msg[12] == 0xf7) { + switch (ibuf->r.midi.msg[7] >> 5) { + case MTC_FPS_24: + fps = 24; + break; + case MTC_FPS_25: + fps = 25; + break; + case MTC_FPS_30: + fps = 30; + break; + default: + p->u.ctl.origin = 0; + return; + } + p->u.ctl.origin = + (ibuf->r.midi.msg[7] & 0x1f) * 3600 * MTC_SEC + + ibuf->r.midi.msg[8] * 60 * MTC_SEC + + ibuf->r.midi.msg[9] * MTC_SEC + + ibuf->r.midi.msg[10] * (MTC_SEC / fps) + + ibuf->r.midi.msg[11] * (MTC_SEC / 100 / fps); + } } int @@ -540,11 +821,33 @@ ctl_out(struct aproc *p, struct abuf *obuf) void ctl_eof(struct aproc *p, struct abuf *ibuf) { + unsigned i; + struct ctl_slot *s; + + if (!(p->flags & APROC_QUIT)) + return; + for (i = 0, s = p->u.ctl.slot; i < CTL_NSLOT; i++, s++) { + if (s->ops) + return; + } + if (!LIST_EMPTY(&p->obuflist) || !LIST_EMPTY(&p->ibuflist)) + aproc_del(p); } void ctl_hup(struct aproc *p, struct abuf *obuf) { + unsigned i; + struct ctl_slot *s; + + if (!(p->flags & APROC_QUIT)) + return; + for (i = 0, s = p->u.ctl.slot; i < CTL_NSLOT; i++, s++) { + if (s->ops) + return; + } + if (!LIST_EMPTY(&p->obuflist) || !LIST_EMPTY(&p->ibuflist)) + aproc_del(p); } void @@ -583,13 +886,14 @@ ctl_new(char *name) p = aproc_new(&ctl_ops, name); p->u.ctl.serial = 0; + p->u.ctl.tstate = CTL_STOP; for (i = 0, s = p->u.ctl.slot; i < CTL_NSLOT; i++, s++) { p->u.ctl.slot[i].unit = i; p->u.ctl.slot[i].ops = NULL; p->u.ctl.slot[i].vol = MIDI_MAXCTL; + p->u.ctl.slot[i].tstate = CTL_OFF; p->u.ctl.slot[i].serial = p->u.ctl.serial++; strlcpy(p->u.ctl.slot[i].name, "unknown", CTL_NAMEMAX); } return p; } - diff --git a/usr.bin/aucat/midi.h b/usr.bin/aucat/midi.h index c92e284ec93..54f73d4ae0b 100644 --- a/usr.bin/aucat/midi.h +++ b/usr.bin/aucat/midi.h @@ -1,4 +1,4 @@ -/* $OpenBSD: midi.h,v 1.4 2009/10/27 22:41:03 ratchov Exp $ */ +/* $OpenBSD: midi.h,v 1.5 2009/11/03 21:31:37 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -20,8 +20,11 @@ struct aproc *thru_new(char *); struct aproc *ctl_new(char *); -int ctl_slotnew(struct aproc *, char *, struct ctl_ops *, void *); +int ctl_slotnew(struct aproc *, char *, struct ctl_ops *, void *, int); void ctl_slotdel(struct aproc *, int); void ctl_slotvol(struct aproc *, int, unsigned); +int ctl_slotstart(struct aproc *, int); +void ctl_slotstop(struct aproc *, int); +void ctl_ontick(struct aproc *, int); #endif /* !defined(MIDI_H) */ diff --git a/usr.bin/aucat/opt.c b/usr.bin/aucat/opt.c index 8eb267a6a2e..83dd689bf8e 100644 --- a/usr.bin/aucat/opt.c +++ b/usr.bin/aucat/opt.c @@ -1,4 +1,4 @@ -/* $OpenBSD: opt.c,v 1.2 2009/09/27 11:51:20 ratchov Exp $ */ +/* $OpenBSD: opt.c,v 1.3 2009/11/03 21:31:37 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -24,7 +24,8 @@ struct optlist opt_list = SLIST_HEAD_INITIALIZER(&opt_list); void -opt_new(char *name, struct aparams *wpar, struct aparams *rpar, int maxweight) +opt_new(char *name, + struct aparams *wpar, struct aparams *rpar, int maxweight, int mmc) { struct opt *o; unsigned len; @@ -53,6 +54,7 @@ opt_new(char *name, struct aparams *wpar, struct aparams *rpar, int maxweight) o->wpar = *wpar; o->rpar = *rpar; o->maxweight = maxweight; + o->mmc = mmc; SLIST_INSERT_HEAD(&opt_list, o, entry); } diff --git a/usr.bin/aucat/opt.h b/usr.bin/aucat/opt.h index 1d412293cb6..081f1763680 100644 --- a/usr.bin/aucat/opt.h +++ b/usr.bin/aucat/opt.h @@ -1,4 +1,4 @@ -/* $OpenBSD: opt.h,v 1.1 2009/07/25 08:44:27 ratchov Exp $ */ +/* $OpenBSD: opt.h,v 1.2 2009/11/03 21:31:37 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -27,11 +27,12 @@ struct opt { int maxweight; /* max dynamic range for clients */ struct aparams wpar; /* template for clients write params */ struct aparams rpar; /* template for clients read params */ + int mmc; /* true if MMC control enabled */ }; SLIST_HEAD(optlist,opt); -void opt_new(char *, struct aparams *, struct aparams *, int); +void opt_new(char *, struct aparams *, struct aparams *, int, int); struct opt *opt_byname(char *); #endif /* !defined(OPT_H) */ diff --git a/usr.bin/aucat/sock.c b/usr.bin/aucat/sock.c index ec5a87cf31e..c49f16dcbe7 100644 --- a/usr.bin/aucat/sock.c +++ b/usr.bin/aucat/sock.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sock.c,v 1.34 2009/10/27 22:41:03 ratchov Exp $ */ +/* $OpenBSD: sock.c,v 1.35 2009/11/03 21:31:37 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -54,9 +54,11 @@ struct fileops sock_ops = { void sock_setvol(void *, unsigned); +void sock_startreq(void *); struct ctl_ops ctl_sockops = { sock_setvol, + sock_startreq }; void @@ -300,10 +302,12 @@ sock_freebuf(struct sock *f) struct abuf *rbuf, *wbuf; f->pstate = SOCK_INIT; + wbuf = LIST_FIRST(&f->pipe.file.wproc->ibuflist); rbuf = LIST_FIRST(&f->pipe.file.rproc->obuflist); + if (rbuf || wbuf) + ctl_slotstop(dev_midi, f->slot); if (rbuf) abuf_eof(rbuf); - wbuf = LIST_FIRST(&f->pipe.file.wproc->ibuflist); if (wbuf) abuf_hup(wbuf); f->tickpending = 0; @@ -328,7 +332,7 @@ sock_allocbuf(struct sock *f) f->delta = 0; f->tickpending = 0; f->pstate = SOCK_START; - if (!(f->mode & AMSG_PLAY)) + if (!(f->mode & AMSG_PLAY) && ctl_slotstart(dev_midi, f->slot)) (void)sock_attach(f, 0); } @@ -350,6 +354,17 @@ sock_setvol(void *arg, unsigned vol) } /* + * Attach the stream. Callback invoked when MMC start + */ +void +sock_startreq(void *arg) +{ + struct sock *f = (struct sock *)arg; + + (void)sock_attach(f, 0); +} + +/* * Attach play and/or record buffers to dev_mix and/or dev_sub. */ int @@ -394,8 +409,10 @@ sock_reset(struct sock *f) { switch (f->pstate) { case SOCK_START: - (void)sock_attach(f, 1); - f->pstate = SOCK_RUN; + if (ctl_slotstart(dev_midi, f->slot)) { + (void)sock_attach(f, 1); + f->pstate = SOCK_RUN; + } /* PASSTHROUGH */ case SOCK_RUN: sock_freebuf(f); @@ -599,6 +616,8 @@ sock_setpar(struct sock *f) return 0; } f->xrun = p->xrun; + if (f->opt->mmc && f->xrun == AMSG_IGNORE) + f->xrun = AMSG_SYNC; } if (AMSG_ISSET(p->bufsz)) { /* @@ -673,6 +692,8 @@ sock_hello(struct sock *f) f->wpar = f->opt->wpar; if (dev_mix) f->rpar = f->opt->rpar; + if (f->opt->mmc) + f->xrun = AMSG_SYNC; if ((p->proto & ~(AMSG_PLAY | AMSG_REC)) != 0 || (p->proto & (AMSG_PLAY | AMSG_REC)) == 0) { return 0; @@ -691,7 +712,9 @@ sock_hello(struct sock *f) f->mode |= AMSG_REC; } if (dev_midi) { - f->slot = ctl_slotnew(dev_midi, p->who, &ctl_sockops, f); + f->slot = ctl_slotnew(dev_midi, + p->who, &ctl_sockops, f, + f->opt->mmc); if (f->slot < 0) { return 0; } @@ -745,7 +768,8 @@ sock_execmsg(struct sock *f) aproc_del(f->pipe.file.rproc); return 0; } - if (f->pstate == SOCK_START) + if (f->pstate == SOCK_START && + ctl_slotstart(dev_midi, f->slot)) (void)sock_attach(f, 1); sock_freebuf(f); AMSG_INIT(m); @@ -943,7 +967,10 @@ sock_read(struct sock *f) f->rstate = SOCK_RMSG; f->rtodo = sizeof(struct amsg); } - if (f->pstate == SOCK_START) + /* + * XXX: have to way that the buffer is full before starting + */ + if (f->pstate == SOCK_START && ctl_slotstart(dev_midi, f->slot)) (void)sock_attach(f, 0); break; case SOCK_RRET: |