Take a 1B-parameter text LLM, bolt 55,000 music tokens onto its vocabulary, train it in two stages — and it writes multitrack MIDI from a free-form prompt like "jazz with violin," editable note by note, generated 50× faster than the prior best.
You type "an upbeat jazz piece with a violin solo" and a model hands you back music. Today there are two very different things it could hand you, and the difference is the whole reason this paper exists.
The first kind is audio: a waveform, an MP3, the actual sound. Models like MusicGen and Stable Audio do this beautifully — they generate realistic-sounding music straight from text. But the output is an opaque wall of samples. You cannot grab the violin line and transpose it up a third. You cannot delete the drums, lengthen the bridge, or hand the chords to a real musician to play. The sound is finished and frozen.
The second kind is symbolic: a score, a piano roll, a MIDI file — a list of notes with pitches, start times, durations, and instruments. This is what musicians actually want, because every note is an editable object. You can drag it, mute it, re-voice it, re-orchestrate it. Symbolic output supports the iterative human-AI back-and-forth that made LLMs so useful for writing text.
Earlier text-to-MIDI systems — the paper's main comparison is Text2midi (2025) — used custom encoder-decoder architectures trained from scratch. Two costs follow from "custom and from-scratch":
Here is the tension in numbers, taken from the paper's results table — keep these in your head, we will explain every one:
| Quantity | Text2midi (baseline) | MIDI-LLM (this paper) |
|---|---|---|
| Parameters | 0.27 B | 1.47 B |
| Quality FAD ↓ (lower better) | 0.818 | 0.173 |
| Text relevance CLAP ↑ (higher better) | 18.7 | 22.1 |
| Time for a 2K-token clip (bsz=1) | 47.0 s | 10.0 s |
The surprise is the last two rows together with the first. MIDI-LLM is 5× larger yet generates ~5× faster and at far higher quality. That combination — bigger but faster — is the signature of choosing an architecture the whole ecosystem already knows how to accelerate. The rest of this lesson is how they pulled it off.
Here is the bet of the entire paper, in one sentence: don't build a music model — adapt a language model. Take a pretrained text LLM (Llama 3.2, 1B parameters), teach it that MIDI is just another "language" it can read and write, and let it inherit everything the LLM already knows.
Why is that a good bet? Two reasons the authors lean on:
MIDI-LLM is: take Llama 3.2 1B → widen its embedding/output layers to hold 55K new MIDI tokens alongside the 128K text tokens (Ch 3) → train in two stages, first on lots of music-adjacent text and standalone MIDI, then on MIDI paired with text captions (Ch 5) → at inference, feed a text prompt as a prefix and let the model autoregressively emit MIDI tokens, three tokens per note (Ch 4) → serve it with vLLM at FP8 for speed (Ch 6). Each piece is built up from scratch below, with the actual tensor shapes.
LlamaForCausalLM "with minimal changes." That single discipline is what lets a standard library accelerate it. Text2midi could in principle be optimized too, but it would require "substantially greater engineering effort" — because the optimizations would have to be re-derived for its non-standard architecture.Before an LLM can "speak" music, the music has to become a sequence of discrete tokens — the same way text becomes a sequence of subword IDs. There are several competing schemes (MIDI-like, REMI, ABC notation). MIDI-LLM adopts the arrival-time MIDI-like tokenization from the Anticipatory Music Transformer (AMT, Thickstun et al. 2024).
The reason for that choice is itself instructive: REMI and ABC tokenizations require the music to be beat-synchronized (quantized to a metrical grid). AMT's arrival-time scheme doesn't — it measures time in plain seconds, so it works on any MIDI, including expressive performances that don't sit on a clean grid. More flexibility, fewer prerequisites on the data.
| Position | What it encodes | Range / quantization |
|---|---|---|
| 1. Arrival time | The note's onset (absolute start) | 0 to 100 s, in 10 ms steps |
| 2. Duration | How long the note is held | 0 to 10 s, in 10 ms steps |
| 3. Instrument-pitch | Joint: which instrument and which pitch | 129 instruments × 128 pitches |
Why joint instrument-pitch instead of two separate tokens? It shortens the sequence (2 tokens of info packed into 1) and ties the two facts that always co-occur — a "piano C4" is one concept, not an independent instrument and an independent pitch. The cost is a big vocabulary: 129 × 128 = 16,512 instrument-pitch tokens alone.
Let's count exactly where AMT's 55K music tokens come from, because the number reappears in the next chapter:
| Component | Count | Arithmetic |
|---|---|---|
| Arrival-time values | ~10,000 | 100 s ÷ 10 ms = 10,000 steps |
| Duration values | ~1,000 | 10 s ÷ 10 ms = 1,000 steps |
| Instrument-pitch values | 16,512 | 129 inst × 128 pitch |
| Subtotal (one "voice") | ~27.5 K | sum of the above |
| + "anticipated" copies | ~27.5 K | a second identical set for infilling (below) |
| Total music vocab | ~55 K | = |VAMT| |
That last doubling is worth a beat. AMT keeps a second identical set of tokens called anticipated tokens. These represent future notes that are handed to the model as conditions — they let the model do infilling (fill a gap in the middle of a piece given the notes that come after it). So the vocabulary is doubled: one copy for notes the model generates, one copy for notes it's told about in advance.
Click "Add a note" to drop a note on the piano roll (top). Each note instantly emits three tokens into the sequence below — arrival time, duration, instrument-pitch — color-coded. Watch the token stream grow at exactly 3 tokens per note. This regular rhythm is what the LLM learns to continue.
Now the crux. The LLM speaks 128K text tokens; AMT defines 55K music tokens. How do you get one model to read and write both in a single sequence?
The naive option — and the one the authors explicitly reject — is to serialize MIDI as text: write each note out as the literal string <onset 10.2s> <duration 120ms> <piano, C4> and feed it to Llama's normal text tokenizer. Why is that bad? Because Llama's tokenizer would shatter that string into many subword pieces — "onset", "10", ".", "2", "s", and so on — bloating one note into a dozen-plus tokens. Longer sequences mean more compute per note and a harder learning problem.
The token-embedding matrix of the LLM is ELLM. We stack a new block of music embeddings on top of it:
Symbol by symbol, with the plain meaning:
The teal block is the inherited 128K-row text embedding table (copied, already meaningful). Click "Expand" to stack the 55K-row music table (orange) on top — randomly initialized noise that training must turn into real music-token vectors. The width D = 2048 is shared, so music vectors live in the same space as words.
An LLM ties (or mirrors) its output projection to the same vocabulary: to predict the next token it produces a logit for every vocabulary entry. So expanding the input embeddings means the output layer also grows from 128K to 183K logits per position. The model can now emit music tokens, not just read them. During generation it will simply pick the highest-probability token among all 183K — sometimes a word, usually (once it's writing MIDI) one of the three note-token types.
# Vocabulary expansion: the core of MIDI-LLM, ~5 lines import torch from transformers import AutoModelForCausalLM model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-3.2-1B") V_text = model.config.vocab_size # 128256 V_amt = 55000 # AMT music tokens (incl. anticipated) # resize_token_embeddings copies old rows, random-inits the new ones, # and grows BOTH the input embedding AND the tied output head model.resize_token_embeddings(V_text + V_amt) # shapes after expansion (D = 2048 for Llama 3.2 1B) print(model.get_input_embeddings().weight.shape) # [183256, 2048] print(model.lm_head.weight.shape) # [183256, 2048] # Everything else (attention, MLP, norms) is UNCHANGED -> vLLM works.
Now we can watch the model actually work. At inference time, the recipe is exactly like prompting a chatbot — except the "answer" is music.
The objective being unrolled here is the ordinary autoregressive factorization — the same one a text LLM uses, now over a sequence that begins with words and continues with music:
where s1..k are the text-prompt tokens (given, not predicted) and sk+1..T are the music tokens the model generates. There is no special "music decoder" — it is the same transformer predicting the next token, every step.
At each step the model gives a probability over all 183K tokens. You don't take the single most likely token (that produces repetitive, lifeless music) — you sample. MIDI-LLM uses nucleus sampling with p = 0.98: keep the smallest set of top tokens whose probabilities sum to 0.98, renormalize, and draw from that set. High p keeps diversity (musical surprise) while still cutting off the long tail of nonsense tokens — "to balance musical diversity and coherence," in the paper's words.
The text prompt is fixed as a prefix (gray). Hit "Sample next token" to emit one token at a time and watch the 3-token cycle build a note onto the piano roll. The bar chart on the right is the model's distribution over candidate tokens — the shaded region is the nucleus (top-p) it samples from. Lower top-p to make it greedier/more repetitive; raise it for more variety. "Auto-generate" runs the loop.
# Text-to-MIDI generation is just LLM .generate() with a prompt prefix prompt = "An upbeat jazz piece with a violin solo, tempo 140, key C major." ids = text_tokenizer(prompt, return_tensors="pt").input_ids # [1, k] out = model.generate( ids, max_new_tokens=2048, # ~30 s of music; multiple of 3 -> whole notes do_sample=True, top_p=0.98, # nucleus sampling: diversity vs coherence ) music_ids = out[0, ids.shape[1]:] # strip the text prefix # every 3 music tokens = one note: (arrival, duration, instrument-pitch) notes = [amt_detokenize(music_ids[i:i+3]) for i in range(0, len(music_ids), 3)] write_midi(notes, "out.mid") # editable MIDI file!
Random music embeddings (Ch 3) don't know anything yet. Training fills them in — and the paper does it in two deliberate stages, both using the same plain next-token cross-entropy loss. The split mirrors a common LLM specialization pattern: first broadly adapt to the domain, then sharpen on the exact target task.
Two goals, the paper states them explicitly:
Now teach the actual translation. Each training example is a text caption as prefix (from MidiCaps — genre, mood, instrumentation, tempo, tonality, chords) followed by the AMT tokens of the matching MIDI (from Lakh MIDI). The model learns: given this description, produce these notes.
Click each box. Stage 1 (continued pretraining) feeds music-text + standalone MIDI to learn vocabulary and MIDI syntax. Stage 2 (finetuning) feeds paired caption→MIDI to learn the text-to-music translation. The arrow shows the model carrying forward what it learned.
| Setting | Value | Why it matters |
|---|---|---|
| Backbone | Llama 3.2 1B | Small enough to be cheap, big enough to carry world knowledge |
| Optimizer | AdamW, lr 2×10−4, cosine decay | Standard LLM finetuning recipe |
| Precision / attention | BF16, FlashAttention-2 | Memory- and compute-efficient training |
| Steps | 25K per stage | Both stages get equal gradient budget |
| Sequence length | 2048 (≈ 30 s music) | The model "sees" ~30 s of context |
| Hardware / time | 4× H100, ~6 days total | Modest by LLM standards — the point of starting from a pretrained 1B |
This is where "preserve the LLM's parameter structure" pays off in wall-clock seconds. Because MIDI-LLM is a standard Llama decoder, it drops straight into vLLM — the same serving library that accelerates text LLMs — and inherits three optimizations with zero custom engineering.
transformers setup.The headline metric is RTF = (duration of generated music) ÷ (wall-clock time to generate it). RTF > 1 means faster than real time — you generate music quicker than it would take to play. Higher is better. The worked comparison, on a single L40S GPU for a 2K-token clip:
| Model / precision | Time (bsz=1) | Avg output | RTF (bsz=1) | RTF (bsz=4) |
|---|---|---|---|---|
| Text2midi (FP32) | 47.0 s | 26.3 s | 0.56 | 1.06 |
| MIDI-LLM (BF16) | 10.0 s | 33.3 s | 3.33 | 11.48 |
| MIDI-LLM (FP8) | 8.1 s | 32.6 s | 4.02 | 14.17 |
Walk the arithmetic for FP8, bsz=1: it produced 32.6 s of music in 8.1 s of compute → RTF = 32.6 / 8.1 = 4.02. Text2midi managed RTF 0.56 — slower than real time. So MIDI-LLM is ~7× better on RTF despite being 5× bigger, and batching (bsz=4) pushes it to 14× real time. FP8 buys the last ~20% on top of BF16.
Bars are RTF (generated seconds per wall-clock second). The dashed line at RTF = 1 is "real time" — Text2midi sits below it at bsz=1. Toggle batch size to watch MIDI-LLM's advantage explode, because a standard decoder batches efficiently in vLLM while a custom architecture does not.
Both quality metrics need audio input, but MIDI-LLM outputs MIDI — so the authors synthesize each generated MIDI into audio with FluidSynth first, then measure. Two automatic metrics, both standard in text-to-music evaluation:
| Model | Params | FAD ↓ | CLAP ↑ |
|---|---|---|---|
| Text2midi | 0.27 B | 0.818 | 18.7 |
| MIDI-LLM (BF16) | 1.47 B | 0.173 | 22.1 |
| MIDI-LLM (FP8) | 1.47 B | 0.216 | 21.8 |
Read it carefully. MIDI-LLM wins decisively on both axes — quality (FAD drops by ~4.7×) and text control (CLAP up 3.4 points). And FP8 quantization barely dents quality (FAD 0.173 → 0.216, CLAP 22.1 → 21.8) while buying the speedup from Ch 6. That tiny gap is the engineering sweet spot: near-free deployment savings.
Toggle between the two metrics. Notice the FP8 bar barely differs from BF16 — quantization is nearly free in quality — while both crush the Text2midi baseline. The direction of "better" flips between the metrics, which the axis label reminds you of.
Good papers report what didn't work, and MIDI-LLM offers two honest negative results that are arguably more interesting than the wins — because they puncture assumptions the design was built on.
Why might this happen? Think about the information balance. For pure text-to-MIDI generation, the prompt is the only signal — the model must lean on it. For infilling, the surrounding notes are an enormously strong, low-entropy constraint (music is locally very predictable), so the cheap, reliable strategy is "match the neighbors" and treat the text as a minor nudge. The model learned the path of least loss.
The authors are appropriately cautious — they flag confounders (the two corpora differ in curation quality, not just topic). But the implication is provocative: maybe the LLM's musical world-knowledge is already baked in from its original pretraining, and the continued-pretraining text mostly serves to keep the language abilities from degrading while the model learns MIDI syntax — not to "teach music." Both findings are explicitly flagged as open directions for future work.
Drag the slider for how much surrounding MIDI context the model sees around a gap. With little context (pure generation, left) the text prompt dominates the decision; as context grows (infilling, right) the surrounding notes take over and the prompt's influence collapses — the paper's negative finding, visualized as a tug-of-war.
MIDI-LLM sits at the meeting point of two lineages: the symbolic-music transformers (Music Transformer → AMT → MIDI-LLM) and the "adapt a general LLM to a new modality by expanding its vocabulary" pattern that recurs across speech, code, and now music.
| Idea | Rule / value | What it means |
|---|---|---|
| Goal | text → editable MIDI | audio's text control + symbolic's editability + LLM ecosystem |
| Tokenization | AMT arrival-time, 3 tokens/note | arrival, duration, joint instrument-pitch; no beat-grid needed |
| Vocab sizes | 128K text + 55K music = 183K | 55K includes a doubled "anticipated" set for infilling |
| Vocab expansion | E = [ELLMT EAMTT]T | stack random music rows onto copied text rows; shared D=2048 |
| Backbone | Llama 3.2 1B (1.47B after expand) | structure preserved → vLLM works unchanged |
| Training | 2 stages, next-token CE | (1) continued pretrain on text+MIDI; (2) finetune on caption→MIDI |
| Generation | prompt prefix + AR decode, top-p 0.98 | text is just the start of one shared sequence |
| Speed | vLLM + paged attn + FP8 | RTF 4.0 (FP8, bsz1) vs Text2midi 0.56 |
| Quality | FAD 0.173, CLAP 22.1 | beats Text2midi (0.818 / 18.7) on both |
MIDI-LLM turns a text LLM into a text-to-MIDI generator by expanding its vocabulary: stack 55K AMT music tokens (3 per note — arrival, duration, instrument-pitch) onto Llama 3.2 1B's 128K text tokens, sharing the same 2048-d embedding space. Train in two stages with plain next-token loss — first continued pretraining on music-adjacent text + standalone MIDI to learn MIDI syntax, then supervised finetuning on caption→MIDI pairs (tripled via AMT infilling augmentation). At inference the text prompt is simply the prefix of one shared sequence the model continues with music tokens (nucleus sampling, p=0.98). Because the model is a vanilla Llama decoder, it serves on vLLM with paged attention and FP8, hitting real-time factor ~4 — versus 0.56 for the custom Text2midi baseline — while scoring far better on FAD (quality) and CLAP (text control). Two honest negatives: infilling mostly ignores the text, and music-adjacent pretraining text didn't measurably help.