summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorratchov <ratchov@openbsd.org>2009-07-25 08:44:26 +0000
committerratchov <ratchov@openbsd.org>2009-07-25 08:44:26 +0000
commit6efede295b0a3247be235c162ff0bf258da6bbd5 (patch)
tree594621929096e267da5f2d6b3c0b84046e0c5360
parent- add a symbol to help gdb location pthread internals (_thread_ctx_offset) (diff)
downloadwireguard-openbsd-6efede295b0a3247be235c162ff0bf258da6bbd5.tar.xz
wireguard-openbsd-6efede295b0a3247be235c162ff0bf258da6bbd5.zip
Currently midi capable programs can control midi hardware, but
cannot cooperate with other programs. The aim of this change is to allow any program to send midi data to other programs as they were midi hardware. For instance, this change should solve the longstanding problem of using a midi sequencer with software synthesizers. More precisely: - new midicat(1) utility (actually hardlink to aucat(1)). it creates software midi thru boxes, allowing programs to send midi messages to other programs as they were midi(4) hardware. - new midi api in libsndio (see mio_open(3)), to access midi(4) devices and midicat(1) sockets in a uniform way. - new device naming scheme <service>:<unit>[.<option>], common to audio and midi. - new sndio(7) manual describing concepts and naming The current audio device naming still works, but people having scripts or configuration files containing device names could read the sndio(7) man page and slowly start updating device names. discussed with jakemsr@ and deraadt@, help form jmc@
-rw-r--r--include/sndio.h14
-rw-r--r--lib/libsndio/Makefile15
-rw-r--r--lib/libsndio/aucat.c27
-rw-r--r--lib/libsndio/mio.c184
-rw-r--r--lib/libsndio/mio_open.3252
-rw-r--r--lib/libsndio/mio_priv.h69
-rw-r--r--lib/libsndio/mio_rmidi.c158
-rw-r--r--lib/libsndio/mio_thru.c213
-rw-r--r--lib/libsndio/shlib_version2
-rw-r--r--lib/libsndio/sio_open.326
-rw-r--r--lib/libsndio/sndio.c46
-rw-r--r--lib/libsndio/sun.c8
-rw-r--r--usr.bin/aucat/Makefile8
-rw-r--r--usr.bin/aucat/abuf.h15
-rw-r--r--usr.bin/aucat/amsg.h5
-rw-r--r--usr.bin/aucat/aproc.c20
-rw-r--r--usr.bin/aucat/aproc.h8
-rw-r--r--usr.bin/aucat/aucat.117
-rw-r--r--usr.bin/aucat/aucat.c440
-rw-r--r--usr.bin/aucat/conf.h7
-rw-r--r--usr.bin/aucat/file.c246
-rw-r--r--usr.bin/aucat/file.h17
-rw-r--r--usr.bin/aucat/listen.c20
-rw-r--r--usr.bin/aucat/listen.h8
-rw-r--r--usr.bin/aucat/midi.c270
-rw-r--r--usr.bin/aucat/midi.h24
-rw-r--r--usr.bin/aucat/midicat.1130
-rw-r--r--usr.bin/aucat/miofile.c150
-rw-r--r--usr.bin/aucat/miofile.h28
-rw-r--r--usr.bin/aucat/opt.c82
-rw-r--r--usr.bin/aucat/opt.h37
-rw-r--r--usr.bin/aucat/pipe.c4
-rw-r--r--usr.bin/aucat/safile.c6
-rw-r--r--usr.bin/aucat/sock.c157
-rw-r--r--usr.bin/aucat/sock.h12
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) */