Detect consecutive perfect intervals (parallel fifths, parallel octaves) in MusicXML scores and colorize the offending notes.
Consecutive perfect intervals are a classic voice-leading prohibition in counterpoint. conseq highlights them so you can review or correct them in your notation software.
- Detects parallel fifths, parallel octaves, or both
- Handles rest look-through: violations across a shared rest gap are flagged even if both voices briefly fall silent
- Works across multiple parts and staves (grand staff, SATB, etc.)
- Independent violation groups get distinct colors (up to 8; uses a matplotlib tab10 palette)
- Violations that share a note element are merged into one color group via union-find
- Excludes grace notes (ornamental) and same-voice octave doublings (piano reinforcement)
- Preserves the original XML prologue verbatim (declaration, DOCTYPE)
- Reads/writes stdin and stdout with
- - No runtime dependencies — stdlib only
python conseq.py [--interval {fifths,octaves,both}] input.xml output.xml
| Argument | Description |
|---|---|
input.xml |
Input MusicXML file. Use - to read from stdin. |
output.xml |
Output MusicXML file. Use - to write to stdout. |
--interval |
Which interval type to detect: fifths (default), octaves, or both. |
After processing, a summary line is printed to stderr:
4 note(s) in 2 consecutive-fifth group(s) colorized.
Check for parallel fifths and write a colorized copy:
python conseq.py piece.xml piece_colored.xmlCheck for both parallel fifths and octaves:
python conseq.py --interval both piece.xml piece_colored.xmlPipe through stdin/stdout:
python conseq.py - - < piece.xml > piece_colored.xmlOpen the output in any MusicXML-aware renderer (MuseScore, Sibelius, Dorico, Finale, Flat.io, etc.) to see the colorized notes.
-
Collect notes — Traverse the MusicXML tree, converting each pitched non-grace note into a
NoteInfowith onset/offset times (as exactFractionvalues), MIDI pitch, and a voice key(part_id, staff_id, voice_id). -
Build a voice index — Group notes by attack time and release time per voice. Precompute sorted structures for efficient lookback queries.
-
Detect violations — For each time point where a perfect interval begins, check two cases:
- Direct boundary: the same voice pair ended the same interval type immediately before.
- Rest look-through: both voices previously sounded that interval, then both fell silent without any intervening attack in either voice, and both re-enter with the same interval.
-
Merge groups — Notes that share an element across violations are unioned via union-find so they receive the same color.
-
Colorize — Set the
colorattribute on each flagged<note>element (and its<stem>/<notehead>children if present) and write the modified XML.
- Python 3.9+ (uses
NamedTuple,Fraction, walrus operator) - No third-party packages
pip install pytest pytest-cov
pytest tests/ -v --cov=conseq --cov-report=term-missing160 tests, 99% coverage.
MIT