nx_audio_pan.nx source
↩ module page · 43 lines · 1518 B
1// nx_audio_pan.nx -- SOVEREIGN AUDIO ENGINE rung R2: stereo + constant-power pan.
2//
3// Places a mono source in the stereo field. Uses CONSTANT-POWER pan (gL^2+gR^2
4// ~= 1) so loudness is steady across the pan -- the law real engines use, baked as
5// a Q8 table (constants computed offline, no runtime trig). Mixes panned sources
6// additively into stereo L/R buses, clamped (reuses R0 ae_clamp). Continuous-pan
7// interpolation + sovereign trig come in a later rung. license_tier: ORIGINAL.
8
9import "nx_syscalls.nx"
10import "nx_audio_mix.nx"
11
12// 5 pan positions, constant-power, Q8 (256 = 1.0). gL^2+gR^2 ~= 65536 each row.
13// 0 hardL | 1 midL | 2 center | 3 midR | 4 hardR
14const AP_NPAN: i64 = 5
15func ap_gl(i: i64) -> i64 {
16 if i == 0 { return 256 }
17 if i == 1 { return 236 }
18 if i == 2 { return 181 }
19 if i == 3 { return 98 }
20 if i == 4 { return 0 }
21 return 181
22}
23func ap_gr(i: i64) -> i64 {
24 if i == 0 { return 0 }
25 if i == 1 { return 98 }
26 if i == 2 { return 181 }
27 if i == 3 { return 236 }
28 if i == 4 { return 256 }
29 return 181
30}
31
32// pan a mono source (Q8 gain g, pan position p) ADDITIVELY into stereo outL/outR
33func ap_pan_into(inb: *i64, n: i64, g: i64, p: i64, outL: *i64, outR: *i64) -> i64 {
34 let gl: i64 = (g * ap_gl(p)) / 256
35 let gr: i64 = (g * ap_gr(p)) / 256
36 var i: i64 = 0
37 while i < n {
38 outL[i] = ae_clamp(outL[i] + (inb[i] * gl) / 256)
39 outR[i] = ae_clamp(outR[i] + (inb[i] * gr) / 256)
40 i = i + 1
41 }
42 return n
43}