I've created a Ruby library called OSC Access for binding OSC directly into Ruby classes and objects.
It conveniently wraps a lot of functionality from osc-ruby, handling server/client sharing and management as well as other tasks commonly associated with OSC.
gem install osc-access
All of OSC Access' functionality is available by including the OSCAccessible module into a class. The module gives you a lot of functionality but you'll want to know about these three methods in particular to get up and running
osc_receive
All OSC input is handled by using the osc_receive method. Here's an example of using osc_receive in a simple way:
class Instrument include OSCAccessible osc_receive("/1/fader1") do |instance, val| instance.velocity = val end def velocity=(val) puts "setting velocity to #{val}" ... end end i = Instrument.new i.osc_start(:input_port => 8000).join
When this example is run, the method velocity= is called on all instances of the Instrument class whenever OSC messages for the address /1/fader1 are received.
A couple of things to note here...
In order to enable OSC input, an input port must be specified for each instance. I've done that in this example using the osc_start method but there is also a method osc_input which just takes a port number. You can also add multiple input ports and share ports across various objects. (see example...)
Another thing to note is that val is, by default, the value of the first argument of the received OSC message. (OSC messages can have an unlimited number of arguments). You can modify which arg is used, or pass in all of them, by setting the :arg option on osc_receive.
You can also use osc_receive as an instance method. (see example...) However, more usefully, you can create a Hash map spec of osc_receive calls and pass it to an instance like this:
map = { "/1/fader1" => { :translate => { :remote => 0..1, :local => 0..127 } :action => Proc.new { |instance, val| instance.pitch = val } } } class Instrument include OSCAccessible def pitch=(val) puts "setting pitch to #{val}" ... end end i = Instrument.new i.osc_start(:map => map, :input_port => 8000).join
This kind of approach gives you more flexibility by decoupling the OSC spec for your object from the class -- like a controller and model in MVC.
Osc_receive has a few options:
:translate
There's another difference between those two examples: the :translate option means that val will be translated from a number between 0 to 1 to the analogous value between 0 and 127 before being passed to the code block. So for example if the first argument of the received OSC message is equal to 0.5, val will be equal to 63.
:thru
By setting the :thru option to true, any messages that are received for /1/fader1 are sent immediately to the output (as well as calling the :action block). For example, using the Instrument class from the last example:
map = { "/1/fader1" => { :thru => true :translate => { :remote => 0..1, :local => 0..127 } :action => Proc.new { |instance, val| instance.pitch = val } } } i = Instrument.new i.osc_start(:map => map, :input_port => 8000, :output => { :host => "192.168.1.9", :port => 9000 }).join
As you can see, I also specified an OSC output host and port for this example. If you're ever missing input or output port or host info, your object simply will not perform IO -- it won't raise any kind of exception.
osc_send
Osc_send gives you the ability to output arbitrary OSC messages. The first argument is the address of the message and any arguments after that are the content. Here is an example of our class definition from this first example with output added
class Instrument include OSCAccessible osc_receive("/1/fader1") do |instance, val| instance.velocity = val instance.osc_send("/velocity", val) end def velocity=(val) puts "setting velocity to #{val}" ... end end i = Instrument.new i.osc_start(:map => map, :input_port => 8000, :output => { :host => "192.168.1.9", :port => 9000 }).join i.osc_send("/greeting", "hi!")
In this example, I'm sending a message from both osc_receive's action block and in the main program block after i is instantiated.
osc_start
Osc_start starts all of the OSC servers that are connected to your objects. You must call it on an instance before osc_receive will function.
I'll be adding OSC Access to Diamond and coming up with a way to use it with MicroMIDI in the next few days. Thanks for reading.