MTXT is a human-editable, text-based format for representing musical performance data. It stores events with precise timing, pitch, and expression values in a simple line-based structure that's easy to edit without requiring specialized tools.
The format is designed for cases where exact performance details matter - arbitrary timings, micro-tuning, dynamic changes, and other expressive parameters. MTXT avoids binary encoding, making it suitable for manual and AI-assisted editing.
## Features
- **Beat-based**: Events are placed on fractional beats using simple decimal notation (e.g., in a 4/4 time signature, 1 beat = 1 quarter note, so 3.25 represents 3 quarter notes plus 1 sixteenth note).
- **One event per line**: Easy to search and modify with any text editor.
- **Human-friendly**: Use of musical note names (C4, D#3, etc.) and custom note aliases (e.g. `kick` or `Cmaj7`). Hand-crafted files are practical and expressive.
- **Transitions**: Built-in support for gliding continuous parameters (CC, tempo) with customizable curves and timing.
- **Real-time ready**: Supports streaming musical events and real-time data transfer with transitions and flexible timing.
- **Microtonal support**: Built-in cents notation for notes (e.g. `C4+50`, `D4-25`) and global tuning commands for alternate tuning systems and just intonation.
- **Flexible organization**: Events can be written in any order in the file, with the parser handling chronological sorting.
- **MIDI compatible**: Reference implementation includes MIDI to MTXT and MTXT to MIDI conversion.
- **LLM compatible**: Can be easily generated and manipulated by language models.
- **Future-proof**: Supports up to 65535 channels, arbitrary CC parameters with custom string keys and custom metadata.
## Quick Example
```
mtxt 1.0
meta global title Sunrise Melody
meta global author Jane Composer
// Define aliases for drums and chords
alias kick C1
alias Cmaj7 C4,E4,G4,B4
// Global tempo and time signature
0.0 tempo 100
0.0 timesig 4/4
// Set defaults for channel, duration, and velocity
ch=0
dur=1.0
vel=0.8
// Set voice. "John's bright grand" has precedence, but it falls back to a more generic "piano" if not found.
0.0 voice piano, John's bright grand
// Start silently
0.0 cc volume 0.0
// Fade in volume over 3.0 beats, ending at beat 4.0
4.0 cc volume 1.0 transition_time=3.0 transition_curve=0.5
// Play melody (uses default duration and velocity from above, unless overridden)
0.0 note C4
2.0 note G4 vel=0.5
// Notes can be put in arbitrary order, the parser will sort them
1.0 note E4
2.0 note G4 vel=0.5
// Chords can also be played (defined above as an alias for C4,E4,G4,B4 notes)
1.0 note Cmaj7 dur=2.0 vel=0.2
This repository includes a reference implementation in Rust that provides:
- **Library (`mtxt`)**: Rust crate for parsing and writing MTXT files, with MIDI conversion features.
- **CLI tool**: Command-line utility for converting between MIDI and MTXT formats with built-in transforms. Builds can be downloaded from [releases](https://github.com/Daninet/mtxt/releases).
### Basic Usage
```bash
mtxt input.mid output.mtxt # MIDI to MTXT
mtxt input.mtxt output.mid # MTXT to MIDI
mtxt input.mtxt output.mtxt --sort # transform MTXT file and sort events by time
```
### Transform Options
The CLI supports various transforms that can be applied during conversion:
**Musical Transforms:**
- `--transpose ` - Transpose all notes by semitones (e.g., `--transpose +2` or `--transpose -12`)
- `-q, --quantize ` - Quantize timing to a grid (e.g., `4` for quarter notes, `16` for 16th notes)
- `--offset ` - Offset all events by beats (e.g., `--offset 1.5`, `--offset -0.5`). Events shifted to negative times are removed.
- `--swing ` - Apply swing feel (0.0 to 1.0)
- `--humanize ` - Add timing randomization for humanization (0.0 to 1.0)
**Channel Filtering:**
- `--include-channels ` - Include only specific channels (comma-separated, e.g., `1,2,10`)
- `--exclude-channels ` - Exclude specific channels (comma-separated, e.g., `1,2,10`)
**File Organization:**
- `--apply-directives` - Apply global directives to events (inline parameters)
- `--extract-directives` - Extract common inline parameters into global directives
- `--merge-notes` - Merge note on / off pairs into note shorthand events with durations
- `--group-channels` - Group events by channel
- `--sort` - Sort events by time
- `--indent` - Enable timestamp padding
---
## MTXT Specification
## Versioning
- First line must declare version:
```
mtxt 1.0
```
## Structure
- A file consists of:
1. Version line
2. Global metadata (optional)
3. Events (can be in any timestamp order)
## Timing
- All times are in beats specified as fractional numbers. e.g. in a 4/4 time signature, 1 beat = 1 quarter note, so 3.25 represents 3 quarter notes plus 1 sixteenth note.
- This allows changing the tempo and time signature without affecting the timing of events.
- Events may appear in any order in the file; the parser will sort them before playback
- Precision is limited to 5 decimal places (5 microseconds at 120 BPM).
## Commands
### version (mtxt)
```
mtxt
```
- Declares the file format version in the major.minor format (e.g., `mtxt 1.0`).
- Must be the first non-comment line.
### meta
```
// global meta (applies to the entire file and all channels)
meta global
// channel meta (applies to a single channel), starting from the specified time
[
### ch (channel directive)
```
ch=<0..65535>
```
- Sets the default MIDI channel for subsequent events.
- Inline `ch=` on events overrides the default for that event only.
- Required before channel-dependent events that omit inline `ch`.
### alias (note naming)
```
alias
```
- Defines a named alias for a note pitch or a chord.
- ``: Alphanumeric identifier (e.g., `snare`, `Cmaj7`).
- ``: Target note(s), comma-separated if multiple.
- No timestamp. Applies to all subsequent events in the file until overridden.
- Name is case-insensitive.
- Example:
```
alias snare C2
alias Cmaj7 C4,E4,G4,B4 // chord alias
0.0 note snare
1.0 note Cmaj7 // plays all 4 notes
```
### vel (default note-on velocity)
```
vel=<0.0..1.0>
```
- Sets the default note-on velocity.
- Inline `vel=` on `note`/`on` overrides for that event.
### offvel (default note-off velocity)
```
offvel=<0.0..1.0>
```
- Sets the default note-off velocity.
- Inline `offvel=` on `note`/`off` overrides for that event.
- Defaults to `1.0` if not set.
### dur (default note duration)
```
dur=
```
- Sets the default note duration in beats.
- Inline `dur=` on `note` overrides for that event.
- Defaults to `1.0` if not set.
### transition settings
```
transition_curve=
transition_interval=
```
- Sets default transition parameters for `cc` and `tempo`.
- `transition_time` must be specified per event (default `0.0`).
- See **Transitions** section for details.
- Defaults: `curve=0.0`, `interval=1.0`.
### note (shorthand)
```
### on (note-on)
```
on [vel=<0..1>] [ch=<0..65535>]
```
- Emits a note-on only; useful for streaming.
- Uses default `vel` and `ch` unless overridden.
### off (note-off)
```
off [offvel=<0..1>] [ch=<0..65535>]
```
- Emits a note-off only; useful for streaming.
- Uses default `offvel` and `ch` unless overridden.
### cc (control change)
```
cc [note] [ch=<0..65535>] [transition_curve=] [transition_time=] [transition_interval=]
```
- Sends a control change. `` identifies the parameter.
- `` is `[0.0..1.0]`. Uses default `ch` unless overridden.
- Uses global transition defaults unless overridden inline.
- Optional `note` to apply CC to a specific note. If a note is specified, it applies to that note only.
- When a note is not specified, it applies to all notes within a channel.
- Arbitrary string keys can be used for custom parameters (e.g., `cc my_param 0.5`).
### voice (instrument selection)
```
voice [ch=<0..65535>]
```
- Sets the instrument voice for the channel.
- `` is a comma-separated list of voice names (e.g., `piano, acoustic piano, john's super piano`).
- The synthesizer should use the **last** voice in the list that it supports.
- It is recommended to use a standard voice from `instruments.md` as the first item for compatibility.
### tempo
```
tempo [transition_curve=] [transition_time=] [transition_interval=]
```
- Sets tempo in BPM at ``.
- Uses global transition defaults unless overridden inline.
- By default, tempo is at 120 BPM at the start of the file.
### timesig
```
timesig /
```
- Sets time signature; affects beat interpretation after ``.
- By default, time signature is 4/4 at the start of the file.
### tuning
```
tuning
```
- By default, notes are defined with equal temperament tuning (12TET).
- Sets the global tuning offset for a note or pitch class.
- `` can be a pitch class (e.g., `C`, `F#`) or a specific note (e.g., `C4`).
- `` is in range `[-100.0..+100.0]`. Positive values must use `+`.
- Tuning is additive: `Final Pitch = Standard Pitch + Tuning + Note Offset`.
- Changes are instantaneous (no interpolation).
- Specific note tuning overrides pitch class tuning.
- Example:
```
0.0 tuning E -13.7 // Flatten all Es
0.0 tuning G +3.5 // Sharpen all Gs
0.0 tuning E4 0.0 // Exception: E4 is standard tuning (overrides previous line)
```
### reset
```
reset [target]
```
- Resets state variables to defaults.
- `` options:
- `all` (default): All notes off, all controllers reset, tuning cleared on all channels.
- `ch=`: Resets controllers and turns off notes on specific channel.
- `tuning`: Clears all global tuning definitions.
- Example:
```
10.0 reset all
12.0 reset ch=1
14.0 reset tuning
```
### sysex
```
sysex
```
- Sends raw SysEx bytes (space-separated hex, including `F0`/`F7` as needed).
- Example: `12.0 sysex F0 7E 7F 09 01 F7`
### Comments
```
// full-line comment
// inline comment
```
- Everything after `//` is ignored by the parser (except for `://` in URLs).
## Transitions
Use transitions to glide a continuous parameter to a target value by a specific beat, with a chosen feel.
- Supported on: `cc`, `tempo`.
- Fields:
- `transition_curve=` controls the feel of the glide:
- `0.0` (linear): steady change from start to finish (default)
- `> 0` (gentle start → speeds up): musical "ease-in", swells late.
- `< 0` (fast start → settles): musical "ease-out", arrives smoothly.
- `transition_time=` (`τ`) is the glide length in beats. Defaults to `0.0` (instant jump). The change begins at `T − τ` and reaches the target at command time `T`.
- `transition_interval=` is the minimum time between each value update in milliseconds. Defaults to `1.0` (as fast as possible).
Examples:
- `0.0 cc pitch 0.0` — pitch is `0.0` at `0.0`.
- `1.0 cc pitch 0.5 transition_time=0.2` — pitch glides to `0.5` value between beats `0.8` and `1.0`.
- `5.0 cc pitch 0.95 transition_curve=0.5 transition_time=1.5` — starts at `3.5`, accelerates toward `0.95` near `5.0`.
- `7.0 cc volume 0.2 transition_curve=-0.4 transition_time=2.0` — begins at `5.0`, moves quickly then coasts into `0.2` at `7.0`.
Curve definition:
- `value(t) = V0 + (V1 − V0) * ( s + max(α,0) * (s^4 − s) − max(−α,0) * ((1 − (1 − s)^4) − s) )`
- `s = (t − (T − τ)) / τ`; if `τ = 0`, the change is instant at `T`.
Notes:
- Each transition needs a defined value before its end time `T` to establish the start (`V0` at `T − τ`). If no prior value exists, this is an error.
- Overlapping transitions on the same parameter/channel: the new transition immediately aborts the previous at the current value and takes over. When segments conflict, the one with the later end beat (`T`) has precedence.