import random
from music21 import corpus, stream, note
def get_ngrams(seq, n):
Given a list seq
returns a list of ngrams
where each element is
an n-tuple.
Example:
>>> seq = [1, 2, 1, 3, 1, 2, 7]
>>> find_ngrams(seq, 2)
[(1, 2), (2, 1), (1, 3), (3, 1), (1, 2), (2, 7)]
>>> find_ngrams(seq, 3)
[(1, 2, 1), (2, 1, 3), (1, 3, 1), (3, 1, 2), (1, 2, 7)]
ngrams = []
Iterate over seq
in n-sized slices each converted to
an n-tuple and appended to ngrams
.
for i in range(len(seq) - n + 1):
new_tuple = tuple(seq[i:i+n])
ngrams.append(new_tuple)
return ngrams
Given a list seq
returns a Markov model of the given order
as a dictionary. The keys are order
-sized tuples. The value for each
key is a list of elements in seq
that immediately follow
occurances of the key in the seq
.
Example:
>>> seq = [1, 2, 1, 3, 1, 2, 7]
>>> markov_model(seq, 1)
{(2,): [1, 7], (3,): [1], (1,): [2, 3, 2]}
>>> markov_model(seq, 2)
{(1, 2): [1, 7], (1, 3): [1], (3, 1): [2], (2, 1): [3]}
def markov_model(seq, order):
markov = {}
First use find_ngrams
to slice seq
appropriately.
ngrams = get_ngrams(seq, order+1)
For each ngram
in ngrams
(where n = order
+ 1) the first
(n - 1) elements are used as the key and the last
element is appended to the list of possible
continuations.
for ngram in ngrams:
try:
markov[ngram[:-1]].append(ngram[-1])
except KeyError:
markov[ngram[:-1]] = [ngram[-1]]
return markov
Given a list seq
returns a Markov chain of
the given order
with a maximum length of the integer max_length
.
def markov_chain(markov, order, max_length=20):
The start_state
for the Markov chain is chosen randomly from a list
of keys()
in the markov
dictionary.
start_state = random.choice(list(markov.keys()))
Markov chain
is a list beginning with the start_state
.
chain = list(start_state)
while
loop to generate markov chain
.
while True:
Choose a random continuation, next_state
from the elements of
the value for the current start_state
.
next_state = random.choice(markov[start_state])
chain.append(next_state)
if len(chain) >= max_length:
break
Concatenate next_state
to start_state
and use the appropriate
slice of the resulting tuple as the new start_state
.
start_state = (start_state + (next_state,))[1:]
return chain
create empty dictionary for Markov model
markov = {}
set order
of the Markov model; experiment with changing the order
order = 2
sourceMaterial = corpus.parse('bach/bwv7.7')
for part in sourceMaterial.parts:
notes = part.flat.notes
Use tuples of note pitches and duration to create Markov model
note_tuples = [(n.nameWithOctave, n.quarterLength) for n in notes]
markov.update(markov_model(note_tuples, order))
chain = markov_chain(markov, order)
output = stream.Stream()
Convert chain of (pitch, duration) tuples to music21 note objects
and append to output
stream.
for ptch, dur in chain:
output.append(note.Note(ptch, quarterLength=dur))
output.show()