From 952f63ef436fd8922ac278f2eeb25edf30029ab2 Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Sun, 6 Jul 2025 15:05:22 +0300 Subject: [PATCH] examples: add examples/sokol/sounds/simple_keyboard_synth.v --- examples/sokol/sounds/simple_keyboard_synth.v | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 examples/sokol/sounds/simple_keyboard_synth.v diff --git a/examples/sokol/sounds/simple_keyboard_synth.v b/examples/sokol/sounds/simple_keyboard_synth.v new file mode 100644 index 0000000000..076c16a55d --- /dev/null +++ b/examples/sokol/sounds/simple_keyboard_synth.v @@ -0,0 +1,114 @@ +import time +import math +import term +import sokol.audio + +struct Note { +mut: + phase f32 + freq f32 + amplitude f32 +} + +struct App { +mut: + notes shared []Note +} + +fn main() { + println('Press the keys (a, s, d, f, g, h, j, k) to play notes. Press ESC to quit.') + mut app := &App{ + notes: []Note{len: 32} + } + audio.setup( + stream_userdata_cb: audio_callback + user_data: app + ) + defer { audio.shutdown() } + term.enable_echo(false) + defer { term.enable_echo(true) } + for { + key := term.key_pressed(blocking: false) + app.handle_key(key) or { break } + time.sleep(5 * time.millisecond) + } +} + +const c_2_pi = 2 * math.pi +const c_phase_step_per_freq = c_2_pi / 44100.0 +const c_note_start_amplitude = 0.33 +const c_note_decay = 0.3 / 44100.0 + +fn (mut app App) handle_key(key i64) ? { + if key == -1 { + return + } + mut freq := f32(0.0) + match key { + 27 { return none } + ` ` { app.silence() } + `a` { freq = 261.63 } + `s` { freq = 293.66 } + `d` { freq = 329.63 } + `f` { freq = 349.23 } + `g` { freq = 392.00 } + `h` { freq = 440.00 } + `j` { freq = 493.88 } + `k` { freq = 523.25 } + else {} + } + if freq > 0 { + app.play(freq, c_note_start_amplitude) + } +} + +fn (mut app App) play(freq f32, volume f32) { + lock app.notes { + for mut note in app.notes { + note.amplitude -= 0.2 + } + for mut note in app.notes { + if note.amplitude <= 0 { + note = Note{ + phase: 0.0 + freq: freq + amplitude: volume + } + break + } + } + } +} + +fn (mut app App) silence() { + lock app.notes { + for mut note in app.notes { + note.amplitude = 0 + } + } +} + +fn audio_callback(mut soundbuffer &f32, num_frames int, num_channels int, mut app App) { + lock app.notes { + for frame in 0 .. num_frames { + mut sample := f32(0.0) + for mut note in app.notes { + if note.amplitude <= 0 { + continue + } + sample += note.amplitude * math.sinf(note.phase) + sample += note.amplitude * math.sinf(note.phase * 2.718) + sample += note.amplitude * math.sinf(note.phase * 3.141) + + note.phase += note.freq * c_phase_step_per_freq + for note.phase >= c_2_pi { + note.phase -= c_2_pi + } + note.amplitude -= c_note_decay + } + for ch in 0 .. num_channels { + soundbuffer[frame * num_channels + ch] = sample + } + } + } +}