From 74dc571f5e51b63cedb00079aa6def1f16aaf5dc Mon Sep 17 00:00:00 2001 From: Vilson Vieira Date: Sun, 20 Feb 2011 22:26:46 -0300 Subject: [PATCH 1/5] some minor fixes --- lib/osc.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/osc.js b/lib/osc.js index 6147a10..fa008e8 100644 --- a/lib/osc.js +++ b/lib/osc.js @@ -150,14 +150,14 @@ var OSCTimeTag = function (time) { var Client = function (port, host) { this.port = port; - this.host = host // || '127.0.0.1'; - this._sock = dgram.createSocket(true); + this.host = host; + this._sock = dgram.createSocket('udp4'); } Client.prototype = { send: function (msg) { var binary = msg.toBinary(); var b = new buffer.Buffer(binary, 'binary'); - this._sock.send(this.port, this.host, b, 0, b.length); + this._sock.send(b, 0, b.length, this.port, this.host); }, sendSimple: function (address, data) { var msg = new Message(address); From 98e1c62993db8a38f28fa841d3d7d7cd7b3c85a6 Mon Sep 17 00:00:00 2001 From: Vilson Vieira Date: Mon, 21 Feb 2011 03:02:16 -0300 Subject: [PATCH 2/5] OSC Server implementation --- README.rst | 12 ++ lib/osc.js | 396 ++++++++++++++++++++++++++++++++++++----------------- 2 files changed, 286 insertions(+), 122 deletions(-) diff --git a/README.rst b/README.rst index 8db3697..8021f73 100644 --- a/README.rst +++ b/README.rst @@ -26,6 +26,18 @@ Example client.send(msg); +You can also create a OSC server: + +:: + + var osc = require('osc'); + + var server = osc.Server(10001, '127.0.0.1'); + + server.addMsgHandler('/bang', function (args) { + console.log('I got a bang!') + }); + Licensing --------- diff --git a/lib/osc.js b/lib/osc.js index fa008e8..3881960 100644 --- a/lib/osc.js +++ b/lib/osc.js @@ -5,164 +5,316 @@ var sys = require('sys'); var jspack = require('jspack').jspack; +//////////////////// +// OSC Message +//////////////////// var Message = function (address) { - this.address = address; - this.typetags = ','; - this.message = []; + this.address = address; + this.typetags = ','; + this.message = []; } + Message.prototype = { - append: function (arg, typehint) { - if (arg instanceof Array) { - for (var i in arg) { - this.append(arg[i], typehint); - } - return null; - } - if (typeof(arg) == 'object') { - for (var k in arg) { - this.append([k, arg[k]]); - } - return null; - } - - if (typehint == 'b') { - binary = OSCBlob(arg); - tag = 'b'; - } else if (typehint == 't') { - binary = OSCTimeTag(arg); - tag = 't'; - } else { - rv = OSCArgument(arg, typehint); - tag = rv[0]; - binary = rv[1]; - } - - this.typetags += tag; - this.message = this.message.concat(binary); - }, - toBinary: function () { - var binary = OSCString(this.address); - binary = binary.concat(OSCString(this.typetags)); - binary = binary.concat(this.message); - return binary; - }, + append: function (arg, typehint) { + if (arg instanceof Array) { + for (var i in arg) { + this.append(arg[i], typehint); + } + return null; + } + if (typeof(arg) == 'object') { + for (var k in arg) { + this.append([k, arg[k]]); + } + return null; + } + + if (typehint == 'b') { + binary = OSCBlob(arg); + tag = 'b'; + } else if (typehint == 't') { + binary = OSCTimeTag(arg); + tag = 't'; + } else { + rv = OSCArgument(arg, typehint); + tag = rv[0]; + binary = rv[1]; + } + + this.typetags += tag; + this.message = this.message.concat(binary); + }, + toBinary: function () { + var binary = OSCString(this.address); + binary = binary.concat(OSCString(this.typetags)); + binary = binary.concat(this.message); + return binary; + }, } exports.Message = Message; - var Bundle = function (address, time) { - Message.call(this, address); - this.timetag = time || 0; + Message.call(this, address); + this.timetag = time || 0; } + sys.inherits(Bundle, Message); + Bundle.prototype.append = function (arg, typehint) { - var binary; - if (arg instanceof Message) { - binary = OSCBlob(arg.toBinary()); - } else { - var msg = Message(this.address); - if (typeof(arg) == 'Object') { - if (arg.addr) { - msg.address = arg.addr; - } - if (arg.args) { - msg.append(arg.args, typehint); - } + var binary; + if (arg instanceof Message) { + binary = OSCBlob(arg.toBinary()); } else { - msg.append(arg, typehint); + var msg = Message(this.address); + if (typeof(arg) == 'Object') { + if (arg.addr) { + msg.address = arg.addr; + } + if (arg.args) { + msg.append(arg.args, typehint); + } + } else { + msg.append(arg, typehint); + } + binary = OSCBlob(msg.toBinary()); } - binary = OSCBlob(msg.toBinary()); - } - this.message += binary; - this.typetags += 'b'; + this.message += binary; + this.typetags += 'b'; }; + Bundle.prototype.toBinary = function () { - var binary = OSCString('#bundle'); - binary = binary.concat(OSCTimeTag(this.timetag)); - binary = binary.concat(this.message); - return binary; + var binary = OSCString('#bundle'); + binary = binary.concat(OSCTimeTag(this.timetag)); + binary = binary.concat(this.message); + return binary; }; + exports.Bundle = Bundle; +//////////////////// +// OSC Message Encoding Functions +//////////////////// var OSCString = function (next) { - var len = Math.ceil((next.length + 1) / 4.0) * 4; - return jspack.Pack('>' + len + 's', [next]); + var len = Math.ceil((next.length + 1) / 4.0) * 4; + var foo = jspack.Pack('>' + len + 's', [next]); + return foo } - var OSCBlob = function (next) { - var binary; - if (typeof(next) == 'String') { - var len = Math.ceil((next.length) / 4.0) * 4; - binary = jspack.Pack('>i' + len + 's', [len, next]); - } else { - binary = ''; - } - return binary; + var binary; + if (typeof(next) == 'String') { + var len = Math.ceil((next.length) / 4.0) * 4; + binary = jspack.Pack('>i' + len + 's', [len, next]); + } else { + binary = ''; + } + return binary; } - var OSCArgument = function (next, typehint) { - var binary, tag; - if (!typehint) { - if (typeof(next) == 'number') { - if (next.toString().indexOf('.') != -1) { - binary = jspack.Pack('>f', [next]); - tag = 'f'; - } else { - binary = jspack.Pack('>i', [next]); - tag = 'i'; - } + var binary, tag; + if (!typehint) { + if (typeof(next) == 'number') { + if (next.toString().indexOf('.') != -1) { + binary = jspack.Pack('>f', [next]); + tag = 'f'; + } else { + binary = jspack.Pack('>i', [next]); + tag = 'i'; + } + } else { + binary = OSCString(next); + tag = 's'; + } + } else if (typehint == 'd') { + try { + binary = jspack.Pack('>f', [parseFloat(next)]); + tag = 'f'; + } catch (e) { + binary = OSCString(next); + tag = 's'; + } + } else if (typehint == 'i') { + try { + binary = jspack.Pack('>i', [parseInt(next)]); + tag = 'i'; + } catch (e) { + binary = OSCString(next); + tag = 's'; + } } else { - binary = OSCString(next); - tag = 's'; - } - } else if (typehint == 'd') { - try { - binary = jspack.Pack('>f', [parseFloat(next)]); - tag = 'f'; - } catch (e) { - binary = OSCString(next); - tag = 's'; - } - } else if (typehint == 'i') { - try { - binary = jspack.Pack('>i', [parseInt(next)]); - tag = 'i'; - } catch (e) { - binary = OSCString(next); - tag = 's'; + binary = OSCString(next); + tag = 's'; } - } else { - binary = OSCString(next); - tag = 's'; - } - return [tag, binary]; + return [tag, binary]; } var OSCTimeTag = function (time) { - // Not Implemented Yet - return jspack.Pack('>LL', 0, 1); + // Not Implemented Yet + return jspack.Pack('>LL', 0, 1); } +//////////////////// +// OSC Client +//////////////////// var Client = function (port, host) { - this.port = port; - this.host = host; - this._sock = dgram.createSocket('udp4'); + this.port = port; + this.host = host; + this._sock = dgram.createSocket('udp4'); } + Client.prototype = { - send: function (msg) { - var binary = msg.toBinary(); - var b = new buffer.Buffer(binary, 'binary'); - this._sock.send(b, 0, b.length, this.port, this.host); - }, - sendSimple: function (address, data) { - var msg = new Message(address); - msg.append(data); - this.send(msg); - }, + send: function (msg) { + var binary = msg.toBinary(); + var b = new buffer.Buffer(binary, 'binary'); + this._sock.send(b, 0, b.length, this.port, this.host); + }, + sendSimple: function (address, data) { + var msg = new Message(address); + msg.append(data); + this.send(msg); + }, } + exports.Client = Client; + +//////////////////// +// OSC Message Decoding Functions +//////////////////// + +var _readString = function (data) { + var data = data.toString('utf8'); + var length = data.search('\0'); + var nextData = parseInt(Math.ceil((length + 1) / 4.0) * 4); + return [data.substring(0, length), data.substr(nextData)]; +}; + +var _readInt = function (data) { + if (data.length < 4) { + console.log('Error: too few bytes for int ' + data + data.length); + rest = data; + value = 0; + } else { + value = jspack.Unpack('>i', new Buffer(data.substring(0,4))); + if (value == undefined) { + value = 0; + } + rest = data.substr(4); + } + return [value, rest]; +}; + +var _readFloat = function (data) { + if (data.length < 4) { + console.log('Error: too few bytes for float ' + data + data.length); + rest = data; + value = 0; + } else { + value = jspack.Unpack('>f', new Buffer(data.substring(0,4))); + if (value == undefined) { + value = 0; + } + rest = data.substr(4); + } + return [value, rest]; +}; + +var _readBlob = function (data) { + var length = fdpack.Unpack('>i', new Buffer(data.substring(0,4))); + var nextData = parseInt(Math.ceil((length) / 4.0) * 4) + 4; + return [data.substring(4, length + 4), data.substr(nextData)] +}; + +var _readDouble = function (data) { + if (data.length < 8) { + console.log('Error: too few bytes for double ' + data + data.length); + rest = data; + value = 0; + } else { + value = jspack.Unpack('>d', new Buffer(data.substring(0,8))); + if (value == undefined) { + value = 0; + } + rest = data.substr(8); + } + return [value, rest]; +}; + +var decodeOSC = function (data) { + // for each tag we use a specific function to decode it's respective data + var table = {'i':_readInt, 'f':_readFloat, 's':_readString, 'b':_readBlob, 'd':_readDouble}; + + // this stores the decoded data as an array + var decoded = []; + + // we start getting the
and of OSC msg /
\0\0\0 + var pair = _readString(data); + var address = pair[0]; + var rest = pair[1]; + + // if we have rest, maybe we have some typetags... let see... + if (rest.length > 0) { + // now we advance on the old rest, getting + var pair = _readString(rest); + var typetags = pair[0]; + var rest = pair[1]; + // so we start building our decoded list + decoded.push(address); + decoded.push(typetags.substr(1)); + + // typetag-string need to start with the magic , + if (typetags[0] == ',') { + // for each tag... + for (var t=0; t, , *] + var decoded = decodeOSC(msg); + + // and we run along the callbacks list + for (var c=0; c<_callbacks.length; c++) { + // if the msg address equals to one of callbacks adresses... + if (_callbacks[c].address == decoded[0]) { + // we call the respective callback function, passing + // the list of of the message + _callbacks[c].callback(decoded.slice(2)); + } + } + }); + + this.addMsgHandler = function(address, callback) { + _callbacks.push({'address': '/' + address.replace('/', ''), + 'callback': callback}); + }; + +} + +exports.Server = Server; \ No newline at end of file From 074d6ea348aa08ff2f6eca7acd2db80096832ec6 Mon Sep 17 00:00:00 2001 From: Nic Luciano Date: Mon, 21 Feb 2011 05:47:48 -0500 Subject: [PATCH 3/5] added regex mode --- lib/osc.js | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/lib/osc.js b/lib/osc.js index 3881960..6439e1f 100644 --- a/lib/osc.js +++ b/lib/osc.js @@ -288,30 +288,44 @@ var decodeOSC = function (data) { //////////////////// var Server = function(port, host) { + var _callbacks = []; this.port = port; this.host = host; this._sock = dgram.createSocket('udp4'); this._sock.bind(port); + + // check regex and string matches + function address_matches(address, test) { + return (typeof(test) == 'string' || address == test? + // string to string or regex to regex + address == test: + // string to regex + address.match(test)); + }; + this._sock.on('message', function (msg, rinfo) { // on every message sent through the UDP socket... // we decode the message getting a beautiful array with the form: // [
, , *] var decoded = decodeOSC(msg); - // and we run along the callbacks list for (var c=0; c<_callbacks.length; c++) { // if the msg address equals to one of callbacks adresses... - if (_callbacks[c].address == decoded[0]) { + if (address_matches(decoded[0], _callbacks[c].address)) { // we call the respective callback function, passing // the list of of the message - _callbacks[c].callback(decoded.slice(2)); + _callbacks[c].callback(decoded[0], decoded[2]); } } }); this.addMsgHandler = function(address, callback) { - _callbacks.push({'address': '/' + address.replace('/', ''), + // if not regex do some replacement + if (typeof(addresss) == 'string') { + address = '/' + address.replace('/', ''); + } + _callbacks.push({'address': address, 'callback': callback}); }; From 3ea042bbad6dd615502d5eb21c4ee0099937d9f7 Mon Sep 17 00:00:00 2001 From: Nic Luciano Date: Mon, 21 Feb 2011 05:48:00 -0500 Subject: [PATCH 4/5] added method to remove index --- lib/osc.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/osc.js b/lib/osc.js index 6439e1f..0926ce3 100644 --- a/lib/osc.js +++ b/lib/osc.js @@ -328,6 +328,15 @@ var Server = function(port, host) { _callbacks.push({'address': address, 'callback': callback}); }; + + this.removeMsgHandler = function(address) { + for (var c=0; c<_callbacks.length; c++) { + // if they're matching strings or matching regex + if (address_matches(address, _callbacks[c].address)) { + delete _callbacks[c]; + } + } + }; } From 55ba3bbae8925b556ae2a627ac62a69a949845bb Mon Sep 17 00:00:00 2001 From: Nic Luciano Date: Mon, 21 Feb 2011 06:23:04 -0500 Subject: [PATCH 5/5] checking for undefined indexes after removal --- lib/osc.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/osc.js b/lib/osc.js index 0926ce3..0892fd0 100644 --- a/lib/osc.js +++ b/lib/osc.js @@ -312,7 +312,7 @@ var Server = function(port, host) { // and we run along the callbacks list for (var c=0; c<_callbacks.length; c++) { // if the msg address equals to one of callbacks adresses... - if (address_matches(decoded[0], _callbacks[c].address)) { + if (_callbacks[c] && address_matches(decoded[0], _callbacks[c].address)) { // we call the respective callback function, passing // the list of of the message _callbacks[c].callback(decoded[0], decoded[2]); @@ -332,7 +332,7 @@ var Server = function(port, host) { this.removeMsgHandler = function(address) { for (var c=0; c<_callbacks.length; c++) { // if they're matching strings or matching regex - if (address_matches(address, _callbacks[c].address)) { + if (_callbacks[c] && address_matches(address, _callbacks[c].address)) { delete _callbacks[c]; } }