In [475]:
%reload_ext abjadext.ipython
import abjad
import abjadext.rmakers
The rhythmmaker classes we saw yesterday are a good example of top-down music generation. Today, we'll learn to write new classes that generate music in a similar top-down fashion.
To begin with, we're going to model the process of generation in a completely procedural way; later, we'll get into basics of object-oriented programming and turn this procedural code into a reusable class.
First, we'll just use an instance of the Talea()
class to generate some leaves; this is basically what an instance of the TaleaRhythmmaker()
class is doing. Then we'll paint a cycle of pitches (a color) onto the generated leaves.
In [476]:
time_signature_pairs = [(4, 4), (3, 4), (7, 16), (6, 8)]
total_duration = sum(abjad.Duration(pair) for pair in time_signature_pairs)
counts = [1, 2, -3, 4]
denominator = 16
talea = abjadext.rmakers.Talea(counts=counts, denominator=denominator)
talea_index = 0
The talea generates a cycle of fractions by pairing each count with its denominator. Negative numerators indicate rests.
In [477]:
talea[:10]
Out[477]:
In [478]:
all_leaves = [] # create an empty list for generated leaves
current_duration = abjad.Duration(0) # keep track of the total duration as we generate each new leaf
while current_duration < total_duration: # generate leaves until they add up to the total duration
leaf_duration = talea[talea_index] # get a fraction from the talea
if leaf_duration > 0:
pitch = abjad.NamedPitch("c'") # assign the leaf a pitch of middle C
else:
pitch = None # if the leaf is a rest, don't assign a pitch
leaf_duration = abs(leaf_duration) # cancel the minus sign on the duration
if (leaf_duration + current_duration) > total_duration:
leaf_duration = total_duration - current_duration # catch any end condition by truncating the last duration
current_leaves = abjad.LeafMaker()([pitch], [leaf_duration]) # make the leaves
all_leaves.extend(current_leaves) # add the new leaves to the list of leaves
current_duration += leaf_duration # advance the total duration
talea_index += 1 # advance the talea index to the next fraction
In [479]:
music = abjad.Container(all_leaves) # put the leaves into a container for ease of further use
In [480]:
abjad.show(music)
Now we need to split the generated leaves at measure boundaries so that they fit into the time signatures we specified.
In [481]:
metr = []
for item in time_signature_pairs:
metr.append(abjad.TimeSignature(item))
splits = abjad.mutate(music[:]).split(time_signature_pairs, cyclic=True)
for i, x in zip(metr, splits):
abjad.attach(i, x[0])
In [482]:
abjad.show(Score)
In [483]:
selector = abjad.select(music).leaves()
measures = selector.group_by_measure()
for time, measure in zip(metr, measures):
abjad.mutate(measure).rewrite_meter(time)
In [484]:
abjad.show(music)
Have a look at the resulting LilyPond output to confirm that we now have both measures -- interior curly braces -- and also time signatures for each measure.
In [485]:
abjad.f(music)
In [486]:
#pitches = abjad.CyclicTuple(["d'", "a''", "gs'", "fs'"])
#pitches = abjad.CyclicTuple(["c'", "c''"])
#pitches = abjad.CyclicTuple([0, 2, 4, 5, 7, 9, 11, 12])
pitches = abjad.CyclicTuple([0, 3, 7, 12, 7, 3])
And then we apply the pitches to our previously generated durations:
In [487]:
logical_ties = abjad.iterate(music).logical_ties(pitched=True)
for i, logical_tie in enumerate(logical_ties):
pitch = pitches[i]
for note in logical_tie:
note.written_pitch = pitch
And now we have both pitches and rhythms:
In [488]:
abjad.show(music)
Finally, we can iterate through runs of leaves to further detail each gesture with articulations, dynamics, and technical indications.
In [489]:
print(abjad.select(music).runs())
for run in abjad.select(music).runs():
#abjad.attach(abjad.Articulation('accent'), run[0])
if 1 < len(run):
abjad.hairpin('p < f', run)
abjad.override(run[0]).dynamic_line_spanner.staff_padding = 4
else:
abjad.attach(abjad.Dynamic('ppp'), run[0])
In [493]:
Staff = abjad.Staff()
Staff.append(music)
Score = abjad.Score([Staff])
abjad.show(Score)
But you still wouldn't want to have to read the music we generated. To obey conventions of metric notation, we impose a metric hierarchy on each of the measures, according to each meter's default divisions. See Day Two's notebooks for how to impose a metric hierarchy on notation.)