- release constraints used to be required in server mode

- hardcode the processing chain (it used to be setup dynamically)
- merge simpler processing code from sndiod
- remove -M option, aucat is not a midi tool, sorry.
- remove -t flag (redundant with -q flag)
- remove -w flag (makes sense for server mode only)
- remove -C flag (necessary for full-duplex streams only)
- remove -x flag (xruns already handled by sndiod)
- make "-j off" the default (sndiod already does the job)
- don't limit the number of played/recorded files.
This commit is contained in:
Alexandre Ratchov 2014-11-15 13:51:02 +01:00
parent a7f38ff59e
commit 7cbcc30319
29 changed files with 2896 additions and 10094 deletions

View File

@ -42,10 +42,7 @@ clean:
# ---------------------------------------------------------- dependencies ---
OBJS = \
abuf.o aparams.o aproc.o aucat.o dbg.o dev.o file.o headers.o \
midi.o miofile.o pipe.o siofile.o \
wav.o
OBJS = abuf.o aucat.o dsp.o utils.o wav.o
aucat: ${OBJS}
${CC} ${LDFLAGS} ${LIB} -o aucat ${OBJS} ${LDADD}
@ -53,25 +50,9 @@ aucat: ${OBJS}
.c.o:
${CC} ${CFLAGS} ${INCLUDE} ${DEFS} -c $<
abuf.o: abuf.c abuf.h aparams.h aproc.h file.h conf.h dbg.h
aparams.o: aparams.c aparams.h dbg.h
aproc.o: aproc.c abuf.h aparams.h aproc.h file.h conf.h midi.h \
dbg.h
aucat.o: aucat.c abuf.h ../libsndio/amsg.h aparams.h aproc.h \
file.h conf.h dev.h midi.h wav.h pipe.h dbg.h \
abuf.o: abuf.c abuf.h utils.h
aucat.o: aucat.c abuf.h dsp.h defs.h sysex.h utils.h wav.h \
../bsd-compat/bsd-compat.h
dbg.o: dbg.c dbg.h
dev.o: dev.c abuf.h aproc.h aparams.h file.h conf.h dev.h \
pipe.h miofile.h siofile.h midi.h dbg.h \
../bsd-compat/bsd-compat.h
file.o: file.c abuf.h aproc.h aparams.h file.h conf.h dbg.h
headers.o: headers.c aparams.h conf.h wav.h pipe.h file.h \
../bsd-compat/bsd-compat.h
midi.o: midi.c abuf.h aproc.h aparams.h file.h conf.h dev.h \
midi.h sysex.h dbg.h ../bsd-compat/bsd-compat.h
miofile.o: miofile.c conf.h file.h miofile.h dbg.h
pipe.o: pipe.c conf.h pipe.h file.h dbg.h
siofile.o: siofile.c aparams.h aproc.h file.h abuf.h conf.h dev.h \
siofile.h dbg.h
wav.o: wav.c abuf.h aproc.h aparams.h file.h conf.h dev.h \
midi.h wav.h pipe.h dbg.h
dsp.o: dsp.c dsp.h defs.h utils.h
utils.o: utils.c utils.h
wav.o: wav.c utils.h wav.h dsp.h defs.h

View File

@ -1,6 +1,6 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
* Copyright (c) 2008-2012 Alexandre Ratchov <alex@caoua.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -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 <err.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "abuf.h"
#include "aparams.h"
#include "aproc.h"
#include "conf.h"
#ifdef DEBUG
#include "dbg.h"
#endif
void abuf_dump(struct abuf *);
int abuf_flush_do(struct abuf *);
int abuf_fill_do(struct abuf *);
void abuf_eof_do(struct abuf *);
void abuf_hup_do(struct abuf *);
#include "utils.h"
#ifdef DEBUG
void
abuf_dbg(struct abuf *buf)
abuf_log(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");
}
log_putu(buf->start);
log_puts("+");
log_putu(buf->used);
log_puts("/");
log_putu(buf->len);
}
#endif
void
abuf_dump(struct abuf *buf)
abuf_init(struct abuf *buf, unsigned int len)
{
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");
}
#endif
struct abuf *
abuf_new(unsigned int nfr, struct aparams *par)
{
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);
xfree(buf->data);
buf->data = (void *)0xdeadbeef;
}
/*
* 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;
}
/*
* 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;
}

View File

@ -1,6 +1,6 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
* Copyright (c) 2008-2012 Alexandre Ratchov <alex@caoua.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -17,106 +17,19 @@
#ifndef ABUF_H
#define ABUF_H
#include <sys/queue.h>
struct aproc;
struct aparams;
struct abuf {
LIST_ENTRY(abuf) ient; /* reader's list of inputs entry */
LIST_ENTRY(abuf) oent; /* writer's list of outputs entry */
/*
* fifo parameters
*/
unsigned int bpf; /* bytes per frame */
unsigned int cmin, cmax; /* channel range of this buf */
unsigned int start; /* offset where data starts */
unsigned int used; /* valid data */
unsigned int len; /* size of the ring */
struct aproc *rproc; /* reader */
struct aproc *wproc; /* writer */
struct abuf *duplex; /* link to buffer of the other dir */
unsigned int inuse; /* in abuf_{flush,fill,run}() */
unsigned int tickets; /* max data to (if throttling) */
/*
* Misc reader aproc-specific per-buffer parameters.
*/
union {
struct {
int weight; /* dynamic range */
int maxweight; /* max dynamic range allowed */
unsigned int vol; /* volume within the vol */
unsigned int done; /* frames ready */
unsigned int xrun; /* underrun policy */
int drop; /* to drop on next read */
} mix;
struct {
unsigned int st; /* MIDI running status */
unsigned int used; /* bytes used from ``msg'' */
unsigned int idx; /* actual MIDI message size */
unsigned int len; /* MIDI message length */
#define MIDI_MSGMAX 16 /* max size of MIDI msg */
unsigned char msg[MIDI_MSGMAX];
} midi;
} r;
/*
* Misc reader aproc-specific per-buffer parameters.
*/
union {
struct {
unsigned int todo; /* frames to process */
} mix;
struct {
unsigned int done; /* frames copied */
unsigned int xrun; /* one of XRUN_XXX */
int silence; /* to add on next write */
} sub;
struct {
struct abuf *owner; /* current input stream */
} midi;
} w;
int start; /* offset (frames) where stored data starts */
int used; /* frames stored in the buffer */
int len; /* total size of the buffer (frames) */
unsigned char *data;
};
/*
* the buffer contains at least one frame. This macro should
* be used to check if the buffer can be flushed
*/
#define ABUF_ROK(b) ((b)->used > 0)
/*
* there's room for at least one frame
*/
#define ABUF_WOK(b) ((b)->len - (b)->used > 0)
/*
* the buffer is empty and has no writer anymore
*/
#define ABUF_EOF(b) (!ABUF_ROK(b) && (b)->wproc == NULL)
/*
* the buffer has no reader anymore, note that it's not
* enough the buffer to be disconnected, because it can
* be not yet connected buffer (eg. socket play buffer)
*/
#define ABUF_HUP(b) (!ABUF_WOK(b) && (b)->rproc == NULL)
struct abuf *abuf_new(unsigned int, struct aparams *);
void abuf_del(struct abuf *);
void abuf_dbg(struct abuf *);
void abuf_clear(struct abuf *);
unsigned char *abuf_rgetblk(struct abuf *, unsigned int *, unsigned int);
unsigned char *abuf_wgetblk(struct abuf *, unsigned int *, unsigned int);
void abuf_rdiscard(struct abuf *, unsigned int);
void abuf_wcommit(struct abuf *, unsigned int);
int abuf_fill(struct abuf *);
int abuf_flush(struct abuf *);
void abuf_eof(struct abuf *);
void abuf_hup(struct abuf *);
void abuf_run(struct abuf *);
void abuf_ipos(struct abuf *, int);
void abuf_opos(struct abuf *, int);
void abuf_init(struct abuf *, unsigned int);
void abuf_done(struct abuf *);
void abuf_log(struct abuf *);
unsigned char *abuf_rgetblk(struct abuf *, int *);
unsigned char *abuf_wgetblk(struct abuf *, int *);
void abuf_rdiscard(struct abuf *, int);
void abuf_wcommit(struct abuf *, int);
#endif /* !defined(ABUF_H) */

View File

