1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
|
#--
# This file is part of Sonic Pi: http://sonic-pi.net
# Full project source: https://github.com/samaaron/sonic-pi
# License: https://github.com/samaaron/sonic-pi/blob/master/LICENSE.md
#
# Copyright 2013, 2014, 2015, 2016 by Sam Aaron (http://sam.aaron.name).
# All rights reserved.
#
# Permission is granted for use, copying, modification, and
# distribution of modified versions of this work as long as this
# notice is included.
#++
module SonicPi
module OSC
class OscDecode
def initialize(use_cache = false, cache_size=1000)
@float_cache = {}
@integer_cache = {}
@cache_size = cache_size
@num_cached_integers = 0
@num_cached_floats = 0
@string_terminator = "\x00".freeze
@args = []
@i_tag = "i".freeze
@f_tag = "f".freeze
@s_tag = "s".freeze
@d_tag = "d".freeze
@h_tag = "h".freeze
@b_tag = "b".freeze
@cap_n = 'N'.freeze
@cap_g = 'G'.freeze
@low_g = 'g'.freeze
@q_lt = 'q>'.freeze
@binary_encoding = "BINARY".freeze
end
def decode_single_message(m)
## Note everything is inlined here for effienciency to remove the
## cost of method dispatch. Apologies if this makes it harder to
## read & understand. See http://opensoundcontrol.org for spec.
m.force_encoding(@binary_encoding)
args, idx = [], 0
# Get OSC address e.g. /foo
orig_idx = idx
idx = m.index(@string_terminator, orig_idx)
address, idx = m[orig_idx...idx], idx + 1 + ((4 - ((idx + 1) % 4)) % 4)
sep, idx = m[idx], idx + 1
# Let's see if we have some args..
if sep == ?,
# Get type tags
orig_idx = idx
idx = m.index(@string_terminator, orig_idx)
tags, idx = m[orig_idx...idx], idx + 1 + ((4 - ((idx + 1) % 4)) % 4)
tags.each_char do |t|
case t
when @i_tag
# int32
raw = m[idx, 4]
arg, idx = @integer_cache[raw], idx + 4
unless arg
arg = raw.unpack(@cap_n)[0]
# Values placed inline for efficiency:
# 2**32 == 4294967296
# 2**31 - 1 == 2147483647
arg -= 4294967296 if arg > 2147483647
if @num_cached_integers < @cache_size
@integer_cache[raw] = arg
@num_cached_integers += 1
end
end
when @f_tag
# float32
raw = m[idx, 4]
arg, idx = @float_cache[raw], idx + 4
unless arg
arg = raw.unpack(@low_g)[0]
if @num_cached_floats < @cache_size
@float_cache[raw] = arg
@num_cached_floats += 1
end
end
when @s_tag
# string
orig_idx = idx
idx = m.index(@string_terminator, orig_idx)
arg, idx = m[orig_idx...idx], idx + 1 + ((4 - ((idx + 1) % 4)) % 4)
when @d_tag
# double64
arg, idx = m[idx, 8].unpack(@cap_g)[0], idx + 8
when @h_tag
# int64
arg, idx = m[idx, 8].unpack(@q_lt)[0], idx + 8
when @b_tag
# binary blob
l = m[idx, 4].unpack(@cap_n)[0]
idx += 4
arg = m[idx, l]
idx += l
#Skip Padding
idx += ((4 - (idx % 4)) % 4)
else
raise "Unknown OSC type #{t}"
end
args << arg
end
end
return address, args
end
end
end
end
|