aberant-osc-ruby 0.1.5 → 0.1.6
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +17 -0
- data/VERSION.yml +2 -2
- data/lib/osc-ruby.rb +16 -2
- data/lib/osc-ruby/address_pattern.rb +51 -0
- data/lib/osc-ruby/bundle.rb +4 -4
- data/lib/osc-ruby/{simple_client.rb → client.rb} +1 -1
- data/lib/osc-ruby/core_ext/numeric.rb +17 -0
- data/lib/osc-ruby/core_ext/object.rb +37 -0
- data/lib/osc-ruby/core_ext/time.rb +6 -0
- data/lib/osc-ruby/message.rb +24 -10
- data/lib/osc-ruby/network_packet.rb +42 -0
- data/lib/osc-ruby/osc_argument.rb +2 -3
- data/lib/osc-ruby/osc_packet.rb +113 -0
- data/lib/osc-ruby/osc_types.rb +1 -1
- data/lib/osc-ruby/packet.rb +19 -5
- data/lib/osc-ruby/server.rb +88 -0
- data/spec/builders/message_builder.rb +51 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/unit/address_pattern_spec.rb +83 -0
- data/spec/unit/message_bundle_spec.rb +6 -0
- data/spec/unit/message_spec.rb +34 -0
- data/spec/unit/network_packet_spec.rb +33 -0
- data/spec/unit/osc_complex_packets_spec.rb +39 -0
- data/spec/unit/osc_simple_packets_spec.rb +108 -0
- metadata +23 -10
- data/README +0 -14
- data/lib/osc-ruby/simple_server.rb +0 -93
- data/spec/integration/send_receive_spec.rb +0 -25
- data/spec/unit/simple_client_spec.rb +0 -11
data/README.rdoc
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
= An OSC client for Ruby
|
2
|
+
|
3
|
+
http://opensoundcontrol.org/
|
4
|
+
|
5
|
+
== GOAL
|
6
|
+
|
7
|
+
I've used the OSC gem originally created by Tadayoshi Funaba and wanted to update it for ruby 1.9 compatibility. I'm also writing rspec tests for this library to allow others to more easily understand/update the code. It's getting closer to awesome every day...
|
8
|
+
|
9
|
+
Guaranteed to be compatible with tuio-ruby
|
10
|
+
|
11
|
+
== CREDITS
|
12
|
+
|
13
|
+
Originally created by...
|
14
|
+
Tadayoshi Funaba
|
15
|
+
http://www.funaba.org/en/
|
16
|
+
|
17
|
+
thx also to Toby Tripp and Obtiva
|
data/VERSION.yml
CHANGED
data/lib/osc-ruby.rb
CHANGED
@@ -4,13 +4,27 @@
|
|
4
4
|
require 'forwardable'
|
5
5
|
require 'socket'
|
6
6
|
require 'thread'
|
7
|
+
require 'monitor'
|
7
8
|
|
9
|
+
|
10
|
+
$:.unshift( File.dirname( __FILE__ ) )
|
11
|
+
|
12
|
+
# core extensions
|
13
|
+
require 'osc-ruby/core_ext/object'
|
14
|
+
require 'osc-ruby/core_ext/numeric'
|
15
|
+
require 'osc-ruby/core_ext/time'
|
16
|
+
|
17
|
+
|
18
|
+
# jus the basics
|
8
19
|
require 'osc-ruby/osc_types'
|
9
20
|
require 'osc-ruby/packet'
|
21
|
+
require 'osc-ruby/osc_packet'
|
10
22
|
require 'osc-ruby/message'
|
11
23
|
require 'osc-ruby/bundle'
|
24
|
+
require 'osc-ruby/address_pattern'
|
12
25
|
|
13
|
-
|
14
|
-
require 'osc-ruby/
|
26
|
+
# now we gettin fancy
|
27
|
+
require 'osc-ruby/server'
|
28
|
+
require 'osc-ruby/client'
|
15
29
|
|
16
30
|
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module OSC
|
2
|
+
class AddressPattern
|
3
|
+
def initialize( pattern )
|
4
|
+
@pattern = pattern
|
5
|
+
|
6
|
+
generate_regex_from_pattern
|
7
|
+
end
|
8
|
+
|
9
|
+
def match?( address )
|
10
|
+
!!(@re.nil? || @re.match( address ))
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
def generate_regex_from_pattern
|
15
|
+
case @pattern
|
16
|
+
when NIL; @re = @pattern
|
17
|
+
when Regexp; @re = @pattern
|
18
|
+
when String
|
19
|
+
|
20
|
+
# i'm unsure what this does
|
21
|
+
# @pattern.gsub!(/[.^(|)]/, '\\1')
|
22
|
+
|
23
|
+
# handles osc single char wildcard matching
|
24
|
+
@pattern.gsub!(/\?/, '[^/]')
|
25
|
+
|
26
|
+
# handles osc * - 0 or more matching
|
27
|
+
@pattern.gsub!(/\*/, '[^/]*')
|
28
|
+
|
29
|
+
# handles [!] matching
|
30
|
+
@pattern.gsub!(/\[!/, '[^')
|
31
|
+
|
32
|
+
# handles {} matching
|
33
|
+
@pattern.gsub!(/\{/, '(')
|
34
|
+
@pattern.gsub!(/,/, '|')
|
35
|
+
@pattern.gsub!(/\}/, ')')
|
36
|
+
|
37
|
+
|
38
|
+
# keeps from matching before the begining of the pattern
|
39
|
+
@pattern.gsub!(/\A/, '\A')
|
40
|
+
|
41
|
+
# keeps from matching beyond the end,
|
42
|
+
# eg. pattern /hi does not match /hidden
|
43
|
+
@pattern.gsub!(/\z/, '\z')
|
44
|
+
|
45
|
+
@re = Regexp.new(@pattern)
|
46
|
+
else
|
47
|
+
raise ArgumentError, 'invalid pattern'
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/lib/osc-ruby/bundle.rb
CHANGED
@@ -7,11 +7,11 @@ module OSC
|
|
7
7
|
|
8
8
|
undef_method :zip
|
9
9
|
|
10
|
-
de = (Array.instance_methods - self.instance_methods)
|
11
|
-
de -= %w(assoc flatten flatten! pack rassoc transpose)
|
12
|
-
de += %w(include? sort)
|
10
|
+
# de = (Array.instance_methods - self.instance_methods)
|
11
|
+
# de -= %w(assoc flatten flatten! pack rassoc transpose)
|
12
|
+
# de += %w(include? sort)
|
13
13
|
|
14
|
-
def_delegators(:@args, *de)
|
14
|
+
# def_delegators(:@args, *de)
|
15
15
|
|
16
16
|
|
17
17
|
def initialize(timetag=nil, *args)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class Numeric
|
2
|
+
# Convert time intervals to seconds
|
3
|
+
def milliseconds; self/1000.0; end
|
4
|
+
def seconds; self; end
|
5
|
+
def minutes; self*60; end
|
6
|
+
def hours; self*60*60; end
|
7
|
+
def days; self*60*60*24; end
|
8
|
+
def weeks; self*60*60*24*7; end
|
9
|
+
|
10
|
+
# Convert seconds to other intervals
|
11
|
+
def to_milliseconds; self*1000; end
|
12
|
+
def to_seconds; self; end
|
13
|
+
def to_minutes; self/60.0; end
|
14
|
+
def to_hours; self/(60*60.0); end
|
15
|
+
def to_days; self/(60*60*24.0); end
|
16
|
+
def to_weeks; self/(60*60*24*7.0); end
|
17
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# lifted from the ruby programming language book, thx matz!
|
2
|
+
|
3
|
+
# Obtain the Mutex associated with the object o, and then evaluate
|
4
|
+
# the block under the protection of that Mutex.
|
5
|
+
# This works like the synchronized keyword of Java.
|
6
|
+
def synchronized(o)
|
7
|
+
o.mutex.synchronize { yield }
|
8
|
+
end
|
9
|
+
# Object.mutex does not actually exist. We've got to define it.
|
10
|
+
# This method returns a unique Mutex for every object, and
|
11
|
+
# always returns the same Mutex for any particular object.
|
12
|
+
# It creates Mutexes lazily, which requires synchronization for
|
13
|
+
# thread safety.
|
14
|
+
class Object
|
15
|
+
# Return the Mutex for this object, creating it if necessary.
|
16
|
+
# The tricky part is making sure that two threads don't call
|
17
|
+
# this at the same time and end up creating two different mutexes.
|
18
|
+
def mutex
|
19
|
+
# If this object already has a mutex, just return it
|
20
|
+
return @__mutex if @__mutex
|
21
|
+
|
22
|
+
# Otherwise, we've got to create a mutex for the object.
|
23
|
+
# To do this safely we've got to synchronize on our class object.
|
24
|
+
synchronized(self.class) {
|
25
|
+
# Check again: by the time we enter this synchronized block,
|
26
|
+
# some other thread might have already created the mutex.
|
27
|
+
@__mutex = @__mutex || Mutex.new
|
28
|
+
}
|
29
|
+
# The return value is @__mutex
|
30
|
+
end
|
31
|
+
end
|
32
|
+
# The Object.mutex method defined above needs to lock the class
|
33
|
+
# if the object doesn't have a Mutex yet. If the class doesn't have
|
34
|
+
# its own Mutex yet, then the class of the class (the Class object)
|
35
|
+
# will be locked. In order to prevent infinite recursion, we must
|
36
|
+
# ensure that the Class object has a mutex.
|
37
|
+
Class.instance_eval { @__mutex = Mutex.new }
|
data/lib/osc-ruby/message.rb
CHANGED
@@ -1,21 +1,30 @@
|
|
1
1
|
module OSC
|
2
2
|
class Message < Packet
|
3
3
|
extend Forwardable
|
4
|
-
include Enumerable
|
4
|
+
# include Enumerable
|
5
5
|
|
6
6
|
attr_accessor :address
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
de -= %w(assoc flatten flatten! pack rassoc transpose)
|
11
|
-
de += %w(include? sort)
|
7
|
+
attr_accessor :time
|
8
|
+
|
9
|
+
# undef_method :zip
|
12
10
|
|
13
|
-
|
11
|
+
# de = (Array.instance_methods - self.instance_methods)
|
12
|
+
# de -= %w(assoc flatten flatten! pack rassoc transpose)
|
13
|
+
# de += %w(include? sort)
|
14
14
|
|
15
|
+
# def_delegators(:@args, *de)
|
16
|
+
|
17
|
+
def self.new_with_time( address, time, tags=nil, *args )
|
18
|
+
message = new( address, tags, *args )
|
19
|
+
message.time = time
|
20
|
+
message
|
21
|
+
end
|
15
22
|
|
16
23
|
def initialize(address, tags=nil, *args)
|
17
24
|
@address = address
|
18
25
|
@args = []
|
26
|
+
|
27
|
+
|
19
28
|
args.each_with_index do |arg, i|
|
20
29
|
if tags && tags[i]
|
21
30
|
case tags[i]
|
@@ -38,14 +47,19 @@ module OSC
|
|
38
47
|
end
|
39
48
|
|
40
49
|
|
41
|
-
def tags()
|
50
|
+
def tags() @args.collect{|x| x.tag}.join end
|
42
51
|
|
43
52
|
def encode
|
44
|
-
s = OSCString.new(@address).encode
|
45
|
-
s << OSCString.new(tags).encode
|
53
|
+
s = OSCString.new( @address ).encode
|
54
|
+
s << OSCString.new( ',' + tags ).encode
|
46
55
|
s << @args.collect{|x| x.encode}.join
|
47
56
|
end
|
48
57
|
|
49
58
|
def to_a() @args.collect{|x| x.val} end
|
59
|
+
|
60
|
+
def eql?( other )
|
61
|
+
@address == other.address &&
|
62
|
+
to_a == other.to_a
|
63
|
+
end
|
50
64
|
end
|
51
65
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module OSC
|
2
|
+
class NetworkPacket
|
3
|
+
def initialize(str)
|
4
|
+
@str, @index = str, 0
|
5
|
+
end
|
6
|
+
|
7
|
+
def to_s
|
8
|
+
@str
|
9
|
+
end
|
10
|
+
|
11
|
+
def rem()
|
12
|
+
@str.length - @index
|
13
|
+
end
|
14
|
+
|
15
|
+
def eof? ()
|
16
|
+
rem <= 0
|
17
|
+
end
|
18
|
+
|
19
|
+
def skip(n)
|
20
|
+
@index += n
|
21
|
+
end
|
22
|
+
|
23
|
+
def skip_padding()
|
24
|
+
skip((4 - (@index % 4)) % 4)
|
25
|
+
end
|
26
|
+
|
27
|
+
def getn(n)
|
28
|
+
raise EOFError if rem < n
|
29
|
+
s = @str[@index, n]
|
30
|
+
skip(n)
|
31
|
+
s
|
32
|
+
end
|
33
|
+
|
34
|
+
def getc
|
35
|
+
raise EOFError if rem < 1
|
36
|
+
c = @str[@index]
|
37
|
+
skip(1)
|
38
|
+
c
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require File.join( File.dirname( __FILE__ ), 'network_packet')
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
module OSC
|
5
|
+
class OSCPacket
|
6
|
+
def self.messages_from_network( string )
|
7
|
+
messages = []
|
8
|
+
osc = new( string )
|
9
|
+
|
10
|
+
if osc.bundle?
|
11
|
+
bundle = osc.get_string
|
12
|
+
time = osc.get_timestamp
|
13
|
+
|
14
|
+
osc.get_bundle_messages.each do | message |
|
15
|
+
messages << decode_simple_message( time, OSCPacket.new( message ) )
|
16
|
+
end
|
17
|
+
|
18
|
+
else
|
19
|
+
messages << decode_simple_message( time, osc )
|
20
|
+
end
|
21
|
+
|
22
|
+
return messages
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.decode_simple_message( time, osc_packet )
|
26
|
+
address = osc_packet.get_string
|
27
|
+
args = osc_packet.get_arguments
|
28
|
+
|
29
|
+
Message.new_with_time(address, time, nil, *args )
|
30
|
+
end
|
31
|
+
|
32
|
+
def initialize( string )
|
33
|
+
@packet = NetworkPacket.new( string )
|
34
|
+
|
35
|
+
@types = { "i" => lambda{ OSCInt32.new( get_int32 ) },
|
36
|
+
"f" => lambda{ OSCFloat32.new( get_float32 ) },
|
37
|
+
"s" => lambda{ OSCString.new( get_string ) },
|
38
|
+
"b" => lambda{ OSCBlob.new( get_blob )}
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
def get_bundle_messages
|
43
|
+
bundle_messages = []
|
44
|
+
|
45
|
+
until @packet.eof?
|
46
|
+
l = @packet.getn(4).unpack('N')[0]
|
47
|
+
bundle_messages << @packet.getn(l)
|
48
|
+
end
|
49
|
+
bundle_messages
|
50
|
+
end
|
51
|
+
|
52
|
+
def get_string
|
53
|
+
result = ''
|
54
|
+
until (c = @packet.getc) == 0
|
55
|
+
result << c
|
56
|
+
end
|
57
|
+
|
58
|
+
@packet.skip_padding
|
59
|
+
result
|
60
|
+
end
|
61
|
+
|
62
|
+
def get_timestamp
|
63
|
+
t1 = @packet.getn(4).unpack('N')[0]
|
64
|
+
t2 = @packet.getn(4).unpack('N')[0]
|
65
|
+
@packet.skip_padding
|
66
|
+
|
67
|
+
if t1 == 0 && t2 == 1
|
68
|
+
time = nil
|
69
|
+
else
|
70
|
+
time = t1 + t2.to_f / (2**32)
|
71
|
+
end
|
72
|
+
|
73
|
+
time
|
74
|
+
end
|
75
|
+
|
76
|
+
def get_arguments
|
77
|
+
if @packet.getc == ?,
|
78
|
+
|
79
|
+
tags = get_string
|
80
|
+
args = []
|
81
|
+
|
82
|
+
tags.scan(/./) do | tag |
|
83
|
+
args << @types[tag].call
|
84
|
+
end
|
85
|
+
args
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def get_int32
|
90
|
+
i = @packet.getn(4).unpack('N')[0]
|
91
|
+
i -= 2**32 if i > (2**31-1)
|
92
|
+
@packet.skip_padding
|
93
|
+
i
|
94
|
+
end
|
95
|
+
|
96
|
+
def get_float32
|
97
|
+
f = @packet.getn(4).unpack('g')[0]
|
98
|
+
@packet.skip_padding
|
99
|
+
f
|
100
|
+
end
|
101
|
+
|
102
|
+
def get_blob
|
103
|
+
l = @packet.getn(4).unpack('N')[0]
|
104
|
+
b = @packet.getn(l)
|
105
|
+
@packet.skip_padding
|
106
|
+
b
|
107
|
+
end
|
108
|
+
|
109
|
+
def bundle?
|
110
|
+
!(@packet.to_s =~ /\A\#bundle/).nil?
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
data/lib/osc-ruby/osc_types.rb
CHANGED
data/lib/osc-ruby/packet.rb
CHANGED
@@ -2,11 +2,25 @@ module OSC
|
|
2
2
|
class Packet
|
3
3
|
|
4
4
|
class PO
|
5
|
-
def initialize(str)
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
def
|
5
|
+
def initialize(str)
|
6
|
+
@str, @index = str, 0
|
7
|
+
end
|
8
|
+
|
9
|
+
def rem()
|
10
|
+
@str.length - @index
|
11
|
+
end
|
12
|
+
|
13
|
+
def eof? ()
|
14
|
+
rem <= 0
|
15
|
+
end
|
16
|
+
|
17
|
+
def skip(n)
|
18
|
+
@index += n
|
19
|
+
end
|
20
|
+
|
21
|
+
def skip_padding()
|
22
|
+
skip((4 - (@index % 4)) % 4)
|
23
|
+
end
|
10
24
|
|
11
25
|
def getn(n)
|
12
26
|
raise EOFError if rem < n
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module OSC
|
2
|
+
class Server
|
3
|
+
|
4
|
+
def initialize(port)
|
5
|
+
@socket = UDPSocket.new
|
6
|
+
@socket.bind('', port)
|
7
|
+
@cb = []
|
8
|
+
@queue = Queue.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def run
|
12
|
+
start_dispatcher
|
13
|
+
|
14
|
+
start_detector
|
15
|
+
end
|
16
|
+
|
17
|
+
def stop
|
18
|
+
@socket.close
|
19
|
+
end
|
20
|
+
|
21
|
+
def add_method(address_pattern, &proc)
|
22
|
+
matcher = AddressPattern.new( address_pattern )
|
23
|
+
|
24
|
+
@cb << [matcher, proc]
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def start_detector
|
30
|
+
begin
|
31
|
+
detector
|
32
|
+
rescue
|
33
|
+
Thread.main.raise $!
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def start_dispatcher
|
38
|
+
Thread.fork do
|
39
|
+
begin
|
40
|
+
dispatcher
|
41
|
+
rescue
|
42
|
+
Thread.main.raise $!
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def sendmesg(mesg)
|
48
|
+
@cb.each do |matcher, obj|
|
49
|
+
if matcher.match?( mesg.address )
|
50
|
+
obj.call( mesg )
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def dispatcher
|
56
|
+
loop do
|
57
|
+
mesg = @queue.pop
|
58
|
+
|
59
|
+
dispatch_message( mesg )
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def detector
|
64
|
+
loop do
|
65
|
+
pa = @socket.recv(16384)
|
66
|
+
begin
|
67
|
+
OSCPacket.messages_from_network(pa).each{|x| @queue.push(x)}
|
68
|
+
rescue EOFError
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def dispatch_message( message )
|
74
|
+
diff = ( message.time || 0 ) - Time.now.to_ntp
|
75
|
+
|
76
|
+
if diff <= 0
|
77
|
+
sendmesg( message)
|
78
|
+
else # spawn a thread to wait until it's time
|
79
|
+
Thread.fork do
|
80
|
+
sleep(diff)
|
81
|
+
sendmesg(mesg)
|
82
|
+
Thread.exit
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
class MessageBuilder
|
2
|
+
|
3
|
+
def initialize
|
4
|
+
@address = ""
|
5
|
+
@tags = []
|
6
|
+
@values = []
|
7
|
+
@time = nil
|
8
|
+
end
|
9
|
+
|
10
|
+
def with_address( addr )
|
11
|
+
@address = addr
|
12
|
+
self
|
13
|
+
end
|
14
|
+
|
15
|
+
def with_float( float )
|
16
|
+
with_arg( "f", float )
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
def with_int( int )
|
21
|
+
with_arg( "i", int )
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
def with_string( string )
|
26
|
+
with_arg( "s", string )
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
def with_blob( blob )
|
31
|
+
with_arg( "b", blob )
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
def with_time( time )
|
36
|
+
@time = time
|
37
|
+
end
|
38
|
+
|
39
|
+
def build
|
40
|
+
message = OSC::Message.new( @address , @tags.join, *@values)
|
41
|
+
message.time = @time
|
42
|
+
message
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def with_arg( tag, value )
|
48
|
+
@tags << tag
|
49
|
+
@values << value
|
50
|
+
end
|
51
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -3,8 +3,10 @@ require 'spec'
|
|
3
3
|
require 'rr'
|
4
4
|
|
5
5
|
$:.unshift( File.join( File.dirname( __FILE__), '..', 'lib' ) )
|
6
|
+
$:.unshift( File.dirname( __FILE__ ) )
|
6
7
|
|
7
8
|
require 'osc-ruby'
|
9
|
+
require 'builders/message_builder'
|
8
10
|
|
9
11
|
Spec::Runner.configure do |config|
|
10
12
|
config.mock_with RR::Adapters::Rspec
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require File.join( File.dirname(__FILE__) , '..', 'spec_helper' )
|
2
|
+
|
3
|
+
describe OSC::AddressPattern do
|
4
|
+
it "should match anything if the pattern is nil" do
|
5
|
+
ap = OSC::AddressPattern.new( nil )
|
6
|
+
|
7
|
+
ap.match?( "/some/nonsense").should be_true
|
8
|
+
ap.match?( "/completely.different").should be_true
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should match based on a regex" do
|
12
|
+
ap = OSC::AddressPattern.new( /hi/ )
|
13
|
+
|
14
|
+
ap.match?( '/hi' ).should be_true
|
15
|
+
ap.match?( '/hidden' ).should be_true
|
16
|
+
|
17
|
+
ap.match?( '/bye' ).should be_false
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should return a regex if the pattern is a string" do
|
21
|
+
ap = OSC::AddressPattern.new( "/hi" )
|
22
|
+
|
23
|
+
ap.match?('/hi').should be_true
|
24
|
+
|
25
|
+
ap.match?(' /hi').should be_false
|
26
|
+
ap.match?('/ahi').should be_false
|
27
|
+
ap.match?( '/hidden' ).should be_false
|
28
|
+
ap.match?( '/bye' ).should be_false
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should match with question mark" do
|
32
|
+
ap = OSC::AddressPattern.new( "/h?l" )
|
33
|
+
|
34
|
+
ap.match?('/hal').should be_true
|
35
|
+
ap.match?('/hel').should be_true
|
36
|
+
ap.match?('/hil').should be_true
|
37
|
+
ap.match?('/hol').should be_true
|
38
|
+
ap.match?('/hul').should be_true
|
39
|
+
ap.match?('/hub').should be_false
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should match with *" do
|
43
|
+
ap = OSC::AddressPattern.new( "/believ*d" )
|
44
|
+
|
45
|
+
ap.match?('/believd').should be_true
|
46
|
+
ap.match?('/believed').should be_true
|
47
|
+
ap.match?('/believeeed').should be_true
|
48
|
+
ap.match?('/believaeeeioud').should be_true
|
49
|
+
ap.match?('/believaeeeioud').should be_true
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should match with []" do
|
53
|
+
ap = OSC::AddressPattern.new( "/believ[aeiou]d" )
|
54
|
+
|
55
|
+
ap.match?('/believad').should be_true
|
56
|
+
ap.match?('/believed').should be_true
|
57
|
+
ap.match?('/believid').should be_true
|
58
|
+
ap.match?('/believod').should be_true
|
59
|
+
ap.match?('/believud').should be_true
|
60
|
+
ap.match?('/believkd').should be_false
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should match with [!]" do
|
64
|
+
ap = OSC::AddressPattern.new( "/believ[!aeiou]d" )
|
65
|
+
|
66
|
+
ap.match?('/believad').should be_false
|
67
|
+
ap.match?('/believed').should be_false
|
68
|
+
ap.match?('/believid').should be_false
|
69
|
+
ap.match?('/believod').should be_false
|
70
|
+
ap.match?('/believud').should be_false
|
71
|
+
ap.match?('/believkd').should be_true
|
72
|
+
ap.match?('/believzd').should be_true
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should match with {}" do
|
76
|
+
ap = OSC::AddressPattern.new( "/{hi,bye}" )
|
77
|
+
|
78
|
+
ap.match?('/hi').should be_true
|
79
|
+
ap.match?('/bye').should be_true
|
80
|
+
ap.match?('/greetings').should be_false
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# some test data for when i unit test this
|
2
|
+
# @simple_packet = "/hi\000,\000\000\000"
|
3
|
+
# @simple_with_integer_arg = "/hi\000,i\000\000\000\000\000*"
|
4
|
+
# @simple_with_two_integer_args = "/hi\000,ii\000\000\000\000*\000\000\000!"
|
5
|
+
# @simple_with_float_arg = "/hi\000,f\000\000B(\n="
|
6
|
+
# @simple_with_two_float_args = "/hi\000,ff\000B(\n=B\004\n="
|
7
|
+
# @simple_with_string_arg = "/hi\000,s\000\000greetings\000\000\000"
|
8
|
+
# @simple_with_two_string_args = "/hi\000,ss\000greetings\000\000\000how are you?\000\000\000\000"
|
9
|
+
# @simple_with_int_float_string = "/hi\000,ifs\000\000\000\000\000\000\000*B\004\n=greetings\000\000\000"
|
10
|
+
|
11
|
+
require File.join( File.dirname(__FILE__) , '..', 'spec_helper' )
|
12
|
+
|
13
|
+
|
14
|
+
describe OSC::Message do
|
15
|
+
before :each do
|
16
|
+
@builder = MessageBuilder.new
|
17
|
+
@builder.with_int( 42 ).
|
18
|
+
with_int( 33 )
|
19
|
+
|
20
|
+
@message = @builder.build
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should have no arguments if you define none" do
|
24
|
+
m = OSC::Message.new( "/hi" )
|
25
|
+
m.to_a.should == []
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should have accept int arguments" do
|
29
|
+
m = OSC::Message.new( "/hi", "i", 42 )
|
30
|
+
m.to_a.should == [42]
|
31
|
+
m.tags.should == "i"
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require File.join( File.dirname(__FILE__) , '..', 'spec_helper' )
|
2
|
+
|
3
|
+
|
4
|
+
describe OSC::NetworkPacket do
|
5
|
+
before :each do
|
6
|
+
@empty = OSC::NetworkPacket.new( "" )
|
7
|
+
@simple = OSC::NetworkPacket.new( "abc" )
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should know if it's at the end of the stream" do
|
11
|
+
@empty.eof?.should be_true
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should know the remainder in the stream" do
|
15
|
+
@simple.rem.should == 3
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should be able to skip positions" do
|
19
|
+
@simple.skip( 1 )
|
20
|
+
@simple.rem.should == 2
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should be able to get a character from the stream" do
|
24
|
+
@simple.getc.should == ?a
|
25
|
+
@simple.getc.should == ?b
|
26
|
+
@simple.getc.should == ?c
|
27
|
+
@simple.eof?.should be_true
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should be able to get a number of characters from the stream" do
|
31
|
+
@simple.getn(3).should == "abc"
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require File.join( File.dirname(__FILE__) , '..', 'spec_helper' )
|
2
|
+
|
3
|
+
|
4
|
+
describe OSC::OSCPacket do
|
5
|
+
before :each do
|
6
|
+
@complex_packet = "#bundle\000\316\034\315T\000\003\030\370\000\000\000$/tuio/2Dobj\000,ss\000source\000\000simulator\000\000\000\000\000\000\030/tuio/2Dobj\000,s\000\000alive\000\000\000\000\000\000\034/tuio/2Dobj\000,si\000fseq\000\000\000\000\377\377\377\377"
|
7
|
+
|
8
|
+
@messages = OSC::OSCPacket.messages_from_network( @complex_packet )
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should have three messages" do
|
12
|
+
@messages.should have(3).items
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should have the propper address for the messages" do
|
16
|
+
3.times do |i|
|
17
|
+
@messages[i].address.should eql("/tuio/2Dobj")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should have a first message with two strings" do
|
22
|
+
args = @messages[0].to_a
|
23
|
+
|
24
|
+
args[0].should eql( "source" )
|
25
|
+
args[1].should eql( "simulator" )
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should have a second message with one string" do
|
29
|
+
args = @messages[1].to_a
|
30
|
+
args[0].should eql( "alive" )
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should have a third message with a string and an int" do
|
34
|
+
args = @messages[2].to_a
|
35
|
+
|
36
|
+
args[0].should eql( "fseq" )
|
37
|
+
args[1].should eql(-1)
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require File.join( File.dirname(__FILE__) , '..', 'spec_helper' )
|
2
|
+
|
3
|
+
|
4
|
+
describe OSC::OSCPacket do
|
5
|
+
before :each do
|
6
|
+
@address = "/hi"
|
7
|
+
@first_int = 42
|
8
|
+
@second_int = 33
|
9
|
+
|
10
|
+
@first_float = 42.01
|
11
|
+
@second_float = 33.01
|
12
|
+
|
13
|
+
@first_string = "greetings"
|
14
|
+
@second_string = "how are you?"
|
15
|
+
|
16
|
+
@first_blob = "this is a fake blob"
|
17
|
+
@second_blob = "tis another fake blob"
|
18
|
+
|
19
|
+
@builder = MessageBuilder.new
|
20
|
+
@builder.with_address( @address )
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should decode the address of a simple message from the network data" do
|
24
|
+
sent_msg = @builder.build
|
25
|
+
|
26
|
+
msg = OSC::OSCPacket.messages_from_network( sent_msg.encode )
|
27
|
+
|
28
|
+
msg.first.address.should == @address
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should decode the int arg of a simple message from the network data" do
|
32
|
+
sent_msg = @builder.with_int( @first_int ).build
|
33
|
+
|
34
|
+
msg = OSC::OSCPacket.messages_from_network( sent_msg.encode )
|
35
|
+
|
36
|
+
msg.first.to_a.should == [@first_int]
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should decode two int args" do
|
40
|
+
sent_msg = @builder.with_int( @first_int ).with_int( @second_int ).build
|
41
|
+
|
42
|
+
msg = OSC::OSCPacket.messages_from_network( sent_msg.encode )
|
43
|
+
|
44
|
+
msg.first.to_a.should == [@first_int, @second_int]
|
45
|
+
end
|
46
|
+
|
47
|
+
it "shold decode address with float arg" do
|
48
|
+
sent_msg = @builder.with_float( @first_float ).build
|
49
|
+
|
50
|
+
msg = OSC::OSCPacket.messages_from_network( sent_msg.encode )
|
51
|
+
|
52
|
+
msg.first.to_a[0].should be_close( @first_float, 0.001 )
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
it "shold decode address with two float args" do
|
57
|
+
sent_msg = @builder.with_float( @first_float ).with_float( @second_float).build
|
58
|
+
|
59
|
+
msg = OSC::OSCPacket.messages_from_network( sent_msg.encode )
|
60
|
+
|
61
|
+
args = msg.first.to_a
|
62
|
+
args.first.should be_close( @first_float, 0.001 )
|
63
|
+
args[1].should be_close( @second_float, 0.0001 )
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should decode address with string arg" do
|
67
|
+
sent_msg = @builder.with_string( @first_string ).build
|
68
|
+
|
69
|
+
msg = OSC::OSCPacket.messages_from_network( sent_msg.encode )
|
70
|
+
|
71
|
+
msg.first.to_a.should == [@first_string]
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should decode address with multiple string args" do
|
75
|
+
sent_msg = @builder.with_string( @first_string ).with_string( @second_string).build
|
76
|
+
msg = OSC::OSCPacket.messages_from_network( sent_msg.encode )
|
77
|
+
|
78
|
+
args = msg.first.to_a
|
79
|
+
args[0].should == @first_string
|
80
|
+
args[1].should == @second_string
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
it "should decode messages with three different types of args" do
|
85
|
+
sent_msg = @builder.with_int( @first_int ).
|
86
|
+
with_float( @second_float ).
|
87
|
+
with_string( @first_string ).
|
88
|
+
build
|
89
|
+
|
90
|
+
msg = OSC::OSCPacket.messages_from_network( sent_msg.encode )
|
91
|
+
|
92
|
+
args = msg.first.to_a
|
93
|
+
|
94
|
+
args[0].should eql( @first_int )
|
95
|
+
args[1].should be_close( @second_float, 0.0001 )
|
96
|
+
args[2].should eql( @first_string )
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should decode messages with blobs" do
|
100
|
+
sent_msg = @builder.with_blob( @first_blob ).build
|
101
|
+
|
102
|
+
|
103
|
+
msg = OSC::OSCPacket.messages_from_network( sent_msg.encode )
|
104
|
+
|
105
|
+
args = msg.first.to_a
|
106
|
+
args.first.should eql( @first_blob )
|
107
|
+
end
|
108
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: aberant-osc-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- aberant
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-08-29 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -20,28 +20,41 @@ executables: []
|
|
20
20
|
extensions: []
|
21
21
|
|
22
22
|
extra_rdoc_files:
|
23
|
-
- README
|
23
|
+
- README.rdoc
|
24
24
|
files:
|
25
|
+
- README.rdoc
|
25
26
|
- VERSION.yml
|
26
27
|
- lib/osc-ruby
|
28
|
+
- lib/osc-ruby/address_pattern.rb
|
27
29
|
- lib/osc-ruby/bundle.rb
|
30
|
+
- lib/osc-ruby/client.rb
|
31
|
+
- lib/osc-ruby/core_ext
|
32
|
+
- lib/osc-ruby/core_ext/numeric.rb
|
33
|
+
- lib/osc-ruby/core_ext/object.rb
|
34
|
+
- lib/osc-ruby/core_ext/time.rb
|
28
35
|
- lib/osc-ruby/message.rb
|
36
|
+
- lib/osc-ruby/network_packet.rb
|
29
37
|
- lib/osc-ruby/osc_argument.rb
|
38
|
+
- lib/osc-ruby/osc_packet.rb
|
30
39
|
- lib/osc-ruby/osc_types.rb
|
31
40
|
- lib/osc-ruby/packet.rb
|
32
|
-
- lib/osc-ruby/
|
33
|
-
- lib/osc-ruby/simple_server.rb
|
41
|
+
- lib/osc-ruby/server.rb
|
34
42
|
- lib/osc-ruby.rb
|
35
|
-
- spec/
|
36
|
-
- spec/
|
43
|
+
- spec/builders
|
44
|
+
- spec/builders/message_builder.rb
|
37
45
|
- spec/spec_helper.rb
|
38
46
|
- spec/unit
|
47
|
+
- spec/unit/address_pattern_spec.rb
|
48
|
+
- spec/unit/message_bundle_spec.rb
|
49
|
+
- spec/unit/message_spec.rb
|
50
|
+
- spec/unit/network_packet_spec.rb
|
39
51
|
- spec/unit/osc_argument_spec.rb
|
52
|
+
- spec/unit/osc_complex_packets_spec.rb
|
53
|
+
- spec/unit/osc_simple_packets_spec.rb
|
40
54
|
- spec/unit/osc_types_spec.rb
|
41
|
-
- spec/unit/simple_client_spec.rb
|
42
|
-
- README
|
43
55
|
has_rdoc: true
|
44
56
|
homepage: http://github.com/aberant/osc-ruby
|
57
|
+
licenses:
|
45
58
|
post_install_message:
|
46
59
|
rdoc_options:
|
47
60
|
- --inline-source
|
@@ -63,7 +76,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
63
76
|
requirements: []
|
64
77
|
|
65
78
|
rubyforge_project:
|
66
|
-
rubygems_version: 1.
|
79
|
+
rubygems_version: 1.3.5
|
67
80
|
signing_key:
|
68
81
|
specification_version: 2
|
69
82
|
summary: inital gem
|
data/README
DELETED
@@ -1,93 +0,0 @@
|
|
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
|
@@ -1,25 +0,0 @@
|
|
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
|
@@ -1,11 +0,0 @@
|
|
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
|