""" Fallback RTMIDI implementation using pygame.midi for Windows compatibility. """ import pygame.midi import time from typing import List, Optional, Callable class MidiOut: def __init__(self, device_id): pygame.midi.init() self.device_id = device_id self.midi_out = pygame.midi.Output(device_id) def send_message(self, message): """Send MIDI message""" if hasattr(message, 'bytes'): # mido message data = message.bytes() else: # Raw bytes data = message if len(data) == 3: self.midi_out.write_short(data[0], data[1], data[2]) elif len(data) == 2: self.midi_out.write_short(data[0], data[1]) def close(self): if hasattr(self, 'midi_out'): self.midi_out.close() class MidiIn: def __init__(self, device_id, callback=None): pygame.midi.init() self.device_id = device_id self.midi_in = pygame.midi.Input(device_id) self.callback = callback def set_callback(self, callback): self.callback = callback def poll(self): """Poll for MIDI input (call this regularly)""" if self.midi_in.poll() and self.callback: midi_events = self.midi_in.read(10) for event in midi_events: # Convert pygame midi event to mido-like message if self.callback: self.callback(event) def close(self): if hasattr(self, 'midi_in'): self.midi_in.close() def get_output_names() -> List[str]: """Get available MIDI output device names""" pygame.midi.init() devices = [] for i in range(pygame.midi.get_count()): info = pygame.midi.get_device_info(i) if info[3]: # is_output devices.append(info[1].decode()) return devices def get_input_names() -> List[str]: """Get available MIDI input device names""" pygame.midi.init() devices = [] for i in range(pygame.midi.get_count()): info = pygame.midi.get_device_info(i) if info[2]: # is_input devices.append(info[1].decode()) return devices def open_output(name: str) -> MidiOut: """Open MIDI output by name""" pygame.midi.init() for i in range(pygame.midi.get_count()): info = pygame.midi.get_device_info(i) if info[3] and info[1].decode() == name: # is_output and name matches return MidiOut(i) raise ValueError(f"MIDI output '{name}' not found") def open_input(name: str, callback=None) -> MidiIn: """Open MIDI input by name""" pygame.midi.init() for i in range(pygame.midi.get_count()): info = pygame.midi.get_device_info(i) if info[2] and info[1].decode() == name: # is_input and name matches return MidiIn(i, callback) raise ValueError(f"MIDI input '{name}' not found")