Skip to content

Commit bb2e464

Browse files
committed
Adds early implementation of bundle reading with test.
1 parent cca09c5 commit bb2e464

File tree

4 files changed

+227
-11
lines changed

4 files changed

+227
-11
lines changed

.jshintrc

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"white": false,
3+
"newcap": true,
4+
"regexp": true,
5+
"browser": true,
6+
"forin": false,
7+
"nomen": true,
8+
"bitwise": false,
9+
"maxerr": 100,
10+
"indent": 4,
11+
"plusplus": false,
12+
"curly": true,
13+
"eqeqeq": true,
14+
"freeze": true,
15+
"latedef": true,
16+
"noarg": true,
17+
"nonew": true,
18+
"quotmark": "double",
19+
"undef": true,
20+
"unused": true,
21+
"strict": true,
22+
"asi": false,
23+
"boss": false,
24+
"evil": false,
25+
"expr": false,
26+
"funcscope": false
27+
}

README.md

+9-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ osc.js is a library for reading and writing [Open Sound Control](http://opensoun
66
Why osc.js?
77
-----------
88

9-
There are several other OSC libraries written in JavaScript. All of them depend on Node.js-specific APIs, which means they can't be run in a browser. osc.js uses only cross-platform APIs (specifically, `TypedArrays` and `DataView`) to ensure that it can run in any modern JavaScript environment.
9+
There are several other OSC libraries available for JavaScript. However, they all depend on Node.js-specific APIs. This means that they can't be run in a browser. osc.js uses only cross-platform APIs (specifically, `TypedArrays` and `DataView`), ensuring that it can run in any modern JavaScript environment.
1010

1111
What Does it Do?
1212
----------------
@@ -15,11 +15,17 @@ osc.js reads and writes OSC-formatted binary data into plain JavaScript objects.
1515

1616
You can receive OSC data in whatever manner works best for your application: serial port APIs such as node-serialport or chrome.serial, socket APIs such as Node.js dgram or WebRTC data channels, WebSockets or binary XHR messages should all work. Connect osc.js up to your source of incoming/outgoing data, and you're all set.
1717

18+
This approach is consistent with the design of Open Sound Control as a _content format_ that is independent from its means of transport.
19+
1820
Status
1921
------
2022

21-
osc.js is new and still in development. It does not yet (but will) support:
23+
osc.js is still under development. It supports all OSC 1.0 and 1.1 required types, and most optional types. The semantics for time tags conform to the OSC 1.0 specification.
24+
25+
osc.js does not yet (but will) support:
2226

23-
* Bundles
27+
* Writing bundles
2428
* color types
2529
* MIDI types
30+
* array types ("[" and "]")
31+
* documentation

osc.js

+53-4
Original file line numberDiff line numberDiff line change
@@ -443,8 +443,7 @@ var osc = osc || {};
443443

444444
for (var i = 0; i < argTypes.length; i++) {
445445
var argType = argTypes[i],
446-
typeSpec = osc.argumentTypes[argType],
447-
argReader;
446+
typeSpec = osc.argumentTypes[argType];
448447

449448
if (!typeSpec) {
450449
throw new Error("'" + argType + "' is not a valid OSC type tag. Type tag string was: " +
@@ -542,6 +541,11 @@ var osc = osc || {};
542541
};
543542

544543
var address = osc.readString(dv, offsetState);
544+
return osc.readMessageContents(address, dv, offsetState, withMetadata);
545+
};
546+
547+
// Unsupported, non-API function.
548+
osc.readMessageContents = function (address, dv, offsetState, withMetadata) {
545549
if (address.indexOf("/") !== 0) {
546550
throw new Error("A malformed OSC address was found while reading " +
547551
"an OSC message. String was: " + address);
@@ -573,6 +577,50 @@ var osc = osc || {};
573577
});
574578
};
575579

580+
osc.readBundle = function (dv, offsetState, withMetadata) {
581+
return osc.readPacket(dv, offsetState, withMetadata);
582+
};
583+
584+
// Unsupported, non-API function.
585+
osc.readBundleContents = function (dv, offsetState, withMetadata) {
586+
var timeTag = osc.readTimeTag(dv, offsetState),
587+
packets = [];
588+
589+
while (offsetState.idx < dv.byteLength) {
590+
var packetSize = osc.readInt32(dv, offsetState),
591+
// TODO: Improve performance by not splitting the underlying ArrayBuffer when we recurse.
592+
packetArr = dv.buffer.slice(offsetState.idx, offsetState.idx + packetSize),
593+
packet = osc.readPacket(packetArr, undefined, withMetadata);
594+
595+
packets.push(packet);
596+
offsetState.idx += packetSize;
597+
}
598+
599+
return {
600+
timeTag: timeTag,
601+
packets: packets
602+
};
603+
};
604+
605+
osc.readPacket = function (dv, offsetState, withMetadata) {
606+
dv = osc.wrapAsDataView(dv);
607+
offsetState = offsetState || {
608+
idx: 0
609+
};
610+
611+
var header = osc.readString(dv, offsetState),
612+
firstChar = header[0];
613+
614+
if (firstChar === "#") {
615+
return osc.readBundleContents(dv, offsetState, withMetadata);
616+
} else if (firstChar === "/") {
617+
return osc.readMessageContents(header, dv, offsetState, withMetadata);
618+
}
619+
620+
throw new Error("The header of an OSC packet didn't contain an OSC address or a #bundle string." +
621+
"Header was: " + header);
622+
};
623+
576624
// Unsupported, non-API.
577625
osc.argumentTypes = {
578626
i: {
@@ -651,9 +699,10 @@ var osc = osc || {};
651699
(typeof Buffer !== "undefined" && arg instanceof Buffer)) {
652700
return "b";
653701
}
654-
default:
655-
throw new Error("Can't infer OSC argument type for value: " + arg);
702+
break;
656703
}
704+
705+
throw new Error("Can't infer OSC argument type for value: " + arg);
657706
};
658707

