예제 #1
0
def test_lilypondparsertools_LilyPondParser__spanners__Hairpin_03():
    r'''Dynamics can terminate hairpins.
    '''

    maker = abjad.NoteMaker()
    target = abjad.Staff(maker([0] * 3, [(1, 4)]))
    hairpin = abjad.Hairpin(descriptor='<')
    abjad.attach(hairpin, target[0:2])
    hairpin = abjad.Hairpin(descriptor='>')
    abjad.attach(hairpin, target[1:])
    dynamic = abjad.Dynamic('p')
    abjad.attach(dynamic, target[1])
    dynamic = abjad.Dynamic('f')
    abjad.attach(dynamic, target[-1])

    assert format(target) == abjad.String.normalize(r'''
        \new Staff
        {
            c'4
            \<
            c'4
            \p
            \>
            c'4
            \f
        }
        ''')

    string = r"\new Staff \relative c' { c \< c \p \> c \f }"
    parser = abjad.lilypondparsertools.LilyPondParser()
    result = parser(string)
    assert format(target) == format(result) and target is not result
예제 #2
0
def test_lilypondparsertools_LilyPondParser__spanners__Hairpin_01():

    maker = abjad.NoteMaker()
    target = abjad.Staff(maker([0] * 5, [(1, 4)]))
    hairpin = abjad.Hairpin(descriptor='<')
    abjad.attach(hairpin, target[:3])
    hairpin = abjad.Hairpin(descriptor='>')
    abjad.attach(hairpin, target[2:])
    dynamic = abjad.Dynamic('ppp')
    abjad.attach(dynamic, target[-1])

    assert format(target) == abjad.String.normalize(r'''
        \new Staff
        {
            c'4
            \<
            c'4
            c'4
            \!
            \>
            c'4
            c'4
            \ppp
        }
        ''')

    parser = abjad.lilypondparsertools.LilyPondParser()
    result = parser(format(target))
    assert format(target) == format(result) and target is not result
예제 #3
0
def test_lilypondparsertools_LilyPondParser__spanners__Hairpin_02():

    maker = abjad.NoteMaker()
    target = abjad.Container(maker([0] * 4, [(1, 4)]))
    hairpin = abjad.Hairpin(descriptor='<')
    abjad.attach(hairpin, target[0:2])
    hairpin = abjad.Hairpin(descriptor='<')
    abjad.attach(hairpin, target[1:3])
    hairpin = abjad.Hairpin(descriptor='<')
    abjad.attach(hairpin, target[2:])

    assert format(target) == abjad.String.normalize(r'''
        {
            c'4
            \<
            c'4
            \!
            \<
            c'4
            \!
            \<
            c'4
            \!
        }
        ''')

    string = r'''\relative c' { c \< c \< c \< c \! }'''
    parser = abjad.lilypondparsertools.LilyPondParser()
    result = parser(string)
    assert format(target) == format(result) and target is not result
def test_scoretools_Inspection_get_duration_01():
    r'''Spanner duration in seconds equals sum of duration
    of all leaves in spanner, in seconds.
    '''

    voice = abjad.Voice([
        abjad.Measure((2, 12), "c'8 d'8", implicit_scaling=True),
        abjad.Measure((2, 8), "c'8 d'8")
    ])
    leaves = abjad.select(voice).leaves()
    mark = abjad.MetronomeMark(abjad.Duration(1, 8), 42)
    abjad.attach(mark, leaves[0], context='Voice')
    beam = abjad.Beam()
    abjad.attach(beam, leaves)
    crescendo = abjad.Hairpin('<')
    abjad.attach(crescendo, voice[0][:])
    diminuendo = abjad.Hairpin('>')
    abjad.attach(diminuendo, voice[1][:])

    assert format(voice) == abjad.String.normalize(r'''
        \new Voice
        {
            {   % measure
                \time 2/12
                \scaleDurations #'(2 . 3) {
                    \tempo 8=42
                    c'8
                    [
                    \<
                    d'8
                    \!
                }
            }   % measure
            {   % measure
                \time 2/8
                c'8
                \>
                d'8
                ]
                \!
            }   % measure
        }
        ''')

    assert abjad.inspect(beam).get_duration(in_seconds=True) == abjad.Duration(
        100, 21)
    assert abjad.inspect(crescendo).get_duration(
        in_seconds=True) == abjad.Duration(40, 21)
    assert abjad.inspect(diminuendo).get_duration(in_seconds=True) == \
        abjad.Duration(20, 7)
