You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
92 lines
2.9 KiB
92 lines
2.9 KiB
"""
|
|
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")
|