Skip to content
learn

Lesson 1: Notes, pitch classes and the fretboard

Every other lesson builds on four ideas: a pitch is a note at a given height, a pitch class is that note in any octave, an interval is the distance between two notes, and on a guitar each fret adds one of those distances, a semitone. This lesson goes through them in that order: the musical idea first, then how it is written, then the types that Guitar Alchemist (GA) uses for it.

All GA links point to commit a826864 of GuitarAlchemist/ga, the one the course program is built against.

The course program is in code/music-theory-ga. fetch-ga.sh clones GA at that commit without its file contents (--filter=blob:none), then checks out only three projects with git sparse-checkout. The C# project references one of them, GA.Domain.Core, like any other project of a solution (GaTheory.csproj). You need the .NET 10 SDK and Git; on Windows, run the commands from Git Bash.

Terminal window
bash code/music-theory-ga/check.sh # every lesson, compared with expected/
dotnet run --project code/music-theory-ga/GaTheory -c Release -- l1 # this lesson only, after check.sh

Each table prints the course’s own computation, written from the textbook definitions in Theory.cs, next to GA’s answer, and ok or DIFF. A DIFF is not a failure of the program: it is something GA does differently, and the lesson explains it. check.sh compares the whole output, DIFF lines included, with expected/l1.txt, on Linux, Windows and macOS in CI.

Play the open low E string, then the same note twelve frets higher: the second note sounds “the same, only higher”. Musicians call that octave equivalence. A pitch is one specific note, such as the E of the low string; a pitch class is every E in every octave (Open Music Theory, “Pitch and Pitch Class”).

Western music divides the octave into twelve equal semitones (half steps). If you know TimeOnly or LocalTime, a pitch class is the hour on a 12-hour clock and a pitch is a full timestamp: adding twelve semitones changes the octave, not the pitch class.

  • Scientific (American Standard) pitch notation writes a pitch as a note name and an octave number. Octave numbers change on C, and middle C is C4 (Open Music Theory, “ASPN”).
  • MIDI note numbers count semitones from 0 to 127: middle C is 60 and A4 (440 Hz) is 69 (MIDI tuning standard). So midi = 12 × (octave + 1) + pitch class.
  • Integer notation numbers the pitch classes from C = 0 to B = 11. All enharmonic spellings of a note share its number: B♯ is 0 and D♭ is 1 (Open Music Theory, “Pitch and Pitch Class”). To keep every pitch class one character long, 10 and 11 are often written T and E, as in the set class table of Open Music Theory, “Set Class and Prime Form”, where the major scale is (013568T).

The first table of the program converts the six open strings of a guitar, middle C, A4 and C♯4:

== Pitches: MIDI number and pitch class (C4 = 60)
pitch course GA check
E2 midi 40 pc 4 midi 40 pc 4 ok
A2 midi 45 pc 9 midi 45 pc 9 ok
D3 midi 50 pc 2 midi 50 pc 2 ok
G3 midi 55 pc 7 midi 55 pc 7 ok
B3 midi 59 pc 11 midi 59 pc 11 ok
E4 midi 64 pc 4 midi 64 pc 4 ok
C4 midi 60 pc 0 midi 60 pc 0 ok
A4 midi 69 pc 9 midi 69 pc 9 ok
C#4 midi 61 pc 1 midi 61 pc 1 ok
GA prints pitch classes as: 0 1 2 3 4 5 6 7 8 9 T E

The seven natural notes C D E F G A B are the white keys of a piano. Between most of them sits one more pitch class (a black key), except between E and F and between B and C, which are only a semitone apart. An accidental raises (♯, sharp) or lowers (♭, flat) a letter by a semitone; double sharps (𝄪, written x) and double flats (𝄫) move it by two.

So one pitch class has several names: C♯ and D♭ are the same key on the piano, and so are B♯ and C. These are enharmonic spellings (Open Music Theory, “Pitch and Pitch Class”). Octave numbers follow the letter, not the sound: B♯3 and C4 are the same key, in two octaves (Open Music Theory, “ASPN”). The name you choose depends on the context: lesson 3 shows why a C minor chord is spelled C E♭ G, not C D♯ G.

== Spellings: many names, one pitch class
note course GA check
C# 1 1 ok
Db 1 1 ok
B# 0 0 ok
Cb 11 11 ok
E# 5 5 ok
Fb 4 4 ok
Bbb 9 9 ok
Fx 7 7 ok

