Getting Started¶
A guided tour of tonal_py's most common operations. Every example here
runs as a doctest in CI — copy them straight into a REPL.
Notes¶
A note is parsed from a string in scientific notation:
>>> from tonal_py import Note
>>> n = Note.get("C4")
>>> n.midi
60
>>> n.freq
261.6255653005986
>>> n.pc # pitch class (no octave)
'C'
Pitch classes (no octave number) are also notes — they just have
midi=None and freq=None:
Transposing¶
There are several flavors of transposition for different ergonomic needs:
>>> # Curry the interval to map over notes
>>> up_a_fifth = Note.transpose_by("P5")
>>> [up_a_fifth(n) for n in ["C", "D", "E"]]
['G', 'A', 'B']
>>> # Curry the note to map over intervals
>>> from_c = Note.transpose_from("C")
>>> [from_c(i) for i in ["1P", "3M", "5P"]]
['C', 'E', 'G']
>>> # Direct fifths/octaves transposition skips interval parsing
>>> Note.transpose_fifths("C", 2)
'D'
>>> Note.transpose_octaves("C4", 2)
'C6'
Enharmonic spelling¶
>>> Note.simplify("C##") # double-sharp -> simpler spelling
'D'
>>> Note.simplify("B#4") # crosses an octave boundary
'C5'
>>> Note.enharmonic("Db") # default: pick the other common spelling
'C#'
>>> Note.enharmonic("F2", "E#") # explicit destination spelling
'E#2'
Intervals¶
>>> from tonal_py import Interval
>>> Interval.semitones("P5")
7
>>> Interval.invert("3m")
'6M'
>>> Interval.simplify("9M") # compound -> simple
'2M'
>>> Interval.add("3m", "5P") # arithmetic
'7m'
>>> Interval.from_semitones(7)
'5P'
>>> Interval.distance("C4", "G4")
'5P'
Chords¶
>>> from tonal_py import Chord
>>> c = Chord.get("Cmaj7")
>>> c.notes
('C', 'E', 'G', 'B')
>>> c.intervals
('1P', '3M', '5P', '7M')
>>> c.quality
'Major'
Slash chords work too:
Detecting chords¶
Transposing whole chords¶
Scales¶
>>> from tonal_py import Scale
>>> s = Scale.get("C major")
>>> s.notes
('C', 'D', 'E', 'F', 'G', 'A', 'B')
>>> s.intervals
('1P', '2M', '3M', '4P', '5P', '6M', '7M')
>>> s.tonic
'C'
>>> s.type
'major'
Detecting scales¶
Modes of a scale¶
>>> Scale.mode_names("C major")
[('C', 'major'), ('D', 'dorian'), ('E', 'phrygian'), ('F', 'lydian'), ('G', 'mixolydian'), ('A', 'minor'), ('B', 'locrian')]
Scale degrees and steps¶
>>> degrees = Scale.degrees("C major")
>>> degrees(1) # 1-based: tonic
'C'
>>> degrees(5)
'G'
>>> steps = Scale.steps("C major")
>>> steps(0) # 0-based: also tonic
'C'
>>> steps(7) # wraps around
'C'
Keys¶
>>> from tonal_py import Key
>>> ck = Key.major_key("C")
>>> ck.chords
('Cmaj7', 'Dm7', 'Em7', 'Fmaj7', 'G7', 'Am7', 'Bm7b5')
>>> ck.minor_relative
'A'
>>> ck.alteration # 0 sharps/flats
0
>>> ck.key_signature
''
The minor-key surface includes natural, harmonic, and melodic sub-scales:
>>> ak = Key.minor_key("A")
>>> ak.relative_major
'C'
>>> ak.natural.chords
('Am7', 'Bm7b5', 'Cmaj7', 'Dm7', 'Em7', 'Fmaj7', 'G7')
>>> ak.harmonic.chords
('AmMaj7', 'Bm7b5', 'Cmaj7#5', 'Dm7', 'E7', 'Fmaj7', 'G#o7')
Voicings¶
Voicing finds concrete pitch realizations of a chord within a note range:
Chain voicings across a progression with smooth voice leading:
>>> Voicing.sequence(["Dm7", "G7", "Cmaj7"], ["C3", "C5"]) # doctest: +ELLIPSIS
[['F3', 'A3', 'C4', 'E4'], [...], [...]]
Rhythm patterns¶
>>> from tonal_py import RhythmPattern
>>> RhythmPattern.euclid(8, 3)
[1, 0, 0, 1, 0, 0, 1, 0]
>>> RhythmPattern.binary(13) # 13 = 0b1101
[1, 1, 0, 1]
>>> RhythmPattern.onsets(1, 2, 1) # 1, then 2 zeros, then 1, then 1 zero
[1, 0, 1, 0, 0, 1, 0]
What to read next¶
- The API Reference lists every function in every namespace — generated from the source docstrings.
- Concepts explains the underlying machinery: pitch coordinates, chroma, the circle of fifths.
- Naming Conventions maps tonal.js names to tonal_py names if you're porting JS code.