osc-ruby 0.2.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -10,7 +10,7 @@ Compatible with ruby 1.8, 1.9, and jruby
10
10
 
11
11
  == Install
12
12
 
13
- sudo gem install aberant-osc-ruby
13
+ sudo gem install osc-ruby
14
14
 
15
15
  for the EMServer, you will need EventMachine
16
16
 
data/Rakefile CHANGED
@@ -1,6 +1,5 @@
1
1
  require 'spec/rake/spectask'
2
2
 
3
-
4
3
  task :default => :spec
5
4
 
6
5
  Spec::Rake::SpecTask.new do |t|
@@ -9,10 +8,26 @@ Spec::Rake::SpecTask.new do |t|
9
8
  t.spec_opts = ["--colour"]
10
9
  end
11
10
 
11
+ require 'rake/rdoctask'
12
+ Rake::RDocTask.new do |rdoc|
13
+ if File.exist?('VERSION')
14
+ version = File.read('VERSION')
15
+ else
16
+ version = ""
17
+ end
18
+
19
+ rdoc.rdoc_dir = 'rdoc'
20
+ rdoc.title = "osc-ruby #{version}"
21
+ rdoc.rdoc_files.include('README*')
22
+ rdoc.rdoc_files.include('lib/**/*.rb')
23
+ end
24
+
25
+
12
26
  begin
13
27
  require 'jeweler'
14
28
  Jeweler::Tasks.new do |gem|
15
29
  gem.name = "osc-ruby"
30
+ gem.description = "a ruby client for the OSC protocol"
16
31
  gem.summary = %Q{inital gem}
17
32
  gem.email = "qzzzq1@gmail.com"
18
33
  gem.homepage = "http://github.com/aberant/osc-ruby"
@@ -20,12 +35,10 @@ begin
20
35
  gem.files = FileList['Rakefile', 'examples/**/*', 'lib/**/*'].to_a
21
36
  gem.test_files = FileList['spec/**/*.rb']
22
37
  gem.rubyforge_project = "osc-ruby"
23
- # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
24
- end
25
-
26
- Jeweler::RubyforgeTasks.new do |rubyforge|
27
- rubyforge.doc_task = "rdoc"
28
38
  end
39
+
40
+ Jeweler::GemcutterTasks.new
41
+
29
42
  rescue LoadError
30
43
  puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
31
44
  end
@@ -1,14 +1,12 @@
1
- require File.join( File.dirname( __FILE__ ), "packet" )
2
-
3
1
  module OSC
4
- class Bundle < Packet
2
+ class Bundle
5
3
  attr_accessor :timetag
6
-
4
+
7
5
  def initialize(timetag=nil, *args)
8
6
  @timetag = timetag
9
7
  @args = args
10
8
  end
11
-
9
+
12
10
  def encode()
13
11
  s = OSCString.new('#bundle').encode
14
12
  s << encode_timetag(@timetag)
@@ -17,12 +15,9 @@ module OSC
17
15
  end.join
18
16
  end
19
17
 
20
-
21
18
  def to_a() @args.collect{|x| x.to_a} end
22
19
 
23
-
24
-
25
- private
20
+ private
26
21
 
27
22
  def encode_timetag(t)
28
23
  case t
@@ -30,16 +25,20 @@ module OSC
30
25
  t1 = 0
31
26
  t2 = 1
32
27
  when Numeric
33
- t1, fr = t.divmod(1)
34
- t2 = (fr * (2**32)).to_i
28
+ t1, t2 = construct_timetag( t )
35
29
  when Time
36
- t1, fr = (t.to_f + 2208988800).divmod(1)
37
- t2 = (fr * (2**32)).to_i
30
+ t1, t2 = construct_timetag( t.to_ntp )
38
31
  else
39
32
  raise ArgumentError, 'invalid time'
40
33
  end
41
34
  [t1, t2].pack('N2')
42
35
  end
43
36
 
37
+ def construct_timetag( time )
38
+ t1, fr = time.divmod(1)
39
+ t2 = (fr * (2**32)).to_i
40
+
41
+ [t1, t2]
42
+ end
44
43
  end
