Skip to content

Instantly share code, notes, and snippets.

@jangler
Last active December 19, 2015 03:39
Show Gist options
  • Save jangler/5891900 to your computer and use it in GitHub Desktop.
Save jangler/5891900 to your computer and use it in GitHub Desktop.
Rudimentary MIDI file writing code in Ruby
SingleTrack = 0
MultipleTracksSync = 1
MultipleTracksAsync = 2
class Event
attr_reader :command_byte, :num_data_bytes
def initialize(command_byte, num_data_bytes)
@command_byte = command_byte
@num_data_bytes = num_data_bytes
end
end
NoteOff = Event.new(0x80, 2)
NoteOn = Event.new(0x90, 2)
KeyAfterTouch = Event.new(0xA0, 2)
ControlChange = Event.new(0xB0, 2)
ProgramChange = Event.new(0xC0, 1)
ChannelAfterTouch = Event.new(0xD0, 1)
PitchWheelCHange = Event.new(0xE0, 2)
class MetaEvent
attr_reader :command_byte, :num_data_bytes
def initialize(command_byte, num_data_bytes)
@command_byte = command_byte
@num_data_bytes = num_data_bytes
end
end
SetSequenceNumber = MetaEvent.new(0x00, 3)
TextEvent = MetaEvent.new(0x01, -1)
CopyrightTextEvent = MetaEvent.new(0x02, -1)
TrackName = MetaEvent.new(0x03, -1)
InstrumentName = MetaEvent.new(0x04, -1)
Lyric = MetaEvent.new(0x05, -1)
Marker = MetaEvent.new(0x06, -1)
CuePoint = MetaEvent.new(0x07, -1)
EndTrack = MetaEvent.new(0x2F, 1)
SetTempo = MetaEvent.new(0x51, 4)
TimeSignature = MetaEvent.new(0x58, 5)
KeySignature = MetaEvent.new(0x59, 3)
SequencerSpecific = MetaEvent.new(0x7f, -1)
# Returns a byte array of a given length containg a representation of a given integer.
# Uses big-endian byte order.
def int_to_bytes(int, num_bytes)
arr = []
for i in 1..num_bytes
arr << ((int >> ((num_bytes - i) * 8)) & 0xFF)
end
return arr
end
# Returns a byte array containing the file header.
def header(file_format, num_tracks, ticks_per_quarter)
arr = []
'MThd'.each_byte { |byte| arr << byte }
arr << 0x00 << 0x00 << 0x00 << 0x06
int_to_bytes(file_format, 2).each { |byte| arr << byte }
int_to_bytes(num_tracks, 2).each { |byte| arr << byte }
int_to_bytes(ticks_per_quarter, 2).each { |byte| arr << byte }
return arr
end
# Returns a byte array containing the track header.
def track_header(num_bytes)
arr = []
'MTrk'.each_byte { |byte| arr << byte }
int_to_bytes(num_bytes, 4).each { |byte| arr << byte }
return arr
end
# Returns a byte array contaning a delta time to precede a MIDI event.
# Uses big-endian byte order.
def int_to_delta_time(int)
arr = []
i = 0
while int >> (i * 7) > 0
if int >> ((i + 1) * 7) & 0xff > 0
arr << (0x80 | (((int >> (i * 7))) & 0x7f))
else
arr << ((int >> (i * 7)) & 0x7f)
end
i += 1
end
if arr.empty?
arr = [0] if arr.empty?
elsif arr.length > 1
arr = arr.reverse.map { |byte| byte ^ 0x80 }
end
return arr
end
#Returns a byte array containing a MIDI event command.
def midi_event(delta_time, command, channel_number, *data)
arr = []
int_to_delta_time(delta_time).each { |byte| arr << byte }
arr << (command.command_byte | channel_number)
throw Exception.new("Wrong number of data bytes.") if data.length != command.num_data_bytes
data.each { |byte| arr << byte }
return arr
end
# Returns a byte array containing a MIDI meta-event command.
def midi_meta_event(delta_time, command, *data)
arr = []
int_to_delta_time(delta_time).each { |byte| arr << byte }
arr << 0xFF
arr << command.command_byte
if data.length != command.num_data_bytes and command.num_data_bytes != -1
throw Exception.new("Wrong number of data bytes.")
end
data.each { |byte| arr << byte }
return arr
end
@edgoulart
Copy link

Hi, all!
I'm around with a problem to make an array in Ruby... perhaps one of you could help me to solve it.
I've written an (ugly!) script to play a little music using the "beep" sound and it's running, ok; but, I'M TRYING TO PUT ALL THOSE BEEP CALS IN AN ARRAY and I'm not succeeding...! Could anyone give a hint?
Thanks, beforehand,

Ed

require "Win32API"
Beep = Win32API.new("kernel32", "Beep", ["I", "I"], 'v')
def beep freq, duration
Beep.call(freq, duration)
end

beep 523.25, 800 # C
beep 493.88, 800 # B
beep 440.00, 400 # A
beep 392.00, 1000 # G
beep 349.23, 400 # F
beep 329.63, 800 # E
beep 293.66, 800 # D
beep 261.63, 800 # C
beep 392.00, 400 # G
beep 440.00, 1000 # A
beep 440.00, 400 # A
beep 493.88, 1000 # B
beep 493.88, 400 # B

beep 523.25, 1000 # C

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment