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 |