GUXO commited on
Commit
7b52ea3
·
verified ·
1 Parent(s): 64f94b5

Upload main.py

Browse files
Files changed (1) hide show
  1. main.py +186 -0
main.py ADDED
@@ -0,0 +1,186 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import xml.etree.ElementTree as ET
2
+ import os
3
+
4
+ # MusicXML filename to be processed
5
+ TARGET_FILENAME = 'GUjianpu.musicxml'
6
+
7
+ # Mapping from standard musical notes to Jianpu numbers
8
+ JIANPU_PITCH_MAP = {
9
+ 'C': '1', 'D': '2', 'E': '3', 'F': '4', 'G': '5', 'A': '6', 'B': '7'
10
+ }
11
+
12
+ # Accidentals (flats, sharps) mapping
13
+ ACCIDENTALS = {
14
+ 1: '♯', # Sharp
15
+ -1: '♭', # Flat
16
+ }
17
+
18
+ # Time values in MusicXML and their corresponding Jianpu notations (for underlines, dots, etc.)
19
+ DURATION_MAP = {
20
+ 960: 'half', # Half note
21
+ 1920: 'whole', # Whole note
22
+ 240: 'eighth', # Eighth note
23
+ 120: 'sixteenth', # Sixteenth note
24
+ }
25
+
26
+ def find_target_file():
27
+ """Look for the target MusicXML file in the current working directory."""
28
+ files_in_directory = os.listdir('.')
29
+ if TARGET_FILENAME in files_in_directory:
30
+ return TARGET_FILENAME
31
+ else:
32
+ print(f"Error: {TARGET_FILENAME} not found in the current directory.")
33
+ return None
34
+
35
+ def convert_pitch_to_jianpu(pitch_element):
36
+ """Convert a pitch element from MusicXML to Jianpu."""
37
+ try:
38
+ step = pitch_element.find('step').text
39
+ octave = int(pitch_element.find('octave').text)
40
+ alter = pitch_element.find('alter')
41
+
42
+ # Convert step (C, D, E, etc.) to Jianpu number (1, 2, 3, etc.)
43
+ jianpu_number = JIANPU_PITCH_MAP.get(step, '')
44
+
45
+ # If there's an accidental (sharp/flat), add it to the Jianpu number
46
+ if alter is not None:
47
+ alter_value = int(alter.text)
48
+ jianpu_number += ACCIDENTALS.get(alter_value, '')
49
+
50
+ # Add octave marking (dot above or below)
51
+ if octave < 4:
52
+ jianpu_number = '·' * (4 - octave) + jianpu_number # Dots below for lower octave
53
+ elif octave > 4:
54
+ jianpu_number = jianpu_number + '·' * (octave - 4) # Dots above for higher octave
55
+
56
+ return jianpu_number
57
+ except Exception as e:
58
+ print(f"Error processing pitch: {e}")
59
+ return None
60
+
61
+ def convert_duration_to_jianpu(duration, dots=0):
62
+ """Handle note duration and convert it to Jianpu equivalent (underlines, dots)."""
63
+ try:
64
+ jianpu_duration = 'normal'
65
+ if duration in DURATION_MAP:
66
+ jianpu_duration = DURATION_MAP[duration]
67
+
68
+ # Handle dotted notes (附点音符)
69
+ if dots > 0:
70
+ jianpu_duration += ' ' + '·' * dots # Add dots to the note
71
+
72
+ return jianpu_duration
73
+ except Exception as e:
74
+ print(f"Error processing duration: {e}")
75
+ return 'normal'
76
+
77
+ def handle_rest(note_element):
78
+ """Handle rests and convert to '0' notation in Jianpu."""
79
+ try:
80
+ rest_element = note_element.find('rest')
81
+ if rest_element is not None:
82
+ return '0' # Jianpu notation for rests
83
+ except Exception as e:
84
+ print(f"Error processing rest: {e}")
85
+ return None
86
+
87
+ def handle_tie(note_element):
88
+ """Handle tied notes, where the note continues across bar lines."""
89
+ try:
90
+ tie_start = note_element.find('tie[@type="start"]')
91
+ tie_stop = note_element.find('tie[@type="stop"]')
92
+ if tie_start is not None:
93
+ return True # Mark as tied note, but no need to duplicate the note
94
+ except Exception as e:
95
+ print(f"Error processing tie: {e}")
96
+ return False
97
+
98
+ def handle_slur(note_element):
99
+ """Handle slurs (连音线) between notes."""
100
+ try:
101
+ slur_start = note_element.find('notations/slur[@type="start"]')
102
+ slur_stop = note_element.find('notations/slur[@type="stop"]')
103
+
104
+ # Slur between notes (连音线)
105
+ if slur_start is not None:
106
+ return "start_slur"
107
+ elif slur_stop is not None:
108
+ return "end_slur"
109
+ except Exception as e:
110
+ print(f"Error processing slur: {e}")
111
+ return None
112
+
113
+ def process_musicxml(input_file, output_file):
114
+ """Parse MusicXML, convert to Jianpu, and save the modified file."""
115
+ try:
116
+ # Parse the MusicXML file
117
+ tree = ET.parse(input_file)
118
+ root = tree.getroot()
119
+
120
+ # Define namespaces for MusicXML
121
+ ns = {'score': 'http://www.musicxml.org/ns/score'}
122
+
123
+ # Process all <note> elements
124
+ for note in root.findall('.//score:note', namespaces=ns):
125
+ try:
126
+ # Handle rests
127
+ rest_jianpu = handle_rest(note)
128
+ if rest_jianpu:
129
+ staff_text = ET.Element('staff-text')
130
+ staff_text.text = rest_jianpu
131
+ note.append(staff_text)
132
+ continue
133
+
134
+ # Handle pitch and duration conversion
135
+ pitch = note.find('score:pitch', namespaces=ns)
136
+ duration = note.find('score:duration', namespaces=ns)
137
+ dots = note.find('score:dots', namespaces=ns)
138
+ tie = handle_tie(note)
139
+ slur = handle_slur(note)
140
+
141
+ if pitch is not None:
142
+ # Convert pitch to Jianpu notation
143
+ jianpu_notation = convert_pitch_to_jianpu(pitch)
144
+
145
+ # Handle duration and dotted notes
146
+ jianpu_duration = convert_duration_to_jianpu(int(duration.text), int(dots.text) if dots is not None else 0)
147
+
148
+ # Append Jianpu notation and additional marks
149
+ jianpu_notation += f" {jianpu_duration}"
150
+ if tie:
151
+ jianpu_notation += ' (tie)'
152
+ if slur == "start_slur":
153
+ jianpu_notation += ' (slur start)'
154
+ elif slur == "end_slur":
155
+ jianpu_notation += ' (slur end)'
156
+
157
+ # Create a new XML element to store the Jianpu notation
158
+ staff_text = ET.Element('staff-text')
159
+ staff_text.text = jianpu_notation
160
+
161
+ # Insert the Jianpu notation element into the MusicXML tree
162
+ note.append(staff_text)
163
+
164
+ except Exception as e:
165
+ print(f"Error processing note: {e}. Skipping this note but continuing.")
166
+
167
+ # Write the modified XML to the output file
168
+ tree.write(output_file, encoding="UTF-8", xml_declaration=True)
169
+ print(f"Jianpu-converted MusicXML saved as {output_file}")
170
+
171
+ except ET.ParseError as e:
172
+ print(f"Error parsing the MusicXML file: {e}")
173
+ except Exception as e:
174
+ print(f"An unexpected error occurred: {e}")
175
+
176
+ def main():
177
+ # Look for the target MusicXML file in the current directory
178
+ input_file = find_target_file()
179
+ if input_file:
180
+ output_file = 'GUjianpu_converted.musicxml'
181
+
182
+ # Process MusicXML to Jianpu
183
+ process_musicxml(input_file, output_file)
184
+
185
+ if __name__ == "__main__":
186
+ main()