/* $OpenBSD: aproc.c,v 1.34 2009/09/27 11:51:20 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov * * 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 * * - resamp: resample streams in native format * */ #include #include #include #include "abuf.h" #include "aparams.h" #include "aproc.h" #include "conf.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; p->refs = 0; return p; } void aproc_del(struct aproc *p) { struct abuf *i; /* * XXX: souldn't call ops->done() and friends twice * use a ``zomb'' flag or whatever */ if (p->ops->done) { p->ops->done(p); } while (!LIST_EMPTY(&p->ibuflist)) { i = LIST_FIRST(&p->ibuflist); abuf_hup(i); } while (!LIST_EMPTY(&p->obuflist)) { i = LIST_FIRST(&p->obuflist); abuf_eof(i); } if (p->refs > 0) return; 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); } void aproc_ipos(struct aproc *p, struct abuf *ibuf, int delta) { struct abuf *obuf; LIST_FOREACH(obuf, &p->obuflist, oent) { abuf_ipos(obuf, delta); } } void aproc_opos(struct aproc *p, struct abuf *obuf, int delta) { struct abuf *ibuf; LIST_FOREACH(ibuf, &p->ibuflist, ient) { abuf_opos(ibuf, delta); } } int aproc_inuse(struct aproc *p) { struct abuf *i; LIST_FOREACH(i, &p->ibuflist, ient) { if (i->inuse) return 1; } LIST_FOREACH(i, &p->obuflist, oent) { if (i->inuse) return 1; } return 0; } int aproc_depend(struct aproc *p, struct aproc *dep) { struct abuf *i; if (p == dep) return 1; LIST_FOREACH(i, &p->ibuflist, ient) { if (i->wproc && aproc_depend(i->wproc, dep)) return 1; } return 0; } 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 (ABUF_FULL(obuf) || !(f->state & FILE_ROK)) return 0; data = abuf_wgetblk(obuf, &count, 0); count = file_read(f, data, count); if (count == 0) return 0; abuf_wcommit(obuf, count); if (!abuf_flush(obuf)) return 0; return 1; } 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_RINUSE) return 0; if (ABUF_FULL(obuf) || !(f->state & FILE_ROK)) return 0; data = abuf_wgetblk(obuf, &count, 0); count = file_read(f, data, count); if (count == 0) return 0; abuf_wcommit(obuf, count); return 1; } void rpipe_done(struct aproc *p) { struct file *f = p->u.io.file; if (f == NULL) return; f->rproc = NULL; if (f->wproc == NULL) file_del(f); p->u.io.file = NULL; } void rpipe_eof(struct aproc *p, struct abuf *ibuf_dummy) { aproc_del(p); } void rpipe_hup(struct aproc *p, struct abuf *obuf) { aproc_del(p); } struct aproc_ops rpipe_ops = { "rpipe", rpipe_in, rpipe_out, rpipe_eof, rpipe_hup, NULL, /* newin */ NULL, /* newout */ aproc_ipos, aproc_opos, 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; return p; } void wpipe_done(struct aproc *p) { struct file *f = p->u.io.file; if (f == NULL) return; f->wproc = NULL; if (f->rproc == NULL) file_del(f); p->u.io.file = NULL; } 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_WINUSE) return 0; if (ABUF_EMPTY(ibuf) || !(f->state & FILE_WOK)) return 0; data = abuf_rgetblk(ibuf, &count, 0); count = file_write(f, data, count); if (count == 0) return 0; abuf_rdiscard(ibuf, count); return 1; } 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 (!abuf_fill(ibuf)) return 0; if (ABUF_EMPTY(ibuf) || !(f->state & FILE_WOK)) return 0; data = abuf_rgetblk(ibuf, &count, 0); if (count == 0) { /* XXX: this can't happen, right ? */ return 0; } count = file_write(f, data, count); if (count == 0) return 0; abuf_rdiscard(ibuf, count); return 1; } void wpipe_eof(struct aproc *p, struct abuf *ibuf) { aproc_del(p); } void wpipe_hup(struct aproc *p, struct abuf *obuf_dummy) { aproc_del(p); } struct aproc_ops wpipe_ops = { "wpipe", wpipe_in, wpipe_out, wpipe_eof, wpipe_hup, NULL, /* newin */ NULL, /* newout */ aproc_ipos, aproc_opos, 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; return p; } /* * Append the given amount of silence (or less if there's not enough * space), and crank mixitodo accordingly. */ void mix_bzero(struct abuf *obuf, unsigned zcount) { short *odata; unsigned ocount; odata = (short *)abuf_wgetblk(obuf, &ocount, obuf->mixitodo); ocount -= ocount % obuf->bpf; if (ocount > zcount) ocount = zcount; memset(odata, 0, ocount); obuf->mixitodo += ocount; } /* * Mix an input block over an output block. */ void mix_badd(struct abuf *ibuf, struct abuf *obuf) { short *idata, *odata; unsigned i, j, icnt, onext, ostart; unsigned scount, icount, ocount, zcount; int vol; /* * Calculate the maximum we can read. */ idata = (short *)abuf_rgetblk(ibuf, &icount, 0); icount /= ibuf->bpf; if (icount == 0) return; /* * Zero-fill if necessary. */ zcount = ibuf->mixodone + icount * obuf->bpf; if (zcount > obuf->mixitodo) mix_bzero(obuf, zcount - obuf->mixitodo); /* * Calculate the maximum we can write. */ odata = (short *)abuf_wgetblk(obuf, &ocount, ibuf->mixodone); ocount /= obuf->bpf; if (ocount == 0) return; vol = (ibuf->mixweight * ibuf->mixvol) >> ADATA_SHIFT; ostart = ibuf->cmin - obuf->cmin; onext = obuf->cmax - ibuf->cmax + ostart; icnt = ibuf->cmax - ibuf->cmin + 1; odata += ostart; scount = (icount < ocount) ? icount : ocount; for (i = scount; i > 0; i--) { for (j = icnt; j > 0; j--) { *odata += (*idata * vol) >> ADATA_SHIFT; idata++; odata++; } odata += onext; } abuf_rdiscard(ibuf, scount * ibuf->bpf); ibuf->mixodone += scount * obuf->bpf; } /* * Handle buffer underrun, return 0 if stream died. */ int mix_xrun(struct abuf *i, struct abuf *obuf) { unsigned fdrop; if (i->mixodone > 0) return 1; if (i->xrun == XRUN_ERROR) { abuf_hup(i); return 0; } mix_bzero(obuf, obuf->len); fdrop = obuf->mixitodo / obuf->bpf; i->mixodone += fdrop * obuf->bpf; if (i->xrun == XRUN_SYNC) i->drop += fdrop * i->bpf; else { abuf_opos(i, -(int)fdrop); if (i->duplex) { i->duplex->drop += fdrop * i->duplex->bpf; abuf_ipos(i->duplex, -(int)fdrop); } } return 1; } int mix_in(struct aproc *p, struct abuf *ibuf) { struct abuf *i, *inext, *obuf = LIST_FIRST(&p->obuflist); unsigned odone; if (!ABUF_ROK(ibuf)) return 0; odone = obuf->len; for (i = LIST_FIRST(&p->ibuflist); i != NULL; i = inext) { inext = LIST_NEXT(i, ient); if (!abuf_fill(i)) continue; /* eof */ mix_badd(i, obuf); if (odone > i->mixodone) odone = i->mixodone; } if (LIST_EMPTY(&p->ibuflist) || odone == 0) return 0; p->u.mix.lat += odone / obuf->bpf; LIST_FOREACH(i, &p->ibuflist, ient) { i->mixodone -= odone; } abuf_wcommit(obuf, odone); obuf->mixitodo -= odone; if (!abuf_flush(obuf)) return 0; /* hup */ return 1; } int mix_out(struct aproc *p, struct abuf *obuf) { struct abuf *i, *inext; unsigned odone; if (!ABUF_WOK(obuf)) return 0; odone = obuf->len; for (i = LIST_FIRST(&p->ibuflist); i != NULL; i = inext) { inext = LIST_NEXT(i, ient); if (!abuf_fill(i)) continue; /* eof */ if (!ABUF_ROK(i)) { if (p->u.mix.flags & MIX_DROP) { if (!mix_xrun(i, obuf)) continue; } } else mix_badd(i, obuf); if (odone > i->mixodone) odone = i->mixodone; } if (LIST_EMPTY(&p->ibuflist)) { if (p->u.mix.flags & MIX_AUTOQUIT) { aproc_del(p); return 0; } if (!(p->u.mix.flags & MIX_DROP)) return 0; mix_bzero(obuf, obuf->len); odone = obuf->mixitodo; p->u.mix.idle += odone / obuf->bpf; } if (odone == 0) return 0; p->u.mix.lat += odone / obuf->bpf; LIST_FOREACH(i, &p->ibuflist, ient) { i->mixodone -= odone; } abuf_wcommit(obuf, odone); obuf->mixitodo -= odone; return 1; } void mix_eof(struct aproc *p, struct abuf *ibuf) { struct abuf *i, *obuf = LIST_FIRST(&p->obuflist); unsigned odone; mix_setmaster(p); if (!aproc_inuse(p)) { /* * Find a blocked input. */ odone = obuf->len; LIST_FOREACH(i, &p->ibuflist, ient) { if (ABUF_ROK(i) && i->mixodone < obuf->mixitodo) { abuf_run(i); return; } if (odone > i->mixodone) odone = i->mixodone; } /* * No blocked inputs. Check if output is blocked. */ if (LIST_EMPTY(&p->ibuflist) || odone == obuf->mixitodo) abuf_run(obuf); } } void mix_hup(struct aproc *p, struct abuf *obuf) { aproc_del(p); } void mix_newin(struct aproc *p, struct abuf *ibuf) { p->u.mix.idle = 0; ibuf->mixodone = 0; ibuf->mixvol = ADATA_UNIT; ibuf->mixweight = ADATA_UNIT; ibuf->mixmaxweight = ADATA_UNIT; ibuf->xrun = XRUN_IGNORE; } void mix_newout(struct aproc *p, struct abuf *obuf) { obuf->mixitodo = 0; } void mix_opos(struct aproc *p, struct abuf *obuf, int delta) { p->u.mix.lat -= delta; aproc_opos(p, obuf, delta); } struct aproc_ops mix_ops = { "mix", mix_in, mix_out, mix_eof, mix_hup, mix_newin, mix_newout, aproc_ipos, mix_opos, NULL }; struct aproc * mix_new(char *name, int maxlat) { struct aproc *p; p = aproc_new(&mix_ops, name); p->u.mix.flags = 0; p->u.mix.idle = 0; p->u.mix.lat = 0; p->u.mix.maxlat = maxlat; return p; } /* * Normalize input levels. */ void mix_setmaster(struct aproc *p) { unsigned n; struct abuf *buf; int weight; n = 0; LIST_FOREACH(buf, &p->ibuflist, ient) { n++; } LIST_FOREACH(buf, &p->ibuflist, ient) { weight = ADATA_UNIT / n; if (weight > buf->mixmaxweight) weight = buf->mixmaxweight; buf->mixweight = weight; } } void mix_clear(struct aproc *p) { struct abuf *obuf = LIST_FIRST(&p->obuflist); p->u.mix.lat = 0; obuf->mixitodo = 0; } /* * Copy data from ibuf to obuf. */ void sub_bcopy(struct abuf *ibuf, struct abuf *obuf) { short *idata, *odata; unsigned i, j, ocnt, inext, istart; unsigned icount, ocount, scount; idata = (short *)abuf_rgetblk(ibuf, &icount, obuf->subidone); icount /= ibuf->bpf; if (icount == 0) return; odata = (short *)abuf_wgetblk(obuf, &ocount, 0); ocount /= obuf->bpf; if (ocount == 0) return; istart = obuf->cmin - ibuf->cmin; inext = ibuf->cmax - obuf->cmax + istart; ocnt = obuf->cmax - obuf->cmin + 1; scount = (icount < ocount) ? icount : ocount; idata += istart; for (i = scount; i > 0; i--) { for (j = ocnt; j > 0; j--) { *odata = *idata; odata++; idata++; } idata += inext; } abuf_wcommit(obuf, scount * obuf->bpf); obuf->subidone += scount * ibuf->bpf; } /* * Handle buffer overruns. Return 0 if the stream died. */ int sub_xrun(struct abuf *ibuf, struct abuf *i) { unsigned fdrop; if (i->subidone > 0) return 1; if (i->xrun == XRUN_ERROR) { abuf_eof(i); return 0; } fdrop = ibuf->used / ibuf->bpf; if (i->xrun == XRUN_SYNC) i->silence += fdrop * i->bpf; else { abuf_ipos(i, -(int)fdrop); if (i->duplex) { i->duplex->silence += fdrop * i->duplex->bpf; abuf_opos(i->duplex, -(int)fdrop); } } i->subidone += fdrop * ibuf->bpf; return 1; } int sub_in(struct aproc *p, struct abuf *ibuf) { struct abuf *i, *inext; unsigned idone; if (!ABUF_ROK(ibuf)) return 0; idone = ibuf->len; for (i = LIST_FIRST(&p->obuflist); i != NULL; i = inext) { inext = LIST_NEXT(i, oent); if (!ABUF_WOK(i)) { if (p->u.sub.flags & SUB_DROP) { if (!sub_xrun(ibuf, i)) continue; } } else sub_bcopy(ibuf, i); if (idone > i->subidone) idone = i->subidone; if (!abuf_flush(i)) continue; } if (LIST_EMPTY(&p->obuflist)) { if (p->u.sub.flags & SUB_AUTOQUIT) { aproc_del(p); return 0; } if (!(p->u.sub.flags & SUB_DROP)) return 0; idone = ibuf->used; p->u.sub.idle += idone / ibuf->bpf; } if (idone == 0) return 0; LIST_FOREACH(i, &p->obuflist, oent) { i->subidone -= idone; } abuf_rdiscard(ibuf, idone); p->u.sub.lat -= idone / ibuf->bpf; return 1; } int sub_out(struct aproc *p, struct abuf *obuf) { struct abuf *ibuf = LIST_FIRST(&p->ibuflist); struct abuf *i, *inext; unsigned idone; if (!ABUF_WOK(obuf)) return 0; if (!abuf_fill(ibuf)) return 0; /* eof */ idone = ibuf->len; for (i = LIST_FIRST(&p->obuflist); i != NULL; i = inext) { inext = LIST_NEXT(i, oent); sub_bcopy(ibuf, i); if (idone > i->subidone) idone = i->subidone; if (!abuf_flush(i)) continue; } if (LIST_EMPTY(&p->obuflist) || idone == 0) return 0; LIST_FOREACH(i, &p->obuflist, oent) { i->subidone -= idone; } abuf_rdiscard(ibuf, idone); p->u.sub.lat -= idone / ibuf->bpf; return 1; } void sub_eof(struct aproc *p, struct abuf *ibuf) { aproc_del(p); } void sub_hup(struct aproc *p, struct abuf *obuf) { struct abuf *i, *ibuf = LIST_FIRST(&p->ibuflist); unsigned idone; if (!aproc_inuse(p)) { /* * Find a blocked output. */ idone = ibuf->len; LIST_FOREACH(i, &p->obuflist, oent) { if (ABUF_WOK(i) && i->subidone < ibuf->used) { abuf_run(i); return; } if (idone > i->subidone) idone = i->subidone; } /* * No blocked outputs. Check if input is blocked. */ if (LIST_EMPTY(&p->obuflist) || idone == ibuf->used) abuf_run(ibuf); } } void sub_newout(struct aproc *p, struct abuf *obuf) { p->u.sub.idle = 0; obuf->subidone = 0; obuf->xrun = XRUN_IGNORE; } void sub_ipos(struct aproc *p, struct abuf *ibuf, int delta) { p->u.sub.lat += delta; aproc_ipos(p, ibuf, delta); } struct aproc_ops sub_ops = { "sub", sub_in, sub_out, sub_eof, sub_hup, NULL, sub_newout, sub_ipos, aproc_opos, NULL }; struct aproc * sub_new(char *name, int maxlat) { struct aproc *p; p = aproc_new(&sub_ops, name); p->u.sub.flags = 0; p->u.sub.idle = 0; p->u.sub.lat = 0; p->u.sub.maxlat = maxlat; return p; } void sub_clear(struct aproc *p) { p->u.mix.lat = 0; } /* * Convert one block. */ void resamp_bcopy(struct aproc *p, struct abuf *ibuf, struct abuf *obuf) { unsigned inch; short *idata; unsigned oblksz; unsigned ifr; unsigned onch; int s1, s2, diff; short *odata; unsigned iblksz; unsigned ofr; unsigned c; short *ctxbuf, *ctx; unsigned ctx_start; unsigned icount, ocount; /* * Calculate max frames readable at once from the input buffer. */ idata = (short *)abuf_rgetblk(ibuf, &icount, 0); ifr = icount / ibuf->bpf; icount = ifr * ibuf->bpf; odata = (short *)abuf_wgetblk(obuf, &ocount, 0); ofr = ocount / obuf->bpf; ocount = ofr * obuf->bpf; /* * Partially copy structures into local variables, to avoid * unnecessary indirections; this also allows the compiler to * order local variables more "cache-friendly". */ diff = p->u.resamp.diff; inch = ibuf->cmax - ibuf->cmin + 1; iblksz = p->u.resamp.iblksz; onch = obuf->cmax - obuf->cmin + 1; oblksz = p->u.resamp.oblksz; ctxbuf = p->u.resamp.ctx; ctx_start = p->u.resamp.ctx_start; /* * Start conversion. */ for (;;) { if (diff < 0) { if (ifr == 0) break; ctx_start ^= 1; ctx = ctxbuf + ctx_start; for (c = inch; c > 0; c--) { *ctx = *idata++; ctx += RESAMP_NCTX; } diff += oblksz; ifr--; } else { if (ofr == 0) break; ctx = ctxbuf; for (c = onch; c > 0; c--) { s1 = ctx[ctx_start]; s2 = ctx[ctx_start ^ 1]; ctx += RESAMP_NCTX; *odata++ = s1 + (s2 - s1) * diff / (int)oblksz; } diff -= iblksz; ofr--; } } p->u.resamp.diff = diff; p->u.resamp.ctx_start = ctx_start; /* * Update FIFO pointers. */ icount -= ifr * ibuf->bpf; ocount -= ofr * obuf->bpf; abuf_rdiscard(ibuf, icount); abuf_wcommit(obuf, ocount); } int resamp_in(struct aproc *p, struct abuf *ibuf) { struct abuf *obuf = LIST_FIRST(&p->obuflist); if (!ABUF_WOK(obuf) || !ABUF_ROK(ibuf)) return 0; resamp_bcopy(p, ibuf, obuf); if (!abuf_flush(obuf)) return 0; return 1; } int resamp_out(struct aproc *p, struct abuf *obuf) { struct abuf *ibuf = LIST_FIRST(&p->ibuflist); if (!abuf_fill(ibuf)) return 0; if (!ABUF_WOK(obuf) || !ABUF_ROK(ibuf)) return 0; resamp_bcopy(p, ibuf, obuf); return 1; } void resamp_eof(struct aproc *p, struct abuf *ibuf) { aproc_del(p); } void resamp_hup(struct aproc *p, struct abuf *obuf) { aproc_del(p); } void resamp_ipos(struct aproc *p, struct abuf *ibuf, int delta) { struct abuf *obuf = LIST_FIRST(&p->obuflist); long long ipos; int ifac, ofac; ifac = p->u.resamp.iblksz; ofac = p->u.resamp.oblksz; ipos = p->u.resamp.idelta + (long long)delta * ofac; delta = (ipos + ifac - 1) / ifac; p->u.resamp.idelta = ipos - (long long)delta * ifac; abuf_ipos(obuf, delta); } void resamp_opos(struct aproc *p, struct abuf *obuf, int delta) { struct abuf *ibuf = LIST_FIRST(&p->ibuflist); long long opos; int ifac, ofac; ifac = p->u.resamp.iblksz; ofac = p->u.resamp.oblksz; opos = p->u.resamp.odelta + (long long)delta * ifac; delta = (opos + ofac - 1) / ofac; p->u.resamp.odelta = opos - (long long)delta * ofac; abuf_opos(ibuf, delta); } struct aproc_ops resamp_ops = { "resamp", resamp_in, resamp_out, resamp_eof, resamp_hup, NULL, NULL, resamp_ipos, resamp_opos, NULL }; struct aproc * resamp_new(char *name, unsigned iblksz, unsigned oblksz) { struct aproc *p; unsigned i; p = aproc_new(&resamp_ops, name); p->u.resamp.iblksz = iblksz; p->u.resamp.oblksz = oblksz; p->u.resamp.diff = 0; p->u.resamp.idelta = 0; p->u.resamp.odelta = 0; p->u.resamp.ctx_start = 0; for (i = 0; i < NCHAN_MAX * RESAMP_NCTX; i++) p->u.resamp.ctx[i] = 0; return p; } /* * Convert one block. */ void cmap_bcopy(struct aproc *p, struct abuf *ibuf, struct abuf *obuf) { unsigned inch; short *idata; unsigned onch; short *odata; short *ctx, *ictx, *octx; unsigned c, f, scount, icount, ocount; /* * Calculate max frames readable at once from the input buffer. */ idata = (short *)abuf_rgetblk(ibuf, &icount, 0); icount /= ibuf->bpf; if (icount == 0) return; odata = (short *)abuf_wgetblk(obuf, &ocount, 0); ocount /= obuf->bpf; if (ocount == 0) return; scount = icount < ocount ? icount : ocount; inch = ibuf->cmax - ibuf->cmin + 1; onch = obuf->cmax - obuf->cmin + 1; ictx = p->u.cmap.ctx + ibuf->cmin; octx = p->u.cmap.ctx + obuf->cmin; for (f = scount; f > 0; f--) { ctx = ictx; for (c = inch; c > 0; c--) { *ctx = *idata; idata++; ctx++; } ctx = octx; for (c = onch; c > 0; c--) { *odata = *ctx; odata++; ctx++; } } abuf_rdiscard(ibuf, scount * ibuf->bpf); abuf_wcommit(obuf, scount * obuf->bpf); } int cmap_in(struct aproc *p, struct abuf *ibuf) { struct abuf *obuf = LIST_FIRST(&p->obuflist); if (!ABUF_WOK(obuf) || !ABUF_ROK(ibuf)) return 0; cmap_bcopy(p, ibuf, obuf); if (!abuf_flush(obuf)) return 0; return 1; } int cmap_out(struct aproc *p, struct abuf *obuf) { struct abuf *ibuf = LIST_FIRST(&p->ibuflist); if (!abuf_fill(ibuf)) return 0; if (!ABUF_WOK(obuf) || !ABUF_ROK(ibuf)) return 0; cmap_bcopy(p, ibuf, obuf); return 1; } void cmap_eof(struct aproc *p, struct abuf *ibuf) { aproc_del(p); } void cmap_hup(struct aproc *p, struct abuf *obuf) { aproc_del(p); } struct aproc_ops cmap_ops = { "cmap", cmap_in, cmap_out, cmap_eof, cmap_hup, NULL, NULL, aproc_ipos, aproc_opos, NULL }; struct aproc * cmap_new(char *name, struct aparams *ipar, struct aparams *opar) { struct aproc *p; unsigned i; p = aproc_new(&cmap_ops, name); for (i = 0; i < NCHAN_MAX; i++) p->u.cmap.ctx[i] = 0; return p; } /* * Convert one block. */ void enc_bcopy(struct aproc *p, struct abuf *ibuf, struct abuf *obuf) { unsigned nch, scount, icount, ocount; unsigned f; short *idata; int s; unsigned oshift; int osigbit; unsigned obps; unsigned i; unsigned char *odata; int obnext; int osnext; /* * Calculate max frames readable at once from the input buffer. */ idata = (short *)abuf_rgetblk(ibuf, &icount, 0); icount /= ibuf->bpf; if (icount == 0) return; odata = abuf_wgetblk(obuf, &ocount, 0); ocount /= obuf->bpf; if (ocount == 0) return; scount = (icount < ocount) ? icount : ocount; nch = ibuf->cmax - ibuf->cmin + 1; /* * Partially copy structures into local variables, to avoid * unnecessary indirections; this also allows the compiler to * order local variables more "cache-friendly". */ oshift = p->u.conv.shift; osigbit = p->u.conv.sigbit; obps = p->u.conv.bps; obnext = p->u.conv.bnext; osnext = p->u.conv.snext; /* * Start conversion. */ odata += p->u.conv.bfirst; for (f = scount * nch; f > 0; f--) { s = *idata++; s <<= 16; s >>= oshift; s ^= osigbit; for (i = obps; i > 0; i--) { *odata = (unsigned char)s; s >>= 8; odata += obnext; } odata += osnext; } /* * Update FIFO pointers. */ abuf_rdiscard(ibuf, scount * ibuf->bpf); abuf_wcommit(obuf, scount * obuf->bpf); } int enc_in(struct aproc *p, struct abuf *ibuf) { struct abuf *obuf = LIST_FIRST(&p->obuflist); if (!ABUF_WOK(obuf) || !ABUF_ROK(ibuf)) return 0; enc_bcopy(p, ibuf, obuf); if (!abuf_flush(obuf)) return 0; return 1; } int enc_out(struct aproc *p, struct abuf *obuf) { struct abuf *ibuf = LIST_FIRST(&p->ibuflist); if (!abuf_fill(ibuf)) return 0; if (!ABUF_WOK(obuf) || !ABUF_ROK(ibuf)) return 0; enc_bcopy(p, ibuf, obuf); return 1; } void enc_eof(struct aproc *p, struct abuf *ibuf) { aproc_del(p); } void enc_hup(struct aproc *p, struct abuf *obuf) { aproc_del(p); } struct aproc_ops enc_ops = { "enc", enc_in, enc_out, enc_eof, enc_hup, NULL, NULL, aproc_ipos, aproc_opos, NULL }; struct aproc * enc_new(char *name, struct aparams *par) { struct aproc *p; p = aproc_new(&enc_ops, name); p->u.conv.bps = par->bps; p->u.conv.sigbit = par->sig ? 0 : 1 << (par->bits - 1); if (par->msb) { p->u.conv.shift = 32 - par->bps * 8; } else { p->u.conv.shift = 32 - par->bits; } if (!par->le) { p->u.conv.bfirst = par->bps - 1; p->u.conv.bnext = -1; p->u.conv.snext = 2 * par->bps; } else { p->u.conv.bfirst = 0; p->u.conv.bnext = 1; p->u.conv.snext = 0; } return p; } /* * Convert one block. */ void dec_bcopy(struct aproc *p, struct abuf *ibuf, struct abuf *obuf) { unsigned nch, scount, icount, ocount; unsigned f; unsigned ibps; unsigned i; int s = 0xdeadbeef; unsigned char *idata; int ibnext; int isnext; int isigbit; unsigned ishift; short *odata; /* * Calculate max frames readable at once from the input buffer. */ idata = abuf_rgetblk(ibuf, &icount, 0); icount /= ibuf->bpf; if (icount == 0) return; odata = (short *)abuf_wgetblk(obuf, &ocount, 0); ocount /= obuf->bpf; if (ocount == 0) return; scount = (icount < ocount) ? icount : ocount; nch = obuf->cmax - obuf->cmin + 1; /* * Partially copy structures into local variables, to avoid * unnecessary indirections; this also allows the compiler to * order local variables more "cache-friendly". */ ibps = p->u.conv.bps; ibnext = p->u.conv.bnext; isigbit = p->u.conv.sigbit; ishift = p->u.conv.shift; isnext = p->u.conv.snext; /* * Start conversion. */ idata += p->u.conv.bfirst; for (f = scount * nch; f > 0; f--) { for (i = ibps; i > 0; i--) { s <<= 8; s |= *idata; idata += ibnext; } idata += isnext; s ^= isigbit; s <<= ishift; s >>= 16; *odata++ = s; } /* * Update FIFO pointers. */ abuf_rdiscard(ibuf, scount * ibuf->bpf); abuf_wcommit(obuf, scount * obuf->bpf); } int dec_in(struct aproc *p, struct abuf *ibuf) { struct abuf *obuf = LIST_FIRST(&p->obuflist); if (!ABUF_WOK(obuf) || !ABUF_ROK(ibuf)) return 0; dec_bcopy(p, ibuf, obuf); if (!abuf_flush(obuf)) return 0; return 1; } int dec_out(struct aproc *p, struct abuf *obuf) { struct abuf *ibuf = LIST_FIRST(&p->ibuflist); if (!abuf_fill(ibuf)) return 0; if (!ABUF_WOK(obuf) || !ABUF_ROK(ibuf)) return 0; dec_bcopy(p, ibuf, obuf); return 1; } void dec_eof(struct aproc *p, struct abuf *ibuf) { aproc_del(p); } void dec_hup(struct aproc *p, struct abuf *obuf) { aproc_del(p); } struct aproc_ops dec_ops = { "dec", dec_in, dec_out, dec_eof, dec_hup, NULL, NULL, aproc_ipos, aproc_opos, NULL }; struct aproc * dec_new(char *name, struct aparams *par) { struct aproc *p; p = aproc_new(&dec_ops, name); p->u.conv.bps = par->bps; p->u.conv.sigbit = par->sig ? 0 : 1 << (par->bits - 1); if (par->msb) { p->u.conv.shift = 32 - par->bps * 8; } else { p->u.conv.shift = 32 - par->bits; } if (par->le) { p->u.conv.bfirst = par->bps - 1; p->u.conv.bnext = -1; p->u.conv.snext = 2 * par->bps; } else { p->u.conv.bfirst = 0; p->u.conv.bnext = 1; p->u.conv.snext = 0; } return p; }