or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-features.mdaudio-system.mdcore-system.mdcursor-management.mddisplay-graphics.mdevent-handling.mdindex.mdinput-systems.mdmath-operations.mdmidi-support.mdsprites-game-objects.mdtyping-support.md

midi-support.mddocs/

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

```