Пример #1
0
def make_assignment_mode_example():
    # create a midi file on which to test the assignment modes in load_midi
    part_1 = score.Part('P1')
    part_2 = score.Part('P2')
    part_3 = score.Part('P3')

    part_1.set_quarter_duration(0, 1)
    part_2.set_quarter_duration(0, 2)
    part_3.set_quarter_duration(0, 3)

    part_1.add(score.TimeSignature(4, 4), 0)
    part_1.add(score.Note(step='C', octave=4, voice=1), 0, 1)
    part_1.add(score.Note(step='B', octave=4, voice=2), 0, 2)
    part_1.add(score.Note(step='B', octave=4, voice=2), 2, 4)
    part_1.add(score.Note(step='B', octave=4, voice=2), 5, 6)
    part_1.add(score.Note(step='B', octave=4, voice=3), 7, 10)

    part_2.add(score.TimeSignature(4, 4), 0)
    part_2.add(score.Tempo(80), 0)
    part_2.add(score.Note(step='D', octave=5, voice=1), 0, 1)
    part_2.add(score.Note(step='E', octave=5, voice=2), 1, 2)
    part_2.add(score.Note(step='F', octave=5, voice=2), 2, 3)
    part_2.add(score.Note(step='G', octave=5, voice=2), 3, 4)

    part_3.add(score.TimeSignature(4, 4), 0)
    part_3.add(score.Note(step='G', octave=4, voice=1), 0, 3)

    pg = score.PartGroup(group_name='P1/P2')
    pg.children = [part_1, part_2]
    for p in pg.children:
        p.parent = pg

    return [pg, part_3]
Пример #2
0
def make_part_tuplet():
    # create a part
    part = score.Part('My Part')

    # create contents
    divs = 12
    ts = score.TimeSignature(3, 4)
    page1 = score.Page(1)
    system1 = score.System(1)

    note1 = score.Note(id='n0', step='A', octave=4, voice=1, staff=1)
    rest1 = score.Rest(voice=1, staff=1)
    note2 = score.Note(id='n2', step='C', octave=4, voice=1, staff=1)
    rest2 = score.Rest(id='r0', voice=1, staff=1)
    
    # and add the contents to the part:
    part.set_quarter_duration(0, divs)
    part.add(ts, 0)
    part.add(page1, 0)
    part.add(system1, 0)
    part.add(note1, 0, 8)
    part.add(rest1, 8, 16)
    part.add(note2, 16, 24)
    part.add(rest2, 24, 36)

    score.add_measures(part)
    score.find_tuplets(part)
    score.set_end_times(part)

    return part
Пример #3
0
    def test_midi_export_anacrusis(self):
        part = score.Part("id")
        # 1 div is 1 quarter
        part.set_quarter_duration(0, 1)
        # 4/4 at t=0
        part.add(score.TimeSignature(4, 4), 0)

        # ANACRUSIS
        # quarter note from t=0 to t=1
        part.add(score.Note("c", 4), 0, 1)
        # incomplete measure from t=0 to t=1
        part.add(score.Measure(), 0, 1)

        # whole note from t=1 to t=5
        part.add(score.Note("c", 4), 1, 5)
        # add missing measures
        score.add_measures(part)

        # print(part.pretty())

        mid = export_and_read(part, anacrusis_behavior="shift")
        t = 0
        for msg in mid.tracks[0]:
            t += msg.time
            if msg.type == "note_on":
                assert t == 0
                break

        mid = export_and_read(part, anacrusis_behavior="pad_bar")
        t = 0
        for msg in mid.tracks[0]:
            t += msg.time
            if msg.type == "note_on":
                assert t == 3, f"Incorrect time of first note on: {t} (should be 3)"
                break