45
44
  end
@@ -9,6 +9,5 @@ module OSC
9
9
  def send(mesg)
10
10
  @so.send(mesg.encode, 0)
11
11
  end
12
-
13
12
  end
14
13
  end
@@ -2,54 +2,51 @@ require 'rubygems'
2
2
  require 'eventmachine'
3
3
  require File.join( File.dirname( __FILE__), '..', 'osc-ruby')
4
4
 
5
-
6
5
  module OSC
7
6
  Channel = EM::Channel.new
8
-
9
- class Connection < EventMachine::Connection
10
7
 
11
- def receive_data data
8
+ class Connection < EventMachine::Connection
9
+ def receive_data data
12
10
  Channel << OSC::OSCPacket.messages_from_network( data )
13
11
  end
14
12
  end
15
13
 
16
-
17
14
  class EMServer
18
-
15
+
19
16
  def initialize( port = 3333 )
20
17
  @port = port
21
18
  setup_dispatcher
22
19
  @tuples = []
23
20
  end
24
-
21
+
25
22
  def run
26
23
  EM::run { EM::open_datagram_socket "localhost", @port, Connection }
27
24
  end
28
-
25
+
29
26
  def add_method(address_pattern, &proc)
30
27
  matcher = AddressPattern.new( address_pattern )
31
-
28
+
32
29
  @tuples << [matcher, proc]
33
- end
34
-
30
+ end
31
+
35
32
  private
36
33
  def setup_dispatcher
37
34
  Channel.subscribe do |messages|
38
- messages.each do |message|
39
- diff = ( message.time || 0 ) - Time.now.to_ntp
35
+ messages.each do |message|
36
+ diff = ( message.time || 0 ) - Time.now.to_ntp
40
37
 
41
- if diff <= 0
38
+ if diff <= 0
42
39
  sendmesg( message )
43
- else
44
- EM.defer do
45
- sleep( diff )
40
+ else
41
+ EM.defer do
42
+ sleep( diff )
46
43
  sendmesg( message )
47
- end
48
- end
49
- end
44
+ end
45
+ end
46
+ end
50
47
  end
51
48
  end
52
-
49
+
53
50
  def sendmesg(mesg)
54
51
  @tuples.each do |matcher, obj|
55
52
  if matcher.match?( mesg.address )
@@ -57,7 +54,6 @@ module OSC
57
54
  end
58
55
  end
59
56
  end
60
-
61
57
  end
62
58
  end
63
59
 
@@ -1,22 +1,19 @@
1
- require File.join( File.dirname( __FILE__ ), "packet" )
2
-
3
1
  module OSC
4
- class Message < Packet
2
+ class Message
5
3
  attr_accessor :address
6
- attr_accessor :time
4
+ attr_accessor :time
7
5
 
8
-
9
6
  def self.new_with_time( address, time, tags=nil, *args )
10
7
  message = new( address, tags, *args )
11
8
  message.time = time
12
9
  message
13
10
  end
14
-
15
- def initialize(address, *args)
11
+
12
+ def initialize( address, *args )
16
13
  @address = address
17
14
  @args = []
18
-
19
- args.each_with_index do |arg, i|
15
+
16
+ args.each do |arg|
20
17
  case arg
21
18
  when Integer; @args << OSCInt32.new(arg)
22
19
  when Float; @args << OSCFloat32.new(arg)
@@ -26,7 +23,6 @@ module OSC
26
23
  end
27
24
  end
28
25
 
29
-
30
26
  def tags() @args.collect{|x| x.tag}.join end
31
27
 
32
28
  def encode
@@ -36,8 +32,8 @@ module OSC
36
32
  end
37
33
 
38
34
  def to_a() @args.collect{|x| x.val} end
39
-
40
- def eql?( other )
35
+
36
+ def ==( other )
41
37
  @address == other.address &&
42
38
  to_a == other.to_a
43
39
  end
@@ -1,29 +1,29 @@
1
1
  module OSC
2
2
  class NetworkPacket
