diff node_modules/node-osc/lib/osc.js @ 77:cd921abc8887

added puredata trigger/OSC router
author Rob Canning <rob@foo.net>
date Tue, 15 Jul 2014 17:48:07 +0100
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/node_modules/node-osc/lib/osc.js	Tue Jul 15 17:48:07 2014 +0100
@@ -0,0 +1,315 @@
+var min = require('osc-min');
+var dgram = require('dgram');
+var util = require('util');
+var events = require('events');
+var jspack = require('jspack').jspack;
+
+////////////////////
+// OSC Message
+////////////////////
+
+
+function Message(address) {
+    this.oscType = "message";
+    this.address = address;
+    this.args = [];
+    
+    for (var i = 1; i < arguments.length; i++) {
+        this.append(arguments[i]);
+    }
+}
+
+Message.prototype = {
+    append: function (arg) {
+        switch (typeof arg) {
+        case 'object':
+            if (arg.type) {
+                this.args.push(arg);
+            } else {
+                throw new Error("don't know how to encode object " + arg)
+            }
+            break;
+        case 'number':
+            if (Math.floor(arg) == arg) {
+                var argOut = new Argument('integer', arg);
+                this.args.push(argOut);
+            } else {
+                var argOut = new Argument('float', arg);
+                this.args.push(argOut);
+            }
+            break;
+        case 'string':
+            var argOut = new Argument('string', arg);
+            this.args.push(argOut);
+            break;
+        default:
+            throw new Error("don't know how to encode " + arg);
+        }   
+    }
+}
+
+exports.Message = Message;
+
+function Argument(type, value){
+    this.type = type;
+    this.value = value;
+}
+
+////////////////////
+// OSC Client
+////////////////////
+
+var Client = function (host, port) {
+    this.host = host;
+    this.port = port;
+    this._sock = dgram.createSocket('udp4');
+}
+
+Client.prototype = {
+    send: function (message) {
+        switch (typeof message) {
+            case 'object':
+                var buf = min.toBuffer(message);
+                this._sock.send(buf, 0, buf.length, this.port, this.host);
+                break;
+            case 'string':
+                mes = new Message(arguments[0]);
+                for (var i = 1; i < arguments.length; i++) {
+                    mes.append(arguments[i]);
+                }
+                var buf = min.toBuffer(mes);
+                this._sock.send(buf, 0, buf.length, this.port, this.host);
+                break;
+            default:
+                throw new Error("That Message Just Doesn't Seem Right");
+        }
+    }                                                  
+}
+
+exports.Client = Client;
+
+////////////////////
+// OSC Message encoding and decoding functions
+////////////////////
+
+function ShortBuffer(type, buf, requiredLength)
+{
+    this.type = "ShortBuffer";
+    var message = "buffer [";
+    for (var i = 0; i < buf.length; i++) {
+        if (i) {
+            message += ", ";
+        }
+        message += buf.charCodeAt(i);
+    }
+    message += "] too short for " + type + ", " + requiredLength + " bytes required";
+    this.message = message;
+}
+
+function TString (value) { this.value = value; }
+TString.prototype = {
+    typetag: 's',
+    decode: function (data) {
+        var end = 0;
+        while (data[end] && end < data.length) {
+            end++;
+        }
+        if (end == data.length) {
+            throw Error("OSC string not null terminated");
+        }
+        this.value = data.toString('ascii', 0, end);
+        var nextData = parseInt(Math.ceil((end + 1) / 4.0) * 4);
+        return data.slice(nextData);
+    },
+    encode: function (buf, pos) {
+        var len = Math.ceil((this.value.length + 1) / 4.0) * 4;
+        return jspack.PackTo('>' + len + 's', buf, pos, [ this.value ]);
+    }
+}
+exports.TString = TString;
+
+function TInt (value) { this.value = value; }
+TInt.prototype = {
+    typetag: 'i',
+    decode: function (data) {
+        if (data.length < 4) {
+            throw new ShortBuffer('int', data, 4);
+        }
+
+        this.value = jspack.Unpack('>i', data.slice(0, 4))[0];
+        return data.slice(4);
+    },
+    encode: function (buf, pos) {
+        return jspack.PackTo('>i', buf, pos, [ this.value ]);
+    }
+}
+exports.TInt = TInt;
+
+function TTime (value) { this.value = value; }
+TTime.prototype = {
+    typetag: 't',
+    decode: function (data) {
+        if (data.length < 8) {
+            throw new ShortBuffer('time', data, 8);
+        }
+        var raw = jspack.Unpack('>LL', data.slice(0, 8));
+        var secs = raw[0];
+        var fracs = raw[1];
+        this.value = secs + fracs / 4294967296;
+        return data.slice(8);
+    },
+    encode: function (buf, pos) {
+        return jspack.PackTo('>LL', buf, pos, this.value);
+    }
+}
+exports.TTime = TTime;
+
+function TFloat (value) { this.value = value; }
+TFloat.prototype = {
+    typetag: 'f',
+    decode: function (data) {
+        if (data.length < 4) {
+            throw new ShortBuffer('float', data, 4);
+        }
+
+        this.value = jspack.Unpack('>f', data.slice(0, 4))[0];
+        return data.slice(4);
+    },
+    encode: function (buf, pos) {
+        return jspack.PackTo('>f', buf, pos, [ this.value ]);
+    }
+}
+exports.TFloat = TFloat;
+
+function TBlob (value) { this.value = value; }
+TBlob.prototype = {
+    typetag: 'b',
+    decode: function (data) {
+        var length = jspack.Unpack('>i', data.slice(0, 4))[0];
+        var nextData = parseInt(Math.ceil((length) / 4.0) * 4) + 4;
+        this.value = data.slice(4, length + 4);
+        return data.slice(nextData);
+    },
+    encode: function (buf, pos) {
+        var len = Math.ceil((this.value.length) / 4.0) * 4;
+        return jspack.PackTo('>i' + len + 's', buf, pos, [len, this.value]);
+    }
+}
+exports.TBlob = TBlob;
+
+function TDouble (value) { this.value = value; }
+TDouble.prototype = {
+    typetag: 'd',
+    decode: function (data) {
+        if (data.length < 8) {
+            throw new ShortBuffer('double', data, 8);
+        }
+        this.value = jspack.Unpack('>d', data.slice(0, 8))[0];
+        return data.slice(8);
+    },
+    encode: function (buf, pos) {
+        return jspack.PackTo('>d', buf, pos, [ this.value ]);
+    }
+}
+exports.TDouble = TDouble;
+
+// for each OSC type tag we use a specific constructor function to decode its respective data
+var tagToConstructor = { 'i': function () { return new TInt },
+                         'f': function () { return new TFloat },
+                         's': function () { return new TString },
+                         'b': function () { return new TBlob },
+                         'd': function () { return new TDouble } };
+
+function decode (data) {
+    // this stores the decoded data as an array
+    var message = [];
+
+    // we start getting the <address> and <rest> of OSC msg /<address>\0<rest>\0<typetags>\0<data>
+    var address = new TString;
+    data = address.decode(data);
+
+    // Checking if we received a bundle (typical for TUIO/OSC)
+    if (address.value == '#bundle') {
+        var time = new TTime;
+        data = time.decode(data);
+
+        message.push('#bundle');
+        message.push(time.value);
+
+        var length, part;
+        while(data.length > 0) {
+            length = new TInt;
+            data = length.decode(data);
+
+            part = data.slice(0, length.value);
+            //message = message.concat(decode(part));
+            message.push(decode(part));
+
+            data = data.slice(length.value, data.length);
+        }
+
+    } else if (data.length > 0) {
+        message.push(address.value);
+
+        // if we have rest, maybe we have some typetags... let see...
+
+        // now we advance on the old rest, getting <typetags>
+        var typetags = new TString;
+        data = typetags.decode(data);
+        typetags = typetags.value;
+        // so we start building our message list
+        if (typetags[0] != ',') {
+            throw "invalid type tag in incoming OSC message, must start with comma";
+        }
+        for (var i = 1; i < typetags.length; i++) {
+            var constructor = tagToConstructor[typetags[i]];
+            if (!constructor) {
+                throw "Unsupported OSC type tag " + typetags[i] + " in incoming message";
+            }
+            var argument = constructor();
+            data = argument.decode(data);
+            message.push(argument.value);
+        }
+    }
+
+    return message;
+};
+
+
+
+////////////////////
+// OSC Server
+////////////////////
+
+var Server = function(port, host) {
+    var _callbacks, server;
+    events.EventEmitter.call(this);
+    _callbacks = [];
+    this.port = port;
+    this.host = host;
+    this._sock = dgram.createSocket('udp4');
+    this._sock.bind(port);
+    server = this;
+    this._sock.on('message', function (msg, rinfo) {
+        try {
+            var decoded = decode(msg);
+            // [<address>, <typetags>, <values>*] 
+            if (decoded) {
+                server.emit('message', decoded, rinfo);
+                server.emit(decoded[0], decoded, rinfo);
+            }
+        }
+        catch (e) {
+            console.log("can't decode incoming message: " + e.message);
+        }
+    });
+    this.kill = function() {
+        this._sock.close();
+    };
+}
+
+util.inherits(Server, events.EventEmitter);
+
+exports.Server = Server;
+
+