miniDSP
A small C library for audio DSP
Loading...
Searching...
No Matches
minidsp_resample.c
Go to the documentation of this file.
1
5
6#include "minidsp.h"
7#include "minidsp_internal.h"
8
10#define RESAMPLE_NUM_PHASES 512
11
12unsigned MD_resample_output_len(unsigned input_len,
13 double in_rate, double out_rate)
14{
15 MD_CHECK(input_len > 0, MD_ERR_INVALID_SIZE, "input_len must be > 0", 0);
16 MD_CHECK(in_rate > 0.0, MD_ERR_INVALID_RANGE, "in_rate must be > 0", 0);
17 MD_CHECK(out_rate > 0.0, MD_ERR_INVALID_RANGE, "out_rate must be > 0", 0);
18 return (unsigned)ceil((double)input_len * out_rate / in_rate);
19}
20
21unsigned MD_resample(const double *input, unsigned input_len,
22 double *output, unsigned max_output_len,
23 double in_rate, double out_rate,
24 unsigned num_zero_crossings, double kaiser_beta)
25{
26 MD_CHECK(input != NULL, MD_ERR_NULL_POINTER, "input must not be NULL", 0);
27 MD_CHECK(output != NULL, MD_ERR_NULL_POINTER, "output must not be NULL", 0);
28 MD_CHECK(input_len > 0, MD_ERR_INVALID_SIZE, "input_len must be > 0", 0);
29 MD_CHECK(in_rate > 0.0, MD_ERR_INVALID_RANGE, "in_rate must be > 0", 0);
30 MD_CHECK(out_rate > 0.0, MD_ERR_INVALID_RANGE, "out_rate must be > 0", 0);
31 MD_CHECK(num_zero_crossings > 0, MD_ERR_INVALID_SIZE, "num_zero_crossings must be > 0", 0);
32
33 unsigned expected_len = MD_resample_output_len(input_len, in_rate, out_rate);
34 MD_CHECK(max_output_len >= expected_len, MD_ERR_INVALID_SIZE,
35 "max_output_len too small for expected output", 0);
36
37 unsigned num_phases = RESAMPLE_NUM_PHASES;
38 unsigned taps_per_phase = 2 * num_zero_crossings;
39 unsigned table_size = (num_phases + 1) * taps_per_phase;
40
41 /* Build polyphase filter table.
42 * table[phase][tap] contains the windowed-sinc value for that
43 * sub-phase offset and tap index. We build num_phases+1 phases
44 * so linear interpolation between phase[p] and phase[p+1] works
45 * at the boundary. */
46 double *table = malloc(table_size * sizeof(double));
47 MD_CHECK(table != NULL, MD_ERR_ALLOC_FAILED, "malloc failed", 0);
48
49 /* Anti-aliasing: scale cutoff for downsampling */
50 double ratio = out_rate / in_rate;
51 double cutoff_ratio = (ratio < 1.0) ? ratio : 1.0;
52
53 for (unsigned p = 0; p <= num_phases; p++) {
54 double frac = (double)p / (double)num_phases;
55 double *phase_row = table + p * taps_per_phase;
56
57 for (unsigned t = 0; t < taps_per_phase; t++) {
58 int tap_offset = (int)t - (int)num_zero_crossings;
59 double x = (double)tap_offset + frac;
60
61 /* Windowed sinc: sinc(cutoff * x) * kaiser(x / half_width) */
62 double sinc_val = MD_sinc(cutoff_ratio * x);
63
64 /* Kaiser window over the full filter span */
65 double half_width = (double)num_zero_crossings;
66 double w;
67 if (fabs(x) >= half_width) {
68 w = 0.0;
69 } else {
70 double r = x / half_width;
71 double arg = 1.0 - r * r;
72 if (arg < 0.0) arg = 0.0;
73 w = MD_bessel_i0(kaiser_beta * sqrt(arg))
74 / MD_bessel_i0(kaiser_beta);
75 }
76
77 phase_row[t] = cutoff_ratio * sinc_val * w;
78 }
79 }
80
81 /* Resample: for each output sample, compute fractional input position,
82 * select sub-phase with linear interpolation, dot with input. */
83 unsigned out_len = expected_len;
84
85 for (unsigned n = 0; n < out_len; n++) {
86 double in_pos = (double)n / ratio;
87 int in_idx = (int)floor(in_pos);
88 double frac = in_pos - (double)in_idx;
89
90 /* Map fractional position to phase index */
91 double phase_exact = frac * (double)num_phases;
92 unsigned phase_lo = (unsigned)phase_exact;
93 double alpha = phase_exact - (double)phase_lo;
94
95 if (phase_lo >= num_phases) {
96 phase_lo = num_phases - 1;
97 alpha = 1.0;
98 }
99
100 const double *row_lo = table + phase_lo * taps_per_phase;
101 const double *row_hi = table + (phase_lo + 1) * taps_per_phase;
102
103 double acc = 0.0;
104 for (unsigned t = 0; t < taps_per_phase; t++) {
105 int si = in_idx + (int)t - (int)num_zero_crossings;
106
107 /* Clamp to input boundaries */
108 double sample;
109 if (si < 0 || (unsigned)si >= input_len) {
110 sample = 0.0;
111 } else {
112 sample = input[si];
113 }
114
115 double coeff = row_lo[t] + alpha * (row_hi[t] - row_lo[t]);
116 acc += sample * coeff;
117 }
118
119 output[n] = acc;
120 }
121
122 free(table);
123 return out_len;
124}
A mini library of DSP (Digital Signal Processing) routines.
@ MD_ERR_INVALID_SIZE
A size or count argument is invalid (e.g.
Definition minidsp.h:63
@ MD_ERR_INVALID_RANGE
A range or bound is invalid (e.g.
Definition minidsp.h:64
@ MD_ERR_ALLOC_FAILED
A memory allocation failed.
Definition minidsp.h:65
@ MD_ERR_NULL_POINTER
A required pointer argument is NULL.
Definition minidsp.h:62
double MD_bessel_i0(double x)
Zeroth-order modified Bessel function of the first kind, .
double MD_sinc(double x)
Normalized sinc function: .
Internal header for cross-file dependencies within the minidsp module.
#define MD_CHECK(cond, code, msg, retval)
Check a precondition in a function that returns a value.
#define RESAMPLE_NUM_PHASES
Number of sub-phases in the polyphase filter table.
unsigned MD_resample_output_len(unsigned input_len, double in_rate, double out_rate)
Compute the output buffer size needed for resampling.
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.