fast_osc 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +48 -0
- data/README.md +95 -0
- data/Rakefile +5 -0
- data/ext/fast_osc/extconf.rb +28 -0
- data/ext/fast_osc/fast_osc_wrapper.c +155 -0
- data/ext/fast_osc/rtosc.c +781 -0
- data/ext/fast_osc/rtosc.h +269 -0
- data/fast_osc.gemspec +26 -0
- data/lib/fast_osc/version.rb +3 -0
- data/lib/fast_osc.rb +6 -0
- metadata +100 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 24934bf4851d8faf6a3f60a298f51519118c5865
|
4
|
+
data.tar.gz: 81d1d5472ae0928587879ffb2ecdee3d265b832f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 104becd81c316514e4171614079d7aee8ba4d33c5788cfd82558ac834374d127ae468dd64c188e89ce94416cdbb692d40d2de00b67b7987a24018284cec031de
|
7
|
+
data.tar.gz: 4eeef0b840558a8a51fcecea56776b734d3e79793421ab13bbce4f75074fe61affc7fe8adbd2b13da6a2b6066cda07b2f350e360402a8e80205077de8c66a110
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# rtosc licence
|
2
|
+
|
3
|
+
Copyright (c) 2012 Mark McCurry
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a
|
6
|
+
copy of this software and associated documentation files (the "Software"),
|
7
|
+
to deal in the Software without restriction, including without limitation
|
8
|
+
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
9
|
+
and/or sell copies of the Software, and to permit persons to whom the
|
10
|
+
Software is furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice (including the next
|
13
|
+
paragraph) shall be included in all copies or substantial portions of the
|
14
|
+
Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
20
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
21
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
22
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
23
|
+
DEALINGS IN THE SOFTWARE.
|
24
|
+
|
25
|
+
# fast_osc gem wrapper licence
|
26
|
+
|
27
|
+
Copyright (c) 2016 Xavier Riley
|
28
|
+
|
29
|
+
MIT License
|
30
|
+
|
31
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
32
|
+
a copy of this software and associated documentation files (the
|
33
|
+
"Software"), to deal in the Software without restriction, including
|
34
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
35
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
36
|
+
permit persons to whom the Software is furnished to do so, subject to
|
37
|
+
the following conditions:
|
38
|
+
|
39
|
+
The above copyright notice and this permission notice shall be
|
40
|
+
included in all copies or substantial portions of the Software.
|
41
|
+
|
42
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
43
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
44
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
45
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
46
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
47
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
48
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
# FastOsc
|
2
|
+
|
3
|
+
`WARNING - Work in progress. Probably not safe for production yet`
|
4
|
+
|
5
|
+
A Ruby wrapper around [rtosc](https://github.com/fundamental/rtosc/) to encode and decode OSC messages.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
gem 'fast_osc'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install fast_osc
|
20
|
+
|
21
|
+
This will only work on a mac at the moment as that's what the `librtosc.a` was compiled on.
|
22
|
+
|
23
|
+
## Is it fast?
|
24
|
+
|
25
|
+
Let's see...
|
26
|
+
|
27
|
+
Key:
|
28
|
+
|
29
|
+
* `fast_osc` - this gem
|
30
|
+
* `osc` - [`osc-ruby`](https://github.com/aberant/osc-ruby)
|
31
|
+
* `samsosc` - `OSC` classes from Sonic Pi (which are optimised pure Ruby based on `pack` and `unpack`)
|
32
|
+
|
33
|
+
### Encoding Benchmark
|
34
|
+
|
35
|
+
```
|
36
|
+
["/feeooblah", ["beans", 1, 2.0]]
|
37
|
+
Calculating -------------------------------------
|
38
|
+
fast_osc 54.101k i/100ms
|
39
|
+
osc 7.688k i/100ms
|
40
|
+
samsosc 21.406k i/100ms
|
41
|
+
-------------------------------------------------
|
42
|
+
fast_osc 909.680k (±21.4%) i/s - 4.328M
|
43
|
+
osc 94.678k (±14.5%) i/s - 468.968k
|
44
|
+
samsosc 271.908k (±19.3%) i/s - 1.327M
|
45
|
+
```
|
46
|
+
|
47
|
+
## Decoding Bencmark
|
48
|
+
|
49
|
+
```
|
50
|
+
["/feeooblah", ["beans", 1, 2.0]]
|
51
|
+
Calculating -------------------------------------
|
52
|
+
fast_osc 91.434k i/100ms
|
53
|
+
samsosc 22.095k i/100ms
|
54
|
+
oscruby 3.522k i/100ms
|
55
|
+
-------------------------------------------------
|
56
|
+
fast_osc 2.635M (±22.2%) i/s - 12.435M
|
57
|
+
samsosc 264.614k (±16.1%) i/s - 1.304M
|
58
|
+
oscruby 36.362k (±16.3%) i/s - 179.622k
|
59
|
+
```
|
60
|
+
|
61
|
+
Benchmark adapted from https://github.com/samaaron/sonic-pi/blob/master/app/server/sonicpi/test/performance/test_osc_perf.rb
|
62
|
+
|
63
|
+
I'll include a better test in the repo in time.
|
64
|
+
|
65
|
+
## Usage
|
66
|
+
|
67
|
+
```
|
68
|
+
>> FastOsc.serialize("/foo", ["baz", 1, 2.0])
|
69
|
+
=> "/foo\x00\x00\x00\x00,sif\x00\x00\x00\x00baz\x00\x00\x00\x00\x01@\x00\x00\x00"
|
70
|
+
>> res = _
|
71
|
+
>> FastOsc.deserialize(res)
|
72
|
+
=> ["baz", 1, 2.0]
|
73
|
+
```
|
74
|
+
|
75
|
+
## Still todo
|
76
|
+
|
77
|
+
* Implement more types
|
78
|
+
* return address with `deserialize` (doh!)
|
79
|
+
* add tests at the Ruby level (rtosc C code is already tested)
|
80
|
+
* figure out build process
|
81
|
+
|
82
|
+
## Development notes
|
83
|
+
|
84
|
+
bundle install
|
85
|
+
rake compile
|
86
|
+
|
87
|
+
https://gist.github.com/xavriley/507eff0a75d4552fa56e
|
88
|
+
|
89
|
+
## Contributing
|
90
|
+
|
91
|
+
1. Fork it ( http://github.com/<my-github-username>/fast_osc/fork )
|
92
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
93
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
94
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
95
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'mkmf'
|
2
|
+
|
3
|
+
LIBDIR = RbConfig::CONFIG['libdir']
|
4
|
+
INCLUDEDIR = RbConfig::CONFIG['includedir']
|
5
|
+
|
6
|
+
#HEADER_DIRS = ["/Users/xriley/Projects/rtosc/include/rtosc", INCLUDEDIR]
|
7
|
+
HEADER_DIRS = [INCLUDEDIR]
|
8
|
+
|
9
|
+
# # setup constant that is equal to that of the file path that holds that static libraries that will need to be compiled against
|
10
|
+
# LIB_DIRS = [LIBDIR, File.expand_path(File.join(File.dirname(__FILE__), "lib"))]
|
11
|
+
LIB_DIRS = [LIBDIR]
|
12
|
+
|
13
|
+
# # array of all libraries that the C extension should be compiled against
|
14
|
+
# libs = ['-lrtosc']
|
15
|
+
|
16
|
+
extension_name = 'fast_osc'
|
17
|
+
dir_config(extension_name, HEADER_DIRS, LIB_DIRS)
|
18
|
+
|
19
|
+
# iterate though the libs array, and append them to the $LOCAL_LIBS array used for the makefile creation
|
20
|
+
# libs.each do |lib|
|
21
|
+
# $LOCAL_LIBS << "#{lib} "
|
22
|
+
# end
|
23
|
+
|
24
|
+
$srcs = ["fast_osc_wrapper.c"]
|
25
|
+
|
26
|
+
$CFLAGS << " -std=c99 -Wall -Wextra -Wno-unused-parameter"
|
27
|
+
|
28
|
+
create_makefile(extension_name)
|
@@ -0,0 +1,155 @@
|
|
1
|
+
#include <ruby.h>
|
2
|
+
#include <rtosc.h>
|
3
|
+
#include <rtosc.c>
|
4
|
+
|
5
|
+
// Allocate VALUE variables to hold the modules we'll create. Ruby values
|
6
|
+
// are all of type VALUE. Qnil is the C representation of Ruby's nil.
|
7
|
+
VALUE FastOsc = Qnil;
|
8
|
+
|
9
|
+
// Declare a couple of functions. The first is initialization code that runs
|
10
|
+
// when this file is loaded, and the second is the actual business logic we're
|
11
|
+
// implementing.
|
12
|
+
void Init_fast_osc();
|
13
|
+
VALUE method_fast_osc_deserialize(VALUE self, VALUE msg);
|
14
|
+
VALUE method_fast_osc_serialize(VALUE self, VALUE address, VALUE args);
|
15
|
+
|
16
|
+
// Initial setup function, takes no arguments and returns nothing. Some API
|
17
|
+
// notes:
|
18
|
+
//
|
19
|
+
// * rb_define_module() creates and returns a top-level module by name
|
20
|
+
//
|
21
|
+
// * rb_define_module_under() takes a module and a name, and creates a new
|
22
|
+
// module within the given one
|
23
|
+
//
|
24
|
+
// * rb_define_singleton_method() take a module, the method name, a reference to
|
25
|
+
// a C function, and the method's arity, and exposes the C function as a
|
26
|
+
// single method on the given module
|
27
|
+
//
|
28
|
+
void Init_fast_osc() {
|
29
|
+
FastOsc = rb_define_module("FastOsc");
|
30
|
+
rb_define_singleton_method(FastOsc, "deserialize", method_fast_osc_deserialize, 1);
|
31
|
+
rb_define_singleton_method(FastOsc, "serialize", method_fast_osc_serialize, 2);
|
32
|
+
}
|
33
|
+
|
34
|
+
VALUE method_fast_osc_deserialize(VALUE self, VALUE msg) {
|
35
|
+
rtosc_arg_itr_t itr;
|
36
|
+
char* data = StringValuePtr(msg);
|
37
|
+
itr = rtosc_itr_begin(data);
|
38
|
+
VALUE output = rb_ary_new();
|
39
|
+
|
40
|
+
rtosc_arg_val_t next_val;
|
41
|
+
|
42
|
+
while(!rtosc_itr_end(itr)) {
|
43
|
+
|
44
|
+
next_val = rtosc_itr_next(&itr);
|
45
|
+
|
46
|
+
switch(next_val.type) {
|
47
|
+
case 'i' :
|
48
|
+
// INT2FIX() for integers within 31bits.
|
49
|
+
rb_ary_push(output, INT2FIX(next_val.val.i));
|
50
|
+
break;
|
51
|
+
case 'f' :
|
52
|
+
rb_ary_push(output, rb_float_new(next_val.val.f));
|
53
|
+
break;
|
54
|
+
case 's' :
|
55
|
+
rb_ary_push(output, rb_str_new2(next_val.val.s));
|
56
|
+
break;
|
57
|
+
case 'b' :
|
58
|
+
rb_ary_push(output, rb_str_new((const char*)next_val.val.b.data, next_val.val.b.len));
|
59
|
+
break;
|
60
|
+
case 'h' :
|
61
|
+
// INT2NUM() for arbitrary sized integers
|
62
|
+
rb_ary_push(output, INT2NUM(next_val.val.h));
|
63
|
+
break;
|
64
|
+
case 't' :
|
65
|
+
// OSC time tag
|
66
|
+
// not implemented
|
67
|
+
break;
|
68
|
+
case 'd' :
|
69
|
+
rb_ary_push(output, rb_float_new(next_val.val.d));
|
70
|
+
break;
|
71
|
+
case 'S' :
|
72
|
+
rb_ary_push(output, ID2SYM(rb_intern(next_val.val.s)));
|
73
|
+
break;
|
74
|
+
case 'c' :
|
75
|
+
rb_ary_push(output, rb_str_concat(rb_str_new2(""), INT2FIX(next_val.val.i)));
|
76
|
+
break;
|
77
|
+
}
|
78
|
+
|
79
|
+
}
|
80
|
+
|
81
|
+
return output;
|
82
|
+
}
|
83
|
+
|
84
|
+
int buffer_size_for_ruby_string(VALUE rstring) {
|
85
|
+
int str_bytesize = FIX2INT(LONG2FIX(RSTRING_LEN(rstring)));
|
86
|
+
return (int)((str_bytesize + sizeof(int) - 1) & ~(sizeof(int) - 1));
|
87
|
+
}
|
88
|
+
|
89
|
+
VALUE method_fast_osc_serialize(VALUE self, VALUE address, VALUE args) {
|
90
|
+
char* c_address = StringValueCStr(address);
|
91
|
+
|
92
|
+
int no_of_args = NUM2INT(LONG2NUM(RARRAY_LEN(args)));
|
93
|
+
int i;
|
94
|
+
int max_buffer_size = 0;
|
95
|
+
VALUE current_arg;
|
96
|
+
|
97
|
+
//output tags and args list
|
98
|
+
VALUE tagstring = rb_str_new2(""); //rtosc will handle comma
|
99
|
+
rtosc_arg_t output_args[no_of_args];
|
100
|
+
|
101
|
+
for(i = 0; i < no_of_args; i++) {
|
102
|
+
current_arg = rb_ary_entry(args, i);
|
103
|
+
|
104
|
+
switch(TYPE(current_arg)) {
|
105
|
+
case T_FIXNUM:
|
106
|
+
// max bytes for a single numeric representation
|
107
|
+
max_buffer_size += 8;
|
108
|
+
|
109
|
+
if(FIX2LONG(current_arg) < ~(1 << 31)) {
|
110
|
+
rb_str_concat(tagstring, rb_str_new2("i"));
|
111
|
+
output_args[i].i = FIX2INT(current_arg);
|
112
|
+
} else {
|
113
|
+
rb_str_concat(tagstring, rb_str_new2("h"));
|
114
|
+
output_args[i].h = FIX2LONG(current_arg);
|
115
|
+
}
|
116
|
+
break;
|
117
|
+
case T_FLOAT:
|
118
|
+
// now align to 4 byte boundary for sizing output buffer
|
119
|
+
max_buffer_size += 8;
|
120
|
+
|
121
|
+
rb_str_concat(tagstring, rb_str_new2("f"));
|
122
|
+
output_args[i].f = NUM2DBL(current_arg);
|
123
|
+
break;
|
124
|
+
case T_STRING:
|
125
|
+
// now align to 4 byte boundary for sizing output buffer
|
126
|
+
max_buffer_size += buffer_size_for_ruby_string(current_arg);
|
127
|
+
|
128
|
+
rb_str_concat(tagstring, rb_str_new2("s"));
|
129
|
+
output_args[i].s = StringValueCStr(current_arg);
|
130
|
+
break;
|
131
|
+
}
|
132
|
+
}
|
133
|
+
|
134
|
+
//add space for the address and tag strings to the buffer
|
135
|
+
max_buffer_size += buffer_size_for_ruby_string(address);
|
136
|
+
max_buffer_size += buffer_size_for_ruby_string(tagstring);
|
137
|
+
|
138
|
+
// Get next largest power of two for buffer
|
139
|
+
max_buffer_size--;
|
140
|
+
max_buffer_size |= max_buffer_size >> 1;
|
141
|
+
max_buffer_size |= max_buffer_size >> 2;
|
142
|
+
max_buffer_size |= max_buffer_size >> 4;
|
143
|
+
max_buffer_size |= max_buffer_size >> 8;
|
144
|
+
max_buffer_size |= max_buffer_size >> 16;
|
145
|
+
max_buffer_size++;
|
146
|
+
|
147
|
+
char buffer[max_buffer_size];
|
148
|
+
|
149
|
+
|
150
|
+
int len = rtosc_amessage(buffer, sizeof(buffer), c_address, StringValueCStr(tagstring), output_args);
|
151
|
+
|
152
|
+
VALUE output = rb_str_new(buffer, len);
|
153
|
+
|
154
|
+
return output;
|
155
|
+
}
|