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.

Revisions

  1. xavriley created this gist Oct 4, 2015.
    6 changes: 6 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,6 @@
    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
    ```
    119 changes: 119 additions & 0 deletions oscdecode.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,119 @@
    #--
    # 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