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 ---
|
# ---------------------------------------------------------- dependencies ---
|
||||||
|
|
||||||
OBJS = \
|
OBJS = abuf.o aucat.o dsp.o utils.o wav.o
|
||||||
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
|
|
||||||
|
|
||||||
aucat: ${OBJS}
|
aucat: ${OBJS}
|
||||||
${CC} ${LDFLAGS} ${LIB} -o aucat ${OBJS} ${LDADD}
|
${CC} ${LDFLAGS} ${LIB} -o aucat ${OBJS} ${LDADD}
|
||||||
|
@ -53,25 +50,9 @@ aucat: ${OBJS}
|
||||||
.c.o:
|
.c.o:
|
||||||
${CC} ${CFLAGS} ${INCLUDE} ${DEFS} -c $<
|
${CC} ${CFLAGS} ${INCLUDE} ${DEFS} -c $<
|
||||||
|
|
||||||
abuf.o: abuf.c abuf.h aparams.h aproc.h file.h conf.h dbg.h
|
abuf.o: abuf.c abuf.h utils.h
|
||||||
aparams.o: aparams.c aparams.h dbg.h
|
aucat.o: aucat.c abuf.h dsp.h defs.h sysex.h utils.h wav.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 \
|
|
||||||
../bsd-compat/bsd-compat.h
|
../bsd-compat/bsd-compat.h
|
||||||
dbg.o: dbg.c dbg.h
|
dsp.o: dsp.c dsp.h defs.h utils.h
|
||||||
dev.o: dev.c abuf.h aproc.h aparams.h file.h conf.h dev.h \
|
utils.o: utils.c utils.h
|
||||||
pipe.h miofile.h siofile.h midi.h dbg.h \
|
wav.o: wav.c utils.h wav.h dsp.h defs.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
|
|
||||||
|
|
587
aucat/abuf.c
587
aucat/abuf.c
|
@ -1,6 +1,6 @@
|
||||||
/* $OpenBSD$ */
|
/* $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
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* 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.
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
*/
|
*/
|
||||||
/*
|
/*
|
||||||
* Simple byte fifo. It has one reader and one writer. The abuf
|
* Simple byte fifo.
|
||||||
* structure is used to interconnect audio processing units (aproc
|
|
||||||
* structures).
|
|
||||||
*
|
*
|
||||||
* The abuf data is split in two parts: (1) valid data available to the reader
|
* 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
|
* (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
|
* 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.
|
* 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 <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "abuf.h"
|
#include "abuf.h"
|
||||||
#include "aparams.h"
|
#include "utils.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 *);
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
void
|
void
|
||||||
abuf_dbg(struct abuf *buf)
|
abuf_log(struct abuf *buf)
|
||||||
{
|
{
|
||||||
if (buf->wproc) {
|
log_putu(buf->start);
|
||||||
aproc_dbg(buf->wproc);
|
log_puts("+");
|
||||||
} else {
|
log_putu(buf->used);
|
||||||
dbg_puts("none");
|
log_puts("/");
|
||||||
}
|
log_putu(buf->len);
|
||||||
dbg_puts(buf->inuse ? "=>" : "->");
|
|
||||||
if (buf->rproc) {
|
|
||||||
aproc_dbg(buf->rproc);
|
|
||||||
} else {
|
|
||||||
dbg_puts("none");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void
|
void
|
||||||
abuf_dump(struct abuf *buf)
|
abuf_init(struct abuf *buf, unsigned int len)
|
||||||
{
|
{
|
||||||
abuf_dbg(buf);
|
buf->data = xmalloc(len);
|
||||||
dbg_puts(": used = ");
|
buf->len = len;
|
||||||
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->used = 0;
|
buf->used = 0;
|
||||||
buf->start = 0;
|
buf->start = 0;
|
||||||
buf->rproc = NULL;
|
|
||||||
buf->wproc = NULL;
|
|
||||||
buf->duplex = NULL;
|
|
||||||
return buf;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
abuf_del(struct abuf *buf)
|
abuf_done(struct abuf *buf)
|
||||||
{
|
{
|
||||||
if (buf->duplex)
|
#ifdef DEBUG
|
||||||
buf->duplex->duplex = NULL;
|
if (buf->used > 0) {
|
||||||
#ifdef DEBUG
|
if (log_level >= 3) {
|
||||||
if (buf->rproc || buf->wproc) {
|
log_puts("deleting non-empty buffer, used = ");
|
||||||
abuf_dbg(buf);
|
log_putu(buf->used);
|
||||||
dbg_puts(": can't delete referenced buffer\n");
|
log_puts("\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");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
free(buf);
|
xfree(buf->data);
|
||||||
|
buf->data = (void *)0xdeadbeef;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Clear buffer contents.
|
* return the reader pointer and the number of bytes available
|
||||||
*/
|
|
||||||
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.
|
|
||||||
*/
|
*/
|
||||||
unsigned char *
|
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;
|
count = buf->len - buf->start;
|
||||||
used = buf->used - ofs;
|
if (count > buf->used)
|
||||||
if (start >= buf->len)
|
count = buf->used;
|
||||||
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;
|
|
||||||
*rsize = count;
|
*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
|
void
|
||||||
abuf_rdiscard(struct abuf *buf, unsigned int count)
|
abuf_rdiscard(struct abuf *buf, int count)
|
||||||
{
|
{
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
if (count > buf->used) {
|
if (count < 0 || count > buf->used) {
|
||||||
abuf_dump(buf);
|
log_puts("abuf_rdiscard: bad count = ");
|
||||||
dbg_puts(": rdiscard: bad count = ");
|
log_putu(count);
|
||||||
dbg_putu(count);
|
log_puts("\n");
|
||||||
dbg_puts("\n");
|
panic();
|
||||||
dbg_panic();
|
|
||||||
}
|
|
||||||
if (debug_level >= 4) {
|
|
||||||
abuf_dbg(buf);
|
|
||||||
dbg_puts(": discard(");
|
|
||||||
dbg_putu(count);
|
|
||||||
dbg_puts(")\n");
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
buf->used -= count;
|
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
|
void
|
||||||
abuf_wcommit(struct abuf *buf, unsigned int count)
|
abuf_wcommit(struct abuf *buf, int count)
|
||||||
{
|
{
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
if (count > (buf->len - buf->used)) {
|
if (count < 0 || count > (buf->len - buf->used)) {
|
||||||
abuf_dump(buf);
|
log_puts("abuf_wcommit: bad count = ");
|
||||||
dbg_puts(": rdiscard: bad count = ");
|
log_putu(count);
|
||||||
dbg_putu(count);
|
log_puts("\n");
|
||||||
dbg_puts("\n");
|
panic();
|
||||||
dbg_panic();
|
|
||||||
}
|
|
||||||
if (debug_level >= 4) {
|
|
||||||
abuf_dbg(buf);
|
|
||||||
dbg_puts(": commit(");
|
|
||||||
dbg_putu(count);
|
|
||||||
dbg_puts(")\n");
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
buf->used += count;
|
buf->used += count;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get a pointer to the writable block at offset ofs.
|
* get writer pointer and the number of bytes writable
|
||||||
*/
|
*/
|
||||||
unsigned char *
|
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;
|
||||||
end = buf->start + buf->used + ofs;
|
|
||||||
if (end >= buf->len)
|
if (end >= buf->len)
|
||||||
end -= buf->len;
|
end -= buf->len;
|
||||||
#ifdef DEBUG
|
avail = buf->len - buf->used;
|
||||||
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);
|
|
||||||
count = buf->len - end;
|
count = buf->len - end;
|
||||||
if (count > avail)
|
if (count > avail)
|
||||||
count = avail;
|
count = avail;
|
||||||
*rsize = count;
|
*rsize = count;
|
||||||
return (unsigned char *)buf + sizeof(struct abuf) + end * buf->bpf;
|
return buf->data + end;
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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);
|
|
||||||
}
|
}
|
||||||
|
|
111
aucat/abuf.h
111
aucat/abuf.h
|
@ -1,6 +1,6 @@
|
||||||
/* $OpenBSD$ */
|
/* $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
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -17,106 +17,19 @@
|
||||||
#ifndef ABUF_H
|
#ifndef ABUF_H
|
||||||
#define ABUF_H
|
#define ABUF_H
|
||||||
|
|
||||||
#include <sys/queue.h>
|
|
||||||
|
|
||||||
struct aproc;
|
|
||||||
struct aparams;
|
|
||||||
|
|
||||||
struct abuf {
|
struct abuf {
|
||||||
LIST_ENTRY(abuf) ient; /* reader's list of inputs entry */
|
int start; /* offset (frames) where stored data starts */
|
||||||
LIST_ENTRY(abuf) oent; /* writer's list of outputs entry */
|
int used; /* frames stored in the buffer */
|
||||||
|
int len; /* total size of the buffer (frames) */
|
||||||
/*
|
unsigned char *data;
|
||||||
* 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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
void abuf_init(struct abuf *, unsigned int);
|
||||||
* the buffer contains at least one frame. This macro should
|
void abuf_done(struct abuf *);
|
||||||
* be used to check if the buffer can be flushed
|
void abuf_log(struct abuf *);
|
||||||
*/
|
unsigned char *abuf_rgetblk(struct abuf *, int *);
|
||||||
#define ABUF_ROK(b) ((b)->used > 0)
|
unsigned char *abuf_wgetblk(struct abuf *, int *);
|
||||||
|
void abuf_rdiscard(struct abuf *, int);
|
||||||
/*
|
void abuf_wcommit(struct abuf *, int);
|
||||||
* 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);
|
|
||||||
|
|
||||||
#endif /* !defined(ABUF_H) */
|
#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
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
.Nm aucat
|
.Nm aucat
|
||||||
.Nd audio/MIDI stream manipulation tool
|
.Nd audio files manipulation tool
|
||||||
.Sh SYNOPSIS
|
.Sh SYNOPSIS
|
||||||
.Nm aucat
|
.Nm aucat
|
||||||
.Bk -words
|
.Bk -words
|
||||||
.Op Fl dMn
|
.Op Fl dn
|
||||||
.Op Fl C Ar min : Ns Ar max
|
|
||||||
.Op Fl c Ar min : Ns Ar max
|
.Op Fl c Ar min : Ns Ar max
|
||||||
.Op Fl e Ar enc
|
.Op Fl e Ar enc
|
||||||
.Op Fl f Ar device
|
.Op Fl f Ar device
|
||||||
|
@ -34,34 +33,32 @@
|
||||||
.Op Fl o Ar file
|
.Op Fl o Ar file
|
||||||
.Op Fl q Ar port
|
.Op Fl q Ar port
|
||||||
.Op Fl r Ar rate
|
.Op Fl r Ar rate
|
||||||
.Op Fl t Ar mode
|
|
||||||
.Op Fl v Ar volume
|
.Op Fl v Ar volume
|
||||||
.Op Fl w Ar flag
|
|
||||||
.Op Fl x Ar policy
|
|
||||||
.Ek
|
.Ek
|
||||||
.Sh DESCRIPTION
|
.Sh DESCRIPTION
|
||||||
The
|
The
|
||||||
.Nm
|
.Nm
|
||||||
utility can play, record, mix, and convert audio files.
|
utility can play, record, mix, and process audio files
|
||||||
|
on the fly.
|
||||||
During playback,
|
During playback,
|
||||||
.Nm
|
.Nm
|
||||||
reads audio data concurrently from all played files, mixes it and sends
|
reads audio data concurrently from all played files,
|
||||||
the result to the device.
|
mixes it and plays the result on the device.
|
||||||
Similarly, during recording it duplicates audio data recorded
|
Similarly, it stores audio data recorded
|
||||||
from the device and stores it into corresponding files.
|
from the device into corresponding files.
|
||||||
It can process audio data on the fly:
|
An
|
||||||
|
.Em off-line
|
||||||
|
mode could be used to process audio files without
|
||||||
|
involving audio hardware.
|
||||||
|
Processing includes:
|
||||||
.Pp
|
.Pp
|
||||||
.Bl -bullet -offset indent -compact
|
.Bl -bullet -offset indent -compact
|
||||||
.It
|
.It
|
||||||
Change the sound encoding.
|
Change the sound encoding.
|
||||||
.It
|
.It
|
||||||
Route the sound from one channel to another,
|
Route the sound from one channel to another.
|
||||||
join stereo or split mono.
|
|
||||||
.It
|
.It
|
||||||
Control the per-file playback volume.
|
Control the per-file playback volume.
|
||||||
.It
|
|
||||||
Monitor the sound being played, allowing the playback mix
|
|
||||||
to be recorded.
|
|
||||||
.El
|
.El
|
||||||
.Pp
|
.Pp
|
||||||
Finally,
|
Finally,
|
||||||
|
@ -78,191 +75,56 @@ Start, stop and relocate playback and recording.
|
||||||
The options are as follows:
|
The options are as follows:
|
||||||
.Bl -tag -width Ds
|
.Bl -tag -width Ds
|
||||||
.It Xo
|
.It Xo
|
||||||
.Fl C Ar min : Ns Ar max ,
|
|
||||||
.Fl c Ar min : Ns Ar max
|
.Fl c Ar min : Ns Ar max
|
||||||
.Xc
|
.Xc
|
||||||
The range of stream channel numbers for recording and playback directions,
|
The range of audio file channel numbers.
|
||||||
respectively.
|
|
||||||
The default is
|
The default is
|
||||||
.Cm 0:1 ,
|
.Cm 0:1 ,
|
||||||
i.e. stereo.
|
i.e. stereo.
|
||||||
.It Fl d
|
.It Fl d
|
||||||
Increase log verbosity.
|
Increase log verbosity.
|
||||||
.It Fl e Ar enc
|
.It Fl e Ar enc
|
||||||
Encoding of the playback or recording stream (see below).
|
Encoding of the audio file.
|
||||||
The default is signed, 16-bit, native byte order.
|
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
|
.It Fl f Ar device
|
||||||
Use this
|
Use this
|
||||||
.Xr sndio 7
|
.Xr sndio 7
|
||||||
audio device.
|
audio device.
|
||||||
Preceding per-device options apply to this device.
|
Device mode and parameters are determined from audio files.
|
||||||
Streams
|
Default is
|
||||||
.Pq Fl io
|
.Pa default .
|
||||||
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.
|
|
||||||
.It Fl h Ar fmt
|
.It Fl h Ar fmt
|
||||||
File format of the playback or record stream (see below).
|
Audio file type.
|
||||||
The default is
|
The following file types are supported:
|
||||||
.Cm auto .
|
.Bl -tag -width auto
|
||||||
.It Fl i Ar file
|
.It Ar raw
|
||||||
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
|
|
||||||
Headerless file.
|
Headerless file.
|
||||||
This format is recommended since it has no limitations.
|
This format is recommended since it has no limitations.
|
||||||
.It wav
|
.It Ar wav
|
||||||
Microsoft WAVE file format.
|
Microsoft WAVE file format.
|
||||||
There are limitations inherent to the file format itself:
|
There are limitations inherent to the file format itself:
|
||||||
not all encodings are supported,
|
not all encodings are supported,
|
||||||
|
@ -270,69 +132,74 @@ file sizes are limited to 2GB,
|
||||||
and the file must support the
|
and the file must support the
|
||||||
.Xr lseek 2
|
.Xr lseek 2
|
||||||
operation (e.g. pipes do not support it).
|
operation (e.g. pipes do not support it).
|
||||||
.It auto
|
.It Ar auto
|
||||||
Try to guess, depending on the file name.
|
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
|
.El
|
||||||
.Pp
|
.Pp
|
||||||
Encodings are specified using the
|
On the command line,
|
||||||
.Fl e
|
per-file parameters
|
||||||
option.
|
.Pq Fl cehjrv
|
||||||
The following encodings are supported:
|
must precede the file definition
|
||||||
|
.Pq Fl io .
|
||||||
.Pp
|
.Pp
|
||||||
.Bl -tag -width s32lexxx -offset indent -compact
|
If
|
||||||
.It s8
|
.Nm
|
||||||
signed 8-bit
|
is sent
|
||||||
.It u8
|
.Dv SIGHUP ,
|
||||||
unsigned 8-bit
|
.Dv SIGINT
|
||||||
.It s16le
|
or
|
||||||
signed 16-bit, little endian
|
.Dv SIGTERM ,
|
||||||
.It u16le
|
it terminates recording to files.
|
||||||
unsigned 16-bit, little endian
|
|
||||||
.It s16be
|
|
||||||
signed 16-bit, big endian
|
|
||||||
.It u16be
|
|
||||||
unsigned 16-bit, big endian
|
|
||||||
.It s24le
|
|
||||||
signed 24-bit, stored in 4 bytes, little endian
|
|
||||||
.It u24le
|
|
||||||
unsigned 24-bit, stored in 4 bytes, little endian
|
|
||||||
.It s24be
|
|
||||||
signed 24-bit, stored in 4 bytes, big endian
|
|
||||||
.It u24be
|
|
||||||
unsigned 24-bit, stored in 4 bytes, big endian
|
|
||||||
.It s32le
|
|
||||||
signed 32-bit, little endian
|
|
||||||
.It u32le
|
|
||||||
unsigned 32-bit, little endian
|
|
||||||
.It s32be
|
|
||||||
signed 32-bit, big endian
|
|
||||||
.It u32be
|
|
||||||
unsigned 32-bit, big endian
|
|
||||||
.It s24le3
|
|
||||||
signed 24-bit, packed in 3 bytes, little endian
|
|
||||||
.It u24le3
|
|
||||||
unsigned 24-bit, packed in 3 bytes, big endian
|
|
||||||
.It s24be3
|
|
||||||
signed 24-bit, packed in 3 bytes, little endian
|
|
||||||
.It u24be3
|
|
||||||
unsigned 24-bit, packed in 3 bytes, big endian
|
|
||||||
.It s20le3
|
|
||||||
signed 20-bit, packed in 3 bytes, little endian
|
|
||||||
.It u20le3
|
|
||||||
unsigned 20-bit, packed in 3 bytes, big endian
|
|
||||||
.It s20be3
|
|
||||||
signed 20-bit, packed in 3 bytes, little endian
|
|
||||||
.It u20be3
|
|
||||||
unsigned 20-bit, packed in 3 bytes, big endian
|
|
||||||
.It s18le3
|
|
||||||
signed 18-bit, packed in 3 bytes, little endian
|
|
||||||
.It u18le3
|
|
||||||
unsigned 18-bit, packed in 3 bytes, big endian
|
|
||||||
.It s18be3
|
|
||||||
signed 18-bit, packed in 3 bytes, little endian
|
|
||||||
.It u18be3
|
|
||||||
unsigned 18-bit, packed in 3 bytes, big endian
|
|
||||||
.El
|
|
||||||
.Sh MIDI CONTROL
|
.Sh MIDI CONTROL
|
||||||
.Nm
|
.Nm
|
||||||
can be controlled through MIDI
|
can be controlled through MIDI
|
||||||
|
@ -340,27 +207,21 @@ can be controlled through MIDI
|
||||||
as follows:
|
as follows:
|
||||||
a MIDI channel is assigned to each stream, and the volume
|
a MIDI channel is assigned to each stream, and the volume
|
||||||
is changed using the standard volume controller (number 7).
|
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
|
.Pp
|
||||||
The master volume can be changed using the standard master volume
|
The master volume can be changed using the standard master volume
|
||||||
system exclusive message.
|
system exclusive message.
|
||||||
.Pp
|
.Pp
|
||||||
Streams created with the
|
All audio files are controlled by the following MMC messages:
|
||||||
.Fl t
|
.Bl -tag -width relocate -offset indent
|
||||||
option are controlled by the following MMC messages:
|
|
||||||
.Bl -tag -width relocateXXX -offset indent
|
|
||||||
.It relocate
|
.It relocate
|
||||||
Files are relocated to the requested time position.
|
All files are relocated to the requested time position.
|
||||||
If the requested position is beyond the end of file,
|
If it is beyond the end of a file, the file is temporarly
|
||||||
playback of the file is temporarly disabled until a valid
|
disabled until a valid position is requested.
|
||||||
position is requested.
|
|
||||||
.It start
|
.It start
|
||||||
Files are started.
|
Playback and/or recording is started.
|
||||||
.It stop
|
.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
|
.El
|
||||||
.Pp
|
.Pp
|
||||||
MIDI control is intended to be used together with
|
MIDI control is intended to be used together with
|
||||||
|
@ -371,7 +232,7 @@ the default
|
||||||
and a MMC-controlled one
|
and a MMC-controlled one
|
||||||
.Va snd/0.mmc :
|
.Va snd/0.mmc :
|
||||||
.Bd -literal -offset indent
|
.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
|
.Ed
|
||||||
.Pp
|
.Pp
|
||||||
Programs using
|
Programs using
|
||||||
|
@ -386,7 +247,7 @@ connected to the
|
||||||
.Va midithru/0
|
.Va midithru/0
|
||||||
MIDI port:
|
MIDI port:
|
||||||
.Bd -literal -offset indent
|
.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
|
.Ed
|
||||||
.Pp
|
.Pp
|
||||||
At this stage,
|
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
|
port as MTC clock source, assured to be synchronous to playback of
|
||||||
.Pa file.wav .
|
.Pa file.wav .
|
||||||
.Sh EXAMPLES
|
.Sh EXAMPLES
|
||||||
Mix and play two stereo streams,
|
Mix and play two files while recording a third file:
|
||||||
the first at 48kHz and the second at 44.1kHz:
|
|
||||||
.Bd -literal -offset indent
|
.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
|
.Ed
|
||||||
.Pp
|
.Pp
|
||||||
Record channels 2 and 3 into one stereo file and
|
Record channels 2 and 3 into one stereo file and
|
||||||
channels 6 and 7 into another stereo file using a 96kHz sampling rate for
|
channels 6 and 7 into another stereo file using a 44.1kHz sampling
|
||||||
both:
|
rate for both:
|
||||||
.Bd -literal -offset indent
|
.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
|
.Ed
|
||||||
.Pp
|
.Pp
|
||||||
Split a stereo file into two mono files:
|
Split a stereo file into two mono files:
|
||||||
.Bd -literal -offset indent
|
.Bd -literal -offset indent
|
||||||
$ aucat -n -j off -i stereo.wav -C 0:0 -o left.wav -C 1:1 \e
|
$ aucat -n -i stereo.wav -c 0:0 -o left.wav \e
|
||||||
-o right.wav
|
-c 1:1 -o right.wav
|
||||||
.Ed
|
.Ed
|
||||||
.Sh SEE ALSO
|
.Sh SEE ALSO
|
||||||
.Xr audioctl 1 ,
|
.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 audio 4 ,
|
||||||
.Xr sndio 7
|
.Xr sndio 7
|
||||||
.Sh BUGS
|
.Sh BUGS
|
||||||
Resampling is low quality; down-sampling especially should be avoided
|
Resampling is low quality.
|
||||||
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.
|
|
||||||
|
|
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$ */
|
/* $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
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* 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
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
*/
|
*/
|
||||||
#ifndef MIOFILE_H
|
#ifndef DEFS_H
|
||||||
#define MIOFILE_H
|
#define DEFS_H
|
||||||
|
|
||||||
struct file;
|
/*
|
||||||
struct fileops;
|
* units used for MTC clock.
|
||||||
struct miofile;
|
*/
|
||||||
|
#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(DEFS_H) */
|
||||||
|
|
||||||
#endif /* !defined(MIOFILE_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$ */
|
/* $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
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* 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
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
*/
|
*/
|
||||||
#ifndef APARAMS_H
|
#ifndef DSP_H
|
||||||
#define APARAMS_H
|
#define DSP_H
|
||||||
|
|
||||||
#include <sys/param.h>
|
#include <sys/types.h>
|
||||||
|
#include "defs.h"
|
||||||
#define NCHAN_MAX 16 /* max channel in a stream */
|
|
||||||
#define RATE_MIN 4000 /* min sample rate */
|
|
||||||
#define RATE_MAX 192000 /* max sample rate */
|
|
||||||
#define BITS_MIN 1 /* min bits per sample */
|
|
||||||
#define BITS_MAX 32 /* max bits per sample */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Maximum size of the encording string (the longest possible
|
|
||||||
* encoding is ``s24le3msb'').
|
|
||||||
*/
|
|
||||||
#define ENCMAX 10
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Default bytes per sample for the given bits per sample.
|
|
||||||
*/
|
|
||||||
#define APARAMS_BPS(bits) (((bits) <= 8) ? 1 : (((bits) <= 16) ? 2 : 4))
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Encoding specification.
|
|
||||||
*/
|
|
||||||
struct aparams {
|
|
||||||
unsigned int bps; /* bytes per sample */
|
|
||||||
unsigned int bits; /* actually used bits */
|
|
||||||
unsigned int le; /* 1 if little endian, 0 if big endian */
|
|
||||||
unsigned int sig; /* 1 if signed, 0 if unsigned */
|
|
||||||
unsigned int msb; /* 1 if msb justified, 0 if lsb justified */
|
|
||||||
unsigned int cmin, cmax; /* provided/consumed channels */
|
|
||||||
unsigned int rate; /* frames per second */
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Samples are numbers in the interval [-1, 1[, note that 1, the upper
|
* Samples are numbers in the interval [-1, 1[, note that 1, the upper
|
||||||
|
@ -62,14 +33,12 @@ struct aparams {
|
||||||
|
|
||||||
#if ADATA_BITS == 16
|
#if ADATA_BITS == 16
|
||||||
|
|
||||||
typedef short adata_t;
|
|
||||||
|
|
||||||
#define ADATA_MUL(x,y) (((int)(x) * (int)(y)) >> (ADATA_BITS - 1))
|
#define ADATA_MUL(x,y) (((int)(x) * (int)(y)) >> (ADATA_BITS - 1))
|
||||||
#define ADATA_MULDIV(x,y,z) ((int)(x) * (int)(y) / (int)(z))
|
#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__)
|
#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"
|
#error "no 24-bit code for this architecture"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
typedef int adata_t;
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#error "only 16-bit and 24-bit precisions are supported"
|
#error "only 16-bit and 24-bit precisions are supported"
|
||||||
#endif
|
#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))
|
#define MIDI_TO_ADATA(m) (aparams_ctltovol[m] << (ADATA_BITS - 16))
|
||||||
|
|
||||||
extern int aparams_ctltovol[128];
|
extern int aparams_ctltovol[128];
|
||||||
extern struct aparams aparams_none;
|
|
||||||
|
|
||||||
void aparams_init(struct aparams *, unsigned int, unsigned int, unsigned int);
|
void aparams_init(struct aparams *);
|
||||||
void aparams_dbg(struct aparams *);
|
void aparams_log(struct aparams *);
|
||||||
int aparams_eqrate(struct aparams *, struct aparams *);
|
|
||||||
int aparams_eqenc(struct aparams *, struct aparams *);
|
|
||||||
int aparams_eq(struct aparams *, struct aparams *);
|
|
||||||
int aparams_subset(struct aparams *, struct aparams *);
|
|
||||||
void aparams_grow(struct aparams *, struct aparams *);
|
|
||||||
unsigned int aparams_bpf(struct aparams *);
|
|
||||||
int aparams_strtoenc(struct aparams *, char *);
|
int aparams_strtoenc(struct aparams *, char *);
|
||||||
int aparams_enctostr(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
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
*/
|
*/
|
||||||
#ifndef AUCAT_SYSEX_H
|
#ifndef SYSEX_H
|
||||||
#define AUCAT_SYSEX_H
|
#define SYSEX_H
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
@ -46,12 +46,9 @@
|
||||||
#define SYSEX_MMC_LOC_CMD 0x01
|
#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_DEV_ANY 0x7f
|
||||||
#define SYSEX_AUCAT_MIXINFO 0x01 /* mixer info */
|
|
||||||
#define SYSEX_AUCAT_DUMPREQ 0x02 /* dump request */
|
|
||||||
#define SYSEX_AUCAT_DUMPEND 0x03 /* end of dump */
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* minimum size of sysex message we accept
|
* minimum size of sysex message we accept
|
||||||
|
@ -59,9 +56,7 @@
|
||||||
#define SYSEX_SIZE(m) (5 + sizeof(struct sysex_ ## m))
|
#define SYSEX_SIZE(m) (5 + sizeof(struct sysex_ ## m))
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* all possible system exclusive messages we support. For aucat-specific
|
* all possible system exclusive messages we support.
|
||||||
* messages we use the same header as real-time messages to simplify the
|
|
||||||
* message parser
|
|
||||||
*/
|
*/
|
||||||
struct sysex {
|
struct sysex {
|
||||||
uint8_t start;
|
uint8_t start;
|
||||||
|
@ -87,7 +82,10 @@ struct sysex {
|
||||||
struct sysex_loc {
|
struct sysex_loc {
|
||||||
uint8_t len;
|
uint8_t len;
|
||||||
uint8_t cmd;
|
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 min;
|
||||||
uint8_t sec;
|
uint8_t sec;
|
||||||
uint8_t fr;
|
uint8_t fr;
|
||||||
|
@ -101,20 +99,7 @@ struct sysex {
|
||||||
uint8_t fr;
|
uint8_t fr;
|
||||||
uint8_t end;
|
uint8_t end;
|
||||||
} full;
|
} 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;
|
} 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$ */
|
/* $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
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* 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
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
* 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 {
|
#include <stddef.h>
|
||||||
struct file file;
|
|
||||||
int fd; /* file descriptor */
|
|
||||||
};
|
|
||||||
|
|
||||||
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 *xmalloc(size_t);
|
||||||
void pipe_close(struct file *);
|
char *xstrdup(char *);
|
||||||
unsigned int pipe_read(struct file *, unsigned char *, unsigned int);
|
void xfree(void *);
|
||||||
unsigned int pipe_write(struct file *, unsigned char *, unsigned int);
|
|
||||||
int pipe_nfds(struct file *);
|
|
||||||
int pipe_pollfd(struct file *, struct pollfd *, int);
|
|
||||||
int pipe_revents(struct file *, struct pollfd *);
|
|
||||||
int pipe_seek(struct file *, off_t);
|
|
||||||
int pipe_trunc(struct file *, off_t);
|
|
||||||
off_t pipe_endpos(struct file *);
|
|
||||||
|
|
||||||
#endif /* !defined(PIPE_H) */
|
/*
|
||||||
|
* 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
|
#define WAV_H
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#include "dsp.h"
|
||||||
#include "aparams.h"
|
|
||||||
#include "pipe.h"
|
|
||||||
|
|
||||||
struct wav {
|
struct wav {
|
||||||
struct pipe pipe;
|
struct aparams par; /* file params */
|
||||||
struct wav *next;
|
int rate; /* file sample rate */
|
||||||
#define HDR_AUTO 0 /* guess by looking at the file name */
|
int nch; /* file channel count */
|
||||||
#define HDR_RAW 1 /* no headers, ie openbsd native ;-) */
|
#define HDR_AUTO 0
|
||||||
#define HDR_WAV 2 /* microsoft riff wave */
|
#define HDR_RAW 1
|
||||||
unsigned int hdr; /* HDR_RAW or HDR_WAV */
|
#define HDR_WAV 2
|
||||||
unsigned int xrun; /* xrun policy */
|
int hdr; /* header type */
|
||||||
struct aparams hpar; /* parameters to write on the header */
|
int fd; /* file descriptor */
|
||||||
off_t rbytes; /* bytes to read, -1 if no limit */
|
#define WAV_FREAD 1 /* open for reading */
|
||||||
off_t wbytes; /* bytes to write, -1 if no limit */
|
#define WAV_FWRITE 2 /* open for writing */
|
||||||
off_t startpos; /* beginning of the data chunk */
|
int flags; /* bitmap of above */
|
||||||
off_t endpos; /* end of the data chunk */
|
off_t curpos; /* read/write position (bytes) */
|
||||||
off_t mmcpos; /* play/rec start point set by MMC */
|
off_t startpos; /* where payload starts */
|
||||||
short *map; /* mulaw/alaw -> s16 conversion table */
|
off_t endpos; /* where payload ends */
|
||||||
int slot; /* mixer ctl slot number */
|
off_t maxpos; /* max allowed pos (.wav limitation) */
|
||||||
int mmc; /* use MMC control */
|
short *map; /* mulaw/alaw conversions */
|
||||||
int join; /* join/expand channels */
|
char *path; /* file name (debug only) */
|
||||||
unsigned int vol; /* current volume */
|
|
||||||
unsigned int maxweight; /* dynamic range when vol == 127 */
|
|
||||||
#define WAV_CFG 0 /* parameters read from headers */
|
|
||||||
#define WAV_INIT 1 /* not trying to do anything */
|
|
||||||
#define WAV_START 2 /* buffer allocated */
|
|
||||||
#define WAV_READY 3 /* buffer filled enough */
|
|
||||||
#define WAV_RUN 4 /* buffer attached to device */
|
|
||||||
#define WAV_MIDI 5 /* midi "syx" file */
|
|
||||||
unsigned int pstate; /* one of above */
|
|
||||||
unsigned int mode; /* bitmap of MODE_* */
|
|
||||||
struct dev *dev; /* device playing or recording */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct fileops wav_ops;
|
int wav_open(struct wav *, char *, int, int, struct aparams *, int, int);
|
||||||
struct wav *wav_list;
|
size_t wav_read(struct wav *, void *, size_t);
|
||||||
|
size_t wav_write(struct wav *, void *, size_t);
|
||||||
struct wav *wav_new_in(struct fileops *, struct dev *,
|
int wav_seek(struct wav *, off_t);
|
||||||
unsigned int, char *, unsigned int, struct aparams *,
|
void wav_close(struct wav *);
|
||||||
unsigned int, unsigned int, int, int);
|
|
||||||
struct wav *wav_new_out(struct fileops *, struct dev *,
|
|
||||||
unsigned int, char *, unsigned int, struct aparams *,
|
|
||||||
unsigned int, int, int);
|
|
||||||
unsigned int wav_read(struct file *, unsigned char *, unsigned int);
|
|
||||||
unsigned int wav_write(struct file *, unsigned char *, unsigned int);
|
|
||||||
void wav_close(struct file *);
|
|
||||||
int wav_readhdr(int, struct aparams *, off_t *, off_t *, short **);
|
|
||||||
int wav_writehdr(int, struct aparams *, off_t *, off_t);
|
|
||||||
void wav_conv(unsigned char *, unsigned int, short *);
|
|
||||||
int wav_init(struct wav *);
|
|
||||||
|
|
||||||
extern short wav_ulawmap[256];
|
|
||||||
extern short wav_alawmap[256];
|
|
||||||
|
|
||||||
#endif /* !defined(WAV_H) */
|
#endif /* !defined(WAV_H) */
|
||||||
|
|
Loading…
Reference in New Issue