diff --git a/docs/conf.py b/docs/conf.py index 76b7909..5d1ffc3 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -34,7 +34,8 @@ templates_path = ["_templates"] exclude_patterns = ["_build"] html_theme = "alabaster" -html_title = "" +html_title = " " +html_short_title = " " html_logo = "_static/logo.png" html_static_path = ["_static"] html_extra_path = ["CNAME"] diff --git a/docs/guide/chords.rst b/docs/guide/chords.rst index bb2f577..0e6a19e 100644 --- a/docs/guide/chords.rst +++ b/docs/guide/chords.rst @@ -1,19 +1,36 @@ Working with Chords =================== -Chords and Chord Charts ------------------------ +A **chord** is two or more tones sounding simultaneously. Chords are the +vertical dimension of music — while melody moves horizontally through +time, harmony stacks tones on top of each other. -PyTheory provides two chord-related classes: +Chord Construction +------------------ -- :class:`~pytheory.chords.Chord` — a collection of tones played together -- :class:`~pytheory.charts.NamedChord` — a chord from the chart database with - fingering support +Chords are built by stacking **intervals** above a **root** note. The +most common chord type is the **triad** — three notes built from +alternating scale degrees (root, 3rd, 5th). + +The four triad types:: + + Major root + major 3rd (4) + perfect 5th (7) Bright, stable + Minor root + minor 3rd (3) + perfect 5th (7) Dark, sad + Diminished root + minor 3rd (3) + diminished 5th (6) Tense, unstable + Augmented root + major 3rd (4) + augmented 5th (8) Eerie, unresolved + +Adding a 7th creates a **seventh chord** — the foundation of jazz +harmony:: + + Dominant 7th root + 4 + 7 + 10 Bluesy, wants to resolve (G7) + Major 7th root + 4 + 7 + 11 Dreamy, sophisticated (Cmaj7) + Minor 7th root + 3 + 7 + 10 Warm, mellow (Am7) + Diminished 7th root + 3 + 6 + 9 Dramatic, symmetrical Using the Chord Chart --------------------- -The built-in chart contains 144 chords (12 roots x 12 qualities): +PyTheory includes 144 pre-built chords (12 roots x 12 qualities): .. code-block:: python @@ -21,29 +38,37 @@ The built-in chart contains 144 chords (12 roots x 12 qualities): chart = CHARTS["western"] - # Access a chord - c_major = chart["C"] - a_minor = chart["Am"] - g_seven = chart["G7"] + c_major = chart["C"] # C major (root position) + a_minor = chart["Am"] # A minor + g_seven = chart["G7"] # G dominant 7th + d_dim = chart["Ddim"] # D diminished - # Available qualities: "", "maj", "m", "5", "7", "9", - # "dim", "m6", "m7", "m9", "maj7", "maj9" +Available qualities: -Chord Tones ------------ - -Each named chord knows which tones it contains: +============ ================ ================================ +Quality Intervals Example tones (from C) +============ ================ ================================ +``""`` 4, 7 C E G (major triad) +``"maj"`` 4, 7 C E G (explicit major) +``"m"`` 3, 7 C Eb G (minor triad) +``"5"`` 7 C G (power chord) +``"7"`` 4, 7, 10 C E G Bb (dominant 7th) +``"9"`` 4, 7, 10, 14 C E G Bb D (dominant 9th) +``"dim"`` 3, 6 C Eb Gb (diminished) +``"m6"`` 3, 7, 9 C Eb G A (minor 6th) +``"m7"`` 3, 7, 10 C Eb G Bb (minor 7th) +``"m9"`` 3, 7, 10, 14 C Eb G Bb D (minor 9th) +``"maj7"`` 4, 7, 11 C E G B (major 7th) +``"maj9"`` 4, 7, 11, 14 C E G B D (major 9th) +============ ================ ================================ .. code-block:: python >>> chart["C"].acceptable_tone_names ('C', 'E', 'G') - >>> chart["Am"].acceptable_tone_names - ('A', 'C', 'E') - - >>> chart["G7"].acceptable_tone_names - ('G', 'B', 'D', 'F') + >>> chart["Cm7"].acceptable_tone_names + ('C', 'D#', 'G', 'A#') # Eb and Bb shown as sharps Building Chords Manually ------------------------- @@ -58,26 +83,100 @@ Building Chords Manually Tone.from_string("G4", system="western"), ]) - # Iteration for tone in c_major: print(tone) len(c_major) # 3 "C" in c_major # True -Chord Properties ----------------- +Intervals +--------- + +The ``intervals`` property returns semitone distances between adjacent +tones — these are musically meaningful and octave-invariant: .. code-block:: python - # Frequency intervals between adjacent tones (Hz) - c_major.intervals + >>> c_major.intervals + [4, 3] # major 3rd (4) + minor 3rd (3) = major triad - # Harmony score (higher = more consonant intervals) - c_major.harmony + >>> Chord(tones=[C4, Eb4, G4]).intervals + [3, 4] # minor 3rd + major 3rd = minor triad - # Dissonance score (higher = wider intervals) - c_major.dissonance +Consonance and Dissonance +------------------------- - # Beat frequency between closest tone pair - c_major.beat_pulse +**Consonance** is the perception of stability and "pleasantness" when +tones sound together. **Dissonance** is the perception of tension and +roughness. Neither is inherently good or bad — music needs both. + +Harmony Score +~~~~~~~~~~~~~ + +The ``harmony`` property measures consonance using **frequency ratio +simplicity**. The insight dates back to Pythagoras (6th century BC): +intervals whose frequencies form simple integer ratios sound consonant. + +=========== ===== ==================== +Interval Ratio Why it sounds "good" +=========== ===== ==================== +Octave 2:1 Every 2nd wave aligns +Perfect 5th 3:2 Every 3rd wave aligns +Perfect 4th 4:3 Every 4th wave aligns +Major 3rd 5:4 Every 5th wave aligns +Minor 3rd 6:5 Every 6th wave aligns +Tritone 45:32 Waves rarely align +=========== ===== ==================== + +.. code-block:: python + + fifth = Chord([C4, G4]) + tritone = Chord([C4, F_sharp_4]) + + fifth.harmony > tritone.harmony # True + # The perfect fifth's 3:2 ratio scores higher + +Dissonance Score +~~~~~~~~~~~~~~~~ + +The ``dissonance`` property uses the **Plomp-Levelt roughness model** +(1965). When two frequencies are close together, their sound waves +interfere and produce rapid amplitude fluctuations called **beating**. +This beating is perceived as roughness — the physiological basis of +dissonance. + +The roughness depends on the frequency difference relative to the +**critical bandwidth** of the human ear (~25% of the frequency at +that register). Maximum roughness occurs when the difference equals +the critical bandwidth. + +.. code-block:: python + + # Octave: frequencies far apart → low roughness + octave = Chord([C4, C5]) + # Major 3rd: closer frequencies → higher roughness + third = Chord([C4, E4]) + + octave.dissonance < third.dissonance # True + +Beat Frequencies +~~~~~~~~~~~~~~~~ + +When two tones with slightly different frequencies are played together, +you hear a pulsing at the **beat frequency**: ``|f1 - f2|`` Hz. + +- **< 1 Hz**: Slow pulsing, used for tuning instruments +- **1–15 Hz**: Audible rhythmic beating +- **15–30 Hz**: Perceived as buzzing/roughness +- **> 30 Hz**: No longer beating — becomes part of the timbre + +.. code-block:: python + + chord = Chord(tones=[A4, E5, A5]) + + # All pairwise beat frequencies, sorted ascending + chord.beat_frequencies + # [(A4, E5, 189.6), (E5, A5, 220.0), (A4, A5, 440.0)] + + # The slowest (most perceptible) beat + chord.beat_pulse # 189.6 Hz diff --git a/docs/guide/fretboard.rst b/docs/guide/fretboard.rst index 5ba1a41..b2152a8 100644 --- a/docs/guide/fretboard.rst +++ b/docs/guide/fretboard.rst @@ -4,6 +4,26 @@ Fretboard and Fingerings The :class:`~pytheory.chords.Fretboard` class represents a fretted instrument's tuning and generates chord fingerings. +How Frets Work +-------------- + +Each fret on a guitar (or any fretted instrument) raises the pitch by +exactly **one semitone**. The open string is fret 0; fret 1 is one +semitone up, fret 2 is two semitones up, and so on. + +Standard guitar tuning (high to low):: + + String 1: E4 (highest) + String 2: B3 + String 3: G3 + String 4: D3 + String 5: A2 + String 6: E2 (lowest) + +This tuning uses intervals of a perfect 4th (5 semitones) between most +strings, except between G and B which is a major 3rd (4 semitones). This +asymmetry is why guitar chord shapes shift when they cross the G-B pair. + Preset Tunings -------------- @@ -11,48 +31,98 @@ Preset Tunings from pytheory import Fretboard - guitar = Fretboard.guitar() # E4 B3 G3 D3 A2 E2 - bass = Fretboard.bass() # G2 D2 A1 E1 - ukulele = Fretboard.ukulele() # A4 E4 C4 G4 + guitar = Fretboard.guitar() # Standard EADGBE + bass = Fretboard.bass() # Standard EADG + bass5 = Fretboard.bass(five_string=True) # 5-string BEADG + ukulele = Fretboard.ukulele() # GCEA (re-entrant) -Custom Tunings --------------- +Alternate Guitar Tunings +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + # Built-in alternate tunings + drop_d = Fretboard.guitar("drop d") # DADGBE — heavy riffs + open_g = Fretboard.guitar("open g") # DGDGBD — slide guitar, Keith Richards + open_d = Fretboard.guitar("open d") # DADF#AD — slide, folk + open_e = Fretboard.guitar("open e") # EBEG#BE — slide blues + open_a = Fretboard.guitar("open a") # EAC#EAE + dadgad = Fretboard.guitar("dadgad") # DADGAD — Celtic, fingerstyle + half_down = Fretboard.guitar("half step down") # Eb standard — Hendrix, SRV + + # Custom tuning with any notes + custom = Fretboard.guitar(("D4", "A3", "F#3", "D3", "A2", "D2")) + +Custom Instruments +------------------ + +Any fretted instrument can be modeled: .. code-block:: python from pytheory import Tone, Fretboard - # Open D tuning - open_d = Fretboard(tones=[ + # Banjo (open G tuning) + banjo = Fretboard(tones=[ Tone.from_string("D4"), - Tone.from_string("A3"), - Tone.from_string("F#3"), + Tone.from_string("B3"), + Tone.from_string("G3"), Tone.from_string("D3"), - Tone.from_string("A2"), - Tone.from_string("D2"), + Tone.from_string("G4"), # 5th string (high drone) + ]) + + # Mandolin + mandolin = Fretboard(tones=[ + Tone.from_string("E5"), + Tone.from_string("A4"), + Tone.from_string("D4"), + Tone.from_string("G3"), ]) Getting Fingerings ------------------ +The fingering algorithm finds the most playable voicing for any chord on +any fretboard. It scores each possibility by: + +1. Preferring **open strings** (fret 0) — they ring freely +2. Preferring **ascending** fret patterns — easier hand position +3. Minimizing the number of **fingers needed** + .. code-block:: python from pytheory import Fretboard, CHARTS fb = Fretboard.guitar() - - # Best fingering for a chord c = CHARTS["western"]["C"] + + # Best single fingering print(c.fingering(fretboard=fb)) # (0, 1, 0, 2, 3, 0) + # String: E4=0 B3=1 G3=0 D3=2 A2=3 E2=0 - # All possible fingerings + # All equally-scored fingerings all_c = c.fingering(fretboard=fb, multiple=True) # Muted strings appear as None f = CHARTS["western"]["F"] print(f.fingering(fretboard=fb)) +Reading Fingerings +~~~~~~~~~~~~~~~~~~ + +The tuple ``(0, 1, 0, 2, 3, 0)`` reads from the highest string to the +lowest:: + + e|--0-- (open — E) + B|--1-- (fret 1 — C) + G|--0-- (open — G) + D|--2-- (fret 2 — E) + A|--3-- (fret 3 — C) + E|--0-- (open — E) + +A value of ``None`` means the string is muted (not played). + Generating Full Charts ---------------------- diff --git a/docs/guide/playback.rst b/docs/guide/playback.rst index 0412420..851cc7a 100644 --- a/docs/guide/playback.rst +++ b/docs/guide/playback.rst @@ -1,7 +1,8 @@ Audio Playback ============== -PyTheory can synthesize and play tones and chords through your speakers. +PyTheory can synthesize and play tones and chords through your speakers +using basic waveform synthesis. .. note:: @@ -36,7 +37,21 @@ Playing a Chord Waveform Types -------------- -Choose between sine, sawtooth, and triangle wave synthesis: +The waveform shape determines the **timbre** (tonal color) of the sound. +Different waveforms contain different combinations of **harmonics** — +integer multiples of the fundamental frequency. + +- **Sine wave** — the purest tone. Contains only the fundamental + frequency with no harmonics. Sounds smooth, clear, and "electronic." + This is the building block of all other waveforms (Fourier's theorem). + +- **Sawtooth wave** — contains all harmonics (both odd and even), + each at amplitude 1/n. Sounds bright, buzzy, and aggressive. + Named for its shape. Used extensively in analog synthesizers. + +- **Triangle wave** — contains only odd harmonics, each at amplitude + 1/n². Sounds softer and more mellow than sawtooth — somewhere between + sine and sawtooth. Often described as "woody" or "hollow." .. code-block:: python @@ -44,17 +59,22 @@ Choose between sine, sawtooth, and triangle wave synthesis: tone = Tone.from_string("C4", system="western") - play(tone, synth=Synth.SINE) # Smooth, pure tone + play(tone, synth=Synth.SINE) # Pure, clean play(tone, synth=Synth.SAW) # Bright, buzzy - play(tone, synth=Synth.TRIANGLE) # Softer than sawtooth + play(tone, synth=Synth.TRIANGLE) # Mellow, hollow Temperaments ------------ -Play in different tuning systems: +Hear the difference between tuning systems: .. code-block:: python - play(tone, temperament="equal") # Default, modern tuning - play(tone, temperament="pythagorean") # Ancient Greek tuning - play(tone, temperament="meantone") # Renaissance tuning + play(tone, temperament="equal") # Modern standard (since ~1917) + play(tone, temperament="pythagorean") # Pure fifths, wolf intervals + play(tone, temperament="meantone") # Pure thirds, Renaissance sound + +Try playing a C major chord in each temperament — you'll hear subtle +differences in the "color" of the major third. Equal temperament is +a compromise; the other systems sacrifice some keys to make the good +keys sound better. diff --git a/docs/guide/quickstart.rst b/docs/guide/quickstart.rst index 2c499c8..a56a365 100644 --- a/docs/guide/quickstart.rst +++ b/docs/guide/quickstart.rst @@ -17,28 +17,28 @@ Create tones, build scales, and explore music theory: from pytheory import Tone, TonedScale, Fretboard, CHARTS - # Create a tone - c4 = Tone.from_string("C4") - print(c4) # C4 - print(c4.frequency) # 261.63 Hz + # Create a tone — A4 is the tuning standard (440 Hz) + a4 = Tone.from_string("A4", system="western") + print(a4.frequency) # 440.0 - # Tone arithmetic - e4 = c4 + 4 # Major third up - g4 = c4 + 7 # Perfect fifth up - print(e4, g4) # E4 G4 + # Tone arithmetic — add semitones to move up the chromatic scale + c4 = Tone.from_string("C4", system="western") + e4 = c4 + 4 # Major third up (4 semitones) + g4 = c4 + 7 # Perfect fifth up (7 semitones) + print(e4, g4) # E4 G4 - # Measure intervals - print(g4 - c4) # 7 (semitones) + # Measure intervals between tones + print(g4 - c4) # 7 (semitones — a perfect fifth) - # Build a scale + # Build a C major scale c_major = TonedScale(tonic="C4")["major"] print(c_major.note_names) # ['C', 'D', 'E', 'F', 'G', 'A', 'B', 'C'] - # Build chords from the scale - I = c_major.triad(0) # C major - IV = c_major.triad(3) # F major - V = c_major.triad(4) # G major + # Build diatonic triads from the scale + I = c_major.triad(0) # C E G (C major) + IV = c_major.triad(3) # F A C (F major) + V = c_major.triad(4) # G B D (G major) # Guitar chord fingerings fb = Fretboard.guitar() @@ -48,9 +48,15 @@ Create tones, build scales, and explore music theory: What's Included --------------- -- **12-tone Western system** with all chromatic notes -- **Scales**: major, minor, harmonic minor, and all 7 modes +- **6 musical systems**: Western, Indian (Hindustani), Arabic (Maqam), + Japanese, Blues/Pentatonic, Javanese Gamelan +- **40+ scales**: major, minor, harmonic minor, 7 modes, 10 thaats, + 10 maqamat, 6 Japanese pentatonic scales, blues, pentatonic, + slendro, pelog, and more - **Pitch calculation** in equal, Pythagorean, and meantone temperaments - **Chord charts** with 144 pre-built chords (12 roots x 12 qualities) -- **Fingering generation** for any fretted instrument +- **Chord analysis**: consonance scoring, Plomp-Levelt dissonance, + beat frequency calculation +- **Fingering generation** for guitar (8 tunings), bass, ukulele, or + any custom fretted instrument - **Audio playback** with sine, sawtooth, and triangle wave synthesis diff --git a/docs/guide/scales.rst b/docs/guide/scales.rst index 3047495..8069f9f 100644 --- a/docs/guide/scales.rst +++ b/docs/guide/scales.rst @@ -1,7 +1,29 @@ Working with Scales =================== -Scales are sequences of tones following a specific interval pattern. +A **scale** is an ordered set of tones spanning an octave, defined by a +pattern of intervals. Scales are the foundation of melody and harmony — +they determine which notes "belong" in a piece of music and shape its +emotional character. + +Scale Construction +------------------ + +Every scale is defined by its **interval pattern** — the sequence of +whole steps (W = 2 semitones) and half steps (H = 1 semitone) between +consecutive tones. + +The major scale:: + + W W H W W W H + C D E F G A B C + 2 2 1 2 2 2 1 ← semitones between each note + +The natural minor scale:: + + W H W W H W W + C D Eb F G Ab Bb C + 2 1 2 2 1 2 2 Building Scales --------------- @@ -14,7 +36,6 @@ Use :class:`~pytheory.scales.TonedScale` to generate scales in any key: c = TonedScale(tonic="C4") - # Access scales by name major = c["major"] minor = c["minor"] harmonic_minor = c["harmonic minor"] @@ -22,62 +43,117 @@ Use :class:`~pytheory.scales.TonedScale` to generate scales in any key: print(major.note_names) # ['C', 'D', 'E', 'F', 'G', 'A', 'B', 'C'] -Available Scales ----------------- +Major and Minor +--------------- + +The **major scale** (Ionian mode) is the foundation of Western tonal +music. Its pattern of whole and half steps creates a bright, resolved +sound. Every major key has a **relative minor** that shares the same +notes but starts from the 6th degree: + +- C major → A minor (both use only white keys) +- G major → E minor (both have one sharp: F#) +- F major → D minor (both have one flat: Bb) .. code-block:: python - >>> c = TonedScale(tonic="C4") - >>> c.scales - ('chromatic', 'major', 'minor', 'harmonic minor', - 'ionian', 'dorian', 'phrygian', 'lydian', - 'mixolydian', 'aeolian', 'locrian') + c_major = TonedScale(tonic="C4")["major"] + a_minor = TonedScale(tonic="A4")["minor"] + + # Same notes, different starting point + set(c_major.note_names) == set(a_minor.note_names) # True + +The **harmonic minor** raises the 7th degree of the natural minor, +creating an augmented 2nd interval (3 semitones) between the 6th and +7th degrees. This gives it a distinctive "Middle Eastern" or "classical" +sound and provides the leading tone needed for dominant harmony:: + + Natural minor: C D Eb F G Ab Bb C + Harmonic minor: C D Eb F G Ab B C + ↑ raised 7th Modes ----- -All seven modes of the major scale are supported: +The seven **modes** of the major scale are rotations of the same interval +pattern, each starting from a different degree. Each mode has a distinct +emotional character: .. code-block:: python c = TonedScale(tonic="C4") - c["ionian"] # Same as major: C D E F G A B C - c["dorian"] # C D Eb F G A Bb C - c["phrygian"] # C Db Eb F G Ab Bb C - c["lydian"] # C D E F# G A B C - c["mixolydian"] # C D E F G A Bb C - c["aeolian"] # Same as minor: C D Eb F G Ab Bb C - c["locrian"] # C Db Eb F Gb Ab Bb C +**Ionian** (I) — the major scale itself. Bright, happy, resolved:: -Accessing Degrees ------------------ + c["ionian"] # C D E F G A B C -Scale tones can be accessed by index, Roman numeral, or degree name: +**Dorian** (ii) — minor with a raised 6th. Jazzy, soulful (So What, +Scarborough Fair):: + + c["dorian"] # C D Eb F G A Bb C + +**Phrygian** (iii) — minor with a flat 2nd. Spanish, flamenco, dark +(White Rabbit):: + + c["phrygian"] # C Db Eb F G Ab Bb C + +**Lydian** (IV) — major with a raised 4th. Dreamy, floating, ethereal +(The Simpsons theme, Flying by ET):: + + c["lydian"] # C D E F# G A B C + +**Mixolydian** (V) — major with a flat 7th. Bluesy, rock, dominant +(Norwegian Wood, Sweet Home Alabama):: + + c["mixolydian"] # C D E F G A Bb C + +**Aeolian** (vi) — the natural minor scale. Sad, dark, introspective +(Stairway to Heaven, Losing My Religion):: + + c["aeolian"] # C D Eb F G Ab Bb C + +**Locrian** (vii) — minor with flat 2nd and flat 5th. Unstable, +rarely used as a home key (used in metal and jazz over diminished +chords):: + + c["locrian"] # C Db Eb F Gb Ab Bb C + +Scale Degrees +------------- + +Each note in a scale has a **degree name** that describes its function: + +=========== ====== ======================================= +Degree Number Function +=========== ====== ======================================= +Tonic I Home base — the key center +Supertonic II One step above tonic +Mediant III Halfway between tonic and dominant +Subdominant IV A fifth below tonic (or fourth above) +Dominant V The strongest pull back to tonic +Submediant VI Root of the relative minor (or major) +Leading Tone VII One semitone below tonic — pulls upward +=========== ====== ======================================= + +Access degrees by index, Roman numeral, or name: .. code-block:: python major = TonedScale(tonic="C4")["major"] - # By index - major[0] # C4 - major[4] # G4 + major[0] # C4 (by index) + major["I"] # C4 (by Roman numeral) + major["tonic"] # C4 (by degree name) - # By Roman numeral - major["I"] # C4 - major["V"] # G4 - - # By degree name - major["tonic"] # C4 + major["V"] # G4 (dominant) major["dominant"] # G4 - # Slicing - major[0:3] # (C4, D4, E4) + major[0:3] # (C4, D4, E4) — slicing works too Iteration --------- -Scales are iterable: +Scales are iterable and support ``len()`` and ``in``: .. code-block:: python @@ -91,16 +167,47 @@ Scales are iterable: Building Chords from Scales ---------------------------- -Build chords directly from scale degrees: +**Diatonic harmony** builds chords by stacking every other note of the +scale. A **triad** takes the 1st, 3rd, and 5th; a **seventh chord** adds +the 7th. + +In the C major scale, the diatonic triads are:: + + I C E G = C major + ii D F A = D minor + iii E G B = E minor + IV F A C = F major + V G B D = G major + vi A C E = A minor + vii° B D F = B diminished + +Notice the pattern: **major** triads on I, IV, V; **minor** triads on +ii, iii, vi; **diminished** on vii°. This pattern holds for every major +key. .. code-block:: python major = TonedScale(tonic="C4")["major"] - # Build a triad (root, 3rd, 5th) - I = major.triad(0) # C E G (C major) - ii = major.triad(1) # D F A (D minor) - V = major.triad(4) # G B D (G major) + # Build diatonic triads + I = major.triad(0) # C E G (C major) + ii = major.triad(1) # D F A (D minor) + iii = major.triad(2) # E G B (E minor) + IV = major.triad(3) # F A C (F major) + V = major.triad(4) # G B D (G major) + vi = major.triad(5) # A C E (A minor) - # Custom chord voicings - cmaj7 = major.chord(0, 2, 4, 6) # C E G B + # Build seventh chords + Imaj7 = major.chord(0, 2, 4, 6) # C E G B = Cmaj7 + V7 = major.chord(4, 6, 8, 10) # G B D F = G7 (dominant 7th) + +Common Progressions +~~~~~~~~~~~~~~~~~~~ + +Some of the most-used chord progressions in Western music: + +- **I–IV–V–I** — the foundation of blues, rock, country, folk +- **I–V–vi–IV** — the "pop progression" (Let It Be, No Woman No Cry, + With or Without You) +- **ii–V–I** — the backbone of jazz harmony +- **I–vi–IV–V** — the "50s progression" (Stand By Me, Every Breath You Take) diff --git a/docs/guide/tones.rst b/docs/guide/tones.rst index 1d2c73a..587613d 100644 --- a/docs/guide/tones.rst +++ b/docs/guide/tones.rst @@ -2,7 +2,40 @@ Working with Tones ================== A :class:`~pytheory.tones.Tone` represents a single musical note, optionally -with an octave number (scientific pitch notation). +with an octave number in scientific pitch notation (e.g. C4 = middle C). + +What is a Tone? +--------------- + +A musical tone is a sound with a definite pitch — a periodic vibration at +a specific frequency. In the Western 12-tone system, the octave (a 2:1 +frequency ratio) is divided into 12 equal steps called **semitones** or +**half steps**. Two semitones make a **whole step** (whole tone). + +The 12 chromatic tones are:: + + C C#/Db D D#/Eb E F F#/Gb G G#/Ab A A#/Bb B + +Notes with two names (like C# and Db) are **enharmonic equivalents** — +different names for the same pitch. Whether you call it C# or Db depends +on the musical context (key signature, harmonic function). + +Scientific Pitch Notation +------------------------- + +Each tone can be assigned an octave number. The standard is **scientific +pitch notation**, where the octave number increments at C:: + + ... B3 C4 C#4 D4 ... A4 B4 C5 C#5 ... + ^ ^ + middle C one octave up + +Key reference points: + +- **A4 = 440 Hz** — the international tuning standard (ISO 16) +- **C4 = 261.63 Hz** — middle C on the piano +- **A0 = 27.5 Hz** — the lowest A on a standard piano +- **C8 = 4186 Hz** — the highest C on a standard piano Creating Tones -------------- @@ -11,7 +44,7 @@ Creating Tones from pytheory import Tone - # From a string + # From a string (most common) c4 = Tone.from_string("C4") cs4 = Tone.from_string("C#4") @@ -39,24 +72,87 @@ Properties Pitch and Frequency ------------------- +Every tone vibrates at a specific frequency measured in Hertz (Hz — +cycles per second). The relationship between pitch and frequency is +**logarithmic**: each octave doubles the frequency, and each semitone +multiplies by the 12th root of 2 (~1.05946). + .. code-block:: python >>> a4 = Tone.from_string("A4", system="western") >>> a4.frequency 440.0 - >>> a4.pitch() - 440.0 - # Different temperaments + >>> Tone.from_string("A3", system="western").frequency + 220.0 # One octave down = half the frequency + + >>> Tone.from_string("C4", system="western").frequency + 261.63 # Middle C + +Temperament +~~~~~~~~~~~ + +**Temperament** is the system used to tune the intervals between notes. +Different temperaments produce slightly different frequencies for the +same note name: + +- **Equal temperament** (default): Every semitone has an identical + frequency ratio of 2^(1/12). This is the modern standard — it allows + free modulation between all keys but no interval is acoustically + "pure" except the octave. + +- **Pythagorean temperament**: Built entirely from pure perfect fifths + (3:2 ratio). Produces beatless fifths but introduces the "Pythagorean + comma" — a small discrepancy when 12 fifths don't quite equal 7 + octaves. Used in medieval European music. + +- **Quarter-comma meantone**: Tunes major thirds to the pure ratio of + 5:4, distributing the resulting error across the fifths. Dominant in + Renaissance and Baroque music (15th–18th century). Sounds beautiful + in closely related keys but "wolf intervals" make distant keys + unusable. + +.. code-block:: python + + >>> a4.pitch(temperament="equal") + 440.0 >>> a4.pitch(temperament="pythagorean") - 440.0 + 440.0 # A4 is always 440 (it's the reference) - # Symbolic (SymPy expression) + >>> c5 = Tone.from_string("C5", system="western") + >>> c5.pitch(temperament="equal") + 523.25 + >>> c5.pitch(temperament="pythagorean") + 521.48 # Slightly different! + + # Symbolic output (SymPy expression) >>> a4.pitch(symbolic=True) 440 -Arithmetic ----------- +Intervals and Arithmetic +------------------------- + +An **interval** is the distance between two pitches, measured in +semitones. Intervals have both a **quantity** (number of scale steps) +and a **quality** (perfect, major, minor, augmented, diminished). + +Common intervals:: + + Semitones Name Sound + ───────── ──── ───── + 0 Unison Same note + 1 Minor 2nd Tense, dissonant (Jaws theme) + 2 Major 2nd A whole step (Do-Re) + 3 Minor 3rd Sad, dark (Greensleeves) + 4 Major 3rd Happy, bright (Kumbaya) + 5 Perfect 4th Open, hollow (Here Comes the Bride) + 6 Tritone Unstable, tense (The Simpsons) + 7 Perfect 5th Strong, stable (Star Wars) + 8 Minor 6th Bittersweet + 9 Major 6th Warm (My Bonnie) + 10 Minor 7th Bluesy (Star Trek TOS) + 11 Major 7th Dreamy, yearning + 12 Octave Same note, higher Tones support ``+`` and ``-`` operators for semitone math: @@ -75,13 +171,17 @@ Subtracting two tones gives the semitone distance: .. code-block:: python >>> g4 = Tone.from_string("G4", system="western") - >>> g4 - c4 # Semitone distance + >>> g4 - c4 # Perfect fifth = 7 semitones 7 + >>> c5 = Tone.from_string("C5", system="western") + >>> c5 - c4 # Octave = 12 semitones + 12 + Comparison and Sorting ---------------------- -Tones can be compared and sorted by pitch: +Tones can be compared and sorted by pitch frequency: .. code-block:: python @@ -94,7 +194,26 @@ Equality checks note name and octave: .. code-block:: python - >>> c4 == "C" # Compare with string + >>> c4 == "C" # Compare with string (name only) True >>> c4 == Tone(name="C", octave=4) True + +The Circle of Fifths +-------------------- + +The **circle of fifths** is the most important diagram in Western music +theory. Starting from any note and ascending by perfect fifths (7 +semitones), you pass through all 12 chromatic tones before returning +to the starting note: + +.. code-block:: python + + >>> t = Tone.from_string("C4", system="western") + >>> for i in range(12): + ... print(t.name, end=" ") + ... t = t + 7 + C G D A E B F# C# G# D# A# F + +Each step clockwise adds one sharp to the key signature; each step +counter-clockwise (ascending by fourths = 5 semitones) adds one flat.