SkillAgentSearch skills...

OLvosc

oLvosc is an Open Sound Control module for Lua

Install / Use

/learn @GModal/OLvosc
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

oLvosc

oLvosc is an OSC ( Open Sound Control ) module for Lua and LÖVE. It's in two parts:

  • oLvosc
    • The main OSC module, which works in Lua (V5.3 or greater) or LÖVE (tested on 11.3, some testing on 11.4)
  • oLvoscT
    • The threaded server module, for LÖVE (tested on 11.3) only

Both modules require the Lua 'socket' library.

About OSC

Open Sound Control is a networking protocol designed to enable communication between multimedia devices, musical instruments, synthesizers, effects and recording equipment (DAWs). It was intended to suppliment (maybe supplant?) MIDI hardware and protocol, and is generally much quicker.

OSC is a wireless protocol, and uses UDP packets over ethernet networks, but can work via USB and also between software applications on a host computer.

Because it utilizes UDP, OSC is not hardened for critical operations. UDP messages are send, but delivery is not guaranteed. Still, for musical performance data it works well.

About oLvosc

tinaV1 pic

The oLvosc module is a 'pure Lua' library for encoding, decoding, sending and receiving OSC messages. It's reasonably fast (but not as quick as a compiled library).

The server routine is non-blocking, a less-reliable approach in a complex Lua script.

oLvosc supports a large sub-set of OSC protocols. As of 0.2.4, Bundles are supported.

The following types are currently supported:

  • s : string
  • S : synonym for 's' (or symbol)
  • c : a char (32 bit int)
  • i : int (32-bit)
  • r : rgb (another 32-bit int)
  • m : MIDI data, four bytes: channel, status, d1, d2
  • t : TIME tag, two 32 ints: seconds, fraction of seconds
  • f : float (32-bit)
  • b : BLOB data, binary bytes
  • h : signed int (64-bit)
  • d : double float (64-bit)

