nx_audio_resample.nx source
↩ module page · 41 lines · 1722 B
1// nx_audio_resample.nx -- SOVEREIGN AUDIO ENGINE rung R1: sample-rate conversion.
2//
3// Any real engine must resample sources to the mixer's rate before mixing (44.1k
4// source -> 48k device, etc.). This is a linear-interpolation resampler in Q16
5// fixed point -- deterministic, no library. Samples are i64 in the i16 range
6// (reuses ae_clamp from R0). Later rungs add higher-order (cubic/sinc) interp.
7// license_tier: ORIGINAL.
8
9import "nx_syscalls.nx"
10import "nx_audio_mix.nx"
11
12const AR_FRAC: i64 = 65536 // Q16 unit (1.0)
13
14// output sample count for a rate change
15func ar_out_len(n_in: i64, rate_in: i64, rate_out: i64) -> i64 {
16 return (n_in * rate_out) / rate_in
17}
18
19// resample inb[0..n_in) from rate_in to rate_out into out (linear interp); returns n_out
20func ar_resample_linear(inb: *i64, n_in: i64, rate_in: i64, rate_out: i64, out: *i64, out_cap: i64) -> i64 {
21 if n_in <= 0 { return 0 }
22 if rate_in <= 0 { return 0 }
23 if rate_out <= 0 { return 0 }
24 let n_out: i64 = (n_in * rate_out) / rate_in
25 let step: i64 = (rate_in * AR_FRAC) / rate_out // Q16 source step per output sample
26 var j: i64 = 0
27 while j < n_out {
28 if j >= out_cap { break }
29 let pos: i64 = j * step // Q16 source position
30 let idx: i64 = pos / AR_FRAC
31 let frac: i64 = pos - (idx * AR_FRAC) // Q16 fractional part
32 var s0: i64 = 0
33 var s1: i64 = 0
34 if idx < n_in { s0 = inb[idx] } else { s0 = inb[n_in - 1] }
35 if idx + 1 < n_in { s1 = inb[idx + 1] } else { s1 = inb[n_in - 1] }
36 let v: i64 = s0 + ((s1 - s0) * frac) / AR_FRAC
37 out[j] = ae_clamp(v)
38 j = j + 1
39 }
40 return n_out
41}