From c3187db65945cea908fd12a79b6f4d50dc22952f Mon Sep 17 00:00:00 2001 From: Alexandre Ratchov Date: Mon, 22 Apr 2024 11:01:03 +0000 Subject: sndiod: Use resampling algorithm from aucat sndiod doesn't use partial blocks as aucat, but having the same algorithm makes code review and testing easier. --- usr.bin/sndiod/dev.c | 7 ++-- usr.bin/sndiod/dsp.c | 113 ++++++++++++++++++++++++++++++++++++++++++++------- usr.bin/sndiod/dsp.h | 6 ++- 3 files changed, 107 insertions(+), 19 deletions(-) diff --git a/usr.bin/sndiod/dev.c b/usr.bin/sndiod/dev.c index c3d32509311..ec9452d941f 100644 --- a/usr.bin/sndiod/dev.c +++ b/usr.bin/sndiod/dev.c @@ -1,4 +1,4 @@ -/* $OpenBSD: dev.c,v 1.111 2024/04/22 10:57:36 ratchov Exp $ */ +/* $OpenBSD: dev.c,v 1.112 2024/04/22 11:01:02 ratchov Exp $ */ /* * Copyright (c) 2008-2012 Alexandre Ratchov * @@ -517,7 +517,8 @@ dev_mix_badd(struct dev *d, struct slot *s) } if (s->mix.resampbuf) { - resamp_do(&s->mix.resamp, in, s->mix.resampbuf, s->round); + resamp_do(&s->mix.resamp, + in, s->mix.resampbuf, s->round, d->round); in = s->mix.resampbuf; } @@ -654,7 +655,7 @@ dev_sub_bcopy(struct dev *d, struct slot *s) if (s->sub.resampbuf) { resamp_do(&s->sub.resamp, - s->sub.resampbuf, resamp_out, d->round); + s->sub.resampbuf, resamp_out, d->round, s->round); } if (s->sub.encbuf) diff --git a/usr.bin/sndiod/dsp.c b/usr.bin/sndiod/dsp.c index 6c963f14d79..57085b8dbbc 100644 --- a/usr.bin/sndiod/dsp.c +++ b/usr.bin/sndiod/dsp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: dsp.c,v 1.18 2021/07/05 08:29:59 ratchov Exp $ */ +/* $OpenBSD: dsp.c,v 1.19 2024/04/22 11:01:02 ratchov Exp $ */ /* * Copyright (c) 2008-2012 Alexandre Ratchov * @@ -269,30 +269,50 @@ aparams_native(struct aparams *par) } /* - * resample the given number of frames + * Return the number of input and output frame that would be consumed + * by resamp_do(p, *icnt, *ocnt). */ void -resamp_do(struct resamp *p, adata_t *in, adata_t *out, int todo) +resamp_getcnt(struct resamp *p, int *icnt, int *ocnt) +{ + long long idiff, odiff; + int cdiff; + + cdiff = p->oblksz - p->diff; + idiff = (long long)*icnt * p->oblksz; + odiff = (long long)*ocnt * p->iblksz; + if (odiff - idiff >= cdiff) + *ocnt = (idiff + cdiff + p->iblksz - 1) / p->iblksz; + else + *icnt = (odiff + p->diff) / p->oblksz; +} + +/* + * Resample the given number of frames. The number of output frames + * must match the corresponding number of input frames. Either always + * use icnt and ocnt such that: + * + * icnt * oblksz = ocnt * iblksz + * + * or use resamp_getcnt() to calculate the proper numbers. + */ +void +resamp_do(struct resamp *p, adata_t *in, adata_t *out, int icnt, int ocnt) { 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; int64_t f[NCHAN_MAX]; adata_t *ctxbuf, *ctx; unsigned int ctx_start; int q, qi, qf, n; -#ifdef DEBUG - if (todo % p->iblksz != 0) { - log_puts("resamp_do: partial blocks not supported\n"); - panic(); - } -#endif - /* * Partially copy structures into local variables, to avoid * unnecessary indirections; this also allows the compiler to @@ -300,16 +320,32 @@ resamp_do(struct resamp *p, adata_t *in, adata_t *out, int todo) */ idata = in; odata = out; - diff = p->oblksz; + diff = p->diff; iblksz = p->iblksz; oblksz = p->oblksz; ctxbuf = p->ctx; ctx_start = p->ctx_start; nch = p->nch; + ifr = icnt; + ofr = ocnt; + /* + * Start conversion. + */ +#ifdef DEBUG + if (log_level >= 4) { + log_puts("resamp: copying "); + log_puti(ifr); + log_puts(" -> "); + log_putu(ofr); + log_puts(" frames, diff = "); + log_puti(diff); + log_puts("\n"); + } +#endif for (;;) { if (diff >= oblksz) { - if (todo == 0) + if (ifr == 0) break; ctx_start = (ctx_start - 1) & (RESAMP_NCTX - 1); ctx = ctxbuf + ctx_start; @@ -318,8 +354,10 @@ resamp_do(struct resamp *p, adata_t *in, adata_t *out, int todo) ctx += RESAMP_NCTX; } diff -= oblksz; - todo--; + ifr--; } else { + if (ofr == 0) + break; for (c = 0; c < nch; c++) f[c] = 0; @@ -361,11 +399,40 @@ resamp_do(struct resamp *p, adata_t *in, adata_t *out, int todo) #endif *odata++ = s; } + diff += iblksz; + ofr--; } } - + p->diff = diff; p->ctx_start = ctx_start; +#ifdef DEBUG + if (ifr != 0) { + log_puts("resamp_do: "); + log_puti(ifr); + log_puts(": too many input frames\n"); + panic(); + } + if (ofr != 0) { + log_puts("resamp_do: "); + log_puti(ofr); + log_puts(": too many output frames\n"); + panic(); + } +#endif +} + +static unsigned int +uint_gcd(unsigned int a, unsigned int b) +{ + unsigned int r; + + while (b > 0) { + r = a % b; + a = b; + b = r; + } + return a; } /* @@ -375,8 +442,26 @@ void resamp_init(struct resamp *p, unsigned int iblksz, unsigned int oblksz, int nch) { + unsigned int g; + + /* + * reduce iblksz/oblksz fraction + */ + g = uint_gcd(iblksz, oblksz); + iblksz /= g; + oblksz /= g; + + /* + * ensure weird rates don't cause integer overflow + */ + while (iblksz > ADATA_UNIT || oblksz > ADATA_UNIT) { + iblksz >>= 1; + oblksz >>= 1; + } + p->iblksz = iblksz; p->oblksz = oblksz; + p->diff = 0; p->nch = nch; p->ctx_start = 0; memset(p->ctx, 0, sizeof(p->ctx)); diff --git a/usr.bin/sndiod/dsp.h b/usr.bin/sndiod/dsp.h index 71ff518c52e..69e1b7a63ba 100644 --- a/usr.bin/sndiod/dsp.h +++ b/usr.bin/sndiod/dsp.h @@ -1,4 +1,4 @@ -/* $OpenBSD: dsp.h,v 1.12 2022/12/26 19:16:03 jmc Exp $ */ +/* $OpenBSD: dsp.h,v 1.13 2024/04/22 11:01:02 ratchov Exp $ */ /* * Copyright (c) 2012 Alexandre Ratchov * @@ -85,6 +85,7 @@ struct resamp { adata_t ctx[NCHAN_MAX * RESAMP_NCTX]; int filt_cutoff, filt_step; unsigned int iblksz, oblksz; + int diff; int nch; }; @@ -115,7 +116,8 @@ int aparams_strtoenc(struct aparams *, char *); int aparams_enctostr(struct aparams *, char *); int aparams_native(struct aparams *); -void resamp_do(struct resamp *, adata_t *, adata_t *, int); +void resamp_getcnt(struct resamp *, int *, int *); +void resamp_do(struct resamp *, adata_t *, adata_t *, int, 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); -- cgit v1.2.3