Note is a closed hierarchy of records, GA’s discriminated union: the C# counterpart of a Java sealed interface with record implementations.

classDiagram
class Note {
<<abstract record>>
PitchClass PitchClass
}
class Chromatic {
int Value
}
class KeyNote {
<<abstract>>
NaturalNote NaturalNote
}
class Sharp {
SharpAccidental? SharpAccidental
}
class Flat {
FlatAccidental? FlatAccidental
}
class Accidented {
Accidental? Accidental
}
Note <|-- Chromatic
Note <|-- KeyNote
KeyNote <|-- Sharp
KeyNote <|-- Flat
Note <|-- Accidented

An interval has two names at once (Open Music Theory, “Intervals”):

  • its number counts letters, both ends included: C up to E is a third (C, D, E), whatever the accidentals;
  • its quality comes from the number of semitones. Unisons, fourths, fifths and octaves are perfect (P) and become augmented (A) or diminished (d) when one semitone wider or narrower. Seconds, thirds, sixths and sevenths are major (M) or minor (m), a semitone apart, then augmented or diminished beyond.

That is why C–D♯ and C–E♭ are both three semitones but different intervals: an augmented second and a minor third. Turning an interval upside down (C up to E, then E up to C) is an inversion: the numbers add up to 9 (a third becomes a sixth), perfect stays perfect, major becomes minor and augmented becomes diminished (same chapter, “Intervallic Inversion”).

When only the pitch classes matter, the distance is folded to at most 6 semitones: that is the interval class, and C–E (4) and E–C (8) are both interval class 4 (Open Music Theory, “Intervals in Integer Notation”). Lesson 4 counts interval classes.

== Intervals: name from the letters, size in semitones, interval class
notes course GA check
C-E M3 4 ic4 M3 4 ic4 ok
C-Eb m3 3 ic3 m3 3 ic3 ok
C-D# A2 3 ic3 A2 3 ic3 ok
C-G P5 7 ic5 P5 7 ic5 ok
C-F# A4 6 ic6 A4 6 ic6 ok
C-Gb d5 6 ic6 d5 6 ic6 ok
B-F d5 6 ic6 d5 6 ic6 ok
E-C m6 8 ic4 m6 8 ic4 ok
!M3 m6 m6 ok

A guitar’s strings are numbered from the highest-sounding one: string 1 is the thin high E, string 6 the low E. Standard tuning is E2 A2 D3 G3 B3 E4 from string 6 to string 1 (Guitar tunings). Each fret raises the string by one semitone, so the note at string s, fret f is the open string’s MIDI number plus f. The same pitch appears on several strings: C4 is string 2 fret 1, string 3 fret 5 and string 4 fret 10. The Streeling module GTR-001 · The Fretboard Map walks through the natural notes on each string.

E2
A2
D3
G3
B3
E4

The diagram draws frets 0 to 12 in standard tuning, string 1 at the top as in tablature, and marks every position of pitch class C with its pitch. There are six: C3 on string 6 at fret 8 and on string 5 at fret 3, C4 on string 4 at fret 10, string 3 at fret 5 and string 2 at fret 1, and C5 on string 1 at fret 8. Three positions play the very same pitch, C4.

0 1 2 3 4 5 6 7 8 9 10 11 12 C5 C4 C4 C4 C3 C3
== Standard tuning: string 1 is the highest
string course GA check
1 E4 E4 ok
2 B3 B3 ok
3 G3 G3 ok
4 D3 D3 ok
5 A2 A2 ok
6 E2 E2 ok
== Fretboard: each fret adds one semitone
string/fret course GA check
6/0 E2 40 E2 40 ok
6/3 G2 43 G2 43 ok
5/3 C3 48 C3 48 ok
2/1 C4 60 C4 60 ok
3/5 C4 60 C4 60 ok
1/8 C5 72 C5 72 ok
4/10 C4 60 C4 60 ok
== Where is pitch class C on frets 0-12? (string/fret)
course 6/8 5/3 4/10 3/5 2/1 1/8
GA, Note.Chromatic 6/8 5/3 4/10 3/5 2/1 1/8
GA, Note.Sharp (none)
  • Tuning is built from a string of pitches: Tuning.Default parses "E2 A2 D3 G3 B3 E4", low to high, and BuildPitchArray reverses it when the first pitch is the lowest, so that the indexer tuning[new Str(1)] is the highest string, as guitarists count.
  • Str (1 to 26) and Fret (−1 for a muted string, 0 open, up to 36) are value objects again.
  • Fretboard.GetNote adds the fret to the open string’s MIDI note, as above, but takes a 0-based string index: GetNote(0, 0) is string 1.
  • GetPositionsForNote compares each position’s note with Equals. GetNote returns a Note.Chromatic, and records of different types are never equal, so asking for Note.Sharp.C finds nothing: the (none) line above.