659708
// Unsupported, non-API function.

tests/js/osc-tests.js

+138-4
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@
120120

121121
equal(actual, expected, "The string should have been read correctly.");
122122
equal(offsetState.idx, testSpec.paddedString.length,
123-
"The offset state should correctly reflect the null padding of the OSC string.")
123+
"The offset state should correctly reflect the null padding of the OSC string.");
124124
});
125125

126126
};
@@ -157,7 +157,7 @@
157157
testReadString(testSpec);
158158
testWriteString(testSpec);
159159
}
160-
}
160+
};
161161

162162
stringTests(stringTestSpecs);
163163

@@ -394,7 +394,7 @@
394394

395395
arrayEqual(actual, expected, "The raw time tag should have have been written correctly.");
396396
});
397-
}
397+
};
398398

399399
var timeTagTestSpecs = [
400400
{
@@ -428,7 +428,7 @@
428428
for (var i = 0; i < testSpecs.length; i++) {
429429
var testSpec = testSpecs[i];
430430
testReadTimeTag(testSpec);
431-
testWriteTimeTag(testSpec)
431+
testWriteTimeTag(testSpec);
432432
}
433433
};
434434

@@ -657,4 +657,138 @@
657657

658658
testMessages(messageTestSpecs);
659659

660+
661+
/***********
662+
* Bundles *
663+
***********/
664+
665+
var testReadBundle = function (testSpec) {
666+
test("readBundle " + testSpec.name, function () {
667+
var expected = testSpec.bundle,
668+
dv = new DataView(testSpec.bytes.buffer),
669+
offsetState = {
670+
idx: 0
671+
};
672+
673+
var actual = osc.readBundle(dv, offsetState);
674+
roundedDeepEqual(actual, expected,
675+
"The bundle should have been read correctly.");
676+
equal(offsetState.idx, dv.byteLength,
677+
"The offset state should have been adjusted correctly.");
678+
});
679+
};
680+
681+
var bundleTestSpecs = [
682+
{
683+
name: "with nested bundles.",
684+
bytes: new Uint8Array([
685+
// "#bundle"
686+
35, 98, 117, 110,
687+
100, 108, 101, 0,
688+
// timetag [3608492400, 0]
689+
215, 21, 57, 112,
690+
0, 0, 0, 0,
691+
692+
// first packet:
693+
// size 24 bytes
694+
0, 0, 0, 24,
695+
// "/cat/meow/freq"
696+
47, 99, 97, 116,
697+
47, 109, 101, 111,
698+
119, 47, 102, 114,
699+
101, 113, 0, 0,
700+
//,f
701+
44, 102, 0, 0,
702+
// 222.2
703+
67, 94, 51, 51,
704+
705+
// second packet:
706+
// size 44 bytes
707+
0, 0, 0, 48,
708+
// "#bundle"
709+
35, 98, 117, 110,
710+
100, 108, 101, 0,
711+
// timetag [3608406000, 0]
712+
215, 19, 231, 240,
713+
0, 0, 0, 0,
714+
715+
// packet 2.a:
716+
// size 28 bytes
717+
0, 0, 0, 28,
718+
// "/hamster/wheel/freq"
719+
47, 104, 97, 109,
720+
115, 116, 101, 114,
721+
47, 119, 104, 101,
722+
101, 108, 47, 102,
723+
114, 101, 113, 0,
724+
// type tag ,i
725+
44, 105, 0, 0,
726+
// 100
727+
0, 0, 0, 100,
728+
729+
// third packet:
730+
// size 32 bytes
731+
0, 0, 0, 32,
732+
// "/fish/burble/amp"
733+
47, 102, 105, 115,
734+
104, 47, 98, 117,
735+
114, 98, 108, 101,
736+
47, 97, 109, 112,
737+
0, 0, 0, 0,
738+
// type tag ,fs
739+
44, 102, 115, 0,
740+
// -6, "dB"
741+
192, 192, 0, 0,
742+
100, 66, 0, 0
743+
]),
744+
745+
bundle: {
746+
timeTag: {
747+
// 215, 21, 57, 112 | 0, 0, 0, 0
748+
raw: [3608492400, 0],
749+
native: 1399503600000
750+
},
751+
752+
packets: [
753+
{
754+
// 47 99 97 116 | 47 109 101 111 | 119 47 102 114 | 101 113 0 0
755+
address: "/cat/meow/freq",
756+
// type tag: ,f: 44 102 0 0 | values: 67 94 51 51
757+
args: 222.2
758+
},
759+
{
760+
timeTag: {
761+
// 215 19 231 240 | 0 0 0 0
762+
raw: [3608406000, 0],
763+
native: 1399417200000
764+
},
765+
packets: [
766+
{
767+
// 47 104 97 109 | 115 116 101 114 | 47 119 104 101 | 101 108 47 102 | 114 101 113 0
768+
address: "/hamster/wheel/freq",
769+
// type tag ,i: 44 105 0 0 | values: 66 200 0 0
770+
args: 100
771+
}
772+
]
773+
},
774+
{
775+
// 47 102 105 115 | 104 47 98 117 | 114 98 108 101 | 47 97 109 112 | 0 0 0 0
776+
address: "/fish/burble/amp",
777+
// type tag ,fs: 44 102 115 0 | values: 255 255 255 250, 100 66 0 0
778+
args: [-6, "dB"]
779+
}
780+
]
781+
}
782+
}
783+
];
784+
785+
var testBundles = function (testSpecs) {
786+
for (var i = 0; i < testSpecs.length; i++) {
787+
var testSpec = testSpecs[i];
788+
testReadBundle(testSpec);
789+
}
790+
};
791+
792+
testBundles(bundleTestSpecs);
793+
660794
}());

0 commit comments

Comments
 (0)