示例#1
0
    def _get_name_node(self):
        vbox_text_nodes = self.xml_tree.findall('Score/Staff/[@id="1"]/VBox/Text')
        for vbox_text_node in vbox_text_nodes:
            if find_exactly_one(vbox_text_node, 'style').text == 'Instrument Name (Part)':
                return find_exactly_one(vbox_text_node, 'text')

        raise ValueError('No vbox part node found')
    def _assert_nonlinked_score_metadata_correct(self, root, work_title):
        score_xml = find_exactly_one(root, 'Score')

        # MuseScore default, true for all the resource files
        self.assertEqual(find_exactly_one(score_xml, 'Style/Spatium').text, '1.76389')
        self.assertGreater(len(score_xml.findall('metaTag')), 1)
        self.assertEqual(find_exactly_one(score_xml, 'metaTag/[@name="workTitle"]').text, work_title)
    def _assert_vbox_field_match(self, root, field_name, field_value):
        vbox_node = find_exactly_one(root, 'Score/Staff/VBox')
        matching_nodes = 0
        for vbox_text_node in vbox_node.findall('Text'):
            if find_exactly_one(vbox_text_node, 'style').text == field_name:
                self.assertEqual(find_exactly_one(vbox_text_node, 'text').text, field_value)
                matching_nodes += 1

        self.assertEqual(matching_nodes, 1, 'Did not find a matching node')
    def test_split_keep_global_text(self):
        [violin1, violin2, piano] = self._multi_part_global_text_score.generate_part_scores()

        violin1_root = _write_score_to_string_and_read_xml(violin1)
        self._assert_staff_id_1_measure_index_has_element_name(violin1_root, 0, 'RehearsalMark')
        self._assert_staff_id_1_measure_index_has_element_name(violin1_root, 0, 'Tempo')
        self._assert_staff_id_1_measure_index_has_element_name(violin1_root, 1, 'SystemText')
        self._assert_staff_id_1_measure_index_has_element_name(violin1_root, 2, 'Tempo')
        self._assert_staff_id_1_measure_index_has_element_name(violin1_root, 3, 'RehearsalMark')

        violin2_root = _write_score_to_string_and_read_xml(violin2)
        self._assert_staff_id_1_measure_index_has_element_name(violin2_root, 0, 'RehearsalMark')
        self._assert_staff_id_1_measure_index_has_element_name(violin2_root, 0, 'Tempo')
        self._assert_staff_id_1_measure_index_has_element_name(violin2_root, 1, 'SystemText')
        self._assert_staff_id_1_measure_index_has_element_name(violin2_root, 2, 'Tempo')
        self._assert_staff_id_1_measure_index_has_element_name(violin2_root, 3, 'RehearsalMark')

        piano_root = _write_score_to_string_and_read_xml(piano)
        self._assert_staff_id_1_measure_index_has_element_name(piano_root, 0, 'RehearsalMark')
        self._assert_staff_id_1_measure_index_has_element_name(piano_root, 0, 'Tempo')
        self._assert_staff_id_1_measure_index_has_element_name(piano_root, 1, 'SystemText')
        self._assert_staff_id_1_measure_index_has_element_name(piano_root, 2, 'Tempo')
        self._assert_staff_id_1_measure_index_has_element_name(piano_root, 3, 'RehearsalMark')

        piano_lh_root = find_exactly_one(piano_root, 'Score/Staff/[@id="2"]')
        self.assertEqual(len(piano_lh_root.findall('Measure/voice/RehearsalMark')), 0)
        self.assertEqual(len(piano_lh_root.findall('Measure/voice/Tempo')), 0)
        self.assertEqual(len(piano_lh_root.findall('Measure/voice/SystemText')), 0)
    def _assert_staff_id_1_measure_index_has_element_name(self, root, measure_index, element_name):
        measure_voice_nodes = find_exactly_one(root, 'Score/Staff/[@id="1"]').findall('Measure/voice')
        self.assertLess(measure_index, len(measure_voice_nodes), 'measure_index is larger than number of measure nodes')

        for i, measure_voice_node in enumerate(measure_voice_nodes):
            if i == measure_index:
                self.assertEqual(len(measure_voice_node.findall(element_name)), 1)
示例#6
0
    def _add_vbox_with_part_text(xml_tree, original_vbox_node):
        vbox_text_node = ET.Element('Text')
        vbox_text_node.extend([
            create_node_with_text('style', 'Instrument Name (Part)'),
            create_node_with_text('text', find_exactly_one(xml_tree, 'Score/Part/Instrument/longName').text)
        ])

        vbox_node = copy.deepcopy(original_vbox_node)
        vbox_node.append(vbox_text_node)

        xml_tree.find('Score/Staff').insert(0, vbox_node)