3
- def initialize(str)
4
- @str, @index = str, 0
3
+ def initialize(str)
4
+ @str, @index = str, 0
5
5
  end
6
-
6
+
7
7
  def to_s
8
8
  @str
9
9
  end
10
-
11
- def rem()
12
- @str.length - @index
10
+
11
+ def rem()
12
+ @str.length - @index
13
13
  end
14
-
15
- def eof? ()
16
- rem <= 0
14
+
15
+ def eof? ()
16
+ rem <= 0
17
17
  end
18
-
19
- def skip(n)
20
- @index += n
18
+
19
+ def skip(n)
20
+ @index += n
21
21
  end
22
-
23
- def skip_padding()
24
- skip((4 - (@index % 4)) % 4)
22
+
23
+ def skip_padding()
24
+ skip((4 - (@index % 4)) % 4)
25
25
  end
26
-
26
+
27
27
  def getn(n)
28
28
  raise EOFError if rem < n
29
29
  s = @str[@index, n]
@@ -37,6 +37,5 @@ module OSC
37
37
  skip(1)
38
38
  c
39
39
  end
40
-
41
40
  end
42
41
  end
@@ -1,6 +1,5 @@
1
1
  module OSC
2
2
  class OSCArgument
3
-
4
3
  def initialize(val) @val = val end
5
4
 
6
5
  attr_accessor :val
@@ -9,7 +8,6 @@ module OSC
9
8
  def to_f() @val.to_f end
10
9
  def to_s() @val.to_s end
11
10
 
12
-
13
11
  private
14
12
  def padding(s)
15
13
  s + ("\000" * ((4 - (s.size % 4)) % 4))
@@ -3,53 +3,53 @@ require 'ostruct'
3
3
 
4
4
  module OSC
5
5
  class OSCPacket
6
-
6
+
7
7
  def self.messages_from_network( string )
8
8
  messages = []
9
9
  osc = new( string )
10
-
10
+
11
11
  if osc.bundle?
12
12
  bundle = osc.get_string
13
13
  time = osc.get_timestamp
14
-
14
+
15
15
  osc.get_bundle_messages.each do | message |
16
16
  messages << decode_simple_message( time, OSCPacket.new( message ) )
17
17
  end
18
-
18
+
19
19
  else
20
20
  messages << decode_simple_message( time, osc )
21
21
  end
22
-
22
+
23
23
  return messages
24
24
  end
25
-
25
+
26
26
  def self.decode_simple_message( time, osc_packet )
27
27
  address = osc_packet.get_string
28
- args = osc_packet.get_arguments
28
+ args = osc_packet.get_arguments
29
29
 
30
30
  Message.new_with_time(address, time, nil, *args )
31
31
  end
32
-
32
+
33
33
  def initialize( string )
34
34
  @packet = NetworkPacket.new( string )
