Shih-Lun Wu, Yoon Kim, Cheng-Zhi Anna Huang (MIT), 2025

MIDI-LLM: Adapting Large Language Models
for Text-to-MIDI Music Generation

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.

Prerequisites: Next-token prediction (LLMs) + What MIDI is
10
Chapters
5+
Simulations

Chapter 0: The Problem

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.

The gap, stated plainly: Audio models have excellent free-form text control but produce uneditable output. Symbolic (MIDI) models produce editable output but historically had weak text control and ran on bespoke architectures that were slow and awkward to deploy. MIDI-LLM's goal: get the editability of symbolic music and the text control of audio models and fast, off-the-shelf serving — all at once.

Why the prior symbolic models fell short

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":

A worked sense of scale

Here is the tension in numbers, taken from the paper's results table — keep these in your head, we will explain every one:

QuantityText2midi (baseline)MIDI-LLM (this paper)
Parameters0.27 B1.47 B
Quality FAD ↓ (lower better)0.8180.173
Text relevance CLAP ↑ (higher better)18.722.1
Time for a 2K-token clip (bsz=1)47.0 s10.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.

Why prefer symbolic (MIDI) output over audio output for a music co-creation tool?

Chapter 1: The Key Insight

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:

world knowledge
An LLM has already read millions of words about music — what "melancholic," "bebop," or "Mozart" means. That semantic knowledge is exactly what you need to condition on a text prompt.
ecosystem
If you keep the LLM's exact transformer structure, every serving optimization (vLLM, paged attention, FP8) works out of the box. No custom kernels.
The insight: Music generation and text generation are the same task — next-token prediction over a sequence. The only thing that differs is the vocabulary. So instead of inventing a music architecture, expand the LLM's token vocabulary to include MIDI tokens, then keep training the same model with the same next-token objective. Text tokens and music tokens become citizens of one shared sequence.

What the whole recipe is, in one breath

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.

Why "preserve the parameter structure" is the load-bearing decision: The authors are explicit that they instantiate the model from HuggingFace's 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.
What is MIDI-LLM's core strategy?

Chapter 2: MIDI Tokenization — Notes as Tokens

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.

The key idea — one note = exactly three tokens: AMT represents every musical note as three consecutive events: (1) arrival time — when the note starts, (2) duration — how long it's held, (3) instrument-pitch — a single joint token saying both which instrument and which pitch. Read three tokens, you have one complete note. This fixed 3-token rhythm is what makes the sequence regular enough for an LLM to learn.

The three token types, with their ranges

PositionWhat it encodesRange / quantization
1. Arrival timeThe note's onset (absolute start)0 to 100 s, in 10 ms steps
2. DurationHow long the note is held0 to 10 s, in 10 ms steps
3. Instrument-pitchJoint: which instrument and which pitch129 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.

Counting the vocabulary — a worked example

Let's count exactly where AMT's 55K music tokens come from, because the number reappears in the next chapter:

ComponentCountArithmetic
Arrival-time values~10,000100 s ÷ 10 ms = 10,000 steps
Duration values~1,00010 s ÷ 10 ms = 1,000 steps
Instrument-pitch values16,512129 inst × 128 pitch
Subtotal (one "voice")~27.5 Ksum of the above
+ "anticipated" copies~27.5 Ka 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.

AMT Tokenizer — Watch a Note Become 3 Tokens

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.

In AMT arrival-time tokenization, how many tokens represent a single note, and what are they?

Chapter 3: Vocabulary Expansion

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 fix: Give each MIDI token its own dedicated entry in the model's vocabulary. Then one note is exactly three tokens to the LLM (not a dozen), and the model can attend to "piano C4" as a single atomic unit instead of reconstructing it from text fragments. You do this by literally widening the embedding matrix.

The one equation in the paper, decoded

The token-embedding matrix of the LLM is ELLM. We stack a new block of music embeddings on top of it:

EMIDI-LLM = [ ELLMT   EAMTT ]T  ∈  R(|VLLM| + |VAMT|) × D

Symbol by symbol, with the plain meaning:

Embedding Matrix — Stacking 55K Music Rows onto 128K Text Rows

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.

