ruby-osc 0.31.0 → 1.0.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.
@@ -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