@ -1,270 +0,0 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "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;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,248 +0,0 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef APROC_H
#define APROC_H
#include <sys/queue.h>
#include "aparams.h"
#include "file.h"
struct abuf;
struct aproc;
struct file;
struct aproc_ops {
/*
* Name of the ops structure, ie type of the unit.
*/
char *name;
/*
* The state of the given input abuf changed (eg. an input block
* is ready for processing). This function must get the block
* from the input, process it and remove it from the buffer.
*
* Processing the block will result in a change of the state of
* OTHER buffers that are attached to the aproc (eg. the output
* buffer was filled), thus this routine MUST notify ALL aproc
* structures that are waiting on it; most of the time this
* means just calling abuf_flush() on the output buffer.
*/
int (*in)(struct aproc *, struct abuf *);
/*
* The state of the given output abuf changed (eg. space for a
* new output block was made available) so processing can
* continue. This function must process more input in order to
* fill the output block.
*
* Producing a block will result in the change of the state of
* OTHER buffers that are attached to the aproc, thus this
* routine MUST notify ALL aproc structures that are waiting on
* it; most of the time this means calling abuf_fill() on the
* source buffers.
*
* Before filling input buffers (using abuf_fill()), this
* routine must ALWAYS check for eof condition, and if needed,
* handle it appropriately and call abuf_hup() to free the input
* buffer.
*/
int (*out)(struct aproc *, struct abuf *);
/*
* The input buffer is empty and we can no more receive data
* from it. The buffer will be destroyed as soon as this call
* returns so the abuf pointer will stop being valid after this
* call returns. There's no need to drain the buffer because the
* in() call-back was just called before.
*
* If this call reads and/or writes data on other buffers,
* abuf_flush() and abuf_fill() must be called appropriately.
*/
void (*eof)(struct aproc *, struct abuf *);
/*
* The output buffer can no more accept data (it should be
* considered as full). After this function returns, it will be
* destroyed and the "abuf" pointer will be no more valid.
*/
void (*hup)(struct aproc *, struct abuf *);
/*
* A new input was connected.
*/
void (*newin)(struct aproc *, struct abuf *);
/*
* A new output was connected
*/
void (*newout)(struct aproc *, struct abuf *);
/*
* Real-time record position changed (for input buffer),
* by the given amount of _frames_.
*/
void (*ipos)(struct aproc *, struct abuf *, int);
/*
* Real-time play position changed (for output buffer),
* by the given amount of _frames_.
*/
void (*opos)(struct aproc *, struct abuf *, int);
/*
* Destroy the aproc, called just before to free the
* aproc structure.
*/
void (*done)(struct aproc *);
};
/*
* The aproc structure represents a simple audio processing unit; they are
* interconnected by abuf structures and form a kind of "circuit". The circuit
* cannot have loops.
*/
struct aproc {
char *name; /* for debug purposes */
struct aproc_ops *ops; /* call-backs */
LIST_HEAD(, abuf) ins; /* list of inputs */
LIST_HEAD(, abuf) outs; /* list of outputs */
unsigned int refs; /* extern references */
#define APROC_ZOMB 1 /* destroyed but not freed */
#define APROC_QUIT 2 /* try to terminate if unused */
#define APROC_DROP 4 /* xrun if capable */
unsigned int flags;
union { /* follow type-specific data */
struct { /* file/device io */
struct file *file; /* file to read/write */
unsigned int partial; /* bytes of partial frame */
} io;
struct {
unsigned int idle; /* frames since idleing */
unsigned int round; /* block size, for xruns */
int lat; /* current latency */
int maxlat; /* max latency allowed */
unsigned int abspos; /* frames produced */
struct aproc *mon; /* snoop output */
unsigned int autovol; /* adjust volume dynamically */
int master; /* master attenuation */
} mix;
struct {
unsigned int idle; /* frames since idleing */
unsigned int round; /* block size, for xruns */
int lat; /* current latency */
int maxlat; /* max latency allowed */
unsigned int abspos; /* frames consumed */
} sub;
struct {
int delta; /* time position */
unsigned int bufsz; /* buffer size (latency) */
unsigned int pending; /* uncommited samples */
} mon;
struct {
#define RESAMP_NCTX 2
unsigned int ctx_start;
adata_t ctx[NCHAN_MAX * RESAMP_NCTX];
unsigned int iblksz, oblksz;
int diff;
int idelta, odelta; /* remainder of resamp_xpos */
} resamp;
struct {
int bfirst; /* bytes to skip at startup */
unsigned int bps; /* bytes per sample */
unsigned int shift; /* shift to get 32bit MSB */
int sigbit; /* sign bits to XOR */
int bnext; /* to reach the next byte */
int snext; /* to reach the next sample */
} conv;
struct {
struct dev *dev; /* controlled device */
struct timo timo; /* timout for throtteling */
unsigned int fps; /* MTC frames per second */
#define MTC_FPS_24 0
#define MTC_FPS_25 1
#define MTC_FPS_30 3
unsigned int fps_id; /* one of above */
unsigned int hr; /* MTC hours */
unsigned int min; /* MTC minutes */
unsigned int sec; /* MTC seconds */
unsigned int fr; /* MTC frames */
unsigned int qfr; /* MTC quarter frames */
int delta; /* rel. to the last MTC tick */
} midi;
} u;
};
/*
* Check if the given pointer is a valid aproc structure.
*
* aproc structures are not free()'d immediately, because
* there may be pointers to them, instead the APROC_ZOMB flag
* is set which means that they should not be used. When
* aprocs reference counter reaches zero, they are actually
* freed
*/
#define APROC_OK(p) ((p) && !((p)->flags & APROC_ZOMB))
struct aproc *aproc_new(struct aproc_ops *, char *);
void aproc_del(struct aproc *);
void aproc_dbg(struct aproc *);
void aproc_setin(struct aproc *, struct abuf *);
void aproc_setout(struct aproc *, struct abuf *);
int aproc_inuse(struct aproc *);
int aproc_depend(struct aproc *, struct aproc *);
void aproc_ipos(struct aproc *, struct abuf *, int);
void aproc_opos(struct aproc *, struct abuf *, int);
struct aproc *rfile_new(struct file *);
struct aproc *wfile_new(struct file *);
struct aproc *mix_new(char *, int, unsigned int, unsigned int, unsigned int);
struct aproc *sub_new(char *, int, unsigned int);
struct aproc *resamp_new(char *, unsigned int, unsigned int);
struct aproc *enc_new(char *, struct aparams *);
struct aproc *dec_new(char *, struct aparams *);
struct aproc *join_new(char *);
struct aproc *mon_new(char *, unsigned int);
int rfile_in(struct aproc *, struct abuf *);
int rfile_out(struct aproc *, struct abuf *);
void rfile_eof(struct aproc *, struct abuf *);
void rfile_hup(struct aproc *, struct abuf *);
void rfile_done(struct aproc *);
int rfile_do(struct aproc *, unsigned int, unsigned int *);
int wfile_in(struct aproc *, struct abuf *);
int wfile_out(struct aproc *, struct abuf *);
void wfile_eof(struct aproc *, struct abuf *);
void wfile_hup(struct aproc *, struct abuf *);
void wfile_done(struct aproc *);
int wfile_do(struct aproc *, unsigned int, unsigned int *);
void mix_setmaster(struct aproc *);
void mix_clear(struct aproc *);
void mix_prime(struct aproc *);
void mix_quit(struct aproc *);
void mix_drop(struct abuf *, int);
void sub_silence(struct abuf *, int);
void sub_clear(struct aproc *);
void mon_snoop(struct aproc *, struct abuf *, unsigned int, unsigned int);
void mon_clear(struct aproc *);
#endif /* !defined(APROC_H) */

View File

@ -19,12 +19,11 @@
.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 Fl c Ar min : Ns Ar max
.Op Fl e Ar enc
.Op Fl f Ar device
@ -34,34 +33,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,
@ -78,191 +75,56 @@ Start, stop and relocate playback and recording.
The options are as follows:
.Bl -tag -width Ds
.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 .
.It Fl i Ar file
Add this file to the list of streams to play.
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.
If the flag is
.Cm off
then stream channels are routed to the corresponding
device channel, possibly discarding channels not present in the device.
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.
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.
.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.
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.
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.
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 .
.It Fl r Ar rate
Sample rate in Hertz of the stream.
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.
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
.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
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
Audio file type.
The following file types are supported:
.Bl -tag -width auto
.It Ar raw
Headerless file.
This format is recommended since it has no limitations.
.It wav
.It Ar wav
Microsoft WAVE file format.
There are limitations inherent to the file format itself:
not all encodings are supported,
@ -270,69 +132,74 @@ 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
.It Ar auto
Try to guess, depending on the file name.
This is the default.
.El
.It Fl i Ar file
Play this audio file.
If the option argument is
.Sq -
then standard input will be used.
.It Fl j Ar flag
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 each source channel is routed to a single destination channel,
possibly discarding channels.
If the flag is
.Cm on ,
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 off .
.It Fl n
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
Record into this audio file.
If the option argument is
.Sq -
then standard output will be used.
.It Fl q Ar port
Control audio device properties through this MIDI port.
This includes per-stream volumes and the ability to
synchronously start, stop and relocate audio files.
.It Fl r Ar rate
Sample rate in Hertz of the audio file.
The default is
.Cm 48000 .
.It Fl v Ar volume
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.
.El
.Pp
Encodings are specified using the
.Fl e
option.
The following encodings are supported:
On the command line,
per-file parameters
.Pq Fl cehjrv
must precede the file definition
.Pq Fl io .
.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
If
.Nm
is sent
.Dv SIGHUP ,
.Dv SIGINT
or
.Dv SIGTERM ,
it terminates recording to files.
.Sh MIDI CONTROL
.Nm
can be controlled through MIDI
@ -340,27 +207,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 +232,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 +247,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 +261,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 +286,4 @@ $ 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.
.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.
Resampling is low quality.

