osc-ruby 0.2.0

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.
@@ -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