Skip to content

Commit e20ac34

Browse files
committed
Adds support for reading and writing time tags.
Untested support for several other OSC optional types.
1 parent ee141be commit e20ac34

File tree

3 files changed

+207
-8
lines changed

3 files changed

+207
-8
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ Status
2020

2121
osc.js is in early, active development. It does not yet (but will) support:
2222

23-
* OSC bundles
24-
* OSC time tags
23+
* Bundles
24+
* Time tags
2525
* int64 types
2626
* float64 types
2727
* char32 types

osc.js

Lines changed: 150 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ var osc = osc || {};
44

55
"use strict";
66

7+
osc.SEVENTY_YEARS_SECS = 2208988800;
8+
79
// Unsupported, non-API function.
810
osc.isArray = function (obj) {
911
return obj && Object.prototype.toString.call(obj) === "[object Array]";
@@ -105,6 +107,7 @@ var osc = osc || {};
105107
return arr;
106108
};
107109

110+
// Unsupported, non-API function.
108111
osc.readPrimitive = function (dv, readerName, numBytes, offsetState) {
109112
var val = dv[readerName](offsetState.idx, false);
110113
offsetState.idx += numBytes;
@@ -151,6 +154,30 @@ var osc = osc || {};
151154
return osc.writePrimitive(val, dv, "setInt32", 4, offset);
152155
};
153156

157+
/**
158+
* Reads an OSC int64 ("h") value.
159+
*
160+
* @param {DataView} dv a DataView containing the raw bytes
161+
* @param {Object} offsetState an offsetState object used to store the current offset index into dv
162+
* @return {Number} the number that was read
163+
*/
164+
// TODO: Unit tests.
165+
osc.readInt64 = function (dv, offsetState) {
166+
return osc.readPrimitive(dv, "getInt64", 8, offsetState);
167+
};
168+
169+
/**
170+
* Writes an OSC int64 ("h") value.
171+
*
172+
* @param {Number} val the number to write
173+
* @param {DataView} [dv] a DataView instance to write the number into
174+
* @param {Number} [offset] an offset into dv
175+
*/
176+
// TODO: Unit tests.
177+
osc.writeInt64 = function (val, dv, offset) {
178+
return osc.writePrimitive(val, dv, "setInt64", 8, offset);
179+
};
180+
154181
/**
155182
* Reads an OSC float32 ("f") value.
156183
*
@@ -173,6 +200,61 @@ var osc = osc || {};
173200
return osc.writePrimitive(val, dv, "setFloat32", 4, offset);
174201
};
175202

203+
/**
204+
* Reads an OSC float64 ("d") value.
205+
*
206+
* @param {DataView} dv a DataView containing the raw bytes
207+
* @param {Object} offsetState an offsetState object used to store the current offset index into dv
208+
* @return {Number} the number that was read
209+
*/
210+
// TODO: Unit tests.
211+
osc.readFloat64 = function (dv, offsetState) {
212+
return osc.readPrimitive(dv, "getFloat64", 8, offsetState);
213+
};
214+
215+
/**
216+
* Writes an OSC float64 ("d") value.
217+
*
218+
* @param {Number} val the number to write
219+
* @param {DataView} [dv] a DataView instance to write the number into
220+
* @param {Number} [offset] an offset into dv
221+
*/
222+
// TODO: Unit tests.
223+
osc.writeFloat64 = function (val, dv, offset) {
224+
return osc.writePrimitive(val, dv, "setFloat64", 8, offset);
225+
};
226+
227+
/**
228+
* Reads an OSC 32-bit ASCII character ("c") value.
229+
*
230+
* @param {DataView} dv a DataView containing the raw bytes
231+
* @param {Object} offsetState an offsetState object used to store the current offset index into dv
232+
* @return {String} a string containing the read character
233+
*/
234+
// TODO: Unit tests.
235+
osc.readChar32 = function (dv, offsetState) {
236+
var charCode = osc.readPrimitive(dv, "getUint32", 4, offsetState);
237+
return String.fromCharCode(charCode);
238+
};
239+
240+
/**
241+
* Writes an OSC 32-bit ASCII character ("c") value.
242+
*
243+
* @param {String} str the string from which the first character will be written
244+
* @param {DataView} [dv] a DataView instance to write the character into
245+
* @param {Number} [offset] an offset into dv
246+
* @return {String} a string containing the read character
247+
*/
248+
// TODO: Unit tests.
249+
osc.readChar32 = function (str, dv, offset) {
250+
var charCode = str.charCodeAt(0);
251+
if (charCode === undefined || charCode < -1) {
252+
return undefined;
253+
}
254+
255+
return osc.writePrimitive(charCode, dv, "setUint32", 4, offsetState);
256+
};
257+
176258
/**
177259
* Reads an OSC blob ("b") (i.e. a Uint8Array).
178260
*
@@ -246,12 +328,65 @@ var osc = osc || {};
246328
return 1.0;
247329
};
248330

331+
/**
332+
* Reads an OSC time tag.
333+
*
334+
* @param {DataView} dv the DataView instance to read from
335+
* @param {Object} offsetState an offset state object containing the current index into dv
336+
* @param {Object} a time tag object containing both the raw NTP as well as the converted native (i.e. JS/UNIX) time
337+
*/
249338
osc.readTimeTag = function (dv, offsetState) {
250-
// TODO: Implement.
339+
var secs1900 = osc.readPrimitive(dv, "getUint32", 4, offsetState),
340+
frac = osc.readPrimitive(dv, "getUint32", 4, offsetState),
341+
native = osc.ntpToJSTime(secs1900, frac);
342+
343+
return {
344+
raw: [secs1900, frac],
345+
native: native
346+
};
251347
};
252348

