Basic Music Theory in ~200 Lines of Python



Point out: your full code for this text will also be discovered here as a Github gist. There’s additionally an incredible dialogue on Hacker Knowledge with rather just a few comments that can also be of interest.


I’m a self-taught guitarist of a long time, and adore rather just a few self-taught musicians,
am woefully inept at (Western) tune notion.

So naturally, I sure to jot down some code.

This text explains the very fundamentals of Western tune notion in around 200 lines
of Python.

We are in a position to first absorb a study the notes in Western tune notion, use them to rep the
chromatic scale in a given key, and then to mix it with interval formulas to
rep overall scales and chords.

Lastly, we can absorb a study modes, which may perchance perchance be complete collections of scales derived
from overall scales, that can also be extinct to evoke extra refined moods and atmospheres
than the cushy-sad dichotomy that main and minor scales provide.

The Twelve Notes

The musical alphabet of Western tune includes the letters A by means of G, and they
signify varied pitches of notes.

We are in a position to signify the musical alphabet with the next listing in Python:

alphabet=['A', 'B', 'C', 'D', 'E', 'F', 'G']

Alternatively, these notes are no longer evenly spaced of their frequencies. To procure a extra
even spacing between pitches, now we absorb the next twelve notes:

notes_basic=[
    ['A'],
    ['A#', 'Bb'],
    ['B'],
    ['C'],
    ['C#', 'Db'],
    ['D'],
    ['D#', 'Eb'],
    ['E'],
    ['F'],
    ['F#', 'Gb'],
    ['G'],
    ['G#', 'Ab'],
]

There are four issues to describe here: first, every of the notes are a half of step
or semitone apart, second, the model here is represented is by an optionally available
trailing image (known as an unintended) to trace a half of-step raise (animated, ♯) or a half of-step
reducing (flat, ♭) of the immoral describe, and third, the above notes merely loop
aid and delivery over, however at a increased octave.

Lastly, you’ll stare that these types of notes
are represented by lists containing greater than one name: these are enharmonic equivalents,
a adore system of announcing that the identical describe can absorb varied “spellings”.
So for instance the describe half of a step above A is A♯, however it absolutely can additionally be idea
of as half of a step under B, and can thus be veritably known as B♭. For historic
reasons, there are no longer any sharps or residences between the notes B/C, and E/F.

Basically the most critical thing to be conscious on why we desire these equivalents is that, when we delivery to rep overall scales
(main, minor and the modes), consecutive notes must delivery with consecutive letters.
Having enharmonic equivalents with varied alphabets allows us to rep
these scales precisely.

In spite of the entirety, surely keys, the above enharmonic notes are no longer enough.
In divulge to fulfill the “varied-alphabets-for-consecutive-notes rule”, we discontinuance up having to make use of
double sharps and double residences that raise or decrease a describe by a paunchy step.
These scales most regularly absorb equivalents that discontinuance no longer require these double
accidentals
, however for completeness, we can include all that it’s worthwhile to also deem of enharmonic
equivalents by rewriting our notes as follows:

notes=[
    ['B#',  'C',  'Dbb'],
    ['B##', 'C#', 'Db'],
    ['C##', 'D',  'Ebb'],
    ['D#',  'Eb', 'Fbb'],
    ['D##', 'E',  'Fb'],
    ['E#',  'F',  'Gbb'],
    ['E##', 'F#', 'Gb'],
    ['F##', 'G',  'Abb'],
    ['G#',  'Ab'],
    ['G##', 'A',  'Bbb'],
    ['A#',  'Bb', 'Cbb'],
    ['A##', 'B',  'Cb'],
]

Chromatic Scales

The chromatic scale is the very best scale that it’s worthwhile to also deem of, and merely includes your full
(twelve) semitones between an octave of a given key (the main describe in a scale, additionally
known as the tonic).

We are in a position to generate a chromatic scale for any given key conveniently: (i) safe the index of
the describe in our notes listing, and then (ii) left-rotate the notes listing that many instances.

Finding the Index of a Given Point out

Let’s write a easy characteristic to search out a particular describe on this listing:

def find_note_index(scale, search_note): 
    ''' Given a scale, safe the index of a particular describe '''
    for index, describe in enumerate(scale):
        
        
        if form(describe)==listing:
            if search_note in describe:
                return index
        elif form(describe)==str:
            if search_note==describe:
                return index

