Source code for rtmidi.midiutil

# -*- coding: utf-8 -*-
#
# midiutil.py
#
"""Collection of utility functions for handling MIDI I/O and ports.

Currently contains functions to list MIDI input/output ports, to get the RtMidi
API to use from the environment and to open MIDI ports.

"""

from __future__ import print_function, unicode_literals

import logging
import os

try:
    raw_input
except NameError:
    # Python 3
    raw_input = input
    basestring = str

import rtmidi


__all__ = (
    'get_api_from_environment',
    'list_available_ports',
    'list_input_ports',
    'list_output_ports',
    'open_midiinput',
    'open_midioutput',
    'open_midiport',
)

log = logging.getLogger(__name__)


def _prompt_for_virtual(type_):
    """Prompt on the console whether a virtual MIDI port should be opened."""
    return raw_input("Do you want to create a virtual MIDI %s port? (y/N) " %
                     type_).strip().lower() in ['y', 'yes']


[docs] def get_api_from_environment(api=rtmidi.API_UNSPECIFIED): """Return RtMidi API specified in the environment if any. If the optional api argument is ``rtmidi.API_UNSPECIFIED`` (the default), look in the environment variable ``RTMIDI_API`` for the name of the RtMidi API to use. Valid names are ``LINUX_ALSA``, ``UNIX_JACK``, ``MACOSX_CORE``, ``WINDOWS_MM`` and ``RTMIDI_DUMMY``. If no valid value is found, ``rtmidi.API_UNSPECIFIED`` will be used. Returns a ``rtmidi.API_*`` constant. """ if api == rtmidi.API_UNSPECIFIED and 'RTMIDI_API' in os.environ: try: api_name = os.environ['RTMIDI_API'].upper() api = getattr(rtmidi, 'API_' + api_name) except AttributeError: log.warning("Ignoring unknown API '%s' in environment variable " "RTMIDI_API." % api_name) return api
[docs] def list_available_ports(ports=None, midiio=None): """List MIDI ports given or available on given MIDI I/O instance.""" if ports is None: ports = midiio.get_ports() type_ = " input" if isinstance(midiio, rtmidi.MidiIn) else " ouput" else: type_ = '' if ports: print("Available MIDI{} ports:\n".format(type_)) for portno, name in enumerate(ports): print("[{}] {}".format(portno, name)) else: print("No MIDI{} ports found.".format(type_)) print()
[docs] def list_input_ports(api=rtmidi.API_UNSPECIFIED): """List available MIDI input ports. Optionally the RtMidi API can be passed with the ``api`` argument. If not it will be determined via the ``get_api_from_environment`` function. Exceptions: ``rtmidi.SystemError`` Raised when RtMidi backend initialization fails. """ midiin = rtmidi.MidiIn(get_api_from_environment(api)) list_available_ports(midiio=midiin) midiin.delete()
[docs] def list_output_ports(api=rtmidi.API_UNSPECIFIED): """List available MIDI output ports. Optionally the RtMidi API can be passed with the ``api`` argument. If not it will be determined via the ``get_api_from_environment`` function. Exceptions: ``rtmidi.SystemError`` Raised when RtMidi backend initialization fails. """ midiout = rtmidi.MidiOut(get_api_from_environment(api)) list_available_ports(midiio=midiout) midiout.delete()
[docs] 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 for in-/output and return MidiIn/-Out instance and port name. Arguments: ``port`` A MIDI port number or (substring of) a port name or ``None``. Available ports are enumerated starting from zero separately for input and output ports. If only a substring of a port name is given, the first matching port is used. ``type_`` Must be ``"input"`` or ``"output"``. Determines whether a ``MidiIn`` or ``MidiOut`` instance will be created and returned. ``api`` Select the low-level MIDI API to use. Defaults to ``API_UNSPECIFIED``, The specified api will be passed to the ``get_api_from_environment`` function and its return value will be used. If it's ``API_UNSPECIFIED`` the first compiled-in API, which has any input resp. output ports available, will be used. ``use_virtual`` If ``port`` is ``None``, should a virtual MIDI port be opened? Defaults to ``False``. ``interactive`` If ``port`` is ``None`` or no MIDI port matching the port number or name is available, should the user be prompted on the console whether to open a virtual MIDI port (if ``use_virtual`` is ``True``) and/or with a list of available MIDI ports and the option to choose one? Defaults to ``True``. ``client_name`` The name of the MIDI client passed when instantiating a ``MidiIn`` or ``MidiOut`` object. See the documentation of the constructor for these classes for the default values and caveats and OS-dependent ideosyncracies regarding the client name. ``port_name`` The name of the MIDI port passed to the ``open_port`` or ``open_virtual_port`` method of the new ``MidiIn`` or ``MidiOut`` instance. See the documentation of the ``open_port`` resp. ``open_virtual_port`` methods for the default values and caveats when wanting to change the port name afterwards. Returns: A two-element tuple of a new ``MidiIn`` or ``MidiOut`` instance and the name of the MIDI port which was opened. Exceptions: ``KeyboardInterrupt, EOFError`` Raised when the user presses Control-C or Control-D during a console prompt. ``rtmidi.SystemError`` Raised when RtMidi backend initialization fails. ``rtmidi.NoDevicesError`` Raised when no MIDI input or output ports (depending on what was requested) are available. ``rtmidi.InvalidPortError`` Raised when an invalid port number or name is passed and ``interactive`` is ``False``. """ midiclass_ = rtmidi.MidiIn if type_ == "input" else rtmidi.MidiOut log.debug("Creating %s object.", midiclass_.__name__) api = get_api_from_environment(api) midiobj = midiclass_(api, name=client_name) type_ = "input" if isinstance(midiobj, rtmidi.MidiIn) else "output" ports = midiobj.get_ports() if port is None: try: if (midiobj.get_current_api() != rtmidi.API_WINDOWS_MM and (use_virtual or (interactive and _prompt_for_virtual(type_)))): if not port_name: port_name = "Virtual MIDI %s" % type_ log.info("Opening virtual MIDI %s port.", type_) midiobj.open_virtual_port(port_name) return midiobj, port_name except (KeyboardInterrupt, EOFError): del midiobj print('') raise if len(ports) == 0: del midiobj raise rtmidi.NoDevicesError("No MIDI %s ports found." % type_) try: port = int(port) except (TypeError, ValueError): if isinstance(port, basestring): portspec = port for portno, name in enumerate(ports): if portspec in name: port = portno break else: log.warning("No port matching '%s' found.", portspec) port = None while interactive and (port is None or (port < 0 or port >= len(ports))): list_available_ports(ports) try: r = raw_input("Select MIDI %s port (Control-C to exit): " % type_) port = int(r) except (KeyboardInterrupt, EOFError): del midiobj print('') raise except (ValueError, TypeError): port = None if port is not None and (port >= 0 and port < len(ports)): if not port_name: port_name = ports[port] log.info("Opening MIDI %s port #%i (%s)." % (type_, port, port_name)) midiobj.open_port(port, port_name) return midiobj, port_name else: raise rtmidi.InvalidPortError("Invalid port.")
[docs] def open_midiinput(port=None, api=rtmidi.API_UNSPECIFIED, use_virtual=False, interactive=True, client_name=None, port_name=None): """Open a MIDI port for input and return a MidiIn instance and port name. See the ``open_midiport`` function for information on parameters, return types and possible exceptions. """ return open_midiport(port, "input", api, use_virtual, interactive, client_name, port_name)
[docs] def open_midioutput(port=None, api=rtmidi.API_UNSPECIFIED, use_virtual=False, interactive=True, client_name=None, port_name=None): """Open a MIDI port for output and return a MidiOut instance and port name. See the ``open_midiport`` function for information on parameters, return types and possible exceptions. """ return open_midiport(port, "output", api, use_virtual, interactive, client_name, port_name)