-# $OpenBSD: Makefile,v 1.21 2013/11/21 08:15:46 ratchov Exp $
+# $OpenBSD: Makefile,v 1.22 2015/01/21 08:43:55 ratchov Exp $
PROG= aucat
-SRCS= aucat.c abuf.c aparams.c aproc.c dev.c midi.c file.c headers.c \
- siofile.c miofile.c pipe.c wav.c dbg.c
+SRCS= abuf.c afile.c aucat.c dsp.c utils.c
MAN= aucat.1
CFLAGS+=-DDEBUG -I${.CURDIR}/../../lib/libsndio
COPTS+= -Wall -Wstrict-prototypes -Wmissing-prototypes -Wpointer-arith -Wundef
-/* $OpenBSD: abuf.c,v 1.25 2013/11/18 17:37:45 ratchov Exp $ */
+/* $OpenBSD: abuf.c,v 1.26 2015/01/21 08:43:55 ratchov Exp $ */
/*
- * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
+ * Copyright (c) 2008-2012 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
* 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).
+ * Simple byte fifo.
*
* 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
- *
- * 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 <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "abuf.h"
-#include "aparams.h"
-#include "aproc.h"
-#include "conf.h"
-#ifdef DEBUG
-#include "dbg.h"
-#endif
-
-void abuf_dump(struct abuf *);
-int abuf_flush_do(struct abuf *);
-int abuf_fill_do(struct abuf *);
-void abuf_eof_do(struct abuf *);
-void abuf_hup_do(struct abuf *);
+#include "utils.h"
#ifdef DEBUG
void
-abuf_dbg(struct abuf *buf)
-{
- if (buf->wproc) {
- aproc_dbg(buf->wproc);
- } else {
- dbg_puts("none");
- }
- dbg_puts(buf->inuse ? "=>" : "->");
- if (buf->rproc) {
- aproc_dbg(buf->rproc);
- } else {
- dbg_puts("none");
- }
-}
-
-void
-abuf_dump(struct abuf *buf)
+abuf_log(struct abuf *buf)
{
- abuf_dbg(buf);
- dbg_puts(": used = ");
- dbg_putu(buf->used);
- dbg_puts("/");
- dbg_putu(buf->len);
- dbg_puts(" start = ");
- dbg_putu(buf->start);
- dbg_puts("\n");
+ log_putu(buf->start);
+ log_puts("+");
+ log_putu(buf->used);
+ log_puts("/");
+ log_putu(buf->len);
}
#endif
-struct abuf *
-abuf_new(unsigned int nfr, struct aparams *par)
+void
+abuf_init(struct abuf *buf, unsigned int len)
{
- struct abuf *buf;
- unsigned int len, bpf;
-
- bpf = aparams_bpf(par);
- len = nfr * bpf;
- buf = malloc(sizeof(struct abuf) + len);
- if (buf == NULL) {
-#ifdef DEBUG
- dbg_puts("couldn't allocate abuf of ");
- dbg_putu(nfr);
- dbg_puts("fr * ");
- dbg_putu(bpf);
- dbg_puts("bpf\n");
- dbg_panic();
-#else
- err(1, "malloc");
-#endif
- }
- buf->bpf = bpf;
- buf->cmin = par->cmin;
- buf->cmax = par->cmax;
- buf->inuse = 0;
-
- /*
- * fill fifo pointers
- */
- buf->len = nfr;
+ buf->data = xmalloc(len);
+ buf->len = len;
buf->used = 0;
buf->start = 0;
- buf->rproc = NULL;
- buf->wproc = NULL;
- buf->duplex = NULL;
- return buf;
}
void
-abuf_del(struct abuf *buf)
+abuf_done(struct abuf *buf)
{
- if (buf->duplex)
- buf->duplex->duplex = NULL;
-#ifdef DEBUG
- if (buf->rproc || buf->wproc) {
- abuf_dbg(buf);
- dbg_puts(": can't delete referenced buffer\n");
- dbg_panic();
- }
- if (ABUF_ROK(buf)) {
- /*
- * XXX: we should call abort(), here.
- * However, poll() doesn't seem to return POLLHUP,
- * so the reader is never destroyed; instead it appears
- * as blocked. Fix file_poll(), if fixable, and add
- * a call to abord() here.
- */
- if (debug_level >= 3) {
- abuf_dbg(buf);
- dbg_puts(": deleting non-empty buffer, used = ");
- dbg_putu(buf->used);
- dbg_puts("\n");
+#ifdef DEBUG
+ if (buf->used > 0) {
+ if (log_level >= 3) {
+ log_puts("deleting non-empty buffer, used = ");
+ log_putu(buf->used);
+ log_puts("\n");
}
}
#endif
- free(buf);
-}
-
-/*
- * Clear buffer contents.
- */
-void
-abuf_clear(struct abuf *buf)
-{
-#ifdef DEBUG
- if (debug_level >= 3) {
- abuf_dbg(buf);
- dbg_puts(": cleared\n");
- }
-#endif
- buf->used = 0;
- buf->start = 0;
+ xfree(buf->data);
+ buf->data = (void *)0xdeadbeef;
}
/*
- * Get a pointer to the readable block at the given offset.
+ * return the reader pointer and the number of bytes available
*/
unsigned char *
-abuf_rgetblk(struct abuf *buf, unsigned int *rsize, unsigned int ofs)
+abuf_rgetblk(struct abuf *buf, int *rsize)
{
- unsigned int count, start, used;
+ int count;
- start = buf->start + ofs;
- used = buf->used - ofs;
- if (start >= buf->len)
- start -= buf->len;
-#ifdef DEBUG
- if (start >= buf->len || used > buf->used) {
- abuf_dump(buf);
- dbg_puts(": rgetblk: bad ofs = ");
- dbg_putu(ofs);
- dbg_puts("\n");
- dbg_panic();
- }
-#endif
- count = buf->len - start;
- if (count > used)
- count = used;
+ count = buf->len - buf->start;
+ if (count > buf->used)
+ count = buf->used;
*rsize = count;
- return (unsigned char *)buf + sizeof(struct abuf) + start * buf->bpf;
+ return buf->data + buf->start;
}
/*
- * Discard the block at the start postion.
+ * discard "count" bytes at the start postion.
*/
void
-abuf_rdiscard(struct abuf *buf, unsigned int count)
+abuf_rdiscard(struct abuf *buf, int count)
{
#ifdef DEBUG
- if (count > buf->used) {
- abuf_dump(buf);
- dbg_puts(": rdiscard: bad count = ");
- dbg_putu(count);
- dbg_puts("\n");
- dbg_panic();
- }
- if (debug_level >= 4) {
- abuf_dbg(buf);
- dbg_puts(": discard(");
- dbg_putu(count);
- dbg_puts(")\n");
+ if (count < 0 || count > buf->used) {
+ log_puts("abuf_rdiscard: bad count = ");
+ log_putu(count);
+ log_puts("\n");
+ panic();
}
#endif
buf->used -= count;
}
/*
- * Commit the data written at the end postion.
+ * advance the writer pointer by "count" bytes
*/
void
-abuf_wcommit(struct abuf *buf, unsigned int count)
+abuf_wcommit(struct abuf *buf, int count)
{
#ifdef DEBUG
- if (count > (buf->len - buf->used)) {
- abuf_dump(buf);
- dbg_puts(": rdiscard: bad count = ");
- dbg_putu(count);
- dbg_puts("\n");
- dbg_panic();
- }
- if (debug_level >= 4) {
- abuf_dbg(buf);
- dbg_puts(": commit(");
- dbg_putu(count);
- dbg_puts(")\n");
+ if (count < 0 || count > (buf->len - buf->used)) {
+ log_puts("abuf_wcommit: bad count = ");
+ log_putu(count);
+ log_puts("\n");
+ panic();
}
#endif
buf->used += count;
}
/*
- * Get a pointer to the writable block at offset ofs.
+ * get writer pointer and the number of bytes writable
*/
unsigned char *
-abuf_wgetblk(struct abuf *buf, unsigned int *rsize, unsigned int ofs)
+abuf_wgetblk(struct abuf *buf, int *rsize)
{
- unsigned int end, avail, count;
+ int end, avail, count;
-
- end = buf->start + buf->used + ofs;
+ end = buf->start + buf->used;
if (end >= buf->len)
end -= buf->len;
-#ifdef DEBUG
- if (end >= buf->len) {
- abuf_dump(buf);
- dbg_puts(": wgetblk: bad ofs = ");
- dbg_putu(ofs);
- dbg_puts("\n");
- dbg_panic();
- }
-#endif
- avail = buf->len - (buf->used + ofs);
+ avail = buf->len - buf->used;
count = buf->len - end;
if (count > avail)
- count = avail;
+ count = avail;
*rsize = count;
- return (unsigned char *)buf + sizeof(struct abuf) + end * buf->bpf;
-}
-
-/*
- * Flush buffer either by dropping samples or by calling the aproc
- * call-back to consume data. Return 0 if blocked, 1 otherwise.
- */
-int
-abuf_flush_do(struct abuf *buf)
-{
- struct aproc *p;
-
- p = buf->rproc;
- if (!p)
- return 0;
-#ifdef DEBUG
- if (debug_level >= 4) {
- aproc_dbg(p);
- dbg_puts(": in\n");
- }
-#endif
- return p->ops->in(p, buf);
-}
-
-/*
- * Fill the buffer either by generating silence or by calling the aproc
- * call-back to provide data. Return 0 if blocked, 1 otherwise.
- */
-int
-abuf_fill_do(struct abuf *buf)
-{
- struct aproc *p;
-
- p = buf->wproc;
- if (!p)
- return 0;
-#ifdef DEBUG
- if (debug_level >= 4) {
- aproc_dbg(p);
- dbg_puts(": out\n");
- }
-#endif
- return p->ops->out(p, buf);
-}
-
-/*
- * 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) {
- buf->rproc = NULL;
- LIST_REMOVE(buf, ient);
- buf->inuse++;
-#ifdef DEBUG
- if (debug_level >= 4) {
- aproc_dbg(p);
- dbg_puts(": eof\n");
- }
-#endif
- p->ops->eof(p, buf);
- buf->inuse--;
- }
- 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)) {
-#ifdef DEBUG
- if (debug_level >= 3) {
- abuf_dbg(buf);
- dbg_puts(": hup: lost ");
- dbg_putu(buf->used);
- dbg_puts(" bytes\n");
- }
-#endif
- buf->used = 0;
- }
- p = buf->wproc;
- if (p != NULL) {
- buf->wproc = NULL;
- LIST_REMOVE(buf, oent);
- buf->inuse++;
-#ifdef DEBUG
- if (debug_level >= 3) {
- aproc_dbg(p);
- dbg_puts(": hup\n");
- }
-#endif
- p->ops->hup(p, buf);
- buf->inuse--;
- }
- 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) {
-#ifdef DEBUG
- if (debug_level >= 4) {
- abuf_dbg(buf);
- dbg_puts(": flush blocked (inuse)\n");
- }
-#endif
- } else {
- buf->inuse++;
- for (;;) {
- if (!abuf_flush_do(buf))
- break;
- }
- buf->inuse--;
- if (ABUF_HUP(buf)) {
- abuf_hup_do(buf);
- return 0;
- }
- }
- return 1;
-}
-
-/*
- * 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.
- *
- * Return 1 if the buffer was filled, and 0 if eof condition occured. The
- * reader must detach the buffer on EOF condition, since its aproc->eof()
- * call-back will never be called.
- */
-int
-abuf_fill(struct abuf *buf)
-{
- if (buf->inuse) {
-#ifdef DEBUG
- if (debug_level >= 4) {
- abuf_dbg(buf);
- dbg_puts(": fill blocked (inuse)\n");
- }
-#endif
- } else {
- buf->inuse++;
- for (;;) {
- if (!abuf_fill_do(buf))
- break;
- }
- buf->inuse--;
- if (ABUF_EOF(buf)) {
- abuf_eof_do(buf);
- return 0;
- }
- }
- return 1;
-}
-
-/*
- * 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 here,
- * 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)
-{
- int canfill = 1, canflush = 1;
-
- if (buf->inuse) {
-#ifdef DEBUG
- if (debug_level >= 4) {
- abuf_dbg(buf);
- dbg_puts(": run blocked (inuse)\n");
- }
-#endif
- return;
- }
- buf->inuse++;
- for (;;) {
- 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;
- }
- buf->inuse--;
- if (ABUF_EOF(buf)) {
- abuf_eof_do(buf);
- return;
- }
- if (ABUF_HUP(buf)) {
- abuf_hup_do(buf);
- return;
- }
-}
-
-/*
- * 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 (debug_level >= 3) {
- abuf_dbg(buf);
- dbg_puts(": eof requested\n");
- }
- if (buf->wproc == NULL) {
- abuf_dbg(buf);
- dbg_puts(": eof, no writer\n");
- dbg_panic();
- }
-#endif
- LIST_REMOVE(buf, oent);
- buf->wproc = NULL;
- if (buf->rproc != NULL) {
- if (!abuf_flush(buf))
- return;
- if (ABUF_ROK(buf)) {
- /*
- * Could not flush everything, the reader will
- * have a chance to delete the abuf later.
- */
-#ifdef DEBUG
- if (debug_level >= 3) {
- abuf_dbg(buf);
- dbg_puts(": eof, blocked (drain)\n");
- }
-#endif
- return;
- }
- }
- if (buf->inuse) {
-#ifdef DEBUG
- if (debug_level >= 3) {
- abuf_dbg(buf);
- dbg_puts(": eof, blocked (inuse)\n");
- }
-#endif
- return;
- }
- abuf_eof_do(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 (debug_level >= 3) {
- abuf_dbg(buf);
- dbg_puts(": hup requested\n");
- }
- if (buf->rproc == NULL) {
- abuf_dbg(buf);
- dbg_puts(": hup, no reader\n");
- dbg_panic();
- }
-#endif
- buf->rproc = NULL;
- LIST_REMOVE(buf, ient);
- if (buf->wproc != NULL) {
- if (buf->inuse) {
-#ifdef DEBUG
- if (debug_level >= 3) {
- abuf_dbg(buf);
- dbg_puts(": eof, blocked (inuse)\n");
- }
-#endif
- return;
- }
- }
- 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++;
-#ifdef DEBUG
- if (debug_level >= 4) {
- aproc_dbg(p);
- dbg_puts(": ipos delta = ");
- dbg_puti(delta);
- dbg_puts("\n");
- }
-#endif
- 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++;
-#ifdef DEBUG
- if (debug_level >= 4) {
- aproc_dbg(p);
- dbg_puts(": opos delta = ");
- dbg_puti(delta);
- dbg_puts("\n");
- }
-#endif
- p->ops->opos(p, buf, delta);
- buf->inuse--;
- }
- if (ABUF_HUP(buf))
- abuf_hup_do(buf);
+ return buf->data + end;
}
-/* $OpenBSD: abuf.h,v 1.25 2012/04/11 06:05:43 ratchov Exp $ */
+/* $OpenBSD: abuf.h,v 1.26 2015/01/21 08:43:55 ratchov Exp $ */
/*
- * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
+ * Copyright (c) 2008-2012 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
#ifndef ABUF_H
#define ABUF_H
-#include <sys/queue.h>
-
-struct aproc;
-struct aparams;
-
struct abuf {
- LIST_ENTRY(abuf) ient; /* reader's list of inputs entry */
- LIST_ENTRY(abuf) oent; /* writer's list of outputs entry */
-
- /*
- * fifo parameters
- */
- unsigned int bpf; /* bytes per frame */
- unsigned int cmin, cmax; /* channel range of this buf */
- unsigned int start; /* offset where data starts */
- unsigned int used; /* valid data */
- unsigned int len; /* size of the ring */
- struct aproc *rproc; /* reader */
- struct aproc *wproc; /* writer */
- struct abuf *duplex; /* link to buffer of the other dir */
- unsigned int inuse; /* in abuf_{flush,fill,run}() */
- unsigned int tickets; /* max data to (if throttling) */
-
- /*
- * Misc reader aproc-specific per-buffer parameters.
- */
- union {
- struct {
- int weight; /* dynamic range */
- int maxweight; /* max dynamic range allowed */
- unsigned int vol; /* volume within the vol */
- unsigned int done; /* frames ready */
- unsigned int xrun; /* underrun policy */
- int drop; /* to drop on next read */
- } mix;
- struct {
- unsigned int st; /* MIDI running status */
- unsigned int used; /* bytes used from ``msg'' */
- unsigned int idx; /* actual MIDI message size */
- unsigned int len; /* MIDI message length */
-#define MIDI_MSGMAX 16 /* max size of MIDI msg */
- unsigned char msg[MIDI_MSGMAX];
- } midi;
- } r;
-
- /*
- * Misc reader aproc-specific per-buffer parameters.
- */
- union {
- struct {
- unsigned int todo; /* frames to process */
- } mix;
- struct {
- unsigned int done; /* frames copied */
- unsigned int xrun; /* one of XRUN_XXX */
- int silence; /* to add on next write */
- } sub;
- struct {
- struct abuf *owner; /* current input stream */
- } midi;
- } w;
+ int start; /* offset (frames) where stored data starts */
+ int used; /* frames stored in the buffer */
+ int len; /* total size of the buffer (frames) */
+ unsigned char *data;
};
-/*
- * the buffer contains at least one frame. This macro should
- * be used to check if the buffer can be flushed
- */
-#define ABUF_ROK(b) ((b)->used > 0)
-
-/*
- * there's room for at least one frame
- */
-#define ABUF_WOK(b) ((b)->len - (b)->used > 0)
-
-/*
- * the buffer is empty and has no writer anymore
- */
-#define ABUF_EOF(b) (!ABUF_ROK(b) && (b)->wproc == NULL)
-
-/*
- * the buffer has no reader anymore, note that it's not
- * enough the buffer to be disconnected, because it can
- * be not yet connected buffer (eg. socket play buffer)
- */
-#define ABUF_HUP(b) (!ABUF_WOK(b) && (b)->rproc == NULL)
-
-struct abuf *abuf_new(unsigned int, struct aparams *);
-void abuf_del(struct abuf *);
-void abuf_dbg(struct abuf *);
-void abuf_clear(struct abuf *);
-unsigned char *abuf_rgetblk(struct abuf *, unsigned int *, unsigned int);
-unsigned char *abuf_wgetblk(struct abuf *, unsigned int *, unsigned int);
-void abuf_rdiscard(struct abuf *, unsigned int);
-void abuf_wcommit(struct abuf *, unsigned int);
-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);
+void abuf_init(struct abuf *, unsigned int);
+void abuf_done(struct abuf *);
+void abuf_log(struct abuf *);
+unsigned char *abuf_rgetblk(struct abuf *, int *);
+unsigned char *abuf_wgetblk(struct abuf *, int *);
+void abuf_rdiscard(struct abuf *, int);
+void abuf_wcommit(struct abuf *, int);
#endif /* !defined(ABUF_H) */
--- /dev/null
+/*
+ * Copyright (c) 2008-2014 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 <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include "afile.h"
+#include "utils.h"
+
+typedef struct {
+ unsigned char ld[4];
+} le32_t;
+
+typedef struct {
+ unsigned char lw[2];
+} le16_t;
+
+typedef struct {
+ unsigned char bd[4];
+} be32_t;
+
+typedef struct {
+ unsigned char bw[2];
+} be16_t;
+
+struct wav_riff {
+ char id[4];
+ le32_t size;
+ char type[4];
+};
+
+struct wav_chunk {
+ char id[4];
+ le32_t size;
+};
+
+struct wav_fmt {
+#define WAV_FMT_PCM 1
+#define WAV_FMT_FLOAT 3
+#define WAV_FMT_ALAW 6
+#define WAV_FMT_ULAW 7
+#define WAV_FMT_EXT 0xfffe
+ le16_t fmt;
+ le16_t nch;
+ le32_t rate;
+ le32_t byterate;
+ le16_t blkalign;
+ le16_t bits;
+#define WAV_FMT_SIZE 16
+#define WAV_FMT_EXT_SIZE (16 + 24)
+ le16_t extsize;
+ le16_t valbits;
+ le32_t chanmask;
+ le16_t extfmt;
+ char guid[14];
+};
+
+struct wav_hdr {
+ struct wav_riff riff; /* 00..11 */
+ struct wav_chunk fmt_hdr; /* 12..20 */
+ struct wav_fmt fmt;
+ struct wav_chunk data_hdr;
+};
+
+struct aiff_form {
+ char id[4];
+ be32_t size;
+ char type[4];
+};
+
+struct aiff_chunk {
+ char id[4];
+ be32_t size;
+};
+
+struct aiff_comm {
+ struct aiff_commbase {
+ be16_t nch;
+ be32_t nfr;
+ be16_t bits;
+ /* rate in 80-bit floating point */
+ be16_t rate_ex;
+ be32_t rate_hi;
+ be32_t rate_lo;
+ } base;
+ char comp_id[4];
+ /* followed by stuff we don't care about */
+};
+
+struct aiff_data {
+ be32_t offs;
+ be32_t blksz;
+};
+
+struct aiff_hdr {
+ struct aiff_form form;
+ struct aiff_chunk comm_hdr;
+ struct aiff_commbase comm;
+ struct aiff_chunk data_hdr;
+ struct aiff_data data;
+};
+
+struct au_hdr {
+ char id[4];
+ be32_t offs;
+ be32_t size;
+#define AU_FMT_PCM8 2
+#define AU_FMT_PCM16 3
+#define AU_FMT_PCM24 4
+#define AU_FMT_PCM32 5
+#define AU_FMT_FLOAT 6
+#define AU_FMT_ALAW 0x1b
+#define AU_FMT_ULAW 1
+ be32_t fmt;
+ be32_t rate;
+ be32_t nch;
+ char desc[8];
+ /* followed by optional desc[] continuation */
+};
+
+char wav_id_riff[4] = {'R', 'I', 'F', 'F'};
+char wav_id_wave[4] = {'W', 'A', 'V', 'E'};
+char wav_id_data[4] = {'d', 'a', 't', 'a'};
+char wav_id_fmt[4] = {'f', 'm', 't', ' '};
+char wav_guid[14] = {
+ 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x80, 0x00,
+ 0x00, 0xAA, 0x00, 0x38,
+ 0x9B, 0x71
+};
+
+char aiff_id_form[4] = {'F', 'O', 'R', 'M'};
+char aiff_id_aiff[4] = {'A', 'I', 'F', 'F'};
+char aiff_id_aifc[4] = {'A', 'I', 'F', 'C'};
+char aiff_id_data[4] = {'S', 'S', 'N', 'D'};
+char aiff_id_comm[4] = {'C', 'O', 'M', 'M'};
+char aiff_id_none[4] = {'N', 'O', 'N', 'E'};
+char aiff_id_fl32[4] = {'f', 'l', '3', '2'};
+char aiff_id_ulaw[4] = {'u', 'l', 'a', 'w'};
+char aiff_id_alaw[4] = {'a', 'l', 'a', 'w'};
+
+char au_id[4] = {'.', 's', 'n', 'd'};
+
+static inline unsigned int
+le16_get(le16_t *p)
+{
+ return p->lw[0] | p->lw[1] << 8;
+}
+
+static inline void
+le16_set(le16_t *p, unsigned int v)
+{
+ p->lw[0] = v;
+ p->lw[1] = v >> 8;
+}
+
+static inline unsigned int
+le32_get(le32_t *p)
+{
+ return p->ld[0] |
+ p->ld[1] << 8 |
+ p->ld[2] << 16 |
+ p->ld[3] << 24;
+}
+
+static inline void
+le32_set(le32_t *p, unsigned int v)
+{
+ p->ld[0] = v;
+ p->ld[1] = v >> 8;
+ p->ld[2] = v >> 16;
+ p->ld[3] = v >> 24;
+}
+
+static inline unsigned int
+be16_get(be16_t *p)
+{
+ return p->bw[1] | p->bw[0] << 8;
+}
+
+static inline void
+be16_set(be16_t *p, unsigned int v)
+{
+ p->bw[1] = v;
+ p->bw[0] = v >> 8;
+}
+
+static inline unsigned int
+be32_get(be32_t *p)
+{
+ return p->bd[3] |
+ p->bd[2] << 8 |
+ p->bd[1] << 16 |
+ p->bd[0] << 24;
+}
+
+static inline void
+be32_set(be32_t *p, unsigned int v)
+{
+ p->bd[3] = v;
+ p->bd[2] = v >> 8;
+ p->bd[1] = v >> 16;
+ p->bd[0] = v >> 24;
+}
+
+static int
+afile_readhdr(struct afile *f, void *addr, size_t size)
+{
+ if (lseek(f->fd, 0, SEEK_SET) < 0) {
+ log_puts(f->path);
+ log_puts(": failed to seek to beginning of file\n");
+ return 0;
+ }
+ if (read(f->fd, addr, size) != size) {
+ log_puts(f->path);
+ log_puts(": failed to read header\n");
+ return 0;
+ }
+ return 1;
+}
+
+static int
+afile_writehdr(struct afile *f, void *addr, size_t size)
+{
+ if (lseek(f->fd, 0, SEEK_SET) < 0) {
+ log_puts(f->path);
+ log_puts(": failed to seek back to header\n");
+ return 0;
+ }
+ if (write(f->fd, addr, size) != size) {
+ log_puts(f->path);
+ log_puts(": failed to write header\n");
+ return 0;
+ }
+ f->curpos = f->startpos;
+ return 1;
+}
+
+static int
+afile_checkpar(struct afile *f)
+{
+ if (f->nch == 0 || f->nch > NCHAN_MAX) {
+ log_puts(f->path);
+ log_puts(": ");
+ log_putu(f->nch);
+ log_puts(": unsupported number of channels\n");
+ return 0;
+ }
+ if (f->rate < RATE_MIN || f->rate > RATE_MAX) {
+ log_puts(f->path);
+ log_puts(": ");
+ log_putu(f->rate);
+ log_puts(": unsupported rate\n");
+ return 0;
+ }
+ if (f->par.bits < BITS_MIN || f->par.bits > BITS_MAX) {
+ log_puts(f->path);
+ log_puts(": ");
+ log_putu(f->par.bits);
+ log_puts(": unsupported bits per sample\n");
+ return 0;
+ }
+ if (f->par.bits > f->par.bps * 8) {
+ log_puts(f->path);
+ log_puts(": bits larger than bytes-per-sample\n");
+ return 0;
+ }
+ if (f->fmt == AFILE_FMT_FLOAT && f->par.bits != 32) {
+ log_puts(f->path);
+ log_puts(": only 32-bit floating points are supported\n");
+ return 0;
+ }
+ return 1;
+}
+
+static int
+afile_wav_readfmt(struct afile *f, unsigned int csize)
+{
+ struct wav_fmt fmt;
+ unsigned int wenc;
+
+ if (csize < WAV_FMT_SIZE) {
+ log_puts(f->path);
+ log_puts(": ");
+ log_putu(csize);
+ log_puts(": bogus format chunk size\n");
+ return 0;
+ }
+ if (csize > WAV_FMT_EXT_SIZE)
+ csize = WAV_FMT_EXT_SIZE;
+ if (read(f->fd, &fmt, csize) != csize) {
+ log_puts(f->path);
+ log_puts(": failed to read format chunk\n");
+ return 0;
+ }
+ wenc = le16_get(&fmt.fmt);
+ f->par.bits = le16_get(&fmt.bits);
+ if (wenc == WAV_FMT_EXT) {
+ if (csize != WAV_FMT_EXT_SIZE) {
+ log_puts(f->path);
+ log_puts(": missing extended format chunk\n");
+ return 0;
+ }
+ if (memcmp(fmt.guid, wav_guid, sizeof(wav_guid)) != 0) {
+ log_puts(f->path);
+ log_puts(": unknown format (GUID)\n");
+ return 0;
+ }
+ f->par.bps = (f->par.bits + 7) / 8;
+ f->par.bits = le16_get(&fmt.valbits);
+ wenc = le16_get(&fmt.extfmt);
+ } else
+ f->par.bps = (f->par.bits + 7) / 8;
+ f->nch = le16_get(&fmt.nch);
+ f->rate = le32_get(&fmt.rate);
+ f->par.le = 1;
+ f->par.msb = 1;
+ switch (wenc) {
+ case WAV_FMT_PCM:
+ f->fmt = AFILE_FMT_PCM;
+ f->par.sig = (f->par.bits <= 8) ? 0 : 1;
+ break;
+ case WAV_FMT_ALAW:
+ f->fmt = AFILE_FMT_ALAW;
+ f->par.bits = 8;
+ f->par.bps = 1;
+ break;
+ case WAV_FMT_ULAW:
+ f->fmt = AFILE_FMT_ULAW;
+ f->par.bits = 8;
+ f->par.bps = 1;
+ break;
+ case WAV_FMT_FLOAT:
+ f->fmt = AFILE_FMT_FLOAT;
+ break;
+ default:
+ log_putu(wenc);
+ log_puts(": unsupported encoding\n");
+ return 0;
+ }
+ return afile_checkpar(f);
+}
+
+static int
+afile_wav_readhdr(struct afile *f)
+{
+ struct wav_riff riff;
+ struct wav_chunk chunk;
+ unsigned int csize, rsize, pos = 0;
+ int fmt_done = 0;
+
+ if (!afile_readhdr(f, &riff, sizeof(struct wav_riff)))
+ return 0;
+ if (memcmp(&riff.id, &wav_id_riff, 4) != 0 ||
+ memcmp(&riff.type, &wav_id_wave, 4)) {
+ log_puts(f->path);
+ log_puts(": not a .wav file\n");
+ return 0;
+ }
+ rsize = le32_get(&riff.size);
+ for (;;) {
+ if (pos + sizeof(struct wav_chunk) > rsize) {
+ log_puts(f->path);
+ log_puts(": missing data chunk\n");
+ return 0;
+ }
+ if (read(f->fd, &chunk, sizeof(chunk)) != sizeof(chunk)) {
+ log_puts(f->path);
+ log_puts(": failed to read chunk header\n");
+ return 0;
+ }
+ csize = le32_get(&chunk.size);
+ if (memcmp(chunk.id, wav_id_fmt, 4) == 0) {
+ if (!afile_wav_readfmt(f, csize))
+ return 0;
+ fmt_done = 1;
+ } else if (memcmp(chunk.id, wav_id_data, 4) == 0) {
+ f->startpos = pos + sizeof(riff) + sizeof(chunk);
+ f->endpos = f->startpos + csize;
+ break;
+ } else {
+#ifdef DEBUG
+ if (log_level >= 2) {
+ log_puts(f->path);
+ log_puts(": skipped unknown chunk\n");
+ }
+#endif
+ }
+
+ /*
+ * next chunk
+ */
+ pos += sizeof(struct wav_chunk) + csize;
+ if (lseek(f->fd, sizeof(riff) + pos, SEEK_SET) < 0) {
+ log_puts(f->path);
+ log_puts(": filed to seek to chunk\n");
+ return 0;
+ }
+ }
+ if (!fmt_done) {
+ log_puts(f->path);
+ log_puts(": missing format chunk\n");
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Write header and seek to start position
+ */
+static int
+afile_wav_writehdr(struct afile *f)
+{
+ struct wav_hdr hdr;
+
+ memset(&hdr, 0, sizeof(struct wav_hdr));
+ memcpy(hdr.riff.id, wav_id_riff, 4);
+ memcpy(hdr.riff.type, wav_id_wave, 4);
+ le32_set(&hdr.riff.size, f->endpos - sizeof(hdr.riff));
+ memcpy(hdr.fmt_hdr.id, wav_id_fmt, 4);
+ le32_set(&hdr.fmt_hdr.size, sizeof(hdr.fmt));
+ le16_set(&hdr.fmt.fmt, 1);
+ le16_set(&hdr.fmt.nch, f->nch);
+ le32_set(&hdr.fmt.rate, f->rate);
+ le32_set(&hdr.fmt.byterate, f->rate * f->par.bps * f->nch);
+ le16_set(&hdr.fmt.blkalign, f->par.bps * f->nch);
+ le16_set(&hdr.fmt.bits, f->par.bits);
+ memcpy(hdr.data_hdr.id, wav_id_data, 4);
+ le32_set(&hdr.data_hdr.size, f->endpos - f->startpos);
+ return afile_writehdr(f, &hdr, sizeof(struct wav_hdr));
+}
+
+static int
+afile_aiff_readcomm(struct afile *f, unsigned int csize,
+ int comp, unsigned int *nfr)
+{
+ struct aiff_comm comm;
+ unsigned int csize_min;
+ unsigned int e, m;
+
+ csize_min = comp ?
+ sizeof(struct aiff_comm) : sizeof(struct aiff_commbase);
+ if (csize < csize_min) {
+ log_puts(f->path);
+ log_puts(": ");
+ log_putu(csize);
+ log_puts(": bogus comm chunk size\n");
+ return 0;
+ }
+ if (read(f->fd, &comm, csize_min) != csize_min) {
+ log_puts(f->path);
+ log_puts(": failed to read comm chunk\n");
+ return 0;
+ }
+ f->nch = be16_get(&comm.base.nch);
+ e = be16_get(&comm.base.rate_ex);
+ m = be32_get(&comm.base.rate_hi);
+ if (e < 0x3fff || e > 0x3fff + 31) {
+ log_puts(f->path);
+ log_puts(": malformed sample rate\n");
+ return 0;
+ }
+ f->rate = m >> (0x3fff + 31 - e);
+ if (comp) {
+ if (memcmp(comm.comp_id, aiff_id_none, 4) == 0) {
+ f->fmt = AFILE_FMT_PCM;
+ f->par.bits = be16_get(&comm.base.bits);
+ } else if (memcmp(comm.comp_id, aiff_id_fl32, 4) == 0) {
+ f->fmt = AFILE_FMT_FLOAT;
+ f->par.bits = 32;
+ } else if (memcmp(comm.comp_id, aiff_id_ulaw, 4) == 0) {
+ f->fmt = AFILE_FMT_ULAW;
+ f->par.bits = 8;
+ } else if (memcmp(comm.comp_id, aiff_id_alaw, 4) == 0) {
+ f->fmt = AFILE_FMT_ALAW;
+ f->par.bits = 8;
+ } else {
+ log_puts(f->path);
+ log_puts(": unsupported encoding\n");
+ return 0;
+ }
+ } else {
+ f->fmt = AFILE_FMT_PCM;
+ f->par.bits = be16_get(&comm.base.bits);
+ }
+ f->par.le = 0;
+ f->par.sig = 1;
+ f->par.msb = 1;
+ f->par.bps = (f->par.bits + 7) / 8;
+ *nfr = be32_get(&comm.base.nfr);
+ return afile_checkpar(f);
+}
+
+static int
+afile_aiff_readdata(struct afile *f, unsigned int csize, unsigned int *roffs)
+{
+ struct aiff_data data;
+
+ if (csize < sizeof(struct aiff_data)) {
+ log_puts(f->path);
+ log_puts(": ");
+ log_putu(csize);
+ log_puts(": bogus data chunk size\n");
+ return 0;
+ }
+ csize = sizeof(struct aiff_data);
+ if (read(f->fd, &data, csize) != csize) {
+ log_puts(f->path);
+ log_puts(": failed to read data chunk\n");
+ return 0;
+ }
+ *roffs = csize + be32_get(&data.offs);
+ return 1;
+}
+
+static int
+afile_aiff_readhdr(struct afile *f)
+{
+ struct aiff_form form;
+ struct aiff_chunk chunk;
+ unsigned int csize, rsize, nfr = 0, pos = 0, offs;
+ int comm_done = 0, comp;
+
+ if (!afile_readhdr(f, &form, sizeof(struct wav_riff)))
+ return 0;
+ if (memcmp(&form.id, &aiff_id_form, 4) != 0) {
+ log_puts(f->path);
+ log_puts(": not an aiff file\n");
+ return 0;
+ }
+ if (memcmp(&form.type, &aiff_id_aiff, 4) == 0) {
+ comp = 0;
+ } else if (memcmp(&form.type, &aiff_id_aifc, 4) == 0)
+ comp = 1;
+ else {
+ log_puts(f->path);
+ log_puts(": unsupported aiff file sub-type\n");
+ return 0;
+ }
+ rsize = be32_get(&form.size);
+ for (;;) {
+ if (pos + sizeof(struct aiff_chunk) > rsize) {
+ log_puts(f->path);
+ log_puts(": missing data chunk\n");
+ return 0;
+ }
+ if (read(f->fd, &chunk, sizeof(chunk)) != sizeof(chunk)) {
+ log_puts(f->path);
+ log_puts(": failed to read chunk header\n");
+ return 0;
+ }
+ csize = be32_get(&chunk.size);
+ if (memcmp(chunk.id, aiff_id_comm, 4) == 0) {
+ if (!afile_aiff_readcomm(f, csize, comp, &nfr))
+ return 0;
+ comm_done = 1;
+ } else if (memcmp(chunk.id, aiff_id_data, 4) == 0) {
+ if (!afile_aiff_readdata(f, csize, &offs))
+ return 0;
+ f->startpos = sizeof(form) + pos + sizeof(chunk) + offs;
+ break;
+ } else {
+#ifdef DEBUG
+ if (log_level >= 2) {
+ log_puts(f->path);
+ log_puts(": skipped unknown chunk\n");
+ }
+#endif
+ }
+
+ /*
+ * The aiff spec says "Each Chunk must contain an even
+ * number of bytes. For those Chunks whose total
+ * contents would yield an odd number of bytes, a zero
+ * pad byte must be added at the end of the Chunk. This
+ * pad byte is not included in ckDataSize, which
+ * indicates the size of the data in the Chunk."
+ */
+ csize = (csize + 1) & ~1;
+ pos += sizeof(struct aiff_chunk) + csize;
+
+ if (lseek(f->fd, sizeof(form) + pos, SEEK_SET) < 0) {
+ log_puts(f->path);
+ log_puts(": filed to seek to chunk\n");
+ return 0;
+ }
+ }
+ if (!comm_done) {
+ log_puts(f->path);
+ log_puts(": missing comm chunk\n");
+ return 0;
+ }
+ f->endpos = f->startpos + f->par.bps * f->nch * nfr;
+ return 1;
+}
+
+/*
+ * Write header and seek to start position
+ */
+static int
+afile_aiff_writehdr(struct afile *f)
+{
+ struct aiff_hdr hdr;
+ unsigned int bpf;
+ unsigned int e, m;
+
+ /* convert rate to 80-bit float (exponent and fraction part) */
+ m = f->rate;
+ e = 0x3fff + 31;
+ while ((m & 0x80000000) == 0) {
+ e--;
+ m <<= 1;
+ }
+
+ /* bytes per frame */
+ bpf = f->nch * f->par.bps;
+
+ memset(&hdr, 0, sizeof(struct aiff_hdr));
+ memcpy(hdr.form.id, aiff_id_form, 4);
+ memcpy(hdr.form.type, aiff_id_aiff, 4);
+ be32_set(&hdr.form.size, f->endpos - sizeof(hdr.form));
+
+ memcpy(hdr.comm_hdr.id, aiff_id_comm, 4);
+ be32_set(&hdr.comm_hdr.size, sizeof(hdr.comm));
+ be16_set(&hdr.comm.nch, f->nch);
+ be16_set(&hdr.comm.bits, f->par.bits);
+ be16_set(&hdr.comm.rate_ex, e);
+ be32_set(&hdr.comm.rate_hi, m);
+ be32_set(&hdr.comm.rate_lo, 0);
+ be32_set(&hdr.comm.nfr, (f->endpos - f->startpos) / bpf);
+
+ memcpy(hdr.data_hdr.id, aiff_id_data, 4);
+ be32_set(&hdr.data_hdr.size, f->endpos - f->startpos);
+ be32_set(&hdr.data.offs, 0);
+ be32_set(&hdr.data.blksz, 0);
+ return afile_writehdr(f, &hdr, sizeof(struct aiff_hdr));
+}
+
+static int
+afile_au_readhdr(struct afile *f)
+{
+ struct au_hdr hdr;
+ unsigned int fmt;
+
+ if (!afile_readhdr(f, &hdr, sizeof(struct wav_riff)))
+ return 0;
+ if (memcmp(&hdr.id, &au_id, 4) != 0) {
+ log_puts(f->path);
+ log_puts(": not a .au file\n");
+ return 0;
+ }
+ f->startpos = be32_get(&hdr.offs);
+ f->endpos = f->startpos + be32_get(&hdr.size);
+ fmt = be32_get(&hdr.fmt);
+ switch (fmt) {
+ case AU_FMT_PCM8:
+ f->fmt = AFILE_FMT_PCM;
+ f->par.bits = 8;
+ break;
+ case AU_FMT_PCM16:
+ f->fmt = AFILE_FMT_PCM;
+ f->par.bits = 16;
+ break;
+ case AU_FMT_PCM24:
+ f->fmt = AFILE_FMT_PCM;
+ f->par.bits = 24;
+ break;
+ case AU_FMT_PCM32:
+ f->fmt = AFILE_FMT_PCM;
+ f->par.bits = 32;
+ break;
+ case AU_FMT_ULAW:
+ f->fmt = AFILE_FMT_ULAW;
+ f->par.bits = 8;
+ f->par.bps = 1;
+ break;
+ case AU_FMT_ALAW:
+ f->fmt = AFILE_FMT_ALAW;
+ f->par.bits = 8;
+ f->par.bps = 1;
+ break;
+ case AU_FMT_FLOAT:
+ f->fmt = AFILE_FMT_FLOAT;
+ f->par.bits = 32;
+ f->par.bps = 4;
+ break;
+ default:
+ log_puts(f->path);
+ log_puts(": ");
+ log_putu(fmt);
+ log_puts(": unsupported encoding\n");
+ return 0;
+ }
+ f->par.le = 0;
+ f->par.sig = 1;
+ f->par.bps = f->par.bits / 8;
+ f->par.msb = 0;
+ f->rate = be32_get(&hdr.rate);
+ f->nch = be32_get(&hdr.nch);
+ if (lseek(f->fd, f->startpos, SEEK_SET) < 0) {
+ log_puts(f->path);
+ log_puts(": ");
+ log_puts("failed to seek to data chunk\n");
+ return 0;
+ }
+ return afile_checkpar(f);
+}
+
+/*
+ * Write header and seek to start position
+ */
+static int
+afile_au_writehdr(struct afile *f)
+{
+ struct au_hdr hdr;
+ unsigned int fmt;
+
+ memset(&hdr, 0, sizeof(struct au_hdr));
+ memcpy(hdr.id, au_id, 4);
+ be32_set(&hdr.offs, f->startpos);
+ be32_set(&hdr.size, f->endpos - f->startpos);
+ switch (f->par.bits) {
+ case 8:
+ fmt = AU_FMT_PCM8;
+ break;
+ case 16:
+ fmt = AU_FMT_PCM16;
+ break;
+ case 24:
+ fmt = AU_FMT_PCM24;
+ break;
+ case 32:
+ fmt = AU_FMT_PCM32;
+ break;
+#ifdef DEBUG
+ default:
+ log_puts(f->path);
+ log_puts(": wrong precision\n");
+ panic();
+ return 0;
+#endif
+ }
+ be32_set(&hdr.fmt, fmt);
+ be32_set(&hdr.rate, f->rate);
+ be32_set(&hdr.nch, f->nch);
+ return afile_writehdr(f, &hdr, sizeof(struct au_hdr));
+}
+
+size_t
+afile_read(struct afile *f, void *data, size_t count)
+{
+ off_t maxread;
+ ssize_t n;
+
+ if (f->endpos >= 0) {
+ maxread = f->endpos - f->curpos;
+ if (maxread == 0) {
+#ifdef DEBUG
+ if (log_level >= 3) {
+ log_puts(f->path);
+ log_puts(": end reached\n");
+ }
+#endif
+ return 0;
+ }
+ if (count > maxread)
+ count = maxread;
+ }
+ n = read(f->fd, data, count);
+ if (n < 0) {
+ log_puts(f->path);
+ log_puts(": couldn't read\n");
+ return 0;
+ }
+ f->curpos += n;
+ return n;
+}
+
+size_t
+afile_write(struct afile *f, void *data, size_t count)
+{
+ off_t maxwrite;
+ int n;
+
+ if (f->maxpos >= 0) {
+ maxwrite = f->maxpos - f->curpos;
+ if (maxwrite == 0) {
+#ifdef DEBUG
+ if (log_level >= 3) {
+ log_puts(f->path);
+ log_puts(": max file size reached\n");
+ }
+#endif
+ return 0;
+ }
+ if (count > maxwrite)
+ count = maxwrite;
+ }
+ n = write(f->fd, data, count);
+ if (n < 0) {
+ log_puts(f->path);
+ log_puts(": couldn't write\n");
+ return 0;
+ }
+ f->curpos += n;
+ if (f->endpos < f->curpos)
+ f->endpos = f->curpos;
+ return n;
+}
+
+int
+afile_seek(struct afile *f, off_t pos)
+{
+ pos += f->startpos;
+ if (f->endpos >= 0 && pos > f->endpos) {
+ log_puts(f->path);
+ log_puts(": attempt to seek outside file boundaries\n");
+ return 0;
+ }
+
+ /*
+ * seek only if needed to avoid errors with pipes & sockets
+ */
+ if (pos != f->curpos) {
+ if (lseek(f->fd, pos, SEEK_SET) < 0) {
+ log_puts(f->path);
+ log_puts(": couldn't seek\n");
+ return 0;
+ }
+ f->curpos = pos;
+ }
+ return 1;
+}
+
+void
+afile_close(struct afile *f)
+{
+ if (f->flags & AFILE_FWRITE) {
+ if (f->hdr == AFILE_HDR_WAV)
+ afile_wav_writehdr(f);
+ else if (f->hdr == AFILE_HDR_AIFF)
+ afile_aiff_writehdr(f);
+ else if (f->hdr == AFILE_HDR_AU)
+ afile_au_writehdr(f);
+ }
+ close(f->fd);
+}
+
+int
+afile_open(struct afile *f, char *path, int hdr, int flags,
+ struct aparams *par, int rate, int nch)
+{
+ char *ext;
+ static union {
+ struct wav_hdr wav;
+ struct aiff_hdr aiff;
+ struct au_hdr au;
+ } dummy;
+
+ f->par = *par;
+ f->rate = rate;
+ f->nch = nch;
+ f->flags = flags;
+ f->hdr = hdr;
+ if (hdr == AFILE_HDR_AUTO) {
+ f->hdr = AFILE_HDR_RAW;
+ ext = strrchr(path, '.');
+ if (ext != NULL) {
+ ext++;
+ if (strcasecmp(ext, "aif") == 0 ||
+ strcasecmp(ext, "aiff") == 0 ||
+ strcasecmp(ext, "aifc") == 0)
+ f->hdr = AFILE_HDR_AIFF;
+ else if (strcasecmp(ext, "au") == 0 ||
+ strcasecmp(ext, "snd") == 0)
+ f->hdr = AFILE_HDR_AU;
+ else if (strcasecmp(ext, "wav") == 0)
+ f->hdr = AFILE_HDR_WAV;
+ }
+ }
+ if (f->flags == AFILE_FREAD) {
+ if (strcmp(path, "-") == 0) {
+ f->path = "stdin";
+ f->fd = STDIN_FILENO;
+ } else {
+ f->path = path;
+ f->fd = open(f->path, O_RDONLY, 0);
+ if (f->fd < 0) {
+ log_puts(f->path);
+ log_puts(": failed to open for reading\n");
+ return 0;
+ }
+ }
+ if (f->hdr == AFILE_HDR_WAV) {
+ if (!afile_wav_readhdr(f))
+ goto bad_close;
+ } else if (f->hdr == AFILE_HDR_AIFF) {
+ if (!afile_aiff_readhdr(f))
+ goto bad_close;
+ } else if (f->hdr == AFILE_HDR_AU) {
+ if (!afile_au_readhdr(f))
+ goto bad_close;
+ } else {
+ f->startpos = 0;
+ f->endpos = -1; /* read until EOF */
+ f->fmt = AFILE_FMT_PCM;
+ }
+ f->curpos = f->startpos;
+ } else if (flags == AFILE_FWRITE) {
+ if (strcmp(path, "-") == 0) {
+ f->path = "stdout";
+ f->fd = STDOUT_FILENO;
+ } else {
+ f->path = path;
+ f->fd = open(f->path, O_WRONLY | O_TRUNC | O_CREAT, 0666);
+ if (f->fd < 0) {
+ log_puts(f->path);
+ log_puts(": failed to create file\n");
+ return 0;
+ }
+ }
+ if (f->hdr == AFILE_HDR_WAV) {
+ f->par.bps = (f->par.bits + 7) >> 3;
+ if (f->par.bits > 8) {
+ f->par.le = 1;
+ f->par.sig = 1;
+ } else
+ f->par.sig = 0;
+ if (f->par.bits & 7)
+ f->par.msb = 1;
+ f->endpos = f->startpos = sizeof(struct wav_hdr);
+ f->maxpos = 0x7fffffff;
+ if (!afile_writehdr(f, &dummy, sizeof(struct wav_hdr)))
+ goto bad_close;
+ } else if (f->hdr == AFILE_HDR_AIFF) {
+ f->par.bps = (f->par.bits + 7) >> 3;
+ if (f->par.bps > 1)
+ f->par.le = 0;
+ f->par.sig = 1;
+ if (f->par.bits & 7)
+ f->par.msb = 1;
+ f->endpos = f->startpos = sizeof(struct aiff_hdr);
+ f->maxpos = 0x7fffffff;
+ if (!afile_writehdr(f, &dummy, sizeof(struct aiff_hdr)))
+ goto bad_close;
+ } else if (f->hdr == AFILE_HDR_AU) {
+ f->par.bits = (f->par.bits + 7) & ~7;
+ f->par.bps = f->par.bits / 8;
+ f->par.le = 0;
+ f->par.sig = 1;
+ f->par.msb = 1;
+ f->endpos = f->startpos = sizeof(struct au_hdr);
+ f->maxpos = 0x7fffffff;
+ if (!afile_writehdr(f, &dummy, sizeof(struct au_hdr)))
+ goto bad_close;
+ } else {
+ f->endpos = f->startpos = 0;
+ f->maxpos = -1;
+ }
+ f->curpos = f->startpos;
+ } else {
+#ifdef DEBUG
+ log_puts("afile_open: wrong flags\n");
+ panic();
+#endif
+ }
+ return 1;
+bad_close:
+ close(f->fd);
+ return 0;
+}
--- /dev/null
+/* $OpenBSD: afile.h,v 1.1 2015/01/21 08:43: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.
+ */
+#ifndef WAV_H
+#define WAV_H
+
+#include <sys/types.h>
+#include "dsp.h"
+
+struct afile {
+ struct aparams par; /* file params */
+#define AFILE_FMT_PCM 0 /* integers (fixed point) */
+#define AFILE_FMT_ULAW 1 /* 8-bit mu-law */
+#define AFILE_FMT_ALAW 2 /* 8-bit a-law */
+#define AFILE_FMT_FLOAT 3 /* IEEE 754 32-bit floats */
+ int fmt; /* one of above */
+ int rate; /* file sample rate */
+ int nch; /* file channel count */
+#define AFILE_HDR_AUTO 0 /* guess from file name */
+#define AFILE_HDR_RAW 1 /* headerless aka "raw" file */
+#define AFILE_HDR_WAV 2 /* microsoft .wav */
+#define AFILE_HDR_AIFF 3 /* apple .aiff */
+#define AFILE_HDR_AU 4 /* sun/next .au */
+ int hdr; /* header type */
+ int fd; /* file descriptor */
+#define AFILE_FREAD 1 /* open for reading */
+#define AFILE_FWRITE 2 /* open for writing */
+ int flags; /* bitmap of above */
+ off_t curpos; /* read/write position (bytes) */
+ off_t startpos; /* where payload starts */
+ off_t endpos; /* where payload ends */
+ off_t maxpos; /* max allowed pos (.wav limitation) */
+ char *path; /* file name (debug only) */
+};
+
+int afile_open(struct afile *, char *, int, int, struct aparams *, int, int);
+size_t afile_read(struct afile *, void *, size_t);
+size_t afile_write(struct afile *, void *, size_t);
+int afile_seek(struct afile *, off_t);
+void afile_close(struct afile *);
+
+#endif /* !defined(WAV_H) */
+++ /dev/null
-/* $OpenBSD: aparams.c,v 1.15 2015/01/16 06:40:05 deraadt 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 <endian.h>
-
-#include "aparams.h"
-#ifdef DEBUG
-#include "dbg.h"
-#endif
-
-int aparams_ctltovol[128] = {
- 0,
- 256, 266, 276, 287, 299, 310, 323, 335,
- 348, 362, 376, 391, 406, 422, 439, 456,
- 474, 493, 512, 532, 553, 575, 597, 621,
- 645, 670, 697, 724, 753, 782, 813, 845,
- 878, 912, 948, 985, 1024, 1064, 1106, 1149,
- 1195, 1241, 1290, 1341, 1393, 1448, 1505, 1564,
- 1625, 1689, 1756, 1825, 1896, 1971, 2048, 2128,
- 2212, 2299, 2389, 2483, 2580, 2682, 2787, 2896,
- 3010, 3128, 3251, 3379, 3511, 3649, 3792, 3941,
- 4096, 4257, 4424, 4598, 4778, 4966, 5161, 5363,
- 5574, 5793, 6020, 6256, 6502, 6757, 7023, 7298,
- 7585, 7883, 8192, 8514, 8848, 9195, 9556, 9931,
- 10321, 10726, 11148, 11585, 12040, 12513, 13004, 13515,
- 14045, 14596, 15170, 15765, 16384, 17027, 17696, 18390,
- 19112, 19863, 20643, 21453, 22295, 23170, 24080, 25025,
- 26008, 27029, 28090, 29193, 30339, 31530, 32768
-};
-
-/*
- * Fake parameters for byte-streams
- */
-struct aparams aparams_none = { 1, 0, 0, 0, 0, 0, 0, 0 };
-
-#ifdef DEBUG
-/*
- * 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;
-}
-#endif /* DEBUG */
-
-/*
- * Parse an encoding string, examples: s8, u8, s16, s16le, s24be ...
- * set *istr to the char following the encoding. Return 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 = ADATA_LE;
-
- /*
- * get (optional) 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 (optional) 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 (optional) 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.
- */
-void
-aparams_init(struct aparams *par, unsigned int cmin, unsigned int cmax,
- unsigned int rate)
-{
- par->bps = sizeof(adata_t);
- par->bits = ADATA_BITS;
- par->le = ADATA_LE;
- par->sig = 1;
- par->msb = 0;
- par->cmin = cmin;
- par->cmax = cmax;
- par->rate = rate;
-}
-
-#ifdef DEBUG
-/*
- * Print the format/channels/encoding on stderr.
- */
-void
-aparams_dbg(struct aparams *par)
-{
- char enc[ENCMAX];
-
- aparams_enctostr(par, enc);
- dbg_puts(enc);
- dbg_puts(",");
- dbg_putu(par->cmin);
- dbg_puts(":");
- dbg_putu(par->cmax);
- dbg_puts(",");
- dbg_putu(par->rate);
-}
-#endif
-
-/*
- * Return true if both encodings are the same.
- */
-int
-aparams_eqenc(struct aparams *par1, struct aparams *par2)
-{
- if (par1->bps != par2->bps ||
- par1->bits != par2->bits ||
- par1->sig != par2->sig)
- return 0;
- if ((par1->bits != 8 * par1->bps) && par1->msb != par2->msb)
- return 0;
- if (par1->bps > 1 && par1->le != par2->le)
- return 0;
- return 1;
-}
-
-/*
- * Grow channels range and sample rate of ``set'' in order ``subset'' to
- * become an actual subset of it.
- */
-void
-aparams_grow(struct aparams *set, struct aparams *subset)
-{
- if (set->cmin > subset->cmin)
- set->cmin = subset->cmin;
- if (set->cmax < subset->cmax)
- set->cmax = subset->cmax;
- if (set->rate < subset->rate)
- set->rate = subset->rate;
-}
-
-/*
- * Return true if rates are the same.
- */
-int
-aparams_eqrate(struct aparams *p1, struct aparams *p2)
-{
- /* XXX: allow 1/9 halftone of difference */
- return p1->rate == p2->rate;
-}
-
-
-/*
- * Return the number of bytes per frame with the given parameters.
- */
-unsigned int
-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;
-}
+++ /dev/null
-/* $OpenBSD: aparams.h,v 1.13 2015/01/16 06:40:05 deraadt 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 APARAMS_H
-#define APARAMS_H
-
-#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
-
-/*
- * 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 {
- unsigned int bps; /* bytes per sample */
- unsigned int bits; /* actually used bits */
- unsigned int le; /* 1 if little endian, 0 if big endian */
- unsigned int sig; /* 1 if signed, 0 if unsigned */
- unsigned int msb; /* 1 if msb justified, 0 if lsb justified */
- unsigned int cmin, cmax; /* provided/consumed channels */
- unsigned int rate; /* frames per second */
-};
-
-/*
- * Samples are numbers in the interval [-1, 1[, note that 1, the upper
- * boundary is excluded. We represent them as signed fixed point numbers
- * of ADATA_BITS. We also assume that 2^(ADATA_BITS - 1) fits in a int.
- */
-#ifndef ADATA_BITS
-#define ADATA_BITS 16
-#endif
-#define ADATA_LE (BYTE_ORDER == LITTLE_ENDIAN)
-#define ADATA_UNIT (1 << (ADATA_BITS - 1))
-
-#if ADATA_BITS == 16
-
-typedef short adata_t;
-
-#define ADATA_MUL(x,y) (((int)(x) * (int)(y)) >> (ADATA_BITS - 1))
-#define ADATA_MULDIV(x,y,z) ((int)(x) * (int)(y) / (int)(z))
-
-#elif ADATA_BITS == 24
-
-typedef int adata_t;
-
-#if defined(__i386__) && defined(__GNUC__)
-
-static inline int
-fp24_mul(int x, int a)
-{
- int res;
-
- asm volatile (
- "imull %2\n\t"
- "shrdl $23, %%edx, %%eax\n\t"
- : "=a" (res)
- : "a" (x), "r" (a)
- : "%edx"
- );
- return res;
-}
-
-static inline int
-fp24_muldiv(int x, int a, int b)
-{
- int res;
-
- asm volatile (
- "imull %2\n\t"
- "idivl %3\n\t"
- : "=a" (res)
- : "a" (x), "d" (a), "r" (b)
- );
- return res;
-}
-
-#define ADATA_MUL(x,y) fp24_mul(x, y)
-#define ADATA_MULDIV(x,y,z) fp24_muldiv(x, y, z);
-
-#elif defined(__amd64__) || defined(__sparc64__)
-
-#define ADATA_MUL(x,y) \
- ((int)(((long long)(x) * (long long)(y)) >> (ADATA_BITS - 1)))
-#define ADATA_MULDIV(x,y,z) \
- ((int)((long long)(x) * (long long)(y) / (long long)(z)))
-
-#else
-#error "no 24-bit code for this architecture"
-#endif
-
-#else
-#error "only 16-bit and 24-bit precisions are supported"
-#endif
-
-#define MIDI_MAXCTL 127
-#define MIDI_TO_ADATA(m) (aparams_ctltovol[m] << (ADATA_BITS - 16))
-
-extern int aparams_ctltovol[128];
-extern struct aparams aparams_none;
-
-void aparams_init(struct aparams *, unsigned int, unsigned int, unsigned int);
-void aparams_dbg(struct aparams *);
-int aparams_eqrate(struct aparams *, struct aparams *);
-int aparams_eqenc(struct aparams *, struct aparams *);
-int aparams_eq(struct aparams *, struct aparams *);
-int aparams_subset(struct aparams *, struct aparams *);
-void aparams_grow(struct aparams *, struct aparams *);
-unsigned int 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) */
+++ /dev/null
-/* $OpenBSD: aproc.c,v 1.74 2013/11/18 17:37:45 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.
- */
-/*
- * aproc structures are simple audio processing units. They are
- * interconnected by abuf structures and form a kind of circuit. aproc
- * structure have call-backs that do the actual processing.
- *
- * This module implements the following processing units:
- *
- * - rpipe: read end of an unix file (pipe, socket, device...)
- *
- * - wpipe: write end of an unix file (pipe, socket, device...)
- *
- * - mix: mix N inputs -> 1 output
- *
- * - sub: from 1 input -> extract/copy N outputs
- *
- * - conv: converts/resamples/remaps a single stream
- *
- * - resamp: resample streams in native format
- *
- */
-#include <err.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "abuf.h"
-#include "aparams.h"
-#include "aproc.h"
-#include "conf.h"
-#include "file.h"
-#include "midi.h"
-#ifdef DEBUG
-#include "dbg.h"
-#endif
-
-/*
- * Same as ABUF_ROK(), but consider that a buffer is
- * readable if there's silence pending to be inserted
- */
-#define MIX_ROK(buf) (ABUF_ROK(buf) || (buf)->r.mix.drop < 0)
-
-/*
- * Same as ABUF_WOK(), but consider that a buffer is
- * writeable if there are samples to drop
- */
-#define SUB_WOK(buf) (ABUF_WOK(buf) || (buf)->w.sub.silence < 0)
-
-int zomb_in(struct aproc *, struct abuf *);
-int zomb_out(struct aproc *, struct abuf *);
-void zomb_eof(struct aproc *, struct abuf *);
-void zomb_hup(struct aproc *, struct abuf *);
-void zomb_newin(struct aproc *, struct abuf *);
-void zomb_newout(struct aproc *, struct abuf *);
-void zomb_ipos(struct aproc *, struct abuf *, int);
-void zomb_opos(struct aproc *, struct abuf *, int);
-
-int rfile_do(struct aproc *, unsigned int, unsigned int *);
-int rfile_in(struct aproc *, struct abuf *);
-int rfile_out(struct aproc *, struct abuf *);
-void rfile_done(struct aproc *);
-void rfile_eof(struct aproc *, struct abuf *);
-void rfile_hup(struct aproc *, struct abuf *);
-
-void wfile_done(struct aproc *);
-int wfile_do(struct aproc *, unsigned int, unsigned int *);
-int wfile_in(struct aproc *, struct abuf *);
-int wfile_out(struct aproc *, struct abuf *);
-void wfile_eof(struct aproc *, struct abuf *);
-void wfile_hup(struct aproc *, struct abuf *);
-
-void mix_drop(struct abuf *, int);
-void mix_bzero(struct abuf *, unsigned int);
-unsigned int mix_badd(struct abuf *, struct abuf *);
-int mix_xrun(struct aproc *, struct abuf *);
-int mix_in(struct aproc *, struct abuf *);
-int mix_out(struct aproc *, struct abuf *);
-void mix_eof(struct aproc *, struct abuf *);
-void mix_hup(struct aproc *, struct abuf *);
-void mix_newin(struct aproc *, struct abuf *);
-void mix_newout(struct aproc *, struct abuf *);
-void mix_opos(struct aproc *, struct abuf *, int);
-void mix_setmaster(struct aproc *);
-void mix_clear(struct aproc *);
-void mix_quit(struct aproc *);
-
-void sub_silence(struct abuf *, int);
-void sub_bcopy(struct abuf *, struct abuf *);
-int sub_xrun(struct aproc *, struct abuf *);
-int sub_in(struct aproc *, struct abuf *);
-int sub_out(struct aproc *, struct abuf *);
-void sub_eof(struct aproc *, struct abuf *);
-void sub_hup(struct aproc *, struct abuf *);
-void sub_newout(struct aproc *, struct abuf *);
-void sub_ipos(struct aproc *, struct abuf *, int);
-void sub_clear(struct aproc *);
-
-void resamp_bcopy(struct aproc *, struct abuf *, struct abuf *);
-int resamp_in(struct aproc *, struct abuf *);
-int resamp_out(struct aproc *, struct abuf *);
-void resamp_eof(struct aproc *, struct abuf *);
-void resamp_hup(struct aproc *, struct abuf *);
-void resamp_ipos(struct aproc *, struct abuf *, int);
-void resamp_opos(struct aproc *, struct abuf *, int);
-
-void enc_bcopy(struct aproc *, struct abuf *, struct abuf *);
-int enc_in(struct aproc *, struct abuf *);
-int enc_out(struct aproc *, struct abuf *);
-void enc_eof(struct aproc *, struct abuf *);
-void enc_hup(struct aproc *, struct abuf *);
-
-void dec_bcopy(struct aproc *, struct abuf *, struct abuf *);
-int dec_in(struct aproc *, struct abuf *);
-int dec_out(struct aproc *, struct abuf *);
-void dec_eof(struct aproc *, struct abuf *);
-void dec_hup(struct aproc *, struct abuf *);
-
-void join_bcopy(struct aproc *, struct abuf *, struct abuf *);
-int join_in(struct aproc *, struct abuf *);
-int join_out(struct aproc *, struct abuf *);
-void join_eof(struct aproc *, struct abuf *);
-void join_hup(struct aproc *, struct abuf *);
-
-void mon_flush(struct aproc *);
-void mon_snoop(struct aproc *, struct abuf *, unsigned int, unsigned int);
-int mon_in(struct aproc *, struct abuf *);
-void mon_clear(struct aproc *);
-int mon_out(struct aproc *, struct abuf *);
-void mon_eof(struct aproc *, struct abuf *);
-void mon_hup(struct aproc *, struct abuf *);
-void mon_ipos(struct aproc *, struct abuf *, int);
-
-#ifdef DEBUG
-void
-aproc_dbg(struct aproc *p)
-{
- dbg_puts(p->ops->name);
- dbg_puts("(");
- dbg_puts(p->name);
- dbg_puts(")");
-}
-
-int
-zomb_in(struct aproc *p, struct abuf *ibuf)
-{
- aproc_dbg(p);
- dbg_puts(": in: terminated\n");
- dbg_panic();
- return 0;
-}
-
-
-int
-zomb_out(struct aproc *p, struct abuf *obuf)
-{
- aproc_dbg(p);
- dbg_puts(": out: terminated\n");
- dbg_panic();
- return 0;
-}
-
-void
-zomb_eof(struct aproc *p, struct abuf *ibuf)
-{
- aproc_dbg(p);
- dbg_puts(": eof: terminated\n");
- dbg_panic();
-}
-
-void
-zomb_hup(struct aproc *p, struct abuf *obuf)
-{
- aproc_dbg(p);
- dbg_puts(": hup: terminated\n");
- dbg_panic();
-}
-
-void
-zomb_newin(struct aproc *p, struct abuf *ibuf)
-{
- aproc_dbg(p);
- dbg_puts(": newin: terminated\n");
- dbg_panic();
-}
-
-void
-zomb_newout(struct aproc *p, struct abuf *obuf)
-{
- aproc_dbg(p);
- dbg_puts(": newout: terminated\n");
- dbg_panic();
-}
-
-void
-zomb_ipos(struct aproc *p, struct abuf *ibuf, int delta)
-{
- aproc_dbg(p);
- dbg_puts(": ipos: terminated\n");
- dbg_panic();
-}
-
-void
-zomb_opos(struct aproc *p, struct abuf *obuf, int delta)
-{
- aproc_dbg(p);
- dbg_puts(": opos: terminated\n");
- dbg_panic();
-}
-
-struct aproc_ops zomb_ops = {
- "zomb",
- zomb_in,
- zomb_out,
- zomb_eof,
- zomb_hup,
- zomb_newin,
- zomb_newout,
- zomb_ipos,
- zomb_opos,
- NULL
-};
-#endif
-
-struct aproc *
-aproc_new(struct aproc_ops *ops, char *name)
-{
- struct aproc *p;
-
- p = malloc(sizeof(struct aproc));
- if (p == NULL)
- err(1, "%s", name);
- LIST_INIT(&p->ins);
- LIST_INIT(&p->outs);
- p->name = name;
- p->ops = ops;
- p->refs = 0;
- p->flags = 0;
- return p;
-}
-
-void
-aproc_del(struct aproc *p)
-{
- struct abuf *i;
-
-#ifdef DEBUG
- if (!p) {
- dbg_puts("aproc_del: called with NULL argument\n");
- dbg_panic();
- }
-#endif
- if (!(p->flags & APROC_ZOMB)) {
-#ifdef DEBUG
- if (debug_level >= 3) {
- aproc_dbg(p);
- dbg_puts(": terminating...\n");
- }
-#endif
- if (p->ops->done) {
-#ifdef DEBUG
- if (debug_level >= 3) {
- aproc_dbg(p);
- dbg_puts(": done\n");
- }
-#endif
- p->ops->done(p);
- }
- while (!LIST_EMPTY(&p->ins)) {
- i = LIST_FIRST(&p->ins);
- abuf_hup(i);
- }
- while (!LIST_EMPTY(&p->outs)) {
- i = LIST_FIRST(&p->outs);
- abuf_eof(i);
- }
- p->flags |= APROC_ZOMB;
- }
- if (p->refs > 0) {
-#ifdef DEBUG
- if (debug_level >= 3) {
- aproc_dbg(p);
- dbg_puts(": free delayed\n");
- p->ops = &zomb_ops;
- }
-#endif
- return;
- }
-#ifdef DEBUG
- if (debug_level >= 3) {
- aproc_dbg(p);
- dbg_puts(": freed\n");
- }
-#endif
- free(p);
-}
-
-void
-aproc_setin(struct aproc *p, struct abuf *ibuf)
-{
- LIST_INSERT_HEAD(&p->ins, ibuf, ient);
- ibuf->rproc = p;
- if (p->ops->newin)
- p->ops->newin(p, ibuf);
-}
-
-void
-aproc_setout(struct aproc *p, struct abuf *obuf)
-{
- LIST_INSERT_HEAD(&p->outs, obuf, oent);
- obuf->wproc = p;
- if (p->ops->newout)
- p->ops->newout(p, obuf);
-}
-
-void
-aproc_ipos(struct aproc *p, struct abuf *ibuf, int delta)
-{
- struct abuf *obuf;
-
- LIST_FOREACH(obuf, &p->outs, oent) {
- abuf_ipos(obuf, delta);
- }
-}
-
-void
-aproc_opos(struct aproc *p, struct abuf *obuf, int delta)
-{
- struct abuf *ibuf;
-
- LIST_FOREACH(ibuf, &p->ins, ient) {
- abuf_opos(ibuf, delta);
- }
-}
-
-int
-aproc_inuse(struct aproc *p)
-{
- struct abuf *i;
-
- LIST_FOREACH(i, &p->ins, ient) {
- if (i->inuse)
- return 1;
- }
- LIST_FOREACH(i, &p->outs, oent) {
- if (i->inuse)
- return 1;
- }
- return 0;
-}
-
-int
-aproc_depend(struct aproc *p, struct aproc *dep)
-{
- struct abuf *i;
-
- if (p == dep)
- return 1;
- if (p == NULL)
- return 0;
- LIST_FOREACH(i, &p->ins, ient) {
- if (i->wproc && aproc_depend(i->wproc, dep))
- return 1;
- }
- return 0;
-}
-
-int
-rfile_do(struct aproc *p, unsigned int todo, unsigned int *done)
-{
- struct abuf *obuf = LIST_FIRST(&p->outs);
- struct file *f = p->u.io.file;
- unsigned char *data;
- unsigned int n, count, off;
-
- off = p->u.io.partial;
- data = abuf_wgetblk(obuf, &count, 0);
- if (count > todo)
- count = todo;
- n = file_read(f, data + off, count * obuf->bpf - off);
- if (n == 0)
- return 0;
- n += off;
- p->u.io.partial = n % obuf->bpf;
- count = n / obuf->bpf;
- if (count > 0)
- abuf_wcommit(obuf, count);
- if (done)
- *done = count;
- return 1;
-}
-
-int
-rfile_in(struct aproc *p, struct abuf *ibuf_dummy)
-{
- struct abuf *obuf = LIST_FIRST(&p->outs);
- struct file *f = p->u.io.file;
-
- if (!ABUF_WOK(obuf) || !(f->state & FILE_ROK))
- return 0;
- if (!rfile_do(p, obuf->len, NULL))
- return 0;
- if (!abuf_flush(obuf))
- return 0;
- return 1;
-}
-
-int
-rfile_out(struct aproc *p, struct abuf *obuf)
-{
- struct file *f = p->u.io.file;
-
- if (f->state & FILE_RINUSE)
- return 0;
- if (!ABUF_WOK(obuf) || !(f->state & FILE_ROK))
- return 0;
- if (!rfile_do(p, obuf->len, NULL))
- return 0;
- return 1;
-}
-
-void
-rfile_done(struct aproc *p)
-{
- struct file *f = p->u.io.file;
- struct abuf *obuf;
-
- if (f == NULL)
- return;
- /*
- * disconnect from file structure
- */
- f->rproc = NULL;
- p->u.io.file = NULL;
-
- /*
- * all buffers must be detached before deleting f->wproc,
- * because otherwise it could trigger this code again
- */
- obuf = LIST_FIRST(&p->outs);
- if (obuf)
- abuf_eof(obuf);
- if (f->wproc) {
- aproc_del(f->wproc);
- } else
- file_del(f);
-
-#ifdef DEBUG
- if (debug_level >= 2 && p->u.io.partial > 0) {
- aproc_dbg(p);
- dbg_puts(": ");
- dbg_putu(p->u.io.partial);
- dbg_puts(" bytes lost in partial read\n");
- }
-#endif
-}
-
-void
-rfile_eof(struct aproc *p, struct abuf *ibuf_dummy)
-{
- aproc_del(p);
-}
-
-void
-rfile_hup(struct aproc *p, struct abuf *obuf)
-{
- aproc_del(p);
-}
-
-struct aproc_ops rfile_ops = {
- "rfile",
- rfile_in,
- rfile_out,
- rfile_eof,
- rfile_hup,
- NULL, /* newin */
- NULL, /* newout */
- aproc_ipos,
- aproc_opos,
- rfile_done
-};
-
-struct aproc *
-rfile_new(struct file *f)
-{
- struct aproc *p;
-
- p = aproc_new(&rfile_ops, f->name);
- p->u.io.file = f;
- p->u.io.partial = 0;
- f->rproc = p;
- return p;
-}
-
-void
-wfile_done(struct aproc *p)
-{
- struct file *f = p->u.io.file;
- struct abuf *ibuf;
-
- if (f == NULL)
- return;
- /*
- * disconnect from file structure
- */
- f->wproc = NULL;
- p->u.io.file = NULL;
-
- /*
- * all buffers must be detached before deleting f->rproc,
- * because otherwise it could trigger this code again
- */
- ibuf = LIST_FIRST(&p->ins);
- if (ibuf)
- abuf_hup(ibuf);
- if (f->rproc) {
- aproc_del(f->rproc);
- } else
- file_del(f);
-#ifdef DEBUG
- if (debug_level >= 2 && p->u.io.partial > 0) {
- aproc_dbg(p);
- dbg_puts(": ");
- dbg_putu(p->u.io.partial);
- dbg_puts(" bytes lost in partial write\n");
- }
-#endif
-}
-
-int
-wfile_do(struct aproc *p, unsigned int todo, unsigned int *done)
-{
- struct abuf *ibuf = LIST_FIRST(&p->ins);
- struct file *f = p->u.io.file;
- unsigned char *data;
- unsigned int n, count, off;
-
- off = p->u.io.partial;
- data = abuf_rgetblk(ibuf, &count, 0);
- if (count > todo)
- count = todo;
- n = file_write(f, data + off, count * ibuf->bpf - off);
- if (n == 0)
- return 0;
- n += off;
- p->u.io.partial = n % ibuf->bpf;
- count = n / ibuf->bpf;
- if (count > 0)
- abuf_rdiscard(ibuf, count);
- if (done)
- *done = count;
- return 1;
-}
-int
-wfile_in(struct aproc *p, struct abuf *ibuf)
-{
- struct file *f = p->u.io.file;
-
- if (f->state & FILE_WINUSE)
- return 0;
- if (!ABUF_ROK(ibuf) || !(f->state & FILE_WOK))
- return 0;
- if (!wfile_do(p, ibuf->len, NULL))
- return 0;
- return 1;
-}
-
-int
-wfile_out(struct aproc *p, struct abuf *obuf_dummy)
-{
- struct abuf *ibuf = LIST_FIRST(&p->ins);
- struct file *f = p->u.io.file;
-
- if (!abuf_fill(ibuf))
- return 0;
- if (!ABUF_ROK(ibuf) || !(f->state & FILE_WOK))
- return 0;
- if (!wfile_do(p, ibuf->len, NULL))
- return 0;
- return 1;
-}
-
-void
-wfile_eof(struct aproc *p, struct abuf *ibuf)
-{
- aproc_del(p);
-}
-
-void
-wfile_hup(struct aproc *p, struct abuf *obuf_dummy)
-{
- aproc_del(p);
-}
-
-struct aproc_ops wfile_ops = {
- "wfile",
- wfile_in,
- wfile_out,
- wfile_eof,
- wfile_hup,
- NULL, /* newin */
- NULL, /* newout */
- aproc_ipos,
- aproc_opos,
- wfile_done
-};
-
-struct aproc *
-wfile_new(struct file *f)
-{
- struct aproc *p;
-
- p = aproc_new(&wfile_ops, f->name);
- p->u.io.file = f;
- p->u.io.partial = 0;
- f->wproc = p;
- return p;
-}
-
-/*
- * Drop as much as possible samples from the reader end,
- * negative values mean ``insert silence''.
- */
-void
-mix_drop(struct abuf *buf, int extra)
-{
- unsigned int count;
-
- buf->r.mix.drop += extra;
- while (buf->r.mix.drop > 0) {
- count = buf->r.mix.drop;
- if (count > buf->used)
- count = buf->used;
- if (count == 0) {
-#ifdef DEBUG
- if (debug_level >= 4) {
- abuf_dbg(buf);
- dbg_puts(": drop: no data\n");
- }
-#endif
- return;
- }
- abuf_rdiscard(buf, count);
- buf->r.mix.drop -= count;
-#ifdef DEBUG
- if (debug_level >= 4) {
- abuf_dbg(buf);
- dbg_puts(": dropped ");
- dbg_putu(count);
- dbg_puts(", to drop = ");
- dbg_putu(buf->r.mix.drop);
- dbg_puts("\n");
- }
-#endif
- }
-}
-
-/*
- * Append the necessary amount of silence, in a way
- * obuf->w.mix.todo doesn't exceed the given value
- */
-void
-mix_bzero(struct abuf *obuf, unsigned int maxtodo)
-{
- adata_t *odata;
- unsigned int ocount, todo;
-
- if (obuf->w.mix.todo >= maxtodo)
- return;
- todo = maxtodo - obuf->w.mix.todo;
- odata = (adata_t *)abuf_wgetblk(obuf, &ocount, obuf->w.mix.todo);
- if (ocount > todo)
- ocount = todo;
- if (ocount == 0)
- return;
- memset(odata, 0, ocount * obuf->bpf);
- obuf->w.mix.todo += ocount;
-#ifdef DEBUG
- if (debug_level >= 4) {
- abuf_dbg(obuf);
- dbg_puts(": bzero(");
- dbg_putu(obuf->w.mix.todo);
- dbg_puts(")\n");
- }
-#endif
-}
-
-/*
- * Mix an input block over an output block.
- */
-unsigned int
-mix_badd(struct abuf *ibuf, struct abuf *obuf)
-{
- adata_t *idata, *odata;
- unsigned int i, scount, icount, ocount;
- int j, cc, cmin, cmax, istart, inext, onext, ostart, onch;
- int vol, s;
-
-#ifdef DEBUG
- if (debug_level >= 4) {
- abuf_dbg(ibuf);
- dbg_puts(": badd: done = ");
- dbg_putu(ibuf->r.mix.done);
- dbg_puts("/");
- dbg_putu(obuf->w.mix.todo);
- dbg_puts(", drop = ");
- dbg_puti(ibuf->r.mix.drop);
- dbg_puts("\n");
- }
-#endif
- /*
- * Insert silence for xrun correction
- */
- while (ibuf->r.mix.drop < 0) {
- icount = -ibuf->r.mix.drop;
- mix_bzero(obuf, ibuf->r.mix.done + icount);
- ocount = obuf->w.mix.todo - ibuf->r.mix.done;
- if (ocount == 0)
- return 0;
- scount = (icount < ocount) ? icount : ocount;
- ibuf->r.mix.done += scount;
- ibuf->r.mix.drop += scount;
- }
-
- /*
- * Calculate the maximum we can read.
- */
- idata = (adata_t *)abuf_rgetblk(ibuf, &icount, 0);
- if (icount == 0)
- return 0;
-
- /*
- * Calculate the maximum we can write.
- */
- odata = (adata_t *)abuf_wgetblk(obuf, &ocount, ibuf->r.mix.done);
- if (ocount == 0)
- return 0;
-
- scount = (icount < ocount) ? icount : ocount;
- mix_bzero(obuf, scount + ibuf->r.mix.done);
-
- vol = ADATA_MUL(ibuf->r.mix.weight, ibuf->r.mix.vol);
- cmin = obuf->cmin > ibuf->cmin ? obuf->cmin : ibuf->cmin;
- cmax = obuf->cmax < ibuf->cmax ? obuf->cmax : ibuf->cmax;
- onch = obuf->cmax - obuf->cmin + 1;
- ostart = cmin - obuf->cmin;
- if (ostart > onch)
- ostart = onch;
- onext = obuf->cmax - cmax;
- if (onext > onch)
- onext = onch;
- istart = cmin - ibuf->cmin;
- inext = ibuf->cmax - cmax;
- cc = cmax - cmin + 1;
- for (i = scount; i > 0; i--) {
- odata += ostart;
- idata += istart;
- for (j = cc; j > 0; j--) {
- s = *odata + ADATA_MUL(*idata, vol);
- if (s >= ADATA_UNIT)
- s = ADATA_UNIT - 1;
- else if (s < -ADATA_UNIT)
- s = -ADATA_UNIT;
- *odata = s;
- idata++;
- odata++;
- }
- odata += onext;
- idata += inext;
- }
- abuf_rdiscard(ibuf, scount);
- ibuf->r.mix.done += scount;
-
-#ifdef DEBUG
- if (debug_level >= 4) {
- abuf_dbg(ibuf);
- dbg_puts(": badd: done = ");
- dbg_putu(ibuf->r.mix.done);
- dbg_puts("/");
- dbg_putu(obuf->w.mix.todo);
- dbg_puts("\n");
- }
-#endif
- return scount;
-}
-
-/*
- * Handle buffer underrun, return 0 if stream died.
- */
-int
-mix_xrun(struct aproc *p, struct abuf *i)
-{
- struct abuf *obuf = LIST_FIRST(&p->outs);
- unsigned int fdrop, remain;
-
- if (i->r.mix.done > 0)
- return 1;
- if (i->r.mix.xrun == XRUN_ERROR) {
- abuf_hup(i);
- return 0;
- }
- fdrop = obuf->w.mix.todo;
-#ifdef DEBUG
- if (debug_level >= 3) {
- abuf_dbg(i);
- dbg_puts(": underrun, dropping ");
- dbg_putu(fdrop);
- dbg_puts(" + ");
- dbg_putu(i->r.mix.drop);
- dbg_puts("\n");
- }
-#endif
- i->r.mix.done += fdrop;
- if (i->r.mix.xrun == XRUN_SYNC)
- mix_drop(i, fdrop);
- else {
- remain = fdrop % p->u.mix.round;
- if (remain)
- remain = p->u.mix.round - remain;
- mix_drop(i, -(int)remain);
- fdrop += remain;
-#ifdef DEBUG
- if (debug_level >= 3) {
- abuf_dbg(i);
- dbg_puts(": underrun, adding ");
- dbg_putu(remain);
- dbg_puts("\n");
- }
-#endif
- abuf_opos(i, -(int)fdrop);
- if (i->duplex) {
-#ifdef DEBUG
- if (debug_level >= 3) {
- abuf_dbg(i->duplex);
- dbg_puts(": full-duplex resync\n");
- }
-#endif
- sub_silence(i->duplex, -(int)fdrop);
- abuf_ipos(i->duplex, -(int)fdrop);
- }
- }
- return 1;
-}
-
-int
-mix_in(struct aproc *p, struct abuf *ibuf)
-{
- struct abuf *i, *inext, *obuf = LIST_FIRST(&p->outs);
- unsigned int odone;
- unsigned int maxwrite;
- unsigned int scount;
-
-#ifdef DEBUG
- if (debug_level >= 4) {
- aproc_dbg(p);
- dbg_puts(": used = ");
- dbg_putu(ibuf->used);
- dbg_puts("/");
- dbg_putu(ibuf->len);
- dbg_puts(", done = ");
- dbg_putu(ibuf->r.mix.done);
- dbg_puts("/");
- dbg_putu(obuf->w.mix.todo);
- dbg_puts("\n");
- }
-#endif
- if (!MIX_ROK(ibuf))
- return 0;
- scount = 0;
- odone = obuf->len;
- for (i = LIST_FIRST(&p->ins); i != NULL; i = inext) {
- inext = LIST_NEXT(i, ient);
- if (i->r.mix.drop >= 0 && !abuf_fill(i))
- continue; /* eof */
- mix_drop(i, 0);
- scount += mix_badd(i, obuf);
- if (odone > i->r.mix.done)
- odone = i->r.mix.done;
- }
- if (LIST_EMPTY(&p->ins) || scount == 0)
- return 0;
-#ifdef DEBUG
- if (debug_level >= 4) {
- aproc_dbg(p);
- dbg_puts(": maxwrite = ");
- dbg_putu(p->u.mix.maxlat);
- dbg_puts(" - ");
- dbg_putu(p->u.mix.lat);
- dbg_puts(" = ");
- dbg_putu(p->u.mix.maxlat - p->u.mix.lat);
- dbg_puts("\n");
- }
-#endif
- maxwrite = p->u.mix.maxlat - p->u.mix.lat;
- if (maxwrite > 0) {
- if (odone > maxwrite)
- odone = maxwrite;
- p->u.mix.lat += odone;
- LIST_FOREACH(i, &p->ins, ient) {
- i->r.mix.done -= odone;
- }
- abuf_wcommit(obuf, odone);
- obuf->w.mix.todo -= odone;
- if (APROC_OK(p->u.mix.mon))
- mon_snoop(p->u.mix.mon, obuf, obuf->used - odone, odone);
- if (!abuf_flush(obuf))
- return 0; /* hup */
- }
- return 1;
-}
-
-int
-mix_out(struct aproc *p, struct abuf *obuf)
-{
- struct abuf *i, *inext;
- unsigned int odone;
- unsigned int maxwrite;
- unsigned int scount;
-
-#ifdef DEBUG
- if (debug_level >= 4) {
- aproc_dbg(p);
- dbg_puts(": used = ");
- dbg_putu(obuf->used);
- dbg_puts("/");
- dbg_putu(obuf->len);
- dbg_puts(", todo = ");
- dbg_putu(obuf->w.mix.todo);
- dbg_puts("/");
- dbg_putu(obuf->len);
- dbg_puts("\n");
- }
-#endif
- if (!ABUF_WOK(obuf))
- return 0;
-#ifdef DEBUG
- if (debug_level >= 4) {
- aproc_dbg(p);
- dbg_puts(": maxwrite = ");
- dbg_putu(p->u.mix.maxlat);
- dbg_puts(" - ");
- dbg_putu(p->u.mix.lat);
- dbg_puts(" = ");
- dbg_putu(p->u.mix.maxlat - p->u.mix.lat);
- dbg_puts("\n");
- }
-#endif
- maxwrite = p->u.mix.maxlat - p->u.mix.lat;
- if (maxwrite > obuf->w.mix.todo) {
- if ((p->flags & (APROC_QUIT | APROC_DROP)) == APROC_DROP)
- mix_bzero(obuf, maxwrite);
- }
- scount = 0;
- odone = obuf->len;
- for (i = LIST_FIRST(&p->ins); i != NULL; i = inext) {
- inext = LIST_NEXT(i, ient);
- if (i->r.mix.drop >= 0 && !abuf_fill(i))
- continue; /* eof */
- mix_drop(i, 0);
- if (maxwrite > 0 && !MIX_ROK(i)) {
- if (p->flags & APROC_DROP) {
- if (!mix_xrun(p, i))
- continue;
- }
- } else
- scount += mix_badd(i, obuf);
- if (odone > i->r.mix.done)
- odone = i->r.mix.done;
- }
- if (LIST_EMPTY(&p->ins) && obuf->w.mix.todo == 0) {
- if (p->flags & APROC_QUIT) {
- aproc_del(p);
- return 0;
- }
- if (!(p->flags & APROC_DROP))
- return 0;
- }
- if (odone > obuf->w.mix.todo)
- odone = obuf->w.mix.todo;
- if (odone > maxwrite)
- odone = maxwrite;
- if (odone > 0) {
- p->u.mix.lat += odone;
- LIST_FOREACH(i, &p->ins, ient) {
- i->r.mix.done -= odone;
- }
- abuf_wcommit(obuf, odone);
- obuf->w.mix.todo -= odone;
- if (APROC_OK(p->u.mix.mon))
- mon_snoop(p->u.mix.mon, obuf, obuf->used - odone, odone);
- }
- if (LIST_EMPTY(&p->ins))
- p->u.mix.idle += odone;
- if (scount == 0)
- return 0;
- return 1;
-}
-
-void
-mix_eof(struct aproc *p, struct abuf *ibuf)
-{
- struct abuf *i, *obuf = LIST_FIRST(&p->outs);
- unsigned int odone;
-
- mix_setmaster(p);
-
- if (!aproc_inuse(p)) {
-#ifdef DEBUG
- if (debug_level >= 3) {
- aproc_dbg(p);
- dbg_puts(": running other streams\n");
- }
-#endif
- /*
- * Find a blocked input.
- */
- odone = obuf->len;
- LIST_FOREACH(i, &p->ins, ient) {
- /*
- * abuf_fill() may trigger mix_eof(), do the job
- * and possibly reorder the list
- */
- if (!abuf_fill(i))
- return;
- if (MIX_ROK(i) && i->r.mix.done < obuf->w.mix.todo) {
- abuf_run(i);
- return;
- }
- if (odone > i->r.mix.done)
- odone = i->r.mix.done;
- }
- /*
- * No blocked inputs. Check if output is blocked.
- */
- if (LIST_EMPTY(&p->ins) || odone == obuf->w.mix.todo)
- abuf_run(obuf);
- }
-}
-
-void
-mix_hup(struct aproc *p, struct abuf *obuf)
-{
- aproc_del(p);
-}
-
-void
-mix_newin(struct aproc *p, struct abuf *ibuf)
-{
- p->u.mix.idle = 0;
- ibuf->r.mix.done = 0;
- ibuf->r.mix.vol = ADATA_UNIT;
- ibuf->r.mix.weight = ADATA_UNIT;
- ibuf->r.mix.maxweight = ADATA_UNIT;
- ibuf->r.mix.xrun = XRUN_IGNORE;
- ibuf->r.mix.drop = 0;
-}
-
-void
-mix_newout(struct aproc *p, struct abuf *obuf)
-{
-#ifdef DEBUG
- if (debug_level >= 3) {
- aproc_dbg(p);
- dbg_puts(": newout, will use ");
- dbg_putu(obuf->len);
- dbg_puts(" fr\n");
- }
-#endif
- obuf->w.mix.todo = 0;
-}
-
-void
-mix_opos(struct aproc *p, struct abuf *obuf, int delta)
-{
- p->u.mix.lat -= delta;
-#ifdef DEBUG
- if (debug_level >= 4) {
- aproc_dbg(p);
- dbg_puts(": opos: lat = ");
- dbg_puti(p->u.mix.lat);
- dbg_puts("/");
- dbg_puti(p->u.mix.maxlat);
- dbg_puts("\n");
- }
-#endif
- aproc_opos(p, obuf, delta);
- if (APROC_OK(p->u.mix.mon))
- p->u.mix.mon->ops->ipos(p->u.mix.mon, NULL, delta);
-}
-
-struct aproc_ops mix_ops = {
- "mix",
- mix_in,
- mix_out,
- mix_eof,
- mix_hup,
- mix_newin,
- mix_newout,
- aproc_ipos,
- mix_opos,
- NULL
-};
-
-struct aproc *
-mix_new(char *name, int maxlat, unsigned int round,
- unsigned int autovol, unsigned int master)
-{
- struct aproc *p;
-
- p = aproc_new(&mix_ops, name);
- p->u.mix.idle = 0;
- p->u.mix.lat = 0;
- p->u.mix.round = round;
- p->u.mix.maxlat = maxlat;
- p->u.mix.mon = NULL;
- p->u.mix.autovol = autovol;
- p->u.mix.master = master;
- return p;
-}
-
-/*
- * Normalize input levels.
- */
-void
-mix_setmaster(struct aproc *p)
-{
- unsigned int n;
- struct abuf *i, *j;
- int weight;
-
- LIST_FOREACH(i, &p->ins, ient) {
- weight = ADATA_UNIT;
- if (p->u.mix.autovol) {
- /*
- * count the number of inputs that have
- * overlapping channel sets
- */
- n = 0;
- LIST_FOREACH(j, &p->ins, ient) {
- if (i->cmin <= j->cmax && i->cmax >= j->cmin)
- n++;
- }
- weight /= n;
- }
- if (weight > i->r.mix.maxweight)
- weight = i->r.mix.maxweight;
- i->r.mix.weight = ADATA_MUL(weight, p->u.mix.master);
-#ifdef DEBUG
- if (debug_level >= 3) {
- abuf_dbg(i);
- dbg_puts(": setmaster: ");
- dbg_puti(i->r.mix.weight);
- dbg_puts("/");
- dbg_puti(i->r.mix.maxweight);
- dbg_puts("\n");
- }
-#endif
- }
-}
-
-void
-mix_clear(struct aproc *p)
-{
- struct abuf *obuf = LIST_FIRST(&p->outs);
-
- p->u.mix.lat = 0;
- obuf->w.mix.todo = 0;
-}
-
-/*
- * Gracefully terminate the mixer: raise the APROC_QUIT flag
- * and let the rest of the code do the job. If there are neither
- * inputs nor uncommited data, then terminate right away
- */
-void
-mix_quit(struct aproc *p)
-{
- struct abuf *obuf = LIST_FIRST(&p->outs);
-
- p->flags |= APROC_QUIT;
-
- /*
- * eof the last input will trigger aproc_del()
- */
- if (!LIST_EMPTY(&p->ins) || obuf->w.mix.todo > 0)
- return;
- aproc_del(p);
-}
-
-/*
- * Append as much as possible silence on the writer end
- */
-void
-sub_silence(struct abuf *buf, int extra)
-{
- unsigned char *data;
- unsigned int count;
-
- buf->w.sub.silence += extra;
- if (buf->w.sub.silence > 0) {
- data = abuf_wgetblk(buf, &count, 0);
- if (count >= buf->w.sub.silence)
- count = buf->w.sub.silence;
- if (count == 0) {
-#ifdef DEBUG
- if (debug_level >= 4) {
- abuf_dbg(buf);
- dbg_puts(": no space for silence\n");
- }
-#endif
- return;
- }
- memset(data, 0, count * buf->bpf);
- abuf_wcommit(buf, count);
- buf->w.sub.silence -= count;
-#ifdef DEBUG
- if (debug_level >= 4) {
- abuf_dbg(buf);
- dbg_puts(": appended ");
- dbg_putu(count);
- dbg_puts(", remaining silence = ");
- dbg_putu(buf->w.sub.silence);
- dbg_puts("\n");
- }
-#endif
- }
-}
-
-/*
- * Copy data from ibuf to obuf.
- */
-void
-sub_bcopy(struct abuf *ibuf, struct abuf *obuf)
-{
- adata_t *idata, *odata;
- unsigned int i, icount, ocount, scount;
- int j, cc, cmin, cmax, istart, inext, onext, ostart, onch;
-
- /*
- * Drop samples for xrun correction
- */
- if (obuf->w.sub.silence < 0) {
- scount = -obuf->w.sub.silence;
- if (scount > ibuf->used)
- scount = ibuf->used;
- obuf->w.sub.done += scount;
- obuf->w.sub.silence += scount;
- }
-
- idata = (adata_t *)abuf_rgetblk(ibuf, &icount, obuf->w.sub.done);
- if (icount == 0)
- return;
- odata = (adata_t *)abuf_wgetblk(obuf, &ocount, 0);
- if (ocount == 0)
- return;
- cmin = obuf->cmin > ibuf->cmin ? obuf->cmin : ibuf->cmin;
- cmax = obuf->cmax < ibuf->cmax ? obuf->cmax : ibuf->cmax;
- onch = obuf->cmax - obuf->cmin + 1;
- ostart = cmin - obuf->cmin;
- if (ostart > onch)
- ostart = onch;
- onext = obuf->cmax - cmax;
- if (onext > onch)
- onext = onch;
- istart = cmin - ibuf->cmin;
- inext = ibuf->cmax - cmax;
- cc = cmax - cmin + 1;
- scount = (icount < ocount) ? icount : ocount;
- for (i = scount; i > 0; i--) {
- idata += istart;
- for (j = ostart; j > 0; j--)
- *odata++ = 0x1111;
- for (j = cc; j > 0; j--) {
- *odata = *idata;
- odata++;
- idata++;
- }
- for (j = onext; j > 0; j--)
- *odata++ = 0x2222;
- idata += inext;
- }
- abuf_wcommit(obuf, scount);
- obuf->w.sub.done += scount;
-#ifdef DEBUG
- if (debug_level >= 4) {
- abuf_dbg(obuf);
- dbg_puts(": bcopy ");
- dbg_putu(scount);
- dbg_puts("\n");
- }
-#endif
-}
-
-/*
- * Handle buffer overruns. Return 0 if the stream died.
- */
-int
-sub_xrun(struct aproc *p, struct abuf *i)
-{
- struct abuf *ibuf = LIST_FIRST(&p->ins);
- unsigned int fdrop, remain;
-
- if (i->w.sub.done > 0)
- return 1;
- if (i->w.sub.xrun == XRUN_ERROR) {
- abuf_eof(i);
- return 0;
- }
- fdrop = ibuf->used;
-#ifdef DEBUG
- if (debug_level >= 3) {
- abuf_dbg(i);
- dbg_puts(": overrun, silence ");
- dbg_putu(fdrop);
- dbg_puts(" + ");
- dbg_putu(i->w.sub.silence);
- dbg_puts("\n");
- }
-#endif
- i->w.sub.done += fdrop;
- if (i->w.sub.xrun == XRUN_SYNC)
- sub_silence(i, fdrop);
- else {
- remain = fdrop % p->u.sub.round;
- if (remain)
- remain = p->u.sub.round - remain;
- sub_silence(i, -(int)remain);
- fdrop += remain;
-#ifdef DEBUG
- if (debug_level >= 3) {
- abuf_dbg(i);
- dbg_puts(": overrun, adding ");
- dbg_putu(remain);
- dbg_puts("\n");
- }
-#endif
-
- abuf_ipos(i, -(int)fdrop);
- if (i->duplex) {
-#ifdef DEBUG
- if (debug_level >= 3) {
- abuf_dbg(i->duplex);
- dbg_puts(": full-duplex resync\n");
- }
-#endif
- mix_drop(i->duplex, -(int)fdrop);
- abuf_opos(i->duplex, -(int)fdrop);
- }
- }
- return 1;
-}
-
-int
-sub_in(struct aproc *p, struct abuf *ibuf)
-{
- struct abuf *i, *inext;
- unsigned int idone;
-
- if (!ABUF_ROK(ibuf))
- return 0;
- idone = ibuf->len;
- for (i = LIST_FIRST(&p->outs); i != NULL; i = inext) {
- inext = LIST_NEXT(i, oent);
- sub_silence(i, 0);
- if (!SUB_WOK(i)) {
- if (p->flags & APROC_DROP) {
- if (!sub_xrun(p, i))
- continue;
- }
- } else
- sub_bcopy(ibuf, i);
- if (idone > i->w.sub.done)
- idone = i->w.sub.done;
- if (!abuf_flush(i))
- continue;
- }
- if (LIST_EMPTY(&p->outs)) {
- if (p->flags & APROC_QUIT) {
- aproc_del(p);
- return 0;
- }
- if (!(p->flags & APROC_DROP))
- return 0;
- idone = ibuf->used;
- p->u.sub.idle += idone;
- }
- if (idone == 0)
- return 0;
- LIST_FOREACH(i, &p->outs, oent) {
- i->w.sub.done -= idone;
- }
- abuf_rdiscard(ibuf, idone);
- abuf_opos(ibuf, idone);
- p->u.sub.lat -= idone;
- return 1;
-}
-
-int
-sub_out(struct aproc *p, struct abuf *obuf)
-{
- struct abuf *ibuf = LIST_FIRST(&p->ins);
- struct abuf *i, *inext;
- unsigned int idone;
-
- if (!SUB_WOK(obuf))
- return 0;
- if (!abuf_fill(ibuf))
- return 0; /* eof */
- idone = ibuf->len;
- for (i = LIST_FIRST(&p->outs); i != NULL; i = inext) {
- inext = LIST_NEXT(i, oent);
- sub_silence(i, 0);
- sub_bcopy(ibuf, i);
- if (idone > i->w.sub.done)
- idone = i->w.sub.done;
- if (!abuf_flush(i))
- continue;
- }
- if (LIST_EMPTY(&p->outs) || idone == 0)
- return 0;
- LIST_FOREACH(i, &p->outs, oent) {
- i->w.sub.done -= idone;
- }
- abuf_rdiscard(ibuf, idone);
- abuf_opos(ibuf, idone);
- p->u.sub.lat -= idone;
- return 1;
-}
-
-void
-sub_eof(struct aproc *p, struct abuf *ibuf)
-{
- aproc_del(p);
-}
-
-void
-sub_hup(struct aproc *p, struct abuf *obuf)
-{
- struct abuf *i, *ibuf = LIST_FIRST(&p->ins);
- unsigned int idone;
-
- if (!aproc_inuse(p)) {
-#ifdef DEBUG
- if (debug_level >= 3) {
- aproc_dbg(p);
- dbg_puts(": running other streams\n");
- }
-#endif
- /*
- * Find a blocked output.
- */
- idone = ibuf->len;
- LIST_FOREACH(i, &p->outs, oent) {
- /*
- * abuf_flush() may trigger sub_hup(), do the job
- * and possibly reorder the list
- */
- if (!abuf_flush(i))
- return;
- if (SUB_WOK(i) && i->w.sub.done < ibuf->used) {
- abuf_run(i);
- return;
- }
- if (idone > i->w.sub.done)
- idone = i->w.sub.done;
- }
- /*
- * No blocked outputs. Check if input is blocked.
- */
- if (LIST_EMPTY(&p->outs) || idone == ibuf->used)
- abuf_run(ibuf);
- }
-}
-
-void
-sub_newout(struct aproc *p, struct abuf *obuf)
-{
- p->u.sub.idle = 0;
- obuf->w.sub.done = 0;
- obuf->w.sub.xrun = XRUN_IGNORE;
- obuf->w.sub.silence = 0;
-}
-
-void
-sub_ipos(struct aproc *p, struct abuf *ibuf, int delta)
-{
- p->u.sub.lat += delta;
-#ifdef DEBUG
- if (debug_level >= 4) {
- aproc_dbg(p);
- dbg_puts(": ipos: lat = ");
- dbg_puti(p->u.sub.lat);
- dbg_puts("/");
- dbg_puti(p->u.sub.maxlat);
- dbg_puts("\n");
- }
-#endif
- aproc_ipos(p, ibuf, delta);
-}
-
-struct aproc_ops sub_ops = {
- "sub",
- sub_in,
- sub_out,
- sub_eof,
- sub_hup,
- NULL,
- sub_newout,
- sub_ipos,
- aproc_opos,
- NULL
-};
-
-struct aproc *
-sub_new(char *name, int maxlat, unsigned int round)
-{
- struct aproc *p;
-
- p = aproc_new(&sub_ops, name);
- p->u.sub.idle = 0;
- p->u.sub.lat = 0;
- p->u.sub.round = round;
- p->u.sub.maxlat = maxlat;
- return p;
-}
-
-void
-sub_clear(struct aproc *p)
-{
- p->u.sub.lat = 0;
-}
-
-/*
- * Convert one block.
- */
-void
-resamp_bcopy(struct aproc *p, struct abuf *ibuf, struct abuf *obuf)
-{
- unsigned int inch;
- adata_t *idata;
- unsigned int oblksz;
- unsigned int ifr;
- unsigned int onch;
- int s, ds, diff;
- adata_t *odata;
- unsigned int iblksz;
- unsigned int ofr;
- unsigned int c;
- adata_t *ctxbuf, *ctx;
- unsigned int ctx_start;
- unsigned int icount, ocount;
-
- /*
- * Calculate max frames readable at once from the input buffer.
- */
- idata = (adata_t *)abuf_rgetblk(ibuf, &icount, 0);
- ifr = icount;
-
- odata = (adata_t *)abuf_wgetblk(obuf, &ocount, 0);
- ofr = ocount;
-
- /*
- * Partially copy structures into local variables, to avoid
- * unnecessary indirections; this also allows the compiler to
- * order local variables more "cache-friendly".
- */
- diff = p->u.resamp.diff;
- inch = ibuf->cmax - ibuf->cmin + 1;
- iblksz = p->u.resamp.iblksz;
- onch = obuf->cmax - obuf->cmin + 1;
- oblksz = p->u.resamp.oblksz;
- ctxbuf = p->u.resamp.ctx;
- ctx_start = p->u.resamp.ctx_start;
-
- /*
- * Start conversion.
- */
-#ifdef DEBUG
- if (debug_level >= 4) {
- aproc_dbg(p);
- dbg_puts(": starting diff = ");
- dbg_puti(diff);
- dbg_puts(", ifr = ");
- dbg_putu(ifr);
- dbg_puts(", ofr = ");
- dbg_putu(ofr);
- dbg_puts(" fr\n");
- }
-#endif
- for (;;) {
- if (diff < 0) {
- if (ifr == 0)
- break;
- ctx_start ^= 1;
- ctx = ctxbuf + ctx_start;
- for (c = inch; c > 0; c--) {
- *ctx = *idata++;
- ctx += RESAMP_NCTX;
- }
- diff += oblksz;
- ifr--;
- } else if (diff > 0) {
- if (ofr == 0)
- break;
- ctx = ctxbuf;
- for (c = onch; c > 0; c--) {
- s = ctx[ctx_start];
- ds = ctx[ctx_start ^ 1] - s;
- ctx += RESAMP_NCTX;
- *odata++ = s + ADATA_MULDIV(ds, diff, oblksz);
- }
- diff -= iblksz;
- ofr--;
- } else {
- if (ifr == 0 || ofr == 0)
- break;
- ctx = ctxbuf + ctx_start;
- for (c = onch; c > 0; c--) {
- *odata++ = *ctx;
- ctx += RESAMP_NCTX;
- }
- ctx_start ^= 1;
- ctx = ctxbuf + ctx_start;
- for (c = inch; c > 0; c--) {
- *ctx = *idata++;
- ctx += RESAMP_NCTX;
- }
- diff -= iblksz;
- diff += oblksz;
- ifr--;
- ofr--;
- }
- }
- p->u.resamp.diff = diff;
- p->u.resamp.ctx_start = ctx_start;
-#ifdef DEBUG
- if (debug_level >= 4) {
- aproc_dbg(p);
- dbg_puts(": done delta = ");
- dbg_puti(diff);
- dbg_puts(", ifr = ");
- dbg_putu(ifr);
- dbg_puts(", ofr = ");
- dbg_putu(ofr);
- dbg_puts(" fr\n");
- }
-#endif
- /*
- * Update FIFO pointers.
- */
- icount -= ifr;
- ocount -= ofr;
- abuf_rdiscard(ibuf, icount);
- abuf_wcommit(obuf, ocount);
-}
-
-int
-resamp_in(struct aproc *p, struct abuf *ibuf)
-{
- struct abuf *obuf = LIST_FIRST(&p->outs);
-
- if (!ABUF_WOK(obuf) || !ABUF_ROK(ibuf))
- return 0;
- resamp_bcopy(p, ibuf, obuf);
- if (!abuf_flush(obuf))
- return 0;
- return 1;
-}
-
-int
-resamp_out(struct aproc *p, struct abuf *obuf)
-{
- struct abuf *ibuf = LIST_FIRST(&p->ins);
-
- if (!abuf_fill(ibuf))
- return 0;
- if (!ABUF_WOK(obuf) || !ABUF_ROK(ibuf))
- return 0;
- resamp_bcopy(p, ibuf, obuf);
- return 1;
-}
-
-void
-resamp_eof(struct aproc *p, struct abuf *ibuf)
-{
- aproc_del(p);
-}
-
-void
-resamp_hup(struct aproc *p, struct abuf *obuf)
-{
- aproc_del(p);
-}
-
-void
-resamp_ipos(struct aproc *p, struct abuf *ibuf, int delta)
-{
- struct abuf *obuf = LIST_FIRST(&p->outs);
- long long ipos;
-
- ipos = (long long)delta * p->u.resamp.oblksz + p->u.resamp.idelta;
- p->u.resamp.idelta = ipos % p->u.resamp.iblksz;
- abuf_ipos(obuf, ipos / (int)p->u.resamp.iblksz);
-}
-
-void
-resamp_opos(struct aproc *p, struct abuf *obuf, int delta)
-{
- struct abuf *ibuf = LIST_FIRST(&p->ins);
- long long opos;
-
- opos = (long long)delta * p->u.resamp.iblksz + p->u.resamp.odelta;
- p->u.resamp.odelta = opos % p->u.resamp.oblksz;
- abuf_opos(ibuf, opos / p->u.resamp.oblksz);
-}
-
-struct aproc_ops resamp_ops = {
- "resamp",
- resamp_in,
- resamp_out,
- resamp_eof,
- resamp_hup,
- NULL,
- NULL,
- resamp_ipos,
- resamp_opos,
- NULL
-};
-
-struct aproc *
-resamp_new(char *name, unsigned int iblksz, unsigned int oblksz)
-{
- struct aproc *p;
- unsigned int i;
-
- p = aproc_new(&resamp_ops, name);
- p->u.resamp.iblksz = iblksz;
- p->u.resamp.oblksz = oblksz;
- p->u.resamp.diff = 0;
- p->u.resamp.idelta = 0;
- p->u.resamp.odelta = 0;
- p->u.resamp.ctx_start = 0;
- for (i = 0; i < NCHAN_MAX * RESAMP_NCTX; i++)
- p->u.resamp.ctx[i] = 0;
-#ifdef DEBUG
- if (debug_level >= 3) {
- aproc_dbg(p);
- dbg_puts(": new ");
- dbg_putu(iblksz);
- dbg_puts("/");
- dbg_putu(oblksz);
- dbg_puts("\n");
- }
-#endif
- return p;
-}
-
-/*
- * Convert one block.
- */
-void
-enc_bcopy(struct aproc *p, struct abuf *ibuf, struct abuf *obuf)
-{
- unsigned int nch, scount, icount, ocount;
- unsigned int f;
- adata_t *idata;
- int s;
- unsigned int oshift;
- int osigbit;
- unsigned int obps;
- unsigned int i;
- unsigned char *odata;
- int obnext;
- int osnext;
-
- /*
- * Calculate max frames readable at once from the input buffer.
- */
- idata = (adata_t *)abuf_rgetblk(ibuf, &icount, 0);
- if (icount == 0)
- return;
- odata = abuf_wgetblk(obuf, &ocount, 0);
- if (ocount == 0)
- return;
- scount = (icount < ocount) ? icount : ocount;
- nch = ibuf->cmax - ibuf->cmin + 1;
-#ifdef DEBUG
- if (debug_level >= 4) {
- aproc_dbg(p);
- dbg_puts(": bcopy ");
- dbg_putu(scount);
- dbg_puts(" fr / ");
- dbg_putu(nch);
- dbg_puts(" ch\n");
- }
-#endif
- /*
- * Partially copy structures into local variables, to avoid
- * unnecessary indirections; this also allows the compiler to
- * order local variables more "cache-friendly".
- */
- oshift = p->u.conv.shift;
- osigbit = p->u.conv.sigbit;
- obps = p->u.conv.bps;
- obnext = p->u.conv.bnext;
- osnext = p->u.conv.snext;
-
- /*
- * Start conversion.
- */
- odata += p->u.conv.bfirst;
- for (f = scount * nch; f > 0; f--) {
- s = *idata++;
- s <<= 32 - ADATA_BITS;
- s >>= oshift;
- s ^= osigbit;
- for (i = obps; i > 0; i--) {
- *odata = (unsigned char)s;
- s >>= 8;
- odata += obnext;
- }
- odata += osnext;
- }
-
- /*
- * Update FIFO pointers.
- */
- abuf_rdiscard(ibuf, scount);
- abuf_wcommit(obuf, scount);
-}
-
-int
-enc_in(struct aproc *p, struct abuf *ibuf)
-{
- struct abuf *obuf = LIST_FIRST(&p->outs);
-
- if (!ABUF_WOK(obuf) || !ABUF_ROK(ibuf))
- return 0;
- enc_bcopy(p, ibuf, obuf);
- if (!abuf_flush(obuf))
- return 0;
- return 1;
-}
-
-int
-enc_out(struct aproc *p, struct abuf *obuf)
-{
- struct abuf *ibuf = LIST_FIRST(&p->ins);
-
- if (!abuf_fill(ibuf))
- return 0;
- if (!ABUF_WOK(obuf) || !ABUF_ROK(ibuf))
- return 0;
- enc_bcopy(p, ibuf, obuf);
- return 1;
-}
-
-void
-enc_eof(struct aproc *p, struct abuf *ibuf)
-{
- aproc_del(p);
-}
-
-void
-enc_hup(struct aproc *p, struct abuf *obuf)
-{
- aproc_del(p);
-}
-
-struct aproc_ops enc_ops = {
- "enc",
- enc_in,
- enc_out,
- enc_eof,
- enc_hup,
- NULL,
- NULL,
- aproc_ipos,
- aproc_opos,
- NULL
-};
-
-struct aproc *
-enc_new(char *name, struct aparams *par)
-{
- struct aproc *p;
-
- p = aproc_new(&enc_ops, name);
- p->u.conv.bps = par->bps;
- p->u.conv.sigbit = par->sig ? 0 : 1 << (par->bits - 1);
- if (par->msb) {
- p->u.conv.shift = 32 - par->bps * 8;
- } else {
- p->u.conv.shift = 32 - par->bits;
- }
- if (!par->le) {
- p->u.conv.bfirst = par->bps - 1;
- p->u.conv.bnext = -1;
- p->u.conv.snext = 2 * par->bps;
- } else {
- p->u.conv.bfirst = 0;
- p->u.conv.bnext = 1;
- p->u.conv.snext = 0;
- }
-#ifdef DEBUG
- if (debug_level >= 3) {
- aproc_dbg(p);
- dbg_puts(": new ");
- aparams_dbg(par);
- dbg_puts("\n");
- }
-#endif
- return p;
-}
-
-/*
- * Convert one block.
- */
-void
-dec_bcopy(struct aproc *p, struct abuf *ibuf, struct abuf *obuf)
-{
- unsigned int nch, scount, icount, ocount;
- unsigned int f;
- unsigned int ibps;
- unsigned int i;
- int s = 0xdeadbeef;
- unsigned char *idata;
- int ibnext;
- int isnext;
- int isigbit;
- unsigned int ishift;
- adata_t *odata;
-
- /*
- * Calculate max frames readable at once from the input buffer.
- */
- idata = abuf_rgetblk(ibuf, &icount, 0);
- if (icount == 0)
- return;
- odata = (adata_t *)abuf_wgetblk(obuf, &ocount, 0);
- if (ocount == 0)
- return;
- scount = (icount < ocount) ? icount : ocount;
- nch = obuf->cmax - obuf->cmin + 1;
-#ifdef DEBUG
- if (debug_level >= 4) {
- aproc_dbg(p);
- dbg_puts(": bcopy ");
- dbg_putu(scount);
- dbg_puts(" fr / ");
- dbg_putu(nch);
- dbg_puts(" ch\n");
- }
-#endif
- /*
- * Partially copy structures into local variables, to avoid
- * unnecessary indirections; this also allows the compiler to
- * order local variables more "cache-friendly".
- */
- ibps = p->u.conv.bps;
- ibnext = p->u.conv.bnext;
- isigbit = p->u.conv.sigbit;
- ishift = p->u.conv.shift;
- isnext = p->u.conv.snext;
-
- /*
- * Start conversion.
- */
- idata += p->u.conv.bfirst;
- for (f = scount * nch; f > 0; f--) {
- for (i = ibps; i > 0; i--) {
- s <<= 8;
- s |= *idata;
- idata += ibnext;
- }
- idata += isnext;
- s ^= isigbit;
- s <<= ishift;
- s >>= 32 - ADATA_BITS;
- *odata++ = s;
- }
-
- /*
- * Update FIFO pointers.
- */
- abuf_rdiscard(ibuf, scount);
- abuf_wcommit(obuf, scount);
-}
-
-int
-dec_in(struct aproc *p, struct abuf *ibuf)
-{
- struct abuf *obuf = LIST_FIRST(&p->outs);
-
- if (!ABUF_WOK(obuf) || !ABUF_ROK(ibuf))
- return 0;
- dec_bcopy(p, ibuf, obuf);
- if (!abuf_flush(obuf))
- return 0;
- return 1;
-}
-
-int
-dec_out(struct aproc *p, struct abuf *obuf)
-{
- struct abuf *ibuf = LIST_FIRST(&p->ins);
-
- if (!abuf_fill(ibuf))
- return 0;
- if (!ABUF_WOK(obuf) || !ABUF_ROK(ibuf))
- return 0;
- dec_bcopy(p, ibuf, obuf);
- return 1;
-}
-
-void
-dec_eof(struct aproc *p, struct abuf *ibuf)
-{
- aproc_del(p);
-}
-
-void
-dec_hup(struct aproc *p, struct abuf *obuf)
-{
- aproc_del(p);
-}
-
-struct aproc_ops dec_ops = {
- "dec",
- dec_in,
- dec_out,
- dec_eof,
- dec_hup,
- NULL,
- NULL,
- aproc_ipos,
- aproc_opos,
- NULL
-};
-
-struct aproc *
-dec_new(char *name, struct aparams *par)
-{
- struct aproc *p;
-
- p = aproc_new(&dec_ops, name);
- p->u.conv.bps = par->bps;
- p->u.conv.sigbit = par->sig ? 0 : 1 << (par->bits - 1);
- if (par->msb) {
- p->u.conv.shift = 32 - par->bps * 8;
- } else {
- p->u.conv.shift = 32 - par->bits;
- }
- if (par->le) {
- p->u.conv.bfirst = par->bps - 1;
- p->u.conv.bnext = -1;
- p->u.conv.snext = 2 * par->bps;
- } else {
- p->u.conv.bfirst = 0;
- p->u.conv.bnext = 1;
- p->u.conv.snext = 0;
- }
-#ifdef DEBUG
- if (debug_level >= 3) {
- aproc_dbg(p);
- dbg_puts(": new ");
- aparams_dbg(par);
- dbg_puts("\n");
- }
-#endif
- return p;
-}
-
-/*
- * Convert one block.
- */
-void
-join_bcopy(struct aproc *p, struct abuf *ibuf, struct abuf *obuf)
-{
- unsigned int h, hops;
- unsigned int inch, inext;
- adata_t *idata;
- unsigned int onch, onext;
- adata_t *odata;
- int scale;
- unsigned int c, f, scount, icount, ocount;
-
- /*
- * Calculate max frames readable at once from the input buffer.
- */
- idata = (adata_t *)abuf_rgetblk(ibuf, &icount, 0);
- if (icount == 0)
- return;
- odata = (adata_t *)abuf_wgetblk(obuf, &ocount, 0);
- if (ocount == 0)
- return;
- scount = icount < ocount ? icount : ocount;
- inch = ibuf->cmax - ibuf->cmin + 1;
- onch = obuf->cmax - obuf->cmin + 1;
- if (2 * inch <= onch) {
- hops = onch / inch;
- inext = inch * hops;
- onext = onch - inext;
- for (f = scount; f > 0; f--) {
- h = hops;
- for (;;) {
- for (c = inch; c > 0; c--)
- *odata++ = *idata++;
- if (--h == 0)
- break;
- idata -= inch;
- }
- for (c = onext; c > 0; c--)
- *odata++ = 0;
- }
- } else if (inch >= 2 * onch) {
- hops = inch / onch;
- inext = inch - onch * hops;
- scale = ADATA_UNIT / hops;
- inch -= onch + inext;
- hops--;
- for (f = scount; f > 0; f--) {
- for (c = onch; c > 0; c--)
- *odata++ = ADATA_MUL(*idata++, scale);
- for (h = hops; h > 0; h--) {
- odata -= onch;
- for (c = onch; c > 0; c--)
- *odata++ += ADATA_MUL(*idata++, scale);
- }
- idata += inext;
- }
- } else {
-#ifdef DEBUG
- aproc_dbg(p);
- dbg_puts(": nothing to do\n");
- dbg_panic();
-#endif
- }
-#ifdef DEBUG
- if (debug_level >= 4) {
- aproc_dbg(p);
- dbg_puts(": bcopy ");
- dbg_putu(scount);
- dbg_puts(" fr\n");
- }
-#endif
- abuf_rdiscard(ibuf, scount);
- abuf_wcommit(obuf, scount);
-}
-
-int
-join_in(struct aproc *p, struct abuf *ibuf)
-{
- struct abuf *obuf = LIST_FIRST(&p->outs);
-
- if (!ABUF_WOK(obuf) || !ABUF_ROK(ibuf))
- return 0;
- join_bcopy(p, ibuf, obuf);
- if (!abuf_flush(obuf))
- return 0;
- return 1;
-}
-
-int
-join_out(struct aproc *p, struct abuf *obuf)
-{
- struct abuf *ibuf = LIST_FIRST(&p->ins);
-
- if (!abuf_fill(ibuf))
- return 0;
- if (!ABUF_WOK(obuf) || !ABUF_ROK(ibuf))
- return 0;
- join_bcopy(p, ibuf, obuf);
- return 1;
-}
-
-void
-join_eof(struct aproc *p, struct abuf *ibuf)
-{
- aproc_del(p);
-}
-
-void
-join_hup(struct aproc *p, struct abuf *obuf)
-{
- aproc_del(p);
-}
-
-struct aproc_ops join_ops = {
- "join",
- join_in,
- join_out,
- join_eof,
- join_hup,
- NULL,
- NULL,
- aproc_ipos,
- aproc_opos,
- NULL
-};
-
-struct aproc *
-join_new(char *name)
-{
- struct aproc *p;
-
- p = aproc_new(&join_ops, name);
-#ifdef DEBUG
- if (debug_level >= 3) {
- aproc_dbg(p);
- dbg_puts(": new\n");
- }
-#endif
- return p;
-}
-
-/*
- * Commit and flush part of the output buffer
- */
-void
-mon_flush(struct aproc *p)
-{
- struct abuf *obuf = LIST_FIRST(&p->outs);
- unsigned int count;
-
-#ifdef DEBUG
- if (debug_level >= 4) {
- aproc_dbg(p);
- dbg_puts(": delta = ");
- dbg_puti(p->u.mon.delta);
- dbg_puts("/");
- dbg_putu(p->u.mon.bufsz);
- dbg_puts(" pending = ");
- dbg_puti(p->u.mon.pending);
- dbg_puts("\n");
- }
-#endif
- if (p->u.mon.delta <= 0 || p->u.mon.pending == 0)
- return;
- count = p->u.mon.delta;
- if (count > p->u.mon.pending)
- count = p->u.mon.pending;
- abuf_wcommit(obuf, count);
- p->u.mon.pending -= count;
- p->u.mon.delta -= count;
- abuf_flush(obuf);
-}
-
-/*
- * Copy one block.
- */
-void
-mon_snoop(struct aproc *p, struct abuf *ibuf,
- unsigned int pos, unsigned int todo)
-{
- struct abuf *obuf = LIST_FIRST(&p->outs);
- unsigned int scount, icount, ocount;
- adata_t *idata, *odata;
-
-#ifdef DEBUG
- if (debug_level >= 4) {
- aproc_dbg(p);
- dbg_puts(": snooping ");
- dbg_putu(pos);
- dbg_puts("..");
- dbg_putu(todo);
- dbg_puts("\n");
- }
-#endif
- if (!abuf_flush(obuf))
- return;
-
- while (todo > 0) {
- /*
- * Calculate max frames readable at once from the input buffer.
- */
- idata = (adata_t *)abuf_rgetblk(ibuf, &icount, pos);
- odata = (adata_t *)abuf_wgetblk(obuf, &ocount, p->u.mon.pending);
- scount = (icount < ocount) ? icount : ocount;
-#ifdef DEBUG
- if (debug_level >= 4) {
- aproc_dbg(p);
- dbg_puts(": snooping ");
- dbg_putu(scount);
- dbg_puts(" fr\n");
- }
- if (scount == 0) {
- dbg_puts("monitor xrun, not allowed\n");
- dbg_panic();
- }
-#endif
- memcpy(odata, idata, scount * obuf->bpf);
- p->u.mon.pending += scount;
- todo -= scount;
- pos += scount;
- }
- mon_flush(p);
-}
-
-int
-mon_in(struct aproc *p, struct abuf *ibuf)
-{
-#ifdef DEBUG
- dbg_puts("monitor can't have inputs to read\n");
- dbg_panic();
-#endif
- return 0;
-}
-
-/*
- * put the monitor into ``empty'' state
- */
-void
-mon_clear(struct aproc *p)
-{
- p->u.mon.pending = 0;
- p->u.mon.delta = 0;
-}
-
-int
-mon_out(struct aproc *p, struct abuf *obuf)
-{
- /*
- * can't trigger monitored stream to produce data
- */
- return 0;
-}
-
-void
-mon_eof(struct aproc *p, struct abuf *ibuf)
-{
-#ifdef DEBUG
- dbg_puts("monitor can't have inputs to eof\n");
- dbg_panic();
-#endif
-}
-
-void
-mon_hup(struct aproc *p, struct abuf *obuf)
-{
- aproc_del(p);
-}
-
-void
-mon_ipos(struct aproc *p, struct abuf *ibuf, int delta)
-{
- aproc_ipos(p, ibuf, delta);
- p->u.mon.delta += delta;
- mon_flush(p);
-}
-
-struct aproc_ops mon_ops = {
- "mon",
- mon_in,
- mon_out,
- mon_eof,
- mon_hup,
- NULL,
- NULL,
- mon_ipos,
- aproc_opos,
- NULL
-};
-
-struct aproc *
-mon_new(char *name, unsigned int bufsz)
-{
- struct aproc *p;
-
- p = aproc_new(&mon_ops, name);
- p->u.mon.pending = 0;
- p->u.mon.delta = 0;
- p->u.mon.bufsz = bufsz;
-#ifdef DEBUG
- if (debug_level >= 3) {
- aproc_dbg(p);
- dbg_puts(": new\n");
- }
-#endif
- return p;
-}
+++ /dev/null
-/* $OpenBSD: aproc.h,v 1.45 2013/11/18 17:37:45 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 APROC_H
-#define APROC_H
-
-#include <sys/queue.h>
-
-#include "aparams.h"
-#include "file.h"
-
-struct abuf;
-struct aproc;
-struct file;
-
-struct aproc_ops {
- /*
- * Name of the ops structure, ie type of the unit.
- */
- char *name;
-
- /*
- * The state of the given input abuf changed (eg. an input block
- * is ready for processing). This function must get the block
- * from the input, process it and remove it from the buffer.
- *
- * Processing the block will result in a change of the state of
- * OTHER buffers that are attached to the aproc (eg. the output
- * buffer was filled), thus this routine MUST notify ALL aproc
- * structures that are waiting on it; most of the time this
- * means just calling abuf_flush() on the output buffer.
- */
- int (*in)(struct aproc *, struct abuf *);
-
- /*
- * The state of the given output abuf changed (eg. space for a
- * new output block was made available) so processing can
- * continue. This function must process more input in order to
- * fill the output block.
- *
- * Producing a block will result in the change of the state of
- * OTHER buffers that are attached to the aproc, thus this
- * routine MUST notify ALL aproc structures that are waiting on
- * it; most of the time this means calling abuf_fill() on the
- * source buffers.
- *
- * Before filling input buffers (using abuf_fill()), this
- * routine must ALWAYS check for eof condition, and if needed,
- * handle it appropriately and call abuf_hup() to free the input
- * buffer.
- */
- int (*out)(struct aproc *, struct abuf *);
-
- /*
- * The input buffer is empty and we can no more receive data
- * from it. The buffer will be destroyed as soon as this call
- * returns so the abuf pointer will stop being valid after this
- * call returns. There's no need to drain the buffer because the
- * in() call-back was just called before.
- *
- * If this call reads and/or writes data on other buffers,
- * abuf_flush() and abuf_fill() must be called appropriately.
- */
- void (*eof)(struct aproc *, struct abuf *);
-
- /*
- * The output buffer can no more accept data (it should be
- * considered as full). After this function returns, it will be
- * destroyed and the "abuf" pointer will be no more valid.
- */
- void (*hup)(struct aproc *, struct abuf *);
-
- /*
- * A new input was connected.
- */
- void (*newin)(struct aproc *, struct abuf *);
-
- /*
- * A new output was connected
- */
- 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.
- */
- void (*done)(struct aproc *);
-};
-
-/*
- * The aproc structure represents a simple audio processing unit; they are
- * interconnected by abuf structures and form a kind of "circuit". The circuit
- * cannot have loops.
- */
-struct aproc {
- char *name; /* for debug purposes */
- struct aproc_ops *ops; /* call-backs */
- LIST_HEAD(, abuf) ins; /* list of inputs */
- LIST_HEAD(, abuf) outs; /* list of outputs */
- unsigned int refs; /* extern references */
-#define APROC_ZOMB 1 /* destroyed but not freed */
-#define APROC_QUIT 2 /* try to terminate if unused */
-#define APROC_DROP 4 /* xrun if capable */
- unsigned int flags;
- union { /* follow type-specific data */
- struct { /* file/device io */
- struct file *file; /* file to read/write */
- unsigned int partial; /* bytes of partial frame */
- } io;
- struct {
- unsigned int idle; /* frames since idleing */
- unsigned int round; /* block size, for xruns */
- int lat; /* current latency */
- int maxlat; /* max latency allowed */
- unsigned int abspos; /* frames produced */
- struct aproc *mon; /* snoop output */
- unsigned int autovol; /* adjust volume dynamically */
- int master; /* master attenuation */
- } mix;
- struct {
- unsigned int idle; /* frames since idleing */
- unsigned int round; /* block size, for xruns */
- int lat; /* current latency */
- int maxlat; /* max latency allowed */
- unsigned int abspos; /* frames consumed */
- } sub;
- struct {
- int delta; /* time position */
- unsigned int bufsz; /* buffer size (latency) */
- unsigned int pending; /* uncommited samples */
- } mon;
- struct {
-#define RESAMP_NCTX 2
- unsigned int ctx_start;
- adata_t ctx[NCHAN_MAX * RESAMP_NCTX];
- unsigned int iblksz, oblksz;
- int diff;
- int idelta, odelta; /* remainder of resamp_xpos */
- } resamp;
- struct {
- int bfirst; /* bytes to skip at startup */
- unsigned int bps; /* bytes per sample */
- unsigned int shift; /* shift to get 32bit MSB */
- int sigbit; /* sign bits to XOR */
- int bnext; /* to reach the next byte */
- int snext; /* to reach the next sample */
- } conv;
- struct {
- struct dev *dev; /* controlled device */
- struct timo timo; /* timout for throtteling */
- unsigned int fps; /* MTC frames per second */
-#define MTC_FPS_24 0
-#define MTC_FPS_25 1
-#define MTC_FPS_30 3
- unsigned int fps_id; /* one of above */
- unsigned int hr; /* MTC hours */
- unsigned int min; /* MTC minutes */
- unsigned int sec; /* MTC seconds */
- unsigned int fr; /* MTC frames */
- unsigned int qfr; /* MTC quarter frames */
- int delta; /* rel. to the last MTC tick */
- } midi;
- } u;
-};
-
-/*
- * Check if the given pointer is a valid aproc structure.
- *
- * aproc structures are not free()'d immediately, because
- * there may be pointers to them, instead the APROC_ZOMB flag
- * is set which means that they should not be used. When
- * aprocs reference counter reaches zero, they are actually
- * freed
- */
-#define APROC_OK(p) ((p) && !((p)->flags & APROC_ZOMB))
-
-
-struct aproc *aproc_new(struct aproc_ops *, char *);
-void aproc_del(struct aproc *);
-void aproc_dbg(struct aproc *);
-void aproc_setin(struct aproc *, struct abuf *);
-void aproc_setout(struct aproc *, struct abuf *);
-int aproc_inuse(struct aproc *);
-int aproc_depend(struct aproc *, struct aproc *);
-
-void aproc_ipos(struct aproc *, struct abuf *, int);
-void aproc_opos(struct aproc *, struct abuf *, int);
-
-struct aproc *rfile_new(struct file *);
-struct aproc *wfile_new(struct file *);
-struct aproc *mix_new(char *, int, unsigned int, unsigned int, unsigned int);
-struct aproc *sub_new(char *, int, unsigned int);
-struct aproc *resamp_new(char *, unsigned int, unsigned int);
-struct aproc *enc_new(char *, struct aparams *);
-struct aproc *dec_new(char *, struct aparams *);
-struct aproc *join_new(char *);
-struct aproc *mon_new(char *, unsigned int);
-
-int rfile_in(struct aproc *, struct abuf *);
-int rfile_out(struct aproc *, struct abuf *);
-void rfile_eof(struct aproc *, struct abuf *);
-void rfile_hup(struct aproc *, struct abuf *);
-void rfile_done(struct aproc *);
-int rfile_do(struct aproc *, unsigned int, unsigned int *);
-
-int wfile_in(struct aproc *, struct abuf *);
-int wfile_out(struct aproc *, struct abuf *);
-void wfile_eof(struct aproc *, struct abuf *);
-void wfile_hup(struct aproc *, struct abuf *);
-void wfile_done(struct aproc *);
-int wfile_do(struct aproc *, unsigned int, unsigned int *);
-
-void mix_setmaster(struct aproc *);
-void mix_clear(struct aproc *);
-void mix_prime(struct aproc *);
-void mix_quit(struct aproc *);
-void mix_drop(struct abuf *, int);
-void sub_silence(struct abuf *, int);
-void sub_clear(struct aproc *);
-void mon_snoop(struct aproc *, struct abuf *, unsigned int, unsigned int);
-void mon_clear(struct aproc *);
-
-#endif /* !defined(APROC_H) */
-.\" $OpenBSD: aucat.1,v 1.106 2014/02/08 15:18:54 ratchov Exp $
+.\" $OpenBSD: aucat.1,v 1.107 2015/01/21 08:43:55 ratchov Exp $
.\"
.\" Copyright (c) 2006 Alexandre Ratchov <alex@caoua.org>
.\"
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd $Mdocdate: February 8 2014 $
+.Dd $Mdocdate: January 21 2015 $
.Dt AUCAT 1
.Os
.Sh NAME
.Nm aucat
-.Nd audio/MIDI stream manipulation tool
+.Nd audio files manipulation tool
.Sh SYNOPSIS
.Nm aucat
.Bk -words
-.Op Fl dMn
-.Op Fl C Ar min : Ns Ar max
+.Op Fl dn
+.Op Dl b Ar size
.Op Fl c Ar min : Ns Ar max
.Op Fl e Ar enc
.Op Fl f Ar device
.Op Fl o Ar file
.Op Fl q Ar port
.Op Fl r Ar rate
-.Op Fl t Ar mode
.Op Fl v Ar volume
-.Op Fl w Ar flag
-.Op Fl x Ar policy
.Ek
.Sh DESCRIPTION
The
.Nm
-utility can play, record, mix, and convert audio files.
+utility can play, record, mix, and process audio files
+on the fly.
During playback,
.Nm
-reads audio data concurrently from all played files, mixes it and sends
-the result to the device.
-Similarly, during recording it duplicates audio data recorded
-from the device and stores it into corresponding files.
-It can process audio data on the fly:
+reads audio data concurrently from all played files,
+mixes it and plays the result on the device.
+Similarly, it stores audio data recorded
+from the device into corresponding files.
+An
+.Em off-line
+mode could be used to process audio files without
+involving audio hardware.
+Processing includes:
.Pp
.Bl -bullet -offset indent -compact
.It
Change the sound encoding.
.It
-Route the sound from one channel to another,
-join stereo or split mono.
+Route the sound from one channel to another.
.It
Control the per-file playback volume.
-.It
-Monitor the sound being played, allowing the playback mix
-to be recorded.
.El
.Pp
Finally,
.Pp
The options are as follows:
.Bl -tag -width Ds
+.It Fl Ar b size
+The buffer size of the audio device in frames.
+Default is 7680.
.It Xo
-.Fl C Ar min : Ns Ar max ,
.Fl c Ar min : Ns Ar max
.Xc
-The range of stream channel numbers for recording and playback directions,
-respectively.
+The range of audio file channel numbers.
The default is
.Cm 0:1 ,
i.e. stereo.
.It Fl d
Increase log verbosity.
.It Fl e Ar enc
-Encoding of the playback or recording stream (see below).
-The default is signed, 16-bit, native byte order.
+Encoding of the audio file.
+The default is
+.Va s16 .
+Encoding names use the follwing scheme: signedness
+.Po
+.Va s
+or
+.Va u
+.Pc
+followed
+by the precision in bits, the byte-order
+.Po
+.Va le
+or
+.Va be
+.Pc ,
+the number of
+bytes per sample, and the alignment
+.Po
+.Va msb
+or
+.Va lsb
+.Pc .
+Only the signedness and the precision are mandatory.
+Examples:
+.Va u8 , s16le , s24le3 , s24le4lsb .
.It Fl f Ar device
Use this
.Xr sndio 7
audio device.
-Preceding per-device options apply to this device.
-Streams
-.Pq Fl io
-and control MIDI ports
-.Pq Fl q
-that are applied after will be attached to this device.
-Device mode and parameters are determined from streams
-attached to it.
+Device mode and parameters are determined from audio files.
+Default is
+.Pa default .
.It Fl h Ar fmt
-File format of the playback or record stream (see below).
-The default is
-.Cm auto .
+Audio file type.
+The following file types are supported:
+.Bl -tag -width auto
+.It Ar raw
+Headerless file.
+.It Ar wav
+Microsoft WAV file format.
+.It Ar aiff
+Apple's audio interchange file format.
+.It Ar au
+Sun/NeXT audio file format.
+.It Ar auto
+Try to guess, depending on the file name.
+This is the default.
+.El
.It Fl i Ar file
-Add this file to the list of streams to play.
+Play this audio file.
If the option argument is
.Sq -
then standard input will be used.
.It Fl j Ar flag
-Control whether stream channels are joined or expanded if
-the stream number of channels is not equal to the device number of channels.
+Control whether source channels are joined or expanded if
+they don't match the destination number of channels.
If the flag is
-.Cm off
-then stream channels are routed to the corresponding
-device channel, possibly discarding channels not present in the device.
+.Cm off ,
+then each source channel is routed to a single destination channel,
+possibly discarding channels.
If the flag is
.Cm on ,
-then a single stream channel may be sent on multiple device channels,
-or multiple stream channels may be sent to a single device channel.
-For instance, this feature could be used to request mono streams to
-be sent on multiple outputs or to record a stereo input into a mono stream.
+then a single source may be sent to multiple destinations
+and multiple sources may be mixed into a single destination.
+For instance, this feature could be used to convert
+a stereo file into a mono file mixing left and right channels together.
The default is
-.Cm on .
-.It Fl M
-Create a MIDI thru box
-.Pq i.e. MIDI-only pseudo device .
-It merges any number of MIDI inputs and broadcasts the result
-to any number of MIDI outputs, similarly to a hardware MIDI thru box.
-Only MIDI ports
-.Pq Fl q
-and MIDI files
-can be attached to it.
+.Cm off .
.It Fl n
-Create a loopback pseudo audio device.
-Send input streams
-to the output, processing them on the fly.
-This pseudo-device is useful to mix, demultiplex, resample or re-encode
-audio files offline.
+Off-line mode.
+Read input files and store the result in the output files,
+processing them on the fly.
+This mode is useful to mix, demultiplex, resample or re-encode
+audio files off-line.
It requires at least one input
.Pq Fl i
and one output
.Pq Fl o .
.It Fl o Ar file
-Add this file to the list of recording streams.
+Record into this audio file.
If the option argument is
.Sq -
then standard output will be used.
.It Fl q Ar port
-Allow audio device properties to be controlled
-through this MIDI port.
+Control audio device properties through this MIDI port.
This includes per-stream volumes and the ability to
-synchronously start, stop and relocate streams created in
-MIDI Machine
-Control (MMC) slave mode
-.Pq Fl t .
+synchronously start, stop and relocate audio files.
.It Fl r Ar rate
-Sample rate in Hertz of the stream.
+Sample rate in Hertz of the audio file.
The default is
.Cm 48000 .
-.It Fl t Ar mode
-Select the way streams are controlled by MIDI Machine Control (MMC)
-messages.
-If the mode is
-.Cm off
-(the default), then streams are not affected by MMC messages.
-If the mode is
-.Cm slave ,
-then streams are started synchronously by MMC start messages.
.It Fl v Ar volume
-Software volume attenuation of the playback stream.
+Software volume attenuation of the file to play.
The value must be between 1 and 127,
corresponding to \-42dB and \-0dB attenuation in 1/3dB steps.
The default is 127, i.e. no attenuation.
-.It Fl w Ar flag
-Control
-.Nm
-behaviour when the maximum volume of the hardware is reached
-and a new stream is connected.
-This happens only when stream volumes
-are not properly set using the
-.Fl v
-option.
-If the flag is
-.Cm on ,
-then the master volume (corresponding to the mix of all playback streams)
-is automatically adjusted to avoid clipping.
-Using
-.Cm off
-makes sense when all streams are recorded or produced with properly lowered
-volumes.
-The default is
-.Cm on .
-.It Fl x Ar policy
-Action when the output stream cannot accept
-recorded data fast enough or the input stream
-cannot provide data to play fast enough.
-If the policy is
-.Cm ignore
-(the default) then samples that cannot be written are discarded
-and samples that cannot be read are replaced by silence.
-If the policy is
-.Cm sync
-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
-.Cm error
-then the stream is closed permanently.
-.Pp
-If a stream is created with the
-.Fl t
-option,
-the
-.Cm ignore
-action is disabled for any stream connected to it
-to ensure proper synchronization.
.El
.Pp
On the command line,
-per-device parameters must precede the device definition
-.Pq Fl fMn ,
-and per-stream parameters
-.Pq Fl Ccehjmrtvx
-must precede the stream definition
+per-file parameters
+.Pq Fl cehjrv
+must precede the file definition
.Pq Fl io .
-MIDI ports
-.Pq Fl q
-and stream definitions
-.Pq Fl io
-must follow the definition of the device
-.Pq Fl fMn
-to which they are attached.
-.Pp
-If no audio devices
-.Pq Fl fMn
-are specified,
-settings are applied as if
-the default device is specified.
.Pp
If
-.Nm aucat
+.Nm
is sent
.Dv SIGHUP ,
.Dv SIGINT
or
.Dv SIGTERM ,
it terminates recording to files.
-.Pp
-File formats are specified using the
-.Fl h
-option.
-The following file formats are supported:
-.Bl -tag -width s32lexxx -offset indent
-.It raw
-Headerless file.
-This format is recommended since it has no limitations.
-.It wav
-Microsoft WAVE file format.
-There are limitations inherent to the file format itself:
-not all encodings are supported,
-file sizes are limited to 2GB,
-and the file must support the
-.Xr lseek 2
-operation (e.g. pipes do not support it).
-.It auto
-Try to guess, depending on the file name.
-.El
-.Pp
-Encodings are specified using the
-.Fl e
-option.
-The following encodings are supported:
-.Pp
-.Bl -tag -width s32lexxx -offset indent -compact
-.It s8
-signed 8-bit
-.It u8
-unsigned 8-bit
-.It s16le
-signed 16-bit, little endian
-.It u16le
-unsigned 16-bit, little endian
-.It s16be
-signed 16-bit, big endian
-.It u16be
-unsigned 16-bit, big endian
-.It s24le
-signed 24-bit, stored in 4 bytes, little endian
-.It u24le
-unsigned 24-bit, stored in 4 bytes, little endian
-.It s24be
-signed 24-bit, stored in 4 bytes, big endian
-.It u24be
-unsigned 24-bit, stored in 4 bytes, big endian
-.It s32le
-signed 32-bit, little endian
-.It u32le
-unsigned 32-bit, little endian
-.It s32be
-signed 32-bit, big endian
-.It u32be
-unsigned 32-bit, big endian
-.It s24le3
-signed 24-bit, packed in 3 bytes, little endian
-.It u24le3
-unsigned 24-bit, packed in 3 bytes, big endian
-.It s24be3
-signed 24-bit, packed in 3 bytes, little endian
-.It u24be3
-unsigned 24-bit, packed in 3 bytes, big endian
-.It s20le3
-signed 20-bit, packed in 3 bytes, little endian
-.It u20le3
-unsigned 20-bit, packed in 3 bytes, big endian
-.It s20be3
-signed 20-bit, packed in 3 bytes, little endian
-.It u20be3
-unsigned 20-bit, packed in 3 bytes, big endian
-.It s18le3
-signed 18-bit, packed in 3 bytes, little endian
-.It u18le3
-unsigned 18-bit, packed in 3 bytes, big endian
-.It s18be3
-signed 18-bit, packed in 3 bytes, little endian
-.It u18be3
-unsigned 18-bit, packed in 3 bytes, big endian
-.El
.Sh MIDI CONTROL
.Nm
can be controlled through MIDI
as follows:
a MIDI channel is assigned to each stream, and the volume
is changed using the standard volume controller (number 7).
-Similarly, when the audio client changes its volume,
-the same MIDI controller message is sent out; it can be used
-for instance for monitoring or as feedback for motorized
-faders.
.Pp
The master volume can be changed using the standard master volume
system exclusive message.
.Pp
-Streams created with the
-.Fl t
-option are controlled by the following MMC messages:
-.Bl -tag -width relocateXXX -offset indent
+All audio files are controlled by the following MMC messages:
+.Bl -tag -width relocate -offset indent
.It relocate
-Files are relocated to the requested time position.
-If the requested position is beyond the end of file,
-playback of the file is temporarly disabled until a valid
-position is requested.
+All files are relocated to the requested time position.
+If it is beyond the end of a file, the file is temporarly
+disabled until a valid position is requested.
.It start
-Files are started.
+Playback and/or recording is started.
.It stop
-Files are stopped and rewound back to the starting position.
+Playback and/or recording is stopped and all files are rewound
+back to the starting position.
.El
.Pp
MIDI control is intended to be used together with
and a MMC-controlled one
.Va snd/0.mmc :
.Bd -literal -offset indent
-$ sndiod -r 48000 -z 400 -s default -t slave -s mmc
+$ sndiod -r 48000 -z 480 -s default -t slave -s mmc
.Ed
.Pp
Programs using
.Va midithru/0
MIDI port:
.Bd -literal -offset indent
-$ aucat -f snd/0.mmc -t slave -q midithru/0 -i file.wav
+$ aucat -f snd/0.mmc -q midithru/0 -i file.wav
.Ed
.Pp
At this stage,
port as MTC clock source, assured to be synchronous to playback of
.Pa file.wav .
.Sh EXAMPLES
-Mix and play two stereo streams,
-the first at 48kHz and the second at 44.1kHz:
+Mix and play two files while recording a third file:
.Bd -literal -offset indent
-$ aucat -r 48000 -i file1.raw -r 44100 -i file2.raw
+$ aucat -i file1.wav -i file2.wav -o file3.wav
.Ed
.Pp
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:
+channels 6 and 7 into another stereo file using a 44.1kHz sampling
+rate for both:
.Bd -literal -offset indent
-$ aucat -j off -r 96000 -C 2:3 -o file1.raw -C 6:7 -o file2.raw
+$ aucat -r 44100 -c 2:3 -o file1.wav -c 6:7 -o file2.wav
.Ed
.Pp
Split a stereo file into two mono files:
.Bd -literal -offset indent
-$ aucat -n -j off -i stereo.wav -C 0:0 -o left.wav -C 1:1 \e
- -o right.wav
+$ aucat -n -i stereo.wav -c 0:0 -o left.wav \e
+ -c 1:1 -o right.wav
.Ed
.Sh SEE ALSO
.Xr audioctl 1 ,
.Xr audio 4 ,
.Xr sndio 7
.Sh BUGS
-Resampling is low quality; down-sampling especially should be avoided
-when recording.
+Resampling is low quality.
.Pp
-Processing is done using 16-bit arithmetic,
-thus samples with more than 16 bits are rounded.
-16 bits (i.e. 97dB dynamic) are largely enough for most applications though.
+There are limitations inherent to the
+.Ar wav ,
+.Ar aiff ,
+and
+.Ar au
+file formats: not all encodings are supported,
+file sizes are limited to 2GB, and the files must support the
+.Xr lseek 2
+operation (e.g. pipes do not support it).
-/* $OpenBSD: aucat.c,v 1.145 2015/01/16 06:40:05 deraadt Exp $ */
/*
- * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
+ * Copyright (c) 2008-2014 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
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include <sys/queue.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/resource.h>
-
-#include <err.h>
-#include <errno.h>
#include <fcntl.h>
-#include <grp.h>
-#include <limits.h>
-#include <pwd.h>
+#include <errno.h>
+#include <poll.h>
#include <signal.h>
#include <sndio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
-
#include "abuf.h"
-#include "amsg.h"
-#include "aparams.h"
-#include "aproc.h"
-#include "conf.h"
-#include "dev.h"
-#include "midi.h"
-#include "wav.h"
-#ifdef DEBUG
-#include "dbg.h"
-#endif
+#include "afile.h"
+#include "dsp.h"
+#include "sysex.h"
+#include "utils.h"
+
+/*
+ * masks to extract command and channel of status byte
+ */
+#define MIDI_CMDMASK 0xf0
+#define MIDI_CHANMASK 0x0f
+
+/*
+ * MIDI status bytes of voice messages
+ */
+#define MIDI_NOFF 0x80 /* note off */
+#define MIDI_NON 0x90 /* note on */
+#define MIDI_KAT 0xa0 /* key after touch */
+#define MIDI_CTL 0xb0 /* controller */
+#define MIDI_PC 0xc0 /* program change */
+#define MIDI_CAT 0xd0 /* channel after touch */
+#define MIDI_BEND 0xe0 /* pitch bend */
+#define MIDI_ACK 0xfe /* active sensing message */
+
+/*
+ * MIDI controller numbers
+ */
+#define MIDI_CTL_VOL 7
+
+/*
+ * Max coarse value
+ */
+#define MIDI_MAXCTL 127
+
+/*
+ * MIDI status bytes for sysex
+ */
+#define MIDI_SX_START 0xf0
+#define MIDI_SX_STOP 0xf7
+
+/*
+ * audio device defaults
+ */
+#define DEFAULT_RATE 48000
+#define DEFAULT_BUFSZ_MS 200
-#define PROG_AUCAT "aucat"
+struct slot {
+ struct slot *next; /* next on the play list */
+ int vol; /* dynamic range */
+ int volctl; /* volume in the 0..127 range */
+ struct abuf buf; /* file i/o buffer */
+ int bpf; /* bytes per frame */
+ int cmin, cmax; /* file channel range */
+ struct cmap cmap; /* channel mapper state */
+ struct resamp resamp; /* resampler state */
+ struct conv conv; /* format encoder state */
+ int join; /* channel join factor */
+ int expand; /* channel expand factor */
+ void *resampbuf, *convbuf; /* conversion tmp buffers */
+ int dup; /* mono-to-stereo and alike */
+ int round; /* slot-side block size */
+ int mode; /* MODE_{PLAY,REC} */
+#define SLOT_CFG 0 /* buffers not allocated yet */
+#define SLOT_INIT 1 /* not trying to do anything */
+#define SLOT_RUN 2 /* playing/recording */
+#define SLOT_STOP 3 /* draining (play only) */
+ int pstate; /* one of above */
+ struct afile afile; /* file desc & friends */
+};
/*
- * sample rate if no ``-r'' is used
+ * device properties
*/
-#ifndef DEFAULT_RATE
-#define DEFAULT_RATE 48000
+unsigned int dev_mode; /* bitmap of SIO_{PLAY,REC} */
+unsigned int dev_bufsz; /* device buffer size */
+unsigned int dev_round; /* device block size */
+int dev_rate; /* device sample rate (Hz) */
+unsigned int dev_pchan, dev_rchan; /* play & rec channels count */
+adata_t *dev_pbuf, *dev_rbuf; /* play & rec buffers */
+unsigned int dev_mmcpos; /* last MMC position */
+#define DEV_STOP 0 /* stopped */
+#define DEV_START 1 /* started */
+unsigned int dev_pstate; /* one of above */
+char *dev_name; /* device sndio(7) name */
+char *dev_port; /* control port sndio(7) name */
+struct sio_hdl *dev_sh; /* device handle */
+struct mio_hdl *dev_mh; /* MIDI control port handle */
+unsigned int dev_volctl = MIDI_MAXCTL; /* master volume */
+
+/*
+ * MIDI parser state
+ */
+#define MIDI_MSGMAX 32 /* max size of MIDI msg */
+unsigned char dev_msg[MIDI_MSGMAX]; /* parsed input message */
+unsigned int dev_mst; /* input MIDI running status */
+unsigned int dev_mused; /* bytes used in ``msg'' */
+unsigned int dev_midx; /* current ``msg'' size */
+unsigned int dev_mlen; /* expected ``msg'' length */
+unsigned int dev_prime; /* blocks to write to start */
+
+unsigned int log_level = 1;
+volatile sig_atomic_t quit_flag = 0;
+struct slot *slot_list = NULL;
+
+/*
+ * length of voice and common MIDI messages (status byte included)
+ */
+unsigned int voice_len[] = { 3, 3, 3, 3, 2, 2, 3 };
+unsigned int common_len[] = { 0, 2, 3, 2, 0, 0, 1, 1 };
+
+char usagestr[] = "usage: aucat [-d] [-b nframes] "
+ "[-c min:max] [-e enc] [-f device]\n\t"
+ "[-j flag] [-q port] [-r rate] [-v volume]\n";
+
+static void
+slot_log(struct slot *s)
+{
+#ifdef DEBUG
+ static char *pstates[] = {
+ "cfg", "ini", "run", "stp"
+ };
+#endif
+ log_puts(s->afile.path);
+#ifdef DEBUG
+ if (log_level >= 3) {
+ log_puts(",pst=");
+ log_puts(pstates[s->pstate]);
+ }
+#endif
+}
+
+static void
+slot_flush(struct slot *s)
+{
+ int todo, count, n;
+ unsigned char *data;
+
+ todo = s->buf.used;
+ while (todo > 0) {
+ data = abuf_rgetblk(&s->buf, &count);
+ if (count > todo)
+ count = todo;
+ n = afile_write(&s->afile, data, count);
+ if (n == 0) {
+ slot_log(s);
+ log_puts(": can't write, disabled\n");
+ s->pstate = SLOT_INIT;
+ return;
+ }
+ abuf_rdiscard(&s->buf, n);
+ todo -= n;
+ }
+}
+
+static void
+slot_fill(struct slot *s)
+{
+ int todo, count, n;
+ unsigned char *data;
+
+ todo = s->buf.len;
+ while (todo > 0) {
+ data = abuf_wgetblk(&s->buf, &count);
+ if (count > todo)
+ count = todo;
+ n = afile_read(&s->afile, data, count);
+ if (n == 0) {
+#ifdef DEBUG
+ if (log_level >= 3) {
+ slot_log(s);
+ log_puts(": eof reached, stopping\n");
+ }
+#endif
+ s->pstate = SLOT_STOP;
+ break;
+ }
+ abuf_wcommit(&s->buf, n);
+ todo -= n;
+ }
+}
+
+static int
+slot_new(char *path, int mode, struct aparams *par, int hdr,
+ int cmin, int cmax, int rate, int dup, int vol)
+{
+ struct slot *s;
+
+ s = xmalloc(sizeof(struct slot));
+ if (!afile_open(&s->afile, path, hdr,
+ mode == SIO_PLAY ? AFILE_FREAD : AFILE_FWRITE,
+ par, rate, cmax - cmin + 1)) {
+ xfree(s);
+ return 0;
+ }
+ s->cmin = cmin;
+ s->cmax = cmin + s->afile.nch - 1;
+ s->dup = dup;
+ s->vol = MIDI_TO_ADATA(vol);
+ s->mode = mode;
+ s->pstate = SLOT_CFG;
+ if (log_level >= 2) {
+ slot_log(s);
+ log_puts(": ");
+ log_puts(s->mode == SIO_PLAY ? "play" : "rec");
+ log_puts(", chan ");
+ log_putu(s->cmin);
+ log_puts(":");
+ log_putu(s->cmax);
+ log_puts(", ");
+ log_putu(s->afile.rate);
+ log_puts("Hz, ");
+ switch (s->afile.fmt) {
+ case AFILE_FMT_PCM:
+ aparams_log(&s->afile.par);
+ break;
+ case AFILE_FMT_ULAW:
+ log_puts("ulaw");
+ break;
+ case AFILE_FMT_ALAW:
+ log_puts("alaw");
+ break;
+ case AFILE_FMT_FLOAT:
+ log_puts("f32le");
+ break;
+ }
+ if (s->mode == SIO_PLAY && s->afile.endpos >= 0) {
+ log_puts(", bytes ");
+ log_puti(s->afile.startpos);
+ log_puts("..");
+ log_puti(s->afile.endpos);
+ }
+ log_puts("\n");
+ }
+ s->next = slot_list;
+ slot_list = s;
+ return 1;
+}
+
+static void
+slot_init(struct slot *s)
+{
+ unsigned int slot_nch, bufsz;
+
+#ifdef DEBUG
+ if (s->pstate != SLOT_CFG) {
+ slot_log(s);
+ log_puts(": slot_init: wrong state\n");
+ panic();
+ }
+#endif
+ s->bpf = s->afile.par.bps * (s->cmax - s->cmin + 1);
+ s->round = (dev_round * s->afile.rate + dev_rate / 2) / dev_rate;
+
+ bufsz = s->round * (dev_bufsz / dev_round);
+ bufsz -= bufsz % s->round;
+ if (bufsz == 0)
+ bufsz = s->round;
+ abuf_init(&s->buf, bufsz * s->bpf);
+#ifdef DEBUG
+ if (log_level >= 3) {
+ slot_log(s);
+ log_puts(": allocated ");
+ log_putu(bufsz);
+ log_puts(" frame buffer\n");
+ }
+#endif
+
+ slot_nch = s->cmax - s->cmin + 1;
+ s->convbuf = NULL;
+ s->resampbuf = NULL;
+ s->join = 1;
+ s->expand = 1;
+ if (s->mode & SIO_PLAY) {
+ if (s->dup) {
+ if (dev_pchan > slot_nch)
+ s->expand = dev_pchan / slot_nch;
+ else if (dev_pchan < slot_nch)
+ s->join = slot_nch / dev_pchan;
+ }
+ cmap_init(&s->cmap,
+ s->cmin, s->cmax,
+ s->cmin, s->cmax,
+ 0, dev_pchan - 1,
+ 0, dev_pchan - 1);
+ if (s->afile.fmt != AFILE_FMT_PCM || !aparams_native(&s->afile.par)) {
+ dec_init(&s->conv, &s->afile.par, slot_nch);
+ s->convbuf =
+ xmalloc(s->round * slot_nch * sizeof(adata_t));
+ }
+ if (s->afile.rate != dev_rate) {
+ resamp_init(&s->resamp, s->round, dev_round,
+ slot_nch);
+ s->resampbuf =
+ xmalloc(dev_round * slot_nch * sizeof(adata_t));
+ }
+ }
+ if (s->mode & SIO_REC) {
+ if (s->dup) {
+ if (dev_rchan > slot_nch)
+ s->join = dev_rchan / slot_nch;
+ else if (dev_rchan < slot_nch)
+ s->expand = slot_nch / dev_rchan;
+ }
+ cmap_init(&s->cmap,
+ 0, dev_rchan - 1,
+ 0, dev_rchan - 1,
+ s->cmin, s->cmax,
+ s->cmin, s->cmax);
+ if (s->afile.rate != dev_rate) {
+ resamp_init(&s->resamp, dev_round, s->round,
+ slot_nch);
+ s->resampbuf =
+ xmalloc(dev_round * slot_nch * sizeof(adata_t));
+ }
+ if (!aparams_native(&s->afile.par)) {
+ enc_init(&s->conv, &s->afile.par, slot_nch);
+ s->convbuf =
+ xmalloc(s->round * slot_nch * sizeof(adata_t));
+ }
+ }
+ s->pstate = SLOT_INIT;
+#ifdef DEBUG
+ if (log_level >= 3) {
+ slot_log(s);
+ log_puts(": chain initialized\n");
+ }
+#endif
+}
+
+static void
+slot_start(struct slot *s, unsigned int mmc)
+{
+ off_t mmcpos;
+
+#ifdef DEBUG
+ if (s->pstate != SLOT_INIT) {
+ slot_log(s);
+ log_puts(": slot_start: wrong state\n");
+ panic();
+ }
+#endif
+ mmcpos = ((off_t)mmc * s->afile.rate / MTC_SEC) * s->bpf;
+ if (!afile_seek(&s->afile, mmcpos)) {
+ s->pstate = SLOT_INIT;
+ return;
+ }
+ s->pstate = SLOT_RUN;
+ if (s->mode & SIO_PLAY)
+ slot_fill(s);
+#ifdef DEBUG
+ if (log_level >= 2) {
+ slot_log(s);
+ log_puts(": started\n");
+ }
+#endif
+}
+
+static void
+slot_stop(struct slot *s)
+{
+ if (s->pstate == SLOT_INIT)
+ return;
+ if (s->mode & SIO_REC)
+ slot_flush(s);
+ if (s->mode & SIO_PLAY)
+ s->buf.used = s->buf.start = 0;
+ s->pstate = SLOT_INIT;
+#ifdef DEBUG
+ if (log_level >= 2) {
+ slot_log(s);
+ log_puts(": stopped\n");
+ }
+#endif
+}
+
+static void
+slot_del(struct slot *s)
+{
+ struct slot **ps;
+
+ if (s->pstate != SLOT_CFG) {
+ slot_stop(s);
+ afile_close(&s->afile);
+#ifdef DEBUG
+ if (log_level >= 3) {
+ slot_log(s);
+ log_puts(": closed\n");
+ }
#endif
+ abuf_done(&s->buf);
+ if (s->resampbuf)
+ xfree(s->resampbuf);
+ if (s->convbuf)
+ xfree(s->convbuf);
+ }
+ for (ps = &slot_list; *ps != s; ps = &(*ps)->next)
+ ; /* nothing */
+ *ps = s->next;
+ xfree(s);
+}
+
+static int
+play_filt_resamp(struct slot *s, void *res_in, void *out, int todo)
+{
+ int i, offs, vol, nch;
+ void *in;
+
+ if (s->resampbuf) {
+ todo = resamp_do(&s->resamp,
+ res_in, s->resampbuf, todo);
+ in = s->resampbuf;
+ } else
+ in = res_in;
+
+ nch = s->cmap.nch;
+ vol = s->vol / s->join; /* XXX */
+ cmap_add(&s->cmap, in, out, vol, todo);
+
+ offs = 0;
+ for (i = s->join - 1; i > 0; i--) {
+ offs += nch;
+ cmap_add(&s->cmap, (adata_t *)in + offs, out, vol, todo);
+ }
+ offs = 0;
+ for (i = s->expand - 1; i > 0; i--) {
+ offs += nch;
+ cmap_add(&s->cmap, in, (adata_t *)out + offs, vol, todo);
+ }
+ return todo;
+}
+
+static int
+play_filt_dec(struct slot *s, void *in, void *out, int todo)
+{
+ void *tmp;
+
+ tmp = s->convbuf;
+ if (tmp) {
+ switch (s->afile.fmt) {
+ case AFILE_FMT_PCM:
+ dec_do(&s->conv, in, tmp, todo);
+ break;
+ case AFILE_FMT_ULAW:
+ dec_do_ulaw(&s->conv, in, tmp, todo, 0);
+ break;
+ case AFILE_FMT_ALAW:
+ dec_do_ulaw(&s->conv, in, tmp, todo, 1);
+ break;
+ case AFILE_FMT_FLOAT:
+ dec_do_float(&s->conv, in, tmp, todo);
+ break;
+ }
+ }
+ return play_filt_resamp(s, tmp ? tmp : in, out, todo);
+}
/*
- * block size if neither ``-z'' nor ``-b'' is used
+ * Mix as many as possible frames (but not more than a block) from the
+ * slot buffer to the given location. Return the number of frames mixed
+ * in the output buffer
*/
-#ifndef DEFAULT_ROUND
-#define DEFAULT_ROUND 960
+static int
+slot_mix_badd(struct slot *s, adata_t *odata)
+{
+ adata_t *idata;
+ int icount, todo, done;
+
+ idata = (adata_t *)abuf_rgetblk(&s->buf, &icount);
+ todo = icount / s->bpf;
+ if (todo > s->round)
+ todo = s->round;
+#ifdef DEBUG
+ if (todo == 0) {
+ log_puts("slot_mix_badd: not enough data\n");
+ panic();
+ }
#endif
+ done = play_filt_dec(s, idata, odata, todo);
+ abuf_rdiscard(&s->buf, todo * s->bpf);
+ return done;
+}
+
+static int
+rec_filt_resamp(struct slot *s, void *in, void *res_out, int todo)
+{
+ int i, vol, offs, nch;
+ void *out = res_out;
+
+ out = (s->resampbuf) ? s->resampbuf : res_out;
+
+ nch = s->cmap.nch;
+ vol = ADATA_UNIT / s->join;
+ cmap_copy(&s->cmap, in, out, vol, todo);
+
+ offs = 0;
+ for (i = s->join - 1; i > 0; i--) {
+ offs += nch;
+ cmap_add(&s->cmap, (adata_t *)in + offs, out, vol, todo);
+ }
+ offs = 0;
+ for (i = s->expand - 1; i > 0; i--) {
+ offs += nch;
+ cmap_copy(&s->cmap, in, (adata_t *)out + offs, vol, todo);
+ }
+ if (s->resampbuf) {
+ todo = resamp_do(&s->resamp,
+ s->resampbuf, res_out, todo);
+ }
+ return todo;
+}
+
+static int
+rec_filt_enc(struct slot *s, void *in, void *out, int todo)
+{
+ void *tmp;
+
+ tmp = s->convbuf;
+ todo = rec_filt_resamp(s, in, tmp ? tmp : out, todo);
+ if (tmp)
+ enc_do(&s->conv, tmp, out, todo);
+ return todo;
+}
/*
- * buffer size if neither ``-z'' nor ``-b'' is used
+ * Copy "todo" frames from the given buffer to the slot buffer,
+ * but not more than a block.
*/
-#ifndef DEFAULT_BUFSZ
-#define DEFAULT_BUFSZ 7860
+static void
+slot_sub_bcopy(struct slot *s, adata_t *idata, int todo)
+{
+ adata_t *odata;
+ int ocount;
+
+ odata = (adata_t *)abuf_wgetblk(&s->buf, &ocount);
+#ifdef DEBUG
+ if (ocount < s->round * s->bpf) {
+ log_puts("slot_sub_bcopy: not enough space\n");
+ panic();
+ }
#endif
+ ocount = rec_filt_enc(s, idata, odata, todo);
+ abuf_wcommit(&s->buf, ocount * s->bpf);
+}
+
+static int
+dev_open(char *dev, int mode, int bufsz, char *port)
+{
+ int rate, pmax, rmax;
+ struct sio_par par;
+ struct slot *s;
+
+ if (port) {
+ dev_port = port;
+ dev_mh = mio_open(dev_port, MIO_IN, 0);
+ if (dev_mh == NULL) {
+ log_puts(port);
+ log_puts(": couldn't open midi port\n");
+ return 0;
+ }
+ } else
+ dev_mh = NULL;
+
+ dev_name = dev;
+ dev_sh = sio_open(dev, mode, 0);
+ if (dev_sh == NULL) {
+ log_puts(dev_name);
+ log_puts(": couldn't open audio device\n");
+ return 0;
+ }
+
+ rate = pmax = rmax = 0;
+ for (s = slot_list; s != NULL; s = s->next) {
+ if (s->afile.rate > rate)
+ rate = s->afile.rate;
+ if (s->mode == SIO_PLAY) {
+ if (s->cmax > pmax)
+ pmax = s->cmax;
+ }
+ if (s->mode == SIO_REC) {
+ if (s->cmax > rmax)
+ rmax = s->cmax;
+ }
+ }
+ sio_initpar(&par);
+ par.bits = ADATA_BITS;
+ par.bps = sizeof(adata_t);
+ par.msb = 0;
+ par.le = SIO_LE_NATIVE;
+ if (mode & SIO_PLAY)
+ par.pchan = pmax + 1;
+ if (mode & SIO_REC)
+ par.rchan = rmax + 1;
+ par.appbufsz = bufsz > 0 ? bufsz : rate * DEFAULT_BUFSZ_MS / 1000;
+ if (!sio_setpar(dev_sh, &par) || !sio_getpar(dev_sh, &par)) {
+ log_puts(dev_name);
+ log_puts(": couldn't set audio params\n");
+ return 0;
+ }
+ if (par.bits != ADATA_BITS ||
+ par.bps != sizeof(adata_t) ||
+ par.le != SIO_LE_NATIVE ||
+ (par.bps != SIO_BPS(par.bits) && par.msb)) {
+ log_puts(dev_name);
+ log_puts(": unsupported audio params\n");
+ return 0;
+ }
+ dev_mode = mode;
+ dev_rate = par.rate;
+ dev_bufsz = par.bufsz;
+ dev_round = par.round;
+ if (mode & SIO_PLAY) {
+ dev_pchan = par.pchan;
+ dev_pbuf = xmalloc(sizeof(adata_t) * dev_pchan * dev_round);
+ }
+ if (mode & SIO_REC) {
+ dev_rchan = par.rchan;
+ dev_rbuf = xmalloc(sizeof(adata_t) * dev_rchan * dev_round);
+ }
+ dev_mmcpos = 0;
+ dev_pstate = DEV_STOP;
+ if (log_level >= 2) {
+ log_puts(dev_name);
+ log_puts(": ");
+ log_putu(dev_rate);
+ log_puts("Hz");
+ if (dev_mode & SIO_PLAY) {
+ log_puts(", play 0:");
+ log_puti(dev_pchan - 1);
+ }
+ if (dev_mode & SIO_REC) {
+ log_puts(", rec 0:");
+ log_puti(dev_rchan - 1);
+ }
+ log_puts(", ");
+ log_putu(dev_bufsz / dev_round);
+ log_puts(" blocks of ");
+ log_putu(dev_round);
+ log_puts(" frames\n");
+ }
+ return 1;
+}
+
+static void
+dev_close(void)
+{
+ sio_close(dev_sh);
+ if (dev_mh)
+ mio_close(dev_mh);
+ if (dev_mode & SIO_PLAY)
+ xfree(dev_pbuf);
+ if (dev_mode & SIO_REC)
+ xfree(dev_rbuf);
+}
-void sigint(int);
-void sigusr1(int);
-void sigusr2(int);
-void opt_ch(struct aparams *);
-void opt_enc(struct aparams *);
-int opt_hdr(void);
-int opt_mmc(void);
-int opt_onoff(void);
-int opt_xrun(void);
-void setsig(void);
-void unsetsig(void);
-struct dev *mkdev(char *, int, int, int, int, int);
+static void
+dev_master(int val)
+{
+ struct slot *s;
+ int mastervol, slotvol;
+ mastervol = MIDI_TO_ADATA(dev_volctl);
+ for (s = slot_list; s != NULL; s = s->next) {
+ slotvol = MIDI_TO_ADATA(val);
+ s->vol = ADATA_MUL(mastervol, slotvol);
+ }
#ifdef DEBUG
-volatile sig_atomic_t debug_level = 1;
+ if (log_level >= 3) {
+ log_puts("master volume set to ");
+ log_putu(val);
+ log_puts("\n");
+ }
#endif
-volatile sig_atomic_t quit_flag = 0;
+}
-char aucat_usage[] = "usage: " PROG_AUCAT " [-dMn]\n\t"
- "[-C min:max] [-c min:max] [-e enc] [-f device]\n\t"
- "[-h fmt] [-i file] [-j flag] [-o file] [-q port]\n\t"
- "[-r rate] [-t mode] [-v volume] [-w flag] [-x policy]\n";
+static void
+dev_slotvol(int midich, int val)
+{
+ struct slot *s;
+ int mastervol, slotvol;
+
+ for (s = slot_list; s != NULL; s = s->next) {
+ if (midich == 0) {
+ mastervol = MIDI_TO_ADATA(dev_volctl);
+ slotvol = MIDI_TO_ADATA(val);
+ s->vol = ADATA_MUL(mastervol, slotvol);
+#ifdef DEBUG
+ if (log_level >= 3) {
+ slot_log(s);
+ log_puts(": volume set to ");
+ log_putu(val);
+ log_puts("\n");
+ }
+#endif
+ break;
+ }
+ }
+}
/*
- * 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.
+ * start all slots simultaneously
*/
-void
-sigint(int s)
+static void
+dev_mmcstart(void)
{
- if (quit_flag)
- _exit(1);
- quit_flag = 1;
-}
+ struct slot *s;
+ if (dev_pstate == DEV_STOP) {
+ dev_pstate = DEV_START;
+ for (s = slot_list; s != NULL; s = s->next)
+ slot_start(s, dev_mmcpos);
+ dev_prime = (dev_mode & SIO_PLAY) ? dev_bufsz / dev_round : 0;
+ sio_start(dev_sh);
+ if (log_level >= 2)
+ log_puts("started\n");
+ } else {
#ifdef DEBUG
+ if (log_level >= 3)
+ log_puts("ignoring mmc start\n");
+#endif
+ }
+}
+
/*
- * Increase debug level on SIGUSR1.
+ * stop all slots simultaneously
*/
-void
-sigusr1(int s)
+static void
+dev_mmcstop(void)
{
- if (debug_level < 4)
- debug_level++;
+ struct slot *s;
+
+ if (dev_pstate == DEV_START) {
+ dev_pstate = DEV_STOP;
+ for (s = slot_list; s != NULL; s = s->next)
+ slot_stop(s);
+ sio_stop(dev_sh);
+ if (log_level >= 2)
+ log_puts("stopped\n");
+ } else {
+#ifdef DEBUG
+ if (log_level >= 3)
+ log_puts("ignored mmc stop\n");
+#endif
+ }
}
/*
- * Decrease debug level on SIGUSR2.
+ * relocate all slots simultaneously
*/
-void
-sigusr2(int s)
+static void
+dev_mmcloc(unsigned int mmc)
{
- if (debug_level > 0)
- debug_level--;
+ if (dev_mmcpos == mmc)
+ return;
+ dev_mmcpos = mmc;
+ if (log_level >= 2) {
+ log_puts("relocated to ");
+ log_putu((dev_mmcpos / (MTC_SEC * 3600)) % 24);
+ log_puts(":");
+ log_putu((dev_mmcpos / (MTC_SEC * 60)) % 60);
+ log_puts(":");
+ log_putu((dev_mmcpos / (MTC_SEC)) % 60);
+ log_puts(".");
+ log_putu((dev_mmcpos / (MTC_SEC / 100)) % 100);
+ log_puts("\n");
+ }
+ if (dev_pstate == DEV_START) {
+ dev_mmcstop();
+ dev_mmcstart();
+ }
}
-#endif
-void
-opt_ch(struct aparams *par)
+static void
+dev_imsg(unsigned char *msg, unsigned int len)
{
- char *next, *end;
- long cmin, cmax;
+ struct sysex *x;
+ unsigned int fps, chan;
- errno = 0;
- cmin = strtol(optarg, &next, 10);
- if (next == optarg || *next != ':')
- goto failed;
- cmax = strtol(++next, &end, 10);
- if (end == next || *end != '\0')
- goto failed;
- if (cmin < 0 || cmax < cmin || cmax > NCHAN_MAX)
- goto failed;
- par->cmin = cmin;
- par->cmax = cmax;
- return;
-failed:
- errx(1, "%s: bad channel range", optarg);
+ if ((msg[0] & MIDI_CMDMASK) == MIDI_CTL && msg[1] == MIDI_CTL_VOL) {
+ chan = msg[0] & MIDI_CHANMASK;
+ dev_slotvol(chan, msg[2]);
+ return;
+ }
+ x = (struct sysex *)msg;
+ if (x->start != SYSEX_START)
+ return;
+ if (len < SYSEX_SIZE(empty))
+ return;
+ if (x->type != SYSEX_TYPE_RT)
+ return;
+ if (x->id0 == SYSEX_CONTROL && x->id1 == SYSEX_MASTER) {
+ if (len == SYSEX_SIZE(master))
+ dev_master(x->u.master.coarse);
+ return;
+ }
+ if (x->id0 != SYSEX_MMC)
+ return;
+ switch (x->id1) {
+ case SYSEX_MMC_STOP:
+ if (len != SYSEX_SIZE(stop))
+ return;
+ dev_mmcstop();
+ break;
+ case SYSEX_MMC_START:
+ if (len != SYSEX_SIZE(start))
+ return;
+ dev_mmcstart();
+ break;
+ case SYSEX_MMC_LOC:
+ if (len != SYSEX_SIZE(loc) ||
+ x->u.loc.len != SYSEX_MMC_LOC_LEN ||
+ x->u.loc.cmd != SYSEX_MMC_LOC_CMD)
+ return;
+ switch (x->u.loc.hr >> 5) {
+ case MTC_FPS_24:
+ fps = 24;
+ break;
+ case MTC_FPS_25:
+ fps = 25;
+ break;
+ case MTC_FPS_30:
+ fps = 30;
+ break;
+ default:
+ dev_mmcstop();
+ return;
+ }
+ dev_mmcloc((x->u.loc.hr & 0x1f) * 3600 * MTC_SEC +
+ x->u.loc.min * 60 * MTC_SEC +
+ x->u.loc.sec * MTC_SEC +
+ x->u.loc.fr * (MTC_SEC / fps) +
+ x->u.loc.cent * (MTC_SEC / 100 / fps));
+ break;
+ }
}
-void
-opt_enc(struct aparams *par)
+/*
+ * parse then given data chunk, and calling imsg() for each message
+ */
+static void
+midi_in(unsigned char *idata, int icount)
{
- int len;
+ int i;
+ unsigned char c;
- len = aparams_strtoenc(par, optarg);
- if (len == 0 || optarg[len] != '\0')
- errx(1, "%s: bad encoding", optarg);
+ for (i = 0; i < icount; i++) {
+ c = *idata++;
+ if (c >= 0xf8) {
+ /* we don't use reat-time events */
+ } else if (c == SYSEX_END) {
+ if (dev_mst == SYSEX_START) {
+ dev_msg[dev_midx++] = c;
+ dev_imsg(dev_msg, dev_midx);
+ }
+ dev_mst = 0;
+ dev_midx = 0;
+ } else if (c >= 0xf0) {
+ dev_msg[0] = c;
+ dev_mlen = common_len[c & 7];
+ dev_mst = c;
+ dev_midx = 1;
+ } else if (c >= 0x80) {
+ dev_msg[0] = c;
+ dev_mlen = voice_len[(c >> 4) & 7];
+ dev_mst = c;
+ dev_midx = 1;
+ } else if (dev_mst) {
+ if (dev_midx == 0 && dev_mst != SYSEX_START)
+ dev_msg[dev_midx++] = dev_mst;
+ dev_msg[dev_midx++] = c;
+ if (dev_midx == dev_mlen) {
+ dev_imsg(dev_msg, dev_midx);
+ if (dev_mst >= 0xf0)
+ dev_mst = 0;
+ dev_midx = 0;
+ } else if (dev_midx == MIDI_MSGMAX) {
+ /* sysex too long */
+ dev_mst = 0;
+ }
+ }
+ }
}
-int
-opt_hdr(void)
+static int
+slot_list_mix(unsigned int round, unsigned int pchan, adata_t *pbuf)
{
- if (strcmp("auto", optarg) == 0)
- return HDR_AUTO;
- if (strcmp("raw", optarg) == 0)
- return HDR_RAW;
- if (strcmp("wav", optarg) == 0)
- return HDR_WAV;
- errx(1, "%s: bad header specification", optarg);
+ unsigned int done, n;
+ struct slot *s;
+
+ memset(pbuf, 0, pchan * round * sizeof(adata_t));
+ done = 0;
+ for (s = slot_list; s != NULL; s = s->next) {
+ if (s->pstate == SLOT_INIT || !(s->mode & SIO_PLAY))
+ continue;
+ if (s->pstate == SLOT_STOP && s->buf.used < s->bpf) {
+ s->pstate = SLOT_INIT;
+#ifdef DEBUG
+ if (log_level >= 3) {
+ slot_log(s);
+ log_puts(": drained, done\n");
+ }
+#endif
+ continue;
+ }
+ n = slot_mix_badd(s, dev_pbuf);
+ if (n > done)
+ done = n;
+ }
+ return done;
}
-int
-opt_mmc(void)
+static int
+slot_list_copy(unsigned int count, unsigned int rchan, adata_t *rbuf)
{
- if (strcmp("off", optarg) == 0)
- return 0;
- if (strcmp("slave", optarg) == 0)
- return 1;
- errx(1, "%s: bad MMC mode", optarg);
+ unsigned int done;
+ struct slot *s;
+
+ done = 0;
+ for (s = slot_list; s != NULL; s = s->next) {
+ if (s->pstate == SLOT_INIT || !(s->mode & SIO_REC))
+ continue;
+ slot_sub_bcopy(s, rbuf, count);
+ done = count;
+ }
+ return done;
}
-int
-opt_onoff(void)
+static void
+slot_list_iodo(void)
{
- if (strcmp("off", optarg) == 0)
- return 0;
- if (strcmp("on", optarg) == 0)
- return 1;
- errx(1, "%s: bad join/expand setting", optarg);
+ struct slot *s;
+
+ for (s = slot_list; s != NULL; s = s->next) {
+ if (s->pstate != SLOT_RUN)
+ continue;
+ if ((s->mode & SIO_PLAY) && (s->buf.used == 0))
+ slot_fill(s);
+ if ((s->mode & SIO_REC) && (s->buf.used == s->buf.len))
+ slot_flush(s);
+ }
}
-int
-opt_xrun(void)
+static int
+offline(void)
{
- if (strcmp("ignore", optarg) == 0)
- return XRUN_IGNORE;
- if (strcmp("sync", optarg) == 0)
- return XRUN_SYNC;
- if (strcmp("error", optarg) == 0)
- return XRUN_ERROR;
- errx(1, "%s: bad underrun/overrun policy", optarg);
+ unsigned int todo;
+ int rate, cmax;
+ struct slot *s;
+
+ rate = cmax = 0;
+ for (s = slot_list; s != NULL; s = s->next) {
+ if (s->afile.rate > rate)
+ rate = s->afile.rate;
+ if (s->cmax > cmax)
+ cmax = s->cmax;
+ }
+ dev_sh = NULL;
+ dev_name = "offline";
+ dev_mode = SIO_PLAY | SIO_REC;
+ dev_rate = rate;
+ dev_bufsz = rate;
+ dev_round = rate;
+ dev_pchan = dev_rchan = cmax + 1;
+ dev_pbuf = dev_rbuf = xmalloc(sizeof(adata_t) * dev_pchan * dev_round);
+ dev_pstate = DEV_STOP;
+ dev_mmcpos = 0;
+ for (s = slot_list; s != NULL; s = s->next)
+ slot_init(s);
+ for (s = slot_list; s != NULL; s = s->next)
+ slot_start(s, 0);
+ for (;;) {
+ todo = slot_list_mix(dev_round, dev_pchan, dev_pbuf);
+ if (todo == 0)
+ break;
+ slot_list_copy(todo, dev_pchan, dev_pbuf);
+ slot_list_iodo();
+ }
+ xfree(dev_pbuf);
+ while (slot_list)
+ slot_del(slot_list);
+ return 1;
}
-void
-setsig(void)
+static int
+playrec_cycle(void)
{
- struct sigaction sa;
+ unsigned int n, todo;
+ unsigned char *p;
+ int pcnt, rcnt;
- quit_flag = 0;
- sigfillset(&sa.sa_mask);
- sa.sa_flags = SA_RESTART;
- sa.sa_handler = sigint;
- if (sigaction(SIGINT, &sa, NULL) < 0)
- err(1, "sigaction(int) failed");
- if (sigaction(SIGTERM, &sa, NULL) < 0)
- err(1, "sigaction(term) failed");
- if (sigaction(SIGHUP, &sa, NULL) < 0)
- err(1, "sigaction(hup) failed");
#ifdef DEBUG
- sa.sa_handler = sigusr1;
- if (sigaction(SIGUSR1, &sa, NULL) < 0)
- err(1, "sigaction(usr1) failed");
- sa.sa_handler = sigusr2;
- if (sigaction(SIGUSR2, &sa, NULL) < 0)
- err(1, "sigaction(usr2) failed1n");
+ if (log_level >= 4) {
+ log_puts(dev_name);
+ log_puts(": cycle, prime = ");
+ log_putu(dev_prime);
+ log_puts("\n");
+ }
#endif
+ pcnt = rcnt = 0;
+ if (dev_mode & SIO_REC) {
+ if (dev_prime > 0)
+ dev_prime--;
+ else {
+ todo = dev_round * dev_rchan * sizeof(adata_t);
+ p = (unsigned char *)dev_rbuf;
+ while (todo > 0) {
+ n = sio_read(dev_sh, p, todo);
+ if (n == 0) {
+ log_puts(dev_name);
+ log_puts(": failed to read from device\n");
+ return 0;
+ }
+ p += n;
+ todo -= n;
+ }
+ rcnt = slot_list_copy(dev_round, dev_rchan, dev_rbuf);
+ }
+ }
+ if (dev_mode & SIO_PLAY) {
+ pcnt = slot_list_mix(dev_round, dev_pchan, dev_pbuf);
+ todo = sizeof(adata_t) * dev_pchan * dev_round;
+ n = sio_write(dev_sh, dev_pbuf, todo);
+ if (n == 0) {
+ log_puts(dev_name);
+ log_puts(": failed to write to device\n");
+ return 0;
+ }
+ }
+ slot_list_iodo();
+ return pcnt > 0 || rcnt > 0;
+}
+
+static void
+sigint(int s)
+{
+ if (quit_flag)
+ _exit(1);
+ quit_flag = 1;
}
-void
-unsetsig(void)
+static int
+playrec(char *dev, int mode, int bufsz, char *port)
{
+#define MIDIBUFSZ 0x100
+ unsigned char mbuf[MIDIBUFSZ];
struct sigaction sa;
+ struct pollfd *pfds;
+ struct slot *s;
+ int n, ns, nm, ev;
+
+ if (!dev_open(dev, mode, bufsz, port))
+ return 0;
+ n = sio_nfds(dev_sh);
+ if (dev_mh)
+ n += mio_nfds(dev_mh);
+ pfds = xmalloc(n * sizeof(struct pollfd));
+ for (s = slot_list; s != NULL; s = s->next)
+ slot_init(s);
+ if (dev_mh == NULL)
+ dev_mmcstart();
+ else {
+ if (log_level >= 2)
+ log_puts("ready, waiting for mmc messages\n");
+ }
+ quit_flag = 0;
+ sigfillset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+ sa.sa_handler = sigint;
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGHUP, &sa, NULL);
+ while (!quit_flag) {
+ if (dev_pstate == DEV_START) {
+ ev = 0;
+ if (mode & SIO_PLAY)
+ ev |= POLLOUT;
+ if (mode & SIO_REC)
+ ev |= POLLIN;
+ ns = sio_pollfd(dev_sh, pfds, ev);
+ } else
+ ns = 0;
+ if (dev_mh)
+ nm = mio_pollfd(dev_mh, pfds + ns, POLLIN);
+ else
+ nm = 0;
+ if (poll(pfds, ns + nm, -1) < 0) {
+ if (errno == EINTR)
+ continue;
+ log_puts("poll failed\n");
+ panic();
+ }
+ if (dev_pstate == DEV_START) {
+ ev = sio_revents(dev_sh, pfds);
+ if (ev & POLLHUP) {
+ log_puts(dev);
+ log_puts(": audio device gone, stopping\n");
+ break;
+ }
+ if (ev & (POLLIN | POLLOUT)) {
+ if (!playrec_cycle() && dev_mh == NULL)
+ break;
+ }
+ }
+ if (dev_mh) {
+ ev = mio_revents(dev_mh, pfds + ns);
+ if (ev & POLLHUP) {
+ log_puts(dev_port);
+ log_puts(": midi port gone, stopping\n");
+ break;
+ }
+ if (ev & POLLIN) {
+ n = mio_read(dev_mh, mbuf, MIDIBUFSZ);
+ midi_in(mbuf, n);
+ }
+ }
+ }
sigfillset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sa.sa_handler = SIG_DFL;
-#ifdef DEBUG
- if (sigaction(SIGUSR2, &sa, NULL) < 0)
- err(1, "unsetsig(usr2): sigaction failed");
- if (sigaction(SIGUSR1, &sa, NULL) < 0)
- err(1, "unsetsig(usr1): sigaction failed");
-#endif
- if (sigaction(SIGHUP, &sa, NULL) < 0)
- err(1, "unsetsig(hup): sigaction failed\n");
- if (sigaction(SIGTERM, &sa, NULL) < 0)
- err(1, "unsetsig(term): sigaction failed\n");
- if (sigaction(SIGINT, &sa, NULL) < 0)
- err(1, "unsetsig(int): sigaction failed\n");
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGHUP, &sa, NULL);
+
+ if (dev_pstate == DEV_START)
+ dev_mmcstop();
+ xfree(pfds);
+ dev_close();
+ while (slot_list)
+ slot_del(slot_list);
+ return 1;
}
-struct dev *
-mkdev(char *path, int mode, int bufsz, int round, int hold, int autovol)
+static int
+opt_onoff(char *s, int *flag)
{
- struct dev *d;
+ if (strcmp("off", s) == 0) {
+ *flag = 0;
+ return 1;
+ }
+ if (strcmp("on", s) == 0) {
+ *flag = 1;
+ return 1;
+ }
+ log_puts(s);
+ log_puts(": on/off expected\n");
+ return 0;
+}
- if (path) {
- for (d = dev_list; d != NULL; d = d->next) {
- if (d->reqmode & (MODE_LOOP | MODE_THRU))
- continue;
- if (strcmp(d->path, path) == 0)
- return d;
- }
- } else {
- if (dev_list)
- return dev_list;
- path = SIO_DEVANY;
- }
- if (!bufsz && !round) {
- round = DEFAULT_ROUND;
- bufsz = DEFAULT_BUFSZ;
- } else if (!bufsz) {
- bufsz = round * 2;
- } else if (!round)
- round = bufsz / 2;
- d = dev_new(path, mode, bufsz, round, hold, autovol);
- if (d == NULL)
- exit(1);
- return d;
+static int
+opt_enc(char *s, struct aparams *par)
+{
+ int len;
+
+ len = aparams_strtoenc(par, s);
+ if (len == 0 || s[len] != '\0') {
+ log_puts(s);
+ log_puts(": bad encoding\n");
+ return 0;
+ }
+ return 1;
+}
+
+static int
+opt_hdr(char *s, int *hdr)
+{
+ if (strcmp("auto", s) == 0) {
+ *hdr = AFILE_HDR_AUTO;
+ return 1;
+ }
+ if (strcmp("raw", s) == 0) {
+ *hdr = AFILE_HDR_RAW;
+ return 1;
+ }
+ if (strcmp("wav", s) == 0) {
+ *hdr = AFILE_HDR_WAV;
+ return 1;
+ }
+ if (strcmp("aiff", s) == 0) {
+ *hdr = AFILE_HDR_AIFF;
+ return 1;
+ }
+ if (strcmp("au", s) == 0) {
+ *hdr = AFILE_HDR_AU;
+ return 1;
+ }
+ log_puts(s);
+ log_puts(": bad header type\n");
+ return 0;
+}
+
+static int
+opt_ch(char *s, int *rcmin, int *rcmax)
+{
+ char *next, *end;
+ long cmin, cmax;
+
+ errno = 0;
+ cmin = strtol(s, &next, 10);
+ if (next == s || *next != ':')
+ goto failed;
+ cmax = strtol(++next, &end, 10);
+ if (end == next || *end != '\0')
+ goto failed;
+ if (cmin < 0 || cmax < cmin || cmax >= NCHAN_MAX)
+ goto failed;
+ *rcmin = cmin;
+ *rcmax = cmax;
+ return 1;
+failed:
+ log_puts(s);
+ log_puts(": channel range expected\n");
+ return 0;
+}
+
+static int
+opt_num(char *s, int min, int max, int *num)
+{
+ const char *errstr;
+
+ *num = strtonum(s, min, max, &errstr);
+ if (errstr) {
+ log_puts(s);
+ log_puts(": expected integer between ");
+ log_puti(min);
+ log_puts(" and ");
+ log_puti(max);
+ log_puts("\n");
+ return 0;
+ }
+ return 1;
}
int
main(int argc, char **argv)
{
- int c, active;
- unsigned int mode, hdr, xrun, rate, join, mmc, vol;
- unsigned int hold, autovol, bufsz, round;
- const char *str;
- struct aparams ppar, rpar;
- struct dev *d, *dnext;
- struct wav *w;
-
- /*
- * global options defaults
- */
- hdr = HDR_AUTO;
- xrun = XRUN_IGNORE;
- vol = MIDI_MAXCTL;
- join = 1;
- mmc = 0;
- hold = 0;
- autovol = 1;
- bufsz = 0;
- round = 0;
- aparams_init(&ppar, 0, 1, DEFAULT_RATE);
- aparams_init(&rpar, 0, 1, DEFAULT_RATE);
- mode = MODE_MIDIMASK | MODE_PLAY | MODE_REC;
-
-#ifdef DEBUG
- atexit(dbg_flush);
-#endif
- setsig();
- filelist_init();
+ int dup, cmin, cmax, rate, vol, bufsz, hdr, mode;
+ char *port, *dev;
+ struct aparams par;
+ int n_flag, c;
- while ((c = getopt(argc, argv,
- "a:b:c:C:de:f:h:i:j:Mno:q:r:t:v:w:x:z:")) != -1) {
+ vol = 127;
+ dup = 0;
+ bufsz = 0;
+ rate = DEFAULT_RATE;
+ cmin = 0;
+ cmax = 1;
+ aparams_init(&par);
+ hdr = AFILE_HDR_AUTO;
+ n_flag = 0;
+ port = NULL;
+ dev = NULL;
+ mode = 0;
+
+ while ((c = getopt(argc, argv, "b:c:de:f:h:i:j:no:q:r:t:v:")) != -1) {
switch (c) {
- case 'd':
-#ifdef DEBUG
- if (debug_level < 4)
- debug_level++;
-#endif
- break;
- case 'h':
- hdr = opt_hdr();
- break;
- case 'x':
- xrun = opt_xrun();
- break;
- case 'j':
- join = opt_onoff();
- break;
- case 't':
- mmc = opt_mmc();
+ case 'b':
+ if (!opt_num(optarg, 1, RATE_MAX, &bufsz))
+ return 1;
break;
case 'c':
- opt_ch(&ppar);
+ if (!opt_ch(optarg, &cmin, &cmax))
+ return 1;
break;
- case 'C':
- opt_ch(&rpar);
+ case 'd':
+ log_level++;
break;
case 'e':
- opt_enc(&ppar);
- aparams_copyenc(&rpar, &ppar);
+ if (!opt_enc(optarg, &par))
+ return 1;
break;
- case 'r':
- rate = strtonum(optarg, RATE_MIN, RATE_MAX, &str);
- if (str)
- errx(1, "%s: rate is %s", optarg, str);
- ppar.rate = rpar.rate = rate;
+ case 'f':
+ dev = optarg;
break;
- case 'v':
- vol = strtonum(optarg, 0, MIDI_MAXCTL, &str);
- if (str)
- errx(1, "%s: volume is %s", optarg, str);
+ case 'h':
+ if (!opt_hdr(optarg, &hdr))
+ return 1;
break;
case 'i':
- d = mkdev(NULL, 0, bufsz, round, 1, autovol);
- w = wav_new_in(&wav_ops, d,
- mode & (MODE_PLAY | MODE_MIDIOUT), optarg,
- hdr, &ppar, xrun, vol, mmc, join);
- if (w == NULL)
- errx(1, "%s: couldn't create stream", optarg);
- dev_adjpar(d, w->mode, NULL, &w->hpar);
- break;
- case 'o':
- d = mkdev(NULL, 0, bufsz, round, 1, autovol);
- w = wav_new_out(&wav_ops, d,
- mode & (MODE_RECMASK | MODE_MIDIIN), optarg,
- hdr, &rpar, xrun, mmc, join);
- if (w == NULL)
- errx(1, "%s: couldn't create stream", optarg);
- dev_adjpar(d, w->mode, &w->hpar, NULL);
+ if (!slot_new(optarg, SIO_PLAY,
+ &par, hdr, cmin, cmax, rate, dup, vol))
+ return 1;
+ mode |= SIO_PLAY;
break;
- case 'q':
- d = mkdev(NULL, mode, bufsz, round, 1, autovol);
- if (!devctl_add(d, optarg, MODE_MIDIMASK))
- errx(1, "%s: can't open port", optarg);
- d->reqmode |= MODE_MIDIMASK;
- break;
- case 'w':
- autovol = opt_onoff();
+ case 'j':
+ if (!opt_onoff(optarg, &dup))
+ return 1;
break;
- case 'b':
- bufsz = strtonum(optarg, 1, RATE_MAX * 5, &str);
- if (str)
- errx(1, "%s: buffer size is %s", optarg, str);
+ case 'n':
+ n_flag = 1;
break;
- case 'z':
- round = strtonum(optarg, 1, SHRT_MAX, &str);
- if (str)
- errx(1, "%s: block size is %s", optarg, str);
+ case 'o':
+ if (!slot_new(optarg, SIO_REC,
+ &par, hdr, cmin, cmax, rate, dup, 0))
+ return 1;
+ mode |= SIO_REC;
break;
- case 'f':
- mkdev(optarg, 0, bufsz, round, hold, autovol);
+ case 'q':
+ port = optarg;
break;
- case 'n':
- mkdev("loopback", MODE_LOOP, bufsz, round, 1, autovol);
+ case 'r':
+ if (!opt_num(optarg, RATE_MIN, RATE_MAX, &rate))
+ return 1;
break;
- case 'M':
- mkdev("midithru", MODE_THRU, 0, 0, hold, 0);
+ case 'v':
+ if (!opt_num(optarg, 0, MIDI_MAXCTL, &vol))
+ return 1;
break;
default:
- fputs(aucat_usage, stderr);
- exit(1);
+ goto bad_usage;
}
}
argc -= optind;
argv += optind;
- if (argc > 0) {
- fputs(aucat_usage, stderr);
- exit(1);
- }
- if (wav_list) {
- if ((d = dev_list) && d->next)
- errx(1, "only one device allowed");
- if ((d->reqmode & MODE_THRU) && d->ctl_list == NULL) {
- if (!devctl_add(d, "default", MODE_MIDIMASK))
- errx(1, "%s: can't open port", optarg);
- d->reqmode |= MODE_MIDIMASK;
- }
- } else {
- fputs(aucat_usage, stderr);
- exit(1);
- }
- for (w = wav_list; w != NULL; w = w->next) {
- if (!wav_init(w))
- exit(1);
- }
- for (d = dev_list; d != NULL; d = d->next) {
- if (!dev_init(d))
- exit(1);
- if (d->autostart && (d->mode & MODE_AUDIOMASK))
- dev_mmcstart(d);
+ if (argc != 0) {
+ bad_usage:
+ log_puts(usagestr);
+ return 1;
}
-
- /*
- * Loop, start audio.
- */
- for (;;) {
- if (quit_flag)
- break;
- active = 0;
- for (d = dev_list; d != NULL; d = dnext) {
- dnext = d->next;
- if (!dev_run(d))
- goto fatal;
- if (d->refcnt > 0)
- active = 1;
+ if (n_flag) {
+ if (dev != NULL || port != NULL) {
+ log_puts("-f and -q make no sense in off-line mode\n");
+ return 1;
}
- if (dev_list == NULL)
- break;
- if (!active)
- break;
- if (!file_poll())
- break;
+ if (mode != (SIO_PLAY | SIO_REC)) {
+ log_puts("both -i and -o required\n");
+ return 0;
+ }
+ if (!offline())
+ return 1;
+ } else {
+ if (dev == NULL)
+ dev = SIO_DEVANY;
+ if (mode == 0) {
+ log_puts("at least of -i and -o required\n");
+ return 1;
+ }
+ if (!playrec(dev, mode, bufsz, port))
+ return 1;
}
- fatal:
-
- /*
- * give a chance to drain
- */
- for (d = dev_list; d != NULL; d = d->next)
- dev_drain(d);
- while (file_poll())
- ; /* nothing */
-
- while (dev_list)
- dev_del(dev_list);
- filelist_done();
- unsetsig();
return 0;
}
+++ /dev/null
-/* $OpenBSD: conf.h,v 1.23 2011/10/12 07:20:04 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 CONF_H
-#define CONF_H
-
-#ifdef DEBUG
-#include <signal.h>
-
-/*
- * Debug trace levels:
- *
- * 0 - fatal errors: bugs, asserts, internal errors.
- * 1 - warnings: bugs in clients, failed allocations, non-fatal errors.
- * 2 - misc information (hardware parameters, incoming clients)
- * 3 - structural changes (new aproc structures and files stream params changes)
- * 4 - data blocks and messages
- */
-extern volatile sig_atomic_t debug_level;
-#endif
-
-/*
- * MIDI buffer size
- */
-#define MIDI_BUFSZ 3125 /* 1 second at 31.25kbit/s */
-
-/*
- * units used for MTC clock.
- */
-#define MTC_SEC 2400 /* 1 second is 2400 ticks */
-
-/*
- * device or sub-device mode, must be a superset of corresponding SIO_XXX
- * and MIO_XXX constants
- */
-#define MODE_PLAY 0x01 /* allowed to play */
-#define MODE_REC 0x02 /* allowed to rec */
-#define MODE_MIDIOUT 0x04 /* allowed to read midi */
-#define MODE_MIDIIN 0x08 /* allowed to write midi */
-#define MODE_MON 0x10 /* allowed to monitor */
-#define MODE_LOOP 0x20 /* deviceless mode */
-#define MODE_THRU 0x40 /* pass thru insted of device control */
-#define MODE_PLAYREC (MODE_PLAY | MODE_REC)
-#define MODE_RECMASK (MODE_REC | MODE_MON)
-#define MODE_AUDIOMASK (MODE_PLAY | MODE_REC | MODE_MON)
-#define MODE_MIDIMASK (MODE_MIDIIN | MODE_MIDIOUT)
-
-/*
- * underrun/overrun policies, must be the same as SIO_XXX
- */
-#define XRUN_IGNORE 0 /* on xrun silently insert/discard samples */
-#define XRUN_SYNC 1 /* catchup to sync to the mix/sub */
-#define XRUN_ERROR 2 /* xruns are errors, eof/hup buffer */
-
-#endif /* !defined(CONF_H) */
+++ /dev/null
-#ifdef DEBUG
-/*
- * Copyright (c) 2003-2007 Alexandre Ratchov <alex@caoua.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-/*
- * dbg_xxx() routines are used to quickly store traces into a trace buffer.
- * This allows trances to be collected during time sensitive operations without
- * disturbing them. The buffer can be flushed on standard error later, when
- * slow syscalls are no longer disruptive, e.g. at the end of the poll() loop.
- */
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include "dbg.h"
-
-/*
- * size of the buffer where traces are stored
- */
-#define DBG_BUFSZ 8192
-
-/*
- * store a character in the trace buffer
- */
-#define DBG_PUTC(c) do { \
- if (dbg_used < DBG_BUFSZ) \
- dbg_buf[dbg_used++] = (c); \
-} while (0)
-
-char dbg_buf[DBG_BUFSZ]; /* buffer where traces are stored */
-unsigned int dbg_used = 0; /* bytes used in the buffer */
-unsigned int dbg_sync = 1; /* if true, flush after each '\n' */
-
-/*
- * write debug info buffer on stderr
- */
-void
-dbg_flush(void)
-{
- if (dbg_used == 0)
- return;
- write(STDERR_FILENO, dbg_buf, dbg_used);
- dbg_used = 0;
-}
-
-/*
- * store a string in the debug buffer
- */
-void
-dbg_puts(char *msg)
-{
- char *p = msg;
- int c;
-
- while ((c = *p++) != '\0') {
- DBG_PUTC(c);
- if (dbg_sync && c == '\n')
- dbg_flush();
- }
-}
-
-/*
- * store a hex in the debug buffer
- */
-void
-dbg_putx(unsigned long num)
-{
- char dig[sizeof(num) * 2], *p = dig, c;
- unsigned int ndig;
-
- if (num != 0) {
- for (ndig = 0; num != 0; ndig++) {
- *p++ = num & 0xf;
- num >>= 4;
- }
- for (; ndig != 0; ndig--) {
- c = *(--p);
- c += (c < 10) ? '0' : 'a' - 10;
- DBG_PUTC(c);
- }
- } else
- DBG_PUTC('0');
-}
-
-/*
- * store a decimal in the debug buffer
- */
-void
-dbg_putu(unsigned long num)
-{
- char dig[sizeof(num) * 3], *p = dig;
- unsigned int ndig;
-
- if (num != 0) {
- for (ndig = 0; num != 0; ndig++) {
- *p++ = num % 10;
- num /= 10;
- }
- for (; ndig != 0; ndig--)
- DBG_PUTC(*(--p) + '0');
- } else
- DBG_PUTC('0');
-}
-
-/*
- * store a signed integer in the trace buffer
- */
-void
-dbg_puti(long num)
-{
- if (num < 0) {
- DBG_PUTC('-');
- num = -num;
- }
- dbg_putu(num);
-}
-
-/*
- * abort program execution after a fatal error, we should
- * put code here to backup user data
- */
-void
-dbg_panic(void)
-{
- dbg_flush();
- abort();
-}
-#endif
+++ /dev/null
-#ifdef DEBUG
-/*
- * Copyright (c) 2003-2007 Alexandre Ratchov <alex@caoua.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MIDISH_DBG_H
-#define MIDISH_DBG_H
-
-void dbg_puts(char *);
-void dbg_putx(unsigned long);
-void dbg_putu(unsigned long);
-void dbg_puti(long);
-void dbg_panic(void);
-void dbg_flush(void);
-
-extern unsigned int dbg_sync;
-
-#endif /* MIDISH_DBG_H */
-#endif /* DEBUG */
--- /dev/null
+/* $OpenBSD: defs.h,v 1.1 2015/01/21 08:43:55 ratchov Exp $ */
+/*
+ * Copyright (c) 2008-2012 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 DEFS_H
+#define DEFS_H
+
+/*
+ * units used for MTC clock.
+ */
+#define MTC_SEC 2400 /* 1 second is 2400 ticks */
+
+/*
+ * limits
+ */
+#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 */
+
+#endif /* !defined(DEFS_H) */
+++ /dev/null
-/* $OpenBSD: dev.c,v 1.85 2014/08/10 10:25:35 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.
- */
-/*
- * Device abstraction module
- *
- * This module exposes a ``enhanced device'' that uses aproc
- * structures framework; it does conversions on the fly and can
- * handle multiple streams. The enhanced device starts and stops
- * automatically, when streams are attached, and provides
- * primitives for MIDI control
- *
- * From the main loop, the device is used as follows:
- *
- * 1. create the device using dev_new_xxx()
- * 2. call dev_run() in the event loop
- * 3. destroy the device using dev_del()
- * 4. continue running the event loop to drain
- *
- * The device is used as follows from aproc context:
- *
- * 1. open the device with dev_ref()
- * 2. negociate parameters (mode, rate, ...)
- * 3. create your stream (ie allocate and fill abufs)
- * 4. attach your stream atomically:
- * - first call dev_wakeup() to ensure device is not suspended
- * - possibly fetch dynamic parameters (eg. dev_getpos())
- * - attach your buffers with dev_attach()
- * 5. close your stream, ie abuf_eof() or abuf_hup()
- * 6. close the device with dev_unref()
- *
- * The device has the following states:
- *
- * CLOSED sio_open() is not called, it's not ready and
- * no streams can be attached; dev_ref() must
- * be called to open the device
- *
- * INIT device is opened, processing chain is ready, but
- * DMA is not started yet. Streams can attach,
- * in which case device will automatically switch
- * to the START state
- *
- * START at least one stream is attached, play buffers
- * are primed (if necessary) DMA is ready and
- * will start immeadiately (next cycle)
- *
- * RUN DMA is started. New streams can attach. If the
- * device is idle (all streams are closed and
- * finished draining), then the device
- * automatically switches to INIT or CLOSED
- */
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "abuf.h"
-#include "aproc.h"
-#include "conf.h"
-#include "dev.h"
-#include "pipe.h"
-#include "miofile.h"
-#include "siofile.h"
-#include "midi.h"
-#ifdef DEBUG
-#include "dbg.h"
-#endif
-
-int dev_open(struct dev *);
-void dev_close(struct dev *);
-void dev_start(struct dev *);
-void dev_stop(struct dev *);
-void dev_clear(struct dev *);
-void dev_onmove(void *, int);
-int devctl_open(struct dev *, struct devctl *);
-int dev_getep(struct dev *, unsigned int, struct abuf **, struct abuf **);
-void dev_sync(struct dev *, unsigned int, struct abuf *, struct abuf *);
-int dev_mkslot(struct dev *, char *);
-int dev_try(struct dev *, int);
-
-
-struct dev *dev_list = NULL;
-unsigned int dev_sndnum = 0, dev_thrnum = 0;
-
-#ifdef DEBUG
-void
-dev_dbg(struct dev *d)
-{
- if (d->num >= DEV_NMAX) {
- dbg_puts("thr");
- dbg_putu(d->num - DEV_NMAX);
- } else {
- dbg_puts("snd");
- dbg_putu(d->num);
- }
-}
-#endif
-
-/*
- * Create a sndio device
- */
-struct dev *
-dev_new(char *path, unsigned int mode, unsigned int bufsz, unsigned int round,
- unsigned int hold, unsigned int autovol)
-{
- struct dev *d;
- unsigned int *pnum, i;
-
- pnum = (mode & MODE_THRU) ? &dev_thrnum : &dev_sndnum;
- if (*pnum == DEV_NMAX) {
-#ifdef DEBUG
- if (debug_level >= 1)
- dbg_puts("too many devices\n");
-#endif
- return NULL;
- }
- d = malloc(sizeof(struct dev));
- if (d == NULL) {
- perror("malloc");
- exit(1);
- }
- d->num = (*pnum)++;
- if (mode & MODE_THRU)
- d->num += DEV_NMAX;
- d->ctl_list = NULL;
- d->path = path;
- d->reqmode = mode;
- aparams_init(&d->reqopar, NCHAN_MAX, 0, 0);
- aparams_init(&d->reqipar, NCHAN_MAX, 0, 0);
- d->reqbufsz = bufsz;
- d->reqround = round;
- d->hold = hold;
- d->autovol = autovol;
- d->autostart = 0;
- d->refcnt = 0;
- d->pstate = DEV_CLOSED;
- d->serial = 0;
- for (i = 0; i < CTL_NSLOT; i++) {
- d->slot[i].unit = i;
- d->slot[i].ops = NULL;
- d->slot[i].vol = MIDI_MAXCTL;
- d->slot[i].tstate = CTL_OFF;
- d->slot[i].serial = d->serial++;
- d->slot[i].name[0] = '\0';
- }
- d->master = MIDI_MAXCTL;
- d->origin = 0;
- d->tstate = CTL_STOP;
- d->next = dev_list;
- dev_list = d;
- return d;
-}
-
-/*
- * adjust device parameters and mode
- */
-void
-dev_adjpar(struct dev *d, unsigned int mode,
- struct aparams *ipar, struct aparams *opar)
-{
- d->reqmode |= (mode | MODE_MIDIMASK);
- if (mode & MODE_REC)
- aparams_grow(&d->reqipar, ipar);
- if (mode & MODE_PLAY)
- aparams_grow(&d->reqopar, opar);
-}
-
-/*
- * Initialize the device with the current parameters
- */
-int
-dev_init(struct dev *d)
-{
- if ((d->reqmode & (MODE_AUDIOMASK | MODE_MIDIMASK)) == 0) {
-#ifdef DEBUG
- dev_dbg(d);
- dbg_puts(": has no streams, skipped\n");
-#endif
- return 1;
- }
- if (d->hold && d->pstate == DEV_CLOSED && !dev_open(d)) {
- dev_del(d);
- return 0;
- }
- return 1;
-}
-
-/*
- * Add a MIDI port to the device
- */
-int
-devctl_add(struct dev *d, char *path, unsigned int mode)
-{
- struct devctl *c;
-
- c = malloc(sizeof(struct devctl));
- if (c == NULL) {
- perror("malloc");
- exit(1);
- }
- c->path = path;
- c->mode = mode;
- c->next = d->ctl_list;
- d->ctl_list = c;
- if (d->pstate != DEV_CLOSED && !devctl_open(d, c))
- return 0;
- return 1;
-}
-
-/*
- * Open a MIDI device and connect it to the thru box
- */
-int
-devctl_open(struct dev *d, struct devctl *c)
-{
- struct file *f;
- struct abuf *rbuf = NULL, *wbuf = NULL;
- struct aproc *rproc, *wproc;
-
- f = (struct file *)miofile_new(&miofile_ops, c->path, c->mode);
- if (f == NULL)
- return 0;
- if (c->mode & MODE_MIDIIN) {
- rproc = rfile_new(f);
- rbuf = abuf_new(MIDI_BUFSZ, &aparams_none);
- aproc_setout(rproc, rbuf);
- }
- if (c->mode & MODE_MIDIOUT) {
- wproc = wfile_new(f);
- wbuf = abuf_new(MIDI_BUFSZ, &aparams_none);
- aproc_setin(wproc, wbuf);
- }
- dev_midiattach(d, rbuf, wbuf);
- return 1;
-}
-
-/*
- * Open the device with the dev_reqxxx capabilities. Setup a mixer, demuxer,
- * monitor, midi control, and any necessary conversions.
- */
-int
-dev_open(struct dev *d)
-{
- struct file *f;
- struct devctl *c;
- struct aparams par;
- struct aproc *conv;
- struct abuf *buf;
- unsigned int siomode, cmin, cmax, rate;
-
- d->mode = d->reqmode;
- d->round = d->reqround;
- d->bufsz = d->reqbufsz;
- d->ipar = d->reqipar;
- d->opar = d->reqopar;
- d->rec = NULL;
- d->play = NULL;
- d->mon = NULL;
- d->mix = NULL;
- d->sub = NULL;
- d->submon = NULL;
- d->midi = NULL;
- d->rate = 0;
-
- if (d->opar.cmin > d->opar.cmax) {
- d->opar.cmin = 0;
- d->opar.cmax = 1;
- }
- if (d->ipar.cmin > d->ipar.cmax) {
- d->ipar.cmin = 0;
- d->ipar.cmax = 1;
- }
- if (d->opar.rate > d->ipar.rate)
- d->ipar.rate = d->opar.rate;
- else
- d->opar.rate = d->ipar.rate;
- if (d->opar.rate == 0)
- d->opar.rate = d->ipar.rate = 48000; /* XXX */
-
- if (d->mode & MODE_THRU)
- d->mode &= ~MODE_AUDIOMASK;
-
- /*
- * If needed, open the device (ie create dev_rec and dev_play)
- */
- if ((d->mode & (MODE_PLAY | MODE_REC)) && !(d->mode & MODE_LOOP)) {
- siomode = d->mode & (MODE_PLAY | MODE_REC);
- f = (struct file *)siofile_new(&siofile_ops,
- d->path,
- &siomode,
- &d->ipar,
- &d->opar,
- &d->bufsz,
- &d->round);
- if (f == NULL) {
-#ifdef DEBUG
- if (debug_level >= 1) {
- dev_dbg(d);
- dbg_puts(": ");
- dbg_puts(d->path);
- dbg_puts(": failed to open audio device\n");
- }
-#endif
- return 0;
- }
- if (!(siomode & MODE_PLAY))
- d->mode &= ~(MODE_PLAY | MODE_MON);
- if (!(siomode & MODE_REC))
- d->mode &= ~MODE_REC;
- if ((d->mode & (MODE_PLAY | MODE_REC)) == 0) {
-#ifdef DEBUG
- if (debug_level >= 1) {
- dev_dbg(d);
- dbg_puts(": mode not supported by device\n");
- }
-#endif
- return 0;
- }
- d->rate = d->mode & MODE_REC ? d->ipar.rate : d->opar.rate;
- if (d->mode & MODE_REC) {
- d->rec = rsio_new(f);
- d->rec->refs++;
- }
- if (d->mode & MODE_PLAY) {
- d->play = wsio_new(f);
- d->play->refs++;
- }
- }
- if (d->mode & MODE_LOOP) {
- if (d->mode & MODE_MON) {
-#ifdef DEBUG
- if (debug_level >= 1) {
- dbg_puts("monitoring not allowed "
- "in loopback mode\n");
- }
-#endif
- return 0;
- }
- if ((d->mode & MODE_PLAYREC) != MODE_PLAYREC) {
-#ifdef DEBUG
- if (debug_level >= 1) {
- dbg_puts("both play and record streams "
- "required in loopback mode\n");
- }
-#endif
- return 0;
- }
- if (d->ctl_list) {
-#ifdef DEBUG
- if (debug_level >= 1) {
- dbg_puts("MIDI control not allowed "
- "in loopback mode\n");
- }
-#endif
- return 0;
- }
- cmin = (d->ipar.cmin < d->opar.cmin) ?
- d->ipar.cmin : d->opar.cmin;
- cmax = (d->ipar.cmax > d->opar.cmax) ?
- d->ipar.cmax : d->opar.cmax;
- rate = (d->ipar.rate > d->opar.rate) ?
- d->ipar.rate : d->opar.rate;
- aparams_init(&par, cmin, cmax, rate);
- d->ipar = par;
- d->opar = par;
- d->rate = rate;
- /*
- * block sizes in the resampling code are limited to
- * 2^15, so use 1/15 of the rate, since all standard
- * sample rates are multiple of 15
- */
- d->round = rate / 15;
- d->bufsz = 2 * d->round;
- }
-#ifdef DEBUG
- if (debug_level >= 2) {
- if (d->mode & MODE_REC) {
- dev_dbg(d);
- dbg_puts(": recording ");
- aparams_dbg(&d->ipar);
- dbg_puts("\n");
- }
- if (d->mode & MODE_PLAY) {
- dev_dbg(d);
- dbg_puts(": playing ");
- aparams_dbg(&d->opar);
- dbg_puts("\n");
- }
- }
-#endif
- /*
- * Create the midi control end, or a simple thru box
- * if there's no device
- */
- if (d->mode & MODE_MIDIMASK) {
- d->midi = midi_new("midi", (d->mode & MODE_THRU) ? NULL : d);
- d->midi->refs++;
- }
-
- /*
- * Create mixer, demuxer and monitor
- */
- if (d->mode & MODE_PLAY) {
- d->mix = mix_new("play", d->bufsz, d->round,
- d->autovol, MIDI_TO_ADATA(d->master));
- d->mix->refs++;
- }
- if (d->mode & MODE_REC) {
- d->sub = sub_new("rec", d->bufsz, d->round);
- d->sub->refs++;
- }
- if (d->mode & MODE_LOOP) {
- /*
- * connect mixer out to demuxer in
- */
- buf = abuf_new(d->bufsz, &d->opar);
- aproc_setout(d->mix, buf);
- aproc_setin(d->sub, buf);
-
- d->mix->flags |= APROC_QUIT;
- d->sub->flags |= APROC_QUIT;
- }
- if (d->rec) {
- aparams_init(&par, d->ipar.cmin, d->ipar.cmax, d->rate);
-
- /*
- * Create device <-> demuxer buffer
- */
- buf = abuf_new(d->bufsz, &d->ipar);
- aproc_setout(d->rec, buf);
-
- /*
- * Insert a converter, if needed.
- */
- if (!aparams_eqenc(&d->ipar, &par)) {
- conv = dec_new("rec", &d->ipar);
- aproc_setin(conv, buf);
- buf = abuf_new(d->round, &par);
- aproc_setout(conv, buf);
- }
- d->ipar = par;
- aproc_setin(d->sub, buf);
- }
- if (d->play) {
- aparams_init(&par, d->opar.cmin, d->opar.cmax, d->rate);
-
- /*
- * Create device <-> mixer buffer
- */
- buf = abuf_new(d->bufsz, &d->opar);
- aproc_setin(d->play, buf);
-
- /*
- * Append a converter, if needed.
- */
- if (!aparams_eqenc(&par, &d->opar)) {
- conv = enc_new("play", &d->opar);
- aproc_setout(conv, buf);
- buf = abuf_new(d->round, &par);
- aproc_setin(conv, buf);
- }
- d->opar = par;
- aproc_setout(d->mix, buf);
- }
- if (d->mode & MODE_MON) {
- d->mon = mon_new("mon", d->bufsz);
- d->mon->refs++;
- buf = abuf_new(d->bufsz, &d->opar);
- aproc_setout(d->mon, buf);
-
- /*
- * Append a "sub" to which clients will connect.
- */
- d->submon = sub_new("mon", d->bufsz, d->round);
- d->submon->refs++;
- aproc_setin(d->submon, buf);
-
- /*
- * Attach to the mixer
- */
- d->mix->u.mix.mon = d->mon;
- d->mon->refs++;
- }
-#ifdef DEBUG
- if (debug_level >= 2) {
- if (d->mode & (MODE_PLAY | MODE_RECMASK)) {
- dev_dbg(d);
- dbg_puts(": block size is ");
- dbg_putu(d->round);
- dbg_puts(" frames, using ");
- dbg_putu(d->bufsz / d->round);
- dbg_puts(" blocks\n");
- }
- }
-#endif
- d->pstate = DEV_INIT;
- for (c = d->ctl_list; c != NULL; c = c->next) {
- if (!devctl_open(d, c)) {
-#ifdef DEBUG
- if (debug_level >= 1) {
- dbg_puts(c->path);
- dbg_puts(": couldn't open MIDI port\n");
- }
-#endif
- dev_close(d);
- return 0;
- }
- }
- return 1;
-}
-
-/*
- * Cleanly stop and drain everything and close the device
- * once both play chain and record chain are gone.
- */
-void
-dev_close(struct dev *d)
-{
- struct file *f;
-
- /*
- * if the device is starting, ensure it actually starts
- * so buffers are drained, else clear any buffers
- */
- switch (d->pstate) {
- case DEV_START:
-#ifdef DEBUG
- if (debug_level >= 3) {
- dev_dbg(d);
- dbg_puts(": draining device\n");
- }
-#endif
- dev_start(d);
- break;
- case DEV_INIT:
-#ifdef DEBUG
- if (debug_level >= 3) {
- dev_dbg(d);
- dbg_puts(": flushing device\n");
- }
-#endif
- dev_clear(d);
- break;
- }
-#ifdef DEBUG
- if (debug_level >= 2) {
- dev_dbg(d);
- dbg_puts(": closing device\n");
- }
-#endif
- d->pstate = DEV_CLOSED;
- if (d->mix) {
- /*
- * Put the mixer in ``autoquit'' state and generate
- * EOF on all inputs connected it. Once buffers are
- * drained the mixer will terminate and shutdown 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().
- */
- if (APROC_OK(d->mix))
- mix_quit(d->mix);
-
- /*
- * XXX: handle this in mix_done()
- */
- if (APROC_OK(d->mix->u.mix.mon)) {
- d->mix->u.mix.mon->refs--;
- aproc_del(d->mix->u.mix.mon);
- d->mix->u.mix.mon = NULL;
- }
- restart_mix:
- LIST_FOREACH(f, &file_list, entry) {
- if (f->rproc != NULL &&
- aproc_depend(d->mix, f->rproc)) {
- file_eof(f);
- goto restart_mix;
- }
- }
- } else if (d->sub) {
- /*
- * Same as above, but since there's no mixer,
- * we generate EOF on the record-end of the
- * device.
- */
- restart_sub:
- LIST_FOREACH(f, &file_list, entry) {
- if (f->rproc != NULL &&
- aproc_depend(d->sub, f->rproc)) {
- file_eof(f);
- goto restart_sub;
- }
- }
- } else if (d->submon) {
- /*
- * Same as above
- */
- restart_submon:
- LIST_FOREACH(f, &file_list, entry) {
- if (f->rproc != NULL &&
- aproc_depend(d->submon, f->rproc)) {
- file_eof(f);
- goto restart_submon;
- }
- }
- }
- if (d->midi) {
- d->midi->flags |= APROC_QUIT;
- if (LIST_EMPTY(&d->midi->ins))
- aproc_del(d->midi);
- restart_midi:
- LIST_FOREACH(f, &file_list, entry) {
- if (f->rproc &&
- aproc_depend(d->midi, f->rproc)) {
- file_eof(f);
- goto restart_midi;
- }
- }
- }
- if (d->mix) {
- if (--d->mix->refs == 0 && (d->mix->flags & APROC_ZOMB))
- aproc_del(d->mix);
- d->mix = NULL;
- }
- if (d->play) {
- if (--d->play->refs == 0 && (d->play->flags & APROC_ZOMB))
- aproc_del(d->play);
- d->play = NULL;
- }
- if (d->sub) {
- if (--d->sub->refs == 0 && (d->sub->flags & APROC_ZOMB))
- aproc_del(d->sub);
- d->sub = NULL;
- }
- if (d->rec) {
- if (--d->rec->refs == 0 && (d->rec->flags & APROC_ZOMB))
- aproc_del(d->rec);
- d->rec = NULL;
- }
- if (d->submon) {
- if (--d->submon->refs == 0 && (d->submon->flags & APROC_ZOMB))
- aproc_del(d->submon);
- d->submon = NULL;
- }
- if (d->mon) {
- if (--d->mon->refs == 0 && (d->mon->flags & APROC_ZOMB))
- aproc_del(d->mon);
- d->mon = NULL;
- }
- if (d->midi) {
- if (--d->midi->refs == 0 && (d->midi->flags & APROC_ZOMB))
- aproc_del(d->midi);
- d->midi = NULL;
- }
-}
-
-/*
- * Unless the device is already in process of closing, request it to close
- */
-void
-dev_drain(struct dev *d)
-{
- unsigned int i;
- struct ctl_slot *s;
-
- for (i = 0, s = d->slot; i < CTL_NSLOT; i++, s++) {
- if (s->ops)
- s->ops->quit(s->arg);
- }
- if (d->pstate != DEV_CLOSED)
- dev_close(d);
-}
-
-/*
- * Free the device
- */
-void
-dev_del(struct dev *d)
-{
- struct dev **p;
-
- dev_drain(d);
- for (p = &dev_list; *p != d; p = &(*p)->next) {
-#ifdef DEBUG
- if (*p == NULL) {
- dbg_puts("device to delete not on the list\n");
- dbg_panic();
- }
-#endif
- }
- *p = d->next;
- free(d);
-}
-
-/*
- * Attach a bi-directional MIDI stream to the MIDI device
- */
-void
-dev_midiattach(struct dev *d, struct abuf *ibuf, struct abuf *obuf)
-{
- if (ibuf)
- aproc_setin(d->midi, ibuf);
- if (obuf) {
- aproc_setout(d->midi, obuf);
- if (ibuf) {
- ibuf->duplex = obuf;
- obuf->duplex = ibuf;
- }
- }
-}
-
-unsigned int
-dev_roundof(struct dev *d, unsigned int newrate)
-{
- return (d->round * newrate + d->rate / 2) / d->rate;
-}
-
-/*
- * Start the (paused) device. By default it's paused.
- */
-void
-dev_start(struct dev *d)
-{
- struct file *f;
-
-#ifdef DEBUG
- if (debug_level >= 2)
- dbg_puts("starting device\n");
-#endif
- d->pstate = DEV_RUN;
- if (d->mode & MODE_LOOP)
- return;
- if (APROC_OK(d->mix))
- d->mix->flags |= APROC_DROP;
- if (APROC_OK(d->sub))
- d->sub->flags |= APROC_DROP;
- if (APROC_OK(d->submon))
- d->submon->flags |= APROC_DROP;
- if (APROC_OK(d->play) && d->play->u.io.file) {
- f = d->play->u.io.file;
- f->ops->start(f, dev_onmove, d);
- } else if (APROC_OK(d->rec) && d->rec->u.io.file) {
- f = d->rec->u.io.file;
- f->ops->start(f, dev_onmove, d);
- }
-}
-
-/*
- * Pause the device. This may trigger context switches,
- * so it shouldn't be called from aproc methods
- */
-void
-dev_stop(struct dev *d)
-{
- struct file *f;
-
-#ifdef DEBUG
- if (debug_level >= 2) {
- dev_dbg(d);
- dbg_puts(": device stopped\n");
- }
-#endif
- d->pstate = DEV_INIT;
- if (d->mode & MODE_LOOP)
- return;
- if (APROC_OK(d->play) && d->play->u.io.file) {
- f = d->play->u.io.file;
- f->ops->stop(f);
- } else if (APROC_OK(d->rec) && d->rec->u.io.file) {
- f = d->rec->u.io.file;
- f->ops->stop(f);
- }
- if (APROC_OK(d->mix))
- d->mix->flags &= ~APROC_DROP;
- if (APROC_OK(d->sub))
- d->sub->flags &= ~APROC_DROP;
- if (APROC_OK(d->submon))
- d->submon->flags &= ~APROC_DROP;
-}
-
-int
-dev_ref(struct dev *d)
-{
-#ifdef DEBUG
- if (debug_level >= 3) {
- dev_dbg(d);
- dbg_puts(": device requested\n");
- }
-#endif
- if (d->pstate == DEV_CLOSED && !dev_open(d)) {
- if (d->hold)
- dev_del(d);
- return 0;
- }
- d->refcnt++;
- return 1;
-}
-
-void
-dev_unref(struct dev *d)
-{
-#ifdef DEBUG
- if (debug_level >= 3) {
- dev_dbg(d);
- dbg_puts(": device released\n");
- }
-#endif
- d->refcnt--;
- if (d->refcnt == 0 && d->pstate == DEV_INIT && !d->hold)
- dev_close(d);
-}
-
-/*
- * There are actions (like start/stop/close ... ) that may trigger aproc
- * operations, a thus cannot be started from aproc context.
- * To avoid problems, aprocs only change the s!tate of the device,
- * and actual operations are triggered from the main loop,
- * outside the aproc code path.
- *
- * The following routine invokes pending actions, returns 0
- * on fatal error
- */
-int
-dev_run(struct dev *d)
-{
- if (d->pstate == DEV_CLOSED)
- return 1;
- /*
- * check if device isn't gone
- */
- if (((d->mode & MODE_PLAY) && !APROC_OK(d->mix)) ||
- ((d->mode & MODE_REC) && !APROC_OK(d->sub)) ||
- ((d->mode & MODE_MON) && !APROC_OK(d->submon))) {
-#ifdef DEBUG
- if (debug_level >= 2) {
- dev_dbg(d);
- dbg_puts(": device disappeared\n");
- }
-#endif
- if (d->hold) {
- dev_del(d);
- return 0;
- }
- dev_close(d);
- return 1;
- }
- switch (d->pstate) {
- case DEV_INIT:
- /* nothing */
- break;
- case DEV_START:
- dev_start(d);
- /* PASSTHROUGH */
- case DEV_RUN:
- /*
- * if the device is not used, then stop it
- */
- if ((!APROC_OK(d->mix) ||
- d->mix->u.mix.idle > 2 * d->bufsz) &&
- (!APROC_OK(d->sub) ||
- d->sub->u.sub.idle > 2 * d->bufsz) &&
- (!APROC_OK(d->submon) ||
- d->submon->u.sub.idle > 2 * d->bufsz) &&
- (!APROC_OK(d->midi) ||
- d->tstate != CTL_RUN)) {
-#ifdef DEBUG
- if (debug_level >= 3) {
- dev_dbg(d);
- dbg_puts(": device idle, suspending\n");
- }
-#endif
- dev_stop(d);
- if (d->refcnt == 0 && !d->hold)
- dev_close(d);
- else
- dev_clear(d);
- }
- break;
- }
- return 1;
-}
-
-/*
- * If the device is paused, then resume it.
- * This routine can be called from aproc context.
- */
-void
-dev_wakeup(struct dev *d)
-{
- if (d->pstate == DEV_INIT)
- d->pstate = DEV_START;
-}
-
-/*
- * Find the end points connected to the mix/sub.
- */
-int
-dev_getep(struct dev *d,
- unsigned int mode, struct abuf **sibuf, struct abuf **sobuf)
-{
- struct abuf *ibuf, *obuf;
-
- if (mode & MODE_PLAY) {
- if (!APROC_OK(d->mix))
- return 0;
- ibuf = *sibuf;
- for (;;) {
- if (!ibuf || !ibuf->rproc) {
-#ifdef DEBUG
- if (debug_level >= 3) {
- abuf_dbg(*sibuf);
- dbg_puts(": not connected to device\n");
- }
-#endif
- return 0;
- }
- if (ibuf->rproc == d->mix)
- break;
- ibuf = LIST_FIRST(&ibuf->rproc->outs);
- }
- *sibuf = ibuf;
- }
- if (mode & MODE_REC) {
- if (!APROC_OK(d->sub))
- return 0;
- obuf = *sobuf;
- for (;;) {
- if (!obuf || !obuf->wproc) {
-#ifdef DEBUG
- if (debug_level >= 3) {
- abuf_dbg(*sobuf);
- dbg_puts(": not connected to device\n");
- }
-#endif
- return 0;
- }
- if (obuf->wproc == d->sub)
- break;
- obuf = LIST_FIRST(&obuf->wproc->ins);
- }
- *sobuf = obuf;
- }
- if (mode & MODE_MON) {
- if (!APROC_OK(d->submon))
- return 0;
- obuf = *sobuf;
- for (;;) {
- if (!obuf || !obuf->wproc) {
-#ifdef DEBUG
- if (debug_level >= 3) {
- abuf_dbg(*sobuf);
- dbg_puts(": not connected to device\n");
- }
-#endif
- return 0;
- }
- if (obuf->wproc == d->submon)
- break;
- obuf = LIST_FIRST(&obuf->wproc->ins);
- }
- *sobuf = obuf;
- }
- return 1;
-}
-
-/*
- * Sync play buffer to rec buffer (for instance when one of
- * them underruns/overruns).
- */
-void
-dev_sync(struct dev *d, unsigned int mode,
- struct abuf *ibuf, struct abuf *obuf)
-{
- int delta, offs;
- struct abuf *mbuf = NULL;
-
- if (!dev_getep(d, mode, &ibuf, &obuf))
- return;
- /*
- * 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.
- */
- offs = 0;
- delta = 0;
- if (APROC_OK(d->mix)) {
- mbuf = LIST_FIRST(&d->mix->outs);
- offs += mbuf->w.mix.todo;
- delta += d->mix->u.mix.lat;
- }
- if (APROC_OK(d->sub))
- delta += d->sub->u.sub.lat;
-#ifdef DEBUG
- if (debug_level >= 3) {
- dev_dbg(d);
- dbg_puts(": syncing device");
- if (APROC_OK(d->mix)) {
- dbg_puts(", ");
- aproc_dbg(d->mix);
- dbg_puts(": todo = ");
- dbg_putu(mbuf->w.mix.todo);
- dbg_puts(": lat = ");
- dbg_putu(d->mix->u.mix.lat);
- }
- if (APROC_OK(d->sub)) {
- dbg_puts(", ");
- aproc_dbg(d->sub);
- dbg_puts(": lat = ");
- dbg_putu(d->sub->u.sub.lat);
- }
- dbg_puts("\n");
- }
-#endif
- if (mode & MODE_PLAY)
- mix_drop(ibuf, -offs);
- if (mode & MODE_RECMASK)
- sub_silence(obuf, -(offs + delta));
-}
-
-/*
- * return the current latency (in frames), ie the latency that
- * a stream would have if dev_attach() is called on it.
- *
- * XXX: return a "unsigned int", since result is always positive, isn't it?
- */
-int
-dev_getpos(struct dev *d)
-{
- struct abuf *mbuf = NULL;
-
- if (APROC_OK(d->mix)) {
- mbuf = LIST_FIRST(&d->mix->outs);
- return -(mbuf->w.mix.todo + d->mix->u.mix.lat);
- } else
- return 0;
-}
-
-/*
- * Attach the given input and output buffers to the mixer and the
- * multiplexer respectively. The operation is done synchronously, so
- * both buffers enter in sync. If buffers do not match play
- * and rec.
- */
-void
-dev_attach(struct dev *d, char *name, unsigned int mode,
- struct abuf *ibuf, struct aparams *sipar, unsigned int inch,
- struct abuf *obuf, struct aparams *sopar, unsigned int onch,
- unsigned int xrun, int vol)
-{
- struct aparams ipar, opar;
- struct aproc *conv;
- unsigned int round, nblk, nch;
-
-#ifdef DEBUG
- if ((!APROC_OK(d->mix) && (mode & MODE_PLAY)) ||
- (!APROC_OK(d->sub) && (mode & MODE_REC)) ||
- (!APROC_OK(d->submon) && (mode & MODE_MON))) {
- dev_dbg(d);
- dbg_puts(": mode beyond device mode, not attaching\n");
- return;
- }
-#endif
- if (mode & MODE_PLAY) {
- ipar = *sipar;
- nblk = (d->bufsz / d->round + 3) / 4;
- round = dev_roundof(d, ipar.rate);
- nch = ipar.cmax - ipar.cmin + 1;
- if (!aparams_eqenc(&ipar, &d->opar)) {
- conv = dec_new(name, &ipar);
- ipar.bps = d->opar.bps;
- ipar.bits = d->opar.bits;
- ipar.sig = d->opar.sig;
- ipar.le = d->opar.le;
- ipar.msb = d->opar.msb;
- aproc_setin(conv, ibuf);
- ibuf = abuf_new(nblk * round, &ipar);
- aproc_setout(conv, ibuf);
- }
- if (inch > 0 && nch >= inch * 2) {
- conv = join_new(name);
- aproc_setin(conv, ibuf);
- ipar.cmax = ipar.cmin + inch - 1;
- ibuf = abuf_new(nblk * round, &ipar);
- aproc_setout(conv, ibuf);
- }
- if (!aparams_eqrate(&ipar, &d->opar)) {
- conv = resamp_new(name, round, d->round);
- ipar.rate = d->opar.rate;
- round = d->round;
- aproc_setin(conv, ibuf);
- ibuf = abuf_new(nblk * round, &ipar);
- aproc_setout(conv, ibuf);
- }
- if (inch > 0 && nch * 2 <= inch) {
- conv = join_new(name);
- aproc_setin(conv, ibuf);
- ipar.cmax = ipar.cmin + inch - 1;
- ibuf = abuf_new(nblk * round, &ipar);
- aproc_setout(conv, ibuf);
- }
- aproc_setin(d->mix, ibuf);
- ibuf->r.mix.xrun = xrun;
- ibuf->r.mix.maxweight = vol;
- mix_setmaster(d->mix);
- }
- if (mode & MODE_REC) {
- opar = *sopar;
- round = dev_roundof(d, opar.rate);
- nblk = (d->bufsz / d->round + 3) / 4;
- nch = opar.cmax - opar.cmin + 1;
- if (!aparams_eqenc(&opar, &d->ipar)) {
- conv = enc_new(name, &opar);
- opar.bps = d->ipar.bps;
- opar.bits = d->ipar.bits;
- opar.sig = d->ipar.sig;
- opar.le = d->ipar.le;
- opar.msb = d->ipar.msb;
- aproc_setout(conv, obuf);
- obuf = abuf_new(nblk * round, &opar);
- aproc_setin(conv, obuf);
- }
- if (onch > 0 && nch >= onch * 2) {
- conv = join_new(name);
- aproc_setout(conv, obuf);
- opar.cmax = opar.cmin + onch - 1;
- obuf = abuf_new(nblk * round, &opar);
- aproc_setin(conv, obuf);
- }
- if (!aparams_eqrate(&opar, &d->ipar)) {
- conv = resamp_new(name, d->round, round);
- opar.rate = d->ipar.rate;
- round = d->round;
- aproc_setout(conv, obuf);
- obuf = abuf_new(nblk * round, &opar);
- aproc_setin(conv, obuf);
- }
- if (onch > 0 && nch * 2 <= onch) {
- conv = join_new(name);
- aproc_setout(conv, obuf);
- opar.cmax = opar.cmin + onch - 1;
- obuf = abuf_new(nblk * round, &opar);
- aproc_setin(conv, obuf);
- }
- aproc_setout(d->sub, obuf);
- obuf->w.sub.xrun = xrun;
- }
- if (mode & MODE_MON) {
- opar = *sopar;
- round = dev_roundof(d, opar.rate);
- nblk = (d->bufsz / d->round + 3) / 4;
- nch = opar.cmax - opar.cmin + 1;
- if (!aparams_eqenc(&opar, &d->opar)) {
- conv = enc_new(name, &opar);
- opar.bps = d->opar.bps;
- opar.bits = d->opar.bits;
- opar.sig = d->opar.sig;
- opar.le = d->opar.le;
- opar.msb = d->opar.msb;
- aproc_setout(conv, obuf);
- obuf = abuf_new(nblk * round, &opar);
- aproc_setin(conv, obuf);
- }
- if (onch > 0 && nch >= onch * 2) {
- conv = join_new(name);
- aproc_setout(conv, obuf);
- opar.cmax = opar.cmin + onch - 1;
- obuf = abuf_new(nblk * round, &opar);
- aproc_setin(conv, obuf);
- }
- if (!aparams_eqrate(&opar, &d->opar)) {
- conv = resamp_new(name, d->round, round);
- opar.rate = d->opar.rate;
- round = d->round;
- aproc_setout(conv, obuf);
- obuf = abuf_new(nblk * round, &opar);
- aproc_setin(conv, obuf);
- }
- if (onch > 0 && nch * 2 <= onch) {
- conv = join_new(name);
- aproc_setout(conv, obuf);
- opar.cmax = opar.cmin + onch - 1;
- obuf = abuf_new(nblk * round, &opar);
- aproc_setin(conv, obuf);
- }
- aproc_setout(d->submon, obuf);
- obuf->w.sub.xrun = xrun;
- }
-
- /*
- * Sync play to record.
- */
- if ((mode & MODE_PLAY) && (mode & MODE_RECMASK)) {
- ibuf->duplex = obuf;
- obuf->duplex = ibuf;
- }
- dev_sync(d, mode, ibuf, obuf);
-}
-
-/*
- * Change the playback volume of the given stream.
- */
-void
-dev_setvol(struct dev *d, struct abuf *ibuf, int vol)
-{
-#ifdef DEBUG
- if (debug_level >= 3) {
- abuf_dbg(ibuf);
- dbg_puts(": setting volume to ");
- dbg_putu(vol);
- dbg_puts("\n");
- }
-#endif
- if (!dev_getep(d, MODE_PLAY, &ibuf, NULL)) {
- return;
- }
- ibuf->r.mix.vol = vol;
-}
-
-/*
- * Clear buffers of the play and record chains so that when the device
- * is started, playback and record start in sync.
- */
-void
-dev_clear(struct dev *d)
-{
- struct abuf *buf;
-
- if (APROC_OK(d->mix)) {
-#ifdef DEBUG
- if (!LIST_EMPTY(&d->mix->ins)) {
- dev_dbg(d);
- dbg_puts(": play end not idle, can't clear device\n");
- dbg_panic();
- }
-#endif
- buf = LIST_FIRST(&d->mix->outs);
- while (buf) {
- abuf_clear(buf);
- buf = LIST_FIRST(&buf->rproc->outs);
- }
- mix_clear(d->mix);
- }
- if (APROC_OK(d->sub)) {
-#ifdef DEBUG
- if (!LIST_EMPTY(&d->sub->outs)) {
- dev_dbg(d);
- dbg_puts(": record end not idle, can't clear device\n");
- dbg_panic();
- }
-#endif
- buf = LIST_FIRST(&d->sub->ins);
- while (buf) {
- abuf_clear(buf);
- buf = LIST_FIRST(&buf->wproc->ins);
- }
- sub_clear(d->sub);
- }
- if (APROC_OK(d->submon)) {
-#ifdef DEBUG
- if (!LIST_EMPTY(&d->submon->outs)) {
- dev_dbg(d);
- dbg_puts(": monitoring end not idle, can't clear device\n");
- dbg_panic();
- }
-#endif
- buf = LIST_FIRST(&d->submon->ins);
- while (buf) {
- abuf_clear(buf);
- buf = LIST_FIRST(&buf->wproc->ins);
- }
- sub_clear(d->submon);
- mon_clear(d->mon);
- }
-}
-
-#ifdef DEBUG
-void
-dev_slotdbg(struct dev *d, int slot)
-{
- struct ctl_slot *s;
-
- if (slot < 0) {
- dbg_puts("none");
- } else {
- s = d->slot + slot;
- dbg_puts(s->name);
- dbg_putu(s->unit);
- dbg_puts("(");
- dbg_putu(s->vol);
- dbg_puts(")/");
- switch (s->tstate) {
- case CTL_OFF:
- dbg_puts("off");
- break;
- case CTL_RUN:
- dbg_puts("run");
- break;
- case CTL_START:
- dbg_puts("sta");
- break;
- case CTL_STOP:
- dbg_puts("stp");
- break;
- default:
- dbg_puts("unk");
- break;
- }
- }
-}
-#endif
-
-/*
- * find the best matching free slot index (ie midi channel).
- * return -1, if there are no free slots anymore
- */
-int
-dev_mkslot(struct dev *d, char *who)
-{
- char *s;
- struct ctl_slot *slot;
- char name[CTL_NAMEMAX];
- unsigned int i, unit, umap = 0;
- unsigned int ser, bestser, bestidx;
-
- /*
- * create a ``valid'' control name (lowcase, remove [^a-z], trucate)
- */
- for (i = 0, s = who; ; s++) {
- if (i == CTL_NAMEMAX - 1 || *s == '\0') {
- name[i] = '\0';
- break;
- } else if (*s >= 'A' && *s <= 'Z') {
- name[i++] = *s + 'a' - 'A';
- } else if (*s >= 'a' && *s <= 'z')
- name[i++] = *s;
- }
- if (i == 0)
- strlcpy(name, "noname", CTL_NAMEMAX);
-
- /*
- * find the instance number of the control name
- */
- for (i = 0, slot = d->slot; i < CTL_NSLOT; i++, slot++) {
- if (slot->ops == NULL)
- continue;
- if (strcmp(slot->name, name) == 0)
- umap |= (1 << slot->unit);
- }
- for (unit = 0; ; unit++) {
- if (unit == CTL_NSLOT) {
-#ifdef DEBUG
- if (debug_level >= 1) {
- dbg_puts(name);
- dbg_puts(": too many instances\n");
- }
-#endif
- return -1;
- }
- if ((umap & (1 << unit)) == 0)
- break;
- }
-
- /*
- * find a free controller slot with the same name/unit
- */
- for (i = 0, slot = d->slot; i < CTL_NSLOT; i++, slot++) {
- if (slot->ops == NULL &&
- strcmp(slot->name, name) == 0 &&
- slot->unit == unit) {
-#ifdef DEBUG
- if (debug_level >= 3) {
- dbg_puts(name);
- dbg_putu(unit);
- dbg_puts(": found slot ");
- dbg_putu(i);
- dbg_puts("\n");
- }
-#endif
- return i;
- }
- }
-
- /*
- * couldn't find a matching slot, pick oldest free slot
- * and set its name/unit
- */
- bestser = 0;
- bestidx = CTL_NSLOT;
- for (i = 0, slot = d->slot; i < CTL_NSLOT; i++, slot++) {
- if (slot->ops != NULL)
- continue;
- ser = d->serial - slot->serial;
- if (ser > bestser) {
- bestser = ser;
- bestidx = i;
- }
- }
- if (bestidx == CTL_NSLOT) {
-#ifdef DEBUG
- if (debug_level >= 1) {
- dbg_puts(name);
- dbg_putu(unit);
- dbg_puts(": out of mixer slots\n");
- }
-#endif
- return -1;
- }
- slot = d->slot + bestidx;
- if (slot->name[0] != '\0')
- slot->vol = MIDI_MAXCTL;
- strlcpy(slot->name, name, CTL_NAMEMAX);
- slot->serial = d->serial++;
- slot->unit = unit;
-#ifdef DEBUG
- if (debug_level >= 3) {
- dbg_puts(name);
- dbg_putu(unit);
- dbg_puts(": overwritten slot ");
- dbg_putu(bestidx);
- dbg_puts("\n");
- }
-#endif
- return bestidx;
-}
-
-/*
- * allocate a new slot and register the given call-backs
- */
-int
-dev_slotnew(struct dev *d, char *who, struct ctl_ops *ops, void *arg, int mmc)
-{
- int slot;
- struct ctl_slot *s;
-
- slot = dev_mkslot(d, who);
- if (slot < 0)
- return -1;
-
- s = d->slot + slot;
- s->ops = ops;
- s->arg = arg;
- s->tstate = mmc ? CTL_STOP : CTL_OFF;
- s->ops->vol(s->arg, s->vol);
-
- if (APROC_OK(d->midi)) {
- midi_send_slot(d->midi, slot);
- midi_send_vol(d->midi, slot, s->vol);
- midi_flush(d->midi);
- } else {
-#ifdef DEBUG
- if (debug_level >= 2) {
- dev_slotdbg(d, slot);
- dbg_puts(": MIDI control not available\n");
- }
-#endif
- }
- return slot;
-}
-
-/*
- * release the given slot
- */
-void
-dev_slotdel(struct dev *d, int slot)
-{
- struct ctl_slot *s;
-
- s = d->slot + slot;
- s->ops = NULL;
-}
-
-/*
- * notifty the mixer that volume changed, called by whom allocad the slot using
- * ctl_slotnew(). Note: it doesn't make sens to call this from within the
- * call-back.
- *
- * XXX: set actual volume here and use only this interface. Now, this
- * can work because all streams have a slot
- */
-void
-dev_slotvol(struct dev *d, int slot, unsigned int vol)
-{
-#ifdef DEBUG
- if (debug_level >= 3) {
- dev_slotdbg(d, slot);
- dbg_puts(": changing volume to ");
- dbg_putu(vol);
- dbg_puts("\n");
- }
-#endif
- d->slot[slot].vol = vol;
- if (APROC_OK(d->midi)) {
- midi_send_vol(d->midi, slot, vol);
- midi_flush(d->midi);
- }
-}
-
-/*
- * check that all clients controlled by MMC are ready to start,
- * if so, start them all but the caller
- */
-int
-dev_try(struct dev *d, int slot)
-{
- unsigned int i;
- struct ctl_slot *s;
-
- if (d->tstate != CTL_START) {
-#ifdef DEBUG
- if (debug_level >= 3) {
- dev_slotdbg(d, slot);
- dbg_puts(": server not started, delayed\n");
- }
-#endif
- return 0;
- }
- for (i = 0, s = d->slot; i < CTL_NSLOT; i++, s++) {
- if (!s->ops || i == slot)
- continue;
- if (s->tstate != CTL_OFF && s->tstate != CTL_START) {
-#ifdef DEBUG
- if (debug_level >= 3) {
- dev_slotdbg(d, i);
- dbg_puts(": not ready, server delayed\n");
- }
-#endif
- return 0;
- }
- }
- for (i = 0, s = d->slot; i < CTL_NSLOT; i++, s++) {
- if (!s->ops || i == slot)
- continue;
- if (s->tstate == CTL_START) {
-#ifdef DEBUG
- if (debug_level >= 3) {
- dev_slotdbg(d, i);
- dbg_puts(": started\n");
- }
-#endif
- s->tstate = CTL_RUN;
- s->ops->start(s->arg);
- }
- }
- if (slot >= 0)
- d->slot[slot].tstate = CTL_RUN;
- d->tstate = CTL_RUN;
- if (APROC_OK(d->midi)) {
- midi_send_full(d->midi,
- d->origin, d->rate, d->round, dev_getpos(d));
- midi_flush(d->midi);
- }
- dev_wakeup(d);
- return 1;
-}
-
-/*
- * notify the MMC layer that the stream is attempting
- * to start. If other streams are not ready, 0 is returned meaning
- * that the stream should wait. If other streams are ready, they
- * are started, and the caller should start immediately.
- */
-int
-dev_slotstart(struct dev *d, int slot)
-{
- struct ctl_slot *s = d->slot + slot;
-
- if (s->tstate == CTL_OFF || d->tstate == CTL_OFF)
- return 1;
-
- /*
- * if the server already started (the client missed the
- * start rendez-vous) or the server is stopped, then
- * tag the client as ``wanting to start''
- */
- s->tstate = CTL_START;
- return dev_try(d, slot);
-}
-
-/*
- * notify the MMC layer that the stream no longer is trying to
- * start (or that it just stopped), meaning that its ``start'' call-back
- * shouldn't be called anymore
- */
-void
-dev_slotstop(struct dev *d, int slot)
-{
- struct ctl_slot *s = d->slot + slot;
-
- /*
- * tag the stream as not trying to start,
- * unless MMC is turned off
- */
- if (s->tstate != CTL_OFF)
- s->tstate = CTL_STOP;
-}
-
-/*
- * start all slots simultaneously
- */
-void
-dev_mmcstart(struct dev *d)
-{
- if (d->tstate == CTL_STOP) {
- d->tstate = CTL_START;
- (void)dev_try(d, -1);
-#ifdef DEBUG
- } else {
- if (debug_level >= 3) {
- dev_dbg(d);
- dbg_puts(": ignoring mmc start\n");
- }
-#endif
- }
-}
-
-/*
- * stop all slots simultaneously
- */
-void
-dev_mmcstop(struct dev *d)
-{
- unsigned int i;
- struct ctl_slot *s;
-
- switch (d->tstate) {
- case CTL_START:
- d->tstate = CTL_STOP;
- return;
- case CTL_RUN:
- d->tstate = CTL_STOP;
- break;
- default:
-#ifdef DEBUG
- if (debug_level >= 3) {
- dev_dbg(d);
- dbg_puts(": ignored mmc stop\n");
- }
-#endif
- return;
- }
- for (i = 0, s = d->slot; i < CTL_NSLOT; i++, s++) {
- if (!s->ops)
- continue;
- if (s->tstate == CTL_RUN) {
-#ifdef DEBUG
- if (debug_level >= 3) {
- dev_slotdbg(d, i);
- dbg_puts(": requested to stop\n");
- }
-#endif
- s->ops->stop(s->arg);
- }
- }
-}
-
-/*
- * relocate all slots simultaneously
- */
-void
-dev_loc(struct dev *d, unsigned int origin)
-{
- unsigned int i;
- struct ctl_slot *s;
-
-#ifdef DEBUG
- if (debug_level >= 2) {
- dbg_puts("server relocated to ");
- dbg_putu(origin);
- dbg_puts("\n");
- }
-#endif
- if (d->tstate == CTL_RUN)
- dev_mmcstop(d);
- d->origin = origin;
- for (i = 0, s = d->slot; i < CTL_NSLOT; i++, s++) {
- if (!s->ops)
- continue;
- s->ops->loc(s->arg, d->origin);
- }
- if (d->tstate == CTL_RUN)
- dev_mmcstart(d);
-}
-
-/*
- * called at every clock tick by the mixer, delta is positive, unless
- * there's an overrun/underrun
- */
-void
-dev_onmove(void *arg, int delta)
-{
- struct dev *d = (struct dev *)arg;
-
- /*
- * don't send ticks before the start signal
- */
- if (d->tstate != CTL_RUN)
- return;
- if (APROC_OK(d->midi)) {
- midi_send_qfr(d->midi, d->rate, delta);
- midi_flush(d->midi);
- }
-}
-
-void
-dev_master(struct dev *d, unsigned int master)
-{
-#ifdef DEBUG
- if (debug_level >= 3) {
- dev_dbg(d);
- dbg_puts(": changing master volume to ");
- dbg_putu(master);
- dbg_puts("\n");
- }
-#endif
- d->master = master;
- if (APROC_OK(d->mix)) {
- d->mix->u.mix.master = MIDI_TO_ADATA(master);
- mix_setmaster(d->mix);
- }
-}
+++ /dev/null
-/* $OpenBSD: dev.h,v 1.36 2012/04/11 06:05: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 DEV_H
-#define DEV_H
-
-#include "aparams.h"
-
-struct aproc;
-struct abuf;
-
-struct dev {
- struct dev *next;
-
- /*
- * desired parameters
- */
- unsigned int reqmode; /* mode */
- struct aparams reqipar, reqopar; /* parameters */
- unsigned int reqbufsz; /* buffer size */
- unsigned int reqround; /* block size */
- unsigned int hold; /* hold the device open ? */
- unsigned int autovol; /* auto adjust playvol ? */
- unsigned int autostart; /* don't wait for MMC start */
- unsigned int refcnt; /* number of openers */
-#define DEV_NMAX 16 /* max number of devices */
- unsigned int num; /* serial number */
-#define DEV_CLOSED 0 /* closed */
-#define DEV_INIT 1 /* stopped */
-#define DEV_START 2 /* ready to start */
-#define DEV_RUN 3 /* started */
- unsigned int pstate; /* on of DEV_xxx */
- char *path; /* sio path */
-
- /*
- * actual parameters and runtime state (i.e. once opened)
- */
- unsigned int mode; /* bitmap of MODE_xxx */
- unsigned int bufsz, round, rate;
- struct aparams ipar, opar;
- struct aproc *mix, *sub, *submon;
- struct aproc *rec, *play, *mon;
- struct aproc *midi;
- struct devctl {
- struct devctl *next;
- unsigned int mode;
- char *path;
- } *ctl_list;
-
- /* volume control and MMC/MTC */
-#define CTL_NSLOT 8
-#define CTL_NAMEMAX 8
- unsigned int serial;
- struct ctl_slot {
- struct ctl_ops {
- void (*vol)(void *, unsigned int);
- void (*start)(void *);
- void (*stop)(void *);
- void (*loc)(void *, unsigned int);
- void (*quit)(void *);
- } *ops;
- void *arg;
- unsigned int unit;
- char name[CTL_NAMEMAX];
- unsigned int serial;
- unsigned int vol;
- unsigned int tstate;
- } slot[CTL_NSLOT];
-#define CTL_OFF 0 /* ignore MMC messages */
-#define CTL_STOP 1 /* stopped, can't start */
-#define CTL_START 2 /* attempting to start */
-#define CTL_RUN 3 /* started */
- unsigned int tstate; /* one of above */
- unsigned int origin; /* MTC start time */
- unsigned int master; /* master volume controller */
-};
-
-extern struct dev *dev_list;
-
-void dev_dbg(struct dev *);
-int dev_init(struct dev *);
-int dev_run(struct dev *);
-int dev_ref(struct dev *);
-void dev_unref(struct dev *);
-void dev_del(struct dev *);
-void dev_wakeup(struct dev *);
-void dev_drain(struct dev *);
-struct dev *dev_new(char *, unsigned int, unsigned int,
- unsigned int, unsigned int, unsigned int);
-void dev_adjpar(struct dev *, unsigned int,
- struct aparams *, struct aparams *);
-int devctl_add(struct dev *, char *, unsigned int);
-void dev_midiattach(struct dev *, struct abuf *, struct abuf *);
-unsigned int dev_roundof(struct dev *, unsigned int);
-int dev_getpos(struct dev *);
-void dev_attach(struct dev *, char *, unsigned int,
- struct abuf *, struct aparams *, unsigned int,
- struct abuf *, struct aparams *, unsigned int,
- unsigned int, int);
-void dev_setvol(struct dev *, struct abuf *, int);
-
-void dev_slotdbg(struct dev *, int);
-int dev_slotnew(struct dev *, char *, struct ctl_ops *, void *, int);
-void dev_slotdel(struct dev *, int);
-void dev_slotvol(struct dev *, int, unsigned int);
-
-int dev_slotstart(struct dev *, int);
-void dev_slotstop(struct dev *, int);
-void dev_mmcstart(struct dev *);
-void dev_mmcstop(struct dev *);
-void dev_loc(struct dev *, unsigned int);
-void dev_master(struct dev *, unsigned int);
-
-#endif /* !define(DEV_H) */
--- /dev/null
+/* $OpenBSD: dsp.c,v 1.1 2015/01/21 08:43:55 ratchov Exp $ */
+/*
+ * Copyright (c) 2008-2012 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 <string.h>
+#include "dsp.h"
+#include "utils.h"
+
+int aparams_ctltovol[128] = {
+ 0,
+ 256, 266, 276, 287, 299, 310, 323, 335,
+ 348, 362, 376, 391, 406, 422, 439, 456,
+ 474, 493, 512, 532, 553, 575, 597, 621,
+ 645, 670, 697, 724, 753, 782, 813, 845,
+ 878, 912, 948, 985, 1024, 1064, 1106, 1149,
+ 1195, 1241, 1290, 1341, 1393, 1448, 1505, 1564,
+ 1625, 1689, 1756, 1825, 1896, 1971, 2048, 2128,
+ 2212, 2299, 2389, 2483, 2580, 2682, 2787, 2896,
+ 3010, 3128, 3251, 3379, 3511, 3649, 3792, 3941,
+ 4096, 4257, 4424, 4598, 4778, 4966, 5161, 5363,
+ 5574, 5793, 6020, 6256, 6502, 6757, 7023, 7298,
+ 7585, 7883, 8192, 8514, 8848, 9195, 9556, 9931,
+ 10321, 10726, 11148, 11585, 12040, 12513, 13004, 13515,
+ 14045, 14596, 15170, 15765, 16384, 17027, 17696, 18390,
+ 19112, 19863, 20643, 21453, 22295, 23170, 24080, 25025,
+ 26008, 27029, 28090, 29193, 30339, 31530, 32768
+};
+
+short dec_ulawmap[256] = {
+ -32124, -31100, -30076, -29052, -28028, -27004, -25980, -24956,
+ -23932, -22908, -21884, -20860, -19836, -18812, -17788, -16764,
+ -15996, -15484, -14972, -14460, -13948, -13436, -12924, -12412,
+ -11900, -11388, -10876, -10364, -9852, -9340, -8828, -8316,
+ -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140,
+ -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092,
+ -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004,
+ -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980,
+ -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436,
+ -1372, -1308, -1244, -1180, -1116, -1052, -988, -924,
+ -876, -844, -812, -780, -748, -716, -684, -652,
+ -620, -588, -556, -524, -492, -460, -428, -396,
+ -372, -356, -340, -324, -308, -292, -276, -260,
+ -244, -228, -212, -196, -180, -164, -148, -132,
+ -120, -112, -104, -96, -88, -80, -72, -64,
+ -56, -48, -40, -32, -24, -16, -8, 0,
+ 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956,
+ 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764,
+ 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412,
+ 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316,
+ 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140,
+ 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092,
+ 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004,
+ 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980,
+ 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436,
+ 1372, 1308, 1244, 1180, 1116, 1052, 988, 924,
+ 876, 844, 812, 780, 748, 716, 684, 652,
+ 620, 588, 556, 524, 492, 460, 428, 396,
+ 372, 356, 340, 324, 308, 292, 276, 260,
+ 244, 228, 212, 196, 180, 164, 148, 132,
+ 120, 112, 104, 96, 88, 80, 72, 64,
+ 56, 48, 40, 32, 24, 16, 8, 0
+};
+
+short dec_alawmap[256] = {
+ -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736,
+ -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784,
+ -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368,
+ -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392,
+ -22016, -20992, -24064, -23040, -17920, -16896, -19968, -18944,
+ -30208, -29184, -32256, -31232, -26112, -25088, -28160, -27136,
+ -11008, -10496, -12032, -11520, -8960, -8448, -9984, -9472,
+ -15104, -14592, -16128, -15616, -13056, -12544, -14080, -13568,
+ -344, -328, -376, -360, -280, -264, -312, -296,
+ -472, -456, -504, -488, -408, -392, -440, -424,
+ -88, -72, -120, -104, -24, -8, -56, -40,
+ -216, -200, -248, -232, -152, -136, -184, -168,
+ -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184,
+ -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696,
+ -688, -656, -752, -720, -560, -528, -624, -592,
+ -944, -912, -1008, -976, -816, -784, -880, -848,
+ 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736,
+ 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784,
+ 2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368,
+ 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392,
+ 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944,
+ 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136,
+ 11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472,
+ 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568,
+ 344, 328, 376, 360, 280, 264, 312, 296,
+ 472, 456, 504, 488, 408, 392, 440, 424,
+ 88, 72, 120, 104, 24, 8, 56, 40,
+ 216, 200, 248, 232, 152, 136, 184, 168,
+ 1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184,
+ 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696,
+ 688, 656, 752, 720, 560, 528, 624, 592,
+ 944, 912, 1008, 976, 816, 784, 880, 848
+};
+
+/*
+ * 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. Return 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 = ADATA_LE;
+
+ /*
+ * get (optional) 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 (optional) 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 (optional) alignment
+ */
+ 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.
+ */
+void
+aparams_init(struct aparams *par)
+{
+ par->bps = sizeof(adata_t);
+ par->bits = ADATA_BITS;
+ par->le = ADATA_LE;
+ par->sig = 1;
+ par->msb = 0;
+}
+
+/*
+ * log the given format/channels/encoding
+ */
+void
+aparams_log(struct aparams *par)
+{
+ char enc[ENCMAX];
+
+ aparams_enctostr(par, enc);
+ log_puts(enc);
+}
+
+/*
+ * return true if encoding corresponds to what we store in adata_t
+ */
+int
+aparams_native(struct aparams *par)
+{
+ return par->bps == sizeof(adata_t) && par->bits == ADATA_BITS &&
+ (par->bps == 1 || par->le == ADATA_LE) &&
+ (par->bits == par->bps * 8 || !par->msb);
+}
+
+/*
+ * resample the given number of frames
+ */
+int
+resamp_do(struct resamp *p, adata_t *in, adata_t *out, int todo)
+{
+ unsigned int nch;
+ adata_t *idata;
+ unsigned int oblksz;
+ unsigned int ifr;
+ int s, ds, diff;
+ adata_t *odata;
+ unsigned int iblksz;
+ unsigned int ofr;
+ unsigned int c;
+ adata_t *ctxbuf, *ctx;
+ unsigned int ctx_start;
+
+ /*
+ * Partially copy structures into local variables, to avoid
+ * unnecessary indirections; this also allows the compiler to
+ * order local variables more "cache-friendly".
+ */
+ idata = in;
+ odata = out;
+ diff = p->diff;
+ iblksz = p->iblksz;
+ oblksz = p->oblksz;
+ ctxbuf = p->ctx;
+ ctx_start = p->ctx_start;
+ nch = p->nch;
+ ifr = todo;
+ ofr = oblksz;
+
+ /*
+ * Start conversion.
+ */
+#ifdef DEBUG
+ if (log_level >= 4) {
+ log_puts("resamp: copying ");
+ log_puti(todo);
+ log_puts(" frames, diff = ");
+ log_putu(diff);
+ log_puts("\n");
+ }
+#endif
+ for (;;) {
+ if (diff < 0) {
+ if (ifr == 0)
+ break;
+ ctx_start ^= 1;
+ ctx = ctxbuf + ctx_start;
+ for (c = nch; c > 0; c--) {
+ *ctx = *idata++;
+ ctx += RESAMP_NCTX;
+ }
+ diff += oblksz;
+ ifr--;
+ } else if (diff > 0) {
+ if (ofr == 0)
+ break;
+ ctx = ctxbuf;
+ for (c = nch; c > 0; c--) {
+ s = ctx[ctx_start];
+ ds = ctx[ctx_start ^ 1] - s;
+ ctx += RESAMP_NCTX;
+ *odata++ = s + ADATA_MULDIV(ds, diff, oblksz);
+ }
+ diff -= iblksz;
+ ofr--;
+ } else {
+ if (ifr == 0 || ofr == 0)
+ break;
+ ctx = ctxbuf + ctx_start;
+ for (c = nch; c > 0; c--) {
+ *odata++ = *ctx;
+ ctx += RESAMP_NCTX;
+ }
+ ctx_start ^= 1;
+ ctx = ctxbuf + ctx_start;
+ for (c = nch; c > 0; c--) {
+ *ctx = *idata++;
+ ctx += RESAMP_NCTX;
+ }
+ diff -= iblksz;
+ diff += oblksz;
+ ifr--;
+ ofr--;
+ }
+ }
+ p->diff = diff;
+ p->ctx_start = ctx_start;
+ return oblksz - ofr;
+}
+
+/*
+ * initialize resampler with ibufsz/obufsz factor and "nch" channels
+ */
+void
+resamp_init(struct resamp *p, unsigned int iblksz, unsigned int oblksz, int nch)
+{
+ unsigned int i;
+
+ p->iblksz = iblksz;
+ p->oblksz = oblksz;
+ p->diff = 0;
+ p->idelta = 0;
+ p->odelta = 0;
+ p->nch = nch;
+ p->ctx_start = 0;
+ for (i = 0; i < NCHAN_MAX * RESAMP_NCTX; i++)
+ p->ctx[i] = 0;
+#ifdef DEBUG
+ if (log_level >= 3) {
+ log_puts("resamp: ");
+ log_putu(iblksz);
+ log_puts("/");
+ log_putu(oblksz);
+ log_puts("\n");
+ }
+#endif
+}
+
+/*
+ * encode "todo" frames from native to foreign encoding
+ */
+void
+enc_do(struct conv *p, unsigned char *in, unsigned char *out, int todo)
+{
+ unsigned int f;
+ adata_t *idata;
+ unsigned int s;
+ unsigned int oshift;
+ unsigned int obias;
+ unsigned int obps;
+ unsigned int i;
+ unsigned char *odata;
+ int obnext;
+ int osnext;
+
+#ifdef DEBUG
+ if (log_level >= 4) {
+ log_puts("enc: copying ");
+ log_putu(todo);
+ log_puts(" frames\n");
+ }
+#endif
+ /*
+ * Partially copy structures into local variables, to avoid
+ * unnecessary indirections; this also allows the compiler to
+ * order local variables more "cache-friendly".
+ */
+ idata = (adata_t *)in;
+ odata = out;
+ oshift = p->shift;
+ obias = p->bias;
+ obps = p->bps;
+ obnext = p->bnext;
+ osnext = p->snext;
+
+ /*
+ * Start conversion.
+ */
+ odata += p->bfirst;
+ for (f = todo * p->nch; f > 0; f--) {
+ /* convert adata to u32 */
+ s = (int)*idata++ + ADATA_UNIT;
+ s <<= 32 - ADATA_BITS;
+ /* convert u32 to uN */
+ s >>= oshift;
+ /* convert uN to sN */
+ s -= obias;
+ /* packetize sN */
+ for (i = obps; i > 0; i--) {
+ *odata = (unsigned char)s;
+ s >>= 8;
+ odata += obnext;
+ }
+ odata += osnext;
+ }
+}
+
+/*
+ * store "todo" frames of silence in foreign encoding
+ */
+void
+enc_sil_do(struct conv *p, unsigned char *out, int todo)
+{
+ unsigned int f;
+ unsigned int s;
+ unsigned int oshift;
+ int obias;
+ unsigned int obps;
+ unsigned int i;
+ unsigned char *odata;
+ int obnext;
+ int osnext;
+
+#ifdef DEBUG
+ if (log_level >= 4) {
+ log_puts("enc: silence ");
+ log_putu(todo);
+ log_puts(" frames\n");
+ }
+#endif
+ /*
+ * Partially copy structures into local variables, to avoid
+ * unnecessary indirections; this also allows the compiler to
+ * order local variables more "cache-friendly".
+ */
+ odata = out;
+ oshift = p->shift;
+ obias = p->bias;
+ obps = p->bps;
+ obnext = p->bnext;
+ osnext = p->snext;
+
+ /*
+ * Start conversion.
+ */
+ odata += p->bfirst;
+ for (f = todo * p->nch; f > 0; f--) {
+ s = ((1U << 31) >> oshift) - obias;
+ for (i = obps; i > 0; i--) {
+ *odata = (unsigned char)s;
+ s >>= 8;
+ odata += obnext;
+ }
+ odata += osnext;
+ }
+}
+
+/*
+ * initialize encoder from native to foreign encoding
+ */
+void
+enc_init(struct conv *p, struct aparams *par, int nch)
+{
+ p->nch = nch;
+ p->bps = par->bps;
+ if (par->msb) {
+ p->shift = 32 - par->bps * 8;
+ } else {
+ p->shift = 32 - par->bits;
+ }
+ if (par->sig) {
+ p->bias = (1U << 31) >> p->shift;
+ } else {
+ p->bias = 0;
+ }
+ if (!par->le) {
+ p->bfirst = par->bps - 1;
+ p->bnext = -1;
+ p->snext = 2 * par->bps;
+ } else {
+ p->bfirst = 0;
+ p->bnext = 1;
+ p->snext = 0;
+ }
+#ifdef DEBUG
+ if (log_level >= 3) {
+ log_puts("enc: ");
+ aparams_log(par);
+ log_puts(", ");
+ log_puti(p->nch);
+ log_puts(" channels\n");
+ }
+#endif
+}
+
+/*
+ * decode "todo" frames from from foreign to native encoding
+ */
+void
+dec_do(struct conv *p, unsigned char *in, unsigned char *out, int todo)
+{
+ unsigned int f;
+ unsigned int ibps;
+ unsigned int i;
+ unsigned int s = 0xdeadbeef;
+ unsigned char *idata;
+ int ibnext;
+ int isnext;
+ unsigned int ibias;
+ unsigned int ishift;
+ adata_t *odata;
+
+#ifdef DEBUG
+ if (log_level >= 4) {
+ log_puts("dec: copying ");
+ log_putu(todo);
+ log_puts(" frames\n");
+ }
+#endif
+ /*
+ * Partially copy structures into local variables, to avoid
+ * unnecessary indirections; this also allows the compiler to
+ * order local variables more "cache-friendly".
+ */
+ idata = in;
+ odata = (adata_t *)out;
+ ibps = p->bps;
+ ibnext = p->bnext;
+ ibias = p->bias;
+ ishift = p->shift;
+ isnext = p->snext;
+
+ /*
+ * Start conversion.
+ */
+ idata += p->bfirst;
+ for (f = todo * p->nch; f > 0; f--) {
+ for (i = ibps; i > 0; i--) {
+ s <<= 8;
+ s |= *idata;
+ idata += ibnext;
+ }
+ idata += isnext;
+ s += ibias;
+ s <<= ishift;
+ s >>= 32 - ADATA_BITS;
+ *odata++ = s - ADATA_UNIT;
+ }
+}
+
+/*
+ * convert a 32-bit float to adata_t, clipping to -1:1, boundaries
+ * excluded
+ */
+static inline int
+f32_to_adata(unsigned int x)
+{
+ unsigned int s, e, m, y;
+
+ s = (x >> 31);
+ e = (x >> 23) & 0xff;
+ m = (x << 8) | 0x80000000;
+ if (e < 127 - 24)
+ y = 0;
+ else if (e > 127 - 1)
+ y = ADATA_UNIT - 1;
+ else
+ y = m >> (127 + (32 - ADATA_BITS) - e);
+ return (y ^ -s) + s;
+}
+
+/*
+ * convert samples from little endian ieee 754 floats to adata_t
+ */
+void
+dec_do_float(struct conv *p, unsigned char *in, unsigned char *out, int todo)
+{
+ unsigned int f;
+ unsigned int i;
+ unsigned int s = 0xdeadbeef;
+ unsigned char *idata;
+ int ibnext;
+ int isnext;
+ adata_t *odata;
+
+#ifdef DEBUG
+ if (log_level >= 4) {
+ log_puts("dec_float: copying ");
+ log_putu(todo);
+ log_puts(" frames\n");
+ }
+#endif
+ /*
+ * Partially copy structures into local variables, to avoid
+ * unnecessary indirections; this also allows the compiler to
+ * order local variables more "cache-friendly".
+ */
+ idata = in;
+ odata = (adata_t *)out;
+ ibnext = p->bnext;
+ isnext = p->snext;
+
+ /*
+ * Start conversion.
+ */
+ idata += p->bfirst;
+ for (f = todo * p->nch; f > 0; f--) {
+ for (i = 4; i > 0; i--) {
+ s <<= 8;
+ s |= *idata;
+ idata += ibnext;
+ }
+ idata += isnext;
+ *odata++ = f32_to_adata(s);
+ }
+}
+
+/*
+ * convert samples from ulaw/alaw to adata_t
+ */
+void
+dec_do_ulaw(struct conv *p, unsigned char *in, unsigned char *out, int todo, int is_alaw)
+{
+ unsigned int f;
+ unsigned char *idata;
+ adata_t *odata;
+ short *map;
+
+#ifdef DEBUG
+ if (log_level >= 4) {
+ log_puts("dec_ulaw: copying ");
+ log_putu(todo);
+ log_puts(" frames\n");
+ }
+#endif
+ map = is_alaw ? dec_alawmap : dec_ulawmap;
+ idata = in;
+ odata = (adata_t *)out;
+ for (f = todo * p->nch; f > 0; f--)
+ *odata++ = map[*idata++] << (ADATA_BITS - 16);
+}
+
+/*
+ * initialize decoder from foreign to native encoding
+ */
+void
+dec_init(struct conv *p, struct aparams *par, int nch)
+{
+ p->bps = par->bps;
+ p->nch = nch;
+ if (par->msb) {
+ p->shift = 32 - par->bps * 8;
+ } else {
+ p->shift = 32 - par->bits;
+ }
+ if (par->sig) {
+ p->bias = (1U << 31) >> p->shift;
+ } else {
+ p->bias = 0;
+ }
+ if (par->le) {
+ p->bfirst = par->bps - 1;
+ p->bnext = -1;
+ p->snext = 2 * par->bps;
+ } else {
+ p->bfirst = 0;
+ p->bnext = 1;
+ p->snext = 0;
+ }
+#ifdef DEBUG
+ if (log_level >= 3) {
+ log_puts("dec: ");
+ aparams_log(par);
+ log_puts(", ");
+ log_puti(p->nch);
+ log_puts(" channels\n");
+ }
+#endif
+}
+
+/*
+ * mix "todo" input frames on the output with the given volume
+ */
+void
+cmap_add(struct cmap *p, void *in, void *out, int vol, int todo)
+{
+ adata_t *idata, *odata;
+ int i, j, nch, istart, inext, onext, ostart, y, v;
+
+#ifdef DEBUG
+ if (log_level >= 4) {
+ log_puts("cmap: adding ");
+ log_puti(todo);
+ log_puts(" frames\n");
+ }
+#endif
+ idata = in;
+ odata = out;
+ ostart = p->ostart;
+ onext = p->onext;
+ istart = p->istart;
+ inext = p->inext;
+ nch = p->nch;
+ v = vol;
+
+ /*
+ * map/mix input on the output
+ */
+ for (i = todo; i > 0; i--) {
+ odata += ostart;
+ idata += istart;
+ for (j = nch; j > 0; j--) {
+ y = *odata + ADATA_MUL(*idata, v);
+ if (y >= ADATA_UNIT)
+ y = ADATA_UNIT - 1;
+ else if (y < -ADATA_UNIT)
+ y = -ADATA_UNIT;
+ *odata = y;
+ idata++;
+ odata++;
+ }
+ odata += onext;
+ idata += inext;
+ }
+}
+
+/*
+ * overwrite output with "todo" input frames with with the given volume
+ */
+void
+cmap_copy(struct cmap *p, void *in, void *out, int vol, int todo)
+{
+ adata_t *idata, *odata;
+ int i, j, nch, istart, inext, onext, ostart, v;
+
+#ifdef DEBUG
+ if (log_level >= 4) {
+ log_puts("cmap: copying ");
+ log_puti(todo);
+ log_puts(" frames\n");
+ }
+#endif
+ idata = in;
+ odata = out;
+ ostart = p->ostart;
+ onext = p->onext;
+ istart = p->istart;
+ inext = p->inext;
+ nch = p->nch;
+ v = vol;
+
+ /*
+ * copy to the output buffer
+ */
+ for (i = todo; i > 0; i--) {
+ idata += istart;
+ odata += ostart;
+ for (j = nch; j > 0; j--) {
+ *odata = ADATA_MUL(*idata, v);
+ odata++;
+ idata++;
+ }
+ odata += onext;
+ idata += inext;
+ }
+}
+
+/*
+ * initialize channel mapper, to map a subset of input channel range
+ * into a subset of the output channel range
+ */
+void
+cmap_init(struct cmap *p,
+ int imin, int imax, int isubmin, int isubmax,
+ int omin, int omax, int osubmin, int osubmax)
+{
+ int cmin, cmax;
+
+ cmin = -NCHAN_MAX;
+ if (osubmin > cmin)
+ cmin = osubmin;
+ if (omin > cmin)
+ cmin = omin;
+ if (isubmin > cmin)
+ cmin = isubmin;
+ if (imin > cmin)
+ cmin = imin;
+
+ cmax = NCHAN_MAX;
+ if (osubmax < cmax)
+ cmax = osubmax;
+ if (omax < cmax)
+ cmax = omax;
+ if (isubmax < cmax)
+ cmax = isubmax;
+ if (imax < cmax)
+ cmax = imax;
+
+ p->ostart = cmin - omin;
+ p->onext = omax - cmax;
+ p->istart = cmin - imin;
+ p->inext = imax - cmax;
+ p->nch = cmax - cmin + 1;
+#ifdef DEBUG
+ if (log_level >= 3) {
+ log_puts("cmap: nch = ");
+ log_puti(p->nch);
+ log_puts(", ostart = ");
+ log_puti(p->ostart);
+ log_puts(", onext = ");
+ log_puti(p->onext);
+ log_puts(", istart = ");
+ log_puti(p->istart);
+ log_puts(", inext = ");
+ log_puti(p->inext);
+ log_puts("\n");
+ }
+#endif
+}
--- /dev/null
+/* $OpenBSD: dsp.h,v 1.1 2015/01/21 08:43:55 ratchov Exp $ */
+/*
+ * Copyright (c) 2012 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 DSP_H
+#define DSP_H
+
+#include <sys/types.h>
+#include "defs.h"
+
+/*
+ * Samples are numbers in the interval [-1, 1[, note that 1, the upper
+ * boundary is excluded. We represent them as signed fixed point numbers
+ * of ADATA_BITS. We also assume that 2^(ADATA_BITS - 1) fits in a int.
+ */
+#ifndef ADATA_BITS
+#define ADATA_BITS 16
+#endif
+#define ADATA_LE (BYTE_ORDER == LITTLE_ENDIAN)
+#define ADATA_UNIT (1 << (ADATA_BITS - 1))
+
+#if ADATA_BITS == 16
+
+#define ADATA_MUL(x,y) (((int)(x) * (int)(y)) >> (ADATA_BITS - 1))
+#define ADATA_MULDIV(x,y,z) ((int)(x) * (int)(y) / (int)(z))
+
+typedef short adata_t;
+
+#elif ADATA_BITS == 24
+
+#if defined(__i386__) && defined(__GNUC__)
+
+static inline int
+fp24_mul(int x, int a)
+{
+ int res;
+
+ asm volatile (
+ "imull %2\n\t"
+ "shrdl $23, %%edx, %%eax\n\t"
+ : "=a" (res)
+ : "a" (x), "r" (a)
+ : "%edx"
+ );
+ return res;
+}
+
+static inline int
+fp24_muldiv(int x, int a, int b)
+{
+ int res;
+
+ asm volatile (
+ "imull %2\n\t"
+ "idivl %3\n\t"
+ : "=a" (res)
+ : "a" (x), "d" (a), "r" (b)
+ );
+ return res;
+}
+
+#define ADATA_MUL(x,y) fp24_mul(x, y)
+#define ADATA_MULDIV(x,y,z) fp24_muldiv(x, y, z);
+
+#elif defined(__amd64__) || defined(__sparc64__)
+
+#define ADATA_MUL(x,y) \
+ ((int)(((long long)(x) * (long long)(y)) >> (ADATA_BITS - 1)))
+#define ADATA_MULDIV(x,y,z) \
+ ((int)((long long)(x) * (long long)(y) / (long long)(z)))
+
+#else
+#error "no 24-bit code for this architecture"
+#endif
+
+typedef int adata_t;
+
+#else
+#error "only 16-bit and 24-bit precisions are supported"
+#endif
+
+/*
+ * Maximum size of the encording string (the longest possible
+ * encoding is ``s24le3msb'').
+ */
+#define ENCMAX 10
+
+/*
+ * Default bytes per sample for the given bits per sample.
+ */
+#define APARAMS_BPS(bits) (((bits) <= 8) ? 1 : (((bits) <= 16) ? 2 : 4))
+
+struct aparams {
+ unsigned int bps; /* bytes per sample */
+ unsigned int bits; /* actually used bits */
+ unsigned int le; /* 1 if little endian, 0 if big endian */
+ unsigned int sig; /* 1 if signed, 0 if unsigned */
+ unsigned int msb; /* 1 if msb justified, 0 if lsb justified */
+};
+
+struct resamp {
+#define RESAMP_NCTX 2
+ unsigned int ctx_start;
+ adata_t ctx[NCHAN_MAX * RESAMP_NCTX];
+ unsigned int iblksz, oblksz;
+ int diff;
+ int idelta, odelta; /* remainder of ipos/opos */
+ int nch;
+};
+
+struct conv {
+ int bfirst; /* bytes to skip at startup */
+ unsigned int bps; /* bytes per sample */
+ unsigned int shift; /* shift to get 32bit MSB */
+ unsigned int bias; /* bias of unsigned samples */
+ int bnext; /* to reach the next byte */
+ int snext; /* to reach the next sample */
+ int nch;
+};
+
+struct cmap {
+ int istart;
+ int inext;
+ int onext;
+ int ostart;
+ int nch;
+};
+
+#define MIDI_TO_ADATA(m) (aparams_ctltovol[m] << (ADATA_BITS - 16))
+extern int aparams_ctltovol[128];
+
+void aparams_init(struct aparams *);
+void aparams_log(struct aparams *);
+int aparams_strtoenc(struct aparams *, char *);
+int aparams_enctostr(struct aparams *, char *);
+int aparams_native(struct aparams *);
+
+int resamp_do(struct resamp *, adata_t *, adata_t *, int);
+void resamp_init(struct resamp *, unsigned int, unsigned int, int);
+void enc_do(struct conv *, unsigned char *, unsigned char *, int);
+void enc_sil_do(struct conv *, unsigned char *, int);
+void enc_init(struct conv *, struct aparams *, int);
+void dec_do(struct conv *, unsigned char *, unsigned char *, int);
+void dec_do_float(struct conv *, unsigned char *, unsigned char *, int);
+void dec_do_ulaw(struct conv *, unsigned char *, unsigned char *, int, int);
+void dec_init(struct conv *, struct aparams *, int);
+void cmap_add(struct cmap *, void *, void *, int, int);
+void cmap_copy(struct cmap *, void *, void *, int, int);
+void cmap_init(struct cmap *, int, int, int, int, int, int, int, int);
+
+#endif /* !defined(DSP_H) */
+++ /dev/null
-/* $OpenBSD: file.c,v 1.31 2013/11/18 17:37:45 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.
- */
-/*
- * non-blocking file i/o module: each file can be read or written (or
- * both). To achieve non-blocking io, we simply use the poll() syscall
- * in an event loop. If a read() or write() syscall return EAGAIN
- * (operation will block), then the file is marked as "for polling", else
- * the file is not polled again.
- *
- * the module also provides trivial timeout implementation,
- * derived from:
- *
- * anoncvs@moule.caoua.org:/midish
- *
- * midish/timo.c rev 1.18
- * midish/mdep.c rev 1.71
- *
- * A timeout is used to schedule the call of a routine (the callback)
- * there is a global list of timeouts that is processed inside the
- * event loop. Timeouts work as follows:
- *
- * first the timo structure must be initialized with timo_set()
- *
- * then the timeout is scheduled (only once) with timo_add()
- *
- * if the timeout expires, the call-back is called; then it can
- * be scheduled again if needed. It's OK to reschedule it again
- * from the callback
- *
- * the timeout can be aborted with timo_del(), it is OK to try to
- * abort a timout that has expired
- *
- */
-
-#include <sys/time.h>
-#include <sys/types.h>
-
-#include <err.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <poll.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <time.h>
-
-#include "abuf.h"
-#include "aproc.h"
-#include "conf.h"
-#include "file.h"
-#ifdef DEBUG
-#include "dbg.h"
-#endif
-
-#define MAXFDS 100
-#define TIMER_USEC 10000
-
-void timo_update(unsigned int);
-void timo_init(void);
-void timo_done(void);
-void file_sigalrm(int);
-
-struct timespec file_ts;
-struct filelist file_list;
-struct timo *timo_queue;
-unsigned int timo_abstime;
-int file_slowaccept = 0;
-#ifdef DEBUG
-long long file_wtime, file_utime;
-#endif
-
-/*
- * initialise a timeout structure, arguments are callback and argument
- * that will be passed to the callback
- */
-void
-timo_set(struct timo *o, void (*cb)(void *), void *arg)
-{
- o->cb = cb;
- o->arg = arg;
- o->set = 0;
-}
-
-/*
- * schedule the callback in 'delta' 24-th of microseconds. The timeout
- * must not be already scheduled
- */
-void
-timo_add(struct timo *o, unsigned int delta)
-{
- struct timo **i;
- unsigned int val;
- int diff;
-
-#ifdef DEBUG
- if (o->set) {
- dbg_puts("timo_add: already set\n");
- dbg_panic();
- }
- if (delta == 0) {
- dbg_puts("timo_add: zero timeout is evil\n");
- dbg_panic();
- }
-#endif
- val = timo_abstime + delta;
- for (i = &timo_queue; *i != NULL; i = &(*i)->next) {
- diff = (*i)->val - val;
- if (diff > 0) {
- break;
- }
- }
- o->set = 1;
- o->val = val;
- o->next = *i;
- *i = o;
-}
-
-/*
- * abort a scheduled timeout
- */
-void
-timo_del(struct timo *o)
-{
- struct timo **i;
-
- for (i = &timo_queue; *i != NULL; i = &(*i)->next) {
- if (*i == o) {
- *i = o->next;
- o->set = 0;
- return;
- }
- }
-#ifdef DEBUG
- if (debug_level >= 4)
- dbg_puts("timo_del: not found\n");
-#endif
-}
-
-/*
- * routine to be called by the timer when 'delta' 24-th of microsecond
- * elapsed. This routine updates time referece used by timeouts and
- * calls expired timeouts
- */
-void
-timo_update(unsigned int delta)
-{
- struct timo *to;
- int diff;
-
- /*
- * update time reference
- */
- timo_abstime += delta;
-
- /*
- * remove from the queue and run expired timeouts
- */
- while (timo_queue != NULL) {
- /*
- * there is no overflow here because + and - are
- * modulo 2^32, they are the same for both signed and
- * unsigned integers
- */
- diff = timo_queue->val - timo_abstime;
- if (diff > 0)
- break;
- to = timo_queue;
- timo_queue = to->next;
- to->set = 0;
- to->cb(to->arg);
- }
-}
-
-/*
- * initialize timeout queue
- */
-void
-timo_init(void)
-{
- timo_queue = NULL;
- timo_abstime = 0;
-}
-
-/*
- * destroy timeout queue
- */
-void
-timo_done(void)
-{
-#ifdef DEBUG
- if (timo_queue != NULL) {
- dbg_puts("timo_done: timo_queue not empty!\n");
- dbg_panic();
- }
-#endif
- timo_queue = (struct timo *)0xdeadbeef;
-}
-
-#ifdef DEBUG
-void
-file_dbg(struct file *f)
-{
- dbg_puts(f->ops->name);
- dbg_puts("(");
- dbg_puts(f->name);
- dbg_puts("|");
- if (f->state & FILE_ROK)
- dbg_puts("r");
- if (f->state & FILE_RINUSE)
- dbg_puts("R");
- if (f->state & FILE_WOK)
- dbg_puts("w");
- if (f->state & FILE_WINUSE)
- dbg_puts("W");
- if (f->state & FILE_EOF)
- dbg_puts("e");
- if (f->state & FILE_HUP)
- dbg_puts("h");
- if (f->state & FILE_ZOMB)
- dbg_puts("Z");
- dbg_puts(")");
-}
-#endif
-
-struct file *
-file_new(struct fileops *ops, char *name, unsigned int nfds)
-{
- struct file *f;
-
- LIST_FOREACH(f, &file_list, entry)
- nfds += f->ops->nfds(f);
- if (nfds > MAXFDS) {
-#ifdef DEBUG
- if (debug_level >= 1) {
- dbg_puts(name);
- dbg_puts(": too many polled files\n");
- }
-#endif
- return NULL;
- }
- f = malloc(ops->size);
- if (f == NULL)
- err(1, "file_new: %s", ops->name);
- f->ops = ops;
- f->name = name;
- f->state = 0;
-#ifdef DEBUG
- f->cycles = 0;
-#endif
- f->rproc = NULL;
- f->wproc = NULL;
- LIST_INSERT_HEAD(&file_list, f, entry);
-#ifdef DEBUG
- if (debug_level >= 3) {
- file_dbg(f);
- dbg_puts(": created\n");
- }
-#endif
- return f;
-}
-
-void
-file_del(struct file *f)
-{
-#ifdef DEBUG
- if (debug_level >= 3) {
- file_dbg(f);
- dbg_puts(": terminating...\n");
- }
-#endif
- if (f->state & (FILE_RINUSE | FILE_WINUSE)) {
- f->state |= FILE_ZOMB;
- } else {
- LIST_REMOVE(f, entry);
-#ifdef DEBUG
- if (debug_level >= 3) {
- file_dbg(f);
- dbg_puts(": destroyed\n");
- }
-#endif
- f->ops->close(f);
- free(f);
- }
-}
-
-int
-file_poll(void)
-{
- nfds_t nfds, n;
- short events, revents;
- struct pollfd pfds[MAXFDS];
- struct file *f, *fnext;
- struct aproc *p;
- struct timespec ts;
-#ifdef DEBUG
- struct timespec sleepts;
-#endif
- long long delta_nsec;
- int res;
-
- if (LIST_EMPTY(&file_list) && timo_queue == NULL) {
-#ifdef DEBUG
- if (debug_level >= 3)
- dbg_puts("nothing to do...\n");
-#endif
- return 0;
- }
- /*
- * Fill the pfds[] array with files that are blocked on reading
- * and/or writing, skipping those that are just waiting.
- */
-#ifdef DEBUG
- dbg_flush();
- if (debug_level >= 4)
- dbg_puts("poll:");
-#endif
- nfds = 0;
- LIST_FOREACH(f, &file_list, entry) {
- events = 0;
- if (f->rproc && !(f->state & FILE_ROK))
- events |= POLLIN;
- if (f->wproc && !(f->state & FILE_WOK))
- events |= POLLOUT;
-#ifdef DEBUG
- if (debug_level >= 4) {
- dbg_puts(" ");
- file_dbg(f);
- }
-#endif
- n = f->ops->pollfd(f, pfds + nfds, events);
- if (n == 0) {
- f->pfd = NULL;
- continue;
- }
- f->pfd = pfds + nfds;
- nfds += n;
- }
-#ifdef DEBUG
- if (debug_level >= 4) {
- dbg_puts("\npfds[] =");
- for (n = 0; n < nfds; n++) {
- dbg_puts(" ");
- dbg_putx(pfds[n].events);
- }
- dbg_puts("\n");
- }
-#endif
-#ifdef DEBUG
- clock_gettime(CLOCK_MONOTONIC, &sleepts);
- file_utime += 1000000000LL * (sleepts.tv_sec - file_ts.tv_sec);
- file_utime += sleepts.tv_nsec - file_ts.tv_nsec;
-#endif
- res = poll(pfds, nfds, -1);
- if (res < 0 && errno != EINTR)
- err(1, "poll");
- clock_gettime(CLOCK_MONOTONIC, &ts);
-#ifdef DEBUG
- file_wtime += 1000000000LL * (ts.tv_sec - sleepts.tv_sec);
- file_wtime += ts.tv_nsec - sleepts.tv_nsec;
-#endif
- delta_nsec = 1000000000LL * (ts.tv_sec - file_ts.tv_sec);
- delta_nsec += ts.tv_nsec - file_ts.tv_nsec;
-#ifdef DEBUG
- if (delta_nsec < 0)
- dbg_puts("file_poll: negative time interval\n");
-#endif
- file_ts = ts;
- if (delta_nsec >= 0 && delta_nsec < 1000000000LL)
- timo_update(delta_nsec / 1000);
- else {
-#ifdef DEBUG
- if (debug_level >= 1)
- dbg_puts("ignored huge clock delta\n");
-#endif
- }
- if (res <= 0)
- return 1;
-
- f = LIST_FIRST(&file_list);
- while (f != NULL) {
- if (f->pfd == NULL) {
- f = LIST_NEXT(f, entry);
- continue;
- }
- revents = f->ops->revents(f, f->pfd);
-#ifdef DEBUG
- if (revents) {
- f->cycles++;
- if (f->cycles > FILE_MAXCYCLES) {
- file_dbg(f);
- dbg_puts(": busy loop, disconnecting\n");
- revents = POLLHUP;
- }
- }
-#endif
- if (!(f->state & FILE_ZOMB) && (revents & POLLIN)) {
- revents &= ~POLLIN;
-#ifdef DEBUG
- if (debug_level >= 4) {
- file_dbg(f);
- dbg_puts(": rok\n");
- }
-#endif
- f->state |= FILE_ROK;
- f->state |= FILE_RINUSE;
- for (;;) {
- p = f->rproc;
- if (!p)
- break;
-#ifdef DEBUG
- if (debug_level >= 4) {
- aproc_dbg(p);
- dbg_puts(": in\n");
- }
-#endif
- if (!p->ops->in(p, NULL))
- break;
- }
- f->state &= ~FILE_RINUSE;
- }
- if (!(f->state & FILE_ZOMB) && (revents & POLLOUT)) {
- revents &= ~POLLOUT;
-#ifdef DEBUG
- if (debug_level >= 4) {
- file_dbg(f);
- dbg_puts(": wok\n");
- }
-#endif
- f->state |= FILE_WOK;
- f->state |= FILE_WINUSE;
- for (;;) {
- p = f->wproc;
- if (!p)
- break;
-#ifdef DEBUG
- if (debug_level >= 4) {
- aproc_dbg(p);
- dbg_puts(": out\n");
- }
-#endif
- if (!p->ops->out(p, NULL))
- break;
- }
- f->state &= ~FILE_WINUSE;
- }
- if (!(f->state & FILE_ZOMB) && (revents & POLLHUP)) {
-#ifdef DEBUG
- if (debug_level >= 3) {
- file_dbg(f);
- dbg_puts(": disconnected\n");
- }
-#endif
- f->state |= (FILE_EOF | FILE_HUP);
- }
- if (!(f->state & FILE_ZOMB) && (f->state & FILE_EOF)) {
-#ifdef DEBUG
- if (debug_level >= 3) {
- file_dbg(f);
- dbg_puts(": eof\n");
- }
-#endif
- p = f->rproc;
- if (p) {
- f->state |= FILE_RINUSE;
-#ifdef DEBUG
- if (debug_level >= 3) {
- aproc_dbg(p);
- dbg_puts(": eof\n");
- }
-#endif
- p->ops->eof(p, NULL);
- f->state &= ~FILE_RINUSE;
- }
- f->state &= ~FILE_EOF;
- }
- if (!(f->state & FILE_ZOMB) && (f->state & FILE_HUP)) {
-#ifdef DEBUG
- if (debug_level >= 3) {
- file_dbg(f);
- dbg_puts(": hup\n");
- }
-#endif
- p = f->wproc;
- if (p) {
- f->state |= FILE_WINUSE;
-#ifdef DEBUG
- if (debug_level >= 3) {
- aproc_dbg(p);
- dbg_puts(": hup\n");
- }
-#endif
- p->ops->hup(p, NULL);
- f->state &= ~FILE_WINUSE;
- }
- f->state &= ~FILE_HUP;
- }
- fnext = LIST_NEXT(f, entry);
- if (f->state & FILE_ZOMB)
- file_del(f);
- f = fnext;
- }
- if (LIST_EMPTY(&file_list) && timo_queue == NULL) {
-#ifdef DEBUG
- if (debug_level >= 3)
- dbg_puts("no files anymore...\n");
-#endif
- return 0;
- }
- return 1;
-}
-
-/*
- * handler for SIGALRM, invoked periodically
- */
-void
-file_sigalrm(int i)
-{
- /* nothing to do, we only want poll() to return EINTR */
-}
-
-
-void
-filelist_init(void)
-{
- static struct sigaction sa;
- struct itimerval it;
- sigset_t set;
-
- sigemptyset(&set);
- (void)sigaddset(&set, SIGPIPE);
- if (sigprocmask(SIG_BLOCK, &set, NULL))
- err(1, "sigprocmask");
- LIST_INIT(&file_list);
- if (clock_gettime(CLOCK_MONOTONIC, &file_ts) < 0) {
- perror("clock_gettime");
- exit(1);
- }
- sa.sa_flags = SA_RESTART;
- sa.sa_handler = file_sigalrm;
- sigfillset(&sa.sa_mask);
- if (sigaction(SIGALRM, &sa, NULL) < 0) {
- perror("sigaction");
- exit(1);
- }
- it.it_interval.tv_sec = 0;
- it.it_interval.tv_usec = TIMER_USEC;
- it.it_value.tv_sec = 0;
- it.it_value.tv_usec = TIMER_USEC;
- if (setitimer(ITIMER_REAL, &it, NULL) < 0) {
- perror("setitimer");
- exit(1);
- }
- timo_init();
-#ifdef DEBUG
- dbg_sync = 0;
-#endif
-}
-
-void
-filelist_done(void)
-{
- struct itimerval it;
-#ifdef DEBUG
- struct file *f;
-
- if (!LIST_EMPTY(&file_list)) {
- LIST_FOREACH(f, &file_list, entry) {
- file_dbg(f);
- dbg_puts(" not closed\n");
- }
- dbg_panic();
- }
- dbg_sync = 1;
- dbg_flush();
-#endif
- timerclear(&it.it_value);
- timerclear(&it.it_interval);
- if (setitimer(ITIMER_REAL, &it, NULL) < 0) {
- perror("setitimer");
- exit(1);
- }
- timo_done();
-}
-
-unsigned int
-file_read(struct file *f, unsigned char *data, unsigned int count)
-{
- unsigned int n;
-#ifdef DEBUG
- struct timespec ts0, ts1;
- long us;
-
- if (!(f->state & FILE_ROK)) {
- file_dbg(f);
- dbg_puts(": read: bad state\n");
- dbg_panic();
- }
- clock_gettime(CLOCK_MONOTONIC, &ts0);
-#endif
- n = f->ops->read(f, data, count);
-#ifdef DEBUG
- if (n > 0)
- f->cycles = 0;
- clock_gettime(CLOCK_MONOTONIC, &ts1);
- us = 1000000L * (ts1.tv_sec - ts0.tv_sec);
- us += (ts1.tv_nsec - ts0.tv_nsec) / 1000;
- if (debug_level >= 4 || (debug_level >= 2 && us >= 5000)) {
- dbg_puts(f->name);
- dbg_puts(": read ");
- dbg_putu(n);
- dbg_puts(" bytes in ");
- dbg_putu(us);
- dbg_puts("us\n");
- }
-#endif
- return n;
-}
-
-unsigned int
-file_write(struct file *f, unsigned char *data, unsigned int count)
-{
- unsigned int n;
-#ifdef DEBUG
- struct timespec ts0, ts1;
- long us;
-
- if (!(f->state & FILE_WOK)) {
- file_dbg(f);
- dbg_puts(": write: bad state\n");
- dbg_panic();
- }
- clock_gettime(CLOCK_MONOTONIC, &ts0);
-#endif
- n = f->ops->write(f, data, count);
-#ifdef DEBUG
- if (n > 0)
- f->cycles = 0;
- clock_gettime(CLOCK_MONOTONIC, &ts1);
- us = 1000000L * (ts1.tv_sec - ts0.tv_sec);
- us += (ts1.tv_nsec - ts0.tv_nsec) / 1000;
- if (debug_level >= 4 || (debug_level >= 2 && us >= 5000)) {
- dbg_puts(f->name);
- dbg_puts(": wrote ");
- dbg_putu(n);
- dbg_puts(" bytes in ");
- dbg_putu(us);
- dbg_puts("us\n");
- }
-#endif
- return n;
-}
-
-void
-file_eof(struct file *f)
-{
- struct aproc *p;
-
-#ifdef DEBUG
- if (debug_level >= 3) {
- file_dbg(f);
- dbg_puts(": eof requested\n");
- }
-#endif
- if (!(f->state & (FILE_RINUSE | FILE_WINUSE))) {
- p = f->rproc;
- if (p) {
- f->state |= FILE_RINUSE;
-#ifdef DEBUG
- if (debug_level >= 3) {
- aproc_dbg(p);
- dbg_puts(": eof\n");
- }
-#endif
- p->ops->eof(p, NULL);
- f->state &= ~FILE_RINUSE;
- }
- if (f->state & FILE_ZOMB)
- file_del(f);
- } else {
- f->state &= ~FILE_ROK;
- f->state |= FILE_EOF;
- }
-}
-
-void
-file_hup(struct file *f)
-{
- struct aproc *p;
-
-#ifdef DEBUG
- if (debug_level >= 3) {
- file_dbg(f);
- dbg_puts(": hup requested\n");
- }
-#endif
- if (!(f->state & (FILE_RINUSE | FILE_WINUSE))) {
- p = f->wproc;
- if (p) {
- f->state |= FILE_WINUSE;
-#ifdef DEBUG
- if (debug_level >= 3) {
- aproc_dbg(p);
- dbg_puts(": hup\n");
- }
-#endif
- p->ops->hup(p, NULL);
- f->state &= ~FILE_WINUSE;
- }
- if (f->state & FILE_ZOMB)
- file_del(f);
- } else {
- f->state &= ~FILE_WOK;
- f->state |= FILE_HUP;
- }
-}
-
-void
-file_close(struct file *f)
-{
- struct aproc *p;
-
-#ifdef DEBUG
- if (debug_level >= 3) {
- file_dbg(f);
- dbg_puts(": closing\n");
- }
-#endif
- if (f->wproc == NULL && f->rproc == NULL)
- f->state |= FILE_ZOMB;
- if (!(f->state & (FILE_RINUSE | FILE_WINUSE))) {
- p = f->rproc;
- if (p) {
- f->state |= FILE_RINUSE;
-#ifdef DEBUG
- if (debug_level >= 3) {
- aproc_dbg(p);
- dbg_puts(": eof\n");
- }
-#endif
- p->ops->eof(p, NULL);
- f->state &= ~FILE_RINUSE;
- }
- p = f->wproc;
- if (p) {
- f->state |= FILE_WINUSE;
-#ifdef DEBUG
- if (debug_level >= 3) {
- aproc_dbg(p);
- dbg_puts(": hup\n");
- }
-#endif
- p->ops->hup(p, NULL);
- f->state &= ~FILE_WINUSE;
- }
- if (f->state & FILE_ZOMB)
- file_del(f);
- } else {
- f->state &= ~(FILE_ROK | FILE_WOK);
- f->state |= (FILE_EOF | FILE_HUP);
- }
-}
+++ /dev/null
-/* $OpenBSD: file.h,v 1.14 2012/04/11 06:05: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 FILE_H
-#define FILE_H
-
-#include <sys/queue.h>
-#include <sys/types.h>
-
-struct file;
-struct aproc;
-struct pollfd;
-
-struct timo {
- struct timo *next;
- unsigned int val; /* time to wait before the callback */
- unsigned int set; /* true if the timeout is set */
- void (*cb)(void *arg); /* routine to call on expiration */
- void *arg; /* argument to give to 'cb' */
-};
-
-struct fileops {
- char *name;
- size_t size;
- void (*close)(struct file *);
- unsigned int (*read)(struct file *, unsigned char *, unsigned int);
- unsigned int (*write)(struct file *, unsigned char *, unsigned int);
- void (*start)(struct file *, void (*)(void *, int), void *);
- void (*stop)(struct file *);
- int (*nfds)(struct file *);
- int (*pollfd)(struct file *, struct pollfd *, int);
- int (*revents)(struct file *, struct pollfd *);
-};
-
-struct file {
- struct fileops *ops;
- struct pollfd *pfd; /* arg to poll(2) syscall */
-#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 /* hang-up on the write end */
-#define FILE_ZOMB 0x10 /* closed, but struct not freed */
-#define FILE_RINUSE 0x20 /* inside rproc->ops->in() */
-#define FILE_WINUSE 0x40 /* inside wproc->ops->out() */
- unsigned int state; /* one of above */
-#ifdef DEBUG
-#define FILE_MAXCYCLES 20
- unsigned int cycles; /* number of POLLIN/POLLOUT events */
-#endif
- char *name; /* for debug purposes */
- struct aproc *rproc, *wproc; /* reader and/or writer */
- LIST_ENTRY(file) entry;
-};
-
-LIST_HEAD(filelist,file);
-
-extern struct filelist file_list;
-extern int file_slowaccept;
-
-#ifdef DEBUG
-extern long long file_wtime, file_utime;
-#endif
-
-void timo_set(struct timo *, void (*)(void *), void *);
-void timo_add(struct timo *, unsigned int);
-void timo_del(struct timo *);
-
-void filelist_init(void);
-void filelist_done(void);
-void filelist_unlisten(void);
-
-struct file *file_new(struct fileops *, char *, unsigned int);
-void file_del(struct file *);
-void file_dbg(struct file *);
-
-void file_attach(struct file *, struct aproc *, struct aproc *);
-unsigned int file_read(struct file *, unsigned char *, unsigned int);
-unsigned int file_write(struct file *, unsigned char *, unsigned int);
-int file_poll(void);
-void file_eof(struct file *);
-void file_hup(struct file *);
-void file_close(struct file *);
-
-#endif /* !defined(FILE_H) */
+++ /dev/null
-/* $OpenBSD: headers.c,v 1.24 2015/01/16 06:40:05 deraadt 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 <err.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "aparams.h"
-#include "conf.h"
-#include "wav.h"
-
-/*
- * Encoding IDs used in .wav headers.
- */
-#define WAV_ENC_PCM 1
-#define WAV_ENC_ALAW 6
-#define WAV_ENC_ULAW 7
-#define WAV_ENC_EXT 0xfffe
-
-struct wavriff {
- char magic[4];
- uint32_t size;
- char type[4];
-} __packed;
-
-struct wavchunk {
- char id[4];
- uint32_t size;
-} __packed;
-
-struct wavfmt {
- uint16_t fmt;
- uint16_t nch;
- uint32_t rate;
- uint32_t byterate;
- uint16_t blkalign;
- uint16_t bits;
-#define WAV_FMT_SIZE 16
-#define WAV_FMT_SIZE2 (16 + 2)
-#define WAV_FMT_EXT_SIZE (16 + 24)
- uint16_t extsize;
- uint16_t valbits;
- uint32_t chanmask;
- uint16_t extfmt;
- char guid[14];
-} __packed;
-
-char wav_id_riff[4] = { 'R', 'I', 'F', 'F' };
-char wav_id_wave[4] = { 'W', 'A', 'V', 'E' };
-char wav_id_data[4] = { 'd', 'a', 't', 'a' };
-char wav_id_fmt[4] = { 'f', 'm', 't', ' ' };
-char wav_guid[14] = {
- 0x00, 0x00, 0x00, 0x00,
- 0x10, 0x00, 0x80, 0x00,
- 0x00, 0xAA, 0x00, 0x38,
- 0x9B, 0x71
-};
-
-int wav_readfmt(int, unsigned int, struct aparams *, short **);
-
-int
-wav_readfmt(int fd, unsigned int csize, struct aparams *par, short **map)
-{
- struct wavfmt fmt;
- unsigned int nch, cmax, rate, bits, bps, enc;
-
- if (csize < WAV_FMT_SIZE) {
- warnx("%u: bugus format chunk size", csize);
- return 0;
- }
- if (csize > WAV_FMT_EXT_SIZE)
- csize = WAV_FMT_EXT_SIZE;
- if (read(fd, &fmt, csize) != csize) {
- warn("riff_read: chunk");
- return 0;
- }
- enc = letoh16(fmt.fmt);
- bits = letoh16(fmt.bits);
- if (enc == WAV_ENC_EXT) {
- if (csize != WAV_FMT_EXT_SIZE) {
- warnx("missing extended format chunk in .wav file");
- return 0;
- }
- if (memcmp(fmt.guid, wav_guid, sizeof(wav_guid)) != 0) {
- warnx("unknown format (GUID) in .wav file");
- return 0;
- }
- bps = (bits + 7) / 8;
- bits = letoh16(fmt.valbits);
- enc = letoh16(fmt.extfmt);
- } else
- bps = (bits + 7) / 8;
- switch (enc) {
- case WAV_ENC_PCM:
- *map = NULL;
- break;
- case WAV_ENC_ALAW:
- *map = wav_alawmap;
- break;
- case WAV_ENC_ULAW:
- *map = wav_ulawmap;
- break;
- default:
- errx(1, "%u: unsupported encoding in .wav file", enc);
- }
- nch = letoh16(fmt.nch);
- if (nch == 0) {
- warnx("zero number of channels");
- return 0;
- }
- cmax = par->cmin + nch - 1;
- if (cmax >= NCHAN_MAX) {
- warnx("%u:%u: bad range", par->cmin, cmax);
- return 0;
- }
- rate = letoh32(fmt.rate);
- if (rate < RATE_MIN || rate > RATE_MAX) {
- warnx("%u: bad sample rate", rate);
- return 0;
- }
- if (bits == 0 || bits > 32) {
- warnx("%u: bad number of bits", bits);
- return 0;
- }
- if (bits > bps * 8) {
- warnx("%u: bits larger than bytes-per-sample", bps);
- return 0;
- }
- if (enc == WAV_ENC_PCM) {
- par->bps = bps;
- par->bits = bits;
- par->le = 1;
- par->sig = (bits <= 8) ? 0 : 1; /* ask microsoft why... */
- par->msb = 1;
- } else {
- if (bits != 8) {
- warnx("%u: mulaw/alaw encoding not 8-bit", bits);
- return 0;
- }
- par->bits = ADATA_BITS;
- par->bps = sizeof(adata_t);
- par->le = ADATA_LE;
- par->sig = 1;
- par->msb = 0;
- }
- par->cmax = cmax;
- par->rate = rate;
- return 1;
-}
-
-int
-wav_readhdr(int fd, struct aparams *par, off_t *startpos, off_t *datasz, short **map)
-{
- struct wavriff riff;
- struct wavchunk chunk;
- unsigned int csize, rsize, pos = 0;
- int fmt_done = 0;
-
- if (lseek(fd, 0, SEEK_SET) < 0) {
- warn("lseek: 0");
- return 0;
- }
- if (read(fd, &riff, sizeof(riff)) != sizeof(riff)) {
- warn("wav_readhdr: header");
- return 0;
- }
- if (memcmp(&riff.magic, &wav_id_riff, 4) != 0 ||
- memcmp(&riff.type, &wav_id_wave, 4)) {
- warnx("not a wave file");
- return 0;
- }
- rsize = letoh32(riff.size);
- for (;;) {
- if (pos + sizeof(struct wavchunk) > rsize) {
- warnx("missing data chunk");
- return 0;
- }
- if (read(fd, &chunk, sizeof(chunk)) != sizeof(chunk)) {
- warn("wav_readhdr: chunk");
- return 0;
- }
- csize = letoh32(chunk.size);
- if (memcmp(chunk.id, wav_id_fmt, 4) == 0) {
- if (!wav_readfmt(fd, csize, par, map))
- return 0;
- fmt_done = 1;
- } else if (memcmp(chunk.id, wav_id_data, 4) == 0) {
- *startpos = pos + sizeof(riff) + sizeof(chunk);
- *datasz = csize;
- break;
- } else {
-#ifdef DEBUG
- if (debug_level >= 2)
- warnx("ignoring chunk <%.4s>\n", chunk.id);
-#endif
- }
-
- /*
- * next chunk
- */
- pos += sizeof(struct wavchunk) + csize;
- if (lseek(fd, sizeof(riff) + pos, SEEK_SET) < 0) {
- warn("lseek");
- return 0;
- }
- }
- if (!fmt_done) {
- warnx("missing format chunk");
- return 0;
- }
- return 1;
-}
-
-/*
- * Write header and seek to start position
- */
-int
-wav_writehdr(int fd, struct aparams *par, off_t *startpos, off_t datasz)
-{
- unsigned int nch = par->cmax - par->cmin + 1;
- struct {
- struct wavriff riff;
- struct wavchunk fmt_hdr;
- struct wavfmt fmt;
- struct wavchunk data_hdr;
- } __packed hdr;
-
- /*
- * Check that encoding is supported by .wav file format.
- */
- if (par->bits > 8 && !par->le) {
- warnx("samples must be little endian");
- return 0;
- }
- if (8 * par->bps - par->bits >= 8) {
- warnx("padding must be less than 8 bits");
- return 0;
- }
- if ((par->bits <= 8 && par->sig) || (par->bits > 8 && !par->sig)) {
- warnx("samples with more (less) than 8 bits must be signed "
- "(unsigned)");
- return 0;
- }
- if (8 * par->bps != par->bits && !par->msb) {
- warnx("samples must be MSB justified");
- return 0;
- }
-
- memcpy(hdr.riff.magic, wav_id_riff, 4);
- memcpy(hdr.riff.type, wav_id_wave, 4);
- hdr.riff.size = htole32(datasz + sizeof(hdr) - sizeof(hdr.riff));
-
- memcpy(hdr.fmt_hdr.id, wav_id_fmt, 4);
- hdr.fmt_hdr.size = htole32(sizeof(hdr.fmt));
- hdr.fmt.fmt = htole16(1);
- hdr.fmt.nch = htole16(nch);
- hdr.fmt.rate = htole32(par->rate);
- hdr.fmt.byterate = htole32(par->rate * par->bps * nch);
- hdr.fmt.blkalign = par->bps * nch;
- hdr.fmt.bits = htole16(par->bits);
-
- memcpy(hdr.data_hdr.id, wav_id_data, 4);
- hdr.data_hdr.size = htole32(datasz);
-
- if (lseek(fd, 0, SEEK_SET) < 0) {
- warn("wav_writehdr: lseek");
- return 0;
- }
- if (write(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) {
- warn("wav_writehdr: write");
- return 0;
- }
- *startpos = sizeof(hdr);
- return 1;
-}
+++ /dev/null
-/* $OpenBSD: midi.c,v 1.46 2013/11/18 17:37:45 ratchov Exp $ */
-/*
- * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-/*
- * TODO
- *
- * use shadow variables (to save NRPNs, LSB of controller)
- * in the midi merger
- */
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "abuf.h"
-#include "aproc.h"
-#include "conf.h"
-#include "dev.h"
-#include "midi.h"
-#include "sysex.h"
-#ifdef DEBUG
-#include "dbg.h"
-#endif
-
-/*
- * input data rate is XFER / TIMO (in bytes per microsecond),
- * it must be slightly larger than the MIDI standard 3125 bytes/s
- */
-#define MIDITHRU_XFER 340
-#define MIDITHRU_TIMO 100000
-
-/*
- * masks to extract command and channel of status byte
- */
-#define MIDI_CMDMASK 0xf0
-#define MIDI_CHANMASK 0x0f
-
-/*
- * MIDI status bytes of voice messages
- */
-#define MIDI_NOFF 0x80 /* note off */
-#define MIDI_NON 0x90 /* note on */
-#define MIDI_KAT 0xa0 /* key after touch */
-#define MIDI_CTL 0xb0 /* controller */
-#define MIDI_PC 0xc0 /* program change */
-#define MIDI_CAT 0xd0 /* channel after touch */
-#define MIDI_BEND 0xe0 /* pitch bend */
-#define MIDI_ACK 0xfe /* active sensing message */
-
-/*
- * MIDI controller numbers
- */
-#define MIDI_CTLVOL 7 /* volume */
-#define MIDI_CTLPAN 11 /* pan */
-
-void midi_cb(void *);
-void midi_msg_info(struct aproc *, int, unsigned char *);
-void midi_msg_vol(struct aproc *, int, unsigned char *);
-void midi_msg_master(struct aproc *, unsigned char *);
-void midi_copy(struct abuf *, struct abuf *, unsigned char *, unsigned int);
-void midi_send(struct aproc *, struct abuf *, unsigned char *, unsigned int);
-void midi_copy_dump(struct aproc *, struct abuf *);
-void midi_onvoice(struct aproc *, struct abuf *);
-void midi_onsysex(struct aproc *, struct abuf *);
-int midi_in(struct aproc *, struct abuf *);
-int midi_out(struct aproc *, struct abuf *);
-void midi_eof(struct aproc *, struct abuf *);
-void midi_hup(struct aproc *, struct abuf *);
-void midi_newin(struct aproc *, struct abuf *);
-void midi_done(struct aproc *);
-
-/*
- * length of voice and common messages (status byte included)
- */
-unsigned int voice_len[] = { 3, 3, 3, 3, 2, 2, 3 };
-unsigned int common_len[] = { 0, 2, 3, 2, 0, 0, 1, 1 };
-
-/*
- * call-back invoked periodically to implement throttling; at each invocation
- * gain more ``tickets'' for processing. If one of the buffer was blocked by
- * the throttling mechanism, then run it
- */
-void
-midi_cb(void *addr)
-{
- struct aproc *p = (struct aproc *)addr;
- struct abuf *i, *inext;
- unsigned int tickets;
-
- timo_add(&p->u.midi.timo, MIDITHRU_TIMO);
-
- for (i = LIST_FIRST(&p->ins); i != NULL; i = inext) {
- inext = LIST_NEXT(i, ient);
- tickets = i->tickets;
- i->tickets = MIDITHRU_XFER;
- if (tickets == 0)
- abuf_run(i);
- }
-}
-
-void
-midi_msg_info(struct aproc *p, int slot, unsigned char *msg)
-{
- struct ctl_slot *s;
- struct sysex *x = (struct sysex *)msg;
-
- s = p->u.midi.dev->slot + slot;
- memset(x, 0, sizeof(struct sysex));
- x->start = SYSEX_START;
- x->type = SYSEX_TYPE_EDU;
- x->id0 = SYSEX_AUCAT;
- x->id1 = SYSEX_AUCAT_MIXINFO;
- if (*s->name != '\0') {
- snprintf((char *)x->u.mixinfo.name,
- SYSEX_NAMELEN, "%s%u", s->name, s->unit);
- }
- x->u.mixinfo.chan = slot;
- x->u.mixinfo.end = SYSEX_END;
-}
-
-void
-midi_msg_vol(struct aproc *p, int slot, unsigned char *msg)
-{
- struct ctl_slot *s;
-
- s = p->u.midi.dev->slot + slot;
- msg[0] = MIDI_CTL | slot;
- msg[1] = MIDI_CTLVOL;
- msg[2] = s->vol;
-}
-
-void
-midi_msg_master(struct aproc *p, unsigned char *msg)
-{
- struct sysex *x = (struct sysex *)msg;
-
- memset(x, 0, sizeof(struct sysex));
- x->start = SYSEX_START;
- x->type = SYSEX_TYPE_RT;
- x->id0 = SYSEX_CONTROL;
- x->id1 = SYSEX_MASTER;
- x->u.master.fine = 0;
- x->u.master.coarse = p->u.midi.dev->master;
- x->u.master.end = SYSEX_END;
-}
-
-/*
- * send a message to the given output
- */
-void
-midi_copy(struct abuf *ibuf, struct abuf *obuf, unsigned char *msg,
- unsigned int len)
-{
- unsigned int ocount;
- unsigned char *odata;
-
- if (msg[0] == SYSEX_START)
- obuf->w.midi.owner = ibuf;
- while (len > 0) {
- if (!ABUF_WOK(obuf)) {
-#ifdef DEBUG
- if (debug_level >= 3) {
- abuf_dbg(obuf);
- dbg_puts(": overrun, discarding ");
- dbg_putu(obuf->used);
- dbg_puts(" bytes\n");
- }
-#endif
- abuf_rdiscard(obuf, obuf->used);
- if (obuf->w.midi.owner == ibuf)
- obuf->w.midi.owner = NULL;
- return;
- }
- odata = abuf_wgetblk(obuf, &ocount, 0);
- if (ocount > len)
- ocount = len;
-#ifdef DEBUG
- if (debug_level >= 4) {
- abuf_dbg(obuf);
- dbg_puts(": stored ");
- dbg_putu(ocount);
- dbg_puts(" bytes\n");
- }
-#endif
- memcpy(odata, msg, ocount);
- abuf_wcommit(obuf, ocount);
- len -= ocount;
- msg += ocount;
- }
-}
-
-/*
- * flush all buffers. Since most of the MIDI traffic is broadcasted to
- * all outputs, the flush is delayed to avoid flushing all outputs for
- * each message.
- */
-void
-midi_flush(struct aproc *p)
-{
- struct abuf *i, *inext;
-
- for (i = LIST_FIRST(&p->outs); i != NULL; i = inext) {
- inext = LIST_NEXT(i, oent);
- if (ABUF_ROK(i))
- (void)abuf_flush(i);
- }
-}
-
-/*
- * broadcast a message to all output buffers on the behalf of ibuf.
- * ie. don't sent back the message to the sender
- */
-void
-midi_send(struct aproc *p, struct abuf *ibuf, unsigned char *msg,
- unsigned int len)
-{
- struct abuf *i, *inext;
-
- for (i = LIST_FIRST(&p->outs); i != NULL; i = inext) {
- inext = LIST_NEXT(i, oent);
- if (i->duplex && i->duplex == ibuf)
- continue;
- midi_copy(ibuf, i, msg, len);
- }
-}
-
-/*
- * send a quarter frame MTC message
- */
-void
-midi_send_qfr(struct aproc *p, unsigned int rate, int delta)
-{
- unsigned char buf[2];
- unsigned int data;
- int qfrlen;
-
- p->u.midi.delta += delta * MTC_SEC;
- qfrlen = rate * (MTC_SEC / (4 * p->u.midi.fps));
- while (p->u.midi.delta >= qfrlen) {
- switch (p->u.midi.qfr) {
- case 0:
- data = p->u.midi.fr & 0xf;
- break;
- case 1:
- data = p->u.midi.fr >> 4;
- break;
- case 2:
- data = p->u.midi.sec & 0xf;
- break;
- case 3:
- data = p->u.midi.sec >> 4;
- break;
- case 4:
- data = p->u.midi.min & 0xf;
- break;
- case 5:
- data = p->u.midi.min >> 4;
- break;
- case 6:
- data = p->u.midi.hr & 0xf;
- break;
- case 7:
- data = (p->u.midi.hr >> 4) | (p->u.midi.fps_id << 1);
- /*
- * tick messages are sent 2 frames ahead
- */
- p->u.midi.fr += 2;
- if (p->u.midi.fr < p->u.midi.fps)
- break;
- p->u.midi.fr -= p->u.midi.fps;
- p->u.midi.sec++;
- if (p->u.midi.sec < 60)
- break;
- p->u.midi.sec = 0;
- p->u.midi.min++;
- if (p->u.midi.min < 60)
- break;
- p->u.midi.min = 0;
- p->u.midi.hr++;
- if (p->u.midi.hr < 24)
- break;
- p->u.midi.hr = 0;
- break;
- default:
- /* NOTREACHED */
- data = 0;
- }
- buf[0] = 0xf1;
- buf[1] = (p->u.midi.qfr << 4) | data;
- p->u.midi.qfr++;
- p->u.midi.qfr &= 7;
- midi_send(p, NULL, buf, 2);
- p->u.midi.delta -= qfrlen;
- }
-}
-
-/*
- * send a full frame MTC message
- */
-void
-midi_send_full(struct aproc *p, unsigned int origin, unsigned int rate,
- unsigned int round, unsigned int pos)
-{
- unsigned char buf[10];
- unsigned int fps;
-
- p->u.midi.delta = MTC_SEC * pos;
- if (rate % (30 * 4 * round) == 0) {
- p->u.midi.fps_id = MTC_FPS_30;
- p->u.midi.fps = 30;
- } else if (rate % (25 * 4 * round) == 0) {
- p->u.midi.fps_id = MTC_FPS_25;
- p->u.midi.fps = 25;
- } else {
- p->u.midi.fps_id = MTC_FPS_24;
- p->u.midi.fps = 24;
- }
-#ifdef DEBUG
- if (debug_level >= 3) {
- aproc_dbg(p);
- dbg_puts(": mtc full frame at ");
- dbg_puti(p->u.midi.delta);
- dbg_puts(", ");
- dbg_puti(p->u.midi.fps);
- dbg_puts(" fps\n");
- }
-#endif
- fps = p->u.midi.fps;
- p->u.midi.hr = (origin / (3600 * MTC_SEC)) % 24;
- p->u.midi.min = (origin / (60 * MTC_SEC)) % 60;
- p->u.midi.sec = (origin / MTC_SEC) % 60;
- p->u.midi.fr = (origin / (MTC_SEC / fps)) % fps;
-
- buf[0] = 0xf0;
- buf[1] = 0x7f;
- buf[2] = 0x7f;
- buf[3] = 0x01;
- buf[4] = 0x01;
- buf[5] = p->u.midi.hr | (p->u.midi.fps_id << 5);
- buf[6] = p->u.midi.min;
- buf[7] = p->u.midi.sec;
- buf[8] = p->u.midi.fr;
- buf[9] = 0xf7;
- p->u.midi.qfr = 0;
- midi_send(p, NULL, buf, 10);
-}
-
-void
-midi_copy_dump(struct aproc *p, struct abuf *obuf)
-{
- unsigned int i;
- unsigned char msg[sizeof(struct sysex)];
- struct ctl_slot *s;
-
- midi_msg_master(p, msg);
- midi_copy(NULL, obuf, msg, SYSEX_SIZE(master));
- for (i = 0, s = p->u.midi.dev->slot; i < CTL_NSLOT; i++, s++) {
- midi_msg_info(p, i, msg);
- midi_copy(NULL, obuf, msg, SYSEX_SIZE(mixinfo));
- midi_msg_vol(p, i, msg);
- midi_copy(NULL, obuf, msg, 3);
- }
- msg[0] = SYSEX_START;
- msg[1] = SYSEX_TYPE_EDU;
- msg[2] = 0;
- msg[3] = SYSEX_AUCAT;
- msg[4] = SYSEX_AUCAT_DUMPEND;
- msg[5] = SYSEX_END;
- midi_copy(NULL, obuf, msg, 6);
-}
-
-/*
- * notifty the mixer that volume changed, called by whom allocated the slot using
- * ctl_slotnew(). Note: it doesn't make sense to call this from within the
- * call-back.
- */
-void
-midi_send_vol(struct aproc *p, int slot, unsigned int vol)
-{
- unsigned char msg[3];
-
- midi_msg_vol(p, slot, msg);
- midi_send(p, NULL, msg, 3);
-}
-
-void
-midi_send_master(struct aproc *p)
-{
- unsigned char msg[sizeof(struct sysex)];
-
- midi_msg_master(p, msg);
- midi_send(p, NULL, msg, SYSEX_SIZE(master));
-}
-
-void
-midi_send_slot(struct aproc *p, int slot)
-{
- unsigned char msg[sizeof(struct sysex)];
-
- midi_msg_info(p, slot, msg);
- midi_send(p, NULL, msg, SYSEX_SIZE(mixinfo));
-}
-
-/*
- * handle a MIDI voice event received from ibuf
- */
-void
-midi_onvoice(struct aproc *p, struct abuf *ibuf)
-{
- struct ctl_slot *slot;
- unsigned int chan;
-#ifdef DEBUG
- unsigned int i;
-
- if (debug_level >= 3) {
- abuf_dbg(ibuf);
- dbg_puts(": got voice event:");
- for (i = 0; i < ibuf->r.midi.idx; i++) {
- dbg_puts(" ");
- dbg_putx(ibuf->r.midi.msg[i]);
- }
- dbg_puts("\n");
- }
-#endif
- if ((ibuf->r.midi.msg[0] & MIDI_CMDMASK) == MIDI_CTL &&
- (ibuf->r.midi.msg[1] == MIDI_CTLVOL)) {
- midi_send(p, ibuf, ibuf->r.midi.msg, 3);
- chan = ibuf->r.midi.msg[0] & MIDI_CHANMASK;
- if (chan >= CTL_NSLOT)
- return;
- slot = p->u.midi.dev->slot + chan;
- slot->vol = ibuf->r.midi.msg[2];
- if (slot->ops == NULL)
- return;
- slot->ops->vol(slot->arg, slot->vol);
- }
-}
-
-/*
- * handle a MIDI sysex received from ibuf
- */
-void
-midi_onsysex(struct aproc *p, struct abuf *ibuf)
-{
- struct sysex *x;
- unsigned int fps, len;
-#ifdef DEBUG
- unsigned int i;
-
- if (debug_level >= 3) {
- abuf_dbg(ibuf);
- dbg_puts(": got sysex:");
- for (i = 0; i < ibuf->r.midi.idx; i++) {
- dbg_puts(" ");
- dbg_putx(ibuf->r.midi.msg[i]);
- }
- dbg_puts("\n");
- }
-#endif
- x = (struct sysex *)ibuf->r.midi.msg;
- len = ibuf->r.midi.idx;
- if (x->start != SYSEX_START)
- return;
- if (len < SYSEX_SIZE(empty))
- return;
- switch (x->type) {
- case SYSEX_TYPE_RT:
- if (x->id0 == SYSEX_CONTROL && x->id1 == SYSEX_MASTER) {
- if (len == SYSEX_SIZE(master)) {
- dev_master(p->u.midi.dev, x->u.master.coarse);
- midi_send(p, ibuf, (unsigned char *)x, len);
- }
- return;
- }
- if (x->id0 != SYSEX_MMC)
- return;
- switch (x->id1) {
- case SYSEX_MMC_STOP:
- if (len != SYSEX_SIZE(stop))
- return;
-#ifdef DEBUG
- if (debug_level >= 3) {
- abuf_dbg(ibuf);
- dbg_puts(": mmc stop\n");
- }
-#endif
- dev_mmcstop(p->u.midi.dev);
- break;
- case SYSEX_MMC_START:
- if (len != SYSEX_SIZE(start))
- return;
-#ifdef DEBUG
- if (debug_level >= 3) {
- abuf_dbg(ibuf);
- dbg_puts(": mmc start\n");
- }
-#endif
- dev_mmcstart(p->u.midi.dev);
- break;
- case SYSEX_MMC_LOC:
- if (len != SYSEX_SIZE(loc) ||
- x->u.loc.len != SYSEX_MMC_LOC_LEN ||
- x->u.loc.cmd != SYSEX_MMC_LOC_CMD)
- return;
- switch (x->u.loc.hr >> 5) {
- case MTC_FPS_24:
- fps = 24;
- break;
- case MTC_FPS_25:
- fps = 25;
- break;
- case MTC_FPS_30:
- fps = 30;
- break;
- default:
- /* XXX: should dev_mmcstop() here */
- return;
- }
- dev_loc(p->u.midi.dev,
- (x->u.loc.hr & 0x1f) * 3600 * MTC_SEC +
- x->u.loc.min * 60 * MTC_SEC +
- x->u.loc.sec * MTC_SEC +
- x->u.loc.fr * (MTC_SEC / fps) +
- x->u.loc.cent * (MTC_SEC / 100 / fps));
- break;
- }
- break;
- case SYSEX_TYPE_EDU:
- if (x->id0 != SYSEX_AUCAT || x->id1 != SYSEX_AUCAT_DUMPREQ)
- return;
- if (len != SYSEX_SIZE(dumpreq))
- return;
- if (ibuf->duplex)
- midi_copy_dump(p, ibuf->duplex);
- break;
- }
-}
-
-int
-midi_in(struct aproc *p, struct abuf *ibuf)
-{
- unsigned char c, *idata;
- unsigned int i, icount;
-
- if (!ABUF_ROK(ibuf))
- return 0;
- if (ibuf->tickets == 0) {
-#ifdef DEBUG
- if (debug_level >= 4) {
- abuf_dbg(ibuf);
- dbg_puts(": out of tickets, blocking\n");
- }
-#endif
- return 0;
- }
- idata = abuf_rgetblk(ibuf, &icount, 0);
- if (icount > ibuf->tickets)
- icount = ibuf->tickets;
- ibuf->tickets -= icount;
- for (i = 0; i < icount; i++) {
- c = *idata++;
- if (c >= 0xf8) {
- if (!p->u.midi.dev && c != MIDI_ACK)
- midi_send(p, ibuf, &c, 1);
- } else if (c == SYSEX_END) {
- if (ibuf->r.midi.st == SYSEX_START) {
- ibuf->r.midi.msg[ibuf->r.midi.idx++] = c;
- if (!p->u.midi.dev) {
- midi_send(p, ibuf,
- ibuf->r.midi.msg, ibuf->r.midi.idx);
- } else
- midi_onsysex(p, ibuf);
- }
- ibuf->r.midi.st = 0;
- ibuf->r.midi.idx = 0;
- } else if (c >= 0xf0) {
- ibuf->r.midi.msg[0] = c;
- ibuf->r.midi.len = common_len[c & 7];
- ibuf->r.midi.st = c;
- ibuf->r.midi.idx = 1;
- } else if (c >= 0x80) {
- ibuf->r.midi.msg[0] = c;
- ibuf->r.midi.len = voice_len[(c >> 4) & 7];
- ibuf->r.midi.st = c;
- ibuf->r.midi.idx = 1;
- } else if (ibuf->r.midi.st) {
- if (ibuf->r.midi.idx == 0 &&
- ibuf->r.midi.st != SYSEX_START) {
- ibuf->r.midi.msg[ibuf->r.midi.idx++] =
- ibuf->r.midi.st;
- }
- ibuf->r.midi.msg[ibuf->r.midi.idx++] = c;
- if (ibuf->r.midi.idx == ibuf->r.midi.len) {
- if (!p->u.midi.dev) {
- midi_send(p, ibuf,
- ibuf->r.midi.msg, ibuf->r.midi.idx);
- } else
- midi_onvoice(p, ibuf);
- if (ibuf->r.midi.st >= 0xf0)
- ibuf->r.midi.st = 0;
- ibuf->r.midi.idx = 0;
- } else if (ibuf->r.midi.idx == MIDI_MSGMAX) {
- if (!p->u.midi.dev) {
- midi_send(p, ibuf,
- ibuf->r.midi.msg, ibuf->r.midi.idx);
- }
- ibuf->r.midi.idx = 0;
- }
- }
- }
- /*
- * XXX: if the sysex is received byte by byte, partial messages
- * won't be sent until the end byte is received. On the other
- * hand we can't flush it here, since we would lose messages
- * we parse
- */
- abuf_rdiscard(ibuf, icount);
- midi_flush(p);
- return 1;
-}
-
-int
-midi_out(struct aproc *p, struct abuf *obuf)
-{
- return 0;
-}
-
-void
-midi_eof(struct aproc *p, struct abuf *ibuf)
-{
- if ((p->flags & APROC_QUIT) && LIST_EMPTY(&p->ins))
- aproc_del(p);
-}
-
-void
-midi_hup(struct aproc *p, struct abuf *obuf)
-{
- if ((p->flags & APROC_QUIT) && LIST_EMPTY(&p->ins))
- aproc_del(p);
-}
-
-void
-midi_newin(struct aproc *p, struct abuf *ibuf)
-{
- ibuf->r.midi.used = 0;
- ibuf->r.midi.len = 0;
- ibuf->r.midi.idx = 0;
- ibuf->r.midi.st = 0;
- ibuf->tickets = MIDITHRU_XFER;
-}
-
-void
-midi_done(struct aproc *p)
-{
- timo_del(&p->u.midi.timo);
-}
-
-struct aproc_ops midi_ops = {
- "midi",
- midi_in,
- midi_out,
- midi_eof,
- midi_hup,
- midi_newin,
- NULL, /* newout */
- NULL, /* ipos */
- NULL, /* opos */
- midi_done,
-};
-
-struct aproc *
-midi_new(char *name, struct dev *dev)
-{
- struct aproc *p;
-
- p = aproc_new(&midi_ops, name);
- timo_set(&p->u.midi.timo, midi_cb, p);
- timo_add(&p->u.midi.timo, MIDITHRU_TIMO);
- p->u.midi.dev = dev;
- return p;
-}
+++ /dev/null
-/* $OpenBSD: midi.h,v 1.13 2012/04/11 06:05: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 MIDI_H
-#define MIDI_H
-
-struct dev;
-
-struct aproc *midi_new(char *, struct dev *);
-
-void midi_ontick(struct aproc *, int);
-void midi_send_slot(struct aproc *, int);
-void midi_send_vol(struct aproc *, int, unsigned int);
-void midi_send_master(struct aproc *);
-void midi_send_full(struct aproc *, unsigned int, unsigned int,
- unsigned int, unsigned int);
-void midi_send_qfr(struct aproc *, unsigned int, int);
-void midi_flush(struct aproc *);
-
-#endif /* !defined(MIDI_H) */
+++ /dev/null
-/* $OpenBSD: miofile.c,v 1.9 2012/06/27 06:46: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 <sndio.h>
-
-#include "conf.h"
-#include "file.h"
-#include "miofile.h"
-#ifdef DEBUG
-#include "dbg.h"
-#endif
-
-struct miofile {
- struct file file;
- struct mio_hdl *hdl;
-};
-
-void miofile_close(struct file *);
-unsigned int miofile_read(struct file *, unsigned char *, unsigned int);
-unsigned int miofile_write(struct file *, unsigned char *, unsigned int);
-void miofile_start(struct file *);
-void miofile_stop(struct file *);
-int miofile_nfds(struct file *);
-int miofile_pollfd(struct file *, struct pollfd *, int);
-int miofile_revents(struct file *, struct pollfd *);
-
-struct fileops miofile_ops = {
- "mio",
- sizeof(struct miofile),
- miofile_close,
- miofile_read,
- miofile_write,
- NULL, /* start */
- NULL, /* stop */
- miofile_nfds,
- miofile_pollfd,
- miofile_revents
-};
-
-/*
- * open the device
- */
-struct miofile *
-miofile_new(struct fileops *ops, char *path, unsigned int mode)
-{
- struct mio_hdl *hdl;
- struct miofile *f;
-
- hdl = mio_open(path, mode, 1);
- if (hdl == NULL)
- return NULL;
- f = (struct miofile *)file_new(ops, path, mio_nfds(hdl));
- if (f == NULL)
- goto bad_close;
- f->hdl = hdl;
- return f;
- bad_close:
- mio_close(hdl);
- return NULL;
-}
-
-unsigned int
-miofile_read(struct file *file, unsigned char *data, unsigned int count)
-{
- struct miofile *f = (struct miofile *)file;
- unsigned int n;
-
- n = mio_read(f->hdl, data, count);
- if (n == 0) {
- f->file.state &= ~FILE_ROK;
- if (mio_eof(f->hdl)) {
-#ifdef DEBUG
- dbg_puts(f->file.name);
- dbg_puts(": failed to read from device\n");
-#endif
- file_eof(&f->file);
- } else {
-#ifdef DEBUG
- if (debug_level >= 4) {
- file_dbg(&f->file);
- dbg_puts(": reading blocked\n");
- }
-#endif
- }
- return 0;
- }
- return n;
-
-}
-
-unsigned int
-miofile_write(struct file *file, unsigned char *data, unsigned int count)
-{
- struct miofile *f = (struct miofile *)file;
- unsigned int n;
-
- n = mio_write(f->hdl, data, count);
- if (n == 0) {
- f->file.state &= ~FILE_WOK;
- if (mio_eof(f->hdl)) {
-#ifdef DEBUG
- dbg_puts(f->file.name);
- dbg_puts(": failed to write on device\n");
-#endif
- file_hup(&f->file);
- } else {
-#ifdef DEBUG
- if (debug_level >= 4) {
- file_dbg(&f->file);
- dbg_puts(": writing blocked\n");
- }
-#endif
- }
- return 0;
- }
- return n;
-}
-
-int
-miofile_nfds(struct file *file)
-{
- return mio_nfds(((struct miofile *)file)->hdl);
-}
-
-int
-miofile_pollfd(struct file *file, struct pollfd *pfd, int events)
-{
- return mio_pollfd(((struct miofile *)file)->hdl, pfd, events);
-}
-
-int
-miofile_revents(struct file *file, struct pollfd *pfd)
-{
- return mio_revents(((struct miofile *)file)->hdl, pfd);
-}
-
-void
-miofile_close(struct file *file)
-{
- mio_close(((struct miofile *)file)->hdl);
-}
+++ /dev/null
-/* $OpenBSD: miofile.h,v 1.3 2012/04/11 06:05: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 MIOFILE_H
-#define MIOFILE_H
-
-struct file;
-struct fileops;
-struct miofile;
-
-struct miofile *miofile_new(struct fileops *, char *, unsigned int);
-
-extern struct fileops miofile_ops;
-
-#endif /* !defined(MIOFILE_H) */
+++ /dev/null
-/*
- * 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/time.h>
-#include <sys/types.h>
-#include <sys/signal.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"
-#ifdef DEBUG
-#include "dbg.h"
-#endif
-
-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);
- if (f == NULL)
- return NULL;
- f->fd = fd;
- return f;
-}
-
-unsigned int
-pipe_read(struct file *file, unsigned char *data, unsigned int count)
-{
- struct pipe *f = (struct pipe *)file;
- int n;
-
- while ((n = read(f->fd, data, count)) < 0) {
- f->file.state &= ~FILE_ROK;
- if (errno == EAGAIN) {
-#ifdef DEBUG
- if (debug_level >= 4) {
- file_dbg(&f->file);
- dbg_puts(": reading blocked\n");
- }
-#endif
- } else {
- warn("%s", f->file.name);
- file_eof(&f->file);
- }
- return 0;
- }
- if (n == 0) {
- f->file.state &= ~FILE_ROK; /* XXX: already cleared in file_eof */
- file_eof(&f->file);
- return 0;
- }
- return n;
-}
-
-
-unsigned int
-pipe_write(struct file *file, unsigned char *data, unsigned int count)
-{
- struct pipe *f = (struct pipe *)file;
- int n;
-
- while ((n = write(f->fd, data, count)) < 0) {
- f->file.state &= ~FILE_WOK;
- if (errno == EAGAIN) {
-#ifdef DEBUG
- if (debug_level >= 4) {
- file_dbg(&f->file);
- dbg_puts(": writing blocked\n");
- }
-#endif
- } else {
- if (errno != EPIPE)
- warn("%s", f->file.name);
- file_hup(&f->file);
- }
- return 0;
- }
- 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);
- file_slowaccept = 0;
-}
-
-off_t
-pipe_endpos(struct file *file)
-{
- struct pipe *f = (struct pipe *)file;
- off_t pos;
-
- pos = lseek(f->fd, 0, SEEK_END);
- if (pos < 0) {
-#ifdef DEBUG
- file_dbg(&f->file);
- dbg_puts(": couldn't get file size\n");
-#endif
- return 0;
- }
- return pos;
-}
-
-int
-pipe_seek(struct file *file, off_t pos)
-{
- struct pipe *f = (struct pipe *)file;
- off_t newpos;
-
- newpos = lseek(f->fd, pos, SEEK_SET);
- if (newpos < 0) {
-#ifdef DEBUG
- file_dbg(&f->file);
- dbg_puts(": couldn't seek\n");
-#endif
- /* XXX: call eof() */
- return 0;
- }
- return 1;
-}
-
-int
-pipe_trunc(struct file *file, off_t pos)
-{
- struct pipe *f = (struct pipe *)file;
-
- if (ftruncate(f->fd, pos) < 0) {
-#ifdef DEBUG
- file_dbg(&f->file);
- dbg_puts(": couldn't truncate file\n");
-#endif
- /* XXX: call hup() */
- return 0;
- }
- return 1;
-}
+++ /dev/null
-/* $OpenBSD: pipe.h,v 1.6 2012/04/11 06:05: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 PIPE_H
-#define PIPE_H
-
-#include "file.h"
-
-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 int pipe_read(struct file *, unsigned char *, unsigned int);
-unsigned int pipe_write(struct file *, unsigned char *, unsigned int);
-int pipe_nfds(struct file *);
-int pipe_pollfd(struct file *, struct pollfd *, int);
-int pipe_revents(struct file *, struct pollfd *);
-int pipe_seek(struct file *, off_t);
-int pipe_trunc(struct file *, off_t);
-off_t pipe_endpos(struct file *);
-
-#endif /* !defined(PIPE_H) */
+++ /dev/null
-/* $OpenBSD: siofile.c,v 1.13 2013/11/18 17:37:45 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/time.h>
-#include <sys/types.h>
-
-#include <poll.h>
-#include <sndio.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "aparams.h"
-#include "aproc.h"
-#include "abuf.h"
-#include "conf.h"
-#include "dev.h"
-#include "file.h"
-#include "siofile.h"
-#ifdef DEBUG
-#include "dbg.h"
-#endif
-
-struct siofile {
- struct file file;
- struct sio_hdl *hdl;
- unsigned int wtickets, wbpf;
- unsigned int rtickets, rbpf;
- unsigned int bufsz;
- int started;
- void (*onmove)(void *, int);
- void *arg;
-#ifdef DEBUG
- long long wtime, utime;
-#endif
-};
-
-void siofile_close(struct file *);
-unsigned int siofile_read(struct file *, unsigned char *, unsigned int);
-unsigned int siofile_write(struct file *, unsigned char *, unsigned int);
-void siofile_start(struct file *, void (*)(void *, int), void *);
-void siofile_stop(struct file *);
-int siofile_nfds(struct file *);
-int siofile_pollfd(struct file *, struct pollfd *, int);
-int siofile_revents(struct file *, struct pollfd *);
-void siofile_cb(void *, int);
-
-struct fileops siofile_ops = {
- "sio",
- sizeof(struct siofile),
- siofile_close,
- siofile_read,
- siofile_write,
- siofile_start,
- siofile_stop,
- siofile_nfds,
- siofile_pollfd,
- siofile_revents
-};
-
-int wsio_out(struct aproc *, struct abuf *);
-int rsio_in(struct aproc *, struct abuf *);
-
-struct aproc_ops rsio_ops = {
- "rsio",
- rsio_in,
- rfile_out,
- rfile_eof,
- rfile_hup,
- NULL, /* newin */
- NULL, /* newout */
- aproc_ipos,
- aproc_opos,
- rfile_done
-};
-
-struct aproc_ops wsio_ops = {
- "wsio",
- wfile_in,
- wsio_out,
- wfile_eof,
- wfile_hup,
- NULL, /* newin */
- NULL, /* newout */
- aproc_ipos,
- aproc_opos,
- wfile_done
-};
-
-struct aproc *
-rsio_new(struct file *f)
-{
- struct aproc *p;
-
- p = aproc_new(&rsio_ops, f->name);
- p->u.io.file = f;
- p->u.io.partial = 0;
- f->rproc = p;
- return p;
-}
-
-struct aproc *
-wsio_new(struct file *f)
-{
- struct aproc *p;
-
- p = aproc_new(&wsio_ops, f->name);
- p->u.io.file = f;
- p->u.io.partial = 0;
- f->wproc = p;
- return p;
-}
-
-int
-wsio_out(struct aproc *p, struct abuf *obuf)
-{
- struct siofile *f = (struct siofile *)p->u.io.file;
-
- if (f->wtickets == 0) {
-#ifdef DEBUG
- if (debug_level >= 4) {
- file_dbg(&f->file);
- dbg_puts(": no more write tickets\n");
- }
-#endif
- f->file.state &= ~FILE_WOK;
- return 0;
- }
- return wfile_out(p, obuf);
-}
-
-int
-rsio_in(struct aproc *p, struct abuf *ibuf)
-{
- struct siofile *f = (struct siofile *)p->u.io.file;
-
- if (f->rtickets == 0) {
-#ifdef DEBUG
- if (debug_level >= 4) {
- file_dbg(&f->file);
- dbg_puts(": no more read tickets\n");
- }
-#endif
- f->file.state &= ~FILE_ROK;
- return 0;
- }
- return rfile_in(p, ibuf);
-}
-
-void
-siofile_cb(void *addr, int delta)
-{
- struct siofile *f = (struct siofile *)addr;
- struct aproc *p;
-
-#ifdef DEBUG
- if (delta < 0 || delta > (60 * RATE_MAX)) {
- file_dbg(&f->file);
- dbg_puts(": ");
- dbg_puti(delta);
- dbg_puts(": bogus sndio delta");
- dbg_panic();
- }
- if (debug_level >= 4) {
- file_dbg(&f->file);
- dbg_puts(": tick, delta = ");
- dbg_puti(delta);
- dbg_puts(", load = ");
- dbg_puti((file_utime - f->utime) / 1000);
- dbg_puts(" + ");
- dbg_puti((file_wtime - f->wtime) / 1000);
- dbg_puts("\n");
- }
- f->wtime = file_wtime;
- f->utime = file_utime;
-#endif
- if (delta != 0) {
- p = f->file.wproc;
- if (p && p->ops->opos)
- p->ops->opos(p, NULL, delta);
- p = f->file.rproc;
- if (p && p->ops->ipos)
- p->ops->ipos(p, NULL, delta);
- }
- if (f->onmove)
- f->onmove(f->arg, delta);
- f->wtickets += delta * f->wbpf;
- f->rtickets += delta * f->rbpf;
-}
-
-/*
- * Open the device.
- */
-struct siofile *
-siofile_new(struct fileops *ops, char *path, unsigned int *rmode,
- struct aparams *ipar, struct aparams *opar,
- unsigned int *bufsz, unsigned int *round)
-{
- struct sio_par par;
- struct sio_hdl *hdl;
- struct siofile *f;
- unsigned int mode = *rmode;
-
- hdl = sio_open(path, mode, 1);
- if (hdl == NULL) {
- if (mode != (SIO_PLAY | SIO_REC))
- return NULL;
- hdl = sio_open(path, SIO_PLAY, 1);
- if (hdl != NULL)
- mode = SIO_PLAY;
- else {
- hdl = sio_open(path, SIO_REC, 1);
- if (hdl != NULL)
- mode = SIO_REC;
- else
- return NULL;
- }
-#ifdef DEBUG
- if (debug_level >= 1) {
- dbg_puts("warning, device opened in ");
- dbg_puts(mode == SIO_PLAY ? "play-only" : "rec-only");
- dbg_puts(" mode\n");
- }
-#endif
- }
-
- sio_initpar(&par);
- if (mode & SIO_REC) {
- 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 + 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 (mode & SIO_PLAY)
- par.pchan = opar->cmax + 1;
- if (*bufsz)
- par.appbufsz = *bufsz;
- if (*round)
- par.round = *round;
- if (!sio_setpar(hdl, &par))
- goto bad_close;
- if (!sio_getpar(hdl, &par))
- goto bad_close;
- if (mode & SIO_REC) {
- 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->cmin = 0;
- ipar->cmax = par.rchan - 1;
- }
- if (mode & SIO_PLAY) {
- 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->cmin = 0;
- opar->cmax = par.pchan - 1;
- }
- *rmode = mode;
- *bufsz = par.bufsz;
- *round = par.round;
- f = (struct siofile *)file_new(ops, path, sio_nfds(hdl));
- if (f == NULL)
- goto bad_close;
- f->hdl = hdl;
- f->started = 0;
- f->wtickets = 0;
- f->rtickets = 0;
- f->wbpf = par.pchan * par.bps;
- f->rbpf = par.rchan * par.bps;
- f->bufsz = par.bufsz;
- sio_onmove(f->hdl, siofile_cb, f);
- return f;
- bad_close:
- sio_close(hdl);
- return NULL;
-}
-
-void
-siofile_start(struct file *file, void (*cb)(void *, int), void *arg)
-{
- struct siofile *f = (struct siofile *)file;
-
- if (!sio_start(f->hdl)) {
-#ifdef DEBUG
- dbg_puts(f->file.name);
- dbg_puts(": failed to start device\n");
-#endif
- file_close(file);
- return;
- }
- f->started = 1;
- f->wtickets = f->bufsz * f->wbpf;
- f->rtickets = 0;
-#ifdef DEBUG
- f->wtime = file_wtime;
- f->utime = file_utime;
- if (debug_level >= 3) {
- file_dbg(&f->file);
- dbg_puts(": started\n");
- }
-#endif
- f->onmove = cb;
- f->arg = arg;
-}
-
-void
-siofile_stop(struct file *file)
-{
- struct siofile *f = (struct siofile *)file;
-
- f->started = 0;
- f->onmove = NULL;
- if (!sio_eof(f->hdl) && !sio_stop(f->hdl)) {
-#ifdef DEBUG
- dbg_puts(f->file.name);
- dbg_puts(": failed to stop device\n");
-#endif
- file_close(file);
- return;
- }
-#ifdef DEBUG
- if (debug_level >= 3) {
- file_dbg(&f->file);
- dbg_puts(": stopped\n");
- }
-#endif
-}
-
-unsigned int
-siofile_read(struct file *file, unsigned char *data, unsigned int count)
-{
- struct siofile *f = (struct siofile *)file;
- unsigned int n;
-
-#ifdef DEBUG
- if (f->rtickets == 0) {
- file_dbg(&f->file);
- dbg_puts(": called with no read tickets\n");
- }
-#endif
- if (count > f->rtickets)
- count = f->rtickets;
- n = f->started ? sio_read(f->hdl, data, count) : 0;
- if (n == 0) {
- f->file.state &= ~FILE_ROK;
- if (sio_eof(f->hdl)) {
-#ifdef DEBUG
- dbg_puts(f->file.name);
- dbg_puts(": failed to read from device\n");
-#endif
- file_eof(&f->file);
- } else {
-#ifdef DEBUG
- if (debug_level >= 4) {
- file_dbg(&f->file);
- dbg_puts(": reading blocked\n");
- }
-#endif
- }
- return 0;
- } else {
- f->rtickets -= n;
- if (f->rtickets == 0) {
- f->file.state &= ~FILE_ROK;
-#ifdef DEBUG
- if (debug_level >= 4) {
- file_dbg(&f->file);
- dbg_puts(": read tickets exhausted\n");
- }
-#endif
- }
- }
- return n;
-
-}
-
-unsigned int
-siofile_write(struct file *file, unsigned char *data, unsigned int count)
-{
- struct siofile *f = (struct siofile *)file;
- unsigned int n;
-
-#ifdef DEBUG
- if (f->wtickets == 0) {
- file_dbg(&f->file);
- dbg_puts(": called with no write tickets\n");
- }
-#endif
- if (count > f->wtickets)
- count = f->wtickets;
- n = f->started ? sio_write(f->hdl, data, count) : 0;
- if (n == 0) {
- f->file.state &= ~FILE_WOK;
- if (sio_eof(f->hdl)) {
-#ifdef DEBUG
- dbg_puts(f->file.name);
- dbg_puts(": failed to write on device\n");
-#endif
- file_hup(&f->file);
- } else {
-#ifdef DEBUG
- if (debug_level >= 4) {
- file_dbg(&f->file);
- dbg_puts(": writing blocked\n");
- }
-#endif
- }
- return 0;
- } else {
- f->wtickets -= n;
- if (f->wtickets == 0) {
- f->file.state &= ~FILE_WOK;
-#ifdef DEBUG
- if (debug_level >= 4) {
- file_dbg(&f->file);
- dbg_puts(": write tickets exhausted\n");
- }
-#endif
- }
- }
- return n;
-}
-
-int
-siofile_nfds(struct file *file)
-{
- return sio_nfds(((struct siofile *)file)->hdl);
-}
-
-int
-siofile_pollfd(struct file *file, struct pollfd *pfd, int events)
-{
- struct siofile *f = (struct siofile *)file;
-
- if (!f->started)
- events &= ~(POLLIN | POLLOUT);
- return sio_pollfd(((struct siofile *)file)->hdl, pfd, events);
-}
-
-int
-siofile_revents(struct file *file, struct pollfd *pfd)
-{
- return sio_revents(((struct siofile *)file)->hdl, pfd);
-}
-
-void
-siofile_close(struct file *file)
-{
- struct siofile *f = (struct siofile *)file;
-
- if (f->started)
- siofile_stop(&f->file);
- sio_close(((struct siofile *)file)->hdl);
-}
+++ /dev/null
-/* $OpenBSD: siofile.h,v 1.6 2012/04/11 06:05: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 SIOFILE_H
-#define SIOFILE_H
-
-struct fileops;
-struct siofile;
-struct aparams;
-struct aproc;
-
-struct siofile *siofile_new(struct fileops *, char *, unsigned int *,
- struct aparams *, struct aparams *, unsigned int *, unsigned int *);
-struct aproc *rsio_new(struct file *f);
-struct aproc *wsio_new(struct file *f);
-
-extern struct fileops siofile_ops;
-
-#endif /* !defined(SIOFILE_H) */
-/* $OpenBSD: sysex.h,v 1.3 2012/03/23 11:59:54 ratchov Exp $ */
+/* $OpenBSD: sysex.h,v 1.4 2015/01/21 08:43:55 ratchov Exp $ */
/*
* Copyright (c) 2011 Alexandre Ratchov <alex@caoua.org>
*
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#ifndef AUCAT_SYSEX_H
-#define AUCAT_SYSEX_H
+#ifndef SYSEX_H
+#define SYSEX_H
#include <stdint.h>
#define SYSEX_MMC_LOC_CMD 0x01
/*
- * aucat-specific messages, in the "edu" namespace
+ * sepcial "any" midi device number
*/
-#define SYSEX_AUCAT 0x23 /* aucat-specific */
-#define SYSEX_AUCAT_MIXINFO 0x01 /* mixer info */
-#define SYSEX_AUCAT_DUMPREQ 0x02 /* dump request */
-#define SYSEX_AUCAT_DUMPEND 0x03 /* end of dump */
+#define SYSEX_DEV_ANY 0x7f
/*
* minimum size of sysex message we accept
#define SYSEX_SIZE(m) (5 + sizeof(struct sysex_ ## m))
/*
- * all possible system exclusive messages we support. For aucat-specific
- * messages we use the same header as real-time messages to simplify the
- * message parser
+ * all possible system exclusive messages we support.
*/
struct sysex {
uint8_t start;
struct sysex_loc {
uint8_t len;
uint8_t cmd;
- uint8_t hr;
+#define MTC_FPS_24 0
+#define MTC_FPS_25 1
+#define MTC_FPS_30 3
+ uint8_t hr; /* MSB contain MTC_FPS */
uint8_t min;
uint8_t sec;
uint8_t fr;
uint8_t fr;
uint8_t end;
} full;
- struct sysex_mixinfo {
- uint8_t chan; /* channel */
- uint8_t vol; /* current volume */
-#define SYSEX_NAMELEN 10 /* \0 included */
- uint8_t name[SYSEX_NAMELEN]; /* stream name */
- uint8_t end;
- } mixinfo;
- struct sysex_dumpreq {
- uint8_t end;
- } dumpreq;
- struct sysex_dumpend {
- uint8_t end;
- } dumpend;
} u;
};
-#endif /* !defined(AUCAT_SYSEX_H) */
+#endif /* !defined(SYSEX_H) */
--- /dev/null
+/* $OpenBSD: utils.c,v 1.1 2015/01/21 08:43:55 ratchov Exp $ */
+/*
+ * Copyright (c) 2003-2012 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.
+ */
+/*
+ * log_xxx() routines are used to quickly store traces into a trace buffer.
+ * This allows trances to be collected during time sensitive operations without
+ * disturbing them. The buffer can be flushed on standard error later, when
+ * slow syscalls are no longer disruptive, e.g. at the end of the poll() loop.
+ */
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include "utils.h"
+
+/*
+ * log buffer size
+ */
+#define LOG_BUFSZ 8192
+
+/*
+ * store a character in the log
+ */
+#define LOG_PUTC(c) do { \
+ if (log_used < LOG_BUFSZ) \
+ log_buf[log_used++] = (c); \
+} while (0)
+
+char log_buf[LOG_BUFSZ]; /* buffer where traces are stored */
+unsigned int log_used = 0; /* bytes used in the buffer */
+unsigned int log_sync = 1; /* if true, flush after each '\n' */
+
+/*
+ * write the log buffer on stderr
+ */
+void
+log_flush(void)
+{
+ if (log_used == 0)
+ return;
+ write(STDERR_FILENO, log_buf, log_used);
+ log_used = 0;
+}
+
+/*
+ * store a string in the log
+ */
+void
+log_puts(char *msg)
+{
+ char *p = msg;
+ int c;
+
+ while ((c = *p++) != '\0') {
+ LOG_PUTC(c);
+ if (log_sync && c == '\n')
+ log_flush();
+ }
+}
+
+/*
+ * store a hex in the log
+ */
+void
+log_putx(unsigned long num)
+{
+ char dig[sizeof(num) * 2], *p = dig, c;
+ unsigned int ndig;
+
+ if (num != 0) {
+ for (ndig = 0; num != 0; ndig++) {
+ *p++ = num & 0xf;
+ num >>= 4;
+ }
+ for (; ndig != 0; ndig--) {
+ c = *(--p);
+ c += (c < 10) ? '0' : 'a' - 10;
+ LOG_PUTC(c);
+ }
+ } else
+ LOG_PUTC('0');
+}
+
+/*
+ * store a unsigned decimal in the log
+ */
+void
+log_putu(unsigned long num)
+{
+ char dig[sizeof(num) * 3], *p = dig;
+ unsigned int ndig;
+
+ if (num != 0) {
+ for (ndig = 0; num != 0; ndig++) {
+ *p++ = num % 10;
+ num /= 10;
+ }
+ for (; ndig != 0; ndig--)
+ LOG_PUTC(*(--p) + '0');
+ } else
+ LOG_PUTC('0');
+}
+
+/*
+ * store a signed decimal in the log
+ */
+void
+log_puti(long num)
+{
+ if (num < 0) {
+ LOG_PUTC('-');
+ num = -num;
+ }
+ log_putu(num);
+}
+
+/*
+ * abort program execution after a fatal error
+ */
+void
+panic(void)
+{
+ log_flush();
+ (void)kill(getpid(), SIGABRT);
+ _exit(1);
+}
+
+/*
+ * allocate a (small) abount of memory, and abort if it fails
+ */
+void *
+xmalloc(size_t size)
+{
+ void *p;
+
+ p = malloc(size);
+ if (p == NULL) {
+ log_puts("failed to allocate ");
+ log_putx(size);
+ log_puts(" bytes\n");
+ panic();
+ }
+ return p;
+}
+
+/*
+ * free memory allocated with xmalloc()
+ */
+void
+xfree(void *p)
+{
+ free(p);
+}
+
+/*
+ * xmalloc-style strdup(3)
+ */
+char *
+xstrdup(char *s)
+{
+ size_t size;
+ void *p;
+
+ size = strlen(s) + 1;
+ p = xmalloc(size);
+ memcpy(p, s, size);
+ return p;
+}
--- /dev/null
+/* $OpenBSD: utils.h,v 1.1 2015/01/21 08:43:55 ratchov Exp $ */
+/*
+ * Copyright (c) 2003-2012 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 UTILS_H
+#define UTILS_H
+
+#include <stddef.h>
+
+void log_puts(char *);
+void log_putx(unsigned long);
+void log_putu(unsigned long);
+void log_puti(long);
+void panic(void);
+void log_flush(void);
+
+void *xmalloc(size_t);
+char *xstrdup(char *);
+void xfree(void *);
+
+/*
+ * Log levels:
+ *
+ * 0 - fatal errors: bugs, asserts, internal errors.
+ * 1 - warnings: bugs in clients, failed allocations, non-fatal errors.
+ * 2 - misc information (hardware parameters, incoming clients)
+ * 3 - structural changes (eg. new streams, new parameters ...)
+ * 4 - data blocks and messages
+ */
+extern unsigned int log_level;
+extern unsigned int log_sync;
+
+#endif
+++ /dev/null
-/*
- * 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 <fcntl.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "abuf.h"
-#include "aproc.h"
-#include "conf.h"
-#include "dev.h"
-#include "midi.h"
-#include "wav.h"
-#ifdef DEBUG
-#include "dbg.h"
-#endif
-
-void wav_dbg(struct wav *);
-void wav_conv(unsigned char *, unsigned int, short *);
-unsigned int wav_read(struct file *, unsigned char *, unsigned int);
-unsigned int wav_write(struct file *, unsigned char *, unsigned int);
-void wav_close(struct file *);
-int wav_attach(struct wav *, int);
-void wav_midiattach(struct wav *);
-void wav_allocbuf(struct wav *);
-void wav_freebuf(struct wav *);
-void wav_reset(struct wav *);
-void wav_exit(struct wav *);
-int wav_init(struct wav *);
-int wav_seekmmc(struct wav *);
-int wav_rdata(struct wav *);
-int wav_wdata(struct wav *);
-void wav_setvol(void *, unsigned int);
-void wav_startreq(void *);
-void wav_stopreq(void *);
-void wav_locreq(void *, unsigned int);
-void wav_quitreq(void *);
-int wav_autohdr(char *, struct dev *, unsigned int *, unsigned int *);
-void rwav_done(struct aproc *);
-int rwav_in(struct aproc *, struct abuf *);
-int rwav_out(struct aproc *, struct abuf *);
-struct aproc *rwav_new(struct file *);
-void wwav_done(struct aproc *);
-int wwav_in(struct aproc *, struct abuf *);
-int wwav_out(struct aproc *, struct abuf *);
-struct aproc *wwav_new(struct file *);
-
-short wav_ulawmap[256] = {
- -32124, -31100, -30076, -29052, -28028, -27004, -25980, -24956,
- -23932, -22908, -21884, -20860, -19836, -18812, -17788, -16764,
- -15996, -15484, -14972, -14460, -13948, -13436, -12924, -12412,
- -11900, -11388, -10876, -10364, -9852, -9340, -8828, -8316,
- -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140,
- -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092,
- -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004,
- -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980,
- -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436,
- -1372, -1308, -1244, -1180, -1116, -1052, -988, -924,
- -876, -844, -812, -780, -748, -716, -684, -652,
- -620, -588, -556, -524, -492, -460, -428, -396,
- -372, -356, -340, -324, -308, -292, -276, -260,
- -244, -228, -212, -196, -180, -164, -148, -132,
- -120, -112, -104, -96, -88, -80, -72, -64,
- -56, -48, -40, -32, -24, -16, -8, 0,
- 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956,
- 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764,
- 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412,
- 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316,
- 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140,
- 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092,
- 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004,
- 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980,
- 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436,
- 1372, 1308, 1244, 1180, 1116, 1052, 988, 924,
- 876, 844, 812, 780, 748, 716, 684, 652,
- 620, 588, 556, 524, 492, 460, 428, 396,
- 372, 356, 340, 324, 308, 292, 276, 260,
- 244, 228, 212, 196, 180, 164, 148, 132,
- 120, 112, 104, 96, 88, 80, 72, 64,
- 56, 48, 40, 32, 24, 16, 8, 0
-};
-
-short wav_alawmap[256] = {
- -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736,
- -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784,
- -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368,
- -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392,
- -22016, -20992, -24064, -23040, -17920, -16896, -19968, -18944,
- -30208, -29184, -32256, -31232, -26112, -25088, -28160, -27136,
- -11008, -10496, -12032, -11520, -8960, -8448, -9984, -9472,
- -15104, -14592, -16128, -15616, -13056, -12544, -14080, -13568,
- -344, -328, -376, -360, -280, -264, -312, -296,
- -472, -456, -504, -488, -408, -392, -440, -424,
- -88, -72, -120, -104, -24, -8, -56, -40,
- -216, -200, -248, -232, -152, -136, -184, -168,
- -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184,
- -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696,
- -688, -656, -752, -720, -560, -528, -624, -592,
- -944, -912, -1008, -976, -816, -784, -880, -848,
- 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736,
- 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784,
- 2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368,
- 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392,
- 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944,
- 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136,
- 11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472,
- 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568,
- 344, 328, 376, 360, 280, 264, 312, 296,
- 472, 456, 504, 488, 408, 392, 440, 424,
- 88, 72, 120, 104, 24, 8, 56, 40,
- 216, 200, 248, 232, 152, 136, 184, 168,
- 1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184,
- 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696,
- 688, 656, 752, 720, 560, 528, 624, 592,
- 944, 912, 1008, 976, 816, 784, 880, 848
-};
-
-/*
- * 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_list = NULL;
-
-int rwav_in(struct aproc *, struct abuf *);
-int rwav_out(struct aproc *, struct abuf *);
-void rwav_eof(struct aproc *, struct abuf *);
-void rwav_hup(struct aproc *, struct abuf *);
-void rwav_done(struct aproc *);
-struct aproc *rwav_new(struct file *);
-
-int wwav_in(struct aproc *, struct abuf *);
-int wwav_out(struct aproc *, struct abuf *);
-void wwav_eof(struct aproc *, struct abuf *);
-void wwav_hup(struct aproc *, struct abuf *);
-void wwav_done(struct aproc *);
-struct aproc *wwav_new(struct file *);
-
-void wav_setvol(void *, unsigned int);
-void wav_startreq(void *);
-void wav_stopreq(void *);
-void wav_locreq(void *, unsigned int);
-void wav_quitreq(void *);
-
-struct ctl_ops ctl_wavops = {
- wav_setvol,
- wav_startreq,
- wav_stopreq,
- wav_locreq,
- wav_quitreq
-};
-
-struct aproc_ops rwav_ops = {
- "rwav",
- rwav_in,
- rwav_out,
- rfile_eof,
- rfile_hup,
- NULL, /* newin */
- NULL, /* newout */
- NULL, /* ipos */
- NULL, /* opos */
- rwav_done
-};
-
-struct aproc_ops wwav_ops = {
- "wwav",
- wwav_in,
- wwav_out,
- wfile_eof,
- wfile_hup,
- NULL, /* newin */
- NULL, /* newout */
- NULL, /* ipos */
- NULL, /* opos */
- wwav_done
-};
-
-#ifdef DEBUG
-/*
- * print the given wav structure
- */
-void
-wav_dbg(struct wav *f)
-{
- static char *pstates[] = { "cfg", "ini", "sta", "rdy", "run", "mid" };
-
- dbg_puts("wav(");
- if (f->slot >= 0) {
- dbg_puts(f->dev->slot[f->slot].name);
- dbg_putu(f->dev->slot[f->slot].unit);
- } else
- dbg_puts(f->pipe.file.name);
- dbg_puts(")/");
- dbg_puts(pstates[f->pstate]);
-}
-#endif
-
-/*
- * convert ``count'' samples using the given char->short map
- */
-void
-wav_conv(unsigned char *data, unsigned int count, short *map)
-{
- unsigned int i;
- unsigned char *iptr;
- adata_t *optr;
-
- iptr = data + count;
- optr = (adata_t *)data + count;
- for (i = count; i > 0; i--) {
- --optr;
- --iptr;
- *optr = (adata_t)(map[*iptr]) << (ADATA_BITS - 16);
- }
-}
-
-/*
- * read method of the file structure
- */
-unsigned int
-wav_read(struct file *file, unsigned char *data, unsigned int count)
-{
- struct wav *f = (struct wav *)file;
- unsigned int n;
-
- if (f->map)
- count /= sizeof(adata_t);
- if (f->rbytes >= 0 && count > f->rbytes) {
- count = f->rbytes; /* file->rbytes fits in count */
- if (count == 0) {
-#ifdef DEBUG
- if (debug_level >= 3) {
- wav_dbg(f);
- dbg_puts(": read complete\n");
- }
-#endif
- if (!f->mmc)
- file_eof(&f->pipe.file);
- return 0;
- }
- }
- n = pipe_read(file, data, count);
- if (n == 0)
- return 0;
- if (f->rbytes >= 0)
- f->rbytes -= n;
- if (f->map) {
- wav_conv(data, n, f->map);
- n *= sizeof(adata_t);
- }
- return n;
-}
-
-/*
- * write method of the file structure
- */
-unsigned int
-wav_write(struct file *file, unsigned char *data, unsigned int count)
-{
- struct wav *f = (struct wav *)file;
- unsigned int n;
-
- if (f->wbytes >= 0 && count > f->wbytes) {
- count = f->wbytes; /* wbytes fits in count */
- if (count == 0) {
-#ifdef DEBUG
- if (debug_level >= 3) {
- wav_dbg(f);
- dbg_puts(": write complete\n");
- }
-#endif
- file_hup(&f->pipe.file);
- return 0;
- }
- }
- n = pipe_write(file, data, count);
- if (f->wbytes >= 0)
- f->wbytes -= n;
- f->endpos += n;
- return n;
-}
-
-/*
- * close method of the file structure
- */
-void
-wav_close(struct file *file)
-{
- struct wav *f = (struct wav *)file, **pf;
-
- if (f->mode & MODE_RECMASK) {
- pipe_trunc(&f->pipe.file, f->endpos);
- if (f->hdr == HDR_WAV) {
- wav_writehdr(f->pipe.fd,
- &f->hpar,
- &f->startpos,
- f->endpos - f->startpos);
- }
- }
- pipe_close(file);
- if (f->pstate != WAV_CFG)
- dev_unref(f->dev);
- for (pf = &wav_list; *pf != f; pf = &(*pf)->next) {
-#ifdef DEBUG
- if (*pf == NULL) {
- dbg_puts("wav_close: not on list\n");
- dbg_panic();
- }
-#endif
- }
- *pf = f->next;
-}
-
-/*
- * attach play (rec) abuf structure to the device and
- * switch to the ``RUN'' state; the play abug must not be empty
- */
-int
-wav_attach(struct wav *f, int force)
-{
- struct abuf *rbuf = NULL, *wbuf = NULL;
- struct dev *d = f->dev;
-
- if (f->mode & MODE_PLAY)
- rbuf = LIST_FIRST(&f->pipe.file.rproc->outs);
- if (f->mode & MODE_RECMASK)
- wbuf = LIST_FIRST(&f->pipe.file.wproc->ins);
- f->pstate = WAV_RUN;
-#ifdef DEBUG
- if (debug_level >= 3) {
- wav_dbg(f);
- dbg_puts(": attaching\n");
- }
-#endif
-
- /*
- * start the device (dev_getpos() and dev_attach() must
- * be called on a started device
- */
- dev_wakeup(d);
-
- dev_attach(d, f->pipe.file.name, f->mode,
- rbuf, &f->hpar, f->join ? d->opar.cmax - d->opar.cmin + 1 : 0,
- wbuf, &f->hpar, f->join ? d->ipar.cmax - d->ipar.cmin + 1 : 0,
- f->xrun, f->maxweight);
- if (f->mode & MODE_PLAY)
- dev_setvol(d, rbuf, MIDI_TO_ADATA(f->vol));
- return 1;
-}
-
-/*
- * allocate buffers, so client can start filling write-end.
- */
-void
-wav_midiattach(struct wav *f)
-{
- struct abuf *rbuf = NULL, *wbuf = NULL;
-
- if (f->mode & MODE_MIDIOUT) {
- rbuf = abuf_new(MIDI_BUFSZ, &aparams_none);
- aproc_setout(f->pipe.file.rproc, rbuf);
- }
- if (f->mode & MODE_MIDIIN) {
- wbuf = abuf_new(MIDI_BUFSZ, &aparams_none);
- aproc_setin(f->pipe.file.wproc, wbuf);
- }
- f->pstate = WAV_MIDI;
- dev_midiattach(f->dev, rbuf, wbuf);
-}
-
-/*
- * allocate the play (rec) abuf structure; if this is a
- * file to record, then attach it to the device
- *
- * XXX: buffer size should be larger than dev_bufsz, because
- * in non-server mode we don't prime play buffers with
- * silence
- */
-void
-wav_allocbuf(struct wav *f)
-{
- struct abuf *buf;
- struct dev *d = f->dev;
- unsigned int nfr;
-
- f->pstate = WAV_START;
- if (f->mode & MODE_PLAY) {
- nfr = 2 * d->bufsz * f->hpar.rate / d->rate;
- buf = abuf_new(nfr, &f->hpar);
- aproc_setout(f->pipe.file.rproc, buf);
- abuf_fill(buf);
- if (!ABUF_WOK(buf) || (f->pipe.file.state & FILE_EOF))
- f->pstate = WAV_READY;
- }
- if (f->mode & MODE_RECMASK) {
- nfr = 2 * d->bufsz * f->hpar.rate / d->rate;
- buf = abuf_new(nfr, &f->hpar);
- aproc_setin(f->pipe.file.wproc, buf);
- f->pstate = WAV_READY;
- }
-#ifdef DEBUG
- if (debug_level >= 3) {
- wav_dbg(f);
- dbg_puts(": allocating buffers\n");
- }
-#endif
- if (f->pstate == WAV_READY && dev_slotstart(d, f->slot))
- (void)wav_attach(f, 0);
-}
-
-/*
- * free abuf structure and switch to the ``INIT'' state
- */
-void
-wav_freebuf(struct wav *f)
-{
- struct abuf *rbuf = NULL, *wbuf = NULL;
-
- if (f->mode & MODE_PLAY)
- rbuf = LIST_FIRST(&f->pipe.file.rproc->outs);
- if (f->mode & MODE_RECMASK)
- wbuf = LIST_FIRST(&f->pipe.file.wproc->ins);
- f->pstate = WAV_INIT;
-#ifdef DEBUG
- if (debug_level >= 3) {
- wav_dbg(f);
- dbg_puts(": freeing buffers\n");
- }
-#endif
- if (rbuf || wbuf)
- dev_slotstop(f->dev, f->slot);
- if (rbuf)
- abuf_eof(rbuf);
- if (wbuf)
- abuf_hup(wbuf);
-}
-
-/*
- * switch to the ``INIT'' state performing
- * necessary actions to reach it
- */
-void
-wav_reset(struct wav *f)
-{
- switch (f->pstate) {
- case WAV_START:
- case WAV_READY:
- if (dev_slotstart(f->dev, f->slot))
- (void)wav_attach(f, 1);
- /* PASSTHROUGH */
- case WAV_RUN:
- wav_freebuf(f);
- /* PASSTHROUGH */
- case WAV_INIT:
- /* nothing yet */
- break;
-#ifdef DEBUG
- case WAV_MIDI:
- dbg_puts("wav_reset: in midi mode\n");
- dbg_panic();
-#endif
- }
-}
-
-/*
- * terminate the wav reader/writer
- */
-void
-wav_exit(struct wav *f)
-{
- /* XXX: call file_close() ? */
- if (f->mode & (MODE_PLAY | MODE_MIDIOUT)) {
- aproc_del(f->pipe.file.rproc);
- } else if (f->mode & (MODE_RECMASK | MODE_MIDIIN)) {
- aproc_del(f->pipe.file.wproc);
- }
-}
-
-/*
- * allocate the device
- */
-int
-wav_init(struct wav *f)
-{
- if (!dev_ref(f->dev)) {
- wav_exit(f);
- return 0;
- }
- if (!f->mmc)
- f->dev->autostart = 1;
- if (f->mode & MODE_MIDIMASK) {
- wav_midiattach(f);
- return 1;
- }
- f->slot = dev_slotnew(f->dev, "wav", &ctl_wavops, f, 1);
- f->pstate = WAV_INIT;
- if ((f->mode & f->dev->mode) != f->mode) {
-#ifdef DEBUG
- if (debug_level >= 1) {
- wav_dbg(f);
- dbg_puts(": ");
- dbg_puts(": operation not supported by device\n");
- }
-#endif
- wav_exit(f);
- return 0;
- }
- wav_allocbuf(f);
- return 1;
-}
-
-/*
- * seek to f->mmcpos and prepare to start, close
- * the file on error.
- */
-int
-wav_seekmmc(struct wav *f)
-{
- /*
- * don't go beyond the end-of-file, if so
- * put it in INIT state so it dosn't start
- */
- if (f->mmcpos > f->endpos && !(f->mode & MODE_RECMASK)) {
- wav_reset(f);
- /*
- * don't make other stream wait for us
- */
- if (f->slot >= 0)
- dev_slotstart(f->dev, f->slot);
- return 0;
- }
- if (!pipe_seek(&f->pipe.file, f->mmcpos)) {
- wav_exit(f);
- return 0;
- }
- if ((f->mode & MODE_RECMASK) && f->mmcpos > f->endpos)
- f->endpos = f->mmcpos;
- if (f->hdr == HDR_WAV)
- f->wbytes = WAV_DATAMAX - f->mmcpos;
- f->rbytes = f->endpos - f->mmcpos;
- wav_reset(f);
- wav_allocbuf(f);
- return 1;
-}
-
-/*
- * read samples from the file and possibly start it
- */
-int
-wav_rdata(struct wav *f)
-{
- struct aproc *p;
- struct abuf *obuf;
-
- p = f->pipe.file.rproc;
- obuf = LIST_FIRST(&p->outs);
- if (obuf == NULL)
- return 0;
- if (!ABUF_WOK(obuf) || !(f->pipe.file.state & FILE_ROK))
- return 0;
- if (!rfile_do(p, obuf->len, NULL))
- return 0;
- switch (f->pstate) {
- case WAV_START:
- if (!ABUF_WOK(obuf) || (f->pipe.file.state & FILE_EOF))
- f->pstate = WAV_READY;
- /* PASSTHROUGH */
- case WAV_READY:
- if (dev_slotstart(f->dev, f->slot))
- (void)wav_attach(f, 0);
- break;
- case WAV_RUN:
- break;
- case WAV_MIDI:
- return 1;
-#ifdef DEBUG
- default:
- wav_dbg(f);
- dbg_puts(": bad state\n");
- dbg_panic();
-#endif
- }
- if (f->rbytes == 0 && f->mmc) {
-#ifdef DEBUG
- if (debug_level >= 3) {
- wav_dbg(f);
- dbg_puts(": trying to restart\n");
- }
-#endif
- if (!wav_seekmmc(f))
- return 0;
- }
- return 1;
-}
-
-int
-wav_wdata(struct wav *f)
-{
- struct aproc *p;
- struct abuf *ibuf;
-
- if (!(f->pipe.file.state & FILE_WOK))
- return 0;
- p = f->pipe.file.wproc;
- ibuf = LIST_FIRST(&p->ins);
- if (ibuf == NULL)
- return 0;
- if (!ABUF_ROK(ibuf))
- return 0;
- if (!wfile_do(p, ibuf->len, NULL))
- return 0;
- return 1;
-}
-
-/*
- * callback to set the volume, invoked by the MIDI control code
- */
-void
-wav_setvol(void *arg, unsigned int vol)
-{
- struct wav *f = (struct wav *)arg;
- struct abuf *rbuf;
-
- f->vol = vol;
- if ((f->mode & MODE_PLAY) && f->pstate == WAV_RUN) {
- rbuf = LIST_FIRST(&f->pipe.file.rproc->outs);
- dev_setvol(f->dev, rbuf, MIDI_TO_ADATA(vol));
- }
-}
-
-/*
- * callback to start the stream, invoked by the MIDI control code
- */
-void
-wav_startreq(void *arg)
-{
- struct wav *f = (struct wav *)arg;
-
- switch (f->pstate) {
- case WAV_INIT:
-#ifdef DEBUG
- if (debug_level >= 2) {
- wav_dbg(f);
- dbg_puts(": skipped (failed to seek)\n");
- }
-#endif
- return;
- case WAV_READY:
- if (f->mode & MODE_RECMASK)
- f->endpos = f->mmcpos + f->startpos;
- (void)wav_attach(f, 0);
- break;
-#ifdef DEBUG
- default:
- wav_dbg(f);
- dbg_puts(": not in READY state\n");
- dbg_panic();
- break;
-#endif
- }
-}
-
-/*
- * callback to stop the stream, invoked by the MIDI control code
- */
-void
-wav_stopreq(void *arg)
-{
- struct wav *f = (struct wav *)arg;
-
-#ifdef DEBUG
- if (debug_level >= 2) {
- wav_dbg(f);
- dbg_puts(": stopping");
- if (f->pstate != WAV_INIT && (f->mode & MODE_RECMASK)) {
- dbg_puts(", ");
- dbg_putu(f->endpos);
- dbg_puts(" bytes recorded");
- }
- dbg_puts("\n");
- }
-#endif
- if (!f->mmc) {
- wav_exit(f);
- return;
- }
- (void)wav_seekmmc(f);
-}
-
-/*
- * callback to relocate the stream, invoked by the MIDI control code
- * on a stopped stream
- */
-void
-wav_locreq(void *arg, unsigned int mmc)
-{
- struct wav *f = (struct wav *)arg;
-
-#ifdef DEBUG
- if (f->pstate == WAV_RUN) {
- wav_dbg(f);
- dbg_puts(": in RUN state\n");
- dbg_panic();
- }
-#endif
- f->mmcpos = f->startpos +
- ((off_t)mmc * f->hpar.rate / MTC_SEC) * aparams_bpf(&f->hpar);
- (void)wav_seekmmc(f);
-}
-
-/*
- * Callback invoked when slot is gone
- */
-void
-wav_quitreq(void *arg)
-{
- struct wav *f = (struct wav *)arg;
-
-#ifdef DEBUG
- if (debug_level >= 3) {
- wav_dbg(f);
- dbg_puts(": slot gone\n");
- }
-#endif
- if (f->pstate != WAV_RUN)
- wav_exit(f);
-}
-
-/*
- * determine the header by the file name
- */
-int
-wav_autohdr(char *name, struct dev *dev, unsigned int *hdr, unsigned int *mode)
-{
- char *ext;
-
- if (dev->reqmode & MODE_THRU)
- *mode &= MODE_MIDIMASK;
- if (*hdr == HDR_AUTO) {
- ext = strrchr(name, '.');
- if (ext != NULL) {
- ext++;
- if (strcasecmp(ext, "wav") == 0) {
- *hdr = HDR_WAV;
- *mode &= ~MODE_MIDIMASK;
- } else if (strcasecmp(ext, "syx") == 0) {
- *hdr = HDR_RAW;
- *mode &= ~MODE_AUDIOMASK;
- }
- } else
- *hdr = HDR_RAW;
- }
- if (*mode & MODE_AUDIOMASK)
- *mode &= ~MODE_MIDIMASK;
- if (*mode == 0) {
-#ifdef DEBUG
- if (debug_level >= 1) {
- dbg_puts(name);
- dbg_puts(": requested mode not supported\n");
- }
-#endif
- return 0;
- }
- return 1;
-}
-
-/*
- * create a file reader in the ``INIT'' state
- */
-struct wav *
-wav_new_in(struct fileops *ops, struct dev *dev,
- unsigned int mode, char *name, unsigned int hdr,
- struct aparams *par, unsigned int xrun,
- unsigned int volctl, int mmc, int join)
-{
- int fd;
- struct wav *f;
-
- if (!wav_autohdr(name, dev, &hdr, &mode))
- return NULL;
- if (strcmp(name, "-") == 0) {
- fd = STDIN_FILENO;
- if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
- perror(name);
- } else {
- fd = open(name, O_RDONLY | O_NONBLOCK, 0666);
- if (fd < 0) {
- perror(name);
- return NULL;
- }
- }
- f = (struct wav *)pipe_new(ops, fd, name);
- if (f == NULL) {
- close(fd);
- return NULL;
- }
- f->mode = mode;
- f->pstate = WAV_CFG;
- f->endpos = f->startpos = 0;
- f->next = wav_list;
- wav_list = f;
- if (hdr == HDR_WAV) {
- if (!wav_readhdr(f->pipe.fd, par,
- &f->startpos, &f->rbytes, &f->map)) {
- file_del((struct file *)f);
- return NULL;
- }
- f->endpos = f->startpos + f->rbytes;
- } else {
- f->endpos = pipe_endpos(&f->pipe.file);
- if (f->endpos > 0) {
- if (!pipe_seek(&f->pipe.file, 0)) {
- file_del((struct file *)f);
- return NULL;
- }
- f->rbytes = f->endpos;
- } else
- f->rbytes = -1;
- f->map = NULL;
- }
- f->dev = dev;
- f->mmc = mmc;
- f->join = join;
- f->mode = mode;
- f->hpar = *par;
- f->hdr = hdr;
- f->xrun = xrun;
- f->maxweight = MIDI_TO_ADATA(volctl);
- f->slot = -1;
- rwav_new((struct file *)f);
-#ifdef DEBUG
- if (debug_level >= 2) {
- dbg_puts(name);
- dbg_puts(":");
- if (f->mode & MODE_PLAY) {
- dbg_puts(" playing ");
- aparams_dbg(par);
- dbg_puts(" ");
- dbg_putu(f->startpos);
- dbg_puts("..");
- dbg_putu(f->endpos);
- if (f->mmc)
- dbg_puts(", mmc");
- }
- if (f->mode & MODE_MIDIOUT)
- dbg_puts(" midi/out");
- dbg_puts("\n");
- }
-#endif
- return f;
-}
-
-/*
- * create a file writer in the ``INIT'' state
- */
-struct wav *
-wav_new_out(struct fileops *ops, struct dev *dev,
- unsigned int mode, char *name, unsigned int hdr,
- struct aparams *par, unsigned int xrun, int mmc, int join)
-{
- int fd;
- struct wav *f;
-
- if (!wav_autohdr(name, dev, &hdr, &mode))
- return NULL;
- if (strcmp(name, "-") == 0) {
- fd = STDOUT_FILENO;
- if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
- perror(name);
- } else {
- fd = open(name,
- O_WRONLY | O_TRUNC | O_CREAT | O_NONBLOCK, 0666);
- if (fd < 0) {
- perror(name);
- return NULL;
- }
- }
- f = (struct wav *)pipe_new(ops, fd, name);
- if (f == NULL) {
- close(fd);
- return NULL;
- }
- f->mode = mode;
- f->pstate = WAV_CFG;
- f->mmcpos = f->endpos = f->startpos = 0;
- f->next = wav_list;
- wav_list = f;
- if (hdr == HDR_WAV) {
- par->le = 1;
- par->sig = (par->bits <= 8) ? 0 : 1;
- par->bps = (par->bits + 7) / 8;
- if (!wav_writehdr(f->pipe.fd, par, &f->startpos, 0)) {
- file_del((struct file *)f);
- return NULL;
- }
- f->wbytes = WAV_DATAMAX;
- f->endpos = f->startpos;
- } else
- f->wbytes = -1;
- f->dev = dev;
- f->mmc = mmc;
- f->join = join;
- f->hpar = *par;
- f->hdr = hdr;
- f->xrun = xrun;
- wwav_new((struct file *)f);
-#ifdef DEBUG
- if (debug_level >= 2) {
- dbg_puts(name);
- dbg_puts(":");
- if (f->mode & MODE_RECMASK) {
- dbg_puts(" recording ");
- aparams_dbg(par);
- if (f->mmc)
- dbg_puts(", mmc");
- }
- if (f->mode & MODE_MIDIIN)
- dbg_puts(" midi/in");
- dbg_puts("\n");
- }
-#endif
- return f;
-}
-
-void
-rwav_done(struct aproc *p)
-{
- struct wav *f = (struct wav *)p->u.io.file;
-
- if (f->slot >= 0)
- dev_slotdel(f->dev, f->slot);
- f->slot = -1;
- rfile_done(p);
-}
-
-int
-rwav_in(struct aproc *p, struct abuf *ibuf_dummy)
-{
- struct wav *f = (struct wav *)p->u.io.file;
- struct abuf *obuf;
-
- if (!wav_rdata(f))
- return 0;
- obuf = LIST_FIRST(&p->outs);
- if (obuf && f->pstate >= WAV_RUN) {
- if (!abuf_flush(obuf))
- return 0;
- }
- return 1;
-}
-
-int
-rwav_out(struct aproc *p, struct abuf *obuf)
-{
- struct wav *f = (struct wav *)p->u.io.file;
-
- if (f->pipe.file.state & FILE_RINUSE)
- return 0;
- for (;;) {
- if (!wav_rdata(f))
- return 0;
- }
- return 1;
-}
-
-struct aproc *
-rwav_new(struct file *f)
-{
- struct aproc *p;
-
- p = aproc_new(&rwav_ops, f->name);
- p->u.io.file = f;
- p->u.io.partial = 0;
- f->rproc = p;
- return p;
-}
-
-void
-wwav_done(struct aproc *p)
-{
- struct wav *f = (struct wav *)p->u.io.file;
-
- if (f->slot >= 0)
- dev_slotdel(f->dev, f->slot);
- f->slot = -1;
- wfile_done(p);
-}
-
-int
-wwav_in(struct aproc *p, struct abuf *ibuf)
-{
- struct wav *f = (struct wav *)p->u.io.file;
-
- if (f->pipe.file.state & FILE_WINUSE)
- return 0;
- for (;;) {
- if (!wav_wdata(f))
- return 0;
- }
- return 1;
-}
-
-int
-wwav_out(struct aproc *p, struct abuf *obuf_dummy)
-{
- struct abuf *ibuf = LIST_FIRST(&p->ins);
- struct wav *f = (struct wav *)p->u.io.file;
-
- if (ibuf && f->pstate == WAV_RUN) {
- if (!abuf_fill(ibuf))
- return 0;
- }
- if (!wav_wdata(f))
- return 0;
- return 1;
-}
-
-struct aproc *
-wwav_new(struct file *f)
-{
- struct aproc *p;
-
- p = aproc_new(&wwav_ops, f->name);
- p->u.io.file = f;
- p->u.io.partial = 0;
- f->wproc = p;
- return p;
-}
+++ /dev/null
-/* $OpenBSD: wav.h,v 1.13 2012/04/11 06:05: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 WAV_H
-#define WAV_H
-
-#include <sys/types.h>
-
-#include "aparams.h"
-#include "pipe.h"
-
-struct wav {
- struct pipe pipe;
- struct wav *next;
-#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 int hdr; /* HDR_RAW or HDR_WAV */
- unsigned int xrun; /* xrun policy */
- 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 */
- off_t startpos; /* beginning of the data chunk */
- off_t endpos; /* end of the data chunk */
- off_t mmcpos; /* play/rec start point set by MMC */
- short *map; /* mulaw/alaw -> s16 conversion table */
- int slot; /* mixer ctl slot number */
- int mmc; /* use MMC control */
- int join; /* join/expand channels */
- unsigned int vol; /* current volume */
- unsigned int maxweight; /* dynamic range when vol == 127 */
-#define WAV_CFG 0 /* parameters read from headers */
-#define WAV_INIT 1 /* not trying to do anything */
-#define WAV_START 2 /* buffer allocated */
-#define WAV_READY 3 /* buffer filled enough */
-#define WAV_RUN 4 /* buffer attached to device */
-#define WAV_MIDI 5 /* midi "syx" file */
- unsigned int pstate; /* one of above */
- unsigned int mode; /* bitmap of MODE_* */
- struct dev *dev; /* device playing or recording */
-};
-
-extern struct fileops wav_ops;
-struct wav *wav_list;
-
-struct wav *wav_new_in(struct fileops *, struct dev *,
- unsigned int, char *, unsigned int, struct aparams *,
- unsigned int, unsigned int, int, int);
-struct wav *wav_new_out(struct fileops *, struct dev *,
- unsigned int, char *, unsigned int, struct aparams *,
- unsigned int, int, int);
-unsigned int wav_read(struct file *, unsigned char *, unsigned int);
-unsigned int wav_write(struct file *, unsigned char *, unsigned int);
-void wav_close(struct file *);
-int wav_readhdr(int, struct aparams *, off_t *, off_t *, short **);
-int wav_writehdr(int, struct aparams *, off_t *, off_t);
-void wav_conv(unsigned char *, unsigned int, short *);
-int wav_init(struct wav *);
-
-extern short wav_ulawmap[256];
-extern short wav_alawmap[256];
-
-#endif /* !defined(WAV_H) */