File diff suppressed because it is too large Load Diff

View File

@ -1,139 +0,0 @@
#ifdef DEBUG
/*
* Copyright (c) 2003-2007 Alexandre Ratchov <alex@caoua.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* dbg_xxx() routines are used to quickly store traces into a trace buffer.
* This allows trances to be collected during time sensitive operations without
* disturbing them. The buffer can be flushed on standard error later, when
* slow syscalls are no longer disruptive, e.g. at the end of the poll() loop.
*/
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include "dbg.h"
/*
* size of the buffer where traces are stored
*/
#define DBG_BUFSZ 8192
/*
* store a character in the trace buffer
*/
#define DBG_PUTC(c) do { \
if (dbg_used < DBG_BUFSZ) \
dbg_buf[dbg_used++] = (c); \
} while (0)
char dbg_buf[DBG_BUFSZ]; /* buffer where traces are stored */
unsigned int dbg_used = 0; /* bytes used in the buffer */
unsigned int dbg_sync = 1; /* if true, flush after each '\n' */
/*
* write debug info buffer on stderr
*/
void
dbg_flush(void)
{
if (dbg_used == 0)
return;
write(STDERR_FILENO, dbg_buf, dbg_used);
dbg_used = 0;
}
/*
* store a string in the debug buffer
*/
void
dbg_puts(char *msg)
{
char *p = msg;
int c;
while ((c = *p++) != '\0') {
DBG_PUTC(c);
if (dbg_sync && c == '\n')
dbg_flush();
}
}
/*
* store a hex in the debug buffer
*/
void
dbg_putx(unsigned long num)
{
char dig[sizeof(num) * 2], *p = dig, c;
unsigned int ndig;
if (num != 0) {
for (ndig = 0; num != 0; ndig++) {
*p++ = num & 0xf;
num >>= 4;
}
for (; ndig != 0; ndig--) {
c = *(--p);
c += (c < 10) ? '0' : 'a' - 10;
DBG_PUTC(c);
}
} else
DBG_PUTC('0');
}
/*
* store a decimal in the debug buffer
*/
void
dbg_putu(unsigned long num)
{
char dig[sizeof(num) * 3], *p = dig;
unsigned int ndig;
if (num != 0) {
for (ndig = 0; num != 0; ndig++) {
*p++ = num % 10;
num /= 10;
}
for (; ndig != 0; ndig--)
DBG_PUTC(*(--p) + '0');
} else
DBG_PUTC('0');
}
/*
* store a signed integer in the trace buffer
*/
void
dbg_puti(long num)
{
if (num < 0) {
DBG_PUTC('-');
num = -num;
}
dbg_putu(num);
}
/*
* abort program execution after a fatal error, we should
* put code here to backup user data
*/
void
dbg_panic(void)
{
dbg_flush();
abort();
}
#endif

View File

@ -1,31 +0,0 @@
#ifdef DEBUG
/*
* Copyright (c) 2003-2007 Alexandre Ratchov <alex@caoua.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef MIDISH_DBG_H
#define MIDISH_DBG_H
void dbg_puts(char *);
void dbg_putx(unsigned long);
void dbg_putu(unsigned long);
void dbg_puti(long);
void dbg_panic(void);
void dbg_flush(void);
extern unsigned int dbg_sync;
#endif /* MIDISH_DBG_H */
#endif /* DEBUG */

View File

@ -1,6 +1,6 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
* Copyright (c) 2008-2012 Alexandre Ratchov <alex@caoua.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -14,15 +14,21 @@
* 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
#ifndef DEFS_H
#define DEFS_H
struct file;
struct fileops;
struct miofile;
/*
* units used for MTC clock.
*/
#define MTC_SEC 2400 /* 1 second is 2400 ticks */
struct miofile *miofile_new(struct fileops *, char *, unsigned int);
/*
* 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 */
extern struct fileops miofile_ops;
#endif /* !defined(MIOFILE_H) */
#endif /* !defined(DEFS_H) */

File diff suppressed because it is too large Load Diff

View File

@ -1,127 +0,0 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef DEV_H
#define DEV_H
#include "aparams.h"
struct aproc;
struct abuf;
struct dev {
struct dev *next;
/*
* desired parameters
*/
unsigned int reqmode; /* mode */
struct aparams reqipar, reqopar; /* parameters */
unsigned int reqbufsz; /* buffer size */
unsigned int reqround; /* block size */
unsigned int hold; /* hold the device open ? */
unsigned int autovol; /* auto adjust playvol ? */
unsigned int autostart; /* don't wait for MMC start */
unsigned int refcnt; /* number of openers */
#define DEV_NMAX 16 /* max number of devices */
unsigned int num; /* serial number */
#define DEV_CLOSED 0 /* closed */
#define DEV_INIT 1 /* stopped */
#define DEV_START 2 /* ready to start */
#define DEV_RUN 3 /* started */
unsigned int pstate; /* on of DEV_xxx */
char *path; /* sio path */
/*
* actual parameters and runtime state (i.e. once opened)
*/
unsigned int mode; /* bitmap of MODE_xxx */
unsigned int bufsz, round, rate;
struct aparams ipar, opar;
struct aproc *mix, *sub, *submon;
struct aproc *rec, *play, *mon;
struct aproc *midi;
struct devctl {
struct devctl *next;
unsigned int mode;
char *path;
} *ctl_list;
/* volume control and MMC/MTC */
#define CTL_NSLOT 8
#define CTL_NAMEMAX 8
unsigned int serial;
struct ctl_slot {
struct ctl_ops {
void (*vol)(void *, unsigned int);
void (*start)(void *);
void (*stop)(void *);
void (*loc)(void *, unsigned int);
void (*quit)(void *);
} *ops;
void *arg;
unsigned int unit;
char name[CTL_NAMEMAX];
unsigned int serial;
unsigned int vol;
unsigned int tstate;
} slot[CTL_NSLOT];
#define CTL_OFF 0 /* ignore MMC messages */
#define CTL_STOP 1 /* stopped, can't start */
#define CTL_START 2 /* attempting to start */
#define CTL_RUN 3 /* started */
unsigned int tstate; /* one of above */
unsigned int origin; /* MTC start time */
unsigned int master; /* master volume controller */
};
extern struct dev *dev_list;
void dev_dbg(struct dev *);
int dev_init(struct dev *);
int dev_run(struct dev *);
int dev_ref(struct dev *);
void dev_unref(struct dev *);
void dev_del(struct dev *);
void dev_wakeup(struct dev *);
void dev_drain(struct dev *);
struct dev *dev_new(char *, unsigned int, unsigned int,
unsigned int, unsigned int, unsigned int);
void dev_adjpar(struct dev *, unsigned int,
struct aparams *, struct aparams *);
int devctl_add(struct dev *, char *, unsigned int);
void dev_midiattach(struct dev *, struct abuf *, struct abuf *);
unsigned int dev_roundof(struct dev *, unsigned int);
int dev_getpos(struct dev *);
void dev_attach(struct dev *, char *, unsigned int,
struct abuf *, struct aparams *, unsigned int,
struct abuf *, struct aparams *, unsigned int,
unsigned int, int);
void dev_setvol(struct dev *, struct abuf *, int);
void dev_slotdbg(struct dev *, int);
int dev_slotnew(struct dev *, char *, struct ctl_ops *, void *, int);
void dev_slotdel(struct dev *, int);
void dev_slotvol(struct dev *, int, unsigned int);
int dev_slotstart(struct dev *, int);
void dev_slotstop(struct dev *, int);
void dev_mmcstart(struct dev *);
void dev_mmcstop(struct dev *);
void dev_loc(struct dev *, unsigned int);
void dev_master(struct dev *, unsigned int);
#endif /* !define(DEV_H) */

701
aucat/dsp.c Normal file
View File

@ -0,0 +1,701 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2008-2012 Alexandre Ratchov <alex@caoua.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <string.h>
#include "dsp.h"
#include "utils.h"
int aparams_ctltovol[128] = {
0,
256, 266, 276, 287, 299, 310, 323, 335,
348, 362, 376, 391, 406, 422, 439, 456,
474, 493, 512, 532, 553, 575, 597, 621,
645, 670, 697, 724, 753, 782, 813, 845,
878, 912, 948, 985, 1024, 1064, 1106, 1149,
1195, 1241, 1290, 1341, 1393, 1448, 1505, 1564,
1625, 1689, 1756, 1825, 1896, 1971, 2048, 2128,
2212, 2299, 2389, 2483, 2580, 2682, 2787, 2896,
3010, 3128, 3251, 3379, 3511, 3649, 3792, 3941,
4096, 4257, 4424, 4598, 4778, 4966, 5161, 5363,
5574, 5793, 6020, 6256, 6502, 6757, 7023, 7298,
7585, 7883, 8192, 8514, 8848, 9195, 9556, 9931,
10321, 10726, 11148, 11585, 12040, 12513, 13004, 13515,
14045, 14596, 15170, 15765, 16384, 17027, 17696, 18390,
19112, 19863, 20643, 21453, 22295, 23170, 24080, 25025,
26008, 27029, 28090, 29193, 30339, 31530, 32768
};
/*
* 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;
}
}
/*
* 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
}

View File

@ -1,6 +1,6 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
* Copyright (c) 2012 Alexandre Ratchov <alex@caoua.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -14,40 +14,11 @@
* 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
#ifndef DSP_H
#define DSP_H
#include <sys/param.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 */
};
#include <sys/types.h>
#include "defs.h"
/*
* Samples are numbers in the interval [-1, 1[, note that 1, the upper
@ -62,14 +33,12 @@ struct aparams {
#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 short adata_t;
typedef int adata_t;
#elif ADATA_BITS == 24
#if defined(__i386__) && defined(__GNUC__)
@ -116,26 +85,77 @@ fp24_muldiv(int x, int a, int b)
#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
#define MIDI_MAXCTL 127
/*
* 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];
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 *);
void aparams_init(struct aparams *);
void aparams_log(struct aparams *);
int aparams_strtoenc(struct aparams *, char *);
int aparams_enctostr(struct aparams *, char *);
void aparams_copyenc(struct aparams *, struct aparams *);
int aparams_native(struct aparams *);
#endif /* !defined(APARAMS_H) */
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_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) */

