fast_osc 0.0.3
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.
- 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
|
+
}
|