Hi,

Interesting diff.

I did not have time to look at it thoroughly, but here are a few observations:

- why do you keep the symmetric filter coefficients? (this could halve your while-loop computations too, right?)

- the diff's mainlobe look kind of strange at the end, why is that? What kind of filter did you use? the side-lobes seem also a bit strange as they do not decrease monotonically nor are the first sidelobes small and then increasing

- would be nice to see some SNR comparisons if you can to better show the effects of your anti-aliasing effort

Thanks for this,
Paul

On 20.12.2020 01:52, Alexandre Ratchov wrote:
Hi,

The current sndiod resampling algorithm is very basic mainly to keep
CPU usage very low, which used to make sense for the zaurus. So,
resampling produces aliasing noise, easily audible in 8kHz to 48kHz
conversions but present in other cases.

The diff below reduces the aliasing noise. It's a trade-off between
audio quality, CPU consumption and code simplicity. It adds a better
resampling filter (8-th order FIR low-pass), which mostly suppresses
aliasing noise in the audible part of the spectrum.

Few plots here: https://vm.caoua.org/src

I measured the CPU usage of resampling 16-bit stereo from 44.1kHz to
48kHz.

On a ~7 years old i5-2500K:
   - current code:      0.07% CPU
   - with diff:         0.45% CPU

On a ~15 years old Thinkpad x40:
   - current:           0.25% CPU
   - with diff:         1.90% CPU

This is still acceptable even for slow machines, IMHO. Enjoy and let
me know if this introduces any regression.

Index: dsp.c
===================================================================
RCS file: /cvs/src/usr.bin/sndiod/dsp.c,v
retrieving revision 1.15
diff -u -p -r1.15 dsp.c
--- dsp.c       10 Dec 2020 17:30:49 -0000      1.15
+++ dsp.c       19 Dec 2020 23:38:24 -0000
@@ -38,6 +38,75 @@ int aparams_ctltovol[128] = {
        26008,  27029,  28090,  29193,  30339,  31530,  32768
  };
