Command: play¶
Audience: Users Level: Beginner to Advanced
Play MMD files in real-time to MIDI output devices with interactive terminal UI.
Synopsis¶
Description¶
The play command provides real-time MIDI playback of compiled MMD files directly to hardware or software MIDI devices. Unlike compile which generates MIDI files, play sends MIDI messages in real-time with sub-5ms timing precision.
Key Features: - Interactive Terminal UI (TUI) with 30 FPS refresh - Real-time event visualization and progress tracking - Keyboard controls (Space/Arrow keys/Q/R) for playback control - Multiple seeking modes: time-based (5s), beat-based (1 beat), and bar-based (1 bar) - Musical navigation using Shift/Ctrl + arrow keys - Support for virtual MIDI ports (IAC, loopMIDI, ALSA) - Tempo tracking and dynamic tempo changes - Event scheduling with microsecond precision
What play does:
- Parse - Parse MMD file to AST
- Compile - Convert to Intermediate Representation (IR)
- Connect - Open MIDI output port
- Schedule - Queue events with precise timing
- Play - Send MIDI messages in real-time
- Display - Show progress and live event feed in TUI
Typical use cases: - Live performance automation (preset changes, CC automation) - Testing MIDI sequences before recording - Controlling hardware synthesizers and effects processors - Syncing with DAWs via virtual MIDI ports
Options¶
Port Selection¶
--port, -p PORT¶
MIDI output port name or index (required for playback).
# By port name (recommended)
mmdc play song.mmd --port "IAC Driver Bus 1"
# By port index
mmdc play song.mmd --port 0
# Shorter alias
mml play song.mmd -p "Network Session 1"
Finding port names: Use --list-ports to see available ports.
--list-ports¶
List available MIDI output ports and exit.
# Show all available MIDI ports
mmdc play --list-ports
# Example output:
# Available MIDI output ports:
# 0: IAC Driver Bus 1
# 1: Network Session 1
# 2: QuadCortex MIDI 1
Note: This command doesn't require an input file.
Display Options¶
--no-ui¶
Disable interactive TUI and use simple progress display.
When to use: - Running in non-interactive environment (CI/scripts) - Terminal doesn't support TUI features - Piping output to logs - Automated playback scenarios
Output difference: - With TUI (default): Full-screen interactive display with progress bar, event list, and keyboard controls - No UI: Simple spinner with "Playing..." message
Debugging¶
--debug¶
Show full error tracebacks instead of formatted errors.
Useful for: Bug reports, troubleshooting MIDI issues, development.
Interactive TUI¶
The default playback mode features a rich Terminal UI with:
Display Components¶
Header:
╭──────────────────────────────────────────────╮
│ 🎵 MMD Player - Live MIDI Playback │
│ File: performance.mmd │
│ Port: IAC Driver Bus 1 │
│ Title: Live Performance Automation │
╰──────────────────────────────────────────────╯
Progress Bar:
Progress: ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 45%
Time: 0:23 / 0:50 (27s remaining)
Tempo: 120 BPM | Events: 104 total
Live Event Feed (last 20 events):
Recent Events:
0:23.245 Note On Ch1 C4 (60) vel=80
0:23.250 CC Ch1 #7 (Volume) = 100
0:23.500 Note Off Ch1 C4 (60)
0:24.000 PC Ch1 Preset 5
Footer:
Controls: [Space] Play/Pause | [← →] ±5s | [Shift+← →] ±1 beat | [Ctrl+← →] ±1 bar | [Q] Quit
Status: ▶ Playing
Keyboard Controls¶
| Key | Action | Description |
|---|---|---|
| Space | Play/Pause | Toggle playback (preserves position) |
| ← → | Seek Time | Jump backward/forward 5 seconds |
| Shift+← → | Seek Beat | Jump backward/forward 1 beat |
| Ctrl+← → | Seek Bar | Jump backward/forward 1 bar |
| Q | Quit | Stop playback and exit |
| R | Restart | Jump back to beginning (future) |
| Ctrl+C | Cancel | Same as Q (graceful exit) |
| Ctrl+D | Exit | Immediate exit |
Playback States:
- ▶ Playing - Active playback
- ⏸ Paused - Paused (resume with Space)
- ⏹ Stopped - Playback complete or quit
Platform-Specific Setup¶
macOS - IAC Driver¶
IAC Driver provides virtual MIDI ports for routing between applications.
Setup: 1. Open Audio MIDI Setup (Applications > Utilities) 2. Window > Show MIDI Studio 3. Double-click IAC Driver icon 4. Check "Device is online" 5. Add buses as needed (e.g., "Bus 1", "Bus 2")
Usage:
# List IAC ports
mmdc play --list-ports
# Output: 0: IAC Driver Bus 1
# Play to IAC
mmdc play song.mmd --port "IAC Driver Bus 1"
Connecting to DAW: - In your DAW (Logic, Ableton, etc.), select "IAC Driver Bus 1" as MIDI input - MMD player sends MIDI → IAC → DAW receives
Linux - ALSA¶
ALSA (Advanced Linux Sound Architecture) provides MIDI support on Linux.
Setup:
# Install ALSA utilities
sudo apt-get install alsa-utils
# List MIDI ports
aconnect -l
# Create virtual port (optional)
sudo modprobe snd-virmidi
Usage:
Virtual MIDI (virmidi):
# Load virmidi kernel module (creates 4 virtual ports)
sudo modprobe snd-virmidi
# Make persistent (add to /etc/modules)
echo "snd-virmidi" | sudo tee -a /etc/modules
Windows - loopMIDI¶
loopMIDI by Tobias Erichsen provides virtual MIDI ports on Windows.
Setup: 1. Download loopMIDI from tobias-erichsen.de 2. Install and run loopMIDI 3. Click + to add virtual port (e.g., "loopMIDI Port 1") 4. Leave loopMIDI running in background
Usage:
# List ports
mmdc play --list-ports
# Output: 0: loopMIDI Port 1
# Play to loopMIDI
mmdc play song.mmd --port "loopMIDI Port 1"
Connecting to DAW: - In DAW, select "loopMIDI Port 1" as MIDI input - MMD player sends MIDI → loopMIDI → DAW receives
Examples¶
List Available Ports¶
Example output:
Available MIDI output ports:
0: IAC Driver Bus 1
1: Network Session 1
2: QuadCortex MIDI 1
3: H90 MIDI 1
Play to Virtual Port (macOS)¶
# Play to IAC Driver (default virtual port on macOS)
mmdc play performance.mmd --port "IAC Driver Bus 1"
Use case: Route MIDI to DAW (Logic, Ableton) for recording.
Play to Hardware Device¶
Use case: Live performance automation, preset changes, CC automation.
Play by Port Index¶
When to use: Scripting, when port names are long or contain special characters.
Simple Mode (No UI)¶
Output:
Compiling: song.mmd
Opening MIDI port: IAC Driver Bus 1
Duration: 50.00s (104 events)
⠋ Playing...
✓ Done
Use case: Automated playback, scripts, CI/CD testing.
Interactive Playback with Controls¶
During playback: - Press Space to pause/resume - Press Q to quit early - Press R to restart from beginning
Multiple Devices¶
# First, identify your devices
mmdc play --list-ports
# Play to specific device by name
mmdc play quad_cortex_presets.mmd --port "QuadCortex MIDI 1"
mmdc play h90_automation.mmd --port "H90 MIDI 1"
Testing Before Performance¶
# Test automation sequence before live show
mmdc play setlist.mmd --port "IAC Driver Bus 1"
# Watch event feed in TUI to verify timing
# Press Q to stop early if issues found
# Edit MMD file, recompile, test again
Performance¶
Timing Precision¶
Scheduler: Sub-5ms event timing precision - Hybrid sleep/busy-wait algorithm - Adaptive scheduling for system load - No drift over long sequences
TUI Refresh: 30 FPS (33ms refresh interval) - Non-blocking display updates - Thread-safe state management - No impact on MIDI timing
Resource Usage¶
Memory: ~10-20 MB for typical sequences CPU: <5% on modern hardware Latency: <5ms end-to-end (parse → MIDI out)
Tested with: - 1000+ event sequences - 30-minute performances - Dynamic tempo changes - Concurrent CC automation
Exit Codes¶
| Code | Meaning |
|---|---|
| 0 | Playback completed successfully |
| 1 | Compilation error (invalid MML) |
| 2 | MIDI port not found or unavailable |
| 3 | File not found |
| 4 | Playback interrupted (Ctrl+C, Q) |
Script usage:
if mmdc play song.mmd --port 0; then
echo "Playback successful"
else
echo "Playback failed with code $?"
fi
Common Issues¶
"MIDI port not found"¶
Problem: Specified port doesn't exist or isn't available.
Solution:
# List all available ports
mmdc play --list-ports
# Use exact port name from list
mmdc play song.mmd --port "IAC Driver Bus 1"
Common causes: - Port name misspelled (case-sensitive) - Virtual MIDI driver not running (loopMIDI, IAC) - Hardware device not connected or powered off - Port in use by another application
"No TTY detected, falling back to simple mode"¶
Problem: TUI can't be displayed in current environment.
Explanation: This happens when running in non-interactive environments (scripts, CI/CD).
Solution: This is expected behavior, playback continues in simple mode.
Manual fallback:
TUI not refreshing properly¶
Problem: Display freezes or doesn't update smoothly.
Possible causes: - Terminal doesn't support ANSI escape codes - Terminal too small (minimum 80x24 recommended) - Running over SSH without proper terminal emulation
Solutions:
# Use simple mode
mmdc play song.mmd --port 0 --no-ui
# Or use modern terminal emulator (iTerm2, Windows Terminal, Alacritty)
Events not playing at correct time¶
Problem: MIDI events arrive too early or too late.
Diagnosis:
# Check if issue is timing or compilation
mmdc inspect song.mmd --format table
# Verify event times are correct
Common causes: - System under heavy load (close other apps) - Incorrect tempo in frontmatter - Musical timing without time signature - PPQ too low (try higher resolution)
Solutions:
# Increase PPQ for better precision
mmdc compile song.mmd --ppq 960
# Verify frontmatter
---
tempo: 120
time_signature: "4/4"
ppq: 480
---
Hardware device not responding¶
Problem: MIDI port opens but device doesn't respond.
Checklist: 1. Verify device is powered on 2. Check MIDI cable connections (if hardware) 3. Confirm device is in MIDI receive mode 4. Verify channel numbers match (1-16) 5. Test with known-working MIDI file
Debugging:
# Inspect compiled events
mmdc inspect song.mmd --format table
# Verify channels, note numbers, CC values
# Compare with device manual
"Permission denied" error¶
Problem: Can't access MIDI port due to permissions (Linux).
Solution (Linux):
# Add user to 'audio' group
sudo usermod -a -G audio $USER
# Log out and back in for changes to take effect
# Verify group membership
groups
Tips & Tricks¶
Quick Port Selection¶
# Create shell alias for your main port
alias mml-play='mmdc play --port "IAC Driver Bus 1"'
# Usage
mml-play song.mmd
Pause, Resume, and Seek¶
# Start playback
mmdc play setlist.mmd --port 0
# During playback:
# - Press Space to pause at current position
# - Press ← → to seek backward/forward 5 seconds (time-based)
# - Press Shift+← → to seek backward/forward 1 beat (musical)
# - Press Ctrl+← → to seek backward/forward 1 bar (musical)
# - Make notes of issues
# - Press Space to resume from same position
# - Press Q to quit
Seeking modes explained: - Time-based (Arrow keys): Fixed 5-second intervals, independent of tempo/time signature - Beat-based (Shift+Arrow): Jump by 1 beat (respects tempo and PPQ) - Bar-based (Ctrl+Arrow): Jump by 1 bar (respects time signature, e.g., 4 beats in 4/4)
Use cases for seeking: - Time seeking: Quick navigation through long performances - Beat seeking: Fine-tune position for precise musical timing - Bar seeking: Jump between song sections (verse, chorus, bridge) - Combined: Use bar seeking for large jumps, beat seeking for fine adjustments
Musical Navigation (Beat and Bar Seeking)¶
Musical seeking respects your song's time signature and tempo, making it perfect for navigating through structured compositions:
# Example: Song in 4/4 time at 120 BPM
mmdc play song.mmd --port 0
# During playback:
# - Ctrl+→ jumps forward 4 beats (1 bar in 4/4)
# - Shift+→ jumps forward 1 beat (0.5 seconds at 120 BPM)
# - Ctrl+← jumps backward 1 bar
# - Shift+← jumps backward 1 beat
Why musical seeking matters: - Tempo-aware: Beat/bar length adjusts with tempo changes - Time signature aware: Bar seeking respects ¾, 4/4, 5/4, etc. - Musically aligned: Land on downbeats, not arbitrary time points - Workflow: Jump to bar 8 (chorus), then fine-tune with beat seeking
Examples by time signature:
# 4/4 time (common time)
time_signature: "4/4"
# 1 bar = 4 beats, 1 beat = quarter note
# 3/4 time (waltz)
time_signature: "3/4"
# 1 bar = 3 beats, 1 beat = quarter note
# 6/8 time (compound meter)
time_signature: "6/8"
# 1 bar = 6 beats, 1 beat = eighth note (compound feel: 2 dotted quarters)
# 5/4 time (progressive rock)
time_signature: "5/4"
# 1 bar = 5 beats, 1 beat = quarter note
Practical workflow:
# 1. Start playback
mmdc play setlist.mmd --port 0
# 2. Jump to chorus (e.g., bar 16) using Ctrl+→ repeatedly
# (Or set up markers in your MMD file)
# 3. Fine-tune position with Shift+← → to land exactly on the downbeat
# 4. Test timing, adjust if needed
# 5. Press Q to quit when done
Testing Specific Sections¶
# Edit MMD to comment out unwanted sections
# [00:00.000]
# - note_on 1.60 80 1b
# ...
# #[01:00.000] # Comment out everything after 1 minute
# #- pc 1.10
# Play reduced file
mmdc play test_section.mmd --port 0
DAW Integration Workflow¶
# 1. Setup virtual MIDI port (IAC/loopMIDI)
# 2. Configure DAW to receive from virtual port
# 3. Arm MIDI track in DAW for recording
# 4. Play MMD file
mmdc play automation.mmd --port "IAC Driver Bus 1"
# 5. DAW records incoming MIDI
# 6. Edit recorded MIDI in DAW if needed
Live Performance Checklist¶
# Before show:
# 1. Test all devices
mmdc play --list-ports
# 2. Validate MMD files
mmdc validate setlist_*.mmd
# 3. Test playback
mmdc play setlist_01.mmd --port "QuadCortex MIDI 1"
# 4. Create backup copy of MMD files
# 5. Have device libraries ready
ls devices/*.mmd
# During show:
# - Use TUI to monitor playback
# - Press Q to skip songs if needed
# - Have backup MIDI files ready
Event Debugging¶
# 1. Inspect events before playing
mmdc inspect song.mmd --format table --limit 50
# 2. Verify timing and values
# 3. Play and watch TUI event feed
mmdc play song.mmd --port 0
# 4. Compare TUI feed with inspect output
# 5. Fix any discrepancies in MMD source
Advanced Usage¶
Scripted Playback¶
#!/bin/bash
# play_setlist.sh - Automated setlist playback
PORT="IAC Driver Bus 1"
for song in setlist_*.mmd; do
echo "Now playing: $song"
mmdc play "$song" --port "$PORT" --no-ui
sleep 2 # Pause between songs
done
Multi-Device Setup¶
# Play different files to different devices
mmdc play guitar.mmd --port "QuadCortex MIDI 1" &
PID1=$!
mmdc play keys.mmd --port "Synthesizer MIDI 1" &
PID2=$!
# Wait for both to complete
wait $PID1
wait $PID2
Tempo Control¶
---
title: "Dynamic Tempo Performance"
tempo: 120 # Starting tempo
---
[00:00.000]
- tempo 140 # Speed up at verse
[01:00.000]
- tempo 100 # Slow down for chorus
[02:00.000]
- tempo 120 # Back to normal
# Play with tempo changes
mmdc play dynamic_tempo.mmd --port 0
# TUI will show current tempo in real-time
See Also¶
- compile command - Generate MIDI files for DAW import
- inspect command - Debug event timing and values
- validate command - Verify MMD before playback
- Real-time Playback Guide - Detailed playback documentation
- Troubleshooting Guide - MIDI port and playback issues
- First Song Tutorial - Learn MMD basics
Next Steps: Try inspecting events or learn about device libraries.