Files
pytheory/pytheory/_statics.py
T
kennethreitz dff12678ba Fix music theory accuracy and core bugs across the library
- Fix Tone.__init__ overwriting explicit octave kwarg when parsing name
- Fix Tone.__eq__ calling names as attribute instead of method, add __hash__
- Fix octave arithmetic to use C-based boundaries (scientific pitch notation)
- Fix pitch() to account for octave (was ignoring it entirely)
- Fix modal scale generation: modes were overwritten due to DEGREES loop bug
- Fix modal offset rotation off-by-one in generate_scale
- Fix scales._scales property being called as function
- Fix chord intervals: major/minor thirds were swapped, added missing chord tones
- Remove broken duplicate NamedChord class in chords.py
- Expand test suite from 11 to 83 tests covering tones, scales, modes, chords,
  pitches, fingerings, and intervals

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 05:34:11 -04:00

90 lines
2.2 KiB
Python

from pytuning import scales
REFERENCE_A = 440
TEMPERAMENTS = {
"equal": scales.create_edo_scale,
"pythagorean": scales.create_pythagorean_scale,
"meantone": scales.create_quarter_comma_meantone_scale,
}
TONES = {
"western": [
("A",),
("A#", "Bb"),
("B",),
("C",),
("C#", "Db"),
("D",),
("D#", "Eb"),
("E",),
("F",),
("F#", "Gb"),
("G",),
("G#", "Ab"),
]
}
DEGREES = {
"western": [
("tonic", ("ionian", "aeolian")),
("supertonic", ("dorian", "locrian")),
("mediant", ("phrygian", "ionian")),
("subdominant", ("lydian", "dorian")),
("dominant", ("mixolydian", "phrygian")),
("submediant", ("aeolian", "lydian")),
("leading tone", ("locrian", "mixolydian")),
("octave", ("ionian", "aeolian")),
]
}
SCALES = {
# Number of semitones.
12: {
# scale type: number of tones.
"chromatic": (12, {}),
# "octatonic": (8, {}),
"heptatonic": [
7,
{
"major": {"major": True, "hemitonic": True},
"minor": {"minor": True, "hemitonic": True},
"harmonic minor": {"minor": True, "harmonic": True, "hemitonic": True},
# "melodic minor": {"minor": True, "melodic": True, "hemitonic": True},
},
],
# TODO: understand this
# "hexatonic": (
# 6,
# {
# # name, arguments to scale generator.
# "wholetone": {},
# "augmented": {},
# "prometheus": {},
# "blues": {},
# },
# ),
# "pentatonic": (5, {}),
# "tetratonic": (4, {}),
# "monotonic": (1, {"monotonic": {"hemitonic": False}}),
}
}
SYSTEMS = NotImplemented
# Modes are rotations of the major scale pattern.
# Each mode's offset is its position in the major scale.
_MODES = {
"ionian": 0,
"dorian": 1,
"phrygian": 2,
"lydian": 3,
"mixolydian": 4,
"aeolian": 5,
"locrian": 6,
}
for mode_name, offset in _MODES.items():
SCALES[12]["heptatonic"][1][mode_name] = {
"major": True, "hemitonic": True, "offset": offset
}