示例#7
0
    def _remove_unneeded_staves_and_staff_vbox(xml_tree):
        staff_ids = [staff.get('id') for staff in xml_tree.findall('Score/Part/Staff')]

        score_node = find_exactly_one(xml_tree, 'Score')
        for score_staff_node in score_node.findall('Staff'):
            if score_staff_node.get('id') not in staff_ids:
                score_node.remove(score_staff_node)
                continue

            existing_staff_vbox_node = score_staff_node.find('VBox')
            if existing_staff_vbox_node is not None:
                score_staff_node.remove(existing_staff_vbox_node)
示例#8
0
    def _find_all_measure_global_text_nodes(xml_tree):
        _GLOBAL_TEXT_NODE_NAMES = ['RehearsalMark', 'Tempo', 'SystemText']

        # At least as of time of writing, MuseScore only allows these elements on staff id 1 (i.e. the first staff)
        measure_global_text_nodes_list = []
        measure_voice_nodes = find_exactly_one(xml_tree, 'Score/Staff/[@id="1"]').findall('Measure/voice')
        for i, measure_voice_node in enumerate(measure_voice_nodes):
            global_text_nodes_for_measure = [child_node for child_node in measure_voice_node
                                             if child_node.tag in _GLOBAL_TEXT_NODE_NAMES]
            if len(global_text_nodes_for_measure) > 0:
                measure_global_text_nodes_list.append(_MeasureGlobalTextNodes(i, global_text_nodes_for_measure))

        return measure_global_text_nodes_list
示例#9
0
    def _fix_split_part_score_part_names(self, part_scores):
        part_name_to_num_appearances = defaultdict(int)
        for part_node in self._xml_tree.findall('Score/Part'):
            part_name_to_num_appearances[find_exactly_one(part_node, 'Instrument/longName').text] += 1

        # Note that this does not handle if there's a "Violin 1", "Violin", and "Violin" part.
        # It's unclear what should be done (maybe the violin parts should be named "Solo Violin", for example)
        part_name_to_correct_part_number = defaultdict(int)
        for part in part_scores:
            part_name = part.get_name()
            is_duplicate_part_name = part_name_to_num_appearances[part_name] > 1
            if is_duplicate_part_name:
                part_name_to_correct_part_number[part_name] += 1
                part.set_name(f'{part_name} {part_name_to_correct_part_number[part_name]}')
示例#10
0
    def _apply_measure_global_text_nodes(xml_tree, measure_global_text_nodes_list):
        measure_voice_nodes = find_exactly_one(xml_tree, 'Score/Staff/[@id="1"]').findall('Measure/voice')
        for measure_global_text_nodes in measure_global_text_nodes_list:
            measure_voice_node = measure_voice_nodes[measure_global_text_nodes.measure_index]
            # Inserting the nodes right after Time/ KeySig (as opposed to just at the end) positions the global text at
            # the beginning of the measure.
            insertion_index = 0
            for measure_voice_child_node in measure_voice_node:
                if measure_voice_child_node.tag not in ['TimeSig', 'KeySig']:
                    break
                insertion_index += 1

            # This inserts a list at insertion_index (i.e. list extend but in the middle)
            measure_voice_node[insertion_index:insertion_index] = measure_global_text_nodes.nodes
示例#11
0
    def create_parts_from_xml(cls, xml_tree):
        vbox_node = find_exactly_one(xml_tree, 'Score/Staff/[@id="1"]/VBox')
        measure_global_text_nodes_list = _PartScore._find_all_measure_global_text_nodes(xml_tree)

        parts = []
        for part_index in range(len(xml_tree.findall('Score/Part'))):
            part_xml_tree = copy.deepcopy(xml_tree)

            # Ordering is important for these method calls, as they depend on each other's results.
            _PartScore._remove_unneeded_parts(part_xml_tree, part_index)
            _PartScore._remove_unneeded_staves_and_staff_vbox(part_xml_tree)
            # Layout breaks from the score are hopefully unneeded in the part itself, as the measure rendering has
            # different lines/ pages.
            _PartScore._remove_layout_breaks(part_xml_tree)
            _PartScore._add_vbox_with_part_text(part_xml_tree, vbox_node)
            _PartScore._fix_staff_ids(part_xml_tree)
            # These were never removed from the first staff, so we skip this on the first part.
            if part_index != 0:
                _PartScore._apply_measure_global_text_nodes(part_xml_tree, measure_global_text_nodes_list)

            parts.append(cls(part_xml_tree))

        return parts
 def _assert_part_correct(self, root, staff_ids, instrument_long_name):
     part_node = find_exactly_one(root, 'Score/Part')
     self.assertListEqual([s.get('id') for s in part_node.findall('Staff')], staff_ids)
     self.assertEqual(find_exactly_one(part_node, 'Instrument/longName').text, instrument_long_name)
示例#13
0
 def _remove_unneeded_parts(xml_tree, part_index):
     score_node = find_exactly_one(xml_tree, 'Score')
     for i, part_node in enumerate(score_node.findall('Part')):
         if i != part_index:
             score_node.remove(part_node)