From c070ac902644324858015fc239f19b7088c8d0a2 Mon Sep 17 00:00:00 2001 From: bklronin Date: Wed, 26 Jun 2024 14:15:53 +0200 Subject: [PATCH] Working 1.0 --- main.py | 157 +++++++++++++++++++++++++++++++++++++++++++++++++ main_direct.py | 72 +++++++++++++++++++++++ 2 files changed, 229 insertions(+) create mode 100644 main.py create mode 100644 main_direct.py diff --git a/main.py b/main.py new file mode 100644 index 0000000..ef5f073 --- /dev/null +++ b/main.py @@ -0,0 +1,157 @@ +import time + +import mido + +from mido.ports import MultiPort + +def match_device_name(input_name, output_list): + # Function to match device names flexibly + for device in output_list: + if any(substring in device for substring in input_name.split()): + return device + return None + + +def determine_host(devices_in): + master = None + """ + Determine the host by listening for 'start' messages on input ports. + + Args: + - devices_in (list of str): The names of the MIDI input ports to listen to. + + Returns: + - str: The name of the input port that received the 'start' message. + """ + # Open all the input ports + inports = [mido.open_input(device) for device in devices_in] + + # Create a MultiPort to listen on all input ports + #ports = MultiPort(inports, True) + + print("Listening for messages...") + try: + while not master: + for port in inports: + for message in port.iter_pending(): + #print(f"Received message: ({port.name}, {message})") + if 'start' in str(message): + master = port.name + break + + except KeyboardInterrupt: + print("Interrupted by user.") + + finally: + # Ensure all ports are closed + for port in inports: + port.close() + print("Closed all input ports.") + if master: + return master + +def relay_midi_host_to_out(host_name, devices_out): + """ + Relay MIDI messages from a specific input port to all specified output ports. + + Args: + - host_name (str): The name of the MIDI input port to listen to. + - devices_out (list of str): The names of the MIDI output ports to send messages to. + """ + #host_name = f'{host_name}' + reset = False + # Open the input port + print("Opening Input ffsk", host_name) + + input_port = mido.open_input(host_name) + output_ports = [mido.open_output(device) for device in devices_out] + + print(f"Listening for messages on {host_name} and relaying to {devices_out}") + recent_messages = [] + + try: + print(reset) + while not reset: + # Process all pending messages + #print("Entering loop", input_port.iter_pending() ) + for message in input_port.iter_pending(): + read_message = str(message) + #spread_the_word = MultiPort(output_ports) + #print(read_message) + + if 'start' in read_message or 'stop' in read_message: + for dev in output_ports: + dev.send(message) + recent_messages.append(read_message) + #print("SS") + + elif 'clock' in read_message: + for dev in output_ports: + dev.send(message) + #print("clock") + + # Keep only the last 3 messages + if len(recent_messages) > 3: + recent_messages.pop(0) + + # Check if the last 3 messages are all 'stop' + if recent_messages[-3:] == ['stop time=0', 'stop time=0', 'stop time=0']: + #print("Received 'stop' three times in a row") + # Perform the desired action when 'stop' is received three times in a row + # For example, resetting the list + recent_messages.clear() + + reset = True + break + + except KeyboardInterrupt: + # Handle user interrupt (e.g., Ctrl+C) + print("MIDI relay interrupted by user.") + + finally: + # Close all ports + input_port.close() + print(input_port) + + for port in output_ports: + #port.reset() + port.close() + print(port) + print("Closed all MIDI ports.") + + #Repeat host detection a nd main loop + time.sleep(1) + main_run() + +def main_run(): + + # Retrieve the list of available input devices + devices_in = mido.get_input_names() + devices_out = mido.get_output_names() + print("Available MIDI input devices:", devices_in) + + # Call the function to start receiving messages from all devices + midi_host_in = determine_host(devices_in) + print(midi_host_in) + + # Find and remove the matching output device to avoid feedback + matching_output_device = match_device_name(midi_host_in, devices_out) + + if matching_output_device: + devices_out.remove(matching_output_device) + + print(devices_out) + #time.sleep(3) + if midi_host_in: + relay_midi_host_to_out(midi_host_in, devices_out) + + print("mido died") + +main_run() + + + + + + + diff --git a/main_direct.py b/main_direct.py new file mode 100644 index 0000000..440cdb2 --- /dev/null +++ b/main_direct.py @@ -0,0 +1,72 @@ +import rtmidi +import time + + +class HostsSync: + def __init__(self): + self.host_id = None + self.available_ports = None + self.midi_out = rtmidi.MidiOut() + self.opened_ports = [] + + def midi_input_callback(self, event, data_pack): + message, deltatime = event + status = message[0] + host_id, midi_in = data_pack + + # Check if the status byte is 0xFA (250 in decimal) or 0xFF (255 in decimal), + # which indicate MIDI Start and Stop messages respectively + if status in [250, 255]: + print(f"MIDI message received from device {host_id}: {message}") + self.host_id = host_id + self.midi_output_called(message) + + def midi_output_called(self, message): + if self.host_id is None: + return + + for i, port in enumerate(self.available_ports): + if i != self.host_id: + if i not in self.opened_ports: + self.midi_out.open_port(i) + self.opened_ports.append(i) + print(f"Opened MIDI output port: {self.available_ports[i]}") + + self.midi_out.send_message(message) + + def main(self): + midi_ins = [] + self.available_ports = rtmidi.MidiIn().get_ports() + + if not self.available_ports: + print("No MIDI input ports available.") + return + + print("Available MIDI input ports:") + for i, port in enumerate(self.available_ports): + print(f"{i}: {port}") + + try: + for i, port in enumerate(self.available_ports): + midi_in = rtmidi.MidiIn() + midi_in.open_port(i) + midi_in.set_callback(self.midi_input_callback, (i, midi_in)) + midi_ins.append(midi_in) + print(f"Listening for MIDI messages on input port: {port}") + + # Keep the script running to listen for MIDI messages + while True: + time.sleep(1) + + except KeyboardInterrupt: + print("\nExiting...") + + finally: + for midi_in in midi_ins: + midi_in.close_port() + + self.midi_out.close_port() + +if __name__ == "__main__": + HostsSync().main() +