Spaces:
Sleeping
Sleeping
| # 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. | |