One of the most powerful features of JRuby (and one that's unique to JRuby) is the fact that you can call out to any Java library as it if were just another piece of Ruby code. In fact, other than a few small details, you might not even realize you're using Java libraries. Today I'll walk through a quick example of "scripting" the MIDI support built into the Java platform.
Because JRuby always strives to be a solid Ruby implementation first, we don't normally expose nonstandard features like Java integration. That said, it's easy to pull it in... just require the 'java' library:
Alternatively, you can -rjava on the command line. And yes. That's all there is to it. You might want to consider taking a break at this point, so your manager doesn't realize just how simple it was to make this happen. Grab a snack, play some video games, and once you're done with all that hard work, we'll be ready to pull in some Java APIs!
Importing Java Classes
Once you've loaded Java support, all the Java classes that the JVM can see are available to you as though they were normal Ruby classes. You can access them directly:
This "package" access works for any Java classes in packages starting with "java", "javax", "org", or "com." In order to avoid polluting the top-level namespace (we actually define Kernel methods for "java", "javax", "org", and "com" to provide this feature), other packages need to be scoped under the Java module:
You can actually "import" these classes in a couple different ways. First, you can simply assign them to a constant:
You can also import them with the "java_import" Kernel method (also available as "import", but that frequently conflicts with libraries like Rake):
Once you have access to the Java classes, there's really nothing tricky about using them. Call the methods you want to call, and you'll get back the results you'd expect to get:
A MIDI Keyboard
Ok, let's put it all together using a more entertaining library: Java's MIDI API. The MIDI API is located under the javax.sound.midi package, which contains all the classes you'd need to load, construct, and play MIDI sequences.
In this first MIDI example, we'll listen for a line of text at a time and convert that text into a playable MIDI sequence. First we'll load Java support and import the classes we need:
Notice I assigned the javax.sound.midi package to the 'midi' local variable in order to shorten the import lines a bit. It's all just objects, after all...
Next up we'll want a simple gets loop with an exit clause:
Ok, now we get to the meat of our app: building a sequence. For every line, we'll construct a new sequence. It's not the most efficient way, but it's nice and simple for now. Then for each character in the sequence we'll add a NOTE_ON event using the character code as the note. Finally we'll add a STOP event to stop playing the sequence.
Finally we'll play the sequence and end the loop. There's also a hard JVM System.exit call here, to shut down the internal event threads that MIDI support starts.
Give it a try! Here's the full source to our first MIDI script.
Making it More Interactive
Given what we've learned, perhaps we can make our MIDI keyboard app a little more interactive. Ideally we'd like it to respond to keystrokes live,turning the note on when the key is pressed and turning it off when the key is released. For that, we'll launch a GUI window to receive keystroke events.
Our preamble this time only imports three classes, since we won't be constructing sequences this time.
In order to make this interactive, we're going to access the system synthesizer directly. Here we retrieve the default synthesizer, open it, and grab its channel number 0:
There are a few ways to receive keystroke events directly at the command line, but a lot of them are system-specific or require special libraries. For simplicity, we'll launch a GUI frame and just set it up to receive keystroke events.
Here we're going to use another feature of JRuby: implementing an interface using a block of code. Every Java interface has a class method "impl" that receives a block and produces an object that implements that interface using the given block. The block itself is called for every interface method, receiving the name of the method and the original arguments as parameters.
In this case, we're going to handle the keyPressed and keyReleased methods on the KeyListener interface by turning notes on and off, again using the character code as the note:
And finally, we'll show the frame to get the whole thing started:
Here's the source for our MIDI keyboard.
Using JRuby, a whole new world of libraries are at your fingertips, and you never have to write a line of Java code. This is just one use case, but there really are an unlimited number of great ways JRuby allows you to leverage technologies that are otherwise out of the picture for Rubyists.
In the coming weeks and months we'll be working on a series of posts about how you can use JRuby, why we love it, and what you can look forward to in the future. If you've got a hankering for something specific, leave a comment—we aim to please :D