simple-osc-lib
v1.1.3
Published
Simple to use modern Open Sound Control implementation
Downloads
13
Readme
simple-osc-lib
This package provides some node.js utilities for working with OSC, a format for sound and systems control.
Here we implement the OSC 1.1 specification. OSC is a transport-independent protocol, so we don't provide any server objects, as you should be able to use OSC over any transport you like. The most common is probably udp, but tcp is not unheard of.
This package was heavily influenced by the osc-min API
Differences from osc-min
- no support for the message translation stuff
- no support for parameter guessing in oscBuildMessage() or oscBuildBundle()
- added
c
andr
data types - zero-dependency package
Input types
s
::string
- string value (String padded to 32 bit block with nulls)f
::float
- numeric value (FloatBE - 32 bits)d
::double
- numeric value (DoubleBE - 64 bits)i
::integer
- numeric value (Int32BE - 32 bits)b
::blob
- node.js Buffer value (Buffer padded to 32 bit block with nulls)T
::true
- no value (0 bits)F
::false
- no value (0 bits)N
::null
- no value (0 bits)I
::bang
- no value (0 bits)r
::color
- rgbA as an array [R(0-255),G,B,A] (4 x UInt8 - 32 bits)c
::char
- Character (Int32BE - 32 bits)t
::timetag
- numeric value (pair of UInt32BE - 64 bits)A
::address
- non-stander string value, with special processing to ensure a valid osc address string (String padded to 32 bit block with nulls)
Note that type
is always a string - i.e. "true"
rather than true
.
Standard Usage
Options
- asciiOnly false Prevent non-ASCII characters in strings
- blockCharacter "¦" Character to delineate 4-byte blocks in debug output (or '')
- coerceStrings false For string type, coerce input if non-string found.
- debugCharacter "•", Character to replace nulls in debug output
- preprocessor <function> osc-message processor
- strictAddress false Require leading slash (address is always asciiOnly)
- strictMode false Use strict mode elsewhere
const osc = require('simple-osc-lib')
const oscRegular = new osc.simpleOscLib( /* options */)
Build an OSC Single Message Buffer for sending
const buffer = oscRegular.buildMessage({
address : '/hello',
args : [
{ type : 'string', value : 'hi' },
{ type : 'string', value : 'there' },
],
})
Build an OSC Bundle Buffer for sending
const oscMessage1 = { address : '/hello', args : [...] }
const oscMessage2 = { address : '/goodbye', args : [...] }
// Generate a timetag half a second into the future
const timeTag = oscRegular.getTimeTagBufferFromDelta(0.5)
const buffer = oscRegular.buildBundle({
timetag : timeTag,
elements : [oscMessage1, oscMessage2],
})
Decode an OSC Packet from receiving
const oscMessage = oscRegular.readPacket(buffer)
Single message
{
type : osc-message,
address : /goodbye,
args: [
{ type : 'string', value : 'cruel' },
{ type : 'string', value : 'world' }
]
}
Bundle (note that bundles can be nested)
{
type : 'osc-bundle',
timetag : [ /* date Object */ ],
elements : [ /* zero or more osc-messages or osc-bundles */ ]
}
Return arguments
The type list above gives the text representation of what will appear in the type
field of the osc-message
. Additionally, arrays will be nested as such
{
type : osc-message,
address : /goodbye,
args: [
{ type : 'array', value : [
{ type : 'string', value : 'cruel' },
{ type : 'string', value : 'world' },
]},
]
}
timetag Processing of Bundles
This package provides no pre-processing for timetags - they are returned as found, in all circumstances. The OSC 1.1 spec does not clarify the proper handling of timetags in the past, as different implementations do different things with them. A timetag in the past may mean the bundle should be discarded, or it may mean it should be acted on immediately - this behavior is left to your preference. Please do not assume a received timetag refers to a future event.
Overriding default options
Options are set when initializing the class - they can be accessed or changed at any time by accessing the options
key.
- asciiOnly - Limit strings to ASCII characters.
- blockCharacter - Character to delineate 4-byte blocks in debug output (or '')
- debugCharacter - Character to replace NULLs in debug output.
- preprocessor - osc-message processor, take a single argument of an osc-message. Only run on individual messages.
- strictAddress - Use strict address mode (all string rules, must begin with slash).
- strictMode - Use strict mode.
By default, no strict mode options are enabled.
The preprocessor can be used as a callback for each message received. See section below for an example
"The code is more what you'd call 'guidelines' than actual rules." --– Barbossa, Pirates of the Caribbean
Exported Functions and Data Structures
Modules
simple-osc-lib
Simple OSC communication for nodeJS
- simple-osc-lib
- ~simpleOscLib
- new simpleOscLib(options)
- .encodeBufferChunk(type, value) ⇒ Buffer
- .decodeBufferChunk(type, buffer_in) ⇒ Object
- .getDateFromTimeTagArray(timetag) ⇒ Date
- .getTypeStringFromChar(type) ⇒ String
- .getTypeCharFromStringOrChar(type) ⇒ String
- .getTimeTagBufferFromTimestamp(number) ⇒ Buffer
- .getTimeTagBufferFromDate(date) ⇒ Buffer
- .getTimeTagBufferFromDelta(seconds, now) ⇒ Buffer
- .printableBuffer(buffer_in, rep_char, blockChar) ⇒ String
- .buildMessage(oscMessageObject) ⇒ Buffer
- .buildBundle(oscBundleObject) ⇒ Buffer
- .readPacket(buffer_in) ⇒ Object
- .readBundle(buffer_in) ⇒ Object
- .readMessage(buffer_in, options) ⇒ Object
- .redirectMessage(buffer_in, newAddress, callBack) ⇒
- .messageBuilder(address) ⇒
- ~simpleOscLib
simple-osc-lib~simpleOscLib
Kind: inner class of simple-osc-lib
- ~simpleOscLib
- new simpleOscLib(options)
- .encodeBufferChunk(type, value) ⇒ Buffer
- .decodeBufferChunk(type, buffer_in) ⇒ Object
- .getDateFromTimeTagArray(timetag) ⇒ Date
- .getTypeStringFromChar(type) ⇒ String
- .getTypeCharFromStringOrChar(type) ⇒ String
- .getTimeTagBufferFromTimestamp(number) ⇒ Buffer
- .getTimeTagBufferFromDate(date) ⇒ Buffer
- .getTimeTagBufferFromDelta(seconds, now) ⇒ Buffer
- .printableBuffer(buffer_in, rep_char, blockChar) ⇒ String
- .buildMessage(oscMessageObject) ⇒ Buffer
- .buildBundle(oscBundleObject) ⇒ Buffer
- .readPacket(buffer_in) ⇒ Object
- .readBundle(buffer_in) ⇒ Object
- .readMessage(buffer_in, options) ⇒ Object
- .redirectMessage(buffer_in, newAddress, callBack) ⇒
- .messageBuilder(address) ⇒
new simpleOscLib(options)
| Param | Type | Description | | --- | --- | --- | | options | object | simple-osc-lib options. | | options.asciiOnly | Boolean | Limit strings to ASCII characters. | | options.blockCharacter | String | Character to delineate 4-byte blocks in debug output (or '') | | options.debugCharacter | String | Character to replace NULLs in debug output. | | options.preprocessor | String | osc-message processor | | options.strictAddress | Boolean | Use strict address mode (all string rules, must begin with slash). | | options.strictMode | Boolean | Use strict mode. |
simpleOscLib.encodeBufferChunk(type, value) ⇒ Buffer
Encode an OSC Data chunk - low level function
Kind: instance method of simpleOscLib
Returns: Buffer - buffer padded to 32-bit blocks with NULLs
| Param | Type | Description | | --- | --- | --- | | type | String | OSC Data type string/char | | value | * | Value for data (must be null for null types) |
simpleOscLib.decodeBufferChunk(type, buffer_in) ⇒ Object
Decode an OSC Data chunk - low level function
Kind: instance method of simpleOscLib
Returns: Object - Contains the type, value, and unused portion of the buffer
| Param | Type | Description | | --- | --- | --- | | type | String | OSC Data type | | buffer_in | Buffer | buffer padded to 32-bit blocks with NULLs |
simpleOscLib.getDateFromTimeTagArray(timetag) ⇒ Date
Get a date object from a timetag array
Kind: instance method of simpleOscLib
| Param | Type | Description | | --- | --- | --- | | timetag | Array | 2 element array for a timetag [unix seconds, fractional seconds] |
simpleOscLib.getTypeStringFromChar(type) ⇒ String
Resolve a character type into the human readable name
Kind: instance method of simpleOscLib
| Param | Type | Description | | --- | --- | --- | | type | String | single character type |
simpleOscLib.getTypeCharFromStringOrChar(type) ⇒ String
Resolve a type from a character or string with error checking
Kind: instance method of simpleOscLib
| Param | Type | Description | | --- | --- | --- | | type | String | character type string or single character |
simpleOscLib.getTimeTagBufferFromTimestamp(number) ⇒ Buffer
Generate a timetag buffer from a timestamp
Kind: instance method of simpleOscLib
Returns: Buffer - 8 byte / 32 bit buffer
| Param | Type | Description | | --- | --- | --- | | number | Number | timestamp (from epoch) |
simpleOscLib.getTimeTagBufferFromDate(date) ⇒ Buffer
Generate a timetag buffer from a date instance
Kind: instance method of simpleOscLib
Returns: Buffer - 8 byte / 32 bit buffer
| Param | Type | Description | | --- | --- | --- | | date | Date | javascript date instance |
simpleOscLib.getTimeTagBufferFromDelta(seconds, now) ⇒ Buffer
Generate a timetag buffer for [seconds] in the future
Kind: instance method of simpleOscLib
Returns: Buffer - 8 byte / 32 bit buffer
| Param | Type | Default | Description | | --- | --- | --- | --- | | seconds | Number | | seconds in the future | | now | Number | null | | point to calculate from (in ms!!) |
simpleOscLib.printableBuffer(buffer_in, rep_char, blockChar) ⇒ String
Format a buffer for console.log()
Kind: instance method of simpleOscLib
| Param | Type | Description | | --- | --- | --- | | buffer_in | Buffer | buffer | | rep_char | String | Character to replace nulls in buffer | | blockChar | String | Character to delineate 4-byte blocks in buffer (or '') |
simpleOscLib.buildMessage(oscMessageObject) ⇒ Buffer
Build an OSC message buffer
address
is a required key, containing the destination address
args
is an array of objects of { type : 'type', value : value }
Kind: instance method of simpleOscLib
Returns: Buffer - 4 byte chunked buffer
| Param | Type | Description | | --- | --- | --- | | oscMessageObject | object | osc message object |
simpleOscLib.buildBundle(oscBundleObject) ⇒ Buffer
Build an OSC bundle buffer
timetag
is a required key, containing a timetag buffer
elements
can contain objects to be passed to oscBuildMessage or
pre-prepared buffers padded to 32-bit blocks with NULLs
Kind: instance method of simpleOscLib
Returns: Buffer - 4 byte chunked buffer
| Param | Type | Description | | --- | --- | --- | | oscBundleObject | object | osc bundle object |
simpleOscLib.readPacket(buffer_in) ⇒ Object
Decode an OSC packet. Useful for when the client might send bundles or messages
Kind: instance method of simpleOscLib
Returns: Object - osc-bundle object or osc-message object
| Param | Type | Description | | --- | --- | --- | | buffer_in | Buffer | buffer padded to 32-bit blocks with NULLs |
simpleOscLib.readBundle(buffer_in) ⇒ Object
Decode an OSC bundle
Kind: instance method of simpleOscLib
Returns: Object - osc-bundle object
| Param | Type | Description | | --- | --- | --- | | buffer_in | Buffer | buffer padded to 32-bit blocks with NULLs |
simpleOscLib.readMessage(buffer_in, options) ⇒ Object
Decode a single OSC message.
Kind: instance method of simpleOscLib
Returns: Object - osc-message object
| Param | Type | Description | | --- | --- | --- | | buffer_in | Buffer | buffer padded to 32-bit blocks with NULLs | | options | Object | options | | options.strictMode | Object | use strict mode | | options.messageCallback | Object | callback to run on each message |
simpleOscLib.redirectMessage(buffer_in, newAddress, callBack) ⇒
Readdress an existing message, including the old address as the first or last string argument
Callback details
The callback takes a function that receives the following parameters
- newAddressBuffer new destination
- oldAddressBuffer original address as a string buffer
- argumentList original argument list
- argumentBuffer existing argument buffer.
This should return a valid osc buffer. To simply redirect the existing to a new address you could do something like
function redirectCallback(newAddressBuffer, _oldAddressBuffer, argumentList, argumentBuffer) {
return Buffer.concat([
newAddressBuffer,
oscLibInstance.encodeToBuffer('s', `,${argumentList.join('')}`),
argumentBuffer
])
}
Kind: instance method of simpleOscLib
Returns: Buffer
| Param | Type | Description | | --- | --- | --- | | buffer_in | Buffer | original message buffer | | newAddress | String | address for the new message | | callBack | function | callback to apply - must return a buffer |
simpleOscLib.messageBuilder(address) ⇒
Build an osc message in a chainable way.
Chainable methods available - for more complex messages, use buildMessage
myMessage
.i(20)
.integer(20)
.f(1.0)
.float(1.0)
.s('hello')
.string('world')
.b(buffer)
.blob(buffer)
To get a transmittable buffer, call myMessage.toBuffer()
To get a human readable version of the buffer, call myMessage.toString()
Kind: instance method of simpleOscLib
Returns: oscBuilder instance
| Param | Type | Description | | --- | --- | --- | | address | String | address to send to |
Example
const myBuffer = oscLib.messageBuilder('/hello').integer(10).float(2.0).string('world').toBuffer()
simple-osc-lib/x32
Extended processing for Behringer X32/M32 consoles. This provides some override and pre-processing tools to make it easier to work with the style of OSC messages the Behringer uses.
- simple-osc-lib/x32
- ~x32PreProcessor
- ~dB2Float ⇒ Number
- ~float2dB ⇒ String
simple-osc-lib/x32~x32PreProcessor
Kind: inner class of simple-osc-lib/x32
new x32PreProcessor(options)
| Param | Type | Description | | --- | --- | --- | | options | object | x32 Preprocessor options. | | options.activeNodeTypes | Boolean | Active node message preprocessors from lib/x32_preprocessors (or 'all') | | options.activeRegularTypes | String | Active regular message preprocessors from lib/x32_preprocessors (or 'all') |
x32PreProcessor.readMessage(oscMessage) ⇒ Object
This is the processor for X32 style messages
Kind: instance method of x32PreProcessor
Returns: Object - an OSC message object with additional data
| Param | Type | Description | | --- | --- | --- | | oscMessage | Object | an OSC message object |
Example
const osc = require('simple-osc-lib')
const osc_x32 = require('simple-osc-lib/x32')
const x32Pre = new osc_x32.x32PreProcessor('all')
// or a list of types or wildcards.
// + dca*, bus*, mtx*, main*, mono*, show*, aux*, chan*
// + dcaLevel, dcaName, dcaMix, dcaMute etc.
// see source for full listing.
const oscRegular = new osc.simpleOscLib({
preprocessor : (msg) => x32Pre.readMessage(msg),
})
simple-osc-lib/x32~dB2Float ⇒ Number
Convert a string or number decibel representation to a floating point number
Kind: inner constant of simple-osc-lib/x32
Returns: Number - floating point representation of decibel level 0->1
| Param | Type | Description | | --- | --- | --- | | db_in | String | Number | string or float representation of decibel level +10->-90 |
simple-osc-lib/x32~float2dB ⇒ String
Convert floating point 0->1 to decibel level
Kind: inner constant of simple-osc-lib/x32
Returns: String - text level [+/-##.# dB]
| Param | Type | Description | | --- | --- | --- | | f | Number | 0->1 floating point level |
X32 Pre-Processing
The X32 version of the oscMessage processor adds some additional data, found in the props
key.
Additionally the wasProcessed
will be set true if the message was covered.
The following example enables all current X32 extra processing - if you are only interesting in a subset you can set to only process what you care about.
Example
const osc = require('simple-osc-lib')
const osc_x32 = require('simple-osc-lib/x32') // X32 specific processing (optional)
// 'all', or a single entry 'dca*', or an array of entries ['dca*', 'bus*', 'show*']
// see source file for full listing
const x32Pre = new osc_x32.x32PreProcessor('all')
const oscRegular = new osc.simpleOscLib({
preprocessor : (msg) => x32Pre.readMessage(msg),
})
Standard OSC Messages by subtype
- showCurrent :: /-show/prepos/current
- showMode :: /-prefs/show_control
- auxLevel :: /auxin/[##]/mix/fader
- auxMute :: /auxin/[##]/mix/on
- auxName :: /auxin/[##]/config/name
- busLevel :: /bus/[##]/mix/fader
- busMute :: /bus/[##]/mix/on
- busName :: /bus/[##]/config/name
- chanLevel :: /ch/[##]/mix/fader
- chanMute :: /ch/[##]/mix/on
- chanName :: /ch/[##]/config/name
- dcaLevel :: /dca/[#]/fader
- dcaMute :: /dca/[#]/on
- dcaName :: /dca/[#]/config/name
- mainLevel :: /main/st/mix/fader
- mainMute :: /main/st/mix/on
- mainName :: /main/st/config/name
- monoLevel :: /main/m/mix/fader
- monoMute :: /main/m/mix/on
- monoName :: /main/m/config/name
- mtxLevel :: /mtx/[##]/mix/fader
- mtxMute :: /mtx/[##]/mix/on
- mtxName :: /mtx/[##]/config/name
- showCueDirty :: /-show/showfile/cue/[#]{3}//
- showSceneDirty :: /-show/showfile/scene/[#]{3}/name
- showSnippetDirty :: /-show/showfile/snippet/[#]{3}/name
node
OSC Messages by subtype
- auxMix :: node /auxin/[##]/mix
- auxName :: node /auxin/[##]/config
- busMix :: node /bus/[##]/mix
- busName :: node /bus/[##]/config
- dcaMix :: node /dca/[#]
- dcaName :: node /dca/[#]/config
- chanMix :: node /ch/[##]/mix
- chanName :: node /ch/[##]/config
- mtxMix :: node /mtx/[##]/mix
- mtxName :: node /mtx/[##]/config
- mainMix :: node /main/st/mix
- mainName :: node /main/st/config
- monoMix :: node /main/m/mix
- monoName :: node /main/m/config
- showCurrent :: node /-show/prepos/current
- showMode :: node /-prefs/show_control
- showName :: node /-show/showfile/show
- showCue :: node /-show/showfile/cue/[###]
- showScene :: node /-show/showfile/scene/[###]
- showSnippet :: node /-show/showfile/snippet/[###]
Example Post-Process - Faders
These items will exist in the returned osc-message under the props
key. The props.subtype
rule will be the name of the operation matched.
const all = {
index : 1,
subtype : 'someLevel', // operation name
zIndex : '01', // fader text index, 1-8 for dca, 01-?? for all else
}
const someLevel = {
...all,
level : {
float : 0.75,
db : '0 dB',
},
}
const someMute = {
..all,
isOn : {
bool : false,
int : 0,
text : 'OFF',
}
}
const someName = {
...all,
name : 'NAME',
}
const someMix = {
...all,
...someMute,
...someLevel,
}
Example Post-Process - Show Control
These items will exist in the returned osc-message under the props
key. The props.subtype
rule will be the name of the operation matched.
const showMode = {
index : 0,
name : 'CUES',
}
const showCue = {
cueNumber : '1.0.0',
cueScene : -1,
cueSkip : false,
cueSnippet : -1,
index : 1,
name : 'Cue Name',
}
const showScene = {
index : 1,
name : 'Scene Name',
note : 'Scene Note',
}
const showSnippet = {
index : 1,
name : 'Snippet Name',
},
// showCueDirty, showSceneDirty and showSnippetDirty
// all have empty properties.
OSC Redirection
You can use the redirectMessage()
function to redirect OSC messages without processing the arguments. This is particularly useful if you need to deal with unknown types. By default, the original address is included as the first argument of type string
const newBuffer = oscLib.redirectMessage(originalBuffer, '/newTown')
It also takes a callback - this example rewrites the first argument, an integer to be the square of that integer, and includes the original address as a second argument. Any additional original arguments would be discarded. Not included in this sample is error checking
// square the first argument which is an integer, add the address to the end
function redirectCallback (bufferNewAddress, bufferOldAddress, argList, argBuffer) {
// Arguments:
// - bufferNewAddress is an 4-byte padded buffer from the address you provided
// with all of the usual error checking.
//
// - bufferOldAddress is a 4-byte padded buffer from the original address.
//
// Note: this is re-encoded from text, so the usual error checking
// on the original address will take place. If for some reason
// you need this to not occur, consider using the low-level
// functions directly.
//
// - argList is an Array of the original argument list. It is not checked for
// array nesting errors *or* valid type definitions. The leading comma
// from the input buffer is stripped, but in strict mode a lack of that
// comma will throw an error.
//
// - argBuffer the original unaltered argument buffer. In practice, this is the
// message packet minus `bufferOldAddress` and a 4-byte padded buffer from
// the original argument list.
// really, we should check that the first item of argList is "i", the integer
// type, and run catch blocks on both this decodeBufferChunk() and the
// below encodeBufferChunk()
const firstArgument = oscLib.decodeBufferChunk('i', argBuffer)
// return a valid OSC buffer. simple-osc-lib does not look at this value
// during the redirectMessage process, so you could fail to return a buffer
// or return nothing at all.
//
// This function could potentially be used for out-of-band processing to read
// and act on message packets that simple-osc-lib doesn't understand, or you
// wish to handle yourself.
return Buffer.concat([
bufferNewAddress,
oscLib.encodeBufferChunk('s', ',is'),
oscLib.encodeBufferChunk('i', firstArgument.value * firstArgument.value),
bufferOldAddress
])
}
const newBuffer = oscLib.redirectMessage(originalBuffer, '/newTown', redirectCallback)
This function could potentially be used for out-of-band processing to read and act on message packets that simple-osc-lib doesn't understand, or you wish to handle yourself - although it does do error checking on the original address. For help in handling the message completely on your own, you will need to look at .decodeBufferChunk()
and .encodeBufferChunk()
© 2024 J.T.Sage - ISC License