mirror of
https://github.com/kennethreitz/pytheory.git
synced 2026-06-05 23:00:20 +00:00
d2058668a6
Flat keys now display flats (Bb, Eb, Ab) instead of sharps (A#, D#, G#). Uses the "no duplicate letter names" rule: if building a scale with sharps produces two notes with the same letter (e.g. C and C# in C minor), the scale is rebuilt with flat spellings instead. - Tone.add() and Tone.from_index() accept prefer_flats parameter - TonedScale detects flat vs sharp per-scale automatically - F major: Bb (not A#), Eb major: Ab Bb (not G# A#), etc. - All tests and docs updated to match Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
367 lines
9.8 KiB
ReStructuredText
367 lines
9.8 KiB
ReStructuredText
Cookbook
|
|
=======
|
|
|
|
Real-world recipes for common musical tasks. Each recipe is self-contained
|
|
and ready to paste into a Python session.
|
|
|
|
Analyze a Song
|
|
--------------
|
|
|
|
Take the chord progression from "Let It Be" (C G Am F) and analyze it
|
|
in the key of C major:
|
|
|
|
.. code-block:: pycon
|
|
|
|
>>> from pytheory import Chord, Key
|
|
|
|
>>> C = Chord.from_name("C")
|
|
>>> G = Chord.from_name("G")
|
|
>>> Am = Chord.from_name("Am")
|
|
>>> F = Chord.from_name("F")
|
|
|
|
>>> [c.identify() for c in [C, G, Am, F]]
|
|
['C major', 'G major', 'A minor', 'F major']
|
|
|
|
>>> [c.analyze("C") for c in [C, G, Am, F]]
|
|
['I', 'V', 'vi', 'IV']
|
|
|
|
>>> key = Key("C", "major")
|
|
>>> [c.identify() for c in key.progression("I", "V", "vi", "IV")]
|
|
['C major', 'G major', 'A minor', 'F major']
|
|
|
|
Write a 12-Bar Blues
|
|
--------------------
|
|
|
|
The `12-bar blues <https://en.wikipedia.org/wiki/Twelve-bar_blues>`_ is
|
|
built from the I, IV, and V chords. Here it is in the key of A:
|
|
|
|
.. code-block:: pycon
|
|
|
|
>>> from pytheory import Key, Chord
|
|
|
|
>>> key = Key("A", "major")
|
|
>>> [c.identify() for c in key.progression("I", "IV", "V")]
|
|
['A major', 'D major', 'E major']
|
|
|
|
>>> bars = ["I","I","I","I", "IV","IV","I","I", "V","IV","I","V"]
|
|
>>> [c.identify() for c in key.progression(*bars)]
|
|
['A major', 'A major', 'A major', 'A major', 'D major', 'D major', 'A major', 'A major', 'E major', 'D major', 'A major', 'E major']
|
|
|
|
>>> Chord.from_name("A7").identify()
|
|
'A dominant 7th'
|
|
>>> Chord.from_name("D7").identify()
|
|
'D dominant 7th'
|
|
>>> Chord.from_name("E7").identify()
|
|
'E dominant 7th'
|
|
|
|
Find Chords in a Key
|
|
--------------------
|
|
|
|
The :class:`~pytheory.scales.Key` class builds diatonic chords for any
|
|
key and lets you pull progressions by Roman numeral or Nashville number:
|
|
|
|
.. code-block:: pycon
|
|
|
|
>>> from pytheory import Key
|
|
|
|
>>> key = Key("G", "major")
|
|
>>> key.chords
|
|
['G major', 'A minor', 'B minor', 'C major', 'D major', 'E minor', 'F# diminished']
|
|
|
|
>>> [c.identify() for c in key.progression("I", "V", "vi", "IV")]
|
|
['G major', 'D major', 'E minor', 'C major']
|
|
|
|
>>> [c.identify() for c in key.nashville(1, 5, 6, 4)]
|
|
['G major', 'D major', 'E minor', 'C major']
|
|
|
|
Compare Scales
|
|
--------------
|
|
|
|
Play the same tonic through different scales to hear how each mode
|
|
reshapes the palette. The western modes share the same notes but start
|
|
on different degrees; the blues scale adds the "blue note" (flat 5th):
|
|
|
|
.. code-block:: pycon
|
|
|
|
>>> from pytheory import TonedScale
|
|
|
|
>>> c = TonedScale(tonic="C4")
|
|
>>> c["major"].note_names
|
|
['C', 'D', 'E', 'F', 'G', 'A', 'B', 'C']
|
|
>>> c["minor"].note_names
|
|
['C', 'D', 'Eb', 'F', 'G', 'Ab', 'Bb', 'C']
|
|
>>> c["dorian"].note_names
|
|
['C', 'D', 'Eb', 'F', 'G', 'A', 'Bb', 'C']
|
|
>>> c["mixolydian"].note_names
|
|
['C', 'D', 'E', 'F', 'G', 'A', 'Bb', 'C']
|
|
|
|
>>> c_blues = TonedScale(tonic="C4", system="blues")
|
|
>>> c_blues["blues"].note_names
|
|
['C', 'Eb', 'F', 'Gb', 'G', 'Bb', 'C']
|
|
|
|
Guitar Chord Chart
|
|
------------------
|
|
|
|
Generate fingerings for guitar and ukulele with
|
|
:class:`~pytheory.tones.Fretboard`:
|
|
|
|
.. code-block:: pycon
|
|
|
|
>>> from pytheory import Fretboard
|
|
|
|
>>> fb = Fretboard.guitar()
|
|
>>> fb.chord("C")
|
|
Fingering(e=0, B=1, G=0, D=2, A=3, E=x)
|
|
>>> fb.chord("G")
|
|
Fingering(e=3, B=0, G=0, D=0, A=2, E=3)
|
|
>>> fb.chord("Am")
|
|
Fingering(e=0, B=1, G=2, D=2, A=0, E=x)
|
|
>>> fb.chord("D")
|
|
Fingering(e=2, B=3, G=2, D=0, A=x, E=x)
|
|
|
|
>>> uke = Fretboard.ukulele()
|
|
>>> uke.chord("C")
|
|
Fingering(A=3, E=0, C=0, G=0)
|
|
>>> uke.chord("G")
|
|
Fingering(A=2, E=3, C=2, G=0)
|
|
|
|
Explore an Interval
|
|
-------------------
|
|
|
|
Start from A4 (440 Hz) and walk through intervals, checking names and
|
|
frequency ratios:
|
|
|
|
.. code-block:: pycon
|
|
|
|
>>> from pytheory import Tone
|
|
|
|
>>> a4 = Tone.from_string("A4", system="western")
|
|
>>> a4.frequency
|
|
440.0
|
|
|
|
>>> minor_3rd = a4 + 3
|
|
>>> a4.interval_to(minor_3rd)
|
|
'minor 3rd'
|
|
|
|
>>> p5 = a4 + 7
|
|
>>> a4.interval_to(p5)
|
|
'perfect 5th'
|
|
>>> round(p5.frequency / a4.frequency, 4)
|
|
1.4983
|
|
|
|
>>> octave = a4 + 12
|
|
>>> a4.interval_to(octave)
|
|
'octave'
|
|
>>> round(octave.frequency / a4.frequency, 4)
|
|
2.0
|
|
|
|
Walk the Circle of Fifths
|
|
-------------------------
|
|
|
|
The `circle of fifths <https://en.wikipedia.org/wiki/Circle_of_fifths>`_
|
|
is the backbone of Western harmony — each step adds one sharp or flat:
|
|
|
|
.. code-block:: pycon
|
|
|
|
>>> from pytheory import Tone
|
|
|
|
>>> c = Tone.from_string("C4", system="western")
|
|
>>> [t.name for t in c.circle_of_fifths()]
|
|
['C', 'G', 'D', 'A', 'E', 'B', 'F#', 'C#', 'G#', 'D#', 'A#', 'F']
|
|
|
|
>>> g = Tone.from_string("G4", system="western")
|
|
>>> [t.name for t in g.circle_of_fifths()]
|
|
['G', 'D', 'A', 'E', 'B', 'F#', 'C#', 'G#', 'D#', 'A#', 'F', 'C']
|
|
|
|
Voice Leading Between Chords
|
|
-----------------------------
|
|
|
|
Find the smoothest path from one chord to the next — each voice moves
|
|
the minimum distance:
|
|
|
|
.. code-block:: pycon
|
|
|
|
>>> from pytheory import Chord
|
|
|
|
>>> c_maj = Chord.from_tones("C", "E", "G")
|
|
>>> f_maj = Chord.from_tones("F", "A", "C")
|
|
|
|
>>> for src, dst, motion in c_maj.voice_leading(f_maj):
|
|
... print(f"{src} -> {dst} ({motion:+d} semitones)")
|
|
G4 -> A4 (+2 semitones)
|
|
E4 -> F4 (+1 semitones)
|
|
C4 -> C4 (+0 semitones)
|
|
|
|
Measure Harmonic Tension
|
|
------------------------
|
|
|
|
Quantify how much a chord "wants to resolve." Dominant 7ths have
|
|
the most tension — the tritone between the 3rd and 7th pulls toward
|
|
resolution:
|
|
|
|
.. code-block:: pycon
|
|
|
|
>>> from pytheory import Chord
|
|
|
|
>>> for name in ["C", "Am", "G7", "Cmaj7"]:
|
|
... ch = Chord.from_name(name)
|
|
... t = ch.tension
|
|
... print(f"{name:6s} tension={t['score']:.2f} tritones={t['tritones']} dominant={t['has_dominant_function']}")
|
|
C tension=0.00 tritones=0 dominant=False
|
|
Am tension=0.00 tritones=0 dominant=False
|
|
G7 tension=0.60 tritones=1 dominant=True
|
|
Cmaj7 tension=0.15 tritones=0 dominant=False
|
|
|
|
Tritone Substitution (Jazz)
|
|
---------------------------
|
|
|
|
Replace any dominant chord with the one a
|
|
`tritone <https://en.wikipedia.org/wiki/Tritone_substitution>`_ away —
|
|
they share the same tritone interval:
|
|
|
|
.. code-block:: pycon
|
|
|
|
>>> from pytheory import Chord
|
|
|
|
>>> g7 = Chord.from_name("G7")
|
|
>>> g7.tritone_sub().identify()
|
|
'C# dominant 7th'
|
|
|
|
>>> # ii-V-I with tritone sub:
|
|
>>> # Dm7 -> G7 -> Cmaj7 (standard)
|
|
>>> # Dm7 -> Db7 -> Cmaj7 (chromatic bass line!)
|
|
|
|
Key Signatures and Detection
|
|
-----------------------------
|
|
|
|
View the accidentals in any key, or detect the key from a set of notes:
|
|
|
|
.. code-block:: pycon
|
|
|
|
>>> from pytheory import Key
|
|
|
|
>>> Key("C", "major").signature
|
|
{'sharps': 0, 'flats': 0, 'accidentals': []}
|
|
>>> Key("G", "major").signature
|
|
{'sharps': 1, 'flats': 0, 'accidentals': ['F#']}
|
|
>>> Key("D", "major").signature
|
|
{'sharps': 2, 'flats': 0, 'accidentals': ['F#', 'C#']}
|
|
|
|
>>> Key.detect("C", "E", "G", "A", "D")
|
|
<Key C major>
|
|
|
|
Relative and Parallel Keys
|
|
--------------------------
|
|
|
|
Every major key has a **relative minor** (same notes, different root)
|
|
and a **parallel minor** (same root, different notes):
|
|
|
|
.. code-block:: pycon
|
|
|
|
>>> from pytheory import Key
|
|
|
|
>>> c = Key("C", "major")
|
|
>>> c.relative
|
|
'A minor'
|
|
>>> c.parallel
|
|
'C minor'
|
|
|
|
Borrowed Chords and Secondary Dominants
|
|
---------------------------------------
|
|
|
|
Add color by borrowing from the parallel key or building secondary
|
|
dominants that approach other scale degrees:
|
|
|
|
.. code-block:: pycon
|
|
|
|
>>> from pytheory import Key
|
|
|
|
>>> c = Key("C", "major")
|
|
|
|
>>> c.borrowed_chords[:4]
|
|
['C minor', 'D diminished', 'Eb major', 'F minor']
|
|
|
|
>>> c.secondary_dominant(5).identify()
|
|
'D dominant 7th'
|
|
>>> c.secondary_dominant(2).identify()
|
|
'A dominant 7th'
|
|
>>> c.secondary_dominant(6).identify()
|
|
'E dominant 7th'
|
|
|
|
The Overtone Series
|
|
-------------------
|
|
|
|
Every musical tone contains a stack of harmonics — the physics behind
|
|
why intervals sound consonant:
|
|
|
|
.. code-block:: pycon
|
|
|
|
>>> from pytheory import Tone
|
|
|
|
>>> a4 = Tone.from_string("A4", system="western")
|
|
>>> [round(f, 1) for f in a4.overtones(6)]
|
|
[440.0, 880.0, 1320.0, 1760.0, 2200.0, 2640.0]
|
|
|
|
>>> # Harmonic 2 = octave (2:1)
|
|
>>> # Harmonic 3 = perfect 5th + octave (3:1)
|
|
>>> # Harmonic 5 = major 3rd + two octaves (5:1)
|
|
|
|
Enharmonic Spellings
|
|
--------------------
|
|
|
|
Find the alternate name for any sharp or flat:
|
|
|
|
.. code-block:: pycon
|
|
|
|
>>> from pytheory import Tone
|
|
|
|
>>> for name in ["C#4", "D#4", "F#4", "G#4"]:
|
|
... t = Tone.from_string(name, system="western")
|
|
... print(f"{t.name} = {t.enharmonic}")
|
|
C# = Db
|
|
D# = Eb
|
|
F# = Gb
|
|
G# = Ab
|
|
|
|
World Scales
|
|
------------
|
|
|
|
Explore scales from Indian, Arabic, and Japanese traditions:
|
|
|
|
.. code-block:: pycon
|
|
|
|
>>> from pytheory import TonedScale
|
|
|
|
>>> indian = TonedScale(tonic="Sa", system="indian")
|
|
>>> indian["bhairav"].note_names
|
|
['Sa', 'komal Re', 'Ga', 'Ma', 'Pa', 'komal Dha', 'Ni', 'Sa']
|
|
|
|
>>> arabic = TonedScale(tonic="Do", system="arabic")
|
|
>>> arabic["hijaz"].note_names
|
|
['Do', 'Reb', 'Mi', 'Fa', 'Sol', 'Solb', 'Sib', 'Do']
|
|
|
|
>>> japanese = TonedScale(tonic="C4", system="japanese")
|
|
>>> japanese["hirajoshi"].note_names
|
|
['C', 'D', 'Eb', 'G', 'Ab', 'C']
|
|
|
|
Visualize a Scale on Guitar
|
|
----------------------------
|
|
|
|
See where the notes fall across the fretboard — E minor pentatonic,
|
|
the most-played scale in rock:
|
|
|
|
.. code-block:: pycon
|
|
|
|
>>> from pytheory import Fretboard, Scale
|
|
|
|
>>> fb = Fretboard.guitar()
|
|
>>> pent = Scale(tonic="E4", system="blues")["minor pentatonic"]
|
|
>>> print(fb.scale_diagram(pent, frets=12))
|
|
0 1 2 3 4 5 6 7 8 9 10 11 12
|
|
E| E | - | - | G | - | A | - | B | - | - | D | - | E |
|
|
B| B | - | - | D | - | E | - | - | G | - | A | - | B |
|
|
G| G | - | A | - | B | - | - | D | - | E | - | - | G |
|
|
D| D | - | E | - | - | G | - | A | - | B | - | - | D |
|
|
A| A | - | B | - | - | D | - | E | - | - | G | - | A |
|
|
E| E | - | - | G | - | A | - | B | - | - | D | - | E |
|