Repository: github.com/LuisMRaimundo/Intervallic_Homogeneity
Streamlit app for intervallic homogeneity in notated pitch aggregates (non-octave-equivalent intervals), from manual note entry or MusicXML — including H-over-time passage profiles and vertical symbolic cardinality (note-count thickness per slice) for verticality slices.
Semantic scope: symbolic / score-based interval descriptors — not audio, spectral, or psychoacoustic consonance analysis. See docs/METRIC_SEMANTICS.md.
From this directory, install the package in editable mode so the iav package and root shims (pitch_model, analysis_core, interval_analysis) resolve:
pip install -e ".[dev]"[dev] adds pytest, ruff, and mypy. To run the app only: pip install -e .
See installers/README.md:
| Platform | Launcher |
|---|---|
| Windows 10/11 | Double-click installers\windows\Install and Run.bat |
| macOS | Double-click installers/macos/Install and Run.command (after chmod +x) |
| Linux | ./installers/linux/install-and-run.sh |
First run downloads a private Python and libraries; the browser opens at http://localhost:8501.
pip install -e .
streamlit run app.pyUse the sidebar page Which metric to use? to choose headline H (pairwise vs adjacent vs weighted vs hybrid).
Open Analysis presets on the main page and choose ①–④, then Apply preset now (or enable auto-apply). Defaults match ANALYSIS_PRESET.md:
| Setting | Default |
|---|---|
| Headline H | **Proximity-weighted (1/ |
| Homogeneity scoring | Entropy concentration (1 − entropy) (span-normalised); dominance available as diagnostic |
| Thresholds | Standard (label sliders) |
| Pitches | Collapse to unique on the selected EDO grid |
| MusicXML transpose | Chromatic ON (horns, trumpets, clarinets…); octave OFF (double bass, piccolo…) |
For MusicXML passages (many chords over time): use Verticalities or Sounding verticalities with All slices or Time window — not Aggregate (which pools all pitches into one sonority). The app shows an H-over-time chart, a vertical note-count chart, slice_summary.csv, and vertical_cardinality_profile.json (see TECHNICAL_MANUAL.md §7.6–7.8).
python analyze_corpus.py --input examples/corpus --output results/
python analyze_corpus.py --verify-canonical -i examples/corpus -o results/
python analyze_corpus.py -i examples/corpus -o results/ --chromatic-transpositions apply --octave-transpositions ignoreAfter pip install -e ., the same entry point is available as iav-analyze.
Outputs: results.csv, results.json, config_used.json, log.txt, summary_statistics.csv.
Canonical regression: iav/data/canonical_sonorities.json (13 sonorities) and iav/data/canonical_musicxml.json (5 MusicXML scores) — see tests/test_canonical_sonorities.py, tests/test_canonical_musicxml.py. Regenerate with python scripts/generate_canonical_expectations.py and python scripts/build_canonical_musicxml.py.
Sensitivity tables for thesis: python scripts/sensitivity_report.py → docs/reports/ (generated files are gitignored; see docs/reports/README.md). Notebook: notebooks/sensitivity_report.ipynb.
Annotated pilot (human labels): python scripts/run_annotated_study.py → docs/reports/annotated_pilot/.
- Metric meaning and interpretive limits (H, pair_score, adjacent, EDO, MusicXML): docs/METRIC_SEMANTICS.md
- Contributing / CI: CONTRIBUTING.md
- Validation protocol (human labels): docs/VALIDATION_PROTOCOL.md
- Fixed analysis preset (exact UI labels): ANALYSIS_PRESET.md
- Interface quick guide (non-specialists): QUICK_GUIDE.md
- Code layout / hygiene before zipping: docs/ARCHITECTURE.md
- Full specification (math, algorithms, MusicXML, tutorial): TECHNICAL_MANUAL.md
- MusicXML scope / limitations: docs/MUSICXML_COVERAGE.md
- Release notes: CHANGELOG.md
Install runtime dependencies (see pyproject.toml [project] dependencies) plus pytest — e.g. pip install -e ".[dev]" for everything including ruff and mypy, or pip install pytest plus the runtime list if you only want tests.
Run pytest from this directory (the repository root). pythonpath = ["."] is set in pyproject.toml so collection resolves iav and the root shims (pitch_model, analysis_core, interval_analysis) without requiring pip install -e . first — useful for an unpacked ZIP. An editable install is still recommended for day-to-day work so tools and imports match an installed layout. Typical full suite: 2423 tests (pytest tests -q).
Implementations live under iav/; legacy tests may still import pitch_model / analysis_core / interval_analysis — those modules are thin shims (tests/test_backward_compat_shims.py locks shim ↔ package identity). The chart smoke module (tests/test_charts_smoke.py) imports altair and pandas: if those are not installed, those tests are skipped automatically.
On GitHub, Actions runs .github/workflows/ci.yml on push / pull request: pytest, ruff check, ruff format --check, mypy on iav, canonical sonority + MusicXML verification, batch smoke on examples/corpus/, sensitivity report, annotated pilot, and optional music21 interval-vector comparison (Python 3.10–3.11).
With [dev] installed:
ruff check iav
ruff format iav
mypy(ci.yml uses ruff format --check iav; omit --check locally to apply formatting.)
mypy is configured for the iav package at a pragmatic strictness level (see pyproject.toml).
- Critical: compressing the project folder with Explorer, Finder, or “select all → ZIP” will ship
.venv(~hundreds of MB),.pytest_cache,.ruff_cache,__pycache__, and*.egg-infounless you removed them first. Reviewers treat that as poor release discipline. The only supported handoff path is the script below (which also runsverify_release_zipon the produced archive). - Recommended (controlled deliverable): run
pwsh -File scripts/make_release_zip.ps1(Windows) orsh scripts/make_release_zip.sh(macOS/Linux). This runs the clean step, then writes a source-only.zipnext to the project folder (timestamped name), explicitly excluding.venv, caches,.git,build/,dist/,__pycache__,*.egg-info, and similar. Your local venv is left in place; it is simply omitted from the archive. The script ends withverify_release_zip: if that step does not print “ZIP verification OK”, do not submit the file. Optional path:pwsh -File scripts/make_release_zip.ps1 -OutZip "D:\exports\ia.zip". - POSIX / line endings:
scripts/*.share LF-only (enforced via.gitattributes). Ifsh scripts/make_release_zip.shfails with errors like$'\r': command not found, the checkout has CRLF in those files; use the PowerShell release script on Windows, or re-clone withcore.autocrlf=false/git add --renormalizeafter pulling this fix. - Clean only:
pwsh -File scripts/clean_repo.ps1orsh scripts/clean_repo.sh(seedocs/ARCHITECTURE.md) strips__pycache__, pytest/mypy/ruff caches,build//dist/,*.egg-info, coverage junk, and stray*.docx/*.mhtml..venv/is not removed unless you pass-IncludeVenv/--include-venv(destructive: you must recreate the env).
- Manual input uses a single Note column:
C4,D#3,Eb,C+4,A#+2,Bb-3(octave defaults to 4 if omitted). - MusicXML analysis uses the pitch inventory (
sounding_midi/used_in_metrics). Display names (A#+2) are labels only; unedited rows keep the parser tuples. - MusicXML upload accepts
.xml,.musicxml, or.mxl(compressed). - MusicXML import supports aggregate (single sonority), onset verticalities, or sounding verticalities. Passage analysis: verticality modes + slice summary table + homogeneity timeline chart + vertical cardinality chart + JSON export (§7.6–7.8 of the manual). Manual/aggregate mode also exports
vertical_cardinality_profile.json(single time point). Aggregate on long scores triggers a warning when note count > 50. - Vertical cardinality counts symbolic notes per vertical slice (not acoustic density or registral dispersion); see §7.8 of the manual. The passage chart plots note count only; JSON may include unique-pitch and pitch-class fields when computed — pitch-class values are never inferred from unique-pitch count when the CSV column is absent.
- Interval labels follow the selected EDO grid (e.g.,
P4,m3,TT4in 12-EDO, or150cin microtonal grids); enharmonic spellings are not distinguished. - This is symbolic pitch-distance analysis; it does not model acoustics (spectra/roughness/critical bands).
- MusicXML timing is normalized to quarter-note units using
<divisions>. - EDO selection (12/24/48) controls pitch quantization and interval labels (cents for microtonal grids).
MusicXML <transpose> is classified at load into two classes (the whole interval, including any octave component, stays in that class):
- Chromatic — non-octave shift: Horn in F (−7), Trumpet/Clarinet in Bb (−2), Clarinet in A (−3), Alto sax (−9), Bass clarinet (−14), Tenor sax (−14), …
- Octave — a pure multiple of 12: Double bass (−12), Contrabassoon (−12), Piccolo (+12), and similar encoded octave transpositions.
Two switches (GUI, CLI, API) decide what enters the metrics:
| Switch | Default | Why |
|---|---|---|
Apply chromatic transpositions (apply_chromatic_transpositions, CLI --chromatic-transpositions apply|ignore) |
ON | Notation programs export written pitch for these staves even when the score was entered in concert-pitch view. Applying the shift recovers what you typed. |
Apply octave transpositions (apply_octave_transpositions, CLI --octave-transpositions apply|ignore) |
OFF | Notation programs still display these staves at written octave in concert-pitch view. Leave OFF if you entered the pitches you want to sound; turn ON if you copied the part at its written octave. |
Per-part exceptions: --transposition-exception PART=apply|ignore (repeatable), or the Advanced table in the GUI.
Application is a numeric shift only (sounding_ps = written_ps + semitones) after microtone repair. Written spelling stays in written_name. Metrics, the digest, and the charts use used_ps (sounding if applied, otherwise written). Check the digest against the score: min / max are the extrema of the pitches that entered H.
Deprecated alias: pitch_reference='sounding' / --sounding-transpose applies both classes; pitch_reference='written' ignores both. Transposition is kept across later clef/key/time <attributes> that have no <transpose>. Sidecar schema "2" records the two switches and exceptions so a file is re-analysed the same way in the GUI, CLI, and API.
- Order of operations: parse → glyph repair → accidental/tie propagation → transposition → part overrides → note overrides → tie merge → slicing → dedup → quantisation → metrics. Height is always
pitch_model.chromatic_midi_float. - Glyph /
<alter>: defaultmicrotone_repair='from_accidentals'repairs Sibelius integer<alter>when it is 0.5 from a known microtonal glyph. Arrow/slash/otherglyphs have no agreed size — the note is kept on<alter>and listed for overrides. CLI:--microtone-repair off|warn|from_accidentals. - Pitch inventory (above the charts) lists every note (
note_id, part, measure, display_name,written_ps,sounding_ps,used_ps,transposing_class,transposition_applied, used_in_metrics, flags). The digest (n_notes,n_unique, min/max, span_cents) is the extrema ofused_psand is written to every JSON/CSV export. - Sidecar overrides:
<score>.pitch_overrides.jsonwithpitch_overrides_schema"2"(switches + exceptions + overrides). Schema"1"files still load. Same scheme as Registral_Dispersion / Textural_Cardinality. - Homogeneity (dominance / entropy concentration / consensus) and related labels are custom exploratory heuristics; they are not externally validated standard post-tonal measures (see
TECHNICAL_MANUAL.md§4–5). - Pitch-class / Forte-style tools (§6 of the manual) apply only when every note maps to an integral 12-TET step; they are orthogonal to the spelling printed in a score.
- Validation fixtures are in
validation/fixtures/; additional stress patterns for regression are invalidation/corpus/(see that folder’s README). Runpytestafter install as above.
Copyright © 2026 Luís Raimundo. All rights reserved.
This repository and its contents are proprietary research material. No open-source licence is granted. No permission to copy, redistribute, modify, publish, or derive works without prior written permission from the copyright holder.
Contact: lmr.2020@outlook.pt
Developed with support from FCT and Universidade NOVA de Lisboa (DOI: 10.54499/2020.08817.BD). The author thanks Isabel Pires for her support.