예제 #5
0
def test_scoretools_Mutation_replace_02():
    r'''Moves parentage and spanners from one old note to five new notes.

    Equivalent to staff[:1] = new_notes.
    '''

    staff = abjad.Staff("c'8 d'8 e'8 f'8")
    beam_1 = abjad.Beam()
    abjad.attach(beam_1, staff[:2])
    beam_2 = abjad.Beam()
    abjad.attach(beam_2, staff[2:])
    crescendo = abjad.Hairpin('<')
    abjad.attach(crescendo, staff[:])

    assert format(staff) == abjad.String.normalize(r'''
        \new Staff
        {
            c'8
            [
            \<
            d'8
            ]
            e'8
            [
            f'8
            ]
            \!
        }
        '''), format(staff)

    old_notes = staff[:1]
    new_notes = 5 * abjad.Note("c''16")
    abjad.mutate(old_notes).replace(new_notes)

    assert format(staff) == abjad.String.normalize(r'''
        \new Staff
        {
            c''16
            [
            \<
            c''16
            c''16
            c''16
            c''16
            d'8
            ]
            e'8
            [
            f'8
            ]
            \!
        }
        '''), format(staff)

    assert abjad.inspect(staff).is_well_formed()
예제 #6
0
def add_attachments(music):
    # THIS IS HOW WE ADD DYNAMICS AND ACCENTS
    for run in abjad.select(music).leaves().runs():
        if not isinstance(run[0], (abjad.Note, abjad.Chord)):
            continue
        abjad.attach(abjad.Articulation('accent'), run[0])
        if 1 < len(run):
            abjad.attach(abjad.Hairpin('p < f'), run)
        else:
            abjad.attach(abjad.Dynamic('ppp'), run[0])
    return music
def test_scoretools_Inspection_get_duration_02():

    voice = abjad.Voice([
        abjad.Measure((2, 12), "c'8 d'8", implicit_scaling=True),
        abjad.Measure((2, 8), "c'8 d'8")
    ])
    leaves = abjad.select(voice).leaves()
    beam = abjad.Beam()
    abjad.attach(beam, leaves)
    crescendo = abjad.Hairpin('<')
    abjad.attach(crescendo, voice[0][:])
    diminuendo = abjad.Hairpin('>')
    abjad.attach(diminuendo, voice[1][:])

    assert format(voice) == abjad.String.normalize(r'''
        \new Voice
        {
            {   % measure
                \time 2/12
                \scaleDurations #'(2 . 3) {
                    c'8
                    [
                    \<
                    d'8
                    \!
                }
            }   % measure
            {   % measure
                \time 2/8
                c'8
                \>
                d'8
                ]
                \!
            }   % measure
        }
        ''')

    assert abjad.inspect(beam).get_duration() == abjad.Duration(5, 12)
    assert abjad.inspect(crescendo).get_duration() == abjad.Duration(2, 12)
    assert abjad.inspect(diminuendo).get_duration() == abjad.Duration(2, 8)
예제 #8
0
 def add_attachments(self, music):
     # THIS IS HOW WE ADD DYNAMICS AND ACCENTS
     for run in abjad.select(music).leaves(pitched=True).runs():
         if not isinstance(run[0], (abjad.Note, abjad.Chord)):
             continue
         abjad.attach(abjad.Articulation('accent'), run[0])
         if 1 < len(run):
             abjad.attach(abjad.Hairpin('p < f'), run)
         else:
             abjad.attach(abjad.Dynamic('ppp'), run[0])
     first_leaf = next(abjad.iterate(music).leaves())
     abjad.attach(self.clef, first_leaf)
     return music
