A comprehensive music theory library and CLI for Rust
This library covers music-theoretic fundamentals through graduate-level theory: notes, intervals, chords, scales, harmony analysis, voice leading, neo-Riemannian transformations, pitch-class set theory, counterpoint, and figured bass.
Every music theory fact in the library has been verified against 4,315 concept cards from 14 authoritative textbooks.
Note that this project started as a fork of Ozan Kaşıkçı's excellent library.
[dependencies]
music-comp-mt = "0.4"use music_comp_mt::note::{Notes, Pitch, PitchSymbol::*};
use music_comp_mt::chord::{Chord, Quality, Number};
use music_comp_mt::scale::{Scale, ScaleType, Mode, Direction};
use music_comp_mt::interval::Interval;
// Notes and chords
let chord = Chord::new(Pitch::from(C), Quality::Major, Number::Triad);
assert_eq!(chord.format_notes(), "Notes:\n 1: C\n 2: E\n 3: G\n");
// Correct enharmonic spelling everywhere
let gm = Chord::new(Pitch::from(G), Quality::Minor, Number::Triad);
let notes = gm.notes(); // G, Bb, D — not G, A#, D
// Identify chords from notes
let matches = Chord::identify(&[Pitch::from(E), Pitch::from(G), Pitch::from(C)]);
// Finds: C major, first inversion
// Calculate intervals between pitches (letter-aware)
let interval = Interval::between(
&Pitch::from(F),
&Pitch::from(B),
).unwrap();
// Augmented 4th (not diminished 5th — letters matter)
// Find scales containing a set of notes
let scales = Scale::identify(&[
Pitch::from(C), Pitch::from(D), Pitch::from(E),
Pitch::from(Fs), Pitch::from(G), Pitch::from(A), Pitch::from(B),
]);
// Matches: C Lydian, G Ionian, ...cargo install music-comp-mt-cliAfter building with make build, the binary is at ./bin/mt:
$ ./bin/mt scale C Ionian
Notes:
1: C
2: D
3: E
4: F
5: G
6: A
7: B
8: C
$ ./bin/mt chord G "dominant seventh"
Notes:
1: G
2: B
3: D
4: F
$ ./bin/mt scale D Locrian
Notes:
1: D
2: Eb
3: F
4: G
5: Ab
6: Bb
7: C
8: D
$ ./bin/mt scale list
$ ./bin/mt chord list| Module | Description |
|---|---|
note |
Pitch, Note, NoteLetter, PitchSymbol, KeySignature, enharmonic equivalence, transposition |
interval |
Simple and compound intervals (0-24 semitones), quality/number classification, letter-aware between(), inversion |
chord |
22+ chord types, letter-based spelling, identification with inversion detection, regex parsing |
scale |
8 scale types, 14 modes, identification from notes, ascending/descending support |
| Module | Description |
|---|---|
harmony |
Diatonic triads/sevenths, common tones, chord-scale compatibility, pivot chords |
analysis |
Roman numeral labeling (I, vi, V7, vii°, etc.), secondary dominant detection (V/x) |
voice_leading |
Optimal voice assignment minimizing total semitone movement |
| Module | Description |
|---|---|
neo_riemannian |
P, R, L operations on triads with chaining |
set_class |
PitchClassSet with normal/prime form, T_n, I_n, interval vector, Forte numbers |
counterpoint |
First-species rule checking (parallel 5ths/8ves, consonance, voice crossing) |
figured_bass |
Realize figured bass symbols into chord voicings |
| Flag | Description |
|---|---|
midi |
Enables Note::midi_pitch() for MIDI pitch number conversion |
serde |
Derives Serialize/Deserialize on all public types |
music-comp-mt = { version = "0.4", features = ["serde", "midi"] }use music_comp_mt::harmony;
use music_comp_mt::analysis;
use music_comp_mt::note::{Pitch, PitchSymbol::*};
use music_comp_mt::chord::{Chord, Quality, Number};
use music_comp_mt::scale::Mode;
// Diatonic chords in C major
let chords = harmony::diatonic_triads(Pitch::from(C), Mode::Ionian);
// I=C maj, ii=D min, iii=E min, IV=F maj, V=G maj, vi=A min, vii°=B dim
// Roman numeral analysis
let g7 = Chord::new(Pitch::from(G), Quality::Dominant, Number::Seventh);
let rn = analysis::roman_numeral(Pitch::from(C), Mode::Ionian, &g7).unwrap();
assert_eq!(rn.label, "V7");
// Secondary dominant detection
let d_major = Chord::new(Pitch::from(D), Quality::Major, Number::Triad);
let sd = analysis::secondary_dominant(Pitch::from(C), Mode::Ionian, &d_major).unwrap();
assert_eq!(sd.label, "V/V");
// Pivot chords between C major and G major
let pivots = harmony::pivot_chords(
Pitch::from(C), Mode::Ionian,
Pitch::from(G), Mode::Ionian,
);
// G major is V in C, I in G; C major is I in C, IV in G; etc.use music_comp_mt::neo_riemannian::{transform, transform_chain, NROperation};
use music_comp_mt::chord::{Chord, Quality, Number};
use music_comp_mt::note::{Pitch, PitchSymbol::*};
let c_major = Chord::new(Pitch::from(C), Quality::Major, Number::Triad);
// P (Parallel): C major → C minor
let c_minor = transform(&c_major, NROperation::P).unwrap();
// R (Relative): C major → A minor
let a_minor = transform(&c_major, NROperation::R).unwrap();
// Chain operations: C major → P → R → L
let path = transform_chain(&c_major, &[NROperation::P, NROperation::R, NROperation::L]).unwrap();use music_comp_mt::set_class::PitchClassSet;
let major_triad = PitchClassSet::new(&[0, 4, 7]);
assert_eq!(major_triad.prime_form(), vec![0, 3, 7]);
assert_eq!(major_triad.forte_number(), Some("3-11".to_string()));
assert_eq!(major_triad.interval_vector(), [0, 0, 1, 1, 1, 0]);
// Transpose and invert
let transposed = major_triad.transpose(5); // T_5
let inverted = major_triad.invert(0); // I_0git clone https://github.com/music-comp/mt-rs && cd mt-rs
make build # Build library + CLI
make test # Run all 349 tests
make lint # Clippy + fmt (same checks as CI)
make coverage # Generate coverage report (96%+)
make docs # Build rustdoc (warnings as errors)
make check-all # Build + lint + coverage + docsCargo.toml workspace root
mt/ library crate (music-comp-mt)
src/
lib.rs
note/, interval/, chord/, scale/
harmony/, analysis/, voice_leading/
neo_riemannian/, set_class/, counterpoint/, figured_bass/
tests/
mt-cli/ binary crate (music-comp-mt-cli)
src/
main.rs, cli.rs
tests/
MIT