module FastOsc
Constants
- VERSION
Public Class Methods
decode(p1)
click to toggle source
# Example of how to process the messages in ruby: msgs.each do |timestamp, osc_messages|
# These are the messages within this bundle puts "T: #{timestamp}, D: #{osc_messages}" osc_messages.each do |path, args| # And this is each message puts "P: #{path}, A: #{args}" end
end
VALUE method_fast_osc_decode(VALUE self, VALUE msg) { VALUE output_ary = rb_ary_new(); // If we get != Qnil, then there were no bundles, and we need to add the element VALUE element = method_fast_osc_decode_do(self, msg, output_ary); if (element != Qnil){ // format [[timestamp, [[ ... msg ...]]]] // enclosing arrays match output shape of decode_do VALUE msgs_ary = rb_ary_new(); VALUE element_ary = rb_ary_new(); // A timestamp of `nil` is a special case meaning "immediately". rb_ary_push(element_ary, Qnil); rb_ary_push(msgs_ary, element); rb_ary_push(element_ary, msgs_ary); rb_ary_push(output_ary, element_ary); } return output_ary; }
decode_no_bundles(p1)
click to toggle source
VALUE method_fast_osc_decode_single_message(VALUE self, VALUE msg) { rtosc_arg_itr_t itr; char* data = StringValuePtr(msg); itr = rtosc_itr_begin(data); VALUE output = rb_ary_new(); VALUE args_output = rb_ary_new(); VALUE string_arg; int enc; VALUE path = rb_str_new2(rtosc_path(data)); rtosc_arg_val_t next_val; // for timestamp arg decoding uint64_t tt, secs, frac; while(!rtosc_itr_end(itr)) { next_val = rtosc_itr_next(&itr); switch(next_val.type) { case 'i' : // INT2FIX() for integers within 31bits. rb_ary_push(args_output, INT2FIX(next_val.val.i)); break; case 'f' : rb_ary_push(args_output, rb_float_new(next_val.val.f)); break; case 's' : string_arg = rb_str_new2(next_val.val.s); enc = rb_enc_find_index("UTF-8"); rb_enc_associate_index(string_arg, enc); rb_ary_push(args_output, string_arg); break; case 'b' : string_arg = rb_str_new((const char*)next_val.val.b.data, next_val.val.b.len); enc = rb_enc_find_index("ASCII-8BIT"); rb_enc_associate_index(string_arg, enc); rb_ary_push(args_output, string_arg); break; case 'h' : // INT2NUM() for arbitrary sized integers rb_ary_push(args_output, INT2NUM(next_val.val.h)); break; case 't' : // OSC time tag // need to decode OSC (ntp style time) to unix timestamp // then call Time.now with that tt = next_val.val.t; secs = (tt >> 32) - JAN_1970; // taken from this SO post on how to convert NTP to Unix epoch // http://stackoverflow.com/a/29138806 frac = ((tt & 0xFFFFFFFF) * 1000000) >> 32; // example call from grpc ruby extension // https://github.com/grpc/grpc/blob/master/src/ruby/ext/grpc/rb_grpc.c // return rb_funcall(rb_cTime, id_at, 2, INT2NUM(real_time.tv_sec), // INT2NUM(real_time.tv_nsec / 1000)); // printf("\noutsec: %08llx\n", secs); // printf("\noutfrac: %08llx\n", frac); // printf("\nouttimetag: %08llx\n", tt); rb_ary_push(args_output, rb_funcall(rb_cTime, rb_intern("at"), 2, LONG2NUM(secs), LONG2NUM(frac))); break; case 'd' : rb_ary_push(args_output, rb_float_new(next_val.val.d)); break; case 'S' : rb_ary_push(args_output, ID2SYM(rb_intern(next_val.val.s))); break; case 'c' : rb_ary_push(args_output, rb_str_concat(rb_str_new2(""), INT2FIX(next_val.val.i))); break; } } rb_ary_push(output, path); rb_ary_push(output, args_output); return output; }
decode_single_message(p1)
click to toggle source
VALUE method_fast_osc_decode_single_message(VALUE self, VALUE msg) { rtosc_arg_itr_t itr; char* data = StringValuePtr(msg); itr = rtosc_itr_begin(data); VALUE output = rb_ary_new(); VALUE args_output = rb_ary_new(); VALUE string_arg; int enc; VALUE path = rb_str_new2(rtosc_path(data)); rtosc_arg_val_t next_val; // for timestamp arg decoding uint64_t tt, secs, frac; while(!rtosc_itr_end(itr)) { next_val = rtosc_itr_next(&itr); switch(next_val.type) { case 'i' : // INT2FIX() for integers within 31bits. rb_ary_push(args_output, INT2FIX(next_val.val.i)); break; case 'f' : rb_ary_push(args_output, rb_float_new(next_val.val.f)); break; case 's' : string_arg = rb_str_new2(next_val.val.s); enc = rb_enc_find_index("UTF-8"); rb_enc_associate_index(string_arg, enc); rb_ary_push(args_output, string_arg); break; case 'b' : string_arg = rb_str_new((const char*)next_val.val.b.data, next_val.val.b.len); enc = rb_enc_find_index("ASCII-8BIT"); rb_enc_associate_index(string_arg, enc); rb_ary_push(args_output, string_arg); break; case 'h' : // INT2NUM() for arbitrary sized integers rb_ary_push(args_output, INT2NUM(next_val.val.h)); break; case 't' : // OSC time tag // need to decode OSC (ntp style time) to unix timestamp // then call Time.now with that tt = next_val.val.t; secs = (tt >> 32) - JAN_1970; // taken from this SO post on how to convert NTP to Unix epoch // http://stackoverflow.com/a/29138806 frac = ((tt & 0xFFFFFFFF) * 1000000) >> 32; // example call from grpc ruby extension // https://github.com/grpc/grpc/blob/master/src/ruby/ext/grpc/rb_grpc.c // return rb_funcall(rb_cTime, id_at, 2, INT2NUM(real_time.tv_sec), // INT2NUM(real_time.tv_nsec / 1000)); // printf("\noutsec: %08llx\n", secs); // printf("\noutfrac: %08llx\n", frac); // printf("\nouttimetag: %08llx\n", tt); rb_ary_push(args_output, rb_funcall(rb_cTime, rb_intern("at"), 2, LONG2NUM(secs), LONG2NUM(frac))); break; case 'd' : rb_ary_push(args_output, rb_float_new(next_val.val.d)); break; case 'S' : rb_ary_push(args_output, ID2SYM(rb_intern(next_val.val.s))); break; case 'c' : rb_ary_push(args_output, rb_str_concat(rb_str_new2(""), INT2FIX(next_val.val.i))); break; } } rb_ary_push(output, path); rb_ary_push(output, args_output); return output; }
encode_no_bundles(address, args=[])
click to toggle source
# File lib/fast_osc.rb, line 18 def self.encode_no_bundles(address, args=[]) encoder.encode_single_message(address, args) end
encode_single_bundle(p1, p2, p3 = v3)
click to toggle source
VALUE method_fast_osc_encode_single_bundle(int argc, VALUE* argv, VALUE self) { VALUE timetag, path, args; rb_scan_args(argc, argv, "21", &timetag, &path, &args); if (NIL_P(args)) args = rb_ary_new(); VALUE combined_args_holder = rb_ary_new(); rb_ary_push(combined_args_holder, path); rb_ary_push(combined_args_holder, args); VALUE* combined_path_and_args = RARRAY_PTR(combined_args_holder); // There are always 2 args here - [path, [args...]] VALUE message = method_fast_osc_encode_single_message(2, combined_path_and_args, self); int bufsize = buffer_size_for_ruby_string(message) + 16; int no_of_elems = 1; uint64_t tt = ruby_time_to_osc_timetag(timetag); char output_buffer[bufsize]; unsigned long int len = rtosc_bundle(output_buffer, bufsize, tt, no_of_elems, StringValuePtr(message)); VALUE output = rb_str_new(output_buffer, len); return output; }
encode_single_message(p1, p2 = v2)
click to toggle source
VALUE method_fast_osc_encode_single_message(int argc, VALUE* argv, VALUE self) { VALUE address, args; rb_scan_args(argc, argv, "11", &address, &args); if (NIL_P(args)) args = rb_ary_new(); // Ruby C API only really allows methods that slurp in all the args // Since we want the method to look like // // def encode_single_message(path, args=[]) // // we need to muck around with the args option a bit // VALUE* brings in args as a C array char* c_address = StringValueCStr(address); int no_of_args = NUM2INT(LONG2NUM(RARRAY_LEN(args))); int i; VALUE current_arg, strval; //output tags and args list VALUE tagstring = rb_str_new2(""); //rtosc will handle comma rtosc_arg_t output_args[no_of_args]; for(i = 0; i < no_of_args; i++) { current_arg = rb_ary_entry(args, i); switch(TYPE(current_arg)) { case T_FIXNUM: if(FIX2LONG(current_arg) < ~(1 << 31)) { rb_str_concat(tagstring, rb_str_new2("i")); output_args[i].i = FIX2INT(current_arg); } else { rb_str_concat(tagstring, rb_str_new2("h")); output_args[i].h = FIX2LONG(current_arg); } break; case T_FLOAT: rb_str_concat(tagstring, rb_str_new2("f")); output_args[i].f = NUM2DBL(current_arg); break; case T_STRING: { VALUE rb_string_encoding; rb_string_encoding = rb_funcall(current_arg, rb_intern("encoding"), 0); // #=> Encoding:UTF-8 // check for ASCII-8BIT after converting to symbol // if present, set as blob arg, else string // if(rb_string_encoding._dump.to_sym == :"ASCII-8BIT") { ... } if(rb_funcall(rb_funcall(rb_string_encoding, rb_intern("_dump"), 0), rb_intern("=="), 1, rb_str_new2("ASCII-8BIT"))) { rb_str_concat(tagstring, rb_str_new2("b")); output_args[i].b = (rtosc_blob_t){RSTRING_LEN(current_arg), StringValuePtr(current_arg)}; } else { rb_str_concat(tagstring, rb_str_new2("s")); output_args[i].s = StringValueCStr(current_arg); } } break; case T_SYMBOL: // now align to 4 byte boundary for sizing output buffer strval = rb_sym_to_s(current_arg); // encode as a string because not all implementation understand S as // alternative string tag rb_str_concat(tagstring, rb_str_new2("s")); output_args[i].s = StringValueCStr(strval); break; case T_DATA: if (CLASS_OF(current_arg) == rb_cTime) { // check for Time as an object arg rb_str_concat(tagstring, rb_str_new2("t")); output_args[i].t = ruby_time_to_osc_timetag(current_arg); } break; } } unsigned long int len; // When buffer is NULL, the function returns the size of the buffer required to store the message if(RSTRING_LEN(tagstring)) { len = rtosc_amessage(NULL, 0, c_address, StringValueCStr(tagstring), output_args); } else { len = rtosc_message(NULL, 0, c_address, ""); } // duplicate if/else due to compiler errors char buffer[len]; if(RSTRING_LEN(tagstring)) { rtosc_amessage(buffer, len, c_address, StringValueCStr(tagstring), output_args); } else { rtosc_message(buffer, len, c_address, ""); } VALUE output = rb_str_new(buffer, len); return output; }
Private Class Methods
decoder()
click to toggle source
# File lib/fast_osc.rb, line 40 def self.decoder @decoder ||= SonicPi::OSC::OscDecode.new end
encoder()
click to toggle source
# File lib/fast_osc.rb, line 36 def self.encoder @encoder ||= SonicPi::OSC::OscEncode.new end