summaryrefslogtreecommitdiff
path: root/usr.bin/aucat/abuf.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.bin/aucat/abuf.c')
-rw-r--r--usr.bin/aucat/abuf.c270
1 files changed, 270 insertions, 0 deletions
diff --git a/usr.bin/aucat/abuf.c b/usr.bin/aucat/abuf.c
new file mode 100644
index 00000000000..eb4b97e86b4
--- /dev/null
+++ b/usr.bin/aucat/abuf.c
@@ -0,0 +1,270 @@
+/* $OpenBSD: abuf.c,v 1.1 2008/05/23 07:15:46 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.
+ */
+/*
+ * Simple byte fifo. It has one reader and one writer. The abuf
+ * structure is used to interconnect audio processing units (aproc
+ * structures).
+ *
+ * The abuf data is split in two parts: (1) valid data available to the reader
+ * (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:
+ *
+ * (easy) create abuf_wcommitblk(), abuf_rdiscardblk() instead of tweeking
+ * the fifo pointers by hand. But first, find shorter function names...
+ *
+ * (easy) dont initialize aproc-specific stuff in abuf_new(), let the
+ * aproc xxx_new() routines do it
+ *
+ * (hard) make abuf_fill() a boolean depending on whether
+ * eof is reached. So the caller can do:
+ *
+ * if (!abuf_fill(buf)) {
+ * ...
+ * }
+ */
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "conf.h"
+#include "aproc.h"
+#include "abuf.h"
+
+struct abuf *
+abuf_new(unsigned nfr, unsigned bpf)
+{
+ struct abuf *buf;
+ unsigned len;
+
+ len = nfr * bpf;
+ buf = malloc(sizeof(struct abuf) + len);
+ if (buf == NULL) {
+ err(1, "abuf_new: malloc");
+ }
+ buf->bpf = bpf;
+
+ /*
+ * fill fifo pointers
+ */
+ buf->len = len;
+ buf->used = 0;
+ buf->start = 0;
+ buf->rproc = NULL;
+ buf->wproc = NULL;
+ return buf;
+}
+
+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);
+ abort();
+ }
+ if (buf->used > 0)
+ fprintf(stderr, "abuf_del: used = %u\n", buf->used);
+ free(buf);
+}
+
+/*
+ * Get a pointer to the readable block at the given offset.
+ */
+unsigned char *
+abuf_rgetblk(struct abuf *buf, unsigned *rsize, unsigned ofs)
+{
+ unsigned count, start, used;
+
+ start = buf->start + ofs;
+ used = buf->used - ofs;
+ count = buf->len - start;
+ if (count > used)
+ count = used;
+ *rsize = count;
+ return buf->data + start;
+}
+
+/*
+ * Get a pointer to the writable block at offset ofs.
+ */
+unsigned char *
+abuf_wgetblk(struct abuf *buf, unsigned *rsize, unsigned ofs)
+{
+ unsigned end, avail, count;
+
+
+ end = buf->start + buf->used + ofs;
+ if (end >= buf->len)
+ end -= buf->len;
+#ifdef DEBUG
+ if (end >= buf->len) {
+ fprintf(stderr, "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);
+ abort();
+ }
+#endif
+ avail = buf->len - (buf->used + ofs);
+ count = buf->len - end;
+ if (count > avail)
+ count = avail;
+ *rsize = count;
+ return buf->data + end;
+}
+
+/*
+ * 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)
+{
+ struct aproc *p = buf->rproc;
+
+ for (;;) {
+ if (!ABUF_ROK(buf))
+ break;
+ if (p == NULL || !p->ops->in(p, buf))
+ break;
+ }
+}
+
+/*
+ * Notify the write end of the buffer that there is room and data can be
+ * 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.
+ */
+void
+abuf_fill(struct abuf *buf)
+{
+ struct aproc *p = buf->wproc;
+
+ for (;;) {
+ if (!ABUF_WOK(buf))
+ break;
+ if (p == NULL || !p->ops->out(p, buf))
+ break;
+ }
+}
+
+/*
+ * Run a read/write loop on the buffer until either the reader or the
+ * writer blocks, or until the buffer reaches eofs. We can not get hup hear,
+ * since hup() is only called from terminal nodes, from the main loop.
+ *
+ * NOTE: The buffer may disappear (ie. be free()ed) if eof is reached, so
+ * do not keep references to the buffer or to its writer or reader.
+ */
+void
+abuf_run(struct abuf *buf)
+{
+ struct aproc *p;
+ int canfill = 1, canflush = 1;
+
+ 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 && buf->wproc) {
+ p = buf->wproc;
+ canfill = p->ops->out(p, buf);
+ } else if (ABUF_ROK(buf) && canflush) {
+ p = buf->rproc;
+ canflush = p->ops->in(p, buf);
+ } else
+ break; /* can neither read nor write */
+ }
+}
+
+/*
+ * Notify the reader that there will be no more input (producer
+ * disappeared). The buffer is flushed and eof() is called only if all
+ * data is flushed.
+ */
+void
+abuf_eof(struct abuf *buf)
+{
+#ifdef DEBUG
+ if (buf->wproc == NULL) {
+ fprintf(stderr, "abuf_eof: no writer\n");
+ abort();
+ }
+#endif
+ DPRINTFN(2, "abuf_eof: requested by %s\n", buf->wproc->name);
+ buf->wproc = NULL;
+ if (buf->rproc != NULL) {
+ abuf_flush(buf);
+ 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);
+ return;
+ }
+ DPRINTFN(2, "abuf_eof: signaling %s\n", buf->rproc->name);
+ buf->rproc->ops->eof(buf->rproc, buf);
+ buf->rproc = NULL;
+ }
+ abuf_del(buf);
+}
+
+
+/*
+ * Notify the writer that the buffer has no more consumer,
+ * and that no more data will accepted.
+ */
+void
+abuf_hup(struct abuf *buf)
+{
+#ifdef DEBUG
+ if (buf->rproc == NULL) {
+ fprintf(stderr, "abuf_hup: no reader\n");
+ abort();
+ }
+#endif
+ DPRINTFN(2, "abuf_hup: initiated by %s\n", buf->rproc->name);
+ buf->rproc = NULL;
+ if (buf->wproc != NULL) {
+ if (ABUF_ROK(buf)) {
+ warnx("abuf_hup: %s: lost %u bytes",
+ buf->wproc->name, buf->used);
+ buf->used = 0;
+ }
+ DPRINTFN(2, "abuf_hup: signaling %s\n", buf->wproc->name);
+ buf->wproc->ops->hup(buf->wproc, buf);
+ buf->wproc = NULL;
+ }
+ abuf_del(buf);
+}