-
Notifications
You must be signed in to change notification settings - Fork 0
/
midi_keyboard.rb
161 lines (137 loc) · 4 KB
/
midi_keyboard.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# ---- ---- ---- ---- midi keyboard input
# ---- midi synth defaults
# use beep synth if no modulation
set :midi_synth, :beep
# use mod beep synth if modulation phase is non zero
set :midi_mod_synth, :mod_beep
# synth list mapped on lower notes
synth_map = [
[:beep, :mod_beep], # C1
[:dsaw, :mod_dsaw],
[:fm, :mod_fm], # D1
[:pulse, :mod_pulse],
[:saw, :mod_saw], # E1
[:sine, :mod_sine], # F1
[:tri, :mod_tri],
[:noise, :mod_pulse], # G1
[:piano, :mod_pulse],
[:pluck, :mod_pulse], # A1
[:pretty_bell, :mod_pulse],
[:tb303, :mod_pulse], # B1
]
# amp volume on ctrl 7, default 1, range 0 to 2
ct_amp = 7
range_amp = [0,2]
midi_amp = 1
# ADSR envelope attack on ctrl 74, default 0, range 0 to 2
ct_attack = 74
range_attack = [0,2]
midi_attack = range_attack[0]
# ADSR envelope decay on ctrl 71, default 0, range 0 to 4
ct_decay = 71
range_decay = [0,4]
midi_decay = range_decay[0]
# ADSR envelope sustain on ctrl 73, default 0, range 0 to 16
ct_sustain = 73
range_sustain = [0,16]
midi_sustain = range_sustain[0]
# ADSR envelope release on ctrl 72, default 0.2, range 0.2 to 8
ct_release = 72
range_release = [0.2,8]
midi_release = range_release[0]
# modulation phase on ctrl 1, default 0 (no modulation), range 1-0 to 1-0.95
ct_mod_phase = 1
range_mod_phase = [0,0.95]
midi_mod_phase = 0
# modulation range on pitch bend, default 0, range -12 to 12 (2 octaves)
mod_range_factor = 24 / Float(16384)
midi_mod_range = 0
# ---- midi chord
synth_nodes = []
define :play_midi_note do | nt |
# use mod synth if nonzero phase
if midi_mod_phase > 0
use_synth (get :midi_mod_synth)
else
use_synth (get :midi_synth)
end
# play note using ADSR envelope
synth_nodes[nt] = play nt, amp: midi_amp,
attack: midi_attack,
decay: midi_decay,
sustain: midi_sustain,
release: midi_release,
# modulation parameters are ignored on non mod synth
mod_phase: 1 - midi_mod_phase,
mod_range: midi_mod_range
end
define :stop_midi_note do | nt |
# silence note with release duration
control synth_nodes[nt], amp: 0, amp_slide: midi_release
synth_nodes[nt] = nil
end
# ---- modulation changed
define :synth_modulation_update do
for node in synth_nodes do
if node
control node,
mod_phase: 1 - midi_mod_phase,
mod_range: midi_mod_range
end
end
end
# ---- midi event capture
live_loop :midi_note_on do
use_real_time
# sync keydown event
nt, vl = sync "/midi:midi_through_port-0:0:1/note_on"
if nt < synth_map.length
# use lower notes to change synth
set :midi_synth, synth_map[nt][0]
set :midi_mod_synth, synth_map[nt][1]
else
# play the note now and store the synth node
play_midi_note nt
end
end
live_loop :midi_note_off do
use_real_time
# sync keyup event
nt, vl = sync "/midi:midi_through_port-0:0:1/note_off"
# get the synth node and stop playing the note
stop_midi_note nt
end
# map 7 bit midi control value va on range rg
define :control_ponderation do | rg, va |
return rg[0] + (va / Float(127)) * (rg[1] - rg[0])
end
live_loop :midi_control_change do
use_real_time
# sync ctrl event
ct, va = sync "/midi:midi_through_port-0:0:1/control_change"
case ct
when ct_amp
midi_amp = (control_ponderation range_amp, va)
when ct_attack
midi_attack = (control_ponderation range_attack, va)
when ct_decay
midi_decay = (control_ponderation range_decay, va)
when ct_sustain
midi_sustain = (control_ponderation range_sustain, va)
when ct_release
midi_release = (control_ponderation range_release, va)
when ct_mod_phase
midi_mod_phase = (control_ponderation range_mod_phase, va)
# update modulation phase on synth nodes
synth_modulation_update
end
end
live_loop :midi_pich_change do
use_real_time
# sync pitch bend event
va = sync "/midi:midi_through_port-0:0:1/pitch_bend"
# default pitch bend 8K, range 0..16K maps on octaves range
midi_mod_range = (va[0] - 8192) * mod_range_factor
# update modulation range on synth nodes
synth_modulation_update
end