View File

@ -1,775 +0,0 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* non-blocking file i/o module: each file can be read or written (or
* both). To achieve non-blocking io, we simply use the poll() syscall
* in an event loop. If a read() or write() syscall return EAGAIN
* (operation will block), then the file is marked as "for polling", else
* the file is not polled again.
*
* the module also provides trivial timeout implementation,
* derived from:
*
* anoncvs@moule.caoua.org:/midish
*
* midish/timo.c rev 1.18
* midish/mdep.c rev 1.71
*
* A timeout is used to schedule the call of a routine (the callback)
* there is a global list of timeouts that is processed inside the
* event loop. Timeouts work as follows:
*
* first the timo structure must be initialized with timo_set()
*
* then the timeout is scheduled (only once) with timo_add()
*
* if the timeout expires, the call-back is called; then it can
* be scheduled again if needed. It's OK to reschedule it again
* from the callback
*
* the timeout can be aborted with timo_del(), it is OK to try to
* abort a timout that has expired
*
*/
#include <sys/time.h>
#include <sys/types.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "abuf.h"
#include "aproc.h"
#include "conf.h"
#include "file.h"
#ifdef DEBUG
#include "dbg.h"
#endif
#define MAXFDS 100
#define TIMER_USEC 10000
void timo_update(unsigned int);
void timo_init(void);
void timo_done(void);
void file_sigalrm(int);
struct timespec file_ts;
struct filelist file_list;
struct timo *timo_queue;
unsigned int timo_abstime;
int file_slowaccept = 0;
#ifdef DEBUG
long long file_wtime, file_utime;
#endif
/*
* initialise a timeout structure, arguments are callback and argument
* that will be passed to the callback
*/
void
timo_set(struct timo *o, void (*cb)(void *), void *arg)
{
o->cb = cb;
o->arg = arg;
o->set = 0;
}
/*
* schedule the callback in 'delta' 24-th of microseconds. The timeout
* must not be already scheduled
*/
void
timo_add(struct timo *o, unsigned int delta)
{
struct timo **i;
unsigned int val;
int diff;
#ifdef DEBUG
if (o->set) {
dbg_puts("timo_add: already set\n");
dbg_panic();
}
if (delta == 0) {
dbg_puts("timo_add: zero timeout is evil\n");
dbg_panic();
}
#endif
val = timo_abstime + delta;
for (i = &timo_queue; *i != NULL; i = &(*i)->next) {
diff = (*i)->val - val;
if (diff > 0) {
break;
}
}
o->set = 1;
o->val = val;
o->next = *i;
*i = o;
}
/*
* abort a scheduled timeout
*/
void
timo_del(struct timo *o)
{
struct timo **i;
for (i = &timo_queue; *i != NULL; i = &(*i)->next) {
if (*i == o) {
*i = o->next;
o->set = 0;
return;
}
}
#ifdef DEBUG
if (debug_level >= 4)
dbg_puts("timo_del: not found\n");
#endif
}
/*
* routine to be called by the timer when 'delta' 24-th of microsecond
* elapsed. This routine updates time referece used by timeouts and
* calls expired timeouts
*/
void
timo_update(unsigned int delta)
{
struct timo *to;
int diff;
/*
* update time reference
*/
timo_abstime += delta;
/*
* remove from the queue and run expired timeouts
*/
while (timo_queue != NULL) {
/*
* there is no overflow here because + and - are
* modulo 2^32, they are the same for both signed and
* unsigned integers
*/
diff = timo_queue->val - timo_abstime;
if (diff > 0)
break;
to = timo_queue;
timo_queue = to->next;
to->set = 0;
to->cb(to->arg);
}
}
/*
* initialize timeout queue
*/
void
timo_init(void)
{
timo_queue = NULL;
timo_abstime = 0;
}
/*
* destroy timeout queue
*/
void
timo_done(void)
{
#ifdef DEBUG
if (timo_queue != NULL) {
dbg_puts("timo_done: timo_queue not empty!\n");
dbg_panic();
}
#endif
timo_queue = (struct timo *)0xdeadbeef;
}
#ifdef DEBUG
void
file_dbg(struct file *f)
{
dbg_puts(f->ops->name);
dbg_puts("(");
dbg_puts(f->name);
dbg_puts("|");
if (f->state & FILE_ROK)
dbg_puts("r");
if (f->state & FILE_RINUSE)
dbg_puts("R");
if (f->state & FILE_WOK)
dbg_puts("w");
if (f->state & FILE_WINUSE)
dbg_puts("W");
if (f->state & FILE_EOF)
dbg_puts("e");
if (f->state & FILE_HUP)
dbg_puts("h");
if (f->state & FILE_ZOMB)
dbg_puts("Z");
dbg_puts(")");
}
#endif
struct file *
file_new(struct fileops *ops, char *name, unsigned int nfds)
{
struct file *f;
LIST_FOREACH(f, &file_list, entry)
nfds += f->ops->nfds(f);
if (nfds > MAXFDS) {
#ifdef DEBUG
if (debug_level >= 1) {
dbg_puts(name);
dbg_puts(": too many polled files\n");
}
#endif
return NULL;
}
f = malloc(ops->size);
if (f == NULL)
err(1, "file_new: %s", ops->name);
f->ops = ops;
f->name = name;
f->state = 0;
#ifdef DEBUG
f->cycles = 0;
#endif
f->rproc = NULL;
f->wproc = NULL;
LIST_INSERT_HEAD(&file_list, f, entry);
#ifdef DEBUG
if (debug_level >= 3) {
file_dbg(f);
dbg_puts(": created\n");
}
#endif
return f;
}
void
file_del(struct file *f)
{
#ifdef DEBUG
if (debug_level >= 3) {
file_dbg(f);
dbg_puts(": terminating...\n");
}
#endif
if (f->state & (FILE_RINUSE | FILE_WINUSE)) {
f->state |= FILE_ZOMB;
} else {
LIST_REMOVE(f, entry);
#ifdef DEBUG
if (debug_level >= 3) {
file_dbg(f);
dbg_puts(": destroyed\n");
}
#endif
f->ops->close(f);
free(f);
}
}
int
file_poll(void)
{
nfds_t nfds, n;
short events, revents;
struct pollfd pfds[MAXFDS];
struct file *f, *fnext;
struct aproc *p;
struct timespec ts;
#ifdef DEBUG
struct timespec sleepts;
#endif
long long delta_nsec;
int res;
if (LIST_EMPTY(&file_list) && timo_queue == NULL) {
#ifdef DEBUG
if (debug_level >= 3)
dbg_puts("nothing to do...\n");
#endif
return 0;
}
/*
* Fill the pfds[] array with files that are blocked on reading
* and/or writing, skipping those that are just waiting.
*/
#ifdef DEBUG
dbg_flush();
if (debug_level >= 4)
dbg_puts("poll:");
#endif
nfds = 0;
LIST_FOREACH(f, &file_list, entry) {
events = 0;
if (f->rproc && !(f->state & FILE_ROK))
events |= POLLIN;
if (f->wproc && !(f->state & FILE_WOK))
events |= POLLOUT;
#ifdef DEBUG
if (debug_level >= 4) {
dbg_puts(" ");
file_dbg(f);
}
#endif
n = f->ops->pollfd(f, pfds + nfds, events);
if (n == 0) {
f->pfd = NULL;
continue;
}
f->pfd = pfds + nfds;
nfds += n;
}
#ifdef DEBUG
if (debug_level >= 4) {
dbg_puts("\npfds[] =");
for (n = 0; n < nfds; n++) {
dbg_puts(" ");
dbg_putx(pfds[n].events);
}
dbg_puts("\n");
}
#endif
#ifdef DEBUG
clock_gettime(CLOCK_MONOTONIC, &sleepts);
file_utime += 1000000000LL * (sleepts.tv_sec - file_ts.tv_sec);
file_utime += sleepts.tv_nsec - file_ts.tv_nsec;
#endif
res = poll(pfds, nfds, -1);
if (res < 0 && errno != EINTR)
err(1, "poll");
clock_gettime(CLOCK_MONOTONIC, &ts);
#ifdef DEBUG
file_wtime += 1000000000LL * (ts.tv_sec - sleepts.tv_sec);
file_wtime += ts.tv_nsec - sleepts.tv_nsec;
#endif
delta_nsec = 1000000000LL * (ts.tv_sec - file_ts.tv_sec);
delta_nsec += ts.tv_nsec - file_ts.tv_nsec;
#ifdef DEBUG
if (delta_nsec < 0)
dbg_puts("file_poll: negative time interval\n");
#endif
file_ts = ts;
if (delta_nsec >= 0 && delta_nsec < 1000000000LL)
timo_update(delta_nsec / 1000);
else {
#ifdef DEBUG
if (debug_level >= 1)
dbg_puts("ignored huge clock delta\n");
#endif
}
if (res <= 0)
return 1;
f = LIST_FIRST(&file_list);
while (f != NULL) {
if (f->pfd == NULL) {
f = LIST_NEXT(f, entry);
continue;
}
revents = f->ops->revents(f, f->pfd);
#ifdef DEBUG
if (revents) {
f->cycles++;
if (f->cycles > FILE_MAXCYCLES) {
file_dbg(f);
dbg_puts(": busy loop, disconnecting\n");
revents = POLLHUP;
}
}
#endif
if (!(f->state & FILE_ZOMB) && (revents & POLLIN)) {
revents &= ~POLLIN;
#ifdef DEBUG
if (debug_level >= 4) {
file_dbg(f);
dbg_puts(": rok\n");
}
#endif
f->state |= FILE_ROK;
f->state |= FILE_RINUSE;
for (;;) {
p = f->rproc;
if (!p)
break;
#ifdef DEBUG
if (debug_level >= 4) {
aproc_dbg(p);
dbg_puts(": in\n");
}
#endif
if (!p->ops->in(p, NULL))
break;
}
f->state &= ~FILE_RINUSE;
}
if (!(f->state & FILE_ZOMB) && (revents & POLLOUT)) {
revents &= ~POLLOUT;
#ifdef DEBUG
if (debug_level >= 4) {
file_dbg(f);
dbg_puts(": wok\n");
}
#endif
f->state |= FILE_WOK;
f->state |= FILE_WINUSE;
for (;;) {
p = f->wproc;
if (!p)
break;
#ifdef DEBUG
if (debug_level >= 4) {
aproc_dbg(p);
dbg_puts(": out\n");
}
#endif
if (!p->ops->out(p, NULL))
break;
}
f->state &= ~FILE_WINUSE;
}
if (!(f->state & FILE_ZOMB) && (revents & POLLHUP)) {
#ifdef DEBUG
if (debug_level >= 3) {
file_dbg(f);
dbg_puts(": disconnected\n");
}
#endif
f->state |= (FILE_EOF | FILE_HUP);
}
if (!(f->state & FILE_ZOMB) && (f->state & FILE_EOF)) {
#ifdef DEBUG
if (debug_level >= 3) {
file_dbg(f);
dbg_puts(": eof\n");
}
#endif
p = f->rproc;
if (p) {
f->state |= FILE_RINUSE;
#ifdef DEBUG
if (debug_level >= 3) {
aproc_dbg(p);
dbg_puts(": eof\n");
}
#endif
p->ops->eof(p, NULL);
f->state &= ~FILE_RINUSE;
}
f->state &= ~FILE_EOF;
}
if (!(f->state & FILE_ZOMB) && (f->state & FILE_HUP)) {
#ifdef DEBUG
if (debug_level >= 3) {
file_dbg(f);
dbg_puts(": hup\n");
}
#endif
p = f->wproc;
if (p) {
f->state |= FILE_WINUSE;
#ifdef DEBUG
if (debug_level >= 3) {
aproc_dbg(p);
dbg_puts(": hup\n");
}
#endif
p->ops->hup(p, NULL);
f->state &= ~FILE_WINUSE;
}
f->state &= ~FILE_HUP;
}
fnext = LIST_NEXT(f, entry);
if (f->state & FILE_ZOMB)
file_del(f);
f = fnext;
}
if (LIST_EMPTY(&file_list) && timo_queue == NULL) {
#ifdef DEBUG
if (debug_level >= 3)
dbg_puts("no files anymore...\n");
#endif
return 0;
}
return 1;
}
/*
* handler for SIGALRM, invoked periodically
*/
void
file_sigalrm(int i)
{
/* nothing to do, we only want poll() to return EINTR */
}
void
filelist_init(void)
{
static struct sigaction sa;
struct itimerval it;
sigset_t set;
sigemptyset(&set);
(void)sigaddset(&set, SIGPIPE);
if (sigprocmask(SIG_BLOCK, &set, NULL))
err(1, "sigprocmask");
LIST_INIT(&file_list);
if (clock_gettime(CLOCK_MONOTONIC, &file_ts) < 0) {
perror("clock_gettime");
exit(1);
}
sa.sa_flags = SA_RESTART;
sa.sa_handler = file_sigalrm;
sigfillset(&sa.sa_mask);
if (sigaction(SIGALRM, &sa, NULL) < 0) {
perror("sigaction");
exit(1);
}
it.it_interval.tv_sec = 0;
it.it_interval.tv_usec = TIMER_USEC;
it.it_value.tv_sec = 0;
it.it_value.tv_usec = TIMER_USEC;
if (setitimer(ITIMER_REAL, &it, NULL) < 0) {
perror("setitimer");
exit(1);
}
timo_init();
#ifdef DEBUG
dbg_sync = 0;
#endif
}
void
filelist_done(void)
{
struct itimerval it;
#ifdef DEBUG
struct file *f;
if (!LIST_EMPTY(&file_list)) {
LIST_FOREACH(f, &file_list, entry) {
file_dbg(f);
dbg_puts(" not closed\n");
}
dbg_panic();
}
dbg_sync = 1;
dbg_flush();
#endif
timerclear(&it.it_value);
timerclear(&it.it_interval);
if (setitimer(ITIMER_REAL, &it, NULL) < 0) {
perror("setitimer");
exit(1);
}
timo_done();
}
unsigned int
file_read(struct file *f, unsigned char *data, unsigned int count)
{
unsigned int n;
#ifdef DEBUG
struct timespec ts0, ts1;
long us;
if (!(f->state & FILE_ROK)) {
file_dbg(f);
dbg_puts(": read: bad state\n");
dbg_panic();
}
clock_gettime(CLOCK_MONOTONIC, &ts0);
#endif
n = f->ops->read(f, data, count);
#ifdef DEBUG
if (n > 0)
f->cycles = 0;
clock_gettime(CLOCK_MONOTONIC, &ts1);
us = 1000000L * (ts1.tv_sec - ts0.tv_sec);
us += (ts1.tv_nsec - ts0.tv_nsec) / 1000;
if (debug_level >= 4 || (debug_level >= 2 && us >= 5000)) {
dbg_puts(f->name);
dbg_puts(": read ");
dbg_putu(n);
dbg_puts(" bytes in ");
dbg_putu(us);
dbg_puts("us\n");
}
#endif
return n;
}
unsigned int
file_write(struct file *f, unsigned char *data, unsigned int count)
{
unsigned int n;
#ifdef DEBUG
struct timespec ts0, ts1;
long us;
if (!(f->state & FILE_WOK)) {
file_dbg(f);
dbg_puts(": write: bad state\n");
dbg_panic();
}
clock_gettime(CLOCK_MONOTONIC, &ts0);
#endif
n = f->ops->write(f, data, count);
#ifdef DEBUG
if (n > 0)
f->cycles = 0;
clock_gettime(CLOCK_MONOTONIC, &ts1);
us = 1000000L * (ts1.tv_sec - ts0.tv_sec);
us += (ts1.tv_nsec - ts0.tv_nsec) / 1000;
if (debug_level >= 4 || (debug_level >= 2 && us >= 5000)) {
dbg_puts(f->name);
dbg_puts(": wrote ");
dbg_putu(n);
dbg_puts(" bytes in ");
dbg_putu(us);
dbg_puts("us\n");
}
#endif
return n;
}
void
file_eof(struct file *f)
{
struct aproc *p;
#ifdef DEBUG
if (debug_level >= 3) {
file_dbg(f);
dbg_puts(": eof requested\n");
}
#endif
if (!(f->state & (FILE_RINUSE | FILE_WINUSE))) {
p = f->rproc;
if (p) {
f->state |= FILE_RINUSE;
#ifdef DEBUG
if (debug_level >= 3) {
aproc_dbg(p);
dbg_puts(": eof\n");
}
#endif
p->ops->eof(p, NULL);
f->state &= ~FILE_RINUSE;
}
if (f->state & FILE_ZOMB)
file_del(f);
} else {
f->state &= ~FILE_ROK;
f->state |= FILE_EOF;
}
}
void
file_hup(struct file *f)
{
struct aproc *p;
#ifdef DEBUG
if (debug_level >= 3) {
file_dbg(f);
dbg_puts(": hup requested\n");
}
#endif
if (!(f->state & (FILE_RINUSE | FILE_WINUSE))) {
p = f->wproc;
if (p) {
f->state |= FILE_WINUSE;
#ifdef DEBUG
if (debug_level >= 3) {
aproc_dbg(p);
dbg_puts(": hup\n");
}
#endif
p->ops->hup(p, NULL);
f->state &= ~FILE_WINUSE;
}
if (f->state & FILE_ZOMB)
file_del(f);
} else {
f->state &= ~FILE_WOK;
f->state |= FILE_HUP;
}
}
void
file_close(struct file *f)
{
struct aproc *p;
#ifdef DEBUG
if (debug_level >= 3) {
file_dbg(f);
dbg_puts(": closing\n");
}
#endif
if (f->wproc == NULL && f->rproc == NULL)
f->state |= FILE_ZOMB;
if (!(f->state & (FILE_RINUSE | FILE_WINUSE))) {
p = f->rproc;
if (p) {
f->state |= FILE_RINUSE;
#ifdef DEBUG
if (debug_level >= 3) {
aproc_dbg(p);
dbg_puts(": eof\n");
}
#endif
p->ops->eof(p, NULL);
f->state &= ~FILE_RINUSE;
}
p = f->wproc;
if (p) {
f->state |= FILE_WINUSE;
#ifdef DEBUG
if (debug_level >= 3) {
aproc_dbg(p);
dbg_puts(": hup\n");
}
#endif
p->ops->hup(p, NULL);
f->state &= ~FILE_WINUSE;
}
if (f->state & FILE_ZOMB)
file_del(f);
} else {
f->state &= ~(FILE_ROK | FILE_WOK);
f->state |= (FILE_EOF | FILE_HUP);
}
}

