///|
pub let pi : Double = 3.141592653589793
///|
pub let two_pi : Double = 6.283185307179586
///|
/// Fast deterministic pseudo-random number generator (Xorshift32) for reproducible audio noise and parameter mutation.
pub struct Rng {
mut state : UInt32
}
///|
/// Creates a new Rng with the given 32-bit unsigned seed.
pub fn Rng::new(seed : UInt32) -> Rng {
let s = if seed == 0U { 2463534242U } else { seed }
{ state: s, }
}
///|
/// Generates the next pseudorandom 32-bit unsigned integer.
pub fn Rng::next_u32(self : Rng) -> UInt32 {
let mut x = self.state
x = x ^ (x << 13)
x = x ^ (x >> 17)
x = x ^ (x << 5)
self.state = x
x
}
///|
/// Generates a uniform float in the range [0.0, 1.0).
pub fn Rng::next_double(self : Rng) -> Double {
let u = self.next_u32()
let masked = (u & 0x7fffffffU).reinterpret_as_int()
masked.to_double() / 2147483648.0
}
///|
/// Generates a uniform bipolar float in the range [-1.0, 1.0).
pub fn Rng::next_bipolar(self : Rng) -> Double {
self.next_double() * 2.0 - 1.0
}
///|
/// Evaluates a single waveform sample at a given normalized phase in [0.0, 1.0).
pub fn sample_waveform(
waveform : Waveform,
phase : Double,
rng : Rng,
) -> Double {
// Wrap phase into [0.0, 1.0)
let raw_p = phase - phase.floor()
let p = if raw_p < 0.0 { 0.0 } else if raw_p >= 1.0 { 0.0 } else { raw_p }
match waveform {
Sine => @math.sin(p * two_pi)
Square(duty) => {
let d = if duty < 0.01 { 0.01 } else if duty > 0.99 { 0.99 } else { duty }
if p < d {
1.0
} else {
-1.0
}
}
Sawtooth => 2.0 * p - 1.0
Triangle => if p < 0.5 { 4.0 * p - 1.0 } else { 3.0 - 4.0 * p }
Noise => rng.next_bipolar()
}
}
///|
/// Generates a continuous audio buffer playing a constant pitch tone for a given duration.
pub fn render_tone(
waveform : Waveform,
frequency : Double,
duration_sec : Double,
sample_rate : Int32,
volume : Double,
) -> AudioBuffer {
let sr = if sample_rate <= 0 { 44100 } else { sample_rate }
let dur = if duration_sec < 0.0 { 0.0 } else { duration_sec }
let num_samples = (dur * sr.to_double()).to_int()
let buffer = AudioBuffer::new(sr, num_samples)
let phase_inc = if frequency <= 0.0 {
0.0
} else {
frequency / sr.to_double()
}
let rng = Rng::new(42U)
let mut phase = 0.0
for i = 0; i < num_samples; i = i + 1 {
let s = sample_waveform(waveform, phase, rng) * volume
buffer.push(s)
phase = phase + phase_inc
if phase >= 1.0 {
phase = phase - phase.floor()
}
}
buffer
}