ruby-osc 0.31.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -13,6 +13,8 @@ Concise OSC implementation for Ruby
13
13
  require 'rubygems'
14
14
  require 'ruby-osc'
15
15
 
16
+ include OSC
17
+
16
18
  OSC.run do
17
19
  server = Server.new 9090
18
20
  client = Client.new 9090
data/Rakefile CHANGED
@@ -1,7 +1,7 @@
1
- require 'bundler/gem_tasks'
2
- require "rspec/core/rake_task"
3
-
4
- desc "Run specs"
5
- RSpec::Core::RakeTask.new(:spec)
6
-
7
- task :default => :spec
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ desc "Run specs"
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task default: :spec
@@ -1,5 +1,5 @@
1
- $:.unshift( File.join( File.dirname( __FILE__), '..', 'lib' ) )
2
- require 'ruby-osc'
1
+ require "rubygems"
2
+ require "ruby-osc"
3
3
 
4
4
  include OSC
5
5
 
@@ -7,7 +7,7 @@ OSC.run do
7
7
  server = Server.new 9090
8
8
  client = Client.new 9090
9
9
 
10
- server.add_pattern /.*/ do |*args| # this will match any address
10
+ server.add_pattern(/.*/) do |*args| # this will match any address
11
11
  p "/.*/: #{ args.join(', ') }"
12
12
  end
13
13
 
@@ -19,11 +19,12 @@ OSC.run do
19
19
  p "'/foo/bar': #{ args.join(', ') }"
20
20
  end
21
21
 
22
- server.add_pattern "/exit" do |*args| # this will just match /exit address
22
+ server.add_pattern "/exit" do |*_args| # this will just match /exit address
23
23
  exit
24
24
  end
25
25
 
26
- client.send Message.new('/foo/bar', 1, 1.2, 'a string')
27
- client.send Message.new('/foo/bar/zar', 1, 1.2, 'a string')
28
- client.send Bundle.new(Time.now + 2, Message.new('/exit'))
26
+
27
+ client.send Message.new("/foo/bar", 1, 1.2, "a string")
28
+ client.send Message.new("/foo/bar/zar", 1, 1.2, "a string")
29
+ client.send Bundle.new(Time.now + 2, Message.new("/exit"))
29
30
  end
@@ -1,61 +1,65 @@
1
- require 'rubygems'
2
- require 'eventmachine'
3
- require 'socket' # Strange side effects with eventmachine udp client and SuperCollider
4
- require 'strscan'
5
- require 'thread'
6
-
7
- $:.unshift( File.join( File.dirname( __FILE__), '..', 'lib' ) )
8
- require 'ruby-osc/message'
9
- require 'ruby-osc/bundle'
10
- require 'ruby-osc/server'
11
- require 'ruby-osc/client'
1
+ require "rubygems"
2
+ require "eventmachine"
3
+ require "socket" # Strange side effects with eventmachine udp client and SuperCollider
4
+ require "strscan"
5
+ require "thread"
6
+
7
+ $LOAD_PATH.unshift( File.join( File.dirname( __FILE__), "..", "lib" ) )
8
+
9
+ # encoding: UTF-8
10
+ require "ruby-osc/message"
11
+ require "ruby-osc/bundle"
12
+ require "ruby-osc/server"
13
+ require "ruby-osc/client"
12
14
  require "ruby-osc/version"
13
15
 
14
16
  module OSC
15
17
  class DecodeError < StandardError; end
16
-
18
+
17
19
  class Blob < String; end
18
-
20
+
19
21
  module OSCArgument
20
22
  def to_osc_type
21
23
  raise NotImplementedError, "#to_osc_type method should be implemented for #{ self.class }"
22
24
  end
23
25
  end
24
-
25
- def self.coerce_argument arg
26
+
27
+ def self.coerce_argument(arg)
26
28
  case arg
27
29
  when OSCArgument then arg.to_osc_type
28
30
  when Symbol then arg.to_s
29
- when String, Float, Fixnum, Blob, String then arg # Pure osc 1.0 specification
30
- else raise(TypeError, "#{ arg.inspect } is not a valid Message argument") end
31
+ when String, Float, Integer, Blob, String then arg # Osc 1.0 spec
32
+ else
33
+ raise(TypeError, "#{ arg.inspect } is not a valid Message argument")
34
+ end
31
35
  end
