Exemple #1
0
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)
Exemple #2
0
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)
Exemple #3
0
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.')