Pärt: Cantus in Memory of Benjamin Britten

Note

Explore the abjad/demos/part directory for the complete code to this example, or import it into your Python session directly with from abjad.demos import part.

Let’s make some imports:

>>> import copy
>>> from abjad import *
def make_part_lilypond_file():
    r'''Makes Pärt LilyPond file.
    '''
    score_template = abjad.demos.part.PartCantusScoreTemplate()
    score = score_template()
    abjad.demos.part.add_bell_music_to_score(score)
    abjad.demos.part.add_string_music_to_score(score)
    abjad.demos.part.apply_bowing_marks(score)
    abjad.demos.part.apply_dynamics(score)
    abjad.demos.part.apply_expressive_marks(score)
    abjad.demos.part.apply_page_breaks(score)
    abjad.demos.part.apply_rehearsal_marks(score)
    abjad.demos.part.apply_final_bar_lines(score)
    abjad.demos.part.configure_score(score)
    lilypond_file = lilypondfiletools.LilyPondFile.new(score)
    abjad.demos.part.configure_lilypond_file(lilypond_file)
    return lilypond_file

The score template

class PartCantusScoreTemplate(abctools.AbjadObject):
    r'''Pärt Cantus score template.
    '''

    ### SPECIAL METHODS ###

    def __call__(self):
        '''Calls score template.

        Returns LilyPond file.
        '''

        # make bell voice and staff
        bell_voice = scoretools.Voice(name='Bell Voice')
        bell_staff = scoretools.Staff([bell_voice], name='Bell Staff')
        clef = indicatortools.Clef('treble')
        attach(clef, bell_staff)
        bells = instrumenttools.Instrument(
            instrument_name='Campana in La',
            short_instrument_name='Camp.',
            pitch_range='[C4, C6]',
            )
        attach(bells, bell_staff)
        tempo = indicatortools.Tempo((1, 4), (112, 120))
        attach(tempo, bell_staff)
        time_signature = indicatortools.TimeSignature((6, 4))
        attach(time_signature, bell_staff)

        # make first violin voice and staff
        first_violin_voice = scoretools.Voice(name='First Violin Voice')
        first_violin_staff = scoretools.Staff(
            [first_violin_voice],
            name='First Violin Staff',
            )
        clef = indicatortools.Clef('treble')
        attach(clef, first_violin_staff)
        violin = instrumenttools.Violin(
            instrument_name_markup=markuptools.Markup('Violin I'),
            short_instrument_name_markup=markuptools.Markup('Vl. I'),
            )
        attach(violin, first_violin_staff)

        # make second violin voice and staff
        second_violin_voice = scoretools.Voice(name='Second Violin Voice')
        second_violin_staff = scoretools.Staff(
            [second_violin_voice],
            name='Second Violin Staff',
            )
        clef = indicatortools.Clef('treble')
        attach(clef, second_violin_staff)
        violin = instrumenttools.Violin(
            instrument_name_markup=markuptools.Markup('Violin II'),
            short_instrument_name_markup=markuptools.Markup('Vl. II'),
            )
        attach(violin, second_violin_staff)

        # make viola voice and staff
        viola_voice = scoretools.Voice(name='Viola Voice')
        viola_staff = scoretools.Staff([viola_voice], name='Viola Staff')
        clef = indicatortools.Clef('alto')
        attach(clef, viola_staff)
        viola = instrumenttools.Viola()
        attach(viola, viola_staff)

        # make cello voice and staff
        cello_voice = scoretools.Voice(name='Cello Voice')
        cello_staff = scoretools.Staff([cello_voice], name='Cello Staff')
        clef = indicatortools.Clef('bass')
        attach(clef, cello_staff)
        cello = instrumenttools.Cello()
        attach(cello, cello_staff)

        # make bass voice and staff
        bass_voice = scoretools.Voice(name='Bass Voice')
        bass_staff = scoretools.Staff([bass_voice], name='Bass Staff')
        clef = indicatortools.Clef('bass')
        attach(clef, bass_staff)
        contrabass = instrumenttools.Contrabass(
            short_instrument_name_markup=markuptools.Markup('Cb.'),
            )
        attach(contrabass, bass_staff)

        # make strings staff group
        strings_staff_group = scoretools.StaffGroup([
            first_violin_staff,
            second_violin_staff,
            viola_staff,
            cello_staff,
            bass_staff,
            ],
            name='Strings Staff Group',
            )

        # make score
        score = scoretools.Score([
            bell_staff,
            strings_staff_group,
            ],
            name='Pärt Cantus Score'
            )

        # return Pärt Cantus score
        return score

