Voice-Leading and Harmonic Spot-Checks with LLMs (Without the AI Slop)
September 2026 — Labs & Notebooks
Most talk about AI and music revolves around “prompt-to-audio”
generators (Suno, Udio), which isn't what serious composition or theory
work is about.
Where LLMs (Claude, ChatGPT, Gemini) actually shine is as an on-demand
theory sounding board—spot-checking counterpoint, flagging awkward
voice crossings, or analyzing harmonic motion. The problem has always been
how to get your musical ideas into the chat:
-
Descriptive Prose: Imprecise and tedious. Trying to type
out something like: “In bar 3 the strings play an open C triad
while the oboe holds an E5 before moving down to D on the and-of-two...”
takes forever and leaves out vital context.
-
Screenshots (Piano Roll / Notation): Vision models are
probabilistic. They frequently miss accidentals, misread ledger lines, or
botch tight rhythmic subdivisions.
-
Symbolic Data (JSON / Extracted Events): Deterministic
ground truth. The model gets exact pitches, bar/beat locations, and
durations. While standard MusicXML provides this too, it's notoriously
bloated with layout markup—clean JSON gives the model pure musical
data at a fraction of the token cost.
The Discussion Process (Try It Yourself)
Here's a clean 4-bar passage (a standard ii–V–I–VI in C
with rootless 4-note voicings under a lead melody).
To see how an LLM handles raw symbolic data, copy the JSON block below,
paste it into your model of choice, and give it a simple prompt:
“Analyze the voice leading and harmonic relationship between the
melody and chord voicings in this 4-bar passage.”
4-Bar Passage — JSON
{
"meter": "4/4",
"tempo": 120.0,
"tracks": [
{
"name": "01-Melody Lead",
"notes": [
{"bar": 1, "beat": 1.00, "pitch": "E5", "len": 2.00, "vel": 96},
{"bar": 1, "beat": 3.00, "pitch": "F5", "len": 1.00, "vel": 92},
{"bar": 1, "beat": 4.00, "pitch": "D5", "len": 1.00, "vel": 88},
{"bar": 2, "beat": 1.00, "pitch": "E5", "len": 1.00, "vel": 94},
{"bar": 2, "beat": 2.00, "pitch": "D5", "len": 1.00, "vel": 90},
{"bar": 2, "beat": 3.00, "pitch": "B4", "len": 2.00, "vel": 98},
{"bar": 3, "beat": 1.00, "pitch": "C5", "len": 2.00, "vel": 96},
{"bar": 3, "beat": 3.00, "pitch": "D5", "len": 1.00, "vel": 88},
{"bar": 3, "beat": 4.00, "pitch": "E5", "len": 1.00, "vel": 92},
{"bar": 4, "beat": 1.00, "pitch": "G5", "len": 2.00, "vel": 100},
{"bar": 4, "beat": 3.00, "pitch": "F5", "len": 1.00, "vel": 94},
{"bar": 4, "beat": 4.00, "pitch": "C#5", "len": 1.00, "vel": 90}
]
},
{
"name": "02-Harmony Keys",
"notes": [
{"bar": 1, "beat": 1.00, "pitch": "F3", "len": 4.00, "vel": 80},
{"bar": 1, "beat": 1.00, "pitch": "C4", "len": 4.00, "vel": 78},
{"bar": 1, "beat": 1.00, "pitch": "E4", "len": 4.00, "vel": 82},
{"bar": 1, "beat": 1.00, "pitch": "A4", "len": 4.00, "vel": 84},
{"bar": 2, "beat": 1.00, "pitch": "F3", "len": 4.00, "vel": 80},
{"bar": 2, "beat": 1.00, "pitch": "B3", "len": 4.00, "vel": 78},
{"bar": 2, "beat": 1.00, "pitch": "E4", "len": 4.00, "vel": 82},
{"bar": 2, "beat": 1.00, "pitch": "A4", "len": 4.00, "vel": 84},
{"bar": 3, "beat": 1.00, "pitch": "E3", "len": 4.00, "vel": 76},
{"bar": 3, "beat": 1.00, "pitch": "B3", "len": 4.00, "vel": 74},
{"bar": 3, "beat": 1.00, "pitch": "D4", "len": 4.00, "vel": 78},
{"bar": 3, "beat": 1.00, "pitch": "G4", "len": 4.00, "vel": 80},
{"bar": 4, "beat": 1.00, "pitch": "E3", "len": 4.00, "vel": 82},
{"bar": 4, "beat": 1.00, "pitch": "G3", "len": 4.00, "vel": 80},
{"bar": 4, "beat": 1.00, "pitch": "C#4", "len": 4.00, "vel": 84},
{"bar": 4, "beat": 1.00, "pitch": "Bb4", "len": 4.00, "vel": 86}
]
}
]
}
Copy / Paste From Your DAW
In my own writing setup, I've got a simple shortcut mapped in REAPER:
select a few bars across a couple of tracks, hit a key, and it copies this
lean JSON structure straight to the clipboard so I can paste it directly
into a chat window mid-composition.
If you try running the test above, how did the theoretical breakdown match
what you expected? And if anyone else is experimenting with pulling
symbolic MIDI data into LLMs—in REAPER or another DAW—I'd love
to compare notes.
Note: I used an LLM as an editorial assistant to help organize and
format my thoughts for this post.