def test_scoretools_Mutation_copy_08():
    r'''Copies hairpin.
    '''

    staff = abjad.Staff("c'8 cs'8 d'8 ef'8 e'8 f'8 fs'8 g'8")
    crescendo = abjad.Hairpin('<')
    abjad.attach(crescendo, staff[:4])

    assert format(staff) == abjad.String.normalize(r'''
        \new Staff
        {
            c'8
            \<
            cs'8
            d'8
            ef'8
            \!
            e'8
            f'8
            fs'8
            g'8
        }
        ''')

    new_notes = abjad.mutate(staff[:4]).copy()
    staff.extend(new_notes)

    assert format(staff) == abjad.String.normalize(r'''
        \new Staff
        {
            c'8
            \<
            cs'8
            d'8
            ef'8
            \!
            e'8
            f'8
            fs'8
            g'8
            c'8
            \<
            cs'8
            d'8
            ef'8
            \!
        }
        ''')
    assert abjad.inspect(staff).is_well_formed()
예제 #10
0
def test_scoretools_Note___copy___07():
    r'''Copying note does note copy hairpin.
    '''

    staff = abjad.Staff([abjad.Note(n, (1, 8)) for n in range(8)])
    crescendo = abjad.Hairpin('<')
    abjad.attach(crescendo, staff[:4])

    assert format(staff) == abjad.String.normalize(
        r'''
        \new Staff
        {
            c'8
            \<
            cs'8
            d'8
            ef'8
            \!
            e'8
            f'8
            fs'8
            g'8
        }
        '''
        )

    new_note = copy.copy(staff[0])
    staff.append(new_note)

    assert format(staff) == abjad.String.normalize(
        r'''
        \new Staff
        {
            c'8
            \<
            cs'8
            d'8
            ef'8
            \!
            e'8
            f'8
            fs'8
            g'8
            c'8
        }
        '''
        )
    assert abjad.inspect(staff).is_well_formed()
예제 #11
0
    def make_hairpin_score_01(self):
        r'''Make 200-note voice with crescendo spanner on every 4 notes.

        2.12 (r9726) initialization:        248,502 function calls
        2.12 (r9728) initialization:        248,502 function calls

        2.12 (r9726) LilyPond format:       138,313 function calls
        2.12 (r9728) LilyPond format:       134,563 function calls

        '''
        import abjad
        voice = abjad.Voice(200 * abjad.Note("c'16"))
        for part in abjad.sequence(voice[:]).partition_by_counts(
            [4],
                cyclic=True,
        ):
            crescendo = abjad.Hairpin('<')
            abjad.attach(crescendo, part)
        return voice
예제 #12
0
    def make_hairpin_score_03(self):
        """
        Make 200-note voice with crescendo spanner on every 100 notes.

        2.12 (r9726) initialization:        249,363 function calls
        2.12 (r9726) initialization:        249,363 function calls

        2.12 (r9726) LilyPond format:       133,898 function calls
        2.12 (r9728) LilyPond format:       128,948 function calls

        """
        import abjad
        voice = abjad.Voice(200 * abjad.Note("c'16"))
        for part in abjad.sequence(voice[:]).partition_by_counts(
            [100],
                cyclic=True,
        ):
            crescendo = abjad.Hairpin('<')
            abjad.attach(crescendo, part)
        return voice
예제 #13
0
    def make_bound_hairpin_score_03(self):
        r'''Make 200-note voice with p-to-f bound crescendo spanner
        on every 100 notes.

        2.12 (r9726) initialization:        267,417 function calls

        2.12 (r9726) LilyPond format:       116,534 function calls

        '''
        import abjad
        voice = abjad.Voice(200 * abjad.Note("c'16"))
        for part in abjad.sequence(voice[:]).partition_by_counts(
            [100],
                cyclic=True,
        ):
            crescendo = abjad.Hairpin('<')
            abjad.attach(crescendo, part)
            dynamic = abjad.Dynamic('p')
            abjad.attach(dynamic, part[0])
            dynamic = abjad.Dynamic('r')
            abjad.attach(dynamic, part[-1])
        return voice
예제 #14
0
    def make_bound_hairpin_score_01(self):
        """
        Make 200-note voice with p-to-f bound crescendo spanner
        on every 4 notes.

        2.12 (r9726) initialization:        279,448 function calls

        2.12 (r9726) LilyPond format:       124,517 function calls

        """
        import abjad
        voice = abjad.Voice(200 * abjad.Note("c'16"))
        for part in abjad.Sequenc(voice[:]).partition_by_counts(
            [4],
                cyclic=True,
        ):
            crescendo = abjad.Hairpin('<')
            abjad.attach(crescendo, part)
            dynamic = abjad.Dynamic('p')
            abjad.attach(dynamic, part[0])
            dynamic = abjad.Dynamic('r')
            abjad.attach(dynamic, part[-1])
        return voice
