Пример #1
0
    def test_elementbyid(self):
        mei = MeiElement("mei")
        mus = MeiElement("music")
        body = MeiElement("body")
        staff = MeiElement("staff")
        staff2 = MeiElement("staff")
        n1 = MeiElement("note")
        wantedId = n1.id
        n2 = MeiElement("note")
        n3 = MeiElement("note")
        n4 = MeiElement("note")

        mei.addChild(mus)
        mus.addChild(body)
        body.addChild(staff)
        body.addChild(staff2)
        staff.addChild(n1)
        staff.addChild(n2)
        staff.addChild(n3)
        staff2.addChild(n4)

        doc = MeiDocument()
        self.assertEqual(None, doc.getElementById(wantedId))

        doc.root = mei

        self.assertEqual(n1, doc.getElementById(wantedId))

        self.assertEqual(None, doc.getElementById("unknownID"))

        n5 = MeiElement("note")
        newid = n5.id
        staff2.addChild(n5)

        self.assertEqual(n5, doc.getElementById(newid))
        staff2.removeChild(n5)
        self.assertEqual(None, doc.getElementById(newid))
Пример #2
0
    def test_elementbyid(self):
        mei = MeiElement("mei")
        mus = MeiElement("music")
        body = MeiElement("body")
        staff = MeiElement("staff")
        staff2 = MeiElement("staff")
        n1 = MeiElement("note")
        wantedId = n1.id
        n2 = MeiElement("note")
        n3 = MeiElement("note")
        n4 = MeiElement("note")

        mei.addChild(mus)
        mus.addChild(body)
        body.addChild(staff)
        body.addChild(staff2)
        staff.addChild(n1)
        staff.addChild(n2)
        staff.addChild(n3)
        staff2.addChild(n4)

        doc = MeiDocument()
        self.assertEqual(None, doc.getElementById(wantedId))

        doc.root = mei

        self.assertEqual(n1, doc.getElementById(wantedId))

        self.assertEqual(None, doc.getElementById("unknownID"))

        n5 = MeiElement("note")
        newid = n5.id
        staff2.addChild(n5)

        self.assertEqual(n5, doc.getElementById(newid))
        staff2.removeChild(n5)
        self.assertEqual(None, doc.getElementById(newid))
