File: [local] / src / usr.bin / aucat / Attic / aproc.c (download)
Revision 1.8, Thu Aug 14 09:45:23 2008 UTC (15 years, 10 months ago) by ratchov
Branch: MAIN
Changes since 1.7: +21 -33 lines
add a xxx_done() method to aproc structures, that cleans up just
before free()ing the aproc structure, this is cleaner and will
allow to reuse wpipe_xxx() and rpipe_xxx() when defining new
aprocs. No behaviour change.
ok jakemsr
|
/* $OpenBSD: aproc.c,v 1.8 2008/08/14 09:45:23 ratchov Exp $ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* aproc structures are simple audio processing units. They are
* interconnected by abuf structures and form a kind of circuit. aproc
* structure have call-backs that do the actual processing.
*
* This module implements the following processing units:
*
* - rpipe: read end of an unix file (pipe, socket, device...)
*
* - wpipe: write end of an unix file (pipe, socket, device...)
*
* - mix: mix N inputs -> 1 output
*
* - sub: from 1 input -> extract/copy N outputs
*
* - conv: converts/resamples/remaps a single stream
*
* TODO
*
* (easy) split the "conv" into 2 converters: one for input (that
* convers anything to 16bit signed) and one for the output (that
* converts 16bit signed to anything)
*
* (hard) add a lowpass filter for the resampler. Quality is
* not acceptable as is.
*/
#include <err.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "conf.h"
#include "aparams.h"
#include "abuf.h"
#include "aproc.h"
#include "file.h"
struct aproc *
aproc_new(struct aproc_ops *ops, char *name)
{
struct aproc *p;
p = malloc(sizeof(struct aproc));
if (p == NULL)
err(1, name);
LIST_INIT(&p->ibuflist);
LIST_INIT(&p->obuflist);
p->name = name;
p->ops = ops;
return p;
}
void
aproc_del(struct aproc *p)
{
if (p->ops->done)
p->ops->done(p);
DPRINTF("aproc_del: %s: %s: deleted\n", p->ops->name, p->name);
free(p);
}
void
aproc_setin(struct aproc *p, struct abuf *ibuf)
{
LIST_INSERT_HEAD(&p->ibuflist, ibuf, ient);
ibuf->rproc = p;
if (p->ops->newin)
p->ops->newin(p, ibuf);
}
void
aproc_setout(struct aproc *p, struct abuf *obuf)
{
LIST_INSERT_HEAD(&p->obuflist, obuf, oent);
obuf->wproc = p;
if (p->ops->newout)
p->ops->newout(p, obuf);
}
int
rpipe_in(struct aproc *p, struct abuf *ibuf_dummy)
{
struct abuf *obuf = LIST_FIRST(&p->obuflist);
struct file *f = p->u.io.file;
unsigned char *data;
unsigned count;
DPRINTFN(3, "rpipe_in: %s\n", p->name);
if (ABUF_FULL(obuf))
return 0;
data = abuf_wgetblk(obuf, &count, 0);
count = file_read(f, data, count);
abuf_wcommit(obuf, count);
abuf_flush(obuf);
return !ABUF_FULL(obuf);
}
int
rpipe_out(struct aproc *p, struct abuf *obuf)
{
struct file *f = p->u.io.file;
unsigned char *data;
unsigned count;
DPRINTFN(3, "rpipe_out: %s\n", p->name);
if (!(f->state & FILE_ROK))
return 0;
data = abuf_wgetblk(obuf, &count, 0);
count = file_read(f, data, count);
abuf_wcommit(obuf, count);
return f->state & FILE_ROK;
}
void
rpipe_done(struct aproc *p)
{
struct file *f = p->u.io.file;
f->rproc = NULL;
f->events &= ~POLLIN;
}
void
rpipe_eof(struct aproc *p, struct abuf *ibuf_dummy)
{
DPRINTFN(3, "rpipe_eof: %s\n", p->name);
abuf_eof(LIST_FIRST(&p->obuflist));
aproc_del(p);
}
void
rpipe_hup(struct aproc *p, struct abuf *obuf)
{
DPRINTFN(3, "rpipe_hup: %s\n", p->name);
aproc_del(p);
}
struct aproc_ops rpipe_ops = {
"rpipe", rpipe_in, rpipe_out, rpipe_eof, rpipe_hup, NULL, NULL, rpipe_done
};
struct aproc *
rpipe_new(struct file *f)
{
struct aproc *p;
p = aproc_new(&rpipe_ops, f->name);
p->u.io.file = f;
f->rproc = p;
f->events |= POLLIN;
return p;
}
void
wpipe_done(struct aproc *p)
{
struct file *f = p->u.io.file;
f->wproc = NULL;
f->events &= ~POLLOUT;
}
int
wpipe_in(struct aproc *p, struct abuf *ibuf)
{
struct file *f = p->u.io.file;
unsigned char *data;
unsigned count;
DPRINTFN(3, "wpipe_in: %s\n", p->name);
if (!(f->state & FILE_WOK))
return 0;
data = abuf_rgetblk(ibuf, &count, 0);
count = file_write(f, data, count);
abuf_rdiscard(ibuf, count);
return f->state & FILE_WOK;
}
int
wpipe_out(struct aproc *p, struct abuf *obuf_dummy)
{
struct abuf *ibuf = LIST_FIRST(&p->ibuflist);
struct file *f = p->u.io.file;
unsigned char *data;
unsigned count;
DPRINTFN(3, "wpipe_out: %s\n", p->name);
if (ABUF_EMPTY(ibuf))
return 0;
data = abuf_rgetblk(ibuf, &count, 0);
count = file_write(f, data, count);
abuf_rdiscard(ibuf, count);
if (ABUF_EOF(ibuf)) {
abuf_hup(ibuf);
aproc_del(p);
return 0;
}
abuf_fill(ibuf);
return 1;
}
void
wpipe_eof(struct aproc *p, struct abuf *ibuf)
{
DPRINTFN(3, "wpipe_eof: %s\n", p->name);
aproc_del(p);
}
void
wpipe_hup(struct aproc *p, struct abuf *obuf_dummy)
{
DPRINTFN(3, "wpipe_hup: %s\n", p->name);
abuf_hup(LIST_FIRST(&p->ibuflist));
aproc_del(p);
}
struct aproc_ops wpipe_ops = {
"wpipe", wpipe_in, wpipe_out, wpipe_eof, wpipe_hup, NULL, NULL, wpipe_done
};
struct aproc *
wpipe_new(struct file *f)
{
struct aproc *p;
p = aproc_new(&wpipe_ops, f->name);
p->u.io.file = f;
f->wproc = p;
f->events |= POLLOUT;
return p;
}
/*
* Fill an output block with silence.
*/
void
mix_bzero(struct aproc *p)
{
struct abuf *obuf = LIST_FIRST(&p->obuflist);
short *odata;
unsigned ocount;
DPRINTFN(4, "mix_bzero: used = %u, todo = %u\n",
obuf->used, obuf->mixtodo);
odata = (short *)abuf_wgetblk(obuf, &ocount, obuf->mixtodo);
if (ocount == 0)
return;
memset(odata, 0, ocount);
obuf->mixtodo += ocount;
DPRINTFN(4, "mix_bzero: ocount %u, todo %u\n", ocount, obuf->mixtodo);
}
/*
* Mix an input block over an output block.
*/
void
mix_badd(struct abuf *ibuf, struct abuf *obuf)
{
short *idata, *odata;
unsigned i, scount, icount, ocount;
int vol = ibuf->mixvol;
DPRINTFN(4, "mix_badd: todo = %u, done = %u\n",
obuf->mixtodo, ibuf->mixdone);
idata = (short *)abuf_rgetblk(ibuf, &icount, 0);
if (icount == 0)
return;
odata = (short *)abuf_wgetblk(obuf, &ocount, ibuf->mixdone);
if (ocount == 0)
return;
scount = (icount < ocount) ? icount : ocount;
for (i = scount / sizeof(short); i > 0; i--) {
*odata += (*idata * vol) >> ADATA_SHIFT;
idata++;
odata++;
}
abuf_rdiscard(ibuf, scount);
ibuf->mixdone += scount;
DPRINTFN(4, "mix_badd: added %u, done = %u, todo = %u\n",
scount, ibuf->mixdone, obuf->mixtodo);
}
/*
* Remove an input stream from the mixer.
*/
void
mix_rm(struct aproc *p, struct abuf *ibuf)
{
LIST_REMOVE(ibuf, ient);
DPRINTF("mix_rm: %s\n", p->name);
}
int
mix_in(struct aproc *p, struct abuf *ibuf)
{
struct abuf *i, *inext, *obuf = LIST_FIRST(&p->obuflist);
unsigned ocount;
DPRINTFN(4, "mix_in: used = %u, done = %u, todo = %u\n",
ibuf->used, ibuf->mixdone, obuf->mixtodo);
if (ibuf->mixdone >= obuf->mixtodo)
return 0;
mix_badd(ibuf, obuf);
ocount = obuf->mixtodo;
LIST_FOREACH(i, &p->ibuflist, ient) {
if (ocount > i->mixdone)
ocount = i->mixdone;
}
if (ocount == 0)
return 0;
abuf_wcommit(obuf, ocount);
obuf->mixtodo -= ocount;
abuf_flush(obuf);
mix_bzero(p);
for (i = LIST_FIRST(&p->ibuflist); i != LIST_END(&p->ibuflist); i = inext) {
inext = LIST_NEXT(i, ient);
i->mixdone -= ocount;
if (i != ibuf && i->mixdone < obuf->mixtodo) {
if (ABUF_EOF(i)) {
mix_rm(p, i);
abuf_hup(i);
continue;
}
mix_badd(i, obuf);
abuf_fill(i);
}
}
return 1;
}
int
mix_out(struct aproc *p, struct abuf *obuf)
{
struct abuf *i, *inext;
unsigned ocount, drop;
DPRINTFN(4, "mix_out: used = %u, todo = %u\n",
obuf->used, obuf->mixtodo);
mix_bzero(p);
ocount = obuf->mixtodo;
for (i = LIST_FIRST(&p->ibuflist); i != LIST_END(&p->ibuflist); i = inext) {
inext = LIST_NEXT(i, ient);
if (!ABUF_ROK(i)) {
if ((p->u.mix.flags & MIX_DROP) && i->mixdone == 0) {
if (i->xrun == XRUN_ERROR) {
mix_rm(p, i);
abuf_hup(i);
continue;
}
drop = obuf->mixtodo;
i->mixdone += drop;
if (i->xrun == XRUN_SYNC)
i->drop += drop;
DPRINTF("mix_out: drop = %u\n", i->drop);
}
} else
mix_badd(i, obuf);
if (ocount > i->mixdone)
ocount = i->mixdone;
if (ABUF_EOF(i)) {
mix_rm(p, i);
abuf_hup(i);
continue;
}
abuf_fill(i);
}
if (ocount == 0)
return 0;
if (LIST_EMPTY(&p->ibuflist)) {
DPRINTF("mix_out: nothing more to do...\n");
obuf->wproc = NULL;
aproc_del(p);
return 0;
}
abuf_wcommit(obuf, ocount);
obuf->mixtodo -= ocount;
LIST_FOREACH(i, &p->ibuflist, ient) {
i->mixdone -= ocount;
}
return 1;
}
void
mix_eof(struct aproc *p, struct abuf *ibuf)
{
struct abuf *obuf = LIST_FIRST(&p->obuflist);
DPRINTF("mix_eof: %s: detached\n", p->name);
mix_rm(p, ibuf);
/*
* If there's no more inputs, abuf_run() will trigger the eof
* condition and propagate it, so no need to handle it here.
*/
abuf_run(obuf);
DPRINTF("mix_eof: done\n");
}
void
mix_hup(struct aproc *p, struct abuf *obuf)
{
struct abuf *ibuf;
while (!LIST_EMPTY(&p->ibuflist)) {
ibuf = LIST_FIRST(&p->ibuflist);
mix_rm(p, ibuf);
abuf_hup(ibuf);
}
DPRINTF("mix_hup: %s: done\n", p->name);
aproc_del(p);
}
void
mix_newin(struct aproc *p, struct abuf *ibuf)
{
ibuf->mixdone = 0;
ibuf->mixvol = ADATA_UNIT;
ibuf->xrun = XRUN_IGNORE;
}
void
mix_newout(struct aproc *p, struct abuf *obuf)
{
obuf->mixtodo = 0;
mix_bzero(p);
}
struct aproc_ops mix_ops = {
"mix", mix_in, mix_out, mix_eof, mix_hup, mix_newin, mix_newout, NULL
};
struct aproc *
mix_new(void)
{
struct aproc *p;
p = aproc_new(&mix_ops, "softmix");
p->u.mix.flags = 0;
return p;
}
/*
* Copy data from ibuf to obuf.
*/
void
sub_bcopy(struct abuf *ibuf, struct abuf *obuf)
{
unsigned char *idata, *odata;
unsigned icount, ocount, scount;
idata = abuf_rgetblk(ibuf, &icount, obuf->subdone);
if (icount == 0)
return;
odata = abuf_wgetblk(obuf, &ocount, 0);
if (ocount == 0)
return;
scount = (icount < ocount) ? icount : ocount;
memcpy(odata, idata, scount);
abuf_wcommit(obuf, scount);
obuf->subdone += scount;
DPRINTFN(4, "sub_bcopy: %u bytes\n", scount);
}
void
sub_rm(struct aproc *p, struct abuf *obuf)
{
LIST_REMOVE(obuf, oent);
DPRINTF("sub_rm: %s\n", p->name);
}
int
sub_in(struct aproc *p, struct abuf *ibuf)
{
struct abuf *i, *inext;
unsigned done, drop;
int again;
again = 1;
done = ibuf->used;
for (i = LIST_FIRST(&p->obuflist); i != LIST_END(&p->obuflist); i = inext) {
inext = LIST_NEXT(i, oent);
if (!ABUF_WOK(i)) {
if ((p->u.sub.flags & SUB_DROP) && i->subdone == 0) {
if (i->xrun == XRUN_ERROR) {
sub_rm(p, i);
abuf_eof(i);
continue;
}
drop = ibuf->used;
if (i->xrun == XRUN_SYNC)
i->silence += drop;
i->subdone += drop;
DPRINTF("sub_in: silence = %u\n", i->silence);
}
} else {
sub_bcopy(ibuf, i);
abuf_flush(i);
}
if (!ABUF_WOK(i))
again = 0;
if (done > i->subdone)
done = i->subdone;
}
LIST_FOREACH(i, &p->obuflist, oent) {
i->subdone -= done;
}
abuf_rdiscard(ibuf, done);
return again;
}
int
sub_out(struct aproc *p, struct abuf *obuf)
{
struct abuf *ibuf = LIST_FIRST(&p->ibuflist);
struct abuf *i, *inext;
unsigned done;
if (obuf->subdone >= ibuf->used)
return 0;
sub_bcopy(ibuf, obuf);
done = ibuf->used;
LIST_FOREACH(i, &p->obuflist, oent) {
if (i != obuf && ABUF_WOK(i)) {
sub_bcopy(ibuf, i);
abuf_flush(i);
}
if (done > i->subdone)
done = i->subdone;
}
if (done == 0)
return 0;
LIST_FOREACH(i, &p->obuflist, oent) {
i->subdone -= done;
}
abuf_rdiscard(ibuf, done);
if (ABUF_EOF(ibuf)) {
abuf_hup(ibuf);
for (i = LIST_FIRST(&p->obuflist);
i != LIST_END(&p->obuflist);
i = inext) {
inext = LIST_NEXT(i, oent);
if (i != ibuf)
abuf_eof(i);
}
ibuf->wproc = NULL;
aproc_del(p);
return 0;
}
abuf_fill(ibuf);
return 1;
}
void
sub_eof(struct aproc *p, struct abuf *ibuf)
{
struct abuf *obuf;
while (!LIST_EMPTY(&p->obuflist)) {
obuf = LIST_FIRST(&p->obuflist);
sub_rm(p, obuf);
abuf_eof(obuf);
}
aproc_del(p);
}
void
sub_hup(struct aproc *p, struct abuf *obuf)
{
struct abuf *ibuf = LIST_FIRST(&p->ibuflist);
DPRINTF("sub_hup: %s: detached\n", p->name);
sub_rm(p, obuf);
if (LIST_EMPTY(&p->obuflist)) {
abuf_hup(ibuf);
aproc_del(p);
} else
abuf_run(ibuf);
DPRINTF("sub_hup: done\n");
}
void
sub_newout(struct aproc *p, struct abuf *obuf)
{
obuf->subdone = 0;
obuf->xrun = XRUN_IGNORE;
}
struct aproc_ops sub_ops = {
"sub", sub_in, sub_out, sub_eof, sub_hup, NULL, sub_newout, NULL
};
struct aproc *
sub_new(void)
{
struct aproc *p;
p = aproc_new(&sub_ops, "copy");
p->u.sub.flags = 0;
return p;
}
/*
* Convert one block.
*/
void
conv_bcopy(struct aconv *ist, struct aconv *ost,
struct abuf *ibuf, struct abuf *obuf)
{
int *ictx;
unsigned inch, ibps;
unsigned char *idata;
int ibnext, isigbit;
unsigned ishift;
int isnext;
unsigned ipos, orate;
unsigned ifr;
int *octx;
unsigned onch, oshift;
int osigbit;
unsigned obps;
unsigned char *odata;
int obnext, osnext;
unsigned opos, irate;
unsigned ofr;
unsigned c, i;
int s, *ctx;
unsigned icount, ocount;
/*
* It's ok to have s uninitialized, but we dont want the compiler to
* complain about it.
*/
s = (int)0xdeadbeef;
/*
* Calculate max frames readable at once from the input buffer.
*/
idata = abuf_rgetblk(ibuf, &icount, 0);
ifr = icount / ibuf->bpf;
odata = abuf_wgetblk(obuf, &ocount, 0);
ofr = ocount / obuf->bpf;
/*
* Partially copy structures into local variables, to avoid
* unnecessary indirections; this also allows the compiler to
* order local variables more "cache-friendly".
*/
ictx = ist->ctx + ist->cmin;
octx = ist->ctx + ost->cmin;
inch = ist->nch;
ibps = ist->bps;
ibnext = ist->bnext;
isigbit = ist->sigbit;
ishift = ist->shift;
isnext = ist->snext;
ipos = ist->pos;
irate = ist->rate;
onch = ost->nch;
oshift = ost->shift;
osigbit = ost->sigbit;
obps = ost->bps;
obnext = ost->bnext;
osnext = ost->snext;
opos = ost->pos;
orate = ost->rate;
/*
* Start conversion.
*/
idata += ist->bfirst;
odata += ost->bfirst;
DPRINTFN(4, "conv_bcopy: ifr=%d ofr=%d\n", ifr, ofr);
for (;;) {
if ((int)(ipos - opos) > 0) {
if (ofr == 0)
break;
ctx = octx;
for (c = onch; c > 0; c--) {
s = *ctx++ << 16;
s >>= oshift;
s ^= osigbit;
for (i = obps; i > 0; i--) {
*odata = (unsigned char)s;
s >>= 8;
odata += obnext;
}
odata += osnext;
}
opos += irate;
ofr--;
} else {
if (ifr == 0)
break;
ctx = ictx;
for (c = inch; c > 0; c--) {
for (i = ibps; i > 0; i--) {
s <<= 8;
s |= *idata;
idata += ibnext;
}
s ^= isigbit;
s <<= ishift;
*ctx++ = (short)(s >> 16);
idata += isnext;
}
ipos += orate;
ifr--;
}
}
ist->pos = ipos;
ost->pos = opos;
DPRINTFN(4, "conv_bcopy: done, ifr=%d ofr=%d\n", ifr, ofr);
/*
* Update FIFO pointers.
*/
icount -= ifr * ist->bpf;
ocount -= ofr * ost->bpf;
abuf_rdiscard(ibuf, icount);
abuf_wcommit(obuf, ocount);
}
int
conv_in(struct aproc *p, struct abuf *ibuf)
{
struct abuf *obuf = LIST_FIRST(&p->obuflist);
if (!ABUF_WOK(obuf))
return 0;
conv_bcopy(&p->u.conv.ist, &p->u.conv.ost, ibuf, obuf);
abuf_flush(obuf);
return ABUF_WOK(obuf);
}
int
conv_out(struct aproc *p, struct abuf *obuf)
{
struct abuf *ibuf = LIST_FIRST(&p->ibuflist);
if (!ABUF_ROK(ibuf))
return 0;
conv_bcopy(&p->u.conv.ist, &p->u.conv.ost, ibuf, obuf);
if (ABUF_EOF(ibuf)) {
obuf->wproc = NULL;
abuf_hup(ibuf);
aproc_del(p);
return 0;
}
abuf_fill(ibuf);
return 1;
}
void
conv_eof(struct aproc *p, struct abuf *ibuf)
{
abuf_eof(LIST_FIRST(&p->obuflist));
aproc_del(p);
}
void
conv_hup(struct aproc *p, struct abuf *obuf)
{
abuf_hup(LIST_FIRST(&p->ibuflist));
aproc_del(p);
}
void
aconv_init(struct aconv *st, struct aparams *par, int input)
{
unsigned i;
st->bps = par->bps;
st->sigbit = par->sig ? 0 : 1 << (par->bits - 1);
if (par->msb) {
st->shift = 32 - par->bps * 8;
} else {
st->shift = 32 - par->bits;
}
if ((par->le && input) || (!par->le && !input)) {
st->bfirst = st->bps - 1;
st->bnext = -1;
st->snext = 2 * st->bps;
} else {
st->bfirst = 0;
st->bnext = 1;
st->snext = 0;
}
st->cmin = par->cmin;
st->nch = par->cmax - par->cmin + 1;
st->bpf = st->nch * st->bps;
st->rate = par->rate;
st->pos = 0;
for (i = 0; i < CHAN_MAX; i++)
st->ctx[i] = 0;
}
struct aproc_ops conv_ops = {
"conv", conv_in, conv_out, conv_eof, conv_hup, NULL, NULL, NULL
};
struct aproc *
conv_new(char *name, struct aparams *ipar, struct aparams *opar)
{
struct aproc *p;
p = aproc_new(&conv_ops, name);
aconv_init(&p->u.conv.ist, ipar, 1);
aconv_init(&p->u.conv.ost, opar, 0);
return p;
}