nx_audio_osc.nx source
↩ module page · 101 lines · 4280 B
1// nx_audio_osc.nx -- SOVEREIGN AUDIO ENGINE rung R3: oscillators (synth source).
2//
3// Generates waveforms via a phase accumulator -- the source side of synthesis
4// (the mixer/resampler/pan are the processing side). Square/saw/triangle are
5// trig-free integer math; sine (table/approx) is a later rung. Phase is Q16 in
6// [0, OSC_PHASE); phase increment = freq*OSC_PHASE/rate. Amplitude in i16 range.
7// license_tier: ORIGINAL.
8
9import "nx_syscalls.nx"
10const OSC_MAGIC_5040: i64 = 5040
11const OSC_MAGIC_65536: i64 = 65536
12const OSC_MAGIC_40320: i64 = 40320
13const OSC_MAGIC_32768: i64 = 32768
14const OSC_MAGIC_16384: i64 = 16384
15const OSC_MAGIC_411774: i64 = 411774
16const OSC_MAGIC_51472: i64 = 51472
17const OSC_MAGIC_102944: i64 = 102944
18
19const OSC_PHASE: i64 = 65536 // one cycle (Q16)
20
21// square: +amp first half of the cycle, -amp second half
22func osc_square(out: *i64, n: i64, freq: i64, rate: i64, amp: i64) -> i64 {
23 let inc: i64 = (freq * OSC_PHASE) / rate
24 let half: i64 = OSC_PHASE / 2
25 var ph: i64 = 0
26 var i: i64 = 0
27 while i < n {
28 if ph < half { out[i] = amp } else { out[i] = 0 - amp }
29 ph = ph + inc
30 while ph >= OSC_PHASE { ph = ph - OSC_PHASE }
31 i = i + 1
32 }
33 return n
34}
35
36// saw: ramps -amp -> +amp across the cycle
37func osc_saw(out: *i64, n: i64, freq: i64, rate: i64, amp: i64) -> i64 {
38 let inc: i64 = (freq * OSC_PHASE) / rate
39 var ph: i64 = 0
40 var i: i64 = 0
41 while i < n {
42 out[i] = (0 - amp) + (ph * (2 * amp)) / OSC_PHASE
43 ph = ph + inc
44 while ph >= OSC_PHASE { ph = ph - OSC_PHASE }
45 i = i + 1
46 }
47 return n
48}
49
50// triangle: -amp -> +amp over first half, +amp -> -amp over second half
51func osc_triangle(out: *i64, n: i64, freq: i64, rate: i64, amp: i64) -> i64 {
52 let inc: i64 = (freq * OSC_PHASE) / rate
53 let half: i64 = OSC_PHASE / 2
54 var ph: i64 = 0
55 var i: i64 = 0
56 while i < n {
57 if ph < half { out[i] = (0 - amp) + (ph * (2 * amp)) / half }
58 else { out[i] = amp - ((ph - half) * (2 * amp)) / half }
59 ph = ph + inc
60 while ph >= OSC_PHASE { ph = ph - OSC_PHASE }
61 i = i + 1
62 }
63 return n
64}
65
66// ---- SINE (the "later rung", now absorbed from the dog-sound synth; range-reduced fixed-point, no float) ----
67func osc_qm(a: i64, b: i64) -> i64 { return (a * b) >> 16 }
68func osc_sin_s(x: i64) -> i64 { let x2: i64=osc_qm(x,x); let x3: i64=osc_qm(x2,x); let x5: i64=osc_qm(x3,x2); let x7: i64=osc_qm(x5,x2); return x - x3/6 + x5/120 - x7/OSC_MAGIC_5040 }
69func osc_cos_s(x: i64) -> i64 { let x2: i64=osc_qm(x,x); let x4: i64=osc_qm(x2,x2); let x6: i64=osc_qm(x4,x2); let x8: i64=osc_qm(x4,x4); return OSC_MAGIC_65536 - x2/2 + x4/24 - x6/720 + x8/OSC_MAGIC_40320 }
70// sine of an OSC_PHASE-unit angle (ph in [0,OSC_PHASE) maps to [0,2pi)); returns Q16 sin in [-65536,65536].
71func osc_sin_unit(ph: i64) -> i64 {
72 var p: i64 = ph % OSC_PHASE; if p < 0 { p = p + OSC_PHASE }
73 var sign: i64 = 1
74 if p >= OSC_MAGIC_32768 { p = p - OSC_MAGIC_32768; sign = 0 - 1 } // [0,OSC_MAGIC_32768) = [0,pi)
75 if p > OSC_MAGIC_16384 { p = OSC_MAGIC_32768 - p } // fold to [0,OSC_MAGIC_16384] = [0,pi/2]
76 let ang: i64 = (p * OSC_MAGIC_411774) / OSC_PHASE // radians Q16 in [0, pi/2]
77 var r: i64 = 0
78 if ang <= OSC_MAGIC_51472 { r = osc_sin_s(ang) } else { r = osc_cos_s(OSC_MAGIC_102944 - ang) }
79 return sign * r
80}
81// pure sine tone (the waveform that was missing alongside square/saw/triangle).
82func osc_sine(out: *i64, n: i64, freq: i64, rate: i64, amp: i64) -> i64 {
83 var ph: i64 = 0
84 let inc: i64 = (freq * (OSC_PHASE << 8)) / rate // 8 fractional bits for clean high-freq pitch
85 var i: i64 = 0
86 while i < n { out[i] = (osc_sin_unit(ph >> 8) * amp) >> 16; ph = ph + inc; i = i + 1 }
87 return n
88}
89// sine SWEEP freq0 -> freq1 (linear) -- absorbs the dog squeak/chirp/whistle sweep capability.
90func osc_sweep(out: *i64, n: i64, f0: i64, f1: i64, rate: i64, amp: i64) -> i64 {
91 var ph: i64 = 0
92 var i: i64 = 0
93 while i < n {
94 let f: i64 = f0 + (f1 - f0) * i / n
95 let inc: i64 = (f * (OSC_PHASE << 8)) / rate
96 ph = ph + inc
97 out[i] = (osc_sin_unit(ph >> 8) * amp) >> 16
98 i = i + 1
99 }
100 return n
101}