35
-
36
- @types = { "i" => lambda{ OSCInt32.new( get_int32 ) },
35
+
36
+ @types = { "i" => lambda{ OSCInt32.new( get_int32 ) },
37
37
  "f" => lambda{ OSCFloat32.new( get_float32 ) },
38
38
  "s" => lambda{ OSCString.new( get_string ) },
39
39
  "b" => lambda{ OSCBlob.new( get_blob )}
40
40
  }
41
41
  end
42
-
42
+
43
43
  def get_bundle_messages
44
44
  bundle_messages = []
45
-
45
+
46
46
  until @packet.eof?
47
47
  l = @packet.getn(4).unpack('N')[0]
48
48
  bundle_messages << @packet.getn(l)
49
49
  end
50
50
  bundle_messages
51
51
  end
52
-
52
+
53
53
  def get_string
54
54
  result = ''
55
55
  until (c = @packet.getc) == string_delemeter
@@ -58,58 +58,58 @@ module OSC
58
58
  @packet.skip_padding
59
59
  result
60
60
  end
61
-
61
+
62
62
  def get_timestamp
63
63
  t1 = @packet.getn(4).unpack('N')[0]
64
64
  t2 = @packet.getn(4).unpack('N')[0]
65
65
  @packet.skip_padding
66
-
66
+
67
67
  if t1 == 0 && t2 == 1
68
68
  time = nil
69
69
  else
70
70
  time = t1 + t2.to_f / (2**32)
71
71
  end
72
-
72
+
73
73
  time
74
74
  end
75
-
75
+
76
76
  def get_arguments
77
77
  if @packet.getc == ?,
78
-
78
+
79
79
  tags = get_string
80
80
  args = []
81
-
81
+
82
82
  tags.scan(/./) do | tag |
83
83
  args << @types[tag].call
84
84
  end
85
85
  args
86
86
  end
87
87
  end
88
-
88
+
89
89
  def get_int32
90
90
  i = @packet.getn(4).unpack('N')[0]
91
91
  i -= 2**32 if i > (2**31-1)
92
92
  @packet.skip_padding
93
93
  i
94
94
  end
95
-
95
+
96
96
  def get_float32
97
97
  f = @packet.getn(4).unpack('g')[0]
98
98
  @packet.skip_padding
99
99
  f
100
100
  end
101
-
101
+
102
102
  def get_blob
103
103
  l = @packet.getn(4).unpack('N')[0]
104
104
  b = @packet.getn(l)
105
105
  @packet.skip_padding
106
106
  b
107
107
  end
108
-
108
+
109
109
  def bundle?
110
110
  !(@packet.to_s =~ /\A\#bundle/).nil?
111
111
  end
112
-
112
+
113
113
  def string_delemeter
114
114
  # ruby 1.9 has multicharacter support
115
115
  RUBY_VERSION.include?( '1.9' ) ? "\x00" : 0
@@ -2,30 +2,22 @@ require File.join( File.dirname( __FILE__ ), "osc_argument" )
2
2
 
3
3
  module OSC
4
4
  class OSCInt32 < OSCArgument
5
-
6
5
  def tag() 'i' end
7
6
  def encode() [@val].pack('N') end
8
-
9
7
  end
10
8
 
11
9
  class OSCFloat32 < OSCArgument
12
-
13
10
  def tag() 'f' end
14
11
  def encode() [@val].pack('g') end # fake - why fake?
15
-
16
12
  end
17
13
 
18
14
  class OSCString < OSCArgument
19
-
20
15
  def tag() 's' end
21
16
  def encode() padding(@val.sub(/\000.*\z/, '') + "\000") end
22
-
23
17
  end
24
18
 
25
19
  class OSCBlob < OSCArgument
26
-
27
20
  def tag() 'b' end
28
21
  def encode() padding([@val.size].pack('N') + @val) end
29
-
30
22
  end
31
23
  end
@@ -0,0 +1,43 @@
1
+ # sanity checks
2
+ require File.join( File.dirname(__FILE__) , '..', 'spec_helper' )
3
+
4
+ describe MessageBuilder do
5
+ before :each do
6
+ @builder = MessageBuilder.new
7
+ end
8
+
9
+ it "encodes just the address" do
10
+ mesg = @builder.with_address("/hi")
11
+ mesg.build.encode.should == "/hi\000,\000\000\000"
12
+ end
13
+
14
+ it "encodes single int values" do
15
+ mesg = @builder.with_address("/hi").
16
+ with_int(33)
17
+
18
+ mesg.build.encode.should == "/hi\000,i\000\000\000\000\000!"
19
+ end
20
+
21
+ it "encodes single string values" do
22
+ mesg = @builder.with_address("/hi").
23
+ with_string("hello")
24
+
25
+ mesg.build.encode.should == "/hi\000,s\000\000hello\000\000\000"
26
+ end
27
+
28
+ it "encodes single float values" do
29
+ mesg = @builder.with_address("/hi").
30
+ with_float(3.14159)
31
+
32
+ mesg.build.encode.should == "/hi\000,f\000\000@I\017\320"
33
+ end
34
+
35
+ it "encodes multiple floats" do
36
+ mesg = @builder.with_address("/hi").
37
+ with_float(3.14159).
38
+ with_float(4.5)
39
+
40
+ mesg.build.encode.should == [47, 104, 105, 0, 44, 102, 102, 0, 64, 73, 15, 208, 64, 144, 0, 0].pack("C*")
41
+ end
42
+
43
+ end
@@ -12,23 +12,47 @@ require File.join( File.dirname(__FILE__) , '..', 'spec_helper' )
12
12
 
13
13
 
14
14
  describe OSC::Message do
15
- before :each do
16
- @builder = MessageBuilder.new
17
- @builder.with_int( 42 ).
18
- with_int( 33 )
19
-
20
- @message = @builder.build
15
+ describe "basic traits" do
16
+ it "should have no arguments if you define none" do
17
+ m = OSC::Message.new( "/hi" )
18
+ m.to_a.should == []
19
+ end
20
+
21
+ it "should accept int arguments" do
22
+ m = OSC::Message.new( "/hi", 42 )
23
+ m.to_a.should == [42]
24
+ m.tags.should == "i"
25
+ end
26
+
27
+ it "should accept string arguments" do
28
+ m = OSC::Message.new( "/hi", "42" )
29
+ m.to_a.should == ["42"]
30
+ m.tags.should == "s"
31
+ end
32
+
33
+ it "should accept float arguments" do
34
+ m = OSC::Message.new( "/hi", 42.001 )
35
+ m.to_a.should == [42.001]
36
+ m.tags.should == "f"
37
+ end
21
38
  end
22
39
 
23
- it "should have no arguments if you define none" do
24
- m = OSC::Message.new( "/hi" )
25
- m.to_a.should == []
26
- end
40
+ describe "more interesting traits" do
41
+ before :each do
42
+ @builder = MessageBuilder.new
43
+ @builder.with_int( 42 ).
44
+ with_int( 33 )
45
+
46
+ @message = @builder.build
47
+ end
27
48
 
28
- it "should have accept int arguments" do
29
- m = OSC::Message.new( "/hi", 42 )
30
- m.to_a.should == [42]
31
- m.tags.should == "i"
32
- end
49
+
33
50
 
51
+ it "should know equality" do
52
+ @message2 = @builder.build
53
+
54
+ @message.object_id.should_not == @message2.object_id
55
+ @message.should == @message2
56
+ end
57
+ end
34
58
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: osc-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - aberant
@@ -9,11 +9,11 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-10-16 00:00:00 -05:00
12
+ date: 2009-12-03 00:00:00 -06:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
16
- description:
16
+ description: a ruby client for the OSC protocol
17
17
  email: qzzzq1@gmail.com
18
18
  executables: []
19
19
 
@@ -38,11 +38,12 @@ files:
38
38
  - lib/osc-ruby/osc_argument.rb
39
39
  - lib/osc-ruby/osc_packet.rb
40
40
  - lib/osc-ruby/osc_types.rb
41
- - lib/osc-ruby/packet.rb
42
41
  - lib/osc-ruby/server.rb
43
42
  - README.rdoc
44
43
  has_rdoc: true
45
44
  homepage: http://github.com/aberant/osc-ruby
45
+ licenses: []
46
+
46
47
  post_install_message:
47
48
  rdoc_options:
48
49
  - --charset=UTF-8
@@ -63,14 +64,15 @@ required_rubygems_version: !ruby/object:Gem::Requirement
63
64
  requirements: []
64
65
 
65
66
  rubyforge_project: osc-ruby
66
- rubygems_version: 1.3.1
67
+ rubygems_version: 1.3.5
67
68
  signing_key:
68
- specification_version: 2
69
+ specification_version: 3
69
70
  summary: inital gem
70
71
  test_files:
71
72
  - spec/builders/message_builder.rb
72
73
  - spec/spec_helper.rb
73
74
  - spec/unit/address_pattern_spec.rb
75
+ - spec/unit/message_builder_spec.rb
74
76
  - spec/unit/message_bundle_spec.rb
75
77
  - spec/unit/message_spec.rb
76
78
  - spec/unit/network_packet_spec.rb
@@ -1,134 +0,0 @@
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