Algorithmic Approaches to Music Theory: Conventional and Graph-Based Methods
Background
As an experienced engineer with a past life in professional music, I have always been fascinated by the convergence of music and technology. Both domains share commonalities, such as mathematical underpinnings and logical structures.
In this article, my objective is to explore and compare conventional and graph-based techniques for developing algorithms that encapsulate music theory concepts. By investigating these methodologies, it is interesting to understand how the basis for modern music theory works.
Approach
Step 1: Establishing the algorithm for a major scale
A major scale in music can be defined by its pattern of whole and half steps. In Western music, the major scale consists of seven notes, following the pattern: whole, whole, half, whole, whole, whole, half.
Here’s an algorithmic representation of generating a major scale for a given root note (assuming a 12-tone equal temperament tuning system):
- Define the pattern of whole and half steps for a major scale: [2, 2, 1, 2, 2, 2, 1].
- Define the list of note names in chromatic order: [“C”, “C#”, “D”, “D#”, “E”, “F”, “F#”, “G”, “G#”, “A”, “A#”, “B”].
- Determine the index of the root note in the chromatic list (e.g., “C” would be 0, “C#” would be 1, “D” would be 2, and so on).
- Initialize an empty list to store the major scale notes.
- Iterate through the pattern of whole and half steps, adding the current step value to the index of the root note, and modulo 12 to wrap around the chromatic list if necessary.
- For each resulting index, append the corresponding note from the chromatic list to the major scale notes list.
- Return the list of major scale notes.
Step 2: Comparing the conventional and graph approaches to representing the major scale algorithm
The conventional approach for this would be to implement the following:
Testing this out by printing the C and D major scales provides the following outputs:
Now, we can build a graph version of the major scale algorithm with the following implementation done in networkx (Python):
When we output the C and D major scales, we receive the same result as with the conventional method.
The elements of the graph can also be visualized in various ways that can be intuitive to a broader audience of users. It is also interesting to note in the visual below the recursive nature of the root notes (tonic).
Here is one mechanism for implementing the visualization so:
While there is more overhead in building the graph initially, the logic can be incrementally built upon in an intuitive way. We can observe this by adding the logic for a minor scale.
Let’s compare the two approaches again.
Step 3: Adding the minor scale
The natural minor scale has the following pattern: whole, half, whole, whole, half, whole, whole.
In the conventional approach, we create a separate function for the minor scale. This is in no way connected to the original snippet for the major scale.
We can test this with the below code:
The composable nature of the graph allows users to see a major scale in relation to a minor scale while also having a single place for the logic. We see this when we layer in the minor scale algorithm on top of the existing foundation of the major scale code.
Let’s try outputting C and D major and minor scales. We can query a single graph for both.
Similar to the major scale graph, this combined graph with both major and minor scales can also be visualized. This graph can be customized and made much more interactive if written in D3, but for this exercise, we will leave it in matplotlib.
Conclusion
In this article, we explored music theory algorithms through two approaches — a conventional python implementation and a graph-based approach. From this exercise, we can see how to construct the algorithms for major and minor scales and start to visualize how they relate to each other.
Check out the repo here for a notebook with all of the code in one place.
What other music theory algorithms should we include next?