diff options
35 files changed, 2447 insertions, 278 deletions
diff --git a/include/sndio.h b/include/sndio.h index bf68e9c8515..c7a48012728 100644 --- a/include/sndio.h +++ b/include/sndio.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sndio.h,v 1.1 2009/04/21 19:14:33 ratchov Exp $ */ +/* $OpenBSD: sndio.h,v 1.2 2009/07/25 08:44:26 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -23,6 +23,7 @@ * private ``handle'' structure */ struct sio_hdl; +struct mio_hdl; /* * parameters of a full-duplex stream @@ -82,6 +83,8 @@ struct sio_cap { */ #define SIO_PLAY 1 #define SIO_REC 2 +#define MIO_OUT 4 +#define MIO_IN 8 /* * maximum size of the encording string (the longest possible @@ -145,6 +148,15 @@ int sio_eof(struct sio_hdl *); int sio_setvol(struct sio_hdl *, unsigned); void sio_onvol(struct sio_hdl *, void (*)(void *, unsigned), void *); +struct mio_hdl *mio_open(char *, unsigned, int); +void mio_close(struct mio_hdl *); +size_t mio_write(struct mio_hdl *, void *, size_t); +size_t mio_read(struct mio_hdl *, void *, size_t); +int mio_nfds(struct mio_hdl *); +int mio_pollfd(struct mio_hdl *, struct pollfd *, int); +int mio_revents(struct mio_hdl *, struct pollfd *); +int mio_eof(struct mio_hdl *); + #ifdef __cplusplus } #endif diff --git a/lib/libsndio/Makefile b/lib/libsndio/Makefile index 42c2166a441..82251017210 100644 --- a/lib/libsndio/Makefile +++ b/lib/libsndio/Makefile @@ -1,8 +1,8 @@ -# $OpenBSD: Makefile,v 1.3 2009/04/21 19:16:26 deraadt Exp $ +# $OpenBSD: Makefile,v 1.4 2009/07/25 08:44:26 ratchov Exp $ LIB= sndio -MAN= sio_open.3 -SRCS= aucat.c sun.c sndio.c +MAN= sio_open.3 mio_open.3 sndio.7 +SRCS= aucat.c sun.c sndio.c mio_rmidi.c mio_thru.c mio.c CFLAGS+=-Wall -Wstrict-prototypes -Werror -Wundef -DDEBUG \ -I${.CURDIR} -I${.CURDIR}/../../usr.bin/aucat @@ -22,6 +22,13 @@ MLINKS = \ sio_open.3 sio_eof.3 \ sio_open.3 sio_setvol.3 \ sio_open.3 sio_onvol.3 \ - sio_open.3 sio_initpar.3 + sio_open.3 sio_initpar.3 \ + mio_open.3 mio_close.3 \ + mio_open.3 mio_read.3 \ + mio_open.3 mio_write.3 \ + mio_open.3 mio_nfds.3 \ + mio_open.3 mio_pollfd.3 \ + mio_open.3 mio_revents.3 \ + mio_open.3 mio_eof.3 .include <bsd.lib.mk> diff --git a/lib/libsndio/aucat.c b/lib/libsndio/aucat.c index cefa0443f0d..5e249f35961 100644 --- a/lib/libsndio/aucat.c +++ b/lib/libsndio/aucat.c @@ -1,4 +1,4 @@ -/* $OpenBSD: aucat.c,v 1.21 2009/05/16 12:10:52 ratchov Exp $ */ +/* $OpenBSD: aucat.c,v 1.22 2009/07/25 08:44:26 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -167,22 +167,34 @@ aucat_runmsg(struct aucat_hdl *hdl) } struct sio_hdl * -sio_open_aucat(char *path, unsigned mode, int nbio) +sio_open_aucat(char *str, unsigned mode, int nbio) { extern char *__progname; int s; + char unit[4], *sep, *opt; struct aucat_hdl *hdl; struct sockaddr_un ca; socklen_t len = sizeof(struct sockaddr_un); uid_t uid; - if (path == NULL) - path = SIO_AUCAT_PATH; + sep = strchr(str, '.'); + if (sep == NULL) { + opt = "default"; + strlcpy(unit, str, sizeof(unit)); + } else { + opt = sep + 1; + if (sep - str >= sizeof(unit)) { + DPRINTF("sio_open_aucat: %s: too long\n", str); + return NULL; + } + strlcpy(unit, str, opt - str); + } + DPRINTF("sio_open_aucat: trying %s -> %s.%s\n", str, unit, opt); uid = geteuid(); - if (strchr(path, '/') != NULL) + if (strchr(str, '/') != NULL) return NULL; snprintf(ca.sun_path, sizeof(ca.sun_path), - "/tmp/aucat-%u/%s", uid, path); + "/tmp/aucat-%u/softaudio%s", uid, unit); ca.sun_family = AF_UNIX; hdl = malloc(sizeof(struct aucat_hdl)); @@ -196,6 +208,7 @@ sio_open_aucat(char *path, unsigned mode, int nbio) while (connect(s, (struct sockaddr *)&ca, len) < 0) { if (errno == EINTR) continue; + DPERROR("sio_open_aucat: connect"); goto bad_connect; } if (fcntl(s, F_SETFD, FD_CLOEXEC) < 0) { @@ -222,6 +235,8 @@ sio_open_aucat(char *path, unsigned mode, int nbio) hdl->wmsg.u.hello.proto |= AMSG_REC; strlcpy(hdl->wmsg.u.hello.who, __progname, sizeof(hdl->wmsg.u.hello.who)); + strlcpy(hdl->wmsg.u.hello.opt, opt, + sizeof(hdl->wmsg.u.hello.opt)); hdl->wtodo = sizeof(struct amsg); if (!aucat_wmsg(hdl)) goto bad_connect; diff --git a/lib/libsndio/mio.c b/lib/libsndio/mio.c new file mode 100644 index 00000000000..b03582d1635 --- /dev/null +++ b/lib/libsndio/mio.c @@ -0,0 +1,184 @@ +/* $OpenBSD: mio.c,v 1.1 2009/07/25 08:44:26 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 <sys/param.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <errno.h> +#include <fcntl.h> +#include <poll.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> + +#include "mio_priv.h" + +#ifdef DEBUG +/* + * debug level, -1 means uninitialized + */ +int mio_debug = -1; +#endif + +struct mio_hdl * +mio_open(char *str, unsigned mode, int nbio) +{ + static char prefix_midithru[] = "midithru"; + static char prefix_rmidi[] = "rmidi"; + struct mio_hdl *hdl; + struct stat sb; + char *sep, buf[4]; + int len; +#ifdef DEBUG + char *dbg; + + if (mio_debug < 0) { + dbg = issetugid() ? NULL : getenv("MIO_DEBUG"); + if (!dbg || sscanf(dbg, "%u", &mio_debug) != 1) + mio_debug = 0; + } +#endif + if ((mode & (MIO_OUT | MIO_IN)) == 0) + return NULL; + if (str == NULL && !issetugid()) + str = getenv("MIDIDEVICE"); + if (str == NULL) { + hdl = mio_open_thru("0", mode, nbio); + if (hdl != NULL) + return hdl; + return mio_open_rmidi("0", mode, nbio); + } + sep = strchr(str, ':'); + if (sep == NULL) { + /* + * try legacy "/dev/rmidioxxx" device name + */ + if (stat(str, &sb) < 0 || !S_ISCHR(sb.st_mode)) { + DPRINTF("mio_open: %s: missing ':' separator\n", str); + return NULL; + } + snprintf(buf, sizeof(buf), "%u", minor(sb.st_rdev)); + return mio_open_rmidi(buf, mode, nbio); + } + if (sep == str) { + /* + * legacy "/dev/rmidixxx" device name + */ + if (stat(str, &sb) < 0) { + DPERROR("mio_open: stat"); + return NULL; + } + if (!S_ISCHR(sb.st_mode)) { + DPRINTF("mio_open: %s: not a char dev\n", str); + return NULL; + } + snprintf(buf, sizeof(buf), "%u", minor(sb.st_rdev)); + return mio_open_rmidi(buf, mode, nbio); + } + + len = sep - str; + if (len == strlen(prefix_midithru) && + memcmp(str, prefix_midithru, len) == 0) + return mio_open_thru(sep + 1, mode, nbio); + if (len == strlen(prefix_rmidi) && + memcmp(str, prefix_rmidi, len) == 0) + return mio_open_rmidi(sep + 1, mode, nbio); + DPRINTF("mio_open: %s: unknown device type\n", str); + return NULL; +} + +void +mio_create(struct mio_hdl *hdl, struct mio_ops *ops, unsigned mode, int nbio) +{ + hdl->ops = ops; + hdl->mode = mode; + hdl->nbio = nbio; + hdl->eof = 0; +} + +void +mio_close(struct mio_hdl *hdl) +{ + return hdl->ops->close(hdl); +} + +size_t +mio_read(struct mio_hdl *hdl, void *buf, size_t len) +{ + if (hdl->eof) { + DPRINTF("mio_read: eof\n"); + return 0; + } + if (!(hdl->mode & MIO_IN)) { + DPRINTF("mio_read: not input device\n"); + hdl->eof = 1; + return 0; + } + if (len == 0) { + DPRINTF("mio_read: zero length read ignored\n"); + return 0; + } + return hdl->ops->read(hdl, buf, len); +} + +size_t +mio_write(struct mio_hdl *hdl, void *buf, size_t len) +{ + if (hdl->eof) { + DPRINTF("mio_write: eof\n"); + return 0; + } + if (!(hdl->mode & MIO_OUT)) { + DPRINTF("mio_write: not output device\n"); + hdl->eof = 1; + return 0; + } + if (len == 0) { + DPRINTF("mio_write: zero length write ignored\n"); + return 0; + } + return hdl->ops->write(hdl, buf, len); +} + +int +mio_nfds(struct mio_hdl *hdl) +{ + return 1; +} + +int +mio_pollfd(struct mio_hdl *hdl, struct pollfd *pfd, int events) +{ + if (hdl->eof) + return 0; + return hdl->ops->pollfd(hdl, pfd, events); +} + +int +mio_revents(struct mio_hdl *hdl, struct pollfd *pfd) +{ + if (hdl->eof) + return POLLHUP; + return hdl->ops->revents(hdl, pfd); +} + +int +mio_eof(struct mio_hdl *hdl) +{ + return hdl->eof; +} diff --git a/lib/libsndio/mio_open.3 b/lib/libsndio/mio_open.3 new file mode 100644 index 00000000000..d8ac43dc04c --- /dev/null +++ b/lib/libsndio/mio_open.3 @@ -0,0 +1,252 @@ +.\" $OpenBSD: mio_open.3,v 1.1 2009/07/25 08:44:26 ratchov Exp $ +.\" +.\" Copyright (c) 2007 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. +.\" +.Dd $Mdocdate: July 25 2009 $ +.Dt MIO_OPEN 3 +.Os +.Sh NAME +.Nm mio_open , +.Nm mio_close , +.Nm mio_read , +.Nm mio_write , +.Nm mio_nfds , +.Nm mio_pollfd , +.Nm mio_revents , +.Nm mio_eof +.Nd interface to MIDI streams +.Sh SYNOPSIS +.Fd #include <sndio.h> +.Ft "struct mio_hdl *" +.Fn "mio_open" "char *name" "unsigned mode" "int nbio_flag" +.Ft "void" +.Fn "mio_close" "struct mio_hdl *hdl" +.Ft "size_t" +.Fn "mio_read" "struct mio_hdl *hdl" "void *addr" "size_t nbytes" +.Ft "size_t" +.Fn "mio_write" "struct mio_hdl *hdl" "void *addr" "size_t nbytes" +.Ft "int" +.Fn "mio_nfds" "struct mio_hdl *hdl" +.Ft "int" +.Fn "mio_pollfd" "struct mio_hdl *hdl" "struct pollfd *pfd" "int events" +.Ft "int" +.Fn "mio_revents" "struct mio_hdl *hdl" "struct pollfd *pfd" +.Ft "int" +.Fn "mio_eof" "struct mio_hdl *hdl" +.Sh DESCRIPTION +The +.Nm sndio +library allows user processes to access +.Xr midi 4 +hardware and +.Xr midicat 1 +MIDI thru boxes in a uniform way. +.Ss Opening and closing an MIDI stream +First the application must call the +.Fn mio_open +function to obtain a handle representing the newly created stream; +later it will be passed as the +.Ar hdl +argument of most other functions. +The +.Fn mio_open +function tries to connect to the +.Xr midicat 1 +software MIDI thru box or to use the +.Xr midi 4 +hardware device. +The +.Ar name +parameter gives the device string discussed in +.Xr sndio 7 . +If the program is using a single device and is providing no device chooser, +it should be set to NULL to allow the user to select it using the +.Ev MIDIDEVICE +environment variable. +.Pp +The +.Ar mode +parameter gives the direction of the stream. +The following are supported: +.Bl -tag -width "MIO_OUT | MIO_IN" +.It MIO_OUT +The stream is output-only; data written to the stream will be sent +to the hardware or other programs. +.It MIO_IN +The stream is input-only; received data from the hardware or +other programs must be read from the stream. +.It MIO_IN | MIO_OUT +The stream sends and receives data. +This mode should be used rather using twice +.Fn mio_open . +.El +.Pp +If the +.Ar nbio_flag +argument is true (i.e. non-zero), then the +.Fn mio_read +and +.Fn mio_write +functions (see below) will be non-blocking. +.Pp +The +.Fn mio_close +function closes the stream and frees all allocated resources +associated with the +.Nm libsndio +handle. +.Ss Sending and receiving data +When input mode is selected, the +.Fn mio_read +function must be called to retrieve received data; it must be called +often enough to ensure that internal buffers will not overrun. +It will store at most +.Ar nbytes +bytes at the +.Ar addr +location and return the number of bytes stored. +Unless the +.Ar nbio_flag +flag is set, it will block until data becomes available and +will return zero only on error. +.Pp +When output mode is selected, the +.Fn mio_write +function can be called to provide data to transmit. +Unless the +.Ar nbio_flag +is set, +.Fn mio_write +will block until the requested amount of data is written. +.Ss Non-blocking mode operation +If the +.Ar nbio_flag +is set on +.Fn mio_open , +then the +.Fn mio_read +and +.Fn mio_write +functions will never block; if no data is available, they will +return zero immediately. +.Pp +To avoid busy loops when non-blocking mode is used, the +.Xr poll 2 +system call can be used to check if data can be +read from or written to the stream. +The +.Fn mio_pollfd +function fills the array +.Ar pfd +of +.Va pollfd +structures, used by +.Xr poll 2 , +with +.Ar events ; +the latter is a bit-mask of +.Va POLLIN +and +.Va POLLOUT +constants; refer to +.Xr poll 2 +for more details. +.Fn mio_pollfd +returns the number of +.Va pollfd +structures filled. +The +.Fn mio_revents +function returns the bit-mask set by +.Xr poll 2 +in the +.Va pfd +array of +.Va pollfd +structures. +If +.Va POLLIN +is set, +.Fn mio_read +can be called without blocking. +If +.Va POLLOUT +is set, +.Fn mio_write +can be called without blocking. +POLLHUP may be set if an error occurs, even if +it is not selected with +.Fn mio_pollfd . +.Pp +The +.Fn mio_nfds +function returns the number of +.Va pollfd +structures the caller must preallocate in order to be sure +that +.Fn mio_pollfd +will never overrun. +.Ss Error handling +Errors related to the MIDI subsystem +(like hardware errors or dropped connections) and +programming errors (such as a call to +.Fn mio_read +on a play-only stream) are considered fatal. +Once an error occurs, all functions which take a +.Va mio_hdl +argument, except +.Fn mio_close +and +.Fn mio_eof , +stop working (i.e. always return 0). +.Pp +The +.Fn mio_eof +function can be used at any stage; +it returns 0 if there's no pending error, and a non-zero +value if there's an error. +.Sh RETURN VALUES +The +.Fn mio_open +function returns the newly created handle on success or NULL +on failure. +The +.Fn mio_pollfd +function returns 1 on success and 0 on failure. +The +.Fn mio_read +and +.Fn mio_write +functions return the number of bytes transferred. +.Sh ENVIRONMENT +.Bl -tag -width "MIO_DEBUGXXX" -compact +.It Ev MIO_DEBUG +The debug level: +may be a value between 0 and 2. +.El +.Sh FILES +.Bl -tag -width "/tmp/aucat-<uid>/midithru0" -compact +.It Pa /tmp/aucat-<uid>/midithru0 +Default path to +.Xr midicat 1 +socket to connect to. +.It Pa /dev/rmidiX +.Xr midi 4 +devices. +.El +.Sh SEE ALSO +.Xr midicat 1 , +.Xr midi 4 , +.Xr sndio 7 diff --git a/lib/libsndio/mio_priv.h b/lib/libsndio/mio_priv.h new file mode 100644 index 00000000000..240267333ba --- /dev/null +++ b/lib/libsndio/mio_priv.h @@ -0,0 +1,69 @@ +/* $OpenBSD: mio_priv.h,v 1.1 2009/07/25 08:44:26 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. + */ +#ifndef MIO_PRIV_H +#define MIO_PRIV_H + +#include <sys/param.h> +#include "sndio.h" + +#ifdef DEBUG +#define DPRINTF(...) \ + do { \ + if (mio_debug > 0) \ + fprintf(stderr, __VA_ARGS__); \ + } while(0) +#define DPERROR(s) \ + do { \ + if (mio_debug > 0) \ + perror(s); \ + } while(0) +#else +#define DPRINTF(...) do {} while(0) +#define DPERROR(s) do {} while(0) +#endif + +/* + * private ``handle'' structure + */ +struct mio_hdl { + struct mio_ops *ops; + unsigned mode; /* MIO_PLAY | MIO_REC */ + int nbio; /* true if non-blocking io */ + int eof; /* true if error occured */ +}; + +/* + * operations every device should support + */ +struct mio_ops { + void (*close)(struct mio_hdl *); + size_t (*write)(struct mio_hdl *, void *, size_t); + size_t (*read)(struct mio_hdl *, void *, size_t); + int (*pollfd)(struct mio_hdl *, struct pollfd *, int); + int (*revents)(struct mio_hdl *, struct pollfd *); +}; + +struct mio_hdl *mio_open_rmidi(char *, unsigned, int); +struct mio_hdl *mio_open_thru(char *, unsigned, int); +void mio_create(struct mio_hdl *, struct mio_ops *, unsigned, int); +void mio_destroy(struct mio_hdl *); + +#ifdef DEBUG +extern int mio_debug; +#endif + +#endif /* !defined(MIO_PRIV_H) */ diff --git a/lib/libsndio/mio_rmidi.c b/lib/libsndio/mio_rmidi.c new file mode 100644 index 00000000000..9aa460633e4 --- /dev/null +++ b/lib/libsndio/mio_rmidi.c @@ -0,0 +1,158 @@ +/* $OpenBSD: mio_rmidi.c,v 1.1 2009/07/25 08:44:26 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 <sys/types.h> +#include <sys/stat.h> +#include <limits.h> +#include <errno.h> +#include <fcntl.h> +#include <poll.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "mio_priv.h" + +#define RMIDI_PATH "/dev/rmidi0" + +struct rmidi_hdl { + struct mio_hdl mio; + int fd; +}; + +static void rmidi_close(struct mio_hdl *); +static size_t rmidi_read(struct mio_hdl *, void *, size_t); +static size_t rmidi_write(struct mio_hdl *, void *, size_t); +static int rmidi_pollfd(struct mio_hdl *, struct pollfd *, int); +static int rmidi_revents(struct mio_hdl *, struct pollfd *); + +static struct mio_ops rmidi_ops = { + rmidi_close, + rmidi_write, + rmidi_read, + rmidi_pollfd, + rmidi_revents, +}; + +struct mio_hdl * +mio_open_rmidi(char *str, unsigned mode, int nbio) +{ + int fd, flags; + struct rmidi_hdl *hdl; + char path[PATH_MAX]; + + hdl = malloc(sizeof(struct rmidi_hdl)); + if (hdl == NULL) + return NULL; + mio_create(&hdl->mio, &rmidi_ops, mode, nbio); + + snprintf(path, sizeof(path), "/dev/rmidi%s", str); + if (mode == (MIO_OUT | MIO_IN)) + flags = O_RDWR; + else + flags = (mode & MIO_OUT) ? O_WRONLY : O_RDONLY; + + while ((fd = open(path, flags | O_NONBLOCK)) < 0) { + if (errno == EINTR) + continue; + DPERROR(path); + goto bad_free; + } + if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) { + DPERROR("FD_CLOEXEC"); + goto bad_close; + } + hdl->fd = fd; + return (struct mio_hdl *)hdl; + bad_close: + while (close(hdl->fd) < 0 && errno == EINTR) + ; /* retry */ + bad_free: + free(hdl); + return NULL; +} + +static void +rmidi_close(struct mio_hdl *sh) +{ + struct rmidi_hdl *hdl = (struct rmidi_hdl *)sh; + int rc; + + do { + rc = close(hdl->fd); + } while (rc < 0 && errno == EINTR); + free(hdl); +} + +static size_t +rmidi_read(struct mio_hdl *sh, void *buf, size_t len) +{ + struct rmidi_hdl *hdl = (struct rmidi_hdl *)sh; + ssize_t n; + + while ((n = read(hdl->fd, buf, len)) < 0) { + if (errno == EINTR) + continue; + if (errno != EAGAIN) { + DPERROR("rmidi_read: read"); + hdl->mio.eof = 1; + } + return 0; + } + if (n == 0) { + DPRINTF("rmidi_read: eof\n"); + hdl->mio.eof = 1; + return 0; + } + return n; +} + +static size_t +rmidi_write(struct mio_hdl *sh, void *buf, size_t len) +{ + struct rmidi_hdl *hdl = (struct rmidi_hdl *)sh; + ssize_t n; + + while ((n = write(hdl->fd, buf, len)) < 0) { + if (errno == EINTR) + continue; + if (errno != EAGAIN) { + DPERROR("rmidi_write: write"); + hdl->mio.eof = 1; + return 0; + } + return 0; + } + return n; +} + +static int +rmidi_pollfd(struct mio_hdl *sh, struct pollfd *pfd, int events) +{ + struct rmidi_hdl *hdl = (struct rmidi_hdl *)sh; + + pfd->fd = hdl->fd; + pfd->events = events; + return 1; +} + +int +rmidi_revents(struct mio_hdl *sh, struct pollfd *pfd) +{ + return pfd->revents; +} diff --git a/lib/libsndio/mio_thru.c b/lib/libsndio/mio_thru.c new file mode 100644 index 00000000000..d3e2953cf18 --- /dev/null +++ b/lib/libsndio/mio_thru.c @@ -0,0 +1,213 @@ +/* $OpenBSD: mio_thru.c,v 1.1 2009/07/25 08:44:27 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 <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <errno.h> +#include <fcntl.h> +#include <poll.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> + +#include "amsg.h" +#include "mio_priv.h" + +#define THRU_SOCKET "midithru" + +struct thru_hdl { + struct mio_hdl mio; + int fd; +}; + +static void thru_close(struct mio_hdl *); +static size_t thru_read(struct mio_hdl *, void *, size_t); +static size_t thru_write(struct mio_hdl *, void *, size_t); +static int thru_pollfd(struct mio_hdl *, struct pollfd *, int); +static int thru_revents(struct mio_hdl *, struct pollfd *); + +static struct mio_ops thru_ops = { + thru_close, + thru_write, + thru_read, + thru_pollfd, + thru_revents, +}; + +struct mio_hdl * +mio_open_thru(char *str, unsigned mode, int nbio) +{ + extern char *__progname; + struct amsg msg; + int s, n, todo; + unsigned char *data; + struct thru_hdl *hdl; + struct sockaddr_un ca; + socklen_t len = sizeof(struct sockaddr_un); + uid_t uid; + + uid = geteuid(); + if (strchr(str, '/') != NULL) + return NULL; + snprintf(ca.sun_path, sizeof(ca.sun_path), + "/tmp/aucat-%u/midithru%s", uid, str); + ca.sun_family = AF_UNIX; + + hdl = malloc(sizeof(struct thru_hdl)); + if (hdl == NULL) + return NULL; + mio_create(&hdl->mio, &thru_ops, mode, nbio); + + s = socket(AF_UNIX, SOCK_STREAM, 0); + if (s < 0) + goto bad_free; + while (connect(s, (struct sockaddr *)&ca, len) < 0) { + if (errno == EINTR) + continue; + DPERROR("mio_open_thru: connect"); + goto bad_connect; + } + if (fcntl(s, F_SETFD, FD_CLOEXEC) < 0) { + DPERROR("FD_CLOEXEC"); + goto bad_connect; + } + hdl->fd = s; + + /* + * say hello to server + */ + AMSG_INIT(&msg); + msg.cmd = AMSG_HELLO; + msg.u.hello.proto = 0; + if (mode & MIO_IN) + msg.u.hello.proto |= AMSG_MIDIIN; + if (mode & MIO_OUT) + msg.u.hello.proto |= AMSG_MIDIOUT; + strlcpy(msg.u.hello.who, __progname, sizeof(msg.u.hello.who)); + n = write(s, &msg, sizeof(struct amsg)); + if (n < 0) { + DPERROR("mio_open_thru"); + goto bad_connect; + } + if (n != sizeof(struct amsg)) { + DPRINTF("mio_open_thru: short write\n"); + goto bad_connect; + } + todo = sizeof(struct amsg); + data = (unsigned char *)&msg; + while (todo > 0) { + n = read(s, data, todo); + if (n < 0) { + DPERROR("mio_open_thru"); + goto bad_connect; + } + if (n == 0) { + DPRINTF("mio_open_thru: eof\n"); + goto bad_connect; + } + todo -= n; + data += n; + } + if (msg.cmd != AMSG_ACK) { + DPRINTF("mio_open_thru: proto error\n"); + goto bad_connect; + } + if (nbio && fcntl(hdl->fd, F_SETFL, O_NONBLOCK) < 0) { + DPERROR("mio_open_thru: fcntl(NONBLOCK)"); + goto bad_connect; + } + return (struct mio_hdl *)hdl; + bad_connect: + while (close(s) < 0 && errno == EINTR) + ; /* retry */ + bad_free: + free(hdl); + return NULL; +} + +static void +thru_close(struct mio_hdl *sh) +{ + struct thru_hdl *hdl = (struct thru_hdl *)sh; + int rc; + + do { + rc = close(hdl->fd); + } while (rc < 0 && errno == EINTR); + free(hdl); +} + +static size_t +thru_read(struct mio_hdl *sh, void *buf, size_t len) +{ + struct thru_hdl *hdl = (struct thru_hdl *)sh; + ssize_t n; + + while ((n = read(hdl->fd, buf, len)) < 0) { + if (errno == EINTR) + continue; + if (errno != EAGAIN) { + DPERROR("thru_read: read"); + hdl->mio.eof = 1; + } + return 0; + } + if (n == 0) { + DPRINTF("thru_read: eof\n"); + hdl->mio.eof = 1; + return 0; + } + return n; +} + +static size_t +thru_write(struct mio_hdl *sh, void *buf, size_t len) +{ + struct thru_hdl *hdl = (struct thru_hdl *)sh; + ssize_t n; + + while ((n = write(hdl->fd, buf, len)) < 0) { + if (errno == EINTR) + continue; + if (errno != EAGAIN) { + DPERROR("thru_write: write"); + hdl->mio.eof = 1; + return 0; + } + return 0; + } + return n; +} + +static int +thru_pollfd(struct mio_hdl *sh, struct pollfd *pfd, int events) +{ + struct thru_hdl *hdl = (struct thru_hdl *)sh; + + pfd->fd = hdl->fd; + pfd->events = events; + return 1; +} + +int +thru_revents(struct mio_hdl *sh, struct pollfd *pfd) +{ + return pfd->revents; +} diff --git a/lib/libsndio/shlib_version b/lib/libsndio/shlib_version index 83a67c373cc..b25072f4e52 100644 --- a/lib/libsndio/shlib_version +++ b/lib/libsndio/shlib_version @@ -1,2 +1,2 @@ major=3 -minor=2 +minor=3 diff --git a/lib/libsndio/sio_open.3 b/lib/libsndio/sio_open.3 index 78658d12c2b..744a556d009 100644 --- a/lib/libsndio/sio_open.3 +++ b/lib/libsndio/sio_open.3 @@ -1,4 +1,4 @@ -.\" $OpenBSD: sio_open.3,v 1.19 2009/05/15 11:07:41 ratchov Exp $ +.\" $OpenBSD: sio_open.3,v 1.20 2009/07/25 08:44:27 ratchov Exp $ .\" .\" Copyright (c) 2007 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: May 15 2009 $ +.Dd $Mdocdate: July 25 2009 $ .Dt SIO_OPEN 3 .Os .Sh NAME @@ -104,11 +104,8 @@ If that fails, it then tries to use the hardware device. The .Ar name -parameter gives the name of the -.Xr aucat 1 -socket or the path of the -.Xr audio 4 -device. +parameter gives the device string discussed in +.Xr sndio 7 . In most cases it should be set to NULL to allow the user to select it using the .Ev AUDIODEVICE @@ -728,18 +725,18 @@ functions return the number of bytes transferred. .Sh ENVIRONMENT .Bl -tag -width "AUDIODEVICEXXX" -compact .It Ev AUDIODEVICE -Name of the -.Xr aucat 1 -socket to connect to, or path to the -.Xr audio 4 -device to use. +Device to use if +.Fn sio_open +is called with a NULL +.Va name +argument. .It Ev SIO_DEBUG The debug level: may be a value between 0 and 2. .El .Sh FILES -.Bl -tag -width "/tmp/aucat-<uid>/default" -compact -.It Pa /tmp/aucat-<uid>/default +.Bl -tag -width "/tmp/aucat-<uid>/softaudio0" -compact +.It Pa /tmp/aucat-<uid>/softaudio0 Default path to .Xr aucat 1 socket to connect to. @@ -754,6 +751,7 @@ device to use. .Sh SEE ALSO .Xr aucat 1 , .Xr audio 4 , +.Xr sndio 7 , .Xr audio 9 .Sh BUGS The diff --git a/lib/libsndio/sndio.c b/lib/libsndio/sndio.c index 6656cc67bb1..c7ab3750c81 100644 --- a/lib/libsndio/sndio.c +++ b/lib/libsndio/sndio.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sndio.c,v 1.15 2009/05/15 13:04:52 ratchov Exp $ */ +/* $OpenBSD: sndio.c,v 1.16 2009/07/25 08:44:27 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -17,6 +17,7 @@ #include <sys/param.h> #include <sys/types.h> #include <sys/time.h> +#include <sys/stat.h> #include <errno.h> #include <fcntl.h> #include <poll.h> @@ -163,11 +164,15 @@ done: return p - istr; } - struct sio_hdl * sio_open(char *str, unsigned mode, int nbio) { + static char prefix_aucat[] = "aucat"; + static char prefix_sun[] = "sun"; struct sio_hdl *hdl; + struct stat sb; + char *sep, buf[NAME_MAX]; + int len; #ifdef DEBUG char *dbg; @@ -181,12 +186,37 @@ sio_open(char *str, unsigned mode, int nbio) return NULL; if (str == NULL && !issetugid()) str = getenv("AUDIODEVICE"); - hdl = sio_open_aucat(str, mode, nbio); - if (hdl != NULL) - return hdl; - hdl = sio_open_sun(str, mode, nbio); - if (hdl != NULL) - return hdl; + if (str == NULL) { + hdl = sio_open_aucat("0", mode, nbio); + if (hdl != NULL) + return hdl; + if (stat("/dev/audio", &sb) == 0 && S_ISCHR(sb.st_mode)) { + snprintf(buf, sizeof(buf), "%u", + minor(sb.st_rdev) & 0xf); + } else + strlcpy(buf, "0", sizeof(buf)); + return sio_open_sun(buf, mode, nbio); + } + sep = strchr(str, ':'); + if (sep == NULL) { + /* + * try legacy "/dev/audioxxx" or ``socket'' device name + */ + if (stat(str, &sb) < 0 || !S_ISCHR(sb.st_mode)) { + snprintf(buf, sizeof(buf), "0.%s", str); + return sio_open_aucat(buf, mode, nbio); + } + snprintf(buf, sizeof(buf), "%u", minor(sb.st_rdev) & 0xf); + return sio_open_sun(buf, mode, nbio); + } + len = sep - str; + if (len == strlen(prefix_aucat) && + memcmp(str, prefix_aucat, len) == 0) + return sio_open_aucat(sep + 1, mode, nbio); + if (len == strlen(prefix_sun) && + memcmp(str, prefix_sun, len) == 0) + return sio_open_sun(sep + 1, mode, nbio); + DPRINTF("sio_open: %s: unknown device type\n", str); return NULL; } diff --git a/lib/libsndio/sun.c b/lib/libsndio/sun.c index 74aaf49a8da..b4030c7dc0f 100644 --- a/lib/libsndio/sun.c +++ b/lib/libsndio/sun.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sun.c,v 1.17 2009/05/15 13:16:58 ratchov Exp $ */ +/* $OpenBSD: sun.c,v 1.18 2009/07/25 08:44:27 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -342,20 +342,20 @@ sun_setvol(struct sio_hdl *sh, unsigned vol) } struct sio_hdl * -sio_open_sun(char *path, unsigned mode, int nbio) +sio_open_sun(char *str, unsigned mode, int nbio) { int fd, flags, fullduplex; struct sun_hdl *hdl; struct audio_info aui; struct sio_par par; + char path[PATH_MAX]; hdl = malloc(sizeof(struct sun_hdl)); if (hdl == NULL) return NULL; sio_create(&hdl->sio, &sun_ops, mode, nbio); - if (path == NULL) - path = SIO_SUN_PATH; + snprintf(path, sizeof(path), "/dev/audio%s", str); if (mode == (SIO_PLAY | SIO_REC)) flags = O_RDWR; else diff --git a/usr.bin/aucat/Makefile b/usr.bin/aucat/Makefile index 993d2609fe9..2a8844d207b 100644 --- a/usr.bin/aucat/Makefile +++ b/usr.bin/aucat/Makefile @@ -1,8 +1,10 @@ -# $OpenBSD: Makefile,v 1.8 2009/06/04 19:05:09 jsg Exp $ +# $OpenBSD: Makefile,v 1.9 2009/07/25 08:44:27 ratchov Exp $ PROG= aucat -SRCS= aucat.c abuf.c aparams.c aproc.c dev.c file.c headers.c \ - safile.c sock.c pipe.c listen.c wav.c legacy.c +SRCS= aucat.c abuf.c aparams.c aproc.c dev.c midi.c file.c headers.c \ + safile.c miofile.c sock.c pipe.c listen.c opt.c wav.c legacy.c +MAN= aucat.1 midicat.1 +LINKS= ${BINDIR}/aucat ${BINDIR}/midicat CFLAGS+= -DDEBUG -Wall -Wstrict-prototypes -Wundef LDADD+= -lsndio .include <bsd.prog.mk> diff --git a/usr.bin/aucat/abuf.h b/usr.bin/aucat/abuf.h index c339236db6b..636d32eb5d1 100644 --- a/usr.bin/aucat/abuf.h +++ b/usr.bin/aucat/abuf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: abuf.h,v 1.16 2009/02/13 20:48:49 ratchov Exp $ */ +/* $OpenBSD: abuf.h,v 1.17 2009/07/25 08:44:27 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -43,6 +43,13 @@ struct abuf { unsigned xrun; /* common to mix and sub */ LIST_ENTRY(abuf) ient; /* for mix inputs list */ LIST_ENTRY(abuf) oent; /* for sub outputs list */ + unsigned mstatus; /* MIDI running status */ + unsigned mindex; /* current MIDI message size */ + unsigned mused; /* bytes used from mdata */ + unsigned mlen; /* MIDI message length */ +#define MDATA_NMAX 16 + unsigned char mdata[MDATA_NMAX]; /* MIDI message data */ + unsigned mtickets; /* max data to transmit (throttling) */ /* * fifo parameters @@ -74,12 +81,14 @@ struct abuf { #define ABUF_WOK(b) ((b)->len - (b)->used >= (b)->bpf) /* - * the buffer is empty and has no more writer + * the buffer is empty and has no writer anymore */ #define ABUF_EOF(b) (!ABUF_ROK(b) && (b)->wproc == NULL) /* - * the buffer is empty and has no more writer + * the buffer has no reader anymore, note that it's not + * enough the buffer to be disconnected, because it can + * be not yet connected buffer (eg. socket play buffer) */ #define ABUF_HUP(b) (!ABUF_WOK(b) && (b)->rproc == NULL) diff --git a/usr.bin/aucat/amsg.h b/usr.bin/aucat/amsg.h index c2eb4dfebee..44950c32ee1 100644 --- a/usr.bin/aucat/amsg.h +++ b/usr.bin/aucat/amsg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: amsg.h,v 1.7 2009/05/16 12:20:31 ratchov Exp $ */ +/* $OpenBSD: amsg.h,v 1.8 2009/07/25 08:44:27 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -88,7 +88,8 @@ struct amsg { #define AMSG_MIDIOUT 0x8 /* MIDI thru output */ #define AMSG_MIXER 0x10 /* MIDI mixer */ uint16_t proto; /* protocol type */ - uint8_t reserved1[18]; /* for future use */ + uint8_t reserved1[6]; /* for future use */ + char opt[12]; /* profile name */ char who[12]; /* hint for leases */ } hello; } u; diff --git a/usr.bin/aucat/aproc.c b/usr.bin/aucat/aproc.c index e1abde18293..16809ca5cb1 100644 --- a/usr.bin/aucat/aproc.c +++ b/usr.bin/aucat/aproc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: aproc.c,v 1.31 2009/01/23 17:38:15 ratchov Exp $ */ +/* $OpenBSD: aproc.c,v 1.32 2009/07/25 08:44:27 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -157,6 +157,20 @@ aproc_inuse(struct aproc *p) } int +aproc_depend(struct aproc *p, struct aproc *dep) +{ + struct abuf *i; + + if (p == dep) + return 1; + LIST_FOREACH(i, &p->ibuflist, ient) { + if (i->wproc && aproc_depend(i->wproc, dep)) + return 1; + } + return 0; +} + +int rpipe_in(struct aproc *p, struct abuf *ibuf_dummy) { struct abuf *obuf = LIST_FIRST(&p->obuflist); @@ -185,7 +199,7 @@ rpipe_out(struct aproc *p, struct abuf *obuf) unsigned char *data; unsigned count; - if (f->refs > 0) + if (f->state & FILE_RINUSE) return 0; DPRINTFN(3, "rpipe_out: %s\n", p->name); @@ -270,7 +284,7 @@ wpipe_in(struct aproc *p, struct abuf *ibuf) unsigned char *data; unsigned count; - if (f->refs > 0) + if (f->state & FILE_WINUSE) return 0; DPRINTFN(3, "wpipe_in: %s\n", p->name); diff --git a/usr.bin/aucat/aproc.h b/usr.bin/aucat/aproc.h index 6ade66dcb6c..41b8ea253f7 100644 --- a/usr.bin/aucat/aproc.h +++ b/usr.bin/aucat/aproc.h @@ -1,4 +1,4 @@ -/* $OpenBSD: aproc.h,v 1.16 2009/01/23 17:38:15 ratchov Exp $ */ +/* $OpenBSD: aproc.h,v 1.17 2009/07/25 08:44:27 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -20,6 +20,7 @@ #include <sys/queue.h> #include "aparams.h" +#include "file.h" struct abuf; struct aproc; @@ -161,6 +162,10 @@ struct aproc { int bnext; /* to reach the next byte */ int snext; /* to reach the next sample */ } conv; + struct { + struct abuf *owner; /* current input stream */ + struct timo timo; /* timout for throtteling */ + } thru; } u; }; @@ -168,6 +173,7 @@ struct aproc *aproc_new(struct aproc_ops *, char *); void aproc_del(struct aproc *); void aproc_setin(struct aproc *, struct abuf *); void aproc_setout(struct aproc *, struct abuf *); +int aproc_depend(struct aproc *, struct aproc *); struct aproc *rpipe_new(struct file *); int rpipe_in(struct aproc *, struct abuf *); diff --git a/usr.bin/aucat/aucat.1 b/usr.bin/aucat/aucat.1 index c06a081f462..091de15c042 100644 --- a/usr.bin/aucat/aucat.1 +++ b/usr.bin/aucat/aucat.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: aucat.1,v 1.53 2009/05/09 09:50:40 ratchov Exp $ +.\" $OpenBSD: aucat.1,v 1.54 2009/07/25 08:44:27 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: May 9 2009 $ +.Dd $Mdocdate: July 25 2009 $ .Dt AUCAT 1 .Os .Sh NAME @@ -35,6 +35,7 @@ .Op Fl o Ar file .Op Fl r Ar rate .Op Fl s Ar socket +.Op Fl U Ar unit .Op Fl v Ar volume .Op Fl x Ar policy .Ek @@ -125,6 +126,15 @@ to the list of sockets to listen on. .Ar socket cannot contain '/'. Meaningful in server mode only. +.It Fl U Ar unit +Unit number to use when running in server mode. +Each +.Nm +server instance has an unique unit number, +used in +.Xr sndio 7 +device names. +The default is 0. .It Fl u Normally .Nm @@ -420,7 +430,8 @@ $ aucat -l -v 65 -s default -v 127 -s max .Xr audioctl 1 , .Xr cdio 1 , .Xr mixerctl 1 , -.Xr audio 4 +.Xr audio 4 , +.Xr sndio 7 .Sh BUGS The .Nm diff --git a/usr.bin/aucat/aucat.c b/usr.bin/aucat/aucat.c index 1b060505f6e..1cd8fbedb69 100644 --- a/usr.bin/aucat/aucat.c +++ b/usr.bin/aucat/aucat.c @@ -1,4 +1,4 @@ -/* $OpenBSD: aucat.c,v 1.60 2009/04/27 18:09:34 ratchov Exp $ */ +/* $OpenBSD: aucat.c,v 1.61 2009/07/25 08:44:27 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -14,36 +14,6 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* - * TODO: - * - * (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 - * what use most linux-centric apps, but for which we have an - * algorithm to convert the name to a aparams structure. - * - * (easy) uses {chmin-chmax} instead of chmin:chmax notation for - * channels specification to match the notation used in rmix. - * - * (easy) use comma-separated parameters syntax, example: - * 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) 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 - * the device) - * - * (hard) implement -n flag (no device) to connect all inputs to - * the outputs. - * - * (hard) ignore input files that are not audible (because channels - * they provide are not used on the output). Similarly ignore - * outputs that are zero filled (because channels they consume are - * not provided). - */ - #include <sys/param.h> #include <sys/types.h> #include <sys/queue.h> @@ -67,10 +37,16 @@ #include "wav.h" #include "listen.h" #include "dev.h" +#include "midi.h" +#include "opt.h" +#include "miofile.h" #define MODE_PLAY 1 #define MODE_REC 2 +#define PROG_AUCAT "aucat" +#define PROG_MIDICAT "midicat" + int debug_level = 0; volatile int quit_flag = 0; @@ -108,16 +84,17 @@ sigusr2(int s) } void -usage(void) +set_debug_level(char *envname) { - extern char *__progname; + char *dbgenv; + const char *errstr; - fprintf(stderr, - "usage: %s [-lnu] [-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 socket]\n" - "\t[-v volume] [-x policy]\n", - __progname); + dbgenv = getenv(envname); + if (dbgenv) { + debug_level = strtonum(dbgenv, 0, 4, &errstr); + if (errstr) + errx(1, "%s is %s: %s", envname, errstr, dbgenv); + } } void @@ -321,34 +298,99 @@ newoutput(struct farg *fa) dev_attach(fa->name, NULL, NULL, 0, buf, &fa->opar, fa->xrun, 0); } +void +setsig(void) +{ + struct sigaction sa; + + 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"); + if (sigaction(SIGTERM, &sa, NULL) < 0) + DPRINTF("sigaction(term) failed\n"); + if (sigaction(SIGHUP, &sa, NULL) < 0) + DPRINTF("sigaction(hup) 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 +} + +void +unsetsig(void) +{ + struct sigaction sa; + + sigfillset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + sa.sa_handler = SIG_DFL; +#ifdef DEBUG + if (sigaction(SIGUSR2, &sa, NULL) < 0) + DPRINTF("unsetsig(usr2): sigaction failed\n"); + if (sigaction(SIGUSR1, &sa, NULL) < 0) + DPRINTF("unsetsig(usr1): sigaction failed\n"); +#endif + if (sigaction(SIGHUP, &sa, NULL) < 0) + DPRINTF("unsetsig(hup): sigaction failed\n"); + if (sigaction(SIGTERM, &sa, NULL) < 0) + DPRINTF("unsetsig(term): sigaction failed\n"); + if (sigaction(SIGINT, &sa, NULL) < 0) + DPRINTF("unsetsig(int): sigaction failed\n"); +} + +void +getbasepath(char *base, size_t size) +{ + uid_t uid; + struct stat sb; + + uid = geteuid(); + snprintf(base, PATH_MAX, "/tmp/aucat-%u", uid); + if (mkdir(base, 0700) < 0) { + if (errno != EEXIST) + err(1, "mkdir(\"%s\")", base); + } + if (stat(base, &sb) < 0) + err(1, "stat(\"%s\")", base); + if (sb.st_uid != uid || (sb.st_mode & 077) != 0) + errx(1, "%s has wrong permissions", base); +} + +void +aucat_usage(void) +{ + (void)fputs("usage: " PROG_AUCAT " [-lnu] [-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 socket]\n" + "\t[-U unit] [-v volume] [-x policy]\n", + stderr); +} + int -main(int argc, char **argv) +aucat_main(int argc, char **argv) { - int c, u_flag, l_flag, n_flag, hdr, xrun, suspend = 0; + int c, u_flag, l_flag, n_flag, hdr, xrun, suspend = 0, unit; struct farg *fa; struct farglist ifiles, ofiles, sfiles; struct aparams ipar, opar, dipar, dopar; - struct sigaction sa; - struct stat sb; char base[PATH_MAX], path[PATH_MAX]; unsigned bufsz, mode; - char *devpath, *dbgenv; - const char *errstr; + char *devpath; unsigned volctl; - uid_t uid; - - dbgenv = getenv("AUCAT_DEBUG"); - if (dbgenv) { - debug_level = strtonum(dbgenv, 0, 4, &errstr); - if (errstr) - errx(1, "AUCAT_DEBUG is %s: %s", errstr, dbgenv); - } aparams_init(&ipar, 0, 1, 44100); aparams_init(&opar, 0, 1, 44100); u_flag = 0; l_flag = 0; n_flag = 0; + unit = -1; devpath = NULL; SLIST_INIT(&ifiles); SLIST_INIT(&ofiles); @@ -359,7 +401,7 @@ main(int argc, char **argv) bufsz = 44100 * 4 / 15; /* XXX: use milliseconds, not frames */ mode = 0; - while ((c = getopt(argc, argv, "nb:c:C:e:r:h:x:v:i:o:f:m:lus:")) != -1) { + while ((c = getopt(argc, argv, "nb:c:C:e:r:h:x:v:i:o:f:m:lus:U:")) != -1) { switch (c) { case 'n': n_flag = 1; @@ -421,8 +463,14 @@ main(int argc, char **argv) exit(1); } break; + case 'U': + if (sscanf(optarg, "%u", &unit) != 1) { + fprintf(stderr, "%s: bad device number\n", optarg); + exit(1); + } + break; default: - usage(); + aucat_usage(); exit(1); } } @@ -433,7 +481,6 @@ main(int argc, char **argv) dopar = ipar; dipar = opar; } - if (!l_flag && SLIST_EMPTY(&ifiles) && SLIST_EMPTY(&ofiles) && argc > 0) { /* @@ -447,12 +494,12 @@ main(int argc, char **argv) } exit(0); } else if (argc > 0) { - usage(); + aucat_usage(); exit(1); } - if (!l_flag && !SLIST_EMPTY(&sfiles)) - errx(1, "can't use -s without -l"); + if (!l_flag && (!SLIST_EMPTY(&sfiles) || unit >= 0)) + errx(1, "can't use -s or -U without -l"); if ((l_flag || mode != 0) && (!SLIST_EMPTY(&ofiles) || !SLIST_EMPTY(&ifiles))) errx(1, "can't use -l, -m and -s with -o or -i"); @@ -461,8 +508,10 @@ main(int argc, char **argv) mode |= MODE_PLAY; if (l_flag || !SLIST_EMPTY(&ofiles)) mode |= MODE_REC; - if (!mode) - errx(1, "nothing to play or record"); + if (!mode) { + aucat_usage(); + exit(1); + } } if (n_flag) { if (devpath != NULL || l_flag) @@ -476,7 +525,7 @@ main(int argc, char **argv) */ if (l_flag && SLIST_EMPTY(&sfiles)) { farg_add(&sfiles, &dopar, &dipar, - volctl, HDR_RAW, XRUN_IGNORE, DEFAULT_SOCKET); + volctl, HDR_RAW, XRUN_IGNORE, DEFAULT_OPT); } if (!u_flag) { @@ -500,35 +549,11 @@ main(int argc, char **argv) } if (l_flag) { - uid = geteuid(); - snprintf(base, PATH_MAX, "/tmp/aucat-%u", uid); - if (mkdir(base, 0700) < 0) { - if (errno != EEXIST) - err(1, "mkdir(\"%s\")", base); - } - if (stat(base, &sb) < 0) - err(1, "stat(\"%s\")", base); - if (sb.st_uid != uid || (sb.st_mode & 077) != 0) - errx(1, "%s has wrong permissions", base); + getbasepath(base, sizeof(base)); + if (unit < 0) + unit = 0; } - 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"); - if (sigaction(SIGTERM, &sa, NULL) < 0) - DPRINTF("sigaction(term) failed\n"); - if (sigaction(SIGHUP, &sa, NULL) < 0) - DPRINTF("sigaction(hup) 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 + setsig(); filelist_init(); /* @@ -565,15 +590,14 @@ main(int argc, char **argv) while (!SLIST_EMPTY(&sfiles)) { fa = SLIST_FIRST(&sfiles); SLIST_REMOVE_HEAD(&sfiles, entry); - if (strchr(fa->name, '/') != NULL) - errx(1, "socket names must not contain '/'"); - snprintf(path, PATH_MAX, "%s/%s", base, fa->name); - listen_new(&listen_ops, path, &fa->opar, &fa->ipar, - MIDI_TO_ADATA(fa->vol)); + opt_new(fa->name, &fa->opar, &fa->ipar, MIDI_TO_ADATA(fa->vol)); free(fa); } - if (l_flag && debug_level == 0) { - if (daemon(0, 0) < 0) + if (l_flag) { + snprintf(path, sizeof(path), "%s/%s%u", base, + DEFAULT_SOFTAUDIO, unit); + listen_new(&listen_ops, path); + if (debug_level == 0 && daemon(0, 0) < 0) err(1, "daemon"); } @@ -626,11 +650,223 @@ main(int argc, char **argv) } else dev_done(); filelist_done(); + unsetsig(); + return 0; +} - 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"); +void +midicat_usage(void) +{ + (void)fputs("usage: " PROG_MIDICAT " [-l] [-f device] " + "[-i file] [-o file] [-U unit]\n", + stderr); +} +int +midicat_main(int argc, char **argv) +{ + static struct aparams noparams = { 1, 0, 0, 0, 0, 0, 0, 0 }; + int c, l_flag, unit, fd; + char base[PATH_MAX], path[PATH_MAX]; + char *input, *output, *devpath; + struct file *dev, *stdx, *f; + struct aproc *p, *send, *recv; + struct abuf *buf; + + l_flag = 0; + unit = -1; + devpath = NULL; + output = NULL; + input = NULL; + + while ((c = getopt(argc, argv, "i:o:lf:U:")) != -1) { + switch (c) { + case 'i': + if (input != NULL) + errx(1, "only one -i allowed"); + input = optarg; + break; + case 'o': + if (output != NULL) + errx(1, "only one -o allowed"); + output = optarg; + break; + case 'f': + devpath = optarg; + break; + case 'l': + l_flag = 1; + break; + case 'U': + if (sscanf(optarg, "%u", &unit) != 1) { + fprintf(stderr, "%s: bad device number\n", optarg); + exit(1); + } + break; + default: + midicat_usage(); + exit(1); + } + } + argc -= optind; + argv += optind; + + if (argc > 0 || (!input && !output && !l_flag)) { + midicat_usage(); + exit(1); + } + if (!l_flag && unit >= 0) + errx(1, "can't use -U without -l"); + if (l_flag) { + if (input || output) + errx(1, "can't use -i or -o with -l"); + getbasepath(base, sizeof(path)); + if (unit < 0) + unit = 0; + } + setsig(); + filelist_init(); + + if (l_flag) { + thrubox = thru_new("thru"); + thrubox->refs++; + snprintf(path, sizeof(path), "%s/%s%u", base, + DEFAULT_MIDITHRU, unit); + listen_new(&listen_ops, path); + if (debug_level == 0 && daemon(0, 0) < 0) + err(1, "daemon"); + } + if (input || output) { + dev = (struct file *)miofile_new(&miofile_ops, devpath, + output ? 1 : 0, input ? 1 : 0); + if (dev == NULL) + errx(1, "%s: can't open device", + devpath ? devpath : "<default>"); + } else + dev = NULL; + if (input) { + send = wpipe_new(dev); + send->refs++; + if (strcmp(input, "-") == 0) { + fd = STDIN_FILENO; + if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) + warn("stdin"); + } else { + fd = open(input, O_RDONLY | O_NONBLOCK, 0666); + if (fd < 0) + err(1, "%s", input); + } + stdx = (struct file *)pipe_new(&pipe_ops, fd, "stdin"); + p = rpipe_new(stdx); + buf = abuf_new(3125, &noparams); + aproc_setout(p, buf); + aproc_setin(send, buf); + } else + send = NULL; + if (output) { + recv = rpipe_new(dev); + recv->refs++; + if (strcmp(output, "-") == 0) { + fd = STDOUT_FILENO; + if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) + warn("stdout"); + } else { + fd = open(output, + O_WRONLY | O_TRUNC | O_CREAT | O_NONBLOCK, 0666); + if (fd < 0) + err(1, "%s", output); + } + stdx = (struct file *)pipe_new(&pipe_ops, fd, "stdout"); + p = wpipe_new(stdx); + buf = abuf_new(3125, &noparams); + aproc_setin(p, buf); + aproc_setout(recv, buf); + } else + recv = NULL; + + /* + * loop, start processing + */ + for (;;) { + if (quit_flag) { + break; + } + if (!file_poll()) + break; + } + if (l_flag) { + filelist_unlisten(); + if (rmdir(base) < 0) + warn("rmdir(\"%s\")", base); + } + if (thrubox) { + restart_thrubox: + LIST_FOREACH(f, &file_list, entry) { + if (f->rproc && aproc_depend(thrubox, f->rproc)) { + file_eof(f); + goto restart_thrubox; + } + } + while (!LIST_EMPTY(&thrubox->ibuflist)) { + if (!file_poll()) + break; + } + thrubox->refs--; + aproc_del(thrubox); + thrubox = NULL; + while (file_poll()) + ; /* nothing */ + } + if (send) { + restart_send: + LIST_FOREACH(f, &file_list, entry) { + if (f->rproc && aproc_depend(send, f->rproc)) { + file_eof(f); + goto restart_send; + } + } + while (!LIST_EMPTY(&send->ibuflist)) { + if (!file_poll()) + break; + } + send->refs--; + aproc_del(send); + send = NULL; + } + if (recv) { + if (recv->u.io.file) + file_eof(recv->u.io.file); + while (!LIST_EMPTY(&recv->obuflist)) { + if (!file_poll()) + break; + } + recv->refs--; + aproc_del(recv); + recv = NULL; + } + filelist_done(); + unsetsig(); return 0; } + + +int +main(int argc, char **argv) +{ + char *prog; + + prog = strrchr(argv[0], '/'); + if (prog == NULL) + prog = argv[0]; + else + prog++; + if (strcmp(prog, PROG_AUCAT) == 0) { + set_debug_level("AUCAT_DEBUG"); + return aucat_main(argc, argv); + } else if (strcmp(prog, PROG_MIDICAT) == 0) { + set_debug_level("MIDICAT_DEBUG"); + return midicat_main(argc, argv); + } else { + fprintf(stderr, "%s: can't determine program to run\n", prog); + } + return 1; +} diff --git a/usr.bin/aucat/conf.h b/usr.bin/aucat/conf.h index 9d2105dd5ce..7d89c965f9b 100644 --- a/usr.bin/aucat/conf.h +++ b/usr.bin/aucat/conf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: conf.h,v 1.6 2009/02/03 19:44:58 ratchov Exp $ */ +/* $OpenBSD: conf.h,v 1.7 2009/07/25 08:44:27 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -56,7 +56,8 @@ extern int debug_level; */ #define WAV_NBLK 6 -#define DEFAULT_DEVICE "/dev/audio" -#define DEFAULT_SOCKET "default" +#define DEFAULT_MIDITHRU "midithru" +#define DEFAULT_SOFTAUDIO "softaudio" +#define DEFAULT_OPT "default" #endif /* !defined(CONF_H) */ diff --git a/usr.bin/aucat/file.c b/usr.bin/aucat/file.c index c1e37b14205..e1c707027fa 100644 --- a/usr.bin/aucat/file.c +++ b/usr.bin/aucat/file.c @@ -1,4 +1,4 @@ -/* $OpenBSD: file.c,v 1.11 2009/02/04 20:35:14 ratchov Exp $ */ +/* $OpenBSD: file.c,v 1.12 2009/07/25 08:44:27 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -21,7 +21,28 @@ * (operation will block), then the file is marked as "for polling", else * the file is not polled again. * + * the module also provides trivial timeout implementation, + * derived from: + * + * anoncvs@moule.caoua.org:/cvs/midish/timo.c rev 1.16 + * + * A timeout is used to schedule the call of a routine (the callback) + * there is a global list of timeouts that is processed inside the + * event loop. Timeouts work as follows: + * + * first the timo structure must be initialized with timo_set() + * + * then the timeout is scheduled (only once) with timo_add() + * + * if the timeout expires, the call-back is called; then it can + * be scheduled again if needed. It's OK to reschedule it again + * from the callback + * + * the timeout can be aborted with timo_del(), it is OK to try to + * abort a timout that has expired + * */ + #include <sys/types.h> #include <err.h> @@ -40,7 +61,133 @@ #define MAXFDS 100 extern struct fileops listen_ops, pipe_ops; + +struct timeval file_tv; struct filelist file_list; +struct timo *timo_queue; +unsigned timo_abstime; + +/* + * initialise a timeout structure, arguments are callback and argument + * that will be passed to the callback + */ +void +timo_set(struct timo *o, void (*cb)(void *), void *arg) +{ + o->cb = cb; + o->arg = arg; + o->set = 0; +} + +/* + * schedule the callback in 'delta' 24-th of microseconds. The timeout + * must not be already scheduled + */ +void +timo_add(struct timo *o, unsigned delta) +{ + struct timo **i; + unsigned val; + int diff; + +#ifdef DEBUG + if (o->set) { + fprintf(stderr, "timo_set: already set\n"); + abort(); + } + if (delta == 0) { + fprintf(stderr, "timo_set: zero timeout is evil\n"); + abort(); + } +#endif + val = timo_abstime + delta; + for (i = &timo_queue; *i != NULL; i = &(*i)->next) { + diff = (*i)->val - val; + if (diff > 0) { + break; + } + } + o->set = 1; + o->val = val; + o->next = *i; + *i = o; +} + +/* + * abort a scheduled timeout + */ +void +timo_del(struct timo *o) +{ + struct timo **i; + + for (i = &timo_queue; *i != NULL; i = &(*i)->next) { + if (*i == o) { + *i = o->next; + o->set = 0; + return; + } + } + DPRINTF("timo_del: not found\n"); +} + +/* + * routine to be called by the timer when 'delta' 24-th of microsecond + * elapsed. This routine updates time referece used by timeouts and + * calls expired timeouts + */ +void +timo_update(unsigned delta) +{ + struct timo *to; + int diff; + + /* + * update time reference + */ + timo_abstime += delta; + + /* + * remove from the queue and run expired timeouts + */ + while (timo_queue != NULL) { + /* + * there is no overflow here because + and - are + * modulo 2^32, they are the same for both signed and + * unsigned integers + */ + diff = timo_queue->val - timo_abstime; + if (diff > 0) + break; + to = timo_queue; + timo_queue = to->next; + to->set = 0; + to->cb(to->arg); + } +} + +/* + * initialize timeout queue + */ +void +timo_init(void) +{ + timo_queue = NULL; + timo_abstime = 0; +} + +/* + * destroy timeout queue + */ +void +timo_done(void) +{ + if (timo_queue != NULL) { + fprintf(stderr, "timo_done: timo_queue not empty!\n"); + abort(); + } + timo_queue = (struct timo *)0xdeadbeef; +} void file_dprint(int n, struct file *f) @@ -80,7 +227,6 @@ file_new(struct fileops *ops, char *name, unsigned nfds) f->state = 0; f->rproc = NULL; f->wproc = NULL; - f->refs = 0; LIST_INSERT_HEAD(&file_list, f, entry); DPRINTF("file_new: %s:%s\n", ops->name, f->name); return f; @@ -91,7 +237,7 @@ file_del(struct file *f) { DPRINTF("file_del: "); file_dprint(1, f); - if (f->refs > 0) { + if (f->state & (FILE_RINUSE | FILE_WINUSE)) { DPRINTF(": delayed\n"); f->state |= FILE_ZOMB; return; @@ -111,9 +257,9 @@ file_poll(void) struct pollfd pfds[MAXFDS]; struct file *f, *fnext; struct aproc *p; -#ifdef DEBUG - unsigned nused, nfound; -#endif + struct timeval tv; + long delta_usec; + int timo; /* * fill the pfds[] array with files that are blocked on reading @@ -121,22 +267,12 @@ file_poll(void) */ DPRINTFN(4, "file_poll:"); nfds = 0; -#ifdef DEBUG - nused = 0; - nfound = 0; -#endif LIST_FOREACH(f, &file_list, entry) { events = 0; if (f->rproc && !(f->state & FILE_ROK)) events |= POLLIN; if (f->wproc && !(f->state & FILE_WOK)) events |= POLLOUT; -#ifdef DEBUG - if (events) - nused++; - if (f->rproc || f->wproc) - nfound++; -#endif DPRINTFN(4, " %s(%x)", f->name, events); n = f->ops->pollfd(f, pfds + nfds, events); if (n == 0) { @@ -147,23 +283,35 @@ file_poll(void) nfds += n; } DPRINTFN(4, "\n"); - -#ifdef DEBUG - if (nused == 0 && nfound > 0) { - fprintf(stderr, "file_poll: deadlock\n"); - abort(); + if (debug_level >= 4) { + DPRINTF("file_poll: pfds[] ="); + for (n = 0; n < nfds; n++) + DPRINTF(" %x", pfds[n].events); + DPRINTF("\n"); } -#endif if (LIST_EMPTY(&file_list)) { DPRINTF("file_poll: nothing to do...\n"); return 0; } if (nfds > 0) { - if (poll(pfds, nfds, -1) < 0) { + if (timo_queue) { + timo = (timo_queue->val - timo_abstime) / (2 * 1000); + if (timo == 0) + timo = 1; + } else + timo = -1; + if (poll(pfds, nfds, timo) < 0) { if (errno == EINTR) return 1; err(1, "file_poll: poll failed"); } + gettimeofday(&tv, NULL); + delta_usec = 1000000L * (tv.tv_sec - file_tv.tv_sec); + delta_usec += tv.tv_usec - file_tv.tv_usec; + if (delta_usec > 0) { + file_tv = tv; + timo_update(delta_usec); + } } f = LIST_FIRST(&file_list); while (f != LIST_END(&file_list)) { @@ -171,27 +319,30 @@ file_poll(void) f = LIST_NEXT(f, entry); continue; } - f->refs++; revents = f->ops->revents(f, f->pfd); if (!(f->state & FILE_ZOMB) && (revents & POLLIN)) { revents &= ~POLLIN; f->state |= FILE_ROK; DPRINTFN(3, "file_poll: %s rok\n", f->name); + f->state |= FILE_RINUSE; for (;;) { p = f->rproc; if (!p || !p->ops->in(p, NULL)) break; } + f->state &= ~FILE_RINUSE; } if (!(f->state & FILE_ZOMB) && (revents & POLLOUT)) { revents &= ~POLLOUT; f->state |= FILE_WOK; DPRINTFN(3, "file_poll: %s wok\n", f->name); + f->state |= FILE_WINUSE; for (;;) { p = f->wproc; if (!p || !p->ops->out(p, NULL)) break; } + f->state &= ~FILE_WINUSE; } if (!(f->state & FILE_ZOMB) && (revents & POLLHUP)) { DPRINTFN(2, "file_poll: %s: disconnected\n", f->name); @@ -200,18 +351,23 @@ file_poll(void) if (!(f->state & FILE_ZOMB) && (f->state & FILE_EOF)) { DPRINTFN(2, "file_poll: %s: eof\n", f->name); p = f->rproc; - if (p) + if (p) { + f->state |= FILE_RINUSE; p->ops->eof(p, NULL); + f->state &= ~FILE_RINUSE; + } f->state &= ~FILE_EOF; } if (!(f->state & FILE_ZOMB) && (f->state & FILE_HUP)) { DPRINTFN(2, "file_poll: %s hup\n", f->name); p = f->wproc; - if (p) + if (p) { + f->state |= FILE_WINUSE; p->ops->hup(p, NULL); + f->state &= ~FILE_WINUSE; + } f->state &= ~FILE_HUP; } - f->refs--; fnext = LIST_NEXT(f, entry); if (f->state & FILE_ZOMB) file_del(f); @@ -233,8 +389,9 @@ filelist_init(void) (void)sigaddset(&set, SIGPIPE); if (sigprocmask(SIG_BLOCK, &set, NULL)) err(1, "sigprocmask"); - LIST_INIT(&file_list); + timo_init(); + gettimeofday(&file_tv, NULL); } void @@ -242,6 +399,7 @@ filelist_done(void) { struct file *f; + timo_done(); if (!LIST_EMPTY(&file_list)) { fprintf(stderr, "filelist_done: list not empty:\n"); LIST_FOREACH(f, &file_list, entry) { @@ -287,13 +445,14 @@ file_eof(struct file *f) { struct aproc *p; - if (f->refs == 0) { + if (!(f->state & (FILE_RINUSE | FILE_WINUSE))) { DPRINTFN(2, "file_eof: %s: immediate\n", f->name); - f->refs++; p = f->rproc; - if (p) + if (p) { + f->state |= FILE_RINUSE; p->ops->eof(p, NULL); - f->refs--; + f->state &= ~FILE_RINUSE; + } if (f->state & FILE_ZOMB) file_del(f); } else { @@ -308,13 +467,14 @@ file_hup(struct file *f) { struct aproc *p; - if (f->refs == 0) { + if (!(f->state & (FILE_RINUSE | FILE_WINUSE))) { DPRINTFN(2, "file_hup: %s immediate\n", f->name); - f->refs++; p = f->wproc; - if (p) + if (p) { + f->state |= FILE_WINUSE; p->ops->hup(p, NULL); - f->refs--; + f->state &= ~FILE_WINUSE; + } if (f->state & FILE_ZOMB) file_del(f); } else { @@ -329,16 +489,20 @@ file_close(struct file *f) { struct aproc *p; - if (f->refs == 0) { + if (!(f->state & (FILE_RINUSE | FILE_WINUSE))) { DPRINTFN(2, "file_close: %s: immediate\n", f->name); - f->refs++; p = f->rproc; - if (p) + if (p) { + f->state |= FILE_RINUSE; p->ops->eof(p, NULL); + f->state &= ~FILE_RINUSE; + } p = f->wproc; - if (p) + if (p) { + f->state |= FILE_WINUSE; p->ops->hup(p, NULL); - f->refs--; + f->state &= ~FILE_WINUSE; + } if (f->state & FILE_ZOMB) file_del(f); } else { diff --git a/usr.bin/aucat/file.h b/usr.bin/aucat/file.h index 3a341ebba28..dc0c825da1f 100644 --- a/usr.bin/aucat/file.h +++ b/usr.bin/aucat/file.h @@ -1,4 +1,4 @@ -/* $OpenBSD: file.h,v 1.6 2009/01/23 17:38:15 ratchov Exp $ */ +/* $OpenBSD: file.h,v 1.7 2009/07/25 08:44:27 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -26,6 +26,14 @@ struct aproc; struct abuf; struct pollfd; +struct timo { + struct timo *next; + unsigned val; /* time to wait before the callback */ + unsigned set; /* true if the timeout is set */ + void (*cb)(void *arg); /* routine to call on expiration */ + void *arg; /* argument to give to 'cb' */ +}; + struct fileops { char *name; size_t size; @@ -47,8 +55,9 @@ struct file { #define FILE_EOF 0x4 /* eof on the read end */ #define FILE_HUP 0x8 /* hang-up on the write end */ #define FILE_ZOMB 0x10 /* closed, but struct not freed */ +#define FILE_RINUSE 0x20 /* inside rproc->ops->in() */ +#define FILE_WINUSE 0x40 /* inside wproc->ops->out() */ unsigned state; /* one of above */ - unsigned refs; /* reference counter */ char *name; /* for debug purposes */ struct aproc *rproc, *wproc; /* reader and/or writer */ LIST_ENTRY(file) entry; @@ -58,6 +67,10 @@ LIST_HEAD(filelist,file); extern struct filelist file_list; +void timo_set(struct timo *, void (*)(void *), void *); +void timo_add(struct timo *, unsigned); +void timo_del(struct timo *); + void filelist_init(void); void filelist_done(void); void filelist_unlisten(void); diff --git a/usr.bin/aucat/listen.c b/usr.bin/aucat/listen.c index 0851d7a461b..06748be0917 100644 --- a/usr.bin/aucat/listen.c +++ b/usr.bin/aucat/listen.c @@ -1,4 +1,4 @@ -/* $OpenBSD: listen.c,v 1.8 2009/02/04 20:35:14 ratchov Exp $ */ +/* $OpenBSD: listen.c,v 1.9 2009/07/25 08:44:27 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -46,8 +46,7 @@ struct fileops listen_ops = { }; struct listen * -listen_new(struct fileops *ops, char *path, - struct aparams *wpar, struct aparams *rpar, int maxweight) +listen_new(struct fileops *ops, char *path) { int sock, oldumask; struct sockaddr_un sockname; @@ -84,18 +83,6 @@ listen_new(struct fileops *ops, char *path, exit(1); } f->fd = sock; - f->wpar = *wpar; - f->rpar = *rpar; - f->maxweight = maxweight; -#ifdef DEBUG - if (debug_level > 0) { - fprintf(stderr, "listen_new: %s: wpar=", f->path); - aparams_print(&f->wpar); - fprintf(stderr, ", rpar="); - aparams_print(&f->rpar); - fprintf(stderr, ", vol=%u\n", f->maxweight); - } -#endif return f; bad_close: close(sock); @@ -138,8 +125,7 @@ listen_revents(struct file *file, struct pollfd *pfd) close(sock); return 0; } - if (sock_new(&sock_ops, sock, "socket", - &f->wpar, &f->rpar, f->maxweight) == NULL) { + if (sock_new(&sock_ops, sock) == NULL) { close(sock); return 0; } diff --git a/usr.bin/aucat/listen.h b/usr.bin/aucat/listen.h index dec41553ad1..d33a8f026f4 100644 --- a/usr.bin/aucat/listen.h +++ b/usr.bin/aucat/listen.h @@ -1,4 +1,4 @@ -/* $OpenBSD: listen.h,v 1.3 2008/11/16 18:34:56 ratchov Exp $ */ +/* $OpenBSD: listen.h,v 1.4 2009/07/25 08:44:27 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -26,13 +26,9 @@ struct listen { struct file file; char *path; int fd; - int maxweight; /* max dynamic range for clients */ - struct aparams wpar; /* template for clients write params */ - struct aparams rpar; /* template for clients read params */ }; -struct listen *listen_new(struct fileops *, char *, - struct aparams *, struct aparams *, int); +struct listen *listen_new(struct fileops *, char *); int listen_nfds(struct file *); int listen_pollfd(struct file *, struct pollfd *, int); int listen_revents(struct file *, struct pollfd *); diff --git a/usr.bin/aucat/midi.c b/usr.bin/aucat/midi.c new file mode 100644 index 00000000000..c9889992b94 --- /dev/null +++ b/usr.bin/aucat/midi.c @@ -0,0 +1,270 @@ +/* $OpenBSD: midi.c,v 1.1 2009/07/25 08:44:27 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. + */ +/* + * TODO + * + * use abuf->duplex to implement bidirectionnal sockets + * that don't receive what they send + * + * use shadow variables in the midi merger + * + * make output and input identical when only one + * input is used (fix running status) + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "conf.h" +#include "abuf.h" +#include "aproc.h" +#include "midi.h" + +/* + * input data rate is XFER / TIMO (in bytes per microsecond), + * it must be slightly larger than the MIDI standard 3125 bytes/s + */ +#define MIDITHRU_XFER 340 +#define MIDITHRU_TIMO 100000 + +struct aproc *thrubox = NULL; + +unsigned voice_len[] = { 3, 3, 3, 3, 2, 2, 3 }; +unsigned common_len[] = { 0, 2, 3, 2, 0, 0, 1, 1 }; + +void +thru_flush(struct aproc *p, struct abuf *ibuf, struct abuf *obuf) +{ + unsigned ocount, itodo; + unsigned char *odata, *idata; + + itodo = ibuf->mused; + idata = ibuf->mdata; + DPRINTFN(4, "thru_flush: mused = %u\n", itodo); + while (itodo > 0) { + if (!ABUF_WOK(obuf)) { + abuf_rdiscard(obuf, obuf->used); + DPRINTFN(2, "thru_flush: discarded %u\n", obuf->used); + if (p->u.thru.owner == ibuf) + p->u.thru.owner = NULL; + return; + } + odata = abuf_wgetblk(obuf, &ocount, 0); + if (ocount > itodo) + ocount = itodo; + memcpy(odata, idata, ocount); + abuf_wcommit(obuf, ocount); + itodo -= ocount; + idata += ocount; + } + ibuf->mused = 0; + p->u.thru.owner = ibuf; +} + +void +thru_rt(struct aproc *p, struct abuf *ibuf, struct abuf *obuf, unsigned c) +{ + unsigned ocount; + unsigned char *odata; + + DPRINTFN(4, "thru_rt:\n"); + if (!ABUF_WOK(obuf)) { + DPRINTFN(2, "thru_rt: discarded %u\n", obuf->used); + abuf_rdiscard(obuf, obuf->used); + if (p->u.thru.owner == ibuf) + p->u.thru.owner = NULL; + } + odata = abuf_wgetblk(obuf, &ocount, 0); + odata[0] = c; + abuf_wcommit(obuf, 1); +} + + +void +thru_bcopy(struct aproc *p, struct abuf *ibuf, struct abuf *obuf, unsigned todo) +{ + unsigned char *idata; + unsigned c, icount, ioffs; + + idata = NULL; + icount = ioffs = 0; + for (;;) { + if (icount == 0) { + if (todo == 0) + break; + idata = abuf_rgetblk(ibuf, &icount, ioffs); + if (icount > todo) + icount = todo; + if (icount == 0) + break; + todo -= icount; + ioffs += icount; + } + c = *idata++; + icount--; + if (c < 0x80) { + if (ibuf->mindex == 0 && ibuf->mstatus) { + ibuf->mdata[ibuf->mused++] = ibuf->mstatus; + ibuf->mindex++; + } + ibuf->mdata[ibuf->mused++] = c; + ibuf->mindex++; + if (ibuf->mindex == ibuf->mlen) { + thru_flush(p, ibuf, obuf); + if (ibuf->mstatus >= 0xf0) + ibuf->mstatus = 0; + ibuf->mindex = 0; + } + if (ibuf->mused == MDATA_NMAX) { + if (ibuf->mused == ibuf->mindex || + p->u.thru.owner == ibuf) + thru_flush(p, ibuf, obuf); + else + ibuf->mused = 0; + } + } else if (c < 0xf8) { + if (ibuf->mused == ibuf->mindex || + p->u.thru.owner == ibuf) { + thru_flush(p, ibuf, obuf); + } else + ibuf->mused = 0; + ibuf->mdata[0] = c; + ibuf->mused = 1; + ibuf->mlen = (c >= 0xf0) ? + common_len[c & 7] : + voice_len[(c >> 4) & 7]; + if (ibuf->mlen == 1) { + thru_flush(p, ibuf, obuf); + ibuf->mindex = 0; + ibuf->mstatus = 0; + ibuf->mlen = 0; + } else { + ibuf->mstatus = c; + ibuf->mindex = 1; + } + } else { + thru_rt(p, ibuf, obuf, c); + } + } +} + +int +thru_in(struct aproc *p, struct abuf *ibuf) +{ + struct abuf *i, *inext; + unsigned todo; + + DPRINTFN(3, "thru_in: %s\n", p->name); + + if (!ABUF_ROK(ibuf)) + return 0; + if (ibuf->mtickets == 0) { + DPRINTFN(2, "thru_in: out of tickets\n"); + return 0; + } + todo = ibuf->used; + if (todo > ibuf->mtickets) + todo = ibuf->mtickets; + ibuf->mtickets -= todo; + for (i = LIST_FIRST(&p->obuflist); i != NULL; i = inext) { + inext = LIST_NEXT(i, oent); + if (ibuf->duplex == i) + continue; + thru_bcopy(p, ibuf, i, todo); + (void)abuf_flush(i); + } + abuf_rdiscard(ibuf, todo); + return 1; +} + +int +thru_out(struct aproc *p, struct abuf *obuf) +{ + return 0; +} + +void +thru_eof(struct aproc *p, struct abuf *ibuf) +{ + DPRINTF("thru_eof: %s: eof\n", p->name); +} + +void +thru_hup(struct aproc *p, struct abuf *obuf) +{ + DPRINTF("thru_hup: %s: detached\n", p->name); +} + +void +thru_newin(struct aproc *p, struct abuf *ibuf) +{ + ibuf->mused = 0; + ibuf->mlen = 0; + ibuf->mindex = 0; + ibuf->mstatus = 0; + ibuf->mtickets = MIDITHRU_XFER; +} + +void +thru_done(struct aproc *p) +{ + timo_del(&p->u.thru.timo); +} + +struct aproc_ops thru_ops = { + "thru", + thru_in, + thru_out, + thru_eof, + thru_hup, + thru_newin, + NULL, /* newout */ + NULL, /* ipos */ + NULL, /* opos */ + thru_done +}; + +void +thru_cb(void *addr) +{ + struct aproc *p = (struct aproc *)addr; + struct abuf *i, *inext; + unsigned tickets; + + timo_add(&p->u.thru.timo, MIDITHRU_TIMO); + + for (i = LIST_FIRST(&p->ibuflist); i != NULL; i = inext) { + inext = LIST_NEXT(i, ient); + tickets = i->mtickets; + i->mtickets = MIDITHRU_XFER; + if (tickets == 0) + abuf_run(i); + } +} + +struct aproc * +thru_new(char *name) +{ + struct aproc *p; + + p = aproc_new(&thru_ops, name); + p->u.thru.owner = NULL; + timo_set(&p->u.thru.timo, thru_cb, p); + timo_add(&p->u.thru.timo, MIDITHRU_TIMO); + return p; +} + diff --git a/usr.bin/aucat/midi.h b/usr.bin/aucat/midi.h new file mode 100644 index 00000000000..597482dd02b --- /dev/null +++ b/usr.bin/aucat/midi.h @@ -0,0 +1,24 @@ +/* $OpenBSD: midi.h,v 1.1 2009/07/25 08:44:27 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. + */ +#ifndef MIDI_H +#define MIDI_H + +struct aproc *thru_new(char *); + +extern struct aproc *thrubox; + +#endif /* !defined(MIDI_H) */ diff --git a/usr.bin/aucat/midicat.1 b/usr.bin/aucat/midicat.1 new file mode 100644 index 00000000000..c4e7167e03c --- /dev/null +++ b/usr.bin/aucat/midicat.1 @@ -0,0 +1,130 @@ +.\" $OpenBSD: midicat.1,v 1.1 2009/07/25 08:44:27 ratchov Exp $ +.\" +.\" Copyright (c) 2006 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. +.\" +.Dd $Mdocdate: July 25 2009 $ +.Dt MIDICAT 1 +.Os +.Sh NAME +.Nm midicat +.Nd MIDI server and manipulation tool +.Sh SYNOPSIS +.Nm midicat +.Op Fl l +.Op Fl f Ar device +.Op Fl i Ar file +.Op Fl o Ar file +.Op Fl U Ar unit +.Sh DESCRIPTION +The +.Nm +utility is used to manipulate MIDI data. +It can send and receive MIDI data from MIDI ports, +or it can create software MIDI thru boxes, +allowing any MIDI-capable application to +send MIDI messages to MIDI hardware +or to another application in a uniform way. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl f Ar device +The +.Xr midi 4 +device or +.Nm +socket to use for MIDI input/output. +.It Fl i Ar file +Send contents of this to the device. +If the option argument is +.Sq - +then standard input will be used. +.It Fl l +Listen for incoming connections on Unix domain sockets. +This allows clients to use +.Nm +instead of the regular +.Xr midi 4 +device as a software MIDI thru box. +.It Fl o Ar file +Store received data from the device into this file. +If the option argument is +.Sq - +then standard output will be used. +.It Fl U Ar unit +Use the given unit number when creating a software MIDI thru box. +Only one +.Nm +process can expose a unit number at a given time. +The default is 0. +.El +.Pp +If +.Nm +is sent +.Dv SIGHUP , +.Dv SIGINT +or +.Dv SIGTERM , +then processing terminates. +If sent +.Dv SIGUSR1 +or +.Dv SIGUSR2 , +it increases or decreases debug level, respectively. +.Sh SERVER MODE +.Nm +can be used in server mode +.Pq Fl l +to create MIDI thru boxes. +A MIDI thru box allows multiple receivers +to receive data from a single source. +Additionaly, +.Nm +software thru boxes allow multiple sources to be connected +to them; in this case MIDI byte-streams are merged, +preserving MIDI message integrity. +This feature is provided to allow multiple applications +acting as sources to keep their connection open while +idling; it does not replace a fully featured MIDI merger. +.Pp +It is generally not desirable to have multiple instances of +.Nm +running in server mode, so it is good practice to start it thus: +.Bd -literal -offset indent +$ pgrep -x midicat || midicat -l +.Ed +.Pp +Generally MIDI applications are real-time. +To reduce jitter, especially on busy machines, the +.Xr renice 8 +command can be used to give a higher priority to the +.Nm +process. +Superuser privileges are required. +For example: +.Bd -literal -offset indent +$ midicat -l +$ sudo renice -n -20 -p `pgrep -x midicat` +.Ed +.Sh ENVIRONMENT +.Bl -tag -width "MIDICAT_DEBUGXXX" -compact +.It Ev MIDICAT_DEBUG +The debug level: +may be a value between 0 and 4. +.El +.Sh SEE ALSO +.Xr aucat 1 , +.Xr midi 4 , +.Xr sndio 7 diff --git a/usr.bin/aucat/miofile.c b/usr.bin/aucat/miofile.c new file mode 100644 index 00000000000..a5effc82d93 --- /dev/null +++ b/usr.bin/aucat/miofile.c @@ -0,0 +1,150 @@ +/* $OpenBSD: miofile.c,v 1.1 2009/07/25 08:44:27 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 <sys/types.h> +#include <sys/time.h> + +#include <poll.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sndio.h> + +#include "conf.h" +#include "file.h" +#include "miofile.h" + +struct miofile { + struct file file; + struct mio_hdl *hdl; +}; + +void miofile_close(struct file *); +unsigned miofile_read(struct file *, unsigned char *, unsigned); +unsigned miofile_write(struct file *, unsigned char *, unsigned); +void miofile_start(struct file *); +void miofile_stop(struct file *); +int miofile_nfds(struct file *); +int miofile_pollfd(struct file *, struct pollfd *, int); +int miofile_revents(struct file *, struct pollfd *); + +struct fileops miofile_ops = { + "mio", + sizeof(struct miofile), + miofile_close, + miofile_read, + miofile_write, + NULL, /* start */ + NULL, /* stop */ + miofile_nfds, + miofile_pollfd, + miofile_revents +}; + +/* + * open the device + */ +struct miofile * +miofile_new(struct fileops *ops, char *path, int input, int output) +{ + struct mio_hdl *hdl; + struct miofile *f; + int mode = 0; + + if (input) + mode |= MIO_IN; + if (output) + mode |= MIO_OUT; + hdl = mio_open(path, mode, 1); + if (hdl == NULL) + return NULL; + f = (struct miofile *)file_new(ops, "miohdl", mio_nfds(hdl)); + if (f == NULL) + goto bad_close; + f->hdl = hdl; + return f; + bad_close: + mio_close(hdl); + return NULL; +} + +unsigned +miofile_read(struct file *file, unsigned char *data, unsigned count) +{ + struct miofile *f = (struct miofile *)file; + unsigned n; + + n = mio_read(f->hdl, data, count); + if (n == 0) { + f->file.state &= ~FILE_ROK; + if (mio_eof(f->hdl)) { + fprintf(stderr, "miofile_read: eof\n"); + file_eof(&f->file); + } else { + DPRINTFN(3, "miofile_read: %s: blocking...\n", + f->file.name); + } + return 0; + } + return n; + +} + +unsigned +miofile_write(struct file *file, unsigned char *data, unsigned count) +{ + struct miofile *f = (struct miofile *)file; + unsigned n; + + n = mio_write(f->hdl, data, count); + if (n == 0) { + f->file.state &= ~FILE_WOK; + if (mio_eof(f->hdl)) { + fprintf(stderr, "miofile_write: %s: hup\n", f->file.name); + file_hup(&f->file); + } else { + DPRINTFN(3, "miofile_write: %s: blocking...\n", + f->file.name); + } + return 0; + } + return n; +} + +int +miofile_nfds(struct file *file) +{ + return mio_nfds(((struct miofile *)file)->hdl); +} + +int +miofile_pollfd(struct file *file, struct pollfd *pfd, int events) +{ + return mio_pollfd(((struct miofile *)file)->hdl, pfd, events); +} + +int +miofile_revents(struct file *file, struct pollfd *pfd) +{ + return mio_revents(((struct miofile *)file)->hdl, pfd); +} + +void +miofile_close(struct file *file) +{ + return mio_close(((struct miofile *)file)->hdl); +} diff --git a/usr.bin/aucat/miofile.h b/usr.bin/aucat/miofile.h new file mode 100644 index 00000000000..e1b2e7324fe --- /dev/null +++ b/usr.bin/aucat/miofile.h @@ -0,0 +1,28 @@ +/* $OpenBSD: miofile.h,v 1.1 2009/07/25 08:44:27 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. + */ +#ifndef MIOFILE_H +#define MIOFILE_H + +struct file; +struct fileops; +struct miofile; + +struct miofile *miofile_new(struct fileops *, char *, int, int); + +extern struct fileops miofile_ops; + +#endif /* !defined(MIOFILE_H) */ diff --git a/usr.bin/aucat/opt.c b/usr.bin/aucat/opt.c new file mode 100644 index 00000000000..1d507c14011 --- /dev/null +++ b/usr.bin/aucat/opt.c @@ -0,0 +1,82 @@ +/* $OpenBSD: opt.c,v 1.1 2009/07/25 08:44:27 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 <string.h> + +#include "conf.h" +#include "opt.h" + +struct optlist opt_list = SLIST_HEAD_INITIALIZER(&opt_list); + +void +opt_new(char *name, struct aparams *wpar, struct aparams *rpar, int maxweight) +{ + struct opt *o; + unsigned len; + char c; + + for (len = 0; name[len] != '\0'; len++) { + if (len == OPT_NAMEMAX) { + fprintf(stderr, "%s: name too long\n", name); + exit(1); + } + c = name[len]; + if (c < 'a' && c > 'z' && + c < 'A' && c > 'Z' && + c < '0' && c > '9' && + c != '_') { + fprintf(stderr, "%s: '%c' not allowed\n", name, c); + exit(1); + } + } + o = malloc(sizeof(struct opt)); + if (o == NULL) { + perror("opt_new: malloc"); + exit(1); + } + memcpy(o->name, name, len + 1); + o->wpar = *wpar; + o->rpar = *rpar; + o->maxweight = maxweight; +#ifdef DEBUG + if (debug_level > 0) { + fprintf(stderr, "opt_new: %s: wpar=", o->name); + aparams_print(&o->wpar); + fprintf(stderr, ", rpar="); + aparams_print(&o->rpar); + fprintf(stderr, ", vol=%u\n", o->maxweight); + } +#endif + SLIST_INSERT_HEAD(&opt_list, o, entry); +} + +struct opt * +opt_byname(char *name) +{ + struct opt *o; + + SLIST_FOREACH(o, &opt_list, entry) { + if (strcmp(name, o->name) == 0) { + DPRINTF("opt_byname: %s found\n", o->name); + return o; + } + } + DPRINTF("opt_byname: %s not found\n", name); + return NULL; +} + diff --git a/usr.bin/aucat/opt.h b/usr.bin/aucat/opt.h new file mode 100644 index 00000000000..1d412293cb6 --- /dev/null +++ b/usr.bin/aucat/opt.h @@ -0,0 +1,37 @@ +/* $OpenBSD: opt.h,v 1.1 2009/07/25 08:44:27 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. + */ +#ifndef OPT_H +#define OPT_H + +#include <sys/queue.h> +#include "aparams.h" + +struct opt { + SLIST_ENTRY(opt) entry; +#define OPT_NAMEMAX 11 + char name[OPT_NAMEMAX + 1]; + int maxweight; /* max dynamic range for clients */ + struct aparams wpar; /* template for clients write params */ + struct aparams rpar; /* template for clients read params */ +}; + +SLIST_HEAD(optlist,opt); + +void opt_new(char *, struct aparams *, struct aparams *, int); +struct opt *opt_byname(char *); + +#endif /* !defined(OPT_H) */ diff --git a/usr.bin/aucat/pipe.c b/usr.bin/aucat/pipe.c index de0f4021947..9752db97304 100644 --- a/usr.bin/aucat/pipe.c +++ b/usr.bin/aucat/pipe.c @@ -72,7 +72,7 @@ pipe_read(struct file *file, unsigned char *data, unsigned count) gettimeofday(&tv1, NULL); timersub(&tv1, &tv0, &dtv); us = dtv.tv_sec * 1000000 + dtv.tv_usec; - DPRINTFN(us < 5000 ? 4 : 1, + DPRINTFN(us < 5000 ? 4 : 2, "pipe_read: %s: got %d bytes in %uus\n", f->file.name, n, us); #endif @@ -111,7 +111,7 @@ pipe_write(struct file *file, unsigned char *data, unsigned count) gettimeofday(&tv1, NULL); timersub(&tv1, &tv0, &dtv); us = dtv.tv_sec * 1000000 + dtv.tv_usec; - DPRINTFN(us < 5000 ? 4 : 1, + DPRINTFN(us < 5000 ? 4 : 2, "pipe_write: %s: wrote %d bytes in %uus\n", f->file.name, n, us); #endif diff --git a/usr.bin/aucat/safile.c b/usr.bin/aucat/safile.c index 392df9556e7..d6768162646 100644 --- a/usr.bin/aucat/safile.c +++ b/usr.bin/aucat/safile.c @@ -1,4 +1,4 @@ -/* $OpenBSD: safile.c,v 1.12 2009/02/06 08:26:34 ratchov Exp $ */ +/* $OpenBSD: safile.c,v 1.13 2009/07/25 08:44:27 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -214,7 +214,7 @@ safile_read(struct file *file, unsigned char *data, unsigned count) gettimeofday(&tv1, NULL); timersub(&tv1, &tv0, &dtv); us = dtv.tv_sec * 1000000 + dtv.tv_usec; - DPRINTFN(us < 5000 ? 4 : 1, + DPRINTFN(us < 5000 ? 4 : 2, "safile_read: %s: got %d bytes in %uus\n", f->file.name, n, us); #endif @@ -253,7 +253,7 @@ safile_write(struct file *file, unsigned char *data, unsigned count) gettimeofday(&tv1, NULL); timersub(&tv1, &tv0, &dtv); us = dtv.tv_sec * 1000000 + dtv.tv_usec; - DPRINTFN(us < 5000 ? 4 : 1, + DPRINTFN(us < 5000 ? 4 : 2, "safile_write: %s: wrote %d bytes in %uus\n", f->file.name, n, us); #endif diff --git a/usr.bin/aucat/sock.c b/usr.bin/aucat/sock.c index 07ef03af278..f94be462b18 100644 --- a/usr.bin/aucat/sock.c +++ b/usr.bin/aucat/sock.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sock.c,v 1.18 2009/05/16 12:20:31 ratchov Exp $ */ +/* $OpenBSD: sock.c,v 1.19 2009/07/25 08:44:27 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -29,6 +29,8 @@ #include "sock.h" #include "dev.h" #include "conf.h" +#include "midi.h" +#include "opt.h" int sock_attach(struct sock *, int); int sock_read(struct sock *); @@ -89,7 +91,7 @@ rsock_out(struct aproc *p, struct abuf *obuf) { struct sock *f = (struct sock *)p->u.io.file; - if (f->pipe.file.refs > 0) + if (f->pipe.file.state & FILE_RINUSE) return 0; DPRINTFN(4, "rsock_out: %p\n", f); @@ -183,11 +185,10 @@ wsock_in(struct aproc *p, struct abuf *ibuf) { struct sock *f = (struct sock *)p->u.io.file; - if (f->pipe.file.refs > 0) + if (f->pipe.file.state & FILE_WINUSE) return 0; DPRINTFN(4, "wsock_in: %p\n", f); - /* * see remark in rsock_out() */ @@ -273,40 +274,37 @@ struct aproc_ops wsock_ops = { * parameters */ struct sock * -sock_new(struct fileops *ops, int fd, char *name, - struct aparams *wpar, struct aparams *rpar, int maxweight) +sock_new(struct fileops *ops, int fd) { struct aproc *rproc, *wproc; struct sock *f; - f = (struct sock *)pipe_new(ops, fd, name); + f = (struct sock *)pipe_new(ops, fd, "sock"); if (f == NULL) return NULL; f->pstate = SOCK_HELLO; f->mode = 0; - if (dev_rec) { - f->templ_wpar = *wpar; - f->wpar = f->templ_wpar; - } - if (dev_play) { - f->templ_rpar = *rpar; - f->rpar = f->templ_rpar; + f->opt = opt_byname("default"); + if (f->opt) { + if (dev_rec) + f->wpar = f->opt->wpar; + if (dev_play) + f->rpar = f->opt->rpar; } f->xrun = AMSG_IGNORE; f->bufsz = dev_bufsz; f->round = dev_round; f->delta = 0; f->tickpending = 0; - f->maxweight = maxweight; f->vol = ADATA_UNIT; - wproc = aproc_new(&wsock_ops, name); + wproc = aproc_new(&wsock_ops, f->pipe.file.name); wproc->u.io.file = &f->pipe.file; f->pipe.file.wproc = wproc; f->wstate = SOCK_WIDLE; f->wtodo = 0xdeadbeef; - rproc = aproc_new(&rsock_ops, name); + rproc = aproc_new(&rsock_ops, f->pipe.file.name); rproc->u.io.file = &f->pipe.file; f->pipe.file.rproc = rproc; f->rstate = SOCK_RMSG; @@ -402,7 +400,7 @@ sock_attach(struct sock *f, int force) dev_attach(f->pipe.file.name, (f->mode & AMSG_PLAY) ? rbuf : NULL, &f->rpar, f->xrun, (f->mode & AMSG_REC) ? wbuf : NULL, &f->wpar, f->xrun, - f->maxweight); + f->opt->maxweight); if (f->mode & AMSG_PLAY) dev_setvol(rbuf, f->vol); @@ -500,23 +498,26 @@ sock_rdata(struct sock *f) unsigned count, n; #ifdef DEBUG - if (f->rtodo == 0) { + if (f->pstate != SOCK_MIDI && f->rtodo == 0) { fprintf(stderr, "sock_rdata: bad call: zero arg\n"); abort(); } #endif p = f->pipe.file.rproc; obuf = LIST_FIRST(&p->obuflist); + if (obuf == NULL) + return 0; if (ABUF_FULL(obuf) || !(f->pipe.file.state & FILE_ROK)) return 0; data = abuf_wgetblk(obuf, &count, 0); - if (count > f->rtodo) + if (f->pstate != SOCK_MIDI && count > f->rtodo) count = f->rtodo; n = file_read(&f->pipe.file, data, count); if (n == 0) return 0; abuf_wcommit(obuf, n); - f->rtodo -= n; + if (f->pstate != SOCK_MIDI) + f->rtodo -= n; return 1; } @@ -535,7 +536,7 @@ sock_wdata(struct sock *f) static char zero[ZERO_MAX]; #ifdef DEBUG - if (f->wtodo == 0) { + if (f->pstate != SOCK_MIDI && f->wtodo == 0) { fprintf(stderr, "sock_wdata: bad call: zero arg\n"); abort(); } @@ -548,14 +549,17 @@ sock_wdata(struct sock *f) if (ABUF_EMPTY(ibuf)) return 0; data = abuf_rgetblk(ibuf, &count, 0); - if (count > f->wtodo) + if (f->pstate != SOCK_MIDI && count > f->wtodo) count = f->wtodo; n = file_write(&f->pipe.file, data, count); if (n == 0) return 0; abuf_rdiscard(ibuf, n); - f->wtodo -= n; + if (f->pstate != SOCK_MIDI) + f->wtodo -= n; } else { + if (f->pstate == SOCK_MIDI) + return 0; /* * there's no dev_detach() routine yet, * so now we abruptly destroy the buffer. @@ -623,10 +627,10 @@ sock_setpar(struct sock *f) p->rchan = 1; if (p->rchan > NCHAN_MAX) p->rchan = NCHAN_MAX; - f->wpar.cmin = f->templ_wpar.cmin; - f->wpar.cmax = f->templ_wpar.cmin + p->rchan - 1; - if (f->wpar.cmax > f->templ_wpar.cmax) - f->wpar.cmax = f->templ_wpar.cmax; + f->wpar.cmin = f->opt->wpar.cmin; + f->wpar.cmax = f->opt->wpar.cmin + p->rchan - 1; + if (f->wpar.cmax > f->opt->wpar.cmax) + f->wpar.cmax = f->opt->wpar.cmax; DPRINTF("sock_setpar: rchan -> %u:%u\n", f->wpar.cmin, f->wpar.cmax); } @@ -635,10 +639,10 @@ sock_setpar(struct sock *f) p->pchan = 1; if (p->pchan > NCHAN_MAX) p->pchan = NCHAN_MAX; - f->rpar.cmin = f->templ_rpar.cmin; - f->rpar.cmax = f->templ_rpar.cmin + p->pchan - 1; - if (f->rpar.cmax > f->templ_rpar.cmax) - f->rpar.cmax = f->templ_rpar.cmax; + f->rpar.cmin = f->opt->rpar.cmin; + f->rpar.cmax = f->opt->rpar.cmin + p->pchan - 1; + if (f->rpar.cmax > f->opt->rpar.cmax) + f->rpar.cmax = f->opt->rpar.cmax; DPRINTF("sock_setpar: pchan -> %u:%u\n", f->rpar.cmin, f->rpar.cmax); } @@ -704,12 +708,58 @@ sock_setpar(struct sock *f) return 1; } +/* + * allocate buffers, so client can start filling write-end. + */ +void +sock_midiattach(struct sock *f, unsigned mode) +{ + struct abuf *rbuf = NULL, *wbuf = NULL; + struct aparams dummy; + + memset(&dummy, 0, sizeof(dummy)); + dummy.bps = 1; + + if (mode & AMSG_MIDIOUT) { + rbuf = abuf_new(3125, &dummy); + aproc_setout(f->pipe.file.rproc, rbuf); + aproc_setin(thrubox, rbuf); + } + if (mode & AMSG_MIDIIN) { + wbuf = abuf_new(3125, &dummy); + aproc_setin(f->pipe.file.wproc, wbuf); + aproc_setout(thrubox, wbuf); + if (mode & AMSG_MIDIOUT) { + rbuf->duplex = wbuf; + wbuf->duplex = rbuf; + } + } +} + int sock_hello(struct sock *f) { struct amsg_hello *p = &f->rmsg.u.hello; - DPRINTF("sock_hello: from <%s>\n", p->who); + DPRINTF("sock_hello: from <%s>, mode = %x\n", p->who, p->proto); + + if (thrubox && (p->proto & (AMSG_MIDIIN | AMSG_MIDIOUT))) { + if (p->proto & ~(AMSG_MIDIIN | AMSG_MIDIOUT)) { + DPRINTF("sock_hello: %x: bad proto\n", p->proto); + return 0; + } + f->mode = p->proto; + f->pstate = SOCK_MIDI; + sock_midiattach(f, p->proto); + return 1; + } + f->opt = opt_byname(p->opt); + if (f->opt == NULL) + return 0; + if (dev_rec) + f->wpar = f->opt->wpar; + if (dev_play) + f->rpar = f->opt->rpar; if ((p->proto & ~(AMSG_PLAY | AMSG_REC)) != 0 || (p->proto & (AMSG_PLAY | AMSG_REC)) == 0) { DPRINTF("sock_hello: %x: unsupported proto\n", p->proto); @@ -744,9 +794,9 @@ sock_execmsg(struct sock *f) struct amsg *m = &f->rmsg; /* - * XXX: allow old clients to work without hello + * XXX: allow old clients to work without hello on the default socket */ - if (f->pstate == SOCK_HELLO && m->cmd != AMSG_HELLO) { + if (f->pstate == SOCK_HELLO && m->cmd != AMSG_HELLO && f->opt != NULL) { DPRINTF("sock_execmsg: legacy client\n"); f->pstate = SOCK_INIT; } @@ -848,9 +898,9 @@ sock_execmsg(struct sock *f) m->cmd = AMSG_GETCAP; m->u.cap.rate = dev_rate; m->u.cap.pchan = dev_mix ? - (f->templ_rpar.cmax - f->templ_rpar.cmin + 1) : 0; + (f->opt->rpar.cmax - f->opt->rpar.cmin + 1) : 0; m->u.cap.rchan = dev_sub ? - (f->templ_wpar.cmax - f->templ_wpar.cmin + 1) : 0; + (f->opt->wpar.cmax - f->opt->wpar.cmin + 1) : 0; m->u.cap.bits = sizeof(short) * 8; m->u.cap.bps = sizeof(short); f->rstate = SOCK_RRET; @@ -900,8 +950,13 @@ sock_execmsg(struct sock *f) !sock_wmsg(f, &f->rmsg, &f->rtodo)) return 0; DPRINTF("sock_execmsg: %p RRET done\n", f); - f->rtodo = sizeof(struct amsg); - f->rstate = SOCK_RMSG; + if (f->pstate == SOCK_MIDI && (f->mode & AMSG_MIDIOUT)) { + f->rstate = SOCK_RDATA; + f->rtodo = 0; + } else { + f->rstate = SOCK_RMSG; + f->rtodo = sizeof(struct amsg); + } } return 1; } @@ -915,6 +970,13 @@ sock_buildmsg(struct sock *f) struct aproc *p; struct abuf *ibuf; + if (f->pstate == SOCK_MIDI) { + DPRINTFN(4, "sock_buildmsg: %p: switched to midi\n", f); + f->wstate = SOCK_WDATA; + f->wtodo = 0; + return 1; + } + /* * if pos changed, build a MOVE message */ @@ -973,7 +1035,7 @@ sock_read(struct sock *f) case SOCK_RDATA: if (!sock_rdata(f)) return 0; - if (f->rtodo == 0) { + if (f->pstate != SOCK_MIDI && f->rtodo == 0) { f->rstate = SOCK_RMSG; f->rtodo = sizeof(struct amsg); } @@ -1000,8 +1062,16 @@ sock_return(struct sock *f) if (!sock_wmsg(f, &f->rmsg, &f->rtodo)) return 0; DPRINTF("sock_return: %p: done\n", f); - f->rstate = SOCK_RMSG; - f->rtodo = sizeof(struct amsg); + if (f->pstate == SOCK_MIDI && (f->mode & AMSG_MIDIOUT)) { + f->rstate = SOCK_RDATA; + f->rtodo = 0; + } else { + f->rstate = SOCK_RMSG; + f->rtodo = sizeof(struct amsg); + } + if (f->pipe.file.state & FILE_RINUSE) + break; + f->pipe.file.state |= FILE_RINUSE; for (;;) { /* * in() may trigger rsock_done and destroy the @@ -1011,6 +1081,7 @@ sock_return(struct sock *f) if (!rp || !rp->ops->in(rp, NULL)) break; } + f->pipe.file.state &= ~FILE_RINUSE; if (f->pipe.file.wproc == NULL) return 0; } @@ -1043,7 +1114,7 @@ sock_write(struct sock *f) case SOCK_WDATA: if (!sock_wdata(f)) return 0; - if (f->wtodo > 0) + if (f->pstate == SOCK_MIDI || f->wtodo > 0) break; f->wstate = SOCK_WIDLE; f->wtodo = 0xdeadbeef; diff --git a/usr.bin/aucat/sock.h b/usr.bin/aucat/sock.h index 3bcbadbe6a6..d9535c8442f 100644 --- a/usr.bin/aucat/sock.h +++ b/usr.bin/aucat/sock.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sock.h,v 1.6 2009/05/16 12:20:31 ratchov Exp $ */ +/* $OpenBSD: sock.h,v 1.7 2009/07/25 08:44:27 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -21,6 +21,8 @@ #include "aparams.h" #include "amsg.h" +struct opt; + struct sock { struct pipe pipe; /* @@ -42,6 +44,7 @@ struct sock { #define SOCK_INIT 1 /* parameter negotiation */ #define SOCK_START 2 /* filling play buffers */ #define SOCK_RUN 3 /* attached to the mix / sub */ +#define SOCK_MIDI 4 /* raw byte stream (midi) */ unsigned pstate; /* one of the above */ unsigned mode; /* a set of AMSG_PLAY, AMSG_REC */ struct aparams rpar; /* read (ie play) parameters */ @@ -52,13 +55,10 @@ struct sock { unsigned round; /* block size */ unsigned xrun; /* one of AMSG_IGNORE, ... */ int vol; /* requested volume */ - int maxweight; /* max dynamic range */ - struct aparams templ_rpar; /* template for rpar */ - struct aparams templ_wpar; /* template for wpar */ + struct opt *opt; /* "subdevice" definition */ }; -struct sock *sock_new(struct fileops *, int fd, char *, - struct aparams *, struct aparams *, int); +struct sock *sock_new(struct fileops *, int fd); extern struct fileops sock_ops; #endif /* !defined(SOCK_H) */ |