-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathguac.py
394 lines (314 loc) · 11.6 KB
/
guac.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
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
import sys
import time
import pygame
import pygame.midi
import RPi.GPIO as GPIO
import Adafruit_MPR121.MPR121 as MPR121
import config
playing=False
recording=False
loop_length=-1
loop_start=-1
events=[]
event_index=0
tracks=[]
for i in range(4):
tracks.append({'midi_channel':i,
'on':True,
'patch':config.default_patch,
'octave':config.default_octave})
tracks[3]['midi_channel']=9
print(tracks)
num_tracks=len(tracks)
current_track=0
key_states=[]
for i in range(len(config.NOTE_OFFSET)):
key_states.append({'on':False,'last_change':0})
def debug_print(msg):
if config.debug:
print(msg)
# get the current time, in ms
def get_time():
current_time=int(round(time.time() * 1000))
return current_time
#turns off all notes on the current track
def notes_off():
global tracks
global key_states
global current_track
track=tracks[current_track]
octave=track['octave']
midi_channel=track['midi_channel']
current_time=get_time()
for i in range(len(key_states)):
key_state=key_states[i]
if key_state['on']:
midi.note_off(octave*12+config.NOTE_OFFSET[i],None,midi_channel)
key_state['on']=False
key_state['last_change']=current_time
# handler for record button press
def record_button():
global playing,recording,event_index,loop_start,loop_length
print("Record")
if recording and loop_length==-1:
# This is a new loop. Mark now as the loop end
current_time=get_time()
loop_length=current_time-loop_start
event_index=0
loop_start=current_time
playing=True
debug_print("end new loop. Length: {0}".format(loop_length))
elif not recording:
debug_print("starting loop record")
recording=True
if not playing:
playing=True
current_time=get_time()
loop_start=current_time
event_index=0
debug_print("starting loop play")
return
# handler for play button press
def play_button():
global playing,recording,event_index,loop_start,loop_length
print("Play")
if playing and not recording:
# right now we'll just do nothing in this state.
return
elif recording:
# come out of recording into play mode
debug_print("changing from loop record to playback")
recording=False
playing=True # just to be on the safe side
if loop_length==-1:
# this is a new loop. Mark now as the loop end
current_time=get_time()
loop_length=current_time-loop_start
event_index=0
loop_start=current_time
debug_print("end new loop. Length: {0}".format(loop_length))
elif not playing and loop_length>0:
debug_print("starting loop play")
playing=True
event_index=0
loop_start=get_time()
return
# handler for stop button press
def stop_button():
print("Stop")
global loop_start,recording,loop_length,event_index,playing,recording
if recording and loop_length==-1:
# stop was hit during a new loop. Mark this as the loop end
current_time=get_time()
loop_length=current_time-loop_start
debug_print("end new loop. Length: {0}".format(loop_length))
recording=False
playing=False
event_index=0
return
# handler for clear button press
def clear_button():
global recording,playing,loop_length,loop_start,events,event_index
print("Clear")
recording=False
playing=False
loop_length=-1
del events[:]
event_index=0
return
# handler for track advance button press
def track_advance_button():
global current_track
notes_off()
GPIO.output(config.track_led_pins[current_track],GPIO.LOW)
current_track+=1
if current_track>=num_tracks:
current_track=0
GPIO.output(config.track_led_pins[current_track],GPIO.HIGH)
print("new track: {0}".format(current_track))
return
# handler for track1 enable/disable button press
def track1_mute_button():
return
# handler for track2 enable/disable button press
def track2_mute_button():
return
# handler for track3 enable/disable button press
def track3_mute_button():
return
# handler for track4 enable/disable button press
def track4_mute_button():
return
# handler for octave up button press
def octave_up_button():
global tracks
global current_track
notes_off()
track=tracks[current_track]
if track['octave']<8:
track['octave']+=1
print ("New octave: {0}".format(track['octave']))
# handler for octave down button press
def octave_down_button():
global tracks
global current_track
notes_off()
track=tracks[current_track]
if track['octave']>0:
track['octave']-=1
print ("New octave: {0}".format(track['octave']))
# handler for patch up button press
def patch_up_button():
global tracks
global current_track
notes_off()
track=tracks[current_track]
if track['patch']<127:
track['patch'] += 1
print ("New patch: {0}".format(track['patch']))
midi.set_instrument(track['patch'],track['midi_channel'])
return
# handler for patch down button press
def patch_down_button():
global tracks
global current_track
notes_off()
track=tracks[current_track]
if track['patch']>0:
track['patch'] -= 1
print ("New patch: {0}".format(track['patch']))
midi.set_instrument(track['patch'],track['midi_channel'])
return
buttons = {
'record':{'handler':record_button,'on':False,'last_change':0},
'play':{'handler':play_button,'on':False,'last_change':0},
'stop':{'handler':stop_button,'on':False,'last_change':0},
'clear':{'handler':clear_button,'on':False,'last_change':0},
'track_advance':{'handler':track_advance_button,'on':False,'last_change':0},
'track1_mute':{'handler':track1_mute_button,'on':False,'last_change':0},
'track2_mute':{'handler':track2_mute_button,'on':False,'last_change':0},
'track3_mute':{'handler':track3_mute_button,'on':False,'last_change':0},
'track4_mute':{'handler':track4_mute_button,'on':False,'last_change':0},
'octave_up':{'handler':octave_up_button,'on':False,'last_change':0},
'octave_down':{'handler':octave_down_button,'on':False,'last_change':0},
'patch_up':{'handler':patch_up_button,'on':False,'last_change':0},
'patch_down':{'handler':patch_down_button,'on':False,'last_change':0},
}
caps=[]
# Create MPR121 instance(s)
for sensor in config.cap_sensors:
cap=MPR121.MPR121()
if not cap.begin(address=sensor):
print('Error initializing MPR121@{0}'.format(sensor))
sys.exit(1)
cap.set_thresholds(config.on_threshold,config.off_threshold)
caps.append(cap)
#cap = MPR121.MPR121()
#if not cap.begin():
# print('Error initializing MPR121.')
# sys.exit(1)
# Alternatively, specify a custom I2C address such as 0x5B (ADDR tied to 3.3V),
# 0x5C (ADDR tied to SDA), or 0x5D (ADDR tied to SCL).
#cap.begin(address=0x5B)
# Also you can specify an optional I2C bus with the bus keyword parameter.
#cap.begin(busnum=1)
pygame.mixer.pre_init(44100, -16, 12, 512)
pygame.init()
GPIO.setmode(GPIO.BCM)
for pin in config.button_pins.keys():
GPIO.setup(config.button_pins[pin],GPIO.IN)
pygame.midi.init()
dev_info=pygame.midi.get_device_info(config.midi_device)
print dev_info
midi=pygame.midi.Output(config.midi_device)
for track in tracks:
midi.set_instrument(track['patch'],track['midi_channel'])
for i in range(4):
GPIO.setup(config.track_led_pins[i],GPIO.OUT)
status=GPIO.LOW
if i==current_track:
status=GPIO.HIGH
GPIO.output(config.track_led_pins[i],status)
def loop():
global loop_start,event_index,loop_length
if playing and loop_length>0:
current_time=get_time()
loop_offset=current_time-loop_start
if loop_offset>=loop_length:
loop_start=current_time
loop_offset=0
event_index=0
while event_index<len(events) and events[event_index]['time']<=loop_offset:
event=events[event_index]
debug_print(event)
debug_print(loop_offset)
if event['velocity']==0:
midi.note_off(event['note'],None,event['midi_channel'])
else:
midi.note_on(event['note'],event['velocity'],event['midi_channel'])
event_index+=1
cap_idx=0
for cap in caps:
current_touched = cap.touched()
# Check each pin's last and current state to see if it was pressed or released.
for i in range(12):
# Each pin is represented by a bit in the touched value. A value of 1
# means the pin is being touched, and 0 means it is not being touched.
pin_bit = 1 << i
key_state = (current_touched & pin_bit)
key_idx=cap_idx+i
if key_state != key_states[key_idx]['on']:
current_time=get_time()
if (current_time-key_states[key_idx]['last_change'])>config.key_debounce:
key_states[key_idx]['on']=key_state
track=tracks[current_track]
octave=track['octave']
midi_channel=track['midi_channel']
debug_print("track {0} octave {1} midi_channel {2}".format(track,octave,midi_channel))
note=octave*12+config.NOTE_OFFSET[key_idx]
if key_state:
debug_print('{0} touched'.format(key_idx))
midi.note_off(note,None,midi_channel)
midi.note_on(note,config.note_velocity,midi_channel)
if recording:
note_event={
'time':current_time-loop_start,
'note':note,
'midi_channel':midi_channel,
'velocity':config.note_velocity
}
events.insert(event_index,note_event)
event_index+=1
debug_print(note_event)
else:
debug_print('{0} released'.format(key_idx))
midi.note_off(note,None,midi_channel)
if recording:
note_event={
'time':current_time-loop_start,
'note':note,
'midi_channel':midi_channel,
'velocity':0
}
events.insert(event_index,note_event)
event_index+=1
key_states[key_idx]['last_change']=current_time
cap_idx+=12
# check buttons
for button in config.button_pins:
button_state=GPIO.input(config.button_pins[button])
if button_state != buttons[button]['on']:
current_time=get_time()
if (current_time-buttons[button]['last_change'])>config.button_debounce:
buttons[button]['on']=button_state
if button_state:
debug_print('{0} pressed'.format(button))
buttons[button]['handler']()
else:
debug_print('{0} released'.format(button))
buttons[button]['last_change']=current_time
return
print('Press Ctrl-C to quit.')
while True:
loop()
time.sleep(0.001)