# MusES MusES is a Python library for representing and transforming musical objects: notes, temporal collections, multi-track pieces, realized chords, scales, chord names, and small generation-oriented workflows. It is deliberately more oriented toward **creating, editing, and generating** music than toward large-scale symbolic analysis. MusES keeps common construction, pitch/key handling, and MIDI-editing operations lightweight. The project continues a line of MusES implementations in Smalltalk, Java, and Python. It is still evolving, so the current priority is to make the core object model reliable, documented, and pleasant to use from other projects. ## What It Provides - `TemporalNote`: a MIDI pitch with start beat, duration, velocity, and channel. - `TemporalCollection`: an ordered stream of temporal objects, useful for melodies, bass lines, chord tracks, or generated material. - `Piece`: a multi-track musical object with MIDI load/save support. - `RealizedChord`: a temporal sonority with one shared span and several pitches. - MusicXML export for simple notatable `Piece` objects. - `ChordNamer`: jazz/pop chord naming from MIDI pitch sets. - Lightweight pitch helpers for pitch names, pitch classes, MIDI conversion, and simple intervals. - `Scale`, `MajorScale`, `HarmonicMinorScale`, `MelodicMinorScale`: scale objects for pitch generation and compatibility checks. - Transformations such as transposition, stretching, reversing, concatenation, soprano extraction, negative harmony, and chordification. ## Installation From the repository root: ```bash python3 -m venv venv source venv/bin/activate python3 -m pip install -e . ``` If you use `uv`: ```bash uv sync uv run pytest -q ``` ## Quick Examples Create a melody and write it to MIDI: ```python from muses.base.temporals import Piece, TemporalCollection melody = TemporalCollection(name="phrase", instrument="piano") melody.insert_note(60, start=0.0, dur=1.0) melody.insert_note(64, start=1.0, dur=1.0) melody.insert_note(67, start=2.0, dur=1.0) piece = Piece(name="demo", melodies=[melody], key_signature="C") piece.save_midi("demo.mid") ``` Export the same material as MusicXML for notation tools: ```python from muses.base.temporals import Piece, TemporalCollection from muses.io import write_musicxml melody = TemporalCollection(name="phrase", instrument="piano") melody.insert_note(60, start=0.0, dur=1.0) melody.insert_note(64, start=1.0, dur=0.5) melody.insert_note(67, start=1.5, dur=0.5) piece = Piece(title="Demo", composer="MusES", melodies=[melody], key_signature="C") write_musicxml(piece, "demo.musicxml") ``` MusicXML exports are written as self-contained score excerpts: the final measure is padded with rests when needed and ends with a final barline. Load a MIDI file and create its negative harmony: ```python from muses.base.temporals import Piece piece = Piece.load_midi("data/prelude_c.mid") negative = piece.negative_harmony(tonic=60, adjust_octaves=True) negative.save_midi("data/prelude_c_negative.mid") ``` Name scale-tone chords: ```python from muses.base.chord_names import ChordNamer from muses.base.scales import MajorScale namer = ChordNamer("data/all_chords_C.txt", max_polyphony=5) for chord in MajorScale("D").get_scale_tone_chords(4): print(chord, namer.name_for(chord)) ``` Chordify a multi-track piece into temporal sonorities: ```python from muses.base.temporals import Piece piece = Piece.load_midi("data/nice_chords.mid") chords = piece.chordify_collection(min_notes=2) for chord in chords.temporals: print(chord.start_beat, chord.duration(), chord.get_pitches()) chords.save_midi("data/chordified.mid") ``` ## Examples The [`examples/`](examples/README.md) directory contains small runnable scripts: - `examples/harmony/analyze_progression.py`: name chords in a progression. - `examples/harmony/compare_tonality_methods.py`: compare the former transition-only DP analysis with tonal-parsimony analysis on the corpus. - `examples/harmony/tonality_path_analysis.py`: assign a parsimonious tonality path to a progression or a slice of `data/chord_sequences.txt`. - `examples/harmony/generate_tonal_parsimony_sequences.py`: use the sibling `vo_regular_bp` project to generate endpoint-constrained chord sequences and cluster them by post-analysis tonal-parsimony K. - `examples/harmony/name_midi_chords.py`: split and name chords from MIDI. - `examples/harmony/negative_harmony.py`: mirror a phrase through negative harmony and optionally save it as MIDI. - `examples/harmony/scale_detection.py`: find candidate scales for a melody. - `examples/harmony/soprano_extraction.py`: extract a top voice from MIDI. - `examples/harmony/pitch_profile_weights.py`: inspect pitch-profile weights. - `examples/generation/scale_phrase.py`: generate a phrase from a scale. - `examples/generation/polya_melody.py`: generate a phrase with the experimental Polya urn. - `examples/midi/transform_midi.py`: load, transform, and optionally save MIDI. - `examples/quickstart.py`: small end-to-end workflow. - `examples/interaction/harmonizer_setup.py`: inspect MIDI ports and bootstrap an interactive harmonizer session. Run them with: ```bash uv run python examples/harmony/negative_harmony.py ``` ## Tonality Analysis Layers The harmonic-analysis code is intentionally split into layers. The detailed guide is in [`src/muses/analysis/README.md`](src/muses/analysis/README.md). The current full-corpus comparison report is in [`docs/tonality_analysis_comparison.md`](docs/tonality_analysis_comparison.md). - `algos.dynaprog.VariableDomainSequenceOptimizer`: generic variable-domain sequence DP. It knows nothing about chords, scales, or tonalities. - `muses.base.tonality_path_analyzer.best_tonality_path`: the core tonal-parsimony solver, minimizing modulations and then distinct tonalities. - `muses.base.chord_sequence_analyzer.ChordSequenceAnalyzer`: the backward-compatible simple facade for one-off chord sequence analysis. - `muses.analysis.tonality_methods`: named analysis methods that can be compared side by side, currently transition-only DP, pure NValue hitting set, and tonal parsimony. - `muses.analysis.tonality_corpus`: corpus parsing, chord-name-to-domain construction, and reusable multi-method comparison utilities. - `muses.analysis.expert_jazz_bench`: curated mDecks expert-jazz cases with both functional-root and chord-scale agreement scores. - `muses.analysis.mdecks_pdf_extractor`: local extraction pipeline for user-owned mDecks PDFs, producing reusable chord sequences plus chord-scale and beat-position metadata. - `examples/harmony/*.py`: command-line entry points that demonstrate or report on the analysis methods without defining the core algorithms. For adding another method, start with `muses.analysis.tonality_methods`; the corpus comparison script is method-list driven, so it can grow without changing the corpus parsing or reporting utilities. ## Design Direction MusES aims to be a compact musical object library for generative systems: - Keep the core temporal model simple and explicit. - Prefer transformations that return new musical objects instead of mutating inputs unexpectedly. - Preserve enough MIDI metadata to make load/transform/save workflows reliable. - Keep chord objects temporal without duplicating onset/duration on every pitch. - Keep experimental generation algorithms available, but separate from the stable object model. - Provide examples that can be copied into creative coding projects. Compared with `music21`, MusES is not trying to cover all musicological analysis. It is meant to make musical material easy to construct, transform, generate, and send back to MIDI. Experimental madrigal-to-string-quartet reduction code now lives in a separate `gesualdo` project next to this repository, so it can evolve as research code without adding heavy notation dependencies to the core package. ## Development Run the tests: ```bash uv run pytest -q ``` The current tests focus on temporal collection behavior, MIDI metadata handling, MusicXML export, chord naming, pitch helpers, chordification, and negative harmony. ## Background MusES is inspired by earlier work including: - Pachet, F., Ramalho, G., and Carrive, J. "Representing Temporal Musical Objects and Reasoning in the MusES System." Journal of New Music Research, 25(3):252-275, 1996. - Pachet, F., Ramalho, G., Carrive, J., and Cornic, G. "Representing temporal objects and reasoning in the MusES system." International Congress on Music and Artificial Intelligence, Edinburgh, 1995. - Pachet, F. "The MusES system: an environment for experimenting with knowledge representation techniques in tonal harmony." First Brazilian Symposium on Computer Music, 1994. - Pachet, F. "An Object-Oriented Representation of Pitch-Classes, Intervals, Scales and Chords." Journees d'Informatique Musicale, Bordeaux, 1994. ## Author [Francois Pachet](https://github.com/fpachet) ## License MIT. See `LICENSE` for details.