summaryrefslogtreecommitdiff
path: root/usr.bin
diff options
context:
space:
mode:
authorAlexandre Ratchov <ratchov@cvs.openbsd.org>2008-10-26 08:49:45 +0000
committerAlexandre Ratchov <ratchov@cvs.openbsd.org>2008-10-26 08:49:45 +0000
commit13e276c156d9b9f3a5064700b447d8e90d89bebf (patch)
tree7c47aad8fcd6da2de5156ec12853b26ee468547e /usr.bin
parentc1f6af90f771854093903e82e7de930b96a15d25 (diff)
add minimal server capability to aucat(1). When started in server
mode, it listens on an unix socket and mixes/demultiplexes any number of full-duplex streams, doing necessary format conversions and resampling on the fly. programs can use the new libsa(3) library to play and record audio. The library provides a very simple API to connect to the audio server; if aucat(1) isn't running, it uses the audio(4) driver transparently instead.
Diffstat (limited to 'usr.bin')
-rw-r--r--usr.bin/aucat/Makefile6
-rw-r--r--usr.bin/aucat/abuf.c327
-rw-r--r--usr.bin/aucat/abuf.h17
-rw-r--r--usr.bin/aucat/amsg.h92
-rw-r--r--usr.bin/aucat/aparams.c147
-rw-r--r--usr.bin/aucat/aparams.h23
-rw-r--r--usr.bin/aucat/aproc.c411
-rw-r--r--usr.bin/aucat/aproc.h29
-rw-r--r--usr.bin/aucat/aucat.1104
-rw-r--r--usr.bin/aucat/aucat.c286
-rw-r--r--usr.bin/aucat/conf.h18
-rw-r--r--usr.bin/aucat/dev.c469
-rw-r--r--usr.bin/aucat/dev.h29
-rw-r--r--usr.bin/aucat/dev_sun.c282
-rw-r--r--usr.bin/aucat/file.c292
-rw-r--r--usr.bin/aucat/file.h61
-rw-r--r--usr.bin/aucat/headers.c4
-rw-r--r--usr.bin/aucat/legacy.c74
-rw-r--r--usr.bin/aucat/listen.c133
-rw-r--r--usr.bin/aucat/listen.h37
-rw-r--r--usr.bin/aucat/pipe.c146
-rw-r--r--usr.bin/aucat/pipe.h39
-rw-r--r--usr.bin/aucat/safile.c288
-rw-r--r--usr.bin/aucat/safile.h37
-rw-r--r--usr.bin/aucat/sock.c931
-rw-r--r--usr.bin/aucat/sock.h58
-rw-r--r--usr.bin/aucat/wav.c116
-rw-r--r--usr.bin/aucat/wav.h51
28 files changed, 3308 insertions, 1199 deletions
diff --git a/usr.bin/aucat/Makefile b/usr.bin/aucat/Makefile
index 88c9a50e5b0..e6e600f3907 100644
--- a/usr.bin/aucat/Makefile
+++ b/usr.bin/aucat/Makefile
@@ -1,8 +1,8 @@
-# $OpenBSD: Makefile,v 1.3 2008/08/14 09:58:55 ratchov Exp $
+# $OpenBSD: Makefile,v 1.4 2008/10/26 08:49:43 ratchov Exp $
PROG= aucat
SRCS= aucat.c abuf.c aparams.c aproc.c dev.c file.c headers.c \
- dev_sun.c legacy.c
+ safile.c sock.c pipe.c listen.c wav.c legacy.c
CFLAGS+= -DDEBUG -Wall -Wstrict-prototypes -Werror -Wundef
-
+LDADD+= -lsa
.include <bsd.prog.mk>
diff --git a/usr.bin/aucat/abuf.c b/usr.bin/aucat/abuf.c
index 574b80262b2..61a18a17293 100644
--- a/usr.bin/aucat/abuf.c
+++ b/usr.bin/aucat/abuf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: abuf.c,v 1.6 2008/08/14 10:02:10 ratchov Exp $ */
+/* $OpenBSD: abuf.c,v 1.7 2008/10/26 08:49:43 ratchov Exp $ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
*
@@ -23,25 +23,45 @@
* (2) space available to the writer, which is not necessarily unused. It works
* as follows: the write starts filling at offset (start + used), once the data
* is ready, the writer adds to used the count of bytes available.
+ */
+/*
+ * TODO
*
- * TODO:
- *
- * (hard) make abuf_fill() a boolean depending on whether
- * eof is reached. So the caller can do:
- *
- * if (!abuf_fill(buf)) {
- * ...
- * }
+ * use blocks instead of frames for WOK and ROK macros. If necessary
+ * (unlikely) define reader block size and writer blocks size to
+ * ease pipe/socket implementation
*/
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <stdarg.h>
#include "conf.h"
#include "aproc.h"
#include "abuf.h"
+#ifdef DEBUG
+void
+abuf_dprn(int n, struct abuf *buf, char *fmt, ...)
+{
+ va_list ap;
+
+ if (debug_level < n)
+ return;
+ fprintf(stderr, "%s->%s: ",
+ buf->wproc ? buf->wproc->name : "none",
+ buf->rproc ? buf->rproc->name : "none");
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+}
+#define ABUF_DPRN(n, buf, ...) abuf_dprn((n), (buf), __VA_ARGS__)
+#define ABUF_DPR(buf, ...) abuf_dprn(1, (buf), __VA_ARGS__)
+#else
+#define ABUF_DPRN(n, buf, ...) do {} while (0)
+#define ABUF_DPR(buf, ...) do {} while (0)
+#endif
+
struct abuf *
abuf_new(unsigned nfr, unsigned bpf)
{
@@ -51,9 +71,11 @@ abuf_new(unsigned nfr, unsigned bpf)
len = nfr * bpf;
buf = malloc(sizeof(struct abuf) + len);
if (buf == NULL) {
- err(1, "abuf_new: malloc");
+ fprintf(stderr, "abuf_new: out of mem: %u * %u\n", nfr, bpf);
+ abort();
}
buf->bpf = bpf;
+ buf->inuse = 0;
/*
* fill fifo pointers
@@ -66,6 +88,7 @@ abuf_new(unsigned nfr, unsigned bpf)
buf->drop = 0;
buf->rproc = NULL;
buf->wproc = NULL;
+ buf->duplex = NULL;
buf->data = (unsigned char *)buf + sizeof(*buf);
return buf;
}
@@ -73,17 +96,14 @@ abuf_new(unsigned nfr, unsigned bpf)
void
abuf_del(struct abuf *buf)
{
- DPRINTF("abuf_del:\n");
- if (buf->rproc) {
- fprintf(stderr, "abuf_del: has rproc: %s\n", buf->rproc->name);
- abort();
- }
- if (buf->wproc) {
- fprintf(stderr, "abuf_del: has wproc: %s\n", buf->wproc->name);
+ if (buf->duplex)
+ buf->duplex->duplex = NULL;
+#ifdef DEBUG
+ if (buf->rproc || buf->wproc || ABUF_ROK(buf)) {
+ ABUF_DPRN(0, buf, "abuf_del: used = %u\n", buf->used);
abort();
}
- if (buf->used > 0)
- fprintf(stderr, "abuf_del: used = %u\n", buf->used);
+#endif
free(buf);
}
@@ -99,6 +119,14 @@ abuf_rgetblk(struct abuf *buf, unsigned *rsize, unsigned ofs)
used = buf->used - ofs;
if (start >= buf->len)
start -= buf->len;
+#ifdef DEBUG
+ if (start >= buf->len || used > buf->used) {
+ ABUF_DPRN(0, buf, "abuf_rgetblk: "
+ "bad ofs: start = %u used = %u/%u, ofs = %u\n",
+ buf->start, buf->used, buf->len, ofs);
+ abort();
+ }
+#endif
count = buf->len - start;
if (count > used)
count = used;
@@ -112,6 +140,12 @@ abuf_rgetblk(struct abuf *buf, unsigned *rsize, unsigned ofs)
void
abuf_rdiscard(struct abuf *buf, unsigned count)
{
+#ifdef DEBUG
+ if (count > buf->used) {
+ ABUF_DPRN(0, buf, "abuf_rdiscard: bad count %u\n", count);
+ abort();
+ }
+#endif
buf->used -= count;
buf->start += count;
if (buf->start >= buf->len)
@@ -125,6 +159,12 @@ abuf_rdiscard(struct abuf *buf, unsigned count)
void
abuf_wcommit(struct abuf *buf, unsigned count)
{
+#ifdef DEBUG
+ if (count > (buf->len - buf->used)) {
+ ABUF_DPR(buf, "abuf_wcommit: bad count\n");
+ abort();
+ }
+#endif
buf->used += count;
}
@@ -142,7 +182,7 @@ abuf_wgetblk(struct abuf *buf, unsigned *rsize, unsigned ofs)
end -= buf->len;
#ifdef DEBUG
if (end >= buf->len) {
- fprintf(stderr, "abuf_wgetblk: %s -> %s: bad ofs, "
+ ABUF_DPR(buf, "abuf_wgetblk: %s -> %s: bad ofs, "
"start = %u, used = %u, len = %u, ofs = %u\n",
buf->wproc->name, buf->rproc->name,
buf->start, buf->used, buf->len, ofs);
@@ -171,33 +211,24 @@ abuf_flush_do(struct abuf *buf)
count = buf->drop;
if (count > buf->used)
count = buf->used;
+ if (count == 0) {
+ ABUF_DPR(buf, "abuf_flush_do: no data to drop\n");
+ return 0;
+ }
abuf_rdiscard(buf, count);
buf->drop -= count;
- DPRINTF("abuf_flush_do: drop = %u\n", buf->drop);
+ ABUF_DPR(buf, "abuf_flush_do: drop = %u\n", buf->drop);
+ p = buf->rproc;
} else {
+ ABUF_DPRN(4, buf, "abuf_flush_do: in ready\n");
p = buf->rproc;
- if (p == NULL || !p->ops->in(p, buf))
+ if (!p || !p->ops->in(p, buf))
return 0;
}
return 1;
}
/*
- * Notify the read end of the buffer that there is input available
- * and that data can be processed again.
- */
-void
-abuf_flush(struct abuf *buf)
-{
- for (;;) {
- if (!ABUF_ROK(buf))
- break;
- if (!abuf_flush_do(buf))
- break;
- }
-}
-
-/*
* fill the buffer either by generating silence or by calling the aproc
* call-back to provide data. Return 0 if blocked, 1 otherwise
*/
@@ -212,14 +243,93 @@ abuf_fill_do(struct abuf *buf)
data = abuf_wgetblk(buf, &count, 0);
if (count >= buf->silence)
count = buf->silence;
+ if (count == 0) {
+ ABUF_DPR(buf, "abuf_fill_do: no space for silence\n");
+ return 0;
+ }
memset(data, 0, count);
abuf_wcommit(buf, count);
buf->silence -= count;
- DPRINTF("abuf_fill_do: silence = %u\n", buf->silence);
+ ABUF_DPR(buf, "abuf_fill_do: silence = %u\n", buf->silence);
+ p = buf->wproc;
} else {
+ ABUF_DPRN(4, buf, "abuf_fill_do: out avail\n");
p = buf->wproc;
- if (p == NULL || !p->ops->out(p, buf))
+ if (p == NULL || !p->ops->out(p, buf)) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/*
+ * Notify the reader that there will be no more input (producer
+ * disappeared) and destroy the buffer
+ */
+void
+abuf_eof_do(struct abuf *buf)
+{
+ struct aproc *p;
+
+ p = buf->rproc;
+ if (p) {
+ ABUF_DPRN(2, buf, "abuf_eof_do: signaling reader\n");
+ buf->rproc = NULL;
+ LIST_REMOVE(buf, ient);
+ buf->inuse++;
+ p->ops->eof(p, buf);
+ buf->inuse--;
+ } else
+ ABUF_DPR(buf, "abuf_eof_do: no reader, freeng buf\n");
+ abuf_del(buf);
+}
+
+/*
+ * Notify the writer that the buffer has no more consumer,
+ * and destroy the buffer
+ */
+void
+abuf_hup_do(struct abuf *buf)
+{
+ struct aproc *p;
+
+ if (ABUF_ROK(buf)) {
+ ABUF_DPR(buf, "abuf_hup_do: lost %u bytes\n", buf->used);
+ buf->used = 0;
+ }
+ p = buf->wproc;
+ if (p != NULL) {
+ ABUF_DPRN(2, buf, "abuf_hup_do: signaling writer\n");
+ buf->wproc = NULL;
+ LIST_REMOVE(buf, oent);
+ buf->inuse++;
+ p->ops->hup(p, buf);
+ buf->inuse--;
+ } else
+ ABUF_DPR(buf, "abuf_hup_do: no writer, freeng buf\n");
+ abuf_del(buf);
+}
+
+/*
+ * Notify the read end of the buffer that there is input available
+ * and that data can be processed again.
+ */
+int
+abuf_flush(struct abuf *buf)
+{
+ if (buf->inuse) {
+ ABUF_DPRN(4, buf, "abuf_flush: blocked\n");
+ } else {
+ buf->inuse++;
+ for (;;) {
+ if (!abuf_flush_do(buf))
+ break;
+ }
+ buf->inuse--;
+ if (ABUF_HUP(buf)) {
+ abuf_hup_do(buf);
return 0;
+ }
}
return 1;
}
@@ -229,18 +339,28 @@ abuf_fill_do(struct abuf *buf)
* written again. This routine can only be called from the out()
* call-back of the reader.
*
- * NOTE: The abuf writer may reach eof condition and disappear, dont keep
- * references to abuf->wproc.
+ * Return 1 if the buffer was filled, and 0 if eof condition occured. The
+ * reader must detach the buffer on EOF condition, since it's aproc->eof()
+ * call-back will never be called.
*/
-void
+int
abuf_fill(struct abuf *buf)
{
- for (;;) {
- if (!ABUF_WOK(buf))
- break;
- if (!abuf_fill_do(buf))
- break;
+ if (buf->inuse) {
+ ABUF_DPRN(4, buf, "abuf_fill: blocked\n");
+ } else {
+ buf->inuse++;
+ for (;;) {
+ if (!abuf_fill_do(buf))
+ break;
+ }
+ buf->inuse--;
+ if (ABUF_EOF(buf)) {
+ abuf_eof_do(buf);
+ return 0;
+ }
}
+ return 1;
}
/*
@@ -254,24 +374,35 @@ abuf_fill(struct abuf *buf)
void
abuf_run(struct abuf *buf)
{
- struct aproc *p;
int canfill = 1, canflush = 1;
+ if (buf->inuse) {
+ ABUF_DPRN(4, buf, "abuf_run: blocked\n");
+ return;
+ }
+ buf->inuse++;
for (;;) {
- if (ABUF_EOF(buf)) {
- p = buf->rproc;
- DPRINTFN(2, "abuf_run: %s: got eof\n", p->name);
- p->ops->eof(p, buf);
- buf->rproc = NULL;
- abuf_del(buf);
- return;
- }
- if (ABUF_WOK(buf) && canfill) {
- canfill = abuf_fill_do(buf);
- } else if (ABUF_ROK(buf) && canflush) {
- canflush = abuf_flush_do(buf);
+ if (canfill) {
+ if (!abuf_fill_do(buf))
+ canfill = 0;
+ else
+ canflush = 1;
+ } else if (canflush) {
+ if (!abuf_flush_do(buf))
+ canflush = 0;
+ else
+ canfill = 1;
} else
- break; /* can neither read nor write */
+ break;
+ }
+ buf->inuse--;
+ if (ABUF_EOF(buf)) {
+ abuf_eof_do(buf);
+ return;
+ }
+ if (ABUF_HUP(buf)) {
+ abuf_hup_do(buf);
+ return;
}
}
@@ -285,31 +416,32 @@ abuf_eof(struct abuf *buf)
{
#ifdef DEBUG
if (buf->wproc == NULL) {
- fprintf(stderr, "abuf_eof: no writer\n");
+ ABUF_DPR(buf, "abuf_eof: no writer\n");
abort();
}
#endif
- DPRINTFN(2, "abuf_eof: requested by %s\n", buf->wproc->name);
+ ABUF_DPRN(2, buf, "abuf_eof: requested\n");
+ LIST_REMOVE(buf, oent);
buf->wproc = NULL;
if (buf->rproc != NULL) {
- abuf_flush(buf);
+ if (!abuf_flush(buf))
+ return;
if (ABUF_ROK(buf)) {
/*
* Could not flush everything, the reader will
* have a chance to delete the abuf later.
*/
- DPRINTFN(2, "abuf_eof: %s will drain the buf later\n",
- buf->rproc->name);
+ ABUF_DPRN(2, buf, "abuf_eof: will drain later\n");
return;
}
- DPRINTFN(2, "abuf_eof: signaling %s\n", buf->rproc->name);
- buf->rproc->ops->eof(buf->rproc, buf);
- buf->rproc = NULL;
}
- abuf_del(buf);
+ if (buf->inuse) {
+ ABUF_DPRN(2, buf, "abuf_eof: signal blocked\n");
+ return;
+ }
+ abuf_eof_do(buf);
}
-
/*
* Notify the writer that the buffer has no more consumer,
* and that no more data will accepted.
@@ -319,21 +451,54 @@ abuf_hup(struct abuf *buf)
{
#ifdef DEBUG
if (buf->rproc == NULL) {
- fprintf(stderr, "abuf_hup: no reader\n");
+ ABUF_DPR(buf, "abuf_hup: no reader\n");
abort();
}
#endif
- DPRINTFN(2, "abuf_hup: initiated by %s\n", buf->rproc->name);
+ ABUF_DPRN(2, buf, "abuf_hup: initiated\n");
+
buf->rproc = NULL;
+ LIST_REMOVE(buf, ient);
if (buf->wproc != NULL) {
- if (ABUF_ROK(buf)) {
- warnx("abuf_hup: %s: lost %u bytes",
- buf->wproc->name, buf->used);
- buf->used = 0;
+ if (buf->inuse) {
+ ABUF_DPRN(2, buf, "abuf_hup: signal blocked\n");
+ return;
}
- DPRINTFN(2, "abuf_hup: signaling %s\n", buf->wproc->name);
- buf->wproc->ops->hup(buf->wproc, buf);
- buf->wproc = NULL;
}
- abuf_del(buf);
+ abuf_hup_do(buf);
+}
+
+/*
+ * Notify the reader of the change of its real-time position
+ */
+void
+abuf_ipos(struct abuf *buf, int delta)
+{
+ struct aproc *p = buf->rproc;
+
+ if (p && p->ops->ipos) {
+ buf->inuse++;
+ p->ops->ipos(p, buf, delta);
+ buf->inuse--;
+ }
+ if (ABUF_HUP(buf))
+ abuf_hup_do(buf);
+}
+
+/*
+ * Notify the writer of the change of its real-time position
+ */
+void
+abuf_opos(struct abuf *buf, int delta)
+{
+ struct aproc *p = buf->wproc;
+
+
+ if (p && p->ops->opos) {
+ buf->inuse++;
+ p->ops->opos(p, buf, delta);
+ buf->inuse--;
+ }
+ if (ABUF_HUP(buf))
+ abuf_hup_do(buf);
}
diff --git a/usr.bin/aucat/abuf.h b/usr.bin/aucat/abuf.h
index e214eb132e7..aefe8698fb8 100644
--- a/usr.bin/aucat/abuf.h
+++ b/usr.bin/aucat/abuf.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: abuf.h,v 1.8 2008/08/14 15:25:16 ratchov Exp $ */
+/* $OpenBSD: abuf.h,v 1.9 2008/10/26 08:49:43 ratchov Exp $ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
*
@@ -41,7 +41,7 @@ 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 */
-
+
/*
* fifo parameters
*/
@@ -54,6 +54,8 @@ struct abuf {
unsigned drop; /* bytes to drop on next read */
struct aproc *rproc; /* reader */
struct aproc *wproc; /* writer */
+ struct abuf *duplex; /* link to buffer of the other direction */
+ unsigned inuse; /* in abuf_{flush,fill,run}() */
unsigned char *data; /* actual data (immediately following) */
};
@@ -74,6 +76,11 @@ struct abuf {
#define ABUF_EOF(b) (!ABUF_ROK(b) && (b)->wproc == NULL)
/*
+ * the buffer is empty and has no more writer
+ */
+#define ABUF_HUP(b) (!ABUF_WOK(b) && (b)->rproc == NULL)
+
+/*
* similar to !ABUF_WOK, but is used for file i/o, where
* operation may not involve an integer number of frames
*/
@@ -91,10 +98,12 @@ unsigned char *abuf_rgetblk(struct abuf *, unsigned *, unsigned);
unsigned char *abuf_wgetblk(struct abuf *, unsigned *, unsigned);
void abuf_rdiscard(struct abuf *, unsigned);
void abuf_wcommit(struct abuf *, unsigned);
-void abuf_fill(struct abuf *);
-void abuf_flush(struct abuf *);
+int abuf_fill(struct abuf *);
+int abuf_flush(struct abuf *);
void abuf_eof(struct abuf *);
void abuf_hup(struct abuf *);
void abuf_run(struct abuf *);
+void abuf_ipos(struct abuf *, int);
+void abuf_opos(struct abuf *, int);
#endif /* !defined(ABUF_H) */
diff --git a/usr.bin/aucat/amsg.h b/usr.bin/aucat/amsg.h
new file mode 100644
index 00000000000..510d45ad1ea
--- /dev/null
+++ b/usr.bin/aucat/amsg.h
@@ -0,0 +1,92 @@
+/* $OpenBSD: amsg.h,v 1.1 2008/10/26 08:49:43 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 SOCKET_H
+#define SOCKET_H
+
+#include <stdint.h>
+
+/*
+ * WARNING: since the protocol may be simultaneously used by static
+ * binaries or by different versions of a shared library, we are not
+ * allowed to change the packet binary representation in a backward
+ * incompatible way.
+ */
+struct amsg {
+#define AMSG_ACK 0 /* ack for START/STOP */
+#define AMSG_GETPAR 1 /* get the current parameters */
+#define AMSG_SETPAR 2 /* set the current parameters */
+#define AMSG_START 3 /* request the server to start the stream */
+#define AMSG_STOP 4 /* request the server to stop the stream */
+#define AMSG_DATA 5 /* data block */
+#define AMSG_MOVE 6 /* position changed */
+#define AMSG_GETCAP 7 /* get capabilities */
+ uint32_t cmd;
+ uint32_t __pad;
+ union {
+ struct amsg_par {
+#define AMSG_PLAY 1 /* will play */
+#define AMSG_REC 2 /* will record */
+ uint8_t mode; /* a bitmap of above */
+#define AMSG_IGNORE 0 /* loose sync */
+#define AMSG_SYNC 1 /* resync after xrun */
+#define AMSG_ERROR 2 /* kill the stream */
+ uint8_t xrun; /* one of above */
+ uint8_t bps; /* bytes per sample */
+ uint8_t bits; /* actually used bits */
+ uint8_t msb; /* 1 if MSB justified */
+ uint8_t le; /* 1 if little endian */
+ uint8_t sig; /* 1 if signed */
+ uint8_t __pad1;
+ uint16_t pchan; /* play channels */
+ uint16_t rchan; /* record channels */
+ uint32_t rate; /* frames per second */
+ uint32_t bufsz; /* buffered frames */
+ uint32_t round;
+ uint32_t _reserved[2]; /* for future use */
+ } par;
+ struct amsg_cap {
+ uint32_t rate; /* native rate */
+ uint32_t rate_div; /* divisor of emul. rates */
+ uint16_t rchan; /* native rec channels */
+ uint16_t pchan; /* native play channels */
+ uint8_t bits; /* native bits per sample */
+ uint8_t bps; /* native ytes per sample */
+ uint8_t _reserved[10]; /* for future use */
+ } cap;
+ struct amsg_data {
+#define AMSG_DATAMAX 0x1000
+ uint32_t size;
+ } data;
+ struct amsg_ts {
+ int32_t delta;
+ } ts;
+ } u;
+};
+
+/*
+ * initialize an amsg structure: fill all fields with 0xff, so the read
+ * can test which fields were set
+ */
+#define AMSG_INIT(m) do { memset((m), 0xff, sizeof(struct amsg)); } while (0)
+
+/*
+ * since the structure is memset to 0xff, the MSB can be used to check
+ * if any filed was set
+ */
+#define AMSG_ISSET(x) (((x) & (1 << (8 * sizeof(x) - 1))) == 0)
+
+#endif /* !defined(SOCKET_H) */
diff --git a/usr.bin/aucat/aparams.c b/usr.bin/aucat/aparams.c
index cbecee24ede..40cc9cd7fb3 100644
--- a/usr.bin/aucat/aparams.c
+++ b/usr.bin/aucat/aparams.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: aparams.c,v 1.1 2008/05/23 07:15:46 ratchov Exp $ */
+/* $OpenBSD: aparams.c,v 1.2 2008/10/26 08:49:43 ratchov Exp $ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
*
@@ -22,6 +22,128 @@
#include "aparams.h"
/*
+ * Generate a string corresponding to the encoding in par,
+ * return the length of the resulting string
+ */
+int
+aparams_enctostr(struct aparams *par, char *ostr)
+{
+ char *p = ostr;
+
+ *p++ = par->sig ? 's' : 'u';
+ if (par->bits > 9)
+ *p++ = '0' + par->bits / 10;
+ *p++ = '0' + par->bits % 10;
+ if (par->bps > 1) {
+ *p++ = par->le ? 'l' : 'b';
+ *p++ = 'e';
+ if (par->bps != APARAMS_BPS(par->bits) ||
+ par->bits < par->bps * 8) {
+ *p++ = par->bps + '0';
+ if (par->bits < par->bps * 8) {
+ *p++ = par->msb ? 'm' : 'l';
+ *p++ = 's';
+ *p++ = 'b';
+ }
+ }
+ }
+ *p++ = '\0';
+ return p - ostr - 1;
+}
+
+/*
+ * Parse an encoding string, examples: s8, u8, s16, s16le, s24be ...
+ * set *istr to the char following the encoding. Retrun the number
+ * of bytes consumed
+ */
+int
+aparams_strtoenc(struct aparams *par, char *istr)
+{
+ char *p = istr;
+ int i, sig, bits, le, bps, msb;
+
+#define IS_SEP(c) \
+ (((c) < 'a' || (c) > 'z') && \
+ ((c) < 'A' || (c) > 'Z') && \
+ ((c) < '0' || (c) > '9'))
+
+ /*
+ * get signedness
+ */
+ if (*p == 's') {
+ sig = 1;
+ } else if (*p == 'u') {
+ sig = 0;
+ } else
+ return 0;
+ p++;
+
+ /*
+ * get number of bits per sample
+ */
+ bits = 0;
+ for (i = 0; i < 2; i++) {
+ if (*p < '0' || *p > '9')
+ break;
+ bits = (bits * 10) + *p - '0';
+ p++;
+ }
+ if (bits < BITS_MIN || bits > BITS_MAX)
+ return 0;
+ bps = APARAMS_BPS(bits);
+ msb = 1;
+ le = NATIVE_LE;
+
+ /*
+ * get (optionnal) endianness
+ */
+ if (p[0] == 'l' && p[1] == 'e') {
+ le = 1;
+ p += 2;
+ } else if (p[0] == 'b' && p[1] == 'e') {
+ le = 0;
+ p += 2;
+ } else if (IS_SEP(*p)) {
+ goto done;
+ } else
+ return 0;
+
+ /*
+ * get (optionnal) number of bytes
+ */
+ if (*p >= '0' && *p <= '9') {
+ bps = *p - '0';
+ if (bps < (bits + 7) / 8 ||
+ bps > (BITS_MAX + 7) / 8)
+ return 0;
+ p++;
+
+ /*
+ * get (optionnal) alignement
+ */
+ if (p[0] == 'm' && p[1] == 's' && p[2] == 'b') {
+ msb = 1;
+ p += 3;
+ } else if (p[0] == 'l' && p[1] == 's' && p[2] == 'b') {
+ msb = 0;
+ p += 3;
+ } else if (IS_SEP(*p)) {
+ goto done;
+ } else
+ return 0;
+ } else if (!IS_SEP(*p))
+ return 0;
+
+done:
+ par->msb = msb;
+ par->sig = sig;
+ par->bits = bits;
+ par->bps = bps;
+ par->le = le;
+ return p - istr;
+}
+
+/*
* Initialise parameters structure with the defaults natively supported
* by the machine.
*/
@@ -44,14 +166,10 @@ aparams_init(struct aparams *par, unsigned cmin, unsigned cmax, unsigned rate)
void
aparams_print(struct aparams *par)
{
- fprintf(stderr, "%c", par->sig ? 's' : 'u');
- fprintf(stderr, "%u", par->bits);
- if (par->bps > 1)
- fprintf(stderr, "%s", par->le ? "le" : "be");
- if ((par->bits & (par->bits - 1)) != 0 || par->bits != 8 * par->bps) {
- fprintf(stderr, "/%u", par->bps);
- fprintf(stderr, "%s", par->msb ? "msb" : "lsb");
- }
+ char enc[ENCMAX];
+
+ aparams_enctostr(par, enc);
+ fprintf(stderr, "%s", enc);
fprintf(stderr, ",%u:%u", par->cmin, par->cmax);
fprintf(stderr, ",%uHz", par->rate);
}
@@ -92,3 +210,14 @@ aparams_bpf(struct aparams *par)
{
return (par->cmax - par->cmin + 1) * par->bps;
}
+
+void
+aparams_copyenc(struct aparams *dst, struct aparams *src)
+{
+ dst->sig = src->sig;
+ dst->le = src->le;
+ dst->msb = src->msb;
+ dst->bits = src->bits;
+ dst->bps = src->bps;
+}
+
diff --git a/usr.bin/aucat/aparams.h b/usr.bin/aucat/aparams.h
index ce1bfa68490..8d394502229 100644
--- a/usr.bin/aucat/aparams.h
+++ b/usr.bin/aucat/aparams.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: aparams.h,v 1.1 2008/05/23 07:15:46 ratchov Exp $ */
+/* $OpenBSD: aparams.h,v 1.2 2008/10/26 08:49:43 ratchov Exp $ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
*
@@ -19,11 +19,18 @@
#include <sys/param.h>
-#define CHAN_MAX 16 /* max number of channels */
-#define RATE_MIN 1 /* min sample rate */
-#define RATE_MAX (1 << 30) /* max sample rate */
+#define NCHAN_MAX 16 /* max channel in a stream */
+#define RATE_MIN 4000 /* min sample rate */
+#define RATE_MAX 192000 /* max sample rate */
+#define BITS_MIN 1 /* min bits per sample */
#define BITS_MAX 32 /* max bits per sample */
+/*
+ * maximum size of the encording string (the longest possible
+ * encoding is ``s24le3msb'')
+ */
+#define ENCMAX 10
+
#if BYTE_ORDER == LITTLE_ENDIAN
#define NATIVE_LE 1
#elif BYTE_ORDER == BIG_ENDIAN
@@ -33,6 +40,11 @@
#endif
/*
+ * default bytes per sample for the given bits per sample
+ */
+#define APARAMS_BPS(bits) (((bits) <= 8) ? 1 : (((bits) <= 16) ? 2 : 4))
+
+/*
* encoding specification
*/
struct aparams {
@@ -62,5 +74,8 @@ void aparams_print(struct aparams *);
void aparams_print2(struct aparams *, struct aparams *);
int aparams_eq(struct aparams *, struct aparams *);
unsigned aparams_bpf(struct aparams *);
+int aparams_strtoenc(struct aparams *, char *);
+int aparams_enctostr(struct aparams *, char *);
+void aparams_copyenc(struct aparams *, struct aparams *);
#endif /* !defined(APARAMS_H) */
diff --git a/usr.bin/aucat/aproc.c b/usr.bin/aucat/aproc.c
index fdace667490..527a874042d 100644
--- a/usr.bin/aucat/aproc.c
+++ b/usr.bin/aucat/aproc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: aproc.c,v 1.11 2008/08/20 14:22:50 ratchov Exp $ */
+/* $OpenBSD: aproc.c,v 1.12 2008/10/26 08:49:43 ratchov Exp $ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
*
@@ -39,6 +39,7 @@
*
* (hard) add a lowpass filter for the resampler. Quality is
* not acceptable as is.
+ *
*/
#include <err.h>
#include <limits.h>
@@ -70,9 +71,22 @@ aproc_new(struct aproc_ops *ops, char *name)
void
aproc_del(struct aproc *p)
{
+ struct abuf *i;
+
+ DPRINTF("aproc_del: %s(%s): terminating...\n", p->ops->name, p->name);
+
if (p->ops->done)
p->ops->done(p);
- DPRINTF("aproc_del: %s: %s: deleted\n", p->ops->name, p->name);
+
+ while (!LIST_EMPTY(&p->ibuflist)) {
+ i = LIST_FIRST(&p->ibuflist);
+ abuf_hup(i);
+ }
+ while (!LIST_EMPTY(&p->obuflist)) {
+ i = LIST_FIRST(&p->obuflist);
+ abuf_eof(i);
+ }
+ DPRINTF("aproc_del: %s(%s): freed\n", p->ops->name, p->name);
free(p);
}
@@ -94,6 +108,30 @@ aproc_setout(struct aproc *p, struct abuf *obuf)
p->ops->newout(p, obuf);
}
+void
+aproc_ipos(struct aproc *p, struct abuf *ibuf, int delta)
+{
+ struct abuf *obuf;
+
+ DPRINTFN(3, "aproc_ipos: %s: delta = %d\n", p->name, delta);
+
+ LIST_FOREACH(obuf, &p->obuflist, oent) {
+ abuf_ipos(obuf, delta);
+ }
+}
+
+void
+aproc_opos(struct aproc *p, struct abuf *obuf, int delta)
+{
+ struct abuf *ibuf;
+
+ DPRINTFN(3, "aproc_opos: %s: delta = %d\n", p->name, delta);
+
+ LIST_FOREACH(ibuf, &p->ibuflist, ient) {
+ abuf_opos(ibuf, delta);
+ }
+}
+
int
rpipe_in(struct aproc *p, struct abuf *ibuf_dummy)
{
@@ -104,13 +142,16 @@ rpipe_in(struct aproc *p, struct abuf *ibuf_dummy)
DPRINTFN(3, "rpipe_in: %s\n", p->name);
- if (ABUF_FULL(obuf))
+ if (ABUF_FULL(obuf) || !(f->state & FILE_ROK))
return 0;
data = abuf_wgetblk(obuf, &count, 0);
count = file_read(f, data, count);
+ if (count == 0)
+ return 0;
abuf_wcommit(obuf, count);
- abuf_flush(obuf);
- return !ABUF_FULL(obuf);
+ if (!abuf_flush(obuf))
+ return 0;
+ return 1;
}
int
@@ -120,14 +161,18 @@ rpipe_out(struct aproc *p, struct abuf *obuf)
unsigned char *data;
unsigned count;
+ if (f->refs > 0)
+ return 0;
DPRINTFN(3, "rpipe_out: %s\n", p->name);
-
- if (!(f->state & FILE_ROK))
+
+ if (ABUF_FULL(obuf) || !(f->state & FILE_ROK))
return 0;
data = abuf_wgetblk(obuf, &count, 0);
count = file_read(f, data, count);
+ if (count == 0)
+ return 0;
abuf_wcommit(obuf, count);
- return f->state & FILE_ROK;
+ return 1;
}
void
@@ -136,14 +181,14 @@ rpipe_done(struct aproc *p)
struct file *f = p->u.io.file;
f->rproc = NULL;
- f->events &= ~POLLIN;
+ if (f->wproc == NULL)
+ file_del(f);
}
void
rpipe_eof(struct aproc *p, struct abuf *ibuf_dummy)
{
DPRINTFN(3, "rpipe_eof: %s\n", p->name);
- abuf_eof(LIST_FIRST(&p->obuflist));
aproc_del(p);
}
@@ -155,7 +200,16 @@ rpipe_hup(struct aproc *p, struct abuf *obuf)
}
struct aproc_ops rpipe_ops = {
- "rpipe", rpipe_in, rpipe_out, rpipe_eof, rpipe_hup, NULL, NULL, rpipe_done
+ "rpipe",
+ rpipe_in,
+ rpipe_out,
+ rpipe_eof,
+ rpipe_hup,
+ NULL, /* newin */
+ NULL, /* newout */
+ aproc_ipos,
+ aproc_opos,
+ rpipe_done
};
struct aproc *
@@ -165,8 +219,7 @@ rpipe_new(struct file *f)
p = aproc_new(&rpipe_ops, f->name);
p->u.io.file = f;
- f->rproc = p;
- f->events |= POLLIN;
+ f->rproc = p;
return p;
}
@@ -176,7 +229,8 @@ wpipe_done(struct aproc *p)
struct file *f = p->u.io.file;
f->wproc = NULL;
- f->events &= ~POLLOUT;
+ if (f->rproc == NULL)
+ file_del(f);
}
int
@@ -186,15 +240,18 @@ wpipe_in(struct aproc *p, struct abuf *ibuf)
unsigned char *data;
unsigned count;
+ if (f->refs > 0)
+ return 0;
DPRINTFN(3, "wpipe_in: %s\n", p->name);
- if (!(f->state & FILE_WOK))
+ if (ABUF_EMPTY(ibuf) || !(f->state & FILE_WOK))
return 0;
-
data = abuf_rgetblk(ibuf, &count, 0);
count = file_write(f, data, count);
+ if (count == 0)
+ return 0;
abuf_rdiscard(ibuf, count);
- return f->state & FILE_WOK;
+ return 1;
}
int
@@ -207,17 +264,21 @@ wpipe_out(struct aproc *p, struct abuf *obuf_dummy)
DPRINTFN(3, "wpipe_out: %s\n", p->name);
- if (ABUF_EMPTY(ibuf))
+ if (!abuf_fill(ibuf)) {
+ DPRINTFN(3, "wpipe_out: fill failed\n");
+ return 0;
+ }
+ if (ABUF_EMPTY(ibuf) || !(f->state & FILE_WOK))
return 0;
data = abuf_rgetblk(ibuf, &count, 0);
- count = file_write(f, data, count);
- abuf_rdiscard(ibuf, count);
- if (ABUF_EOF(ibuf)) {
- abuf_hup(ibuf);
- aproc_del(p);
+ if (count == 0) {
+ DPRINTF("wpipe_out: %s: underrun\n", p->name);
return 0;
}
- abuf_fill(ibuf);
+ count = file_write(f, data, count);
+ if (count == 0)
+ return 0;
+ abuf_rdiscard(ibuf, count);
return 1;
}
@@ -232,12 +293,20 @@ void
wpipe_hup(struct aproc *p, struct abuf *obuf_dummy)
{
DPRINTFN(3, "wpipe_hup: %s\n", p->name);
- abuf_hup(LIST_FIRST(&p->ibuflist));
aproc_del(p);
}
struct aproc_ops wpipe_ops = {
- "wpipe", wpipe_in, wpipe_out, wpipe_eof, wpipe_hup, NULL, NULL, wpipe_done
+ "wpipe",
+ wpipe_in,
+ wpipe_out,
+ wpipe_eof,
+ wpipe_hup,
+ NULL, /* newin */
+ NULL, /* newout */
+ aproc_ipos,
+ aproc_opos,
+ wpipe_done
};
struct aproc *
@@ -248,7 +317,6 @@ wpipe_new(struct file *f)
p = aproc_new(&wpipe_ops, f->name);
p->u.io.file = f;
f->wproc = p;
- f->events |= POLLOUT;
return p;
}
@@ -265,6 +333,7 @@ mix_bzero(struct aproc *p)
DPRINTFN(4, "mix_bzero: used = %u, todo = %u\n",
obuf->used, obuf->mixtodo);
odata = (short *)abuf_wgetblk(obuf, &ocount, obuf->mixtodo);
+ ocount -= ocount % obuf->bpf;
if (ocount == 0)
return;
memset(odata, 0, ocount);
@@ -286,10 +355,12 @@ mix_badd(struct abuf *ibuf, struct abuf *obuf)
obuf->mixtodo, ibuf->mixdone);
idata = (short *)abuf_rgetblk(ibuf, &icount, 0);
+ icount -= icount % ibuf->bpf;
if (icount == 0)
return;
odata = (short *)abuf_wgetblk(obuf, &ocount, ibuf->mixdone);
+ ocount -= ocount % obuf->bpf;
if (ocount == 0)
return;
@@ -306,16 +377,6 @@ mix_badd(struct abuf *ibuf, struct abuf *obuf)
scount, ibuf->mixdone, obuf->mixtodo);
}
-/*
- * Remove an input stream from the mixer.
- */
-void
-mix_rm(struct aproc *p, struct abuf *ibuf)
-{
- LIST_REMOVE(ibuf, ient);
- DPRINTF("mix_rm: %s\n", p->name);
-}
-
int
mix_in(struct aproc *p, struct abuf *ibuf)
{
@@ -325,8 +386,9 @@ mix_in(struct aproc *p, struct abuf *ibuf)
DPRINTFN(4, "mix_in: used = %u, done = %u, todo = %u\n",
ibuf->used, ibuf->mixdone, obuf->mixtodo);
- if (ibuf->mixdone >= obuf->mixtodo)
+ if (!ABUF_ROK(ibuf) || ibuf->mixdone == obuf->mixtodo)
return 0;
+
mix_badd(ibuf, obuf);
ocount = obuf->mixtodo;
LIST_FOREACH(i, &p->ibuflist, ient) {
@@ -337,21 +399,18 @@ mix_in(struct aproc *p, struct abuf *ibuf)
return 0;
abuf_wcommit(obuf, ocount);
+ p->u.mix.lat += ocount / obuf->bpf;
obuf->mixtodo -= ocount;
- abuf_flush(obuf);
+ if (!abuf_flush(obuf))
+ return 0; /* hup */
mix_bzero(p);
- for (i = LIST_FIRST(&p->ibuflist); i != LIST_END(&p->ibuflist); i = inext) {
+ for (i = LIST_FIRST(&p->ibuflist); i != NULL; i = inext) {
inext = LIST_NEXT(i, ient);
i->mixdone -= ocount;
- if (i != ibuf && i->mixdone < obuf->mixtodo) {
- if (ABUF_EOF(i)) {
- mix_rm(p, i);
- abuf_hup(i);
- continue;
- }
+ if (i->mixdone < obuf->mixtodo)
mix_badd(i, obuf);
- abuf_fill(i);
- }
+ if (!abuf_fill(i))
+ continue;
}
return 1;
}
@@ -365,14 +424,18 @@ mix_out(struct aproc *p, struct abuf *obuf)
DPRINTFN(4, "mix_out: used = %u, todo = %u\n",
obuf->used, obuf->mixtodo);
+ if (!ABUF_WOK(obuf))
+ return 0;
+
mix_bzero(p);
ocount = obuf->mixtodo;
- for (i = LIST_FIRST(&p->ibuflist); i != LIST_END(&p->ibuflist); i = inext) {
+ for (i = LIST_FIRST(&p->ibuflist); i != NULL; i = inext) {
inext = LIST_NEXT(i, ient);
+ if (!abuf_fill(i))
+ continue;
if (!ABUF_ROK(i)) {
if ((p->u.mix.flags & MIX_DROP) && i->mixdone == 0) {
if (i->xrun == XRUN_ERROR) {
- mix_rm(p, i);
abuf_hup(i);
continue;
}
@@ -380,28 +443,33 @@ mix_out(struct aproc *p, struct abuf *obuf)
i->mixdone += drop;
if (i->xrun == XRUN_SYNC)
i->drop += drop;
+ else {
+ abuf_opos(i, -(int)(drop / i->bpf));
+ if (i->duplex) {
+ DPRINTF("mix_out: duplex %u\n",
+ drop);
+ i->duplex->drop += drop *
+ i->duplex->bpf / i->bpf;
+ abuf_ipos(i->duplex,
+ -(int)(drop / i->bpf));
+ }
+ }
DPRINTF("mix_out: drop = %u\n", i->drop);
}
} else
mix_badd(i, obuf);
if (ocount > i->mixdone)
ocount = i->mixdone;
- if (ABUF_EOF(i)) {
- mix_rm(p, i);
- abuf_hup(i);
- continue;
- }
- abuf_fill(i);
}
if (ocount == 0)
return 0;
if (LIST_EMPTY(&p->ibuflist) && (p->u.mix.flags & MIX_AUTOQUIT)) {
DPRINTF("mix_out: nothing more to do...\n");
- obuf->wproc = NULL;
aproc_del(p);
return 0;
}
abuf_wcommit(obuf, ocount);
+ p->u.mix.lat += ocount / obuf->bpf;
obuf->mixtodo -= ocount;
LIST_FOREACH(i, &p->ibuflist, ient) {
i->mixdone -= ocount;
@@ -415,7 +483,8 @@ mix_eof(struct aproc *p, struct abuf *ibuf)
struct abuf *obuf = LIST_FIRST(&p->obuflist);
DPRINTF("mix_eof: %s: detached\n", p->name);
- mix_rm(p, ibuf);
+ mix_setmaster(p);
+
/*
* If there's no more inputs, abuf_run() will trigger the eof
* condition and propagate it, so no need to handle it here.
@@ -431,7 +500,6 @@ mix_hup(struct aproc *p, struct abuf *obuf)
while (!LIST_EMPTY(&p->ibuflist)) {
ibuf = LIST_FIRST(&p->ibuflist);
- mix_rm(p, ibuf);
abuf_hup(ibuf);
}
DPRINTF("mix_hup: %s: done\n", p->name);
@@ -444,6 +512,7 @@ mix_newin(struct aproc *p, struct abuf *ibuf)
ibuf->mixdone = 0;
ibuf->mixvol = ADATA_UNIT;
ibuf->xrun = XRUN_IGNORE;
+ mix_setmaster(p);
}
void
@@ -453,17 +522,36 @@ mix_newout(struct aproc *p, struct abuf *obuf)
mix_bzero(p);
}
+void
+mix_opos(struct aproc *p, struct abuf *obuf, int delta)
+{
+ DPRINTFN(3, "mix_opos: lat = %d/%d\n", p->u.mix.lat, p->u.mix.maxlat);
+ p->u.mix.lat -= delta;
+ aproc_opos(p, obuf, delta);
+}
+
struct aproc_ops mix_ops = {
- "mix", mix_in, mix_out, mix_eof, mix_hup, mix_newin, mix_newout, NULL
+ "mix",
+ mix_in,
+ mix_out,
+ mix_eof,
+ mix_hup,
+ mix_newin,
+ mix_newout,
+ aproc_ipos,
+ mix_opos,
+ NULL
};
struct aproc *
-mix_new(void)
+mix_new(char *name, int maxlat)
{
struct aproc *p;
- p = aproc_new(&mix_ops, "softmix");
+ p = aproc_new(&mix_ops, name);
p->u.mix.flags = 0;
+ p->u.mix.lat = 0;
+ p->u.mix.maxlat = maxlat;
return p;
}
@@ -473,6 +561,7 @@ mix_pushzero(struct aproc *p)
struct abuf *obuf = LIST_FIRST(&p->obuflist);
abuf_wcommit(obuf, obuf->mixtodo);
+ p->u.mix.lat += obuf->mixtodo / obuf->bpf;
obuf->mixtodo = 0;
abuf_run(obuf);
mix_bzero(p);
@@ -504,63 +593,67 @@ sub_bcopy(struct abuf *ibuf, struct abuf *obuf)
unsigned icount, ocount, scount;
idata = abuf_rgetblk(ibuf, &icount, obuf->subdone);
+ icount -= icount % ibuf->bpf;
if (icount == 0)
return;
odata = abuf_wgetblk(obuf, &ocount, 0);
+ ocount -= icount % obuf->bpf;
if (ocount == 0)
return;
scount = (icount < ocount) ? icount : ocount;
memcpy(odata, idata, scount);
- abuf_wcommit(obuf, scount);
+ abuf_wcommit(obuf, scount);
obuf->subdone += scount;
DPRINTFN(4, "sub_bcopy: %u bytes\n", scount);
}
-void
-sub_rm(struct aproc *p, struct abuf *obuf)
-{
- LIST_REMOVE(obuf, oent);
- DPRINTF("sub_rm: %s\n", p->name);
-}
-
int
sub_in(struct aproc *p, struct abuf *ibuf)
{
struct abuf *i, *inext;
unsigned done, drop;
- int again;
-
- again = 1;
+
+ if (!ABUF_ROK(ibuf))
+ return 0;
done = ibuf->used;
- for (i = LIST_FIRST(&p->obuflist); i != LIST_END(&p->obuflist); i = inext) {
+ for (i = LIST_FIRST(&p->obuflist); i != NULL; i = inext) {
inext = LIST_NEXT(i, oent);
if (!ABUF_WOK(i)) {
if ((p->u.sub.flags & SUB_DROP) && i->subdone == 0) {
if (i->xrun == XRUN_ERROR) {
- sub_rm(p, i);
abuf_eof(i);
continue;
}
drop = ibuf->used;
if (i->xrun == XRUN_SYNC)
i->silence += drop;
+ else {
+ abuf_ipos(i, -(int)(drop / i->bpf));
+ if (i->duplex) {
+ DPRINTF("sub_in: duplex %u\n",
+ drop);
+ i->duplex->silence += drop *
+ i->duplex->bpf / i->bpf;
+ abuf_opos(i->duplex,
+ -(int)(drop / i->bpf));
+ }
+ }
i->subdone += drop;
- DPRINTF("sub_in: silence = %u\n", i->silence);
+ DPRINTF("sub_in: silence = %u\n", i->silence);
}
- } else {
+ } else
sub_bcopy(ibuf, i);
- abuf_flush(i);
- }
- if (!ABUF_WOK(i))
- again = 0;
if (done > i->subdone)
done = i->subdone;
+ if (!abuf_flush(i))
+ continue;
}
LIST_FOREACH(i, &p->obuflist, oent) {
i->subdone -= done;
}
abuf_rdiscard(ibuf, done);
- return again;
+ p->u.sub.lat -= done / ibuf->bpf;
+ return 1;
}
int
@@ -570,40 +663,28 @@ sub_out(struct aproc *p, struct abuf *obuf)
struct abuf *i, *inext;
unsigned done;
- if (obuf->subdone >= ibuf->used)
+ if (!ABUF_WOK(obuf))
+ return 0;
+ if (!abuf_fill(ibuf)) {
+ return 0;
+ }
+ if (obuf->subdone == ibuf->used)
return 0;
-
- sub_bcopy(ibuf, obuf);
done = ibuf->used;
- LIST_FOREACH(i, &p->obuflist, oent) {
- if (i != obuf && ABUF_WOK(i)) {
- sub_bcopy(ibuf, i);
- abuf_flush(i);
- }
+ for (i = LIST_FIRST(&p->obuflist); i != NULL; i = inext) {
+ inext = LIST_NEXT(i, oent);
+ if (!abuf_flush(i))
+ continue;
+ sub_bcopy(ibuf, i);
if (done > i->subdone)
done = i->subdone;
}
- if (done == 0)
- return 0;
LIST_FOREACH(i, &p->obuflist, oent) {
i->subdone -= done;
}
abuf_rdiscard(ibuf, done);
- if (ABUF_EOF(ibuf)) {
- abuf_hup(ibuf);
- for (i = LIST_FIRST(&p->obuflist);
- i != LIST_END(&p->obuflist);
- i = inext) {
- inext = LIST_NEXT(i, oent);
- if (i != ibuf)
- abuf_eof(i);
- }
- ibuf->wproc = NULL;
- aproc_del(p);
- return 0;
- }
- abuf_fill(ibuf);
+ p->u.sub.lat -= done / ibuf->bpf;
return 1;
}
@@ -614,7 +695,6 @@ sub_eof(struct aproc *p, struct abuf *ibuf)
while (!LIST_EMPTY(&p->obuflist)) {
obuf = LIST_FIRST(&p->obuflist);
- sub_rm(p, obuf);
abuf_eof(obuf);
}
aproc_del(p);
@@ -626,12 +706,7 @@ sub_hup(struct aproc *p, struct abuf *obuf)
struct abuf *ibuf = LIST_FIRST(&p->ibuflist);
DPRINTF("sub_hup: %s: detached\n", p->name);
- sub_rm(p, obuf);
- if (LIST_EMPTY(&p->obuflist) && (p->u.sub.flags & SUB_AUTOQUIT)) {
- abuf_hup(ibuf);
- aproc_del(p);
- } else
- abuf_run(ibuf);
+ abuf_run(ibuf);
DPRINTF("sub_hup: done\n");
}
@@ -642,21 +717,39 @@ sub_newout(struct aproc *p, struct abuf *obuf)
obuf->xrun = XRUN_IGNORE;
}
+void
+sub_ipos(struct aproc *p, struct abuf *ibuf, int delta)
+{
+ p->u.sub.lat += delta;
+ DPRINTFN(3, "sub_ipos: lat = %d/%d\n", p->u.sub.lat, p->u.sub.maxlat);
+ aproc_ipos(p, ibuf, delta);
+}
+
struct aproc_ops sub_ops = {
- "sub", sub_in, sub_out, sub_eof, sub_hup, NULL, sub_newout, NULL
+ "sub",
+ sub_in,
+ sub_out,
+ sub_eof,
+ sub_hup,
+ NULL,
+ sub_newout,
+ sub_ipos,
+ aproc_opos,
+ NULL
};
struct aproc *
-sub_new(void)
+sub_new(char *name, int maxlat)
{
struct aproc *p;
- p = aproc_new(&sub_ops, "copy");
+ p = aproc_new(&sub_ops, name);
p->u.sub.flags = 0;
+ p->u.sub.lat = 0;
+ p->u.sub.maxlat = maxlat;
return p;
}
-
/*
* Convert one block.
*/
@@ -695,9 +788,11 @@ conv_bcopy(struct aconv *ist, struct aconv *ost,
*/
idata = abuf_rgetblk(ibuf, &icount, 0);
ifr = icount / ibuf->bpf;
+ icount = ifr * ibuf->bpf;
odata = abuf_wgetblk(obuf, &ocount, 0);
ofr = ocount / obuf->bpf;
+ ocount = ofr * obuf->bpf;
/*
* Partially copy structures into local variables, to avoid
@@ -784,11 +879,14 @@ conv_in(struct aproc *p, struct abuf *ibuf)
{
struct abuf *obuf = LIST_FIRST(&p->obuflist);
- if (!ABUF_WOK(obuf))
+ DPRINTFN(4, "conv_in: %s\n", p->name);
+
+ if (!ABUF_WOK(obuf) || !ABUF_ROK(ibuf))
return 0;
conv_bcopy(&p->u.conv.ist, &p->u.conv.ost, ibuf, obuf);
- abuf_flush(obuf);
- return ABUF_WOK(obuf);
+ if (!abuf_flush(obuf))
+ return 0;
+ return 1;
}
int
@@ -796,30 +894,29 @@ conv_out(struct aproc *p, struct abuf *obuf)
{
struct abuf *ibuf = LIST_FIRST(&p->ibuflist);
- if (!ABUF_ROK(ibuf))
+ DPRINTFN(4, "conv_out: %s\n", p->name);
+
+ if (!abuf_fill(ibuf))
return 0;
- conv_bcopy(&p->u.conv.ist, &p->u.conv.ost, ibuf, obuf);
- if (ABUF_EOF(ibuf)) {
- obuf->wproc = NULL;
- abuf_hup(ibuf);
- aproc_del(p);
+ if (!ABUF_WOK(obuf) || !ABUF_ROK(ibuf))
return 0;
- }
- abuf_fill(ibuf);
+ conv_bcopy(&p->u.conv.ist, &p->u.conv.ost, ibuf, obuf);
return 1;
}
void
conv_eof(struct aproc *p, struct abuf *ibuf)
{
- abuf_eof(LIST_FIRST(&p->obuflist));
+ DPRINTFN(4, "conv_eof: %s\n", p->name);
+
aproc_del(p);
}
void
conv_hup(struct aproc *p, struct abuf *obuf)
{
- abuf_hup(LIST_FIRST(&p->ibuflist));
+ DPRINTFN(4, "conv_hup: %s\n", p->name);
+
aproc_del(p);
}
@@ -850,12 +947,55 @@ aconv_init(struct aconv *st, struct aparams *par, int input)
st->rate = par->rate;
st->pos = 0;
- for (i = 0; i < CHAN_MAX; i++)
+ for (i = 0; i < NCHAN_MAX; i++)
st->ctx[i] = 0;
}
+void
+conv_ipos(struct aproc *p, struct abuf *ibuf, int delta)
+{
+ struct abuf *obuf = LIST_FIRST(&p->obuflist);
+ long long ipos;
+ int ifac, ofac;
+
+ DPRINTFN(3, "conv_ipos: %d\n", delta);
+
+ ifac = p->u.conv.ist.rate;
+ ofac = p->u.conv.ost.rate;
+ ipos = p->u.conv.idelta + (long long)delta * ofac;
+ delta = (ipos + ifac - 1) / ifac;
+ p->u.conv.idelta = ipos - (long long)delta * ifac;
+ abuf_ipos(obuf, delta);
+}
+
+void
+conv_opos(struct aproc *p, struct abuf *obuf, int delta)
+{
+ struct abuf *ibuf = LIST_FIRST(&p->ibuflist);
+ long long opos;
+ int ifac, ofac;
+
+ DPRINTFN(3, "conv_opos: %d\n", delta);
+
+ ifac = p->u.conv.ist.rate;
+ ofac = p->u.conv.ost.rate;
+ opos = p->u.conv.odelta + (long long)delta * ifac;
+ delta = (opos + ofac - 1) / ofac;
+ p->u.conv.odelta = opos - (long long)delta * ofac;
+ abuf_opos(ibuf, delta);
+}
+
struct aproc_ops conv_ops = {
- "conv", conv_in, conv_out, conv_eof, conv_hup, NULL, NULL, NULL
+ "conv",
+ conv_in,
+ conv_out,
+ conv_eof,
+ conv_hup,
+ NULL,
+ NULL,
+ conv_ipos,
+ conv_opos,
+ NULL
};
struct aproc *
@@ -866,5 +1006,12 @@ conv_new(char *name, struct aparams *ipar, struct aparams *opar)
p = aproc_new(&conv_ops, name);
aconv_init(&p->u.conv.ist, ipar, 1);
aconv_init(&p->u.conv.ost, opar, 0);
+ p->u.conv.idelta = 0;
+ p->u.conv.odelta = 0;
+ if (debug_level > 0) {
+ DPRINTF("conv_new: %s: ", p->name);
+ aparams_print2(ipar, opar);
+ DPRINTF("\n");
+ }
return p;
}
diff --git a/usr.bin/aucat/aproc.h b/usr.bin/aucat/aproc.h
index c3cb00786cb..cdf97965c3f 100644
--- a/usr.bin/aucat/aproc.h
+++ b/usr.bin/aucat/aproc.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: aproc.h,v 1.5 2008/08/14 09:58:55 ratchov Exp $ */
+/* $OpenBSD: aproc.h,v 1.6 2008/10/26 08:49:43 ratchov Exp $ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
*
@@ -93,6 +93,18 @@ struct aproc_ops {
void (*newout)(struct aproc *, struct abuf *);
/*
+ * Real-time record position changed (for input buffer),
+ * by the given amount of _frames_
+ */
+ void (*ipos)(struct aproc *, struct abuf *, int);
+
+ /*
+ * Real-time play position changed (for output buffer),
+ * by the given amount of _frames_
+ */
+ void (*opos)(struct aproc *, struct abuf *, int);
+
+ /*
* destroy the aproc, called just before to free the
* aproc structure
*/
@@ -115,7 +127,7 @@ struct aconv {
int snext; /* bytes to skip to reach the next sample */
unsigned cmin; /* provided/consumed channels */
unsigned bpf; /* bytes per frame: bpf = nch * bps */
- int ctx[CHAN_MAX]; /* current frame (for resampling) */
+ int ctx[NCHAN_MAX]; /* current frame (for resampling) */
};
/*
@@ -134,16 +146,21 @@ struct aproc {
} io;
struct {
struct aconv ist, ost;
+ int idelta, odelta; /* reminder of conv_[io]pos */
} conv;
struct {
#define MIX_DROP 1
#define MIX_AUTOQUIT 2
- unsigned flags;
+ unsigned flags; /* bit mask of above */
+ int lat; /* current latency */
+ int maxlat; /* max latency allowed*/
} mix;
struct {
#define SUB_DROP 1
#define SUB_AUTOQUIT 2
- unsigned flags;
+ unsigned flags; /* bit mask of above */
+ int lat; /* current latency */
+ int maxlat; /* max latency allowed*/
} sub;
} u;
};
@@ -167,8 +184,8 @@ int wpipe_out(struct aproc *, struct abuf *);
void wpipe_eof(struct aproc *, struct abuf *);
void wpipe_hup(struct aproc *, struct abuf *);
-struct aproc *mix_new(void);
-struct aproc *sub_new(void);
+struct aproc *mix_new(char *, int);
+struct aproc *sub_new(char *, int);
struct aproc *conv_new(char *, struct aparams *, struct aparams *);
void mix_pushzero(struct aproc *);
diff --git a/usr.bin/aucat/aucat.1 b/usr.bin/aucat/aucat.1
index a1439e816db..d016d4479b5 100644
--- a/usr.bin/aucat/aucat.1
+++ b/usr.bin/aucat/aucat.1
@@ -1,4 +1,4 @@
-.\" $OpenBSD: aucat.1,v 1.25 2008/06/03 19:27:42 jmc Exp $
+.\" $OpenBSD: aucat.1,v 1.26 2008/10/26 08:49:43 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: June 3 2008 $
+.Dd $Mdocdate: October 26 2008 $
.Dt AUCAT 1
.Os
.Sh NAME
@@ -23,19 +23,16 @@
.Sh SYNOPSIS
.Nm aucat
.Bk -words
-.Op Fl qu
+.Op Fl lqu
+.Op Fl b Ar nsamples
.Op Fl C Ar min : Ns Ar max
.Op Fl c Ar min : Ns Ar max
-.Op Fl E Ar enc
.Op Fl e Ar enc
.Op Fl f Ar device
-.Op Fl H Ar fmt
.Op Fl h Ar fmt
.Op Fl i Ar file
.Op Fl o Ar file
-.Op Fl R Ar rate
.Op Fl r Ar rate
-.Op Fl X Ar policy
.Op Fl x Ar policy
.Ek
.Sh DESCRIPTION
@@ -51,15 +48,19 @@ also has a legacy mode that works like previous versions of
which does not convert on the fly and supports playback of .au files.
.Pp
The options are as follows:
-.Bl -tag -width "-m mmmmmmmm "
+.Bl -tag -width Ds
+.It Fl b Ar nsamples
+The buffer size in frames.
+This is the number of samples that will be buffered before being played
+and controls the playback latency.
.It Xo
.Fl C Ar min : Ns Ar max ,
.Fl c Ar min : Ns Ar max
.Xc
-The range of channel numbers on the output or input stream, respectively.
+The range of channel numbers on the record or playback stream, respectively.
The default is 0:1, i.e. stereo.
-.It Fl E Ar enc , Fl e Ar enc
-Encoding of the output or input stream, respectively (see below).
+.It Fl e Ar enc
+Encoding of the playback or recording stream (see below).
The default is signed, 16-bit, native byte order.
.It Fl f Ar device
The
@@ -67,23 +68,34 @@ The
device to use for playing and/or recording.
The default is
.Pa /dev/audio .
-.It Fl H Ar fmt , Fl h Ar fmt
-File format of the output or input stream, respectively (see below).
+.It Fl h Ar fmt
+File format of the playback or record stream (see below).
The default is auto.
.It Fl i Ar file
Add this file to the list of files to play.
If the option argument is
.Sq -
then standard input will be used.
+.It Fl l
+Listen for incoming connections on a Unix domain socket.
+A client might use
+.Nm
+instead of the regular
+.Xr audio 4
+device for audio input and output
+in order to share the physical device with other clients.
+The default socket path is
+.Pa /tmp/aucat.sock
+but it can be changed with the
+.Ev AUCAT_SOCKET
+environment variable.
.It Fl o Ar file
Add this file to the list of files in which to store recorded samples.
If the option argument is
.Sq -
then standard output will be used.
-.It Fl q
-Do not print progress information; run quietly.
-.It Fl R Ar rate , Fl r Ar rate
-Sample rate in Hertz of the output or input stream, respectively.
+.It Fl r Ar rate
+Sample rate in Hertz of the playback or record stream.
The default is 44100Hz.
.It Fl u
Normally
@@ -91,32 +103,23 @@ Normally
tries to automatically determine the optimal parameters for the audio device;
if this option is specified,
it will instead use the parameters specified by the
-.Fl CcEeRr
+.Fl Ccer
options.
-.It Fl X Ar policy
+.It Fl x Ar policy
Action when the output stream cannot accept
-recorded data fast enough.
+recorded data fast enough or the input stream
+cannot provide data to play fast enough.
If the policy
is
.Dq ignore
-(the default) then samples that cannot be written are discarded.
-If the policy is
-.Dq sync
-then samples are discarded, but the same amount of silence will be written
-once the stream is unblocked, in order to reach the right position in time.
-If the policy is
-.Dq error
-then the stream is closed permanently.
-.It Fl x Ar policy
-Action when the input stream cannot provide
-data to play fast enough.
-If the policy is
-.Dq ignore
-(the default) then silence is played.
+(the default) then samples that cannot be written are discarded
+and samples that cannot be read are replaced by silence.
If the policy is
.Dq sync
-then silence is played, but the same amount of samples will be discarded
+then recorded samples are discarded, but the same amount of silence will be written
once the stream is unblocked, in order to reach the right position in time.
+Similarly silence is played, but the same amount of samples will be discarded
+once the stream is unblocked.
If the policy is
.Dq error
then the stream is closed permanently.
@@ -127,10 +130,10 @@ Settings for input
and output
.Pq Fl o
files can be changed using the
-.Fl CcEeHhRrXx
+.Fl Ccehrx
options.
The last
-.Fl CcEeHhRrXx
+.Fl Ccehrx
options specified before an
.Fl i
or
@@ -140,13 +143,13 @@ are applied to
.Pp
Settings for the audio device
can be changed using the
-.Fl CcEeRr
+.Fl Ccer
options.
They apply to the audio device only if the
.Fl u
option is given as well.
The last
-.Fl CcEeRr
+.Fl Ccer
option specified before an
.Fl f
is applied to
@@ -158,7 +161,7 @@ is specified but
.Fl u
is given anyway,
any
-.Fl CcEeRr
+.Fl Ccer
options specified before
.Fl io
options are applied both to
@@ -170,10 +173,8 @@ options,
they will be applied only to the default audio device.
.Pp
File formats are specified using the
-.Fl H
-and
.Fl h
-options.
+option.
The following file formats are supported:
.Bl -tag -width s32lexxx -offset -indent
.It raw
@@ -192,10 +193,8 @@ Try to guess, depending on the file name.
.El
.Pp
Encodings are specified using the
-.Fl E
-and
.Fl e
-options.
+option.
The following encodings are supported:
.Pp
.Bl -tag -width s32lexxx -offset -indent -compact
@@ -282,12 +281,21 @@ If the device does not support the encoding,
.Nm
will exit with an error.
.Sh ENVIRONMENT
-.Bl -tag -width "AUDIODEVICEXXX" -compact
+.Bl -tag -width "AUCAT_SOCKETXXX" -compact
.It Ev AUCAT_DEBUG
The debug level:
may be a value between 0 and 4.
+.It Ev AUCAT_SOCKET
+Path to the Unix domain socket to use.
.It Ev AUDIODEVICE
The audio device to use.
+.El
+.Sh SIGNALS
+.Bl -tag -width "SIGUSR1, SIGUSR2X" -compact
+.It Va SIGINT
+Terminate saving recorded files.
+.It Va SIGUSR1 , Va SIGUSR2
+Increase or decrease debug level respectively.
.El
.Sh EXAMPLES
The following command will record a stereo s16le stream at
@@ -314,7 +322,7 @@ The following will record channels 2 and 3 into one stereo file and
channels 6 and 7 into another stereo file using a 96kHz sampling rate for
both:
.Bd -literal -offset indent
-$ aucat -R 96000 -C 2:3 -o file1.raw -C 6:7 -o file2.raw
+$ aucat -r 96000 -C 2:3 -o file1.raw -C 6:7 -o file2.raw
.Ed
.Pp
The following will play two s18le mono files, one on each channel:
diff --git a/usr.bin/aucat/aucat.c b/usr.bin/aucat/aucat.c
index 9ad9eec6373..ae74bc87932 100644
--- a/usr.bin/aucat/aucat.c
+++ b/usr.bin/aucat/aucat.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: aucat.c,v 1.27 2008/08/25 11:56:12 sobrado Exp $ */
+/* $OpenBSD: aucat.c,v 1.28 2008/10/26 08:49:43 ratchov Exp $ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
*
@@ -48,6 +48,7 @@
#include <sys/types.h>
#include <sys/queue.h>
+#include <signal.h>
#include <err.h>
#include <fcntl.h>
#include <signal.h>
@@ -61,73 +62,44 @@
#include "aparams.h"
#include "aproc.h"
#include "abuf.h"
-#include "file.h"
+#include "wav.h"
+#include "listen.h"
#include "dev.h"
-int debug_level = 0, quiet_flag = 0;
-volatile int quit_flag = 0, pause_flag = 0;
+int debug_level = 0;
+volatile int quit_flag = 0;
-void suspend(struct file *);
-void fill(struct file *);
-void flush(struct file *);
+/*
+ * SIGINT handler, it raises the quit flag. If the flag is already set,
+ * that means that the last SIGINT was not handled, because the process
+ * is blocked somewhere, so exit
+ */
+void
+sigint(int s)
+{
+ if (quit_flag)
+ _exit(1);
+ quit_flag = 1;
+}
/*
- * List of allowed encodings and their names.
+ * increase debug level on SIGUSR1
*/
-struct enc {
- char *name;
- struct aparams par;
-} enc_list[] = {
- /* name bps, bits, le, sign, msb, unused */
- { "s8", { 1, 8, 1, 1, 1, 0, 0, 0 } },
- { "u8", { 1, 8, 1, 0, 1, 0, 0, 0 } },
- { "s16le", { 2, 16, 1, 1, 1, 0, 0, 0 } },
- { "u16le", { 2, 16, 1, 0, 1, 0, 0, 0 } },
- { "s16be", { 2, 16, 0, 1, 1, 0, 0, 0 } },
- { "u16be", { 2, 16, 0, 0, 1, 0, 0, 0 } },
- { "s24le", { 4, 24, 1, 1, 1, 0, 0, 0 } },
- { "u24le", { 4, 24, 1, 0, 1, 0, 0, 0 } },
- { "s24be", { 4, 24, 0, 1, 1, 0, 0, 0 } },
- { "u24be", { 4, 24, 0, 0, 1, 0, 0, 0 } },
- { "s32le", { 4, 32, 1, 1, 1, 0, 0, 0 } },
- { "u32le", { 4, 32, 1, 0, 1, 0, 0, 0 } },
- { "s32be", { 4, 32, 0, 1, 1, 0, 0, 0 } },
- { "u32be", { 4, 32, 0, 0, 1, 0, 0, 0 } },
- { "s24le3", { 3, 24, 1, 1, 1, 0, 0, 0 } },
- { "u24le3", { 3, 24, 1, 0, 1, 0, 0, 0 } },
- { "s24be3", { 3, 24, 0, 1, 1, 0, 0, 0 } },
- { "u24be3", { 3, 24, 0, 0, 1, 0, 0, 0 } },
- { "s20le3", { 3, 20, 1, 1, 1, 0, 0, 0 } },
- { "u20le3", { 3, 20, 1, 0, 1, 0, 0, 0 } },
- { "s20be3", { 3, 20, 0, 1, 1, 0, 0, 0 } },
- { "u20be3", { 3, 20, 0, 0, 1, 0, 0, 0 } },
- { "s18le3", { 3, 18, 1, 1, 1, 0, 0, 0 } },
- { "u18le3", { 3, 18, 1, 0, 1, 0, 0, 0 } },
- { "s18be3", { 3, 18, 0, 1, 1, 0, 0, 0 } },
- { "u18be3", { 3, 18, 0, 0, 1, 0, 0, 0 } },
- { NULL, { 0, 0, 0, 0, 0, 0, 0, 0 } }
-};
+void
+sigusr1(int s)
+{
+ if (debug_level < 4)
+ debug_level++;
+}
/*
- * Search an encoding in the above table. On success fill encoding
- * part of "par" and return 1, otherwise return 0.
+ * decrease debug level on SIGUSR2
*/
-unsigned
-enc_lookup(char *name, struct aparams *par)
+void
+sigusr2(int s)
{
- struct enc *e;
-
- for (e = enc_list; e->name != NULL; e++) {
- if (strcmp(e->name, name) == 0) {
- par->bps = e->par.bps;
- par->bits = e->par.bits;
- par->sig = e->par.sig;
- par->le = e->par.le;
- par->msb = e->par.msb;
- return 1;
- }
- }
- return 0;
+ if (debug_level > 0)
+ debug_level--;
}
void
@@ -136,10 +108,10 @@ usage(void)
extern char *__progname;
fprintf(stderr,
- "usage: %s [-qu] [-C min:max] [-c min:max] [-E enc] [-e enc] "
- "[-f device]\n"
- "\t[-H fmt] [-h fmt] [-i file] [-o file] [-R rate] [-r rate]\n"
- "\t[-X policy] [-x policy]\n",
+ "usage: %s [-lu] [-C min:max] [-c min:max] [-d level] "
+ "[-e enc]\n"
+ "\t[-f device] [-h fmt] [-i file] [-o file] "
+ "[-r rate] [-x policy]\n",
__progname);
}
@@ -147,8 +119,7 @@ void
opt_ch(struct aparams *par)
{
if (sscanf(optarg, "%u:%u", &par->cmin, &par->cmax) != 2 ||
- par->cmin > CHAN_MAX || par->cmax > CHAN_MAX ||
- par->cmin > par->cmax)
+ par->cmax < par->cmin || par->cmax > NCHAN_MAX - 1)
err(1, "%s: bad channel range", optarg);
}
@@ -163,8 +134,11 @@ opt_rate(struct aparams *par)
void
opt_enc(struct aparams *par)
{
- if (!enc_lookup(optarg, par))
- err(1, "%s: bad encoding", optarg);
+ int len;
+
+ len = aparams_strtoenc(par, optarg);
+ if (len == 0 || optarg[len] != '\0')
+ errx(1, "%s: bad encoding", optarg);
}
int
@@ -246,7 +220,7 @@ void
newinput(struct farg *fa)
{
int fd;
- struct file *f;
+ struct wav *f;
struct aproc *proc;
struct abuf *buf;
unsigned nfr;
@@ -261,18 +235,16 @@ newinput(struct farg *fa)
if (fd < 0)
err(1, "%s", fa->name);
}
- f = file_new(fd, fa->name);
- f->hdr = 0;
- f->hpar = fa->par;
- if (fa->hdr == HDR_WAV) {
- if (!wav_readhdr(fd, &f->hpar, &f->rbytes))
- exit(1);
- }
- nfr = dev_onfr * f->hpar.rate / dev_opar.rate;
- buf = abuf_new(nfr, aparams_bpf(&f->hpar));
- proc = rpipe_new(f);
+ /*
+ * XXX : we should round rate, right ?
+ */
+ f = wav_new_in(&wav_ops, fd, fa->name, &fa->par, fa->hdr);
+ nfr = dev_bufsz * fa->par.rate / dev_rate;
+ buf = abuf_new(nfr, aparams_bpf(&fa->par));
+ proc = rpipe_new((struct file *)f);
aproc_setout(proc, buf);
- dev_attach(fa->name, buf, &f->hpar, fa->xrun, NULL, NULL, 0);
+ abuf_fill(buf); /* XXX: move this in dev_attach() ? */
+ dev_attach(fa->name, buf, &fa->par, fa->xrun, NULL, NULL, 0);
}
/*
@@ -282,7 +254,7 @@ void
newoutput(struct farg *fa)
{
int fd;
- struct file *f;
+ struct wav *f;
struct aproc *proc;
struct abuf *buf;
unsigned nfr;
@@ -298,31 +270,31 @@ newoutput(struct farg *fa)
if (fd < 0)
err(1, "%s", fa->name);
}
- f = file_new(fd, fa->name);
- f->hdr = fa->hdr;
- f->hpar = fa->par;
- if (f->hdr == HDR_WAV) {
- f->wbytes = WAV_DATAMAX;
- if (!wav_writehdr(fd, &f->hpar))
- exit(1);
- }
- nfr = dev_infr * f->hpar.rate / dev_ipar.rate;
- proc = wpipe_new(f);
- buf = abuf_new(nfr, aparams_bpf(&f->hpar));
+ /*
+ * XXX : we should round rate, right ?
+ */
+ f = wav_new_out(&wav_ops, fd, fa->name, &fa->par, fa->hdr);
+ nfr = dev_bufsz * fa->par.rate / dev_rate;
+ proc = wpipe_new((struct file *)f);
+ buf = abuf_new(nfr, aparams_bpf(&fa->par));
aproc_setin(proc, buf);
- dev_attach(fa->name, NULL, NULL, 0, buf, &f->hpar, fa->xrun);
+ dev_attach(fa->name, NULL, NULL, 0, buf, &fa->par, fa->xrun);
}
int
main(int argc, char **argv)
{
- int c, u_flag, ohdr, ihdr, ixrun, oxrun;
+ int c, u_flag, l_flag, hdr, xrun;
struct farg *fa;
struct farglist ifiles, ofiles;
struct aparams ipar, opar, dipar, dopar;
- unsigned ivol, ovol;
- char *devpath, *dbgenv;
+ struct sigaction sa;
+ unsigned ivol, ovol, bufsz = 0;
+ char *devpath, *dbgenv, *listenpath;
const char *errstr;
+ extern char *malloc_options;
+
+ malloc_options = "FGJ";
dbgenv = getenv("AUCAT_DEBUG");
if (dbgenv) {
@@ -335,27 +307,22 @@ main(int argc, char **argv)
aparams_init(&opar, 0, 1, 44100);
u_flag = 0;
+ l_flag = 0;
devpath = NULL;
SLIST_INIT(&ifiles);
SLIST_INIT(&ofiles);
- ihdr = ohdr = HDR_AUTO;
- ixrun = oxrun = XRUN_IGNORE;
+ hdr = HDR_AUTO;
+ xrun = XRUN_IGNORE;
ivol = ovol = MIDI_TO_ADATA(127);
- while ((c = getopt(argc, argv, "c:C:e:E:r:R:h:H:x:X:i:o:f:qu"))
+ while ((c = getopt(argc, argv, "b:c:C:e:r:h:x:i:o:f:lqu"))
!= -1) {
switch (c) {
case 'h':
- ihdr = opt_hdr();
- break;
- case 'H':
- ohdr = opt_hdr();
+ hdr = opt_hdr();
break;
case 'x':
- ixrun = opt_xrun();
- break;
- case 'X':
- oxrun = opt_xrun();
+ xrun = opt_xrun();
break;
case 'c':
opt_ch(&ipar);
@@ -365,35 +332,37 @@ main(int argc, char **argv)
break;
case 'e':
opt_enc(&ipar);
- break;
- case 'E':
- opt_enc(&opar);
+ aparams_copyenc(&opar, &ipar);
break;
case 'r':
opt_rate(&ipar);
- break;
- case 'R':
- opt_rate(&opar);
+ opar.rate = ipar.rate;
break;
case 'i':
- opt_file(&ifiles, &ipar, 127, ihdr, ixrun, optarg);
+ opt_file(&ifiles, &ipar, 127, hdr, xrun, optarg);
break;
case 'o':
- opt_file(&ofiles, &opar, 127, ohdr, oxrun, optarg);
+ opt_file(&ofiles, &opar, 127, hdr, xrun, optarg);
break;
case 'f':
if (devpath)
err(1, "only one -f allowed");
devpath = optarg;
- dipar = ipar;
- dopar = opar;
+ dipar = opar;
+ dopar = ipar;
break;
- case 'q':
- quiet_flag = 1;
+ case 'l':
+ l_flag = 1;
break;
case 'u':
u_flag = 1;
break;
+ case 'b':
+ if (sscanf(optarg, "%u", &bufsz) != 1) {
+ fprintf(stderr, "%s: bad buf size\n", optarg);
+ exit(1);
+ }
+ break;
default:
usage();
exit(1);
@@ -403,14 +372,12 @@ main(int argc, char **argv)
argv += optind;
if (!devpath) {
- devpath = getenv("AUDIODEVICE");
- if (devpath == NULL)
- devpath = DEFAULT_DEVICE;
dipar = ipar;
dopar = opar;
}
- if (SLIST_EMPTY(&ifiles) && SLIST_EMPTY(&ofiles) && argc > 0) {
+ if (!l_flag && SLIST_EMPTY(&ifiles) &&
+ SLIST_EMPTY(&ofiles) && argc > 0) {
/*
* Legacy mode: if no -i or -o options are provided, and
* there are arguments then assume the arguments are files
@@ -426,15 +393,17 @@ main(int argc, char **argv)
exit(1);
}
+ if (l_flag && (!SLIST_EMPTY(&ofiles) || !SLIST_EMPTY(&ifiles)))
+ errx(1, "can't use -l with -o or -i");
- if (!u_flag) {
+ if (!u_flag && !l_flag) {
/*
* Calculate "best" device parameters. Iterate over all
* inputs and outputs and find the maximum sample rate
* and channel number.
*/
- aparams_init(&dipar, CHAN_MAX, 0, RATE_MAX);
- aparams_init(&dopar, CHAN_MAX, 0, RATE_MIN);
+ aparams_init(&dipar, NCHAN_MAX - 1, 0, RATE_MAX);
+ aparams_init(&dopar, NCHAN_MAX - 1, 0, RATE_MIN);
SLIST_FOREACH(fa, &ifiles, entry) {
if (dopar.cmin > fa->par.cmin)
dopar.cmin = fa->par.cmin;
@@ -452,15 +421,37 @@ main(int argc, char **argv)
dipar.rate = fa->par.rate;
}
}
- file_start();
+
+ 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");
+#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
+ filelist_init();
/*
- * Open the device, dev_init() will return new parameters
- * that must be used by all inputs and outputs.
+ * Open the device.
*/
dev_init(devpath,
- (!SLIST_EMPTY(&ofiles)) ? &dipar : NULL,
- (!SLIST_EMPTY(&ifiles)) ? &dopar : NULL);
+ (l_flag || !SLIST_EMPTY(&ofiles)) ? &dipar : NULL,
+ (l_flag || !SLIST_EMPTY(&ifiles)) ? &dopar : NULL,
+ bufsz);
+
+ if (l_flag) {
+ listenpath = getenv("AUCAT_SOCKET");
+ if (!listenpath)
+ listenpath = DEFAULT_SOCKET;
+ (void)listen_new(&listen_ops, listenpath);
+ }
/*
* Create buffers for all input and output pipes.
@@ -479,24 +470,35 @@ main(int argc, char **argv)
}
/*
- * Normalize input levels
+ * automatically terminate when there no are streams
*/
- if (dev_mix)
- mix_setmaster(dev_mix);
+ if (!l_flag) {
+ if (dev_mix)
+ dev_mix->u.mix.flags |= MIX_AUTOQUIT;
+ if (dev_sub)
+ dev_sub->u.sub.flags |= SUB_AUTOQUIT;
+ }
/*
- * start audio
+ * loop, start audio
*/
- if (!quiet_flag)
- fprintf(stderr, "starting device...\n");
- dev_start();
- if (!quiet_flag)
- fprintf(stderr, "process started...\n");
- dev_run(1);
- if (!quiet_flag)
- fprintf(stderr, "stopping device...\n");
+ for (;;) {
+ if (quit_flag) {
+ if (l_flag)
+ filelist_unlisten();
+ break;
+ }
+ if (!file_poll())
+ break;
+ }
+
dev_done();
+ filelist_done();
- file_stop();
+ 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");
return 0;
}
diff --git a/usr.bin/aucat/conf.h b/usr.bin/aucat/conf.h
index 0938315a2b5..c57a75acad6 100644
--- a/usr.bin/aucat/conf.h
+++ b/usr.bin/aucat/conf.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: conf.h,v 1.3 2008/08/14 09:39:16 ratchov Exp $ */
+/* $OpenBSD: conf.h,v 1.4 2008/10/26 08:49:43 ratchov Exp $ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
*
@@ -47,8 +47,20 @@ extern int debug_level;
#define MIDI_MAXCTL 127
#define MIDI_TO_ADATA(m) ((ADATA_UNIT * (m) + 64) / 127)
-#define DEFAULT_NFR 0x400 /* buf size in frames */
-#define DEFAULT_NBLK 0x2 /* blocks per buffer */
+/*
+ * number of blocks in the device play/record buffers. because Sun API
+ * cannot notify apps of the current positions, we have to use all N
+ * buffers devices blocks plus one extra block, to make write() block,
+ * so that poll() can return the exact postition.
+ */
+#define DEV_NBLK 2
+
+/*
+ * number of blocks in the wav-file i/o buffers
+ */
+#define WAV_NBLK 6
+
#define DEFAULT_DEVICE "/dev/audio" /* defaul device */
+#define DEFAULT_SOCKET "/tmp/aucat.sock"
#endif /* !defined(CONF_H) */
diff --git a/usr.bin/aucat/dev.c b/usr.bin/aucat/dev.c
index 225ed6ed377..0f418a63fc8 100644
--- a/usr.bin/aucat/dev.c
+++ b/usr.bin/aucat/dev.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: dev.c,v 1.2 2008/08/14 15:25:16 ratchov Exp $ */
+/* $OpenBSD: dev.c,v 1.3 2008/10/26 08:49:43 ratchov Exp $ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
*
@@ -17,204 +17,130 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
-#include <signal.h>
-#include <err.h>
#include "dev.h"
#include "abuf.h"
#include "aproc.h"
-#include "file.h"
+#include "pipe.h"
#include "conf.h"
+#include "safile.h"
-int quit_flag, pause_flag;
-unsigned dev_infr, dev_onfr;
+unsigned dev_bufsz, dev_round, dev_rate;
+unsigned dev_rate_div, dev_round_div;
struct aparams dev_ipar, dev_opar;
struct aproc *dev_mix, *dev_sub, *dev_rec, *dev_play;
-struct file *dev_file;
-struct devops *devops = &devops_sun;
+struct file *dev_file;
/*
- * SIGINT handler, it raises the quit flag. If the flag is already set,
- * that means that the last SIGINT was not handled, because the process
- * is blocked somewhere, so exit
+ * supported rates
*/
-void
-sigint(int s)
-{
- if (quit_flag)
- _exit(1);
- quit_flag = 1;
-}
-
-/*
- * called when the user hits ctrl-z
- */
-void
-sigtstp(int s)
-{
- pause_flag = 1;
-}
-
-/*
- * SIGCONT is send when resumed after SIGTSTP or SIGSTOP. If the pause
- * flag is not set, that means that the process was not suspended by
- * dev_suspend(), which means that we lost the sync; since we cannot
- * resync, just exit
- */
-void
-sigcont(int s)
-{
- static char msg[] = "can't resume afer SIGSTOP, terminating...\n";
-
- if (!pause_flag) {
- write(STDERR_FILENO, msg, sizeof(msg) - 1);
- _exit(1);
- }
-}
+#define NRATES (sizeof(dev_rates) / sizeof(dev_rates[0]))
+unsigned dev_rates[] = {
+ 6400, 7200, 8000, 9600, 11025, 12000,
+ 12800, 14400, 16000, 19200, 22050, 24000,
+ 25600, 28800, 32000, 38400, 44100, 48000,
+ 51200, 57600, 64000, 76800, 88200, 96000,
+ 102400, 115200, 128000, 153600, 176400, 192000
+};
/*
- * suicide with SIGTSTP (tty stop) as if the user had hit ctrl-z
+ * factors of supported rates
*/
-void
-dev_suspend(void)
-{
- struct sigaction sa;
+#define NPRIMES (sizeof(dev_primes) / sizeof(dev_primes[0]))
+unsigned dev_primes[] = {2, 3, 5, 7};
- sigfillset(&sa.sa_mask);
- sa.sa_flags = SA_RESTART;
- sa.sa_handler = SIG_DFL;
- if (sigaction(SIGTSTP, &sa, NULL) < 0)
- err(1, "sigaction");
- DPRINTF("suspended by tty\n");
- kill(getpid(), SIGTSTP);
- pause_flag = 0;
- sa.sa_handler = sigtstp;
- if (sigaction(SIGTSTP, &sa, NULL) < 0)
- err(1, "sigaction");
- DPRINTF("resumed after suspend\n");
-}
-
-/*
- * fill playback buffer, so when device is started there
- * are samples to play
- */
-void
-dev_fill(void)
+int
+dev_setrate(unsigned rate)
{
- struct abuf *buf;
-
+ unsigned i, r, p;
- /*
- * if there are no inputs, zero fill the mixer
- */
- if (dev_mix && LIST_EMPTY(&dev_mix->ibuflist))
- mix_pushzero(dev_mix);
- DPRINTF("filling play buffers...\n");
- for (;;) {
- if (!dev_file->wproc) {
- DPRINTF("fill: no writer\n");
+ r = 1000 * rate;
+ for (i = 0; i < NRATES; i++) {
+ if (i == NRATES) {
+ fprintf(stderr, "dev_setrate: %u, unsupported\n", rate);
+ return 0;
+ }
+ if (r > 996 * dev_rates[i] &&
+ r < 1004 * dev_rates[i]) {
+ dev_rate = dev_rates[i];
break;
}
- if (dev_file->events & POLLOUT) {
- /*
- * kernel buffers are full, but continue
- * until the play buffer is full too.
- */
- buf = LIST_FIRST(&dev_file->wproc->ibuflist);
- if (!ABUF_WOK(buf))
- break; /* buffer full */
- if (!buf->wproc)
- break; /* will never be filled */
+ }
+
+ dev_rate_div = dev_rate;
+ dev_round_div = dev_round;
+ for (i = 0; i < NPRIMES; i++) {
+ p = dev_primes[i];
+ while (dev_rate_div % p == 0 && dev_round_div % p == 0) {
+ dev_rate_div /= p;
+ dev_round_div /= p;
}
- if (!file_poll())
- break;
- if (pause_flag)
- dev_suspend();
}
+ return 1;
}
-/*
- * flush recorded samples once the device is stopped so
- * they aren't lost
- */
void
-dev_flush(void)
+dev_roundrate(unsigned *newrate, unsigned *newround)
{
- struct abuf *buf;
-
- DPRINTF("flushing record buffers...\n");
- for (;;) {
- if (!dev_file->rproc) {
- DPRINTF("flush: no more reader\n");
- break;
- }
- if (dev_file->events & POLLIN) {
- /*
- * we drained kernel buffers, but continue
- * until the record buffer is empty.
- */
- buf = LIST_FIRST(&dev_file->rproc->obuflist);
- if (!ABUF_ROK(buf))
- break; /* buffer empty */
- if (!buf->rproc)
- break; /* will never be drained */
- }
- if (!file_poll())
- break;
- if (pause_flag)
- dev_suspend();
- }
+ *newrate += dev_rate_div - 1;
+ *newrate -= *newrate % dev_rate_div;
+ *newround = *newrate * dev_round_div / dev_rate_div;
}
-
/*
* open the device with the given hardware parameters and create a mixer
* and a multiplexer connected to it with all necessary conversions
* setup
*/
void
-dev_init(char *devpath, struct aparams *dipar, struct aparams *dopar)
+dev_init(char *devpath,
+ struct aparams *dipar, struct aparams *dopar, unsigned bufsz)
{
- int fd;
- struct sigaction sa;
- unsigned infr, onfr;
struct aparams ipar, opar;
struct aproc *conv;
struct abuf *buf;
+ unsigned nfr, ibufsz, obufsz;
- quit_flag = 0;
- pause_flag = 0;
-
- sigfillset(&sa.sa_mask);
- sa.sa_flags = SA_RESTART;
- sa.sa_handler = sigint;
- if (sigaction(SIGINT, &sa, NULL) < 0)
- err(1, "sigaction");
- sa.sa_handler = sigtstp;
- if (sigaction(SIGTSTP, &sa, NULL) < 0)
- err(1, "sigaction");
- sa.sa_handler = sigcont;
- if (sigaction(SIGCONT, &sa, NULL) < 0)
- err(1, "sigaction");
-
- fd = devops->open(devpath, dipar, dopar, &infr, &onfr);
- if (fd < 0)
+ /*
+ * use 1/4 of the total buffer for the device
+ */
+ dev_bufsz = (bufsz + 3) / 4;
+ dev_file = (struct file *)safile_new(&safile_ops, devpath,
+ dipar, dopar, &dev_bufsz, &dev_round);
+ if (!dev_file)
+ exit(1);
+ if (!dev_setrate(dipar ? dipar->rate : dopar->rate))
exit(1);
- dev_file = file_new(fd, devpath);
+ if (dipar) {
+ dipar->rate = dev_rate;
+ if (debug_level > 0) {
+ DPRINTF("dev_init: dipar: ");
+ aparams_print(dipar);
+ DPRINTF("\n");
+ }
+ }
+ if (dopar) {
+ dopar->rate = dev_rate;
+ if (debug_level > 0) {
+ DPRINTF("dev_init: dopar: ");
+ aparams_print(dopar);
+ DPRINTF("\n");
+ }
+ }
+ nfr = ibufsz = obufsz = dev_bufsz;
/*
- * create record chain
+ * create record chain: use 1/4 for the file i/o buffers
*/
if (dipar) {
aparams_init(&ipar, dipar->cmin, dipar->cmax, dipar->rate);
- infr *= DEFAULT_NBLK;
-
/*
* create the read end
*/
dev_rec = rpipe_new(dev_file);
- buf = abuf_new(infr, aparams_bpf(dipar));
+ buf = abuf_new(nfr, aparams_bpf(dipar));
aproc_setout(dev_rec, buf);
+ ibufsz += nfr;
/*
* append a converter, if needed
@@ -227,16 +153,16 @@ dev_init(char *devpath, struct aparams *dipar, struct aparams *dopar)
}
conv = conv_new("subconv", dipar, &ipar);
aproc_setin(conv, buf);
- buf = abuf_new(infr, aparams_bpf(&ipar));
+ buf = abuf_new(nfr, aparams_bpf(&ipar));
aproc_setout(conv, buf);
+ ibufsz += nfr;
}
dev_ipar = ipar;
- dev_infr = infr;
/*
* append a "sub" to which clients will connect
*/
- dev_sub = sub_new();
+ dev_sub = sub_new("sub", nfr);
aproc_setin(dev_sub, buf);
} else {
dev_rec = NULL;
@@ -248,15 +174,14 @@ dev_init(char *devpath, struct aparams *dipar, struct aparams *dopar)
*/
if (dopar) {
aparams_init(&opar, dopar->cmin, dopar->cmax, dopar->rate);
- onfr *= DEFAULT_NBLK;
-
/*
* create the write end
*/
dev_play = wpipe_new(dev_file);
- buf = abuf_new(onfr, aparams_bpf(dopar));
+ buf = abuf_new(nfr, aparams_bpf(dopar));
aproc_setin(dev_play, buf);
-
+ obufsz += nfr;
+
/*
* append a converter, if needed
*/
@@ -268,22 +193,24 @@ dev_init(char *devpath, struct aparams *dipar, struct aparams *dopar)
}
conv = conv_new("mixconv", &opar, dopar);
aproc_setout(conv, buf);
- buf = abuf_new(onfr, aparams_bpf(&opar));
+ buf = abuf_new(nfr, aparams_bpf(&opar));
aproc_setin(conv, buf);
- *dopar = opar;
+ obufsz += nfr;
}
dev_opar = opar;
- dev_onfr = onfr;
/*
* append a "mix" to which clients will connect
*/
- dev_mix = mix_new();
+ dev_mix = mix_new("mix", nfr);
aproc_setout(dev_mix, buf);
} else {
dev_play = NULL;
dev_mix = NULL;
}
+ dev_bufsz = (dopar) ? obufsz : ibufsz;
+ DPRINTF("dev_init: using %u fpb\n", dev_bufsz);
+ dev_start();
}
/*
@@ -293,43 +220,56 @@ dev_init(char *devpath, struct aparams *dipar, struct aparams *dopar)
void
dev_done(void)
{
- struct sigaction sa;
struct file *f;
- /*
- * generate EOF on all inputs (including device), so once
- * buffers are drained, everything will be cleaned
- */
- LIST_FOREACH(f, &file_list, entry) {
- if (f->rproc)
- file_eof(f);
+ if (dev_mix) {
+ /*
+ * generate EOF on all inputs (but not the device), and
+ * put the mixer in ``autoquit'' state, so once buffers
+ * are drained the mixer will terminate and shutdown the
+ * write-end of the device
+ *
+ * NOTE: since file_eof() can destroy the file and
+ * reorder the file_list, we have to restart the loop
+ * after each call to file_eof()
+ */
+ restart:
+ LIST_FOREACH(f, &file_list, entry) {
+ if (f != dev_file && f->rproc) {
+ file_eof(f);
+ goto restart;
+ }
+ }
+ if (dev_mix)
+ dev_mix->u.mix.flags |= MIX_AUTOQUIT;
+
+ /*
+ * wait play chain to terminate
+ */
+ while (dev_file->wproc != NULL) {
+ if (!file_poll())
+ break;
+ }
+ dev_mix = 0;
}
- /*
- * destroy automatically mixe instead
- * of generating silence
- */
- if (dev_mix)
- dev_mix->u.mix.flags |= MIX_AUTOQUIT;
- if (dev_sub)
- dev_sub->u.sub.flags |= SUB_AUTOQUIT;
- /*
- * drain buffers of terminated inputs.
- */
- for (;;) {
- if (!file_poll())
- break;
+ if (dev_sub) {
+ /*
+ * same as above, but for the record chain: generate eof
+ * on the read-end of the device and wait record buffers
+ * to desappear. We must stop the device first, because
+ * play-end will underrun (and xrun correction code will
+ * insert silence on the record-end of the device)
+ */
+ dev_stop();
+ file_eof(dev_file);
+ if (dev_sub)
+ dev_sub->u.sub.flags |= SUB_AUTOQUIT;
+ for (;;) {
+ if (!file_poll())
+ break;
+ }
+ dev_sub = NULL;
}
- devops->close(dev_file->fd);
-
- sigfillset(&sa.sa_mask);
- sa.sa_flags = SA_RESTART;
- sa.sa_handler = SIG_DFL;
- if (sigaction(SIGINT, &sa, NULL) < 0)
- err(1, "sigaction");
- if (sigaction(SIGTSTP, &sa, NULL) < 0)
- err(1, "sigaction");
- if (sigaction(SIGCONT, &sa, NULL) < 0)
- err(1, "sigaction");
}
/*
@@ -338,12 +278,11 @@ dev_done(void)
void
dev_start(void)
{
- dev_fill();
if (dev_mix)
dev_mix->u.mix.flags |= MIX_DROP;
if (dev_sub)
dev_sub->u.sub.flags |= SUB_DROP;
- devops->start(dev_file->fd);
+ dev_file->ops->start(dev_file);
}
/*
@@ -352,34 +291,78 @@ dev_start(void)
void
dev_stop(void)
{
- devops->stop(dev_file->fd);
+ dev_file->ops->stop(dev_file);
if (dev_mix)
dev_mix->u.mix.flags &= ~MIX_DROP;
if (dev_sub)
dev_sub->u.sub.flags &= ~SUB_DROP;
- dev_flush();
}
/*
- * loop until there's either input or output to process
+ * sync play buffer to rec buffer (for instance when one of
+ * them underruns/overruns)
*/
void
-dev_run(int autoquit)
+dev_sync(struct abuf *ibuf, struct abuf *obuf)
{
- while (!quit_flag) {
- if ((!dev_mix || LIST_EMPTY(&dev_mix->ibuflist)) &&
- (!dev_sub || LIST_EMPTY(&dev_sub->obuflist)) && autoquit)
- break;
- if (!file_poll())
+ struct abuf *pbuf, *rbuf;
+ int delta;
+
+ if (!dev_mix || !dev_sub)
+ return;
+ pbuf = LIST_FIRST(&dev_mix->obuflist);
+ if (!pbuf)
+ return;
+ rbuf = LIST_FIRST(&dev_sub->ibuflist);
+ if (!rbuf)
+ return;
+ for (;;) {
+ if (!ibuf || !ibuf->rproc) {
+ DPRINTF("dev_sync: reader desappeared\n");
+ return;
+ }
+ if (ibuf->rproc == dev_mix)
break;
- if (pause_flag) {
- devops->stop(dev_file->fd);
- dev_flush();
- dev_suspend();
- dev_fill();
- devops->start(dev_file->fd);
+ ibuf = LIST_FIRST(&ibuf->rproc->obuflist);
+ }
+ for (;;) {
+ if (!obuf || !obuf->wproc) {
+ DPRINTF("dev_sync: writer desappeared\n");
+ return;
}
+ if (obuf->wproc == dev_sub)
+ break;
+ obuf = LIST_FIRST(&obuf->wproc->ibuflist);
}
+
+ /*
+ * calculate delta, the number of frames the play chain is ahead
+ * of the record chain. It's necessary to schedule silences (or
+ * drops) in order to start playback and record in sync.
+ */
+ delta =
+ rbuf->bpf * (pbuf->abspos + pbuf->used) -
+ pbuf->bpf * rbuf->abspos;
+ delta /= pbuf->bpf * rbuf->bpf;
+ DPRINTF("dev_sync: delta = %d, ppos = %u, pused = %u, rpos = %u\n",
+ delta, pbuf->abspos, pbuf->used, rbuf->abspos);
+
+ if (delta > 0) {
+ /*
+ * if the play chain is ahead (most cases) drop some of
+ * the recorded input, to get both in sync
+ */
+ obuf->drop += delta * obuf->bpf;
+ abuf_ipos(obuf, -delta);
+ } else if (delta < 0) {
+ /*
+ * if record chain is ahead (should never happen,
+ * right?) then insert silence to play
+ */
+ ibuf->silence += -delta * ibuf->bpf;
+ abuf_opos(ibuf, delta);
+ } else
+ DPRINTF("dev_sync: nothing to do\n");
}
/*
@@ -393,9 +376,9 @@ dev_attach(char *name,
struct abuf *ibuf, struct aparams *ipar, unsigned underrun,
struct abuf *obuf, struct aparams *opar, unsigned overrun)
{
- int delta;
struct abuf *pbuf = NULL, *rbuf = NULL;
struct aproc *conv;
+ unsigned nfr;
if (ibuf) {
pbuf = LIST_FIRST(&dev_mix->obuflist);
@@ -405,14 +388,17 @@ dev_attach(char *name,
aparams_print2(ipar, &dev_opar);
fprintf(stderr, "\n");
}
+ nfr = (dev_bufsz + 3) / 4 + dev_round - 1;
+ nfr -= nfr % dev_round;
conv = conv_new(name, ipar, &dev_opar);
aproc_setin(conv, ibuf);
- ibuf = abuf_new(dev_onfr, aparams_bpf(&dev_opar));
+ ibuf = abuf_new(nfr, aparams_bpf(&dev_opar));
aproc_setout(conv, ibuf);
+ /* XXX: call abuf_fill() here ? */
}
aproc_setin(dev_mix, ibuf);
+ abuf_opos(ibuf, -dev_mix->u.mix.lat);
ibuf->xrun = underrun;
- mix_setmaster(dev_mix);
}
if (obuf) {
rbuf = LIST_FIRST(&dev_sub->ibuflist);
@@ -422,63 +408,24 @@ dev_attach(char *name,
aparams_print2(&dev_ipar, opar);
fprintf(stderr, "\n");
}
+ nfr = (dev_bufsz + 3) / 4 + dev_round - 1;
+ nfr -= nfr % dev_round;
conv = conv_new(name, &dev_ipar, opar);
aproc_setout(conv, obuf);
- obuf = abuf_new(dev_infr, aparams_bpf(&dev_ipar));
+ obuf = abuf_new(nfr, aparams_bpf(&dev_ipar));
aproc_setin(conv, obuf);
}
aproc_setout(dev_sub, obuf);
+ abuf_ipos(obuf, -dev_sub->u.sub.lat);
obuf->xrun = overrun;
}
/*
- * calculate delta, the number of frames the play chain is ahead
- * of the record chain. It's necessary to schedule silences (or
- * drops) in order to start playback and record in sync.
+ * sync play to record
*/
if (ibuf && obuf) {
- delta =
- rbuf->bpf * (pbuf->abspos + pbuf->used) -
- pbuf->bpf * rbuf->abspos;
- delta /= pbuf->bpf * rbuf->bpf;
- DPRINTF("dev_attach: ppos = %u, pused = %u, rpos = %u\n",
- pbuf->abspos, pbuf->used, rbuf->abspos);
- } else
- delta = 0;
- DPRINTF("dev_attach: delta = %u\n", delta);
-
- if (delta > 0) {
- /*
- * if the play chain is ahead (most cases) drop some of
- * the recorded input, to get both in sync
- */
- obuf->drop += delta * obuf->bpf;
- } else if (delta < 0) {
- /*
- * if record chain is ahead (should never happen,
- * right?) then insert silence to play
- */
- ibuf->silence += -delta * ibuf->bpf;
- }
- if (ibuf && (dev_mix->u.mix.flags & MIX_DROP)) {
- /*
- * fill the play buffer with silence to avoid underruns,
- * drop samples on the input to keep play/record in sync
- * after the silence insertion
- */
- ibuf->silence += dev_onfr * ibuf->bpf;
- if (obuf)
- obuf->drop += dev_onfr * obuf->bpf;
- /*
- * force data to propagate
- */
- abuf_run(ibuf);
- DPRINTF("dev_attach: ibuf: used = %u, silence = %u\n",
- ibuf->used, ibuf->silence);
- }
- if (obuf && (dev_sub->u.mix.flags & SUB_DROP)) {
- abuf_run(obuf);
- DPRINTF("dev_attach: ibuf: used = %u, drop = %u\n",
- obuf->used, obuf->drop);
+ ibuf->duplex = obuf;
+ obuf->duplex = ibuf;
+ dev_sync(ibuf, obuf);
}
}
diff --git a/usr.bin/aucat/dev.h b/usr.bin/aucat/dev.h
index e5b50af53a3..b23fd9e1561 100644
--- a/usr.bin/aucat/dev.h
+++ b/usr.bin/aucat/dev.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: dev.h,v 1.2 2008/08/14 09:58:55 ratchov Exp $ */
+/* $OpenBSD: dev.h,v 1.3 2008/10/26 08:49:43 ratchov Exp $ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
*
@@ -22,37 +22,22 @@ struct aparams;
struct file;
struct abuf;
-extern unsigned dev_infr, dev_onfr;
+extern unsigned dev_bufsz, dev_round, dev_rate;
+extern unsigned dev_rate_div, dev_round_div;
extern struct aparams dev_ipar, dev_opar;
extern struct aproc *dev_mix, *dev_sub, *dev_rec, *dev_play;
-extern struct file *dev_file;
-void dev_fill(void);
-void dev_flush(void);
-void dev_init(char *, struct aparams *, struct aparams *);
+void dev_roundrate(unsigned *, unsigned *);
+void dev_init(char *, struct aparams *, struct aparams *, unsigned);
void dev_start(void);
void dev_stop(void);
void dev_run(int);
void dev_done(void);
+void dev_sync(struct abuf *, struct abuf *);
void dev_attach(char *,
struct abuf *, struct aparams *, unsigned,
struct abuf *, struct aparams *, unsigned);
-struct devops {
- int (*open)(char *, struct aparams *, struct aparams *,
- unsigned *, unsigned *);
- void (*close)(int);
- void (*start)(int);
- void (*stop)(int);
-};
-
-extern struct devops *devops, devops_sun;
-
-/*
- * Sun API specific functions
- */
-struct audio_prinfo;
-int sun_infotopar(struct audio_prinfo *, struct aparams *);
-void sun_partoinfo(struct audio_prinfo *, struct aparams *);
+extern struct devops *devops, devops_sun, devops_aucat;
#endif /* !define(DEV_H) */
diff --git a/usr.bin/aucat/dev_sun.c b/usr.bin/aucat/dev_sun.c
deleted file mode 100644
index 2e97f6def0e..00000000000
--- a/usr.bin/aucat/dev_sun.c
+++ /dev/null
@@ -1,282 +0,0 @@
-/* $OpenBSD: dev_sun.c,v 1.5 2008/08/14 09:58:55 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/ioctl.h>
-#include <sys/audioio.h>
-
-#include <err.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "conf.h"
-#include "aparams.h"
-#include "dev.h"
-
-/*
- * Convert sun device parameters to struct aparams
- */
-int
-sun_infotopar(struct audio_prinfo *ai, struct aparams *par)
-{
- par->rate = ai->sample_rate;
- par->bps = ai->precision / 8;
- par->bits = ai->precision;
- par->cmax = par->cmin + ai->channels - 1;
- if (par->cmax >= CHAN_MAX) {
- warnx("%u:%u: channel range out of bounds",
- par->cmin, par->cmax);
- return 0;
- }
- par->msb = 1;
- switch (ai->encoding) {
- case AUDIO_ENCODING_SLINEAR_LE:
- par->le = 1;
- par->sig = 1;
- break;
- case AUDIO_ENCODING_SLINEAR_BE:
- par->le = 0;
- par->sig = 1;
- break;
- case AUDIO_ENCODING_ULINEAR_LE:
- par->le = 1;
- par->sig = 0;
- break;
- case AUDIO_ENCODING_ULINEAR_BE:
- par->le = 0;
- par->sig = 0;
- break;
- case AUDIO_ENCODING_SLINEAR:
- par->le = NATIVE_LE;
- par->sig = 1;
- break;
- case AUDIO_ENCODING_ULINEAR:
- par->le = NATIVE_LE;
- par->sig = 0;
- break;
- default:
- warnx("only linear encodings are supported for audio devices");
- return 0;
- }
- return 1;
-}
-
-/*
- * Convert struct aparams to sun device parameters.
- */
-void
-sun_partoinfo(struct audio_prinfo *ai, struct aparams *par)
-{
- ai->sample_rate = par->rate;
- ai->precision = par->bps * 8;
- ai->channels = par->cmax - par->cmin + 1;
- if (par->le && par->sig) {
- ai->encoding = AUDIO_ENCODING_SLINEAR_LE;
- } else if (!par->le && par->sig) {
- ai->encoding = AUDIO_ENCODING_SLINEAR_BE;
- } else if (par->le && !par->sig) {
- ai->encoding = AUDIO_ENCODING_ULINEAR_LE;
- } else {
- ai->encoding = AUDIO_ENCODING_ULINEAR_BE;
- }
-}
-
-/*
- * Open the device and pause it, so later play and record
- * can be started simultaneously.
- *
- * in "infr" and "onfd" we return the input and the output
- * block sizes respectively.
- */
-int
-sun_open(char *path, struct aparams *ipar, struct aparams *opar,
- unsigned *infr, unsigned *onfr)
-{
- int fd;
- int fullduplex;
- int flags;
- struct audio_info aui;
- struct audio_bufinfo aubi;
-
- if (!ipar && !opar)
- errx(1, "%s: must at least play or record", path);
-
- if (ipar && opar) {
- flags = O_RDWR;
- } else if (ipar) {
- flags = O_RDONLY;
- } else {
- flags = O_WRONLY;
- }
- fd = open(path, flags | O_NONBLOCK);
- if (fd < 0) {
- warn("%s", path);
- return -1;
- }
-
- /*
- * If both play and record are requested then
- * set full duplex mode.
- */
- if (ipar && opar) {
- fullduplex = 1;
- if (ioctl(fd, AUDIO_SETFD, &fullduplex) < 0) {
- warn("%s: can't set full-duplex", path);
- close(fd);
- return -1;
- }
- }
-
- /*
- * Set parameters and pause the device. When paused, the write(2)
- * syscall will queue samples but the the kernel will not start playing
- * them. Setting the 'mode' and pausing the device must be done in a
- * single ioctl() call, otherwise the sun driver will start the device
- * and fill the record buffers.
- */
- AUDIO_INITINFO(&aui);
- aui.mode = 0;
- if (opar) {
- sun_partoinfo(&aui.play, opar);
- aui.play.pause = 1;
- aui.mode |= AUMODE_PLAY;
- }
- if (ipar) {
- sun_partoinfo(&aui.record, ipar);
- aui.record.pause = 1;
- aui.mode |= AUMODE_RECORD;
- }
- if (ioctl(fd, AUDIO_SETINFO, &aui) < 0) {
- fprintf(stderr, "%s: can't set audio params to ", path);
- if (ipar)
- aparams_print(ipar);
- if (opar) {
- if (ipar)
- fprintf(stderr, " and ");
- aparams_print(opar);
- }
- fprintf(stderr, ": %s\n", strerror(errno));
- close(fd);
- return -1;
- }
- if (ioctl(fd, AUDIO_GETINFO, &aui) < 0) {
- warn("sun_open: getinfo");
- close(fd);
- return -1;
- }
- if (opar) {
- if (!sun_infotopar(&aui.play, opar)) {
- close(fd);
- return -1;
- }
- if (ioctl(fd, AUDIO_GETPRINFO, &aubi) < 0) {
- warn("%s: AUDIO_GETPRINFO", path);
- close(fd);
- return -1;
- }
- *onfr = aubi.blksize * aubi.hiwat /
- (aui.play.channels * aui.play.precision / 8);
- }
- if (ipar) {
- if (!sun_infotopar(&aui.record, ipar)) {
- close(fd);
- return -1;
- }
- if (ioctl(fd, AUDIO_GETRRINFO, &aubi) < 0) {
- warn("%s: AUDIO_GETRRINFO", path);
- close(fd);
- return -1;
- }
- *infr = aubi.blksize * aubi.hiwat /
- (aui.record.channels * aui.record.precision / 8);
- }
- return fd;
-}
-
-/*
- * Drain and close the device
- */
-void
-sun_close(int fd)
-{
- close(fd);
- DPRINTF("sun_close: closed\n");
-}
-
-/*
- * Start play/record simultaneously. Play buffers must be filled.
- */
-void
-sun_start(int fd)
-{
- audio_info_t aui;
-
- /*
- * Just unpause the device. The kernel will start playback and record
- * simultaneously. There must be samples already written.
- */
- AUDIO_INITINFO(&aui);
- aui.play.pause = aui.record.pause = 0;
- if (ioctl(fd, AUDIO_SETINFO, &aui) < 0)
- err(1, "sun_start: setinfo");
-
- DPRINTF("sun_start: play/rec started\n");
-}
-
-/*
- * Drain play buffers and then stop play/record simultaneously.
- */
-void
-sun_stop(int fd)
-{
- audio_info_t aui;
-
- /*
- * Sun API doesn't not allows us to drain and stop without
- * loosing the sync between playback and record. So, for now we
- * just pause the device until this problem is worked around.
- *
- * there are three possible workarounds:
- *
- * 1) stop depending on this, ie. make the rest of the code
- * able to resynchronize playback to record. Then just
- * close/reset the device to stop it.
- *
- * 2) send "hiwat" blocks of silence and schedule the
- * very same amount of silence to drop.
- *
- * 3) modify the AUDIO_DRAIN ioctl(2) not to loose sync
- *
- */
- AUDIO_INITINFO(&aui);
- aui.play.pause = aui.record.pause = 1;
- if (ioctl(fd, AUDIO_SETINFO, &aui) < 0)
- err(1, "sun_stop: setinfo");
-
- DPRINTF("sun_stop: play/rec stopped\n");
-}
-
-struct devops devops_sun = {
- sun_open,
- sun_close,
- sun_start,
- sun_stop
-};
diff --git a/usr.bin/aucat/file.c b/usr.bin/aucat/file.c
index 70995e64fa2..8ce62b5bbeb 100644
--- a/usr.bin/aucat/file.c
+++ b/usr.bin/aucat/file.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: file.c,v 1.3 2008/08/14 09:58:55 ratchov Exp $ */
+/* $OpenBSD: file.c,v 1.4 2008/10/26 08:49:44 ratchov Exp $ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
*
@@ -31,62 +31,55 @@
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
#include "conf.h"
#include "file.h"
#include "aproc.h"
#include "abuf.h"
-#include "dev.h"
#define MAXFDS 100
+extern struct fileops listen_ops, pipe_ops;
struct filelist file_list;
void
file_dprint(int n, struct file *f)
{
- if (debug_level >= n) {
- fprintf(stderr, "%s <", f->name);
- if (f->state & FILE_ROK)
- fprintf(stderr, "ROK");
- if (f->state & FILE_WOK)
- fprintf(stderr, "WOK");
- if (f->state & FILE_EOF)
- fprintf(stderr, "EOF");
- if (f->state & FILE_HUP)
- fprintf(stderr, "HUP");
- fprintf(stderr, ">");
- }
+ if (debug_level < n)
+ return;
+ fprintf(stderr, "%s:%s <", f->ops->name, f->name);
+ if (f->state & FILE_ROK)
+ fprintf(stderr, "ROK");
+ if (f->state & FILE_WOK)
+ fprintf(stderr, "WOK");
+ if (f->state & FILE_EOF)
+ fprintf(stderr, "EOF");
+ if (f->state & FILE_HUP)
+ fprintf(stderr, "HUP");
+ fprintf(stderr, ">");
}
struct file *
-file_new(int fd, char *name)
+file_new(struct fileops *ops, char *name, unsigned nfds)
{
- unsigned i;
struct file *f;
- i = 0;
LIST_FOREACH(f, &file_list, entry)
- i++;
- if (i >= MAXFDS)
+ nfds += f->ops->nfds(f);
+ if (nfds > MAXFDS)
err(1, "%s: too many polled files", name);
- f = malloc(sizeof(struct file));
+ f = malloc(ops->size);
if (f == NULL)
- err(1, "%s", name);
-
- f->fd = fd;
- f->events = 0;
- f->rbytes = -1;
- f->wbytes = -1;
+ err(1, "file_new: %s", ops->name);
+ f->ops = ops;
f->name = name;
f->state = 0;
f->rproc = NULL;
f->wproc = NULL;
+ f->refs = 0;
LIST_INSERT_HEAD(&file_list, f, entry);
- DPRINTF("file_new: %s\n", f->name);
+ DPRINTF("file_new: %s:%s\n", ops->name, f->name);
return f;
}
@@ -95,54 +88,52 @@ file_del(struct file *f)
{
DPRINTF("file_del: ");
file_dprint(1, f);
- DPRINTF("\n");
-
- if (f->hdr == HDR_WAV)
- wav_writehdr(f->fd, &f->hpar);
- close(f->fd);
- free(f);
+ if (f->refs > 0) {
+ DPRINTF(": delayed\n");
+ f->state |= FILE_ZOMB;
+ return;
+ } else {
+ DPRINTF(": immediate\n");
+ LIST_REMOVE(f, entry);
+ f->ops->close(f);
+ free(f);
+ }
}
int
file_poll(void)
{
-#ifdef DEBUG
- int ndead;
-#endif
- nfds_t nfds;
+ nfds_t nfds, n;
+ short events, revents;
struct pollfd pfds[MAXFDS];
- struct pollfd *pfd;
struct file *f, *fnext;
struct aproc *p;
+ /*
+ * fill the pfds[] array with files that are blocked on reading
+ * and/or writing, skipping those that're just waiting
+ */
+ DPRINTFN(4, "file_poll:");
nfds = 0;
-#ifdef DEBUG
- ndead = 0;
-#endif
LIST_FOREACH(f, &file_list, entry) {
- if (!f->events) {
-#ifdef DEBUG
- if (f->state & (FILE_EOF | FILE_HUP))
- ndead++;
-#endif
+ events = 0;
+ if (f->rproc && !(f->state & FILE_ROK))
+ events |= POLLIN;
+ if (f->wproc && !(f->state & FILE_WOK))
+ events |= POLLOUT;
+ DPRINTFN(4, " %s(%x)", f->name, events);
+ n = f->ops->pollfd(f, pfds + nfds, events);
+ if (n == 0) {
f->pfd = NULL;
continue;
}
- pfd = &pfds[nfds++];
- f->pfd = pfd;
- pfd->fd = f->fd;
- pfd->events = f->events;
+ f->pfd = pfds + nfds;
+ nfds += n;
}
+ DPRINTFN(4, "\n");
#ifdef DEBUG
- if (debug_level >= 4) {
- fprintf(stderr, "file_poll:");
- LIST_FOREACH(f, &file_list, entry) {
- fprintf(stderr, " %s(%x)", f->name, f->events);
- }
- fprintf(stderr, "\n");
- }
- if (nfds == 0 && ndead == 0 && !LIST_EMPTY(&file_list)) {
+ if (nfds == 0 && !LIST_EMPTY(&file_list)) {
fprintf(stderr, "file_poll: deadlock\n");
abort();
}
@@ -151,60 +142,58 @@ file_poll(void)
DPRINTF("file_poll: nothing to do...\n");
return 0;
}
- if (nfds) {
- while (poll(pfds, nfds, -1) < 0) {
- if (errno != EINTR)
- err(1, "file_poll: poll failed");
- }
+ if (poll(pfds, nfds, -1) < 0) {
+ if (errno == EINTR)
+ return 1;
+ err(1, "file_poll: poll failed");
}
- LIST_FOREACH(f, &file_list, entry) {
- pfd = f->pfd;
- if (pfd == NULL)
+ f = LIST_FIRST(&file_list);
+ while (f != LIST_END(&file_list)) {
+ if (f->pfd == NULL) {
+ f = LIST_NEXT(f, entry);
continue;
- if ((f->events & POLLIN) && (pfd->revents & POLLIN)) {
- f->events &= ~POLLIN;
+ }
+ 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);
- while (f->state & FILE_ROK) {
+ for (;;) {
p = f->rproc;
if (!p || !p->ops->in(p, NULL))
break;
}
}
- if ((f->events & POLLOUT) && (pfd->revents & POLLOUT)) {
- f->events &= ~POLLOUT;
+ if (!(f->state & FILE_ZOMB) && (revents & POLLOUT)) {
+ revents &= ~POLLOUT;
f->state |= FILE_WOK;
DPRINTFN(3, "file_poll: %s wok\n", f->name);
- while (f->state & FILE_WOK) {
+ for (;;) {
p = f->wproc;
if (!p || !p->ops->out(p, NULL))
break;
}
}
- }
- LIST_FOREACH(f, &file_list, entry) {
- if (f->state & FILE_EOF) {
+ if (!(f->state & FILE_ZOMB) && (f->state & FILE_EOF)) {
DPRINTFN(2, "file_poll: %s: eof\n", f->name);
p = f->rproc;
if (p)
p->ops->eof(p, NULL);
f->state &= ~FILE_EOF;
}
- if (f->state & FILE_HUP) {
+ if (!(f->state & FILE_ZOMB) && (f->state & FILE_HUP)) {
DPRINTFN(2, "file_poll: %s hup\n", f->name);
p = f->wproc;
if (p)
p->ops->hup(p, NULL);
f->state &= ~FILE_HUP;
}
- }
- for (f = LIST_FIRST(&file_list); f != NULL; f = fnext) {
+ f->refs--;
fnext = LIST_NEXT(f, entry);
- if (f->rproc == NULL && f->wproc == NULL) {
- LIST_REMOVE(f, entry);
- DPRINTF("file_poll: %s: removed\n", f->name);
+ if (f->state & FILE_ZOMB)
file_del(f);
- }
+ f = fnext;
}
if (LIST_EMPTY(&file_list)) {
DPRINTFN(2, "file_poll: terminated\n");
@@ -214,7 +203,7 @@ file_poll(void)
}
void
-file_start(void)
+filelist_init(void)
{
sigset_t set;
@@ -227,109 +216,88 @@ file_start(void)
}
void
-file_stop(void)
+filelist_done(void)
{
struct file *f;
if (!LIST_EMPTY(&file_list)) {
- fprintf(stderr, "file_stop:");
+ fprintf(stderr, "filelist_done: list not empty:\n");
LIST_FOREACH(f, &file_list, entry) {
- fprintf(stderr, " %s(%x)", f->name, f->events);
+ fprintf(stderr, "\t");
+ file_dprint(0, f);
+ fprintf(stderr, "\n");
}
- fprintf(stderr, "\nfile_stop: list not empty\n");
- exit(1);
+ abort();
}
}
-unsigned
-file_read(struct file *file, unsigned char *data, unsigned count)
+/*
+ * close all listening sockets
+ *
+ * XXX: remove this
+ */
+void
+filelist_unlisten(void)
{
- int n;
+ struct file *f, *fnext;
- if (file->rbytes >= 0 && count > file->rbytes) {
- count = file->rbytes; /* file->rbytes fits in count */
- if (count == 0) {
- DPRINTFN(2, "file_read: %s: complete\n", file->name);
- file->state &= ~FILE_ROK;
- file->state |= FILE_EOF;
- return 0;
- }
- }
- while ((n = read(file->fd, data, count)) < 0) {
- if (errno == EINTR)
- continue;
- file->state &= ~FILE_ROK;
- if (errno == EAGAIN) {
- DPRINTFN(3, "file_read: %s: blocking...\n",
- file->name);
- file->events |= POLLIN;
- } else {
- warn("%s", file->name);
- file->state |= FILE_EOF;
- }
- return 0;
- }
- if (n == 0) {
- DPRINTFN(2, "file_read: %s: eof\n", file->name);
- file->state &= ~FILE_ROK;
- file->state |= FILE_EOF;
- return 0;
+ for (f = LIST_FIRST(&file_list); f != NULL; f = fnext) {
+ fnext = LIST_NEXT(f, entry);
+ if (f->ops == &listen_ops)
+ file_del(f);
}
- if (file->rbytes >= 0)
- file->rbytes -= n;
- DPRINTFN(4, "file_read: %s: got %d bytes\n", file->name, n);
- return n;
}
+unsigned
+file_read(struct file *file, unsigned char *data, unsigned count)
+{
+ return file->ops->read(file, data, count);
+}
unsigned
file_write(struct file *file, unsigned char *data, unsigned count)
{
- int n;
-
- if (file->wbytes >= 0 && count > file->wbytes) {
- count = file->wbytes; /* file->wbytes fits in count */
- if (count == 0) {
- DPRINTFN(2, "file_write: %s: complete\n", file->name);
- file->state &= ~FILE_WOK;
- file->state |= FILE_HUP;
- return 0;
- }
- }
- while ((n = write(file->fd, data, count)) < 0) {
- if (errno == EINTR)
- continue;
- file->state &= ~FILE_WOK;
- if (errno == EAGAIN) {
- DPRINTFN(3, "file_write: %s: blocking...\n",
- file->name);
- file->events |= POLLOUT;
- } else {
- warn("%s", file->name);
- file->state |= FILE_HUP;
- }
- return 0;
- }
- if (file->wbytes >= 0)
- file->wbytes -= n;
- DPRINTFN(4, "file_write: %s: wrote %d bytes\n", file->name, n);
- return n;
+ return file->ops->write(file, data, count);
}
void
file_eof(struct file *f)
{
- DPRINTFN(2, "file_eof: %s: scheduled for eof\n", f->name);
- f->events &= ~POLLIN;
- f->state &= ~FILE_ROK;
- f->state |= FILE_EOF;
+ struct aproc *p;
+
+ if (f->refs == 0) {
+ DPRINTFN(2, "file_eof: %s: immediate\n", f->name);
+ f->refs++;
+ p = f->rproc;
+ if (p)
+ p->ops->eof(p, NULL);
+ f->refs--;
+ if (f->state & FILE_ZOMB)
+ file_del(f);
+ } else {
+ DPRINTFN(2, "file_eof: %s: delayed\n", f->name);
+ f->state &= ~FILE_ROK;
+ f->state |= FILE_EOF;
+ }
}
void
file_hup(struct file *f)
{
- DPRINTFN(2, "file_hup: %s: scheduled for hup\n", f->name);
- f->events &= ~POLLOUT;
- f->state &= ~FILE_WOK;
- f->state |= FILE_HUP;
+ struct aproc *p;
+
+ if (f->refs == 0) {
+ DPRINTFN(2, "file_hup: %s immediate\n", f->name);
+ f->refs++;
+ p = f->wproc;
+ if (p)
+ p->ops->hup(p, NULL);
+ f->refs--;
+ if (f->state & FILE_ZOMB)
+ file_del(f);
+ } else {
+ DPRINTFN(2, "file_hup: %s: delayed\n", f->name);
+ f->state &= ~FILE_WOK;
+ f->state |= FILE_HUP;
+ }
}
diff --git a/usr.bin/aucat/file.h b/usr.bin/aucat/file.h
index 338d7a3c426..b800636a8cb 100644
--- a/usr.bin/aucat/file.h
+++ b/usr.bin/aucat/file.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: file.h,v 1.3 2008/08/14 09:58:55 ratchov Exp $ */
+/* $OpenBSD: file.h,v 1.4 2008/10/26 08:49:44 ratchov Exp $ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
*
@@ -19,46 +19,52 @@
#include <sys/queue.h>
#include <sys/types.h>
-#include <poll.h>
-#include "aparams.h"
+struct file;
struct aparams;
struct aproc;
struct abuf;
+struct pollfd;
+
+struct fileops {
+ char *name;
+ size_t size;
+ void (*close)(struct file *);
+ unsigned (*read)(struct file *, unsigned char *, unsigned);
+ unsigned (*write)(struct file *, unsigned char *, unsigned);
+ void (*start)(struct file *);
+ void (*stop)(struct file *);
+ int (*nfds)(struct file *);
+ int (*pollfd)(struct file *, struct pollfd *, int);
+ int (*revents)(struct file *, struct pollfd *);
+};
struct file {
- int fd; /* file descriptor */
+ struct fileops *ops;
struct pollfd *pfd; /* arg to poll(2) syscall */
- off_t rbytes; /* bytes to read, -1 if no limit */
- off_t wbytes; /* bytes to write, -1 if no limit */
- int events; /* events for poll(2) */
#define FILE_ROK 0x1 /* file readable */
#define FILE_WOK 0x2 /* file writable */
#define FILE_EOF 0x4 /* eof on the read end */
-#define FILE_HUP 0x8 /* eof on the write end */
- int state; /* one of above */
+#define FILE_HUP 0x8 /* hang-up on the write end */
+#define FILE_ZOMB 0x10 /* closed, but struct not freed */
+ 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;
-
- /*
- * disk-file specific stuff
- */
-#define HDR_AUTO 0 /* guess by looking at the file name */
-#define HDR_RAW 1 /* no headers, ie openbsd native ;-) */
-#define HDR_WAV 2 /* microsoft riff wave */
- unsigned hdr; /* HDR_RAW or HDR_WAV */
- struct aparams hpar; /* parameters to write on the header */
};
LIST_HEAD(filelist,file);
extern struct filelist file_list;
-void file_start(void);
-void file_stop(void);
-struct file *file_new(int, char *);
+void filelist_init(void);
+void filelist_done(void);
+void filelist_unlisten(void);
+
+struct file *file_new(struct fileops *, char *, unsigned);
void file_del(struct file *);
+
void file_attach(struct file *, struct aproc *, struct aproc *);
unsigned file_read(struct file *, unsigned char *, unsigned);
unsigned file_write(struct file *, unsigned char *, unsigned);
@@ -66,17 +72,4 @@ int file_poll(void);
void file_eof(struct file *);
void file_hup(struct file *);
-/*
- * max data of a .wav file. The total file size must be smaller than
- * 2^31, and we also have to leave some space for the headers (around 40
- * bytes)
- */
-#define WAV_DATAMAX (0x7fff0000)
-
-int wav_readhdr(int, struct aparams *, off_t *);
-int wav_writehdr(int, struct aparams *);
-
-/* legacy */
-int legacy_play(char *, char *);
-
#endif /* !defined(FILE_H) */
diff --git a/usr.bin/aucat/headers.c b/usr.bin/aucat/headers.c
index f2960ec5a44..4795c47ee7a 100644
--- a/usr.bin/aucat/headers.c
+++ b/usr.bin/aucat/headers.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: headers.c,v 1.1 2008/05/23 07:15:46 ratchov Exp $ */
+/* $OpenBSD: headers.c,v 1.2 2008/10/26 08:49:44 ratchov Exp $ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
*
@@ -76,7 +76,7 @@ wav_readfmt(int fd, unsigned csize, struct aparams *par)
return 0;
}
cmax = par->cmin + nch - 1;
- if (cmax >= CHAN_MAX) {
+ if (cmax >= NCHAN_MAX) {
warnx("%u:%u: bad range", par->cmin, cmax);
return 0;
}
diff --git a/usr.bin/aucat/legacy.c b/usr.bin/aucat/legacy.c
index fcb705120db..70df6ec4750 100644
--- a/usr.bin/aucat/legacy.c
+++ b/usr.bin/aucat/legacy.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: legacy.c,v 1.2 2008/08/14 09:58:55 ratchov Exp $ */
+/* $OpenBSD: legacy.c,v 1.3 2008/10/26 08:49:44 ratchov Exp $ */
/*
* Copyright (c) 1997 Kenneth Stailey. All rights reserved.
*
@@ -37,9 +37,7 @@
#include <unistd.h>
#include <err.h>
-#include "file.h"
-#include "aparams.h"
-#include "dev.h"
+#include "wav.h"
/* headerless data files. played at /dev/audio's defaults.
@@ -58,6 +56,74 @@
#define FMT_WAV 2
+/*
+ * Convert sun device parameters to struct aparams
+ */
+int
+sun_infotopar(struct audio_prinfo *ai, struct aparams *par)
+{
+ par->rate = ai->sample_rate;
+ par->bps = ai->precision / 8;
+ par->bits = ai->precision;
+ par->cmax = par->cmin + ai->channels - 1;
+ if (par->cmax > NCHAN_MAX - 1) {
+ warnx("%u:%u: channel range out of bounds",
+ par->cmin, par->cmax);
+ return 0;
+ }
+ par->msb = 1;
+ switch (ai->encoding) {
+ case AUDIO_ENCODING_SLINEAR_LE:
+ par->le = 1;
+ par->sig = 1;
+ break;
+ case AUDIO_ENCODING_SLINEAR_BE:
+ par->le = 0;
+ par->sig = 1;
+ break;
+ case AUDIO_ENCODING_ULINEAR_LE:
+ par->le = 1;
+ par->sig = 0;
+ break;
+ case AUDIO_ENCODING_ULINEAR_BE:
+ par->le = 0;
+ par->sig = 0;
+ break;
+ case AUDIO_ENCODING_SLINEAR:
+ par->le = NATIVE_LE;
+ par->sig = 1;
+ break;
+ case AUDIO_ENCODING_ULINEAR:
+ par->le = NATIVE_LE;
+ par->sig = 0;
+ break;
+ default:
+ warnx("only linear encodings are supported for audio devices");
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Convert struct aparams to sun device parameters.
+ */
+void
+sun_partoinfo(struct audio_prinfo *ai, struct aparams *par)
+{
+ ai->sample_rate = par->rate;
+ ai->precision = par->bps * 8;
+ ai->channels = par->cmax - par->cmin + 1;
+ if (par->le && par->sig) {
+ ai->encoding = AUDIO_ENCODING_SLINEAR_LE;
+ } else if (!par->le && par->sig) {
+ ai->encoding = AUDIO_ENCODING_SLINEAR_BE;
+ } else if (par->le && !par->sig) {
+ ai->encoding = AUDIO_ENCODING_ULINEAR_LE;
+ } else {
+ ai->encoding = AUDIO_ENCODING_ULINEAR_BE;
+ }
+}
+
int
legacy_play(char *dev, char *aufile)
{
diff --git a/usr.bin/aucat/listen.c b/usr.bin/aucat/listen.c
new file mode 100644
index 00000000000..1e63c4b4dc3
--- /dev/null
+++ b/usr.bin/aucat/listen.c
@@ -0,0 +1,133 @@
+/* $OpenBSD: listen.c,v 1.1 2008/10/26 08:49:44 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 <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "conf.h"
+#include "sock.h"
+#include "listen.h"
+
+struct fileops listen_ops = {
+ "listen",
+ sizeof(struct listen),
+ listen_close,
+ NULL, /* read */
+ NULL, /* write */
+ NULL, /* start */
+ NULL, /* stop */
+ listen_nfds,
+ listen_pollfd,
+ listen_revents
+};
+
+struct listen *
+listen_new(struct fileops *ops, char *path)
+{
+ int sock;
+ struct sockaddr_un sockname;
+ struct listen *f;
+
+ sock = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (sock < 0) {
+ perror("socket");
+ exit(1);
+ }
+ if (unlink(path) < 0 && errno != ENOENT) {
+ perror("unlink");
+ exit(1);
+ }
+ sockname.sun_family = AF_UNIX;
+ strlcpy(sockname.sun_path, path, sizeof(sockname.sun_path));
+ if (bind(sock, (struct sockaddr *)&sockname,
+ sizeof(struct sockaddr_un)) < 0) {
+ perror("bind");
+ exit(1);
+ }
+ if (listen(sock, 1) < 0) {
+ perror("listen");
+ exit(1);
+ }
+ f = (struct listen *)file_new(ops, path, 1);
+ f->path = strdup(path);
+ if (f->path == NULL) {
+ perror("strdup");
+ exit(1);
+ }
+ f->fd = sock;
+ return f;
+}
+
+int
+listen_nfds(struct file *f) {
+ return 1;
+}
+
+int
+listen_pollfd(struct file *file, struct pollfd *pfd, int events)
+{
+ struct listen *f = (struct listen *)file;
+
+ pfd->fd = f->fd;
+ pfd->events = POLLIN;
+ return 1;
+}
+
+int
+listen_revents(struct file *file, struct pollfd *pfd)
+{
+ struct listen *f = (struct listen *)file;
+ struct sockaddr caddr;
+ socklen_t caddrlen;
+ int sock;
+
+ if (pfd->revents & POLLIN) {
+ DPRINTF("listen_revents: accepting connection\n");
+ caddrlen = sizeof(caddrlen);
+ sock = accept(f->fd, &caddr, &caddrlen);
+ if (sock < 0) {
+ perror("accept");
+ return 0;
+ }
+ if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
+ perror("fcntl(sock, O_NONBLOCK)");
+ close(sock);
+ return 0;
+ }
+ (void)sock_new(&sock_ops, sock, "socket");
+ }
+ return 0;
+}
+
+void
+listen_close(struct file *file)
+{
+ struct listen *f = (struct listen *)file;
+
+ (void)unlink(f->path);
+ free(f->path);
+ (void)close(f->fd);
+}
diff --git a/usr.bin/aucat/listen.h b/usr.bin/aucat/listen.h
new file mode 100644
index 00000000000..042287fcbc2
--- /dev/null
+++ b/usr.bin/aucat/listen.h
@@ -0,0 +1,37 @@
+/* $OpenBSD: listen.h,v 1.1 2008/10/26 08:49:44 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 LISTEN_H
+#define LISTEN_H
+
+#include <sys/types.h>
+
+#include "file.h"
+
+struct listen {
+ struct file file;
+ char *path;
+ int fd;
+};
+
+struct listen *listen_new(struct fileops *, char *);
+int listen_nfds(struct file *);
+int listen_pollfd(struct file *, struct pollfd *, int events);
+int listen_revents(struct file *, struct pollfd *);
+void listen_close(struct file *);
+extern struct fileops listen_ops;
+
+#endif /* !defined(LISTEN_H) */
diff --git a/usr.bin/aucat/pipe.c b/usr.bin/aucat/pipe.c
new file mode 100644
index 00000000000..d7cc750df5d
--- /dev/null
+++ b/usr.bin/aucat/pipe.c
@@ -0,0 +1,146 @@
+#include <sys/time.h>
+#include <sys/types.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "conf.h"
+#include "pipe.h"
+
+struct fileops pipe_ops = {
+ "pipe",
+ sizeof(struct pipe),
+ pipe_close,
+ pipe_read,
+ pipe_write,
+ NULL, /* start */
+ NULL, /* stop */
+ pipe_nfds,
+ pipe_pollfd,
+ pipe_revents
+};
+
+struct pipe *
+pipe_new(struct fileops *ops, int fd, char *name)
+{
+ struct pipe *f;
+
+ f = (struct pipe *)file_new(ops, name, 1);
+ f->fd = fd;
+ return f;
+}
+
+unsigned
+pipe_read(struct file *file, unsigned char *data, unsigned count)
+{
+ struct pipe *f = (struct pipe *)file;
+ int n;
+#ifdef DEBUG
+ struct timeval tv0, tv1, dtv;
+ unsigned us;
+
+ if (!(f->file.state & FILE_ROK)) {
+ DPRINTF("pipe_read: %s: bad state\n", f->file.name);
+ abort();
+ }
+ gettimeofday(&tv0, NULL);
+#endif
+ while ((n = read(f->fd, data, count)) < 0) {
+ f->file.state &= ~FILE_ROK;
+ if (errno == EAGAIN) {
+ DPRINTFN(3, "pipe_read: %s: blocking...\n",
+ f->file.name);
+ } else {
+ warn("%s", f->file.name);
+ file_eof(&f->file);
+ }
+ return 0;
+ }
+ if (n == 0) {
+ DPRINTFN(2, "pipe_read: %s: eof\n", f->file.name);
+ f->file.state &= ~FILE_ROK;
+ file_eof(&f->file);
+ return 0;
+ }
+#ifdef DEBUG
+ gettimeofday(&tv1, NULL);
+ timersub(&tv1, &tv0, &dtv);
+ us = dtv.tv_sec * 1000000 + dtv.tv_usec;
+ DPRINTFN(us < 5000 ? 4 : 1,
+ "pipe_read: %s: got %d bytes in %uus\n",
+ f->file.name, n, us);
+#endif
+ return n;
+}
+
+
+unsigned
+pipe_write(struct file *file, unsigned char *data, unsigned count)
+{
+ struct pipe *f = (struct pipe *)file;
+ int n;
+#ifdef DEBUG
+ struct timeval tv0, tv1, dtv;
+ unsigned us;
+
+ if (!(f->file.state & FILE_WOK)) {
+ DPRINTF("pipe_write: %s: bad state\n", f->file.name);
+ abort();
+ }
+ gettimeofday(&tv0, NULL);
+#endif
+ while ((n = write(f->fd, data, count)) < 0) {
+ f->file.state &= ~FILE_WOK;
+ if (errno == EAGAIN) {
+ DPRINTFN(3, "pipe_write: %s: blocking...\n",
+ f->file.name);
+ } else {
+ if (errno != EPIPE)
+ warn("%s", f->file.name);
+ file_hup(&f->file);
+ }
+ return 0;
+ }
+#ifdef DEBUG
+ gettimeofday(&tv1, NULL);
+ timersub(&tv1, &tv0, &dtv);
+ us = dtv.tv_sec * 1000000 + dtv.tv_usec;
+ DPRINTFN(us < 5000 ? 4 : 1,
+ "pipe_write: %s: wrote %d bytes in %uus\n",
+ f->file.name, n, us);
+#endif
+ return n;
+}
+
+int
+pipe_nfds(struct file *file) {
+ return 1;
+}
+
+int
+pipe_pollfd(struct file *file, struct pollfd *pfd, int events)
+{
+ struct pipe *f = (struct pipe *)file;
+
+ pfd->fd = f->fd;
+ pfd->events = events;
+ return (events != 0) ? 1 : 0;
+}
+
+int
+pipe_revents(struct file *f, struct pollfd *pfd)
+{
+ return pfd->revents;
+}
+
+void
+pipe_close(struct file *file)
+{
+ struct pipe *f = (struct pipe *)file;
+
+ close(f->fd);
+}
diff --git a/usr.bin/aucat/pipe.h b/usr.bin/aucat/pipe.h
new file mode 100644
index 00000000000..043dab40c3d
--- /dev/null
+++ b/usr.bin/aucat/pipe.h
@@ -0,0 +1,39 @@
+/* $OpenBSD: pipe.h,v 1.1 2008/10/26 08:49:44 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 PIPE_H
+#define PIPE_H
+
+#include "file.h"
+
+struct aparams;
+
+struct pipe {
+ struct file file;
+ int fd; /* file descriptor */
+};
+
+extern struct fileops pipe_ops;
+
+struct pipe *pipe_new(struct fileops *, int, char *);
+void pipe_close(struct file *);
+unsigned pipe_read(struct file *, unsigned char *, unsigned);
+unsigned pipe_write(struct file *, unsigned char *, unsigned);
+int pipe_nfds(struct file *);
+int pipe_pollfd(struct file *, struct pollfd *, int);
+int pipe_revents(struct file *, struct pollfd *);
+
+#endif /* !defined(FILE_H) */
diff --git a/usr.bin/aucat/safile.c b/usr.bin/aucat/safile.c
new file mode 100644
index 00000000000..2b77bdf596f
--- /dev/null
+++ b/usr.bin/aucat/safile.c
@@ -0,0 +1,288 @@
+/* $OpenBSD: safile.c,v 1.1 2008/10/26 08:49:44 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 <libsa.h>
+
+#include "conf.h"
+#include "file.h"
+#include "aproc.h"
+#include "aparams.h"
+#include "safile.h"
+#include "dev.h"
+
+struct safile {
+ struct file file;
+ struct sa_hdl *hdl;
+#ifdef DEBUG
+ struct timeval itv, otv;
+#endif
+};
+
+void safile_close(struct file *);
+unsigned safile_read(struct file *, unsigned char *, unsigned);
+unsigned safile_write(struct file *, unsigned char *, unsigned);
+void safile_start(struct file *);
+void safile_stop(struct file *);
+int safile_nfds(struct file *);
+int safile_pollfd(struct file *, struct pollfd *, int);
+int safile_revents(struct file *, struct pollfd *);
+
+struct fileops safile_ops = {
+ "libsa",
+ sizeof(struct safile),
+ safile_close,
+ safile_read,
+ safile_write,
+ safile_start,
+ safile_stop,
+ safile_nfds,
+ safile_pollfd,
+ safile_revents
+};
+
+void
+safile_cb(void *addr, int delta)
+{
+ struct safile *f = (struct safile *)addr;
+ struct aproc *p;
+
+ if (delta != 0) {
+ p = f->file.wproc;
+ if (p && p->ops->opos)
+ p->ops->opos(p, NULL, delta);
+ }
+ if (delta != 0) {
+ p = f->file.rproc;
+ if (p && p->ops->ipos)
+ p->ops->ipos(p, NULL, delta);
+ }
+}
+
+/*
+ * open the device
+ */
+struct safile *
+safile_new(struct fileops *ops, char *path,
+ struct aparams *ipar, struct aparams *opar,
+ unsigned *bufsz, unsigned *round)
+{
+ struct sa_par par;
+ struct sa_hdl *hdl;
+ struct safile *f;
+ int mode;
+
+ mode = 0;
+ if (ipar)
+ mode |= SA_REC;
+ if (opar)
+ mode |= SA_PLAY;
+ if (!mode)
+ fprintf(stderr, "%s: must at least play or record", path);
+ hdl = sa_open(path, mode, 1);
+ if (hdl == NULL) {
+ fprintf(stderr, "safile_new: can't open device\n");
+ return NULL;
+ }
+ sa_initpar(&par);
+ if (ipar) {
+ par.bits = ipar->bits;
+ par.bps = ipar->bps;
+ par.sig = ipar->sig;
+ par.le = ipar->le;
+ par.msb = ipar->msb;
+ par.rate = ipar->rate;
+ par.rchan = ipar->cmax - ipar->cmin + 1;
+ } else {
+ par.bits = opar->bits;
+ par.bps = opar->bps;
+ par.sig = opar->sig;
+ par.le = opar->le;
+ par.msb = opar->msb;
+ par.rate = opar->rate;
+ }
+ if (opar)
+ par.pchan = opar->cmax - opar->cmin + 1;
+ if (*bufsz)
+ par.bufsz = *bufsz;
+ if (!sa_setpar(hdl, &par)) {
+ fprintf(stderr, "safile_new: sa_setpar failed\n");
+ exit(1);
+ }
+ if (!sa_getpar(hdl, &par)) {
+ fprintf(stderr, "safile_new: sa_getpar failed\n");
+ exit(1);
+ }
+ if (ipar) {
+ ipar->bits = par.bits;
+ ipar->bps = par.bps;
+ ipar->sig = par.sig;
+ ipar->le = par.le;
+ ipar->msb = par.msb;
+ ipar->rate = par.rate;
+ ipar->cmax = par.rchan - 1;
+ ipar->cmin = 0;
+ }
+ if (opar) {
+ opar->bits = par.bits;
+ opar->bps = par.bps;
+ opar->sig = par.sig;
+ opar->le = par.le;
+ opar->msb = par.msb;
+ opar->rate = par.rate;
+ opar->cmax = par.pchan - 1;
+ opar->cmin = 0;
+ }
+ *bufsz = par.bufsz;
+ *round = par.round;
+ DPRINTF("safile_open: using %u(%u) fpb\n", *bufsz, *round);
+ f = (struct safile *)file_new(ops, "hdl", sa_nfds(hdl));
+ f->hdl = hdl;
+ sa_onmove(f->hdl, safile_cb, f);
+ return f;
+}
+
+void
+safile_start(struct file *file)
+{
+ struct safile *f = (struct safile *)file;
+
+ if (!sa_start(f->hdl)) {
+ fprintf(stderr, "safile_start: sa_start() failed\n");
+ exit(1);
+ }
+ DPRINTF("safile_start: play/rec started\n");
+}
+
+void
+safile_stop(struct file *file)
+{
+ struct safile *f = (struct safile *)file;
+
+ if (!sa_stop(f->hdl)) {
+ fprintf(stderr, "safile_stop: sa_start() filed\n");
+ exit(1);
+ }
+ DPRINTF("safile_stop: play/rec stopped\n");
+}
+
+unsigned
+safile_read(struct file *file, unsigned char *data, unsigned count)
+{
+ struct safile *f = (struct safile *)file;
+ unsigned n;
+#ifdef DEBUG
+ struct timeval tv0, tv1, dtv;
+ unsigned us;
+
+ if (!(f->file.state & FILE_ROK)) {
+ DPRINTF("file_read: %s: bad state\n", f->file.name);
+ abort();
+ }
+ gettimeofday(&tv0, NULL);
+#endif
+ n = sa_read(f->hdl, data, count);
+ if (n == 0) {
+ f->file.state &= ~FILE_ROK;
+ if (sa_eof(f->hdl)) {
+ fprintf(stderr, "safile_read: eof\n");
+ file_eof(&f->file);
+ } else {
+ DPRINTFN(3, "safile_read: %s: blocking...\n",
+ f->file.name);
+ }
+ return 0;
+ }
+#ifdef DEBUG
+ gettimeofday(&tv1, NULL);
+ timersub(&tv1, &tv0, &dtv);
+ us = dtv.tv_sec * 1000000 + dtv.tv_usec;
+ DPRINTFN(us < 5000 ? 4 : 1,
+ "safile_read: %s: got %d bytes in %uus\n",
+ f->file.name, n, us);
+#endif
+ return n;
+
+}
+
+unsigned
+safile_write(struct file *file, unsigned char *data, unsigned count)
+{
+ struct safile *f = (struct safile *)file;
+ unsigned n;
+#ifdef DEBUG
+ struct timeval tv0, tv1, dtv;
+ unsigned us;
+
+ if (!(f->file.state & FILE_WOK)) {
+ DPRINTF("safile_write: %s: bad state\n", f->file.name);
+ abort();
+ }
+ gettimeofday(&tv0, NULL);
+#endif
+ n = sa_write(f->hdl, data, count);
+ if (n == 0) {
+ f->file.state &= ~FILE_WOK;
+ if (sa_eof(f->hdl)) {
+ fprintf(stderr, "safile_write: %s: hup\n", f->file.name);
+ file_hup(&f->file);
+ } else {
+ DPRINTFN(3, "safile_write: %s: blocking...\n",
+ f->file.name);
+ }
+ return 0;
+ }
+#ifdef DEBUG
+ gettimeofday(&tv1, NULL);
+ timersub(&tv1, &tv0, &dtv);
+ us = dtv.tv_sec * 1000000 + dtv.tv_usec;
+ DPRINTFN(us < 5000 ? 4 : 1,
+ "safile_write: %s: wrote %d bytes in %uus\n",
+ f->file.name, n, us);
+#endif
+ return n;
+}
+
+int
+safile_nfds(struct file *file)
+{
+ return sa_nfds(((struct safile *)file)->hdl);
+}
+
+int
+safile_pollfd(struct file *file, struct pollfd *pfd, int events)
+{
+ return sa_pollfd(((struct safile *)file)->hdl, pfd, events);
+}
+
+int
+safile_revents(struct file *file, struct pollfd *pfd)
+{
+ return sa_revents(((struct safile *)file)->hdl, pfd);
+}
+
+void
+safile_close(struct file *file)
+{
+ return sa_close(((struct safile *)file)->hdl);
+}
diff --git a/usr.bin/aucat/safile.h b/usr.bin/aucat/safile.h
new file mode 100644
index 00000000000..328e9863531
--- /dev/null
+++ b/usr.bin/aucat/safile.h
@@ -0,0 +1,37 @@
+/* $OpenBSD: safile.h,v 1.1 2008/10/26 08:49:44 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 SAFILE_H
+#define SAFILE_H
+
+struct file;
+struct fileops;
+struct safile;
+struct aparams;
+
+struct safile *safile_new(struct fileops *, char *,
+ struct aparams *, struct aparams *, unsigned *, unsigned *);
+
+extern struct fileops safile_ops;
+
+/*
+ * Sun API specific functions
+ */
+struct audio_prinfo;
+int sun_infotopar(struct audio_prinfo *, struct aparams *);
+void sun_partoinfo(struct audio_prinfo *, struct aparams *);
+
+#endif /* !defined(SUN_H) */
diff --git a/usr.bin/aucat/sock.c b/usr.bin/aucat/sock.c
new file mode 100644
index 00000000000..7c9aecce3ea
--- /dev/null
+++ b/usr.bin/aucat/sock.c
@@ -0,0 +1,931 @@
+/* $OpenBSD: sock.c,v 1.1 2008/10/26 08:49:44 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:
+ *
+ * change f->bufsz to contain only socket-side buffer,
+ * because it's less error prone
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "aproc.h"
+#include "abuf.h"
+#include "sock.h"
+#include "dev.h"
+#include "conf.h"
+
+int sock_attach(struct sock *, int);
+int sock_read(struct sock *);
+int sock_write(struct sock *);
+int sock_execmsg(struct sock *);
+void sock_reset(struct sock *);
+
+struct fileops sock_ops = {
+ "sock",
+ sizeof(struct sock),
+ pipe_close,
+ pipe_read,
+ pipe_write,
+ NULL, /* start */
+ NULL, /* stop */
+ pipe_nfds,
+ pipe_pollfd,
+ pipe_revents
+};
+
+void
+rsock_done(struct aproc *p)
+{
+ struct sock *f = (struct sock *)p->u.io.file;
+
+ DPRINTFN(1, "rsock_done: %p\n", f);
+ sock_reset(f);
+ f->pipe.file.rproc = NULL;
+ if (f->pipe.file.wproc) {
+ aproc_del(f->pipe.file.wproc);
+ file_del(&f->pipe.file);
+ }
+}
+
+int
+rsock_in(struct aproc *p, struct abuf *ibuf_dummy)
+{
+ struct sock *f = (struct sock *)p->u.io.file;
+ struct abuf *obuf;
+
+ DPRINTFN(4, "rsock_in: %p\n", f);
+
+ if (!sock_read(f))
+ return 0;
+ obuf = LIST_FIRST(&p->obuflist);
+ if (obuf) {
+ if (!abuf_flush(obuf))
+ return 0;
+ }
+ return 1;
+}
+
+int
+rsock_out(struct aproc *p, struct abuf *obuf)
+{
+ struct sock *f = (struct sock *)p->u.io.file;
+
+ if (f->pipe.file.refs > 0)
+ return 0;
+
+ DPRINTFN(4, "rsock_out: %p\n", f);
+
+ /*
+ * when calling sock_read(), we may receive a ``STOP'' command,
+ * and detach ``obuf''. In this case, there's no more caller and
+ * we'll stop processing further messages, resulting in a dead lock.
+ * The solution is to iterate over sock_read() in order to
+ * consume all messages().
+ */
+ for (;;) {
+ if (!sock_read(f))
+ return 0;
+ }
+ return 1;
+}
+
+void
+rsock_eof(struct aproc *p, struct abuf *ibuf_dummy)
+{
+ DPRINTFN(3, "rsock_eof: %p\n", p->u.io.file);
+ aproc_del(p);
+}
+
+void
+rsock_hup(struct aproc *p, struct abuf *ibuf)
+{
+ DPRINTFN(3, "rsock_hup: %p\n", p->u.io.file);
+ aproc_del(p);
+}
+
+void
+rsock_opos(struct aproc *p, struct abuf *obuf, int delta)
+{
+ struct sock *f = (struct sock *)p->u.io.file;
+
+ f->odelta += delta;
+ DPRINTFN(3, "rsock_opos: %p: delta = %d, odelta = %d\n",
+ f, delta, f->odelta);
+
+ /*
+ * negative deltas are xrun notifications for internal uses
+ * only. Dont generate a packet for this, the client will be
+ * notified later.
+ */
+ if (delta <= 0)
+ return;
+ for (;;) {
+ if (!sock_write(f))
+ break;
+ }
+}
+
+struct aproc_ops rsock_ops = {
+ "rsock",
+ rsock_in,
+ rsock_out,
+ rsock_eof,
+ rsock_hup,
+ NULL, /* newin */
+ NULL, /* newout */
+ NULL, /* ipos */
+ rsock_opos,
+ rsock_done
+};
+
+void
+wsock_done(struct aproc *p)
+{
+ struct sock *f = (struct sock *)p->u.io.file;
+
+ DPRINTFN(1, "wsock_done: %p\n", f);
+ sock_reset(f);
+ f->pipe.file.wproc = NULL;
+ if (f->pipe.file.rproc) {
+ aproc_del(f->pipe.file.rproc);
+ file_del(&f->pipe.file);
+ }
+}
+
+int
+wsock_in(struct aproc *p, struct abuf *ibuf)
+{
+ struct sock *f = (struct sock *)p->u.io.file;
+
+ if (f->pipe.file.refs > 0)
+ return 0;
+
+ DPRINTFN(4, "wsock_in: %p\n", f);
+
+ /*
+ * see remark in rsock_out()
+ */
+ for (;;) {
+ if (!sock_write(f))
+ return 0;
+ }
+ return 1;
+}
+
+int
+wsock_out(struct aproc *p, struct abuf *obuf_dummy)
+{
+ struct abuf *ibuf = LIST_FIRST(&p->ibuflist);
+ struct sock *f = (struct sock *)p->u.io.file;
+
+ DPRINTFN(3, "wsock_out: %p\n", f);
+
+ if (ibuf) {
+ DPRINTFN(3, "wsock_out: %p, filling ibuf\n", f);
+ if (!abuf_fill(ibuf))
+ return 0;
+ }
+ if (!sock_write(f))
+ return 0;
+ return 1;
+}
+
+void
+wsock_eof(struct aproc *p, struct abuf *obuf)
+{
+ DPRINTFN(3, "wsock_eof: %p\n", p->u.io.file);
+ aproc_del(p);
+}
+
+void
+wsock_hup(struct aproc *p, struct abuf *obuf_dummy)
+{
+ DPRINTFN(3, "wsock_hup: %p\n", p->u.io.file);
+ aproc_del(p);
+}
+
+void
+wsock_ipos(struct aproc *p, struct abuf *obuf, int delta)
+{
+ struct sock *f = (struct sock *)p->u.io.file;
+
+ f->idelta += delta;
+ DPRINTFN(3, "wsock_ipos: %p, delta = %d, odelta = %d\n",
+ f, delta, f->idelta);
+
+ /*
+ * negative deltas are xrun notifications for internal uses
+ * only. Dont generate a packet for this, the client will be
+ * notified later.
+ */
+ if (delta <= 0)
+ return;
+ for (;;) {
+ if (!sock_write(f))
+ break;
+ }
+}
+
+struct aproc_ops wsock_ops = {
+ "wsock",
+ wsock_in,
+ wsock_out,
+ wsock_eof,
+ wsock_hup,
+ NULL, /* newin */
+ NULL, /* newout */
+ wsock_ipos,
+ NULL, /* opos */
+ wsock_done
+};
+
+/*
+ * initialise socket in the SOCK_INIT state with default
+ * parameters
+ */
+struct sock *
+sock_new(struct fileops *ops, int fd, char *name)
+{
+ struct aproc *rproc, *wproc;
+ struct sock *f;
+
+ f = (struct sock *)pipe_new(ops, fd, name);
+ f->pstate = SOCK_INIT;
+ f->mode = 0;
+ if (dev_rec) {
+ f->wpar = dev_ipar;
+ f->mode |= AMSG_REC;
+ }
+ if (dev_play) {
+ f->rpar = dev_opar;
+ f->mode |= AMSG_PLAY;
+ }
+ f->xrun = AMSG_IGNORE;
+ f->bufsz = 2 * dev_bufsz;
+ f->round = dev_round;
+ f->odelta = f->idelta = 0;
+
+ wproc = aproc_new(&wsock_ops, 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->u.io.file = &f->pipe.file;
+ f->pipe.file.rproc = rproc;
+ f->rstate = SOCK_RMSG;
+ f->rtodo = sizeof(struct amsg);
+ return f;
+}
+
+/*
+ * free buffers
+ */
+void
+sock_freebuf(struct sock *f)
+{
+ struct abuf *rbuf, *wbuf;
+
+ f->pstate = SOCK_INIT;
+ DPRINTF("sock_freebuf:\n");
+ rbuf = LIST_FIRST(&f->pipe.file.rproc->obuflist);
+ if (rbuf)
+ abuf_eof(rbuf);
+ wbuf = LIST_FIRST(&f->pipe.file.wproc->ibuflist);
+ if (wbuf)
+ abuf_hup(wbuf);
+}
+
+/*
+ * allocate buffers, so client can start filling write-end.
+ */
+void
+sock_allocbuf(struct sock *f)
+{
+ struct abuf *rbuf = NULL, *wbuf = NULL;
+ unsigned nfr = 0;
+
+ if (f->mode & AMSG_PLAY) {
+ nfr = f->bufsz - dev_bufsz * f->rpar.rate / dev_rate;
+ rbuf = abuf_new(nfr, aparams_bpf(&f->rpar));
+ aproc_setout(f->pipe.file.rproc, rbuf);
+ f->odelta = 0;
+ }
+ if (f->mode & AMSG_REC) {
+ nfr = f->bufsz - dev_bufsz * f->wpar.rate / dev_rate;
+ wbuf = abuf_new(nfr, aparams_bpf(&f->wpar));
+ aproc_setin(f->pipe.file.wproc, wbuf);
+ f->idelta = 0;
+ }
+
+ DPRINTF("sock_allocbuf: %p, using %u/%u frames buffer\n",
+ f, nfr, f->bufsz);
+
+ f->pstate = SOCK_START;
+ if (!(f->mode & AMSG_PLAY))
+ (void)sock_attach(f, 0);
+}
+
+/*
+ * attach play and/or record buffers to dev_mix and/or dev_sub
+ */
+int
+sock_attach(struct sock *f, int force)
+{
+ struct abuf *rbuf, *wbuf;
+
+ rbuf = LIST_FIRST(&f->pipe.file.rproc->obuflist);
+ wbuf = LIST_FIRST(&f->pipe.file.wproc->ibuflist);
+
+ /*
+ * if in SOCK_START state, dont attach until
+ * the buffer isn't completely filled
+ */
+ if (!force && rbuf && ABUF_WOK(rbuf))
+ return 0;
+
+ DPRINTF("sock_attach: %p\n", f);
+ f->pstate = SOCK_RUN;
+
+ /*
+ * attach them to the device
+ */
+ 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);
+
+ /*
+ * send the initial position, if needed
+ */
+ for (;;) {
+ if (!sock_write(f))
+ break;
+ }
+ return 1;
+}
+
+void
+sock_reset(struct sock *f)
+{
+ switch (f->pstate) {
+ case SOCK_START:
+ (void)sock_attach(f, 1);
+ f->pstate = SOCK_RUN;
+ /* PASSTHROUGH */
+ case SOCK_RUN:
+ sock_freebuf(f);
+ f->pstate = SOCK_INIT;
+ /* PASSTHROUGH */
+ case SOCK_INIT:
+ /* nothing yet */
+ break;
+ }
+}
+
+/*
+ * read a message from the file descriptor, return 1 if done, 0
+ * otherwise. The message is stored in f->rmsg
+ */
+int
+sock_rmsg(struct sock *f)
+{
+ unsigned count;
+ unsigned char *data;
+
+ while (f->rtodo > 0) {
+ if (!(f->pipe.file.state & FILE_ROK)) {
+ DPRINTFN(4, "sock_rmsg: blk, rtodo = %u\n", f->rtodo);
+ return 0;
+ }
+ data = (unsigned char *)&f->rmsg;
+ data += sizeof(struct amsg) - f->rtodo;
+ count = file_read(&f->pipe.file, data, f->rtodo);
+ if (count == 0)
+ return 0;
+ f->rtodo -= count;
+ }
+ DPRINTFN(4, "sock_rmsg: %p: done\n", f);
+ return 1;
+}
+
+/*
+ * write a message to the file descriptor, return 1 if done, 0
+ * otherwise. The "m" argument is f->rmsg or f->wmsg, and the "ptodo"
+ * points to the f->rtodo or f->wtodo respectively.
+ */
+int
+sock_wmsg(struct sock *f, struct amsg *m, unsigned *ptodo)
+{
+ unsigned count;
+ unsigned char *data;
+
+ while (*ptodo > 0) {
+ if (!(f->pipe.file.state & FILE_WOK)) {
+ DPRINTFN(4, "sock_wmsg: blk, *ptodo = %u\n", *ptodo);
+ return 0;
+ }
+ data = (unsigned char *)m;
+ data += sizeof(struct amsg) - *ptodo;
+ count = file_write(&f->pipe.file, data, *ptodo);
+ if (count == 0)
+ return 0;
+ *ptodo -= count;
+ }
+ DPRINTFN(4, "sock_wmsg: %p: done\n", f);
+ return 1;
+}
+
+/*
+ * read data chunk from the file descriptor, return 1 if at least one
+ * byte was read, 0 if the file blocked.
+ */
+int
+sock_rdata(struct sock *f)
+{
+ struct aproc *p;
+ struct abuf *obuf;
+ unsigned char *data;
+ unsigned count, n;
+
+#ifdef DEBUG
+ if (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 (ABUF_FULL(obuf) || !(f->pipe.file.state & FILE_ROK))
+ return 0;
+ data = abuf_wgetblk(obuf, &count, 0);
+ if (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;
+ return 1;
+}
+
+/*
+ * write data chunk to the file descriptor, return 1 if at least one
+ * byte was written, 0 if the file blocked.
+ */
+int
+sock_wdata(struct sock *f)
+{
+ struct aproc *p;
+ struct abuf *ibuf;
+ unsigned char *data;
+ unsigned count, n;
+#define ZERO_MAX 0x1000
+ static char zero[ZERO_MAX];
+
+#ifdef DEBUG
+ if (f->wtodo == 0) {
+ fprintf(stderr, "sock_wdata: bad call: zero arg\n");
+ abort();
+ }
+#endif
+ if (!(f->pipe.file.state & FILE_WOK))
+ return 0;
+ p = f->pipe.file.wproc;
+ ibuf = LIST_FIRST(&p->ibuflist);
+ if (ibuf) {
+ if (ABUF_EMPTY(ibuf))
+ return 0;
+ data = abuf_rgetblk(ibuf, &count, 0);
+ if (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;
+ } else {
+ /*
+ * there's no dev_detach() routine yet,
+ * so now we abruptly destroy the buffer.
+ * Until we implement dev_detach, complete
+ * the packet with zeros...
+ */
+ count = ZERO_MAX;
+ if (count > f->wtodo)
+ count = f->wtodo;
+ n = file_write(&f->pipe.file, zero, count);
+ if (n == 0)
+ return 0;
+ f->wtodo -= n;
+ }
+ return 1;
+}
+
+int
+sock_setpar(struct sock *f)
+{
+ struct amsg_par *p = &f->rmsg.u.par;
+ unsigned minbuf, maxbuf;
+
+ if (AMSG_ISSET(p->mode)) {
+ if ((p->mode & ~(AMSG_PLAY | AMSG_REC)) || p->mode == 0) {
+ DPRINTF("sock_setpar: bad mode %x\n", p->mode);
+ return 0;
+ }
+ f->mode = 0;
+ if ((p->mode & AMSG_PLAY) && dev_mix)
+ f->mode |= AMSG_PLAY;
+ if ((p->mode & AMSG_REC) && dev_sub)
+ f->mode |= AMSG_REC;
+ DPRINTF("sock_setpar: mode -> %x\n", f->mode);
+ }
+ if (AMSG_ISSET(p->bits)) {
+ if (p->bits < BITS_MIN || p->bits > BITS_MAX) {
+ DPRINTF("sock_setpar: bits out of bounds\n");
+ return 0;
+ }
+ if (AMSG_ISSET(p->bps)) {
+ if (p->bps < ((p->bits + 7) / 8) || p->bps > 4) {
+ DPRINTF("sock_setpar: bps out of bounds\n");
+ return 0;
+ }
+ } else
+ p->bps = APARAMS_BPS(p->bits);
+ f->rpar.bits = f->wpar.bits = p->bits;
+ f->rpar.bps = f->wpar.bps = p->bps;
+ DPRINTF("sock_setpar: bits/bps -> %u/%u\n", p->bits, p->bps);
+ }
+ if (AMSG_ISSET(p->sig))
+ f->rpar.sig = f->wpar.sig = p->sig ? 1 : 0;
+ if (AMSG_ISSET(p->le))
+ f->rpar.le = f->wpar.le = p->le ? 1 : 0;
+ if (AMSG_ISSET(p->msb))
+ f->rpar.msb = f->wpar.msb = p->msb ? 1 : 0;
+ if (AMSG_ISSET(p->rchan) && (f->mode & AMSG_REC)) {
+ if (p->rchan < 1)
+ p->rchan = 1;
+ if (p->rchan > NCHAN_MAX - 1)
+ p->rchan = NCHAN_MAX - 1;
+ f->wpar.cmin = 0;
+ f->wpar.cmax = p->rchan - 1;
+ DPRINTF("sock_setpar: rchan -> %u\n", p->rchan);
+ }
+ if (AMSG_ISSET(p->pchan) && (f->mode & AMSG_PLAY)) {
+ if (p->pchan < 1)
+ p->pchan = 1;
+ if (p->pchan > NCHAN_MAX - 1)
+ p->pchan = NCHAN_MAX - 1;
+ f->rpar.cmin = 0;
+ f->rpar.cmax = p->pchan - 1;
+ DPRINTF("sock_setpar: pchan -> %u\n", p->pchan);
+ }
+ if (AMSG_ISSET(p->rate)) {
+ if (p->rate < RATE_MIN)
+ p->rate = RATE_MIN;
+ if (p->rate > RATE_MAX)
+ p->rate = RATE_MAX;
+ dev_roundrate(&p->rate, &f->round);
+ f->rpar.rate = f->wpar.rate = p->rate;
+ if (f->mode & AMSG_PLAY)
+ f->bufsz = 2 * dev_bufsz * f->rpar.rate / dev_rate;
+ else
+ f->bufsz = 2 * dev_bufsz * f->wpar.rate / dev_rate;
+ DPRINTF("sock_setpar: rate -> %u\n", p->rate);
+ }
+ if (AMSG_ISSET(p->xrun)) {
+ if (p->xrun != AMSG_IGNORE &&
+ p->xrun != AMSG_SYNC &&
+ p->xrun != AMSG_ERROR) {
+ DPRINTF("sock_setpar: bad xrun: %u\n", p->xrun);
+ return 0;
+ }
+ f->xrun = p->xrun;
+ DPRINTF("sock_setpar: xrun -> %u\n", f->xrun);
+ }
+ if (AMSG_ISSET(p->bufsz)) {
+ minbuf = 3 * dev_bufsz / 2;
+ minbuf -= minbuf % dev_round;
+ maxbuf = dev_bufsz;
+ if (f->mode & AMSG_PLAY) {
+ minbuf = minbuf * f->rpar.rate / dev_rate;
+ maxbuf = maxbuf * f->rpar.rate / dev_rate;
+ maxbuf += f->rpar.rate;
+ } else {
+ minbuf = minbuf * f->wpar.rate / dev_rate;
+ maxbuf = maxbuf * f->wpar.rate / dev_rate;
+ maxbuf += f->wpar.rate;
+ }
+ if (p->bufsz < minbuf)
+ p->bufsz = minbuf;
+ if (p->bufsz > maxbuf)
+ p->bufsz = maxbuf;
+ f->bufsz = p->bufsz + f->round - 1;
+ f->bufsz -= f->bufsz % f->round;
+ DPRINTF("sock_setpar: bufsz -> %u\n", f->bufsz);
+ }
+ if (debug_level > 0) {
+ fprintf(stderr, "sock_setpar: %p: rpar=", f);
+ aparams_print(&f->rpar);
+ fprintf(stderr, ", wpar=");
+ aparams_print(&f->wpar);
+ fprintf(stderr, ", mode=%u, bufsz=%u\n", f->mode, f->bufsz);
+ }
+ return 1;
+}
+
+/*
+ * execute message in f->rmsg and change the state accordingly; return 1
+ * on success, and 0 on failure, in which case the socket is destroyed.
+ */
+int
+sock_execmsg(struct sock *f)
+{
+ struct amsg *m = &f->rmsg;
+
+ switch (m->cmd) {
+ case AMSG_DATA:
+ DPRINTFN(4, "sock_execmsg: %p: DATA\n", f);
+ if (f->pstate != SOCK_RUN && f->pstate != SOCK_START) {
+ DPRINTF("sock_execmsg: %p: DATA, bad state\n", f);
+ aproc_del(f->pipe.file.rproc);
+ return 0;
+ }
+ f->rstate = SOCK_RDATA;
+ f->rtodo = m->u.data.size;
+ if (f->rtodo == 0) {
+ DPRINTF("sock_execmsg: zero-length data chunk\n");
+ aproc_del(f->pipe.file.rproc);
+ return 0;
+ }
+ break;
+ case AMSG_START:
+ DPRINTFN(2, "sock_execmsg: %p: START\n", f);
+ if (f->pstate != SOCK_INIT) {
+ DPRINTF("sock_execmsg: %p: START, bad state\n", f);
+ aproc_del(f->pipe.file.rproc);
+ return 0;
+ }
+ sock_allocbuf(f);
+ f->rstate = SOCK_RMSG;
+ f->rtodo = sizeof(struct amsg);
+ break;
+ case AMSG_STOP:
+ DPRINTFN(2, "sock_execmsg: %p: STOP\n", f);
+ if (f->pstate != SOCK_RUN && f->pstate != SOCK_START) {
+ DPRINTF("sock_execmsg: %p: STOP, bad state\n", f);
+ aproc_del(f->pipe.file.rproc);
+ return 0;
+ }
+ if (f->pstate == SOCK_START)
+ (void)sock_attach(f, 1);
+ sock_freebuf(f);
+ AMSG_INIT(m);
+ m->cmd = AMSG_ACK;
+ f->rstate = SOCK_RRET;
+ f->rtodo = sizeof(struct amsg);
+ break;
+ case AMSG_SETPAR:
+ DPRINTFN(2, "sock_execmsg: %p: SETPAR\n", f);
+ if (f->pstate != SOCK_INIT) {
+ DPRINTF("sock_execmsg: %p: SETPAR, bad state\n", f);
+ aproc_del(f->pipe.file.rproc);
+ return 0;
+ }
+ if (!sock_setpar(f)) {
+ aproc_del(f->pipe.file.rproc);
+ return 0;
+ }
+ f->rtodo = sizeof(struct amsg);
+ f->rstate = SOCK_RMSG;
+ break;
+ case AMSG_GETPAR:
+ DPRINTFN(2, "sock_execmsg: %p: GETPAR\n", f);
+ if (f->pstate != SOCK_INIT) {
+ DPRINTF("sock_execmsg: %p: GETPAR, bad state\n", f);
+ aproc_del(f->pipe.file.rproc);
+ return 0;
+ }
+ AMSG_INIT(m);
+ m->cmd = AMSG_GETPAR;
+ m->u.par.mode = f->mode;
+ m->u.par.bits = f->rpar.bits;
+ m->u.par.bps = f->rpar.bps;
+ m->u.par.sig = f->rpar.sig;
+ m->u.par.le = f->rpar.le;
+ m->u.par.msb = f->rpar.msb;
+ m->u.par.rate = f->rpar.rate;
+ m->u.par.rchan = f->wpar.cmax - f->wpar.cmin + 1;
+ m->u.par.pchan = f->rpar.cmax - f->rpar.cmin + 1;
+ m->u.par.bufsz = f->bufsz;
+ m->u.par.round = f->round;
+ f->rstate = SOCK_RRET;
+ f->rtodo = sizeof(struct amsg);
+ break;
+ case AMSG_GETCAP:
+ DPRINTFN(2, "sock_execmsg: %p: GETCAP\n", f);
+ if (f->pstate != SOCK_INIT) {
+ DPRINTF("sock_execmsg: %p: GETCAP, bad state\n", f);
+ aproc_del(f->pipe.file.rproc);
+ return 0;
+ }
+ AMSG_INIT(m);
+ m->cmd = AMSG_GETCAP;
+ m->u.cap.rate = dev_rate;
+ m->u.cap.rate_div = dev_rate_div;
+ m->u.cap.pchan = dev_opar.cmax - dev_opar.cmin + 1;
+ m->u.cap.rchan = dev_ipar.cmax - dev_ipar.cmin + 1;
+ m->u.cap.bits = sizeof(short) * 8;
+ m->u.cap.bps = sizeof(short);
+ f->rstate = SOCK_RRET;
+ f->rtodo = sizeof(struct amsg);
+ break;
+ default:
+ DPRINTF("sock_execmsg: %p bogus command\n", f);
+ aproc_del(f->pipe.file.rproc);
+ return 0;
+ }
+ if (f->rstate == SOCK_RRET) {
+ if (f->wstate != SOCK_WIDLE ||
+ !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;
+ }
+ return 1;
+}
+
+/*
+ * create a new data/pos message
+ */
+int
+sock_buildmsg(struct sock *f)
+{
+ struct aproc *p;
+ struct abuf *ibuf;
+ int *pdelta;
+
+ /*
+ * if pos changed, build a MOVE message
+ */
+ pdelta = (f->mode & AMSG_REC) ? &f->idelta : &f->odelta;
+ if ((f->pstate == SOCK_RUN && *pdelta > 0) ||
+ (f->pstate == SOCK_START && *pdelta < 0)) {
+ DPRINTFN(4, "sock_buildmsg: %p: POS: %d\n", f, *pdelta);
+ AMSG_INIT(&f->wmsg);
+ f->wmsg.cmd = AMSG_MOVE;
+ f->wmsg.u.ts.delta = *pdelta;
+ *pdelta = 0;
+ f->wtodo = sizeof(struct amsg);
+ f->wstate = SOCK_WMSG;
+ return 1;
+ }
+
+ /*
+ * if data available, build a DATA message
+ */
+ p = f->pipe.file.wproc;
+ ibuf = LIST_FIRST(&p->ibuflist);
+ if (ibuf && ABUF_ROK(ibuf)) {
+ AMSG_INIT(&f->wmsg);
+ f->wmsg.cmd = AMSG_DATA;
+ f->wmsg.u.data.size = ibuf->used - (ibuf->used % ibuf->bpf);
+ if (f->wmsg.u.data.size > AMSG_DATAMAX)
+ f->wmsg.u.data.size =
+ AMSG_DATAMAX - (AMSG_DATAMAX % ibuf->bpf);
+ f->wtodo = sizeof(struct amsg);
+ f->wstate = SOCK_WMSG;
+ return 1;
+ }
+
+ DPRINTFN(4, "sock_buildmsg: %p: idling...\n", f);
+ f->wstate = SOCK_WIDLE;
+ return 0;
+}
+
+/*
+ * read from the socket file descriptor, fill input buffer and update
+ * the state. Return 1 if at least one message or 1 data byte was
+ * processed, 0 if something blocked.
+ */
+int
+sock_read(struct sock *f)
+{
+ DPRINTFN(4, "sock_read: %p; rstate = %u, rtodo = %u\n",
+ f, f->rstate, f->rtodo);
+
+ switch (f->rstate) {
+ case SOCK_RMSG:
+ if (!sock_rmsg(f))
+ return 0;
+ if (!sock_execmsg(f))
+ return 0;
+ break;
+ case SOCK_RDATA:
+ if (!sock_rdata(f))
+ return 0;
+ if (f->rtodo == 0) {
+ f->rstate = SOCK_RMSG;
+ f->rtodo = sizeof(struct amsg);
+ }
+ if (f->pstate == SOCK_START)
+ (void)sock_attach(f, 0);
+ break;
+ case SOCK_RRET:
+ DPRINTF("sock_read: %p: blocked in RRET\n", f);
+ return 0;
+ }
+ DPRINTFN(4, "sock_read: %p: done, rstate = %u\n", f, f->rstate);
+ return 1;
+}
+
+/*
+ * process messages to return
+ */
+int
+sock_return(struct sock *f)
+{
+ struct aproc *rp;
+
+ while (f->rstate == SOCK_RRET) {
+ 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);
+ for (;;) {
+ /*
+ * in() may trigger rsock_done and destroy the
+ * wsock
+ */
+ rp = f->pipe.file.rproc;
+ if (!rp || !rp->ops->in(rp, NULL))
+ break;
+ }
+ if (f->pipe.file.wproc == NULL)
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * write messages and data on the socket file descriptor. Return 1 if
+ * at least one message or one data byte was processed, 0 if something
+ * blocked.
+ */
+int
+sock_write(struct sock *f)
+{
+ DPRINTFN(4, "sock_write: %p: wstate = %u, wtodo = %u\n",
+ f, f->wstate, f->wtodo);
+
+ switch (f->wstate) {
+ case SOCK_WMSG:
+ if (!sock_wmsg(f, &f->wmsg, &f->wtodo))
+ return 0;
+ if (f->wmsg.cmd != AMSG_DATA) {
+ f->wstate = SOCK_WIDLE;
+ f->wtodo = 0xdeadbeef;
+ break;
+ }
+ f->wstate = SOCK_WDATA;
+ f->wtodo = f->wmsg.u.data.size;
+ /* PASSTHROUGH */
+ case SOCK_WDATA:
+ if (!sock_wdata(f))
+ return 0;
+ if (f->wtodo > 0)
+ break;
+ f->wstate = SOCK_WIDLE;
+ f->wtodo = 0xdeadbeef;
+ /* PASSTHROUGH */
+ case SOCK_WIDLE:
+ if (!sock_return(f))
+ return 0;
+ if (!sock_buildmsg(f))
+ return 0;
+ break;
+ default:
+ fprintf(stderr, "sock_write: unknown state\n");
+ abort();
+ }
+ return 1;
+}
+
diff --git a/usr.bin/aucat/sock.h b/usr.bin/aucat/sock.h
new file mode 100644
index 00000000000..45d96b177e8
--- /dev/null
+++ b/usr.bin/aucat/sock.h
@@ -0,0 +1,58 @@
+/* $OpenBSD: sock.h,v 1.1 2008/10/26 08:49:44 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 SOCK_H
+#define SOCK_H
+
+#include "pipe.h"
+#include "aparams.h"
+#include "amsg.h"
+
+struct sock {
+ struct pipe pipe;
+ /*
+ * socket and protocol specific stuff, mainly used
+ * to decode/encode messages in the stream.
+ */
+ struct amsg rmsg, wmsg; /* messages being sent/received */
+ unsigned rtodo; /* input bytes not read yet */
+ unsigned wtodo; /* output bytes not written yet */
+#define SOCK_RDATA 0 /* data chunk being read */
+#define SOCK_RMSG 1 /* amsg query being processed */
+#define SOCK_RRET 2 /* amsg reply being returned */
+ unsigned rstate; /* state of the read-end FSM */
+#define SOCK_WIDLE 0 /* nothing to do */
+#define SOCK_WMSG 1 /* amsg being written */
+#define SOCK_WDATA 2 /* data chunk being written */
+ unsigned wstate; /* state of the write-end FSM */
+#define SOCK_INIT 0 /* parameter negotiation */
+#define SOCK_START 1 /* filling play buffers */
+#define SOCK_RUN 2 /* attached to the mix / sub */
+ unsigned pstate; /* one of the above */
+ unsigned mode; /* a set of AMSG_PLAY, AMSG_REC */
+ struct aparams rpar; /* read (ie play) parameters */
+ struct aparams wpar; /* write (ie rec) parameters */
+ int idelta; /* input (rec) pos. change to send */
+ int odelta; /* output (play) pos. change to send */
+ unsigned bufsz; /* total buffer size */
+ unsigned round; /* block size */
+ unsigned xrun; /* one of AMSG_IGNORE, ... */
+};
+
+struct sock *sock_new(struct fileops *, int fd, char *);
+extern struct fileops sock_ops;
+
+#endif /* !defined(SOCK_H) */
diff --git a/usr.bin/aucat/wav.c b/usr.bin/aucat/wav.c
new file mode 100644
index 00000000000..ae85e809f13
--- /dev/null
+++ b/usr.bin/aucat/wav.c
@@ -0,0 +1,116 @@
+#include <sys/types.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "conf.h"
+#include "wav.h"
+
+/*
+ * max data of a .wav file. The total file size must be smaller than
+ * 2^31, and we also have to leave some space for the headers (around 40
+ * bytes)
+ */
+#define WAV_DATAMAX (0x7fff0000)
+
+struct fileops wav_ops = {
+ "wav",
+ sizeof(struct wav),
+ wav_close,
+ wav_read,
+ wav_write,
+ NULL, /* start */
+ NULL, /* stop */
+ pipe_nfds,
+ pipe_pollfd,
+ pipe_revents
+};
+
+struct wav *
+wav_new_in(struct fileops *ops, int fd, char *name,
+ struct aparams *par, unsigned hdr)
+{
+ struct wav *f;
+
+ f = (struct wav *)pipe_new(ops, fd, name);
+ if (hdr == HDR_WAV) {
+ if (!wav_readhdr(f->pipe.fd, par, &f->rbytes))
+ exit(1);
+ f->hpar = *par;
+ } else
+ f->rbytes = -1;
+ f->hdr = 0;
+ return f;
+}
+
+struct wav *
+wav_new_out(struct fileops *ops, int fd, char *name,
+ struct aparams *par, unsigned hdr)
+{
+ struct wav *f;
+
+ f = (struct wav *)pipe_new(ops, fd, name);
+ if (hdr == HDR_WAV) {
+ if (!wav_writehdr(f->pipe.fd, par))
+ exit(1);
+ f->hpar = *par;
+ f->wbytes = WAV_DATAMAX;
+ } else
+ f->wbytes = -1;
+ f->hdr = hdr;
+ return f;
+}
+
+unsigned
+wav_read(struct file *file, unsigned char *data, unsigned count)
+{
+ struct wav *f = (struct wav *)file;
+ unsigned n;
+
+ if (f->rbytes >= 0 && count > f->rbytes) {
+ count = f->rbytes; /* file->rbytes fits in count */
+ if (count == 0) {
+ DPRINTFN(2, "wav_read: %s: complete\n", f->pipe.file.name);
+ file_eof(&f->pipe.file);
+ return 0;
+ }
+ }
+ n = pipe_read(file, data, count);
+ if (f->rbytes >= 0)
+ f->rbytes -= n;
+ return n;
+}
+
+
+unsigned
+wav_write(struct file *file, unsigned char *data, unsigned count)
+{
+ struct wav *f = (struct wav *)file;
+ unsigned n;
+
+ if (f->wbytes >= 0 && count > f->wbytes) {
+ count = f->wbytes; /* wbytes fits in count */
+ if (count == 0) {
+ DPRINTFN(2, "wav_write: %s: complete\n",
+ f->pipe.file.name);
+ file_hup(&f->pipe.file);
+ return 0;
+ }
+ }
+ n = pipe_write(file, data, count);
+ if (f->wbytes >= 0)
+ f->wbytes -= n;
+ return n;
+}
+
+void
+wav_close(struct file *file)
+{
+ struct wav *f = (struct wav *)file;
+
+ if (f->hdr == HDR_WAV)
+ wav_writehdr(f->pipe.fd, &f->hpar);
+ pipe_close(file);
+}
diff --git a/usr.bin/aucat/wav.h b/usr.bin/aucat/wav.h
new file mode 100644
index 00000000000..dea4971d869
--- /dev/null
+++ b/usr.bin/aucat/wav.h
@@ -0,0 +1,51 @@
+/* $OpenBSD: wav.h,v 1.1 2008/10/26 08:49:44 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 WAV_H
+#define WAV_H
+
+#include <sys/types.h>
+
+#include "pipe.h"
+#include "aparams.h"
+
+struct wav {
+ struct pipe pipe;
+#define HDR_AUTO 0 /* guess by looking at the file name */
+#define HDR_RAW 1 /* no headers, ie openbsd native ;-) */
+#define HDR_WAV 2 /* microsoft riff wave */
+ unsigned hdr; /* HDR_RAW or HDR_WAV */
+ struct aparams hpar; /* parameters to write on the header */
+ off_t rbytes; /* bytes to read, -1 if no limit */
+ off_t wbytes; /* bytes to write, -1 if no limit */
+};
+
+extern struct fileops wav_ops;
+
+struct wav *wav_new_in(struct fileops *, int, char *,
+ struct aparams *, unsigned);
+struct wav *wav_new_out(struct fileops *, int, char *,
+ struct aparams *, unsigned);
+unsigned wav_read(struct file *, unsigned char *, unsigned);
+unsigned wav_write(struct file *, unsigned char *, unsigned);
+void wav_close(struct file *);
+int wav_readhdr(int, struct aparams *, off_t *);
+int wav_writehdr(int, struct aparams *);
+
+/* legacy */
+int legacy_play(char *, char *);
+
+#endif /* !defined(WAV_H) */