aberant-osc-ruby 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
|