Music Theory Fundamentals

This page covers the essential concepts of music theory — the framework behind everything PyTheory does. Don’t worry if you’re new to this: music theory isn’t a set of rules you have to memorize, it’s a vocabulary for describing what you already hear. Every concept below connects to something you’ve felt while listening to music — this page just gives it a name.

Sound and Pitch

All sound is vibration. When an object vibrates, it pushes air molecules back and forth, creating pressure waves that travel to your ears. The speed of this vibration — measured in cycles per second (Hertz, Hz) — determines the pitch you hear.

  • 20 Hz: the lowest pitch most humans can hear

  • 60–250 Hz: the range of the human voice (speaking)

  • 261.63 Hz: middle C (C4)

  • 440 Hz: the concert pitch tuning standard A (A4)

  • 4186 Hz: the highest C on a piano (C8)

  • 20,000 Hz: the upper limit of human hearing

The relationship between pitch and frequency is logarithmic — each octave doubles the frequency. This means the distance from A3 (220 Hz) to A4 (440 Hz) is 220 Hz, but the distance from A4 to A5 (880 Hz) is 440 Hz. Both sound like “one octave” to our ears.

Why Twelve Notes?

The Western chromatic scale has 12 notes per octave. This isn’t arbitrary — it emerges from the physics of vibrating strings and air columns.

The harmonic series is the sequence of frequencies produced when a string vibrates: f, 2f, 3f, 4f, 5f… The relationships between these harmonics create the intervals we perceive as consonant:

If you stack perfect fifths (multiply by 3/2 repeatedly) and reduce to within one octave, you get 12 roughly evenly-spaced notes before the cycle almost closes. The tiny gap where it doesn’t close perfectly is the Pythagorean comma — the reason we need temperament.

>>> 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']

Other cultures divide the octave differently: Indonesian gamelan uses 5 or 7 unequal divisions; Indian classical music theoretically has 22 shrutis (microtones); Arabic maqam uses quarter-tones.

These aren’t just footnotes — PyTheory ships them as real tuning systems (SYSTEMS["slendro"], SYSTEMS["pelog"], SYSTEMS["shruti"], SYSTEMS["maqam"], and more), plus equal-temperament experiments like TET(19), TET(31), and bohlen-pierce. There are also two dedicated melodic-tradition classes: Raga (54 ragas, both Hindustani and Carnatic) and Maqam (true Arabic quarter-tones a piano can’t play). The Musical Systems guide goes deep on all of them.

>>> from pytheory import Raga, Maqam

>>> Raga.get("yaman").note_names(sa="C")
['C', 'D', 'E', 'F#', 'G', 'A', 'B']
>>> Maqam.get("rast").degree_names()
['Do', 'Re', 'Mi↓', 'Fa', 'Sol', 'La', 'Si↓']

The marks a quarter-flat — the half-flat third that gives a maqam its colour and that twelve equal notes simply can’t reach.

Intervals: The Atoms of Music

An interval is the distance between two pitches. Intervals are the building blocks of everything — melodies are sequences of intervals, chords are stacks of intervals, and scales are patterns of intervals.

Every interval has two properties:

Size (how many scale steps):

Unison → 2nd → 3rd → 4th → 5th → 6th → 7th → Octave

Quality (exact number of semitones):

Perfect:     unison (0), 4th (5), 5th (7), octave (12)
Major:       2nd (2), 3rd (4), 6th (9), 7th (11)
Minor:       2nd (1), 3rd (3), 6th (8), 7th (10)
Augmented:   one semitone larger than perfect or major
Diminished:  one semitone smaller than perfect or minor

The “perfect” intervals (unison, 4th, 5th, octave) are called perfect because they appear in both major AND minor scales unchanged. They’ve been considered consonant across virtually all musical cultures throughout history.

The tritone (augmented 4th / diminished 5th = 6 semitones) divides the octave exactly in half. Medieval theorists called it diabolus in musica (“the devil in music”) because of its extreme instability. Today it’s the foundation of dominant harmony and the blues.

PyTheory gives every interval a name. Interval holds the semitone constants, Tone.interval_to names the gap between two tones (compound forms and all), and cents_difference measures it to the cent — one hundred per semitone:

