= Scale("major")
major major
Scale: Major. Intervals: ['1', '2', '3', '4', '5', '6', '7']
The Scale
object is a collection of intervals and agnostic to any key. From the basic scale, notes can be derived by providing a root note.
Scale (name:str)
Initialize self. See help(type(self)) for accurate signature.
The Scale
representation gives you the name and intervals of the scale.
The Scale
object stores how many flats, sharps and naturals are in the key.
For example, a major scale contains only naturals. No flats or sharps.
A dorian scale has 2 flats (the 3rd and 7th degrees).
A lydian scale has 1 sharp (the 4th degree).
Listify the scale to get the intervals or call .intervals
.
Semitones for the invervals in a scale can be retrieved in an absolute (intervals between notes) or relative (intervals from root).
Scales can be compared to each other.
Almost all scales are available to initialize by name, but custom scales can be created with from_intervals
. Consult Scale.available_scales
to get the built-in scales.
For example, we can create a custom persian
scale.
Scale: Persian. Intervals: ['1', 'b2', '3', '4', 'b5', 'b6', '7']
If no name is given, Scale
will try to infer the name.
From a Scale
object, we can get the notes given a root note.
Scale.get_notes (root, oct=4)
Get the notes of a scale from a root note.
We can get all diatonic chords in the scale with get_diatonic_chords
.
Scale.get_diatonic_chords (root, min_notes=3)
For example, let’s look at all the diatonic E chords in the C major scale we can identify.
[Chord: 'E minor seventh, second inversion'. Notes: ['D4', 'E4', 'G4'],
Chord: 'E minor seventh, second inversion'. Notes: ['D4', 'E4', 'B4'],
Chord: 'E minor triad'. Notes: ['E4', 'G4', 'B4'],
Chord: 'E suspended fourth triad'. Notes: ['E4', 'A4', 'B4'],
Chord: 'Esus4|CM6'. Notes: ['C4', 'E4', 'A4', 'B4'],
Chord: 'E suspended seventh, third inversion'. Notes: ['D4', 'E4', 'A4', 'B4']]
You can easily get relative and absolute interval names. Both shorthand and full names are available.
Scale.abs_interval_names (short=False)
Scale.rel_interval_names (short=False)
assert major.rel_interval_names() == ['major second', 'major third', 'perfect fourth', 'perfect fifth', 'major sixth', 'major seventh']
assert major.rel_interval_names(short=True) == ['1', '2', '3', '4', '5', '6', '7']
assert major.abs_interval_names() == ['major second', 'major second', 'minor second', 'major second', 'major second', 'major second', 'minor second']
assert major.abs_interval_names(short=True) == ['2', '2', 'b2', '2', '2', '2', 'b2']
major.rel_interval_names()
['major second',
'major third',
'perfect fourth',
'perfect fifth',
'major sixth',
'major seventh']
Many scales have alternative names. We can derive them using get_scale_names
.
Scale.get_scale_names ()
A mode is a scale derived from a note on the scale. We can build a new Scale
on every step of a Scale
by cycling through the modes.
For this to work, we first need to shift the semitones of our scale and get a new set of relative semitones.
assert major.abs_semitones == [2, 2, 1, 2, 2, 2, 1]
assert major._shift_abs_semitones(1) == [2, 1, 2, 2, 2, 1, 2]
# Semitones for dorian scale (shift semitones 1 to the left)
major._shift_abs_semitones(1)
[2, 1, 2, 2, 2, 1, 2]
When we accumulate the absolute semitones of a scale, we get the relative semitones.
assert major.rel_semitones == [0, 2, 4, 5, 7, 9, 11]
assert major._shift_rel_semitones(1) == [0, 2, 3, 5, 7, 9, 10]
# Relative semitones for dorian scale
major._shift_rel_semitones(1)
[0, 2, 3, 5, 7, 9, 10]
We can now reconstruct the intervals from the relative semitones.
semi_to_intvals (semitones)
Convert relative semitone values to interval names.
dorian_intervals = semi_to_intvals(major._shift_rel_semitones(1))
assert dorian_intervals == ['1', '2', 'b3', '4', '5', '6', 'b7']
dorian_intervals
['1', '2', 'b3', '4', '5', '6', 'b7']
Now let’s reconstruct a new Scale
object from these intervals.
Scale: Dorian. Intervals: ['1', '2', 'b3', '4', '5', '6', 'b7']
Now we can create a method to retrieve all modes for any given Scale
.
Scale.get_modes ()
Scale.shift_intvals (n:int)
Shift the intervals of a scale by n steps.
The most well known modes are the modes of the major scale, which are: 1. Ionian
(Major) 2. Dorian
3. Phrygian
4. Lydian
5. Mixolydian
6. Aeolian
(Natural Minor) 7. Locrian
maj_modes = major.get_modes()
assert [m.name for m in maj_modes] == ["ionian", "dorian", "phrygian", "lydian", "mixolydian", "minor", "locrian"]
maj_modes
[Scale: Ionian. Intervals: ['1', '2', '3', '4', '5', '6', '7'],
Scale: Dorian. Intervals: ['1', '2', 'b3', '4', '5', '6', 'b7'],
Scale: Phrygian. Intervals: ['1', 'b2', 'b3', '4', '5', 'b6', 'b7'],
Scale: Lydian. Intervals: ['1', '2', '3', '#4', '5', '6', '7'],
Scale: Mixolydian. Intervals: ['1', '2', '3', '4', '5', '6', 'b7'],
Scale: Minor. Intervals: ['1', '2', 'b3', '4', '5', 'b6', 'b7'],
Scale: Locrian. Intervals: ['1', 'b2', 'b3', '4', 'b5', 'b6', 'b7']]
If we take the modes of one of these modes we get the same modes in a different order. For example, the phrygian modes are: 1. Phrygian
2. Lydian
3. Mixolydian
4. Aeolian
(Natural Minor) 5. Locrian
6. Ionian
(Major)
phrygian = maj_modes[2]
phrygian_modes = phrygian.get_modes()
assert [m.name for m in phrygian_modes] == ["phrygian", "lydian", "mixolydian", "minor", "locrian", "ionian", "dorian"]
phrygian_modes
[Scale: Phrygian. Intervals: ['1', 'b2', 'b3', '4', '5', 'b6', 'b7'],
Scale: Lydian. Intervals: ['1', '2', '3', '#4', '5', '6', '7'],
Scale: Mixolydian. Intervals: ['1', '2', '3', '4', '5', '6', 'b7'],
Scale: Minor. Intervals: ['1', '2', 'b3', '4', '5', 'b6', 'b7'],
Scale: Locrian. Intervals: ['1', 'b2', 'b3', '4', 'b5', 'b6', 'b7'],
Scale: Ionian. Intervals: ['1', '2', '3', '4', '5', '6', '7'],
Scale: Dorian. Intervals: ['1', '2', 'b3', '4', '5', '6', 'b7']]
Let’s take a more complicated example, like the Double Harmonic Major
scale. The modes of this scale are: 1. Double Harmonic Major 2. Lydian with #2
and #6
3. Ultraphrygian 4. Hungarian Minor 5. Oriental 6. Ionian with #2
and #5
(i.e. aug#2
) 7. Locrian with bb3
and bb7
Scale: Double Harmonic Major. Intervals: ['1', 'b2', '3', '4', '5', 'b6', '7']
dhm_modes = dhm.get_modes()
assert [m.name for m in dhm_modes] == ["double harmonic major", "lydian #2#6", "ultraphrygian", "hungarian minor", "oriental", "ionian aug#2", "locrian bb3bb7"]
dhm_modes
[Scale: Double Harmonic Major. Intervals: ['1', 'b2', '3', '4', '5', 'b6', '7'],
Scale: Lydian #2#6. Intervals: ['1', '#2', '3', '#4', '5', '#6', '7'],
Scale: Ultraphrygian. Intervals: ['1', 'b2', 'b3', 'b4', '5', 'b6', '6'],
Scale: Hungarian Minor. Intervals: ['1', '2', 'b3', '#4', '5', 'b6', '7'],
Scale: Oriental. Intervals: ['1', 'b2', '3', '4', 'b5', '6', 'b7'],
Scale: Ionian Aug#2. Intervals: ['1', '#2', '3', '4', '#5', '6', '7'],
Scale: Locrian Bb3Bb7. Intervals: ['1', 'b2', '2', '4', 'b5', 'b6', '6']]
Like with Note
and Chord
objects, Scale
objects can be played in any given key with the play
method.
Scale.play (root, oct=4, length=0.3)
Scale.get_audio_array (root, oct=4, length=0.3)
length
and oct
can be customized for the play
method.
We can derive all triads in the Scale
.
Scale.get_triads (root)
Get all triads in scale starting from root note.
[Chord: 'C major triad'. Notes: ['C4', 'E4', 'G4'],
Chord: 'D minor triad'. Notes: ['D4', 'F4', 'A4'],
Chord: 'E minor triad'. Notes: ['E4', 'G4', 'B4'],
Chord: 'F major triad'. Notes: ['F4', 'A4', 'C5'],
Chord: 'G major triad'. Notes: ['G4', 'B4', 'D5'],
Chord: 'A minor triad'. Notes: ['A4', 'C5', 'E5'],
Chord: 'B diminished triad'. Notes: ['B4', 'D5', 'F5']]
[Chord: 'D minor triad'. Notes: ['D4', 'F4', 'A4'],
Chord: 'No chord found.'. Notes: ['D#4', 'G4', 'A#4'],
Chord: 'F major triad'. Notes: ['F4', 'A4', 'C4'],
Chord: 'No chord found.'. Notes: ['G4', 'A#4', 'D5'],
Chord: 'No chord found.'. Notes: ['A4', 'C4', 'D#5'],
Chord: 'No chord found.'. Notes: ['A#4', 'D5', 'F5'],
Chord: 'No chord found.'. Notes: ['C5', 'D#6', 'G6']]
All triads in a given Scale
can be played.
Scale.play_triads (root)
Play all triads in scale starting from root note.
Now that we can get all the triads, we can also get all the secondary dominants within a given Scale
. These are V7
chords based on each Chord
in the Scale
.
Scale.secondary_dominants (root:str)
We expect that the secondary dominant roots are all a fifth above the underlying root and that all chords are dominant seventh chords.
normal_roots = [n.root for n in major.get_triads("C")]
sec_dom_roots = [n.root for n in major.secondary_dominants("C")]
assert all([n == c.P5() for n, c in zip(sec_dom_roots, normal_roots)])
major.secondary_dominants("C")
[Chord: 'G dominant seventh'. Notes: ['G4', 'B4', 'D5', 'F5'],
Chord: 'A dominant seventh'. Notes: ['A4', 'C#5', 'E5', 'G5'],
Chord: 'B dominant seventh'. Notes: ['B4', 'D#5', 'F#5', 'A5'],
Chord: 'No chord found.'. Notes: ['C5', 'E5', 'G5', 'A#5'],
Chord: 'D dominant seventh'. Notes: ['D5', 'F#5', 'A5', 'C6'],
Chord: 'E dominant seventh'. Notes: ['E5', 'G#5', 'B5', 'D6'],
Chord: 'F# dominant seventh'. Notes: ['F#5', 'A#5', 'C#6', 'E6']]
We can also get all seventh chords in the Scale
.
Scale.get_sevenths (root)
Get all seventh chords in scale starting from root note.
[Chord: 'C major seventh'. Notes: ['C4', 'E4', 'G4', 'B4'],
Chord: 'D minor seventh'. Notes: ['D4', 'F4', 'A4', 'C5'],
Chord: 'E minor seventh'. Notes: ['E4', 'G4', 'B4', 'D5'],
Chord: 'F major seventh'. Notes: ['F4', 'A4', 'C5', 'E5'],
Chord: 'G dominant seventh'. Notes: ['G4', 'B4', 'D5', 'F5'],
Chord: 'A minor seventh'. Notes: ['A4', 'C5', 'E5', 'G5'],
Chord: 'B half diminished seventh'. Notes: ['B4', 'D5', 'F5', 'A5']]
Scale.play_sevenths (root)
Play all seventh chords in scale starting from root note.
[Chord: 'D major seventh'. Notes: ['D4', 'F#4', 'A4', 'C#4'],
Chord: 'E dominant seventh'. Notes: ['E4', 'G#4', 'B4', 'D5'],
Chord: 'F# minor seventh'. Notes: ['F#4', 'A4', 'C#4', 'E5'],
Chord: 'G# half diminished seventh'. Notes: ['G#4', 'B4', 'D5', 'F#5'],
Chord: 'A major seventh'. Notes: ['A4', 'C#4', 'E5', 'G#5'],
Chord: 'B minor seventh'. Notes: ['B4', 'D5', 'F#5', 'A5'],
Chord: 'C# minor seventh'. Notes: ['C#5', 'E6', 'G#6', 'B6']]
We can display all relevant information from the scale in a Pandas DataFrame table.
Scale.to_frame (root=None)
The to_frame
method can be called without a root note, but is most informative when given a root note. In that case it shows additional information such as the triads and seventh chords.
Degree | Relative Interval | Mode | Relative Semitones | Absolute Semitones | Notes | Triad | Seventh Chord | |
---|---|---|---|---|---|---|---|---|
0 | 1 | unison | ionian | 0 | 2 | D | D major triad | D major seventh |
1 | 2 | major second | dorian | 2 | 2 | E | E minor triad | E minor seventh |
2 | 3 | major third | phrygian | 4 | 1 | F# | F# minor triad | F# minor seventh |
3 | 4 | perfect fourth | lydian | 5 | 2 | G | G major triad | G major seventh |
4 | 5 | perfect fifth | mixolydian | 7 | 2 | A | A major triad | A dominant seventh |
5 | 6 | major sixth | minor | 9 | 2 | B | B minor triad | B minor seventh |
6 | 7 | major seventh | locrian | 11 | 1 | C# | C# diminished triad | C# half diminished seventh |
TODO: Comparison method for scales