I have extracted notes, chords, rests and time signatures from parsed MIDI file. Storing them and again playing them back, I still cannot get the original speed and mix of sounds. What am I doing wrong? What am I forgetting?
temp_name = 'Chopin.midi'#any example of MIDI song
temp_score = converter.parse(temp_name)
flat = temp_score.flat
all_elements = []
# Iterate over the elements in the flattened stream
for el in flat:
if isinstance(el, music21.note.Note):
pitch = el.pitch
note_name = pitch.nameWithOctave
new_note_c = note.Note(note_name)
new_note_c.duration = el.duration
dur = float(el.duration.quarterLength)
all_elements.append((str(note_name), dur))
elif isinstance(el, music21.chord.Chord):
chord_pitches = []
for p in el.pitches:
#note_name = p.pitch
#note_name_oct = note_name.nameWithOctave
new_note_c = note.Note(p.nameWithOctave)
#new_note_c.duration = el.duration
dur = float(el.duration.quarterLength)
chord_pitches.append((str(new_note_c.nameWithOctave)))
chord_pitches = ".".join([i for i in chord_pitches])
all_elements.append((chord_pitches,dur))
#elif isinstance(el, note.Rest):
# rest_duration = el.duration.quarterLength
# temp_str = 'REST'
# all_elements.append((temp_str, rest_duration))
elif isinstance(el, music21.meter.TimeSignature):
current_time_signature = el.ratioString
all_elements.append(('TIME_SIGNATURE', current_time_signature))
Here, extracting the rests resulted in unnecessary rests in the output and I have no idea how it is possible. The code to run back these sequences is as follows:
def get_music_midi_filename_from_chords(input_chords, initial_ts, initial_tempo):
midi_stream = stream.Stream()
i=0
new_tempo = tempo.MetronomeMark(number=initial_tempo)
midi_stream.append(new_tempo)
midi_stream.append(initial_ts)
for note_pattern, duration_pattern in input_chords:
if 'TIME_SIGNATURE' in note_pattern:
ts_numerator, ts_denominator = duration_pattern.split('/')
new_ts = meter.TimeSignature(f'{ts_numerator}/{ts_denominator}')
midi_stream.append(new_ts)
if '.' in note_pattern:
notes_in_chord = note_pattern.split('.')
chord_notes = []
for current_note in notes_in_chord:
new_note = note.Note(current_note)
new_note.duration = duration.Duration(duration_pattern)
new_note.storedInstrument = instrument.Piano()
chord_notes.append(new_note)
new_chord = chord.Chord(chord_notes)
midi_stream.append(new_chord)
elif 'REST' in note_pattern:
rest_ = note.Rest()
rest_.duration = duration.Duration(duration_pattern)
midi_stream.append(rest_)
else:
if is_valid_pitch(note_pattern):
note_single = note.Note(note_pattern)
note_single.duration = duration.Duration(duration_pattern)
note_single.storedInstrument = instrument.Piano()
midi_stream.append(note_single)
midi_stream = midi_stream.chordify()
timestr = time.strftime("%Y%m%d-%H%M%S")
new_file = 'output-' + timestr + '.mid'
return midi_stream.write('midi', fp=new_file
)
Here, initial tempo is taken as
initial_tempo = temp_score.flat.getElementsByClass('MetronomeMark')[0].number
and initial time signature is taken as
initial_ts = temp_score.recurse().getElementsByClass(music21.meter.TimeSignature)[0]
The function is_valid_pitch() is :
def is_valid_pitch(note_pattern):
try:
p = music21.pitch.Pitch(note_pattern)
return True
except:
return False
I am addressing time signatures and tempo as well from the song. But again, the timing duration is differed from the original. When I include the rests from the MIDI flat, why am I getting unusual lengths of rests and the duration is increased way too much? How can I improve this code? What am I missing? I am quite lost in it now.
I need to be able to store all the required features under an array, basically trying to create an audio dataset, and then after training, I will be producing the music. But before training, I am not able to even replay back the same MIDI I have parsed in the original way.