Q-Sys OSC Unofficial Library

OSC implementation for QSC Q-Sys - Via UDP

Created by programmedav.com.au

Getting Started

Given that there is little information on the topic, i decided to create a general/basic guide for interacting with OSC devices via udp. Not all features are covered.

TLDR - just use the tool to create the code you need.

Loading the Library

osc = require("osc")

Creating a UDP Socket

udp = UdpSocket.New()

Opening a Socket

-- Bind to port (Sending and receiving on this port)
udp:Open(nil, 9000) 

-- Ephemeral port (system chooses)
udp:Open()

Core Functions

osc.encode(message)

Encodes an OSC message

msg = osc.encode({
  "/address/pattern",  -- OSC address
  "i",                 -- Type tag // i = integer -  More on this below
  42                   -- Integer value
})

osc.decode(data)

Decodes OSC data into a lua table

decoded = osc.decode(packet.Data)
-- Result format: {"/address/pattern", "i", 42}

Data Types

When encoding OSC messages (in Qsys), there are a few argument types:

Type Tag Description Example
Integer "i" 32-bit integer {"i", 42}
Float "f" 32-bit float {"f", 0.75}
String "s" Text string {"s", "hello-this-is-sean"}

Sending OSC Messages

Basic Send Example

osc = require("osc")
udp = UdpSocket.New()

-- Send a single float value
local msg = osc.encode({
  "/device/fader",  -- Address
  "f",              -- Type (float)
  0.75              -- Value
})

udp:Send("192.168.1.100", 8000, msg)

Multiple Arguments Example

local msg = osc.encode({
  "/mixer/channel",
  "i", 1,       -- First argument (integer)
  "f", 0.75,    -- Second argument (float)
  "s", "cmd_string"  -- Third argument (string)
})
udp:Send("192.168.1.100", 8000, msg)

Receiving OSC Messages

Basic Event Handler

osc = require("osc")
udp = UdpSocket.New()

-- Open socket
udp:Open(nil, 9000)

-- Handle incoming packets
udp.EventHandler = function(_, packet)
  -- Extract address and values
  local address = osc.get_addr_from_data(packet.Data)
  local values = osc.decode_message(packet.Data)
  
  print("Received OSC address:", address)
  for i, v in ipairs(values) do
    print("Value " .. i .. ":", v)
  end
end

Processing by Address

udp.EventHandler = function(_, packet)
  local address = osc.get_addr_from_data(packet.Data)
  local values = osc.decode_message(packet.Data)
  
  if address == "/fader/1" then
    -- Handle fader 1 messages
    local faderValue = values[1]
    -- Do something with the fader value
    
  elseif address == "/mute/1" then
    -- Handle mute 1 messages
    local muteState = (values[1] == 1)
    -- Do something with the mute state
  end
end

Common Patterns

Reopening Sockets When Port Changes

-- Create a function to refresh the socket
function refreshSocket(port)
  udp:Close()
  udp:Open(nil, port)
end

-- Call this whenever the port needs to change
refreshSocket(9000)

Filtering Messages by Source

udp.EventHandler = function(_, packet)
  -- Only handle messages from specific IP addresses
  if packet.Address == "192.168.1.100" then
    local address = osc.get_addr_from_data(packet.Data)
    local values = osc.decode_message(packet.Data)
    -- do the things you need to do
  end
end

Tips

Complete Example: Sending Basic OSC Commands

-- Example: Basic OSC command sender for controlling thiings 

-- Load the OSC library
osc = require("osc")

-- Create UDP socket
udp = UdpSocket.New()

-- Target device information
deviceIP = "192.168.1.100"
devicePort = 8000

-- Function to send a basic OSC command
function sendOscCommand(address, ...)
  -- Build arguments array with address as first element
  local args = {address}
  
  -- Add remaining arguments (type tags and values)
  for i = 1, select("#", ...) do
    table.insert(args, select(i, ...))
  end
  
  -- Encode OSC message
  local msg = osc.encode(args)
  
  -- Send message to the device
  udp:Send(deviceIP, devicePort, msg)
  
  print("Sent OSC message:", address)
end

-- Make sure socket is open before sending
udp:Open()

-- Examples of sending various OSC commands:

-- Example 1: Turn on a device (using integer)
sendOscCommand("/device/power", "i", 1)

-- Example 2: Set volume level (using float)
sendOscCommand("/device/volume", "f", 0.75)

-- Example 3: Send a preset name (using string)
sendOscCommand("/device/preset", "s", "Theater Mode")

-- Example 4: Send multiple parameters to a lighting cue
sendOscCommand("/lighting/cue", "i", 5, "f", 3.5, "s", "Blue Wash")

OSC Command Generator

Use this tool to generate generic OSC code for Q-Sys.

Generated Code:

Paste this into a script

-- Generated code will appear here