View File

@ -1,97 +0,0 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef FILE_H
#define FILE_H
#include <sys/queue.h>
#include <sys/types.h>
struct file;
struct aproc;
struct pollfd;
struct timo {
struct timo *next;
unsigned int val; /* time to wait before the callback */
unsigned int set; /* true if the timeout is set */
void (*cb)(void *arg); /* routine to call on expiration */
void *arg; /* argument to give to 'cb' */
};
struct fileops {
char *name;
size_t size;
void (*close)(struct file *);
unsigned int (*read)(struct file *, unsigned char *, unsigned int);
unsigned int (*write)(struct file *, unsigned char *, unsigned int);
void (*start)(struct file *, void (*)(void *, int), void *);
void (*stop)(struct file *);
int (*nfds)(struct file *);
int (*pollfd)(struct file *, struct pollfd *, int);
int (*revents)(struct file *, struct pollfd *);
};
struct file {
struct fileops *ops;
struct pollfd *pfd; /* arg to poll(2) syscall */
#define FILE_ROK 0x1 /* file readable */
#define FILE_WOK 0x2 /* file writable */
#define FILE_EOF 0x4 /* eof on the read end */
#define FILE_HUP 0x8 /* hang-up on the write end */
#define FILE_ZOMB 0x10 /* closed, but struct not freed */
#define FILE_RINUSE 0x20 /* inside rproc->ops->in() */
#define FILE_WINUSE 0x40 /* inside wproc->ops->out() */
unsigned int state; /* one of above */
#ifdef DEBUG
#define FILE_MAXCYCLES 20
unsigned int cycles; /* number of POLLIN/POLLOUT events */
#endif
char *name; /* for debug purposes */
struct aproc *rproc, *wproc; /* reader and/or writer */
LIST_ENTRY(file) entry;
};
LIST_HEAD(filelist,file);
extern struct filelist file_list;
extern int file_slowaccept;
#ifdef DEBUG
extern long long file_wtime, file_utime;
#endif
void timo_set(struct timo *, void (*)(void *), void *);
void timo_add(struct timo *, unsigned int);
void timo_del(struct timo *);
void filelist_init(void);
void filelist_done(void);
void filelist_unlisten(void);
struct file *file_new(struct fileops *, char *, unsigned int);
void file_del(struct file *);
void file_dbg(struct file *);
void file_attach(struct file *, struct aproc *, struct aproc *);
unsigned int file_read(struct file *, unsigned char *, unsigned int);
unsigned int file_write(struct file *, unsigned char *, unsigned int);
int file_poll(void);
void file_eof(struct file *);
void file_hup(struct file *);
void file_close(struct file *);
#endif /* !defined(FILE_H) */

