In :import abjad %reload_ext abjadext.ipython import abjadext.rmakers
The last notebook gave us a series of steps that together generate music, but they clearly divide up into several different code blocks that each accomplish a different specific task. Wouldn't it be nice to reuse these blocks if useful? In this notebook, we will clean up our procedural code and encapsulate it into several reusable functions.
In :# THIS IS THE INPUT TO MY MUSICAL IDEA time_signature_pairs = [(4, 4), (3, 4), (7, 16), (6, 8)] counts = [1, 2, -3, 4] denominator = 16 pitches = abjad.CyclicTuple([0, 3, 7, 12, 7, 3])
In :def make_basic_rhythm(time_signature_pairs, counts, denominator): # THIS IS HOW WE MAKE THE BASIC RHYTHM total_duration = sum(abjad.Duration(pair) for pair in time_signature_pairs) talea = abjadext.rmakers.Talea(counts=counts, denominator=denominator) talea_index = 0 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 music = abjad.Container(all_leaves) return music
In :def clean_up_rhythm(music, time_signature_pairs): # THIS IS HOW WE CLEAN UP THE RHYTHM time_signatures =  for item in time_signature_pairs: time_signatures.append(abjad.TimeSignature(item)) splits = abjad.mutate(music[:]).split(time_signature_pairs, cyclic=True) for time, measure in zip(time_signatures, splits): abjad.attach(time, measure) selector = abjad.select(music).leaves() measures = selector.group_by_measure() for time, measure in zip(time_signatures, measures): abjad.mutate(measure).rewrite_meter(time) return music
In :def add_pitches(music, pitches): # THIS IS HOW WE ADD PITCHES pitches = abjad.CyclicTuple(pitches) 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 return music
In :def add_attachments(music): # THIS IS HOW WE ADD DYNAMICS AND ACCENTS for run in abjad.select(music).runs(): abjad.attach(abjad.Articulation('accent'), run) if 1 < len(run): abjad.hairpin('p < f', run) abjad.override(run).dynamic_line_spanner.staff_padding = 3 else: abjad.attach(abjad.Dynamic('ppp'), run) return music
In :def make_music(time_signature_pairs, counts, denominator, pitches): music = make_basic_rhythm(time_signature_pairs, counts, denominator) music = clean_up_rhythm(music, time_signature_pairs) music = add_pitches(music, pitches) music = add_attachments(music) return music
And we can call the function once to get music:
In :music = make_music(time_signature_pairs, counts, denominator, pitches) abjad.show(music)
We've now converted our procedural code into reusable functions. You might notice that we're passing the same variables into these functions, over and over. This is usually a good hint that it might be a good idea to refactor the code with an object-oriented frame of mind. In our next notebook, these reusable functions will become the methods of a music-making class.