>>> from pytheory import Tone, Interval

>>> Interval.PERFECT_FIFTH, Interval.MAJOR_THIRD, Interval.TRITONE
(7, 4, 6)

>>> C4 = Tone.from_string("C4", system="western")
>>> C4.interval_to(C4 + Interval.PERFECT_FIFTH)
'perfect 5th'
>>> C4.interval_to(C4 + Interval.OCTAVE)
'octave'
>>> round(C4.cents_difference(C4 + 1), 1)  # one semitone
100.0

Keys and Key Signatures

A key is a group of notes that form the tonal center of a piece. The key of C major uses only the white keys on the piano: C D E F G A B. The key of G major uses the same notes except F becomes F#.

Key signatures tell you which notes are sharped or flatted throughout a piece. They follow the circle of fifths:

Sharp keys (add one sharp per step clockwise):

C major:  no sharps or flats
G major:  F#
D major:  F# C#
A major:  F# C# G#
E major:  F# C# G# D#
B major:  F# C# G# D# A#

Flat keys (add one flat per step counter-clockwise):

C major:  no sharps or flats
F major:  Bb
Bb major: Bb Eb
Eb major: Bb Eb Ab
Ab major: Bb Eb Ab Db
Db major: Bb Eb Ab Db Gb

The order of sharps is always F C G D A E B (Father Charles Goes Down And Ends Battle). The order of flats is the reverse: B E A D G C F.

Harmony: How Chords Work

Harmony is the art of combining tones simultaneously. While melody is horizontal (tones in sequence), harmony is vertical (tones stacked).

