Sending OSC blobs

Hi there,

I am trying to control DMX lights via OSC from Sonic Pi. I have installed the OLA daemon and I manage to get my OSC messages through, like so:

use_osc "localhost", 7770
osc "/dmx/universe/1/25", 250

This sets a single slot to a specific value. There is basically one problem: DMX is very bad at handling messages this way. In order to control one device several slots have to be set, I want to control several devices, and the messages have to be padded, resulting in very many and very inefficient datagrams. It’s best to send messages to DMX devices as blobs. OSC supports blobs, and I gather so did Sonic Pi. In the code I find 2 significant references to OSC. The change log says for the 3.2.0 ‘Tau’ release: »The osc fn now forces all outgoing args to either be numbers or strings (binary blobs and timestamps are not supported at this point).« So, I understand there had been support for blobs before and dropping it was intended.

Then, osc.erl does seem to carry all that is needed to encode blobs. There is encode_binary and a switch for the data type with a when that calls it (encode_arg(X) when is_binary(X) -> encode_binary(X).). However, I complete fail to see if this is still the code that is actually used. It is part of the Tau stuff in the end. Also, I wouldn’t know how to modify it to get OSC support back.

Any hints on how to go about getting OSC blobs to work are very welcome. I’d also appreciate any suggestion for code modifications. Especially, if there is a specific reason for dropping OSC blobs from SP then I’ll be happy to make some (suggested) modifications and build my own.

Also, have I chosen the wrong approach? Is there a better way to control DMX from SP, maybe just writing to /dev/ttyUSB?

Finally, I’d welcome suggestions on how to create byte arrays in SP. I wonder if something like this is the right way: [112, 220, 17].pack('C*').

Ben

So, after hours of fiddling and messing around with debug messages, the solution was really quite simple. Sonic Pi has an OSC blob data type that simply does the trick.

oBlob = SonicPi::OSC::Blob.new([255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0].pack('C*'))
osc "/dmx/universe/1", oBlob

The blob type takes a string as argument, but if one wants to start from numeric values this can be conveniently created from an array with the pack method. The SonicPi::OSC::Blob type formats the string data to fulfill 2 requirements for OSC blobs:

  1. Tell the payload length in a 32-bit integer at the start
  2. Meet 32-bit boundaries

oscencode.rb then properly recognises the type and and prefixed the data with a »b«, so OSC can treat it like a blob type.

Sonic Pi is really great for controlling lights! For reference, just in case someone is interested, here is my setup:

  • Ubuntu 22.04, using the stock ola package
  • An Enttec Open DMX USB interface; used the FTDI driver, using the config from here
  • Set up a DMX universe using the OLA web UI running on http://localhost:9090, configuring OSC as the input port and the Enttec (showing as FT232R USB UART) as output
  • Connected some PAR DMX spots, configured them to use 6 DMX channels

With 2 spots for testing connected, the following would flash them synchronously with the snare sound:

live_loop :dmx do

    sample :sn_dolf

    channels = [
        rand_i(255), rand_i(255), rand_i(255), 0, 0, 0,
        rand_i(255), rand_i(255), rand_i(255), 0, 0, 0,
    ]

    osc "/dmx/universe/1", SonicPi::OSC::Blob.new(channels.pack('C*'))

    sleep 0.125

    channels = Array.new(12) { |z| 0 }

    osc "/dmx/universe/1", SonicPi::OSC::Blob.new(channels.pack('C*'))

    sleep 1
end
1 Like