Пример #3
0
class AomrMeiOutput(object):
    # define the form of a neume.
    # form: [ num, interval_dir... ]
    # e.g., clivis: [2, 'd']
    # torculus: [3, 'u', 'd']
    NEUME_NOTES = {
        'punctum': [],
        'virga': [],
        'cephalicus': ['d'],
        'clivis': ['d'],
        'epiphonus': ['u'],
        'podatus': ['u'],
        'porrectus': ['d', 'u'],
        'salicus': ['u', 'u'],
        'scandicus': ['u', 'u'],
        'torculus': ['u', 'd'],
        'ancus': ['d', 'd'],  # See note 1 below
    }

    # given an alternate form, how many notes does it add to the neume?
    ADD_NOTES = {
        'flexus': ['d'],  # scandicus.flexus, porrectus.flexus
        'resupinus': ['u'],  # torculus.resupinus
    }

    SCALE = ['a', 'b', 'c', 'd', 'e', 'f', 'g']

    def __init__(self, incoming_data, original_image, page_number=None):
        self._recognition_results = incoming_data
        # self.mei = mod.mei_()
        self.mei = MeiElement("mei")
        self.staff = None

        # Hack: I'm not sure what staff_num is. In any case it's strange to set it to None.
        # I therefore set it to 0 if page_number is None.
        if page_number is not None:
            self.staff_num = int(page_number)
        else:
            self.staff_num = 0
        self.glyph = None

        self._note_elements = None
        self._neume_pitches = []

        # set up a basic MEI document structure

        # header
        # self.meihead = mod.meiHead_()
        self.meihead = MeiElement("meiHead")
        # self.filedesc = mod.fileDesc_()
        self.filedesc = MeiElement("fileDesc")
        # self.titlestmt = mod.titleStmt_()
        self.titlestmt = MeiElement("titleStmt")
        # self.title = mod.title_()
        self.title = MeiElement("title")
        # self.pubstmt = mod.pubStmt_()
        self.pubstmt = MeiElement("pubStmt")

        self.titlestmt.addChild(self.title)

        self.filedesc.addChild(self.titlestmt)
        self.filedesc.addChild(self.pubstmt)

        self.meihead.addChild(self.filedesc)
        self.mei.addChild(self.meihead)

        # music
        # self.music = mod.music_()
        self.music = MeiElement("music")
        self.facsimile = self._create_facsimile_element()
        self.surface = self._create_surface_element()
        self.graphic = self._create_graphic_element(original_image)
        lg.debug("SELF GRAPHIC:{0}".format(
            XmlExport.meiElementToText(self.graphic)))

        self.surface.addChild(self.graphic)
        self.facsimile.addChild(self.surface)
        self.music.addChild(self.facsimile)

        self.layout = self._create_layout_element()
        self.pg = self._create_page_element()
        if page_number:
            # self.pg.attributes = {"n": page_number}
            self.pg.addAttribute("n", page_number)

        self.layout.addChild(self.pg)
        self.music.addChild(self.layout)

        # self.body = mod.body_()
        self.body = MeiElement("body")
        self.music.addChild(self.body)

        self.mdiv = MeiElement("mdiv")
        # self.mdiv = mod.mdiv_()
        self.mdiv.addAttribute("type", "solesmes")
        self.body.addChild(self.mdiv)

        # self.score = mod.score_()
        self.score = MeiElement("score")
        self.mdiv.addChild(self.score)

        # self.scoredef = mod.scoreDef_()
        self.scoredef = MeiElement("scoreDef")
        self.score.addChild(self.scoredef)

        # self.section = mod.section_()
        self.section = MeiElement("section")
        self.pagebreak = self._create_pb_element()
        # self.pagebreak.attributes = {"pageref": self.pg.id}
        self.pagebreak.addAttribute("pageref", self.pg.id)

        self.section.addChild(self.pagebreak)
        self.score.addChild(self.section)

        self.staffgrp = self._create_staffgrp_element()
        self.staffdef = self._create_staffdef_element()
        self.staffdef.addAttribute("n", str(self.staff_num))  # trouble

        self.staffgrp.addChild(self.staffdef)
        self.scoredef.addChild(self.staffgrp)

        self.layer = self._create_layer_element()
        self.layer.addAttribute("n", "1")

        self.staffel = self._create_staff_element()
        self.staffel.addAttribute("n", str(self.staff_num))  # trouble
        self.section.addChild(self.staffel)
        self.staffel.addChild(self.layer)

        for sysnum in sorted(self._recognition_results.keys()):
            syst = self._recognition_results[sysnum]
            lg.debug("sysnum:{0}".format(sysnum))
            self.system = syst
            self.systembreak = self._parse_system(sysnum, syst)
            # z = mod.zone_()
            z = MeiElement("zone")
            # z.id = self._idgen()
            # z.attributes = {'ulx': self.system['coord'][0], 'uly': self.system['coord'][1], \
            #                     'lrx': self.system['coord'][2], 'lry': self.system['coord'][3]}
            z.addAttribute("ulx", str(self.system['coord'][0]))
            z.addAttribute("uly", str(self.system['coord'][1]))
            z.addAttribute("lrx", str(self.system['coord'][2]))
            z.addAttribute("lry", str(self.system['coord'][3]))
            self.surface.addChild(z)
            # self.system.facs = z.id
            s = self._create_system_element()
            s.facs = z.id
            s.addAttribute("facs", s.facs)
            self.pg.addChild(s)
            self.systembreak.addAttribute("systemref", s.id)

        self.mei.addChild(self.music)

        # if not self.staffel.descendants_by_name('neume'):
        if not self.staffel.getDescendantsByName("neume"):
            self.staffgrp.removeChild(self.staffdef)
            self.section.removeChild(self.staffel)

        # self.md = MeiDocument.MeiDocument()
        # self.md.addelement(self.mei)
        self.md = MeiDocument()
        self.md.setRootElement(self.mei)
        print XmlExport.meiElementToText(
            self.md.getElementById(self.graphic.getId()))

    def _parse_system(self, sysnum, syst):
        sysbrk = self._create_sb_element()
        # sysbrk.attributes = {"n": sysnum + 1}
        sysnum = int(sysnum)
        sysbrk.addAttribute("n", "%d" % (sysnum + 1))
        self.layer.addChild(sysbrk)
        # staffel = self._create_staff_element()
        # staffel.addAttribute("n", stfnum)

        for c in self.system['content']:
            # parse the glyphs per staff.
            self.glyph = c
            if c['type'] == 'neume':
                if not self.glyph['form']:
                    lg.debug("Skipping glyph: {0}".format(self.glyph))
                    continue
                if self.glyph['form'][0] not in self.NEUME_NOTES.keys():
                    continue
                else:
                    self.layer.addChild(self._create_neume_element())

            elif c['type'] == 'clef':
                self.layer.addChild(self._create_clef_element())
            elif c['type'] == 'division':
                self.layer.addChild(self._create_division_element())
                if "final" in c['form']:
                    self.staff_num += 1
                    new_staff = self._create_staff_element()
                    new_staffdef = self._create_staffdef_element()
                    # new_staffdef.attributes = {'n': self.staff_num} TROUBLE
                    new_staffdef.addAttribute('n', str(self.staff_num))
                    # new_staff.attributes = {'n': self.staff_num} TROUBLE
                    new_staff.addAttribute('n', str(self.staff_num))
                    new_layer = self._create_layer_element()
                    # new_layer.attributes = {'n': 1} TROUBLE
                    new_layer.addAttribute('n', str(1))
                    self.layer = new_layer
                    self.staffel = new_staff
                    self.staffdef = new_staffdef
                    self.staffgrp.addChild(self.staffdef)
                    self.staffel.addChild(self.layer)
                    self.section.addChild(self.staffel)

            elif c['type'] == 'custos':
                self.layer.addChild(self._create_custos_element())

            elif c['type'] == "alteration":
                # staffel.addChild(self._create_alteration_element()) #GVM_OLD
                pass
        return sysbrk

    def _create_graphic_element(self, imgfile):
        graphic = MeiElement("graphic")
        # xlink = MeiNamespace("xlink", "http://www.w3.org/1999/xlink")
        # ns_attr = MeiAttribute("xlink")
        graphic.addAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink")
        graphic.addAttribute("xlink:href", imgfile)
        return graphic

    def _create_alteration_element(self):
        # accid = mod.accid_()
        accid = MeiElement("accid")
        accid.id = self._idgen()
        if self.glyph['form'] is "sharp":
            accid.addAttribute("accid", "s")
        elif self.glyph['form'] is "flat":
            accid.addAttribute("accid", "f")

        # zone = self._create_zone_element()
        # note.facs = zone.id
        return accid

    def _create_surface_element(self):
        # surface = mod.surface_()
        surface = MeiElement("surface")
        return surface

    def _create_facsimile_element(self):
        # facsimile = mod.facsimile_()
        facsimile = MeiElement("facsimile")
        # facsimile.id = self._idgen()
        return facsimile

    def _create_zone_element(self):
        zone = MeiElement("zone")
        # zone = mod.zone_()
        # zone.id = self._idgen()
        # zone.attributes = {'ulx': self.glyph['coord'][0], 'uly': self.glyph['coord'][1], \
        #                     'lrx': self.glyph['coord'][2], 'lry': self.glyph['coord'][3]}

        zone.addAttribute("ulx", str(self.glyph['coord'][0]))
        zone.addAttribute("uly", str(self.glyph['coord'][1]))
        zone.addAttribute("lrx", str(self.glyph['coord'][2]))
        zone.addAttribute("lry", str(self.glyph['coord'][3]))
        self.surface.addChild(zone)
        return zone

    def _create_layer_element(self):
        layer = MeiElement("layer")
        # layer = mod.layer_()
        # layer.id = self._idgen()
        lg.debug("layer:{0}".format(layer))
        return layer

    def _create_staffgrp_element(self):
        # stfgrp = mod.staffGrp_()
        stfgrp = MeiElement("staffGrp")
        # stfgrp.id = self._idgen()
        return stfgrp

    def _create_staffdef_element(self):
        stfdef = MeiElement("staffDef")
        # stfdef = mod.staffDef_()
        # stfdef.id = self._idgen()
        return stfdef

    def _create_staff_element(self):
        # staff = mod.staff_()
        staff = MeiElement("staff")
        # staff.id = self._idgen()
        return staff

    def _create_sb_element(self):
        sb = MeiElement("sb")
        # sb = mod.sb_()
        # sb.id = self._idgen()
        return sb

    def _create_pb_element(self):
        # pb = mod.pb_()
        pb = MeiElement("pb")
        # pb.id = self._idgen()
        return pb

    def _create_layout_element(self):
        layout = MeiElement("layout")  # GVM_FIXED
        return layout

    def _create_page_element(self):
        page = MeiElement("page")  # FIXED?
        # page = mod.page_()
        # page.id = self._idgen()
        return page

    def _create_system_element(self):
        system = MeiElement("system")  # FIXED?
        # system = mod.system_()
        # system.id = self._idgen()
        return system

    def _create_episema_element(self):
        epi = MeiElement("episema")
        # epi = mod.episema_()
        # epi.id = self._idgen()
        return epi

    def _create_neume_element(self):
        full_width_episema = False
        has_dot = False
        has_vertical_episema = False
        has_horizontal_episema = False
        has_quilisma = False
        this_neume_form = None
        local_horizontal_episema = None

        start_octave = self.glyph['octv']
        clef_pos = self.glyph['clef_pos']
        clef_type = self.glyph['clef'].split(".")[-1]  # f or c.

        # neume = mod.neume_()
        neume = MeiElement("neume")  # CHECK!
        # neume.id = self._idgen()
        zone = self._create_zone_element()
        neume.facs = zone.id
        neume.addAttribute("facs", neume.facs)

        # neumecomponent = mod.nc_()
        neumecomponent = MeiElement("nc")  # CHECK!
        # neumecomponent.id = self._idgen()
        neume.addChild(neumecomponent)
        if self.glyph['form'][0] == "he":
            full_width_episema = True
            del self.glyph['form'][0]

        # we've removed any global he's, so
        # any leftovers should be local.
        if 'he' in self.glyph['form']:
            has_horizontal_episema = True

        if 'dot' in self.glyph['form']:
            has_dot = True

        if 'q' in self.glyph['form']:
            has_quilisma = True

        if 've' in self.glyph['form']:
            has_vertical_episema = True

        if 'inclinatum' in self.glyph['form']:
            # neumecomponent.attributes = {'inclinatum': 'true'}
            neumecomponent.addAttribute("inclinatum", "true")

        # neume.attributes = {'name': self.glyph['form'][0]}
        neume.addAttribute("name", str(self.glyph['form'][0]))

        if 'compound' in self.glyph['form']:
            # do something and create a new set of pitch contours
            this_neume_form = [
                y for y in (self.__parse_contour(n)
                            for n in self.glyph['form']) if y
            ]
            self._note_elements = [
                y for y in (self.__parse_steps(n) for n in self.glyph['form'])
                if y
            ]
        else:
            this_neume_form = copy.deepcopy(
                self.NEUME_NOTES[self.glyph['form'][0]])
            self._note_elements = self.glyph['form'][1:]
        # get the form so we can find the number of notes we need to construct.

        num_notes = len(this_neume_form) + 1
        # we don't have an off-by-one problem here, since an added interval means an added note
        check_additional = [
            i for i in self.ADD_NOTES.keys() if i in self.glyph['form'][1:]
        ]
        if check_additional:
            for f in check_additional:
                this_neume_form.extend(self.ADD_NOTES[f])

                ## THIS SHOULD BE CHANGED. Otherwise we may end up with two attributes with the
                # same name.
                neume.addAttribute("variant", str(f))

            num_notes = num_notes + len(check_additional)

        self._neume_pitches = []
        # note elements are everything after the first form. This determines the shape a note takes.
        self._neume_pitches.append(self.glyph['strt_pitch'])
        nc = []
        note_octaves = [start_octave]
        if num_notes > 1:
            # we need to figure out the rest of the pitches in the neume.
            ivals = [int(d) for d in self._note_elements if d.isdigit()]
            idx = self.SCALE.index(self.glyph['strt_pitch'])

            if len(ivals) != (num_notes - 1):
                if 'scandicus' in self.glyph['form']:
                    diffr = abs(len(ivals) - (num_notes - 1))
                    num_notes = num_notes + diffr
                    this_neume_form.extend(diffr * 'u')
                else:
                    raise AomrMeiNoteIntervalMismatchError(
                        "There is a mismatch between the number of notes and number of intervals."
                    )

            # note elements = torculus.2.2.he.ve
            # ivals = [2,2]
            # torculus = ['u','d']
            this_pos = copy.deepcopy(self.glyph['strt_pos'])
            for n in xrange(len(ivals)):
                # get the direction
                dir = this_neume_form[n]
                iv = ivals[n]
                n_idx = idx
                if dir == "u":
                    n_idx = ((idx + iv) % len(self.SCALE)) - 1
                    this_pos -= (iv - 1)
                elif dir == "d":
                    n_idx = idx - (iv - 1)
                    this_pos += (iv - 1)
                    if n_idx < 0:
                        n_idx += len(self.SCALE)
                idx = n_idx
                self._neume_pitches.append(self.SCALE[n_idx])

                actual_line = 10 - (2 * (clef_pos - 1))

                if clef_type:
                    if this_pos <= actual_line:
                        note_octaves.append(4)
                    elif this_pos > actual_line + 7:
                        note_octaves.append(2)
                    else:
                        note_octaves.append(3)
                # elif clef_type == "f":
                #     if (actual_line + 3) >= this_pos > (actual_line - 3):
                #         note_octaves.append(3)
                #     elif this_pos < (actual_line - 3):
                #         note_octaves.append(4)
                #     elif this_pos > (actual_line + 3):
                #         note_octaves.append(2)

        if full_width_episema is True:
            epi = self._create_episema_element()
            # epi.attributes = {"form": "horizontal"} TROUBLE
            epi.addAttribute("form", "horizontal")
            self.layer.addChild(epi)

        qidxs = []
        if has_quilisma:
            self.__note_addition_figurer_outer("q", qidxs)

        dotidxs = []
        if has_dot:
            self.__note_addition_figurer_outer("dot", dotidxs)

        veidxs = []
        if has_vertical_episema:
            self.__note_addition_figurer_outer("ve", veidxs)

        heidxs = []
        if has_horizontal_episema:
            self.__note_addition_figurer_outer("he", heidxs)

        for n in xrange(num_notes):
            p = self._neume_pitches[n]
            o = note_octaves[n]
            # lg.debug("n:{0}, p:{1}, o:{2}".format(n, p, o))
            nt = self._create_note_element(p)
            nt.addAttribute("oct", str(o))
            # lg.debug("nt.pitchname:{0}".format(nt.pname))
            if n == 0 and full_width_episema is True:
                epi.addAttribute("startid", str(nt.id))
            elif n == num_notes and full_width_episema is True:
                epi.addAttribute("endid", str(nt.id))

            if has_quilisma:
                if n in qidxs:
                    neumecomponent.addAttribute("quilisma", "true")

            if has_dot:
                if n in dotidxs:
                    d = self._create_dot_element()
                    nt.addChild(d)

            if has_vertical_episema:
                if n in veidxs:
                    ep = self._create_episema_element()
                    ep.addAttribute("form", "vertical")
                    ep.addAttribute("startid", str(nt.id))
                    self.layer.addChild(ep)

            if has_horizontal_episema:
                if n in heidxs:
                    local_horizontal_episema = self._create_episema_element()
                    local_horizontal_episema.addAttribute("form", "horizontal")
                    local_horizontal_episema.addAttribute(
                        "startid", str(nt.id))
                    self.layer.addChild(local_horizontal_episema)

            if n == num_notes - 1 and local_horizontal_episema:
                # we've reached the end, and we have an HE we need to close up.
                local_horizontal_episema.addAttribute("endid", str(nt.id))

            nc.append(nt)

        for c in nc:
            neumecomponent.addChild(c)
        return neume

    def _create_note_element(self, pname=None):
        # note = mod.note_()
        note = MeiElement("note")
        # note.id = self._idgen()
        note.addAttribute("pname", str(pname))
        return note

    def _create_dot_element(self):
        # dot = mod.dot_()
        dot = MeiElement("dot")
        # dot.id = self._idgen()
        dot.addAttribute("form", "aug")
        return dot

    def _create_custos_element(self):
        custos = MeiElement("custos")
        # custos = mod.custos_()
        # custos.id = self._idgen()
        zone = self._create_zone_element()
        custos.facs = zone.id
        custos.addAttribute("pname", str(self.glyph['strt_pitch']))
        custos.addAttribute("oct", str(self.glyph['octv']))
        custos.addAttribute("facs", str(custos.facs))
        return custos

    def _create_clef_element(self):
        clef = MeiElement("clef")
        # clef = mod.clef_()
        # clef.id = self._idgen()
        zone = self._create_zone_element()
        clef.facs = zone.id
        clef.addAttribute("facs", str(clef.facs))

        # clef.attributes = {"line": self.glyph['strt_pos'], 'shape': self.glyph['form'][0].upper() }
        clef.addAttribute("line", str(self.glyph['strt_pos']))
        clef.addAttribute("shape", str(self.glyph['form'][0].upper()))
        lg.debug("clef:{0}".format(clef))
        return clef

    def _create_division_element(self):
        division = MeiElement("division")
        # division = mod.division_()
        # division.id = self._idgen()
        zone = self._create_zone_element()
        division.addAttribute("facs", str(zone.id))

        if self.glyph['form']:
            div = str(self.glyph['form'][0])
        else:
            div = "minor"

        division.addAttribute("form", div)
        return division

    def __parse_contour(self, form):
        # removes the contour indicator from the neume
        # and creates a neume form.
        if len(form) is 2 and (form.startswith("u") or form.startswith("d")):
            # do something
            return form[0]
        else:
            return None

    def __parse_steps(self, form):
        if len(form) is 2 and (form.startswith("u") or form.startswith("d")):
            return form[1]
        else:
            return None

    def __note_addition_figurer_outer(self, ntype, idxarray):
        for i, n in enumerate(self.glyph['form']):
            if n == ntype:
                j = copy.copy(i) - 1
                if j == 0:
                    idxarray.append(0)
                while j:
                    if self.__is_valid_note_indicator(self.glyph['form'][j]):
                        idxarray.append(j)
                        break
                    else:
                        j -= 1

    def __is_valid_note_indicator(self, form):
        # used to test if a form is a valid indicator of a note (and not a q, dot, or anything else)
        if form.isdigit():
            return True
        elif len(form) == 2 and form.startswith("u") or form.startswith("d"):
            return True
        else:
            return False
