Skip to content

Music Theory Primitives

This tutorial covers the music theory primitives available in the coremusic.music module for working with notes, intervals, scales, and chords.

Overview

The music module provides:

  • Note: Musical note with MIDI number, name, octave, and frequency
  • Interval: Distance between two notes with quality (major, minor, perfect)
  • Scale: 25+ scale types including modes and exotic scales
  • Chord: 35+ chord types from triads to extended/altered chords

Music Theory Basics

Notes

The Note class represents a musical note:

from coremusic.music.theory import Note

# Create notes from name and octave
c4 = Note('C', 4)  # Middle C
fs3 = Note('F#', 3)
bb5 = Note('Bb', 5)

# Note properties
print(f"MIDI: {c4.midi}")           # 60
print(f"Name: {c4.name}")           # C
print(f"Octave: {c4.octave}")       # 4
print(f"Frequency: {c4.frequency:.2f} Hz")  # 261.63 Hz

# Create from MIDI number
a4 = Note.from_midi(69)  # A440

# Transposition
e4 = c4.transpose(4)   # Up 4 semitones -> E4
g3 = c4.transpose(-5)  # Down 5 semitones -> G3

Intervals

The Interval class represents the distance between two notes:

from coremusic.music.theory import Interval

# Standard intervals (semitone values)
print(Interval.UNISON)         # 0 semitones
print(Interval.MINOR_SECOND)   # 1 semitone
print(Interval.MAJOR_SECOND)   # 2 semitones
print(Interval.MINOR_THIRD)    # 3 semitones
print(Interval.MAJOR_THIRD)    # 4 semitones
print(Interval.PERFECT_FOURTH) # 5 semitones
print(Interval.TRITONE)        # 6 semitones
print(Interval.PERFECT_FIFTH)  # 7 semitones
print(Interval.OCTAVE)         # 12 semitones

Scales

The Scale class provides 25+ scale types:

from coremusic.music.theory import Note, Scale, ScaleType

root = Note('C', 4)

# Diatonic scales
major = Scale(root, ScaleType.MAJOR)
natural_minor = Scale(root, ScaleType.NATURAL_MINOR)
harmonic_minor = Scale(root, ScaleType.HARMONIC_MINOR)
melodic_minor = Scale(root, ScaleType.MELODIC_MINOR)

# Modes
dorian = Scale(Note('D', 4), ScaleType.DORIAN)
phrygian = Scale(Note('E', 4), ScaleType.PHRYGIAN)
lydian = Scale(Note('F', 4), ScaleType.LYDIAN)
mixolydian = Scale(Note('G', 4), ScaleType.MIXOLYDIAN)
locrian = Scale(Note('B', 4), ScaleType.LOCRIAN)

# Pentatonic and blues
major_pent = Scale(root, ScaleType.MAJOR_PENTATONIC)
minor_pent = Scale(root, ScaleType.MINOR_PENTATONIC)
blues = Scale(root, ScaleType.BLUES)
major_blues = Scale(root, ScaleType.MAJOR_BLUES)

# Symmetric scales
whole_tone = Scale(root, ScaleType.WHOLE_TONE)
diminished = Scale(root, ScaleType.DIMINISHED)
chromatic = Scale(root, ScaleType.CHROMATIC)

# World scales
double_harmonic = Scale(root, ScaleType.DOUBLE_HARMONIC)
hungarian_minor = Scale(root, ScaleType.HUNGARIAN_MINOR)
persian = Scale(root, ScaleType.PERSIAN)
arabian = Scale(root, ScaleType.ARABIAN)

# Exotic scales
hirajoshi = Scale(root, ScaleType.HIRAJOSHI)
in_sen = Scale(root, ScaleType.IN_SEN)
iwato = Scale(root, ScaleType.IWATO)
balinese = Scale(root, ScaleType.BALINESE)

