Source code for jdxi_editor.devel.rtmidi_compat

"""
rtmidi Compatibility Module

This module provides compatibility between different versions of rtmidi.
It creates wrapper classes that match the older MidiIn/MidiOut API expected by the JD-Xi Editor codebase.
"""

import rtmidi

# Store original rtmidi classes before we potentially overwrite them
[docs] _original_rtmidi_in = getattr(rtmidi, "RtMidiIn", None)
[docs] _original_rtmidi_out = getattr(rtmidi, "RtMidiOut", None)
[docs] _original_midi_in = getattr(rtmidi, "MidiIn", None)
[docs] _original_midi_out = getattr(rtmidi, "MidiOut", None)
[docs] class MidiIn: """Compatibility wrapper for RtMidiIn""" def __init__(self): # Try to use the original rtmidi classes (before we overwrote them) if _original_rtmidi_in is not None: try: self._midi = _original_rtmidi_in() except (AttributeError, TypeError): self._midi = self._create_mock() elif _original_midi_in is not None and _original_midi_in is not MidiIn: try: self._midi = _original_midi_in() except (AttributeError, TypeError): self._midi = self._create_mock() else: # Fallback: create a mock for testing self._midi = self._create_mock() @staticmethod
[docs] def _create_mock(): """Create a mock MidiIn for testing""" class MockMidiIn: def getPortCount(self): return 0 def getPortName(self, i): return "" def isPortOpen(self): return False def openPort(self, port): pass def closePort(self): pass def setCallback(self, callback): pass def ignoreTypes(self, sysex, timing, active_sense): pass return MockMidiIn()
[docs] def get_ports(self): """Get list of available input ports""" # Handle both old API (getPortCount/getPortName) and new API (get_port_count/get_port_name) if hasattr(self._midi, "get_port_count"): ports = [] for i in range(self._midi.get_port_count()): ports.append(self._midi.get_port_name(i)) return ports elif hasattr(self._midi, "getPortCount"): ports = [] for i in range(self._midi.getPortCount()): ports.append(self._midi.getPortName(i)) return ports else: return []
[docs] def is_port_open(self): """Check if port is open""" if hasattr(self._midi, "is_port_open"): return self._midi.is_port_open() elif hasattr(self._midi, "isPortOpen"): return self._midi.isPortOpen() else: return False
[docs] def open_port(self, port): """Open port by name or index""" if isinstance(port, str): # Find port by name if hasattr(self._midi, "get_port_count"): for i in range(self._midi.get_port_count()): if self._midi.get_port_name(i) == port: return self._midi.open_port(i) elif hasattr(self._midi, "getPortCount"): for i in range(self._midi.getPortCount()): if self._midi.getPortName(i) == port: return self._midi.openPort(i) raise ValueError(f"Port '{port}' not found") else: # Assume it's a port index if hasattr(self._midi, "open_port"): return self._midi.open_port(port) elif hasattr(self._midi, "openPort"): return self._midi.openPort(port) else: pass
[docs] def close_port(self): """Close the port""" if hasattr(self._midi, "close_port"): return self._midi.close_port() elif hasattr(self._midi, "closePort"): return self._midi.closePort() else: pass
[docs] def set_callback(self, callback): """Set MIDI callback""" # Handle both old API (setCallback) and new API (set_callback) if hasattr(self._midi, "set_callback"): return self._midi.set_callback(callback) elif hasattr(self._midi, "setCallback"): return self._midi.setCallback(callback) else: # Mock doesn't need to do anything pass
[docs] def ignore_types(self, sysex=False, timing=True, active_sense=True): """Set ignored MIDI types""" # Handle both old API (ignoreTypes) and new API (ignore_types) if hasattr(self._midi, "ignore_types"): return self._midi.ignore_types(sysex, timing, active_sense) elif hasattr(self._midi, "ignoreTypes"): return self._midi.ignoreTypes(sysex, timing, active_sense) else: # Mock doesn't need to do anything pass
[docs] def send_message(self, message): """Send MIDI message (for compatibility)""" # This is typically not used on input, but included for compatibility pass
[docs] class MidiOut: """Compatibility wrapper for RtMidiOut""" def __init__(self): # Try to use the original rtmidi classes (before we overwrote them) if _original_rtmidi_out is not None: try: self._midi = _original_rtmidi_out() except (AttributeError, TypeError): self._midi = self._create_mock() elif _original_midi_out is not None and _original_midi_out is not MidiOut: try: self._midi = _original_midi_out() except (AttributeError, TypeError): self._midi = self._create_mock() else: # Fallback: create a mock for testing self._midi = self._create_mock() @staticmethod
[docs] def _create_mock(): """Create a mock MidiOut for testing""" class MockMidiOut: def getPortCount(self): return 0 def getPortName(self, i): return "" def isPortOpen(self): return False def openPort(self, port): pass def closePort(self): pass def sendMessage(self, message): pass def send_message(self, message): pass # Alternative method name return MockMidiOut()
[docs] def get_ports(self): """Get list of available output ports""" # Handle both old API (getPortCount/getPortName) and new API (get_port_count/get_port_name) if hasattr(self._midi, "get_port_count"): ports = [] for i in range(self._midi.get_port_count()): ports.append(self._midi.get_port_name(i)) return ports elif hasattr(self._midi, "getPortCount"): ports = [] for i in range(self._midi.getPortCount()): ports.append(self._midi.getPortName(i)) return ports else: return []
[docs] def is_port_open(self): """Check if port is open""" if hasattr(self._midi, "is_port_open"): return self._midi.is_port_open() elif hasattr(self._midi, "isPortOpen"): return self._midi.isPortOpen() else: return False
[docs] def open_port(self, port): """Open port by name or index""" if isinstance(port, str): # Find port by name if hasattr(self._midi, "get_port_count"): for i in range(self._midi.get_port_count()): if self._midi.get_port_name(i) == port: return self._midi.open_port(i) elif hasattr(self._midi, "getPortCount"): for i in range(self._midi.getPortCount()): if self._midi.getPortName(i) == port: return self._midi.openPort(i) raise ValueError(f"Port '{port}' not found") else: # Assume it's a port index if hasattr(self._midi, "open_port"): return self._midi.open_port(port) elif hasattr(self._midi, "openPort"): return self._midi.openPort(port) else: pass
[docs] def close_port(self): """Close the port""" if hasattr(self._midi, "close_port"): return self._midi.close_port() elif hasattr(self._midi, "closePort"): return self._midi.closePort() else: pass
[docs] def send_message(self, message): """Send MIDI message""" if hasattr(self._midi, "sendMessage"): return self._midi.sendMessage(message) elif hasattr(self._midi, "send_message"): return self._midi.send_message(message) else: pass # Mock doesn't need to do anything
# Create aliases for compatibility with existing codebase # Only assign if they don't already exist to avoid recursion if not hasattr(rtmidi, "MidiIn") or ( hasattr(rtmidi, "MidiIn") and rtmidi.MidiIn is not MidiIn ): rtmidi.MidiIn = MidiIn if not hasattr(rtmidi, "MidiOut") or ( hasattr(rtmidi, "MidiOut") and rtmidi.MidiOut is not MidiOut ): rtmidi.MidiOut = MidiOut