Writing the comparisons turned up a few places where GA answers something else than the theory. The course program keeps them as DIFF lines, so that CI notices if a later GA commit changes them; they are also in the journal.

== Surprises found while writing this lesson
call course GA check
Pitch.Flat.DFlat(4) Db4 D4 DIFF
Pitch.Flat.GFlat(4) Gb4 A4 DIFF
Pitch.Flat.FFlat(4) Fb4 G4 DIFF
Pitch.Sharp.TryParse Eb2 rejected B2 DIFF
Note.Flat.Parse B B Bb DIFF
PitchClass.Parse A 9 10 DIFF
IntervalSize.TryParse x False throws ArgumentException DIFF
  • Pitch.Flat.DFlat(octave), FFlat and GFlat build D, G and A instead of D♭, F♭ and G♭. The properties DFlat4 and friends are right; only the methods are wrong.
  • PitchParser searches its pattern ([A-G])(#?)(10|11|[0-9]) without ^…$ anchors and ignores case: in "Eb2", the sharp parser skips the E and matches b2 as B2.
  • Note.Flat.TryParse upper-cases its input before replacing ♭ with b, then treats a trailing B as a flat sign. Those two lines break it in both directions: "B" itself becomes B♭, and a real "D♭" is rejected, because the replacement leaves a lowercase b where the case-sensitive EndsWith("B") no longer recognises it.
  • PitchClass.TryParse reads A and B as the digits 10 and 11, on purpose: the comment calls them “common hex-like aliases sometimes used in set notation”. The convention is real; the bug is the order. The aliases are tried before the note names, so "A" is pitch class 10 and the note A, pitch class 9, can never be parsed at all.
  • SimpleIntervalSize.TryParse throws on bad input instead of returning false, unlike the IParsable<TSelf>.TryParse contract, and CompoundIntervalSize.TryParse does the same. The table shortens the name: the type is SimpleIntervalSize.
  1. Which pitch, MIDI number and pitch class is string 4, fret 7 in standard tuning?
  2. Name the intervals E–B♭ and A–C♯, with their size in semitones and their interval class.
  3. In drop D tuning, string 6 is lowered to D2 (Guitar tunings). Where is pitch class D on frets 0 to 5? Build that fretboard with GA’s Tuning and Fretboard types.
Solutions
  1. String 4 is D3 (MIDI 50); seven frets higher is A3, MIDI 57, pitch class 9.

  2. E to B♭ covers five letters (E F G A B) and 6 semitones, one less than a perfect fifth: a diminished fifth, interval class 6. A to C♯ covers three letters and 4 semitones: a major third, interval class 4.

  3. Strings 6 and 4 open, string 5 fret 5 and string 2 fret 3. With GA, a tuning is a parsed pitch collection (Lesson1.cs):

    var dropD = new Fretboard(new Tuning(PitchCollection.Parse("D2 A2 D3 G3 B3 E4")), 5);
    var positions = dropD.GetPositionsForNote(Note.Chromatic.D);
    == Exercise solutions
    question course GA check
    1. string 4, fret 7 A3 midi 57 pc 9 A3 midi 57 pc 9 ok
    2. E-Bb d5 6 ic6 d5 6 ic6 ok
    2. A-C# M3 4 ic4 M3 4 ic4 ok
    3. D in drop D, 0-5 6/0 5/5 4/0 2/3 6/0 5/5 4/0 2/3 ok
  • A pitch is a pitch class plus an octave; with MIDI numbers, pitch class = midi % 12 and middle C is 60.
  • A note name is a letter plus accidentals; several names share a pitch class. GA keeps both: PitchClass for the number, the Note records for the spelling.
  • An interval’s number comes from the letters and its quality from the semitones; the interval class forgets both the direction and the octave.
  • On a guitar, a position is a string and a fret: the open string’s pitch plus one semitone per fret. GA numbers strings from the highest, like guitarists.
  • GA’s primitives are small value objects, readonly record structs that validate or normalize their range, and records for the unions: the same modelling you would use for money or identifiers.