summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexandre Ratchov <ratchov@cvs.openbsd.org>2024-04-22 11:01:03 +0000
committerAlexandre Ratchov <ratchov@cvs.openbsd.org>2024-04-22 11:01:03 +0000
commitc3187db65945cea908fd12a79b6f4d50dc22952f (patch)
tree8c018e4cc502ef322c60c54949b48d7f2019a48a
parent31c348120a94047f5d5c8a31f5a6fdb68a8ac2a1 (diff)
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.
-rw-r--r--usr.bin/sndiod/dev.c7
-rw-r--r--usr.bin/sndiod/dsp.c113
-rw-r--r--usr.bin/sndiod/dsp.h6
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 <alex@caoua.org>
*
@@ -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 <alex@caoua.org>
*
@@ -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 <alex@caoua.org>
*
@@ -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);