Replace 0xC0<byte> with 0xC000 to “reset” MIDI instrument?
-
30-05-2021 - |
質問
I've got some MIDI files made by Sibelius (v.4?..) with "unusual" instrument within; I can not hear the melody. I decided that I need to "reset" instrument to 0 (Acoustic Grand Piano, I believe).
So, I wrote a Python programs using both python-midi and mingus; these programs could change MIDI instrument, but they both changed rhythm dramatically.
Rosegarden does this job quite well, but I can not use it in a batch.
Next, after some reading of MIDI format information (here and here, for instance) I wrote a simple Python script (see below), which simply replaces some strings with another ones --- replaces 0xC0[byte] with 0xC000 and like that.
Now those MIDI files sounds OK, I can hear everything.
So, my question is --- is it "safe" (in any meaning) to do this? How I can change instruments better? How I can "detect" 0xCx command in MIDI file better?
Thank you!
#!/usr/bin/env python
#
import sys
import re
patt = re.compile('(\xC0|\xC1|\xC2|\xC3|\xC4|\xC5|\xC6|\xC7).')
try:
filein = sys.argv[1]
except IndexError:
print "Specify input file as an argument."
sys.exit(1)
try:
fileout = sys.argv[2]
except IndexError:
fileout = '%s-out2.mid' % filein.replace('.mid', '')
filein_data = open(filein, 'rb').read()
open(fileout, 'wb').write(patt.sub('\1\x00',filein_data))
正しい解決策はありません
他のヒント
It is not safe to simply replace all 0xC??? bytes with 0xC?00, mainly because 0xC??? could be indicating an event's delta time and not actually be part of a Program Change event. Accidentally modifying a delta time will shift all subsequent events off-time, which will sound pretty awful if it's part of a multi-track composition.
As for a solution, I doubt there's a regex that can detect Program Change events without possibly picking up delta times as well. My only experience with modifying MIDI is with the Java library I wrote, and you would accomplish your task with the following code:
MidiFile input = new MidiFile(INPUT_FILE);
for(MidiTrack T : input.getTracks()) {
Iterator<MidiEvent> it = T.getEvents().iterator();
while(it.hasNext()) {
MidiEvent E = it.next();
if(E instanceof ProgramChange) {
E.setProgramNumber(0);
}
}
}
input.writeToFile(OUTPUT_FILE);
I wrote that free-hand so you'll likely have to use some exception catching, but it's fairly straight-forward.
Hope that helps.
This works for me. Dontknow what's wrong prevoiusly. python-midi
is nice.
#!/usr/bin/env python
#
# Sets all instruments' numbers to zero.
#
import sys
import midi
try:
infile = sys.argv[1]
except IndexError:
print "Specify input file as an argument, please."
sys.exit(1)
try:
outfile = sys.argv[2]
except IndexError:
outfile = '%s-out.mid' % infile.replace('.mid', '')
midi_data = midi.read_midifile(infile)
for track_idx in range(len(midi_data)):
for event in midi_data[track_idx]:
if isinstance(event, midi.ProgramChangeEvent):
event.data = map(lambda x: 0, event.data)
midi.write_midifile(outfile, midi_data)