32
-
33
- def self.decode str #:nodoc:
36
+
37
+ def self.decode(str) #:nodoc:
34
38
  str.match(/^#bundle/) ? Bundle.decode(str) : Message.decode(str)
35
39
  end
36
-
37
- def self.padding_size size
38
- (4 - (size) % 4) % 4
40
+
41
+ def self.padding_size(size)
42
+ (4 - (size) % 4) % 4
39
43
  end
40
44
 
41
45
  def self.run
42
46
  EM.run do
43
47
  EM.error_handler { |e| puts e }
44
- EM.set_quantum 5
48
+ EM.set_quantum 5
45
49
  yield
46
50
  end
47
51
  end
48
-
49
- def self.encoding_directive obj #:nodoc:
52
+
53
+ def self.encoding_directive(obj) #:nodoc:
50
54
  case obj
51
- when Float then [obj, 'f', 'g']
52
- when Fixnum then [obj, 'i', 'N']
53
- when Blob then [[obj.size, obj], 'b', "Na*x#{ padding_size obj.size + 4 }"]
54
- when String then [obj, 's', "Z*x#{ padding_size obj.size + 1 }"]
55
+ when Float then [obj, "f", "g"]
56
+ when Integer then [obj, "i", "N"]
57
+ when Blob then [[obj.bytesize, obj], "b", "Na*x#{ padding_size obj.bytesize + 4 }"]
58
+ when String then [obj, "s", "Z*x#{ padding_size obj.bytesize + 1 }"]
55
59
  when Time
56
- t1, fr = (obj.to_f + 2208988800).divmod(1)
60
+ t1, fr = (obj.to_f + 2_208_988_800).divmod(1)
57
61
  t2 = (fr * (2**32)).to_i
58
- [[t1, t2], 't', 'N2']
62
+ [[t1, t2], "t", "N2"]
59
63
  end
60
64
  end
61
65
  end
@@ -1,52 +1,59 @@
1
+ # encoding: UTF-8
1
2
  module OSC
2
3
  class Bundle < Array
3
4
  attr_accessor :timetag
4
5
 
5
- def initialize timetag = nil, *args
6
+ def initialize(timetag = nil, *args)
6
7
  args.each{ |arg| raise TypeError, "#{ arg.inspect } is required to be a Bundle or Message" unless Bundle === arg or Message === arg }
7
- raise TypeError, "#{ timetag.inspect } is required to be Time or nil" unless timetag == nil or Time === timetag
8
+ raise TypeError, "#{ timetag.inspect } is required to be Time or nil" unless timetag.nil? or Time === timetag
8
9
  super args
9
10
  @timetag = timetag
10
11
  end
11
12
 
12
13
  def encode
13
14
  timetag =
14
- if @timetag
15
- time, tag, dir = OSC.encoding_directive @timetag
16
- time.pack dir
17
- else "\000\000\000\000\000\000\000\001" end
18
-
15
+ if @timetag
16
+ time, _tag, dir = OSC.encoding_directive(@timetag)
17
+ time.pack dir
18
+ else
19
+ "\000\000\000\000\000\000\000\001"
20
+ end
21
+
19
22
  "#bundle\000#{ timetag }" + collect do |x|
20
23
  x = x.encode
21
- [x.size].pack('N') + x
24
+ [x.size].pack("N") + x
22
25
  end.join
23
26
  end
24
27
 
25
- def self.decode string
26
- string.sub! /^#bundle\000/, ''
27
- t1, t2, content_str = string.unpack('N2a*')
28
-
28
+ def self.decode(string)
29
+ string.sub!(/^#bundle\000/, "")
30
+ t1, t2, content_str = string.unpack("N2a*")
31
+
29
32
  timetag = t1 == 0 && t2 == 1 ? nil : Time.at(t1 + t2 / (2**32.0) - 2_208_988_800)
30
33
  scanner = StringScanner.new content_str
31
34
  args = []
32
-
35
+
33
36
  until scanner.eos?
34
- size = scanner.scan(/.{4}/).unpack('N').first
35
- arg_str = scanner.scan(/.{#{ size }}/nm) rescue raise(DecodeError, "An error occured while trying to decode bad formatted osc bundle")
36
- args << OSC.decode(arg_str)
37
+ size = scanner.scan(/.{4}/).unpack("N").first
38
+ arg_str = begin
39
+ scanner.scan(/.{#{ size }}/nm)
40
+ rescue
41
+ raise(DecodeError, "An error occured while trying to decode bad formatted osc bundle")
42
+ end
43
+ args << OSC.decode(arg_str)
37
44
  end
38
-
45
+
39
46
  new timetag, *args
40
47
  end
41
-
42
- def == other
43
- self.class == other.class and self.timetag == other.timetag and self.to_a == other.to_a
48
+
49
+ def ==(other)
50
+ self.class == other.class and timetag == other.timetag and to_a == other.to_a
44
51
  end
45
52
 
46
53
  def to_a; Array.new self; end
47
-
54
+
48
55
  def to_s
49
- "OSC::Bundle(#{ self.join(', ') })"
56
+ "OSC::Bundle(#{ join(', ') })"
50
57
  end
51
58
  end
52
59
  end
@@ -1,13 +1,17 @@
1
+ # encoding: UTF-8
2
+ require "socket" # Strange side effects with eventmachine udp client and SuperCollider
3
+
1
4
  # From the Funaba osc gem:
2
5
  module OSC
3
6
  class Client
4
-
5
- def initialize port, host = '127.0.0.1'
7
+ def initialize(port, host = "127.0.0.1")
6
8
  @socket = UDPSocket.new
9
+ @socket = UDPSocket.open
10
+ @socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_BROADCAST, true)
7
11
  @socket.connect host, port
8
12
  end
9
-
10
- def send mesg, *args
13
+
14
+ def send(mesg, *_args)
11
15
  @socket.send mesg.encode, 0
12
16
  end
13
17
  end
@@ -15,24 +19,24 @@ end
15
19
 
16
20
  # module OSC
17
21
  # class Client
18
- #
22
+ #
19
23
  # def initialize port, address = 'localhost'
20
24
  # @address, @port = address, port
21
25
  # run
22
26
  # end
23
- #
27
+ #
24
28
  # def run
25
29
  # @connection = EventMachine.open_datagram_socket 'localhost', 0, Connection
26
30
  # end
27
- #
31
+ #
28
32
  # def stop
29
33
  # @connection.close_connection if @connection
30
34
  # end
31
- #
35
+ #
32
36
  # def send item
33
- # @connection.send_datagram item.encode, @address, @port
37
+ # @connection.send_datagram item.encode, @address, @port
34
38
  # end
35
- #
39
+ #
36
40
  # class Connection < EventMachine::Connection #:nodoc:
37
41
  # end
38
42
  # end
@@ -2,7 +2,7 @@ module OSC
2
2
  class Message
3
3
  attr_accessor :address, :time, :args
4
4
 
5
- def initialize address = '', *args
5
+ def initialize(address = "", *args)
6
6
  args.collect! { |arg| OSC.coerce_argument arg }
7
7
  args.flatten! # won't harm we're not accepting arrays anyway, in case an custom coerced arg coerces to Array eg. Hash
8
8
  raise(TypeError, "Expected address to be a string") unless String === address
@@ -14,7 +14,7 @@ module OSC
14
14
  dirs ||= [] and objs ||= []
15
15
 
16
16
  [",#{ tags and tags.join }", @address].each do |str|
17
- obj, tag, dir = OSC.encoding_directive str
17
+ obj, _tag, dir = OSC.encoding_directive(str)
18
18
  objs.unshift obj
19
19
  dirs.unshift dir
20
20
  end
@@ -22,45 +22,62 @@ module OSC
22
22
  objs.flatten.compact.pack dirs.join
23
23
  end
24
24
 
25
- def == other
25
+ def ==(other)
26
26
  self.class == other.class and to_a == other.to_a
27
27
  end
28
28
 
29
29
  def to_a; @args.dup.unshift(@address) end
30
30
  def to_s; "OSC::Message(#{ args.join(', ') })" end
31
31
 
32
- def self.decode string
32
+ def self.decode(string)
33
33
  scanner = StringScanner.new string
34
- address, tags = (1..2).map do
35
- string = scanner.scan(/[^\000]+\000/)
36
- scanner.pos += OSC.padding_size(string.size)
37
- string.chomp("\000")
38
- end
34
+ msg = decode_message scanner
35
+ raise DecodeError unless scanner.eos?
36
+ msg
37
+ end
39
38
 
40
- args = []
41
- tags.scan(/\w/) do |tag|
42
- case tag
43
- when 'i'
44
- int = scanner.scan(/.{4}/nm).unpack('N').first
45
- args.push( int > (2**31-1) ? int - 2**32 : int )
46
- when 'f'
47
- args.push scanner.scan(/.{4}/nm).unpack('g').first
48
- when 's'
49
- str = scanner.scan(/[^\000]+\000/)
50
- scanner.pos += OSC.padding_size(str.size)
51
- args.push str.chomp("\000")
52
- when 'b'
53
- size = scanner.scan(/.{4}/).unpack('N').first
54
- str = scanner.scan(/.{#{ size }}/nm)
55
- scanner.pos += OSC.padding_size(size + 4)
56
- args.push Blob.new(str)
57
- else
58
- raise DecodeError, "#{ t } is not a known tag"
39
+ def self.decode_message(scanner)
40
+ pos = scanner.pos
41
+ begin
42
+ address, tags = (1..2).map do
43
+ string = scanner.scan(/[^\000]+\000/)
44
+ raise DecodeError, "no address or tags" if string.nil?
45
+ scanner.pos += OSC.padding_size(string.bytesize)
46
+ string.chomp("\000")
59
47
  end
60
- end
61
48
 
62
- new address, *args
49
+ args = []
50
+ tags.scan(/\w/) do |tag|
51
+ case tag
52
+ when "i"
53
+ int = scanner.scan(/.{4}/nm).unpack("N").first
54
+ args.push( int > (2**31 - 1) ? int - 2**32 : int )
55
+ when "f"
56
+ args.push scanner.scan(/.{4}/nm).unpack("g").first
57
+ when "d"
58
+ args.push scanner.scan(/.{8}/nm).unpack("G").first
59
+ when "s"
60
+ str = scanner.scan(/[^\000]+\000/)
61
+ scanner.pos += OSC.padding_size(str.size)
62
+ str = str.respond_to?(:force_encoding) ? str.force_encoding("UTF-8") : str
63
+ args.push str.chomp("\000")
64
+ when "b"
65
+ size = scanner.scan(/.{4}/).unpack("N").first
66
+ str = scanner.scan(/.{#{ size }}/nm)
67
+ scanner.pos += OSC.padding_size(size + 4)
68
+ args.push Blob.new(str)
69
+ else
70
+ raise DecodeError, "#{ tag } is not a known tag"
71
+ end
72
+ end
73
+ new address, *args
74
+ end
75
+ rescue DecodeError => e
76
+ scanner.pos = pos
77
+ raise e
78
+ rescue => e
79
+ scanner.pos = pos
80
+ raise DecodeError, e
63
81
  end
64
-
65
82
  end
66
83
  end
@@ -2,7 +2,7 @@ module OSC
2
2
  class Server
3
3
  attr_accessor :port, :address
4
4
 
5
- def initialize port, address = '127.0.0.1'
5
+ def initialize(port, address = "127.0.0.1")
6
6
  @port, @address = port, address
7
7
  @queue, @patterns = [], []
8
8
  @mutex = Mutex.new
@@ -20,27 +20,28 @@ module OSC
20
20
  @timer.cancel
21
21
  end
22
22
 
23
- def add_pattern pattern, &block
24
- raise ArgumentError.new("A block must be given") unless block
23
+ def add_pattern(pattern, &block)
24
+ raise ArgumentError, "A block must be given" unless block
25
25
  @patterns << [pattern, block]
26
26
  end
27
27
 
28
- def delete_pattern pattern
28
+ def delete_pattern(pattern)
29
29
  @patterns.delete pattern
30
30
  end
31
31
 
32
- def receive data
32
+ def receive(data)
33
33
  case decoded = OSC.decode(data)
34
34
  when Bundle
35
35
  decoded.timetag.nil? ? decoded.each{ |m| dispatch m } : @mutex.synchronize{@queue.push(decoded)}
36
36
  when Message
37
37
  dispatch decoded
38
38
  end
39
- rescue => e
40
- warn "Bad data received: #{ e }"
39
+ rescue => e
40
+ warn "Bad data received: #{ e }"
41
41
  end
42
42
 
43
43
  private
44
+
44
45
  def check_queue
45
46
  @timer = EventMachine::PeriodicTimer.new 0.002 do
46
47
  now = Time.now
@@ -53,19 +54,19 @@ module OSC
53
54
  end
54
55
  end
55
56
 
56
- def dispatch message
57
- @patterns.each do |pat, block|
57
+ def dispatch(message)
58
+ @patterns.each do |pat, block|
58
59
  block.call(*message.to_a) if pat === message.address
59
60
  end
60
61
  end
61
62
 
62
63
  class Connection < EventMachine::Connection #:nodoc:
63
- def initialize server
64
+ def initialize(server)
64
65
  @server = server
65
66
  end
66
67
 
67
- def receive_data data
68
- @server.receive(data)
68
+ def receive_data(data)
69
+ @server.receive(data)
69
70
  end
70
71
  end
71
72
  end