Пример #4
0
class BarlineDataConverter:
    '''
    Convert the output of the barline detection algorithm
    to MEI.
    '''

    def __init__(self, staff_bb, bar_bb, verbose):
        '''
        Initialize the converter
        '''

        self.staff_bb = staff_bb
        self.bar_bb = bar_bb
        self.verbose = verbose

    def bardata_to_mei(self, sg_hint, image_path, image_width, image_height, image_dpi):
        '''
        Perform the data conversion to mei
        '''

        self.meidoc = MeiDocument()
        mei = MeiElement('mei')
        self.meidoc.setRootElement(mei)

        ###########################
        #         MetaData        #
        ###########################
        mei_head = self._create_header()
        mei.addChild(mei_head)

        ###########################
        #           Body          #
        ###########################
        music = MeiElement('music')
        body = MeiElement('body')
        mdiv = MeiElement('mdiv')
        score = MeiElement('score')
        score_def = MeiElement('scoreDef')
        section = MeiElement('section')

        # physical location data
        facsimile = MeiElement('facsimile')
        surface = MeiElement('surface')

        graphic = self._create_graphic(image_path, image_width, image_height)
        surface.addChild(graphic)

        # parse staff group hint to generate staff group
        sg_hint = sg_hint.split(" ")
        systems = []
        for s in sg_hint:
            parser = nestedExpr()
            sg_list = parser.parseString(s).asList()[0]
            staff_grp, n = self._create_staff_group(sg_list, MeiElement('staffGrp'), 0)

            # parse repeating staff groups (systems)
            num_sb = 1
            match = re.search('(?<=x)(\d+)$', s)
            if match is not None:
                # there are multiple systems of this staff grouping
                num_sb = int(match.group(0))
            
            for i in range(num_sb):
                systems.append(staff_grp)

            if self.verbose:
                print "number of staves in system: %d x %d system(s)" % (n, num_sb)

        # there may be hidden staves in a system
        # make the encoded staff group the largest number of staves in a system
        final_staff_grp = max(systems, key=lambda x: len(x.getDescendantsByName('staffDef')))

        mei.addChild(music)
        music.addChild(facsimile)
        facsimile.addChild(surface)
        
        # list of staff bounding boxes within a system
        staves = []
        for staff_bb in self.staff_bb:
            # get bounding box of the staff
            # parse bounding box integers
            #staff_bb = [int(x) for x in staff_bb]
            staves.append(staff_bb[1:])
        
        music.addChild(body)
        body.addChild(mdiv)
        mdiv.addChild(score)
        score.addChild(score_def)
        score_def.addChild(final_staff_grp)
        score.addChild(section)

        # parse barline data file [staffnum][barlinenum_ulx]
        barlines = []
        for i, bar in enumerate(self.bar_bb):
            staff_num = int(bar[0])
            ulx = bar[1]
            try:
                barlines[staff_num-1].append(ulx)
            except IndexError:
                barlines.append([ulx])

        staff_offset = 0
        n_measure = 1
        for s_ind, s in enumerate(systems):
            # measures in a system
            s_measures = []
            staff_defs = s.getDescendantsByName('staffDef')
            # for each staff in the system
            for i in range(len(staff_defs)):
                staff_num = staff_offset + i
                s_bb = staves[staff_num]
                # bounding box of the staff
                s_ulx = s_bb[0]
                s_uly = s_bb[1]
                s_lrx = s_bb[2]
                s_lry = s_bb[3]

                # for each barline on this staff
                try:
                    staff_bars = barlines[staff_num]
                except IndexError:
                    # a staff was found, but no bar candidates have been found on the staff
                    continue

                # for each barline on this staff
                for n, b in enumerate(staff_bars[:-1]):
                    # calculate bounding box of the measure
                    m_uly = s_uly
                    m_lry = s_lry
                    m_ulx = b
                    m_lrx = staff_bars[n+1]

                    zone = self._create_zone(m_ulx, m_uly, m_lrx, m_lry)
                    surface.addChild(zone)
                    if len(sg_hint) == 1 or len(staff_defs) == len(final_staff_grp.getDescendantsByName('staffDef')):
                        staff_n = str(i+1)    
                    else:
                        # take into consideration hidden staves
                        staff_n = i + self._calc_staff_num(len(staff_defs), [final_staff_grp]) + 1
                    
                    staff = self._create_staff(staff_n, zone)
                    #print '  ', staff_n, m_ulx, m_uly, m_lrx, m_lry
                    try:
                        s_measures[n].addChild(staff)
                    except IndexError:
                        # create a new measure
                        measure = self._create_measure(str(n_measure))
                        s_measures.append(measure)
                        section.addChild(measure)
                        measure.addChild(staff)
                        n_measure += 1

            # calculate min/max of measure/staff bounding boxes to get measure zone
            self._calc_measure_zone(s_measures)

            staff_offset += len(staff_defs)

            # add a system break, if necessary
            if s_ind+1 < len(systems):
                sb = MeiElement('sb')
                section.addChild(sb)

    def _calc_staff_num(self, num_staves, staff_grps):
        '''
        In the case where there are hidden staves,
        search for the correct staff number within the staff
        group definition.
        '''

        if len(staff_grps) == 0:
            # termination condition (or no match found)
            return 0
        else:
            sg_staves = staff_grps[0].getChildrenByName('staffDef')
            sgs = staff_grps[0].getChildrenByName('staffGrp')
            if num_staves == len(sg_staves):
                # no need to look at subsequent staff groups
                n = int(sg_staves[0].getAttribute('n').value)
            else:
                n = self._calc_staff_num(num_staves, sgs)

            return n + self._calc_staff_num(num_staves, staff_grps[1:])
        
    def _calc_measure_zone(self, measures):
        '''
        Calculate the bounding box of the provided measures
        by calculating the min and max of the bounding boxes
        of the staves which compose the measure.
        '''

        # for each measure
        for m in measures:
            staff_measure_zones = []
            min_ulx = sys.maxint
            min_uly = sys.maxint
            max_lrx = -sys.maxint - 1
            max_lry = -sys.maxint - 1
            for s in m.getChildrenByName('staff'):
                # have to skip # at the beginning of the id ref since using URIs
                s_zone = self.meidoc.getElementById(s.getAttribute('facs').value[1:])
                ulx = int(s_zone.getAttribute('ulx').value)
                if ulx < min_ulx:
                    min_ulx = ulx

                uly = int(s_zone.getAttribute('uly').value)
                if uly < min_uly:
                    min_uly = uly

                lrx = int(s_zone.getAttribute('lrx').value)
                if lrx > max_lrx:
                    max_lrx = lrx

                lry = int(s_zone.getAttribute('lry').value)
                if lry > max_lry:
                    max_lry = lry

            m_zone = self._create_zone(min_ulx, min_uly, max_lrx, max_lry)
            m.addAttribute('facs', '#'+m_zone.getId())
            surface = self.meidoc.getElementsByName('surface')[0]
            surface.addChild(m_zone)

    def _create_header(self, rodan_version='0.1'):
        '''
        Create a meiHead element
        '''

        mei_head = MeiElement('meiHead')
        today = datetime.date.today().isoformat()

        app_name = 'RODAN/barlineFinder'

        # file description
        file_desc = MeiElement('fileDesc')

        title_stmt = MeiElement('titleStmt')
        title = MeiElement('title')
        resp_stmt = MeiElement('respStmt')
        corp_name = MeiElement('corpName')
        corp_name.setValue('Distributed Digital Music Archives and Libraries Lab (DDMAL)')
        title_stmt.addChild(title)
        title_stmt.addChild(resp_stmt)
        resp_stmt.addChild(corp_name)
        
        pub_stmt = MeiElement('pubStmt')
        resp_stmt = MeiElement('respStmt')
        corp_name = MeiElement('corpName')
        corp_name.setValue('Distributed Digital Music Archives and Libraries Lab (DDMAL)')
        pub_stmt.addChild(resp_stmt)
        resp_stmt.addChild(corp_name)

        mei_head.addChild(file_desc)
        file_desc.addChild(title_stmt)
        file_desc.addChild(pub_stmt)

        # encoding description
        encoding_desc = MeiElement('encodingDesc')
        app_info = MeiElement('appInfo')
        application = MeiElement('application')
        application.addAttribute('version', rodan_version)
        name = MeiElement('name')
        name.setValue(app_name)
        ptr = MeiElement('ptr')
        ptr.addAttribute('target', 'https://github.com/DDMAL/barlineFinder')

        mei_head.addChild(encoding_desc)
        encoding_desc.addChild(app_info)
        app_info.addChild(application)
        application.addChild(name)
        application.addChild(ptr)

        # revision description
        revision_desc = MeiElement('revisionDesc')
        change = MeiElement('change')
        change.addAttribute('n', '1')
        resp_stmt = MeiElement('respStmt')
        corp_name = MeiElement('corpName')
        corp_name.setValue('Distributed Digital Music Archives and Libraries Lab (DDMAL)')
        change_desc = MeiElement('changeDesc')
        ref = MeiElement('ref')
        ref.addAttribute('target', '#'+application.getId())
        ref.setValue(app_name)
        ref.setTail('.')
        p = MeiElement('p')
        p.addChild(ref)
        p.setValue('Encoded using ')
        date = MeiElement('date')
        date.setValue(today)
        
        mei_head.addChild(revision_desc)
        revision_desc.addChild(change)
        change.addChild(resp_stmt)
        resp_stmt.addChild(corp_name)
        change.addChild(change_desc)
        change_desc.addChild(p)
        change.addChild(date)

        return mei_head

    def _create_graphic(self, image_path, image_width, image_height):
        '''
        Create a graphic element.
        '''

        graphic = MeiElement('graphic')
        graphic.addAttribute('height', str(image_height))
        graphic.addAttribute('width', str(image_width))
        graphic.addAttribute('target', image_path)
        graphic.addAttribute('unit', 'px')

        return graphic

    def _create_staff_group(self, sg_list, staff_grp, n):
        '''
        Recursively create the staff group element from the parsed
        user input of the staff groupings
        '''
        if not sg_list:
            return staff_grp, n
        else:
            if type(sg_list[0]) is list:
                new_staff_grp, n = self._create_staff_group(sg_list[0], MeiElement('staffGrp'), n)
                staff_grp.addChild(new_staff_grp)
            else:
                # check for barthrough character
                if sg_list[0][-1] == '|':
                    # the barlines go through all the staves in the staff group
                    staff_grp.addAttribute('barthru', 'true')
                    # remove the barthrough character, should now only be an integer
                    sg_list[0] = sg_list[0][:-1]

                n_staff_defs = int(sg_list[0])
                # get current staffDef number
                for i in range(n_staff_defs):
                    staff_def = MeiElement('staffDef')
                    staff_def.addAttribute('n', str(n+i+1))
                    staff_def.addAttribute('lines', '5')
                    staff_grp.addChild(staff_def)
                n += n_staff_defs

            return self._create_staff_group(sg_list[1:], staff_grp, n)

    def _create_staff(self, n, zone):
        '''
        Create a staff element, and attach a zone reference to it
        '''

        staff = MeiElement('staff')
        staff.addAttribute('n', str(n))
        staff.addAttribute('facs', '#'+zone.getId())

        return staff

    def _create_measure(self, n, zone = None):
        '''
        Create a measure element and attach a zone reference to it.
        The zone element is optional, since the zone of the measure is
        calculated once all of the staves within a measure have been added
        to the MEI.
        '''

        measure = MeiElement('measure')
        measure.addAttribute('n', str(n))

        if zone is not None:
            measure.addAttribute('facs', '#'+zone.getId())

        return measure

    def _create_zone(self, ulx, uly, lrx, lry):
        '''
        Create a zone element
        '''

        zone = MeiElement('zone')
        zone.addAttribute('ulx', str(ulx))
        zone.addAttribute('uly', str(uly))
        zone.addAttribute('lrx', str(lrx))
        zone.addAttribute('lry', str(lry))

        return zone

    def output_mei(self, output_path):
        '''
        Write the generated mei to disk
        '''

        # output mei file
        XmlExport.meiDocumentToFile(self.meidoc, output_path)
