0
# MIDI Support
1
2
Musical Instrument Digital Interface support for music applications and real-time musical input/output. Provides comprehensive MIDI device management, event processing, and musical utility functions.
3
4
## Capabilities
5
6
### MIDI System Management
7
8
Core functions for initializing and managing the MIDI subsystem.
9
10
```python { .api }
11
def init() -> None:
12
"""Initialize MIDI module."""
13
14
def quit() -> None:
15
"""Quit MIDI module."""
16
17
def get_init() -> bool:
18
"""
19
Check if MIDI module is initialized.
20
21
Returns:
22
bool: True if MIDI module is initialized
23
"""
24
25
def get_count() -> int:
26
"""
27
Get number of MIDI devices.
28
29
Returns:
30
int: Total number of MIDI devices (input + output)
31
"""
32
33
def get_default_input_id() -> int:
34
"""
35
Get default input device ID.
36
37
Returns:
38
int: Default input device ID, or -1 if none available
39
"""
40
41
def get_default_output_id() -> int:
42
"""
43
Get default output device ID.
44
45
Returns:
46
int: Default output device ID, or -1 if none available
47
"""
48
49
def get_device_info(device_id: int) -> tuple[str, str, int, int, int] | None:
50
"""
51
Get device information.
52
53
Parameters:
54
device_id: Device ID to query
55
56
Returns:
57
tuple[str, str, int, int, int] | None: (interface, name, input, output, opened) or None if invalid
58
"""
59
60
def time() -> int:
61
"""
62
Get current MIDI time.
63
64
Returns:
65
int: Current time in milliseconds
66
"""
67
```
68
69
### MIDI Input
70
71
Real-time MIDI input for processing musical keyboard, controller, and sequencer data.
72
73
```python { .api }
74
class Input:
75
def __init__(self, device_id: int, buffer_size: int = 4096):
76
"""
77
Initialize MIDI input device.
78
79
Parameters:
80
device_id: Input device ID from get_device_info()
81
buffer_size: Input buffer size in bytes
82
"""
83
84
def close(self) -> None:
85
"""Close input device and free resources."""
86
87
def read(self, num_events: int) -> list[list]:
88
"""
89
Read MIDI events from device.
90
91
Parameters:
92
num_events: Maximum number of events to read
93
94
Returns:
95
list[list]: List of MIDI events, each event is [[[status, data1, data2, data3], timestamp], ...]
96
"""
97
98
def poll(self) -> bool:
99
"""
100
Check if events are available to read.
101
102
Returns:
103
bool: True if events are pending in buffer
104
"""
105
```
106
107
### MIDI Output
108
109
Real-time MIDI output for controlling synthesizers, sound modules, and external MIDI devices.
110
111
```python { .api }
112
class Output:
113
def __init__(self, device_id: int, latency: int = 0, buffer_size: int = 256):
114
"""
115
Initialize MIDI output device.
116
117
Parameters:
118
device_id: Output device ID from get_device_info()
119
latency: Output latency in milliseconds
120
buffer_size: Output buffer size in bytes
121
"""
122
123
def close(self) -> None:
124
"""Close output device and free resources."""
125
126
def abort(self) -> None:
127
"""Abort all pending output immediately."""
128
129
def write(self, data: list) -> None:
130
"""
131
Write MIDI events to device.
132
133
Parameters:
134
data: List of MIDI events [[[status, data1, data2, data3], timestamp], ...]
135
"""
136
137
def write_short(self, status: int, data1: int = 0, data2: int = 0) -> None:
138
"""
139
Send short MIDI message immediately.
140
141
Parameters:
142
status: MIDI status byte (includes channel)
143
data1: First data byte
144
data2: Second data byte
145
"""
146
147
def write_sys_ex(self, when: int, msg: bytes | list) -> None:
148
"""
149
Send system exclusive message.
150
151
Parameters:
152
when: Timestamp when to send (milliseconds)
153
msg: System exclusive message data
154
"""
155
156
def note_on(self, note: int, velocity: int = 127, channel: int = 0) -> None:
157
"""
158
Send note on message.
159
160
Parameters:
161
note: MIDI note number (0-127)
162
velocity: Note velocity (0-127)
163
channel: MIDI channel (0-15)
164
"""
165
166
def note_off(self, note: int, velocity: int = 0, channel: int = 0) -> None:
167
"""
168
Send note off message.
169
170
Parameters:
171
note: MIDI note number (0-127)
172
velocity: Release velocity (0-127)
173
channel: MIDI channel (0-15)
174
"""
175
176
def set_instrument(self, instrument_id: int, channel: int = 0) -> None:
177
"""
178
Set instrument (program change).
179
180
Parameters:
181
instrument_id: General MIDI instrument ID (0-127)
182
channel: MIDI channel (0-15)
183
"""
184
185
def pitch_bend(self, value: int = 0, channel: int = 0) -> None:
186
"""
187
Send pitch bend message.
188
189
Parameters:
190
value: Pitch bend value (-8192 to 8191, 0 = no bend)
191
channel: MIDI channel (0-15)
192
"""
193
```
194
195
### Musical Utilities
196
197
Helper functions for musical calculations and conversions.
198
199
```python { .api }
200
def frequency_to_midi(frequency: float) -> int:
201
"""
202
Convert frequency to nearest MIDI note number.
203
204
Parameters:
205
frequency: Frequency in Hz
206
207
Returns:
208
int: MIDI note number (0-127)
209
"""
210
211
def midi_to_frequency(midi_note: int) -> float:
212
"""
213
Convert MIDI note to frequency.
214
215
Parameters:
216
midi_note: MIDI note number (0-127)
217
218
Returns:
219
float: Frequency in Hz
220
"""
221
222
def midi_to_ansi_note(midi_note: int) -> str:
223
"""
224
Convert MIDI note to musical note name.
225
226
Parameters:
227
midi_note: MIDI note number (0-127)
228
229
Returns:
230
str: Note name (e.g., "C4", "F#3", "Bb5")
231
"""
232
```
233
234
### Event Integration
235
236
Functions for integrating MIDI with pygame's event system.
237
238
```python { .api }
239
def midis2events(midis: list, device_id: int) -> list[Event]:
240
"""
241
Convert MIDI events to pygame events.
242
243
Parameters:
244
midis: List of MIDI events from Input.read()
245
device_id: Device ID that generated the events
246
247
Returns:
248
list[Event]: List of pygame MIDI events
249
"""
250
```
251
252
### Exception Handling
253
254
```python { .api }
255
class MidiException(Exception):
256
"""Exception raised by MIDI operations."""
257
```
258
259
## Usage Examples
260
261
### Basic MIDI Input
262
263
```python
264
import pygame
265
import pygame.midi
266
267
pygame.init()
268
pygame.midi.init()
269
270
# List available input devices
271
print("MIDI input devices:")
272
for i in range(pygame.midi.get_count()):
273
info = pygame.midi.get_device_info(i)
274
if info[2]: # if it's an input device
275
print(f" {i}: {info[1].decode()}")
276
277
# Open default input device
278
input_id = pygame.midi.get_default_input_id()
279
if input_id != -1:
280
midi_input = pygame.midi.Input(input_id)
281
282
print("Press MIDI keys (press Ctrl+C to quit)...")
283
try:
284
while True:
285
if midi_input.poll():
286
events = midi_input.read(10)
287
for event in events:
288
# event format: [[[status, data1, data2, data3], timestamp], ...]
289
data, timestamp = event
290
status, note, velocity = data[0], data[1], data[2]
291
292
if status == 144: # Note on
293
print(f"Note ON: {note} velocity {velocity}")
294
elif status == 128: # Note off
295
print(f"Note OFF: {note}")
296
297
except KeyboardInterrupt:
298
pass
299
300
midi_input.close()
301
302
pygame.midi.quit()
303
pygame.quit()
304
```
305
306
### Basic MIDI Output
307
308
```python
309
import pygame
310
import pygame.midi
311
import time
312
313
pygame.init()
314
pygame.midi.init()
315
316
# List available output devices
317
print("MIDI output devices:")
318
for i in range(pygame.midi.get_count()):
319
info = pygame.midi.get_device_info(i)
320
if info[3]: # if it's an output device
321
print(f" {i}: {info[1].decode()}")
322
323
# Open default output device
324
output_id = pygame.midi.get_default_output_id()
325
if output_id != -1:
326
midi_output = pygame.midi.Output(output_id)
327
328
# Set piano instrument
329
midi_output.set_instrument(0) # Acoustic Grand Piano
330
331
# Play a C major scale
332
notes = [60, 62, 64, 65, 67, 69, 71, 72] # C4 to C5
333
334
for note in notes:
335
midi_output.note_on(note, 100) # velocity 100
336
time.sleep(0.5)
337
midi_output.note_off(note)
338
time.sleep(0.1)
339
340
midi_output.close()
341
342
pygame.midi.quit()
343
pygame.quit()
344
```
345
346
### Musical Note Conversion
347
348
```python
349
import pygame.midi
350
351
pygame.midi.init()
352
353
# Convert frequencies to MIDI notes
354
frequencies = [440.0, 523.25, 659.25, 783.99] # A4, C5, E5, G5
355
print("Frequency to MIDI conversion:")
356
for freq in frequencies:
357
midi_note = pygame.midi.frequency_to_midi(freq)
358
note_name = pygame.midi.midi_to_ansi_note(midi_note)
359
print(f" {freq} Hz = MIDI {midi_note} = {note_name}")
360
361
# Convert MIDI notes to frequencies
362
print("\nMIDI to frequency conversion:")
363
for midi_note in [60, 64, 67, 72]: # C4, E4, G4, C5
364
freq = pygame.midi.midi_to_frequency(midi_note)
365
note_name = pygame.midi.midi_to_ansi_note(midi_note)
366
print(f" MIDI {midi_note} ({note_name}) = {freq:.2f} Hz")
367
368
pygame.midi.quit()
369
```
370
371
### MIDI with Pygame Events
372
373
```python
374
import pygame
375
import pygame.midi
376
377
pygame.init()
378
pygame.midi.init()
379
380
screen = pygame.display.set_mode((800, 600))
381
pygame.display.set_caption("MIDI Events Demo")
382
clock = pygame.time.Clock()
383
384
# Setup MIDI input
385
input_id = pygame.midi.get_default_input_id()
386
midi_input = None
387
if input_id != -1:
388
midi_input = pygame.midi.Input(input_id)
389
390
running = True
391
pressed_notes = set()
392
393
while running:
394
# Handle pygame events
395
for event in pygame.event.get():
396
if event.type == pygame.QUIT:
397
running = False
398
399
# Handle MIDI input
400
if midi_input and midi_input.poll():
401
midi_events = midi_input.read(10)
402
403
# Convert to pygame events
404
pygame_events = pygame.midi.midis2events(midi_events, input_id)
405
406
for event in pygame_events:
407
if event.type == pygame.MIDIIN:
408
status = event.status
409
note = event.data1
410
velocity = event.data2
411
412
if status == 144 and velocity > 0: # Note on
413
pressed_notes.add(note)
414
elif status == 128 or (status == 144 and velocity == 0): # Note off
415
pressed_notes.discard(note)
416
417
# Draw pressed notes
418
screen.fill((0, 0, 0))
419
420
y = 50
421
for note in sorted(pressed_notes):
422
note_name = pygame.midi.midi_to_ansi_note(note)
423
font = pygame.font.Font(None, 36)
424
text = font.render(f"Note: {note_name} ({note})", True, (255, 255, 255))
425
screen.blit(text, (50, y))
426
y += 40
427
428
pygame.display.flip()
429
clock.tick(60)
430
431
if midi_input:
432
midi_input.close()
433
434
pygame.midi.quit()
435
pygame.quit()
436
```
437
438
### Advanced MIDI Controller
439
440
```python
441
import pygame
442
import pygame.midi
443
import time
444
445
class MIDIController:
446
def __init__(self):
447
pygame.midi.init()
448
self.input = None
449
self.output = None
450
self.setup_devices()
451
452
def setup_devices(self):
453
# Setup input
454
input_id = pygame.midi.get_default_input_id()
455
if input_id != -1:
456
self.input = pygame.midi.Input(input_id)
457
458
# Setup output
459
output_id = pygame.midi.get_default_output_id()
460
if output_id != -1:
461
self.output = pygame.midi.Output(output_id)
462
463
def process_input(self):
464
"""Process MIDI input and return note events"""
465
events = []
466
if self.input and self.input.poll():
467
midi_events = self.input.read(10)
468
for event in midi_events:
469
data, timestamp = event
470
status, note, velocity = data[0], data[1], data[2]
471
472
if status == 144: # Note on
473
events.append(('note_on', note, velocity))
474
elif status == 128: # Note off
475
events.append(('note_off', note, velocity))
476
elif status == 176: # Control change
477
events.append(('control_change', note, velocity))
478
479
return events
480
481
def play_chord(self, notes, velocity=100, duration=1.0):
482
"""Play a chord"""
483
if not self.output:
484
return
485
486
# Note on for all notes
487
for note in notes:
488
self.output.note_on(note, velocity)
489
490
time.sleep(duration)
491
492
# Note off for all notes
493
for note in notes:
494
self.output.note_off(note)
495
496
def close(self):
497
if self.input:
498
self.input.close()
499
if self.output:
500
self.output.close()
501
pygame.midi.quit()
502
503
# Example usage
504
controller = MIDIController()
505
506
# Play some chords
507
chords = [
508
[60, 64, 67], # C major
509
[65, 69, 72], # F major
510
[67, 71, 74], # G major
511
[60, 64, 67], # C major
512
]
513
514
for chord in chords:
515
controller.play_chord(chord, velocity=80, duration=0.8)
516
time.sleep(0.2)
517
518
controller.close()
519
```
520
521
## Constants
522
523
MIDI message types and device constants:
524
525
```python { .api }
526
# Device types
527
MIDIIN: int # Input device flag
528
MIDIOUT: int # Output device flag
529
530
# Common MIDI status bytes
531
NOTE_OFF: int = 128 # 0x80
532
NOTE_ON: int = 144 # 0x90
533
CONTROL_CHANGE: int = 176 # 0xB0
534
PROGRAM_CHANGE: int = 192 # 0xC0
535
PITCH_BEND: int = 224 # 0xE0
536
537
# General MIDI Instruments (selection)
538
ACOUSTIC_GRAND_PIANO: int = 0
539
ELECTRIC_PIANO: int = 4
540
ORGAN: int = 16
541
ACOUSTIC_GUITAR: int = 24
542
VIOLIN: int = 40
543
TRUMPET: int = 56
544
FLUTE: int = 73
545
```