aberant-osc-ruby 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- data/README +14 -0
- data/VERSION.yml +4 -0
- data/lib/osc-ruby.rb +16 -0
- data/lib/osc-ruby/bundle.rb +55 -0
- data/lib/osc-ruby/message.rb +51 -0
- data/lib/osc-ruby/osc_argument.rb +19 -0
- data/lib/osc-ruby/osc_types.rb +31 -0
- data/lib/osc-ruby/packet.rb +120 -0
- data/lib/osc-ruby/simple_client.rb +14 -0
- data/lib/osc-ruby/simple_server.rb +93 -0
- data/spec/integration/send_receive_spec.rb +25 -0
- data/spec/spec_helper.rb +11 -0
- data/spec/unit/osc_argument_spec.rb +7 -0
- data/spec/unit/osc_types_spec.rb +26 -0
- data/spec/unit/simple_client_spec.rb +11 -0
- metadata +71 -0
data/README
ADDED
data/VERSION.yml
ADDED
data/lib/osc-ruby.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# osc.rb: Written by Tadayoshi Funaba 2005,2006
|
2
|
+
# $Id: osc.rb,v 1.4 2006-11-10 21:54:37+09 tadf Exp $
|
3
|
+
|
4
|
+
require 'forwardable'
|
5
|
+
require 'socket'
|
6
|
+
require 'thread'
|
7
|
+
|
8
|
+
require 'osc-ruby/osc_types'
|
9
|
+
require 'osc-ruby/packet'
|
10
|
+
require 'osc-ruby/message'
|
11
|
+
require 'osc-ruby/bundle'
|
12
|
+
|
13
|
+
require 'osc-ruby/simple_server'
|
14
|
+
require 'osc-ruby/simple_client'
|
15
|
+
|
16
|
+
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module OSC
|
2
|
+
class Bundle < Packet
|
3
|
+
extend Forwardable
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
attr_accessor :timetag
|
7
|
+
|
8
|
+
undef_method :zip
|
9
|
+
|
10
|
+
de = (Array.instance_methods - self.instance_methods)
|
11
|
+
de -= %w(assoc flatten flatten! pack rassoc transpose)
|
12
|
+
de += %w(include? sort)
|
13
|
+
|
14
|
+
def_delegators(:@args, *de)
|
15
|
+
|
16
|
+
|
17
|
+
def initialize(timetag=nil, *args)
|
18
|
+
@timetag = timetag
|
19
|
+
@args = args
|
20
|
+
end
|
21
|
+
|
22
|
+
def encode()
|
23
|
+
s = OSCString.new('#bundle').encode
|
24
|
+
s << encode_timetag(@timetag)
|
25
|
+
s << @args.collect do |x|
|
26
|
+
x2 = x.encode; [x2.size].pack('N') + x2
|
27
|
+
end.join
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
def to_a() @args.collect{|x| x.to_a} end
|
32
|
+
|
33
|
+
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def encode_timetag(t)
|
38
|
+
case t
|
39
|
+
when NIL # immediately
|
40
|
+
t1 = 0
|
41
|
+
t2 = 1
|
42
|
+
when Numeric
|
43
|
+
t1, fr = t.divmod(1)
|
44
|
+
t2 = (fr * (2**32)).to_i
|
45
|
+
when Time
|
46
|
+
t1, fr = (t.to_f + 2208988800).divmod(1)
|
47
|
+
t2 = (fr * (2**32)).to_i
|
48
|
+
else
|
49
|
+
raise ArgumentError, 'invalid time'
|
50
|
+
end
|
51
|
+
[t1, t2].pack('N2')
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module OSC
|
2
|
+
class Message < Packet
|
3
|
+
extend Forwardable
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
attr_accessor :address
|
7
|
+
undef_method :zip
|
8
|
+
|
9
|
+
de = (Array.instance_methods - self.instance_methods)
|
10
|
+
de -= %w(assoc flatten flatten! pack rassoc transpose)
|
11
|
+
de += %w(include? sort)
|
12
|
+
|
13
|
+
def_delegators(:@args, *de)
|
14
|
+
|
15
|
+
|
16
|
+
def initialize(address, tags=nil, *args)
|
17
|
+
@address = address
|
18
|
+
@args = []
|
19
|
+
args.each_with_index do |arg, i|
|
20
|
+
if tags && tags[i]
|
21
|
+
case tags[i]
|
22
|
+
when ?i; @args << OSCInt32.new(arg)
|
23
|
+
when ?f; @args << OSCFloat32.new(arg)
|
24
|
+
when ?s; @args << OSCString.new(arg)
|
25
|
+
when ?b; @args << OSCBlob.new(arg)
|
26
|
+
when ?*; @args << arg
|
27
|
+
else; raise ArgumentError, 'unknown type'
|
28
|
+
end
|
29
|
+
else
|
30
|
+
case arg
|
31
|
+
when Integer; @args << OSCInt32.new(arg)
|
32
|
+
when Float; @args << OSCFloat32.new(arg)
|
33
|
+
when String; @args << OSCString.new(arg)
|
34
|
+
when OSCArgument; @args << arg
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
def tags() ',' + @args.collect{|x| x.tag}.join end
|
42
|
+
|
43
|
+
def encode
|
44
|
+
s = OSCString.new(@address).encode
|
45
|
+
s << OSCString.new(tags).encode
|
46
|
+
s << @args.collect{|x| x.encode}.join
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_a() @args.collect{|x| x.val} end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module OSC
|
2
|
+
class OSCArgument
|
3
|
+
|
4
|
+
def initialize(val) @val = val end
|
5
|
+
|
6
|
+
attr_accessor :val
|
7
|
+
|
8
|
+
def to_i() @val.to_i end
|
9
|
+
def to_f() @val.to_f end
|
10
|
+
def to_s() @val.to_s end
|
11
|
+
|
12
|
+
def padding(s)
|
13
|
+
s + ("\000" * ((4 - (s.size % 4)) % 4))
|
14
|
+
end
|
15
|
+
|
16
|
+
private :padding
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'osc-ruby/osc_argument'
|
2
|
+
|
3
|
+
module OSC
|
4
|
+
class OSCInt32 < OSCArgument
|
5
|
+
|
6
|
+
def tag() 'i' end
|
7
|
+
def encode() [@val].pack('N') end
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
class OSCFloat32 < OSCArgument
|
12
|
+
|
13
|
+
def tag() 'f' end
|
14
|
+
def encode() [@val].pack('g') end # fake
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
class OSCString < OSCArgument
|
19
|
+
|
20
|
+
def tag() 's' end
|
21
|
+
def encode() padding(@val.sub(/\000.*\z/, '') + "\000") end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
class OSCBlob < OSCArgument
|
26
|
+
|
27
|
+
def tag() 'b' end
|
28
|
+
def encode() padding([@val.size].pack('N') + @val) end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
module OSC
|
2
|
+
class Packet
|
3
|
+
|
4
|
+
class PO
|
5
|
+
def initialize(str) @str, @index = str, 0 end
|
6
|
+
def rem() @str.length - @index end
|
7
|
+
def eof? () rem <= 0 end
|
8
|
+
def skip(n) @index += n end
|
9
|
+
def skip_padding() skip((4 - (@index % 4)) % 4) end
|
10
|
+
|
11
|
+
def getn(n)
|
12
|
+
raise EOFError if rem < n
|
13
|
+
s = @str[@index, n]
|
14
|
+
skip(n)
|
15
|
+
s
|
16
|
+
end
|
17
|
+
|
18
|
+
def getc
|
19
|
+
raise EOFError if rem < 1
|
20
|
+
c = @str[@index]
|
21
|
+
skip(1)
|
22
|
+
c
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.decode_int32(io)
|
27
|
+
i = io.getn(4).unpack('N')[0]
|
28
|
+
i -= 2**32 if i > (2**31-1)
|
29
|
+
i
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.decode_float32(io)
|
33
|
+
f = io.getn(4).unpack('g')[0]
|
34
|
+
f
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.decode_string(io)
|
38
|
+
s = ''
|
39
|
+
until (c = io.getc) == 0
|
40
|
+
s << c
|
41
|
+
end
|
42
|
+
io.skip_padding
|
43
|
+
s
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.decode_blob(io)
|
47
|
+
l = io.getn(4).unpack('N')[0]
|
48
|
+
b = io.getn(l)
|
49
|
+
io.skip_padding
|
50
|
+
b
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.decode_timetag(io)
|
54
|
+
t1 = io.getn(4).unpack('N')[0]
|
55
|
+
t2 = io.getn(4).unpack('N')[0]
|
56
|
+
[t1, t2]
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.decode2(time, packet, list)
|
60
|
+
io = PO.new(packet)
|
61
|
+
id = decode_string(io)
|
62
|
+
if id =~ /\A\#/
|
63
|
+
if id == '#bundle'
|
64
|
+
t1, t2 = decode_timetag(io)
|
65
|
+
if t1 == 0 && t2 == 1
|
66
|
+
time = nil
|
67
|
+
else
|
68
|
+
time = t1 + t2.to_f / (2**32)
|
69
|
+
end
|
70
|
+
until io.eof?
|
71
|
+
l = io.getn(4).unpack('N')[0]
|
72
|
+
s = io.getn(l)
|
73
|
+
decode2(time, s, list)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
elsif id =~ /\//
|
77
|
+
address = id
|
78
|
+
if io.getc == ?,
|
79
|
+
tags = decode_string(io)
|
80
|
+
args = []
|
81
|
+
tags.scan(/./) do |t|
|
82
|
+
case t
|
83
|
+
when 'i'
|
84
|
+
i = decode_int32(io)
|
85
|
+
args << OSCInt32.new(i)
|
86
|
+
when 'f'
|
87
|
+
f = decode_float32(io)
|
88
|
+
args << OSCFloat32.new(f)
|
89
|
+
when 's'
|
90
|
+
s = decode_string(io)
|
91
|
+
args << OSCString.new(s)
|
92
|
+
when 'b'
|
93
|
+
b = decode_blob(io)
|
94
|
+
args << OSCBlob.new(b)
|
95
|
+
when /[htd]/; io.read(8)
|
96
|
+
when 'S'; decode_string(io)
|
97
|
+
when /[crm]/; io.read(4)
|
98
|
+
when /[TFNI\[\]]/;
|
99
|
+
end
|
100
|
+
end
|
101
|
+
list << [time, Message.new(address, nil, *args)]
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
private_class_method :decode_int32,
|
107
|
+
:decode_float32,
|
108
|
+
:decode_string,
|
109
|
+
:decode_blob,
|
110
|
+
:decode_timetag,
|
111
|
+
:decode2
|
112
|
+
|
113
|
+
def self.decode(packet)
|
114
|
+
list = []
|
115
|
+
decode2(nil, packet, list)
|
116
|
+
list
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module OSC
|
2
|
+
class SimpleServer
|
3
|
+
|
4
|
+
def initialize(port)
|
5
|
+
@so = UDPSocket.new
|
6
|
+
@so.bind('', port)
|
7
|
+
@cb = []
|
8
|
+
@qu = Queue.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def add_method(pat, obj=nil, &proc)
|
12
|
+
case pat
|
13
|
+
when NIL; re = pat
|
14
|
+
when Regexp; re = pat
|
15
|
+
when String
|
16
|
+
pat = pat.dup
|
17
|
+
pat.gsub!(/[.^(|)]/, '\\1')
|
18
|
+
pat.gsub!(/\?/, '[^/]')
|
19
|
+
pat.gsub!(/\*/, '[^/]*')
|
20
|
+
pat.gsub!(/\[!/, '[^')
|
21
|
+
pat.gsub!(/\{/, '(')
|
22
|
+
pat.gsub!(/,/, '|')
|
23
|
+
pat.gsub!(/\}/, ')')
|
24
|
+
pat.gsub!(/\A/, '\A')
|
25
|
+
pat.gsub!(/\z/, '\z')
|
26
|
+
re = Regexp.new(pat)
|
27
|
+
else
|
28
|
+
raise ArgumentError, 'invalid pattern'
|
29
|
+
end
|
30
|
+
|
31
|
+
unless ( obj && !proc) ||
|
32
|
+
(!obj && proc)
|
33
|
+
raise ArgumentError, 'wrong number of arguments'
|
34
|
+
end
|
35
|
+
@cb << [re, (obj || proc)]
|
36
|
+
end
|
37
|
+
|
38
|
+
def sendmesg(mesg)
|
39
|
+
@cb.each do |re, obj|
|
40
|
+
if re.nil? || re =~ mesg.address
|
41
|
+
obj.send(if Proc === obj then :call else :accept end, mesg)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def dispatcher
|
47
|
+
loop do
|
48
|
+
time, mesg = @qu.pop
|
49
|
+
now = Time.now.to_f + 2208988800
|
50
|
+
diff = if time.nil?
|
51
|
+
then 0 else time - now end
|
52
|
+
|
53
|
+
if diff <= 0
|
54
|
+
sendmesg(mesg)
|
55
|
+
else
|
56
|
+
Thread.fork do
|
57
|
+
sleep(diff)
|
58
|
+
sendmesg(mesg)
|
59
|
+
Thread.exit
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def detector
|
66
|
+
loop do
|
67
|
+
pa = @so.recv(16384)
|
68
|
+
begin
|
69
|
+
Packet.decode(pa).each{|x| @qu.push(x)}
|
70
|
+
rescue EOFError
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
private :sendmesg, :dispatcher, :detector
|
76
|
+
|
77
|
+
def run
|
78
|
+
Thread.fork do
|
79
|
+
begin
|
80
|
+
dispatcher
|
81
|
+
rescue
|
82
|
+
Thread.main.raise $!
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
begin
|
87
|
+
detector
|
88
|
+
rescue
|
89
|
+
Thread.main.raise $!
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# require File.join( File.dirname(__FILE__) , '..', 'spec_helper' )
|
2
|
+
# TODO: figure out what thread magic i need to get this to work
|
3
|
+
|
4
|
+
# describe "send and receive" do
|
5
|
+
# it "should be able to send and receive messages" do
|
6
|
+
# server = OSC::SimpleServer.new( 3333 )
|
7
|
+
#
|
8
|
+
# watcher = Object.new
|
9
|
+
# stub( watcher ).called
|
10
|
+
#
|
11
|
+
# server.add_method( "/hi") do |m|
|
12
|
+
# watcher.called
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# Thread.new do
|
16
|
+
# server.run
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
#
|
20
|
+
# client = OSC::SimpleClient.new( "localhost", 3333 )
|
21
|
+
# client.send( OSC::Message.new( "/hi" ) )
|
22
|
+
#
|
23
|
+
# watcher.should have_received.called
|
24
|
+
# end
|
25
|
+
# end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require File.join( File.dirname(__FILE__) , '..', 'spec_helper' )
|
2
|
+
|
3
|
+
describe OSC::OSCInt32 do
|
4
|
+
it "should not blow up" do
|
5
|
+
OSC::OSCInt32.new 1
|
6
|
+
end
|
7
|
+
|
8
|
+
end
|
9
|
+
|
10
|
+
describe OSC::OSCFloat32 do
|
11
|
+
it "should not blow up" do
|
12
|
+
OSC::OSCFloat32.new 1.0
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe OSC::OSCString do
|
17
|
+
it "should not blow up" do
|
18
|
+
OSC::OSCString.new "1"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe OSC::OSCBlob do
|
23
|
+
it "should not blow up" do
|
24
|
+
OSC::OSCBlob.new 1
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require File.join( File.dirname(__FILE__) , '..', 'spec_helper' )
|
2
|
+
|
3
|
+
describe OSC::SimpleClient do
|
4
|
+
it "should not blow up" do
|
5
|
+
udp = Object.new
|
6
|
+
stub( udp ).connect
|
7
|
+
stub( UDPSocket ).new.returns( udp )
|
8
|
+
|
9
|
+
OSC::SimpleClient.new( "localhost", 3333 )
|
10
|
+
end
|
11
|
+
end
|
metadata
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: aberant-osc-ruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.5
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- aberant
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-07-16 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email: qzzzq1@gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README
|
24
|
+
files:
|
25
|
+
- VERSION.yml
|
26
|
+
- lib/osc-ruby
|
27
|
+
- lib/osc-ruby/bundle.rb
|
28
|
+
- lib/osc-ruby/message.rb
|
29
|
+
- lib/osc-ruby/osc_argument.rb
|
30
|
+
- lib/osc-ruby/osc_types.rb
|
31
|
+
- lib/osc-ruby/packet.rb
|
32
|
+
- lib/osc-ruby/simple_client.rb
|
33
|
+
- lib/osc-ruby/simple_server.rb
|
34
|
+
- lib/osc-ruby.rb
|
35
|
+
- spec/integration
|
36
|
+
- spec/integration/send_receive_spec.rb
|
37
|
+
- spec/spec_helper.rb
|
38
|
+
- spec/unit
|
39
|
+
- spec/unit/osc_argument_spec.rb
|
40
|
+
- spec/unit/osc_types_spec.rb
|
41
|
+
- spec/unit/simple_client_spec.rb
|
42
|
+
- README
|
43
|
+
has_rdoc: true
|
44
|
+
homepage: http://github.com/aberant/osc-ruby
|
45
|
+
post_install_message:
|
46
|
+
rdoc_options:
|
47
|
+
- --inline-source
|
48
|
+
- --charset=UTF-8
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: "0"
|
56
|
+
version:
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: "0"
|
62
|
+
version:
|
63
|
+
requirements: []
|
64
|
+
|
65
|
+
rubyforge_project:
|
66
|
+
rubygems_version: 1.2.0
|
67
|
+
signing_key:
|
68
|
+
specification_version: 2
|
69
|
+
summary: inital gem
|
70
|
+
test_files: []
|
71
|
+
|