Background
I've recently been trying to compose music, but I'm an absolute novice and I've no background in music theory. So I wanted to train LLaMA to help me give suggestions on how to continue a piece of music with multiple instruments. However, sheet music is two-dimensional (time and #instruments) and attention is one-dimensional, so writing the music down in a way that is easy for the attention mechanism can make a big difference.
Problem
I would like to convert MusicXML into ABC format with one additional criterium. What I would like to have is every voice split per bar, so it more easily be parsed with a large language model. Bellow is a sample of what I had imagined.
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 |]
Does anybody know of a to create this format?
I tried midi2abc, but it's limited and only create While both MusicXML and ABC notation are fairly expressive, MIDI has some limitations, and is therefore not an ideal intermediate format.
I also tried writing my own program, but musical notation is very extensive. https://abcnotation.com/wiki/abc:standard: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\nT: One Bar Tune\nM:{time_signature}\nL:1/4\nK:C\n"
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\n"
abc += f'V:{voice_counter} clef=treble\n'
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"\nV:{voice_num}\n"
abc += f"[V:{voice_num}] {voice_measures[measure_num]} |]\n"
return abc
if __name__ == "__main__":
musicxml_file_path = 'path/to/musicxml'
abc_output = musicxml_to_abc(musicxml_file_path)
print(abc_output)