Home

To speed up some analyses and avoid doing everything by hand, I quickly wrote some Python functions to perform transformational music theory. You can find the source code at the bottom of this post. I usually save it as a “neor.py” file to use it as a module. Note that there already seems to exist a Python library, “Sator”, for atonal music analysis available here (though it hasn’t been updated since 2012).

For now, the objective of neor is more modest: it simply allows one to get the transformations between the specified chords. For example, if we want to find the transformation from the T/I group which transforms F# major into A minor, we would write

import neor as nr
nr.TI_operation("Fs","Am")                        
'I_10'

If now we want to find the transformation from the PRL group between the same chords, we would write

import neor as nr
nr.PRL_operation("Fs","Am")
'(RL)^6R'

The transformations in the PRL group are given using the normal form (RL)^nR^p with n \in \{0,1,...,12\}, and p \in \{0,1\}.

You can also get the transformations in a given progression. For example, using the famous Beethoven progression, we have:

nr.TI_progression(["C","Am","F","Dm","Bb","Gm","Eb","Cm","Ab","Fm","Cs","Bbm"])
[('C', 'Am', 'I_4'),
('Am', 'F', 'I_9'),
('F', 'Dm', 'I_2'),
('Dm', 'Bb', 'I_7'),
('Bb', 'Gm', 'I_0'),
('Gm', 'Eb', 'I_5'),
('Eb', 'Cm', 'I_10'),
('Cm', 'Ab', 'I_3'),
('Ab', 'Fm', 'I_8'),
('Fm', 'Cs', 'I_1'),
('Cs', 'Bbm', 'I_6')]
nr.PRL_progression(["C","Am","F","Dm","Bb","Gm","Eb","Cm","Ab","Fm","Cs","Bbm"])
[('C', 'Am', 'R'),
('Am', 'F', '(RL)^11R'),
('F', 'Dm', 'R'),
('Dm', 'Bb', '(RL)^11R'),
('Bb', 'Gm', 'R'),
('Gm', 'Eb', '(RL)^11R'),
('Eb', 'Cm', 'R'),
('Cm', 'Ab', '(RL)^11R'),
('Ab', 'Fm', 'R'),
('Fm', 'Cs', '(RL)^11R'),
('Cs', 'Bbm', 'R')]

You will notice in the souce code that I have implemented another group of transformations, which is also an extension of \mathbb{Z}_{12} by \mathbb{Z}_2, but with a non-trivial 2-cocycle. I haven’t quite figured its real interest yet in music, but the calculations sure are easier.

The purpose of the remaining functions should be quite evident. I don’t yet know if I will extend this module into something more complex. Feel free to comment if you are interested !

 

And now for the source code:

chords = {'C':(0,1), 'Cs':(1,1), 'D':(2,1), 'Eb':(3,1), 'E':(4,1), 'F':(5,1), 'Fs':(6,1), 'G':(7,1), 'Ab':(8,1), 'A':(9,1), 'Bb':(10,1), 'B':(11,1),
          'Cm':(0,11), 'Csm':(1,11), 'Dm':(2,11), 'Ebm':(3,11), 'Em':(4,11), 'Fm':(5,11), 'Fsm':(6,11), 'Gm':(7,11), 'Abm':(8,11), 'Am':(9,11), 'Bbm':(10,11), 'Bm':(11,11)}

TI_operations = {(0,1):"T_0", (1,1):"T_1", (2,1):"T_2", (3,1):"T_3", (4,1):"T_4", (5,1):"T_5", (6,1):"T_6", (7,1):"T_7", (8,1):"T_8", (9,1):"T_9", (10,1):"T_10", (11,1):"T_11",
                 (0,11):"I_7", (1,11):"I_8", (2,11):"I_9", (3,11):"I_10", (4,11):"I_11", (5,11):"I_0", (6,11):"I_1", (7,11):"I_2", (8,11):"I_3", (9,11):"I_4", (10,11):"I_5", (11,11):"I_6"}

