makepad/libs/score_player/shaders/supersaw.splash

75 lines
2.2 KiB
Text

// Supersaw with drift: seven band-limited saws whose detune offsets are
// re-drawn a few times a second (a living ensemble, not a fixed beat),
// random start phases, spread across the stereo field, then a low-pass
// with its own envelope.
let N = 7
let detune = param(0.18, 0, 1)
let drift = param(8, 0, 60)
let spread = param(0.8, 0, 1)
let cutoff = param(5200, 100, 16000)
let res = param(0.7, 0.5, 4)
let env_amt = param(3000, 0, 10000)
let attack = param(0.01, 0.001, 4)
let decay = param(0.6, 0.01, 8)
let sustain = param(0.8, 0, 1)
let release = param(0.4, 0.01, 8)
let gain = param(0.35, 0, 1)
var ph = [0.0; N]
var off = [0.0; N]
var target = [0.0; N]
var redraw = 0.0
var dts = [0.0; N]
var fl = Svf{}
var fr = Svf{}
var amp = Adsr{}
var fenv = Adsr{}
// Control rate: the drifting offsets glide toward their targets and each
// saw's phase increment is recomputed (64 samples is far finer than drift).
fn block() {
let c = cutoff + env_amt * fenv.level
svf_set(fl, c, res)
svf_set(fr, c, res)
for i in 0..N {
off[i] = off[i] + (target[i] - off[i]) * 0.03
let spread_i = (i - 3) / 3.0
dts[i] = freq * exp2((spread_i + off[i] * 0.25) * detune / 12.0) / sample_rate
}
}
fn voice() {
if trigger {
for i in 0..N {
ph[i] = rand() * 0.5 + 0.5
target[i] = rand()
off[i] = target[i]
}
}
// Re-draw the detune offsets `drift` times a second, gliding to them.
redraw = redraw + drift / sample_rate
if redraw >= 1.0 {
redraw = redraw - 1.0
for i in 0..N {
target[i] = rand()
}
}
let l = 0.0
let r = 0.0
for i in 0..N {
// Symmetric spread: voice 3 is the centre, the edges go widest.
let spread_i = (i - 3) / 3.0
let dt = dts[i]
ph[i] = advance(ph[i], dt)
let s = saw_blep(ph[i], dt)
let p = 0.5 + 0.5 * spread_i * spread
l = l + s * (1.0 - p)
r = r + s * p
}
let e = adsr(amp, attack, decay, sustain, release)
adsr(fenv, 0.001, decay * 0.5, 0.0, release)
if gate == 0.0 && e == 0.0 {
stop()
}
vec2(svf_lp(fl, l), svf_lp(fr, r)) * (e * gain * velocity * 0.4)
}