The bell music

def add_bell_music_to_score(score):
    r'''Adds bell music to score.
    '''

    bell_voice = score['Bell Voice']

    def make_bell_phrase():
        phrase = []
        for _ in range(3):
            phrase.append(scoretools.Measure((6, 4), r"r2. a'2. \laissezVibrer"))
            phrase.append(scoretools.Measure((6, 4), 'R1.'))
        for _ in range(2):
            phrase.append(scoretools.Measure((6, 4), 'R1.'))
        return phrase

    for _ in range(11):
        bell_voice.extend(make_bell_phrase())

    for _ in range(19):
        bell_voice.append(scoretools.Measure((6, 4), 'R1.'))

    bell_voice.append(scoretools.Measure((6,4), r"a'1. \laissezVibrer"))

The string music

Creating the music for the strings is a bit more involved, but conceptually falls into two steps. First, we’ll procedurally generate basic pitches and rhythms for all string voices. Then, we’ll make edits to the generated material by hand. The entire process is encapsulated in the following function:

def add_string_music_to_score(score):
    r'''Adds string music to score.
    '''

    # generate some pitch and rhythm information
    pitch_contour_reservoir = \
        abjad.demos.part.create_pitch_contour_reservoir()
    shadowed_contour_reservoir = \
        abjad.demos.part.shadow_pitch_contour_reservoir(
        pitch_contour_reservoir)
    durated_reservoir = abjad.demos.part.durate_pitch_contour_reservoir(
        shadowed_contour_reservoir)

    # add six dotted-whole notes and the durated contours to each string voice
    for instrument_name, descents in durated_reservoir.items():
        instrument_voice = score['%s Voice' % instrument_name]
        instrument_voice.extend("R1. R1. R1. R1. R1. R1.")
        for descent in descents:
            instrument_voice.extend(descent)

    # apply instrument-specific edits
    abjad.demos.part.edit_first_violin_voice(score, durated_reservoir)
    abjad.demos.part.edit_second_violin_voice(score, durated_reservoir)
    abjad.demos.part.edit_viola_voice(score, durated_reservoir)
    abjad.demos.part.edit_cello_voice(score, durated_reservoir)
    abjad.demos.part.edit_bass_voice(score, durated_reservoir)

    # chop all string parts into 6/4 measures
    strings_staff_group = score['Strings Staff Group']
    with systemtools.ForbidUpdate(score):
        for voice in  iterate(strings_staff_group).by_class(scoretools.Voice):
            shards = mutate(voice[:]).split([(6, 4)], cyclic=True)
            for shard in shards:
                scoretools.Measure((6, 4), shard)

The pitch material is the same for all of the strings: a descending a-minor scale, generally decorated with diads. But, each instrument uses a different overall range, with the lower instrument playing slower and slower than the higher instruments, creating a sort of mensuration canon.

For each instrument, the descending scale is fragmented into what we’ll call “descents”. The first descent uses only the first note of that instrument’s scale, while the second descent adds the second note, and the third another. We’ll generate as many descents per instruments as there are pitches in its overall scale:

def create_pitch_contour_reservoir():
    r'''Creates pitch contour reservoir.
    '''

    scale = tonalanalysistools.Scale('a', 'minor')
    pitch_ranges = {
        'First Violin': pitchtools.PitchRange('[C4, A6]'),
        'Second Violin': pitchtools.PitchRange('[A3, A5]'),
        'Viola': pitchtools.PitchRange('[E3, A4]'),
        'Cello': pitchtools.PitchRange('[A2, A3]'),
        'Bass': pitchtools.PitchRange('[C3, A3]'),
    }

    reservoir = {}
    for instrument_name, pitch_range in pitch_ranges.items():
        pitch_set = scale.create_named_pitch_set_in_pitch_range(pitch_range)
        pitches = sorted(pitch_set, reverse=True)
        pitch_descents = []
        for i in range(len(pitches)):
            descent = tuple(pitches[:i + 1])
            pitch_descents.append(descent)
        reservoir[instrument_name] = tuple(pitch_descents)

    return reservoir