Пример #4
0
    def do_test(
        self,
        q_times,
        q_durs,
        note_ons,
        note_offs,
        new_q_time,
        new_q_dur,
        new_note_ons,
        new_note_offs,
    ):

        part = score.Part("P0")

        for q_time, q_dur in zip(q_times, q_durs):
            part.set_quarter_duration(q_time, q_dur)

        for i, (note_on, note_off) in enumerate(zip(note_ons, note_offs)):
            n = score.Note(id="n{}".format(i), step="C", octave=4, voice=1)
            part.add(n, note_on, note_off)

        for q_time, q_dur in zip(new_q_time[::-1], new_q_dur[::-1]):
            part.set_quarter_duration(q_time, q_dur, adjust_times=True)

        for n, start, end in zip(part.notes, new_note_ons, new_note_offs):
            msg = "Note onset {} should be {}".format(n.start.t, start)
            self.assertEqual(n.start.t, start, msg)
            msg = "Note offset {} should be {}".format(n.end.t, end)
            self.assertEqual(n.end.t, end, msg)
Пример #5
0
    def test_export_import_pprint(self):
        # create a part
        part1 = score.Part('My Part')

        # create contents
        divs = 10
        ts = score.TimeSignature(3, 4)
        page1 = score.Page(1)
        system1 = score.System(1)
        measure1 = score.Measure(number=1)
        note1 = score.Note(step='A', octave=4, voice=1, staff=1)
        rest1 = score.Rest(voice=1, staff=1)
        note2 = score.Note(step='C', octave=5, alter=-1, voice=2, staff=1)
        
        # and add the contents to the part:
        part1.set_quarter_duration(0, divs)
        part1.add(ts, 0)
        part1.add(measure1, 0, 30)
        part1.add(page1, 0)
        part1.add(system1, 0)
        part1.add(note1, 0, 15)
        part1.add(rest1, 15, 30)
        part1.add(note2, 0, 30)
        
        score.set_end_times(part1)
        
        # pretty print the part
        pstring1 = part1.pretty()

        with TemporaryFile() as f:
            # save part to musicxml
            save_musicxml(part1, f)
            f.flush()
            f.seek(0)
            # load part from musicxml
            part2 = load_musicxml(f)

        # pretty print saved/loaded part:
        pstring2 = part2.pretty()

        # test pretty printed strings for equality
        equal = pstring1 == pstring2

        if not equal:
            show_diff(pstring1, pstring2)
        msg = 'Exported and imported score does not yield identical pretty printed representations'
        self.assertTrue(equal, msg)
Пример #6
0
    def test_times_2(self):
        # 6/8 anacrusis
        part = score.Part("id")
        # 2 divs is 1 quarter
        part.set_quarter_duration(0, 2)
        part.add(score.TimeSignature(6, 8), 0)

        # ANACRUSIS
        part.add(score.Note("c", 4), 0, 3)
        part.add(score.Measure(), 0, 3)

        part.add(score.Note("c", 4), 3, 9)

        score.add_measures(part)

        time_pairs = [(-3, -1.5), (0, 0), (6, 3)]
        test_time_pairs(part, time_pairs)
Пример #7
0
    def test_notearray_1(self):
        part = score.Part("P0", "My Part")

        part.set_quarter_duration(0, 10)
        part.add(score.TimeSignature(3, 4), start=0)
        part.add(score.Note(id="n0", step="A", octave=4), start=0, end=10)

        note_array = part.note_array
        self.assertTrue(len(note_array) == 1)
Пример #8
0
    def test_times_1(self):
        # 4/4 anacrusis
        part = score.Part("id")
        # 1 div is 1 quarter
        part.set_quarter_duration(0, 1)
        # 4/4 at t=0
        part.add(score.TimeSignature(4, 4), 0)

        # ANACRUSIS
        # quarter note from t=0 to t=1
        part.add(score.Note("c", 4), 0, 1)
        # incomplete measure from t=0 to t=1
        part.add(score.Measure(), 0, 1)

        # whole note from t=1 to t=5
        part.add(score.Note("c", 4), 1, 5)
        # add missing measures
        score.add_measures(part)
        time_pairs = [(-1, -1), (0, 0), (4, 4)]
        test_time_pairs(part, time_pairs)
