maca-rosc 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,131 @@
1
+ require 'set'
2
+ module OSC
3
+ class Pattern < String
4
+ # Create an OSC pattern from a string or (experimental) from a Regex.
5
+ def initialize(s)
6
+ case s
7
+ when Regexp # This is experimental
8
+ s = Regexp.source s
9
+ s.gsub! /(\\\\)*\[^\/\]\*/, "\1*"
10
+ s.gsub! /(\\\\)*\[^\/\]/, "\1?"
11
+ s.gsub! /(\\\\)*\[^/, "\1[!"
12
+ s.gsub! /(\\\\)*\(/, "\1{"
13
+ s.gsub! /(\\\\)*\|/, "\1,"
14
+ s.gsub! /(\\\\)*\)/, "\1}"
15
+ s.gsub! /\\\\/, "\\"
16
+ end
17
+ super s
18
+ end
19
+
20
+ # Return a Regex representing this pattern
21
+ def regexp
22
+ s = Regexp.escape self
23
+ s.gsub! /\\\?/, '[^/]'
24
+ s.gsub! /\\\*/, '[^/]*'
25
+ s.gsub! /\\\[!/, '[^'
26
+ s.gsub! /\\\]/, ']'
27
+ s.gsub! /\\\{/, '('
28
+ s.gsub! /,/, '|'
29
+ s.gsub! /\\\}/, ')'
30
+ Regexp.new s
31
+ end
32
+
33
+ # Do these two patterns intersect?
34
+ #--
35
+ # This might be improved by following the (much simpler, but related)
36
+ # algorithm here:
37
+ #
38
+ # http://groups.google.com/group/comp.theory/browse_frm/thread/f33e033269bd5ab0/c87e19081f45454c?lnk=st&q=regular+expression+intersection&rnum=1&hl=en#c87e19081f45454c
39
+ #
40
+ # That is, convert each regexp into an NFA, then generate the set of valid
41
+ # state pairs, then check if the pair of final states is included.
42
+ # That's basically what I'm doing here, but I'm not generating all the
43
+ # state pairs, I'm just doing a search. My way may be faster and/or
44
+ # smaller, or it may not. My initial feeling is that it is faster since
45
+ # we're basically doing a depth-first search and OSC patterns are going to
46
+ # tend to be fairly simple. Still it might be a fun experiment for the
47
+ # masochistic.
48
+ def self.intersect?(s1,s2)
49
+ r = /\*|\?|\[[^\]]*\]|\{[^\}]*\}|./
50
+ a = s1.to_s.scan r
51
+ b = s2.to_s.scan r
52
+ q = [[a,b]]
53
+ until q.empty?
54
+ q.uniq!
55
+ a,b = q.pop
56
+ a = a.dup
57
+ b = b.dup
58
+
59
+ return true if a.empty? and b.empty?
60
+ next if a.empty? or b.empty?
61
+
62
+ x,y = a.shift, b.shift
63
+
64
+ # branch {}
65
+ if x =~ /^\{/
66
+ x.scan /[^\{\},]+/ do |x|
67
+ q.push [x.scan(/./)+a,[y]+b]
68
+ end
69
+ next
70
+ end
71
+ if y =~ /^\{/
72
+ y.scan /[^\{\},]+/ do |y|
73
+ q.push [[x]+a,y.scan(/./)+b]
74
+ end
75
+ next
76
+ end
77
+
78
+ # sort
79
+ if y =~ /^\[/
80
+ x,y = y,x
81
+ a,b = b,a
82
+ end
83
+ if y =~ /^(\*|\?)/
84
+ x,y = y,x
85
+ a,b = b,a
86
+ end
87
+
88
+ # match
89
+ case x
90
+ when '*'
91
+ unless y == '/'
92
+ q.push [a,b]
93
+ q.push [[x]+a,b]
94
+ end
95
+ if y == '*'
96
+ q.push [a,[y]+b]
97
+ q.push [[x]+a,b]
98
+ end
99
+ when '?'
100
+ q.push [a,b] unless y == '/'
101
+ q.push [a,[y]+b] if y == '*'
102
+ when /^\[/
103
+ xinv = (x[1] == ?!)
104
+ yinv = (y =~ /^\[!/)
105
+ x = x[(xinv ? 2 : 1)..-2].scan(/./).to_set
106
+ if y =~ /^\[/
107
+ y = y[(yinv ? 2 : 1)..-2].scan(/./).to_set
108
+ else
109
+ y = [y].to_set
110
+ end
111
+
112
+ # simplifying assumption: nobody in their right mind is going to do
113
+ # [^everyprintablecharacter]
114
+ if xinv and yinv
115
+ q.push [a,b]
116
+ elsif xinv and not yinv
117
+ q.push [a,b] unless (y-x).empty?
118
+ elsif not xinv and yinv
119
+ q.push [a,b] unless (x-y).empty?
120
+ else
121
+ q.push [a,b] unless (x&y).empty?
122
+ end
123
+ else
124
+ q.push [a,b] if x == y
125
+ end
126
+ end
127
+
128
+ false # no intersection
129
+ end
130
+ end
131
+ end
data/lib/osc/server.rb ADDED
@@ -0,0 +1,94 @@
1
+ module OSC
2
+ # Mixin for making servers.
3
+ # Your job is to read a packet and call dispatch(Packet.decode(raw)), ad
4
+ # infinitum (e.g. in a method named serve).
5
+ module Server
6
+ # prock.respond_to?(:call) #=> true
7
+ # Pass an OSC pattern, a typespec, and either prock or a block.
8
+ # The block/prock will be called if the pattern and typspec match. Numeric
9
+ # types will be coerced, so e.g. 'fi' would match 'ii' and the float would
10
+ # be coerced to an int.
11
+ def add_method(pat, typespec, prock=nil, &block)
12
+ pat = Pattern.new(pat) unless Pattern === pat
13
+ if block_given? and prock
14
+ raise ArgumentError, 'Specify either a block or a Proc, not both.'
15
+ end
16
+ prock = block if block_given?
17
+ unless prock.respond_to?(:call)
18
+ raise ArgumentError, "Prock doesn't respond to :call"
19
+ end
20
+ unless typespec.nil? or typespec =~ /[ifsb]*/
21
+ raise ArgumentError, "Bad typespec '#{typespec}'"
22
+ end
23
+ @cb ||= []
24
+ @cb << [pat, typespec, prock]
25
+ end
26
+
27
+ # dispatch the provided message. It can be raw or already decoded with
28
+ # Packet.decode
29
+ def dispatch(mesg)
30
+ case mesg
31
+ when Bundle, Message
32
+ else
33
+ mesg = Packet.decode(mesg)
34
+ end
35
+
36
+ case mesg
37
+ when Bundle; dispatch_bundle(mesg)
38
+ when Message
39
+ unless @cb.nil?
40
+ @cb.each do |pat, typespec, obj|
41
+ if pat.nil? or Pattern.intersect?(pat, mesg.address)
42
+ if typespec
43
+ if typespec.size == mesg.args.size
44
+ match = true
45
+ typespec.size.times do |i|
46
+ c = typespec[i]
47
+ case c
48
+ when ?i, ?f
49
+ match &&= (Numeric === mesg.args[i])
50
+ when ?s, ?b
51
+ match &&= (String === mesg.args[i])
52
+ end
53
+ end
54
+ if match
55
+ typespec.size.times do |i|
56
+ case typespec[i]
57
+ when ?i
58
+ mesg.args[i] = mesg.args[i].to_i
59
+ when ?f
60
+ mesg.args[i] = mesg.args[i].to_f
61
+ when ?s,?b
62
+ mesg.args[i] = mesg.args[i].to_s
63
+ mesg.args[i] = mesg.args[i].to_s
64
+ end
65
+ end
66
+
67
+ obj.call(mesg)
68
+ end
69
+ end
70
+ else # no typespec
71
+ obj.call(mesg)
72
+ end
73
+ end # pattern match
74
+ end # @cb.each
75
+ end # unless @cb.nil?
76
+ else
77
+ raise "bad mesg"
78
+ end
79
+ end
80
+
81
+ # May create a new thread to wait to dispatch according to p.timetag.
82
+ def dispatch_bundle(p)
83
+ diff = p.timetag.to_f - TimeTag.now.to_f
84
+ if diff <= 0
85
+ p.each {|m| m.source = p.source; dispatch m}
86
+ else
87
+ Thread.new do
88
+ sleep diff
89
+ p.each {|m| m.source = p.source; dispatch m}
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,42 @@
1
+ module OSC
2
+ # Mixin for OSC transports. You implement (or in many cases just alias)
3
+ # send_raw, recvfrom_raw, and recv_raw, which have the semantics of send,
4
+ # recvfrom, and recv in e.g. UDPSocket
5
+ module Transport
6
+ # Send a Message, Bundle, or even raw data
7
+ def send(msg, *args)
8
+ case msg
9
+ when Message,Bundle
10
+ send_raw(msg.encode, *args)
11
+ else
12
+ send_raw(msg, *args)
13
+ end
14
+ end
15
+
16
+ # Receive a Message, Bundle, or raw data and the sender. The source
17
+ # attribute of the Message or Bundle is also set to sender. e.g.
18
+ # packet, sender = udp_osc_client.recvfrom(32768)
19
+ def recvfrom(*args)
20
+ data, sender = recvfrom_raw(*args)
21
+ m = Packet.decode(data)
22
+ m.source = sender
23
+ [m, sender]
24
+ rescue
25
+ [data, sender]
26
+ end
27
+
28
+ # Receive a Message, Bundle, or raw data.
29
+ def recv(*args)
30
+ data = recv_raw(*args)
31
+ Packet.decode(data)
32
+ rescue
33
+ end
34
+
35
+ # Send a Message/Bundle with a timestamp (a Time or TimeTag object).
36
+ def send_timestamped(msg, ts, *args)
37
+ m = Bundle.new(ts, msg)
38
+ send(m, *args)
39
+ end
40
+ alias :send_ts :send_timestamped
41
+ end
42
+ end
data/lib/osc/udp.rb ADDED
@@ -0,0 +1,30 @@
1
+ require 'osc/transport'
2
+ require 'socket'
3
+
4
+ module OSC
5
+ # A ::UDPSocket with a send method that accepts a Message or Bundle or
6
+ # a raw String.
7
+ class UDPSocket < ::UDPSocket
8
+ alias :send_raw :send
9
+ alias :recvfrom_raw :recvfrom
10
+ alias :recv_raw :recv
11
+ include Transport
12
+ end
13
+
14
+ class UDPServer < OSC::UDPSocket
15
+ MAX_MSG_SIZE=32768
16
+ include Server
17
+ def serve
18
+ loop do
19
+ p, sender = recvfrom(MAX_MSG_SIZE)
20
+ dispatch p
21
+ end
22
+ end
23
+
24
+ # send msg2 as a reply to msg1
25
+ def reply(msg1, msg2)
26
+ domain, port, host, ip = msg2.source
27
+ send(msg2, 0, host, port)
28
+ end
29
+ end
30
+ end
data/lib/rosc.rb ADDED
@@ -0,0 +1,8 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require 'osc'
5
+
6
+ module Rosc
7
+ VERSION = '0.0.1'
8
+ end
data/script/console ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ # File: script/console
3
+ irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
4
+
5
+ libs = " -r irb/completion"
6
+ # Perhaps use a console_lib to store any extra methods I may want available in the cosole
7
+ # libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
8
+ libs << " -r #{File.dirname(__FILE__) + '/../lib/rosc.rb'}"
9
+ puts "Loading rosc gem"
10
+ exec "#{irb} #{libs} --simple-prompt"
data/script/destroy ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/destroy'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Destroy.new.run(ARGV)
data/script/generate ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/generate'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Generate.new.run(ARGV)
data/test/test_osc.rb ADDED
@@ -0,0 +1,160 @@
1
+ $:.unshift(File.dirname(__FILE__) + "/../lib/")
2
+ require 'osc'
3
+ require 'time'
4
+ require 'test/unit'
5
+
6
+ class TC_OSC < Test::Unit::TestCase
7
+ include OSC
8
+ # def setup
9
+ # end
10
+
11
+ # def teardown
12
+ # end
13
+
14
+ def test_datatype
15
+ s = 'foo'
16
+ i = 42
17
+ f = 3.14
18
+
19
+ assert_equal 'i', Packet.tag(i)
20
+ assert_equal 'f', Packet.tag(f)
21
+ assert_equal 's', Packet.tag(s)
22
+ assert_equal s+"\000", Packet.encode(s)
23
+ b = Blob.new("foobardoobar\0\0x200")
24
+ assert_equal 'b', Packet.tag(b)
25
+ assert_equal b.size+4 + (b.size+4)%4, Packet.encode(b).size
26
+ end
27
+
28
+ def test_timetag
29
+ t1 = TimeTag::JAN_1970
30
+ t2 = Time.now
31
+ t3 = t2.to_f+t1
32
+
33
+ tt = TimeTag.new t2
34
+ assert_equal t3, tt.to_f
35
+ assert_equal t3.floor, tt.to_i
36
+ assert_equal t3.floor - t3, tt.to_i - tt.to_f
37
+ assert_equal [0,1].pack('NN'), Packet.encode(TimeTag.new(nil))
38
+ assert_equal t2.to_i,tt.to_time.to_i # to_f has roundoff error at the lsb
39
+ end
40
+
41
+ def test_message
42
+ a = 'foo'
43
+ b = 'quux'
44
+ m = Message.new '/foobar', 'ssi', a, b, 1
45
+ assert_equal "/foobar\000"+",ssi\000\000\000\000"+
46
+ "foo\000"+"quux\000\000\000\000"+"\000\000\000\001", Packet.encode(m)
47
+ end
48
+
49
+ def test_bundle
50
+ m1 = Message.new '/foo','s','foo'
51
+ m2 = Message.new '/bar','s','bar'
52
+ t = Time.now
53
+ b = Bundle.new(TimeTag.new(Time.at(t + 10)), m1, m2)
54
+ b2 = Bundle.new(nil, b, m1)
55
+
56
+ assert_equal 10, b.timetag.to_time.to_i - t.to_i
57
+ e = Packet.encode(b2)
58
+ assert_equal '#bundle', e[0,7]
59
+ assert_equal "\000\000\000\000\000\000\000\001", e[8,8]
60
+ assert_equal '#bundle', e[16+4,7]
61
+ assert_equal '/foo', e[16+4+Packet.encode(b).size+4,4]
62
+ assert_equal 0, e.size % 4
63
+
64
+ assert_instance_of Array, b2.to_a
65
+ assert_instance_of Bundle, b2.to_a[0]
66
+ assert_instance_of Message, b2.to_a[1]
67
+
68
+ bundle = Packet.decode(e)
69
+ assert_instance_of Bundle, bundle
70
+
71
+
72
+ end
73
+
74
+
75
+
76
+ def test_packet
77
+ m = Message.new '/foo','s','foo'
78
+ b = Bundle.new nil,m
79
+
80
+ m2 = Packet.decode("/foo\000\000\000\000,s\000\000foo\000")
81
+ assert_equal m.address,m2.address
82
+ m2 = Packet.decode(Packet.encode(m))
83
+ assert_equal m.address,m2.address
84
+ assert_equal m.typetag,m2.typetag
85
+ assert_equal m.args.size,m2.args.size
86
+ b2 = Packet.decode(Packet.encode(b))
87
+ assert_equal b.args.size,b2.args.size
88
+ end
89
+
90
+ class TestServer
91
+ include OSC::Transport
92
+ include OSC::Server
93
+ def test_request(p)
94
+ send p
95
+ end
96
+
97
+ def send_raw(msg, *args)
98
+ dispatch msg
99
+ end
100
+
101
+ end
102
+
103
+ def test_server
104
+ s = TestServer.new
105
+ s.add_method('/foo/bar',nil) { |msg|
106
+ assert_equal 'si',msg.typetag
107
+ assert_equal 'Hello, World!',msg[0]
108
+ assert_equal 42,msg[1]
109
+ }
110
+ s.test_request Message.new("/foo/bar",'si','Hello, World!',42)
111
+ end
112
+
113
+ def test_server_with_bundle
114
+ s = TestServer.new
115
+ s.add_method('/foo/bar',nil) { |msg|
116
+ assert_equal 'si',msg.typetag
117
+ assert_equal 'Hello, World!',msg[0]
118
+ assert_equal 42,msg[1]
119
+ }
120
+ s.test_request Bundle.new(nil, Message.new("/foo/bar",'si','Hello, World!',42), Message.new("/foo/bar",'si','Hello, World!',42), Message.new("/foo/bar",'si','Hello, World!',42))
121
+ end
122
+
123
+ def test_pattern
124
+ # test *
125
+ assert Pattern.intersect?('/*/bar/baz','/foo/*/baz')
126
+ assert Pattern.intersect?('/f*','/*o')
127
+ assert ! Pattern.intersect?('/f*','/foo/bar')
128
+ assert ! Pattern.intersect?('/f*','/bar')
129
+ # test ?
130
+ assert Pattern.intersect?('/fo?/bar','/foo/?ar')
131
+ assert ! Pattern.intersect?('/foo?','/foo')
132
+ # test []
133
+ assert Pattern.intersect?('/foo/ba[rz]','/foo/bar')
134
+ assert Pattern.intersect?('/[!abcde]/a','/[!abcde]/a')
135
+ assert Pattern.intersect?('/[!abcde]/a','/f/a')
136
+ assert Pattern.intersect?('/[!abcde]/a','/[abf]/a')
137
+ assert ! Pattern.intersect?('/[ab]/a','/[!abc]/a')
138
+ assert ! Pattern.intersect?('/[abcde]','/[!abcde]')
139
+ assert ! Pattern.intersect?('/[abcde]','/f')
140
+ assert ! Pattern.intersect?('/[!abcde]','/a')
141
+ # test {}
142
+ assert Pattern.intersect?('/{foo,bar,baz}','/foo')
143
+ assert Pattern.intersect?('/{foo,bar,baz}','/bar')
144
+ assert Pattern.intersect?('/{foo,bar,baz}','/baz')
145
+ assert ! Pattern.intersect?('/{foo,bar,baz}','/quux')
146
+ assert ! Pattern.intersect?('/{foo,bar,baz}','/fo')
147
+ # * with *,?,[]
148
+ assert Pattern.intersect?('/*/bar','/*/ba?')
149
+ assert Pattern.intersect?('/*/bar','/*x/ba?')
150
+ assert Pattern.intersect?('/*/bar','/?/ba?')
151
+ assert Pattern.intersect?('/*/bar','/?x/ba?')
152
+ assert Pattern.intersect?('/*/bar','/[abcde]/ba?')
153
+ assert Pattern.intersect?('/*/bar','/[abcde]x/ba?')
154
+ assert Pattern.intersect?('/*/bar','/[!abcde]/ba?')
155
+ assert Pattern.intersect?('/*/bar','/[!abcde]x/ba?')
156
+ # ? with []
157
+ assert Pattern.intersect?('/?','/[abcde]')
158
+ assert Pattern.intersect?('/?','/[!abcde]')
159
+ end
160
+ end