Working with Tones

A Tone represents a single musical note, optionally 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

>>> from pytheory import Tone

>>> c4 = Tone.from_string("C4")
>>> cs4 = Tone.from_string("C#4")
>>> db4 = Tone.from_string("Db4")

>>> d = Tone(name="D", octave=3)

>>> a4 = Tone.from_string("A4", system="western")

>>> Tone.from_frequency(440)
<Tone A4>
>>> Tone.from_frequency(261.63)
<Tone C4>

>>> Tone.from_midi(60)
<Tone C4>
>>> Tone.from_midi(69)
<Tone A4>

Properties

>>> c4 = Tone.from_string("C4", system="western")
>>> c4.name
'C'
>>> c4.octave
4
>>> c4.full_name
'C4'
>>> c4.scientific      # alias of full_name
'C4'
>>> c4.letter
'C'
>>> c4.midi
60
>>> c4.exists
True

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).

>>> a4 = Tone.from_string("A4", system="western")
>>> a4.frequency
440.0

>>> Tone.from_string("A3", system="western").frequency
220.0

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

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.

  • Just intonation: Tunes every interval to a small whole-number frequency ratio — a pure 5:4 major third, a pure 3:2 fifth. Maximally consonant in a single key, but unplayable in distant ones without retuning. It’s the tuning behind the Indian ragas and Arabic maqamat (see Musical Systems).

The reference A is fixed, so every temperament agrees on it:

>>> a4.pitch(temperament="equal")
440.0
>>> a4.pitch(temperament="pythagorean")
440.0

The differences show up on the other notes:

>>> c5 = Tone.from_string("C5", system="western")
>>> c5.pitch(temperament="equal")
523.2511306011972
>>> c5.pitch(temperament="just")
528.0
>>> c5.pitch(temperament="pythagorean")
528.59619140625
>>> c5.pitch(temperament="meantone")
514.0261435447944

Reference Pitch and Precision

pitch() defaults to the modern concert standard of A4 = 440 Hz, but you can tune to any reference with reference_pitch — for example the “Verdi” A of 432 Hz that some musicians prefer. Pass precision to round the result to a fixed number of decimal places:

>>> a4 = Tone.from_string("A4", system="western")
>>> a4.pitch(reference_pitch=432.0)
432.0

>>> c4 = Tone.from_string("C4", system="western")
>>> c4.pitch(precision=2)
261.63
>>> c4.pitch(reference_pitch=432.0, precision=2)
256.87

For tuning systems built on exact rational ratios — like the shruti just intonation behind the ragas — pitch(symbolic=True) returns the value without coercing it to a float. In equal temperament the two are the same; the difference only matters once a system defines its own ratios (see Musical Systems).

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:

>>> c4 = Tone.from_string("C4", system="western")
>>> c4 + 4
<Tone E4>
>>> c4 + 7
<Tone G4>
>>> c4 + 12
<Tone C5>

If counting semitones by hand feels error-prone, the Interval class exports named constants (UNISON through OCTAVE) that read more musically:

>>> from pytheory import Interval
>>> c4 + Interval.PERFECT_FIFTH
<Tone G4>
>>> c4 + Interval.MAJOR_THIRD
<Tone E4>

Subtracting two tones gives the semitone distance:

>>> g4 = Tone.from_string("G4", system="western")
>>> g4 - c4
7

>>> c5 = Tone.from_string("C5", system="western")
>>> c5 - c4
12

Naming Intervals

The interval_to method gives the musical name of the interval between two tones, including compound intervals that span more than one octave:

>>> c4.interval_to(g4)
'perfect 5th'
>>> c4.interval_to(c4 + 4)
'major 3rd'
>>> c4.interval_to(c5)
'octave'

>>> c4.interval_to(c4 + 19)
'perfect 5th + 1 octave'

Transposition

The transpose method returns a new tone shifted by a number of semitones — equivalent to the + operator but reads more clearly in some contexts:

>>> c4.transpose(7)
<Tone G4>
>>> c4.transpose(-2)
<Tone A#3>

MIDI

Every tone maps to a MIDI note number (0–127), the standard for communicating with synthesizers, DAWs, and digital instruments:

>>> c4.midi
60
>>> Tone.from_string("A4", system="western").midi
69

>>> Tone.from_midi(60).midi
60

Black keys come back spelled with sharps by default; pass prefer_flats=True for the flat spelling instead:

>>> Tone.from_midi(61)
<Tone C#4>
>>> Tone.from_midi(61, prefer_flats=True)
<Tone Db4>

Comparison and Sorting

Tones can be compared and sorted by pitch frequency:

>>> c4 < g4
True
>>> sorted([g4, c4, e4])
[<Tone C4>, <Tone E4>, <Tone G4>]

Equality checks note name and octave:

>>> c4 == "C"
True
>>> c4 == Tone(name="C", octave=4)
True

The Overtone Series

Every tone you hear is actually a composite of many frequencies. When a string vibrates, it doesn’t just vibrate as a whole — it also vibrates in halves, thirds, quarters, and so on, producing the harmonic series:

>>> a4 = Tone.from_string("A4", system="western")
>>> a4.overtones(8)
[440.0, 880.0, 1320.0, 1760.0, 2200.0, 2640.0, 3080.0, 3520.0]

These harmonics correspond to musical intervals:

