|
| 1 | +import threading |
| 2 | +import mido |
| 3 | +from goofi.data import Data, DataType |
| 4 | +from goofi.node import Node |
| 5 | +from goofi.params import StringParam, IntParam, BoolParam |
| 6 | + |
| 7 | +class MidiCCout(Node): |
| 8 | + def config_input_slots(): |
| 9 | + return { |
| 10 | + "cc1": DataType.ARRAY, |
| 11 | + "cc2": DataType.ARRAY, |
| 12 | + "cc3": DataType.ARRAY, |
| 13 | + "cc4": DataType.ARRAY, |
| 14 | + "cc5": DataType.ARRAY |
| 15 | + } |
| 16 | + |
| 17 | + def config_output_slots(): |
| 18 | + return { |
| 19 | + "midi_status": DataType.STRING |
| 20 | + } |
| 21 | + |
| 22 | + def config_params(): |
| 23 | + available_ports = mido.get_output_names() |
| 24 | + return { |
| 25 | + "MIDI": { |
| 26 | + "port_name": StringParam("", options=available_ports, doc="MIDI output port name"), |
| 27 | + "channel": IntParam(1, 1, 15, doc="MIDI channel"), |
| 28 | + "cc1": IntParam(1, 0, 127, doc="MIDI CC number for input 1"), |
| 29 | + "cc2": IntParam(2, 0, 127, doc="MIDI CC number for input 2"), |
| 30 | + "cc3": IntParam(3, 0, 127, doc="MIDI CC number for input 3"), |
| 31 | + "cc4": IntParam(4, 0, 127, doc="MIDI CC number for input 4"), |
| 32 | + "cc5": IntParam(5, 0, 127, doc="MIDI CC number for input 5") |
| 33 | + } |
| 34 | + } |
| 35 | + |
| 36 | + def send_cc(self, outport, cc_number, value, channel): |
| 37 | + """Send a MIDI CC message.""" |
| 38 | + # convert cc_number to int |
| 39 | + cc_number = int(cc_number) |
| 40 | + value = int(value) |
| 41 | + outport.send(mido.Message('control_change', control=cc_number, value=value, channel=channel)) |
| 42 | + |
| 43 | + def process(self, cc1: Data, cc2: Data, cc3: Data, cc4: Data, cc5: Data): |
| 44 | + port_name = self.params.MIDI["port_name"].value |
| 45 | + channel = self.params.MIDI["channel"].value |
| 46 | + channel = channel - 1 # MIDI channels are 0-indexed |
| 47 | + cc_numbers = [ |
| 48 | + self.params.MIDI["cc1"].value, |
| 49 | + self.params.MIDI["cc2"].value, |
| 50 | + self.params.MIDI["cc3"].value, |
| 51 | + self.params.MIDI["cc4"].value, |
| 52 | + self.params.MIDI["cc5"].value |
| 53 | + ] |
| 54 | + cc_data = [cc1, cc2, cc3, cc4, cc5] |
| 55 | + |
| 56 | + outport = mido.open_output(port_name) if port_name != 'goofi' else self.goofi_port |
| 57 | + |
| 58 | + alert_on = False |
| 59 | + try: |
| 60 | + threads = [] |
| 61 | + for i, data in enumerate(cc_data): |
| 62 | + if data is not None and len(data.data) > 0: |
| 63 | + for value in data.data: |
| 64 | + if value < 0 or value > 127: |
| 65 | + print(f'Value outside of MIDI 0-127 range: {value}') |
| 66 | + alert_on = True |
| 67 | + error_message = f'Value outside of MIDI 0-127 range: {value}' |
| 68 | + pass |
| 69 | + t = threading.Thread(target=self.send_cc, args=(outport, cc_numbers[i], value, channel)) |
| 70 | + t.start() |
| 71 | + threads.append(t) |
| 72 | + |
| 73 | + for t in threads: |
| 74 | + t.join() |
| 75 | + finally: |
| 76 | + if port_name != 'goofi': |
| 77 | + outport.close() # Ensure that the MIDI port is closed when done |
| 78 | + |
| 79 | + if alert_on: |
| 80 | + return { |
| 81 | + "midi_status": (f"CC messages sent with errors\n{error_message}", cc1.meta) |
| 82 | + } |
| 83 | + else: |
| 84 | + return { |
| 85 | + "midi_status": ("CC messages sent successfully", cc1.meta) |
| 86 | + } |
0 commit comments