CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-pygame-ce

Python Game Development library providing comprehensive multimedia functionality for creating games and interactive applications.

Pending
Overview
Eval results
Files

midi-support.mddocs/

MIDI Support

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.

Capabilities

MIDI System Management

Core functions for initializing and managing the MIDI subsystem.

def init() -> None:
    """Initialize MIDI module."""

def quit() -> None:
    """Quit MIDI module."""

def get_init() -> bool:
    """
    Check if MIDI module is initialized.
    
    Returns:
        bool: True if MIDI module is initialized
    """

def get_count() -> int:
    """
    Get number of MIDI devices.
    
    Returns:
        int: Total number of MIDI devices (input + output)
    """

def get_default_input_id() -> int:
    """
    Get default input device ID.
    
    Returns:
        int: Default input device ID, or -1 if none available
    """

def get_default_output_id() -> int:
    """
    Get default output device ID.
    
    Returns:
        int: Default output device ID, or -1 if none available
    """

def get_device_info(device_id: int) -> tuple[str, str, int, int, int] | None:
    """
    Get device information.
    
    Parameters:
        device_id: Device ID to query
    
    Returns:
        tuple[str, str, int, int, int] | None: (interface, name, input, output, opened) or None if invalid
    """

def time() -> int:
    """
    Get current MIDI time.
    
    Returns:
        int: Current time in milliseconds
    """

MIDI Input

Real-time MIDI input for processing musical keyboard, controller, and sequencer data.

class Input:
    def __init__(self, device_id: int, buffer_size: int = 4096):
        """
        Initialize MIDI input device.
        
        Parameters:
            device_id: Input device ID from get_device_info()
            buffer_size: Input buffer size in bytes
        """
    
    def close(self) -> None:
        """Close input device and free resources."""
    
    def read(self, num_events: int) -> list[list]:
        """
        Read MIDI events from device.
        
        Parameters:
            num_events: Maximum number of events to read
        
        Returns:
            list[list]: List of MIDI events, each event is [[[status, data1, data2, data3], timestamp], ...]
        """
    
    def poll(self) -> bool:
        """
        Check if events are available to read.
        
        Returns:
            bool: True if events are pending in buffer
        """

MIDI Output

Real-time MIDI output for controlling synthesizers, sound modules, and external MIDI devices.

class Output:
    def __init__(self, device_id: int, latency: int = 0, buffer_size: int = 256):
        """
        Initialize MIDI output device.
        
        Parameters:
            device_id: Output device ID from get_device_info()
            latency: Output latency in milliseconds
            buffer_size: Output buffer size in bytes
        """
    
    def close(self) -> None:
        """Close output device and free resources."""
    
    def abort(self) -> None:
        """Abort all pending output immediately."""
    
    def write(self, data: list) -> None:
        """
        Write MIDI events to device.
        
        Parameters:
            data: List of MIDI events [[[status, data1, data2, data3], timestamp], ...]
        """
    
    def write_short(self, status: int, data1: int = 0, data2: int = 0) -> None:
        """
        Send short MIDI message immediately.
        
        Parameters:
            status: MIDI status byte (includes channel)
            data1: First data byte
            data2: Second data byte
        """
    
    def write_sys_ex(self, when: int, msg: bytes | list) -> None:
        """
        Send system exclusive message.
        
        Parameters:
            when: Timestamp when to send (milliseconds)
            msg: System exclusive message data
        """
    
    def note_on(self, note: int, velocity: int = 127, channel: int = 0) -> None:  
        """
        Send note on message.
        
        Parameters:
            note: MIDI note number (0-127)
            velocity: Note velocity (0-127)
            channel: MIDI channel (0-15)
        """
    
    def note_off(self, note: int, velocity: int = 0, channel: int = 0) -> None:
        """
        Send note off message.
        
        Parameters:
            note: MIDI note number (0-127)
            velocity: Release velocity (0-127)
            channel: MIDI channel (0-15)
        """
    
    def set_instrument(self, instrument_id: int, channel: int = 0) -> None:
        """
        Set instrument (program change).
        
        Parameters:
            instrument_id: General MIDI instrument ID (0-127)
            channel: MIDI channel (0-15)
        """
    
    def pitch_bend(self, value: int = 0, channel: int = 0) -> None:
        """
        Send pitch bend message.
        
        Parameters:
            value: Pitch bend value (-8192 to 8191, 0 = no bend)
            channel: MIDI channel (0-15)
        """

