Building Chord Progressions using Stacks, Part 1
Photo by Kelly Sikkema on Unsplash
In the last post, I wrote about stack data structures and their musical applications. Now, I will show you how stacks can be used to build chord progressions. We will use the music21 Python library to understand and play the music. We’ll use the same musical stack structure from before:
from music21 import *
class MusicStack:
def __init__(self):
self.items = stream.Stream()
def push(self, item):
self.items.append(item)
def pop(self):
if self.is_empty():
raise IndexError("Sorry, your stack is empty")
else:
return self.items.pop()
def peek(self):
if self.is_empty():
raise IndexError("Sorry, your stack is empty")
else:
return self.items[-1]
def is_empty(self):
return self.items == []
Thanks for reading Learning Data Structures and Algorithms with Music! Subscribe for free to receive new posts and support my work.
What’s different about this stack is that we are using the stream object. The stream object is a fundamental container for music21 objects. A container is like a Python list. Streams are similar to Python lists in that they hold single elements in order. They’re different in that they can only hold music21 objects.
We can build a stack simply of musical notes with the Note object. Regarding this notation, C, G, and E, represent the note and the numbers 4 , and 5, represent the octave. Lastly, the minus sign, ‘-’, represents the flat. Sharps are represented as “#.”
Here is some code:
stack = MusicStack()
stack.push(note.Note("C4"))
stack.push(note.Note("G4"))
stack.push(note.Note("E-5"))
Let’s see how the peek() function works.
stack.peek() # cool!
Output:
<music21.note.Note E->
We use the show from the stream module in music21 to see the musical notation and to hear the notes using MIDI. Musescore 3 or another musical notation software is needed to see the notes. In order to access the stream module, we have to add items.
stack.items.show()
stack.items.show('midi')
Now let’s see how the pop() function works out.
stack.pop()
Output:
<music21.note.Note E->
stack.items.show()
stack.items.show('midi')
Sweet! So now, let’s get a little more complicated. Instead of note progressions, let’s work with chord progressions. We will first use the C major, F major, to G major progression. This is known as the I, IV, V chord progression and is very popular in music. Instead of the Note object, we will use the Chord object.
stack_chords = MusicStack()
stack_chords.push(chord.Chord(['C4', 'E4', 'G4'])) # C major
stack_chords.push(chord.Chord(['F4', 'A4', 'C5'])) # F major
stack_chords.push(chord.Chord(['G4', 'B4', 'D4'])) # G major
stack_chords.items.show()
stack_chords.items.show('midi')
Let’s get a little fun now. I found a few chord progressions of “My Shot” by Lin-Manuel Miranda. I love the sound of the major 7th, which is always one half step below the root of the chord.
stack_myshot = MusicStack()
chord_data = [
(['G2', 'B-3', 'D3', 'G4'], 'whole'), # G minor
(['A2', 'C4', 'F4', 'A4'], 'quarter'), # F major over A
(['B-2', 'B-3', 'D4', 'F4'], 'whole'), # B flat major
(['B2', 'D4', 'F4', 'G4'], 'quarter'), # G major 7 over B
(['C3', 'C4', 'E-4', 'G4'], 'whole'), # C minor
(['E-3', 'B-3', 'C4', 'G4'], 'half'), # E flat major 6th
(['F#2', 'C4', 'D4', 'A4'], 'half') # D major 7 over F sharp major. I love how it goes to this chord!
]
for notes, duration in chord_data:
stack_myshot.push(chord.Chord(notes, type=duration))
stack_myshot.items.show()
stack_myshot.items.show('midi')
Now, what if we use the pop() function from the stack to reverse the chords? Let’s see what this sounds like.
reverse_myshot = MusicStack()
num_items = len(stack_myshot.items)
for _ in range(num_items):
reverse_myshot.push(stack_myshot.pop())
reverse_myshot.items.show()
reverse_myshot.items.show('midi') # interesting!
Thanks for reading Learning Data Structures and Algorithms with Music! Subscribe for free to receive new posts and support my work.




