code wiki / (root) / nx_audio_osc.nx

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}