Пример #9
0
    def do_test(self, q_times, q_durs, note_ons, note_offs, factor,
                new_note_ons, new_note_offs):

        part = score.Part("P0")

        for q_time, q_dur in zip(q_times, q_durs):
            part.set_quarter_duration(q_time, q_dur)

        for i, (note_on, note_off) in enumerate(zip(note_ons, note_offs)):
            n = score.Note(id="n{}".format(i), step="C", octave=4, voice=1)
            part.add(n, note_on, note_off)

        part.multiply_quarter_durations(factor)

        for n, start, end in zip(part.notes, new_note_ons, new_note_offs):
            msg = "Note {} onset {} should be {}".format(
                n.id, n.start.t, start)
            self.assertEqual(n.start.t, start, msg)
            msg = "Note {} offset {} should be {}".format(n.id, n.end.t, end)
            self.assertEqual(n.end.t, end, msg)
Пример #10
0
def create_part_from_spec(spec):
    """Create a part from a specification of divisions, time signatures
    and notes.

    This is a helper function for the TestBeatMap test case.

    Parameters
    ----------
    spec : dictionary
        Part specification

    Returns
    -------
    Part
        Part instance
    
    """

    part = score.Part('beatmaptest')
    # for t, divs in spec['divs']:
    #     part.add(score.Divisions(divs), t)
    for t, divs in spec['divs']:
        part.set_quarter_duration(t, divs)

    for t, num, den in spec['ts']:
        part.add(score.TimeSignature(num, den), t)

    # divs_map = part.divisions_map

    for t, dur in spec['notes']:
        # sd = score.estimate_symbolic_duration(dur, int(divs_map(t)))
        part.add(score.Note(step='A', alter=None, octave=4), t, t + dur)

    score.add_measures(part)

    return part
Пример #11
0
def make_part_slur():
    # create a part
    part = score.Part('My Part')
    # create contents
    divs = 12
    ts = score.TimeSignature(3, 4)
    page1 = score.Page(1)
    system1 = score.System(1)

    note0 = score.Note(id='n0', step='A', octave=4, voice=1, staff=1)
    note1 = score.Note(id='n1', step='A', octave=4, voice=1, staff=1)
    note2 = score.Note(id='n2', step='A', octave=4, voice=1, staff=1)
    note3 = score.Note(id='n3', step='A', octave=4, voice=1, staff=1)

    note4 = score.Note(id='n4', step='A', octave=3, voice=2, staff=1)
    note5 = score.Note(id='n5', step='A', octave=3, voice=2, staff=1)

    slur1 = score.Slur(start_note=note0, end_note=note5)
    slur2 = score.Slur(start_note=note4, end_note=note3)
    
    # and add the contents to the part:
    part.set_quarter_duration(0, divs)
    part.add(ts, 0)
    part.add(page1, 0)
    part.add(system1, 0)
    part.add(note0, 0, 12)
    part.add(note1, 12, 24)
    part.add(note2, 24, 36)
    part.add(note3, 36, 48)
    part.add(note4, 0, 6)
    part.add(note5, 6, 33)
    part.add(slur1, 
                      slur1.start_note.start.t,
                      slur1.end_note.end.t)
    part.add(slur2, 
                      slur2.start_note.start.t,
                      slur2.end_note.end.t)

    score.add_measures(part)
    score.tie_notes(part)
    score.set_end_times(part)
    return part
