如何实现MusicXML转按小节拆分声部的ABC记谱格式
背景
我最近尝试作曲,但完全是新手,没有乐理基础。因此我想训练LLaMA来辅助我为多乐器乐曲续写提供建议。然而乐谱是二维的(时间和乐器数量),而注意力机制是一维的,所以采用便于注意力机制解析的记谱方式至关重要。
问题
我希望将MusicXML转换为ABC格式,且满足一个额外条件:每个声部按小节拆分,以便大语言模型更易解析。以下是我设想的示例格式:
X:1 T: Three Bar Tune M:4/4 L:1/4 K:C %%MIDI program 1 0 %%MIDI program 2 40 %%MIDI program 3 42 V:1 [V:1 "Piano"] C G E G |] V:2 clef=treble [V:2 "Violin"] E B G B |] V:3 clef=bass [V:3 "Cello"] G, D B, D |] V:1 [V:1 "Piano"] D A F A |] V:2 [V:2 "Violin"] F C A C |] V:3 [V:3 "Cello"] A, E C E |] V:1 [V:1 "Piano"] E B G B |] V:2 [V:2 "Violin"] G D B D |] V:3 [V:3 "Cello"] B, F D F |] V:1 [V:1 "Piano"] F C A C |] V:2 [V:2 "Violin"] A, E C E |] V:3 [V:3 "Cello"] C G E G |]
请问有没有办法生成这种格式?
我尝试过midi2abc,但它功能有限,且由于MusicXML和ABC记谱法的表达性较强,而MIDI存在局限性,因此MIDI并非理想的中间格式。
我还尝试过自己编写程序,但音乐记谱体系非常复杂。参考过ABC记谱标准v2.1的规范。
import xml.etree.ElementTree as ET from music21 import converter, pitch def get_note_name(pitch_obj): abc_note_names = ['C', '^C', 'D', '^D', 'E', 'F', '^F', 'G', '^G', 'A', '^A', 'B'] note_name = abc_note_names[pitch_obj.pitchClass] octave = pitch_obj.octave - 1 if octave == -1: return note_name + ',' elif octave == 0: return note_name elif octave == 1: return note_name.lower() elif octave == 2: return note_name.lower() + "'" else: return note_name.lower() + "'" * (octave - 1) def musicxml_to_abc(musicxml_file_path): # Load the MusicXML file score = converter.parse(musicxml_file_path) tree = ET.parse(musicxml_file_path) root = tree.getroot() time_signature = '4/4' # Find the time signature for attributes in root.iter('attributes'): for time in attributes.iter('time'): beats = time.find('beats').text beat_type = time.find('beat-type').text time_signature = f"{beats}/{beat_type}" break abc = f"X:1 T: One Bar Tune M:{time_signature} L:1/4 K:C " voice_names = {} voices = {} for part in root.iter('part'): part_id = part.attrib['id'] for score_part in root.find('part-list').iter('score-part'): if score_part.attrib['id'] == part_id: part_name = score_part.find('part-name').text voice_names[part_id] = part_name voices[part_id] = [] for measure in part.iter('measure'): measure_notes = '' for note in measure.iter('note'): pitch_elem = note.find('pitch') if note.find('rest') is not None or pitch_elem is None: measure_notes += 'z ' else: step = pitch_elem.find('step').text alter = pitch_elem.find('alter') octave = int(pitch_elem.find('octave').text) if alter is not None: alter = int(alter.text) else: alter = 0 pitch_obj = pitch.Pitch(step=step, octave=octave, accidental=alter) note_name = get_note_name(pitch_obj) measure_notes += note_name + ' ' voices[part_id].append(measure_notes) voice_counter = 1 for part_id, voice_name in voice_names.items(): abc += f"%%MIDI program {voice_counter} 0 " abc += f'V:{voice_counter} clef=treble ' abc += f'[V:{voice_counter} "{voice_name}"] ' voice_counter += 1 for measure_num in range(len(voices[next(iter(voices))])): for voice_num, (part_id, voice_measures) in enumerate(voices.items(), start=1): abc += f" V:{voice_num} " abc += f"[V:{voice_num}] {voice_measures[measure_num]} |] " return abc if __name__ == "__main__": musicxml_file_path = 'path/to/musicxml' abc_output = musicxml_to_abc(musicxml_file_path) print(abc_output)
内容的提问来源于stack exchange,提问作者Coen Hacking
相关产品推荐
相关产品推荐