예제 #15
0
def test_lilypondproxytools_LilyPondGrobNameManager___setattr___10():
    r'''Override LilyPond abjad.DynamicLineSpanner grob.
    '''

    voice = abjad.Voice("c'8 d'8 e'8 f'8")
    hairpin = abjad.Hairpin(descriptor='p < f')
    abjad.attach(hairpin, voice[:])
    abjad.override(hairpin).dynamic_line_spanner.staff_padding = 4

    assert format(voice) == abjad.String.normalize(r'''
        \new Voice
        {
            \override DynamicLineSpanner.staff-padding = #4
            c'8
            \<
            \p
            d'8
            e'8
            \revert DynamicLineSpanner.staff-padding
            f'8
            \f
        }
        ''')
def test_scoretools_Selection__give_dominant_spanners_01():
    r'''Find spanners that dominate donor_components.
    Apply dominant spanners to recipient_components.
    Withdraw donor_components from spanners.
    The operation can mangle spanners.
    Remove donor_components from parentage immediately after.
    '''

    voice = abjad.Voice("c'8 d'8 e'8 f'8")
    crescendo = abjad.Hairpin('<')
    abjad.attach(crescendo, voice[:])
    beam = abjad.Beam()
    abjad.attach(beam, voice[:2])
    slur = abjad.Slur()
    abjad.attach(slur, voice[1:3])

    assert format(voice) == abjad.String.normalize(r'''
        \new Voice
        {
            c'8
            [
            \<
            d'8
            ]
            (
            e'8
            )
            f'8
            \!
        }
        ''')

    recipient = abjad.Voice("c'16 c'16 c'16")
    beam = abjad.Beam()
    abjad.attach(beam, recipient[:])

    assert format(recipient) == abjad.String.normalize(r'''
        \new Voice
        {
            c'16
            [
            c'16
            c'16
            ]
        }
        ''')

    voice[1:3]._give_dominant_spanners(recipient[:])

    "Both crescendo and beam are now discontiguous."

    assert format(voice) == abjad.String.normalize(r'''
        \new Voice
        {
            c'8
            [
            \<
            d'8
            ]
            e'8
            f'8
            \!
        }
        ''')

    assert not abjad.inspect(voice).is_well_formed()

    "Slur is contiguous but recipient participates in discont. cresc."

    assert format(recipient) == abjad.String.normalize(r'''
        \new Voice
        {
            c'16
            [
            (
            c'16
            c'16
            ]
            )
        }
        ''')

    assert not abjad.inspect(recipient).is_well_formed()
