Procedural Music Format
01 What PMF Is
PMF is a JSON-based format for describing procedural music compositions in a way that is immediately useful to any tool powered by an LLM or API. It is the serialization layer between a generative music engine and the wider world of AI tooling, game editors, web apps, and human inspection.
A .pmf file fully describes a composition — not the rendered audio, but the parameters, structure, and intent that produced it. Any tool that can read JSON can read a PMF file. Any LLM that knows music theory can reason about one without documentation.
02 Why PMF Exists
Existing music formats solve different problems:
| Format | Designed for | Why it fails here |
|---|---|---|
| MIDI | Performance capture | Binary; every note is a timed event, no compositional intent |
| MusicXML | Score printing | XML verbosity destroys LLM sequence efficiency |
| ABC Notation | Score transcription | Note-level only; no seed, mood, or generative parameters |
| ChordPro | Chord sheets | No melody, arrangement, or voice detail |
| YNote / HNote | LLM note generation | Note sequences only; no composition structure |
None of them describe how to generate music — only music that already exists. PMF fills that gap.
PMF is the right format when:
- A composition is produced by a generative engine from a seed or parameters
- You want an LLM to inspect, describe, modify, or reproduce it
- You want any tool — game editor, web app, CLI — to read the same file
03 Design Principles
3.1 Musical Vocabulary First
Every numeric value that has a standard musical name is given that name. An LLM has been trained on music theory; use it.
"key": "A minor",
"progression": ["Am", "F", "C", "G"],
"progression_degrees": ["i", "VI", "III", "VII"],
"bpm_label": "mid-tempo",
"reverb_room": "warm medium hall"
3.2 Dual Representation
Machine-precise values and semantic labels coexist. Neither is omitted.
"bpm": 128.0,
"bpm_label": "mid-tempo",
"key_shift_semitones": 0,
"key": "A minor",
"delay_timing": "dotted_eighth",
"delay_timing_label": "3 × sixteenth note at 128 BPM ≈ 140ms"
3.3 Summary First
The top-level summary field is a single human-readable paragraph generated by the engine at render time. An LLM can read the summary alone and understand the composition. Everything below is supporting detail.
3.4 Layered Depth
Fields are ordered from high-level to precise. A tool that only needs to describe or compare tracks reads summary and identity. A tool that needs to reconstruct reads everything.
3.5 ABC for Note Sequences
Melody motifs are expressed in ABC notation — the format with the strongest LLM research backing for note-level data (ChatMusician, ABC-Eval 2024). PMF does not invent a new note syntax.
3.6 Engine-Agnostic Core, Engine-Specific Detail
The top sections (summary, identity, harmony, melody, arrangement) are generic — valid for any procedural music engine. The voices, fx, and mix sections carry engine-specific detail and are identified via the engine field at the root.
04 Top-Level Structure
{
"pmf_version": "1.0",
"engine": "<engine-name>",
"engine_version": "<semver>",
"summary": "<natural language paragraph>",
"identity": { ... },
"render_key": { ... },
"harmony": { ... },
"melody": { ... },
"arrangement": { ... },
"voices": { ... },
"fx": { ... },
"mix": { ... }
}
pmf_version, engine, summary, identity, and render_key are required. All other sections are optional — a minimal PMF file is valid with just those five.
05 Field Reference
5.1 Root
| Field | Type | Required | Description |
|---|---|---|---|
| pmf_version | string | yes | PMF format version. Currently "1.0" |
| engine | string | yes | Name of the generating engine (e.g. "synthwave_engine") |
| engine_version | string | no | Semver of the engine that wrote this file |
| summary | string | yes | Engine-generated natural language description of the composition |
5.2 identity
Who this composition is and what it sounds like.
| Field | Type | Required | Description |
|---|---|---|---|
| seed | u64 | yes | The seed that produced this composition |
| mood | string | no | Mood preset name (engine-defined) |
| bpm | f32 | yes | Tempo in beats per minute |
| bpm_label | string | no | Human label for the tempo (e.g. "mid-tempo") |
| key | string | yes | Key name in standard notation (e.g. "A minor", "F# minor") |
| key_type | string | no | Scale type: "natural_minor", "harmonic_minor", "major", etc. |
| key_shift_semitones | i8 | no | Semitone offset from the engine's reference key |
| bars | u32 | yes | Number of bars in the loop |
| time_signature | string | no | Time signature (default "4/4") |
5.3 render_key
The exact rendering context. Together with identity.seed and pmf_version, this is sufficient to reproduce byte-identical output from a compatible engine version.
| Field | Type | Required | Description |
|---|---|---|---|
| sample_rate | u32 | yes | Audio sample rate in Hz |
| bars | u32 | yes | Bar count used for this render (must match identity.bars) |
| stem_format | string | yes | Channel layout: "mono", "stereo", or engine-defined variants |
| loop_mode | string | yes | "looped" or "finite" |
| engine_version_major | u32 | yes | Major version of the engine — determinism is stable within a major version |
| palette_pack_version | u32 | no | Version of the palette pack used, if the engine supports swappable packs |
5.4 harmony
The harmonic structure of the composition.
| Field | Type | Required | Description |
|---|---|---|---|
| progression | string[] | no | Chord names in order (e.g. ["Am", "F", "C", "G"]) |
| progression_degrees | string[] | no | Scale degrees (e.g. ["i", "VI", "III", "VII"]) |
| progression_label | string | no | Human description (e.g. "classic minor turnaround") |
| key_root_midi | u8 | no | MIDI note number of the key root (A4=69) |
| bass_pattern | i8[] | no | Per-beat interval offsets from chord root in semitones |
| bass_pattern_label | string | no | Human description (e.g. "root-root-fifth-root") |
| arp_pattern | string | no | Arp pattern name (engine-defined) |
| arp_pattern_label | string | no | Human description of the arp movement |
| arp_bar_order | u8[] | no | Which arp pattern index plays each bar |
5.5 melody
Named melodic motifs. Keys are string identifiers ("motif_a", "motif_b", or any name the engine uses). Each motif object:
| Field | Type | Required | Description |
|---|---|---|---|
| abc | string | yes | Motif body in ABC notation (no headers — key comes from identity.key) |
| intervals_semitones | i8[] | no | Interval steps in semitones (for LLM reasoning without ABC parsing) |
| rhythm | string | no | Human-readable rhythm description |
| label | string | no | Human description of the motif's musical character |
| active_bars | u8[] | no | Bar indices where this motif plays |
identity. Example: "A2 c B A4" — two beats on A, then C, B, A.5.6 arrangement
How the composition unfolds bar by bar.
| Field | Type | Required | Description |
|---|---|---|---|
| template | string | no | Arrangement template name (engine-defined) |
| sections | object[] | no | Named sections with bar ranges and active stems |
| melody_on_bars | u8[] | no | Bar indices where melody plays |
| bar_gains | object | no | Per-stem float arrays — gain multiplier per bar per stem |
Each section object:
{ "name": "Intro", "bars": [0, 1], "stems_active": ["drums", "bass"] }
bar_gains keys are stem names. Values are arrays with one entry per bar. Gain semantics: 0.0 = silent, 1.0 = full, >1.0 = boosted.
5.7 voices
Per-voice synthesis parameters. Keys are voice names. Content is engine-defined — PMF specifies no fixed schema, only the convention that semantic labels accompany raw values.
5.8 fx
Post-processing chain. Common sub-keys: reverb, delay, sidechain, limiter. Engine-defined.
5.9 mix
Master mix parameters: stem volume presets, tween timing, reactive control tables. Engine-defined.
5.10 extensions
Optional. Engines may attach custom data here under a namespaced key to avoid polluting the core namespace. The key should match engine at the root.
"extensions": {
"my_engine": {
"custom_field": "value"
}
}
06 Summary Generation
The summary field is computed by the engine — not written by hand and not filled by an external AI. A good PMF summary answers in one paragraph:
- What does it sound like overall? (tempo, mood, key, genre character)
- How does it unfold? (arrangement arc, when stems enter or drop)
- What makes it distinctive? (motif character, notable FX, anything unusual)
Template:
{tempo_label} {mood} track in {key} at {bpm} BPM. {arrangement_arc}. {melody_description}. {fx_highlight_if_notable}.
Example:
Mid-tempo NightDrive track in A minor at 128 BPM. Sparse intro over bars 0–1 (drums and bass only) builds through a melancholy i–VI–III–VII progression to full mix by bar 4. Ascending minor-third motif enters at bar 4 and resolves with a pentatonic descent in bar 6. Warm medium-hall reverb and dotted-eighth slap delay on the lead.
Rules:
- Use musical vocabulary, not parameter names
- Mention the arrangement arc if non-trivial
- Keep it under 80 words
- Do not mention seed, sample rate, or version numbers
07 AI Consumption Guide
When an LLM reads a PMF file, read in this order:
summaryfirst. Gestalt in one paragraph. Stop here for description or comparison tasks.identityandharmony. Key, BPM, progression, and mood determine character.arrangement.sectionsandbar_gains. Structure and dynamics.melodymotifs. ABC body +labelfor melodic content.voices,fx,mix. Only for synthesis modification or exact reconstruction.
For modification tasks: change identity, harmony, or melody fields. Re-submit to the engine. Do not hand-edit bar_gains unless the arrangement template changes — the engine owns those values.
For comparison tasks: identity.mood, harmony.progression_degrees, arrangement.sections, and melody.*.label are the most semantically rich fields for clustering or ranking.
For reconstruction: identity.seed + render_key + engine + engine_version_major is the minimum key. All other fields are derived — the engine ignores them on exact round-trip.
08 Round-Trip Contract
Exact round-trip (byte-identical audio): identity.seed + render_key.* + engine + engine_version_major unchanged. Engine re-derives everything from the seed.
Approximate round-trip (musically consistent, different PCM): engine accepts an edited PMF with changed identity or harmony fields, renders a new composition consistent with those params.
Engine support for approximate round-trip is declared per-engine. Engines that do not support it may ignore all fields except the reconstruction key.
09 Versioning
pmf_version follows semver. The current version is 1.0.
| Bump | What changes | Notes |
|---|---|---|
| Patch | Bug fixes in this document | No format change |
| Minor | New optional fields added | Existing readers ignore unknown fields |
| Major | Required fields changed or removed | Readers must warn on mismatch |
Field removal is always a major bump. Field rename is always a major bump.
10 Complete Example
See examples/synthwave-example.pmf for a full annotated file.
Minimal valid file: examples/minimal.pmf
11 What PMF Is Not
- Not an audio format. Use WAV or OGG for rendered audio.
- Not a score format. PMF does not encode every note in the final render. Use MIDI export for that.
- Not a DAW project format. No clip editing, automation lanes, or plugin state.
- Not a streaming format. PMF is a snapshot, not a real-time exchange protocol.
- Not a replacement for MIDI. PMF and MIDI are complementary — an engine can export both from the same render.