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)
- Optional navigation markers (
--annotate): one numbered, color-matched rehearsal mark per violation group - 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}] [--annotate] 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. |
--annotate |
Insert a numbered, color-matched rehearsal mark (‖1, ‖2, …) above the first note of each violation group, to locate highlights in large scores. In MuseScore these appear in the Timeline panel as clickable jump targets. |
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.8+ (uses the walrus operator)
- No third-party packages
pip install pytest pytest-cov
pytest tests/ -v --cov=conseq --cov-report=term-missing175 tests, 99% coverage.
MIT