예제 #17
0
def make_bartok_score():
    score = abjad.Score()
    piano_staff = abjad.StaffGroup([], lilypond_type='PianoStaff')
    upper_staff = abjad.Staff([])
    lower_staff = abjad.Staff([])
    piano_staff.append(upper_staff)
    piano_staff.append(lower_staff)
    score.append(piano_staff)
    upper_measures = []
    upper_measures.append(abjad.Measure((2, 4), []))
    upper_measures.append(abjad.Measure((3, 4), []))
    upper_measures.append(abjad.Measure((2, 4), []))
    upper_measures.append(abjad.Measure((2, 4), []))
    upper_measures.append(abjad.Measure((2, 4), []))
    lower_measures = copy.deepcopy(upper_measures)
    upper_staff.extend(upper_measures)
    lower_staff.extend(lower_measures)
    upper_measures[0].extend("a'8 g'8 f'8 e'8")
    upper_measures[1].extend("d'4 g'8 f'8 e'8 d'8")
    upper_measures[2].extend("c'8 d'16 e'16 f'8 e'8")
    upper_measures[3].append("d'2")
    upper_measures[4].append("d'2")
    lower_measures[0].extend("b4 d'8 c'8")
    lower_measures[1].extend("b8 a8 af4 c'8 bf8")
    lower_measures[2].extend("a8 g8 fs8 g16 a16")
    upper_voice = abjad.Voice("b2", name='upper voice')
    command = abjad.LilyPondCommand('voiceOne')
    abjad.attach(command, upper_voice)
    lower_voice = abjad.Voice("b4 a4", name='lower voice')
    command = abjad.LilyPondCommand('voiceTwo')
    abjad.attach(command, lower_voice)
    lower_measures[3].extend([upper_voice, lower_voice])
    lower_measures[3].is_simultaneous = True
    upper_voice = abjad.Voice("b2", name='upper voice')
    command = abjad.LilyPondCommand('voiceOne')
    abjad.attach(command, upper_voice)
    lower_voice = abjad.Voice("g2", name='lower voice')
    command = abjad.LilyPondCommand('voiceTwo')
    abjad.attach(command, lower_voice)
    lower_measures[4].extend([upper_voice, lower_voice])
    lower_measures[4].is_simultaneous = True
    clef = abjad.Clef('bass')
    leaf = abjad.inspect(lower_staff).get_leaf(0)
    abjad.attach(clef, leaf)
    dynamic = abjad.Dynamic('pp')
    abjad.attach(dynamic, upper_measures[0][0])
    dynamic = abjad.Dynamic('mp')
    abjad.attach(dynamic, upper_measures[1][1])
    dynamic = abjad.Dynamic('pp')
    abjad.attach(dynamic, lower_measures[0][1])
    dynamic = abjad.Dynamic('mp')
    abjad.attach(dynamic, lower_measures[1][3])
    score.add_final_bar_line()
    abjad.selector = abjad.select().leaves()
    upper_leaves = abjad.selector(upper_staff)
    lower_leaves = abjad.selector(lower_staff)
    beam = abjad.Beam()
    abjad.attach(beam, upper_leaves[:4])
    beam = abjad.Beam()
    abjad.attach(beam, lower_leaves[1:5])
    beam = abjad.Beam()
    abjad.attach(beam, lower_leaves[6:10])
    slur = abjad.Slur()
    abjad.attach(slur, upper_leaves[:5])
    slur = abjad.Slur()
    abjad.attach(slur, upper_leaves[5:])
    slur = abjad.Slur()
    abjad.attach(slur, lower_leaves[1:6])
    crescendo = abjad.Hairpin('<')
    abjad.attach(crescendo, upper_leaves[-7:-2])
    diminuendo = abjad.Hairpin('>')
    abjad.attach(diminuendo, upper_leaves[-2:])
    markup = abjad.Markup('ritard.')
    text_spanner = abjad.TextSpanner()
    abjad.override(text_spanner).text_spanner.bound_details__left__text = markup
    abjad.attach(text_spanner, upper_leaves[-7:])
    tie = abjad.Tie()
    abjad.attach(tie, upper_leaves[-2:])
    note_1 = lower_staff[-2]['upper voice'][0]
    note_2 = lower_staff[-1]['upper voice'][0]
    notes = abjad.select([note_1, note_2])
    abjad.attach(abjad.Tie(), notes)
    return score
예제 #18
0
    all_leaves.extend(current_leaves)
    current_duration += leaf_duration
    talea_index += 1

music = abjad.Container(all_leaves)

shards = abjad.mutate(music[:]).split(time_signature_pairs)
for i, shard in enumerate(shards):
    measure = abjad.Measure(time_signature_pairs[i])
    abjad.mutate(shard).wrap(measure)

# 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])
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

for run in abjad.select(music).leaves().runs():
    if not isinstance(run[0], (abjad.Note, abjad.Chord)):
        continue
    abjad.attach(abjad.Articulation('accent'), run[0])
    if 1 < len(run):
        abjad.attach(abjad.Hairpin('p < f'), run)
    else:
        abjad.attach(abjad.Dynamic('ppp'), run[0])