349+
// TODO: Unit tests.
253350
osc.writeTimeTag = function (timeTag) {
254-
// TODO: Implement.
351+
var raw = timeTag.raw ? timeTag.raw : osc.jsTimeToNTP(timeTag.native),
352+
arr = new Uint8Array(8), // Two Unit32s.
353+
dv = new DataView(arr.buffer);
354+
355+
osc.writeInt32(raw[0], dv, 0);
356+
osc.writeInt32(raw[1], dv, 4);
357+
358+
return arr;
359+
};
360+
361+
// TODO: Unit tests.
362+
osc.futureTimeTag = function (secs) {
363+
var ms = sec * 1000,
364+
futureMS = Date.now() + ms;
365+
366+
return {
367+
native: futureMS
368+
};
369+
};
370+
371+
// TODO: Unit tests.
372+
osc.ntpToJSTime = function (secs1900, frac) {
373+
var secs1970 = secs1900 - osc.SEVENTY_YEARS_SECS,
374+
ms1970 = secs1970 * 1000,
375+
decimals = (frac / 4294967296) * 1000,
376+
msTime = ms1970 + decimals;
377+
378+
return msTime;
379+
};
380+
381+
// TODO: Unit tests.
382+
osc.jsTimeToNTP = function (jsTime) {
383+
var ms = jsTime | 0,
384+
secs = (ms / 1000) | 0,
385+
secs1900 = secs + osc.SEVENTY_YEARS_SECS,
386+
fracMs = jsTime - ms,
387+
fracSec = ((fracMs / 1000) * 4294967296) | 0;
388+
389+
return [secs1900, fracSec];
255390
};
256391

257392
/**
@@ -446,12 +581,21 @@ var osc = osc || {};
446581
},
447582
I: {
448583
reader: "readImpulse"
584+
},
585+
h: {
586+
reader: "readInt64",
587+
writer: "writeInt64"
588+
},
589+
d: {
590+
reader: "readFloat64",
591+
writer: "writeFloat64"
592+
},
593+
c: {
594+
reader: "readChar32",
595+
writer: "writeChar32"
449596
}
450597

451598
// Missing optional OSC 1.0 types:
452-
// h: "readInt64",
453-
// d: "readFloat64",
454-
// c: "readChar32",
455599
// r: "readColor",
456600
// m: "readMIDI"
457601
};
@@ -488,7 +632,7 @@ var osc = osc || {};
488632
if (!osc.isArray(args)) {
489633
args = [args];
490634
}
491-
635+
492636
var annotated = [];
493637
for (var i = 0; i < args.length; i++) {
494638
var arg = args[i],

tests/js/osc-tests.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,61 @@
368368
});
369369

370370

371+
/*************
372+
* Time Tags *
373+
*************/
374+
375+
var testTimeTag = function (testSpec) {
376+
test("Time tag " + testSpec.name, function () {
377+
var expected = testSpec.timeTag,
378+
dv = new DataView(testSpec.timeTagBytes.buffer);
379+
380+
var actual = osc.readTimeTag(dv, {
381+
idx: 0
382+
});
383+
384+
deepEqual(actual, expected, "The date should have be read correctly.");
385+
});
386+
};
387+
388+
var timeTagTestSpecs = [
389+
{
390+
name: "Seconds-only time tag",
391+
// May 4, 2014 at 0:00:00 UTC.
392+
timeTagBytes: new Uint8Array([
393+
215, 15, 243, 112,
394+
0, 0, 0, 0
395+
]),
396+
timeTag: {
397+
raw: [3608146800, 0],
398+
native: 1399158000 * 1000
399+
}
400+
},
401+
{
402+
name: "with fractions of a second",
403+
// Half a second past midnight on Sunday May 4, 2014.
404+
timeTagBytes: new Uint8Array([
405+
// [3608146800, 2147483648]
406+
215, 15, 243, 112,
407+
128, 0, 0 , 0
408+
]),
409+
timeTag: {
410+
raw: [3608146800, 4294967296 / 2],
411+
native: (1399158000 * 1000) + 500
412+
}
413+
}
414+
];
415+
416+
var timeTagTests = function (testSpecs) {
417+
for (var i = 0; i < testSpecs.length; i++) {
418+
var testSpec = testSpecs[i];
419+
testTimeTag(testSpec);
420+
}
421+
};
422+
423+
timeTagTests(timeTagTestSpecs);
424+
425+
371426
/**********************************************
372427
* Read Type-Only Arguments (e.g. T, F, N, I) *
373428
**********************************************/

0 commit comments

Comments
 (0)