PRL_operations = {(0,1):"e", (1,1):"(RL)^7", (2,1):"(RL)^2", (3,1):"(RL)^9", (4,1):"(RL)^4", (5,1):"(RL)^11", (6,1):"(RL)^6", (7,1):"(RL)^1", (8,1):"(RL)^8", (9,1):"(RL)^3", (10,1):"(RL)^10", (11,1):"(RL)^5",
                 (0,11):"(RL)^3R", (1,11):"(RL)^8R", (2,11):"(RL)^1R", (3,11):"(RL)^6R", (4,11):"(RL)^11R", (5,11):"(RL)^4R", (6,11):"(RL)^9R", (7,11):"(RL)^2R", (8,11):"(RL)^7R", (9,11):"R", (10,11):"(RL)^5R", (11,11):"(RL)^10R"}

####

def LEFT_2CHI_operation(chord1,chord2):
    if (type(chord1)==str):
        n1,k1 = chords[chord1]
    else:
        n1,k1 = chord1
    if (type(chord2)==str):
        n2,k2 = chords[chord2]
    else:
        n2,k2 = chord2

    z = (k2*k1) % 12
    if (k1==11 and z==11):
        x = (n2-z*n1+6) % 12
    else:
        x = (n2-z*n1) % 12

    return (x,z)

def RIGHT_2CHI_operation(chord1,chord2):
    if (type(chord1)==str):
        n1,k1 = chords[chord1]
    else:
        n1,k1 = chord1
    if (type(chord2)==str):
        n2,k2 = chords[chord2]
    else:
        n2,k2 = chord2

    z = (k2*k1) % 12
    if (k1==11 and z==11):
        x = ((n2-n1+6)*k1) % 12
    else:
        x = ((n2-n1)*k1) % 12

    return (x,z)

def TI_operation(chord1,chord2,as_numeric=False):
    if (type(chord1)==str):
        n1,k1 = chords[chord1]
    else:
        n1,k1 = chord1
    if (type(chord2)==str):
        n2,k2 = chords[chord2]
    else:
        n2,k2 = chord2

    z = (k2*k1) % 12
    x = (n2-z*n1) % 12

    if as_numeric:
        return (x,z)
    else:
        return TI_operations[(x,z)]

def PRL_operation(chord1,chord2,as_numeric=False):
    if (type(chord1)==str):
        n1,k1 = chords[chord1]
    else:
        n1,k1 = chord1
    if (type(chord2)==str):
        n2,k2 = chords[chord2]
    else:
        n2,k2 = chord2
        
    z = (k2*k1) % 12
    x = ((n2-n1)*k1) % 12

    if as_numeric:
        return (x,z)
    else:
        return PRL_operations[(x,z)]

####

def TI_progression(list_chords,as_numeric=False):
    list_op = [(x,y,TI_operation(x,y,as_numeric)) for i,x in enumerate(list_chords) for j,y in enumerate(list_chords) if j==(i+1)]
    return list_op    

def PRL_progression(list_chords,as_numeric=False):
    list_op = [(x,y,PRL_operation(x,y,as_numeric)) for i,x in enumerate(list_chords) for j,y in enumerate(list_chords) if j==(i+1)]
    return list_op    

def LEFT_2CHI_progression(list_chords):
    list_op = [(x,y,LEFT_2CHI_operation(x,y)) for i,x in enumerate(list_chords) for j,y in enumerate(list_chords) if j==(i+1)]
    return list_op

def RIGHT_2CHI_progression(list_chords):
    list_op = [(x,y,RIGHT_2CHI_operation(x,y)) for i,x in enumerate(list_chords) for j,y in enumerate(list_chords) if j==(i+1)]
    return list_op    

####

def TI_interactions(list_chords,as_numeric=False,get_reversed=True):
    list_op = [(x,y,TI_operation(x,y,as_numeric)) for i,x in enumerate(list_chords) for j,y in enumerate(list_chords) if (i!=j or get_reversed)]
    return list_op    
    
def PRL_interactions(list_chords,as_numeric=False,get_reversed=True):
    list_op = [(x,y,PRL_operation(x,y,as_numeric)) for i,x in enumerate(list_chords) for j,y in enumerate(list_chords) if (i!=j or get_reversed)]
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s