0
$\begingroup$

Background: My overall goal is to create an audio file player which can playback an audio file at an arbitrary rate. The rate can change continuously as the file plays. This means upsampling to slowdown the playback when the rate < 1, and downsampling to speedup the playback when the rate > 1. (I'm not concerned with maintaining pitch.)

Let's focus on the slowdown (upsample) part to make the question simpler.

I read that to upsample by a rational fraction L/M, I should first

  1. Upsample by L
  2. Apply an interpolation (lowpass) filter
  3. Downsample by M

However, this doesn't work well for an online, live context, because L or M can be very large depending on the desired playback rate, requiring large buffers for upsampling/downsampling. I want to process the audio file in small chunks as it plays, so my buffers must be kept to a reasonable size.

I decided to try the following algorithm instead, for upsampling by an arbitrary floating point rate:

// pseudo-code upsample algorithm
sloppy_upsample(int source[], int destination[], float rate, int frames):
  for each frame i:
    destination[i] = source[round(i * rate)];

This is a sloppy "zero order hold" algorithm that probably doesn't get the sample rate exactly correct, but it actually sounds surprisingly good for my purposes. I also like this algorithm for its simplicity. But, of course, there's audible aliasing.

I know that I can apply a lowpass filter before upsampling to prevent aliasing, but I'm not sure what frequency to set the cutoff at. In similar situations such as this one, the cutoff could be set to less than L * 0.5.

  1. Is it possible to select a good cutoff for my sloppy_upsample function above that prevents aliasing?
  2. Is there another approach for upsampling/downsampling that works well for live playback?
$\endgroup$
1
$\begingroup$

Some solutions that are ordered by complexity, simplest first.

  1. The simplest solution is the one you have already implemented- pick the nearest value.
  2. The next simplest is linear interpolation. You use the two nearest values and use a weighted average based on how far the point is from each. For instance, if the point you want is .3 samples past $x_1$ and .7 samples before $x_2$, the value would be $y = x_1*.7 + x_2*.3$. This is still pretty simple and should have much better sound performance than your current solution.
  3. The next simplest is quadratic interpolation, or, more generally, polynomial interpolation. Polynomial interpolation is also sometimes referred to as spline interpolation.
  4. The most complicated solution is Lanczos resampling. This is where you use a windowed sinc filter to low-pass filter the data. You pick the particular set of taps to use from the filter based on the particular delay (i.e. at what point you are between the samples).
$\endgroup$
  • $\begingroup$ Thanks for the reply, Jim. I've already decided I want to use a low-pass filter; the issue is I don't know what cutoff frequency to use. $\endgroup$ – Alex Miller Mar 15 '15 at 1:04
  • $\begingroup$ If $f_b$ is the highest frequency in your music and $f_s$ is your original sample rate, then your filter's pass band should be $f_b$ and your cutoff should be $f_s - f_b$. $\endgroup$ – Jim Clay Mar 15 '15 at 1:28
  • $\begingroup$ Hm, so the cutoff frequency doesn't depend on how much I'm upsampling? That doesn't seem right to me... $\endgroup$ – Alex Miller Mar 15 '15 at 23:22
  • $\begingroup$ This answer may help you to understand. dsp.stackexchange.com/questions/1775/… $\endgroup$ – Jim Clay Mar 16 '15 at 0:51
0
$\begingroup$

The best solution is to use a window-ed Sinc interpolation kernel, which will both low-pass and reconstruct with much less aliasing than other methods of interpolation. The size of the Sinc kernel's main lobe sets the low-pass frequency cut-off as needed, and can be dynamically changed, as long as the rate-of-change isn't a significant FM modulation. The low-pass cutoff should be below half the lowest of the two sample rates to account for finite transitions bands.

$\endgroup$
  • $\begingroup$ I'm using a build-in audio lowpass filter (Apple's AULowPass) with two parameters: cutoff frequency and resonance. I assume that under the hood, it's using a window-ed sinc filter, but I'm not sure. I don't understand what you mean when you say the cutoff frequency is set as needed. You don't have to manually choose a cutoff based on how much you're upsampling/downsampling? $\endgroup$ – Alex Miller Mar 15 '15 at 23:26
  • $\begingroup$ The needed cutoff should always be below half the lowest of the two sample rates. So the cutoff frequency can be automatic given those two rates, which thus defines the minimum Sinc function lobe width. $\endgroup$ – hotpaw2 Mar 16 '15 at 0:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.