Harmonic  Frequency  Interval from fundamental
1st       440 Hz     Unison (A4)
2nd       880 Hz     Octave (A5)
3rd       1320 Hz    Octave + perfect 5th (E6)
4th       1760 Hz    Two octaves (A6)
5th       2200 Hz    Two octaves + major 3rd (C#7)
6th       2640 Hz    Two octaves + perfect 5th (E7)
7th       3080 Hz    Two octaves + minor 7th (≈G7, slightly flat)
8th       3520 Hz    Three octaves (A7)

The overtone series is why a perfect fifth sounds consonant — the 3rd harmonic of the lower note matches the 2nd harmonic of the upper note. It’s also why the major triad (root, major 3rd, perfect 5th) feels “natural” — these intervals appear in the first 6 harmonics.

Different instruments emphasize different harmonics, which is why a violin and a flute playing the same note sound different. This quality is called timbre.

Enharmonic Equivalents

In equal temperament, C# and Db are the same pitch (they have the same frequency). They’re called enharmonic equivalents. Which name you use depends on context:

  • In the key of D major (2 sharps), you write C#

  • In the key of Gb major (6 flats), you write Db

The rule: each letter name should appear exactly once in a scale. The D major scale is D E F# G A B C# — not D E Gb G A B Db, even though F#=Gb and C#=Db.

PyTheory uses sharps by default (following the tone list ordering), but every tone knows its enharmonic spelling:

>>> Tone.from_string("C#4", system="western").enharmonic
'Db'

>>> Tone.from_string("A#4", system="western").enharmonic
'Bb'

>>> Tone.from_string("C4", system="western").enharmonic is None
True

When you build tones from numbers rather than names — interval math via add, or from_midi() above — pass prefer_flats=True to get flat spellings instead of the default sharps:

>>> c4 = Tone.from_string("C4", system="western")
>>> c4.add(1)
<Tone C#4>
>>> c4.add(1, prefer_flats=True)
<Tone Db4>

Accidental Properties

Check whether a tone is natural, sharp, or flat:

>>> c = Tone.from_string("C4", system="western")
>>> c.is_natural
True
>>> c.is_sharp
False

>>> cs = Tone.from_string("C#4", system="western")
>>> cs.is_sharp
True
>>> cs.is_natural
False

>>> bb = Tone.from_string("Bb4", system="western")
>>> bb.is_flat
True

Useful for filtering — for example, finding all natural notes in a scale, or counting accidentals in a melody.

Extended Enharmonics

PyTheory supports the full range of enharmonic spellings used in real music theory:

  • Cb and Fb — musically valid flats (Cb = B, Fb = E)

  • E# and B# — musically valid sharps (E# = F, B# = C)

  • Double sharps (## or x) — e.g. F## = G

  • Double flats (bb) — e.g. Dbb = C

  • Unicode symbols (sharp), (flat), 𝄪 (double sharp), 𝄫 (double flat) are all recognized and normalized to ASCII

>>> Tone.from_string("E#4", system="western")
<Tone E#4>
>>> Tone.from_string("Fb4", system="western")
<Tone Fb4>
>>> Tone.from_string("Cb4", system="western")
<Tone Cb3>
>>> Tone.from_string("B#4", system="western")
<Tone B#5>

PyTheory keeps the spelling you wrote — E# stays E# rather than collapsing to F — so your enharmonic intent survives into chords, scales, and exported notation. The one adjustment happens at the B/C octave boundary: because scientific pitch notation rolls the octave number over at C, Cb4 sits just below C4 and becomes Cb3, while B# sits just above B and crosses up an octave.

Tone Validation

Validation only happens when a tone is attached to a system — that’s the tone table its name is checked against. from_string without a system skips the check, so it accepts any spelling (handy for parsing first and attaching a system later), but pitch and interval math will raise until a system is attached:

>>> Tone.from_string("X4")
<Tone X4>

Pass system="western" (or any system) to validate on construction — an unrecognized name raises a ValueError:

>>> Tone.from_string("X4", system="western")
Traceback (most recent call last):
    ...
ValueError: Unknown tone name: 'X'. Not found in the 'western' system.

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:

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

>>> [t.name for t in c4.circle_of_fifths()]
['C', 'G', 'D', 'A', 'E', 'B', 'F#', 'C#', 'G#', 'D#', 'A#', 'F']

>>> [t.name for t in c4.circle_of_fourths()]
['C', 'F', 'A#', 'D#', 'G#', 'C#', 'F#', 'B', 'E', 'A', 'D', 'G']

Each step clockwise adds one sharp to the key signature; each step counter-clockwise (ascending by fourths = 5 semitones) adds one flat.

Solfege

The fixed-Do solfege system maps each note to a singable syllable. PyTheory uses fixed Do (C is always Do):

>>> Tone.from_string("C4").solfege
'Do'
>>> Tone.from_string("D4").solfege
'Re'
>>> Tone.from_string("F#4").solfege
'Fi'
>>> Tone.from_string("Bb4").solfege
'Te'

Helmholtz Notation

The older Helmholtz notation uses case and tick marks instead of numbers:

>>> Tone.from_string("C3").helmholtz    # Great octave
'C'
>>> Tone.from_string("C4").helmholtz    # Middle C
'c'
>>> Tone.from_string("C5").helmholtz    # One-line octave
"c'"
>>> Tone.from_string("C2").helmholtz    # Contra octave
'CC'

Cents

A cent is 1/100th of a semitone — the standard unit for measuring fine pitch differences. Use cents_difference to compare tones or temperaments:

>>> c4 = Tone.from_string("C4", system="western")
>>> c4.cents_difference(c4 + 1)    # One semitone = 100 cents
100.0
>>> c4.cents_difference(c4 + 7)    # Perfect fifth
700.0

Tones are the atoms of music – everything else is built from them. Get comfortable here, and chords, scales, and harmony all start to make intuitive sense.