예제 #19
0
파일: bartok.py 프로젝트: gsy/gmajor
def make_bartok_score():
    """
    Build the Bartok example score.
    """

    # Build score skeleton
    score = abjad.Score()
    piano_staff = abjad.StaffGroup(lilypond_type='PianoStaff')
    upper_staff = abjad.Staff([])
    lower_staff = abjad.Staff([])
    piano_staff.append(upper_staff)
    piano_staff.append(lower_staff)
    score.append(piano_staff)

    # Build upper measures
    upper_measures = []
    upper_measures.append(abjad.Measure((2, 4), []))
    upper_measures.append(abjad.Measure((3, 4), []))
    upper_measures.append(abjad.Measure((2, 4), []))
    upper_measures.append(abjad.Measure((2, 4), []))
    upper_measures.append(abjad.Measure((2, 4), []))
    lower_measures = copy.deepcopy(upper_measures)
    upper_staff.extend(upper_measures)
    lower_staff.extend(lower_measures)

    # Add leaves to upper measures
    upper_measures[0].extend("a'8 g'8 f'8 e'8")
    upper_measures[1].extend("d'4 g'8 f'8 e'8 d'8")
    upper_measures[2].extend("c'8 d'16 e'16 f'8 e'8")
    upper_measures[3].append("d'2")
    upper_measures[4].append("d'2")

    # Add leaves to lower measures
    lower_measures[0].extend("b4 d'8 c'8")
    lower_measures[1].extend("b8 a8 af4 c'8 bf8")
    lower_measures[2].extend("a8 g8 fs8 g16 a16")

    # Build parallel music for measure 4
    upper_voice = abjad.Voice("b2", name='upper voice')
    command = abjad.LilyPondLiteral(r'\voiceOne')
    abjad.attach(command, upper_voice)
    lower_voice = abjad.Voice("b4 a4", name='lower voice')
    command = abjad.LilyPondLiteral(r'\voiceTwo')
    abjad.attach(command, lower_voice)
    lower_measures[3].extend([upper_voice, lower_voice])
    lower_measures[3].is_simultaneous = True

    # Build parallel music for measure 5
    upper_voice = abjad.Voice("b2", name='upper voice')
    command = abjad.LilyPondLiteral(r'\voiceOne')
    abjad.attach(command, upper_voice)
    lower_voice = abjad.Voice("g2", name='lower voice')
    command = abjad.LilyPondLiteral(r'\voiceTwo')
    abjad.attach(command, lower_voice)
    lower_measures[4].extend([upper_voice, lower_voice])
    lower_measures[4].is_simultaneous = True

    # Add bass clef
    clef = abjad.Clef('bass')
    leaf = abjad.inspect(lower_staff).leaf(0)
    abjad.attach(clef, leaf)

    # Add dynamics
    dynamic = abjad.Dynamic('pp')
    abjad.attach(dynamic, upper_measures[0][0])
    dynamic = abjad.Dynamic('mp')
    abjad.attach(dynamic, upper_measures[1][1])
    dynamic = abjad.Dynamic('pp')
    abjad.attach(dynamic, lower_measures[0][1])
    dynamic = abjad.Dynamic('mp')
    abjad.attach(dynamic, lower_measures[1][3])

    # Add final bar line
    score.add_final_bar_line()

    # Select leaves for attaching spanners to
    upper_leaves = abjad.select(upper_staff).leaves()
    lower_leaves = abjad.select(lower_staff).leaves()

    # Attach beams
    beam = abjad.Beam()
    abjad.attach(beam, upper_leaves[:4])
    beam = abjad.Beam()
    abjad.attach(beam, lower_leaves[1:5])
    beam = abjad.Beam()
    abjad.attach(beam, lower_leaves[6:10])

    # Attach slurs
    slur = abjad.Slur()
    abjad.attach(slur, upper_leaves[:5])
    slur = abjad.Slur()
    abjad.attach(slur, upper_leaves[5:])
    slur = abjad.Slur()
    abjad.attach(slur, lower_leaves[1:6])

    # Attach hairpins
    crescendo = abjad.Hairpin('<')
    abjad.attach(crescendo, upper_leaves[-7:-2])
    decrescendo = abjad.Hairpin('>')
    abjad.attach(decrescendo, upper_leaves[-2:])

    # Attach a ritardando with markup
    markup = abjad.Markup('ritard.')
    text_spanner = abjad.TextSpanner()
    abjad.tweak(text_spanner).bound_details__left__text = markup
    abjad.attach(text_spanner, upper_leaves[-7:])

    # Tie notes
    tie = abjad.Tie()
    abjad.attach(tie, upper_leaves[-2:])

    # Tie more notes
    note_1 = lower_staff[-2]['upper voice'][0]
    note_2 = lower_staff[-1]['upper voice'][0]
    notes = abjad.select([note_1, note_2])
    abjad.attach(abjad.Tie(), notes)

    # Return the score
    return score