REF / Music API / OSC

Ascene . Agraph . Aengine
Anode . Modulator . Envelope
Songbook . Song . TimeKeeper
Voices . SampleMgr . StreamMgr
Loader . Async . Plot
Random . Rhythm . Util
MIDI . OSC . HID . Piano
Music Theory with Tonal

right-click to navigate to page sections


OSC refers to the Open Sound Control network protocol. You can use it to collaborate with other programs on your machine or within your local network. There are a number of slick apps for iPad and desktop for designing control surfaces and these should readily communicate to your programs.

OSC is more expressive than MIDI since you can design your own messaging patterns. Because of this blank page you may find it a little daunting. The good news is: easy things are easy!

OSCIn API

Use the OSCIn class to install one or more listeners for OSC activity. As a listener you need only specifiy the OSC port. The port represents a meeting point between sender and listener. By default Hz's OSC listening port is port 22000, but you can initialize your OSCIn instance to listen on any legal and available port.

let oin = new OSCIn();

Method Description
async Connect(port=0) Attempt to listen on the requested port. 0 means use default.
Disconnect() Cleanup the prior connection.
async Recv() Wait for activity on the Connected port. Returns an OSC msg.
Subscribe(callback) Install a callback for OSC messages, alternative to Recv.
AddMsgFilter(filter, invert=false) Installs a msg address filter. Only messages with a matching address will trigger activity.
AddIPFilter(filter, invert=false) Installs a msg IP filter. Only messages that come from matching IP addresses will trigger activity.
ClearSubs() Clears installed subscriptions.
ClearMsgFilters() Clears installed msg address filters.
ClearIPFilters() Clears installed msg IP filters.

Example

let oin = new OSCIn(); 
let err = await oin.Connect(); // listen on default port (22000)
if(!err)
{
    console.info("oin connected");
    for(let i=0;i<30;i++)
    {
        let msg = await oin.Recv();
        console.log(`${i} OSC msg: ` + JSON.stringify(msg));
    }
    console.info("oin done after 30 messages");
    oin.Disconnect();
}
else
    console.warn("Problem listening on default OSC port.");

Here's a portion of the output:

15:25 info oin connected
15:25 info 0 OSC msg: {"addr":"/foo/notes","types":"if","origin":"192.168.1.113","data":[47,0.445024]}
15:25 info 1 OSC msg: {"addr":"/foo/notes","types":"if","origin":"192.168.1.113","data":[42,0.399837]}
15:25 info 2 OSC msg: {"addr":"/foo/notes","types":"if","origin":"192.168.1.113","data":[34,0.392982]}

OSCOut API

let oout = new OSCOut(hostip, port);

Method Description
SetTarget(hostip, port) Changes the target host and port for ensuing messages.
StartMsg(addr) The first step in constructing an OSC message.
AddValue(value, type=null) Append a new value to the current OSC message. We infer the datatypes f, i, s but you can override it.
SendMsg() Completes and sends the OSC msg initiated by StartMsg and popluated by AddValue.

Example

// This script sends dota from Hz to separate
// processes on the desired host(s) listening on specified port. 
// If no process is listening or the host is invalid, the script 
// still runs but has no effect other than network pollution.
// Here we "spawn" three separate fibers and communicate differently
// with each target.

let scene = await Ascene.BeginFiber(this);

let sDefaultCfg = {host:"localhost", port:10000, loops:10, notedur:.25};

async function go(cfg = {})
{
    let lcfg = Object.assign({}, sDefaultCfg, cfg);
    let {host, port, loops, notedur} = lcfg;
    let ipaddress;
    if(host.match(/[a-zA-z]/) && host != "localhost")
    {
        // DNS lookups can take some time
        ipaddress = await OSCUtil.GetHostAddr(host, port);
        console.log(`${host} -> ${ipaddress}`);
    }
    else
        ipaddress = host;
    let oout = new OSCOut(ipaddress, port);
    let tgt = `${host}:${port}`;
    let dur = scene.Seconds(notedur);
    for(let i=0;i<loops;i++)
    {
        oout.StartMsg("/foo/note")
        oout.AddValue(40+i); // the MIDI key
        oout.AddValue(.33 * i); // the velocity
        oout.AddValue(tgt);
        oout.SendMsg();
        console.info(`send ${tgt} ${i}/${loops}`);
        await scene.Wait(dur)
    }
}

let promises = [];
// promises.push(Async.Run(go, {host:"255.255.255.255", notedur:1})); // permission denied
promises.push(Async.Run(go, {host:"limpet.local", notedur:1}));
promises.push(Async.Run(go, {host:"localhost", notedur:.1}));
promises.push(Async.Run(go, {host:"192.168.1.113", notedur:.5}));
await Promise.all(promises);
console.log("done");

OSCUtil API

This module collections functions that may be useful in your OSC endeavors.

Method Description
async GetHostAddr(host, port) returns a promise that resolves to the IP address for host. This is useful since OSCOut requires an ip address.
GetOSCTimestamp(date=null) returns a bigint in a form suitable for OSC's t message type. date is an optional JavaScript Date object. If not provided we use the current time.
GetOSCTimestampAsDate(oscTs) returns a JavaScript date object approximating the higher resolution OSC timestamp.
GetOSCDelayMs(oscTs) returns an approximate delay, in ms, between the current system time and the target time expressed as an OSC timestamp. Returns 0 if we missed the memo.

OSC Lua API

Recv methods

method description
OSC.Recv:new(cfg) constructs instance of OSC.Recv class. Optional cfg can include port.
r:recv() blocks until an OSC message is available
r:addMsgFilter(filter, invert)
r:clearMsgFilters()
r:addIPFilter(filter, invert)
r:clearIPFilters()
r:done()

Send methods

Produce OSC packets for consumption by another coroutine, process or computer.

method description
OSC.Send:new(cfg) constructs instance of OSC.Send class. Optional cfg can include port and host
s:start(addr) initiate creation of a new OSC packet.
s:add(val, [t]) append a new value to the current packet. If t ('i', 'f', 's', …) isn't provided, it is inferred.
s:send()
home .. topics .. interface .. reference .. examples .. tipjar