The simplest harmony is the triad — three notes built by stacking thirds. The quality of each third determines the chord type:

  • Major triad = major 3rd + minor 3rd (e.g. C-E-G)

  • Minor triad = minor 3rd + major 3rd (e.g. C-Eb-G)

  • Diminished triad = minor 3rd + minor 3rd (e.g. B-D-F)

  • Augmented triad = major 3rd + major 3rd (e.g. C-E-G#)

In any major key, the triads built on each scale degree always follow the same pattern:

Degree   Quality        Function
I        Major          Tonic (home)
ii       Minor          Pre-dominant
iii      Minor          Tonic substitute
IV       Major          Subdominant (departure)
V        Major          Dominant (tension, wants to go home)
vi       Minor          Tonic substitute, relative minor
vii°     Diminished     Dominant substitute (leading tone chord)

This pattern is the DNA of Western harmony. Pop songs, classical sonatas, jazz standards, and church hymns all derive from it.

Functional Harmony

Chords don’t just have names — they have functions:

  • Tonic function (I, iii, vi): stability, rest, home

  • Subdominant function (ii, IV): motion away from home

  • Dominant function (V, vii°): tension, desire to return home

The most fundamental progression in Western music is T → S → D → T (tonic → subdominant → dominant → tonic). The classic I-IV-V-I is exactly this pattern. Every “Louie Louie” and every Bach chorale follows this basic tonal gravity.

>>> from pytheory import TonedScale

>>> scale = TonedScale(tonic="C4")["major"]

>>> scale.triad(0).identify()
'C major'
>>> scale.triad(3).identify()
'F major'
>>> scale.triad(4).identify()
'G major'

The Dominant Seventh

The most important chord in tonal music is the dominant seventh — the V7 chord. In C major, this is G-B-D-F. It contains:

  • A leading tone (B) that pulls up to the tonic (C) by half step

  • A tritone (B-F) that wants to resolve inward (B→C, F→E)

  • The dominant note (G) that falls to the tonic by a fifth

This combination creates the strongest possible pull toward resolution. When you hear V7→I, you feel arrival.

>>> from pytheory import Chord, Tone

>>> C4 = Tone.from_string("C4", system="western")
>>> G4 = Tone.from_string("G4", system="western")

>>> g7 = Chord([G4, G4+4, G4+7, G4+10])
>>> g7.identify()
'G dominant 7th'
>>> g7.tension['has_dominant_function']
True
>>> g7.tension['tritones']
1

>>> c_major = Chord([C4, C4+4, C4+7])
>>> c_major.tension['score']
0.0

Cadences

A cadence is the harmonic punctuation that ends a phrase — the musical equivalent of a full stop, comma, or question mark. detect_cadence names the gesture from the last two chords and the key:

>>> from pytheory import Chord, detect_cadence

>>> detect_cadence(Chord.from_name("G"), Chord.from_name("C"), "C")
'imperfect authentic'
>>> detect_cadence(Chord.from_name("G"), Chord.from_name("Am"), "C")
'deceptive'
>>> detect_cadence(Chord.from_name("F"), Chord.from_name("C"), "C")
'plagal'
>>> detect_cadence(Chord.from_name("Dm"), Chord.from_name("G"), "C")
'half'

The cadences, from most to least conclusive:

  • Perfect authentic (PAC) — V → I, both root position, with the tonic in the top voice. The strongest ending; the sound of “the end.”

  • Imperfect authentic (IAC) — also V → I (or vii° → I), but softened by an inversion or a non-tonic soprano. A close root-position triad lands its fifth on top, so it reads as imperfect — voice the tonic in the soprano for a PAC.

  • Half — the phrase ends on the dominant (… → V): unfinished, a comma.

  • Phrygian half — in minor, iv⁶ → V, the bass falling a semitone.

  • Deceptive — V → vi instead of the expected tonic: the surprise.

  • Plagal — IV → I, the “Amen” cadence.

>>> tonic_top = Chord.from_midi_message(48, 52, 55, 60)   # C3 E3 G3 C4
>>> detect_cadence(Chord.from_name("G"), tonic_top, "C")
'perfect authentic'

find_cadences scans a whole progression and returns each cadential motion (by the index of its final chord):

>>> from pytheory import find_cadences
>>> prog = [Chord.from_name(n) for n in ("C", "F", "G", "Am")]
>>> find_cadences(prog, "C")
[(2, 'half'), (3, 'deceptive')]

Non-Chord Tones

Not every melody note belongs to the chord underneath it. The notes that don’tnon-chord tones — are what give a line its shape: passing notes that fill a gap, neighbors that lean and return, suspensions that hang and resolve. analyze_non_chord_tones labels each note from its melodic context and the harmony beneath it:

>>> from pytheory import Chord, Tone, analyze_non_chord_tones

>>> melody = [Tone.from_string(n) for n in ("C4", "D4", "E4")]
>>> [r["type"] for r in analyze_non_chord_tones(melody, Chord.from_name("C"))]
['chord tone', 'passing', 'chord tone']

Pass a single chord for the whole melody, or a list with one chord per note. The recognised figures are passing, upper / lower neighbor, suspension, anticipation, appoggiatura, and escape tone — for example a suspension, prepared on one chord and resolved down a step on the next:

>>> C, G = Chord.from_name("C"), Chord.from_name("G")
>>> notes = [Tone.from_string(n) for n in ("C4", "C4", "B3")]
>>> [r["type"] for r in analyze_non_chord_tones(notes, [C, G, G])]
['chord tone', 'suspension', 'chord tone']

Secondary Dominants

A secondary dominant is a chord that briefly acts as the dominant of some chord other than the tonic — borrowing a chromatic leading tone to point at it. In C major, D7 (with its F♯) leans toward G, so it functions as V7/V. detect_secondary_dominant spots one chord, and analyze_progression can label them in context:

>>> from pytheory import Chord, detect_secondary_dominant, analyze_progression

>>> detect_secondary_dominant(Chord.from_symbol("D7"), "C")
'V7/V'
>>> detect_secondary_dominant(Chord.from_symbol("E7"), "C")
'V7/vi'

>>> prog = [Chord.from_symbol(s) for s in ("C", "D7", "G7", "C")]
>>> analyze_progression(prog, "C", secondary_dominants=True)
['I', 'V7/V', 'V7', 'I']

Without the flag those same chords read as the plainer II7 and V7; turning it on reveals the brief tonicisation of G.

Voice Leading and Beyond

Naming chords is only half the craft; the other half is connecting them. Good voice leading moves each voice as little as possible. Chord.voice_leading finds the smoothest path between two chords — each voice paired with its destination and the signed number of semitones it travels:

>>> from pytheory import Chord

>>> [(a.name, b.name, semis)
...  for a, b, semis in Chord.from_name("C").voice_leading(Chord.from_name("G"))]
[('G', 'B', 4), ('E', 'G', 3), ('C', 'D', 2)]

That’s the entry point to a larger analysis toolkit that lives in the Working with Chords guide: check_voice_leading flags parallel fifths, parallel octaves, and voice crossings across a progression; chord_scales and avoid_notes answer “what can I play over this chord?”; and reharmonize / negative_harmony suggest fresh chords for an old tune.

Rhythm and Meter

While PyTheory focuses on pitch, rhythm is the other half of music.

Rhythm is the pattern of durations. Meter is the recurring pattern of strong and weak beats that organizes rhythm.

  • 4/4 time: the most common meter. Strong-weak-medium-weak. Used in rock, pop, hip-hop, most Western music.

  • 3/4 time: waltz time. Strong-weak-weak. A lilting, circular feel.

  • 6/8 time: compound duple. Two groups of three. Irish jigs, many ballads.

  • 12/8 time: compound quadruple. Four groups of three. Slow blues, doo-wop, gospel. Has a triplet feel over a 4/4 pulse — the shuffle groove of “Stormy Monday” and “Oh! Darling.”

  • 5/4 time: asymmetric. “Take Five” by Dave Brubeck. Creates constant forward momentum because it never fully settles.

  • 7/8 time: common in Balkan folk music. Often felt as 2+2+3 or 3+2+2.

The Physics of Consonance

Why do some intervals sound “good” and others “bad”? The answer lies in the physics of sound waves and the Plomp-Levelt model of sensory dissonance.

When two frequencies are related by a simple ratio (like 3:2 for a perfect fifth), their waveforms align regularly. The combined wave is smooth and periodic — the brain perceives this as consonant.

When two frequencies are related by a complex ratio (like 45:32 for a tritone), their waveforms rarely align. The combined wave is irregular and the brain perceives roughness — dissonance.

But consonance and dissonance are also cultural. The major third (5:4) was considered dissonant in medieval European music but consonant since the Renaissance. The tritone was forbidden in church music but is the foundation of blues and jazz. Indonesian gamelan embraces beating between paired instruments as a core aesthetic.

>>> from pytheory import Chord, Tone

>>> C4 = Tone.from_string("C4", system="western")
>>> E4 = Tone.from_string("E4", system="western")
>>> G4 = Tone.from_string("G4", system="western")

>>> [round(f, 2) for f in C4.overtones(6)]
[261.63, 523.25, 784.88, 1046.5, 1308.13, 1569.75]

>>> fifth = Chord([C4, G4])
>>> tritone = Chord([C4, C4 + 6])
>>> fifth.harmony > tritone.harmony
True

>>> octave = Chord([C4, C4 + 12])
>>> third = Chord([C4, E4])
>>> octave.dissonance < third.dissonance
True

>>> c_major = Chord([C4, E4, G4])
>>> c_major.tension['score']
0.0

>>> g7 = Chord([G4, G4+4, G4+7, G4+10])
>>> g7.tension['score']
0.6
>>> g7.tension['tritones']
1
>>> g7.tension['has_dominant_function']
True

From Theory to Composition

Everything on this page — tones, intervals, chords, scales, keys — is the foundation. But PyTheory goes further: you can use these building blocks to compose and play actual music. The Sequencing guide shows how to arrange multi-part scores with melodies, chord pads, bass lines, drum patterns, and audio effects — then export them to LilyPond, MusicXML, or ABC notation (articulations and lyrics included).

The flow runs both ways. The Command-Line Interface guide’s pytheory analyze song.mid detects the key and prints a Roman-numeral chord timeline for any MIDI file, and the Listening — Microphone In guide covers transcription — Score.from_midi and Score.from_wav turn recordings back into scores you can analyze with the very tools above.

Further Reading

Theory is just a vocabulary for what you already hear. You don’t need it to make music – but once you have the words, you can talk about what you’re doing, understand why it works, and find new places to go.