Пример #12
0
def _handle_note(e, position, part, ongoing, prev_note):

    # prev_note is used when the current note has a <chord/> tag

    # get some common features of element if available
    duration = get_value_from_tag(e, 'duration', int) or 0
    # elements may have an explicit temporal offset
    # offset = get_value_from_tag(e, 'offset', int) or 0
    staff = get_value_from_tag(e, 'staff', int) or None
    voice = get_value_from_tag(e, 'voice', int) or None

    note_id = get_value_from_attribute(e, 'id', str)

    symbolic_duration = {}
    dur_type = get_value_from_tag(e, 'type', str)
    if dur_type:
        symbolic_duration['type'] = dur_type

    dots = len(e.findall('dot'))
    if dots:
        symbolic_duration['dots'] = dots

    actual_notes = get_value_from_tag(e, 'time-modification/actual-notes', int)
    if actual_notes:
        symbolic_duration['actual_notes'] = actual_notes

    normal_notes = get_value_from_tag(e, 'time-modification/normal-notes', int)
    if normal_notes:
        symbolic_duration['normal_notes'] = normal_notes

    chord = e.find('chord')
    if chord is not None:
        # this note starts at the same position as the previous note, and has
        # same duration
        assert prev_note is not None
        position = prev_note.start.t

    articulations_e = e.find('notations/articulations')
    if articulations_e is not None:
        articulations = get_articulations(articulations_e)
    else:
        articulations = {}

    pitch = e.find('pitch')
    if pitch is not None:

        step = get_value_from_tag(pitch, 'step', str)
        alter = get_value_from_tag(pitch, 'alter', int)
        octave = get_value_from_tag(pitch, 'octave', int)

        grace = e.find('grace')

        if grace is not None:
            grace_type, steal_proportion = get_grace_info(grace)
            note = score.GraceNote(grace_type, step, octave, alter,
                                   note_id, voice=voice, staff=staff,
                                   symbolic_duration=symbolic_duration,
                                   articulations=articulations,
                                   steal_proportion=steal_proportion)
            if (isinstance(prev_note, score.GraceNote)
                and prev_note.voice == voice):
                note.grace_prev = prev_note
        else:

            note = score.Note(step, octave, alter, note_id,
                              voice=voice, staff=staff,
                              symbolic_duration=symbolic_duration,
                              articulations=articulations)

        if (isinstance(prev_note, score.GraceNote)
            and prev_note.voice == voice):
            prev_note.grace_next = note
    else:
        # note element is a rest
        note = score.Rest(note_id, voice=voice, staff=staff,
                          symbolic_duration=symbolic_duration,
                          articulations=articulations)

    part.add(note, position, position+duration)

    ties = e.findall('tie')
    if len(ties) > 0:

        tie_key = ('tie', getattr(note, 'midi_pitch', 'rest'))
        tie_types = set(tie.attrib['type'] for tie in ties)

        if 'stop' in tie_types:

            tie_prev = ongoing.get(tie_key, None)

            if tie_prev:

                note.tie_prev = tie_prev
                tie_prev.tie_next = note
                del ongoing[tie_key]

        if 'start' in tie_types:

            ongoing[tie_key] = note

    notations = e.find('notations')

    if notations is not None:

        if notations.find('fermata') is not None:

            fermata = score.Fermata(note)
            part.add(fermata, position)
            note.fermata = fermata

        starting_slurs, stopping_slurs = handle_slurs(notations, ongoing, note, position)

        for slur in starting_slurs:

            part.add(slur, position)

        for slur in stopping_slurs:

            part.add(slur, end=position+duration)

        starting_tups, stopping_tups = handle_tuplets(notations, ongoing, note)

        for tup in starting_tups:

            part.add(tup, position)

        for tup in stopping_tups:

            part.add(tup, end=position+duration)

    new_position = position + duration

    return new_position, note
