import random
from music21 import corpus, stream, notedef 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 ngramsGiven 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 markovGiven 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:
            breakConcatenate 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 chaincreate 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.notesUse 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()