View File

@ -1,295 +0,0 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/param.h>
#include <err.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "aparams.h"
#include "conf.h"
#include "wav.h"
#include "bsd-compat.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;
}

View File

@ -1,694 +0,0 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* TODO
*
* use shadow variables (to save NRPNs, LSB of controller)
* in the midi merger
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "abuf.h"
#include "aproc.h"
#include "conf.h"
#include "dev.h"
#include "midi.h"
#include "sysex.h"
#ifdef DEBUG
#include "dbg.h"
#endif
#include "bsd-compat.h"
/*
* input data rate is XFER / TIMO (in bytes per microsecond),
* it must be slightly larger than the MIDI standard 3125 bytes/s
*/
#define MIDITHRU_XFER 340
#define MIDITHRU_TIMO 100000
/*
* 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;
}

View File

@ -1,33 +0,0 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef MIDI_H
#define MIDI_H
struct dev;
struct aproc *midi_new(char *, struct dev *);
void midi_ontick(struct aproc *, int);
void midi_send_slot(struct aproc *, int);
void midi_send_vol(struct aproc *, int, unsigned int);
void midi_send_master(struct aproc *);
void midi_send_full(struct aproc *, unsigned int, unsigned int,
unsigned int, unsigned int);
void midi_send_qfr(struct aproc *, unsigned int, int);
void midi_flush(struct aproc *);
#endif /* !defined(MIDI_H) */

View File

@ -1,162 +0,0 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <sys/time.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sndio.h>
#include "conf.h"
#include "file.h"
#include "miofile.h"
#ifdef DEBUG
#include "dbg.h"
#endif
struct miofile {
struct file file;
struct mio_hdl *hdl;
};
void miofile_close(struct file *);
unsigned int miofile_read(struct file *, unsigned char *, unsigned int);
unsigned int miofile_write(struct file *, unsigned char *, unsigned int);
void miofile_start(struct file *);
void miofile_stop(struct file *);
int miofile_nfds(struct file *);
int miofile_pollfd(struct file *, struct pollfd *, int);
int miofile_revents(struct file *, struct pollfd *);
struct fileops miofile_ops = {
"mio",
sizeof(struct miofile),
miofile_close,
miofile_read,
miofile_write,
NULL, /* start */
NULL, /* stop */
miofile_nfds,
miofile_pollfd,
miofile_revents
};
/*
* open the device
*/
struct miofile *
miofile_new(struct fileops *ops, char *path, unsigned int mode)
{
struct mio_hdl *hdl;
struct miofile *f;
hdl = mio_open(path, mode, 1);
if (hdl == NULL)
return NULL;
f = (struct miofile *)file_new(ops, path, mio_nfds(hdl));
if (f == NULL)
goto bad_close;
f->hdl = hdl;
return f;
bad_close:
mio_close(hdl);
return NULL;
}
unsigned int
miofile_read(struct file *file, unsigned char *data, unsigned int count)
{
struct miofile *f = (struct miofile *)file;
unsigned int n;
n = mio_read(f->hdl, data, count);
if (n == 0) {
f->file.state &= ~FILE_ROK;
if (mio_eof(f->hdl)) {
#ifdef DEBUG
dbg_puts(f->file.name);
dbg_puts(": failed to read from device\n");
#endif
file_eof(&f->file);
} else {
#ifdef DEBUG
if (debug_level >= 4) {
file_dbg(&f->file);
dbg_puts(": reading blocked\n");
}
#endif
}
return 0;
}
return n;
}
unsigned int
miofile_write(struct file *file, unsigned char *data, unsigned int count)
{
struct miofile *f = (struct miofile *)file;
unsigned int n;
n = mio_write(f->hdl, data, count);
if (n == 0) {
f->file.state &= ~FILE_WOK;
if (mio_eof(f->hdl)) {
#ifdef DEBUG
dbg_puts(f->file.name);
dbg_puts(": failed to write on device\n");
#endif
file_hup(&f->file);
} else {
#ifdef DEBUG
if (debug_level >= 4) {
file_dbg(&f->file);
dbg_puts(": writing blocked\n");
}
#endif
}
return 0;
}
return n;
}
int
miofile_nfds(struct file *file)
{
return mio_nfds(((struct miofile *)file)->hdl);
}
int
miofile_pollfd(struct file *file, struct pollfd *pfd, int events)
{
return mio_pollfd(((struct miofile *)file)->hdl, pfd, events);
}
int
miofile_revents(struct file *file, struct pollfd *pfd)
{
return mio_revents(((struct miofile *)file)->hdl, pfd);
}
void
miofile_close(struct file *file)
{
mio_close(((struct miofile *)file)->hdl);
}

View File

