osc-ruby 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,18 @@
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
+
13
+ private
14
+ def padding(s)
15
+ s + ("\000" * ((4 - (s.size % 4)) % 4))
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,118 @@
1
+ require File.join( File.dirname( __FILE__ ), 'network_packet')
2
+ require 'ostruct'
3
+
4
+ module OSC
5
+ class OSCPacket
6
+
7
+ def self.messages_from_network( string )
8
+ messages = []
9
+ osc = new( string )
10
+
11
+ if osc.bundle?
12
+ bundle = osc.get_string
13
+ time = osc.get_timestamp
14
+
15
+ osc.get_bundle_messages.each do | message |
16
+ messages << decode_simple_message( time, OSCPacket.new( message ) )
17
+ end
18
+
19
+ else
20
+ messages << decode_simple_message( time, osc )
21
+ end
22
+
23
+ return messages
24
+ end
25
+
26
+ def self.decode_simple_message( time, osc_packet )
27
+ address = osc_packet.get_string
28
+ args = osc_packet.get_arguments
29
+
30
+ Message.new_with_time(address, time, nil, *args )
31
+ end
32
+
33
+ def initialize( string )
34
+ @packet = NetworkPacket.new( string )
35
+
36
+ @types = { "i" => lambda{ OSCInt32.new( get_int32 ) },
37
+ "f" => lambda{ OSCFloat32.new( get_float32 ) },
38
+ "s" => lambda{ OSCString.new( get_string ) },
39
+ "b" => lambda{ OSCBlob.new( get_blob )}
40
+ }
41
+ end
42
+
43
+ def get_bundle_messages
44
+ bundle_messages = []
45
+
46
+ until @packet.eof?
47
+ l = @packet.getn(4).unpack('N')[0]
48
+ bundle_messages << @packet.getn(l)
49
+ end
50
+ bundle_messages
51
+ end
52
+
53
+ def get_string
54
+ result = ''
55
+ until (c = @packet.getc) == string_delemeter
56
+ result << c
57
+ end
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
+
113
+ def string_delemeter
114
+ # ruby 1.9 has multicharacter support
115
+ RUBY_VERSION.include?( '1.9' ) ? "\x00" : 0
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,31 @@
1
+ require File.join( File.dirname( __FILE__ ), "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 - why 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,134 @@
1
+ module OSC
2
+ class Packet
3
+
4
+ class PO
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
24
+
25
+ def getn(n)
26
+ raise EOFError if rem < n
27
+ s = @str[@index, n]
28
+ skip(n)
29
+ s
30
+ end
31
+
32
+ def getc
33
+ raise EOFError if rem < 1
34
+ c = @str[@index]
35
+ skip(1)
36
+ c
37
+ end
38
+ end
39
+
40
+ def self.decode_int32(io)
41
+ i = io.getn(4).unpack('N')[0]
42
+ i -= 2**32 if i > (2**31-1)
43
+ i
44
+ end
45
+
46
+ def self.decode_float32(io)
47
+ f = io.getn(4).unpack('g')[0]
48
+ f
49
+ end
50
+
51
+ def self.decode_string(io)
52
+ s = ''
53
+ until (c = io.getc) == 0
54
+ s << c
55
+ end
56
+ io.skip_padding
57
+ s
58
+ end
59
+
60
+ def self.decode_blob(io)
61
+ l = io.getn(4).unpack('N')[0]
62
+ b = io.getn(l)
63
+ io.skip_padding
64
+ b
65
+ end
66
+
67
+ def self.decode_timetag(io)
68
+ t1 = io.getn(4).unpack('N')[0]
69
+ t2 = io.getn(4).unpack('N')[0]
70
+ [t1, t2]
71
+ end
72
+
73
+ def self.decode2(time, packet, list)
74
+ io = PO.new(packet)
75
+ id = decode_string(io)
76
+ if id =~ /\A\#/
77
+ if id == '#bundle'
78
+ t1, t2 = decode_timetag(io)
79
+ if t1 == 0 && t2 == 1
80
+ time = nil
81
+ else
82
+ time = t1 + t2.to_f / (2**32)
83
+ end
84
+ until io.eof?
85
+ l = io.getn(4).unpack('N')[0]
86
+ s = io.getn(l)
87
+ decode2(time, s, list)
88
+ end
89
+ end
90
+ elsif id =~ /\//
91
+ address = id
92
+ if io.getc == ?,
93
+ tags = decode_string(io)
94
+ args = []
95
+ tags.scan(/./) do |t|
96
+ case t
97
+ when 'i'
98
+ i = decode_int32(io)
99
+ args << OSCInt32.new(i)
100
+ when 'f'
101
+ f = decode_float32(io)
102
+ args << OSCFloat32.new(f)
103
+ when 's'
104
+ s = decode_string(io)
105
+ args << OSCString.new(s)
106
+ when 'b'
107
+ b = decode_blob(io)
108
+ args << OSCBlob.new(b)
109
+ when /[htd]/; io.read(8)
110
+ when 'S'; decode_string(io)
111
+ when /[crm]/; io.read(4)
112
+ when /[TFNI\[\]]/;
113
+ end
114
+ end
115
+ list << [time, Message.new(address, nil, *args)]
116
+ end
117
+ end
118
+ end
119
+
120
+ private_class_method :decode_int32,
121
+ :decode_float32,
122
+ :decode_string,
123
+ :decode_blob,
124
+ :decode_timetag,
125
+ :decode2
126
+
127
+ def self.decode(packet)
128
+ list = []
129
+ decode2(nil, packet, list)
130
+ list
131
+ end
132
+
133
+ end
134
+ end
@@ -0,0 +1,91 @@
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
+ dispatch_message( mesg )
59
+ end
60
+ end
61
+
62
+ def detector
63
+ loop do
64
+ pa, network = @socket.recvfrom(16384)
65
+ begin
66
+
67
+ OSCPacket.messages_from_network(pa).each do |x|
68
+ @queue.push(x)
69
+ end
70
+
71
+ rescue EOFError
72
+ end
73
+ end
74
+ end
75
+
76
+ def dispatch_message( message )
77
+ diff = ( message.time || 0 ) - Time.now.to_ntp
78
+
79
+ if diff <= 0
80
+ sendmesg( message)
81
+ else # spawn a thread to wait until it's time
82
+ Thread.fork do
83
+ sleep(diff)
84
+ sendmesg(mesg)
85
+ Thread.exit
86
+ end
87
+ end
88
+ end
89
+
90
+ end
91
+ 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 , *@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