code wiki / (root) / nx_audio_sfx.nx

nx_audio_sfx.nx source

↩ module page · 113 lines · 3515 B

1// nx_audio_sfx.nx -- SOVEREIGN AUDIO ENGINE: a game SFX library. 2// 3// Named, ready-to-use game sounds composed from the engine's primitives: 4// beep, coin (two-tone ding), laser (descending pitch sweep), explosion 5// (filtered noise burst). Includes a sovereign xorshift noise source (NOT an LCG 6// -- good bit mixing). These drop straight into econsim / any game. Reuses 7// ae_clamp (R0), adsr_apply (R3b), fx_lowpass (R5). license_tier: ORIGINAL. 8 9import "nx_syscalls.nx" 10import "nx_audio_mix.nx" 11import "nx_audio_synth.nx" 12import "nx_audio_fx.nx" 13const SFX_MAGIC_9000: i64 = 9000 14const SFX_MAGIC_1319: i64 = 1319 15const SFX_MAGIC_1400: i64 = 1400 16const SFX_MAGIC_12000: i64 = 12000 17const SFX_MAGIC_2654435761: i64 = 2654435761 18 19const SFX_PH: i64 = 65536 20 21// xorshift64 PRNG step (good mixing; never LCG low bits) 22func sfx_rng(st: *i64) -> i64 { 23 var x: i64 = st[0] 24 x = x ^ (x << 13) 25 x = x ^ (x >> 7) 26 x = x ^ (x << 17) 27 st[0] = x 28 return x 29} 30 31// white noise into out[0..n) in [-amp,amp] 32func sfx_noise(out: *i64, n: i64, amp: i64, seed: i64) -> i64 { 33 let st: *i64 = (sys_mmap(8)) as *i64 34 st[0] = seed 35 if st[0] == 0 { st[0] = 1 } 36 let span: i64 = 2 * amp + 1 37 var i: i64 = 0 38 while i < n { 39 var u: i64 = sfx_rng(st) 40 if u < 0 { u = 0 - u } 41 out[i] = (u % span) - amp 42 i = i + 1 43 } 44 return n 45} 46 47// square tone via phase accumulator 48func sfx_tone(out: *i64, n: i64, freq: i64, rate: i64, amp: i64) -> i64 { 49 let inc: i64 = (freq * SFX_PH) / rate 50 let half: i64 = SFX_PH / 2 51 var ph: i64 = 0 52 var i: i64 = 0 53 while i < n { 54 if ph < half { out[i] = amp } else { out[i] = 0 - amp } 55 ph = ph + inc 56 while ph >= SFX_PH { ph = ph - SFX_PH } 57 i = i + 1 58 } 59 return n 60} 61 62func sfx_beep(out: *i64, n: i64, rate: i64) -> i64 { 63 sfx_tone(out, n, 880, rate, SFX_MAGIC_9000) 64 adsr_apply(out, n, n / 10, n / 8, 200, n / 2, n / 4) 65 return n 66} 67 68// coin: two-tone ding (B5 -> E6), square, quick decay 69func sfx_coin(out: *i64, n: i64, rate: i64) -> i64 { 70 let q: i64 = n / 4 71 let inc1: i64 = (988 * SFX_PH) / rate 72 let inc2: i64 = (SFX_MAGIC_1319 * SFX_PH) / rate 73 let half: i64 = SFX_PH / 2 74 var ph: i64 = 0 75 var i: i64 = 0 76 while i < n { 77 var inc: i64 = inc1 78 if i >= q { inc = inc2 } 79 if ph < half { out[i] = SFX_MAGIC_9000 } else { out[i] = 0 - SFX_MAGIC_9000 } 80 ph = ph + inc 81 while ph >= SFX_PH { ph = ph - SFX_PH } 82 i = i + 1 83 } 84 adsr_apply(out, n, q / 4, q / 4, 220, n / 2, n / 4) 85 return n 86} 87 88// laser: descending pitch sweep (1400 -> 200 Hz), square 89func sfx_laser(out: *i64, n: i64, rate: i64) -> i64 { 90 let f_hi: i64 = SFX_MAGIC_1400 91 let f_lo: i64 = 200 92 let half: i64 = SFX_PH / 2 93 var ph: i64 = 0 94 var i: i64 = 0 95 while i < n { 96 let f: i64 = f_hi - ((f_hi - f_lo) * i) / n 97 let inc: i64 = (f * SFX_PH) / rate 98 if ph < half { out[i] = SFX_MAGIC_9000 } else { out[i] = 0 - SFX_MAGIC_9000 } 99 ph = ph + inc 100 while ph >= SFX_PH { ph = ph - SFX_PH } 101 i = i + 1 102 } 103 adsr_apply(out, n, n / 20, n / 8, 180, n / 2, n / 3) 104 return n 105} 106 107// explosion: filtered noise burst with sharp attack + long decay 108func sfx_explosion(out: *i64, n: i64, rate: i64) -> i64 { 109 sfx_noise(out, n, SFX_MAGIC_12000, SFX_MAGIC_2654435761) 110 fx_lowpass(out, n, 60) // muffle 111 adsr_apply(out, n, n / 40, n / 6, 120, n / 4, n / 2) 112 return n 113}