Musical Utilities

Helper functions for musical calculations and conversions.

def frequency_to_midi(frequency: float) -> int:
    """
    Convert frequency to nearest MIDI note number.
    
    Parameters:
        frequency: Frequency in Hz
    
    Returns:
        int: MIDI note number (0-127)
    """

def midi_to_frequency(midi_note: int) -> float:
    """
    Convert MIDI note to frequency.
    
    Parameters:
        midi_note: MIDI note number (0-127)
    
    Returns:
        float: Frequency in Hz
    """

def midi_to_ansi_note(midi_note: int) -> str:
    """
    Convert MIDI note to musical note name.
    
    Parameters:
        midi_note: MIDI note number (0-127)
    
    Returns:
        str: Note name (e.g., "C4", "F#3", "Bb5")
    """

Event Integration

Functions for integrating MIDI with pygame's event system.

def midis2events(midis: list, device_id: int) -> list[Event]:
    """
    Convert MIDI events to pygame events.
    
    Parameters:
        midis: List of MIDI events from Input.read()
        device_id: Device ID that generated the events
    
    Returns:
        list[Event]: List of pygame MIDI events
    """

Exception Handling

class MidiException(Exception):
    """Exception raised by MIDI operations."""

Usage Examples

Basic MIDI Input

import pygame
import pygame.midi

pygame.init()
pygame.midi.init()

# List available input devices
print("MIDI input devices:")
for i in range(pygame.midi.get_count()):
    info = pygame.midi.get_device_info(i)
    if info[2]:  # if it's an input device
        print(f"  {i}: {info[1].decode()}")

# Open default input device
input_id = pygame.midi.get_default_input_id()
if input_id != -1:
    midi_input = pygame.midi.Input(input_id)
    
    print("Press MIDI keys (press Ctrl+C to quit)...")
    try:
        while True:
            if midi_input.poll():
                events = midi_input.read(10)
                for event in events:
                    # event format: [[[status, data1, data2, data3], timestamp], ...]
                    data, timestamp = event
                    status, note, velocity = data[0], data[1], data[2]
                    
                    if status == 144:  # Note on
                        print(f"Note ON:  {note} velocity {velocity}")
                    elif status == 128:  # Note off
                        print(f"Note OFF: {note}")
    
    except KeyboardInterrupt:
        pass
    
    midi_input.close()

pygame.midi.quit()
pygame.quit()

Basic MIDI Output

import pygame
import pygame.midi
import time

pygame.init()
pygame.midi.init()

# List available output devices
print("MIDI output devices:")
for i in range(pygame.midi.get_count()):
    info = pygame.midi.get_device_info(i)
    if info[3]:  # if it's an output device
        print(f"  {i}: {info[1].decode()}")

# Open default output device
output_id = pygame.midi.get_default_output_id()
if output_id != -1:
    midi_output = pygame.midi.Output(output_id)
    
    # Set piano instrument
    midi_output.set_instrument(0)  # Acoustic Grand Piano
    
    # Play a C major scale
    notes = [60, 62, 64, 65, 67, 69, 71, 72]  # C4 to C5
    
    for note in notes:
        midi_output.note_on(note, 100)  # velocity 100
        time.sleep(0.5)
        midi_output.note_off(note)
        time.sleep(0.1)
    
    midi_output.close()

pygame.midi.quit()
pygame.quit()

Musical Note Conversion

import pygame.midi

pygame.midi.init()

# Convert frequencies to MIDI notes
frequencies = [440.0, 523.25, 659.25, 783.99]  # A4, C5, E5, G5
print("Frequency to MIDI conversion:")
for freq in frequencies:
    midi_note = pygame.midi.frequency_to_midi(freq)
    note_name = pygame.midi.midi_to_ansi_note(midi_note)
    print(f"  {freq} Hz = MIDI {midi_note} = {note_name}")

# Convert MIDI notes to frequencies
print("\nMIDI to frequency conversion:")
for midi_note in [60, 64, 67, 72]:  # C4, E4, G4, C5
    freq = pygame.midi.midi_to_frequency(midi_note)
    note_name = pygame.midi.midi_to_ansi_note(midi_note)
    print(f"  MIDI {midi_note} ({note_name}) = {freq:.2f} Hz")

pygame.midi.quit()

MIDI with Pygame Events

import pygame
import pygame.midi

