mirror of https://github.com/ericonr/sndio.git
- 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:
parent
a7f38ff59e
commit
7cbcc30319
|
@ -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
|
||||
|
|
587
aucat/abuf.c
587
aucat/abuf.c
|
@ -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;
|
||||
}
|
||||
|
|
111
aucat/abuf.h
111
aucat/abuf.h
|
@ -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) */
|
||||
|
|
270
aucat/aparams.c
270
aucat/aparams.c
|
@ -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;
|
||||
}
|
2394
aucat/aproc.c
2394
aucat/aproc.c
File diff suppressed because it is too large
Load Diff
248
aucat/aproc.h
248
aucat/aproc.h
|
@ -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) */
|
407
aucat/aucat.1
407
aucat/aucat.1
|
@ -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.
|
||||
|
|
1643
aucat/aucat.c
1643
aucat/aucat.c
File diff suppressed because it is too large
Load Diff
139
aucat/dbg.c
139
aucat/dbg.c
|
@ -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
|
31
aucat/dbg.h
31
aucat/dbg.h
|
@ -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 */
|
|
@ -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) */
|
1736
aucat/dev.c
1736
aucat/dev.c
File diff suppressed because it is too large
Load Diff
127
aucat/dev.h
127
aucat/dev.h
|
@ -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) */
|
|
@ -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
|
||||
}
|
|
@ -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) */
|
775
aucat/file.c
775
aucat/file.c
|
@ -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);
|
||||
}
|
||||
}
|
97
aucat/file.h
97
aucat/file.h
|
@ -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) */
|
295
aucat/headers.c
295
aucat/headers.c
|
@ -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;
|
||||
}
|
694
aucat/midi.c
694
aucat/midi.c
|
@ -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;
|
||||
}
|
33
aucat/midi.h
33
aucat/midi.h
|
@ -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) */
|
162
aucat/miofile.c
162
aucat/miofile.c
|
@ -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);
|
||||
}
|
194
aucat/pipe.c
194
aucat/pipe.c
|
@ -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;
|
||||
}
|
484
aucat/siofile.c
484
aucat/siofile.c
|
@ -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);
|
||||
}
|
|
@ -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) */
|
|
@ -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) */
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
1315
aucat/wav.c
1315
aucat/wav.c
File diff suppressed because it is too large
Load Diff
73
aucat/wav.h
73
aucat/wav.h
|
@ -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) */
|
||||
|
|
Loading…
Reference in New Issue