Unimidi is a universal Ruby library for realtime MIDI IO.
It currently works with MRI 1.9.2 on Linux, OSX, Windows/cygwin and under JRuby in 1.9 mode on any platform.
gem install unimidi
No compilation is required, install the gem and start playing.
Unimidi deals in raw bytes rather than high level message objects with the intention of allowing people to use whichever message objects or helpers they choose. There's a few libraries out there for that, including
one by me.
Under the hood, unimidi is essentially linkage to a set of platform specific C bindings (that use
ruby-ffi). Or in the case of JRuby, some code that wraps javax.sound. These platform specific libraries are broken out in to their own gems and the appropriate one should install automatically when the unimidi gem is installed. In unusual cases where your platform isn't recognized, all of those gems will be installed. You can see which library goes with which platform on the
unimidi github page. It is possible to use those gems on their own, but currently none of them contain any features that aren't also made available through unimidi.
Here's a couple of quick examples to get started
Sending notes to a MIDI output
First, load unimidi and then define a set of note values and a duration value for how long the notes will be held out.
require 'unimidi'
notes = [36, 40, 43, 48, 52, 55, 60, 64, 67] # C E G arpeggios
duration = 0.1
Next, select an output. To list all available outputs, you can do
unimidi list from the command line or
UniMIDI::Device.list in irb. In this case, the first output is selected and enabled. (
here is another post with more information about selecting a device)
output = UniMIDI::Output.open(:first)
Now open that output. Passing a block to open is optional but it ensures that the device is closed when the program is finished. This can alleviate some headaches when multitasking MIDI/audio programs. Note that it's also possible to select, open and use multiple inputs and outputs concurrently.
output.open do |output|
notes.each do |note|
output.puts(0x90, note, 100) # note on message
sleep(duration) # wait
output.puts(0x80, note, 100) # note off message
end
end
Output#puts can also be used for sysex messages in the same manner as note messages, a la:
output.puts(0xF0, 0x41, 0x10, 0x42, 0x12, 0x40, 0x00, 0x7F, 0x00, 0x41, 0xF7)
Note that some OS's will silently reject invalid sysex or short messages.
You can also Output#puts a string of hex bytes if you prefer to work that way
output.puts("904040")
output.puts("F04110421240007F41F7")
Working with input
Input can be collected two ways. First, unimidi has a method
Input#gets which waits for input before returning. It works exactly the way Ruby's
$stdin.gets works when waiting for keyboard input.
Here is a demonstration of Input#gets
Select an input and open it...
input = UniMIDI::Input.first
input.open do |input|
$stdout.puts "send some MIDI to your input now..."
loop do
m = input.gets
$stdout.puts(m)
end
end
When MIDI input is received, you'll see inspected Hash objects like this:
{ :data => [144, 60, 100], :timestamp => 1024 }
In this case [144, 60, 100] is a note-on message for channel 0, note C4 (aka 60), with velocity 100. The timestamp is the number of milliseconds since the input was opened.
The other more advanced way to receive input is by polling
Input#buffer manually. As new messages are received, they are added to that buffer array. I normally use this with a pointer to keep track of the index of the last message seen. Polling this way would for example, allow you to create your own background thread to collect input while your program does other things. The library Topaz,
which I wrote about in my last post, collects clock messages from unimidi that way.
Your computer should be able to receive data with 1-2 millisecond accuracy
http://github.com/arirusso/unimidi