Пример #5
0
class AomrMeiOutput(object):
    # define the form of a neume.
    # form: [ num, interval_dir... ]
    # e.g., clivis: [2, 'd']
    # torculus: [3, 'u', 'd']
    NEUME_NOTES = {
        'punctum': [],
        'virga': [],
        'cephalicus': ['d'],
        'clivis': ['d'],
        'epiphonus': ['u'],
        'podatus': ['u'],
        'porrectus': ['d', 'u'],
        'salicus': ['u', 'u'],
        'scandicus': ['u', 'u'],
        'torculus': ['u', 'd'],
        'ancus': ['d', 'd'],  # See note 1 below
    }

    # given an alternate form, how many notes does it add to the neume?
    ADD_NOTES = {
        'flexus': ['d'],  # scandicus.flexus, porrectus.flexus
        'resupinus': ['u'],  # torculus.resupinus
    }

    SCALE = ['a', 'b', 'c', 'd', 'e', 'f', 'g']

    def __init__(self, incoming_data, original_image, page_number=None):
        self._recognition_results = incoming_data
        # self.mei = mod.mei_()
        self.mei = MeiElement("mei")
        self.staff = None

        # Hack: I'm not sure what staff_num is. In any case it's strange to set it to None.
        # I therefore set it to 0 if page_number is None.
        if page_number is not None:
            self.staff_num = int(page_number)
        else:
            self.staff_num = 0
        self.glyph = None

        self._note_elements = None
        self._neume_pitches = []

        # set up a basic MEI document structure

        # header
        # self.meihead = mod.meiHead_()
        self.meihead = MeiElement("meiHead")
        # self.filedesc = mod.fileDesc_()
        self.filedesc = MeiElement("fileDesc")
        # self.titlestmt = mod.titleStmt_()
        self.titlestmt = MeiElement("titleStmt")
        # self.title = mod.title_()
        self.title = MeiElement("title")
        # self.pubstmt = mod.pubStmt_()
        self.pubstmt = MeiElement("pubStmt")

        self.titlestmt.addChild(self.title)

        self.filedesc.addChild(self.titlestmt)
        self.filedesc.addChild(self.pubstmt)

        self.meihead.addChild(self.filedesc)
        self.mei.addChild(self.meihead)

        # music
        # self.music = mod.music_()
        self.music = MeiElement("music")
        self.facsimile = self._create_facsimile_element()
        self.surface = self._create_surface_element()
        self.graphic = self._create_graphic_element(original_image)
        lg.debug("SELF GRAPHIC:{0}".format(XmlExport.meiElementToText(self.graphic)))

        self.surface.addChild(self.graphic)
        self.facsimile.addChild(self.surface)
        self.music.addChild(self.facsimile)

        self.layout = self._create_layout_element()
        self.pg = self._create_page_element()
        if page_number:
            # self.pg.attributes = {"n": page_number}
            self.pg.addAttribute("n", page_number)

        self.layout.addChild(self.pg)
        self.music.addChild(self.layout)

        # self.body = mod.body_()
        self.body = MeiElement("body")
        self.music.addChild(self.body)

        self.mdiv = MeiElement("mdiv")
        # self.mdiv = mod.mdiv_()
        self.mdiv.addAttribute("type", "solesmes")
        self.body.addChild(self.mdiv)

        # self.score = mod.score_()
        self.score = MeiElement("score")
        self.mdiv.addChild(self.score)

        # self.scoredef = mod.scoreDef_()
        self.scoredef = MeiElement("scoreDef")
        self.score.addChild(self.scoredef)

        # self.section = mod.section_()
        self.section = MeiElement("section")
        self.pagebreak = self._create_pb_element()
        # self.pagebreak.attributes = {"pageref": self.pg.id}
        self.pagebreak.addAttribute("pageref", self.pg.id)

        self.section.addChild(self.pagebreak)
        self.score.addChild(self.section)

        self.staffgrp = self._create_staffgrp_element()
        self.staffdef = self._create_staffdef_element()
        self.staffdef.addAttribute("n", str(self.staff_num))   # trouble

        self.staffgrp.addChild(self.staffdef)
        self.scoredef.addChild(self.staffgrp)

        self.layer = self._create_layer_element()
        self.layer.addAttribute("n", "1")

        self.staffel = self._create_staff_element()
        self.staffel.addAttribute("n", str(self.staff_num))   # trouble
        self.section.addChild(self.staffel)
        self.staffel.addChild(self.layer)

        for sysnum in sorted(self._recognition_results.keys()):
            syst = self._recognition_results[sysnum]
            lg.debug("sysnum:{0}".format(sysnum))
            self.system = syst
            self.systembreak = self._parse_system(sysnum, syst)
            # z = mod.zone_()
            z = MeiElement("zone")
            # z.id = self._idgen()
            # z.attributes = {'ulx': self.system['coord'][0], 'uly': self.system['coord'][1], \
            #                     'lrx': self.system['coord'][2], 'lry': self.system['coord'][3]}
            z.addAttribute("ulx", str(self.system['coord'][0]))
            z.addAttribute("uly", str(self.system['coord'][1]))
            z.addAttribute("lrx", str(self.system['coord'][2]))
            z.addAttribute("lry", str(self.system['coord'][3]))
            self.surface.addChild(z)
            # self.system.facs = z.id
            s = self._create_system_element()
            s.facs = z.id
            s.addAttribute("facs", s.facs)
            self.pg.addChild(s)
            self.systembreak.addAttribute("systemref", s.id)

        self.mei.addChild(self.music)

        # if not self.staffel.descendants_by_name('neume'):
        if not self.staffel.getDescendantsByName("neume"):
            self.staffgrp.removeChild(self.staffdef)
            self.section.removeChild(self.staffel)

        # self.md = MeiDocument.MeiDocument()
        # self.md.addelement(self.mei)
        self.md = MeiDocument()
        self.md.setRootElement(self.mei)
        print XmlExport.meiElementToText(self.md.getElementById(self.graphic.getId()))

    def _parse_system(self, sysnum, syst):
        sysbrk = self._create_sb_element()
        # sysbrk.attributes = {"n": sysnum + 1}
        sysnum = int(sysnum)
        sysbrk.addAttribute("n", "%d" % (sysnum+1))
        self.layer.addChild(sysbrk)
        # staffel = self._create_staff_element()
        # staffel.addAttribute("n", stfnum)

        for c in self.system['content']:
            # parse the glyphs per staff.
            self.glyph = c
            if c['type'] == 'neume':
                if not self.glyph['form']:
                    lg.debug("Skipping glyph: {0}".format(self.glyph))
                    continue
                if self.glyph['form'][0] not in self.NEUME_NOTES.keys():
                    continue
                else:
                    self.layer.addChild(self._create_neume_element())

            elif c['type'] == 'clef':
                self.layer.addChild(self._create_clef_element())
            elif c['type'] == 'division':
                self.layer.addChild(self._create_division_element())
                if "final" in c['form']:
                    self.staff_num += 1
                    new_staff = self._create_staff_element()
                    new_staffdef = self._create_staffdef_element()
                    # new_staffdef.attributes = {'n': self.staff_num} TROUBLE
                    new_staffdef.addAttribute('n', str(self.staff_num))
                    # new_staff.attributes = {'n': self.staff_num} TROUBLE
                    new_staff.addAttribute('n', str(self.staff_num))
                    new_layer = self._create_layer_element()
                    # new_layer.attributes = {'n': 1} TROUBLE
                    new_layer.addAttribute('n', str(1))
                    self.layer = new_layer
                    self.staffel = new_staff
                    self.staffdef = new_staffdef
                    self.staffgrp.addChild(self.staffdef)
                    self.staffel.addChild(self.layer)
                    self.section.addChild(self.staffel)

            elif c['type'] == 'custos':
                self.layer.addChild(self._create_custos_element())

            elif c['type'] == "alteration":
                # staffel.addChild(self._create_alteration_element()) #GVM_OLD
                pass
        return sysbrk

    def _create_graphic_element(self, imgfile):
        graphic = MeiElement("graphic")
        # xlink = MeiNamespace("xlink", "http://www.w3.org/1999/xlink")
        # ns_attr = MeiAttribute("xlink")
        graphic.addAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink")
        graphic.addAttribute("xlink:href", imgfile)
        return graphic

    def _create_alteration_element(self):
        # accid = mod.accid_()
        accid = MeiElement("accid")
        accid.id = self._idgen()
        if self.glyph['form'] is "sharp":
            accid.addAttribute("accid", "s")
        elif self.glyph['form'] is "flat":
            accid.addAttribute("accid", "f")

        # zone = self._create_zone_element()
        # note.facs = zone.id
        return accid

    def _create_surface_element(self):
        # surface = mod.surface_()
        surface = MeiElement("surface")
        return surface

    def _create_facsimile_element(self):
        # facsimile = mod.facsimile_()
        facsimile = MeiElement("facsimile")
        # facsimile.id = self._idgen()
        return facsimile

    def _create_zone_element(self):
        zone = MeiElement("zone")
        # zone = mod.zone_()
        # zone.id = self._idgen()
        # zone.attributes = {'ulx': self.glyph['coord'][0], 'uly': self.glyph['coord'][1], \
        #                     'lrx': self.glyph['coord'][2], 'lry': self.glyph['coord'][3]}

        zone.addAttribute("ulx", str(self.glyph['coord'][0]))
        zone.addAttribute("uly", str(self.glyph['coord'][1]))
        zone.addAttribute("lrx", str(self.glyph['coord'][2]))
        zone.addAttribute("lry", str(self.glyph['coord'][3]))
        self.surface.addChild(zone)
        return zone

    def _create_layer_element(self):
        layer = MeiElement("layer")
        # layer = mod.layer_()
        # layer.id = self._idgen()
        lg.debug("layer:{0}".format(layer))
        return layer

    def _create_staffgrp_element(self):
        # stfgrp = mod.staffGrp_()
        stfgrp = MeiElement("staffGrp")
        # stfgrp.id = self._idgen()
        return stfgrp

    def _create_staffdef_element(self):
        stfdef = MeiElement("staffDef")
        # stfdef = mod.staffDef_()
        # stfdef.id = self._idgen()
        return stfdef

    def _create_staff_element(self):
        # staff = mod.staff_()
        staff = MeiElement("staff")
        # staff.id = self._idgen()
        return staff

    def _create_sb_element(self):
        sb = MeiElement("sb")
        # sb = mod.sb_()
        # sb.id = self._idgen()
        return sb

    def _create_pb_element(self):
        # pb = mod.pb_()
        pb = MeiElement("pb")
        # pb.id = self._idgen()
        return pb

    def _create_layout_element(self):
        layout = MeiElement("layout")           # GVM_FIXED
        return layout

    def _create_page_element(self):
        page = MeiElement("page")               # FIXED?
        # page = mod.page_()
        # page.id = self._idgen()
        return page

    def _create_system_element(self):
        system = MeiElement("system")           # FIXED?
        # system = mod.system_()
        # system.id = self._idgen()
        return system

    def _create_episema_element(self):
        epi = MeiElement("episema")
        # epi = mod.episema_()
        # epi.id = self._idgen()
        return epi

    def _create_neume_element(self):
        full_width_episema = False
        has_dot = False
        has_vertical_episema = False
        has_horizontal_episema = False
        has_quilisma = False
        this_neume_form = None
        local_horizontal_episema = None

        start_octave = self.glyph['octv']
        clef_pos = self.glyph['clef_pos']
        clef_type = self.glyph['clef'].split(".")[-1]  # f or c.

        # neume = mod.neume_()
        neume = MeiElement("neume")                            # CHECK!
        # neume.id = self._idgen()
        zone = self._create_zone_element()
        neume.facs = zone.id
        neume.addAttribute("facs", neume.facs)

        # neumecomponent = mod.nc_()
        neumecomponent = MeiElement("nc")                      # CHECK!
        # neumecomponent.id = self._idgen()
        neume.addChild(neumecomponent)
        if self.glyph['form'][0] == "he":
            full_width_episema = True
            del self.glyph['form'][0]

        # we've removed any global he's, so
        # any leftovers should be local.
        if 'he' in self.glyph['form']:
            has_horizontal_episema = True

        if 'dot' in self.glyph['form']:
            has_dot = True

        if 'q' in self.glyph['form']:
            has_quilisma = True

        if 've' in self.glyph['form']:
            has_vertical_episema = True

        if 'inclinatum' in self.glyph['form']:
            # neumecomponent.attributes = {'inclinatum': 'true'}
            neumecomponent.addAttribute("inclinatum", "true")

        # neume.attributes = {'name': self.glyph['form'][0]}
        neume.addAttribute("name", str(self.glyph['form'][0]))

        if 'compound' in self.glyph['form']:
            # do something and create a new set of pitch contours
            this_neume_form = [y for y in (self.__parse_contour(n) for n in self.glyph['form']) if y]
            self._note_elements = [y for y in (self.__parse_steps(n) for n in self.glyph['form']) if y]
        else:
            this_neume_form = copy.deepcopy(self.NEUME_NOTES[self.glyph['form'][0]])
            self._note_elements = self.glyph['form'][1:]
        # get the form so we can find the number of notes we need to construct.

        num_notes = len(this_neume_form) + 1
        # we don't have an off-by-one problem here, since an added interval means an added note
        check_additional = [i for i in self.ADD_NOTES.keys() if i in self.glyph['form'][1:]]
        if check_additional:
            for f in check_additional:
                this_neume_form.extend(self.ADD_NOTES[f])

                ## THIS SHOULD BE CHANGED. Otherwise we may end up with two attributes with the
                # same name.
                neume.addAttribute("variant", str(f))

            num_notes = num_notes + len(check_additional)

        self._neume_pitches = []
        # note elements are everything after the first form. This determines the shape a note takes.
        self._neume_pitches.append(self.glyph['strt_pitch'])
        nc = []
        note_octaves = [start_octave]
        if num_notes > 1:
            # we need to figure out the rest of the pitches in the neume.
            ivals = [int(d) for d in self._note_elements if d.isdigit()]
            idx = self.SCALE.index(self.glyph['strt_pitch'])

            if len(ivals) != (num_notes - 1):
                if 'scandicus' in self.glyph['form']:
                    diffr = abs(len(ivals) - (num_notes - 1))
                    num_notes = num_notes + diffr
                    this_neume_form.extend(diffr * 'u')
                else:
                    raise AomrMeiNoteIntervalMismatchError("There is a mismatch between the number of notes and number of intervals.")

            # note elements = torculus.2.2.he.ve
            # ivals = [2,2]
            # torculus = ['u','d']
            this_pos = copy.deepcopy(self.glyph['strt_pos'])
            for n in xrange(len(ivals)):
                # get the direction
                dir = this_neume_form[n]
                iv = ivals[n]
                n_idx = idx
                if dir == "u":
                    n_idx = ((idx + iv) % len(self.SCALE)) - 1
                    this_pos -= (iv - 1)
                elif dir == "d":
                    n_idx = idx - (iv - 1)
                    this_pos += (iv - 1)
                    if n_idx < 0:
                        n_idx += len(self.SCALE)
                idx = n_idx
                self._neume_pitches.append(self.SCALE[n_idx])

                actual_line = 10 - (2*(clef_pos-1))

                if clef_type:
                    if this_pos <= actual_line:
                        note_octaves.append(4)
                    elif this_pos > actual_line + 7:
                        note_octaves.append(2)
                    else:
                        note_octaves.append(3)
                # elif clef_type == "f":
                #     if (actual_line + 3) >= this_pos > (actual_line - 3):
                #         note_octaves.append(3)
                #     elif this_pos < (actual_line - 3):
                #         note_octaves.append(4)
                #     elif this_pos > (actual_line + 3):
                #         note_octaves.append(2)

        if full_width_episema is True:
            epi = self._create_episema_element()
            # epi.attributes = {"form": "horizontal"} TROUBLE
            epi.addAttribute("form", "horizontal")
            self.layer.addChild(epi)

        qidxs = []
        if has_quilisma:
            self.__note_addition_figurer_outer("q", qidxs)

        dotidxs = []
        if has_dot:
            self.__note_addition_figurer_outer("dot", dotidxs)

        veidxs = []
        if has_vertical_episema:
            self.__note_addition_figurer_outer("ve", veidxs)

        heidxs = []
        if has_horizontal_episema:
            self.__note_addition_figurer_outer("he", heidxs)

        for n in xrange(num_notes):
            p = self._neume_pitches[n]
            o = note_octaves[n]
            # lg.debug("n:{0}, p:{1}, o:{2}".format(n, p, o))
            nt = self._create_note_element(p)
            nt.addAttribute("oct", str(o))
            # lg.debug("nt.pitchname:{0}".format(nt.pname))
            if n == 0 and full_width_episema is True:
                epi.addAttribute("startid", str(nt.id))
            elif n == num_notes and full_width_episema is True:
                epi.addAttribute("endid", str(nt.id))

            if has_quilisma:
                if n in qidxs:
                    neumecomponent.addAttribute("quilisma", "true")

            if has_dot:
                if n in dotidxs:
                    d = self._create_dot_element()
                    nt.addChild(d)

            if has_vertical_episema:
                if n in veidxs:
                    ep = self._create_episema_element()
                    ep.addAttribute("form", "vertical")
                    ep.addAttribute("startid", str(nt.id))
                    self.layer.addChild(ep)

            if has_horizontal_episema:
                if n in heidxs:
                    local_horizontal_episema = self._create_episema_element()
                    local_horizontal_episema.addAttribute("form", "horizontal")
                    local_horizontal_episema.addAttribute("startid", str(nt.id))
                    self.layer.addChild(local_horizontal_episema)

            if n == num_notes - 1 and local_horizontal_episema:
                # we've reached the end, and we have an HE we need to close up.
                local_horizontal_episema.addAttribute("endid", str(nt.id))

            nc.append(nt)

        for c in nc:
            neumecomponent.addChild(c)
        return neume

    def _create_note_element(self, pname=None):
        # note = mod.note_()
        note = MeiElement("note")
        # note.id = self._idgen()
        note.addAttribute("pname", str(pname))
        return note

    def _create_dot_element(self):
        # dot = mod.dot_()
        dot = MeiElement("dot")
        # dot.id = self._idgen()
        dot.addAttribute("form", "aug")
        return dot

    def _create_custos_element(self):
        custos = MeiElement("custos")
        # custos = mod.custos_()
        # custos.id = self._idgen()
        zone = self._create_zone_element()
        custos.facs = zone.id
        custos.addAttribute("pname", str(self.glyph['strt_pitch']))
        custos.addAttribute("oct", str(self.glyph['octv']))
        custos.addAttribute("facs", str(custos.facs))
        return custos

    def _create_clef_element(self):
        clef = MeiElement("clef")
        # clef = mod.clef_()
        # clef.id = self._idgen()
        zone = self._create_zone_element()
        clef.facs = zone.id
        clef.addAttribute("facs", str(clef.facs))

        # clef.attributes = {"line": self.glyph['strt_pos'], 'shape': self.glyph['form'][0].upper() }
        clef.addAttribute("line", str(self.glyph['strt_pos']))
        clef.addAttribute("shape", str(self.glyph['form'][0].upper()))
        lg.debug("clef:{0}".format(clef))
        return clef

    def _create_division_element(self):
        division = MeiElement("division")
        # division = mod.division_()
        # division.id = self._idgen()
        zone = self._create_zone_element()
        division.addAttribute("facs", str(zone.id))

        if self.glyph['form']:
            div = str(self.glyph['form'][0])
        else:
            div = "minor"

        division.addAttribute("form", div)
        return division

    def __parse_contour(self, form):
        # removes the contour indicator from the neume
        # and creates a neume form.
        if len(form) is 2 and (form.startswith("u") or form.startswith("d")):
            # do something
            return form[0]
        else:
            return None

    def __parse_steps(self, form):
        if len(form) is 2 and (form.startswith("u") or form.startswith("d")):
            return form[1]
        else:
            return None

    def __note_addition_figurer_outer(self, ntype, idxarray):
        for i,n in enumerate(self.glyph['form']):
            if n == ntype:
                j = copy.copy(i) - 1
                if j == 0:
                    idxarray.append(0)
                while j:
                    if self.__is_valid_note_indicator(self.glyph['form'][j]):
                        idxarray.append(j)
                        break
                    else:
                        j -= 1

    def __is_valid_note_indicator(self, form):
        # used to test if a form is a valid indicator of a note (and not a q, dot, or anything else)
        if form.isdigit():
            return True
        elif len(form) == 2 and form.startswith("u") or form.startswith("d"):
            return True
        else:
            return False