Пример #13
0
def create_part(ticks, notes, spellings, voices, note_ids, time_sigs, key_sigs, part_id=None, part_name=None):
    LOGGER.debug('create_part')

    part = score.Part(part_id, part_name=part_name)
    part.set_quarter_duration(0, ticks)

    clef = score.Clef(number=1, **estimate_clef_properties([pitch for _, pitch, _ in notes]))
    part.add(clef, 0)
    for t, name in key_sigs:
        fifths, mode = key_name_to_fifths_mode(name)
        part.add(score.KeySignature(fifths, mode), t)

    LOGGER.debug('add notes')

    for (onset, pitch, duration), (step, alter, octave), voice, note_id in zip(notes, spellings, voices, note_ids):
        if duration > 0:
            note = score.Note(step, octave, alter, voice=int(voice or 0), id=note_id,
                              symbolic_duration=estimate_symbolic_duration(duration, ticks))
        else:
            note = score.GraceNote('appoggiatura', step, octave, alter, voice=int(voice or 0), id=note_id,
                                   symbolic_duration=dict(type='quarter'))

        part.add(note, onset, onset+duration)

    if not time_sigs:
        warnings.warn('No time signatures found, assuming 4/4')
        time_sigs = [(0, 4, 4)]

    time_sigs = np.array(time_sigs, dtype=np.int)

    # for convenience we add the end times for each time signature
    ts_end_times = np.r_[time_sigs[1:, 0], np.iinfo(np.int).max]
    time_sigs = np.column_stack((time_sigs, ts_end_times))

    LOGGER.debug('add time sigs and measures')

    for ts_start, num, den, ts_end in time_sigs:
        time_sig = score.TimeSignature(num.item(), den.item())
        part.add(time_sig, ts_start.item())

    score.add_measures(part)

    # this is the old way to add measures. Since part comes from MIDI we
    # only have a single global divs value, which makes add it easier to compute
    # measure durations:
    
    # measure_counter = 1
    # # we call item() on numpy numbers to get the value in the equivalent python type
    # for ts_start, num, den, ts_end in time_sigs:
    #     time_sig = score.TimeSignature(num.item(), den.item())
    #     part.add(time_sig, ts_start.item())
    #     measure_duration = (num.item() * ticks * 4) // den.item()
    #     measure_start_limit = min(ts_end.item(), part.last_point.t)
    #     for m_start in range(ts_start, measure_start_limit, measure_duration):
    #         measure = score.Measure(number=measure_counter)
    #         m_end = min(m_start+measure_duration, ts_end)
    #         part.add(measure, m_start, m_end)
    #         measure_counter += 1
    #     if np.isinf(ts_end):
    #         ts_end = m_end

    LOGGER.debug('tie notes')
    # tie notes where necessary (across measure boundaries, and within measures
    # notes with compound duration)
    score.tie_notes(part)

    LOGGER.debug('find tuplets')
    # apply simplistic tuplet finding heuristic
    score.find_tuplets(part)

    LOGGER.debug('done create_part')
    return part
