コード例 #1
0
    def processContigRange(self, ema_range):
        """ Process a contigous range of measures give an MEI doc and an
            EmaExpression.EmaMeasureRange object """

        # get all the spanners for total extension of meaure selection
        # (including gap measures, if present)
        # NB: Doing this for every range may be inefficient for larger files
        spanners = self.getMultiMeasureSpanners(ema_range.measures[0].idx - 1,
                                                ema_range.measures[-1].idx - 1)

        # Let's start with measures
        for i, ema_m in enumerate(ema_range.measures):

            is_first_m = i == 0
            is_last_m = i == len(ema_range.measures) - 1

            # Get requested measure
            measure = self.measures[ema_m.idx - 1]
            events = measure.getChildren()

            # determine current measure beat info
            meter = None
            for change in self.timeChanges:
                if int(change) + 1 <= ema_m.idx:
                    meter = self.beatsInfo[change]

            # Get list of staff numbers in current measure
            sds = [
                int(sd.getAttribute("n").getValue())
                for sd in measure.getClosestStaffDefs()
            ]

            # Set aside selected staff numbers
            s_nos = []

            # Proceed to locate requested staves
            for ema_s in ema_m.staves:
                if ema_s.number not in sds:
                    # CAREFUL: there may be issues with "silent" staves
                    # that may be defined by missing from current measure.
                    # TODO: Write test, fix.
                    raise BadApiRequest("Requested staff is not defined")
                s_nos.append(ema_s.number)

            for s_i, staff in enumerate(measure.getChildrenByName("staff")):
                s_no = s_i
                if staff.hasAttribute("n"):
                    s_no = int(staff.getAttribute("n").getValue())

                if s_no in s_nos:
                    ema_s = ema_m.staves[s_nos.index(s_no)]

                    # Get other elements affecting the staff, e.g. slurs
                    around = []

                    for el in list(events):
                        if self._isInStaff(el, s_no):
                            around.append(el)

                    # Create sets of elements marked for selection, removal and
                    # cutting. Elements are not removed or cut immediately to make
                    # sure that beat calcualtions are accurate.
                    marked_as_selected = MeiElementSet()
                    marked_as_space = MeiElementSet()
                    marked_for_removal = MeiElementSet()
                    marked_for_cutting = MeiElementSet()

                    # Now locate the requested beat ranges within the staff
                    for b_i, ema_beat_range in enumerate(ema_s.beat_ranges):

                        is_first_b = i == 0
                        is_last_b = i == len(ema_s.beat_ranges) - 1

                        def _remove(el):
                            """ Determine whether a removed element needs to
                            be converted to a space or removed altogether"""
                            if is_last_b and is_last_m:
                                marked_for_removal.add(el)
                            else:
                                marked_as_space.add(el)

                        # shorten them names
                        tstamp_first = ema_beat_range.tstamp_first
                        tstamp_final = ema_beat_range.tstamp_final
                        co = self.ema_exp.completenessOptions

                        # check that the requested beats actually fit in the meter
                        if tstamp_first > int(meter["count"])+1 or \
                           tstamp_final > int(meter["count"])+1:
                            raise BadApiRequest(
                                "Request beat is out of measure bounds")

                        # Find all descendants with att.duration.musical (@dur)
                        for layer in staff.getDescendantsByName("layer"):
                            cur_beat = 1.0
                            is_first_match = True

                            for el in layer.getDescendants():

                                if el.hasAttribute(
                                        "dur"
                                ) and not el.hasAttribute("grace"):
                                    dur = self._calculateDur(el, meter)
                                    # TODO still problems with non-consecutive beat ranges
                                    # e.g. @1@3
                                    # exclude descendants at and in between tstamps
                                    if cur_beat >= tstamp_first:
                                        # We round to 4 decimal places to avoid issues caused by
                                        # tuplet-related calculations, which are admittedly not
                                        # well expressed in floating numbers.
                                        if round(cur_beat, 4) <= round(
                                                tstamp_final, 4):
                                            marked_as_selected.add(el)
                                            if is_first_match and "cut" in co:
                                                marked_for_cutting.add(el)
                                                is_first_match = False

                                            # discard from removal set if it had
                                            # been placed there from other beat
                                            # range
                                            marked_as_space.discard(el)

                                            # Cut the duration of the last element
                                            # if completeness = cut
                                            needs_cut = cur_beat + dur > tstamp_final + 1
                                            if needs_cut and "cut" in co:
                                                marked_for_cutting.add(el)
                                                is_first_match = False
                                        elif not marked_as_selected.get(el):
                                            _remove(el)
                                    elif not marked_as_selected.get(el):
                                        marked_as_space.add(el)

                                    # continue
                                    cur_beat += dur

                        # select elements affecting the staff occurring
                        # within beat range
                        for event in around:
                            if not marked_as_selected.get(event):
                                if event.hasAttribute("tstamp"):
                                    ts = float(
                                        event.getAttribute(
                                            "tstamp").getValue())
                                    if ts < 1 and "cut" not in self.ema_exp.completenessOptions:
                                        ts = 1
                                    ts2_att = None
                                    if event.hasAttribute("tstamp2"):
                                        ts2_att = event.getAttribute("tstamp2")
                                    if ts > tstamp_final or (
                                            not ts2_att and ts < tstamp_first):
                                        marked_for_removal.add(event)
                                    elif ts2_att:
                                        ts2 = ts2_att.getValue()
                                        if "+" not in ts2:
                                            if ts2 < tstamp_first:
                                                marked_for_removal.add(event)
                                            elif ts2 == tstamp_final:
                                                marked_as_selected.add(event)
                                                marked_for_removal.discard(
                                                    event)
                                            if ts < tstamp_first and ts2 >= tstamp_final:
                                                marked_as_selected.add(event)
                                                marked_for_removal.discard(
                                                    event)
                                            else:
                                                marked_for_removal.add(event)
                                        else:
                                            marked_as_selected.add(event)
                                    else:
                                        marked_as_selected.add(event)

                                elif event.hasAttribute("startid"):
                                    startid = (event.getAttribute(
                                        "startid").getValue().replace("#", ""))
                                    target = self.doc.getElementById(startid)
                                    if not target:
                                        msg = """Unsupported Encoding: attribute
                                        startid on element {0} does not point to any
                                        element in the document.""".format(
                                            event.getName())
                                        raise UnsupportedEncoding(
                                            re.sub(r'\s+', ' ', msg.strip()))
                                    # Make sure the target event is in the same measure
                                    event_m = event.getAncestor(
                                        "measure").getId()
                                    target_m = target.getAncestor(
                                        "measure").getId()
                                    if not event_m == target_m:
                                        msg = """Unsupported Encoding: attribute
                                        startid on element {0} does not point to an
                                        element in the same measure.""".format(
                                            event.getName())
                                        raise UnsupportedEncoding(
                                            re.sub(r'\s+', ' ', msg.strip()))
                                    else:
                                        if marked_as_selected.get(target):
                                            marked_as_selected.add(event)
                                            marked_for_removal.discard(event)
                                        elif not event.hasAttribute("endid"):
                                            marked_for_removal.add(event)
                                        else:
                                            # Skip if event starts after latest
                                            # selected element with duration
                                            pos = target.getPositionInDocument(
                                            )
                                            is_ahead = False
                                            for i in reversed(
                                                    marked_as_selected.
                                                    getElements()):
                                                if i.hasAttribute("dur"):
                                                    if pos > i.getPositionInDocument(
                                                    ):
                                                        marked_for_removal.add(
                                                            event)
                                                        is_ahead = True
                                                    break

                                            if not is_ahead:
                                                # last chance to keep it:
                                                # must start before and end after
                                                # latest selected element with duration

                                                endid = (
                                                    event.getAttribute("endid")
                                                    .getValue().replace(
                                                        "#", ""))
                                                target2 = self.doc.getElementById(
                                                    endid)
                                                if marked_as_selected.get(
                                                        target2):
                                                    marked_as_selected.add(
                                                        event)
                                                    marked_for_removal.discard(
                                                        event)
                                                else:
                                                    pos2 = target2.getPositionInDocument(
                                                    )
                                                    for i in reversed(
                                                            marked_as_selected.
                                                            getElements()):
                                                        if i.hasAttribute(
                                                                "dur"):
                                                            if pos2 > i.getPositionInDocument(
                                                            ):
                                                                marked_as_selected.add(
                                                                    event)
                                                                marked_for_removal.discard(
                                                                    event)
                                                            else:
                                                                marked_for_removal.add(
                                                                    event)
                                                            break

                    # Remove elements marked for removal
                    if "highlight" not in self.ema_exp.completenessOptions:
                        for el in marked_for_removal:
                            el.getParent().removeChild(el)

                        # Replace elements marked as spaces with actual spaces,
                        # unless completion = nospace, then remove the elements.
                        for el in marked_as_space:
                            parent = el.getParent()
                            if "nospace" not in self.ema_exp.completenessOptions:
                                space = MeiElement("space")
                                space.setId(el.id)
                                space.addAttribute(el.getAttribute("dur"))
                                if el.getAttribute("dots"):
                                    space.addAttribute(el.getAttribute("dots"))
                                elif el.getChildrenByName("dot"):
                                    dots = str(len(
                                        el.getChildrenByName("dot")))
                                    space.addAttribute(
                                        MeiAttribute("dots", dots))
                                parent.addChildBefore(el, space)
                            el.getParent().removeChild(el)
                    else:
                        for el in marked_as_selected:
                            # add to list
                            if self.highlight_el:
                                cur_plist = self.highlight_el.getAttribute(
                                    "plist").getValue()
                                space = ""
                                if len(cur_plist) > 0:
                                    space = " "
                                val = cur_plist + space + "#" + el.getId()
                                self.highlight_el.addAttribute("plist", val)

                else:
                    if "highlight" not in self.ema_exp.completenessOptions:
                        # Remove this staff and its attached events
                        staff.getParent().removeChild(staff)

                        for el in list(events):
                            if self._isInStaff(el, s_no):
                                el.getParent().removeChild(el)

            # At the first measure, also add relevant multi-measure spanners
            # for each selected staff
            if is_first_m:
                if "highlight" not in self.ema_exp.completenessOptions:
                    for evs in spanners.values():
                        for event_id in evs:
                            ev = self.doc.getElementById(event_id)
                            # Spanners starting outside of beat ranges
                            # may be already gone
                            if ev:
                                # Determine staff of event for id changes
                                for ema_s in ema_m.staves:
                                    staff_no = self._isInStaff(
                                        ev, ema_s.number)

                                if staff_no and staff_no in s_nos:
                                    # If the event is attached to more than one staff, just
                                    # consider it attached to the its first one
                                    staff_no = staff_no[0]

                                    staff = None
                                    for staff_candidate in measure.getDescendantsByName(
                                            "staff"):
                                        if staff_candidate.hasAttribute("n"):
                                            n = int(
                                                staff_candidate.getAttribute(
                                                    "n").getValue())
                                            if n == staff_no:
                                                staff = staff_candidate

                                    # Truncate event to start at the beginning of the beat range
                                    if ev.hasAttribute("startid"):
                                        # Set startid to the first event still on staff,
                                        # at the first available layer
                                        try:
                                            layer = (staff.getChildrenByName(
                                                "layer"))
                                            first_id = layer[0].getChildren(
                                            )[0].getId()
                                            ev.getAttribute(
                                                "startid").setValue("#" +
                                                                    first_id)
                                        except IndexError:
                                            msg = """
                                                Unsupported encoding. Omas attempted to adjust the
                                                starting point of a selected multi-measure element
                                                that starts before the selection, but the staff or
                                                layer could not be located.
                                                """
                                            msg = re.sub(
                                                r'\s+', ' ', msg.strip())
                                            raise UnsupportedEncoding(msg)

                                    if ev.hasAttribute("tstamp"):
                                        # Set tstamp to first in beat selection
                                        tstamp_first = 0
                                        for e_s in ema_m.staves:
                                            if e_s.number == staff_no:
                                                tstamp_first = e_s.beat_ranges[
                                                    0].tstamp_first
                                        ev.getAttribute("tstamp").setValue(
                                            str(tstamp_first))

                                    # Truncate to end of range if completeness = cut
                                    # (actual beat cutting will happen when beat ranges are procesed)
                                    if "cut" in self.ema_exp.completenessOptions:
                                        if ev.hasAttribute("tstamp2"):
                                            att = ev.getAttribute("tstamp2")
                                            t2 = att.getValue()
                                            p = re.compile(r"([1-9]+)(?=m\+)")
                                            multimeasure = p.match(t2)
                                            if multimeasure:
                                                new_val = len(mm) - 1
                                                att.setValue(
                                                    p.sub(str(new_val), t2))

                                    # Otherwise adjust tspan2 value to correct distance.
                                    # E.g. given 4 measures with a spanner originating
                                    # in 1 and ending in 4 and a selection of measures 2 and 3,
                                    # change @tspan2 from 3m+X to 2m+X
                                    else:
                                        if ev.hasAttribute("tstamp2"):
                                            att = ev.getAttribute("tstamp2")
                                            t2 = att.getValue()
                                            p = re.compile(r"([1-9]+)(?=m\+)")
                                            multimeasure = p.match(t2)
                                            if multimeasure:
                                                dis = evs[event_id]["distance"]
                                                new_val = int(
                                                    multimeasure.group(
                                                        1)) - dis
                                                att.setValue(
                                                    p.sub(str(new_val), t2))

                                    # move element to first measure and add it to selected
                                    # events "around" the staff.
                                    ev.moveTo(measure)
                else:
                    # Add spanners to annotated selection
                    for evs in spanners.values():
                        for event_id in evs:
                            if self.highlight_el:
                                cur_plist = self.highlight_el.getAttribute(
                                    "plist").getValue()
                                space = ""
                                if len(cur_plist) > 0:
                                    space = " "
                                val = cur_plist + space + "#" + event_id
                                self.highlight_el.addAttribute("plist", val)

        return self.doc