@ -1,194 +0,0 @@
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/time.h>
#include <sys/types.h>
#include <sys/signal.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "conf.h"
#include "pipe.h"
#ifdef DEBUG
#include "dbg.h"
#endif
struct fileops pipe_ops = {
"pipe",
sizeof(struct pipe),
pipe_close,
pipe_read,
pipe_write,
NULL, /* start */
NULL, /* stop */
pipe_nfds,
pipe_pollfd,
pipe_revents
};
struct pipe *
pipe_new(struct fileops *ops, int fd, char *name)
{
struct pipe *f;
f = (struct pipe *)file_new(ops, name, 1);
if (f == NULL)
return NULL;
f->fd = fd;
return f;
}
unsigned int
pipe_read(struct file *file, unsigned char *data, unsigned int count)
{
struct pipe *f = (struct pipe *)file;
int n;
while ((n = read(f->fd, data, count)) < 0) {
f->file.state &= ~FILE_ROK;
if (errno == EAGAIN) {
#ifdef DEBUG
if (debug_level >= 4) {
file_dbg(&f->file);
dbg_puts(": reading blocked\n");
}
#endif
} else {
warn("%s", f->file.name);
file_eof(&f->file);
}
return 0;
}
if (n == 0) {
f->file.state &= ~FILE_ROK; /* XXX: already cleared in file_eof */
file_eof(&f->file);
return 0;
}
return n;
}
unsigned int
pipe_write(struct file *file, unsigned char *data, unsigned int count)
{
struct pipe *f = (struct pipe *)file;
int n;
while ((n = write(f->fd, data, count)) < 0) {
f->file.state &= ~FILE_WOK;
if (errno == EAGAIN) {
#ifdef DEBUG
if (debug_level >= 4) {
file_dbg(&f->file);
dbg_puts(": writing blocked\n");
}
#endif
} else {
if (errno != EPIPE)
warn("%s", f->file.name);
file_hup(&f->file);
}
return 0;
}
return n;
}
int
pipe_nfds(struct file *file) {
return 1;
}
int
pipe_pollfd(struct file *file, struct pollfd *pfd, int events)
{
struct pipe *f = (struct pipe *)file;
pfd->fd = f->fd;
pfd->events = events;
return (events != 0) ? 1 : 0;
}
int
pipe_revents(struct file *f, struct pollfd *pfd)
{
return pfd->revents;
}
void
pipe_close(struct file *file)
{
struct pipe *f = (struct pipe *)file;
close(f->fd);
file_slowaccept = 0;
}
off_t
pipe_endpos(struct file *file)
{
struct pipe *f = (struct pipe *)file;
off_t pos;
pos = lseek(f->fd, 0, SEEK_END);
if (pos < 0) {
#ifdef DEBUG
file_dbg(&f->file);
dbg_puts(": couldn't get file size\n");
#endif
return 0;
}
return pos;
}
int
pipe_seek(struct file *file, off_t pos)
{
struct pipe *f = (struct pipe *)file;
off_t newpos;
newpos = lseek(f->fd, pos, SEEK_SET);
if (newpos < 0) {
#ifdef DEBUG
file_dbg(&f->file);
dbg_puts(": couldn't seek\n");
#endif
/* XXX: call eof() */
return 0;
}
return 1;
}
int
pipe_trunc(struct file *file, off_t pos)
{
struct pipe *f = (struct pipe *)file;
if (ftruncate(f->fd, pos) < 0) {
#ifdef DEBUG
file_dbg(&f->file);
dbg_puts(": couldn't truncate file\n");
#endif
/* XXX: call hup() */
return 0;
}
return 1;
}

View File