Пример #14
0
def part_from_matchfile(mf):
    part = score.Part('P1', mf.info('piece'))
    # snotes = sorted(mf.snotes, key=attrgetter('OnsetInBeats'))
    snotes = sort_snotes(mf.snotes)
    divs = np.lcm.reduce(np.unique([note.Offset.denominator * (note.Offset.tuple_div or 1)
                                    for note in snotes]))
    part.set_quarter_duration(0, divs)
    min_time = snotes[0].OnsetInBeats  # sorted by OnsetInBeats
    max_time = max(n.OffsetInBeats for n in snotes)

    ts = mf.time_signatures

    beats_map, beat_type_map, min_time_q, max_time_q = make_timesig_maps(ts, max_time)

    bars = np.unique([n.Bar for n in snotes])
    t = min_time
    t = t * 4 / beat_type_map(min_time_q)
    offset = t
    # bar map: bar_number-> start in quarters
    bar_times = {}
    for b0, b1 in iter_current_next(bars, start=0):

        bar_times.setdefault(b1, t)
        if t < 0:
            t = 0
        else:
            # multiply by diff between consecutive bar numbers
            n_bars = b1 - b0
            if t <= max_time_q:
                t += (n_bars * 4 * beats_map(t)) / beat_type_map(t)

    for note in snotes:
        # start of bar in quarter units
        bar_start = bar_times[note.Bar]

        # offset within bar in quarter units
        bar_offset = (note.Beat - 1) * 4 / beat_type_map(bar_start)
        # offset within beat in quarter units
        beat_offset = (4 * note.Offset.numerator
                       / (note.Offset.denominator * (note.Offset.tuple_div or 1)))

        # # anacrusis
        if bar_start < 0:
            # in case of anacrusis we set the bar_start to -bar_duration (in
            # quarters) so that the below calculation is correct
            bar_start = - beats_map(bar_start) * 4 / beat_type_map(bar_start)

        # note onset in divs
        onset_divs = int(divs * (bar_start + bar_offset + beat_offset - offset))
        # print(note.Anchor, onset_divs, bar_start, bar_offset, beat_offset, offset)

        articulations = set()
        if 'staccato' in note.ScoreAttributesList:
            articulations.add('staccato')
        if 'accent' in note.ScoreAttributesList:
            articulations.add('accent')

        # dictionary with keyword args with which the Note (or GraceNote) will be instantiated
        note_attributes = dict(step=note.NoteName,
                               octave=note.Octave,
                               alter=note.Modifier,
                               id=note.Anchor,
                               articulations=articulations)

        staff_nr = next((a[-1] for a in note.ScoreAttributesList if a.startswith('staff')), None)
        try:
            note_attributes['staff'] = int(staff_nr)
        except (TypeError, ValueError):
            # no staff attribute, or staff attribute does not end with a number
            note_attributes['staff'] = None

        note_attributes['voice'] = next((int(a) for a in note.ScoreAttributesList
                                         if NUMBER_PAT.match(a)), None)

        # get rid of this if as soon as we have a way to iterate over the
        # duration components. For now we have to treat the cases simple
        # and compound durations separately.

        if note.Duration.add_components:
            prev_part_note = None

            for i, (num, den, tuple_div) in enumerate(note.Duration.add_components):

                # when we add multiple notes that are tied, the first note will
                # get the original note id, and subsequent notes will get a
                # derived note id (by appending, 'a', 'b', 'c',...)
                if i > 0:
                    # tnote_id = 'n{}_{}'.format(note.Anchor, i)
                    note_attributes['id'] = score.make_tied_note_id(note_attributes['id'])

                part_note = score.Note(**note_attributes)

                duration_divs = int(divs * 4 * num / (den * (tuple_div or 1)))

                assert duration_divs > 0

                offset_divs = onset_divs + duration_divs

                part.add(part_note, onset_divs, offset_divs)

                if prev_part_note:
                    prev_part_note.tie_next = part_note
                    part_note.tie_prev = prev_part_note
                prev_part_note = part_note
                onset_divs = offset_divs

        else:

            num = note.Duration.numerator
            den = note.Duration.denominator
            tuple_div = note.Duration.tuple_div
            duration_divs = int(divs * 4 * num / (den * (tuple_div or 1)))

            offset_divs = onset_divs + duration_divs

            # notes with duration 0, are also treated as grace notes, even if
            # they do not have a 'grace' score attribute
            if ('grace' in note.ScoreAttributesList or
                    note.Duration.numerator == 0):

                part_note = score.GraceNote('appoggiatura', **note_attributes)

            else:

                part_note = score.Note(**note_attributes)

            part.add(part_note, onset_divs, offset_divs)

    # add time signatures
    for (ts_beat_time, ts_bar, (ts_beats, ts_beat_type)) in ts:

        bar_start_divs = int(divs * (bar_times[ts_bar] - offset))  # in quarters
        part.add(score.TimeSignature(ts_beats, ts_beat_type), bar_start_divs)

    # add key signatures
    for (ks_beat_time, ks_bar, keys) in mf.key_signatures:

        if len(keys) > 1:
            # there are multple equivalent keys, so we check which one is most
            # likely according to the key estimator
            est_keys = estimate_key(notes_to_notearray(part.notes_tied), return_sorted_keys=True)
            idx = [est_keys.index(key) if key in est_keys else np.inf
                   for key in keys]
            key_name = keys[np.argmin(idx)]

        else:

            key_name = keys[0]

        fifths, mode = key_name_to_fifths_mode(key_name)
        part.add(score.KeySignature(fifths, mode), 0)

    add_staffs(part)
    # add_clefs(part)

    # add incomplete measure if necessary

    if offset < 0:

        part.add(score.Measure(number=1), 0, int(-offset * divs))

    # add the rest of the measures automatically
    score.add_measures(part)
    # print(part.pretty())
    score.tie_notes(part)
    score.find_tuplets(part)

    if not all([n.voice for n in part.notes_tied]):
        # print('notes without voice detected')
        add_voices(part)

    return part
