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 ADDED
@@ -0,0 +1,14 @@
1
+ An "OSC" module
2
+ ==============
3
+
4
+ OSC is an "OpenSound Control" module for an object‐oriented
5
+ scripting language Ruby.
6
+
7
+
8
+
9
+
10
+
11
+ Originally created by...
12
+ Tadayoshi Funaba
13
+ mailto:tadf@funaba.org
14
+ http://www.funaba.org/en/
@@ -0,0 +1,4 @@
1
+ ---
2
+ :minor: 1
3
+ :patch: 5
4
+ :major: 0
@@ -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,14 @@
1
+ module OSC
2
+ class SimpleClient
3
+
4
+ def initialize(host, port)
5
+ @so = UDPSocket.new
6
+ @so.connect(host, port)
7
+ end
8
+
9
+ def send(mesg)
10
+ @so.send(mesg.encode, 0)
11
+ end
12
+
13
+ end
14
+ 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
@@ -0,0 +1,11 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+ require 'rr'
4
+
5
+ $:.unshift( File.join( File.dirname( __FILE__), '..', 'lib' ) )
6
+
7
+ require 'osc-ruby'
8
+
9
+ Spec::Runner.configure do |config|
10
+ config.mock_with RR::Adapters::Rspec
11
+ end
@@ -0,0 +1,7 @@
1
+ require File.join( File.dirname(__FILE__) , '..', 'spec_helper' )
2
+
3
+ describe OSC::OSCArgument do
4
+ it "should not blow up" do
5
+ OSC::OSCArgument.new 1
6
+ end
7
+ end
@@ -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
+