0
# MIDI Utilities
1
2
Helper functions for MIDI port management, API selection, and interactive port opening with support for environment-based configuration. Available through the `rtmidi.midiutil` module.
3
4
## Capabilities
5
6
### Environment-based API Selection
7
8
Determine which MIDI API backend to use based on environment variables and system capabilities.
9
10
```python { .api }
11
def get_api_from_environment(api=rtmidi.API_UNSPECIFIED):
12
"""
13
Get RtMidi API from environment variable or return specified API.
14
15
Parameters:
16
- api: Default API to use if environment doesn't specify one
17
18
Returns:
19
- int: One of the rtmidi.API_* constants
20
21
Environment:
22
- RTMIDI_API: Set to API name (LINUX_ALSA, UNIX_JACK, MACOSX_CORE,
23
WINDOWS_MM, RTMIDI_DUMMY) to specify backend
24
25
Note: If RTMIDI_API is unset or invalid, returns the api parameter value.
26
"""
27
```
28
29
### Port Listing Functions
30
31
List available MIDI input and output ports on the system.
32
33
```python { .api }
34
def list_available_ports(ports=None, midiio=None):
35
"""
36
Print list of MIDI ports to console.
37
38
Parameters:
39
- ports: List of port names (if None, gets from midiio instance)
40
- midiio: MidiIn or MidiOut instance to query ports from
41
42
Output: Prints numbered list of available ports to console
43
"""
44
45
def list_input_ports(api=rtmidi.API_UNSPECIFIED):
46
"""
47
List available MIDI input ports.
48
49
Parameters:
50
- api: MIDI API to use (determined via get_api_from_environment if API_UNSPECIFIED)
51
52
Raises:
53
- rtmidi.SystemError: If RtMidi backend initialization fails
54
55
Output: Prints numbered list of input ports to console
56
"""
57
58
def list_output_ports(api=rtmidi.API_UNSPECIFIED):
59
"""
60
List available MIDI output ports.
61
62
Parameters:
63
- api: MIDI API to use (determined via get_api_from_environment if API_UNSPECIFIED)
64
65
Raises:
66
- rtmidi.SystemError: If RtMidi backend initialization fails
67
68
Output: Prints numbered list of output ports to console
69
"""
70
```
71
72
### Generic Port Opening
73
74
Flexible port opening function with interactive selection and virtual port support.
75
76
```python { .api }
77
def open_midiport(port=None, type_="input", api=rtmidi.API_UNSPECIFIED,
78
use_virtual=False, interactive=True, client_name=None,
79
port_name=None):
80
"""
81
Open MIDI port with flexible port selection and configuration.
82
83
Parameters:
84
- port: Port number (int), port name substring (str), or None for interactive selection
85
- type_: "input" or "output" to determine MidiIn vs MidiOut instance
86
- api: MIDI API backend (passed to get_api_from_environment)
87
- use_virtual: Create virtual port if no hardware ports or port=None
88
- interactive: Enable console prompts for port selection and virtual port creation
89
- client_name: MIDI client name for the instance
90
- port_name: Name for the opened port (uses default if None)
91
92
Returns:
93
- tuple[MidiIn|MidiOut, str]: (midi_instance, port_name)
94
95
Raises:
96
- KeyboardInterrupt: User pressed Ctrl-C during interactive selection
97
- EOFError: User pressed Ctrl-D during interactive selection
98
- rtmidi.SystemError: RtMidi backend initialization failed
99
- rtmidi.NoDevicesError: No MIDI ports available and virtual not enabled
100
- rtmidi.InvalidPortError: Invalid port specified and interactive=False
101
102
Port Selection Behavior:
103
- port=int: Open specific port number
104
- port=str: Open first port containing substring (case-sensitive)
105
- port=None: Interactive selection or virtual port creation
106
107
Interactive Mode:
108
- Lists available ports with numbers
109
- Prompts user to select port by number
110
- Offers virtual port creation option (if supported by API)
111
- Continues prompting until valid selection or user exits
112
"""
113
```
114
115
### Specialized Port Opening Functions
116
117
Convenience functions for opening specific input or output ports.
118
119
```python { .api }
120
def open_midiinput(port=None, api=rtmidi.API_UNSPECIFIED, use_virtual=False,
121
interactive=True, client_name=None, port_name=None):
122
"""
123
Open MIDI input port using open_midiport with type_="input".
124
125
Parameters: Same as open_midiport (excluding type_ parameter)
126
127
Returns:
128
- tuple[MidiIn, str]: (midi_input_instance, port_name)
129
130
Raises: Same as open_midiport
131
"""
132
133
def open_midioutput(port=None, api=rtmidi.API_UNSPECIFIED, use_virtual=False,
134
interactive=True, client_name=None, port_name=None):
135
"""
136
Open MIDI output port using open_midiport with type_="output".
137
138
Parameters: Same as open_midiport (excluding type_ parameter)
139
140
Returns:
141
- tuple[MidiOut, str]: (midi_output_instance, port_name)
142
143
Raises: Same as open_midiport
144
"""
145
```
146
147
## Usage Examples
148
149
### Environment-based API Selection
150
151
```python
152
import os
153
import rtmidi.midiutil as midiutil
154
155
# Set preferred MIDI API via environment
156
os.environ['RTMIDI_API'] = 'LINUX_ALSA' # or 'UNIX_JACK', 'MACOSX_CORE', etc.
157
158
# Use environment setting
159
api = midiutil.get_api_from_environment()
160
print(f"Using API: {api}")
161
162
# Override environment setting
163
api = midiutil.get_api_from_environment(rtmidi.API_UNIX_JACK)
164
print(f"Using API: {api}") # Will use JACK regardless of environment
165
```
166
167
### Listing Available Ports
168
169
```python
170
import rtmidi.midiutil as midiutil
171
172
# List all input ports using default API
173
print("MIDI Input Ports:")
174
midiutil.list_input_ports()
175
176
# List all output ports using specific API
177
print("MIDI Output Ports (ALSA):")
178
midiutil.list_output_ports(api=rtmidi.API_LINUX_ALSA)
179
```
180
181
Output example:
182
```
183
MIDI Input Ports:
184
Available MIDI input ports:
185
186
[0] Midi Through:Midi Through Port-0 14:0
187
[1] USB MIDI Device:USB MIDI Device MIDI 1 20:0
188
189
MIDI Output Ports (ALSA):
190
Available MIDI output ports:
191
192
[0] Midi Through:Midi Through Port-0 14:0
193
[1] USB MIDI Device:USB MIDI Device MIDI 1 20:0
194
```
195
196
### Interactive Port Opening
197
198
```python
199
import rtmidi.midiutil as midiutil
200
201
# Interactive input port selection
202
try:
203
midiin, port_name = midiutil.open_midiinput(
204
interactive=True,
205
use_virtual=True,
206
client_name="My MIDI App"
207
)
208
print(f"Opened input port: {port_name}")
209
210
# Use the MIDI input
211
# ... your code here ...
212
213
finally:
214
midiin.close_port()
215
del midiin
216
```
217
218
### Programmatic Port Opening
219
220
```python
221
import rtmidi.midiutil as midiutil
222
223
# Open specific port by number
224
try:
225
midiout, port_name = midiutil.open_midioutput(
226
port=0, # First available port
227
interactive=False,
228
port_name="My Output Port"
229
)
230
print(f"Opened output port: {port_name}")
231
232
except rtmidi.InvalidPortError:
233
print("Port 0 not available")
234
except rtmidi.NoDevicesError:
235
print("No MIDI output ports found")
236
237
# Open port by name substring
238
try:
239
midiout, port_name = midiutil.open_midioutput(
240
port="USB MIDI", # Matches first port containing "USB MIDI"
241
interactive=False
242
)
243
print(f"Opened output port: {port_name}")
244
245
except rtmidi.InvalidPortError:
246
print("No port matching 'USB MIDI' found")
247
```
248
249
### Virtual Port Creation
250
251
```python
252
import rtmidi.midiutil as midiutil
253
254
# Create virtual port automatically if no hardware ports
255
try:
256
midiin, port_name = midiutil.open_midiinput(
257
port=None, # No specific port
258
use_virtual=True, # Enable virtual port creation
259
interactive=False, # Don't prompt user
260
port_name="Virtual Input App"
261
)
262
print(f"Created/opened: {port_name}")
263
264
# Virtual port is now available for other applications to connect to
265
print("Waiting for MIDI input...")
266
while True:
267
message = midiin.get_message()
268
if message:
269
print(f"Received: {message}")
270
271
except KeyboardInterrupt:
272
print("Exiting...")
273
finally:
274
midiin.close_port()
275
del midiin
276
```
277
278
### Comprehensive Example with Error Handling
279
280
```python
281
import rtmidi
282
import rtmidi.midiutil as midiutil
283
import time
284
285
def setup_midi_io():
286
"""Set up MIDI input and output with comprehensive error handling."""
287
288
try:
289
# Try to open specific devices by name
290
midiin, in_name = midiutil.open_midiinput(
291
port="Keystation", # Look for keyboard with "Keystation" in name
292
use_virtual=True, # Fall back to virtual if not found
293
interactive=False, # Don't prompt user
294
client_name="MIDI Processor"
295
)
296
print(f"MIDI Input: {in_name}")
297
298
midiout, out_name = midiutil.open_midioutput(
299
port="TiMidity", # Look for software synth
300
use_virtual=True, # Fall back to virtual if not found
301
interactive=False,
302
client_name="MIDI Processor"
303
)
304
print(f"MIDI Output: {out_name}")
305
306
return midiin, midiout
307
308
except rtmidi.SystemError as e:
309
print(f"MIDI system error: {e}")
310
return None, None
311
except Exception as e:
312
print(f"Unexpected error: {e}")
313
return None, None
314
315
def main():
316
midiin, midiout = setup_midi_io()
317
if not midiin or not midiout:
318
print("Failed to set up MIDI I/O")
319
return
320
321
try:
322
# Process MIDI for 30 seconds
323
print("Processing MIDI for 30 seconds...")
324
start_time = time.time()
325
326
while time.time() - start_time < 30:
327
message = midiin.get_message()
328
if message:
329
# Echo received message to output
330
midiout.send_message(message[0])
331
print(f"Echoed: {message[0]}")
332
time.sleep(0.01)
333
334
except KeyboardInterrupt:
335
print("Interrupted by user")
336
finally:
337
# Clean up
338
midiin.close_port()
339
midiout.close_port()
340
del midiin
341
del midiout
342
print("MIDI I/O closed")
343
344
if __name__ == "__main__":
345
main()
346
```
347
348
### Custom Port Selection Logic
349
350
```python
351
import rtmidi
352
import rtmidi.midiutil as midiutil
353
354
def find_preferred_ports():
355
"""Find preferred MIDI ports based on custom criteria."""
356
357
# Get all available ports
358
midiin = rtmidi.MidiIn()
359
input_ports = midiin.get_ports()
360
midiin.delete()
361
362
midiout = rtmidi.MidiOut()
363
output_ports = midiout.get_ports()
364
midiout.delete()
365
366
# Custom port selection logic
367
preferred_input = None
368
preferred_output = None
369
370
# Prefer hardware controllers for input
371
for i, port in enumerate(input_ports):
372
if any(keyword in port.lower() for keyword in ['keyboard', 'controller', 'keystation']):
373
preferred_input = i
374
break
375
376
# Prefer software synths for output
377
for i, port in enumerate(output_ports):
378
if any(keyword in port.lower() for keyword in ['timidity', 'fluidsynth', 'synth']):
379
preferred_output = i
380
break
381
382
return preferred_input, preferred_output, input_ports, output_ports
383
384
def open_preferred_ports():
385
"""Open MIDI ports using custom selection logic."""
386
387
in_port, out_port, in_ports, out_ports = find_preferred_ports()
388
389
# Open input
390
if in_port is not None:
391
midiin, in_name = midiutil.open_midiinput(port=in_port, interactive=False)
392
print(f"Opened preferred input: {in_name}")
393
else:
394
print("No preferred input found, using interactive selection:")
395
midiin, in_name = midiutil.open_midiinput(interactive=True)
396
397
# Open output
398
if out_port is not None:
399
midiout, out_name = midiutil.open_midioutput(port=out_port, interactive=False)
400
print(f"Opened preferred output: {out_name}")
401
else:
402
print("No preferred output found, using interactive selection:")
403
midiout, out_name = midiutil.open_midioutput(interactive=True)
404
405
return midiin, midiout
406
407
# Usage
408
try:
409
midiin, midiout = open_preferred_ports()
410
# ... use MIDI I/O ...
411
finally:
412
midiin.close_port()
413
midiout.close_port()
414
del midiin
415
del midiout
416
```