code wiki / (root) / nx_audio_resample.nx

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}