# Get scale notes
notes = major.get_notes()
print([n.name for n in notes])
# ['C', 'D', 'E', 'F', 'G', 'A', 'B']

# Get MIDI note numbers
midi_notes = major.get_midi_notes()
print(midi_notes)  # [60, 62, 64, 65, 67, 69, 71]

Chords

The Chord class provides 35+ chord types:

from coremusic.music.theory import Chord, ChordType, Note

root = Note('C', 4)

# Triads
major = Chord(root, ChordType.MAJOR)           # C, E, G
minor = Chord(root, ChordType.MINOR)           # C, Eb, G
diminished = Chord(root, ChordType.DIMINISHED) # C, Eb, Gb
augmented = Chord(root, ChordType.AUGMENTED)   # C, E, G#
sus2 = Chord(root, ChordType.SUS2)             # C, D, G
sus4 = Chord(root, ChordType.SUS4)             # C, F, G

# Seventh chords
dom7 = Chord(root, ChordType.DOMINANT_7)       # C, E, G, Bb
maj7 = Chord(root, ChordType.MAJOR_7)          # C, E, G, B
min7 = Chord(root, ChordType.MINOR_7)          # C, Eb, G, Bb
dim7 = Chord(root, ChordType.DIMINISHED_7)     # C, Eb, Gb, Bbb
half_dim7 = Chord(root, ChordType.HALF_DIMINISHED_7)  # C, Eb, Gb, Bb
min_maj7 = Chord(root, ChordType.MINOR_MAJOR_7)       # C, Eb, G, B

# Extended chords
dom9 = Chord(root, ChordType.DOMINANT_9)       # C, E, G, Bb, D
maj9 = Chord(root, ChordType.MAJOR_9)          # C, E, G, B, D
dom11 = Chord(root, ChordType.DOMINANT_11)     # C, E, G, Bb, D, F
dom13 = Chord(root, ChordType.DOMINANT_13)     # C, E, G, Bb, D, F, A

# Altered chords
dom7b5 = Chord(root, ChordType.DOMINANT_7_FLAT_5)
dom7b9 = Chord(root, ChordType.DOMINANT_7_FLAT_9)
dom7s9 = Chord(root, ChordType.DOMINANT_7_SHARP_9)

# Added tone chords
add9 = Chord(root, ChordType.ADD9)             # C, E, G, D
six = Chord(root, ChordType.MAJOR_6)           # C, E, G, A

# Get chord notes
notes = maj7.get_notes()
print([n.name for n in notes])
# ['C', 'E', 'G', 'B']

# Get MIDI note numbers
midi_notes = maj7.get_midi_notes()
print(midi_notes)  # [60, 64, 67, 71]

Key Signatures

Work with key signatures and the circle of fifths:

from coremusic.music.theory import CIRCLE_OF_FIFTHS, KEY_SIGNATURES

# Key signatures show sharps/flats, and whether the key is minor
print(KEY_SIGNATURES['G'])   # (['F#'], False) - 1 sharp, major
print(KEY_SIGNATURES['Bb'])  # (['Bb', 'Eb'], False) - 2 flats, major
print(KEY_SIGNATURES['Am'])  # ([], True) - no accidentals, minor

# Circle of fifths
print(CIRCLE_OF_FIFTHS)
# ['C', 'G', 'D', 'A', 'E', 'B', 'F#', 'Db', 'Ab', 'Eb', 'Bb', 'F']

Utility Functions

Convert between note names and MIDI numbers:

from coremusic.music.theory import midi_to_note_name, note_name_to_midi

# Name to MIDI
midi = note_name_to_midi('C', 4)  # 60
midi = note_name_to_midi('A', 4)  # 69

# MIDI to name, as a single string with the octave
name = midi_to_note_name(60)  # 'C4'
name = midi_to_note_name(69)  # 'A4'

# Note carries the two apart
from coremusic.music.theory import Note

note = Note.from_midi(60)
print(note.name, note.octave)  # C 4

See Also