mirror of
https://github.com/kennethreitz/pytheory.git
synced 2026-07-21 18:19:30 +00:00
33b0636cc4
A full freshness + quality pass across the documentation, with every code example verified by actually running it against v0.57.8. Content / accuracy: - Fixed stale and broken examples throughout: SymPy-based "symbolic pitch" (SymPy was removed), the case-sensitive progression parser (I/V/vi/IV vs i/iv), analog_drift -> analog, Key.relative/parallel returning Key objects, Tone.from_string validation needing a system, stale tab()/scale_diagram() output, and many counts (74 drum sounds, 100 patterns, 37 fills, 19 chord types, 16 systems, 25 fretboard instruments, 56 waveforms, 83 presets). - Documented major recent features that were missing: Maqam (quarter-tone Arabic maqamat) and Raga (Hindustani + Carnatic, shruti just intonation) in the systems guide and the CLI; SVG/PNG diagram export; progression / cadence / secondary-dominant analysis and reharmonization; notation export to LilyPond/MusicXML/ABC incl. lyrics; from_wav transcription; render_scores batch rendering; CLI raga/maqam and `analyze song.mid`. Navigation / presentation: - Added sphinx-design + sphinx-copybutton to the docs deps and conf. - Rebuilt the homepage "Why would I want this?" into a sphinx-design persona card grid with CTA buttons and a badge row; wired the brand logo into the sidebar (and suppressed the now-redundant project-name text). - Added API-reference pages for the public Raga and Maqam classes. Also: removed a duplicate `jati` attribute line from the Raga docstring so autodoc no longer double-documents it. Sphinx build is clean (0 warnings).
552 lines
16 KiB
ReStructuredText
552 lines
16 KiB
ReStructuredText
Working with Tones
|
||
==================
|
||
|
||
A :class:`~pytheory.tones.Tone` represents a single musical note, optionally
|
||
with an octave number in `scientific pitch notation <https://en.wikipedia.org/wiki/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 <https://en.wikipedia.org/wiki/Enharmonic>`_ —
|
||
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 <https://en.wikipedia.org/wiki/A440_(pitch_standard)>`_ — 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
|
||
--------------
|
||
|
||
.. code-block:: pycon
|
||
|
||
>>> 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
|
||
----------
|
||
|
||
.. code-block:: pycon
|
||
|
||
>>> 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).
|
||
|
||
.. code-block:: pycon
|
||
|
||
>>> 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 <https://en.wikipedia.org/wiki/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 <https://en.wikipedia.org/wiki/Pythagorean_tuning>`_: 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 <https://en.wikipedia.org/wiki/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 <https://en.wikipedia.org/wiki/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 :doc:`systems`).
|
||
|
||
The reference A is fixed, so every temperament agrees on it:
|
||
|
||
.. code-block:: pycon
|
||
|
||
>>> a4.pitch(temperament="equal")
|
||
440.0
|
||
>>> a4.pitch(temperament="pythagorean")
|
||
440.0
|
||
|
||
The differences show up on the other notes:
|
||
|
||
.. code-block:: pycon
|
||
|
||
>>> 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:
|
||
|
||
.. code-block:: pycon
|
||
|
||
>>> 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 :doc:`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:
|
||
|
||
.. code-block:: pycon
|
||
|
||
>>> 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
|
||
:class:`~pytheory.tones.Interval` class exports named constants
|
||
(``UNISON`` through ``OCTAVE``) that read more musically:
|
||
|
||
.. code-block:: pycon
|
||
|
||
>>> from pytheory import Interval
|
||
>>> c4 + Interval.PERFECT_FIFTH
|
||
<Tone G4>
|
||
>>> c4 + Interval.MAJOR_THIRD
|
||
<Tone E4>
|
||
|
||
Subtracting two tones gives the semitone distance:
|
||
|
||
.. code-block:: pycon
|
||
|
||
>>> 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:
|
||
|
||
.. code-block:: pycon
|
||
|
||
>>> 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:
|
||
|
||
.. code-block:: pycon
|
||
|
||
>>> c4.transpose(7)
|
||
<Tone G4>
|
||
>>> c4.transpose(-2)
|
||
<Tone A#3>
|
||
|
||
MIDI
|
||
~~~~
|
||
|
||
Every tone maps to a `MIDI note number <https://en.wikipedia.org/wiki/MIDI>`_
|
||
(0–127), the standard for communicating with synthesizers, DAWs, and
|
||
digital instruments:
|
||
|
||
.. code-block:: pycon
|
||
|
||
>>> 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:
|
||
|
||
.. code-block:: pycon
|
||
|
||
>>> 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:
|
||
|
||
.. code-block:: pycon
|
||
|
||
>>> c4 < g4
|
||
True
|
||
>>> sorted([g4, c4, e4])
|
||
[<Tone C4>, <Tone E4>, <Tone G4>]
|
||
|
||
Equality checks note name and octave:
|
||
|
||
.. code-block:: pycon
|
||
|
||
>>> 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 <https://en.wikipedia.org/wiki/Harmonic_series_(music)>`_:
|
||
|
||
.. code-block:: pycon
|
||
|
||
>>> 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 <https://en.wikipedia.org/wiki/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:
|
||
|
||
.. code-block:: pycon
|
||
|
||
>>> 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 :func:`~pytheory.tones.Tone.from_midi` above — pass
|
||
``prefer_flats=True`` to get flat spellings instead of the default sharps:
|
||
|
||
.. code-block:: pycon
|
||
|
||
>>> 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:
|
||
|
||
.. code-block:: pycon
|
||
|
||
>>> 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
|
||
|
||
.. code-block:: pycon
|
||
|
||
>>> 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:
|
||
|
||
.. code-block:: pycon
|
||
|
||
>>> Tone.from_string("X4")
|
||
<Tone X4>
|
||
|
||
Pass ``system="western"`` (or any system) to validate on construction — an
|
||
unrecognized name raises a ``ValueError``:
|
||
|
||
.. code-block:: pycon
|
||
|
||
>>> 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 <https://en.wikipedia.org/wiki/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:: pycon
|
||
|
||
>>> 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 <https://en.wikipedia.org/wiki/Solf%C3%A8ge>`_ system
|
||
maps each note to a singable syllable. PyTheory uses fixed Do (C is always Do):
|
||
|
||
.. code-block:: pycon
|
||
|
||
>>> 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 <https://en.wikipedia.org/wiki/Helmholtz_pitch_notation>`_
|
||
uses case and tick marks instead of numbers:
|
||
|
||
.. code-block:: pycon
|
||
|
||
>>> 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:
|
||
|
||
.. code-block:: pycon
|
||
|
||
>>> 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.
|