CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-python-rtmidi

A Python binding for the RtMidi C++ library providing cross-platform realtime MIDI input/output functionality.

Pending
Overview
Eval results
Files

midi-utilities.mddocs/

MIDI Utilities

Helper functions for MIDI port management, API selection, and interactive port opening with support for environment-based configuration. Available through the rtmidi.midiutil module.

Capabilities

Environment-based API Selection

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.
    """

Port Listing Functions

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
    """

Generic Port Opening

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
    """

Specialized Port Opening Functions

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
    """

Usage Examples

Environment-based API Selection

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 environment

Listing Available Ports

import 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:0

Interactive Port Opening

import 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 midiin

Programmatic Port Opening

import 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")

Virtual Port Creation

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 midiin

Comprehensive Example with Error Handling

import 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()

Custom Port Selection Logic

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 midiout

Install with Tessl CLI

npx tessl i tessl/pypi-python-rtmidi

docs

index.md

midi-constants.md

midi-io.md

midi-utilities.md

tile.json