Skip to content

Instantly share code, notes, and snippets.

@dtinth
Created June 4, 2016 03:15
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save dtinth/28916cc8bb668bd131c08c61c4b1f200 to your computer and use it in GitHub Desktop.
require 'midilib'
seq = MIDI::Sequence.new()
File.open(ARGV[0], 'rb') { |file| seq.read(file) }
events = [ ]
id = 0
seq.tracks.each do |track|
track.events.each do |event|
case event
when MIDI::NoteEvent
velocity = (MIDI::NoteOff === event ? 0 : event.velocity)
note_name = event.pch_oct.downcase.sub(/.\#/) { $&[0].upcase }.reverse
time = (event.time_from_start.to_f * 12 / seq.ppqn).round
if velocity > 0
events << { note_name: note_name, value: (id += 1), time: time }
else
events << { note_name: note_name, value: 0, time: time }
end
end
end
end
events.sort_by! { |event| [ event[:time], event[:note_name] ] }
queue = events.dup
time = 0
state = { }
until queue.empty?
next_state = state.dup
while !queue.empty? && queue[0][:time] <= time
item = queue.shift
next_state[item[:note_name]] = item[:value]
end
next_state.each do |key, value|
prev_v = state[key] || 0
next_v = next_state[key] || 0
if prev_v == 0 && next_v > 0 # note on
print "+#{key}"
elsif prev_v > 0 && next_v == 0 # note off
print "-#{key}"
elsif prev_v != next_v # off and on
print "-#{key}+#{key}"
end
end
puts
state = next_state
time += 1
end
puts "."
require 'midilib'
seq = MIDI::Sequence.new()
track = MIDI::Track.new(seq)
seq.tracks << track
track.events << MIDI::Tempo.new(MIDI::Tempo.bpm_to_mpq(150))
track.events << MIDI::MetaEvent.new(MIDI::META_SEQ_NAME, 'lol')
class Writer
def initialize(track)
@track = track
@last = 0
@note_on = { }
@last_on = { }
end
def write(time, is_on, note)
if is_on && @note_on[note]
# $stderr.puts "Note #{note} already on!"
write(time, false, note)
elsif !is_on && !@note_on[note]
# $stderr.puts "Note #{note} already off!"
return
end
delta = time - @last
@last = time
@note_on[note] = is_on
@last_on[note] = time if is_on
@track.events << (is_on ? MIDI::NoteOn : MIDI::NoteOff).new(0, note, 64, delta)
end
end
data = ((ARGV[1] || '') + File.read(ARGV[0])).scan(/[\+\-]\d[a-gA-G]|\n/)
current_time = 0
quarter_note_length = seq.note_to_delta('quarter')
timestep_length = quarter_note_length / 12
current_time = 0
writer = Writer.new(track)
data.each do |item|
if item == "\n"
current_time += timestep_length
elsif item.length == 3
is_on = item[0] == '+'
octave = item[1].to_i
pitch = 'cCdDefFgGaAb'.index(item[2])
note = (octave + 1) * 12 + pitch
writer.write(current_time, is_on, note)
end
end
File.open('output.mid', 'wb') { |file| seq.write(file) }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment