[BACK]Return to aproc.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / aucat

File: [local] / src / usr.bin / aucat / Attic / aproc.c (download)

Revision 1.3, Mon Jun 2 17:03:25 2008 UTC (16 years ago) by ratchov
Branch: MAIN
Changes since 1.2: +53 -10 lines

Allows any input/output stream to underrun/overrun without disturbing
other streams. Beside making aucat usable with slow apps (eg. cdio),
this change will ease turning aucat into an audio server later

if one of the input buffers underruns, then silence is generated in
place of the missing samples. Later, as many samples are dropped in
order to maintain the stream in sync with the rest

if one of the output buffers overruns then newer samples are
discarded. Later silence is generated in order to maintain the
stream in sync with the rest.

ok jakemsr

/*	$OpenBSD: aproc.c,v 1.3 2008/06/02 17:03:25 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 icount, ocount;

	DPRINTFN(4, "mix_in: used = %u, done = %u, zero = %u\n",
	    ibuf->used, ibuf->mixdone, obuf->mixtodo);

	/* 
	 * discard data already sent as silence
	 */
	if (ibuf->mixdrop > 0) {
		icount = ibuf->mixdrop;
		if (icount > ibuf->used)
			icount = ibuf->used;
		ibuf->used -= icount;
		ibuf->start += icount;
		if (ibuf->start >= ibuf->len)
			ibuf->start -= ibuf->len;
		ibuf->mixdrop -= icount;
		DPRINTF("mix_in: catched xruns, drop = %u\n", ibuf->mixdrop);
	}
		
	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(&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, zero = %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) && i->mixdone == 0) {
			drop = obuf->mixtodo;
			i->mixdone += drop;
			i->mixdrop += drop;
			DPRINTF("mix_out: xrun, drop = %u\n", i->mixdrop);
		} 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;
	}
	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->mixdrop = 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, 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) && i->subdone == 0) {
			drop = ibuf->used;
			i->subdrop += drop;
			i->subdone += drop;
			DPRINTF("sub_in: xrun, drop =  %u\n", i->subdrop);
		} else {
			sub_bcopy(ibuf, i);
			abuf_flush(i);
		}
#ifdef sub_xrun_disabled		
		if (!ABUF_WOK(i))
			again = 0;
#endif
		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 char *odata;
	unsigned ocount;
	unsigned done;

	/*
	 * generate silence for dropped samples
	 */
	while (obuf->subdrop > 0) {
		odata = abuf_wgetblk(obuf, &ocount, 0);
		if (ocount >= obuf->subdrop)
			ocount = obuf->subdrop;
		if (ocount == 0)
			break;
		memset(odata, 0, ocount);
		obuf->used += ocount;
		obuf->subdrop -= ocount;
		DPRINTF("sub_out: catched, drop = %u\n", obuf->subdrop);
	}

	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(&p->obuflist);
		     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;
	obuf->subdrop = 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;
}