Created
September 22, 2012 15:37
-
-
Save jsawruk/3766546 to your computer and use it in GitHub Desktop.
Compute the pitch class normal form of a pitch class set
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def rangeDiff(pcs): | |
# Return the difference between the first and last items of a list of pitch class sets | |
diff = (pcs[len(pcs) - 1] - pcs[0]) | |
if diff < 0: | |
diff += 12 | |
return diff | |
def cycleLeft(pcs): | |
# Return a single cyclic left permutation of a pitch class set | |
# Ex: [a,b,c] -> [b, c, a] | |
cycle = pcs[1:] | |
cycle.append(pcs[0]) | |
return cycle | |
def cycleDiff(pcs): | |
# Given a pitch class set as a list | |
# Return a duple (2-tuple) of the form ([pcs], diff) | |
# Example: | |
# Input: [0,1,2,3] | |
# Output: ([0,1,2,3], 3) | |
return (pcs, rangeDiff(pcs)) | |
def removeLast(list): | |
# Given a list, remove the last element | |
# Unlike pop(), which returns the value of the | |
# last element this function returns the | |
# modified list instead | |
return list[0:len(list) - 1] | |
def selectSmallest(pcsDupleList): | |
# From a list of PCS duples (2-tuples), | |
# select the N sets with the smallest | |
# outside intervals | |
# If N = 1, return | |
# Else, recurse until N = 1 | |
# Use a lambda to sort by the difference | |
# the smallest difference will be first | |
pcsDupleList.sort(key = lambda x: x[1]) | |
# Get the value of the smallest interval | |
smallestDiff = pcsDupleList[0][1] | |
# filter for the N with smallest outer intervals | |
pcsDupleList = filter(lambda x: x[1] == smallestDiff, pcsDupleList) | |
if len(pcsDupleList) == 1: | |
return pcsDupleList | |
else : | |
subsets = [cycleDiff(removeLast(x[0])) for x in pcsDupleList] | |
return selectSmallest(subsets) | |
def normalForm(pcs): | |
# Return the normal form of the input pitch class set | |
cycle = pcs | |
cycleCount = 0 | |
cycles = [] | |
while cycleCount < len(cycle) : | |
cycle = cycleLeft(cycle) | |
cycleCount += 1 | |
# Insert into a quasi-associative array | |
# cannot use an actual associative array | |
# because lists are not hashable | |
# Format [([pcs], diff), ... ] | |
cycles.append(cycleDiff(cycle)) | |
if cycle == pcs: | |
break | |
smallestSets = selectSmallest(cycles) | |
if len(smallestSets[0][0]) < len(cycles[0][0]) : | |
lengthDiff = len(cycles[0][0]) - len(smallestSets[0][0]) | |
# create a list that only has subsets from the cycle list | |
# that start with the same number element | |
filterList = [x[0][:len(x[0]) - lengthDiff] for x in cycles] | |
# look for its position in the list | |
index = filterList.index(smallestSets[0][0]) | |
# because the lists are sorted, the indexes match | |
smallestSets[0] = cycles[index] | |
return smallestSets[0][0] | |
def inversion(pcs): | |
# Determine the non-transposed inversion of the | |
# pitch class set by subtracting every element from 12 | |
return [12 - x for x in pcs] | |
def zeroNormal(pcs): | |
# Convert the normal form so that the first element is 0 | |
# By subtracting the first element from every element | |
# Add 12 if the result is less than 0 | |
normal = normalForm(pcs) | |
zeroNormal = [x - normal[0] % 12 for x in normal] | |
# Note the unusual order for the ternary operator | |
return [x if x >= 0 else x + 12 for x in zeroNormal] | |
def primeForm(pcs): | |
# To determine the prime form, compute the zeroNormal of the set | |
# as well as the zeroNormal of its inversion | |
# Select whichever one is more packed to the left (smaller intervals first) | |
zeroNorm = zeroNormal(pcs) | |
zeroInv = sorted(zeroNormal(inversion(pcs))) | |
primeForm = zeroNorm | |
# Luckily, we can use Python's built-in list comparison | |
# to determine which set is more packed to the left | |
if zeroInv < zeroNorm : | |
primeForm = zeroInv | |
return primeForm |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment