From: ratchov Date: Wed, 21 Jan 2015 08:43:55 +0000 (+0000) Subject: Simplify internals and rewrite file header parsing code: X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=10ba9548b8330d5929c96b7a6ca705c9ae801027;p=openbsd Simplify internals and rewrite file header parsing code: - remove -M, -t, -w, -C, -x flags that don't make sense anymore - make "-j off" the default (sndiod already does the job) - don't limit the number of played/recorded files. - add support for floating-point encoded files. - add support for apple .aiff and sun/next .au files --- diff --git a/usr.bin/aucat/Makefile b/usr.bin/aucat/Makefile index 2d35539aa2a..7f4e336a3f8 100644 --- a/usr.bin/aucat/Makefile +++ b/usr.bin/aucat/Makefile @@ -1,8 +1,7 @@ -# $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 diff --git a/usr.bin/aucat/abuf.c b/usr.bin/aucat/abuf.c index 63f7f4b261e..dd350769d07 100644 --- a/usr.bin/aucat/abuf.c +++ b/usr.bin/aucat/abuf.c @@ -1,6 +1,6 @@ -/* $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 + * Copyright (c) 2008-2012 Alexandre Ratchov * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -15,204 +15,84 @@ * 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 -#include #include #include #include #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; @@ -222,404 +102,37 @@ abuf_rdiscard(struct abuf *buf, unsigned int 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; } diff --git a/usr.bin/aucat/abuf.h b/usr.bin/aucat/abuf.h index 2440393ea4c..0c38b9f46f7 100644 --- a/usr.bin/aucat/abuf.h +++ b/usr.bin/aucat/abuf.h @@ -1,6 +1,6 @@ -/* $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 + * Copyright (c) 2008-2012 Alexandre Ratchov * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -17,106 +17,19 @@ #ifndef ABUF_H #define ABUF_H -#include - -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) */ diff --git a/usr.bin/aucat/afile.c b/usr.bin/aucat/afile.c new file mode 100644 index 00000000000..69680e7c9c7 --- /dev/null +++ b/usr.bin/aucat/afile.c @@ -0,0 +1,986 @@ +/* + * Copyright (c) 2008-2014 Alexandre Ratchov + * + * 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 +#include +#include +#include +#include +#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; +} diff --git a/usr.bin/aucat/afile.h b/usr.bin/aucat/afile.h new file mode 100644 index 00000000000..4966ed3aa25 --- /dev/null +++ b/usr.bin/aucat/afile.h @@ -0,0 +1,55 @@ +/* $OpenBSD: afile.h,v 1.1 2015/01/21 08:43:55 ratchov Exp $ */ +/* + * Copyright (c) 2008 Alexandre Ratchov + * + * 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 +#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) */ diff --git a/usr.bin/aucat/aparams.c b/usr.bin/aucat/aparams.c deleted file mode 100644 index 7fe727ec9ac..00000000000 --- a/usr.bin/aucat/aparams.c +++ /dev/null @@ -1,272 +0,0 @@ -/* $OpenBSD: aparams.c,v 1.15 2015/01/16 06:40:05 deraadt Exp $ */ -/* - * Copyright (c) 2008 Alexandre Ratchov - * - * 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 - -#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; -} diff --git a/usr.bin/aucat/aparams.h b/usr.bin/aucat/aparams.h deleted file mode 100644 index c9c59b81862..00000000000 --- a/usr.bin/aucat/aparams.h +++ /dev/null @@ -1,139 +0,0 @@ -/* $OpenBSD: aparams.h,v 1.13 2015/01/16 06:40:05 deraadt Exp $ */ -/* - * Copyright (c) 2008 Alexandre Ratchov - * - * 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) */ diff --git a/usr.bin/aucat/aproc.c b/usr.bin/aucat/aproc.c deleted file mode 100644 index f65ac2d38c1..00000000000 --- a/usr.bin/aucat/aproc.c +++ /dev/null @@ -1,2394 +0,0 @@ -/* $OpenBSD: aproc.c,v 1.74 2013/11/18 17:37:45 ratchov Exp $ */ -/* - * Copyright (c) 2008 Alexandre Ratchov - * - * 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 -#include -#include - -#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; -} diff --git a/usr.bin/aucat/aproc.h b/usr.bin/aucat/aproc.h deleted file mode 100644 index 659d756d896..00000000000 --- a/usr.bin/aucat/aproc.h +++ /dev/null @@ -1,248 +0,0 @@ -/* $OpenBSD: aproc.h,v 1.45 2013/11/18 17:37:45 ratchov Exp $ */ -/* - * Copyright (c) 2008 Alexandre Ratchov - * - * 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 - -#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) */ diff --git a/usr.bin/aucat/aucat.1 b/usr.bin/aucat/aucat.1 index 7fb89a612c4..010018b9c34 100644 --- a/usr.bin/aucat/aucat.1 +++ b/usr.bin/aucat/aucat.1 @@ -1,4 +1,4 @@ -.\" $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 .\" @@ -14,17 +14,17 @@ .\" 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 @@ -34,34 +34,32 @@ .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, @@ -77,262 +75,132 @@ Start, stop and relocate playback and recording. .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 @@ -340,27 +208,21 @@ 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 @@ -371,7 +233,7 @@ the default 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 @@ -386,7 +248,7 @@ connected to the .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, @@ -400,23 +262,22 @@ Furthermore, the MIDI sequencer could be configured to use the 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 , @@ -426,9 +287,14 @@ $ aucat -n -j off -i stereo.wav -C 0:0 -o left.wav -C 1:1 \e .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). diff --git a/usr.bin/aucat/aucat.c b/usr.bin/aucat/aucat.c index ea3a78816b3..70fbe809ffb 100644 --- a/usr.bin/aucat/aucat.c +++ b/usr.bin/aucat/aucat.c @@ -1,6 +1,5 @@ -/* $OpenBSD: aucat.c,v 1.145 2015/01/16 06:40:05 deraadt Exp $ */ /* - * Copyright (c) 2008 Alexandre Ratchov + * Copyright (c) 2008-2014 Alexandre Ratchov * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -15,459 +14,1353 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include -#include -#include - -#include -#include #include -#include -#include -#include +#include +#include #include #include #include #include #include #include - #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; } diff --git a/usr.bin/aucat/conf.h b/usr.bin/aucat/conf.h deleted file mode 100644 index ce32140ad74..00000000000 --- a/usr.bin/aucat/conf.h +++ /dev/null @@ -1,68 +0,0 @@ -/* $OpenBSD: conf.h,v 1.23 2011/10/12 07:20:04 ratchov Exp $ */ -/* - * Copyright (c) 2008 Alexandre Ratchov - * - * 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 - -/* - * 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) */ diff --git a/usr.bin/aucat/dbg.c b/usr.bin/aucat/dbg.c deleted file mode 100644 index eb5202b5c96..00000000000 --- a/usr.bin/aucat/dbg.c +++ /dev/null @@ -1,139 +0,0 @@ -#ifdef DEBUG -/* - * Copyright (c) 2003-2007 Alexandre Ratchov - * - * 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 -#include -#include -#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 diff --git a/usr.bin/aucat/dbg.h b/usr.bin/aucat/dbg.h deleted file mode 100644 index 49038abda15..00000000000 --- a/usr.bin/aucat/dbg.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifdef DEBUG -/* - * Copyright (c) 2003-2007 Alexandre Ratchov - * - * 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 */ diff --git a/usr.bin/aucat/defs.h b/usr.bin/aucat/defs.h new file mode 100644 index 00000000000..019e6541986 --- /dev/null +++ b/usr.bin/aucat/defs.h @@ -0,0 +1,34 @@ +/* $OpenBSD: defs.h,v 1.1 2015/01/21 08:43:55 ratchov Exp $ */ +/* + * Copyright (c) 2008-2012 Alexandre Ratchov + * + * 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) */ diff --git a/usr.bin/aucat/dev.c b/usr.bin/aucat/dev.c deleted file mode 100644 index d87d6d18dcf..00000000000 --- a/usr.bin/aucat/dev.c +++ /dev/null @@ -1,1735 +0,0 @@ -/* $OpenBSD: dev.c,v 1.85 2014/08/10 10:25:35 ratchov Exp $ */ -/* - * Copyright (c) 2008 Alexandre Ratchov - * - * 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 -#include -#include -#include - -#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); - } -} diff --git a/usr.bin/aucat/dev.h b/usr.bin/aucat/dev.h deleted file mode 100644 index cecedf175fd..00000000000 --- a/usr.bin/aucat/dev.h +++ /dev/null @@ -1,127 +0,0 @@ -/* $OpenBSD: dev.h,v 1.36 2012/04/11 06:05:43 ratchov Exp $ */ -/* - * Copyright (c) 2008 Alexandre Ratchov - * - * 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) */ diff --git a/usr.bin/aucat/dsp.c b/usr.bin/aucat/dsp.c new file mode 100644 index 00000000000..927e00f1971 --- /dev/null +++ b/usr.bin/aucat/dsp.c @@ -0,0 +1,863 @@ +/* $OpenBSD: dsp.c,v 1.1 2015/01/21 08:43:55 ratchov Exp $ */ +/* + * Copyright (c) 2008-2012 Alexandre Ratchov + * + * 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 +#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 +} diff --git a/usr.bin/aucat/dsp.h b/usr.bin/aucat/dsp.h new file mode 100644 index 00000000000..1fc80c2f654 --- /dev/null +++ b/usr.bin/aucat/dsp.h @@ -0,0 +1,163 @@ +/* $OpenBSD: dsp.h,v 1.1 2015/01/21 08:43:55 ratchov Exp $ */ +/* + * Copyright (c) 2012 Alexandre Ratchov + * + * 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 +#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) */ diff --git a/usr.bin/aucat/file.c b/usr.bin/aucat/file.c deleted file mode 100644 index b2e6a7f1121..00000000000 --- a/usr.bin/aucat/file.c +++ /dev/null @@ -1,775 +0,0 @@ -/* $OpenBSD: file.c,v 1.31 2013/11/18 17:37:45 ratchov Exp $ */ -/* - * Copyright (c) 2008 Alexandre Ratchov - * - * 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 -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#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); - } -} diff --git a/usr.bin/aucat/file.h b/usr.bin/aucat/file.h deleted file mode 100644 index 723bdc7cda9..00000000000 --- a/usr.bin/aucat/file.h +++ /dev/null @@ -1,97 +0,0 @@ -/* $OpenBSD: file.h,v 1.14 2012/04/11 06:05:43 ratchov Exp $ */ -/* - * Copyright (c) 2008 Alexandre Ratchov - * - * 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 -#include - -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) */ diff --git a/usr.bin/aucat/headers.c b/usr.bin/aucat/headers.c deleted file mode 100644 index b69e1c539a1..00000000000 --- a/usr.bin/aucat/headers.c +++ /dev/null @@ -1,292 +0,0 @@ -/* $OpenBSD: headers.c,v 1.24 2015/01/16 06:40:05 deraadt Exp $ */ -/* - * Copyright (c) 2008 Alexandre Ratchov - * - * 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 -#include -#include -#include -#include -#include - -#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; -} diff --git a/usr.bin/aucat/midi.c b/usr.bin/aucat/midi.c deleted file mode 100644 index 63272679420..00000000000 --- a/usr.bin/aucat/midi.c +++ /dev/null @@ -1,693 +0,0 @@ -/* $OpenBSD: midi.c,v 1.46 2013/11/18 17:37:45 ratchov Exp $ */ -/* - * Copyright (c) 2008 Alexandre Ratchov - * - * 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 -#include -#include - -#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; -} diff --git a/usr.bin/aucat/midi.h b/usr.bin/aucat/midi.h deleted file mode 100644 index 73a6fc8cd8a..00000000000 --- a/usr.bin/aucat/midi.h +++ /dev/null @@ -1,33 +0,0 @@ -/* $OpenBSD: midi.h,v 1.13 2012/04/11 06:05:43 ratchov Exp $ */ -/* - * Copyright (c) 2008 Alexandre Ratchov - * - * 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) */ diff --git a/usr.bin/aucat/miofile.c b/usr.bin/aucat/miofile.c deleted file mode 100644 index 0f205e4c105..00000000000 --- a/usr.bin/aucat/miofile.c +++ /dev/null @@ -1,162 +0,0 @@ -/* $OpenBSD: miofile.c,v 1.9 2012/06/27 06:46:44 ratchov Exp $ */ -/* - * Copyright (c) 2008 Alexandre Ratchov - * - * 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 -#include - -#include -#include -#include -#include -#include - -#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); -} diff --git a/usr.bin/aucat/miofile.h b/usr.bin/aucat/miofile.h deleted file mode 100644 index 5c59d4e1ca4..00000000000 --- a/usr.bin/aucat/miofile.h +++ /dev/null @@ -1,28 +0,0 @@ -/* $OpenBSD: miofile.h,v 1.3 2012/04/11 06:05:43 ratchov Exp $ */ -/* - * Copyright (c) 2008 Alexandre Ratchov - * - * 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) */ diff --git a/usr.bin/aucat/pipe.c b/usr.bin/aucat/pipe.c deleted file mode 100644 index efe777a6563..00000000000 --- a/usr.bin/aucat/pipe.c +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright (c) 2008 Alexandre Ratchov - * - * 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 -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#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; -} diff --git a/usr.bin/aucat/pipe.h b/usr.bin/aucat/pipe.h deleted file mode 100644 index 02c7c447f8b..00000000000 --- a/usr.bin/aucat/pipe.h +++ /dev/null @@ -1,40 +0,0 @@ -/* $OpenBSD: pipe.h,v 1.6 2012/04/11 06:05:43 ratchov Exp $ */ -/* - * Copyright (c) 2008 Alexandre Ratchov - * - * 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) */ diff --git a/usr.bin/aucat/siofile.c b/usr.bin/aucat/siofile.c deleted file mode 100644 index f6a1e77aa90..00000000000 --- a/usr.bin/aucat/siofile.c +++ /dev/null @@ -1,484 +0,0 @@ -/* $OpenBSD: siofile.c,v 1.13 2013/11/18 17:37:45 ratchov Exp $ */ -/* - * Copyright (c) 2008 Alexandre Ratchov - * - * 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 -#include - -#include -#include -#include -#include -#include - -#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); -} diff --git a/usr.bin/aucat/siofile.h b/usr.bin/aucat/siofile.h deleted file mode 100644 index 203a8abd1c1..00000000000 --- a/usr.bin/aucat/siofile.h +++ /dev/null @@ -1,32 +0,0 @@ -/* $OpenBSD: siofile.h,v 1.6 2012/04/11 06:05:43 ratchov Exp $ */ -/* - * Copyright (c) 2008 Alexandre Ratchov - * - * 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) */ diff --git a/usr.bin/aucat/sysex.h b/usr.bin/aucat/sysex.h index d4c6a43f6bb..548a2fc8727 100644 --- a/usr.bin/aucat/sysex.h +++ b/usr.bin/aucat/sysex.h @@ -1,4 +1,4 @@ -/* $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 * @@ -14,8 +14,8 @@ * 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 @@ -46,12 +46,9 @@ #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 @@ -59,9 +56,7 @@ #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; @@ -87,7 +82,10 @@ struct sysex { 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; @@ -101,20 +99,7 @@ struct sysex { 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) */ diff --git a/usr.bin/aucat/utils.c b/usr.bin/aucat/utils.c new file mode 100644 index 00000000000..b4e1f3133d1 --- /dev/null +++ b/usr.bin/aucat/utils.c @@ -0,0 +1,182 @@ +/* $OpenBSD: utils.c,v 1.1 2015/01/21 08:43:55 ratchov Exp $ */ +/* + * Copyright (c) 2003-2012 Alexandre Ratchov + * + * 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 +#include +#include +#include +#include +#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; +} diff --git a/usr.bin/aucat/utils.h b/usr.bin/aucat/utils.h new file mode 100644 index 00000000000..951df5e3e74 --- /dev/null +++ b/usr.bin/aucat/utils.h @@ -0,0 +1,46 @@ +/* $OpenBSD: utils.h,v 1.1 2015/01/21 08:43:55 ratchov Exp $ */ +/* + * Copyright (c) 2003-2012 Alexandre Ratchov + * + * 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 + +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 diff --git a/usr.bin/aucat/wav.c b/usr.bin/aucat/wav.c deleted file mode 100644 index 36c36d2abe9..00000000000 --- a/usr.bin/aucat/wav.c +++ /dev/null @@ -1,1059 +0,0 @@ -/* - * Copyright (c) 2008 Alexandre Ratchov - * - * 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 -#include -#include -#include -#include - -#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; -} diff --git a/usr.bin/aucat/wav.h b/usr.bin/aucat/wav.h deleted file mode 100644 index 2e9ca346aeb..00000000000 --- a/usr.bin/aucat/wav.h +++ /dev/null @@ -1,76 +0,0 @@ -/* $OpenBSD: wav.h,v 1.13 2012/04/11 06:05:43 ratchov Exp $ */ -/* - * Copyright (c) 2008 Alexandre Ratchov - * - * 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 - -#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) */