Last active
December 19, 2015 03:39
-
-
Save jangler/5891900 to your computer and use it in GitHub Desktop.
Rudimentary MIDI file writing code in Ruby
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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