What also changes — the output head

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.

python
# 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.
Why expand the embedding matrix with dedicated MIDI tokens instead of serializing MIDI as text strings?

Chapter 4: Generation (the showcase)

Now we can watch the model actually work. At inference time, the recipe is exactly like prompting a chatbot — except the "answer" is music.

  1. The user's text prompt (genre, mood, instrumentation, tempo, key, chords) is tokenized with the text tokens and laid down as a prefix — up to 256 tokens.
  2. The model then autoregressively emits music tokens, one at a time, each conditioned on everything before it: the prompt and all notes generated so far.
  3. Tokens arrive in the fixed 3-token rhythm: arrival, duration, instrument-pitch, arrival, duration, instrument-pitch, ... Every three tokens snap together into one playable note.

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:

p(midi | text) = ∏t p(st | s1, ..., st−1)

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.

Why this is the elegant part: because text and MIDI live in one vocabulary and one sequence, conditioning on text is free. You don't need a separate text encoder feeding cross-attention (as Text2midi does with a frozen T5). The prompt is simply the start of the sequence the model continues. The LLM's pretrained understanding of "melancholic" or "violin" is already in its weights and directly shapes the next music token.

Sampling: nucleus (top-p) at p = 0.98

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.

Text-to-MIDI Generation — Decode Note by Note

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.

python
# 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!
How does MIDI-LLM condition generation on the text prompt?

Chapter 5: The Two-Stage Training Recipe

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.

Stage 1 — Continued pretraining (~3 B tokens)

Two goals, the paper states them explicitly:

Stage 2 — Supervised finetuning (~1.7 B tokens, 5.1 B after augmentation)

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.

The clever data augmentation: AMT natively supports infilling via its anticipated tokens (Ch 2). So the authors construct extra training examples where the model must fill a gap given surrounding notes — and they use Qwen2.5-Omni (an LLM that can caption music) to write text prompts for these synthetic examples. This roughly triples the finetuning data (1.7 B → 5.1 B tokens) and adds textual diversity for free.
Two-Stage Pipeline — Click a Stage to Inspect Its Data
Click a stage box to see what data flows in and what the model learns.

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.

The training facts, for the record

SettingValueWhy it matters
BackboneLlama 3.2 1BSmall enough to be cheap, big enough to carry world knowledge
OptimizerAdamW, lr 2×10−4, cosine decayStandard LLM finetuning recipe
Precision / attentionBF16, FlashAttention-2Memory- and compute-efficient training
Steps25K per stageBoth stages get equal gradient budget
Sequence length2048 (≈ 30 s music)The model "sees" ~30 s of context
Hardware / time4× H100, ~6 days totalModest by LLM standards — the point of starting from a pretrained 1B
What is the purpose of Stage 1 (continued pretraining) before the paired finetuning?

Chapter 6: Fast Inference — Cashing the Ecosystem Dividend

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.

Paged-attention KV cache
Stores past keys/values in non-contiguous "pages" of GPU memory, like virtual memory for the attention cache — far less memory waste, so you fit bigger batches and run faster.
FP8 W8A8 quantization
Cast weights and activations to 8-bit floats. ~50% memory savings (in theory) and another ~20% speedup, for only a modest quality dip.
CUDA graphs
Capture the repeated decode step as a single replayable GPU graph, killing per-step kernel-launch overhead — which dominates when you emit thousands of tiny tokens.
Why this matters specifically for MIDI: generation is autoregressive and a 30-second clip is ~2048 tokens — that's thousands of sequential decode steps, each tiny. The bottleneck is not raw FLOPs, it's overhead per step and memory pressure from the KV cache. Paged attention and CUDA graphs attack exactly those. The paper reports these techniques accelerated inference by >50% over the plain HuggingFace transformers setup.

Reading the speed numbers — the real-time factor (RTF)

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 / precisionTime (bsz=1)Avg outputRTF (bsz=1)RTF (bsz=4)
Text2midi (FP32)47.0 s26.3 s0.561.06
MIDI-LLM (BF16)10.0 s33.3 s3.3311.48
MIDI-LLM (FP8)8.1 s32.6 s4.0214.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.

