Skip to content

Instantly share code, notes, and snippets.

@PARC6502
Last active April 6, 2024 20:27
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save PARC6502/3c8102d7cae8d47d542609528bae86e5 to your computer and use it in GitHub Desktop.
Save PARC6502/3c8102d7cae8d47d542609528bae86e5 to your computer and use it in GitHub Desktop.
Converts a midi file into a file Sonic Pi can play
#! /usr/bin/env ruby
require 'midilib/sequence'
DEFAULT_MIDI_TEST_FILE = 'Lullaby_-_Brahms.mid'
# Read from MIDI file
seq = MIDI::Sequence.new()
# Either open the file provided by the first argument or the one defined above
File.open(ARGV[0] || DEFAULT_MIDI_TEST_FILE, 'rb') do |file|
# The block we pass in to Sequence.read is called at the end of every
# track read. It is optional, but is useful for progress reports.
seq.read(file) do |track, num_tracks, i|
#puts "read track #{track ? track.name : ''} (#{i} of #{num_tracks})"
end
end
PPQN = seq.ppqn # Pulses per quarter note, the number of ticks in one beat
def to_note (ticks)
# Converts the time in ticks to time in notes
return (ticks*1.0)/PPQN
end
melodies = {}
sleeps = {}
bpm = seq.bpm.round.to_s
seq.each_with_index do |track, idx|
# Filter the track so it only had Note On events
track = track.select { |e| e.is_a?(MIDI::NoteOn) }
melody = []
slps = []
track.each_with_index do |e, i|
# Get the first sleep
if i == 0
first_slp = to_note(e.time_from_start)
slps.push(first_slp)
end
# Calculate duration of note by comparing the time between on and off events. Convert from ticks to beats.
dur_delta = e.off.time_from_start - e.time_from_start
duration = to_note(dur_delta)
# Round duration to the closest 1/16
#duration = (duration*16).round / 16.0
note = e.note
melody.push({note: note, duration: duration})
# There is one more note than there is sleep, so we have to break early on the last iteration
break if i == (track.length - 1)
slp_delta = track[i+1].time_from_start - e.time_from_start
slp = to_note(slp_delta)
slps.push(slp)
end
melodies[idx] = melody
sleeps[idx] = slps
end
#= Sonic Pi Output
# This section creates a file that's readable by sonic pi
#== Attack/sutain/release ratios
ATTACK = 0
SUSTAIN = 0.8
RELEASE = 0.2
out = File.new('out.rb', 'w')
out.write("use_bpm #{bpm}\n\n")
melodies.each do |key,value|
idx = key.to_s
out.write("melody#{idx} = #{value}\n")
out.write("sleeps#{idx} = #{sleeps[key]}\n\n")
out.write("in_thread do\n")
out.write(" sleep sleeps#{idx}[0]\n")
out.write(" melody#{idx}.each_with_index do |item,i|\n")
out.write(" play item[:note]")
out.write(", attack: item[:duration]*#{ATTACK}") if ATTACK > 0
out.write(", sustain: item[:duration]*#{SUSTAIN}") if SUSTAIN > 0
out.write(", release: item[:duration]*#{RELEASE}") if RELEASE > 0
out.write("\n")
out.write(" sleep sleeps#{idx}[i+1] if i+1 < sleeps#{idx}.length\n")
out.write(" end\n")
out.write("end\n\n")
end
out.close
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment