A Python binding for the RtMidi C++ library providing cross-platform realtime MIDI input/output functionality.
—
Helper functions for MIDI port management, API selection, and interactive port opening with support for environment-based configuration. Available through the rtmidi.midiutil module.
Determine which MIDI API backend to use based on environment variables and system capabilities.
def get_api_from_environment(api=rtmidi.API_UNSPECIFIED):
"""
Get RtMidi API from environment variable or return specified API.
Parameters:
- api: Default API to use if environment doesn't specify one
Returns:
- int: One of the rtmidi.API_* constants
Environment:
- RTMIDI_API: Set to API name (LINUX_ALSA, UNIX_JACK, MACOSX_CORE,
WINDOWS_MM, RTMIDI_DUMMY) to specify backend
Note: If RTMIDI_API is unset or invalid, returns the api parameter value.
"""List available MIDI input and output ports on the system.
def list_available_ports(ports=None, midiio=None):
"""
Print list of MIDI ports to console.
Parameters:
- ports: List of port names (if None, gets from midiio instance)
- midiio: MidiIn or MidiOut instance to query ports from
Output: Prints numbered list of available ports to console
"""
def list_input_ports(api=rtmidi.API_UNSPECIFIED):
"""
List available MIDI input ports.
Parameters:
- api: MIDI API to use (determined via get_api_from_environment if API_UNSPECIFIED)
Raises:
- rtmidi.SystemError: If RtMidi backend initialization fails
Output: Prints numbered list of input ports to console
"""
def list_output_ports(api=rtmidi.API_UNSPECIFIED):
"""
List available MIDI output ports.
Parameters:
- api: MIDI API to use (determined via get_api_from_environment if API_UNSPECIFIED)
Raises:
- rtmidi.SystemError: If RtMidi backend initialization fails
Output: Prints numbered list of output ports to console
"""Flexible port opening function with interactive selection and virtual port support.
def open_midiport(port=None, type_="input", api=rtmidi.API_UNSPECIFIED,
use_virtual=False, interactive=True, client_name=None,
port_name=None):
"""
Open MIDI port with flexible port selection and configuration.
Parameters:
- port: Port number (int), port name substring (str), or None for interactive selection
- type_: "input" or "output" to determine MidiIn vs MidiOut instance
- api: MIDI API backend (passed to get_api_from_environment)
- use_virtual: Create virtual port if no hardware ports or port=None
- interactive: Enable console prompts for port selection and virtual port creation
- client_name: MIDI client name for the instance
- port_name: Name for the opened port (uses default if None)
Returns:
- tuple[MidiIn|MidiOut, str]: (midi_instance, port_name)
Raises:
- KeyboardInterrupt: User pressed Ctrl-C during interactive selection
- EOFError: User pressed Ctrl-D during interactive selection
- rtmidi.SystemError: RtMidi backend initialization failed
- rtmidi.NoDevicesError: No MIDI ports available and virtual not enabled
- rtmidi.InvalidPortError: Invalid port specified and interactive=False
Port Selection Behavior:
- port=int: Open specific port number
- port=str: Open first port containing substring (case-sensitive)
- port=None: Interactive selection or virtual port creation
Interactive Mode:
- Lists available ports with numbers
- Prompts user to select port by number
- Offers virtual port creation option (if supported by API)
- Continues prompting until valid selection or user exits
"""Convenience functions for opening specific input or output ports.
def open_midiinput(port=None, api=rtmidi.API_UNSPECIFIED, use_virtual=False,
interactive=True, client_name=None, port_name=None):
"""
Open MIDI input port using open_midiport with type_="input".
Parameters: Same as open_midiport (excluding type_ parameter)
Returns:
- tuple[MidiIn, str]: (midi_input_instance, port_name)
Raises: Same as open_midiport
"""
def open_midioutput(port=None, api=rtmidi.API_UNSPECIFIED, use_virtual=False,
interactive=True, client_name=None, port_name=None):
"""
Open MIDI output port using open_midiport with type_="output".
Parameters: Same as open_midiport (excluding type_ parameter)
Returns:
- tuple[MidiOut, str]: (midi_output_instance, port_name)
Raises: Same as open_midiport
"""import os
import rtmidi.midiutil as midiutil
# Set preferred MIDI API via environment
os.environ['RTMIDI_API'] = 'LINUX_ALSA' # or 'UNIX_JACK', 'MACOSX_CORE', etc.
# Use environment setting
api = midiutil.get_api_from_environment()
print(f"Using API: {api}")
# Override environment setting
api = midiutil.get_api_from_environment(rtmidi.API_UNIX_JACK)
print(f"Using API: {api}") # Will use JACK regardless of environmentimport rtmidi.midiutil as midiutil
# List all input ports using default API
print("MIDI Input Ports:")
midiutil.list_input_ports()
# List all output ports using specific API
print("MIDI Output Ports (ALSA):")
midiutil.list_output_ports(api=rtmidi.API_LINUX_ALSA)Output example:
MIDI Input Ports:
Available MIDI input ports:
[0] Midi Through:Midi Through Port-0 14:0
[1] USB MIDI Device:USB MIDI Device MIDI 1 20:0
MIDI Output Ports (ALSA):
Available MIDI output ports:
[0] Midi Through:Midi Through Port-0 14:0
[1] USB MIDI Device:USB MIDI Device MIDI 1 20:0import rtmidi.midiutil as midiutil
# Interactive input port selection
try:
midiin, port_name = midiutil.open_midiinput(
interactive=True,
use_virtual=True,
client_name="My MIDI App"
)
print(f"Opened input port: {port_name}")
# Use the MIDI input
# ... your code here ...
finally:
midiin.close_port()
del midiinimport rtmidi.midiutil as midiutil
# Open specific port by number
try:
midiout, port_name = midiutil.open_midioutput(
port=0, # First available port
interactive=False,
port_name="My Output Port"
)
print(f"Opened output port: {port_name}")
except rtmidi.InvalidPortError:
print("Port 0 not available")
except rtmidi.NoDevicesError:
print("No MIDI output ports found")
# Open port by name substring
try:
midiout, port_name = midiutil.open_midioutput(
port="USB MIDI", # Matches first port containing "USB MIDI"
interactive=False
)
print(f"Opened output port: {port_name}")
except rtmidi.InvalidPortError:
print("No port matching 'USB MIDI' found")import rtmidi.midiutil as midiutil
# Create virtual port automatically if no hardware ports
try:
midiin, port_name = midiutil.open_midiinput(
port=None, # No specific port
use_virtual=True, # Enable virtual port creation
interactive=False, # Don't prompt user
port_name="Virtual Input App"
)
print(f"Created/opened: {port_name}")
# Virtual port is now available for other applications to connect to
print("Waiting for MIDI input...")
while True:
message = midiin.get_message()
if message:
print(f"Received: {message}")
except KeyboardInterrupt:
print("Exiting...")
finally:
midiin.close_port()
del midiinimport rtmidi
import rtmidi.midiutil as midiutil
import time
def setup_midi_io():
"""Set up MIDI input and output with comprehensive error handling."""
try:
# Try to open specific devices by name
midiin, in_name = midiutil.open_midiinput(
port="Keystation", # Look for keyboard with "Keystation" in name
use_virtual=True, # Fall back to virtual if not found
interactive=False, # Don't prompt user
client_name="MIDI Processor"
)
print(f"MIDI Input: {in_name}")
midiout, out_name = midiutil.open_midioutput(
port="TiMidity", # Look for software synth
use_virtual=True, # Fall back to virtual if not found
interactive=False,
client_name="MIDI Processor"
)
print(f"MIDI Output: {out_name}")
return midiin, midiout
except rtmidi.SystemError as e:
print(f"MIDI system error: {e}")
return None, None
except Exception as e:
print(f"Unexpected error: {e}")
return None, None
def main():
midiin, midiout = setup_midi_io()
if not midiin or not midiout:
print("Failed to set up MIDI I/O")
return
try:
# Process MIDI for 30 seconds
print("Processing MIDI for 30 seconds...")
start_time = time.time()
while time.time() - start_time < 30:
message = midiin.get_message()
if message:
# Echo received message to output
midiout.send_message(message[0])
print(f"Echoed: {message[0]}")
time.sleep(0.01)
except KeyboardInterrupt:
print("Interrupted by user")
finally:
# Clean up
midiin.close_port()
midiout.close_port()
del midiin
del midiout
print("MIDI I/O closed")
if __name__ == "__main__":
main()import rtmidi
import rtmidi.midiutil as midiutil
def find_preferred_ports():
"""Find preferred MIDI ports based on custom criteria."""
# Get all available ports
midiin = rtmidi.MidiIn()
input_ports = midiin.get_ports()
midiin.delete()
midiout = rtmidi.MidiOut()
output_ports = midiout.get_ports()
midiout.delete()
# Custom port selection logic
preferred_input = None
preferred_output = None
# Prefer hardware controllers for input
for i, port in enumerate(input_ports):
if any(keyword in port.lower() for keyword in ['keyboard', 'controller', 'keystation']):
preferred_input = i
break
# Prefer software synths for output
for i, port in enumerate(output_ports):
if any(keyword in port.lower() for keyword in ['timidity', 'fluidsynth', 'synth']):
preferred_output = i
break
return preferred_input, preferred_output, input_ports, output_ports
def open_preferred_ports():
"""Open MIDI ports using custom selection logic."""
in_port, out_port, in_ports, out_ports = find_preferred_ports()
# Open input
if in_port is not None:
midiin, in_name = midiutil.open_midiinput(port=in_port, interactive=False)
print(f"Opened preferred input: {in_name}")
else:
print("No preferred input found, using interactive selection:")
midiin, in_name = midiutil.open_midiinput(interactive=True)
# Open output
if out_port is not None:
midiout, out_name = midiutil.open_midioutput(port=out_port, interactive=False)
print(f"Opened preferred output: {out_name}")
else:
print("No preferred output found, using interactive selection:")
midiout, out_name = midiutil.open_midioutput(interactive=True)
return midiin, midiout
# Usage
try:
midiin, midiout = open_preferred_ports()
# ... use MIDI I/O ...
finally:
midiin.close_port()
midiout.close_port()
del midiin
del midioutInstall with Tessl CLI
npx tessl i tessl/pypi-python-rtmidi