Пример #15
0
def _handle_note(e, position, part, ongoing, prev_note, doc_order):

    # get some common features of element if available
    duration = get_value_from_tag(e, "duration", int) or 0
    # elements may have an explicit temporal offset
    # offset = get_value_from_tag(e, 'offset', int) or 0
    staff = get_value_from_tag(e, "staff", int) or None
    voice = get_value_from_tag(e, "voice", int) or None

    # add support of uppercase "ID" tags
    note_id = (get_value_from_attribute(e, "id", str)
               if get_value_from_attribute(e, "id", str) else
               get_value_from_attribute(e, "ID", str))

    symbolic_duration = {}
    dur_type = get_value_from_tag(e, "type", str)
    if dur_type:
        symbolic_duration["type"] = dur_type

    dots = len(e.findall("dot"))
    if dots:
        symbolic_duration["dots"] = dots

    actual_notes = get_value_from_tag(e, "time-modification/actual-notes", int)
    if actual_notes:
        symbolic_duration["actual_notes"] = actual_notes

    normal_notes = get_value_from_tag(e, "time-modification/normal-notes", int)
    if normal_notes:
        symbolic_duration["normal_notes"] = normal_notes

    chord = e.find("chord")
    if chord is not None:
        # this note starts at the same position as the previous note, and has
        # same duration
        assert prev_note is not None
        position = prev_note.start.t

    articulations_e = e.find("notations/articulations")
    if articulations_e is not None:
        articulations = get_articulations(articulations_e)
    else:
        articulations = {}

    pitch = e.find("pitch")
    if pitch is not None:

        step = get_value_from_tag(pitch, "step", str)
        alter = get_value_from_tag(pitch, "alter", int)
        octave = get_value_from_tag(pitch, "octave", int)

        grace = e.find("grace")

        if grace is not None:
            grace_type, steal_proportion = get_grace_info(grace)
            note = score.GraceNote(
                grace_type=grace_type,
                step=step,
                octave=octave,
                alter=alter,
                id=note_id,
                voice=voice,
                staff=staff,
                symbolic_duration=symbolic_duration,
                articulations=articulations,
                steal_proportion=steal_proportion,
                doc_order=doc_order,
            )
            if isinstance(prev_note,
                          score.GraceNote) and prev_note.voice == voice:
                note.grace_prev = prev_note
        else:
            note = score.Note(
                step=step,
                octave=octave,
                alter=alter,
                id=note_id,
                voice=voice,
                staff=staff,
                symbolic_duration=symbolic_duration,
                articulations=articulations,
                doc_order=doc_order,
            )

        if isinstance(prev_note, score.GraceNote) and prev_note.voice == voice:
            prev_note.grace_next = note
    else:
        # note element is a rest
        note = score.Rest(
            id=note_id,
            voice=voice,
            staff=staff,
            symbolic_duration=symbolic_duration,
            articulations=articulations,
            doc_order=doc_order,
        )

    part.add(note, position, position + duration)

    ties = e.findall("tie")
    if len(ties) > 0:

        tie_key = ("tie", getattr(note, "midi_pitch", "rest"))
        tie_types = set(tie.attrib["type"] for tie in ties)

        if "stop" in tie_types:

            tie_prev = ongoing.get(tie_key, None)

            if tie_prev:

                note.tie_prev = tie_prev
                tie_prev.tie_next = note
                del ongoing[tie_key]

        if "start" in tie_types:

            ongoing[tie_key] = note

    notations = e.find("notations")

    if notations is not None:

        if notations.find("fermata") is not None:

            fermata = score.Fermata(note)
            part.add(fermata, position)
            note.fermata = fermata

        starting_slurs, stopping_slurs = handle_slurs(notations, ongoing, note,
                                                      position)

        for slur in starting_slurs:

            part.add(slur, position)

        for slur in stopping_slurs:

            part.add(slur, end=position + duration)

        starting_tups, stopping_tups = handle_tuplets(notations, ongoing, note)

        for tup in starting_tups:

            part.add(tup, position)

        for tup in stopping_tups:

            part.add(tup, end=position + duration)

    new_position = position + duration

    return new_position, note