Here’s what the first 10 descents for the first violin look like:

>>> reservoir = create_pitch_contour_reservoir()
>>> for i in range(10):
...     descent = reservoir['First Violin'][i]
...     print(' '.join(str(x) for x in descent))
... 
a'''
a''' g'''
a''' g''' f'''
a''' g''' f''' e'''
a''' g''' f''' e''' d'''
a''' g''' f''' e''' d''' c'''
a''' g''' f''' e''' d''' c''' b''
a''' g''' f''' e''' d''' c''' b'' a''
a''' g''' f''' e''' d''' c''' b'' a'' g''
a''' g''' f''' e''' d''' c''' b'' a'' g'' f''

Next we add diads to all of the descents, except for the viola’s. We’ll use a dictionary as a lookup table, to tell us what interval to add below a given pitch class:

def shadow_pitch_contour_reservoir(pitch_contour_reservoir):
    r'''Shadows pitch contour reservoir.
    '''

    shadow_pitch_lookup = {
        pitchtools.NamedPitchClass('a'): -5, # add a P4 below
        pitchtools.NamedPitchClass('g'): -3, # add a m3 below
        pitchtools.NamedPitchClass('f'): -1, # add a m2 below
        pitchtools.NamedPitchClass('e'): -4, # add a M3 below
        pitchtools.NamedPitchClass('d'): -2, # add a M2 below
        pitchtools.NamedPitchClass('c'): -3, # add a m3 below
        pitchtools.NamedPitchClass('b'): -2, # add a M2 below
    }

    shadowed_reservoir = {}

    for instrument_name, pitch_contours in pitch_contour_reservoir.items():
        # The viola does not receive any diads
        if instrument_name == 'Viola':
            shadowed_reservoir['Viola'] = pitch_contours
            continue

        shadowed_pitch_contours = []

        for pitch_contour in pitch_contours[:-1]:
            shadowed_pitch_contour = []
            for pitch in pitch_contour:
                pitch_class = pitch.named_pitch_class
                shadow_pitch = pitch + shadow_pitch_lookup[pitch_class]
                diad = (shadow_pitch, pitch)
                shadowed_pitch_contour.append(diad)
            shadowed_pitch_contours.append(tuple(shadowed_pitch_contour))

        # treat the final contour differently: the last note does not become a diad
        final_shadowed_pitch_contour = []
        for pitch in pitch_contours[-1][:-1]:
            pitch_class = pitch.named_pitch_class
            shadow_pitch = pitch + shadow_pitch_lookup[pitch_class]
            diad = (shadow_pitch, pitch)
            final_shadowed_pitch_contour.append(diad)
        final_shadowed_pitch_contour.append(pitch_contours[-1][-1])
        shadowed_pitch_contours.append(tuple(final_shadowed_pitch_contour))

        shadowed_reservoir[instrument_name] = tuple(shadowed_pitch_contours)

    return shadowed_reservoir

Finally, we’ll add rhythms to the pitch contours we’ve been constructing. Each string instrument plays twice as slow as the string instrument above it in the score. Additionally, all the strings start with some rests, and use a “long-short” pattern for their rhythms:

def durate_pitch_contour_reservoir(pitch_contour_reservoir):
    r'''Durates pitch contour reservoir.
    '''

    instrument_names = [
        'First Violin',
        'Second Violin',
        'Viola',
        'Cello',
        'Bass',
        ]

    durated_reservoir = {}

    for i, instrument_name in enumerate(instrument_names):
        long_duration = durationtools.Duration(1, 2) * pow(2, i)
        short_duration = long_duration / 2
        rest_duration = long_duration * durationtools.Multiplier(3, 2)

        div = rest_duration // durationtools.Duration(3, 2)
        mod = rest_duration % durationtools.Duration(3, 2)

        initial_rest = scoretools.MultimeasureRest((3, 2)) * div
        if mod:
            initial_rest += scoretools.make_rests(mod)

        durated_contours = [tuple(initial_rest)]

        pitch_contours = pitch_contour_reservoir[instrument_name]
        durations = [long_duration, short_duration]
        counter = 0
        for pitch_contour in pitch_contours:
            contour = []
            for pitch in pitch_contour:
                contour.extend(
                    scoretools.make_leaves([pitch], [durations[counter]])
                    )
                counter = (counter + 1) % 2
            durated_contours.append(tuple(contour))

        durated_reservoir[instrument_name] = tuple(durated_contours)

    return durated_reservoir

Let’s see what a few of those look like. First, we’ll build the entire reservoir from scratch, so you can see the process:

>>> pitch_contour_reservoir = create_pitch_contour_reservoir()
>>> shadowed_contour_reservoir = shadow_pitch_contour_reservoir(pitch_contour_reservoir)
>>> durated_reservoir = durate_pitch_contour_reservoir(shadowed_contour_reservoir)

Then we’ll grab the sub-reservoir for the first violins, taking the first ten descents (which includes the silences we’ve been adding as well). We’ll label each descent with some markup, to distinguish them, throw them into a Staff and give them a 6/4 time signature, just so they line up properly.

>>> descents = durated_reservoir['First Violin'][:10]
>>> for i, descent in enumerate(descents[1:], 1):
...     markup = markuptools.Markup(
...         r'\rounded-box \bold {}'.format(i),
...         Up,
...         )
...     attach(markup, descent[0])
... 
>>> staff = Staff(sequence(descents).flatten())
>>> time_signature = TimeSignature((6, 4))
>>> attach(time_signature, staff)
>>> show(staff)

Let’s look at the second violins too:

>>> descents = durated_reservoir['Second Violin'][:10]
>>> for i, descent in enumerate(descents[1:], 1):
...     markup = markuptools.Markup(
...         r'\rounded-box \bold {}'.format(i),
...         Up,
...         )
...     attach(markup, descent[0])
... 
>>> staff = Staff(sequence(descents).flatten())
>>> time_signature = TimeSignature((6, 4))
>>> attach(time_signature, staff)
>>> show(staff)

And, last we’ll take a peek at the violas. They have some longer notes, so we’ll split their music cyclically every 3 half notes, just so nothing crosses the bar lines accidentally:

>>> descents = durated_reservoir['Viola'][:10]
>>> for i, descent in enumerate(descents[1:], 1):
...     markup = markuptools.Markup(
...         r'\rounded-box \bold {}'.format(i),
...         Up,
...         )
...     attach(markup, descent[0])
... 
>>> staff = Staff(sequence(descents).flatten())
>>> shards = mutate(staff[:]).split([(3, 2)], cyclic=True)
>>> time_signature = indicatortools.TimeSignature((6, 4))
>>> attach(time_signature, staff)
>>> show(staff)

You can see how each part is twice as slow as the previous, and starts a little bit later too.

The edits

def edit_first_violin_voice(score, durated_reservoir):
    r'''Edits first violin voice.
    '''

    voice = score['First Violin Voice']
    descents = durated_reservoir['First Violin']
    descents = selectiontools.Selection(descents)

    last_descent = select(descents[-1])
    copied_descent = mutate(last_descent).copy()
    voice.extend(copied_descent)

    final_sustain_rhythm = [(6, 4)] * 43 + [(1, 2)]
    final_sustain_notes = scoretools.make_notes(["c'"], final_sustain_rhythm)
    voice.extend(final_sustain_notes)
    tie = spannertools.Tie()
    attach(tie, final_sustain_notes)
    voice.extend('r4 r2.')
def edit_second_violin_voice(score, durated_reservoir):
    r'''Edits second violin voice.
    '''

    voice = score['Second Violin Voice']
    descents = durated_reservoir['Second Violin']

    last_descent = select(descents[-1])
    copied_descent = mutate(last_descent).copy()
    copied_descent = list(copied_descent)
    copied_descent[-1].written_duration = durationtools.Duration(1, 1)
    copied_descent.append(scoretools.Note('a2'))
    for leaf in copied_descent:
        articulation = indicatortools.Articulation('accent')
        attach(articulation, leaf)
        articulation = indicatortools.Articulation('tenuto')
        attach(articulation, leaf)
    voice.extend(copied_descent)

    final_sustain = []
    for _ in range(32):
        final_sustain.append(scoretools.Note('a1.'))
    final_sustain.append(scoretools.Note('a2'))
    articulation = indicatortools.Articulation('accent')
    attach(articulation, final_sustain[0])
    articulation = indicatortools.Articulation('tenuto')
    attach(articulation, final_sustain[0])

    voice.extend(final_sustain)
    tie = spannertools.Tie()
    attach(tie, final_sustain)
    voice.extend('r4 r2.')
def edit_viola_voice(score, durated_reservoir):
    r'''Edits viola voice.
    '''

    voice = score['Viola Voice']
    descents = durated_reservoir['Viola']

    for leaf in descents[-1]:
        articulation = indicatortools.Articulation('accent')
        attach(articulation, leaf)
        articulation = indicatortools.Articulation('tenuto')
        attach(articulation, leaf)
    last_descent = select(descents[-1])
    copied_descent = mutate(last_descent).copy()
    for leaf in copied_descent:
        if leaf.written_duration == durationtools.Duration(4, 4):
            leaf.written_duration = durationtools.Duration(8, 4)
        else:
            leaf.written_duration = durationtools.Duration(4, 4)
    voice.extend(copied_descent)

    bridge = scoretools.Note('e1')
    articulation = indicatortools.Articulation('tenuto')
    attach(articulation, bridge)
    articulation = indicatortools.Articulation('accent')
    attach(articulation, bridge)
    voice.append(bridge)

    final_sustain_rhythm = [(6, 4)] * 21 + [(1, 2)]
    final_sustain_notes = scoretools.make_notes(['e'], final_sustain_rhythm)
    articulation = indicatortools.Articulation('accent')
    attach(articulation, final_sustain_notes[0])
    articulation = indicatortools.Articulation('tenuto')
    attach(articulation, final_sustain_notes[0])
    voice.extend(final_sustain_notes)
    tie = spannertools.Tie()
    attach(tie, final_sustain_notes)
    voice.extend('r4 r2.')
def edit_cello_voice(score, durated_reservoir):
    r'''Edits cello voice.
    '''

    voice = score['Cello Voice']
    descents = durated_reservoir['Cello']

    logical_tie = inspect_(voice[-1]).get_logical_tie()
    for leaf in logical_tie.leaves:
        parent = leaf._get_parentage().parent
        index = parent.index(leaf)
        parent[index] = scoretools.Chord(['e,', 'a,'], leaf.written_duration)

    selection = voice[-len(descents[-1]):]
    unison_descent = mutate(selection).copy()
    voice.extend(unison_descent)
    for chord in unison_descent:
        index = inspect_(chord).get_parentage().parent.index(chord)
        parent[index] = scoretools.Note(
            chord.written_pitches[1], chord.written_duration)
        articulation = indicatortools.Articulation('accent')
        attach(articulation, parent[index])
        articulation = indicatortools.Articulation('tenuto')
        attach(articulation, parent[index])

    voice.extend('a,1. ~ a,2')
    voice.extend('b,1 ~ b,1. ~ b,1.')
    voice.extend('a,1. ~ a,1. ~ a,1. ~ a,1. ~ a,1. ~ a,2')
    voice.extend('r4 r2.')
def edit_bass_voice(score, durated_reservoir):
    r'''Edits bass voice.
    '''

    voice = score['Bass Voice']

    voice[-3:] = '<e, e>\maxima <d, d>\longa <c, c>\maxima <b,>\longa <a,>\maxima r4 r2.'

The marks

Now we’ll apply various kinds of marks, including dynamics, articulations, bowing indications, expressive instructures, page breaks and rehearsal marks.

We’ll start with the bowing marks. This involves creating a piece of custom markup to indicate rebowing. We accomplish this by aggregating together some markuptools.MarkupCommand and markuptools.MusicGlyph objects. The completed markuptools.Markup object is then copied and attached at the correct locations in the score.

Why copy it? A Mark can only be attached to a single Component. If we attached the original piece of markup to each of our target components in turn, only the last would actually receive the markup, as it would have be detached from the preceding components.

Let’s take a look:

def apply_bowing_marks(score):
    r'''Applies bowing marks to score.
    '''

    # apply alternating upbow and downbow for first two sounding bars
    # of the first violin
    for measure in score['First Violin Voice'][6:8]:
        for i, chord in enumerate(iterate(measure).by_class(scoretools.Chord)):
            if i % 2 == 0:
                articulation = indicatortools.Articulation('downbow')
                attach(articulation, chord)
            else:
                articulation = indicatortools.Articulation('upbow')
                attach(articulation, chord)

    # create and apply rebowing markup
    rebow_markup = markuptools.Markup.concat([
        markuptools.Markup.musicglyph('scripts.downbow'),
        markuptools.Markup.hspace(1),
        markuptools.Markup.musicglyph('scripts.upbow'),
        ])
    markup = copy.copy(rebow_markup)
    attach(markup, score['First Violin Voice'][64][0])
    markup = copy.copy(rebow_markup)
    attach(markup, score['Second Violin Voice'][75][0])
    markup = copy.copy(rebow_markup)
    attach(markup, score['Viola Voice'][86][0])

After dealing with custom markup, applying dynamics is easy. Just instantiate and attach:

def apply_dynamics(score):
    r'''Applies dynamics to score.
    '''

    voice = score['Bell Voice']
    dynamic = indicatortools.Dynamic('ppp')
    attach(dynamic, voice[0][1])
    dynamic = indicatortools.Dynamic('pp')
    attach(dynamic, voice[8][1])
    dynamic = indicatortools.Dynamic('p')
    attach(dynamic, voice[18][1])
    dynamic = indicatortools.Dynamic('mp')
    attach(dynamic, voice[26][1])
    dynamic = indicatortools.Dynamic('mf')
    attach(dynamic, voice[34][1])
    dynamic = indicatortools.Dynamic('f')
    attach(dynamic, voice[42][1])
    dynamic = indicatortools.Dynamic('ff')
    attach(dynamic, voice[52][1])
    dynamic = indicatortools.Dynamic('fff')
    attach(dynamic, voice[60][1])
    dynamic = indicatortools.Dynamic('ff')
    attach(dynamic, voice[68][1])
    dynamic = indicatortools.Dynamic('f')
    attach(dynamic, voice[76][1])
    dynamic = indicatortools.Dynamic('mf')
    attach(dynamic, voice[84][1])
    dynamic = indicatortools.Dynamic('pp')
    attach(dynamic, voice[-1][0])

    voice = score['First Violin Voice']
    dynamic = indicatortools.Dynamic('ppp')
    attach(dynamic, voice[6][1])
    dynamic = indicatortools.Dynamic('pp')
    attach(dynamic, voice[15][0])
    dynamic = indicatortools.Dynamic('p')
    attach(dynamic, voice[22][3])
    dynamic = indicatortools.Dynamic('mp')
    attach(dynamic, voice[31][0])
    dynamic = indicatortools.Dynamic('mf')
    attach(dynamic, voice[38][3])
    dynamic = indicatortools.Dynamic('f')
    attach(dynamic, voice[47][0])
    dynamic = indicatortools.Dynamic('ff')
    attach(dynamic, voice[55][2])
    dynamic = indicatortools.Dynamic('fff')
    attach(dynamic, voice[62][2])

    voice = score['Second Violin Voice']
    dynamic = indicatortools.Dynamic('pp')
    attach(dynamic, voice[7][0])
    dynamic = indicatortools.Dynamic('p')
    attach(dynamic, voice[12][0])
    dynamic = indicatortools.Dynamic('p')
    attach(dynamic, voice[16][0])
    dynamic = indicatortools.Dynamic('mp')
    attach(dynamic, voice[25][1])
    dynamic = indicatortools.Dynamic('mf')
    attach(dynamic, voice[34][1])
    dynamic = indicatortools.Dynamic('f')
    attach(dynamic, voice[44][1])
    dynamic = indicatortools.Dynamic('ff')
    attach(dynamic, voice[54][0])
    dynamic = indicatortools.Dynamic('fff')
    attach(dynamic, voice[62][1])

    voice = score['Viola Voice']
    dynamic = indicatortools.Dynamic('p')
    attach(dynamic, voice[8][0])
    dynamic = indicatortools.Dynamic('mp')
    attach(dynamic, voice[19][1])
    dynamic = indicatortools.Dynamic('mf')
    attach(dynamic, voice[30][0])
    dynamic = indicatortools.Dynamic('f')
    attach(dynamic, voice[36][0])
    dynamic = indicatortools.Dynamic('f')
    attach(dynamic, voice[42][0])
    dynamic = indicatortools.Dynamic('ff')
    attach(dynamic, voice[52][0])
    dynamic = indicatortools.Dynamic('fff')
    attach(dynamic, voice[62][0])

    voice = score['Cello Voice']
    dynamic = indicatortools.Dynamic('p')
    attach(dynamic, voice[10][0])
    dynamic = indicatortools.Dynamic('mp')
    attach(dynamic, voice[21][0])
    dynamic = indicatortools.Dynamic('mf')
    attach(dynamic, voice[31][0])
    dynamic = indicatortools.Dynamic('f')
    attach(dynamic, voice[43][0])
    dynamic = indicatortools.Dynamic('ff')
    attach(dynamic, voice[52][1])
    dynamic = indicatortools.Dynamic('fff')
    attach(dynamic, voice[62][0])

    voice = score['Bass Voice']
    dynamic = indicatortools.Dynamic('mp')
    attach(dynamic, voice[14][0])
    dynamic = indicatortools.Dynamic('mf')
    attach(dynamic, voice[27][0])
    dynamic = indicatortools.Dynamic('f')
    attach(dynamic, voice[39][0])
    dynamic = indicatortools.Dynamic('ff')
    attach(dynamic, voice[51][0])
    dynamic = indicatortools.Dynamic('fff')
    attach(dynamic, voice[62][0])

We apply expressive marks the same way we applied our dynamics:

def apply_expressive_marks(score):
    r'''Applies expressive marks to score.
    '''

    voice = score['First Violin Voice']
    markup = markuptools.Markup(
        r'\left-column { div. \line { con sord. } }', Up)
    attach(markup, voice[6][1])
    markup = markuptools.Markup('sim.', Up)
    attach(markup, voice[8][0])
    markup = markuptools.Markup('uniti', Up)
    attach(markup, voice[58][3])
    markup = markuptools.Markup('div.', Up)
    attach(markup, voice[59][0])
    markup = markuptools.Markup('uniti', Up)
    attach(markup, voice[63][3])

    voice = score['Second Violin Voice']
    markup = markuptools.Markup('div.', Up)
    attach(markup, voice[7][0])
    markup = markuptools.Markup('uniti', Up)
    attach(markup, voice[66][1])
    markup = markuptools.Markup('div.', Up)
    attach(markup, voice[67][0])
    markup = markuptools.Markup('uniti', Up)
    attach(markup, voice[74][0])

    voice = score['Viola Voice']
    markup = markuptools.Markup('sole', Up)
    attach(markup, voice[8][0])

    voice = score['Cello Voice']
    markup = markuptools.Markup('div.', Up)
    attach(markup, voice[10][0])
    markup = markuptools.Markup('uniti', Up)
    attach(markup, voice[74][0])
    markup = markuptools.Markup('uniti', Up)
    attach(markup, voice[84][1])
    markup = markuptools.Markup(r'\italic { espr. }', Down)
    attach(markup, voice[86][0])
    markup = markuptools.Markup(r'\italic { molto espr. }', Down)
    attach(markup, voice[88][1])

    voice = score['Bass Voice']
    markup = markuptools.Markup('div.', Up)
    attach(markup, voice[14][0])
    markup = markuptools.Markup(r'\italic { espr. }', Down)
    attach(markup, voice[86][0])
    mutate(voice[88][:]).split(
        [durationtools.Duration(1, 1), durationtools.Duration(1, 2)]
        )
    markup = markuptools.Markup(r'\italic { molto espr. }', Down)
    attach(markup, voice[88][1])
    markup = markuptools.Markup('uniti', Up)
    attach(markup, voice[99][1])

    strings_staff_group = score['Strings Staff Group']
    for voice in iterate(strings_staff_group).by_class(scoretools.Voice):
        markup = markuptools.Markup(r'\italic { (non dim.) }', Down)
        attach(markup, voice[102][0])

We use the indicatortools.LilyPondCommand class to create LilyPond system breaks, and attach them to measures in the percussion part. After this, our score will break in the exact same places as the original:

def apply_page_breaks(score):
    r'''Applies page breaks to score.
    '''

    bell_voice = score['Bell Voice']

    measure_indices = [
        5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 72,
        79, 86, 93, 100,
        ]

    for measure_index in measure_indices:
        command = indicatortools.LilyPondCommand('break', 'after')
        attach(command, bell_voice[measure_index])

We’ll make the rehearsal marks the exact same way we made our line breaks:

def apply_rehearsal_marks(score):
    r'''Applies rehearsal marks to score.
    '''

    bell_voice = score['Bell Voice']

    measure_indices = [
        6, 12, 18, 24, 30, 36, 42, 48, 54, 60, 66, 72, 78, 84,
        90, 96, 102,
        ]

    for measure_index in measure_indices:
        command = indicatortools.LilyPondCommand(r'mark \default', 'before')
        attach(command, bell_voice[measure_index])

And then we add our final bar lines. indicatortools.BarLine objects inherit from indicatortools.Mark, so you can probably guess by now how we add them to the score... instantiate and attach:

def apply_final_bar_lines(score):
    r'''Applies final bar lines to score.
    '''

    for voice in iterate(score).by_class(scoretools.Voice):
        bar_line = indicatortools.BarLine('|.')
        attach(bar_line, voice[-1])

The LilyPond file

Finally, we create some functions to apply formatting directives to our Score object, then wrap it into a LilyPondFile and apply some more formatting.

In our configure_score() functions, we use schemetools.make_spacing_vector() to create the correct Scheme construct to tell LilyPond how to handle vertical space for its staves and staff groups. You should consult LilyPond’s vertical spacing documentation for a complete explanation of what this Scheme code means:

>>> spacing_vector = schemetools.make_spacing_vector(0, 0, 8, 0)
>>> print(format(spacing_vector))
#'((basic-distance . 0) (minimum-distance . 0) (padding . 8) (stretchability . 0))
def configure_score(score):
    r'''Configures score.
    '''

    spacing_vector = schemetools.make_spacing_vector(0, 0, 8, 0)
    override(score).vertical_axis_group.staff_staff_spacing = spacing_vector
    override(score).staff_grouper.staff_staff_spacing = spacing_vector
    override(score).staff_symbol.thickness = 0.5
    set_(score).mark_formatter = schemetools.Scheme('format-mark-box-numbers')

In our configure_lilypond_file() function, we need to construct a ContextBlock definition in order to tell LilyPond to hide empty staves, and additionally to hide empty staves if they appear in the first system:

def configure_lilypond_file(lilypond_file):
    r'''Configures LilyPond file.
    '''

    lilypond_file._global_staff_size = 8

    context_block = lilypondfiletools.ContextBlock(
        source_context_name=r'Staff \RemoveEmptyStaves',
        )
    override(context_block).vertical_axis_group.remove_first = True
    lilypond_file.layout_block.items.append(context_block)

    slash_separator = indicatortools.LilyPondCommand('slashSeparator')
    lilypond_file.paper_block.system_separator_markup = slash_separator

    bottom_margin = lilypondfiletools.LilyPondDimension(0.5, 'in')
    lilypond_file.paper_block.bottom_margin = bottom_margin

    top_margin = lilypondfiletools.LilyPondDimension(0.5, 'in')
    lilypond_file.paper_block.top_margin = top_margin

    left_margin = lilypondfiletools.LilyPondDimension(0.75, 'in')
    lilypond_file.paper_block.left_margin = left_margin

    right_margin = lilypondfiletools.LilyPondDimension(0.5, 'in')
    lilypond_file.paper_block.right_margin = right_margin

    paper_width = lilypondfiletools.LilyPondDimension(5.25, 'in')
    lilypond_file.paper_block.paper_width = paper_width

    paper_height = lilypondfiletools.LilyPondDimension(7.25, 'in')
    lilypond_file.paper_block.paper_height = paper_height

    lilypond_file.header_block.composer = markuptools.Markup('Arvo Pärt')
    title = 'Cantus in Memory of Benjamin Britten (1980)'
    lilypond_file.header_block.title = markuptools.Markup(title)

Let’s run our original toplevel function to build the complete score:

>>> lilypond_file = make_part_lilypond_file()

And here we show it:

>>> show(lilypond_file)

Note

We only show the first two pages as the Cantus is still under copyright.