-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmidi_launchkey.py
182 lines (147 loc) · 5.97 KB
/
midi_launchkey.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
import threading
import pygame.midi as midi
known_midi_devices = [
'Launchkey Mini MIDI 1',
'Launchkey Mini LK Mini MIDI',
]
known_control_devices = [
'Launchkey Mini InControl 1',
'Launchkey Mini LK Mini InControl',
]
event_codes = {
8: 'noteoff',
9: 'noteon',
11: 'controller',
12: 'pgmchange',
}
button_names = {
104: 'sceneUp',
105: 'sceneDown',
106: 'trackLeft',
107: 'trackRight',
108: 'roundTop',
109: 'roundBottom',
}
note_button_names = {
104: 'roundTop',
120: 'roundBottom',
}
class Launchkey:
def __init__(self, debug=False):
print('Using Pygame MIDI interface')
self.debug = debug
midi.init()
self.midi_input = None
self.control_input = None
self.control_output = None
for device_id in range(midi.get_count()):
interf, name, is_input, is_output, opened = midi.get_device_info(device_id)
interf = interf.decode('utf-8')
name = name.decode('utf-8')
imode = 'input' if is_input else 'output' if is_output else 'none'
iopen = '(open)' if opened else ''
if self.debug:
print(f'{interf} / {name} ({imode}) {iopen}')
if self.midi_input is None and name in known_midi_devices and is_input:
self.midi_input = midi.Input(device_id)
self.midi_input.name = f'{name} ({imode})'
print(f'Using midi input device {name}')
if self.control_input is None and name in known_control_devices and is_input:
self.control_input = midi.Input(device_id)
self.control_input.name = f'{name} ({imode})'
print(f'Using control input device {name}')
if self.control_output is None and name in known_control_devices and is_output:
self.control_output = midi.Output(device_id)
self.control_output.name = f'{name} ({imode})'
self.set_incontrol(True)
print(f'Using control output device {name}')
self.binds = {}
self.running = True
self.done = False
def bind(self, event, func):
self.binds[event] = func
def start(self):
self.set_incontrol(True)
def do_loop():
while self.running:
self.emit_events(self.midi_input)
self.emit_events(self.control_input)
self.done = True
loop = threading.Thread(target=do_loop)
loop.start()
def emit_events(self, input_device):
for event, channel, id, value in self.get_raw_events(input_device):
self.emit_event(event, channel, id, value)
if event in ['noteon', 'noteoff']:
status = event[4:]
if input_device == self.midi_input:
if channel == 0:
self.emit_event(f'key{status}', id, value / 127)
elif channel == 9 and id >= 36 and id <= 51:
if id <= 39:
self.emit_event(f'pad{status}', id - 28, value / 127)
elif id <= 43:
self.emit_event(f'pad{status}', id - 40, value / 127)
elif id <= 47:
self.emit_event(f'pad{status}', id - 32, value / 127)
else:
self.emit_event(f'pad{status}', id - 44, value / 127)
elif input_device == self.control_input:
if id >= 96 and id <= 103:
self.emit_event(f'pad{status}', id - 96, value / 127)
elif id >= 112 and id <= 119:
self.emit_event(f'pad{status}', id - 104, value / 127)
elif id in note_button_names:
self.emit_event(f'button{status}', note_button_names[id])
elif event == 'controller':
if id >= 21 and id <= 28:
self.emit_event('dial', id - 21, value / 127)
elif id in button_names:
status = 'on' if value > 0 else 'off'
self.emit_event(f'button{status}', button_names[id])
def get_raw_events(self, input_device):
events = []
while input_device and input_device.poll():
[[[status, *params], timestamp]] = input_device.read(1)
etype = status >> 4
channel = status & 0xf
event = event_codes[etype] if etype in event_codes else etype
if self.debug:
print(f'{input_device.name}: {event} <{channel}> {params} : {timestamp}')
events.append((event, channel, *params[:2]))
return events
def emit_event(self, event, *params):
if self.debug:
print('Emitting:', event, *params)
if 'event' in self.binds:
self.binds['event'](event, *params)
if event in self.binds:
self.binds[event](*params)
def set_incontrol(self, enable=True):
if self.control_output:
# 144 = noteon, channel 0
self.control_output.write([[[144, 12, 127 if enable else 0], 0]])
def receive(self, command, *params):
if self.debug:
print(f'Received command {command} {params}')
if command == 'padcolor' and self.control_output:
pad, red, green = [int(p) for p in params]
note = None
if pad <= 7:
note = pad + 96
elif pad <= 15:
note = pad + 104
color = red + (green << 4)
if note is not None:
self.control_output.write([[[144, note, color], 0]])
def end(self):
self.set_incontrol(False)
self.running = False
while not self.done:
pass
if self.midi_input:
self.midi_input.close()
if self.control_input:
self.control_input.close()
if self.control_output:
self.control_output.close()