summaryrefslogtreecommitdiff
path: root/usr.bin/aucat/aproc.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.bin/aucat/aproc.c')
-rw-r--r--usr.bin/aucat/aproc.c850
1 files changed, 850 insertions, 0 deletions
diff --git a/usr.bin/aucat/aproc.c b/usr.bin/aucat/aproc.c
new file mode 100644
index 00000000000..12b6aebb6a1
--- /dev/null
+++ b/usr.bin/aucat/aproc.c
@@ -0,0 +1,850 @@
+/* $OpenBSD: aproc.c,v 1.1 2008/05/23 07:15:46 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) handle underruns in rpipe and mix
+ *
+ * (hard) handle overruns in wpipe and sub
+ *
+ * (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)
+{
+ 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;
+
+ if (!(f->state & FILE_RFLOW) && ABUF_FULL(obuf))
+ errx(1, "%s: overrun, unimplemented", f->name);
+
+ if (ABUF_FULL(obuf))
+ return 0;
+ data = abuf_wgetblk(obuf, &count, 0);
+ obuf->used += file_read(f, data, 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;
+
+ if (!(f->state & FILE_ROK))
+ return 0;
+ data = abuf_wgetblk(obuf, &count, 0);
+ obuf->used += file_read(f, data, count);
+ return f->state & FILE_ROK;
+}
+
+void
+rpipe_del(struct aproc *p)
+{
+ struct file *f = p->u.io.file;
+
+ f->rproc = NULL;
+ f->events &= ~POLLIN;
+ aproc_del(p);
+}
+
+void
+rpipe_eof(struct aproc *p, struct abuf *ibuf_dummy)
+{
+ DPRINTFN(3, "rpipe_eof: %s\n", p->name);
+ abuf_eof(LIST_FIRST(&p->obuflist));
+ rpipe_del(p);
+}
+
+void
+rpipe_hup(struct aproc *p, struct abuf *obuf)
+{
+ DPRINTFN(3, "rpipe_hup: %s\n", p->name);
+ rpipe_del(p);
+}
+
+struct aproc_ops rpipe_ops = {
+ "rpipe", rpipe_in, rpipe_out, rpipe_eof, rpipe_hup, NULL, NULL
+};
+
+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;
+ f->state |= FILE_RFLOW;
+ return p;
+}
+
+void
+wpipe_del(struct aproc *p)
+{
+ struct file *f = p->u.io.file;
+
+ f->wproc = NULL;
+ f->events &= ~POLLOUT;
+ aproc_del(p);
+}
+
+int
+wpipe_in(struct aproc *p, struct abuf *ibuf)
+{
+ struct file *f = p->u.io.file;
+ unsigned char *data;
+ unsigned count;
+
+ if (!(f->state & FILE_WOK))
+ return 0;
+
+ data = abuf_rgetblk(ibuf, &count, 0);
+ count = file_write(f, data, count);
+ ibuf->used -= count;
+ ibuf->start += count;
+ if (ibuf->start >= ibuf->len)
+ ibuf->start -= ibuf->len;
+ 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;
+
+ if (!(f->state & FILE_WFLOW) && ABUF_EMPTY(ibuf))
+ errx(1, "%s: underrun, unimplemented", f->name);
+
+ if (ABUF_EMPTY(ibuf))
+ return 0;
+ data = abuf_rgetblk(ibuf, &count, 0);
+ count = file_write(f, data, count);
+ ibuf->used -= count;
+ ibuf->start += count;
+ if (ibuf->start >= ibuf->len)
+ ibuf->start -= ibuf->len;
+ if (ABUF_EOF(ibuf)) {
+ abuf_hup(ibuf);
+ wpipe_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);
+ wpipe_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));
+ wpipe_del(p);
+}
+
+struct aproc_ops wpipe_ops = {
+ "wpipe", wpipe_in, wpipe_out, wpipe_eof, wpipe_hup, NULL, NULL
+};
+
+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;
+ f->state |= FILE_WFLOW;
+ 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, zero = %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: zero = %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++;
+ }
+
+ ibuf->used -= scount;
+ ibuf->mixdone += scount;
+ ibuf->start += scount;
+ if (ibuf->start >= ibuf->len)
+ ibuf->start -= ibuf->len;
+
+ DPRINTFN(4, "mix_badd: added %u, done = %u, zero = %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, zero = %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;
+
+ obuf->used += ocount;
+ obuf->mixtodo -= ocount;
+ abuf_flush(obuf);
+ mix_bzero(p);
+ for (i = LIST_FIRST(&p->ibuflist); i != LIST_END(); 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;
+
+ DPRINTFN(4, "mix_out: used = %u, zero = %u\n",
+ obuf->used, obuf->mixtodo);
+
+ /*
+ * XXX: should handle underruns here, currently if one input is
+ * blocked, then the output block can underrun.
+ */
+ mix_bzero(p);
+ ocount = obuf->mixtodo;
+ for (i = LIST_FIRST(&p->ibuflist); i != LIST_END(); i = inext) {
+ inext = LIST_NEXT(i, ient);
+ 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;
+ }
+ obuf->used += 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;
+}
+
+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
+};
+
+struct aproc *
+mix_new(void)
+{
+ struct aproc *p;
+
+ p = aproc_new(&mix_ops, "softmix");
+ 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);
+ obuf->subdone += scount;
+ obuf->used += 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);
+}
+
+void
+sub_del(struct aproc *p)
+{
+ aproc_del(p);
+}
+
+int
+sub_in(struct aproc *p, struct abuf *ibuf)
+{
+ struct abuf *i, *inext;
+ unsigned done;
+ int again;
+
+ again = 1;
+ done = ibuf->used;
+ for (i = LIST_FIRST(&p->obuflist); i != LIST_END(); i = inext) {
+ inext = LIST_NEXT(i, oent);
+ if (ABUF_WOK(i)) {
+ 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;
+ }
+ ibuf->used -= done;
+ ibuf->start += done;
+ if (ibuf->start >= ibuf->len)
+ ibuf->start -= ibuf->len;
+ 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;
+ }
+ ibuf->used -= done;
+ ibuf->start += done;
+ if (ibuf->start >= ibuf->len)
+ ibuf->start -= ibuf->len;
+ if (ABUF_EOF(ibuf)) {
+ abuf_hup(ibuf);
+ for (i = LIST_FIRST(&p->obuflist);
+ i != LIST_END();
+ i = inext) {
+ inext = LIST_NEXT(i, oent);
+ if (i != ibuf)
+ abuf_eof(i);
+ }
+ ibuf->wproc = NULL;
+ sub_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);
+ }
+ sub_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);
+ sub_del(p);
+ } else
+ abuf_run(ibuf);
+ DPRINTF("sub_hup: done\n");
+}
+
+void
+sub_newout(struct aproc *p, struct abuf *obuf)
+{
+ obuf->subdone = 0;
+}
+
+struct aproc_ops sub_ops = {
+ "sub", sub_in, sub_out, sub_eof, sub_hup, NULL, sub_newout
+};
+
+struct aproc *
+sub_new(void)
+{
+ struct aproc *p;
+
+ p = aproc_new(&sub_ops, "copy");
+ 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;
+ ibuf->used -= icount;
+ ibuf->start += icount;
+ if (ibuf->start >= ibuf->len)
+ ibuf->start -= ibuf->len;
+
+ ocount -= ofr * ost->bpf;
+ obuf->used += ocount;
+}
+
+void
+conv_del(struct aproc *p)
+{
+ aproc_del(p);
+}
+
+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);
+ conv_del(p);
+ return 0;
+ }
+ abuf_fill(ibuf);
+ return 1;
+}
+
+void
+conv_eof(struct aproc *p, struct abuf *ibuf)
+{
+ abuf_eof(LIST_FIRST(&p->obuflist));
+ conv_del(p);
+}
+
+void
+conv_hup(struct aproc *p, struct abuf *obuf)
+{
+ abuf_hup(LIST_FIRST(&p->ibuflist));
+ conv_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
+};
+
+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;
+}