コード例 #2
0
ファイル: meislicer.py プロジェクト: edsu/ema
    def processContigRange(self, ema_range):
        """ Process a contigous range of measures give an MEI doc and an
            EmaExpression.EmaMeasureRange object """

        # get all the spanners for total extension of meaure selection
        # (including gap measures, if present)
        # NB: Doing this for every range may be inefficient for larger files
        spanners = self.getMultiMeasureSpanners(ema_range.measures[0].idx-1,
                                                ema_range.measures[-1].idx-1)

        # Let's start with measures
        for i, ema_m in enumerate(ema_range.measures):

            is_first_m = i == 0
            is_last_m = i == len(ema_range.measures)-1

            # Get requested measure
            measure = self.measures[ema_m.idx-1]
            events = measure.getChildren()

            # determine current measure beat info
            meter = None
            for change in self.timeChanges:
                if int(change)+1 <= ema_m.idx:
                    meter = self.beatsInfo[change]

            # Get list of staff numbers in current measure
            sds = [int(sd.getAttribute("n").getValue())
                   for sd in measure.getClosestStaffDefs()]

            # Set aside selected staff numbers
            s_nos = []

            # Proceed to locate requested staves
            for ema_s in ema_m.staves:
                if ema_s.number not in sds:
                    # CAREFUL: there may be issues with "silent" staves
                    # that may be defined by missing from current measure.
                    # TODO: Write test, fix.
                    raise BadApiRequest("Requested staff is not defined")
                s_nos.append(ema_s.number)

            for s_i, staff in enumerate(measure.getChildrenByName("staff")):
                s_no = s_i
                if staff.hasAttribute("n"):
                    s_no = int(staff.getAttribute("n").getValue())

                if s_no in s_nos:
                    ema_s = ema_m.staves[s_nos.index(s_no)]

                    # Get other elements affecting the staff, e.g. slurs
                    around = []

                    for el in list(events):
                        if self._isInStaff(el, s_no):
                            around.append(el)

                    # Create sets of elements marked for selection, removal and
                    # cutting. Elements are not removed or cut immediately to make
                    # sure that beat calcualtions are accurate.
                    marked_as_selected = MeiElementSet()
                    marked_as_space = MeiElementSet()
                    marked_for_removal = MeiElementSet()
                    marked_for_cutting = MeiElementSet()

                    # Now locate the requested beat ranges within the staff
                    for b_i, ema_beat_range in enumerate(ema_s.beat_ranges):

                        is_first_b = i == 0
                        is_last_b = i == len(ema_s.beat_ranges)-1

                        def _remove(el):
                            """ Determine whether a removed element needs to
                            be converted to a space or removed altogether"""
                            if is_last_b and is_last_m:
                                marked_for_removal.add(el)
                            else:
                                marked_as_space.add(el)

                        # shorten them names
                        tstamp_first = ema_beat_range.tstamp_first
                        tstamp_final = ema_beat_range.tstamp_final
                        co = self.ema_exp.completenessOptions

                        # check that the requested beats actually fit in the meter
                        if tstamp_first > int(meter["count"])+1 or \
                           tstamp_final > int(meter["count"])+1:
                            raise BadApiRequest(
                                "Request beat is out of measure bounds")

                        # Find all descendants with att.duration.musical (@dur)
                        for layer in staff.getDescendantsByName("layer"):
                            cur_beat = 1.0
                            is_first_match = True

                            for el in layer.getDescendants():

                                if el.hasAttribute("dur") and not el.hasAttribute("grace"):
                                    dur = self._calculateDur(el, meter)
                                    # TODO still problems with non-consecutive beat ranges
                                    # e.g. @1@3
                                    # exclude descendants at and in between tstamps
                                    if cur_beat >= tstamp_first:
                                        # We round to 4 decimal places to avoid issues caused by
                                        # tuplet-related calculations, which are admittedly not
                                        # well expressed in floating numbers.
                                        if round(cur_beat, 4) <= round(tstamp_final, 4):
                                            marked_as_selected.add(el)
                                            if is_first_match and "cut" in co:
                                                marked_for_cutting.add(el)
                                                is_first_match = False

                                            # discard from removal set if it had
                                            # been placed there from other beat
                                            # range
                                            marked_as_space.discard(el)

                                            # Cut the duration of the last element
                                            # if completeness = cut
                                            needs_cut = cur_beat+dur > tstamp_final+1
                                            if needs_cut and "cut" in co:
                                                marked_for_cutting.add(el)
                                                is_first_match = False
                                        elif not marked_as_selected.get(el):
                                            _remove(el)
                                    elif not marked_as_selected.get(el):
                                        marked_as_space.add(el)

                                    # continue
                                    cur_beat += dur

                        # select elements affecting the staff occurring
                        # within beat range
                        for event in around:
                            if not marked_as_selected.get(event):
                                if event.hasAttribute("tstamp"):
                                    ts = float(event.getAttribute("tstamp").getValue())
                                    if ts < 1 and "cut" not in self.ema_exp.completenessOptions:
                                        ts = 1
                                    ts2_att = None
                                    if event.hasAttribute("tstamp2"):
                                        ts2_att = event.getAttribute("tstamp2")
                                    if ts > tstamp_final or (not ts2_att and ts < tstamp_first):
                                        marked_for_removal.add(event)
                                    elif ts2_att:
                                        ts2 = ts2_att.getValue()
                                        if "+" not in ts2:
                                            if ts2 < tstamp_first:
                                                marked_for_removal.add(event)
                                            elif ts2 == tstamp_final:
                                                marked_as_selected.add(event)
                                                marked_for_removal.discard(event)
                                            if ts < tstamp_first and ts2 >= tstamp_final:
                                                marked_as_selected.add(event)
                                                marked_for_removal.discard(event)
                                            else:
                                                marked_for_removal.add(event)
                                        else:
                                            marked_as_selected.add(event)
                                    else:
                                        marked_as_selected.add(event)

                                elif event.hasAttribute("startid"):
                                    startid = (
                                        event.getAttribute("startid")
                                        .getValue()
                                        .replace("#", "")
                                    )
                                    target = self.doc.getElementById(startid)
                                    if not target:
                                        msg = """Unsupported Encoding: attribute
                                        startid on element {0} does not point to any
                                        element in the document.""".format(
                                            event.getName())
                                        raise UnsupportedEncoding(
                                            re.sub(r'\s+', ' ', msg.strip()))
                                    # Make sure the target event is in the same measure
                                    event_m = event.getAncestor("measure").getId()
                                    target_m = target.getAncestor("measure").getId()
                                    if not event_m == target_m:
                                        msg = """Unsupported Encoding: attribute
                                        startid on element {0} does not point to an
                                        element in the same measure.""".format(
                                            event.getName())
                                        raise UnsupportedEncoding(
                                            re.sub(r'\s+', ' ', msg.strip()))
                                    else:
                                        if marked_as_selected.get(target):
                                            marked_as_selected.add(event)
                                            marked_for_removal.discard(event)
                                        elif not event.hasAttribute("endid"):
                                            marked_for_removal.add(event)
                                        else:
                                            # Skip if event starts after latest
                                            # selected element with duration
                                            pos = target.getPositionInDocument()
                                            is_ahead = False
                                            for i in reversed(marked_as_selected.getElements()):
                                                if i.hasAttribute("dur"):
                                                    if pos > i.getPositionInDocument():
                                                        marked_for_removal.add(event)
                                                        is_ahead = True
                                                    break

                                            if not is_ahead:
                                                # last chance to keep it:
                                                # must start before and end after
                                                # latest selected element with duration

                                                endid = (
                                                    event.getAttribute("endid")
                                                    .getValue()
                                                    .replace("#", "")
                                                )
                                                target2 = self.doc.getElementById(endid)
                                                if marked_as_selected.get(target2):
                                                    marked_as_selected.add(event)
                                                    marked_for_removal.discard(event)
                                                else:
                                                    pos2 = target2.getPositionInDocument()
                                                    for i in reversed(marked_as_selected.getElements()):
                                                        if i.hasAttribute("dur"):
                                                            if pos2 > i.getPositionInDocument():
                                                                marked_as_selected.add(event)
                                                                marked_for_removal.discard(event)
                                                            else:
                                                                marked_for_removal.add(event)
                                                            break

                    # Remove elements marked for removal
                    for el in marked_for_removal:
                        el.getParent().removeChild(el)

                    # Replace elements marked as spaces with actual spaces,
                    # unless completion = nospace, then remove the elements.
                    for el in marked_as_space:
                        parent = el.getParent()
                        if "nospace" not in self.ema_exp.completenessOptions:
                            space = MeiElement("space")
                            space.setId(el.id)
                            space.addAttribute(el.getAttribute("dur"))
                            if el.getAttribute("dots"):
                                space.addAttribute(el.getAttribute("dots"))
                            elif el.getChildrenByName("dot"):
                                dots = str(len(el.getChildrenByName("dot")))
                                space.addAttribute(MeiAttribute("dots", dots))
                            parent.addChildBefore(el, space)
                        el.getParent().removeChild(el)

                else:
                    # Remove this staff and its attached events
                    staff.getParent().removeChild(staff)

                    for el in list(events):
                        if self._isInStaff(el, s_no):
                            el.getParent().removeChild(el)

            # At the first measure, also add relevant multi-measure spanners
            # for each selected staff
            if is_first_m:
                for evs in spanners.values():
                    for event_id in evs:
                        ev = self.doc.getElementById(event_id)
                        # Spanners starting outside of beat ranges
                        # may be already gone
                        if ev:
                            # Determine staff of event for id changes
                            for ema_s in ema_m.staves:
                                staff_no = self._isInStaff(ev, ema_s.number)

                            if staff_no and staff_no in s_nos:
                                # If the event is attached to more than one staff, just
                                # consider it attached to the its first one
                                staff_no = staff_no[0]

                                staff = None
                                for staff_candidate in measure.getDescendantsByName("staff"):
                                    if staff_candidate.hasAttribute("n"):
                                        n = int(staff_candidate.getAttribute("n").getValue())
                                        if n == staff_no:
                                            staff = staff_candidate

                                # Truncate event to start at the beginning of the beat range
                                if ev.hasAttribute("startid"):
                                    # Set startid to the first event still on staff,
                                    # at the first available layer
                                    try:
                                        layer = (
                                            staff.getChildrenByName("layer")
                                        )
                                        first_id = layer[0].getChildren()[0].getId()
                                        ev.getAttribute("startid").setValue("#"+first_id)
                                    except IndexError:
                                        msg = """
                                            Unsupported encoding. Omas attempted to adjust the
                                            starting point of a selected multi-measure element
                                            that starts before the selection, but the staff or
                                            layer could not be located.
                                            """
                                        msg = re.sub(r'\s+', ' ', msg.strip())
                                        raise UnsupportedEncoding(msg)

                                if ev.hasAttribute("tstamp"):
                                    # Set tstamp to first in beat selection
                                    tstamp_first = 0
                                    for e_s in ema_m.staves:
                                        if e_s.number == staff_no:
                                            tstamp_first = e_s.beat_ranges[0].tstamp_first
                                    ev.getAttribute("tstamp").setValue(str(tstamp_first))

                                # Truncate to end of range if completeness = cut
                                # (actual beat cutting will happen when beat ranges are procesed)
                                if "cut" in self.ema_exp.completenessOptions:
                                    if ev.hasAttribute("tstamp2"):
                                        att = ev.getAttribute("tstamp2")
                                        t2 = att.getValue()
                                        p = re.compile(r"([1-9]+)(?=m\+)")
                                        multimeasure = p.match(t2)
                                        if multimeasure:
                                            new_val = len(mm) - 1
                                            att.setValue(p.sub(str(new_val), t2))

                                # Otherwise adjust tspan2 value to correct distance.
                                # E.g. given 4 measures with a spanner originating
                                # in 1 and ending in 4 and a selection of measures 2 and 3,
                                # change @tspan2 from 3m+X to 2m+X
                                else:
                                    if ev.hasAttribute("tstamp2"):
                                        att = ev.getAttribute("tstamp2")
                                        t2 = att.getValue()
                                        p = re.compile(r"([1-9]+)(?=m\+)")
                                        multimeasure = p.match(t2)
                                        if multimeasure:
                                            dis = evs[event_id]["distance"]
                                            new_val = int(multimeasure.group(1)) - dis
                                            att.setValue(p.sub(str(new_val), t2))

                                # move element to first measure and add it to selected
                                # events "around" the staff.
                                ev.moveTo(measure)

        return self.doc