Skip to content

Instantly share code, notes, and snippets.

@xavriley
Created October 4, 2015 19:42
Show Gist options
  • Select an option

  • Save xavriley/af7c756a057a7f35524e to your computer and use it in GitHub Desktop.

Select an option

Save xavriley/af7c756a057a7f35524e to your computer and use it in GitHub Desktop.
WIP decoding OSC messages using only unpack

This doesn't currently work because the OSC spec calls for the ints to be packed with a "twos complement" which basically means they are packed unsigned and converted when they get decoded.

[-1].pack('N').unpack('N') # => 16777216
[-1].pack('N').unpack('N').pack('V').unpack('l') # => -1
#--
# 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 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.
#++
require 'strscan'
module SonicPi
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 = Regexp.new("\x00")
@tag_separator = ','
@args = []
@i_tag = "i"
@f_tag = "f"
@s_tag = "s"
@d_tag = "d"
@h_tag = "h"
@b_tag = "b"
@cap_n = 'N'
@cap_g = 'G'
@low_g = 'g'
@low_l = 'l'
@q_lt = 'q>'
@binary_encoding = "BINARY"
end
def decode_single_message(msg)
## 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 = StringScanner.new(msg)
pack_string = ""
# Get OSC address e.g. /foo
m.skip_until(@string_terminator)
m.pos = 4 * ((m.pos + 0.01)/4).ceil
pack_string << "Z#{m.pos}"
# Let's see if we have some args..
# check the next char to see if it's a comma
if m.peek(1) == @tag_separator
# Get type tags
orig_idx = m.pos
tags = m.scan_until(@string_terminator)
m.pos = 4 * ((m.pos + 0.01)/4).ceil
if tags.length > 1
pack_string << "Z#{m.pos - orig_idx}"
end
# skip the comma
tags.each_char do |t|
case t
when @tag_separator
# skip the comma
nil
when @string_terminator
# skip the null bytes
nil
when @i_tag
# int32
m.pos = m.pos+4
pack_string << @cap_n
when @f_tag
# float32
m.pos = m.pos+4
pack_string << @low_g
when @s_tag
# string
orig_idx = m.pos
m.skip_until(@string_terminator)
m.pos = 4 * ((m.pos + 0.01)/4).ceil
pack_string << "Z#{m.pos - orig_idx}"
when @d_tag
# double64
m.pos = m.pos+8
pack_string << @cap_g
when @h_tag
# int64
m.pos = m.pos+8
pack_string << @q_lt
when @b_tag
# binary blob
orig_idx = m.pos
pack_string << @cap_n
blob_length = m.getch.unpack(@cap_n)[0]
m.skip(Regexp.new(".#{blob_length}"))
pack_string << "Z#{m.pos - orig_idx}"
m.pos = 4 * ((m.pos + 0.01)/4).ceil
else
raise "Unknown OSC type #{t}"
end
end
end
address, tags, *args = msg.unpack(pack_string)
[address, args]
end
end
end