Resampling — Sample Rate Conversion

  • Given: A signal at some fixed sampling rate s

  • Wanted: A signal at some different sampling rate r that represents the same signal

  • Obvious approach: drop or duplicate samples to get the new rate

  • Obvious approach is wrong: Nyquist will punish this

Example: 2× Downsampling 48000 sps → 24000 sps

  • Obvious approach would drop every other sample

  • But Nyquist says that frequencies above 12 KHz will be aliased: this sounds terrible

Example: 2× Upsampling 24000 sps → 48000 sps

  • Obvious approach would double every sample

  • But this will produce "jaggies" at every other sample: these will translate to 12KHz noise that will be quite objectionable

Solution: Low-Pass Filtering

  • If we remove the unwanted frequencies, then everything turns out OK

  • To downsample 2×, low-pass at half the input bandwidth and then you can safely take every other sample

  • To upsample 2×, double each sample and then low-pass at half the output bandwidth to get rid of the noise

  • Both solutions use an anti-aliasing filter: a brick-wall-as-possible low-pass filter

Digital Filtering Is Expensive: Time-Domain Kludge

  • Just average adjacent samples before downsampling; average adjacent samples after upsampling

  • The average is essentially a bad digital filter here: will work OK but not great

Reminder: Applying A Digital Filter

  • Remember how a digital filter works:

    $$y[i] = \sum_{j=0}^{N-1} a[j] x[i - j]$$

    where the a[j] are filter coefficients derived from some kind of black magic

  • In Python

    y = []
    for i in range(N, len(x)):
        y0 = 0
        for j in range(N):
            y0 += a[j] * x[i - j]
  • Oops: the output signal y will be N shorter than the input. May want to stick N zeros on the front of x and then start from 0 instead of N or something

Aside: Python, numpy, scipy

  • The previous loop is going to be crazily slow in Python (at least 40× C): running time M N where M is the length of x

    • Use numpy arrays with convolve():

       y = []
       for i in range(N, len(x)):
          y0 = numpy.convolve(x[i-N+1:i+1], a)
    • Go all the way with scipy and lfilter()

       y = scipy.signal.lfilter(a, [1], x)
  • More C-like speed: still not gonna be super-fast for long filters

Filter and Decimate, Interpolate and Filter

  • OK, so we have a plan for downsampling and upsampling by 2×

  • 2× is just a special case: the plan works for any integer multiple or submultiple

  • But as the rates get more dramatic, good filters get longer and longer

  • We need to allow our filter function to have a transition band: sharper transition bands make good filters longer and longer

  • Can handle rational factors by upsampling to numerator frequency and down to denominator: ⅔× = 2× up, 3× down

  • This gets gross for ratios close to 1, e.g. 44100 / 48000 = 147 / 160 so about 300× work

  • Clever algorithms exist, e.g. ASRC

Last modified: Monday, 20 April 2020, 2:10 AM