@9c5s/node-tcnet 0.5.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +434 -319
- package/dist/index.d.ts +434 -319
- package/dist/index.js +268 -43
- package/dist/index.mjs +244 -19
- package/package.json +10 -6
package/dist/index.mjs
CHANGED
|
@@ -360,6 +360,163 @@ var TCNetDataPacketMetadata = class extends TCNetDataPacket {
|
|
|
360
360
|
return 548;
|
|
361
361
|
}
|
|
362
362
|
};
|
|
363
|
+
var TCNetDataPacketCUE = class extends TCNetDataPacket {
|
|
364
|
+
data = null;
|
|
365
|
+
read() {
|
|
366
|
+
const loopInTime = this.buffer.readUInt32LE(42);
|
|
367
|
+
const loopOutTime = this.buffer.readUInt32LE(46);
|
|
368
|
+
const cues = [];
|
|
369
|
+
const cueStart = 50;
|
|
370
|
+
for (let i = 0; i < 18; i++) {
|
|
371
|
+
const offset = cueStart + i * 22;
|
|
372
|
+
if (offset + 22 > this.buffer.length) break;
|
|
373
|
+
const type = this.buffer.readUInt8(offset);
|
|
374
|
+
if (type === 0) continue;
|
|
375
|
+
cues.push({
|
|
376
|
+
index: i + 1,
|
|
377
|
+
type,
|
|
378
|
+
inTime: this.buffer.readUInt32LE(offset + 2),
|
|
379
|
+
outTime: this.buffer.readUInt32LE(offset + 6),
|
|
380
|
+
color: {
|
|
381
|
+
r: this.buffer.readUInt8(offset + 11),
|
|
382
|
+
g: this.buffer.readUInt8(offset + 12),
|
|
383
|
+
b: this.buffer.readUInt8(offset + 13)
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
this.data = { loopInTime, loopOutTime, cues };
|
|
388
|
+
}
|
|
389
|
+
write() {
|
|
390
|
+
throw new Error("not supported!");
|
|
391
|
+
}
|
|
392
|
+
length() {
|
|
393
|
+
return 436;
|
|
394
|
+
}
|
|
395
|
+
};
|
|
396
|
+
function parseWaveformBars(source, dataStart, maxBytes) {
|
|
397
|
+
const bars = [];
|
|
398
|
+
const end = maxBytes !== void 0 ? Math.min(dataStart + maxBytes, source.length) : source.length;
|
|
399
|
+
const safeEnd = dataStart + (end - dataStart & ~1);
|
|
400
|
+
for (let i = dataStart; i < safeEnd; i += 2) {
|
|
401
|
+
bars.push({
|
|
402
|
+
level: source.readUInt8(i),
|
|
403
|
+
color: source.readUInt8(i + 1)
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
return bars;
|
|
407
|
+
}
|
|
408
|
+
var TCNetDataPacketSmallWaveForm = class extends TCNetDataPacket {
|
|
409
|
+
data = null;
|
|
410
|
+
read() {
|
|
411
|
+
this.data = { bars: parseWaveformBars(this.buffer, 42, 2400) };
|
|
412
|
+
}
|
|
413
|
+
write() {
|
|
414
|
+
throw new Error("not supported!");
|
|
415
|
+
}
|
|
416
|
+
length() {
|
|
417
|
+
return 2442;
|
|
418
|
+
}
|
|
419
|
+
};
|
|
420
|
+
var TCNetDataPacketMixer = class extends TCNetDataPacket {
|
|
421
|
+
data = null;
|
|
422
|
+
read() {
|
|
423
|
+
if (this.buffer.length < 259) {
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
const parseChannel = (offset) => ({
|
|
427
|
+
sourceSelect: this.buffer.readUInt8(offset),
|
|
428
|
+
audioLevel: this.buffer.readUInt8(offset + 1),
|
|
429
|
+
faderLevel: this.buffer.readUInt8(offset + 2),
|
|
430
|
+
trimLevel: this.buffer.readUInt8(offset + 3),
|
|
431
|
+
compLevel: this.buffer.readUInt8(offset + 4),
|
|
432
|
+
eqHi: this.buffer.readUInt8(offset + 5),
|
|
433
|
+
eqHiMid: this.buffer.readUInt8(offset + 6),
|
|
434
|
+
eqLowMid: this.buffer.readUInt8(offset + 7),
|
|
435
|
+
eqLow: this.buffer.readUInt8(offset + 8),
|
|
436
|
+
filterColor: this.buffer.readUInt8(offset + 9),
|
|
437
|
+
send: this.buffer.readUInt8(offset + 10),
|
|
438
|
+
cueA: this.buffer.readUInt8(offset + 11),
|
|
439
|
+
cueB: this.buffer.readUInt8(offset + 12),
|
|
440
|
+
crossfaderAssign: this.buffer.readUInt8(offset + 13)
|
|
441
|
+
});
|
|
442
|
+
this.data = {
|
|
443
|
+
mixerId: this.buffer.readUInt8(25),
|
|
444
|
+
mixerType: this.buffer.readUInt8(26),
|
|
445
|
+
mixerName: this.buffer.slice(29, 45).toString("ascii").replace(/\0.*$/g, ""),
|
|
446
|
+
masterAudioLevel: this.buffer.readUInt8(61),
|
|
447
|
+
masterFaderLevel: this.buffer.readUInt8(62),
|
|
448
|
+
masterFilter: this.buffer.readUInt8(69),
|
|
449
|
+
masterIsolatorOn: this.buffer.readUInt8(74) === 1,
|
|
450
|
+
masterIsolatorHi: this.buffer.readUInt8(75),
|
|
451
|
+
masterIsolatorMid: this.buffer.readUInt8(76),
|
|
452
|
+
masterIsolatorLow: this.buffer.readUInt8(77),
|
|
453
|
+
filterHpf: this.buffer.readUInt8(79),
|
|
454
|
+
filterLpf: this.buffer.readUInt8(80),
|
|
455
|
+
filterResonance: this.buffer.readUInt8(81),
|
|
456
|
+
crossFader: this.buffer.readUInt8(99),
|
|
457
|
+
crossFaderCurve: this.buffer.readUInt8(98),
|
|
458
|
+
channelFaderCurve: this.buffer.readUInt8(97),
|
|
459
|
+
beatFxOn: this.buffer.readUInt8(100) === 1,
|
|
460
|
+
beatFxSelect: this.buffer.readUInt8(103),
|
|
461
|
+
beatFxLevelDepth: this.buffer.readUInt8(101),
|
|
462
|
+
beatFxChannelSelect: this.buffer.readUInt8(102),
|
|
463
|
+
headphonesALevel: this.buffer.readUInt8(108),
|
|
464
|
+
headphonesBLevel: this.buffer.readUInt8(110),
|
|
465
|
+
boothLevel: this.buffer.readUInt8(112),
|
|
466
|
+
channels: [125, 149, 173, 197, 221, 245].map(parseChannel)
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
write() {
|
|
470
|
+
throw new Error("not supported!");
|
|
471
|
+
}
|
|
472
|
+
length() {
|
|
473
|
+
return 270;
|
|
474
|
+
}
|
|
475
|
+
};
|
|
476
|
+
var TCNetDataPacketBeatGrid = class extends TCNetDataPacket {
|
|
477
|
+
data = null;
|
|
478
|
+
read() {
|
|
479
|
+
this.readFromOffset(42);
|
|
480
|
+
}
|
|
481
|
+
// アセンブル済みバッファからのパース用
|
|
482
|
+
readAssembled(assembled) {
|
|
483
|
+
this.readFromOffset(0, assembled);
|
|
484
|
+
}
|
|
485
|
+
readFromOffset(dataStart, buf) {
|
|
486
|
+
const source = buf ?? this.buffer;
|
|
487
|
+
const entries = [];
|
|
488
|
+
for (let offset = dataStart; offset + 8 <= source.length; offset += 8) {
|
|
489
|
+
const beatNumber = source.readUInt16LE(offset);
|
|
490
|
+
const beatType = source.readUInt8(offset + 2);
|
|
491
|
+
const timestampMs = source.readUInt32LE(offset + 4);
|
|
492
|
+
if (beatNumber === 0 && timestampMs === 0) continue;
|
|
493
|
+
entries.push({ beatNumber, beatType, timestampMs });
|
|
494
|
+
}
|
|
495
|
+
this.data = { entries };
|
|
496
|
+
}
|
|
497
|
+
write() {
|
|
498
|
+
throw new Error("not supported!");
|
|
499
|
+
}
|
|
500
|
+
length() {
|
|
501
|
+
return 2442;
|
|
502
|
+
}
|
|
503
|
+
};
|
|
504
|
+
var TCNetDataPacketBigWaveForm = class extends TCNetDataPacket {
|
|
505
|
+
data = null;
|
|
506
|
+
read() {
|
|
507
|
+
this.data = { bars: parseWaveformBars(this.buffer, 42) };
|
|
508
|
+
}
|
|
509
|
+
// アセンブル済みバッファからのパース用
|
|
510
|
+
readAssembled(assembled) {
|
|
511
|
+
this.data = { bars: parseWaveformBars(assembled, 0) };
|
|
512
|
+
}
|
|
513
|
+
write() {
|
|
514
|
+
throw new Error("not supported!");
|
|
515
|
+
}
|
|
516
|
+
length() {
|
|
517
|
+
return -1;
|
|
518
|
+
}
|
|
519
|
+
};
|
|
363
520
|
var TCNetPackets = {
|
|
364
521
|
[2 /* OptIn */]: TCNetOptInPacket,
|
|
365
522
|
[3 /* OptOut */]: TCNetOptOutPacket,
|
|
@@ -385,22 +542,48 @@ var TCNetPackets = {
|
|
|
385
542
|
var TCNetDataPackets = {
|
|
386
543
|
[2 /* MetricsData */]: TCNetDataPacketMetrics,
|
|
387
544
|
[4 /* MetaData */]: TCNetDataPacketMetadata,
|
|
388
|
-
[8 /* BeatGridData */]:
|
|
389
|
-
|
|
390
|
-
[
|
|
391
|
-
|
|
392
|
-
[
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
545
|
+
[8 /* BeatGridData */]: TCNetDataPacketBeatGrid,
|
|
546
|
+
[12 /* CUEData */]: TCNetDataPacketCUE,
|
|
547
|
+
[16 /* SmallWaveFormData */]: TCNetDataPacketSmallWaveForm,
|
|
548
|
+
[32 /* BigWaveFormData */]: TCNetDataPacketBigWaveForm,
|
|
549
|
+
[150 /* MixerData */]: TCNetDataPacketMixer
|
|
550
|
+
};
|
|
551
|
+
|
|
552
|
+
// src/multi-packet.ts
|
|
553
|
+
var MultiPacketAssembler = class {
|
|
554
|
+
packets = /* @__PURE__ */ new Map();
|
|
555
|
+
totalPackets = 0;
|
|
556
|
+
// パケットを追加し、全パケットが揃ったら true を返す
|
|
557
|
+
add(buffer) {
|
|
558
|
+
if (buffer.length < 42) return false;
|
|
559
|
+
const newTotalPackets = buffer.readUInt32LE(30);
|
|
560
|
+
if (newTotalPackets === 0) return false;
|
|
561
|
+
if (this.totalPackets > 0 && newTotalPackets !== this.totalPackets) return false;
|
|
562
|
+
this.totalPackets = newTotalPackets;
|
|
563
|
+
const packetNo = buffer.readUInt32LE(34);
|
|
564
|
+
const clusterSize = buffer.readUInt32LE(38);
|
|
565
|
+
const dataStart = 42;
|
|
566
|
+
if (dataStart + clusterSize > buffer.length) return false;
|
|
567
|
+
this.packets.set(packetNo, Buffer.from(buffer.slice(dataStart, dataStart + clusterSize)));
|
|
568
|
+
return this.packets.size >= this.totalPackets;
|
|
569
|
+
}
|
|
570
|
+
// packetNo 順にソートしてデータを結合する
|
|
571
|
+
assemble() {
|
|
572
|
+
const sorted = [...this.packets.entries()].sort((a, b) => a[0] - b[0]);
|
|
573
|
+
return Buffer.concat(sorted.map(([, buf]) => buf));
|
|
574
|
+
}
|
|
575
|
+
// 状態をリセットする
|
|
576
|
+
reset() {
|
|
577
|
+
this.packets.clear();
|
|
578
|
+
this.totalPackets = 0;
|
|
579
|
+
}
|
|
398
580
|
};
|
|
399
581
|
|
|
400
582
|
// src/tcnet.ts
|
|
401
583
|
var EventEmitter = __require("events");
|
|
402
584
|
var TCNET_BROADCAST_PORT = 6e4;
|
|
403
585
|
var TCNET_TIMESTAMP_PORT = 60001;
|
|
586
|
+
var MULTI_PACKET_TYPES = /* @__PURE__ */ new Set([32 /* BigWaveFormData */, 8 /* BeatGridData */]);
|
|
404
587
|
var TCNetConfiguration = class {
|
|
405
588
|
logger = null;
|
|
406
589
|
unicastPort = 65023;
|
|
@@ -589,15 +772,44 @@ var TCNetClient = class extends EventEmitter {
|
|
|
589
772
|
dataPacket.dataType = packet.dataType;
|
|
590
773
|
dataPacket.layer = packet.layer;
|
|
591
774
|
dataPacket.read();
|
|
592
|
-
if (this.connected) {
|
|
593
|
-
this.emit("data", dataPacket);
|
|
594
|
-
}
|
|
595
775
|
const key = `${dataPacket.dataType}-${dataPacket.layer}`;
|
|
596
776
|
const pendingRequest = this.requests.get(key);
|
|
597
|
-
if (pendingRequest) {
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
777
|
+
if (pendingRequest && pendingRequest.assembler) {
|
|
778
|
+
const complete = pendingRequest.assembler.add(msg);
|
|
779
|
+
if (complete) {
|
|
780
|
+
const assembled = pendingRequest.assembler.assemble();
|
|
781
|
+
const finalPacket = new dataPacketClass();
|
|
782
|
+
finalPacket.buffer = msg;
|
|
783
|
+
finalPacket.header = mgmtHeader;
|
|
784
|
+
finalPacket.dataType = dataPacket.dataType;
|
|
785
|
+
finalPacket.layer = dataPacket.layer;
|
|
786
|
+
if ("readAssembled" in finalPacket && typeof finalPacket.readAssembled === "function") {
|
|
787
|
+
finalPacket.readAssembled(assembled);
|
|
788
|
+
}
|
|
789
|
+
this.requests.delete(key);
|
|
790
|
+
clearTimeout(pendingRequest.timeout);
|
|
791
|
+
if (this.connected) {
|
|
792
|
+
this.emit("data", finalPacket);
|
|
793
|
+
}
|
|
794
|
+
pendingRequest.resolve(finalPacket);
|
|
795
|
+
} else {
|
|
796
|
+
clearTimeout(pendingRequest.timeout);
|
|
797
|
+
pendingRequest.timeout = setTimeout(() => {
|
|
798
|
+
if (this.requests.delete(key)) {
|
|
799
|
+
pendingRequest.assembler?.reset();
|
|
800
|
+
pendingRequest.reject(new Error("Timeout while requesting data"));
|
|
801
|
+
}
|
|
802
|
+
}, this.config.requestTimeout);
|
|
803
|
+
}
|
|
804
|
+
} else {
|
|
805
|
+
if (this.connected) {
|
|
806
|
+
this.emit("data", dataPacket);
|
|
807
|
+
}
|
|
808
|
+
if (pendingRequest) {
|
|
809
|
+
this.requests.delete(key);
|
|
810
|
+
clearTimeout(pendingRequest.timeout);
|
|
811
|
+
pendingRequest.resolve(dataPacket);
|
|
812
|
+
}
|
|
601
813
|
}
|
|
602
814
|
}
|
|
603
815
|
} else if (packet instanceof TCNetOptInPacket) {
|
|
@@ -725,11 +937,18 @@ var TCNetClient = class extends EventEmitter {
|
|
|
725
937
|
request.layer = layer + 1;
|
|
726
938
|
const key = `${dataType}-${layer}`;
|
|
727
939
|
const timeout = setTimeout(() => {
|
|
728
|
-
|
|
940
|
+
const req = this.requests.get(key);
|
|
941
|
+
if (req && this.requests.delete(key)) {
|
|
942
|
+
req.assembler?.reset();
|
|
729
943
|
reject(new Error("Timeout while requesting data"));
|
|
730
944
|
}
|
|
731
945
|
}, this.config.requestTimeout);
|
|
732
|
-
this.requests.set(key, {
|
|
946
|
+
this.requests.set(key, {
|
|
947
|
+
resolve,
|
|
948
|
+
reject,
|
|
949
|
+
timeout,
|
|
950
|
+
assembler: MULTI_PACKET_TYPES.has(dataType) ? new MultiPacketAssembler() : void 0
|
|
951
|
+
});
|
|
733
952
|
this.sendServer(request).catch((err) => {
|
|
734
953
|
if (this.requests.delete(key)) {
|
|
735
954
|
clearTimeout(timeout);
|
|
@@ -740,12 +959,18 @@ var TCNetClient = class extends EventEmitter {
|
|
|
740
959
|
}
|
|
741
960
|
};
|
|
742
961
|
export {
|
|
962
|
+
MultiPacketAssembler,
|
|
743
963
|
NodeType,
|
|
744
964
|
TCNetClient,
|
|
745
965
|
TCNetConfiguration,
|
|
746
966
|
TCNetDataPacket,
|
|
967
|
+
TCNetDataPacketBeatGrid,
|
|
968
|
+
TCNetDataPacketBigWaveForm,
|
|
969
|
+
TCNetDataPacketCUE,
|
|
747
970
|
TCNetDataPacketMetadata,
|
|
748
971
|
TCNetDataPacketMetrics,
|
|
972
|
+
TCNetDataPacketMixer,
|
|
973
|
+
TCNetDataPacketSmallWaveForm,
|
|
749
974
|
TCNetDataPacketType,
|
|
750
975
|
TCNetDataPackets,
|
|
751
976
|
TCNetLayerStatus,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@9c5s/node-tcnet",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Implements the TC-Supply TCNet protocol used by ShowKontrol and ProDJLink Bridge app from Pioneer",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -19,15 +19,17 @@
|
|
|
19
19
|
"/dist"
|
|
20
20
|
],
|
|
21
21
|
"scripts": {
|
|
22
|
+
"test": "vitest run",
|
|
23
|
+
"test:watch": "vitest",
|
|
22
24
|
"example": "node --import tsx examples",
|
|
23
25
|
"build": "rm -rf dist && tsup && check-export-map",
|
|
24
26
|
"watch": "tsc -b -w",
|
|
25
27
|
"changeset": "changeset",
|
|
26
28
|
"clean": "tsc -b --clean",
|
|
27
29
|
"lint": "eslint . --ext ts --ignore-pattern '**/*.d.ts'",
|
|
28
|
-
"format:fix": "prettier --write \"./**/*.{ts,
|
|
29
|
-
"format:check": "prettier --check \"./**/*.{ts,
|
|
30
|
-
"format-pre-commit": "pretty-quick --staged --pattern '*/**/*.{ts,
|
|
30
|
+
"format:fix": "prettier --write \"./**/*.{ts,js,mjs,json,jsonc}\"",
|
|
31
|
+
"format:check": "prettier --check \"./**/*.{ts,js,mjs,json,jsonc}\"",
|
|
32
|
+
"format-pre-commit": "pretty-quick --staged --pattern '*/**/*.{ts,js,mjs,json,jsonc}'",
|
|
31
33
|
"mdlint": "markdownlint-cli2 \"docs/wiki/**/*.md\" \"*.{md,MD}\" \"!CHANGELOG.md\"",
|
|
32
34
|
"mdlint:fix": "markdownlint-cli2 --fix \"docs/wiki/**/*.md\" \"*.{md,MD}\" \"!CHANGELOG.md\"",
|
|
33
35
|
"textlint": "textlint \"docs/wiki/**/*.md\" \"README.MD\"",
|
|
@@ -35,9 +37,10 @@
|
|
|
35
37
|
},
|
|
36
38
|
"devDependencies": {
|
|
37
39
|
"@changesets/cli": "^2.27.9",
|
|
40
|
+
"@changesets/parse": "^0.4.3",
|
|
38
41
|
"@commitlint/cli": "^20.5.0",
|
|
39
42
|
"@commitlint/config-conventional": "^20.5.0",
|
|
40
|
-
"@types/node": "^
|
|
43
|
+
"@types/node": "^25.5.0",
|
|
41
44
|
"@typescript-eslint/eslint-plugin": "^4.22.1",
|
|
42
45
|
"@typescript-eslint/parser": "^4.22.1",
|
|
43
46
|
"check-export-map": "^1.3.1",
|
|
@@ -50,7 +53,8 @@
|
|
|
50
53
|
"textlint-rule-preset-ja-technical-writing": "^12.0.2",
|
|
51
54
|
"tsup": "^8.3.5",
|
|
52
55
|
"tsx": "^4.19.2",
|
|
53
|
-
"typescript": "^4.2.4"
|
|
56
|
+
"typescript": "^4.2.4",
|
|
57
|
+
"vitest": "^4.1.0"
|
|
54
58
|
},
|
|
55
59
|
"publishConfig": {
|
|
56
60
|
"access": "public",
|