예제 #1
0
    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))
예제 #2
0
    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))
예제 #3
0
class BarlineDataConverter:
    '''
    Convert the output of the barline detection algorithm
    to MEI.
    '''

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            staff_offset += len(staff_defs)

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

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

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

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

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

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

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

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

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

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

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

        app_name = 'RODAN/barlineFinder'

        # file description
        file_desc = MeiElement('fileDesc')

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

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

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

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

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

        return mei_head

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

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

        return graphic

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

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

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

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

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

        return staff

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

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

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

        return measure

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

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

        return zone

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

        # output mei file
        XmlExport.meiDocumentToFile(self.meidoc, output_path)
예제 #4
0
파일: meicreate.py 프로젝트: agpar/Rodan
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