comparison 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
comparison
equal deleted inserted replaced
76:0ae87af84e2f 77:cd921abc8887
1 var min = require('osc-min');
2 var dgram = require('dgram');
3 var util = require('util');
4 var events = require('events');
5 var jspack = require('jspack').jspack;
6
7 ////////////////////
8 // OSC Message
9 ////////////////////
10
11
12 function Message(address) {
13 this.oscType = "message";
14 this.address = address;
15 this.args = [];
16
17 for (var i = 1; i < arguments.length; i++) {
18 this.append(arguments[i]);
19 }
20 }
21
22 Message.prototype = {
23 append: function (arg) {
24 switch (typeof arg) {
25 case 'object':
26 if (arg.type) {
27 this.args.push(arg);
28 } else {
29 throw new Error("don't know how to encode object " + arg)
30 }
31 break;
32 case 'number':
33 if (Math.floor(arg) == arg) {
34 var argOut = new Argument('integer', arg);
35 this.args.push(argOut);
36 } else {
37 var argOut = new Argument('float', arg);
38 this.args.push(argOut);
39 }
40 break;
41 case 'string':
42 var argOut = new Argument('string', arg);
43 this.args.push(argOut);
44 break;
45 default:
46 throw new Error("don't know how to encode " + arg);
47 }
48 }
49 }
50
51 exports.Message = Message;
52
53 function Argument(type, value){
54 this.type = type;
55 this.value = value;
56 }
57
58 ////////////////////
59 // OSC Client
60 ////////////////////
61
62 var Client = function (host, port) {
63 this.host = host;
64 this.port = port;
65 this._sock = dgram.createSocket('udp4');
66 }
67
68 Client.prototype = {
69 send: function (message) {
70 switch (typeof message) {
71 case 'object':
72 var buf = min.toBuffer(message);
73 this._sock.send(buf, 0, buf.length, this.port, this.host);
74 break;
75 case 'string':
76 mes = new Message(arguments[0]);
77 for (var i = 1; i < arguments.length; i++) {
78 mes.append(arguments[i]);
79 }
80 var buf = min.toBuffer(mes);
81 this._sock.send(buf, 0, buf.length, this.port, this.host);
82 break;
83 default:
84 throw new Error("That Message Just Doesn't Seem Right");
85 }
86 }
87 }
88
89 exports.Client = Client;
90
91 ////////////////////
92 // OSC Message encoding and decoding functions
93 ////////////////////
94
95 function ShortBuffer(type, buf, requiredLength)
96 {
97 this.type = "ShortBuffer";
98 var message = "buffer [";
99 for (var i = 0; i < buf.length; i++) {
100 if (i) {
101 message += ", ";
102 }
103 message += buf.charCodeAt(i);
104 }
105 message += "] too short for " + type + ", " + requiredLength + " bytes required";
106 this.message = message;
107 }
108
109 function TString (value) { this.value = value; }
110 TString.prototype = {
111 typetag: 's',
112 decode: function (data) {
113 var end = 0;
114 while (data[end] && end < data.length) {
115 end++;
116 }
117 if (end == data.length) {
118 throw Error("OSC string not null terminated");
119 }
120 this.value = data.toString('ascii', 0, end);
121 var nextData = parseInt(Math.ceil((end + 1) / 4.0) * 4);
122 return data.slice(nextData);
123 },
124 encode: function (buf, pos) {
125 var len = Math.ceil((this.value.length + 1) / 4.0) * 4;
126 return jspack.PackTo('>' + len + 's', buf, pos, [ this.value ]);
127 }
128 }
129 exports.TString = TString;
130
131 function TInt (value) { this.value = value; }
132 TInt.prototype = {
133 typetag: 'i',
134 decode: function (data) {
135 if (data.length < 4) {
136 throw new ShortBuffer('int', data, 4);
137 }
138
139 this.value = jspack.Unpack('>i', data.slice(0, 4))[0];
140 return data.slice(4);
141 },
142 encode: function (buf, pos) {
143 return jspack.PackTo('>i', buf, pos, [ this.value ]);
144 }
145 }
146 exports.TInt = TInt;
147
148 function TTime (value) { this.value = value; }
149 TTime.prototype = {
150 typetag: 't',
151 decode: function (data) {
152 if (data.length < 8) {
153 throw new ShortBuffer('time', data, 8);
154 }
155 var raw = jspack.Unpack('>LL', data.slice(0, 8));
156 var secs = raw[0];
157 var fracs = raw[1];
158 this.value = secs + fracs / 4294967296;
159 return data.slice(8);
160 },
161 encode: function (buf, pos) {
162 return jspack.PackTo('>LL', buf, pos, this.value);
163 }
164 }
165 exports.TTime = TTime;
166
167 function TFloat (value) { this.value = value; }
168 TFloat.prototype = {
169 typetag: 'f',
170 decode: function (data) {
171 if (data.length < 4) {
172 throw new ShortBuffer('float', data, 4);
173 }
174
175 this.value = jspack.Unpack('>f', data.slice(0, 4))[0];
176 return data.slice(4);
177 },
178 encode: function (buf, pos) {
179 return jspack.PackTo('>f', buf, pos, [ this.value ]);
180 }
181 }
182 exports.TFloat = TFloat;
183
184 function TBlob (value) { this.value = value; }
185 TBlob.prototype = {
186 typetag: 'b',
187 decode: function (data) {
188 var length = jspack.Unpack('>i', data.slice(0, 4))[0];
189 var nextData = parseInt(Math.ceil((length) / 4.0) * 4) + 4;
190 this.value = data.slice(4, length + 4);
191 return data.slice(nextData);
192 },
193 encode: function (buf, pos) {
194 var len = Math.ceil((this.value.length) / 4.0) * 4;
195 return jspack.PackTo('>i' + len + 's', buf, pos, [len, this.value]);
196 }
197 }
198 exports.TBlob = TBlob;
199
200 function TDouble (value) { this.value = value; }
201 TDouble.prototype = {
202 typetag: 'd',
203 decode: function (data) {
204 if (data.length < 8) {
205 throw new ShortBuffer('double', data, 8);
206 }
207 this.value = jspack.Unpack('>d', data.slice(0, 8))[0];
208 return data.slice(8);
209 },
210 encode: function (buf, pos) {
211 return jspack.PackTo('>d', buf, pos, [ this.value ]);
212 }
213 }
214 exports.TDouble = TDouble;
215
216 // for each OSC type tag we use a specific constructor function to decode its respective data
217 var tagToConstructor = { 'i': function () { return new TInt },
218 'f': function () { return new TFloat },
219 's': function () { return new TString },
220 'b': function () { return new TBlob },
221 'd': function () { return new TDouble } };
222
223 function decode (data) {
224 // this stores the decoded data as an array
225 var message = [];
226
227 // we start getting the <address> and <rest> of OSC msg /<address>\0<rest>\0<typetags>\0<data>
228 var address = new TString;
229 data = address.decode(data);
230
231 // Checking if we received a bundle (typical for TUIO/OSC)
232 if (address.value == '#bundle') {
233 var time = new TTime;
234 data = time.decode(data);
235
236 message.push('#bundle');
237 message.push(time.value);
238
239 var length, part;
240 while(data.length > 0) {
241 length = new TInt;
242 data = length.decode(data);
243
244 part = data.slice(0, length.value);
245 //message = message.concat(decode(part));
246 message.push(decode(part));
247
248 data = data.slice(length.value, data.length);
249 }
250
251 } else if (data.length > 0) {
252 message.push(address.value);
253
254 // if we have rest, maybe we have some typetags... let see...
255
256 // now we advance on the old rest, getting <typetags>
257 var typetags = new TString;
258 data = typetags.decode(data);
259 typetags = typetags.value;
260 // so we start building our message list
261 if (typetags[0] != ',') {
262 throw "invalid type tag in incoming OSC message, must start with comma";
263 }
264 for (var i = 1; i < typetags.length; i++) {
265 var constructor = tagToConstructor[typetags[i]];
266 if (!constructor) {
267 throw "Unsupported OSC type tag " + typetags[i] + " in incoming message";
268 }
269 var argument = constructor();
270 data = argument.decode(data);
271 message.push(argument.value);
272 }
273 }
274
275 return message;
276 };
277
278
279
280 ////////////////////
281 // OSC Server
282 ////////////////////
283
284 var Server = function(port, host) {
285 var _callbacks, server;
286 events.EventEmitter.call(this);
287 _callbacks = [];
288 this.port = port;
289 this.host = host;
290 this._sock = dgram.createSocket('udp4');
291 this._sock.bind(port);
292 server = this;
293 this._sock.on('message', function (msg, rinfo) {
294 try {
295 var decoded = decode(msg);
296 // [<address>, <typetags>, <values>*]
297 if (decoded) {
298 server.emit('message', decoded, rinfo);
299 server.emit(decoded[0], decoded, rinfo);
300 }
301 }
302 catch (e) {
303 console.log("can't decode incoming message: " + e.message);
304 }
305 });
306 this.kill = function() {
307 this._sock.close();
308 };
309 }
310
311 util.inherits(Server, events.EventEmitter);
312
313 exports.Server = Server;
314
315