Пример #6
0
class BarlineDataConverter:
    '''
    Convert the output of the barline detection algorithm
    to MEI.
    '''

    def __init__(self, staff_bb, bar_bb, verbose=False):
        '''
        Initialize the converter
        '''

        self.staff_bb = staff_bb
        self.bar_bb = bar_bb
        self.verbose = verbose

    def bardata_to_mei(self, sg_hint, image_path, image_width, image_height, image_dpi=72):
        '''
        Perform the data conversion to mei
        '''

        self.meidoc = MeiDocument()
        mei = MeiElement('mei')
        self.meidoc.setRootElement(mei)

        ###########################
        #         MetaData        #
        ###########################
        mei_head = self._create_header()
        mei.addChild(mei_head)

        ###########################
        #           Body          #
        ###########################
        music = MeiElement('music')
        body = MeiElement('body')
        mdiv = MeiElement('mdiv')
        score = MeiElement('score')
        score_def = MeiElement('scoreDef')
        section = MeiElement('section')

        # physical location data
        facsimile = MeiElement('facsimile')
        surface = MeiElement('surface')

        graphic = self._create_graphic(image_path, image_width, image_height)
        surface.addChild(graphic)

        # parse staff group hint to generate staff group
        sg_hint = sg_hint.split(" ")
        systems = []
        for s in sg_hint:
            parser = nestedExpr()
            sg_list = parser.parseString(s).asList()[0]
            staff_grp, n = self._create_staff_group(sg_list, MeiElement('staffGrp'), 0)

            # parse repeating staff groups (systems)
            num_sb = 1
            match = re.search('(?<=x)(\d+)$', s)
            if match is not None:
                # there are multiple systems of this staff grouping
                num_sb = int(match.group(0))

            for i in range(num_sb):
                systems.append(staff_grp)

            if self.verbose:
                print "number of staves in system: %d x %d system(s)" % (n, num_sb)

        # there may be hidden staves in a system
        # make the encoded staff group the largest number of staves in a system
        final_staff_grp = max(systems, key=lambda x: len(x.getDescendantsByName('staffDef')))

        mei.addChild(music)
        music.addChild(facsimile)
        facsimile.addChild(surface)

        # list of staff bounding boxes within a system
        staves = []
        for staff_bb in self.staff_bb:
            # get bounding box of the staff
            # parse bounding box integers
            #staff_bb = [int(x) for x in staff_bb]
            staves.append(staff_bb[1:])

        music.addChild(body)
        body.addChild(mdiv)
        mdiv.addChild(score)
        score.addChild(score_def)
        score_def.addChild(final_staff_grp)
        score.addChild(section)

        # parse barline data file [staffnum][barlinenum_ulx]
        barlines = []
        for i, bar in enumerate(self.bar_bb):
            staff_num = int(bar[0])
            ulx = bar[1]
            try:
                barlines[staff_num - 1].append(ulx)
            except IndexError:
                barlines.append([ulx])

        staff_offset = 0
        n_measure = 1
        b1_thresh = 1.25
        bn_thresh = 1.25
        # for each system
        for s_ind, s in enumerate(systems):
            # measures in a system
            s_measures = []
            staff_defs = s.getDescendantsByName('staffDef')
            # for each staff in the system
            for i in range(len(staff_defs)):
                staff_num = staff_offset + i
                s_bb = staves[staff_num]
                # bounding box of the staff
                s_ulx = s_bb[0]
                s_uly = s_bb[1]
                s_lrx = s_bb[2]
                s_lry = s_bb[3]

                # for each barline on this staff
                try:
                    staff_bars = barlines[staff_num]
                except IndexError:
                    # a staff was found, but no bar candidates have been found on the staff
                    continue

                # check the first barline candidate
                # If it is sufficiently close to the beginning of the staff then ignore it.
                b1_x = staff_bars[0]
                if abs(b1_x / image_dpi - s_ulx / image_dpi) < b1_thresh:
                    del staff_bars[0]

                # check the last barline candidate
                # if there is no candidate near the end of the interior of the staff, add one
                bn_x = staff_bars[-1]
                if bn_x < s_lrx and abs(bn_x / image_dpi - s_lrx / image_dpi) > bn_thresh:
                    staff_bars.append(s_lrx)

                for n, b in enumerate(staff_bars):
                    # calculate bounding box of the measure
                    m_uly = s_uly
                    m_lry = s_lry

                    m_lrx = b
                    if n == len(staff_bars) - 1:
                        m_lrx = s_lrx

                    if n == 0:
                        m_ulx = s_ulx
                    else:
                        m_ulx = staff_bars[n - 1]

                    # create staff element
                    zone = self._create_zone(m_ulx, m_uly, m_lrx, m_lry)
                    surface.addChild(zone)
                    if len(sg_hint) == 1 or len(staff_defs) == len(final_staff_grp.getDescendantsByName('staffDef')):
                        staff_n = str(i + 1)
                    else:
                        # take into consideration hidden staves
                        staff_n = i + self._calc_staff_num(len(staff_defs), [final_staff_grp])

                    staff = self._create_staff(staff_n, zone)

                    try:
                        s_measures[n].addChild(staff)
                    except IndexError:
                        # create a new measure
                        measure = self._create_measure(str(n_measure))
                        s_measures.append(measure)
                        section.addChild(measure)
                        measure.addChild(staff)
                        n_measure += 1

            # calculate min/max of measure/staff bounding boxes to get measure zone
            self._calc_measure_zone(s_measures)

            staff_offset += len(staff_defs)

            # add a system break, if necessary
            if s_ind + 1 < len(systems):
                sb = MeiElement('sb')
                section.addChild(sb)

    def _calc_staff_num(self, num_staves, staff_grps):
        '''
        In the case where there are hidden staves,
        search for the correct staff number within the staff
        group definition.
        '''

        if len(staff_grps) == 0:
            # termination condition (or no match found)
            return 0
        else:
            sg_staves = staff_grps[0].getChildrenByName('staffDef')
            sgs = staff_grps[0].getChildrenByName('staffGrp')
            if num_staves == len(sg_staves):
                # no need to look at subsequent staff groups
                n = int(sg_staves[0].getAttribute('n').value)
            else:
                n = self._calc_staff_num(num_staves, sgs)

            return n + self._calc_staff_num(num_staves, staff_grps[1:])

    def _calc_measure_zone(self, measures):
        '''
        Calculate the bounding box of the provided measures
        by calculating the min and max of the bounding boxes
        of the staves which compose the measure.
        '''

        # for each measure
        for m in measures:
            min_ulx = sys.maxint
            min_uly = sys.maxint
            max_lrx = -sys.maxint - 1
            max_lry = -sys.maxint - 1
            for s in m.getChildrenByName('staff'):
                # have to skip # at the beginning of the id ref since using URIs
                s_zone = self.meidoc.getElementById(s.getAttribute('facs').value[1:])
                ulx = int(s_zone.getAttribute('ulx').value)
                if ulx < min_ulx:
                    min_ulx = ulx

                uly = int(s_zone.getAttribute('uly').value)
                if uly < min_uly:
                    min_uly = uly

                lrx = int(s_zone.getAttribute('lrx').value)
                if lrx > max_lrx:
                    max_lrx = lrx

                lry = int(s_zone.getAttribute('lry').value)
                if lry > max_lry:
                    max_lry = lry

            m_zone = self._create_zone(min_ulx, min_uly, max_lrx, max_lry)
            m.addAttribute('facs', '#' + m_zone.getId())
            surface = self.meidoc.getElementsByName('surface')[0]
            surface.addChild(m_zone)

    def _create_header(self, rodan_version='0.1'):
        '''
        Create a meiHead element
        '''

        mei_head = MeiElement('meiHead')
        today = datetime.date.today().isoformat()

        app_name = 'RODAN/barlineFinder'

        # file description
        file_desc = MeiElement('fileDesc')

        title_stmt = MeiElement('titleStmt')
        title = MeiElement('title')
        resp_stmt = MeiElement('respStmt')
        corp_name = MeiElement('corpName')
        corp_name.setValue('Distributed Digital Music Archives and Libraries Lab (DDMAL)')
        title_stmt.addChild(title)
        title_stmt.addChild(resp_stmt)
        resp_stmt.addChild(corp_name)

        pub_stmt = MeiElement('pubStmt')
        resp_stmt = MeiElement('respStmt')
        corp_name = MeiElement('corpName')
        corp_name.setValue('Distributed Digital Music Archives and Libraries Lab (DDMAL)')
        pub_stmt.addChild(resp_stmt)
        resp_stmt.addChild(corp_name)

        mei_head.addChild(file_desc)
        file_desc.addChild(title_stmt)
        file_desc.addChild(pub_stmt)

        # encoding description
        encoding_desc = MeiElement('encodingDesc')
        app_info = MeiElement('appInfo')
        application = MeiElement('application')
        application.addAttribute('version', rodan_version)
        name = MeiElement('name')
        name.setValue(app_name)
        ptr = MeiElement('ptr')
        ptr.addAttribute('target', 'https://github.com/DDMAL/barlineFinder')

        mei_head.addChild(encoding_desc)
        encoding_desc.addChild(app_info)
        app_info.addChild(application)
        application.addChild(name)
        application.addChild(ptr)

        # revision description
        revision_desc = MeiElement('revisionDesc')
        change = MeiElement('change')
        change.addAttribute('n', '1')
        resp_stmt = MeiElement('respStmt')
        corp_name = MeiElement('corpName')
        corp_name.setValue('Distributed Digital Music Archives and Libraries Lab (DDMAL)')
        change_desc = MeiElement('changeDesc')
        ref = MeiElement('ref')
        ref.addAttribute('target', '#' + application.getId())
        ref.setValue(app_name)
        ref.setTail('.')
        p = MeiElement('p')
        p.addChild(ref)
        p.setValue('Encoded using ')
        date = MeiElement('date')
        date.setValue(today)

        mei_head.addChild(revision_desc)
        revision_desc.addChild(change)
        change.addChild(resp_stmt)
        resp_stmt.addChild(corp_name)
        change.addChild(change_desc)
        change_desc.addChild(p)
        change.addChild(date)

        return mei_head

    def _create_graphic(self, image_path, image_width, image_height):
        '''
        Create a graphic element.
        '''

        graphic = MeiElement('graphic')
        graphic.addAttribute('height', str(image_height))
        graphic.addAttribute('width', str(image_width))
        graphic.addAttribute('target', str(image_path))
        graphic.addAttribute('unit', 'px')

        return graphic

    def _create_staff_group(self, sg_list, staff_grp, n):
        '''
        Recursively create the staff group element from the parsed
        user input of the staff groupings
        '''
        if not sg_list:
            return staff_grp, n
        else:
            if type(sg_list[0]) is list:
                new_staff_grp, n = self._create_staff_group(sg_list[0], MeiElement('staffGrp'), n)
                staff_grp.addChild(new_staff_grp)
            else:
                # check for barthrough character
                if sg_list[0][-1] == '|':
                    # the barlines go through all the staves in the staff group
                    staff_grp.addAttribute('barthru', 'true')
                    # remove the barthrough character, should now only be an integer
                    sg_list[0] = sg_list[0][:-1]

                n_staff_defs = int(sg_list[0])
                # get current staffDef number
                for i in range(n_staff_defs):
                    staff_def = MeiElement('staffDef')
                    staff_def.addAttribute('n', str(n + i + 1))
                    staff_def.addAttribute('lines', '5')
                    staff_grp.addChild(staff_def)
                n += n_staff_defs

            return self._create_staff_group(sg_list[1:], staff_grp, n)

    def _create_staff(self, n, zone):
        '''
        Create a staff element, and attach a zone reference to it
        '''

        staff = MeiElement('staff')
        staff.addAttribute('n', str(n))
        staff.addAttribute('facs', '#' + zone.getId())

        return staff

    def _create_measure(self, n, zone=None):
        '''
        Create a measure element and attach a zone reference to it.
        The zone element is optional, since the zone of the measure is
        calculated once all of the staves within a measure have been added
        to the MEI.
        '''

        measure = MeiElement('measure')
        measure.addAttribute('n', str(n))

        if zone is not None:
            measure.addAttribute('facs', '#' + zone.getId())

        return measure

    def _create_zone(self, ulx, uly, lrx, lry):
        '''
        Create a zone element
        '''

        zone = MeiElement('zone')
        zone.addAttribute('ulx', str(ulx))
        zone.addAttribute('uly', str(uly))
        zone.addAttribute('lrx', str(lrx))
        zone.addAttribute('lry', str(lry))

        return zone

    def output_mei(self, output_path):
        '''
        Write the generated mei to disk
        '''

        # output mei file
        XmlExport.meiDocumentToFile(self.meidoc, output_path)

    def get_wrapped_mei(self):
        '''
        Return the generated mei document
        '''

        mw = MeiWrapper(self.meidoc)

        return mw