miniDSP
A small C library for audio DSP
Loading...
Searching...
No Matches
Sample Rate Conversion

Sample rate conversion (resampling) changes a signal from one sample rate to another. This is essential when combining audio from different sources, preparing data for systems that expect a specific rate, or reducing storage by downsampling.

miniDSP provides a high-quality offline polyphase sinc resampler that handles arbitrary rate ratios (e.g., 44100 Hz to 48000 Hz) with >100 dB stopband attenuation using default parameters.


Mathematical building blocks

Bessel I₀

The zeroth-order modified Bessel function of the first kind appears in the Kaiser window formula. It is computed via the convergent power series:

\[I_0(x) = \sum_{k=0}^{\infty} \left[\frac{(x/2)^k}{k!}\right]^2 \]

Reading the formula in C:

// x -> input value, sum -> I₀(x), term -> (x/2)^k / k!
double sum = 1.0;
double term = 1.0;
double half_x = x / 2.0;
for (unsigned k = 1; k < 300; k++) {
term *= (half_x / (double)k);
double term_sq = term * term;
sum += term_sq;
if (term_sq < 1e-15 * sum) break; // converged
}
// sum now holds I₀(x)

API:

double MD_bessel_i0(double x);
double MD_bessel_i0(double x)
Zeroth-order modified Bessel function of the first kind, .

Normalized sinc

The sinc function is the ideal lowpass interpolation kernel. The normalized form is:

\[\mathrm{sinc}(x) = \begin{cases} 1 & \text{if } |x| < 10^{-12} \\ \dfrac{\sin(\pi x)}{\pi x} & \text{otherwise} \end{cases} \]

Reading the formula in C:

// x -> input value, result -> sinc(x)
double result;
if (fabs(x) < 1e-12) {
result = 1.0;
} else {
double px = M_PI * x;
result = sin(px) / px;
}

API:

double MD_sinc(double x);
double MD_sinc(double x)
Normalized sinc function: .

The polyphase sinc resampler

How it works

The resampler converts a signal from sample rate \(f_{\mathrm{in}}\) to \(f_{\mathrm{out}}\) by treating each output sample as a fractional-position lookup into the input signal, filtered through a windowed sinc kernel.

For each output sample \(n\):

  1. Compute the fractional input position: \(p = n \cdot f_{\mathrm{in}} / f_{\mathrm{out}}\)
  2. Split into integer index \(\lfloor p \rfloor\) and fractional offset
  3. Select two adjacent filter sub-phases from a precomputed table
  4. Linearly interpolate the sub-phase coefficients
  5. Dot product with surrounding input samples

The filter table contains 512 sub-phases, each with \(2 \times \mathrm{num\_zero\_crossings}\) taps of a Kaiser-windowed sinc. Anti-aliasing is handled automatically: for downsampling, the sinc cutoff is scaled to \(\min(f_{\mathrm{in}}, f_{\mathrm{out}})/2\).

Output buffer sizing

\[N_{\mathrm{out}} = \left\lceil N_{\mathrm{in}} \cdot \frac{f_{\mathrm{out}}}{f_{\mathrm{in}}} \right\rceil \]

Reading the formula in C:

// input_len -> N_in, in_rate -> f_in, out_rate -> f_out
unsigned out_len = (unsigned)ceil((double)input_len * out_rate / in_rate);

API:

unsigned MD_resample_output_len(unsigned input_len,
double in_rate, double out_rate);
unsigned MD_resample_output_len(unsigned input_len, double in_rate, double out_rate)
Compute the output buffer size needed for resampling.

Resampling a signal

API:

unsigned MD_resample(const double *input, unsigned input_len,
double *output, unsigned max_output_len,
double in_rate, double out_rate,
unsigned num_zero_crossings, double kaiser_beta);
unsigned MD_resample(const double *input, unsigned input_len, double *output, unsigned max_output_len, double in_rate, double out_rate, unsigned num_zero_crossings, double kaiser_beta)
Resample a signal from one sample rate to another using polyphase sinc interpolation.

Parameters:

  • num_zero_crossings controls filter quality. Range: 8 (fast) to 64 (high quality). Default recommendation: 32.
  • kaiser_beta controls stopband attenuation. Recommendation: 10.0 for ~100 dB.
  • Returns the number of samples written to output.

Quick example:

const double out_rate = 48000.0;
unsigned N_out = MD_resample_output_len(N_in, in_rate, out_rate);
double *output = malloc(N_out * sizeof(double));
unsigned n_written = MD_resample(input, N_in, output, N_out,
in_rate, out_rate, 32, 10.0);

Common rate pairs

Conversion Ratio Use case
44100 to 48000 160/147 CD audio to professional video/DAW
48000 to 44100 147/160 Professional to CD quality
48000 to 16000 1/3 Wideband to narrowband speech
44100 to 22050 1/2 Half-rate for reduced storage
16000 to 8000 1/2 Wideband to telephone bandwidth

Further reading

API reference