-
Notifications
You must be signed in to change notification settings - Fork 4
/
code.py
208 lines (183 loc) · 8.43 KB
/
code.py
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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# some of the midi code derived from John Park's macropad tutorial
import time
from adafruit_macropad import MacroPad
macropad = MacroPad(rotation=90) # orientation horizontal, knob left
# colors
active_color = (255, 0, 0) # color when key is active step
inactive_color = (0, 0, 0) # color when note turned off
on_off_color = (0, 0, 32) # base color for on/off mode
pitch_color = (0, 32, 0) # base color for pitch mode
velocity_color = (32, 32, 0) # base color for velocity mode
legato_color = (32, 0, 32) # base color for legato mode
selected_color = (255, 255, 255) # color when key selected
base_color = on_off_color # init base color to on/off mode
macropad.pixels.brightness = 0.2 # default brightness
# set default colors on keys
for x in range(8):
macropad.pixels[x] = on_off_color
macropad.pixels[8] = on_off_color
macropad.pixels[9] = pitch_color
macropad.pixels[10] = velocity_color
macropad.pixels[11] = legato_color
# 8-step lists for storing the state of the sequencer
default_pitches = [36, 40, 39, 42, 41, 38, 39, 40]
note_on_map = [True] * 8 # step on/off state
pitch_map = default_pitches # step pitch
velocity_map = [127] * 8 # step velocity
legato_map = [0.90] * 8 # step length
sel_key = None # currently selected key
# MIDI variables
mode = 0
mode_text = [" On/Off", " MIDI Note", " Velocity", " Length"]
note_names = ["A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#"]
octave = 2
# variables for storing user input info
last_knob_pos = macropad.encoder # store encoder rotation value
key = None # for storing last key pressed
# variables for timing
tempo = 180 # beats (quarter notes) per minute
min_tempo = 10
max_tempo = 480
current_step = 7 # number of steps per beat
# display text setup
text_lines = macropad.display_text(" SEQUENCER")
text_lines[0].text = " Mode:"
text_lines[1].text = " On/Off"
text_lines[2].text = " "
text_lines[3].text = "BPM: %d" % tempo
text_lines.show()
while True:
stamp = time.monotonic() # store current time stamp
# next beat!
current_step = (current_step + 1) % 8
# updateUI
for step in range(8):
if note_on_map[step]:
if (step == sel_key) and (mode != 0):
macropad.pixels[step] = selected_color
else:
macropad.pixels[step] = base_color
else:
macropad.pixels[step] = inactive_color
if note_on_map[current_step]:
macropad.pixels[current_step] = active_color
text_lines[3].text = "BPM: %d" % tempo
if mode == 1:
note = pitch_map[current_step]
octave = ((note - 4) // 12) + 1
note_name = note_names[(note % 12) - 1]
text_lines[4].text = ("CUR:%d/%s%d" % (note, note_name, octave))
if mode == 2:
text_lines[4].text = ("Vel: %d" % velocity_map[current_step])
if mode == 3:
text_lines[4].text = ("Length: %f" % legato_map[current_step])
while time.monotonic() - stamp < 60/tempo: # while elapsed time < 1 beat
noteDuration = (60/tempo) * legato_map[current_step]
# if current step on, play MIDI
if note_on_map[current_step]:
if time.monotonic() - stamp < noteDuration:
macropad.midi.send(macropad.NoteOn(pitch_map[current_step], velocity_map[current_step]))
else:
macropad.midi.send(macropad.NoteOff(pitch_map[current_step], 0))
# Check for pressed buttons and store relevent info
key_events = macropad.keys.events.get()
if key_events:
if key_events.pressed:
key = key_events.key_number
else:
key = None
if key is not None:
if key < 8: # if user is trying to do something with a step
if mode == 0:
# if we're in on/off mode, pressing a key
# toggles it off or on
note_on_map[key] = not note_on_map[key]
else:
# if we're in any other mode, pressing a key
# clears previous selection and makes the
# pressed key the new selection
if note_on_map[key]:
if key == sel_key:
sel_key = None
text_lines[4].text = ""
else:
sel_key = key
else:
# pressing lower row keys changes mode
# and sets the key color for that mode
if key == 9:
mode = 1
base_color = pitch_color
text_lines[1].text = " Pitch"
elif key == 10:
mode = 2
base_color = velocity_color
text_lines[1].text = " Velocity"
elif key == 11:
mode = 3
base_color = legato_color
text_lines[1].text = " Legato"
else:
mode = 0
base_color = on_off_color
# encoder button press resets everything to default for current mode
macropad.encoder_switch_debounced.update()
if macropad.encoder_switch_debounced.pressed:
# if the button is pressed, we'll clear automation for the mode we're in
if mode == 0:
for x in range(8):
note_on_map[x] = True
elif mode == 1:
pitch_map = [36, 40, 39, 42, 41, 38, 39, 40]
elif mode == 2:
for x in range(8):
velocity_map[x] = 127
elif mode == 3:
for x in range(8):
legato_map[x] = 0.5
# encoder knob turns change bpm in on/off mode
# and change selected step's value in other modes
# if nothing is selected it will change bpm no matter the mode
if last_knob_pos is not macropad.encoder: # the knob has been turned
knob_pos = macropad.encoder
knob_delta = knob_pos - last_knob_pos
last_knob_pos = knob_pos
text_lines[3].text = ("BPM: %d" % tempo)
if mode is 0: # ON/OFF
tempo = tempo + (knob_delta * 5)
tempo = min(max(tempo, 10), 480)
if mode is 1: # PITCH
if sel_key != None:
sel_pitch = pitch_map[sel_key]
# turn off note we're going to change so we don't
# end up with hanging midi-on signals
macropad.midi.send(macropad.NoteOff(sel_pitch, 0))
sel_pitch = sel_pitch + knob_delta
sel_pitch = min(max(sel_pitch, 0), 127)
text_lines[5].text = ("SEL: %d" % sel_pitch)
pitch_map[sel_key] = sel_pitch
else:
tempo = tempo + (knob_delta * 5)
tempo = min(max(tempo, 10), 480)
if mode is 2: # VELOCITY
if sel_key != None:
sel_vel = velocity_map[sel_key]
sel_vel = sel_vel+ knob_delta
sel_vel = min(max(sel_vel, 0), 127)
text_lines[5].text = ("SET:%d" % sel_vel)
velocity_map[sel_key] = sel_vel
else:
tempo = tempo + (knob_delta * 5)
tempo = min(max(tempo, 10), 480)
if mode is 3: # LEGATO
if sel_key != None:
sel_length = legato_map[sel_key]
sel_length = sel_length + (knob_delta * 0.05)
sel_length = min(max(sel_length, 0), .95) # setting this to 1. bugs out
text_lines[5].text = ("SET %d: %f" % (sel_key,sel_length))
legato_map[sel_key] = sel_length
else:
tempo = tempo + (knob_delta * 5)
tempo = min(max(tempo, 10), 480)
last_knob_pos = macropad.encoder
time.sleep(0.01) # a little delay here helps avoid debounce annoyances