The find_note_index() characteristic takes as parameters a series of notes (scale),
and a describe to search for (search_note), and returns the index by means of a easy linear
search. We take care of two instances contained in the loop: (i) where the offered scale consists
of individual notes (adore our alphabet listing above), or (ii) where it includes
a listing of enharmonic equivalents (adore our notes or notes_basic lists above).
Here is an instance of how the characteristic works for every:

>>> find_note_index(notes, 'A')    # notes is a listing of lists
9
>>> find_note_index(alphabet, 'A') # alphabet is a listing of notes
0

Left-Rotating a Scale

We are in a position to now write a characteristic to rotate a given scale by n steps:

def rotate(scale, n): 
    ''' Left-rotate a scale by n positions. '''
    return scale[n:] + scale[:n]

We prick the scale listing at space n and substitute the 2 halves. Here is
an instance of rotating our alphabet listing three locations (which brings the describe D
to the entrance):

>>> alphabet
['A', 'B', 'C', 'D', 'E', 'F', 'G']
>>> rotate(alphabet, 3)
['D', 'E', 'F', 'G', 'A', 'B', 'C']

Producing a Chromatic Scale in a Given Key

We are in a position to now in the raze write our chromatic() characteristic that generates a chromatic
scale for a given key by rotating the notes array:

def chromatic(key): 
    ''' Generate a chromatic scale in a given key. '''
    
    
    num_rotations=find_note_index(notes, key)
    return rotate(notes, num_rotations)

The chromatic() characteristic above finds the index of the offered key in the notes
listing (the usage of our find_note_index() characteristic), and then rotates it by that quantity
to carry it to the entrance (the usage of our rotate() characteristic). Here is an instance of
generating the D chromatic scale:

>>> import pprint
>>> pprint.pprint(chromatic('D'))
[['C##', 'D', 'Ebb'],
 ['D#', 'Eb', 'Fbb'],
 ['D##', 'E', 'Fb'],
 ['E#', 'F', 'Gbb'],
 ['E##', 'F#', 'Gb'],
 ['F##', 'G', 'Abb'],
 ['G#', 'Ab'],
 ['G##', 'A', 'Bbb'],
 ['A#', 'Bb', 'Cbb'],
 ['A##', 'B', 'Cb'],
 ['B#', 'C', 'Dbb'],
 ['B##', 'C#', 'Db']]

For chromatic scales, one on the full uses sharps
when ascending and residences when descending.
Alternatively, for now, we rush away enharmonic equivalents excellent as they are; we can deem about
the excellent technique to comprehend the appropriate describe to make use of later.

Intervals

Intervals specify the relative distance between notes.

The notes of a chromatic scale can therfore be given names basically based totally mostly on their relative distance from
the tonic or root describe. Under are the usual names for every describe, ordered
connected to the indexes in the notes listing:

intervals=[
    ['P1', 'd2'],  
    ['m2', 'A1'],  
    ['M2', 'd3'],  
    ['m3', 'A2'],  
    ['M3', 'd4'],  
    ['P4', 'A3'],  
    ['d5', 'A4'],  
    ['P5', 'd6'],  
    ['m6', 'A5'],  
    ['M6', 'd7'],  
    ['m7', 'A6'],  
    ['M7', 'd8'],  
    ['P8', 'A7'],  
]

Any other time, the identical describe can absorb varied interval names. As an instance,
the root describe will also be regarded as a excellent unison or an diminished
2nd.

Picking Out Notes from Enharmonic Equivalents

Given a chromatic scale in a given key, and an interval name in the above
array, we can pin point the true describe to make use of (and filter it out from a spot
of enharmonic equivalents). Let’s absorb a study the conventional system to discontinuance this.

As an instance, let’s absorb a study the excellent technique to search out the describe a lot like M3, or
the main third interval, from the D chromatic scale.

  1. From our intervals array, we can deem about that the index at which we safe M3 is 4. That is 'M3' in intervals[4]==Graceful.
  2. Now we absorb a study the identical index in our D chromatic scale (modulo its length). We safe that chromatic('D')[4] is the listing of notes ['E##', 'F#', 'Gb'].
  3. The number in M3 (i.e., the 3) signifies the alphabet now we must make use of, with 1 indicating the root alphabet. So for instance, for the main of D, 1=D, 2=E, 3=F, 4=G, 5=A, 6=B, 7=C, 8=D… etc. So now we must search for a describe in our listing of notes (['E##', 'F#', 'Gb']) containing the alphabet F. That’s the describe F#.
  4. Conclusion: the main third (M3) relative to D is F#.

