music21 models notation superbly and models performance not at all, and it does not render audio. That gap is where generated music goes wrong: theoretically correct, audibly dead, or silently broken in ways you cannot hear because you cannot listen.
This skill covers both halves — the library in depth, and the performance and rendering layer that turns a Stream into something a person would play twice.
Setup
pip install music21 # 10.5.0 stable; everything here is verified on it
brew install fluidsynth ffmpeg # or: apt install fluidsynth ffmpeg
python -c "from m21kit import render; print(render.ensure_soundfont())" # ~141MB, once
m21kit/ is this skill's helper library — put it on PYTHONPATH or copy it into the
project. It is plain functions over music21; nothing is subclassed or monkeypatched.
The five rules that matter most
- Give every Part its own MIDI channel. music21 folds Parts that share an instrument
onto one channel, and then one voice's note-off cuts another's identical pitch short.
Always
midiio.write_midi(score, path, channels=[1,2,3,10]). Verify withmidiio.describe_midi. - Percussion goes on channel 10 or it plays as a piano.
Dynamicscales velocity -- including velocity you set yourself. A note withvelocity = 100under aDynamic('pp')renders at 50, because the Dynamic multiplies rather than defers. Shape velocity by hand and keepDynamicobjects out of the Part. Hairpins (Crescendo/Diminuendo) do nothing at all. Default velocity is 90.- Never write below an instrument's real range. A guitar stops at E2 (40). Sampled
patches will render lower notes and they will sound synthetic.
verify.range_check. - You cannot hear it, so measure it. Before you call anything finished, run the
checks in
references/15-verifying-without-listening.md.
Workflow
Composing from scratch
- Decide form, key/mode, meter, tempo and instrumentation in words first. Name the climax. If you cannot say why a section exists, the listener won't find a reason.
- Build flat Parts and
insert()at absolute quarter-length offsets — not Measures +append()— for anything with performance timing. (references/01-object-model.md) - Melody and harmony from
scales/roman/harmony; voicings as real instrument shapes. - Add performance: strums, rolls, tremolo, micro-timing, velocity shaping
(
m21kit.perform), and a groove fromm21kit.drums. render.score_to_mp3(score, out, stems=[...])— one stem per voice, balanced in dB.- Verify. Fix. Re-render.
Arranging or restyling existing material — get the score first (m21kit
scripts/fetch_score.py, or converter.parse on MIDI/MusicXML/kern/ABC), extract the
line you want, then treat it as step 3 above. Quote the source exactly and prove it with
verify.melody_match; the arrangement lives in the accompaniment, compás and articulation.
From audio — scripts/audio_to_score.py transcribes to a music21 Score, then as above.
Reference chapters
Read the one you need; do not read them all.
00-quickstart.md |
shortest path to a working piece |
01-object-model.md |
Streams, offsets, flatten vs recurse, makeNotation, deepcopy |
02-pitch-notes-chords.md |
pitch, notes, chords, velocity, microtones, grace notes |
03-meter-tempo-expression.md |
meter incl. additive, tempo maps, ornaments, articulations |
04-harmony-scales-theory.md |
intervals, keys, custom/non-Western scales, roman numerals, chord symbols, figured bass |
05-io-formats.md |
every parse/write format, tinyNotation, MusicXML, PDF/PNG |
06-midi-deep.md |
channels, percussion, ticks, tempo events, round-trip |
07-audio-to-score.md |
transcription: music21's own, and the modern external pipeline |
08-analysis-and-verification.md |
key finding, chordify, features, self-checking |
09-instruments-and-notation.md |
full instrument/GM/range table, transposing instruments |
10-scores-and-datasets.md |
where to get scores, verified sources |
11-audio-rendering.md |
soundfonts, FluidSynth, stems, mixing, mastering |
12-performance-realism.md |
strums, tremolo, swing, humanisation, voicing |
13-idioms-and-grooves.md |
concrete style recipes across traditions |
14-workflows.md |
end-to-end playbooks |
15-verifying-without-listening.md |
read this before shipping anything |
16-pitfalls.md |
202 recorded traps, consolidated from every chapter |
Scripts
python scripts/analyze_score.py <file|corpus-path> # key, chords, form, features
python scripts/audio_to_score.py in.mp3 -o out.mid # audio -> music21
python scripts/fetch_score.py --corpus bach/bwv66.6 # get source material
python scripts/check_ranges.py score.mid --instrument Violin
python scripts/render_audio.py score.mid -o out.mp3 # MIDI -> mastered mp3
python scripts/verify_music.py out.mp3 --midi out.mid
python scripts/build_pitfalls.py # regenerate ch.16 after editing a chapter
python scripts/new_project.py my-piece # scaffold a composition
Honesty
Say that you verified numerically and could not listen. Do not imply you heard it. When
you quote existing music, say what is the source's and what is yours. When a tradition's
rhythm is non-metric (an adhan, an alap, a taranta), do not force it into a compás and
call it faithful — verify.pulse will show you whether you did.