+int resamp_filt[RESAMP_LENGTH / RESAMP_STEP + 1] = {
+             0,       0,       3,       9,      22,      42,      73,     116,
+           174,     248,     341,     454,     589,     749,     934,    1148,
+          1392,    1666,    1974,    2316,    2693,    3107,    3560,    4051,
+          4582,    5154,    5766,    6420,    7116,    7853,    8632,    9451,
+         10311,   11210,   12148,   13123,   14133,   15178,   16253,   17359,
+         18491,   19647,   20824,   22018,   23226,   24443,   25665,   26888,
+         28106,   29315,   30509,   31681,   32826,   33938,   35009,   36033,
+         37001,   37908,   38744,   39502,   40174,   40750,   41223,   41582,
+         41819,   41925,   41890,   41704,   41358,   40842,   40147,   39261,
+         38176,   36881,   35366,   33623,   31641,   29411,   26923,   24169,
+         21140,   17827,   14222,   10317,    6105,    1580,   -3267,   -8440,
+        -13944,  -19785,  -25967,  -32492,  -39364,  -46584,  -54153,  -62072,
+        -70339,  -78953,  -87911,  -97209, -106843, -116806, -127092, -137692,
+       -148596, -159795, -171276, -183025, -195029, -207271, -219735, -232401,
+       -245249, -258259, -271407, -284670, -298021, -311434, -324880, -338329,
+       -351750, -365111, -378378, -391515, -404485, -417252, -429775, -442015,
+       -453930, -465477, -476613, -487294, -497472, -507102, -516137, -524527,
+       -532225, -539181, -545344, -550664, -555090, -558571, -561055, -562490,
+       -562826, -562010, -559990, -556717, -552139, -546205, -538866, -530074,
+       -519779, -507936, -494496, -479416, -462652, -444160, -423901, -401835,
+       -377923, -352132, -324425, -294772, -263143, -229509, -193847, -156134,
+       -116348,  -74474,  -30494,   15601,   63822,  114174,  166661,  221283,
+        278037,  336916,  397911,  461009,  526194,  593446,  662741,  734054,
+        807354,  882608,  959779, 1038826, 1119706, 1202370, 1286768, 1372846,
+       1460546, 1549808, 1640566, 1732753, 1826299, 1921130, 2017169, 2114336,
+       2212550, 2311723, 2411770, 2512598, 2614116, 2716228, 2818836, 2921841,
+       3025142, 3128636, 3232218, 3335782, 3439219, 3542423, 3645282, 3747687,
+       3849526, 3950687, 4051059, 4150530, 4248987, 4346320, 4442415, 4537163,
+       4630453, 4722177, 4812225, 4900493, 4986873, 5071263, 5153561, 5233668,
+       5311485, 5386917, 5459872, 5530259, 5597992, 5662986, 5725160, 5784436,
+       5840739, 5893999, 5944148, 5991122, 6034862, 6075313, 6112422, 6146142,
+       6176430, 6203247, 6226559, 6246335, 6262551, 6275185, 6284220, 6289647,
+       6291456, 6289647, 6284220, 6275185, 6262551, 6246335, 6226559, 6203247,
+       6176430, 6146142, 6112422, 6075313, 6034862, 5991122, 5944148, 5893999,
+       5840739, 5784436, 5725160, 5662986, 5597992, 5530259, 5459872, 5386917,
+       5311485, 5233668, 5153561, 5071263, 4986873, 4900493, 4812225, 4722177,
+       4630453, 4537163, 4442415, 4346320, 4248987, 4150530, 4051059, 3950687,
+       3849526, 3747687, 3645282, 3542423, 3439219, 3335782, 3232218, 3128636,
+       3025142, 2921841, 2818836, 2716228, 2614116, 2512598, 2411770, 2311723,
+       2212550, 2114336, 2017169, 1921130, 1826299, 1732753, 1640566, 1549808,
+       1460546, 1372846, 1286768, 1202370, 1119706, 1038826,  959779,  882608,
+        807354,  734054,  662741,  593446,  526194,  461009,  397911,  336916,
+        278037,  221283,  166661,  114174,   63822,   15601,  -30494,  -74474,
+       -116348, -156134, -193847, -229509, -263143, -294772, -324425, -352132,
+       -377923, -401835, -423901, -444160, -462652, -479416, -494496, -507936,
+       -519779, -530074, -538866, -546205, -552139, -556717, -559990, -562010,
+       -562826, -562490, -561055, -558571, -555090, -550664, -545344, -539181,
+       -532225, -524527, -516137, -507102, -497472, -487294, -476613, -465477,
+       -453930, -442015, -429775, -417252, -404485, -391515, -378378, -365111,
+       -351750, -338329, -324880, -311434, -298021, -284670, -271407, -258259,
+       -245249, -232401, -219735, -207271, -195029, -183025, -171276, -159795,
+       -148596, -137692, -127092, -116806, -106843,  -97209,  -87911,  -78953,
+        -70339,  -62072,  -54153,  -46584,  -39364,  -32492,  -25967,  -19785,
+        -13944,   -8440,   -3267,    1580,    6105,   10317,   14222,   17827,
+         21140,   24169,   26923,   29411,   31641,   33623,   35366,   36881,
+         38176,   39261,   40147,   40842,   41358,   41704,   41890,   41925,
+         41819,   41582,   41223,   40750,   40174,   39502,   38744,   37908,
+         37001,   36033,   35009,   33938,   32826,   31681,   30509,   29315,
+         28106,   26888,   25665,   24443,   23226,   22018,   20824,   19647,
+         18491,   17359,   16253,   15178,   14133,   13123,   12148,   11210,
+         10311,    9451,    8632,    7853,    7116,    6420,    5766,    5154,
+          4582,    4051,    3560,    3107,    2693,    2316,    1974,    1666,
+          1392,    1148,     934,     749,     589,     454,     341,     248,
+           174,     116,      73,      42,      22,       9,       3,       0,
+             0
+};
+
+
  /*
   * Generate a string corresponding to the encoding in par,
   * return the length of the resulting string.
@@ -212,8 +281,10 @@ resamp_do(struct resamp *p, adata_t *in,
        adata_t *odata;
        unsigned int iblksz;
        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) {
@@ -240,7 +311,7 @@ resamp_do(struct resamp *p, adata_t *in,
                if (diff >= oblksz) {
                        if (todo == 0)
                                break;
-                       ctx_start ^= 1;
+                       ctx_start = (ctx_start - 1) & (RESAMP_NCTX - 1);
                        ctx = ctxbuf + ctx_start;
                        for (c = nch; c > 0; c--) {
                                *ctx = *idata++;
@@ -249,12 +320,32 @@ resamp_do(struct resamp *p, adata_t *in,
                        diff -= oblksz;
                        todo--;
                } else {
-                       ctx = ctxbuf;
+
+                       for (c = nch; c > 0; c--)
+                               f[c] = 0;
+
+                       q = diff * p->filt_step;
+                       n = ctx_start;
+
+                       while (q < RESAMP_LENGTH) {
+                               qi = q >> RESAMP_STEP_BITS;
+                               qf = q & (RESAMP_STEP - 1);
+                               s = resamp_filt[qi];
+                               ds = resamp_filt[qi + 1] - s;
+                               s += (int64_t)qf * ds >> RESAMP_STEP_BITS;
+                               ctx = ctxbuf;
+                               for (c = nch; c > 0; c--) {
+                                       f[c] += (int64_t)ctx[n] * s;
+                                       ctx += RESAMP_NCTX;
+                               }
+                               q += p->filt_cutoff;
+                               n = (n + 1) & (RESAMP_NCTX - 1);
+                       }
+
                        for (c = nch; c > 0; c--) {
-                               s = ctx[ctx_start ^ 1];
-                               ds = ctx[ctx_start] - s;
-                               ctx += RESAMP_NCTX;
-                               *odata++ = s + ADATA_MULDIV(ds, diff, oblksz);
+                               s = f[c] >> RESAMP_BITS;
+                               s = (int64_t)s * p->filt_cutoff >> RESAMP_BITS;
+                               *odata++ = s;
                        }
                        diff += iblksz;
                }
@@ -275,6 +366,13 @@ resamp_init(struct resamp *p, unsigned i
        p->nch = nch;
        p->ctx_start = 0;
        memset(p->ctx, 0, sizeof(p->ctx));
+       if (p->iblksz < p->oblksz) {
+               p->filt_cutoff = RESAMP_UNIT;
+               p->filt_step = RESAMP_UNIT / p->oblksz;
+       } else {
+               p->filt_cutoff = (int64_t)RESAMP_UNIT * p->oblksz / p->iblksz;
+               p->filt_step = RESAMP_UNIT / p->iblksz;
+       }
  #ifdef DEBUG
        if (log_level >= 3) {
                log_puts("resamp: ");
Index: dsp.h
===================================================================
RCS file: /cvs/src/usr.bin/sndiod/dsp.h,v
retrieving revision 1.7
diff -u -p -r1.7 dsp.h
--- dsp.h       8 Jun 2018 06:21:56 -0000       1.7
+++ dsp.h       19 Dec 2020 23:38:24 -0000
@@ -92,6 +92,32 @@ typedef int adata_t;
  #endif
/*
+ * The FIR is sampled and stored in a table of fixed-point numbers
+ * with 23 fractional bits. For convenience, we use the same fixed-point
+ * numbers to represent time and to walk through the table.
+ */
+#define RESAMP_BITS            23
+#define RESAMP_UNIT            (1 << RESAMP_BITS)
+
+/*
+ * Filter window length (the time unit is RESAMP_UNIT)
+ */
+#define RESAMP_LENGTH          (8 * RESAMP_UNIT)
+
+/*
+ * Time between samples of the FIR (the time unit is RESAMP_UNIT)
+ */
+#define RESAMP_STEP_BITS       (RESAMP_BITS - 6)
+#define RESAMP_STEP            (1 << RESAMP_STEP_BITS)
+
+/*
+ * Maximum downsample/upsample ratio we support, must be a power of two.
+ * The ratio between the max and the min sample rates is 192kHz / 4kHz = 48,
+ * so we can use 64
+ */
+#define RESAMP_RATIO           64
+
+/*
   * Maximum size of the encording string (the longest possible
   * encoding is ``s24le3msb'').
   */
@@ -111,9 +137,10 @@ struct aparams {
  };
struct resamp {
-#define RESAMP_NCTX    2
+#define RESAMP_NCTX    (RESAMP_LENGTH / RESAMP_UNIT * RESAMP_RATIO)
        unsigned int ctx_start;
        adata_t ctx[NCHAN_MAX * RESAMP_NCTX];
+       int filt_cutoff, filt_step;
        unsigned int iblksz, oblksz;
        int nch;
  };


Reply via email to