@ -1,484 +0,0 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/time.h>
#include <sys/types.h>
#include <poll.h>
#include <sndio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "aparams.h"
#include "aproc.h"
#include "abuf.h"
#include "conf.h"
#include "dev.h"
#include "file.h"
#include "siofile.h"
#ifdef DEBUG
#include "dbg.h"
#endif
struct siofile {
struct file file;
struct sio_hdl *hdl;
unsigned int wtickets, wbpf;
unsigned int rtickets, rbpf;
unsigned int bufsz;
int started;
void (*onmove)(void *, int);
void *arg;
#ifdef DEBUG
long long wtime, utime;
#endif
};
void siofile_close(struct file *);
unsigned int siofile_read(struct file *, unsigned char *, unsigned int);
unsigned int siofile_write(struct file *, unsigned char *, unsigned int);
void siofile_start(struct file *, void (*)(void *, int), void *);
void siofile_stop(struct file *);
int siofile_nfds(struct file *);
int siofile_pollfd(struct file *, struct pollfd *, int);
int siofile_revents(struct file *, struct pollfd *);
void siofile_cb(void *, int);
struct fileops siofile_ops = {
"sio",
sizeof(struct siofile),
siofile_close,
siofile_read,
siofile_write,
siofile_start,
siofile_stop,
siofile_nfds,
siofile_pollfd,
siofile_revents
};
int wsio_out(struct aproc *, struct abuf *);
int rsio_in(struct aproc *, struct abuf *);
struct aproc_ops rsio_ops = {
"rsio",
rsio_in,
rfile_out,
rfile_eof,
rfile_hup,
NULL, /* newin */
NULL, /* newout */
aproc_ipos,
aproc_opos,
rfile_done
};
struct aproc_ops wsio_ops = {
"wsio",
wfile_in,
wsio_out,
wfile_eof,
wfile_hup,
NULL, /* newin */
NULL, /* newout */
aproc_ipos,
aproc_opos,
wfile_done
};
struct aproc *
rsio_new(struct file *f)
{
struct aproc *p;
p = aproc_new(&rsio_ops, f->name);
p->u.io.file = f;
p->u.io.partial = 0;
f->rproc = p;
return p;
}
struct aproc *
wsio_new(struct file *f)
{
struct aproc *p;
p = aproc_new(&wsio_ops, f->name);
p->u.io.file = f;
p->u.io.partial = 0;
f->wproc = p;
return p;
}
int
wsio_out(struct aproc *p, struct abuf *obuf)
{
struct siofile *f = (struct siofile *)p->u.io.file;
if (f->wtickets == 0) {
#ifdef DEBUG
if (debug_level >= 4) {
file_dbg(&f->file);
dbg_puts(": no more write tickets\n");
}
#endif
f->file.state &= ~FILE_WOK;
return 0;
}
return wfile_out(p, obuf);
}
int
rsio_in(struct aproc *p, struct abuf *ibuf)
{
struct siofile *f = (struct siofile *)p->u.io.file;
if (f->rtickets == 0) {
#ifdef DEBUG
if (debug_level >= 4) {
file_dbg(&f->file);
dbg_puts(": no more read tickets\n");
}
#endif
f->file.state &= ~FILE_ROK;
return 0;
}
return rfile_in(p, ibuf);
}
void
siofile_cb(void *addr, int delta)
{
struct siofile *f = (struct siofile *)addr;
struct aproc *p;
#ifdef DEBUG
if (delta < 0 || delta > (60 * RATE_MAX)) {
file_dbg(&f->file);
dbg_puts(": ");
dbg_puti(delta);
dbg_puts(": bogus sndio delta");
dbg_panic();
}
if (debug_level >= 4) {
file_dbg(&f->file);
dbg_puts(": tick, delta = ");
dbg_puti(delta);
dbg_puts(", load = ");
dbg_puti((file_utime - f->utime) / 1000);
dbg_puts(" + ");
dbg_puti((file_wtime - f->wtime) / 1000);
dbg_puts("\n");
}
f->wtime = file_wtime;
f->utime = file_utime;
#endif
if (delta != 0) {
p = f->file.wproc;
if (p && p->ops->opos)
p->ops->opos(p, NULL, delta);
p = f->file.rproc;
if (p && p->ops->ipos)
p->ops->ipos(p, NULL, delta);
}
if (f->onmove)
f->onmove(f->arg, delta);
f->wtickets += delta * f->wbpf;
f->rtickets += delta * f->rbpf;
}
/*
* Open the device.
*/
struct siofile *
siofile_new(struct fileops *ops, char *path, unsigned int *rmode,
struct aparams *ipar, struct aparams *opar,
unsigned int *bufsz, unsigned int *round)
{
struct sio_par par;
struct sio_hdl *hdl;
struct siofile *f;
unsigned int mode = *rmode;
hdl = sio_open(path, mode, 1);
if (hdl == NULL) {
if (mode != (SIO_PLAY | SIO_REC))
return NULL;
hdl = sio_open(path, SIO_PLAY, 1);
if (hdl != NULL)
mode = SIO_PLAY;
else {
hdl = sio_open(path, SIO_REC, 1);
if (hdl != NULL)
mode = SIO_REC;
else
return NULL;
}
#ifdef DEBUG
if (debug_level >= 1) {
dbg_puts("warning, device opened in ");
dbg_puts(mode == SIO_PLAY ? "play-only" : "rec-only");
dbg_puts(" mode\n");
}
#endif
}
sio_initpar(&par);
if (mode & SIO_REC) {
par.bits = ipar->bits;
par.bps = ipar->bps;
par.sig = ipar->sig;
par.le = ipar->le;
par.msb = ipar->msb;
par.rate = ipar->rate;
par.rchan = ipar->cmax + 1;
} else {
par.bits = opar->bits;
par.bps = opar->bps;
par.sig = opar->sig;
par.le = opar->le;
par.msb = opar->msb;
par.rate = opar->rate;
}
if (mode & SIO_PLAY)
par.pchan = opar->cmax + 1;
if (*bufsz)
par.appbufsz = *bufsz;
if (*round)
par.round = *round;
if (!sio_setpar(hdl, &par))
goto bad_close;
if (!sio_getpar(hdl, &par))
goto bad_close;
if (mode & SIO_REC) {
ipar->bits = par.bits;
ipar->bps = par.bps;
ipar->sig = par.sig;
ipar->le = par.le;
ipar->msb = par.msb;
ipar->rate = par.rate;
ipar->cmin = 0;
ipar->cmax = par.rchan - 1;
}
if (mode & SIO_PLAY) {
opar->bits = par.bits;
opar->bps = par.bps;
opar->sig = par.sig;
opar->le = par.le;
opar->msb = par.msb;
opar->rate = par.rate;
opar->cmin = 0;
opar->cmax = par.pchan - 1;
}
*rmode = mode;
*bufsz = par.bufsz;
*round = par.round;
f = (struct siofile *)file_new(ops, path, sio_nfds(hdl));
if (f == NULL)
goto bad_close;
f->hdl = hdl;
f->started = 0;
f->wtickets = 0;
f->rtickets = 0;
f->wbpf = par.pchan * par.bps;
f->rbpf = par.rchan * par.bps;
f->bufsz = par.bufsz;
sio_onmove(f->hdl, siofile_cb, f);
return f;
bad_close:
sio_close(hdl);
return NULL;
}
void
siofile_start(struct file *file, void (*cb)(void *, int), void *arg)
{
struct siofile *f = (struct siofile *)file;
if (!sio_start(f->hdl)) {
#ifdef DEBUG
dbg_puts(f->file.name);
dbg_puts(": failed to start device\n");
#endif
file_close(file);
return;
}
f->started = 1;
f->wtickets = f->bufsz * f->wbpf;
f->rtickets = 0;
#ifdef DEBUG
f->wtime = file_wtime;
f->utime = file_utime;
if (debug_level >= 3) {
file_dbg(&f->file);
dbg_puts(": started\n");
}
#endif
f->onmove = cb;
f->arg = arg;
}
void
siofile_stop(struct file *file)
{
struct siofile *f = (struct siofile *)file;
f->started = 0;
f->onmove = NULL;
if (!sio_eof(f->hdl) && !sio_stop(f->hdl)) {
#ifdef DEBUG
dbg_puts(f->file.name);
dbg_puts(": failed to stop device\n");
#endif
file_close(file);
return;
}
#ifdef DEBUG
if (debug_level >= 3) {
file_dbg(&f->file);
dbg_puts(": stopped\n");
}
#endif
}
unsigned int
siofile_read(struct file *file, unsigned char *data, unsigned int count)
{
struct siofile *f = (struct siofile *)file;
unsigned int n;
#ifdef DEBUG
if (f->rtickets == 0) {
file_dbg(&f->file);
dbg_puts(": called with no read tickets\n");
}
#endif
if (count > f->rtickets)
count = f->rtickets;
n = f->started ? sio_read(f->hdl, data, count) : 0;
if (n == 0) {
f->file.state &= ~FILE_ROK;
if (sio_eof(f->hdl)) {
#ifdef DEBUG
dbg_puts(f->file.name);
dbg_puts(": failed to read from device\n");
#endif
file_eof(&f->file);
} else {
#ifdef DEBUG
if (debug_level >= 4) {
file_dbg(&f->file);
dbg_puts(": reading blocked\n");
}
#endif
}
return 0;
} else {
f->rtickets -= n;
if (f->rtickets == 0) {
f->file.state &= ~FILE_ROK;
#ifdef DEBUG
if (debug_level >= 4) {
file_dbg(&f->file);
dbg_puts(": read tickets exhausted\n");
}
#endif
}
}
return n;
}
unsigned int
siofile_write(struct file *file, unsigned char *data, unsigned int count)
{
struct siofile *f = (struct siofile *)file;
unsigned int n;
#ifdef DEBUG
if (f->wtickets == 0) {
file_dbg(&f->file);
dbg_puts(": called with no write tickets\n");
}
#endif
if (count > f->wtickets)
count = f->wtickets;
n = f->started ? sio_write(f->hdl, data, count) : 0;
if (n == 0) {
f->file.state &= ~FILE_WOK;
if (sio_eof(f->hdl)) {
#ifdef DEBUG
dbg_puts(f->file.name);
dbg_puts(": failed to write on device\n");
#endif
file_hup(&f->file);
} else {
#ifdef DEBUG
if (debug_level >= 4) {
file_dbg(&f->file);
dbg_puts(": writing blocked\n");
}
#endif
}
return 0;
} else {
f->wtickets -= n;
if (f->wtickets == 0) {
f->file.state &= ~FILE_WOK;
#ifdef DEBUG
if (debug_level >= 4) {
file_dbg(&f->file);
dbg_puts(": write tickets exhausted\n");
}
#endif
}
}
return n;
}
int
siofile_nfds(struct file *file)
{
return sio_nfds(((struct siofile *)file)->hdl);
}
int
siofile_pollfd(struct file *file, struct pollfd *pfd, int events)
{
struct siofile *f = (struct siofile *)file;
if (!f->started)
events &= ~(POLLIN | POLLOUT);
return sio_pollfd(((struct siofile *)file)->hdl, pfd, events);
}
int
siofile_revents(struct file *file, struct pollfd *pfd)
{
return sio_revents(((struct siofile *)file)->hdl, pfd);
}
void
siofile_close(struct file *file)
{
struct siofile *f = (struct siofile *)file;
if (f->started)
siofile_stop(&f->file);
sio_close(((struct siofile *)file)->hdl);
}

View File

@ -1,32 +0,0 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef SIOFILE_H
#define SIOFILE_H
struct fileops;
struct siofile;
struct aparams;
struct aproc;
struct siofile *siofile_new(struct fileops *, char *, unsigned int *,
struct aparams *, struct aparams *, unsigned int *, unsigned int *);
struct aproc *rsio_new(struct file *f);
struct aproc *wsio_new(struct file *f);
extern struct fileops siofile_ops;
#endif /* !defined(SIOFILE_H) */

View File

@ -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 <stdint.h>
@ -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) */

182
aucat/utils.c Normal file
View File

@ -0,0 +1,182 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2003-2012 Alexandre Ratchov <alex@caoua.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* log_xxx() routines are used to quickly store traces into a trace buffer.
* This allows trances to be collected during time sensitive operations without
* disturbing them. The buffer can be flushed on standard error later, when
* slow syscalls are no longer disruptive, e.g. at the end of the poll() loop.
*/
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include "utils.h"
/*
* log buffer size
*/
#define LOG_BUFSZ 8192
/*
* store a character in the log
*/
#define LOG_PUTC(c) do { \
if (log_used < LOG_BUFSZ) \
log_buf[log_used++] = (c); \
} while (0)
char log_buf[LOG_BUFSZ]; /* buffer where traces are stored */
unsigned int log_used = 0; /* bytes used in the buffer */
unsigned int log_sync = 1; /* if true, flush after each '\n' */
/*
* write the log buffer on stderr
*/
void
log_flush(void)
{
if (log_used == 0)
return;
write(STDERR_FILENO, log_buf, log_used);
log_used = 0;
}
/*
* store a string in the log
*/
void
log_puts(const char *msg)
{
const 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;
}

View File

@ -1,6 +1,6 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
* Copyright (c) 2003-2012 Alexandre Ratchov <alex@caoua.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -14,27 +14,33 @@
* 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"
#ifndef UTILS_H
#define UTILS_H
struct pipe {
struct file file;
int fd; /* file descriptor */
};
#include <stddef.h>
extern struct fileops pipe_ops;
void log_puts(const char *);
void log_putx(unsigned long);
void log_putu(unsigned long);
void log_puti(long);
void panic(void);
void log_flush(void);
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 *);
void *xmalloc(size_t);
char *xstrdup(char *);
void xfree(void *);
#endif /* !defined(PIPE_H) */
/*
* 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

File diff suppressed because it is too large Load Diff

View File

@ -18,59 +18,32 @@
#define WAV_H
#include <sys/types.h>
#include "aparams.h"
#include "pipe.h"
#include "dsp.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 */
struct aparams par; /* file params */
int rate; /* file sample rate */
int nch; /* file channel count */
#define HDR_AUTO 0
#define HDR_RAW 1
#define HDR_WAV 2
int hdr; /* header type */
int fd; /* file descriptor */
#define WAV_FREAD 1 /* open for reading */
#define WAV_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) */
short *map; /* mulaw/alaw conversions */
char *path; /* file name (debug only) */
};
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];
int wav_open(struct wav *, char *, int, int, struct aparams *, int, int);
size_t wav_read(struct wav *, void *, size_t);
size_t wav_write(struct wav *, void *, size_t);
int wav_seek(struct wav *, off_t);
void wav_close(struct wav *);
#endif /* !defined(WAV_H) */