import argparse import random from collections import defaultdict from music21 import *
Generates the trigrams of an array of elements. For example, if
data = [a, b, c, d] then the
output will be
for i in range(len(data) - 2): yield data[i:i + 3]
Helper function that, when given a dictionary of keys and weights, chooses a random key based on its weight.
n = random.uniform(0, sum(states.values())) for key, val in states.items(): if n < val: return key n -= val
Something went wrong, don't make a choice.
This is the entry point of our music generator.
if __name__ == '__main__':
Create the Markov Chain. I use
defaultdict as a simple (error-free) counter. This just
means that every key in the dictionary has a default value. If a key isn't in the dictionary
then it will automatically have a value generated. The
lambda: defaultdict(int) means the
default value will be another dictionary which sets the default values to zero.
So if you wrote
markov['test'] you would get a
defaultdict. Going one step further,
you could write
markov['a']['b'] which would result in 0. This let's you write a counter
in the form
markov['a']['b'] += 1 without getting a
markov = defaultdict(lambda: defaultdict(int))
There are a few ways to initialize the Markov Chain. Here, I choose to treat all the parts in the source material equally.
sourceMaterial = corpus.parse('bach/bwv7.7') for part in sourceMaterial.parts: notes = part.flat.notes
Generate trigrams for each part.
for (w1, w2, w3) in get_trigrams(notes):
Update the tally for this combination.
markov[(w1, w2)][w3] += 1
Create a music21 stream to hold the result
stream = stream.Stream()
You can use the following code for a preview of the Markov Chain probabilites:
for state in markov: print('%r => %r' % (state, markov[state]))
Use random to pick a random initial state (or set it yourself).
You can preview it by writing
print("Initial State: %s" % repr(start_state))
start_state = random.choice(list(markov.keys()))
Now start 'walking' along the Markov Chain to generate stuff. Unfortunately, the hard part is knowing when to stop. Here I say I want a song no longer than 30 notes, and if there's a dead-end, stop immediately. Naturally, there are much better ways to do this.
s1, s2 = start_state max_length = 30 result = [s1, s2] for i in range(max_length): next_state = weighted_choice(markov[(s1, s2)]) if next_state is None: break stream.append(next_state) s1 = s2 s2 = next_state stream.show('midi')