Inference Speed — Real-Time Factor by Model & Batch Size

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.

An FP8 MIDI-LLM generates 32.6 s of music in 8.1 s. What is its real-time factor, and what does it mean?

Chapter 7: Experiments & Results

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:

FAD (Fréchet Audio Distance), lower is better. Synthesize your generations to audio, push them through a fixed audio feature extractor (VGGish), and compute the Fréchet distance between the feature distribution of your generations and that of a real ground-truth set. It roughly measures "do these sound like real music?" — overall realism/quality. A FAD of 0.173 vs 0.818 means MIDI-LLM's outputs cluster much closer to real music in feature space.
CLAP score, higher is better. CLAP is a contrastively trained pair of encoders — one for text, one for audio — that map both into a shared space. Feed the prompt to the text encoder and the generated audio to the audio encoder; their cosine similarity says "does the audio match what the text asked for?" This is the direct measure of text control. MIDI-LLM scores 22.1 vs 18.7 — stronger prompt adherence.

The headline table (MidiCaps test subset, 896 samples)

ModelParamsFAD ↓CLAP ↑
Text2midi0.27 B0.81818.7
MIDI-LLM (BF16)1.47 B0.17322.1
MIDI-LLM (FP8)1.47 B0.21621.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.

Quality & Text Control — MIDI-LLM vs Text2midi

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.

What do the FAD and CLAP metrics respectively measure?

Chapter 8: The Negative Findings

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.

Negative finding #1 — infilling ignores the text. Recall that Stage 2 trained on infilling examples paired with text prompts (Ch 5). Yet at inference, when the model fills a gap in the middle of a piece, the text has minimal influence — the infilled notes are "primarily determined by the surrounding MIDI context." In other words, when the model can see the notes before and after a hole, the musical context overwhelms the prompt. The text steering largely evaporates for infilling.

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.

Negative finding #2 — music-adjacent text may not be necessary. Stage 1 deliberately used music-adjacent text (MusicPile) on the theory that it would surface the LLM's musical knowledge. But when the authors swapped MusicPile for general-domain text (FineWeb-Edu) in continued pretraining, they saw no noticeable change in final text-to-MIDI performance. The careful curation of music-specific text may not have mattered.

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.

Negative Finding #1 — Text Influence Fades for Infilling

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.

What did the authors observe about text control during MIDI infilling?

Chapter 9: Connections & Cheat Sheet

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.

2019
Music Transformer / REMI — symbolic music as a token sequence for transformers
2024
Anticipatory Music Transformer (AMT) — arrival-time tokens, infilling via anticipated tokens (the tokenizer MIDI-LLM adopts)
2024
MidiCaps — large MIDI dataset with text captions (the paired finetuning data)
2025
MIDI-LLM — expand a text LLM's vocabulary with MIDI tokens; text control + editability + vLLM-fast serving
future
Preference tuning (RLHF/DPO) from demo feedback; text-guided editing; musician co-creation

The cheat sheet — every key fact

IdeaRule / valueWhat it means
Goaltext → editable MIDIaudio's text control + symbolic's editability + LLM ecosystem
TokenizationAMT arrival-time, 3 tokens/notearrival, duration, joint instrument-pitch; no beat-grid needed
Vocab sizes128K text + 55K music = 183K55K includes a doubled "anticipated" set for infilling
Vocab expansionE = [ELLMT EAMTT]Tstack random music rows onto copied text rows; shared D=2048
BackboneLlama 3.2 1B (1.47B after expand)structure preserved → vLLM works unchanged
Training2 stages, next-token CE(1) continued pretrain on text+MIDI; (2) finetune on caption→MIDI
Generationprompt prefix + AR decode, top-p 0.98text is just the start of one shared sequence
SpeedvLLM + paged attn + FP8RTF 4.0 (FP8, bsz1) vs Text2midi 0.56
QualityFAD 0.173, CLAP 22.1beats Text2midi (0.818 / 18.7) on both

The one-paragraph summary you could give on a whiteboard

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.

"By preserving the original LLM's parameter structure, we can directly leverage the vLLM library for accelerated inference."
— Wu, Kim & Huang, MIDI-LLM (2025)