pip3 install python-rtmidi
python3 chip_midi_delegate.py
#!/usr/bin/env python | |
import logging | |
import sys | |
import time | |
import rtmidi.midiutil | |
import os | |
os.system('sudo echo "Welcome!"') | |
class MidiHandler(object): | |
def __init__(self, name, channel): | |
self.name = name | |
self.channel = channel | |
def __call__(self, event, data=None): | |
message, deltatime = event | |
if target is None: | |
return | |
if 'Axoloti Core' in self.name or 'Audio Kontrol 1' in self.name: | |
status = None | |
data1 = message[1] | |
data2 = message[2] | |
if(message[0] >= 144 and message[0] <= 159): # note on | |
status = 144 | |
if(message[0] >= 128 and message[0] <= 143): # note off | |
status = 128 | |
if(message[0] >= 176 and message[0] <= 191): # cc | |
status = 176 | |
if(status is not None): | |
returnChannel = message[0]-status+1 | |
if(any(list(filter(lambda item: item['channel'] == returnChannel, controllers)))): | |
controller = list(filter(lambda item: item['channel'] == returnChannel, controllers))[0] | |
if(channelMap[0] in controller['name'] and status == 128): # for midi mix change note off to note on vel=0 | |
status = 144 | |
data2 = 0 | |
controller['returnPort'].send_message([status, data1, data2]) | |
return | |
alteredMessage = message[:] | |
alteredMessage[0] += self.channel-1 | |
try: | |
target.send_message(alteredMessage) | |
except: | |
print('MIDI SEND ERROR'); | |
print(self.name, message, alteredMessage) | |
# These lines introduce huge latency | |
# if message[0] == 144: | |
# os.system('sudo i2cset -f -y 0 0x34 0x93 0x1') | |
# else: | |
# os.system('sudo i2cset -f -y 0 0x34 0x93 0x0') | |
channelMap = [ | |
'MIDI Mix', #1 | |
'LPD8', #2 | |
'GarageKey mini', #3 | |
'K-Mix MIDI 1', #4 | |
'K-Mix MIDI 2', #5 | |
'K-Mix MIDI 3' #6 | |
] | |
midiin = rtmidi.MidiIn() | |
midiout = rtmidi.MidiOut() | |
controllers = [] | |
target = None | |
def updateOutputTarget(): | |
global target | |
names = midiout.get_ports() | |
existenceCheck = False | |
for name in names: | |
if 'Axoloti Core' in name or 'Audio Kontrol 1' in name: | |
if 'RtMidiIn Client' in name: | |
continue | |
existenceCheck = True | |
if target is not None: | |
break; | |
print ('+ Found Axoloti or AK1. Adding it as midi target') | |
try: | |
target, port_name = rtmidi.midiutil.open_midiport(name, "output") | |
except (EOFError, KeyboardInterrupt): | |
print('Failed to open midi port', name) | |
break | |
if target is not None and existenceCheck is False: | |
print ('+ Removing Axo target') | |
try: | |
target.close_port() | |
except ValueError: | |
print('could not close target midi port') | |
pass | |
target = None | |
def updateControllerList(): | |
names = midiin.get_ports() | |
returnPortNames = midiout.get_ports() | |
cachedControllerNames = [tmp['name'] for tmp in controllers] | |
for name in names: | |
if not list(filter(lambda item: item['name'] == name, controllers)): | |
blacklist = ['Midi Through', 'RtMidiOut Client', 'IAC Driver KASE']#, 'K-Mix Audio Control', 'K-Mix Expander'] | |
if any([s for s in blacklist if s in name]): | |
continue | |
port = None | |
if not name: | |
continue | |
try: | |
port, port_name = rtmidi.midiutil.open_midiport(name) | |
except (EOFError, KeyboardInterrupt, ValueError): | |
print('Failed to open midi port', name) | |
if not port: | |
continue | |
try: | |
returnPort, port_name = rtmidi.midiutil.open_midiport(name, "output") | |
except (EOFError, KeyboardInterrupt): | |
print('Failed to open RETURN midi port', name) | |
channel = 3 | |
if any([s for s in channelMap if s in name]): | |
channel = channelMap.index([s for s in channelMap if s in name][0]) +1 | |
port.set_callback(MidiHandler(name, channel)) | |
controllers.append(dict(port=port, name=name, channel=channel, returnPort=returnPort)) | |
print('> Adding controller', name) | |
try: | |
cachedControllerNames.remove(name) | |
except ValueError: | |
pass | |
for removed in cachedControllerNames: | |
removedController = [item for item in controllers if item['name'] == removed] | |
if removedController: | |
removedController = removedController[0] | |
try: | |
removedController['port'].close_port() | |
removedController['returnPort'].close_port() | |
except ValueError: | |
print('could not close midi port', removedController) | |
pass | |
try: | |
controllers.remove(removedController) | |
print('> Removing controller', removedController['name']) | |
except ValueError: | |
print('could not remove from controllers', removedController['name']) | |
pass | |
else: | |
print('problem: could not find controller to remove!') | |
try: | |
# just wait for keyboard interrupt in main thread | |
while True: | |
updateOutputTarget() | |
updateControllerList() | |
time.sleep(3) | |
except KeyboardInterrupt: | |
print('') | |
finally: | |
print("Exit.") | |
del midiin | |
del midiout |