ruby osc seqencer version2
i updated my osc sequencer in ruby. now it doesn't just send osc events but also react to it. i made a small processing sketch that sends osc play,pause,stop events for each track in the sequencer. i also refactored the ruby code a bit, to use classes. next steps i plan are to separate the sequencer code from the sequences and allow sequences to be added or deleted at runtime via osc messages.
the code isn't very reusable now, so i still considere this more as a prove of concept, but i like the idea of separating the frontend and the backend via osc events and having sequences that can run independently.
that should enable some nice osc controlled audio/video installations.
write me a comment or mail if you have some ideas for improvement or if you use the code in one of your projects
the ruby code
require 'osc'
Host = 'localhost'
Port = 3334
class BaseSequence
attr_accessor :seq, :pos, :msg, :name, :res
def initialize( osc, name, res, host = 'localhost', port = 3334 )
@pos = 0;
@res = res
@name = name;
@running = false;
@osc = osc
@host = host
@port = port
end
def play()
@running = true;
end
def stop()
@running = false;
@pos = 0;
end
def pause()
@running = false;
end
def update()
end
end
class Sequence < BaseSequence
def update()
if @running then
@msg.args = [@seq[ @pos % @seq.length]]
@osc.send( @msg, 0, @host, @port ) if @seq[ @pos % @seq.length ] != 0
@pos+=1
end
end
end
class DrumSequence < BaseSequence
def update()
if @running then
(0..@msg.length-1).each { |i|
@osc.send(@msg[i], 0, @host, @port) if @seq[i][@pos % @seq[i].length] == 1
}
@pos+=1
end
end
end
c = OSC::UDPSocket.new
bpm = 120
step = 1.0/96;
s = bpm * step / 60.0 ;
drum = DrumSequence.new(c, "drum", 1.0/8 )
hh = OSC::Message.new('/drum', 's', "hh" )
bd = OSC::Message.new('/drum', 's', "bd" )
sn = OSC::Message.new('/drum', 's', "sn" )
drum.msg = [ bd, sn, hh ]
drum.seq = [
[1, 0, 0, 1, 0, 1, 0, 0 ],
[0, 0, 1, 0, 0, 0, 1, 1 ],
[1, 1, 1, 1, 1, 1, 1, 1 ]
]
bass = Sequence.new(c, "bass", 1.0/8)
bass.seq = [ 45, 45, 0, 45, 45, 0, 48, 0,
45, 45, 0, 45, 45, 0, 43, 0 ]
bass.msg = OSC::Message.new('/bass','i', 64 )
mel = Sequence.new(c, "mel", 1.0/16, 'localhost', 57120)
mel.seq = [ 57, 57, 0, 60, 57, 0, 50 ]
mel.msg = OSC::Message.new('/melody', 'i', 64 )
count = 0;
count2 = 0
sequences = {
bass.name => bass,
mel.name => mel,
drum.name => drum
}
bass.play()
mel.play()
drum.play()
srv = OSC::UDPServer.new
srv.bind Host, 3333
srv.add_method '/seq/*', 's' do |msg|
domain, port, host, ip = msg.source
puts "#{msg.address} -> #{msg.args.inspect} from #{host}:#{port}"
puts msg.address, msg.args[0]
if msg.args[0] == "play" then
sequences[msg.address.split("/")[-1]].play
elsif msg.args[0] == "stop" then
sequences[msg.address.split("/")[-1]].stop
elsif msg.args[0] == "pause" then
sequences[msg.address.split("/")[-1]].pause
end
end
Thread.new do
srv.serve
end
count =0;
while(true)
sequences.each{ |k,v|
v.update if (count % 96 * v.res == 0)
}
count+=1
sleep s
end
and the processing code
import oscP5.*;
import netP5.*;
OscP5 oscP5;
String oscP5event;
NetAddress myRemoteLocation;
void setup() {
size(300,300);
oscP5 = new OscP5(this,12000);
myRemoteLocation = new NetAddress("127.0.0.1",3333);
}
void draw() {
line( 0, 100, 300, 100 );
line( 0, 200, 300, 200 );
line( 100, 0, 100, 300 );
line( 200, 0, 200, 300 );
}
void mousePressed() {
String track = "";
String cmd = "";
if (mouseY < 100) {
track = "drum";
} else if ( mouseY < 200 ) {
track = "bass";
} else {
track = "mel";
}
if (mouseX < 100) {
cmd = "play";
} else if ( mouseX < 200 ) {
cmd = "pause";
} else {
cmd = "stop";
}
println( track + " " + cmd );
OscMessage msg = oscP5.newMsg( "/seq/" + track );
msg.add( cmd );
oscP5.send(msg, myRemoteLocation);
}
See also:
osc sequencer in ruby
sketch experiment 7 - osc events
Ronin experiment 5 - osc
Sonic Pi beatslicing livecoding session