OMP specification
Licensed CC-BY-4.0. The schemas and tools are on the Project format page.
Version 1.3.0
An open, documented DAW project format. It stores MIDI patterns, an FL-style playlist arrangement that places clips on a timeline, and resolvable links to audio (WAV / MP3 / FLAC / OGG). Everything is packaged as a ZIP of already-open formats, so any tool with a ZIP and JSON reader can open it.
- Spec license: CC-BY-4.0
- Reference tooling license: MIT
- Status: draft, versioned with SemVer via the
formatVersionfield.
1. Design goals
- Open building blocks only. ZIP, JSON, Standard MIDI File, PCM/FLAC. No patent-encumbered part is ever required. MP3 is supported but never mandatory.
- JSON-first and diffable. Structure lives in
project.json, so projects version-control and merge cleanly. - Portable. Relative references inside the container; a project can be zipped and moved without breaking.
- Resilient audio links. Each media asset lists several resolution candidates plus a content hash, so moved files can be relinked automatically.
- Graceful degradation. Host-specific data (plugins) is stored opaquely. The MIDI / arrangement / audio core stays usable even when a plugin is absent.
- Extensible without breakage. Unknown vendor data rides in an
extensionsnamespace and must round-trip untouched.
2. Container
An .omp file is a ZIP archive. .omp is the canonical extension; readers SHOULD also accept .odaw as Veslate's historical alias for the identical container. The same tree, left unzipped, is a valid project folder (preferred for git). Readers accept either shape.
song.omp (zip)
├── project.json # entry point: structure
├── assets.json # media registry
├── patterns/ # reusable MIDI clips as Standard MIDI Files
│ └── drums_a.mid
├── audio/ # embedded audio (optional)
│ └── vox_take3.wav
├── automation/ # optional automation curves (JSON)
└── meta/ # optional (thumbnail, notes)
project.jsonMUST be the first member in a bundled.ompso the header is previewable without decompressing media.- Suggested MIME type:
application/x-omp+zip. Suggested extension:.omp. - Text members are UTF-8, JSON indented by 2 spaces, trailing newline.
3. Timebase
Musical time is measured in ticks against ppq (pulses per quarter note, default 960), identical to MIDI. A tempo map and time-signature map convert ticks to seconds.
"timebase": {
"ppq": 960,
"key": "C",
"tempoMap": [ { "tick": 0, "bpm": 120.0 } ],
"timeSignatures": [ { "tick": 0, "num": 4, "den": 4 } ]
}
The optional key names the project tonal center. Its value is one of the twelve sharp-spelled pitch classes (C through B) for major, or the same name with an m suffix for natural minor (for example, F#m). Omission defaults to C.
Audio-only positions and lengths use frames (sample count at the asset's own sample rate), never ticks, because they are independent of tempo.
4. project.json
Top-level keys: format (const "omp"), formatVersion, generator?, meta?, timebase, channels?, patterns?, arrangement, mixer?, automation?, extensions?.
4.1 Channels (the Channel Rack)
A channel is a sound source. Channels are global: patterns reference them, so one instrument instance serves every pattern that uses it. Fields: name, color?, mixer?, instrument.
mixer is the id of the mixer track this channel feeds. Routing lives on the channel so a single pattern can split across buses (its kick to the drum bus while its keys hit the keys bus). When mixer is absent, the channel follows the mixer of the arrangement track its clip sits on.
instrument.kind is one of:
| kind | portable? | fields |
|---|---|---|
sampler |
yes | asset (asset id), rootNote? (0–127), stretchMode? (off | repitch | stretch) |
plugin |
opaque | uid, state (base64), params? |
external |
ref only | uid |
none |
yes | (none) |
plugin.state is an opaque, host-specific blob. A host without the plugin still reads every note that targets the channel.
4.2 Patterns (multi-part clips)
A pattern is a reusable clip made of parts. Each part pairs one channel with its own note source, so a single pattern can hold a whole groove (kick, snare, hats, a keys line) and still route each voice through its channel's mixer track. length (ticks) is required; parts needs at least one entry.
"pat-drums": {
"name": "Night Drums", "length": 3840, "color": "#ff8844", "swing": 66.7,
"parts": [
{ "channel": "ch-kick", "source": { "kind": "smf", "rel": "patterns/drums_a.mid" } },
{ "channel": "ch-keys", "source": { "kind": "inline", "notes": [ { "t": 0, "d": 480, "n": 69, "v": 96 } ] } }
]
}
swing is an optional MPC-style sixteenth swing percentage from 50 through 75; omission means 50 (straight). It is a non-destructive playback transform: for each pair of sixteenths, an authored note exactly on the second sixteenth moves to pairStart + pairLength × swing / 100, rounded to the nearest tick. The performed start clamps to the final pattern tick and the authored duration is preserved. Notes off that exact lattice do not move: swing is grid feel, not note feel. Authored ticks remain the editing and visualization truth.
A part's source.kind selects storage:
inline: notes (and optionalcc) live inproject.json. Most diff-friendly.smf: notes live in a linked Standard MIDI File atsource.rel. Maximum interoperability: the.midopens in any DAW or MIDI library on its own.
Note object (compact keys to keep large patterns small):
{ "t": 0, "d": 480, "n": 60, "v": 100, "ch": 0 }
t start tick, d duration ticks, n note 0–127, v velocity 0–127, ch MIDI channel 0–15 (omitted when 0). Control changes: { "t", "num", "val", "ch?" }.
4.3 Arrangement (the playlist)
The arrangement is a list of timeline tracks; each holds items placed at a start tick. Two item types:
Pattern instance: places a pattern clip. clipStart is the offset into the source pattern in ticks (default 0), and length is the visible timeline extent in ticks. loop repeats the pattern to fill length; when a non-zero clipStart is combined with looping, it sets the loop phase and wraps modulo the pattern length.
{ "type": "pattern", "ref": "pat-bass", "start": 0,
"clipStart": 960, "length": 3840, "loop": true }
Audio instance: places an audio asset; timeline start is in ticks, while clipStart / clipLength index into the source in frames.
{ "type": "audio", "ref": "asset-vox", "start": 7680,
"clipStart": 0, "clipLength": 88200, "gain": -3.0, "fadeIn": 100, "fadeOut": 200 }
This is the FL model: reusable patterns and audio clips arranged on one playlist.
An arrangement track's mixer field holds a mixer track id. It routes the track's audio items, and acts as the fallback route for pattern parts whose channel has no mixer of its own.
4.4 Mixer
mixer.tracks[] describe buses. Each has a required stable id (every reference between tracks, channels, and arrangement tracks uses ids, so renaming a bus never breaks a project), a display name, role (master on exactly one track; track for an arrangement-owned strip; default bus), gain, pan, persisted mute (default false), ordered inserts, sendTo (id of the bus this track's main output feeds), and sends.
pan on mixer strips is stereo balance: centre is unity on both channels, and moving toward either extreme attenuates only the opposite channel.
An insert slot:
{ "uid": "vst3:demo-comp", "state": "<base64>", "params": { "ratio": 4 },
"bypassed": false, "wet": 0.35 }
uid is required; state is the opaque plugin blob (same approach as plugin instruments); wet is a 0–1 dry/wet mix where the host supports one. Array order is chain order. The format sets no slot limit; hosts may impose their own (Veslate caps a chain at 100).
A send:
{ "target": "mix-fx", "gainDb": -6.0 }
4.5 Automation
Automation is optional. When present, project.json references one JSON member:
"automation": { "rel": "automation/lanes.json" }
The referenced member contains per-target lanes with tick-stamped points:
{
"lanes": [
{
"target": "mixer/mix-bass/gain",
"name": "Volume",
"enabled": true,
"points": [
{ "tick": 0, "value": 0.0 },
{ "tick": 3840, "value": -6.0 }
]
}
]
}
target is the stable lane identity; a file MUST NOT contain more than one lane for the same target. OMP 1.2 defines these addresses:
| target | value domain |
|---|---|
mixer/<trackId>/gain |
decibels, finite; editors SHOULD expose at least -60..+6 dB |
mixer/<trackId>/pan |
stereo balance, -1..+1 |
mixer/<trackId>/insert/<slot>/param/<index> |
normalized 0..1 |
channel/<channelId>/param/<index> |
normalized 0..1 |
Entity ids are UTF-8 percent-encoded as URI path segments, so / and % in an id remain unambiguous. Slot and parameter indices are zero-based decimal integers. name is an optional, non-authoritative display hint; it is not part of lane identity. enabled defaults to true and is portable musical state.
Points MUST have a non-negative integer tick and finite numeric value. Conforming writers emit at least one point per lane in strictly increasing tick order. Readers SHOULD recover duplicate ticks deterministically by retaining the last occurrence. Evaluation holds the first point backwards, interpolates linearly between points in the target's value domain, and holds the last point forwards. Unknown well-formed targets MUST be preserved even when a host cannot execute them.
Writers MUST NOT emit fields beyond their declared format version's definition inside known automation objects. Readers MAY drop unknown fields within a known lane object, but MUST preserve the complete lane when its target is unknown.
Mixer mute and host solo state take precedence over automation: an inaudible strip remains silent regardless of its gain lane. Hosts SHOULD smooth mixer gain and pan within each processing block. Plugin parameter lanes may be stepped once per processing block.
4.6 Extensions
extensions is an object namespace for vendor data. Readers MUST preserve unknown keys on save. Recommended convention: extensions["com.vendor.app"].
5. assets.json: the media registry
Keyed by asset id. Each asset lists ordered sources; a host tries each in turn (embedded first, then local path, then remote). hash verifies integrity and enables auto-relink when a file moves. audio carries relink metadata.
{
"asset-vox": {
"kind": "audio",
"sources": [
{ "rel": "audio/vox_take3.wav", "embedded": true },
{ "uri": "file:///home/user/song/vox_take3.wav" },
{ "uri": "https://cdn.example.com/stems/vox_take3.flac" }
],
"hash": { "algo": "sha256", "value": "9f86d0..." },
"audio": { "sampleRate": 44100, "channels": 2, "frames": 88200,
"durationSec": 2.0, "codec": "pcm_s16le" }
}
}
Rules:
- At least one source is required. A source has
rel(inside the container) and/oruri(file://orhttps://). embedded: truemeans the bytes are present atrelinside the ZIP.- A codec may differ per source (embedded lossless WAV, remote FLAC): the same musical asset can resolve to different encodings.
hashis over the bytes of the canonical (first embedded, else first) source.- Asset reachability is defined by typed consumers: sampler
instrument.assetand audio arrangement-itemrefin this version. Opaqueextensionscontent is not a GC root. Any future version introducing an asset-referencing entity MUST extend the reachability definition in the same change.
6. Conformance
A minimal reader MUST: open the container, parse project.json and assets.json, honour timebase, read every part of inline and SMF-sourced patterns, and place pattern/audio items from the arrangement. It MAY ignore mixer, automation, plugin state and extensions, but MUST preserve them on save.
A conforming writer MUST emit valid project.json / assets.json per the schemas in docs/schema/, keep references (ref, channel, asset) resolvable, and store at least one resolvable source per referenced asset.
7. Prior art and how OMP differs
- Standard MIDI File: reused directly for
smfpatterns. - Bitwig DAWproject (open, MIT, XML+ZIP): the closest existing open interchange format. It supports VST2/VST3/CLAP devices by storing an opaque state blob in the container. OMP takes the same opaque-plugin approach but is JSON-first (git-friendly), models an FL-style playlist-of-patterns rather than a linear track layout, and treats audio links as a multi-source registry with hashes.
- Ableton
.als(gzipped XML, undocumented) and FL.flp(closed binary): the closed incumbents OMP is an open alternative to. - MusicXML / MEI: open, but notation formats, a different domain.
8. Versioning
formatVersion is SemVer. Additive, backward-compatible changes bump MINOR; breaking changes bump MAJOR. Readers should accept any file whose MAJOR matches and whose MINOR is less than or equal to the reader's.
- 1.1.0: pattern arrangement items gain optional
clipStart, measured in ticks, for persisted crop and loop-phase offsets. - 1.2.0: optional tick-based parameter automation is defined through
automation/lanes.jsonwith stable mixer, insert, and channel targets. - 1.3.0: patterns gain optional MPC-style sixteenth
swingpercentages from 50 (straight/default) through 75, applied non-destructively at playback.