Programmatically Labeling Intervals for a Given Key

We are in a position to jot down a fairly easy characteristic to apply this good judgment for us programmatically, and affords us a dict mapping all interval names to the honest describe names in a given key:

def make_intervals_standard(key): 
    
    labels={}
    
    
    chromatic_scale=chromatic(key)
    
    
    alphabet_key=rotate(alphabet, find_note_index(alphabet, key[0]))
    
    
    for index, interval_list in enumerate(intervals):
    
        
        notes_to_search=chromatic_scale[index % len(chromatic_scale)]
        
        for interval_name in interval_list:
            
            stage=int(interval_name[1]) - 1 
            
            
            alphabet_to_search=alphabet_key[degree % len(alphabet_key)]
            
            are attempting:
                describe=[x for x in notes_to_search if x[0]==alphabet_to_search][0]
            except:
                describe=notes_to_search[0]
            
            labels[interval_name]=describe

    return labels

And here is the dict we procure aid for the main of C:

>>> import pprint
>>> pprint.pprint(make_intervals_standard('C'), sort_dicts=Untrue)
{'P1': 'C',
 'd2': 'Dbb',
 'm2': 'Db',
 'A1': 'C#',
 'M2': 'D',
 'd3': 'Ebb',
 'm3': 'Eb',
 'A2': 'D#',
 'M3': 'E',
 'd4': 'Fb',
 'P4': 'F',
 'A3': 'E#',
 'd5': 'Gb',
 'A4': 'F#',
 'P5': 'G',
 'd6': 'Abb',
 'm6': 'Ab',
 'A5': 'G#',
 'M6': 'A',
 'd7': 'Bbb',
 'm7': 'Bb',
 'A6': 'A#',
 'M7': 'B',
 'd8': 'Cb',
 'P8': 'C',
 'A7': 'B#'}

Interval Formulas

We are in a position to now specify formulas, or groups of notes, the usage of interval names, and
be in a space to scheme them to any key that we desire:

def make_formula(system, labeled): 
    '''
    Given a comma-separated interval system, and a spot of labeled
    notes in a key, return the notes of the system.
    '''
    return [labeled[x] for x in system.split(',')]

Essential Scale Formulation

As an instance, the system for a important scale is:

system='P1,M2,M3,P4,P5,M6,M7,P8'

We are in a position to use this to generate the main scale without ache for quite lots of keys as shown under:

>>> for key in alphabet:
>>>     print(key, make_formula(system, make_intervals_standard(key)))
C ['C', 'D', 'E', 'F', 'G', 'A', 'B', 'C']
D ['D', 'E', 'F#', 'G', 'A', 'B', 'C#', 'D']
E ['E', 'F#', 'G#', 'A', 'B', 'C#', 'D#', 'E']
F ['F', 'G', 'A', 'Bb', 'C', 'D', 'E', 'F']
G ['G', 'A', 'B', 'C', 'D', 'E', 'F#', 'G']
A ['A', 'B', 'C#', 'D', 'E', 'F#', 'G#', 'A']
B ['B', 'C#', 'D#', 'E', 'F#', 'G#', 'A#', 'B']

Prettifying Scales

Let’s additionally rapidly write a characteristic to print scales in a nicer system:

def dump(scale, separator=' '): 
    '''
    Graceful-print the notes of a scale. Replaces b and # characters
    for unicode flat and animated symbols.
    '''
    return separator.join(['{:.format(x) for x in scale]) 
                    .substitute('b', 'u266d') 
                    .substitute('#', 'u266f')

Here’s a nicer output the usage of the appropriate unicode characters:

>>> for key in alphabet:
>>>     scale=make_formula(system, make_intervals_standard(key))
>>>     print('{}: {}'.layout(key, dump(scale)))
C: C   D   E   F   G   A   B   C
D: D   E   F♯  G   A   B   C♯  D
E: E   F♯  G♯  A   B   C♯  D♯  E
F: F   G   A   B♭  C   D   E   F
G: G   A   B   C   D   E   F♯  G
A: A   B   C♯  D   E   F♯  G♯  A
B: B   C♯  D♯  E   F♯  G♯  A♯  B

Utilizing Essential-Scale Intervals for Formulas

Read More

Similar Products:

Recent Content