Skip to content

Instantly share code, notes, and snippets.

@monomadic
Last active August 29, 2015 14:14
Show Gist options
  • Save monomadic/b89e5ea914ed3296acb1 to your computer and use it in GitHub Desktop.
Save monomadic/b89e5ea914ed3296acb1 to your computer and use it in GitHub Desktop.
Batch split multi-part MIDI files into single-part MIDI files.
require 'fileutils'
require 'midilib/sequence'
require 'midilib/io/midifile'
DEBUG = false
LOGGING = true
LOG_FILE = Dir.pwd + '/error.log'
INPUT_DIR = './vgm/'
OUTPUT_DIR = '../split'
def log_error error
puts error_text(error)
File.open(LOG_FILE, 'a') {|f| f.write(error) } if LOGGING
end
def fetch_instrument_name track
program_change = track.events.select{ |event| event.class == MIDI::ProgramChange }.first
if program_change.nil?
if track.instrument.nil? || track.instrument.empty? || track.instrument == 'Unnamed'
if track.name.nil? || track.name.empty? || track.name == 'Unnamed'
return 'no name'
else
return track.name
end
else
track.instrument
end
else
MIDI::GM_PATCH_NAMES[program_change.program]
end
end
def read_midi_to_sequence(filename)
seq = MIDI::Sequence.new()
File.open(filename, 'rb') do |file|
begin
seq.read(file)
rescue
log_error "[!] error reading file: #{filename}\n"
if DEBUG
require 'pry'; binding.pry
end
end
end
return seq
end
def success_text string
"\033[32m#{string}\033[0m"
end
def warn_text string
"\033[33m#{string}\033[0m"
end
def error_text string
"\033[41m#{string}\033[0m"
end
def sanitize_filename(filename)
fn = filename.downcase.split /(?<=.)\.(?=[^.])(?!.*\.[^.])/m
fn.map! { |s| s.gsub /[^a-z0-9\-]+/i, '_' }
return fn.join '.'
end
def write_dir_unless_exists(dir)
unless Dir.exist?(dir)
FileUtils.mkpath dir
puts success_text("[+] mkdir #{dir}")
end
end
def write_track_to_file track, filename, tempo
# require 'pry'; binding.pry
if track.nil? || track.events.count == 0 || !track.events.map(&:class).include?(MIDI::NoteOn)
puts warn_text("[!] empty track found. skipping.")
else
# seq = MIDI::Sequence.new()
seq = @seq.clone
seq.tracks = []
# add meta data
# meta_track = MIDI::Track.new(seq)
# seq.tracks << meta_track
# meta_track.events << MIDI::Tempo.new(MIDI::Tempo.bpm_to_mpq(tempo.round))
# meta_track.events << MIDI::MetaEvent.new(MIDI::META_SEQ_NAME, 'Sequence Name')
notes_track = MIDI::Track.new(seq)
seq.tracks << notes_track
notes_track.events << MIDI::Tempo.new(MIDI::Tempo.bpm_to_mpq(tempo.round))
notes_track.events << MIDI::MetaEvent.new(MIDI::META_SEQ_NAME, fetch_instrument_name(track))
notes_track.merge(track.events)
begin
file = File.open(filename, 'wb')
seq.write(file)
# file.close
rescue
log_error "[!] error writing file: #{filename}\n"
if DEBUG
require 'pry'; binding.pry
end
end
puts success_text("[+] wrote #{filename}")
end
end
FileUtils.cd(INPUT_DIR, verbose: true)
Dir.glob('**/*.{mid,MID}') do |file|
@seq = read_midi_to_sequence(file)
write_dir_unless_exists(OUTPUT_DIR)
dirname = "#{OUTPUT_DIR}/" + file.downcase.split('.mid').first + "-#{@seq.tempo.round}bpm"
write_dir_unless_exists(dirname)
@seq.tracks.each_with_index do |track, idx|
instrument_name = fetch_instrument_name(track)
filename = "#{idx}-#{instrument_name}"
write_track_to_file(track, "#{dirname}/#{sanitize_filename(filename)}.mid", @seq.tempo)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment