This module builds on the Transposition module by illustrating how to create transposition-related functions.
In the previous module, we covered how to identify how two melodies were related by transposition. In this module, we’ll create a few transposition-related functions.
Let’s start out with a function to transpose a given melody. We’ll use the violin melody from the Berg Violin Concerto, as in the previous module:
violin_melody = [55, 62, 69, 76, 76, 69, 62, 55]
If we want to transpose it, we simply add the value n from the operation Tn to each value in the list. For example, if we say n = 5 and use a list comprehension:
transposed_melody = [i + 5 for i in violin_melody] transposed_melody > [60, 67, 74, 81, 81, 74, 67, 60]
To turn this into a function, we want to be able to specify not only the melody, but also the value for n. So we’ll need not one but two arguments:
def transposer(melody, n_val): transposed_melody =  transposed_melody = [i + n_val for i in melody] return transposed_melody
Let’s use it to bring the violin melody down one octave:
transposer(violin_melody, -12) [43, 50, 57, 64, 64, 57, 50, 43]
We can assign the transposed melody to a new variable directly:
new_version = transposer(violin_melody, -12) new_version > [43, 50, 57, 64, 64, 57, 50, 43]
For transposing pitch classes, we add the mod (%) 12 function to our function:
def pc_transposer(melody, n_val): transposed_melody =  transposed_melody = [(i + n_val) % 12 for i in melody] return transposed_melody
Then our result will always use pitch classes (even if the input is in pitches):
pc_melody = [7, 2, 9, 4, 4, 9, 2, 7] pc_transposer(pc_melody, 6) > [1, 8, 3, 10, 10, 3, 8, 1]
This process is relatively straightforward, and doesn’t go far beyond what we’ve previously covered. A more intriguing challenge would be an analysis function: given two melodies, is there a transposition-based relationship between them?
Let’s recall where we left off in the previous module:
for x in range(len(melody1)): print((melody1[x] - melody2[x]) % 12)
Given two melodies of equal length, this code gives us the pitch-class intervals between each pair of corresponding notes. Now let’s make a list of the things our function will have to do:
- Find the pitch-class intervals between each pair of notes.
- Determine whether there’s a transposition-based relationship.
- If yes, output the n value.
- If no, print “Not related by transposition.”
We’ll collect all of the pitch-class intervals into a list, and then determine whether they are all the same by using a conditional if statement with Python’s set data type. Unlike lists, sets do not allow duplicates, so if the length of the set is 1, that means that all of the list values were identical.
Also note that we subtract pitches in melody1 from melody2 so that the output makes logical sense based on the format of the input. Finally, we’ll convert the for loop above into a list comprehension:
def transpose_analysis(melody1, melody2): list_of_n_vals =  list_of_n_vals = [(melody2[x] - melody1[x]) % 12 for x in range(len(melody1))] if len(set(list_of_n_vals)) == 1: print("Related by transposition at n =", list_of_n_vals) else: print("Not related by transposition")
It works as expected:
a = [60, 62, 63] b = [49, 51, 52] transpose_analysis(a,b) > Related by transposition at n = 1
Remember, we’re using pitch classes instead of pitches directly (it’s more common to refer to transposition levels modulo 12). That’s why we get a 1 here instead of a -11.
If we wanted to get pitch transpositions, we could simply eliminate the mod (%) 12 from the function. However, that would exclude examples like the harp and violin melodies from the Berg example, where the pitch class intervals are identical even though there are wider octave leaps in the harp melody:
harp_melody = [34, 53, 60, 67, 67, 60, 53, 34] violin_melody = [55, 62, 69, 76, 76, 69, 62, 55] transpose_analysis(harp_melody, violin_melody) > Related by transposition at n = 9
A final feature we can add to our function is to check whether the two melodies have the same number of notes before proceeding. Performing this check will prevent Python from generating an error, and instead send a message to the user:
def transpose_analysis(melody1, melody2): if len(melody1) == len(melody2): list_of_n_vals =  list_of_n_vals = [(melody2[x] - melody1[x]) % 12 for x in range(len(melody1))] if len(set(list_of_n_vals)) == 1: print("Related by transposition at n =", list_of_n_vals) else: print("Not related by transposition") else: print("Melodies must contain the same number of notes!")
Now we have a robust analysis function that will be useful in many contexts!
- What is the relationship between the n values when the melodies are reversed (i.e. “a,b” vs. “b,a”)?
- Can you adapt this function so that it can accept other types of input besides lists of MIDI numbers? For example, music21 streams?