Permalink
Browse files

Support for array arguments, version 0.11

closes #6
  • Loading branch information...
1 parent c6456cc commit bfa16b5a8add0f08fb7ed05c9f44d20bb8e1a4dc @russellmcc committed Sep 2, 2013
Showing with 149 additions and 34 deletions.
  1. +3 −1 lib/index.js
  2. +63 −17 lib/osc-utilities.coffee
  3. +2 −2 package.json
  4. +9 −7 readme.md
  5. +72 −7 test/test-osc-utilities.coffee
View
4 lib/index.js
@@ -63,6 +63,7 @@
// + `null` - no value
// + `bang` - no value (this is the `I` type tag)
// + `timetag` - numeric value
+// + `array` - array of _OSC Arguments_
//
// Note that `type` is always a string - i.e. `"true"` rather than `true`.
//
@@ -72,7 +73,8 @@
//
// For messages sent to the `toBuffer` function, `type` is optional.
// If the argument is not an object, it will be interpreted as either
-// `string`, `float`, or `blob`, depending on its javascript type.
+// `string`, `float`, `array` or `blob`, depending on its javascript type
+// (String, Number, Array, Buffer, respectively)
//
// + An _OSC Bundle_ is represented as a javascript object with the following layout
//
View
80 lib/osc-utilities.coffee
@@ -331,8 +331,31 @@ exports.fromOscMessage = (buffer, strict) ->
types = types[1..(types.length)]
args = []
+
+ # we use this to build up array arguments.
+ # arrayStack[-1] is always the currently contructing
+ # array.
+ arrayStack = [args]
+
# grab each argument.
for type in types
+ # special case: we're beginning construction of an array.
+ if type is '['
+ arrayStack.push([])
+ continue
+
+ # special case: we've just finished constructing an array.
+ if type is ']'
+ if arrayStack.length <= 1
+ throw new StrictError "Mismatched ']' character." if strict
+ else
+ built = arrayStack.pop()
+ arrayStack[arrayStack.length-1].push(
+ type: 'array'
+ value: built
+ )
+ continue
+
# by the standard, we have to ignore the whole message
# if we don't understand an argument
typeString = exports.oscTypeCodeToTypeString type
@@ -345,11 +368,13 @@ exports.fromOscMessage = (buffer, strict) ->
buffer = arg.rest if arg?
# add it to the list.
- args.push(
+ arrayStack[arrayStack.length-1].push(
type : typeString
value : arg?.value
)
+ if arrayStack.length isnt 1 and strict
+ throw new StrictError "Mismatched '[' character"
{address : address, args : args, oscType : "message"}
#
@@ -381,6 +406,39 @@ exports.fromOscPacket = (buffer, strict) ->
else
exports.fromOscMessage buffer, strict
+# helper - is it an argument that represents an array?
+getArrayArg = (arg) ->
+ if IsArray arg
+ arg
+ else if (arg?.type is "array") and (IsArray arg?.value)
+ arg.value
+ else
+ null
+
+# helper - converts an argument list into a pair of a type string and a
+# data buffer
+# argList must be an array!!!
+toOscTypeAndArgs = (argList, strict) ->
+ osctype = ""
+ oscargs = []
+ for arg in argList
+ if (getArrayArg arg)?
+ [thisType, thisArgs] = toOscTypeAndArgs (getArrayArg arg), strict
+ osctype += "[" + thisType + "]"
+ oscargs = oscargs.concat thisArgs
+ continue
+ typeCode = exports.argToTypeCode arg, strict
+ if typeCode?
+ value = arg?.value
+ if value is undefined
+ value = arg
+ buff = exports.toOscArgument value,
+ (exports.oscTypeCodeToTypeString typeCode), strict
+ if buff?
+ oscargs.push buff
+ osctype += typeCode
+ [osctype, oscargs]
+
#
# convert a javascript format message into an osc buffer
#
@@ -402,27 +460,15 @@ exports.toOscMessage = (message, strict) ->
args[0] = old_arg
oscaddr = exports.toOscString address, strict
- osctype = ","
- oscargs = []
+ [osctype, oscargs] = toOscTypeAndArgs args, strict
+ osctype = "," + osctype
- # fill in args
- for arg in args
- typeCode = exports.argToTypeCode arg, strict
- if typeCode?
- value = arg?.value
- if value is undefined
- value = arg
- buff = exports.toOscArgument value,
- exports.oscTypeCodeToTypeString(typeCode), strict
- if buff?
- oscargs.push buff
- osctype += typeCode
+ # bundle everything together.
+ allArgs = exports.concat oscargs
# convert the type tag into an oscString.
osctype = exports.toOscString osctype
- # bundle everything together.
- allArgs = exports.concat oscargs
exports.concat [oscaddr, osctype, allArgs]
#
View
4 package.json
@@ -1,11 +1,11 @@
{
"name": "osc-min",
- "version": "0.0.10",
+ "version": "0.0.11",
"main": "lib/index",
"author": {
"name": "Russell McClellan",
"email": "russell.mcclellan@gmail.com",
- "url": "http://www.ghostfact.com"
+ "url": "http://www.russellmcc.com"
},
"description": "Simple utilities for open sound control in node.js",
"keywords": [
View
16 readme.md
@@ -42,11 +42,12 @@ npm run-script coverage
### A simple OSC printer;
```javascript
-
sock = udp.createSocket("udp4", function(msg, rinfo) {
+ var error;
try {
return console.log(osc.fromBuffer(msg));
- } catch (error) {
+ } catch (_error) {
+ error = _error;
return console.log("invalid OSC packet");
}
});
@@ -57,7 +58,6 @@ sock.bind(inport);
### Send a bunch of args every two seconds;
```javascript
-
sendHeartbeat = function() {
var buf;
buf = osc.toBuffer({
@@ -78,15 +78,15 @@ setInterval(sendHeartbeat, 2000);
### A simple OSC redirecter;
```javascript
-
sock = udp.createSocket("udp4", function(msg, rinfo) {
- var redirected;
+ var error, redirected;
try {
redirected = osc.applyAddressTransform(msg, function(address) {
return "/redirect" + address;
});
return sock.send(redirected, 0, redirected.length, outport, "localhost");
- } catch (error) {
+ } catch (_error) {
+ error = _error;
return console.log("error redirecting: " + error);
}
});
@@ -184,6 +184,7 @@ See the [spec][spec] for more information on the OSC types.
+ `null` - no value
+ `bang` - no value (this is the `I` type tag)
+ `timetag` - numeric value
+ + `array` - array of _OSC Arguments_
Note that `type` is always a string - i.e. `"true"` rather than `true`.
@@ -193,7 +194,8 @@ See the [spec][spec] for more information on the OSC types.
For messages sent to the `toBuffer` function, `type` is optional.
If the argument is not an object, it will be interpreted as either
- `string`, `float`, or `blob`, depending on its javascript type.
+ `string`, `float`, `array` or `blob`, depending on its javascript type
+ (String, Number, Array, Buffer, respectively)
+ An _OSC Bundle_ is represented as a javascript object with the following layout
View
79 test/test-osc-utilities.coffee
@@ -191,7 +191,6 @@ exports["fromOscMessage with blob argument works"] = ->
assert.strictEqual translate?.args?[0]?.type, "blob"
assert.strictEqual (translate?.args?[0]?.value?.toString "utf8"), "argu"
-
exports["fromOscMessage with integer argument works"] = ->
oscaddr = osc.toOscString "/stuff"
osctype = osc.toOscString ",i"
@@ -210,6 +209,62 @@ exports["fromOscMessage with timetag argument works"] = ->
assert.strictEqual translate?.args?[0]?.type, "timetag"
assert.strictEqual (translate?.args?[0]?.value), 8888
+exports["fromOscMessage with mismatched array doesn't throw"] = ->
+ oscaddr = osc.toOscString "/stuff"
+ assert.doesNotThrow (-> osc.fromOscMessage osc.concat(
+ [oscaddr, osc.toOscString ",["]))
+ assert.doesNotThrow (-> osc.fromOscMessage osc.concat(
+ [oscaddr, osc.toOscString ",["]))
+
+exports["fromOscMessage with mismatched array throws in strict"] = ->
+ oscaddr = osc.toOscString "/stuff"
+ assert.throws (-> osc.fromOscMessage (osc.concat(
+ [oscaddr, osc.toOscString ",["])), true)
+ assert.throws (-> osc.fromOscMessage (osc.concat(
+ [oscaddr, osc.toOscString ",]"])), true)
+
+exports["fromOscMessage with empty array argument works"] = ->
+ oscaddr = osc.toOscString "/stuff"
+ osctype = osc.toOscString ",[]"
+ translate = osc.fromOscMessage osc.concat [oscaddr, osctype]
+ assert.strictEqual translate?.address, "/stuff"
+ assert.strictEqual translate?.args?[0]?.type, "array"
+ assert.strictEqual (translate?.args?[0]?.value?.length), 0
+ assert.deepEqual (translate?.args?[0]?.value), []
+
+exports["fromOscMessage with bang array argument works"] = ->
+ oscaddr = osc.toOscString "/stuff"
+ osctype = osc.toOscString ",[I]"
+ translate = osc.fromOscMessage osc.concat [oscaddr, osctype]
+ assert.strictEqual translate?.address, "/stuff"
+ assert.strictEqual translate?.args?[0]?.type, "array"
+ assert.strictEqual (translate?.args?[0]?.value?.length), 1
+ assert.strictEqual (translate?.args?[0]?.value?[0]?.type), "bang"
+ assert.strictEqual (translate?.args?[0]?.value?[0]?.value), "bang"
+
+exports["fromOscMessage with string array argument works"] = ->
+ oscaddr = osc.toOscString "/stuff"
+ osctype = osc.toOscString ",[s]"
+ oscarg = osc.toOscString "argu"
+ translate = osc.fromOscMessage osc.concat [oscaddr, osctype, oscarg]
+ assert.strictEqual translate?.address, "/stuff"
+ assert.strictEqual translate?.args?[0]?.type, "array"
+ assert.strictEqual (translate?.args?[0]?.value?.length), 1
+ assert.strictEqual (translate?.args?[0]?.value?[0]?.type), "string"
+ assert.strictEqual (translate?.args?[0]?.value?[0]?.value), "argu"
+
+exports["fromOscMessage with nested array argument works"] = ->
+ oscaddr = osc.toOscString "/stuff"
+ osctype = osc.toOscString ",[[I]]"
+ translate = osc.fromOscMessage osc.concat [oscaddr, osctype]
+ assert.strictEqual translate?.address, "/stuff"
+ assert.strictEqual translate?.args?[0]?.type, "array"
+ assert.strictEqual translate?.args?[0]?.value?.length, 1
+ assert.strictEqual (translate?.args?[0]?.value?[0]?.type), "array"
+ assert.strictEqual (translate?.args?[0]?.value?[0]?.value?.length), 1
+ assert.strictEqual (translate?.args?[0]?.value?[0]?.value?[0]?.type), "bang"
+ assert.strictEqual (translate?.args?[0]?.value?[0]?.value?[0]?.value), "bang"
+
exports["fromOscMessage with multiple args works."] = ->
oscaddr = osc.toOscString "/stuff"
osctype = osc.toOscString ",sbi"
@@ -244,7 +299,6 @@ exports["fromOscMessage strict fails if type address doesn't begin with /"] = ->
assert.throws ->
osc.fromOscMessage (osc.concat [oscaddr, osctype]), true
-
exports["fromOscBundle works with no messages"] = ->
oscbundle = osc.toOscString "#bundle"
osctimetag = osc.toIntegerBuffer 0, "UInt64"
@@ -253,7 +307,6 @@ exports["fromOscBundle works with no messages"] = ->
assert.strictEqual translate?.timetag, 0
assert.deepEqual translate?.elements, []
-
exports["fromOscBundle works with single message"] = ->
oscbundle = osc.toOscString "#bundle"
osctimetag = osc.toIntegerBuffer 0, "UInt64"
@@ -267,7 +320,6 @@ exports["fromOscBundle works with single message"] = ->
assert.strictEqual translate?.elements?.length, 1
assert.strictEqual translate?.elements?[0]?.address, "/addr"
-
exports["fromOscBundle works with multiple messages"] = ->
oscbundle = osc.toOscString "#bundle"
osctimetag = osc.toIntegerBuffer 0, "UInt64"
@@ -286,7 +338,6 @@ exports["fromOscBundle works with multiple messages"] = ->
assert.strictEqual translate?.elements?[0]?.address, "/addr"
assert.strictEqual translate?.elements?[1]?.address, "/addr2"
-
exports["fromOscBundle works with nested bundles"] = ->
oscbundle = osc.toOscString "#bundle"
osctimetag = osc.toIntegerBuffer 0, "UInt64"
@@ -347,9 +398,9 @@ roundTripMessage = (args) ->
assert.strictEqual roundTrip?.args?[i]?.type, args[i].type if args[i]?.type?
if Buffer.isBuffer comp
for j in [0...comp.length]
- assert.strictEqual roundTrip?.args?[i]?.value?[j], comp[j]
+ assert.deepEqual roundTrip?.args?[i]?.value?[j], comp[j]
else
- assert.strictEqual roundTrip?.args?[i]?.value, comp
+ assert.deepEqual roundTrip?.args?[i]?.value, comp
exports["toOscArgument fails when given bogus type"] = ->
assert.throws -> osc.toOscArgument "bleh", "bogus"
@@ -364,6 +415,20 @@ exports["toOscMessage strict with null argument throws"] = ->
exports["toOscMessage with string argument works"] = ->
roundTripMessage ["strr"]
+exports["toOscMessage with empty array argument works"] = ->
+ roundTripMessage [[]]
+
+exports["toOscMessage with string array argument works"] = ->
+ roundTripMessage [[{type:"string", value:"hello"},
+ {type:"string", value:"goodbye"}]]
+
+exports["toOscMessage with multi-type array argument works"] = ->
+ roundTripMessage [[{type:"string", value:"hello"},
+ {type:"integer", value:7}]]
+
+exports["toOscMessage with nested array argument works"] = ->
+ roundTripMessage [[{type:"array", value:[{type:"string", value:"hello"}]}]]
+
buffeq = (buff, exp_buff) ->
assert.strictEqual buff.length, exp_buff.length
for i in [0...exp_buff.length]

0 comments on commit bfa16b5

Please sign in to comment.