The following have NO data block (but are DEcoded to a string: 'NIL', 'TRUE', etc...

  • N : NIL
  • T : TRUE
  • F : FALSE
  • I : IMPULSE (was INFINITUM)
  • [ : Array begin
  • ] : Array end

Some of these (notably the array brackets) are experimental, as there's no universally-accepted interpretation for their usage. The 'c' tag (char) is also subject to interpretation. Here it is implemented as a utf8 unicode char. Other libraries limit the char to ASCII.

About oLvoscT

The oLvoscT module is a LÖVE-only OSC threaded server. It runs concurrently with the main LÖVE script, and pushes incoming OSC packets into a queue.

As a separate thread it is more reliable than the standard non-blocking OSC server in oLvosc.

Demos, LÖVE & Lua

The docs for the console-only Lua demos is here.

A brief synopsis of the LÖVE demos is available on the oLvgui repo (not all demo feature OSC) and the demo source code also.

oLvosc Network Functions

client_socket = oLvosc.cliSetup(address_string, port#)

  • Creates a new client (sending) socket

nil = oLvosc.sendOSC(client_socket, packet)

  • Send an OSC packet to the client socket

server_socket = oLvosc.servSetup(address_string, port#)

  • Creates a new non-blocking server (receiving) socket

packet = oLvosc.oscPoll(server_socket)

  • Poll the non-blocking server
    • Do this often, or incoming data is lost

nil = oLvosc.close(any_socket_type)

  • Closes any open socket

oLvosc Data Functions

packet = oLvosc.oscPacket(address_str, type_str, data_table)

  • Encodes an address string, a type string and a table of data into a UDP packet
    • packet is ready for sending ( oLvosc.sendOSC(server, packet) )

address_str, type_str, data = oLvosc.oscUnpack(packet)

  • Decodes a received UDP packet into:
    • an address string
    • a type string
    • a data sub-packet

table = oLvosc.oscDataUnpack(type_str, data)

  • Decodes a data sub-packet into a table of usable data
    • the type string is the 'key' to the packed data
    • skip this step if the incoming type_str is nil -- in that case, there is no data

string = oLvosc.oscAddrCmd(address_str)

  • Returns the last portion of the address string (after the last forward slash)
  • many applications send ONLY the address string -- the string is the data

string_table = oLvosc.oscAddrSplit(address_str)

  • Splits the address string into it's atoms ('/' is the separator)

midi_packet = oLvosc.packMIDI(mChannel, mStatus, mByte1, mByte2)

  • Encodes the four bytes of an OSC 'MIDI' message into a sub-packet

mChannel, mStatus, mByte1, mByte2 = oLvosc.unpackMIDI(midi_packet)

  • Decodes a MIDI sub-packet into the four original bytes

blob_size, blob_data = oLvosc.unpackBLOB(blob)

  • Decodes a blob to an integer (size of data), and a data block

  • Time functions

time_secs, time_fraction, fraction_as_float, epoch_time = oLvosc.time()

  • Returns the current time (OSC, which is NPT time) as:
    • Time in seconds since Jan 1, 1900 (32 bit integer)
    • Additional fraction of second (32 bit integer)
    • That fraction as a float
    • The current time as Unix 'Epoch' time

Epoch_time = oLvosc.NTPtoEPOCH(sec, frac)

  • <sec><frac>: the seconds, fractions of a second of NTP time
  • Returns that time in Epoch time

time_secs, time_fraction, float fraction = function oLvosc.EPOCHtoNTP(epoch)

  • <epoch> : time in Epoch format
  • Returns NTP time : <time_secs>, <time_fraction> and the fraction as a float

time_packet = oLvosc.packTIME(time_seconds, time_fraction)

  • Encodes time in seconds, fractions of seconds (both integers)
    • 'OSC timetag' is the same as NTP time: time since Jan 1, 1900
  • Returns a packed 8-byte data blk of OSC (NTP) format time

time_secs, time_fraction = oLvosc.unpackTIME(tPack)

  • Takes a timetag binary packet
  • Returns time in seconds, fractions of seconds
  • Time Constants:

timetag = oLvosc.TT_IMMEDIATE**

  • This constant creates an OSC "immediate time tag", a part of the OSC 1.0 bundle spec.
  • It's interpreted as NOW.

oLvosc Bundle Support

As of version 0.2.4., oLvosc now supports bundles! Bundles can be nested per the OSC 1.0 spec. The current code does this recursively -- see oLvosc.addBundleToBundle(). This works for both decoding and creating bundles.

Thanks to Github user halee88 for contributing the initial bundle unpacking function.

Bundle Packet Decoding

  • Functions:

The oLvosc.oscUnpackBundle() function returns a bundle "tree" structure. This table includes all the embedded message and timetag data, and also a "level" value, which indicates how deeply "nested" the bundle is within the tree.

timetag = oLvosc.isBundle(packet)

  • Returns an oLvosc timetag if the packet is a bundle, nil if not.

bundle_tree = oLvosc.oscUnpackBundle(packet [, level])

  • Returns a bundle in a "tree" structured table.
  • <level> sets the starting level of the tree. It's optional when called, as it's real use is to recursively find the current depth of the nested data.

bundle_list = oLvosc.bundleResultsToList(bundle_tree [, filter])

  • Returns a "flattened" version of the results of oLvosc.oscUnpackBundle(). I.E., it converts the tree structure to a flat list.
  • The "level" information is still retained in the list.
  • <filter> set the type of information returned in the list:
    • <'b'> : returns only bundle info
    • <'m'> : returns only messages

Bundle Creation

The bundle packet creation routines are described below. This code has some error checking, but it's a still a light-weight implementation... DON'T create circular references (like adding a bundle to itself).

Before generating a bundle packet, use these function to build a bundle "data table," which is a simple tree structure table. This table is NOT identical to the tree structure returned by oLvosc.oscUnpackBundle().

  • Constants:

timetag = oLvosc.TT_IMMEDIATE

  • This constant creates an OSC "immediate time tag", a part of the OSC 1.0 bundle spec.
  • It's interpreted as NOW.
  • Functions:

bundle_dt = oLvosc.oLvosc.newBundle(oscTimetag)

  • Creates a new, empty bundle_dt (data table), and sets the timetag.

A bundle_dt is a data table which holds the bundle structure & data, pre-build. A bundle osc packet is built from this preliminary information & framework.

bool = oLvosc.addMsgToBundle(bundle_dt, msg_packet)

  • Add an osc Msg packet to a bundle_dt
  • if rval == false, the bundle is locked (or the bundle is invalid).

nil = oLvosc.addBundleToBundle(parentBundle, childBundle)

  • No additional data can be added to the child (sub-bundle) after this operation.
  • Once added, the child bundle is locked, and addMsgToBundle() won't function on the child.

Therefore, sub-bundles should be fully populated before adding to a parent bundle. However, additional elements (msgs & bundles) can be added to the parent bundle.

bundle_packet = oLvosc.oscBundlePack(bundle_dt)

  • Generate a transmissible bundle packet from a populated bundle_dt.
  • Sending bundles

Bundles packets are sent like any other OSC packet -- with oLvosc.sendOSC()

Example of a bundle structure (formatted)

Below is a screen capture of a bundle-formatted display in oscMonD (in LÖVE), illustrating the tree structure of this bundle.

This is very similar to the output of other formatting helpers in the utility scripts. The oscdump.lua script creates a very similar display in a shell.

tinaV1 pic

(The osc bundle was generated randomly with the sendrandpacket.lua script.)

Bundle Support routines:

While not built into the oLvosc library module, there are useful utility routines in the demo folder. They include functions to:

Related Skills

View on GitHub
GitHub Stars8
CategoryDevelopment
Updated9mo ago
Forks1

Languages

Lua

Security Score

82/100

Audited on Jun 8, 2025

No findings