Mercurial > hg > nodescore
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 |