pygame.init()
pygame.midi.init()

screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("MIDI Events Demo")
clock = pygame.time.Clock()

# Setup MIDI input
input_id = pygame.midi.get_default_input_id()
midi_input = None
if input_id != -1:
    midi_input = pygame.midi.Input(input_id)

running = True
pressed_notes = set()

while running:
    # Handle pygame events
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
    
    # Handle MIDI input
    if midi_input and midi_input.poll():
        midi_events = midi_input.read(10)
        
        # Convert to pygame events
        pygame_events = pygame.midi.midis2events(midi_events, input_id)
        
        for event in pygame_events:
            if event.type == pygame.MIDIIN:
                status = event.status
                note = event.data1
                velocity = event.data2
                
                if status == 144 and velocity > 0:  # Note on
                    pressed_notes.add(note)
                elif status == 128 or (status == 144 and velocity == 0):  # Note off
                    pressed_notes.discard(note)
    
    # Draw pressed notes
    screen.fill((0, 0, 0))
    
    y = 50
    for note in sorted(pressed_notes):
        note_name = pygame.midi.midi_to_ansi_note(note)
        font = pygame.font.Font(None, 36)
        text = font.render(f"Note: {note_name} ({note})", True, (255, 255, 255))
        screen.blit(text, (50, y))
        y += 40
    
    pygame.display.flip()
    clock.tick(60)

if midi_input:
    midi_input.close()

pygame.midi.quit()
pygame.quit()

Advanced MIDI Controller

import pygame
import pygame.midi
import time

class MIDIController:
    def __init__(self):
        pygame.midi.init()
        self.input = None
        self.output = None
        self.setup_devices()
    
    def setup_devices(self):
        # Setup input
        input_id = pygame.midi.get_default_input_id()
        if input_id != -1:
            self.input = pygame.midi.Input(input_id)
        
        # Setup output
        output_id = pygame.midi.get_default_output_id()
        if output_id != -1:
            self.output = pygame.midi.Output(output_id)
    
    def process_input(self):
        """Process MIDI input and return note events"""
        events = []
        if self.input and self.input.poll():
            midi_events = self.input.read(10)
            for event in midi_events:
                data, timestamp = event
                status, note, velocity = data[0], data[1], data[2]
                
                if status == 144:  # Note on
                    events.append(('note_on', note, velocity))
                elif status == 128:  # Note off
                    events.append(('note_off', note, velocity))
                elif status == 176:  # Control change
                    events.append(('control_change', note, velocity))
        
        return events
    
    def play_chord(self, notes, velocity=100, duration=1.0):
        """Play a chord"""
        if not self.output:
            return
        
        # Note on for all notes
        for note in notes:
            self.output.note_on(note, velocity)
        
        time.sleep(duration)
        
        # Note off for all notes
        for note in notes:
            self.output.note_off(note)
    
    def close(self):
        if self.input:
            self.input.close()
        if self.output:
            self.output.close()
        pygame.midi.quit()

# Example usage
controller = MIDIController()

# Play some chords
chords = [
    [60, 64, 67],  # C major
    [65, 69, 72],  # F major
    [67, 71, 74],  # G major
    [60, 64, 67],  # C major
]

for chord in chords:
    controller.play_chord(chord, velocity=80, duration=0.8)
    time.sleep(0.2)

controller.close()

Constants

MIDI message types and device constants:

# Device types
MIDIIN: int   # Input device flag
MIDIOUT: int  # Output device flag

# Common MIDI status bytes
NOTE_OFF: int = 128      # 0x80
NOTE_ON: int = 144       # 0x90
CONTROL_CHANGE: int = 176 # 0xB0
PROGRAM_CHANGE: int = 192 # 0xC0
PITCH_BEND: int = 224    # 0xE0

# General MIDI Instruments (selection)
ACOUSTIC_GRAND_PIANO: int = 0
ELECTRIC_PIANO: int = 4
ORGAN: int = 16
ACOUSTIC_GUITAR: int = 24
VIOLIN: int = 40
TRUMPET: int = 56
FLUTE: int = 73

Install with Tessl CLI

npx tessl i tessl/pypi-pygame-ce

docs

advanced-features.md

audio-system.md

core-system.md

cursor-management.md

display-graphics.md

event-handling.md

index.md

input-systems.md

math-operations.md

midi-support.md

sprites-game-objects.md

typing-support.md

tile.json