MMD Syntax Reference¶
Audience: Users Level: Beginner to Intermediate
Complete reference for MIDI Markdown (MMD) syntax. This document covers all syntax elements, timing formats, MIDI commands, and directives.
Table of Contents¶
- Document Structure
- Frontmatter
- Timing Markers
- MIDI Commands
- Directives
- Comments
- Advanced Features
- Random Expressions
- Examples
- See Also
Document Structure¶
An MMD file consists of four main parts:
- Frontmatter (YAML) - Document properties and metadata
- Imports - Device libraries and shared definitions
- Definitions - Variables, aliases, and constants
- Events - Timed MIDI commands
Basic Example¶
---
title: "My First Song"
author: "Your Name"
tempo: 120
ppq: 480
---
# This is a comment
@define MAIN_CHANNEL 1
[00:00.000]
- tempo 120
- note_on 1.C4 100 1b
Frontmatter¶
Frontmatter uses YAML syntax and must appear at the beginning of the file, enclosed in --- delimiters.
Required Properties¶
None! All frontmatter properties are optional with sensible defaults.
Common Properties¶
---
title: "Song Title" # Document title
author: "Your Name" # Author name
description: "What this does" # Brief description
tempo: 120 # Default tempo (BPM)
time_signature: [4, 4] # Time signature [numerator, denominator]
ppq: 480 # Pulses per quarter note (resolution)
midi_format: 1 # MIDI file format (0, 1, or 2)
default_channel: 1 # Default MIDI channel (1-16)
default_velocity: 100 # Default note velocity (0-127)
---
MIDI Format Types¶
- Format 0: Single track (all channels combined)
- Format 1: Multi-track synchronous (most common)
- Format 2: Multi-track asynchronous (rarely used)
Device Configuration¶
Full Example¶
---
title: "Live Performance Automation"
author: "Guitarist"
description: "Preset changes and effects automation"
tempo: 128
time_signature: [4, 4]
ppq: 480
midi_format: 1
default_channel: 1
devices:
- cortex: channel 1
- h90: channel 2
---
Timing Markers¶
MML supports four timing paradigms that can be mixed freely in the same document.
1. Absolute Timecode¶
Format: [mm:ss.milliseconds]
Specifies exact time from the start of the song.
[00:00.000] # Start (0 seconds)
[00:01.500] # 1.5 seconds
[01:23.250] # 1 minute, 23.25 seconds
[00:00.050] # 50 milliseconds
Use cases: - Syncing to existing audio/video - Precise timing requirements - Non-musical automation
2. Musical Time¶
Format: [bars.beats.ticks]
Specifies time in musical units relative to tempo and time signature.
[1.1.000] # Bar 1, beat 1, tick 0 (downbeat)
[2.3.240] # Bar 2, beat 3, tick 240
[8.4.120] # Bar 8, beat 4, tick 120
[4.2.0] # Bar 4, beat 2
Requirements:
- Requires tempo in frontmatter or a tempo event
- Requires time_signature for correct beat counting (defaults to 4/4)
Use cases: - Musical compositions - Tempo-synchronized automation - Score-based arrangements
Common Tick Values (at 480 PPQ): - Quarter note = 480 ticks - Eighth note = 240 ticks - Sixteenth note = 120 ticks - Thirty-second note = 60 ticks
3. Relative Delta¶
Format: [+duration]
Specifies time relative to the previous event.
[+0.500s] # 500ms after previous
[+250ms] # 250 milliseconds after previous
[+1b] # 1 beat after previous
[+2.1.0] # 2 bars and 1 beat after previous
[+1.5s] # 1.5 seconds after previous
Units:
- s = seconds
- ms = milliseconds
- b = beats
- t = ticks
- bars.beats.ticks = musical time delta
Use cases: - Sequential patterns - Delays between commands - Device-specific timing (e.g., Quad Cortex bank/preset delays)
4. Simultaneous Execution¶
Format: [@]
Executes at the same time as the previous event.
[00:00.000]
- note_on 1.C4 100 1b # Middle C
[@]
- note_on 1.E4 100 1b # Major third (same time)
[@]
- note_on 1.G4 100 1b # Perfect fifth (same time)
# Result: C major chord
Use cases: - Chords (multiple simultaneous notes) - Multi-channel commands at same time - Grouped parameter changes
Timing Rules¶
- Monotonically increasing: Time must always move forward (except
[@]) - First event must be absolute: Document must start with absolute or musical time
- Musical time requires tempo: Cannot use musical time without tempo defined
Correct Timing¶
# ✅ Correct - monotonically increasing
[00:00.000]
- tempo 120
[00:01.000]
- note_on 1.60 100 1b
[+500ms]
- note_off 1.60 64
Incorrect Timing¶
# ❌ Wrong - time goes backwards
[00:02.000]
- note_on 1.60 100 1b
[00:01.000] # Error: time before previous event
- note_on 1.62 100 1b
MIDI Commands¶
MML supports all standard MIDI commands with human-readable syntax.
Channel Voice Messages¶
Note Commands¶
Note On with Duration (automatic note-off):
[00:00.000]
- note_on 1.C4 100 1b # Middle C, velocity 100, 1 beat
- note_on 1.60 127 500ms # MIDI note 60, velocity 127, 500ms
- note_on 2.D#5 80 2b # D# in octave 5, 2 beats
- note_on 1.Bb3 90 250ms # B-flat, 250ms
Note On/Off (manual control):
[00:00.000]
- note_on 1.C4 100 # Note on
[00:01.000]
- note_off 1.C4 64 # Note off with release velocity
Note Names:
- Format: [Note][Accidental][Octave]
- Notes: C, D, E, F, G, A, B
- Accidentals: # (sharp), b (flat)
- Octaves: -1 to 9
- Range: C-1 (0) to G9 (127)
Examples:
C4 # Middle C (MIDI note 60)
C#4 # C sharp
Db4 # D flat (enharmonic to C#4)
A3 # A below middle C
G9 # Highest note (MIDI 127)
Velocity Range: 0-127 - 0 = silent (effectively note off) - 64 = medium - 127 = maximum
Duration Units:
- b = beats
- s = seconds
- ms = milliseconds
- t = ticks
Program Change¶
[00:00.000]
- pc 1.0 # Program 0 (often piano)
- pc 1.42 # Program 42
- program_change 2.127 # Program 127 on channel 2
Range: 0-127 (128 programs per channel)
Common MIDI Programs (General MIDI): - 0-7: Piano - 8-15: Chromatic Percussion - 24-31: Guitar - 32-39: Bass - 40-47: Strings - 48-55: Ensemble
Control Change (CC)¶
[00:00.000]
- cc 1.7.127 # Volume (CC#7) maximum
- cc 1.10.64 # Pan (CC#10) center
- cc 1.11.100 # Expression (CC#11)
- cc 2.1.0 # Mod wheel (CC#1) minimum
- cc 1.64.127 # Sustain pedal on
- cc 1.64.0 # Sustain pedal off
Range: - Controller: 0-127 - Value: 0-127
Common Controllers: - CC#1: Modulation Wheel - CC#7: Channel Volume - CC#10: Pan (0=left, 64=center, 127=right) - CC#11: Expression - CC#64: Sustain Pedal (0-63=off, 64-127=on) - CC#74: Filter Cutoff - CC#91: Reverb Depth - CC#93: Chorus Depth
Pitch Bend¶
[00:00.000]
- pb 1.0 # No bend (center)
- pb 1.8192 # Center (alternative)
- pb 1.+2000 # Bend up
- pb 1.-4096 # Bend down
- pb 1.16383 # Maximum bend up
- pb 1.-8192 # Maximum bend down
Range: - -8192 to +8191 (signed) - OR 0 to 16383 (unsigned, 8192 = center)
Note: Pitch bend range (semitones) is controlled by RPN#0 (Pitch Bend Sensitivity).
Aftertouch / Pressure¶
Channel Pressure (monophonic aftertouch):
Polyphonic Pressure (per-note aftertouch):
Range: 0-127
Channel Reset Commands¶
- all_notes_off <channel> # CC#123 - Stop all notes
- all_sound_off <channel> # CC#120 - Immediate silence
- reset_controllers <channel> # CC#121 - Reset all controllers
System Common Messages¶
System Exclusive (SysEx)¶
Send manufacturer-specific commands.
Inline hex bytes:
Multi-line:
From file:
Note: SysEx messages must start with F0 and end with F7.
Song Position / Select¶
MIDI Timecode¶
Tune Request¶
System Real-Time Messages¶
Transport and clock control.
# Transport
- clock_start # Start sequencer
- clock_stop # Stop sequencer
- clock_continue # Continue from current position
- clock_tick # Single timing clock pulse (24 per quarter)
# System
- active_sensing # Keep-alive message
- system_reset # Reset all devices
Meta Events (MIDI File Only)¶
Meta events are stored in MIDI files but not sent to devices during playback.
Tempo¶
Range: Typically 20-300 BPM (no hard limit)
Time Signature¶
[00:00.000]
- time_signature 4/4 # Common time
- time_signature 3/4 # Waltz
- time_signature 6/8 # Compound meter
- time_signature 7/8 # Odd meter
- time_signature 5/4 # Take Five
Optional parameters:
- clocks: MIDI clocks per metronome click (default: 24)
- 32nds: Number of 32nd notes per quarter (default: 8)
Key Signature¶
[00:00.000]
- key_signature C # C major (default)
- key_signature Cm # C minor
- key_signature F# # F# major
- key_signature Bbm # B-flat minor
- key_signature G # G major
Text and Metadata Events¶
- text "Any text content"
- copyright "© 2025 Your Name"
- track_name "Lead Guitar"
- instrument_name "Stratocaster"
- lyric "These are the lyrics"
- marker "Chorus"
- cue_point "Lighting change here"
- device_name "Quad Cortex"
[00:00.000]
- marker "Intro"
- text "Song begins"
[00:16.000]
- marker "Verse 1"
- lyric "First line of verse"
[00:32.000]
- marker "Chorus"
Use cases: - marker: Section boundaries (Intro, Verse, Chorus) - text: General annotations - lyric: Karaoke-style lyrics - cue_point: Synchronization cues
End of Track¶
Usually auto-generated by the compiler, but can be explicit:
Directives¶
Directives start with @ and provide advanced functionality.
Imports¶
Import device libraries and shared definitions.
@import "devices/quad_cortex.mmd"
@import "devices/eventide_h90.mmd"
@import "shared/common_macros.mmd"
Features: - Loads alias definitions from external files - Circular import detection - Relative paths from current file
See Also: Device Libraries Guide
Variables¶
Define constants and expressions.
Usage:
Expressions:
@define NEXT_PRESET ${VERSE_PRESET + 1}
@define HALF_TEMPO ${INTRO_TEMPO / 2}
@define SCALED_VALUE ${BASE_VALUE * 1.5}
Operators: +, -, *, /, %, (, )
Aliases¶
Create reusable command shortcuts.
Basic syntax:
Single-command alias:
@alias cortex_preset pc.{channel}.{preset} "Load preset"
@alias h90_mix cc.{channel}.84.{value} "Set A/B mix"
Multi-command alias (macro):
@alias cortex_load {channel}.{setlist}.{group}.{preset} "Complete preset load"
- cc {channel}.32.{setlist}
- cc {channel}.0.{group}
- pc {channel}.{preset}
@end
Parameter types:
{name} # Basic parameter (0-127)
{name:0-127} # Explicit range
{name=default} # Optional with default
{channel:1-16} # MIDI channel
{note} # Note name or number
{percent:0-100} # Percentage (scaled to 0-127)
{velocity:0-127} # Note velocity
{bool:0-1} # Boolean
{mode=option1:0,option2:1} # Enum values
Usage:
[00:00.000]
- cortex_preset 1 5 # Expands to: pc 1.5
- cortex_load 1 2 0 10 # Expands to 3 CC/PC commands
See Also: Alias System Guide
Loops¶
Repeat commands without manual duplication.
Complex example:
Interval units: b (beats), s (seconds), ms (milliseconds), t (ticks)
Sweeps¶
Smooth parameter automation over time.
@sweep from [<start_time>] to [<end_time>] every <interval>
- cc <channel>.<controller>.ramp(<start_val>, <end_val>, <type>)
@end
Ramp types:
- linear: Constant rate (default)
- exponential: Accelerates
- logarithmic: Decelerates
Volume fade in:
Filter sweep:
Pan automation:
Multiple concurrent sweeps:
[00:18.000]
- note_on 1.65 100 4s
# Pan left to right
@sweep from [00:18.000] to [00:22.000] every 75ms
- cc 1.10.ramp(0, 127, linear)
@end
# Expression swell
@sweep from [00:18.000] to [00:22.000] every 80ms
- cc 1.11.ramp(0, 127, exponential)
@end
Conditionals¶
Conditional command execution.
@if ${DEVICE_TYPE} == "cortex"
- cortex_load_preset 1 10
@elif ${DEVICE_TYPE} == "h90"
- h90_preset 2 5
@else
- pc 1.10
@end
Note: Currently conditionals work in aliases, but document-level conditionals are planned.
Tracks¶
Multi-track support (MIDI Format 1).
## Track 1: Control
@track control channel=1
[00:00.000]
- tempo 120
- marker "Start"
## Track 2: Melody
@track melody channel=2
[00:00.000]
- note_on 2.C4 100 1b
Note: Track blocks are parsed but full multi-track MIDI file generation is in development.
Comments¶
Single-Line Comments¶
# This is a comment
// This is also a comment
[00:00.000]
- tempo 120 # Inline comment
- note_on 1.C4 100 1b // Another inline comment
Multi-Line Comments¶
/*
This is a multi-line comment.
It can span multiple lines.
Useful for documenting sections.
*/
[00:00.000]
- tempo 120
Section Headers¶
Use markdown headers as visual separators:
# ============================================
# Intro Section
# ============================================
[00:00.000]
- tempo 120
# ============================================
# Verse 1
# ============================================
[00:16.000]
- marker "Verse 1"
Advanced Features¶
Simultaneous Commands¶
Execute multiple commands at the same time using [@]:
# C major chord
[00:00.000]
- note_on 1.C4 100 2b
[@]
- note_on 1.E4 100 2b
[@]
- note_on 1.G4 100 2b
# Multi-channel setup
[00:00.000]
- cc 1.7.100 # Channel 1 volume
[@]
- cc 2.7.90 # Channel 2 volume
[@]
- cc 3.7.80 # Channel 3 volume
Multi-Channel Patterns¶
@define SYNTH_CHANNEL 1
@define BASS_CHANNEL 2
@define DRUMS_CHANNEL 10
# Setup all channels
[00:00.000]
- cc ${SYNTH_CHANNEL}.7.80
[@]
- cc ${BASS_CHANNEL}.7.70
[@]
- cc ${DRUMS_CHANNEL}.7.100
# Play on different channels
[00:01.000]
- note_on ${SYNTH_CHANNEL}.C4 100 1b
[@]
- note_on ${BASS_CHANNEL}.C2 90 1b
[@]
- note_on ${DRUMS_CHANNEL}.C1 127 100ms
Device-Specific Timing¶
Some devices (e.g., Quad Cortex) require delays between bank and preset changes:
@alias cortex_load {channel}.{setlist}.{group}.{preset} "Load preset with delays"
- cc {channel}.32.{setlist}
[+100ms]
- cc {channel}.0.{group}
[+100ms]
- pc {channel}.{preset}
@end
Expression Evaluation¶
@define BASE_NOTE 60
@define INTERVAL 7
[00:00.000]
- note_on 1.${BASE_NOTE} 100 1b
[00:01.000]
- note_on 1.${BASE_NOTE + INTERVAL} 100 1b # Perfect fifth
[00:02.000]
- note_on 1.${BASE_NOTE + INTERVAL * 2} 100 1b # Two fifths
Random Expressions¶
Generate random values for creating variations and humanized sequences.
Basic syntax:
random(min, max) # Random value between min and max
random(min, max, seed=number) # Reproducible with fixed seed
Supported contexts:
# Random velocity for humanized dynamics
[00:00.000]
- note_on 1.60 random(70, 100) 1b
# Random note selection
[00:01.000]
- note_on 1.random(C3, C5) 80 1b
# Random CC value for parameter variation
[00:02.000]
- cc 1.74.random(30, 90) # Random filter cutoff
Use cases:
- Humanization: Add subtle velocity variation (±5-15%) for natural feel
- Generative melodies: Random note selection from specified range
- Parameter variation: Evolving CC values (filter, reverb, pan, etc.)
- Reproducibility: Use seed parameter for consistent testing
Examples:
# Humanized drum pattern
@loop 8 times every 1b
- note_on 1.36.random(100, 127) 0.5b # Kick with varying velocity
@end
# Generative arpeggio
@loop 16 times every 0.5b
- note_on 1.random(C4, G4).random(70, 100) 0.5b
@end
# Reproducible variation
- note_on 1.60 random(70, 90, seed=42) 1b # Same value each run
Limitations:
- Cannot use random in timing markers or durations
- Cannot use random in variable definitions (@define)
- For advanced techniques, see Generative Music Guide
Examples¶
Complete Song Structure¶
---
title: "Complete Song Example"
author: "Composer"
tempo: 128
time_signature: [4, 4]
ppq: 480
midi_format: 1
---
@define MAIN_CHANNEL 1
@define INTRO_PRESET 1
@define VERSE_PRESET 5
@define CHORUS_PRESET 10
# ============================================
# Intro (8 bars)
# ============================================
[1.1.0]
- tempo 128
- marker "Intro"
- pc ${MAIN_CHANNEL}.${INTRO_PRESET}
# Simple arpeggio
@loop 32 times every 0.5b
- note_on 1.C4 90 0.4b
- note_on 1.E4 90 0.4b
- note_on 1.G4 90 0.4b
- note_on 1.E4 90 0.4b
@end
# ============================================
# Verse 1 (16 bars)
# ============================================
[9.1.0]
- marker "Verse 1"
- pc ${MAIN_CHANNEL}.${VERSE_PRESET}
# Melody line
[9.1.0]
- note_on 1.C5 100 1b
[10.1.0]
- note_on 1.D5 100 1b
[11.1.0]
- note_on 1.E5 100 2b
# ============================================
# Chorus (8 bars)
# ============================================
[25.1.0]
- marker "Chorus"
- pc ${MAIN_CHANNEL}.${CHORUS_PRESET}
# Volume swell
@sweep from [25.1.0] to [33.1.0] every 8t
- cc 1.7.ramp(60, 127, linear)
@end
# Chords
[25.1.0]
- note_on 1.C4 110 4b
[@]
- note_on 1.E4 110 4b
[@]
- note_on 1.G4 110 4b
[29.1.0]
- note_on 1.F4 110 4b
[@]
- note_on 1.A4 110 4b
[@]
- note_on 1.C5 110 4b
# ============================================
# End
# ============================================
[33.1.0]
- marker "End"
- all_notes_off 1
- end_of_track
Live Performance Automation¶
---
title: "Live Performance"
author: "Guitarist"
ppq: 480
---
@import "devices/quad_cortex.mmd"
@import "devices/eventide_h90.mmd"
@define CORTEX_CH 1
@define H90_CH 2
# ============================================
# Song Start
# ============================================
[00:00.000]
- marker "Intro - Clean"
- cortex_load ${CORTEX_CH} 1 0 0 # Setlist 1, Group 0, Preset 0
- h90_preset ${H90_CH} 10 # Reverb preset
# ============================================
# Verse - Add Drive
# ============================================
[00:30.000]
- marker "Verse - Drive"
- cortex_load ${CORTEX_CH} 1 0 5 # Switch to drive preset
# Gradually increase gain
@sweep from [00:30.000] to [00:45.000] every 200ms
- cc ${CORTEX_CH}.21.ramp(0, 100, linear)
@end
# ============================================
# Chorus - Full Drive
# ============================================
[01:00.000]
- marker "Chorus - Full"
- cortex_load ${CORTEX_CH} 1 1 10 # Heavy preset
- h90_preset ${H90_CH} 25 # Big reverb + delay
# ============================================
# Solo - Wah Effect
# ============================================
[01:30.000]
- marker "Solo"
- cortex_scene ${CORTEX_CH} 2 # Solo scene
# Auto-wah sweep
@loop 8 times every 1b
@sweep from [+0b] to [+0.5b] every 50ms
- cc ${CORTEX_CH}.1.ramp(0, 127, exponential)
@end
@sweep from [+0.5b] to [+1b] every 50ms
- cc ${CORTEX_CH}.1.ramp(127, 0, logarithmic)
@end
@end
# ============================================
# End
# ============================================
[02:30.000]
- marker "Outro"
- cortex_load ${CORTEX_CH} 1 0 0 # Back to clean
# Fade out
@sweep from [02:30.000] to [02:45.000] every 100ms
- cc ${CORTEX_CH}.7.ramp(127, 0, linear)
@end
[02:45.000]
- all_sound_off ${CORTEX_CH}
- all_sound_off ${H90_CH}
See Also¶
- Quickstart Guide - Get started quickly
- Alias System Guide - Deep dive into aliases
- Device Libraries Guide - Using device libraries
- Timing System Guide - Understanding timing paradigms (coming soon)
- CLI Reference - Command-line tools
- Examples Directory - 51 runnable examples across 7 categories
Next Steps: 1. Try the quickstart guide to compile your first file 2. Explore example files for real-world patterns 3. Learn about device libraries for your gear 4. Read the alias system guide for advanced automation