Krumhansl-Schmuckler Algorithm in Python
Krumhansl-Schmuckler algorithm is a key finding algorithm based on pitch class distribution, that calculates the correlation between the pitch class distribution array, and each of the 12 major and 12 minor profile weights.
The full Python code could be found on my GH Gist.
Retrieve Pitch Class Distribution from MIDI
Let’s use the main theme music of Metro-Cross as an example, and it’s MIDI file could be downloaded here:
(Spoiler: It’s in A minor.)
First we use mido to read the MIDI file:
then retrieve the MIDI notes:
Each msg_dict
looks like this:
Remember that the 10th channel is reserved for percussion, so we need to exclude this channel before extracting all notes.
After retrieving all notes, we calculate the pitch class distribution vector:
Key-Profile Weights
There are 5 different key-profile weights available in music21: SimpleWeights, AardenEssen, BellmanBudge, KrumhanslSchmuckler, and TemperleyKostkaPayne, and their characteristics are explained in keycor manpage:
Krumhansl-Kessler:
Strong tendancy to identify the dominant key as the tonic.
Aarden-Essen:
Weak tendancy to identify the subdominant key as the tonic.
Bellman-Budge:
No particular tendancies for confusions with neighboring keys.
Temperley-Kostka-Payne:
Strong tendancy to identify the relative major as the tonic in minor keys. Well-balanced for major keys.
Simple:
Performs most consistently with large regions of music, becomes noiser with smaller regions of music.
Those key-profile weights could be retrieved using music21:
However, since they are arrays we could define them as global variables:
The original major weights are for C major. In order to get the weights for C# major, we simply shift one element to the right, so the array becomes like this:
Before we use Pearson correlation to calculate the result, let’s define an array that stores the order of different modes so the code part will not be confusing:
Finally, the index of the maximum value in corr_res
indicates the most likely mode of our MIDI file:
However, the correlation result using Krumhansl-Kessler weights shows that this music is in E major, but as its characteristic mentions, it tends to “identify the dominant key as the tonic”.
Let’s try another set of weights:
This time the result shows A minor, which is correct.
Limitations
Of course there are other modes besides major and minor:
This music is in C Dorian, but if we use this algorithm and Bellman-Budge weights to calculate its key, the result is G minor.
We could simply find that C Dorian and G natural minor share a same set of notes:
Even so, they are two modes that are totally different.
Besides, a song may change its modes several times. Without windowing the inputs, the result may not be precise also.
References
- https://web.mit.edu/music21/doc/moduleReference/moduleAnalysisDiscrete.html
- http://extras.humdrum.org/man/keycor/