def test_elementsbyname(self): mei = MeiElement("mei") mus = MeiElement("music") body = MeiElement("body") staff = MeiElement("staff") staff2 = MeiElement("staff") n1 = MeiElement("note") 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() doc.root = mei notes = doc.getElementsByName("note") self.assertEqual(4, len(notes)) rests = doc.getElementsByName("rest") self.assertEqual(0, len(rests)) n5 = MeiElement("note") staff2.addChild(n5) notes_new = doc.getElementsByName('note') self.assertEqual(5, len(notes_new))
def test_documentpointers(self): mei = MeiElement("mei") mus = MeiElement("music") body = MeiElement("body") staff = MeiElement("staff") staff2 = MeiElement("staff") n1 = MeiElement("note") n2 = MeiElement("note") n3 = MeiElement("note") self.assertEqual(None, mei.document) mei.addChild(mus) self.assertEqual(None, mus.document) doc = MeiDocument() mus.addChild(body) doc.root = mei self.assertEqual(doc, mei.document) self.assertEqual(doc, mus.document) self.assertEqual(doc, body.document) self.assertEqual(None, staff.document) body.addChild(staff) self.assertEqual(doc, staff.document)
def test_getpositionindocument(self): m = MeiElement("mei") m1 = MeiElement("music") musicid = m1.id b1 = MeiElement("body") s1 = MeiElement("staff") n1 = MeiElement("note") noteid = n1.id n2 = MeiElement("note") n3 = MeiElement("note") n4 = MeiElement("note") note4id = n4.id m.addChild(m1) m1.addChild(b1) b1.addChild(s1) s1.addChild(n1) s1.addChild(n2) s1.addChild(n3) doc = MeiDocument() doc.root = m self.assertEqual(4, n1.getPositionInDocument()) # an unattached element will return -1 self.assertEqual(-1, n4.getPositionInDocument())
def test_effective_meter(self): music = MeiElement('music') body = MeiElement('body') mdiv = MeiElement('mdiv') score = MeiElement('score') sctn = MeiElement('section') scD1 = MeiElement('scoreDef') scD2 = MeiElement('scoreDef') stG1 = MeiElement('staffGrp') stG2 = MeiElement('staffGrp') stD1 = MeiElement('staffDef') stD2 = MeiElement('staffDef') m1 = MeiElement('measure') m2 = MeiElement('measure') l1 = MeiElement('layer') l2 = MeiElement('layer') s1 = MeiElement('staff') s2 = MeiElement('staff') mR1 = MeiElement('mRest') mR2 = MeiElement('mRest') scD1.addAttribute('meter.unit', '2') stD1.addAttribute('meter.count', '2') stD2.addAttribute('meter.count', '3') s1.addAttribute('n', '1') music.addChild(body) body.addChild(mdiv) mdiv.addChild(score) score.addChild(sctn) sctn.addChild(scD1) scD1.addChild(stG1) stG1.addChild(stD1) sctn.addChild(m1) m1.addChild(s1) s1.addChild(l1) l1.addChild(mR1) sctn.addChild(scD2) scD2.addChild(stG2) stG2.addChild(stD2) sctn.addChild(m2) m2.addChild(s2) s2.addChild(l2) l2.addChild(mR2) doc = MeiDocument() doc.setRootElement(music) meter1 = utilities.effective_meter(mR1) meter2 = utilities.effective_meter(mR2) self.assertEqual(meter1.count, '2') self.assertEqual(meter1.unit, '2') self.assertEqual(meter2.count, '3') self.assertEqual(meter2.unit, '2')
def test_documentwritefailure(self): doc = MeiDocument() root = MeiElement("mei") root.id = "myid" doc.root = root with self.assertRaises(FileWriteFailureException) as cm: ret = XmlExport.meiDocumentToFile(doc, "C:/StupidPath") self.assertTrue(isinstance(cm.exception, FileWriteFailureException))
def test_effective_meter(self): music = MeiElement("music") body = MeiElement("body") mdiv = MeiElement("mdiv") score = MeiElement("score") sctn = MeiElement("section") scD1 = MeiElement("scoreDef") scD2 = MeiElement("scoreDef") stG1 = MeiElement("staffGrp") stG2 = MeiElement("staffGrp") stD1 = MeiElement("staffDef") stD2 = MeiElement("staffDef") m1 = MeiElement("measure") m2 = MeiElement("measure") l1 = MeiElement("layer") l2 = MeiElement("layer") s1 = MeiElement("staff") s2 = MeiElement("staff") mR1 = MeiElement("mRest") mR2 = MeiElement("mRest") scD1.addAttribute("meter.unit", "2") stD1.addAttribute("meter.count", "2") stD2.addAttribute("meter.count", "3") s1.addAttribute("n", "1") music.addChild(body) body.addChild(mdiv) mdiv.addChild(score) score.addChild(sctn) sctn.addChild(scD1) scD1.addChild(stG1) stG1.addChild(stD1) sctn.addChild(m1) m1.addChild(s1) s1.addChild(l1) l1.addChild(mR1) sctn.addChild(scD2) scD2.addChild(stG2) stG2.addChild(stD2) sctn.addChild(m2) m2.addChild(s2) s2.addChild(l2) l2.addChild(mR2) doc = MeiDocument() doc.setRootElement(music) meter1 = utilities.effective_meter(mR1) meter2 = utilities.effective_meter(mR2) self.assertEqual(meter1.count, "2") self.assertEqual(meter1.unit, "2") self.assertEqual(meter2.count, "3") self.assertEqual(meter2.unit, "2")
def test_exporttostring(self): doc = MeiDocument() root = MeiElement("mei") root.id = "myid" doc.root = root expected = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<mei xml:id=\"myid\" xmlns=\"http://www.music-encoding.org/ns/mei\" meiversion=\"2013\" />\n" ret = documentToText(doc) self.assertEqual(expected, ret)
def test_dur_in_semibreves_mRests(self): music = MeiElement('music') body = MeiElement('body') mdiv = MeiElement('mdiv') score = MeiElement('score') sctn = MeiElement('section') scD1 = MeiElement('scoreDef') scD2 = MeiElement('scoreDef') stG1 = MeiElement('staffGrp') stG2 = MeiElement('staffGrp') stD1 = MeiElement('staffDef') stD2 = MeiElement('staffDef') m1 = MeiElement('measure') m2 = MeiElement('measure') l1 = MeiElement('layer') l2 = MeiElement('layer') s1 = MeiElement('staff') s2 = MeiElement('staff') mR1 = MeiElement('mRest') mR2 = MeiElement('mRest') scD1.addAttribute('meter.unit', '2') stD1.addAttribute('meter.count', '2') stD2.addAttribute('meter.count', '3') s1.addAttribute('n', '1') music.addChild(body) body.addChild(mdiv) mdiv.addChild(score) score.addChild(sctn) sctn.addChild(scD1) scD1.addChild(stG1) stG1.addChild(stD1) sctn.addChild(m1) m1.addChild(l1) l1.addChild(s1) s1.addChild(mR1) sctn.addChild(scD2) scD2.addChild(stG2) stG2.addChild(stD2) sctn.addChild(m2) m2.addChild(l2) l2.addChild(s2) s2.addChild(mR2) doc = MeiDocument() doc.setRootElement(music) self.assertEqual(doc.lookBack(body, 'music'), music) self.assertEqual(utilities.dur_in_semibreves(mR1), 1) self.assertEqual(utilities.dur_in_semibreves(mR2), 1.5)
def test_dur_in_semibreves_mRests(self): music = MeiElement("music") body = MeiElement("body") mdiv = MeiElement("mdiv") score = MeiElement("score") sctn = MeiElement("section") scD1 = MeiElement("scoreDef") scD2 = MeiElement("scoreDef") stG1 = MeiElement("staffGrp") stG2 = MeiElement("staffGrp") stD1 = MeiElement("staffDef") stD2 = MeiElement("staffDef") m1 = MeiElement("measure") m2 = MeiElement("measure") l1 = MeiElement("layer") l2 = MeiElement("layer") s1 = MeiElement("staff") s2 = MeiElement("staff") mR1 = MeiElement("mRest") mR2 = MeiElement("mRest") scD1.addAttribute("meter.unit", "2") stD1.addAttribute("meter.count", "2") stD2.addAttribute("meter.count", "3") s1.addAttribute("n", "1") music.addChild(body) body.addChild(mdiv) mdiv.addChild(score) score.addChild(sctn) sctn.addChild(scD1) scD1.addChild(stG1) stG1.addChild(stD1) sctn.addChild(m1) m1.addChild(l1) l1.addChild(s1) s1.addChild(mR1) sctn.addChild(scD2) scD2.addChild(stG2) stG2.addChild(stD2) sctn.addChild(m2) m2.addChild(l2) l2.addChild(s2) s2.addChild(mR2) doc = MeiDocument() doc.setRootElement(music) self.assertEqual(doc.lookBack(body, "music"), music) self.assertEqual(utilities.dur_in_semibreves(mR1), 1) self.assertEqual(utilities.dur_in_semibreves(mR2), 1.5)
def test_documentwritefailure(self): doc = MeiDocument() root = MeiElement("mei") root.id = "myid" doc.root = root with self.assertRaises(FileWriteFailureException) as cm: ret = documentToFile(doc, "C:/StupidPath") self.assertTrue(isinstance(cm.exception, FileWriteFailureException))
def test_setdocument(self): m = MeiElement("mei") doc = MeiDocument() with self.assertRaises(DocumentRootNotSetException) as cm: m.setDocument(doc) self.assertTrue(isinstance(cm.exception, DocumentRootNotSetException)) doc.setRootElement(m) self.assertEqual(doc.root, m)
def test_exportcomment(self): doc = MeiDocument() root = MeiElement("mei") root.id = "myid" doc.root = root comment = MeiElement("_comment") comment.value = "comment" comment.tail = "t" root.addChild(comment) expected = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<mei xml:id=\"myid\" xmlns=\"http://www.music-encoding.org/ns/mei\" meiversion=\"2013\">\n\t<!--comment-->t</mei>\n" ret = documentToText(doc) self.assertEqual(expected, ret)
def test_exportnamespace(self): doc = MeiDocument() root = MeiElement("mei") root.id = "myid" doc.root = root xlink = MeiNamespace("xlink", "http://www.w3.org/1999/xlink") attr = MeiAttribute(xlink, "title", "my awesome thing") root.addAttribute(attr) expected = "<?xml version=\"1.0\"?>\n<mei xmlns=\"http://www.music-encoding.org/ns/mei\" \ xmlns:xlink=\"http://www.w3.org/1999/xlink\" xml:id=\"myid\" xlink:title=\"my awesome thing\" meiversion=\"2013\"/>\n"; ret = XmlExport.meiDocumentToText(doc) self.assertEqual(expected, ret)
def test_exportvalueandtail(self): doc = MeiDocument() root = MeiElement("mei") root.id = "myid" doc.root = root note = MeiElement("note") note.id = "noteid" note.value = "value" note.tail = "tail" root.addChild(note) expected = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<mei xml:id=\"myid\" xmlns=\"http://www.music-encoding.org/ns/mei\" meiversion=\"2013\">\n\t<note xml:id=\"noteid\">value</note>tail</mei>\n" ret = documentToText(doc) self.assertEqual(expected, ret)
def test_getnamespace(self): doc = MeiDocument() ns = MeiNamespace("prefix", "http://example.com/ns") self.assertEqual("http://www.music-encoding.org/ns/mei", doc.getNamespaces()[0].href) doc.addNamespace(ns) self.assertEqual(2, len(doc.getNamespaces())) self.assertEqual(ns, doc.getNamespace("http://example.com/ns")) self.assertTrue( doc.hasNamespace("http://www.music-encoding.org/ns/mei")) self.assertFalse(doc.hasNamespace("http://www.mcgill.ca"))
def test_lookback(self): m = MeiElement("mei") m1 = MeiElement("music") b1 = MeiElement("body") s1 = MeiElement("staff") n1 = MeiElement("note") doc = MeiDocument() doc.setRootElement(m) m.addChild(m1) m1.addChild(b1) b1.addChild(s1) s1.addChild(n1) self.assertEqual(m1.lookBack('mei'), m) self.assertEqual(s1.lookBack('mei'), m)
def test_getnamespace(self): doc = MeiDocument() ns = MeiNamespace("prefix", "http://example.com/ns") self.assertEqual("http://www.music-encoding.org/ns/mei", doc.getNamespaces()[0].href) doc.addNamespace(ns) self.assertEqual(2, len(doc.getNamespaces())) self.assertEqual(ns, doc.getNamespace("http://example.com/ns")) self.assertTrue(doc.hasNamespace("http://www.music-encoding.org/ns/mei")) self.assertFalse(doc.hasNamespace("http://www.mcgill.ca"))
def test_exportProcessingInstructions(self): procinst = XmlInstructions() xpi1 = XmlProcessingInstruction("xml-model", "href=\"mei-2012.rng\" type=\"application/xml\" schematypens=\"http://purl.oclc.org/dsdl/schematron\"") xpi2 = XmlProcessingInstruction("xml-stylesheet", "href=\"mei-2012.rng\" type=\"application/xml\" schematypens=\"http://purl.oclc.org/dsdl/schematron\"") procinst.extend([xpi1, xpi2]) doc = MeiDocument() root = MeiElement("mei") root.id = "myid" doc.root = root ret = XmlExport.meiDocumentToText(doc, procinst) expected = "<?xml version=\"1.0\"?>\n<?xml-model href=\"mei-2012.rng\" type=\"application/xml\" \ schematypens=\"http://purl.oclc.org/dsdl/schematron\"?>\n<?xml-stylesheet href=\"mei-2012.rng\" type=\"application/xml\" \ schematypens=\"http://purl.oclc.org/dsdl/schematron\"?>\n<mei xmlns=\"http://www.music-encoding.org/ns/mei\" \ xml:id=\"myid\" meiversion=\"2013\"/>\n" self.assertEqual(expected, ret)
def test_printElement(self): m = MeiElement("mei") m1 = MeiElement("music") b1 = MeiElement("body") s1 = MeiElement("staff") n1 = MeiElement("note") n2 = MeiElement("note") m.addAttribute("meiversion", "2013") doc = MeiDocument() doc.setRootElement(m) m.addChild(m1) m1.addChild(b1) b1.addChild(s1) s1.addChild(n1) s1.addChild(n2) n1.addAttribute('headshape', 'diamond') n1.addAttribute('pname', 'c') n2.addAttribute('pname', 'd') m.printElement()
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))
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 generate_base_document(): ''' Generates a generic template for an MEI document for neume notation. Currently a bit of this is hardcoded and should probably be made more customizable. ''' meiDoc = MeiDocument("4.0.0") mei = MeiElement("mei") mei.addAttribute("meiversion", "4.0.0") meiDoc.root = mei # placeholder meiHead meihead = MeiElement('meiHead') mei.addChild(meihead) fileDesc = MeiElement('fileDesc') meihead.addChild(fileDesc) titleSt = MeiElement('titleStmt') fileDesc.addChild(titleSt) title = MeiElement('title') titleSt.addChild(title) title.setValue('MEI Encoding Output (%s)' % __version__) pubStmt = MeiElement('pubStmt') fileDesc.addChild(pubStmt) music = MeiElement("music") mei.addChild(music) facs = MeiElement("facsimile") music.addChild(facs) surface = MeiElement("surface") facs.addChild(surface) body = MeiElement('body') music.addChild(body) mdiv = MeiElement('mdiv') body.addChild(mdiv) score = MeiElement('score') mdiv.addChild(score) scoreDef = MeiElement('scoreDef') score.addChild(scoreDef) staffGrp = MeiElement('staffGrp') scoreDef.addChild(staffGrp) staffDef = MeiElement('staffDef') staffGrp.addChild(staffDef) # these hardcoded attributes define a single staff with 4 lines, neume notation, with a default c clef staffDef.addAttribute('n', '1') staffDef.addAttribute('lines', '4') staffDef.addAttribute('notationtype', 'neume') staffDef.addAttribute('clef.line', '3') staffDef.addAttribute('clef.shape', 'C') section = MeiElement('section') score.addChild(section) staff = MeiElement('staff') section.addChild(staff) layer = MeiElement('layer') staff.addChild(layer) return meiDoc, surface, layer
def test_flattenedtree(self): mei = MeiElement("mei") mus = MeiElement("music") body = MeiElement("body") staff = MeiElement("staff") staff2 = MeiElement("staff") n1 = MeiElement("note") n2 = MeiElement("note") n3 = MeiElement("note") doc = MeiDocument() mei.addChild(mus) doc.root = mei mus.addChild(body) body.addChild(staff) body.addChild(staff2) staff.addChild(n1) staff.addChild(n2) staff2.addChild(n3) doc.lookBack(n2, "mei") self.assertEqual(8, len(doc.getFlattenedTree())) staff.removeChild(n2) self.assertEqual(7, len(doc.getFlattenedTree())) self.assertEqual(staff2, doc.getFlattenedTree()[5]) staff.removeChildrenWithName("note") self.assertEqual(6, len(doc.getFlattenedTree())) body.deleteAllChildren() self.assertEqual(3, len(doc.getFlattenedTree())) children = MeiElementList() staff3 = MeiElement("staff") staff4 = MeiElement("staff") children.append(staff3) children.append(staff4) body.children = children self.assertEqual(5, len(doc.getFlattenedTree())) elements = [mei, mus, body, staff3, staff4] for i, el in enumerate(doc.getFlattenedTree()): self.assertEqual(elements[i], doc.getFlattenedTree()[i])
def write_mei(self, notes, output_path=None): # begin constructing mei document meidoc = MeiDocument() mei = MeiElement('mei') meidoc.setRootElement(mei) mei_head = MeiElement('meiHead') mei.addChild(mei_head) music = MeiElement('music') body = MeiElement('body') mdiv = MeiElement('mdiv') score = MeiElement('score') score_def = MeiElement('scoreDef') # assume 4/4 time signature meter_count = 4 meter_unit = 4 score_def.addAttribute('meter.count', str(meter_count)) score_def.addAttribute('meter.unit', str(meter_unit)) staff_def = MeiElement('staffDef') staff_def.addAttribute('n', '1') staff_def.addAttribute('label.full', 'Electric Guitar') staff_def.addAttribute('clef.shape', 'TAB') instr_def = MeiElement('instrDef') instr_def.addAttribute('n', 'Electric_Guitar') instr_def.addAttribute('midi.channel', '1') instr_def.addAttribute('midi.instrnum', '28') mei.addChild(music) music.addChild(body) body.addChild(mdiv) mdiv.addChild(score) score.addChild(score_def) score_def.addChild(staff_def) staff_def.addChild(instr_def) section = MeiElement('section') score.addChild(section) # another score def score_def = MeiElement('scoreDef') score_def.addAttribute('meter.count', str(meter_count)) score_def.addAttribute('meter.unit', str(meter_unit)) section.addChild(score_def) # start writing pitches to file note_container = None for i, frame_n in enumerate(notes): if i % meter_count == 0: measure = MeiElement('measure') measure.addAttribute('n', str(int(i/meter_count + 1))) staff = MeiElement('staff') staff.addAttribute('n', '1') layer = MeiElement('layer') layer.addAttribute('n', '1') section.addChild(measure) measure.addChild(staff) staff.addChild(layer) note_container = layer if len(frame_n) > 1: chord = MeiElement('chord') for n in frame_n: note = MeiElement('note') pname = n['pname'][0].upper() note.addAttribute('pname', pname) note.addAttribute('oct', str(n['oct'])) if len(n['pname']) > 1 and n['pname'][1] == '#': # there is an accidental note.addAttribute('accid.ges', 's') note.addAttribute('dur', str(meter_unit)) chord.addChild(note) note_container.addChild(chord) else: n = frame_n[0] note = MeiElement('note') pname = n['pname'][0].upper() note.addAttribute('pname', pname) note.addAttribute('oct', str(n['oct'])) if len(n['pname']) > 1 and n['pname'][1] == '#': # there is an accidental note.addAttribute('accid.ges', 's') note.addAttribute('dur', str(meter_unit)) note_container.addChild(note) if output_path is not None: XmlExport.meiDocumentToFile(meidoc, output_path) else: return XmlExport.meiDocumentToText(meidoc)
def test_setversion(self): doc = MeiDocument() self.assertEqual("2012", doc.version)
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
def test_getroot(self): doc = MeiDocument() mei = MeiElement("mei") doc.root = mei self.assertEqual(mei, doc.root)
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()))
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
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
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)
def test_flattenedtree(self): mei = MeiElement("mei") mus = MeiElement("music") body = MeiElement("body") staff = MeiElement("staff") staff2 = MeiElement("staff") n1 = MeiElement("note") n2 = MeiElement("note") n3 = MeiElement("note") doc = MeiDocument() mei.addChild(mus) doc.root = mei mus.addChild(body) body.addChild(staff) body.addChild(staff2) staff.addChild(n1) staff.addChild(n2) staff2.addChild(n3) doc.lookBack(n2, "mei") self.assertEqual(8, len(doc.getFlattenedTree())) staff.removeChild(n2) self.assertEqual(7, len(doc.getFlattenedTree())) self.assertEqual(staff2, doc.getFlattenedTree()[5]) staff.removeChildrenWithName("note") self.assertEqual(6, len(doc.getFlattenedTree())) body.deleteAllChildren() self.assertEqual(3, len(doc.getFlattenedTree())) children = MeiElementList() staff3 = MeiElement("staff") staff4 = MeiElement("staff") children.append(staff3) children.append(staff4) body.children = children self.assertEqual(5, len(doc.getFlattenedTree())) elements = [mei, mus, body, staff3, staff4] for i,el in enumerate(doc.getFlattenedTree()): self.assertEqual(elements[i], doc.getFlattenedTree()[i])
def write_mei(self, notes, audio_path, output_path=None): from pymei import MeiDocument, MeiElement, XmlExport # combine features with the same timestamps note_events = [] for n in notes: ts = n.timestamp.toSeconds() note_num = int(n.values[0]) + 1 # it looks like everything is transposed a semitone down ... transposing up # if the last timestamp is equal to this timestamp, combine into a chord if len(note_events) > 0 and note_events[-1][0] == ts: note_events[-1][1].append(note_num) else: note_events.append([ts, [note_num]]) # sort by timestamp in ascending order note_events = sorted(note_events, key=lambda n: n[0]) # begin constructing mei document meidoc = MeiDocument() mei = MeiElement('mei') meidoc.setRootElement(mei) music = MeiElement('music') timeline = MeiElement('timeline') timeline.addAttribute('avref', str(audio_path)) body = MeiElement('body') mdiv = MeiElement('mdiv') score = MeiElement('score') score_def = MeiElement('scoreDef') # assume 4/4 time signature meter_count = 4 meter_unit = 4 score_def.addAttribute('meter.count', str(meter_count)) score_def.addAttribute('meter.unit', str(meter_unit)) staff_def = MeiElement('staffDef') staff_def.addAttribute('n', '1') staff_def.addAttribute('label.full', 'Electric Guitar') staff_def.addAttribute('clef.shape', 'TAB') instr_def = MeiElement('instrDef') instr_def.addAttribute('n', 'Electric_Guitar') instr_def.addAttribute('midi.channel', '1') instr_def.addAttribute('midi.instrnum', '28') mei.addChild(music) music.addChild(timeline) music.addChild(body) body.addChild(mdiv) mdiv.addChild(score) score.addChild(score_def) score_def.addChild(staff_def) staff_def.addChild(instr_def) section = MeiElement('section') score.addChild(section) # another score def score_def = MeiElement('scoreDef') score_def.addAttribute('meter.count', str(meter_count)) score_def.addAttribute('meter.unit', str(meter_unit)) section.addChild(score_def) # start writing pitches to file note_container = None for i, note_event in enumerate(note_events): if i % meter_count == 0: measure = MeiElement('measure') measure.addAttribute('n', str(int(i/meter_count + 1))) staff = MeiElement('staff') staff.addAttribute('n', '1') layer = MeiElement('layer') layer.addAttribute('n', '1') section.addChild(measure) measure.addChild(staff) staff.addChild(layer) note_container = layer ts = note_event[0] when = MeiElement('when') if i == 0: timeline.addAttribute('origin', when.getId()) when.addAttribute('absolute', str(ts)) timeline.addChild(when) notes = note_event[1] if len(notes) > 1: chord = MeiElement('chord') chord.addAttribute('when', when.getId()) for n in notes: note = MeiElement('note') note_info = PolyTrans.midi_map[n] pname = note_info[0] oct = note_info[1] note.addAttribute('pname', pname[0]) note.addAttribute('oct', str(oct)) if len(pname) > 1 and pname[-1] == '#': # there is an accidental note.addAttribute('accid.ges', 's') note.addAttribute('dur', str(meter_unit)) chord.addChild(note) note_container.addChild(chord) else: n = notes[0] note = MeiElement('note') note.addAttribute('when', when.getId()) note_info = PolyTrans.midi_map[n] pname = note_info[0] oct = note_info[1] note.addAttribute('pname', pname[0]) note.addAttribute('oct', str(oct)) if len(pname) > 1 and pname[-1] == '#': # there is an accidental note.addAttribute('accid.ges', 's') note.addAttribute('dur', str(meter_unit)) note_container.addChild(note) if output_path is not None: XmlExport.meiDocumentToFile(meidoc, output_path) else: return XmlExport.meiDocumentToText(meidoc)
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)
class GroundTruthBarlineDataConverter: ''' Convert the stored measures of the Ground Truth System to MEI. TODO: Only considers barpanels, not staffpanels ''' def __init__(self, staffbb, barbb, verbose=False): ''' Constructor of converter. staffbb are the boxes bouding the staves (or systems) barbb are the boxes bounding the measures (or bars) These are passed in as lists of Rects defined in gtruth-rect ''' # Print errors / messages self.verbose = verbose # The coordinates of the staves stored in the format: # [index, top-corner x, top-corner y, bottom-corner x, bottom-corner y] # (note this is a python list) # To get an idea of what the staves are, image a piano score where each # system consists (usually) of a treble staff and bass staff. This # system is divided into measures and you can divide the measure into # two parts: the box encompassing the top staff within the measure and # the box encompassing the bottom staff within the measure. These two # boxes are what is meant by staff bounding boxes. self.staffbb = staffbb if self.verbose: sys.stderr.write("Staff bounding boxes" + repr(self.staffbb)) # The coordinates of the bars stored in the format: # (index, top-corner x, top-corner y, bottom-corner x, bottom-corner y) # (note this is a python tuple) # The differentiation is for compatibility with meicreate.py # Bars are the measures of the system. That means for an orchestral # score the bars can be very tall and thin. self.barbb = barbb if self.verbose: sys.stderr.write("Bar bounding boxes" + repr(self.barbb)) self.meidoc = None; def bardata_to_mei(self, imagepath, imagewidth, imageheight, imagedpi=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(imagepath, imagewidth, imageheight,\ imagedpi) surface.addChild(graphic) mei.addChild(music) music.addChild(facsimile) facsimile.addChild(surface) music.addChild(body) body.addChild(mdiv) mdiv.addChild(score) score.addChild(score_def) score.addChild(section) barmeasdict = dict() # Only add the bar bounding boxes for bar in self.barbb: # Zone is the coordinates where the measure is found in the image zone = self._create_zone(\ int(bar.pos[0]),int(bar.pos[1]),\ int(bar.pos[0])+int(bar.size[0]),\ int(bar.pos[1])+int(bar.size[1])); # Zone is a child element of the surface surface.addChild(zone) # The measure is found in the zone measure = self._create_measure(bar.number,zone); section.addChild(measure); # store which measure this bar corresponds to so we can add it as a # child to the staff bounding boxes later TODO: We don't do this # right now barmeasdict[bar] = measure # Add the staff bounding boxes # We don't do this but when we do, the staff needs to look up its # children in barmeasdict def _create_header(self, rodan_version='0.1'): ''' Create a meiHead element ''' mei_head = MeiElement('meiHead') today = datetime.date.today().isoformat() app_name = 'gtruth_write_mei' # 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, image_dpi): ''' 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('resolution', str(image_dpi)) graphic.addAttribute('unit', 'px') return graphic 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 if self.meidoc == None: raise Warning('The MEI document has not yet been created'); return XmlExport.meiDocumentToFile(self.meidoc, output_path)
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 bardata_to_mei(self, imagepath, imagewidth, imageheight, imagedpi=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(imagepath, imagewidth, imageheight,\ imagedpi) surface.addChild(graphic) mei.addChild(music) music.addChild(facsimile) facsimile.addChild(surface) music.addChild(body) body.addChild(mdiv) mdiv.addChild(score) score.addChild(score_def) score.addChild(section) barmeasdict = dict() # Only add the bar bounding boxes for bar in self.barbb: # Zone is the coordinates where the measure is found in the image zone = self._create_zone(\ int(bar.pos[0]),int(bar.pos[1]),\ int(bar.pos[0])+int(bar.size[0]),\ int(bar.pos[1])+int(bar.size[1])); # Zone is a child element of the surface surface.addChild(zone) # The measure is found in the zone measure = self._create_measure(bar.number,zone); section.addChild(measure); # store which measure this bar corresponds to so we can add it as a # child to the staff bounding boxes later TODO: We don't do this # right now barmeasdict[bar] = measure
def write_mei(self, notes, output_path=None): # begin constructing mei document meidoc = MeiDocument() mei = MeiElement('mei') meidoc.setRootElement(mei) mei_head = MeiElement('meiHead') mei.addChild(mei_head) music = MeiElement('music') body = MeiElement('body') mdiv = MeiElement('mdiv') score = MeiElement('score') score_def = MeiElement('scoreDef') # assume 4/4 time signature meter_count = 4 meter_unit = 4 score_def.addAttribute('meter.count', str(meter_count)) score_def.addAttribute('meter.unit', str(meter_unit)) staff_def = MeiElement('staffDef') staff_def.addAttribute('n', '1') staff_def.addAttribute('label.full', 'Electric Guitar') staff_def.addAttribute('clef.shape', 'TAB') instr_def = MeiElement('instrDef') instr_def.addAttribute('n', 'Electric_Guitar') instr_def.addAttribute('midi.channel', '1') instr_def.addAttribute('midi.instrnum', '28') mei.addChild(music) music.addChild(body) body.addChild(mdiv) mdiv.addChild(score) score.addChild(score_def) score_def.addChild(staff_def) staff_def.addChild(instr_def) section = MeiElement('section') score.addChild(section) # another score def score_def = MeiElement('scoreDef') score_def.addAttribute('meter.count', str(meter_count)) score_def.addAttribute('meter.unit', str(meter_unit)) section.addChild(score_def) # start writing pitches to file note_container = None for i, frame_n in enumerate(notes): if i % meter_count == 0: measure = MeiElement('measure') measure.addAttribute('n', str(int(i / meter_count + 1))) staff = MeiElement('staff') staff.addAttribute('n', '1') layer = MeiElement('layer') layer.addAttribute('n', '1') section.addChild(measure) measure.addChild(staff) staff.addChild(layer) note_container = layer if len(frame_n) > 1: chord = MeiElement('chord') for n in frame_n: note = MeiElement('note') pname = n['pname'][0].upper() note.addAttribute('pname', pname) note.addAttribute('oct', str(n['oct'])) if len(n['pname']) > 1 and n['pname'][1] == '#': # there is an accidental note.addAttribute('accid.ges', 's') note.addAttribute('dur', str(meter_unit)) chord.addChild(note) note_container.addChild(chord) else: n = frame_n[0] note = MeiElement('note') pname = n['pname'][0].upper() note.addAttribute('pname', pname) note.addAttribute('oct', str(n['oct'])) if len(n['pname']) > 1 and n['pname'][1] == '#': # there is an accidental note.addAttribute('accid.ges', 's') note.addAttribute('dur', str(meter_unit)) note_container.addChild(note) if output_path is not None: XmlExport.meiDocumentToFile(meidoc, output_path) else: return XmlExport.meiDocumentToText(meidoc)
def test_documentrootnotset(self): doc = MeiDocument() with self.assertRaises(DocumentRootNotSetException) as cm: ret = documentToText(doc) self.assertTrue(isinstance(cm.exception, DocumentRootNotSetException))
def __init__(self): """ Setup some connecting apps. Connecting apps are: 1. they are in subsequent measures, 2. they are on the same staff, 3. there isn't any notes or rests are in between them and 4. their rdgs represent the exact same set of sources Apps need to have xml:id. Only apps lower than <layer> level will be linked together. """ music = MeiElement('music') body = MeiElement('body') mdiv = MeiElement('mdiv') score = MeiElement('score') section = MeiElement('section') m1 = MeiElement('measure') m2 = MeiElement('measure') s1 = MeiElement('staff') s2 = MeiElement('staff') l1 = MeiElement('layer') l2 = MeiElement('layer') app1 = MeiElement('app') app2 = MeiElement('app') app3 = MeiElement('app') lem1 = MeiElement('lem') lem2 = MeiElement('lem') lem3 = MeiElement('lem') rdg1_A = MeiElement('rdg') rdg1_B = MeiElement('rdg') rdg2_A = MeiElement('rdg') rdg2_B = MeiElement('rdg') rdg1_A.addAttribute('source', "SRC-A") rdg1_B.addAttribute('source', "SRC-B") rdg2_A.addAttribute('source', "SRC-A") rdg2_B.addAttribute('source', "SRC-B") music.addChild(body) body.addChild(mdiv) mdiv.addChild(score) score.addChild(section) section.addChild(m1) m1.addChild(s1) s1.addChild(l1) section.addChild(m2) m2.addChild(s2) s2.addChild(l2) """ app1 and app2 are connecting apps in m1 and m2 """ l1.addChild(app1) app1.addChild(lem1) app1.addChild(rdg1_A) app1.addChild(rdg1_B) l2.addChild(app2) app2.addChild(lem2) app2.addChild(rdg2_A) app2.addChild(rdg2_B) self.music = music self.app1 = app1 self.app2 = app2 doc = MeiDocument() doc.setRootElement(music)
def _initMEI(self): """Initialize a new MEI document Sets the attributes meiDoc, surface, and initLayer """ self.meiDoc = MeiDocument() root = MeiElement("mei") root.id = generate_MEI_ID() self.meiDoc.root = root #needs meiHead here meiHead = MeiElement('meiHead') fileDesc = MeiElement('fileDesc') titleStmt = MeiElement('titleStmt') title = MeiElement('title') pubStmt = MeiElement('pubStmt') date = MeiElement('date') encodingDesc = MeiElement('encodingDesc') projectDesc = MeiElement('projectDesc') p = MeiElement('p') music = MeiElement('music') facsimile = MeiElement('facsimile') self.surface = MeiElement('surface') # Label the surface with the name of the input file, which could help # identify the original image label = os.path.basename(os.path.splitext(self.xmlFile)[0]) self.surface.addAttribute(MeiAttribute('label', label)) #systems get added to page #neumes get added to systems body = MeiElement('body') mdiv = MeiElement('mdiv') pages = MeiElement('pages') page = MeiElement('page') page.id = generate_MEI_ID() initSystem = MeiElement('system') initSystem.id = generate_MEI_ID() initStaff = MeiElement('staff') initStaff.id = generate_MEI_ID() self.initLayer = MeiElement('layer') self.initLayer.id = generate_MEI_ID() root.addChild(meiHead) meiHead.addChild(fileDesc) fileDesc.addChild(titleStmt) titleStmt.addChild(title) fileDesc.addChild(pubStmt) pubStmt.addChild(date) meiHead.addChild(encodingDesc) encodingDesc.addChild(projectDesc) projectDesc.addChild(p) root.addChild(music) music.addChild(facsimile) facsimile.addChild(self.surface) music.addChild(body) body.addChild(mdiv) mdiv.addChild(pages) pages.addChild(page) page.addChild(initSystem) initSystem.addChild(initStaff) initStaff.addChild(self.initLayer)