v2 / examples / sokol / sounds / simple_keyboard_synth.v
116 lines · 106 sloc · 2.21 KB · 8e35f4d9848f7ad35d857a187dddbfd2eca5e19d
Raw
1// vtest build: !openbsd
2import time
3import math
4import term
5import sokol.audio
6
7struct Note {
8mut:
9 phase f32
10 freq f32
11 amplitude f32
12}
13
14struct App {
15mut:
16 notes shared []Note
17}
18
19fn main() {
20 println('Press the keys (a, s, d, f, g, h, j, k) to play notes. Press ESC to quit.')
21 mut app := &App{
22 notes: []Note{len: 32}
23 }
24 audio.setup(
25 stream_userdata_cb: audio_callback
26 user_data: app
27 )
28 defer { audio.shutdown() }
29 term.enable_echo(false)
30 defer { term.enable_echo(true) }
31 for {
32 key := term.key_pressed(blocking: false)
33 app.handle_key(key) or { break }
34 time.sleep(5 * time.millisecond)
35 }
36}
37
38const c_2_pi = 2 * math.pi
39const c_phase_step_per_freq = c_2_pi / 44100.0
40const c_note_start_amplitude = 0.33
41const c_note_decay = 0.3 / 44100.0
42
43fn (mut app App) handle_key(key i64) ? {
44 if key == -1 {
45 return
46 }
47 mut freq := f32(0.0)
48 match key {
49 27 { return none }
50 ` ` { app.silence() }
51 `a` { freq = 261.63 }
52 `s` { freq = 293.66 }
53 `d` { freq = 329.63 }
54 `f` { freq = 349.23 }
55 `g` { freq = 392.00 }
56 `h` { freq = 440.00 }
57 `j` { freq = 493.88 }
58 `k` { freq = 523.25 }
59 else {}
60 }
61
62 if freq > 0 {
63 app.play(freq, c_note_start_amplitude)
64 }
65}
66
67fn (mut app App) play(freq f32, volume f32) {
68 lock app.notes {
69 for mut note in app.notes {
70 note.amplitude -= 0.2
71 }
72 for mut note in app.notes {
73 if note.amplitude <= 0 {
74 note = Note{
75 phase: 0.0
76 freq: freq
77 amplitude: volume
78 }
79 break
80 }
81 }
82 }
83}
84
85fn (mut app App) silence() {
86 lock app.notes {
87 for mut note in app.notes {
88 note.amplitude = 0
89 }
90 }
91}
92
93fn audio_callback(mut soundbuffer &f32, num_frames int, num_channels int, mut app App) {
94 lock app.notes {
95 for frame in 0 .. num_frames {
96 mut sample := f32(0.0)
97 for mut note in app.notes {
98 if note.amplitude <= 0 {
99 continue
100 }
101 sample += note.amplitude * math.sinf(note.phase)
102 sample += note.amplitude * math.sinf(note.phase * 2.718)
103 sample += note.amplitude * math.sinf(note.phase * 3.141)
104
105 note.phase += note.freq * c_phase_step_per_freq
106 for note.phase >= c_2_pi {
107 note.phase -= c_2_pi
108 }
109 note.amplitude -= c_note_decay
110 }
111 for ch in 0 .. num_channels {
112 soundbuffer[frame * num_channels + ch] = sample
113 }
114 }
115 }
116}
117