class M2K: def __init__(self): self.config_path = Path().cwd() / "m2k.json" self.table: dict = {} self.midi = RtMidiIn() self.keyboard = Controller() def set_config(self) -> NoReturn: with open(str(self.config_path), "r") as j: self.table = json.load(j) def update_config(self, update: dict) -> NoReturn: with open(str(self.config_path), "w") as new: new.write(json.dumps(update)) self.set_config() def send_key(self, midi_message) -> NoReturn: key = midi_message.getMidiNoteName(midi_message.getNoteNumber()) if key not in self.table: return if midi_message.isNoteOn(): self.keyboard.press(self.table[key]) elif midi_message.isNoteOff(): self.keyboard.release(self.table[key]) def run(self) -> NoReturn: ports = range(self.midi.getPortCount()) if ports: print("Running") self.midi.openPort(1) while True: if midi_message := self.midi.getMessage(0): self.send_key(midi_message)
def __init__(self, usb_device_name, app): self._log = logging.getLogger(__name__) self._midi_in, self._midi_out = RtMidiIn(), RtMidiOut() self._connect_midi(usb_device_name) self._midi_in.setCallback(self._midi_message_cb) self._app = app self.enabled = True
def __init__(self, port_index): self.device = RtMidiIn() self.device_name = self.device.getPortName(port_index) print("[Device] Input:", self.device_name) self.device.openPort(port_index) self.device.ignoreTypes(False, True, False) # midiSysex, midiTime, midiSense self.device.setCallback(self.on_message)
def test_transmission(self): import multiprocessing as mp if hasattr(mp, 'set_start_method'): mp.set_start_method('spawn') iq = mp.Queue() oq = mp.Queue() portName = 'TestVirtualPorts.%i' % time.time() senderProc = mp.Process(target=SenderProc, args=(oq, iq, portName)) senderProc.start() # handshake self.assertEqual(iq.get(), 'init') # virtual midi port is now open # Supposedly you can't just open a virtual port by name from # within the same proc or proc group? Anyway opening by index # works. device = RtMidiIn() for i in range(device.getPortCount()): if device.getPortName(i) == portName: device.openPort(i) break self.assertTrue(device.isPortOpen()) # collect messages and print progress self.messages = [] self.last_s = '' def put(m): self.messages.append(m) if len(self.messages) % 10 == 0: sys.stdout.write('\b' * len(self.last_s)) # backspace self.last_s = '%s: Received message %i / 32640' % (__name__, len(self.messages)) sys.stdout.write(self.last_s) # sys.stdout.write('.') sys.stdout.flush() oq.put('start') for i in range(128): for j in range(1, 128): msg = device.getMessage(1000) self.assertTrue(msg is not None) self.assertTrue(msg.isNoteOn()) self.assertEqual(msg.getNoteNumber(), i) self.assertEqual(msg.getVelocity(), j) put(msg) oq.put('next') for i in range(128): for j in range(128): msg = device.getMessage(1000) self.assertTrue(msg is not None) self.assertTrue(msg.isController()) self.assertEqual(msg.getControllerNumber(), i) self.assertEqual(msg.getControllerValue(), j) put(msg) oq.put('next') self.assertEqual(len(self.messages), 32640) oq.put('done')
def __init__(self, midi_controller): self._midi_in, self._midi_out = RtMidiIn(), RtMidiOut() self._connect_midi(midi_controller) self._midi_in.setCallback(self._midi_message_cb) self._osc_client = udp_client.SimpleUDPClient('127.0.0.1', 5005) self._osc_client.send_message('/ping', '1') # This part needs to be configurable per mode/user config/DIP switches/etc # At the moment, Arduino Micro MIDI device should do: # -------------- # | 5 6 7 8 | # | 1 2 3 4 | # -------------- # Press Press Press Long # Preset Stomp Looper Press # Mode Mode Mode # 1) Preset 1[10] Stomp1En[20] Undo[30] PresetMod[40] # 2) Preset 2[11] Stomp2En[21] Record[31] StompMode[41] # 3) Preset 3[12] Stomp3En[22] Overdub[32] LooperMod[42] # 4) Preset 4[13] Stomp4En[23] Tuner[43] # 5) Stomp1En[14] Stomp5En[24] Stomp1Sel[44] # 6) Stomp2En[15] Stomp6En[25] Stomp2Sel[45] # 7) Stomp3En[16] Stomp7En[26] Stomp3Sel[46] # 8) Stomp4En[17] TapTempo[27] Stomp4Sel[47] self._cc_osc_translation = { # Presets 10: '/preset/1', 11: '/preset/2', 12: '/preset/3', 13: '/preset/4', 14: '/stomp/1/enable', 15: '/stomp/2/enable', 16: '/stomp/3/enable', 17: '/stomp/4/enable', # Stompboxes 20: '/stomp/1/enable', 21: '/stomp/2/enable', 22: '/stomp/3/enable', 23: '/stomp/4/enable', 24: '/stomp/5/enable', 25: '/stomp/6/enable', 26: '/stomp/7/enable', 27: '/stomp/8/enable', # Looper 30: '/looper/undo', 31: '/looper/record', 32: '/looper/overdub', 33: '/looper/mute_trigger', 34: '/looper/redo', 35: '/looper/insert', 36: '/looper/multiply', 37: '/looper/pause', # Metronome 40: '/metronome/pause', 41: '/metronome/dec_bpm', 42: '/metronome/inc_bpm', 43: '/metronome/tap', 44: '', 45: '', 46: '', 47: '', # Long press 100: '/mode/preset', 101: '/mode/stomp', 102: '/mode/looper', 103: '/mode/metronome', 104: '/stomp/1/select', 105: '/stomp/2/select', 106: '/stomp/3/select', 107: '/stomp/4/select' }
class Reader: def __init__(self, port_index): self.device = RtMidiIn() self.device_name = self.device.getPortName(port_index) print("[Device] Input:", self.device_name) self.device.openPort(port_index) self.device.ignoreTypes(False, True, False) # midiSysex, midiTime, midiSense self.device.setCallback(self.on_message) def on_message(self, msg): try: channel = msg.getChannel() if msg.isNoteOn(): note = msg.getNoteNumber() print(f'[{self.device_name}] ch={channel} n={note}/{msg.getMidiNoteName(note)} v={msg.getVelocity()}') elif msg.isNoteOff(): note = msg.getNoteNumber() print(f'[{self.device_name}] ch={channel} n={note}/{msg.getMidiNoteName(note)} v=0') elif msg.isController(): print(f'[{self.device_name}] ch={channel} cc={msg.getControllerNumber()} v={msg.getControllerValue()}') else: print(f'[{self.device_name}] other: {msg}') if msg.isController(): cc = msg.getControllerNumber() v = msg.getControllerValue() if cc == 64 and self.device_name != VIRTUAL_OUTPUT_NAME: if v > 0: smart_start(send_midi=None) else: smart_stop(send_midi=None) except Exception as e: logging.exception(e)
class MidiReceiver: """ Handles incoming MIDI messages from attached controllers or instruments. Incoming messages can be remapped to other MIDI events, OSC, or trigger events inside the app. """ def __init__(self, usb_device_name, app): self._log = logging.getLogger(__name__) self._midi_in, self._midi_out = RtMidiIn(), RtMidiOut() self._connect_midi(usb_device_name) self._midi_in.setCallback(self._midi_message_cb) self._app = app self.enabled = True def _connect_midi(self, usb_device_name): def find_port(ports, name): for i, p in enumerate(ports): if p.startswith(name): return i return None # Find the MIDI In port port = find_port([self._midi_in.getPortName(i) for i in range(self._midi_in.getPortCount())], usb_device_name) if port is None: raise ValueError('Could not find "{}" MIDI port'.format(usb_device_name)) self._log.info("MidiIn connecting to {}".format(port)) self._midi_in.openPort(port) # Find the MIDI Out port port = find_port([self._midi_out.getPortName(i) for i in range(self._midi_out.getPortCount())], usb_device_name) assert port is not None self._log.info("MidiOut connecting to {}".format(port)) self._midi_out.openPort(port) def _midi_message_cb(self, msg): if not self.enabled: return ch = msg.getChannel() cc = msg.getControllerNumber() self._log.debug('Received MIDI message: {} {}'.format(ch, cc)) # Mapping (channel, cc, value) to event self._mapping = [ # single click MidiMapping(channel=2, cc=10, event_target=MidiMapping.EVENT_TARGET_MIDI_LOOP, payload=1), # toggle loop 1 # MidiMapping(channel=2, cc=11, event_target=MidiMapping.EVENT_TARGET_DRUMS, payload=1), # play drums # MidiMapping(channel=2, cc=12, event_target=MidiMapping.EVENT_TARGET_LOOPER, payload='record'), # record # MidiMapping(channel=2, cc=13, event_target=MidiMapping.EVENT_TARGET_LOOPER, payload='stop'), # stop MidiMapping(channel=2, cc=14, event_target=MidiMapping.EVENT_TARGET_PRESET, payload=0), # switch loops off MidiMapping(channel=2, cc=15, event_target=MidiMapping.EVENT_TARGET_PRESET, payload=1), # switch to preset 1 MidiMapping(channel=2, cc=16, event_target=MidiMapping.EVENT_TARGET_PRESET, payload=2), # switch to preset 2 MidiMapping(channel=2, cc=17, event_target=MidiMapping.EVENT_TARGET_PRESET, payload=3), # switch to preset 3 # long click ] def find_mapping(ch_, cc_): for m in self._mapping: if m.channel == ch_ and m.cc == cc_: return m m = find_mapping(ch, cc) if m: self._log.info('Sending event {}:{}'.format(m.event_target, m.payload)) self._app.send_event(m.event_target, m.payload)
def test_transmission(self): import multiprocessing as mp if hasattr(mp, 'set_start_method'): mp.set_start_method('spawn') iq = mp.Queue() oq = mp.Queue() portName = 'TestVirtualPorts.%i' % time.time() senderProc = mp.Process(target=SenderProc, args=(oq, iq, portName)) senderProc.start() # handshake self.assertEqual(iq.get(), 'init') # virtual midi port is now open # Supposedly you can't just open a virtual port by name from # within the same proc or proc group? Anyway opening by index # works. device = RtMidiIn() for i in range(device.getPortCount()): if device.getPortName(i) == portName: device.openPort(i) break self.assertTrue(device.isPortOpen()) # collect messages and print progress self.messages = [] self.last_s = '' def put(m): self.messages.append(m) if len(self.messages) % 10 == 0: sys.stdout.write('\b' * len(self.last_s)) # backspace self.last_s = '%s: Received message %i / 32640' % ( __name__, len(self.messages)) sys.stdout.write(self.last_s) # sys.stdout.write('.') sys.stdout.flush() oq.put('start') for i in range(128): for j in range(1, 128): msg = device.getMessage(1000) self.assertTrue(msg is not None) self.assertTrue(msg.isNoteOn()) self.assertEqual(msg.getNoteNumber(), i) self.assertEqual(msg.getVelocity(), j) put(msg) oq.put('next') for i in range(128): for j in range(128): msg = device.getMessage(1000) self.assertTrue(msg is not None) self.assertTrue(msg.isController()) self.assertEqual(msg.getControllerNumber(), i) self.assertEqual(msg.getControllerValue(), j) put(msg) oq.put('next') self.assertEqual(len(self.messages), 32640) oq.put('done')
# /Users/macbook/miniconda3/envs/AIP/bin/pip install rtmidi from rtmidi import MidiMessage, RtMidiIn, RtMidiOut import time import midi_tools midiout = RtMidiOut() midiout.openPort(2) midiin = RtMidiIn() midiin.openPort(2) midi_tools.print_ports(midiin) midi_tools.print_ports(midiout) class MidiConnection: def __init__(self, _channel, _DEBUG=False, _port=0): self.port = _port self.channel = _channel self.DEBUG = _DEBUG def output(self, m): midiout.sendMessage(m) if (self.DEBUG): midi_tools.print_message(m) def sendNoteSignal(self, note, vel, wait=.1): m = MidiMessage.noteOn(self.channel, note, vel) self.output(m) time.sleep(wait) m = MidiMessage.noteOff(self.channel, note)
class MidiToOsc: """ Translates MIDI messages from a controller to OSC messages for the main program. Sets up MIDI and OSC connects, then everything is handled through a callback. Uses ALSA/RtMidi to receive MIDI messages. Assumes the OSC server is on localhost at the default port. """ def __init__(self, midi_controller): self._midi_in, self._midi_out = RtMidiIn(), RtMidiOut() self._connect_midi(midi_controller) self._midi_in.setCallback(self._midi_message_cb) self._osc_client = udp_client.SimpleUDPClient('127.0.0.1', 5005) self._osc_client.send_message('/ping', '1') # This part needs to be configurable per mode/user config/DIP switches/etc # At the moment, Arduino Micro MIDI device should do: # -------------- # | 5 6 7 8 | # | 1 2 3 4 | # -------------- # Press Press Press Long # Preset Stomp Looper Press # Mode Mode Mode # 1) Preset 1[10] Stomp1En[20] Undo[30] PresetMod[40] # 2) Preset 2[11] Stomp2En[21] Record[31] StompMode[41] # 3) Preset 3[12] Stomp3En[22] Overdub[32] LooperMod[42] # 4) Preset 4[13] Stomp4En[23] Tuner[43] # 5) Stomp1En[14] Stomp5En[24] Stomp1Sel[44] # 6) Stomp2En[15] Stomp6En[25] Stomp2Sel[45] # 7) Stomp3En[16] Stomp7En[26] Stomp3Sel[46] # 8) Stomp4En[17] TapTempo[27] Stomp4Sel[47] self._cc_osc_translation = { # Presets 10: '/preset/1', 11: '/preset/2', 12: '/preset/3', 13: '/preset/4', 14: '/stomp/1/enable', 15: '/stomp/2/enable', 16: '/stomp/3/enable', 17: '/stomp/4/enable', # Stompboxes 20: '/stomp/1/enable', 21: '/stomp/2/enable', 22: '/stomp/3/enable', 23: '/stomp/4/enable', 24: '/stomp/5/enable', 25: '/stomp/6/enable', 26: '/stomp/7/enable', 27: '/stomp/8/enable', # Looper 30: '/looper/undo', 31: '/looper/record', 32: '/looper/overdub', 33: '/looper/mute_trigger', 34: '/looper/redo', 35: '/looper/insert', 36: '/looper/multiply', 37: '/looper/pause', # Metronome 40: '/metronome/pause', 41: '/metronome/dec_bpm', 42: '/metronome/inc_bpm', 43: '/metronome/tap', 44: '', 45: '', 46: '', 47: '', # Long press 100: '/mode/preset', 101: '/mode/stomp', 102: '/mode/looper', 103: '/mode/metronome', 104: '/stomp/1/select', 105: '/stomp/2/select', 106: '/stomp/3/select', 107: '/stomp/4/select' } def _connect_midi(self, midi_controller): def find_port(ports, name): for i, p in enumerate(ports): if p.startswith(name): return i return None # Find the MIDI In port the Arduino Micro is connected to arduino_port = find_port([self._midi_in.getPortName(i) for i in range(self._midi_in.getPortCount())], midi_controller) if arduino_port is None: raise ValueError('Could not find "Arduino Micro" MIDI port') print("MidiIn connecting to {}".format(arduino_port)) self._midi_in.openPort(arduino_port) # Find the MIDI Out port the Arduino Micro is connected to arduino_port = find_port([self._midi_out.getPortName(i) for i in range(self._midi_out.getPortCount())], midi_controller) assert arduino_port is not None print("MidiOut connecting to {}".format(arduino_port)) self._midi_out.openPort(arduino_port) def _midi_message_cb(self, msg): cc, value = msg.getControllerNumber(), msg.getControllerValue() osc_topic = self._cc_osc_translation.get(cc, None) print('{} -> {} -> {}'.format(cc, '1' if value > 0 else '0', str(osc_topic))) if osc_topic: self._osc_client.send_message(osc_topic, '1') print('sent.')
def __init__(self): self.config_path = Path().cwd() / "m2k.json" self.table: dict = {} self.midi = RtMidiIn() self.keyboard = Controller()
print '%s: ON: ' % port, msg.getNoteNumber(), "=", msg.getMidiNoteName( msg.getNoteNumber()), msg.getVelocity() elif msg.isNoteOff(): print '%s: OFF:' % port, msg.getNoteNumber(), "=", msg.getMidiNoteName( msg.getNoteNumber()) elif msg.isController(): print '%s: CONTROLLER' % port, msg.getControllerNumber( ), msg.getControllerValue() if __name__ == '__main__': # increase stack size to avoid segfault resource.setrlimit(resource.RLIMIT_CORE, (resource.RLIM_INFINITY, resource.RLIM_INFINITY)) midi_in = RtMidiIn() midi_out = RtMidiOut() print "AVAILABLE PORTS:" for i in range(midi_in.getPortCount()): print " ", i, midi_in.getPortName(i) in_port = raw_input("INPUT PORT (1): ") in_port = 1 if in_port.strip() == "" else int(in_port) out_port = raw_input("OUTPUT PORT (0): ") out_port = 0 if out_port.strip() == "" else int(out_port) server = Server(midi_in, in_port, midi_out, out_port) server.start() print 'HIT ENTER TO EXIT'
if __name__ == '__main__': say("Listening.") # Shows that speech synthesis isn't blocked. parser = argparse.ArgumentParser(description="A MIDI/Keyboard signal router.") parser.add_argument('-v', '--volume', dest="volume", help='Listen for volume key.', action='store_true', required=False) parser.add_argument('-a', '--accent', dest="accent", help='Listen for accent key.', action='store_true', required=False) parser.add_argument('-r', '--record', dest="send_midi_record", help='Send synthetic MIDI record/stop/new', action='store_true', required=False) parser.add_argument('-p', '--play', dest="send_midi_play", help='Send synthetic MIDI play/stop', action='store_true', required=False) ARGS = parser.parse_args() #listen_to_all_keys() listen_to_hot_keys() for i in range(RtMidiIn().getPortCount()): Reader(i) fake_out = RtMidiOut() for i in range(fake_out.getPortCount()): if fake_out.getPortName(i) == VIRTUAL_OUTPUT_NAME: VIRTUAL_OUTPUT = Writer(i) while True: # print("HI") time.sleep(100) #gevent.sleep(1.0) #gevent.spawn_later(0.1, call_obs_api)