Skip to content

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:

>>> Note.get("Bb").midi is None
True
>>> Note.get("Bb").pc
'Bb'

Transposing

>>> Note.transpose("C4", "M3")
'E4'
>>> Note.transpose("D", "P5")
'A'

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:

>>> Chord.get("F#dim/A").notes
('F#', 'A', 'C')

Detecting chords

>>> Chord.detect(["C", "E", "G"])
['CM', 'Em#5/C']
>>> Chord.detect(["D", "F#", "A", "C"])
['D7']

Transposing whole chords

>>> Chord.transpose("Cmaj7", "M3")
'Emaj7'

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

>>> Scale.detect(["C", "D", "E", "F", "G", "A", "B"], {"match": "exact"})
['C major']

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:

>>> from tonal_py import Voicing
>>> Voicing.get("Dm7", ["C3", "C5"])
['F3', 'A3', 'C4', 'E4']

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]
  • 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.