Frequently Asked Questions (FAQ)¶
Audience: All users Level: Beginner to Advanced
Quick answers to common questions about MIDI Markdown.
General Questions¶
What is MIDI Markdown (MMD)?¶
MML is a human-readable, text-based format for creating MIDI sequences. It uses Markdown-inspired syntax to define MIDI events with precise timing, making it ideal for:
- Live performance automation (controlling hardware devices like Neural DSP Quad Cortex, Eventide H90)
- DAW composition (creating MIDI files for import)
- MIDI scripting (programmatic MIDI generation)
- Education (learning MIDI concepts in a readable format)
Think of it as "Markdown for MIDI" - easy to write, easy to read, easy to version control.
How is MMD different from ABC notation or LilyPond?¶
| Feature | MMD | ABC Notation | LilyPond |
|---|---|---|---|
| Focus | MIDI automation | Sheet music | Professional engraving |
| Target | Hardware devices, DAWs | Folk music notation | Classical sheet music |
| MIDI Coverage | Complete (all MIDI commands) | Basic (notes, chords) | Complete but complex |
| Timing | Absolute/musical/relative | Musical only | Musical only |
| CC Automation | ✅ Full support | ❌ No | ⚠️ Limited |
| Device Control | ✅ Built-in aliases | ❌ No | ❌ No |
| Learning Curve | Gentle | Gentle | Steep |
Summary: MMD is purpose-built for MIDI performance and automation, not sheet music notation.
Can I use MMD for live performance?¶
Yes! This is MML's primary use case. Features designed for live performance:
- Real-time playback -
mmdc playsends MIDI to hardware in real-time - Device libraries - Pre-built commands for Quad Cortex, H90, Helix, etc.
- Precise timing - Sub-5ms scheduling accuracy
- Aliases - Create shortcuts for complex preset changes
- Loops and patterns - Repeat sections without duplication
Example live setup:
# Load preset on Quad Cortex during song intro
[00:00.000]
- cortex_load 1.2.3.5 # Setlist 2, Group 3, Preset 5
# Switch to different preset at chorus
[00:32.000]
- cortex_load 1.2.4.2 # Setlist 2, Group 4, Preset 2
See: Live Performance Tutorial (coming soon)
What MIDI devices are supported?¶
All MIDI devices are supported because MMD generates standard MIDI. However, some devices have device libraries with pre-built aliases:
Currently available: - Neural DSP Quad Cortex (86 aliases) - Eventide H90 (61 aliases) - Line 6 Helix Floor/LT/Rack (49 aliases) - Line 6 HX Effects (40+ aliases) - Line 6 HX Stomp (39 aliases) - Line 6 HX Stomp XL (40+ aliases)
Don't see your device? You can: 1. Use raw MIDI commands (PC, CC, SysEx) 2. Create your own device library (see Device Library Creation Guide)
Is MMD free and open source?¶
Yes! MMD is MIT licensed and completely free:
- ✅ Use commercially without restrictions
- ✅ Modify and distribute freely
- ✅ No attribution required (but appreciated!)
- ✅ Open source on GitHub
Source code: github.com/cjgdev/midi-markdown
Installation & Setup¶
How do I install MML?¶
Option 1: pipx (recommended):
Option 2: pip:
Option 3: Standalone executable: Download from GitHub Releases (no Python required).
See: Installation Guide
What operating systems are supported?¶
MML works on all major platforms:
- ✅ macOS (10.14+)
- ✅ Linux (Ubuntu, Debian, Fedora, Arch)
- ✅ Windows (10/11)
Real-time MIDI playback requires:
- macOS: IAC Driver (built-in)
- Linux: ALSA (libasound2-dev)
- Windows: Virtual MIDI port (loopMIDI recommended)
See: Troubleshooting MIDI Playback
Can I use MMD without Python installed?¶
Yes! Download the standalone executable from GitHub Releases:
- No Python installation required
- No dependencies to manage
- Self-contained binary for your platform
- Same features as pip-installed version
Syntax & Language¶
Do I have to use YAML frontmatter?¶
Yes, frontmatter is required for document metadata:
Minimum required:
- ppq - Pulses per quarter note (timing resolution)
Commonly used:
- title, author - Metadata stored in MIDI file
- tempo - Initial tempo (can also be set with tempo command)
- time_signature - Required for musical timing ([1.1.0])
- midi_format - 0 (single track), 1 (multi-track), 2 (patterns)
Can I use note names instead of MIDI numbers?¶
Yes! MMD supports standard note names:
# ✅ All of these are equivalent (MIDI note 60 = C4)
- note_on 1.60 80 1000ms
- note_on 1.C4 80 1000ms
- note_on 1.B#3 80 1000ms # Enharmonic equivalent
Supported formats:
- C4, D5, A#3, Gb2 (sharps and flats)
- Octave range: C-1 to G9 (MIDI 0-127)
- Middle C = C4 = MIDI note 60
What timing formats are supported?¶
MML supports four timing paradigms:
1. Absolute timecode (most common):
2. Musical time (bars.beats.ticks):
3. Relative delta (from previous event):
[+500ms] # 500ms after previous event
[+2b] # 2 beats after previous event
[+1.2.0] # 1 bar, 2 beats after previous
4. Simultaneous (same time as previous):
See: Timing System Guide
How do I add comments?¶
Single-line comments (like Python, Bash):
Multi-line comments (like C, JavaScript):
Can I split my MMD file into multiple files?¶
Yes! Use @import to include other files:
# Import device library
@import "devices/quad_cortex.mmd"
# Import shared song sections
@import "sections/verse.mmd"
@import "sections/chorus.mmd"
Import paths are relative to the current file.
Use cases: - Device library sharing - Song section organization - Alias library reuse
Features & Capabilities¶
Can I create loops or repeated patterns?¶
Yes! Use @loop directive:
@loop 4 # Repeat 4 times
[+1b]
- note_on 10.36 100 100ms # Kick
[+1b]
- note_on 10.38 80 100ms # Snare
@end
Features: - Timing accumulates automatically - Supports variable substitution - Nest loops up to reasonable depth
See: Loops Example
Can I automate CC parameters (filters, reverb, etc.)?¶
Yes! Use cc (control change) commands:
# Manual CC automation
[00:00.000]
- cc 1.74.0 # Filter closed
[00:02.000]
- cc 1.74.127 # Filter open
# Automated sweep
[00:00.000]
@sweep 1.74 0 127 4000ms linear # 4-second filter sweep
Common CC numbers: - 1: Modulation wheel - 7: Volume - 10: Pan - 11: Expression - 74: Brightness/Filter cutoff - 91: Reverb - 93: Chorus
See: MIDI CC Reference
Can I send SysEx messages?¶
Yes! Use sysex command:
Format: Hexadecimal bytes separated by dots.
Use cases: - Device-specific configuration - Patch dumps - Custom device control
Can I create my own aliases for my devices?¶
Yes! Aliases are a core MMD feature:
@alias my_preset {channel}.{preset_num}
- cc {channel}.0.0
- pc {channel}.{preset_num}
@end
# Use it
[00:00.000]
- my_preset 1.5
Advanced features: - Parameter types (note, percent, enum) - Default values - Nested aliases - Conditional logic
See: Alias System Guide
Does MMD support MIDI 2.0?¶
Not yet. MMD currently generates MIDI 1.0 files (the standard supported by all devices).
MIDI 2.0 support is planned for a future release.
Random Functions (Phase 6)¶
What is random() and how do I use it?¶
random() generates random numbers within a specified range for creating variation in MIDI sequences. Use it to add humanization, randomness, or generative patterns without manual variation:
The basic syntax is random(min, max) where both values are inclusive integers (0-127 for MIDI values).
Where can I use random() expressions?¶
random() works in command parameters where numeric values are expected:
- Velocity:
note_on 1.60 random(60, 100) 1b - CC values:
cc 1.74.random(0, 127) - Note numbers:
note_on 1.random(48, 72) 80 0.5b - Pitch bend:
pitch_bend 1.random(-2000, 2000) - Multiple per command:
note_on 1.random(60, 72) random(70, 90) 0.5b
See: Random Expressions Reference
Why doesn't random() work in timing/duration/define?¶
random() is designed for MIDI parameter values (velocity, CC, notes), not for timing control. Timing must be deterministic (known in advance) to:
- Schedule events correctly
- Synchronize with hardware devices
- Support live performance playback
If you need randomized timing, use multiple loops with fixed timing instead, or create separate variations of your sequence.
How do I make random patterns reproducible?¶
Use the seed parameter in random() calls to make generation reproducible:
# Same seed = same random sequence every time
@loop 8
[+1b]
- note_on 1.random(48, 72, seed=42) random(60, 100, seed=42) 0.5b
@end
Without a seed, each random() call generates a different value. With seed=N, the same seed always produces the same value for that call.
Can I combine random() with modulation features?¶
Yes! You can use random values in notes and CC values while using modulation for automation:
# Random notes with wave modulation
[00:00.000]
- note_on 1.random(48, 72) 80 2b
- cc 1.74.wave(sine, 70, freq=0.5, depth=30)
# Random CC with fixed value
[00:02.000]
- cc 1.74.random(40, 90)
However, you cannot use random values for timing or modulation parameters (frequency, depth, duration) - those must be deterministic.
See: Modulation Guide and Random Expressions
What's the difference between random() and wave() modulation?¶
random() and wave() serve different purposes:
| Feature | random() | wave() |
|---|---|---|
| Purpose | Introduce unpredictability | Create periodic oscillation |
| Pattern | Unpredictable variation | Repeating waveform (sine/square/triangle) |
| Use case | Humanization, generative patterns | Tremolo, vibrato, LFO-style modulation |
| Determinism | With seed (reproducible) | Always repeating (deterministic) |
| MIDI application | Velocity, CC values, notes | CC automation, pitch bend effects |
Example comparison:
# random() - unpredictable
- cc 1.74.random(20, 100) # Each call is different
# wave() - periodic
- cc 1.74.wave(sine, 60, freq=1.0, depth=20) # Smooth oscillation
See: Modulation Guide and Generative Music Guide
Computed Values in Aliases (Stage 4)¶
How do computed value blocks work in aliases?¶
Computed value blocks allow aliases to calculate parameter values dynamically at expansion time:
@alias scaled_cc {ch}.{param}.{input:0-100}
- cc {ch}.{param}.{computed = (${input} * 127 / 100)}
@end
# Use it - scales 0-100% input to 0-127 MIDI range
[00:00.000]
- scaled_cc 1.7.75 # 75% of volume = MIDI value 95
Computed values bridge the gap between human-readable parameters and MIDI values, enabling powerful abstractions.
What expressions can I use in computed values?¶
Computed values support arithmetic expressions with the following operations:
- Arithmetic:
+,-,*,/(standard order of operations) - Parentheses:
(expression)for grouping - Integer division: Results automatically rounded/truncated
- Parameter references:
${param_name}to reference other parameters
@alias ramped_cc {ch}.{param}.{start:0-100}.{end:0-100}.{time_ms}
- cc {ch}.{param}.{computed = (${start} * 127 / 100)}
@sweep {ch}.{param} ${computed} {computed = (${end} * 127 / 100)} {time_ms}ms linear
@end
See: Alias System Guide
Modulation Features (Stage 7)¶
When should I use curve vs wave vs envelope?¶
curve() - Smooth transitions between values:
wave() - Repeating oscillation (LFO-style):
envelope() - Amplitude shaping (ADSR-style, future):
Use curve for organic transitions, wave for oscillating effects, and envelope for complex amplitude shaping.
How do I create vibrato or tremolo effects?¶
Vibrato (pitch modulation) using pitch bend:
Tremolo (volume modulation) using CC 7 (volume):
Both use @modulate wave sine with different CC channels and frequencies. Adjust the period (0.5s, 0.3s) and PPQ for different rates.
Can I layer multiple modulations?¶
Yes! Stack multiple @modulate directives for complex effects:
[00:00.000]
- note_on 1.60 80 3000ms
# Layer 1: Vibrato (pitch bend)
- @modulate wave sine 1.pb 0.4s 6
# Layer 2: Tremolo (volume)
- @modulate wave sine 1.7.0 1.2s 2
# Layer 3: Filter sweep (automation)
- @sweep 1.74 30 100 3000ms linear
Each modulation runs independently in parallel, creating complex, expressive sounds. Start simple and add layers gradually to avoid overwhelming the effect.
Workflow & Integration¶
Can I version control my MMD files?¶
Yes! This is a major advantage of text-based formats.
Benefits: - Track changes over time - Collaborate with others - Branching for variations - Code review for complex sequences
Can I convert existing MIDI files to MML?¶
Not yet. MIDI → MMD conversion is planned for a future release.
Workaround: Use mmdc inspect to view MIDI file contents and manually recreate in MML:
Can I use MMD with my DAW?¶
Yes! Compile to MIDI and import:
Then drag output.mid into your DAW:
- Ableton Live
- FL Studio
- Logic Pro
- Pro Tools
- Cubase
- Reaper
- Any MIDI-compatible DAW
Can I trigger MMD playback from another application?¶
Yes! Use the CLI in scripts:
# Shell script
mmdc play setlist/song1.mmd --port "IAC Driver"
# Python script
import subprocess
subprocess.run(["mmdc", "play", "song.mmd", "--port", "IAC Driver"])
Use cases: - Automated setlist playback - MIDI sequencer integration - Live performance scripting
Performance & Optimization¶
How fast is MMD compilation?¶
Very fast! Performance benchmarks:
- Small files (<100 events): <50ms
- Medium files (100-500 events): <200ms
- Large files (1000+ events): <1s
Tips for faster compilation:
- Use mmdc check for syntax-only validation (faster)
- Break large files into sections with @import
- Use --dry-run flag to skip file writing
How accurate is real-time MIDI playback?¶
Sub-5ms scheduling accuracy with hybrid sleep/busy-wait algorithm.
Measured latency: <2ms average on modern hardware.
Good enough for: - Live performance - Tight MIDI synchronization - Multi-device setups
Not suitable for: - Audio synthesis (use DAW for sub-millisecond precision)
Can I compile very large files (10,000+ events)?¶
Yes, but compile time increases linearly with event count.
Best practices for large files:
- Use @loop to reduce source file size
- Split into multiple files with @import
- Consider multi-track format (midi_format: 1)
Troubleshooting¶
Why am I getting parse errors?¶
Common causes:
- Invalid syntax - Check command spelling
- Out-of-range values - MIDI values must be 0-127
- Missing frontmatter - File must start with
--- - Timing errors - Events must be chronologically ordered
Why is there no sound when I play?¶
Checklist:
-
MIDI port configured?
-
Port selected?
-
Instruments loaded? (in DAW)
-
Assign synths to MIDI channels
-
Permissions? (Linux)
See: MIDI Playback Troubleshooting
Why do my notes sound robotic?¶
Problem: Using exact velocities and no expression.
Solution: Add variation and expression:
# ❌ Robotic
- note_on 1.60 80 1000ms
- note_on 1.62 80 1000ms
- note_on 1.64 80 1000ms
# ✅ Expressive
- note_on 1.60 75 1000ms # Vary velocity
- note_on 1.62 82 1000ms
- note_on 1.64 78 1000ms
# Add modulation
[00:01.000]
- cc 1.1.30 # Modulation wheel
# Add pitch bend
[00:02.000]
- pb 1.100 # Slight pitch bend
Humanization tips: - Vary velocity (70-85 instead of constant 80) - Add subtle CC automation (modulation, expression) - Use pitch bend for expression - Vary note durations slightly
Getting Help¶
Where can I find more examples?¶
Check the examples/ directory in the project:
00_hello_world.mmd- Simplest possible file01-08- Feature-specific examples09_comprehensive_song.mmd- Complete song demonstration10-13- Advanced features (loops, sweeps, musical timing, imports)alias_showcase.mmd- Alias system demonstrationlive_performance_aliases.mmd- Real-world live performance
Where can I get help or report bugs?¶
GitHub Issues: github.com/cjgdev/midi-markdown/issues
Before posting:
1. Check Troubleshooting Guide
2. Search existing issues
3. Include MMD file and error message
4. Specify version: mmdc version
How can I contribute?¶
Contributions welcome!
- 🐛 Report bugs
- 💡 Suggest features
- 📝 Improve documentation
- 🎸 Create device libraries
- 💻 Submit pull requests
See: Contributing Guide
Is there a community forum or Discord?¶
Not yet. For now, use GitHub Issues for questions and discussions.
A Discord server may be created if there's sufficient community interest.
See Also¶
- Quickstart Guide - 5-minute introduction
- First Song Tutorial - Step-by-step song creation
- Troubleshooting Guide - Common issues and solutions
- CLI Reference - All commands documented
- MML Syntax Guide - Complete syntax reference
Still have questions? Open an issue on GitHub or check the documentation.