summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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) */