@arcanewizards/tcnet 0.1.1
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/chunk-2L35L5P5.js +86 -0
- package/dist/chunk-7IUB6OJ3.cjs +435 -0
- package/dist/chunk-FAJ3XTSE.js +435 -0
- package/dist/chunk-VRXVI5VA.cjs +86 -0
- package/dist/chunk-VXAKTGOW.js +70 -0
- package/dist/chunk-VYNI4G3K.cjs +70 -0
- package/dist/errors.cjs +14 -0
- package/dist/errors.d.cts +17 -0
- package/dist/errors.d.ts +17 -0
- package/dist/errors.js +14 -0
- package/dist/index.cjs +6076 -0
- package/dist/index.d.cts +15 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +6076 -0
- package/dist/monitor.cjs +307 -0
- package/dist/monitor.d.cts +70 -0
- package/dist/monitor.d.ts +70 -0
- package/dist/monitor.js +307 -0
- package/dist/protocol.cjs +41 -0
- package/dist/protocol.d.cts +308 -0
- package/dist/protocol.d.ts +308 -0
- package/dist/protocol.js +41 -0
- package/dist/types.cjs +1 -0
- package/dist/types.d.cts +91 -0
- package/dist/types.d.ts +91 -0
- package/dist/types.js +0 -0
- package/dist/utils.cjs +17 -0
- package/dist/utils.d.cts +25 -0
- package/dist/utils.d.ts +25 -0
- package/dist/utils.js +17 -0
- package/package.json +68 -0
package/dist/monitor.js
ADDED
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import {
|
|
2
|
+
TCNET_LAYER_COUNT
|
|
3
|
+
} from "./chunk-FAJ3XTSE.js";
|
|
4
|
+
import {
|
|
5
|
+
calculateUniqueNodeId,
|
|
6
|
+
differsByMoreThan
|
|
7
|
+
} from "./chunk-2L35L5P5.js";
|
|
8
|
+
import {
|
|
9
|
+
TCNetProtocolError
|
|
10
|
+
} from "./chunk-VXAKTGOW.js";
|
|
11
|
+
|
|
12
|
+
// src/monitor.ts
|
|
13
|
+
import EventEmitter from "node:events";
|
|
14
|
+
var MAX_DELTA_MS = 10;
|
|
15
|
+
var layerDataIdToIndex = (dataLayerId) => {
|
|
16
|
+
return dataLayerId - 1;
|
|
17
|
+
};
|
|
18
|
+
var layerIndexToDataId = (index) => {
|
|
19
|
+
return index + 1;
|
|
20
|
+
};
|
|
21
|
+
var asLayerIndex = (rawNumber) => {
|
|
22
|
+
return rawNumber;
|
|
23
|
+
};
|
|
24
|
+
var getLayerId = (node, layer) => {
|
|
25
|
+
return `${calculateUniqueNodeId(node)}:${layer}`;
|
|
26
|
+
};
|
|
27
|
+
var TCNET_LAYER_INDEXES = Array.from(
|
|
28
|
+
{ length: TCNET_LAYER_COUNT },
|
|
29
|
+
(_, i) => asLayerIndex(i)
|
|
30
|
+
);
|
|
31
|
+
var createTCNetTimecodeMonitor = (tcNetNode, logger) => {
|
|
32
|
+
const events = new EventEmitter();
|
|
33
|
+
const on = events.on.bind(events);
|
|
34
|
+
const addListener = events.addListener.bind(events);
|
|
35
|
+
const removeListener = events.removeListener.bind(events);
|
|
36
|
+
const nodeStates = /* @__PURE__ */ new Map();
|
|
37
|
+
const getNodeState = (node) => {
|
|
38
|
+
const nodeKey = calculateUniqueNodeId(node);
|
|
39
|
+
let nodeState = nodeStates.get(nodeKey);
|
|
40
|
+
if (nodeState) {
|
|
41
|
+
return nodeState;
|
|
42
|
+
}
|
|
43
|
+
const layerInfoArray = new Array(TCNET_LAYER_COUNT);
|
|
44
|
+
nodeState = {
|
|
45
|
+
node,
|
|
46
|
+
trackInfo: /* @__PURE__ */ new Map(),
|
|
47
|
+
getLayerInfo: (i) => layerInfoArray[i]
|
|
48
|
+
};
|
|
49
|
+
nodeStates.set(nodeKey, nodeState);
|
|
50
|
+
for (const i of TCNET_LAYER_INDEXES) {
|
|
51
|
+
layerInfoArray[i] = {
|
|
52
|
+
name: null,
|
|
53
|
+
status: "IDLE",
|
|
54
|
+
trackId: null,
|
|
55
|
+
pitchBend: 0,
|
|
56
|
+
speed: 0,
|
|
57
|
+
layerState: {
|
|
58
|
+
layerId: `${i}`,
|
|
59
|
+
layerName: `Layer ${i + 1}`,
|
|
60
|
+
totalTime: null,
|
|
61
|
+
info: null,
|
|
62
|
+
playState: {
|
|
63
|
+
state: "stopped",
|
|
64
|
+
currentTimeMillis: 0,
|
|
65
|
+
speed: 1,
|
|
66
|
+
onAir: false
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
return nodeState;
|
|
72
|
+
};
|
|
73
|
+
const updateMetadataForLayer = (node, layer) => {
|
|
74
|
+
const trackID = getNodeState(node).getLayerInfo(layer)?.trackId;
|
|
75
|
+
if (typeof trackID !== "number") {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
tcNetNode.requestData(node, "METADATA", layerIndexToDataId(layer));
|
|
79
|
+
};
|
|
80
|
+
const updateMetricsForLayer = (node, layer) => {
|
|
81
|
+
const trackID = getNodeState(node).getLayerInfo(layer)?.trackId;
|
|
82
|
+
if (typeof trackID !== "number") {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
tcNetNode.requestData(node, "METRICS_DATA", layerIndexToDataId(layer));
|
|
86
|
+
};
|
|
87
|
+
const getProbableTrackInfo = (node, trackID) => {
|
|
88
|
+
if (trackID === null) {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
const trackInfoForTrack = getNodeState(node).trackInfo.get(trackID);
|
|
92
|
+
if (!trackInfoForTrack) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
let bestMatch = null;
|
|
96
|
+
for (const info of trackInfoForTrack.values()) {
|
|
97
|
+
const bestMatchCount = bestMatch?.matchCount ?? 0;
|
|
98
|
+
if (
|
|
99
|
+
// Best match if we've seen this track the most times
|
|
100
|
+
info.matchCount > bestMatchCount || // Or if we've seen it aa similar same amount of times,
|
|
101
|
+
// but more recently
|
|
102
|
+
info.matchCount > bestMatchCount - 4 && info.lastMatchTime > (bestMatch?.lastMatchTime ?? 0)
|
|
103
|
+
) {
|
|
104
|
+
bestMatch = info;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return bestMatch?.info ?? null;
|
|
108
|
+
};
|
|
109
|
+
const updatePlayingTimecode = (node, i, info, layer) => {
|
|
110
|
+
if (!layer) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const now = Date.now();
|
|
114
|
+
const effectiveStartTime = now - layer.currentTimeMillis / info.speed;
|
|
115
|
+
const trackInfo = getProbableTrackInfo(node, info.trackId);
|
|
116
|
+
if (!info.layerState || info.layerState.playState.state !== "playing" || info.layerState.playState.state === "playing" && (differsByMoreThan(
|
|
117
|
+
info.layerState.playState.effectiveStartTime,
|
|
118
|
+
effectiveStartTime,
|
|
119
|
+
MAX_DELTA_MS
|
|
120
|
+
) || differsByMoreThan(
|
|
121
|
+
info.layerState.totalTime?.timeMillis ?? 0,
|
|
122
|
+
layer.totalTimeMillis,
|
|
123
|
+
MAX_DELTA_MS
|
|
124
|
+
)) || info.layerState.info !== trackInfo || info.layerState.playState.speed !== info.speed || info.layerState.playState.onAir !== info.layerState.playState.onAir) {
|
|
125
|
+
info.layerState = {
|
|
126
|
+
layerId: getLayerId(node, i),
|
|
127
|
+
layerName: info.name ?? `Layer ${layerIndexToDataId(i)}`,
|
|
128
|
+
totalTime: layer.totalTimeMillis > 0 ? {
|
|
129
|
+
timeMillis: layer.totalTimeMillis,
|
|
130
|
+
precisionMillis: 1e3
|
|
131
|
+
} : null,
|
|
132
|
+
info: trackInfo,
|
|
133
|
+
playState: {
|
|
134
|
+
state: "playing",
|
|
135
|
+
effectiveStartTime,
|
|
136
|
+
speed: info.speed,
|
|
137
|
+
onAir: info.layerState?.playState.onAir
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
events.emit("timecode-changed", info.layerState);
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
const updatePausedTimecode = (node, i, info, layer) => {
|
|
144
|
+
if (!layer) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const trackInfo = getProbableTrackInfo(node, info.trackId);
|
|
148
|
+
if (!info.layerState || info.layerState.playState.state !== "stopped" || info.layerState.playState.state === "stopped" && (differsByMoreThan(
|
|
149
|
+
info.layerState.playState.currentTimeMillis,
|
|
150
|
+
layer.currentTimeMillis,
|
|
151
|
+
MAX_DELTA_MS
|
|
152
|
+
) || differsByMoreThan(
|
|
153
|
+
info.layerState.totalTime?.timeMillis ?? 0,
|
|
154
|
+
layer.totalTimeMillis,
|
|
155
|
+
MAX_DELTA_MS
|
|
156
|
+
)) || info.layerState.info !== trackInfo || info.layerState.playState.speed !== info.speed || info.layerState.playState.onAir !== info.layerState.playState.onAir) {
|
|
157
|
+
info.layerState = {
|
|
158
|
+
layerId: getLayerId(node, i),
|
|
159
|
+
layerName: info.name ?? `Layer ${layerIndexToDataId(i)}`,
|
|
160
|
+
totalTime: layer.totalTimeMillis > 0 ? {
|
|
161
|
+
timeMillis: layer.totalTimeMillis,
|
|
162
|
+
precisionMillis: 1e3
|
|
163
|
+
} : null,
|
|
164
|
+
info: trackInfo,
|
|
165
|
+
playState: {
|
|
166
|
+
state: "stopped",
|
|
167
|
+
currentTimeMillis: layer.currentTimeMillis,
|
|
168
|
+
speed: info.speed,
|
|
169
|
+
onAir: info.layerState?.playState.onAir
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
events.emit("timecode-changed", info.layerState);
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
tcNetNode.on("data", ({ packet, node }) => {
|
|
176
|
+
const nodeState = getNodeState(node);
|
|
177
|
+
if (packet.dataType === "METADATA") {
|
|
178
|
+
if (packet.trackId) {
|
|
179
|
+
logger.warn(
|
|
180
|
+
new TCNetProtocolError(
|
|
181
|
+
`Received unexpected trackId in METADATA packet, implementation needs to be updated to handle this!`
|
|
182
|
+
)
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
const info = {
|
|
186
|
+
title: packet.trackTitle || null,
|
|
187
|
+
artist: packet.trackArtist || null
|
|
188
|
+
};
|
|
189
|
+
if (!info.title && !info.artist) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
const trackID = nodeState.getLayerInfo(
|
|
193
|
+
layerDataIdToIndex(packet.layer)
|
|
194
|
+
)?.trackId;
|
|
195
|
+
if (typeof trackID !== "number") {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
let trackInfoForTrack = nodeState.trackInfo.get(trackID);
|
|
199
|
+
if (!trackInfoForTrack) {
|
|
200
|
+
trackInfoForTrack = /* @__PURE__ */ new Map();
|
|
201
|
+
nodeState.trackInfo.set(trackID, trackInfoForTrack);
|
|
202
|
+
}
|
|
203
|
+
const key = `${info.artist} - ${info.title}`;
|
|
204
|
+
const weightedInfo = trackInfoForTrack.get(key) || {
|
|
205
|
+
matchCount: 0,
|
|
206
|
+
lastMatchTime: Date.now(),
|
|
207
|
+
info
|
|
208
|
+
};
|
|
209
|
+
weightedInfo.matchCount++;
|
|
210
|
+
weightedInfo.lastMatchTime = Date.now();
|
|
211
|
+
trackInfoForTrack.set(key, weightedInfo);
|
|
212
|
+
} else if (packet.dataType === "METRICS_DATA") {
|
|
213
|
+
const info = nodeState.getLayerInfo(layerDataIdToIndex(packet.layer));
|
|
214
|
+
if (info) {
|
|
215
|
+
info.pitchBend = packet.pitchBend ?? 0;
|
|
216
|
+
info.speed = 1 + info.pitchBend / 1e4;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
tcNetNode.on("time", ({ packet, node }) => {
|
|
221
|
+
const nodeState = getNodeState(node);
|
|
222
|
+
for (const i of TCNET_LAYER_INDEXES) {
|
|
223
|
+
const info = nodeState.getLayerInfo(i);
|
|
224
|
+
switch (info?.status) {
|
|
225
|
+
case "PLAYING":
|
|
226
|
+
case "LOOPING":
|
|
227
|
+
case "CUEDOWN":
|
|
228
|
+
updatePlayingTimecode(node, i, info, packet.layers[i]);
|
|
229
|
+
break;
|
|
230
|
+
case "PAUSED":
|
|
231
|
+
case "STOPPED":
|
|
232
|
+
case "LOADING":
|
|
233
|
+
case "PLATTERDOWN":
|
|
234
|
+
case "HOLD":
|
|
235
|
+
case "FFWD":
|
|
236
|
+
case "FFRV":
|
|
237
|
+
updatePausedTimecode(node, i, info, packet.layers[i]);
|
|
238
|
+
break;
|
|
239
|
+
case "UNKNOWN":
|
|
240
|
+
case "IDLE":
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
tcNetNode.on("node-status", ({ packet, node }) => {
|
|
245
|
+
for (const i of TCNET_LAYER_INDEXES) {
|
|
246
|
+
const layer = packet.layers[i];
|
|
247
|
+
const info = getNodeState(node).getLayerInfo(i);
|
|
248
|
+
if (!info || !layer) {
|
|
249
|
+
throw new Error("Inconsistent layer indexes");
|
|
250
|
+
}
|
|
251
|
+
info.status = layer.status;
|
|
252
|
+
info.name = layer.name;
|
|
253
|
+
if (info.trackId !== layer.trackId && layer.trackId > 0) {
|
|
254
|
+
logger.info(
|
|
255
|
+
`Updated trackID for layer ${i}: ${info.trackId} -> ${layer.trackId}`
|
|
256
|
+
);
|
|
257
|
+
info.trackId = layer.trackId;
|
|
258
|
+
updateMetadataForLayer(node, i);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
tcNetNode.on("nodes-changed", (nodes) => {
|
|
263
|
+
const knownNodeIds = new Set(
|
|
264
|
+
Object.values(nodes).map((node) => calculateUniqueNodeId(node))
|
|
265
|
+
);
|
|
266
|
+
for (const [nodeId, nodeState] of nodeStates.entries()) {
|
|
267
|
+
if (!knownNodeIds.has(nodeId)) {
|
|
268
|
+
for (const i of TCNET_LAYER_INDEXES) {
|
|
269
|
+
const layerState = nodeState.getLayerInfo(i)?.layerState;
|
|
270
|
+
if (layerState) {
|
|
271
|
+
events.emit("layer-removed", { layerId: layerState.layerId });
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
nodeStates.delete(nodeId);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
const updateLoadedTracks = () => {
|
|
279
|
+
for (const nodeState of nodeStates.values()) {
|
|
280
|
+
for (const i of TCNET_LAYER_INDEXES) {
|
|
281
|
+
const info = nodeState.getLayerInfo(i);
|
|
282
|
+
if (info?.trackId) {
|
|
283
|
+
updateMetadataForLayer(nodeState.node, i);
|
|
284
|
+
updateMetricsForLayer(nodeState.node, i);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
let autoUpdater = null;
|
|
290
|
+
tcNetNode.on("ready", () => {
|
|
291
|
+
autoUpdater = setInterval(updateLoadedTracks, 1e3);
|
|
292
|
+
updateLoadedTracks();
|
|
293
|
+
});
|
|
294
|
+
tcNetNode.on("destroy", () => {
|
|
295
|
+
if (autoUpdater) {
|
|
296
|
+
clearInterval(autoUpdater);
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
return {
|
|
300
|
+
on,
|
|
301
|
+
addListener,
|
|
302
|
+
removeListener
|
|
303
|
+
};
|
|
304
|
+
};
|
|
305
|
+
export {
|
|
306
|
+
createTCNetTimecodeMonitor
|
|
307
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true});
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
var _chunk7IUB6OJ3cjs = require('./chunk-7IUB6OJ3.cjs');
|
|
21
|
+
require('./chunk-VYNI4G3K.cjs');
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
exports.MAX_NODE_ID = _chunk7IUB6OJ3cjs.MAX_NODE_ID; exports.MGMT_HEADER_MINOR_VERSION = _chunk7IUB6OJ3cjs.MGMT_HEADER_MINOR_VERSION; exports.MGMT_HEADER_VERSION = _chunk7IUB6OJ3cjs.MGMT_HEADER_VERSION; exports.TCNET_LAYER_COUNT = _chunk7IUB6OJ3cjs.TCNET_LAYER_COUNT; exports.generateTcNetPortNodeOptionsFlags = _chunk7IUB6OJ3cjs.generateTcNetPortNodeOptionsFlags; exports.getTcNetDataPacketType = _chunk7IUB6OJ3cjs.getTcNetDataPacketType; exports.getTcNetLayerState = _chunk7IUB6OJ3cjs.getTcNetLayerState; exports.getTcNetLayerTCState = _chunk7IUB6OJ3cjs.getTcNetLayerTCState; exports.getTcNetMessageType = _chunk7IUB6OJ3cjs.getTcNetMessageType; exports.getTcNetMixerType = _chunk7IUB6OJ3cjs.getTcNetMixerType; exports.getTcNetNodeType = _chunk7IUB6OJ3cjs.getTcNetNodeType; exports.parseManagementHeader = _chunk7IUB6OJ3cjs.parseManagementHeader; exports.parsePacket = _chunk7IUB6OJ3cjs.parsePacket; exports.parseTcNetPortNodeOptions = _chunk7IUB6OJ3cjs.parseTcNetPortNodeOptions; exports.writeManagementHeader = _chunk7IUB6OJ3cjs.writeManagementHeader; exports.writeOptInPacket = _chunk7IUB6OJ3cjs.writeOptInPacket; exports.writeOptOutPacket = _chunk7IUB6OJ3cjs.writeOptOutPacket; exports.writeRequestPacket = _chunk7IUB6OJ3cjs.writeRequestPacket;
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Branded Layer ID as provided / required in TCNet Data Packets.
|
|
3
|
+
*
|
|
4
|
+
* Allows for type-checking to avoid confusion between
|
|
5
|
+
* layer indices used in status packets and layer IDs used in data packets,
|
|
6
|
+
* which are not necessarily the same.
|
|
7
|
+
*
|
|
8
|
+
* Example: 1=LAYER1, 2=LAYER2, 3=LAYER3, 4=LAYER4, 5=LAYER A, 6=LAYER B, 7=MASTER OUT, 8=RESERVED
|
|
9
|
+
*/
|
|
10
|
+
type LayerDataId = number & {
|
|
11
|
+
__brand: 'LayerDataId';
|
|
12
|
+
};
|
|
13
|
+
declare const TCNET_LAYER_COUNT = 8;
|
|
14
|
+
declare const TCNET_LAYER_STATE_IDS: Readonly<{
|
|
15
|
+
IDLE: 0;
|
|
16
|
+
/**
|
|
17
|
+
* Not in spec, but seems to be what's observed.
|
|
18
|
+
*/
|
|
19
|
+
LOADING: 2;
|
|
20
|
+
PLAYING: 3;
|
|
21
|
+
LOOPING: 4;
|
|
22
|
+
PAUSED: 5;
|
|
23
|
+
STOPPED: 6;
|
|
24
|
+
CUEDOWN: 7;
|
|
25
|
+
PLATTERDOWN: 8;
|
|
26
|
+
FFWD: 9;
|
|
27
|
+
FFRV: 10;
|
|
28
|
+
HOLD: 11;
|
|
29
|
+
/**
|
|
30
|
+
* Not in spec, but seems to be what's observed.
|
|
31
|
+
*/
|
|
32
|
+
END: 17;
|
|
33
|
+
/**
|
|
34
|
+
* TODO: determine what this means, seems to be disconnected state?
|
|
35
|
+
*/
|
|
36
|
+
UNKNOWN: 255;
|
|
37
|
+
}>;
|
|
38
|
+
type TCNetLayerState = keyof typeof TCNET_LAYER_STATE_IDS;
|
|
39
|
+
declare const getTcNetLayerState: (id: number) => TCNetLayerState;
|
|
40
|
+
declare const TCNET_MIXER_TYPE_IDS: Readonly<{
|
|
41
|
+
STANDARD: 0;
|
|
42
|
+
EXTENDED: 2;
|
|
43
|
+
}>;
|
|
44
|
+
type TCNetMixerType = keyof typeof TCNET_MIXER_TYPE_IDS;
|
|
45
|
+
declare const getTcNetMixerType: (id: number) => TCNetMixerType;
|
|
46
|
+
declare const TCNET_MESSAGE_TYPE_IDS: Readonly<{
|
|
47
|
+
OPT_IN: 2;
|
|
48
|
+
OPT_OUT: 3;
|
|
49
|
+
STATUS: 5;
|
|
50
|
+
TIME_SYNC: 10;
|
|
51
|
+
ERROR: 13;
|
|
52
|
+
REQUEST: 20;
|
|
53
|
+
APPLICATION_SPECIFIC_DATA_1: 30;
|
|
54
|
+
CONTROL: 101;
|
|
55
|
+
TEXT_DATA: 128;
|
|
56
|
+
KEYBOARD_DATA: 132;
|
|
57
|
+
DATA: 200;
|
|
58
|
+
FILE: 204;
|
|
59
|
+
APPLICATION_SPECIFIC_DATA_2: 213;
|
|
60
|
+
TIME: 254;
|
|
61
|
+
}>;
|
|
62
|
+
type TCNetMessageType = keyof typeof TCNET_MESSAGE_TYPE_IDS;
|
|
63
|
+
declare const getTcNetMessageType: (id: number) => TCNetMessageType;
|
|
64
|
+
declare const TCNET_DATA_PACKET_TYPE_IDS: Readonly<{
|
|
65
|
+
METRICS_DATA: 2;
|
|
66
|
+
METADATA: 4;
|
|
67
|
+
BEAT_GRID_DATA: 8;
|
|
68
|
+
CUE_DATA: 12;
|
|
69
|
+
SMALL_WAVEFORM: 16;
|
|
70
|
+
BIG_WAVEFORM: 32;
|
|
71
|
+
MIXER_DATA: 150;
|
|
72
|
+
}>;
|
|
73
|
+
type TCNetDataPacketType = keyof typeof TCNET_DATA_PACKET_TYPE_IDS;
|
|
74
|
+
declare const getTcNetDataPacketType: (id: number) => TCNetDataPacketType;
|
|
75
|
+
declare const TCNET_NODE_TYPE_IDS: Readonly<{
|
|
76
|
+
AUTO: 1;
|
|
77
|
+
MASTER: 2;
|
|
78
|
+
SLAVE: 4;
|
|
79
|
+
REPEATER: 8;
|
|
80
|
+
}>;
|
|
81
|
+
type TCNetNodeType = keyof typeof TCNET_NODE_TYPE_IDS;
|
|
82
|
+
declare const getTcNetNodeType: (id: number) => TCNetNodeType;
|
|
83
|
+
declare const TCNET_PORT_NODE_OPTION_FLAGS: Readonly<{
|
|
84
|
+
NEED_AUTHENTICATION: 1;
|
|
85
|
+
/**
|
|
86
|
+
* Listens to TCNet Control Messages
|
|
87
|
+
*/
|
|
88
|
+
SUPPORTS_TCNCM: 2;
|
|
89
|
+
/**
|
|
90
|
+
* Listens to TCNet Application Specific Data Packets
|
|
91
|
+
*/
|
|
92
|
+
SUPPORTS_TCNASDP: 4;
|
|
93
|
+
/**
|
|
94
|
+
* Do not disturb/Sleeping. Node will request data itself if needed to avoid traffic
|
|
95
|
+
*/
|
|
96
|
+
DO_NOT_DISTURB: 8;
|
|
97
|
+
}>;
|
|
98
|
+
type TCNetPortNodeOption = keyof typeof TCNET_PORT_NODE_OPTION_FLAGS;
|
|
99
|
+
type TCNetPortNodeOptions = Partial<Record<TCNetPortNodeOption, true | undefined>>;
|
|
100
|
+
declare const parseTcNetPortNodeOptions: (flags: number) => TCNetPortNodeOptions;
|
|
101
|
+
declare const generateTcNetPortNodeOptionsFlags: (options: TCNetPortNodeOptions) => number;
|
|
102
|
+
declare const TCNET_LAYER_TC_STATE_IDS: Readonly<{
|
|
103
|
+
STOPPED: 0;
|
|
104
|
+
RUNNING: 1;
|
|
105
|
+
FORCE_RESYNC: 2;
|
|
106
|
+
}>;
|
|
107
|
+
type TCNetLayerTCState = keyof typeof TCNET_LAYER_TC_STATE_IDS;
|
|
108
|
+
declare const getTcNetLayerTCState: (id: number) => TCNetLayerTCState;
|
|
109
|
+
declare const MAX_NODE_ID: number;
|
|
110
|
+
declare const MGMT_HEADER_VERSION = 3;
|
|
111
|
+
declare const MGMT_HEADER_MINOR_VERSION = 5;
|
|
112
|
+
type TCNetManagementHeader = {
|
|
113
|
+
/**
|
|
114
|
+
* Unique Node ID. When multiple applications/services are running on same IP, this number must be unique
|
|
115
|
+
*/
|
|
116
|
+
nodeId: number;
|
|
117
|
+
protocolVersionMajor: number;
|
|
118
|
+
protocolVersionMinor: number;
|
|
119
|
+
messageType: TCNetMessageType;
|
|
120
|
+
/**
|
|
121
|
+
* GW Code of software/machine/source that sends packet.
|
|
122
|
+
*/
|
|
123
|
+
nodeName: Buffer;
|
|
124
|
+
/**
|
|
125
|
+
* Sequence number of packet
|
|
126
|
+
*/
|
|
127
|
+
seq: number;
|
|
128
|
+
nodeType: TCNetNodeType;
|
|
129
|
+
nodeOptions: TCNetPortNodeOptions;
|
|
130
|
+
timestamp: number;
|
|
131
|
+
};
|
|
132
|
+
type TCNetManagementHeaderWithoutMessageType = Omit<TCNetManagementHeader, 'messageType'>;
|
|
133
|
+
declare const writeManagementHeader: (buffer: Buffer, header: TCNetManagementHeaderWithoutMessageType, messageType: TCNetMessageType) => void;
|
|
134
|
+
declare const parseManagementHeader: (buffer: Buffer) => TCNetManagementHeader;
|
|
135
|
+
type TCNetBasePacket<T extends TCNetMessageType> = {
|
|
136
|
+
header: TCNetManagementHeaderWithoutMessageType;
|
|
137
|
+
type: T;
|
|
138
|
+
};
|
|
139
|
+
type TCNetOptInPacket = TCNetBasePacket<'OPT_IN'> & {
|
|
140
|
+
/**
|
|
141
|
+
* Number of nodes registered by system
|
|
142
|
+
*/
|
|
143
|
+
nodeCount: number;
|
|
144
|
+
/**
|
|
145
|
+
* Listener port of node (Used to receive unicast messages)
|
|
146
|
+
*/
|
|
147
|
+
nodeListenerPort: number;
|
|
148
|
+
uptime: number;
|
|
149
|
+
vendorName: Buffer;
|
|
150
|
+
applicationName: Buffer;
|
|
151
|
+
applicationVersion: Buffer;
|
|
152
|
+
};
|
|
153
|
+
declare const writeOptInPacket: (data: Omit<TCNetOptInPacket, "type">) => Buffer;
|
|
154
|
+
type TCNetOptOutPacket = TCNetBasePacket<'OPT_OUT'> & Pick<TCNetOptInPacket, 'nodeCount' | 'nodeListenerPort'>;
|
|
155
|
+
declare const writeOptOutPacket: (data: Omit<TCNetOptOutPacket, "type">) => Buffer;
|
|
156
|
+
type TCNetStatusPacketLayer = {
|
|
157
|
+
source: number;
|
|
158
|
+
status: TCNetLayerState;
|
|
159
|
+
trackId: number;
|
|
160
|
+
name: string;
|
|
161
|
+
};
|
|
162
|
+
type TCNetStatusPacket = TCNetBasePacket<'STATUS'> & {
|
|
163
|
+
nodeCount: number;
|
|
164
|
+
nodeListenerPort: number;
|
|
165
|
+
smpteMode: number;
|
|
166
|
+
/**
|
|
167
|
+
* Auto Master mode on node (0=Disabled, 1=HTP Master, 2=Link Master)
|
|
168
|
+
*/
|
|
169
|
+
autoMasterMode: number;
|
|
170
|
+
layers: TCNetStatusPacketLayer[];
|
|
171
|
+
};
|
|
172
|
+
type TCNetRequestPacket = TCNetBasePacket<'REQUEST'> & {
|
|
173
|
+
dataType: TCNetDataPacketType;
|
|
174
|
+
layer: LayerDataId;
|
|
175
|
+
};
|
|
176
|
+
declare const writeRequestPacket: (data: Omit<TCNetRequestPacket, "type">) => Buffer;
|
|
177
|
+
type TCNetApplicationSpecificData1Packet = TCNetBasePacket<'APPLICATION_SPECIFIC_DATA_1'>;
|
|
178
|
+
type TCNetDataPacket<D extends TCNetDataPacketType> = TCNetBasePacket<'DATA'> & {
|
|
179
|
+
dataType: D;
|
|
180
|
+
};
|
|
181
|
+
type TCNetMetricsDataPacket = TCNetDataPacket<'METRICS_DATA'> & {
|
|
182
|
+
/**
|
|
183
|
+
* Layer number of layer sending data.
|
|
184
|
+
*
|
|
185
|
+
* see {@link LayerDataId}
|
|
186
|
+
*/
|
|
187
|
+
layer: LayerDataId;
|
|
188
|
+
/** Play head status of layer */
|
|
189
|
+
state: TCNetLayerState;
|
|
190
|
+
/** Sync master status of layer. Example use of this status is to follow the current active layer and allows auto cue to this layer. */
|
|
191
|
+
syncMaster: number;
|
|
192
|
+
/**
|
|
193
|
+
* Range 1-4
|
|
194
|
+
*/
|
|
195
|
+
beatMarker: number;
|
|
196
|
+
trackLengthMillis: number;
|
|
197
|
+
/**
|
|
198
|
+
* Play head position of layer
|
|
199
|
+
*/
|
|
200
|
+
currentPositionMillis: number;
|
|
201
|
+
/**
|
|
202
|
+
* Play head speed on layer
|
|
203
|
+
*
|
|
204
|
+
* 0~65536 (Where 32768 = 100% speed, 0 = 0% Speed, 65536=200% speed)
|
|
205
|
+
*/
|
|
206
|
+
speed: number;
|
|
207
|
+
beatNumber: number;
|
|
208
|
+
/** Play head BPM speed of layer. Example: 0.01~999.99 */
|
|
209
|
+
bpm: number;
|
|
210
|
+
/**
|
|
211
|
+
* Play head speed bend value of layer. (Used for live adjust.)
|
|
212
|
+
* Example: 0~65536 (Where 32768 = 100% speed, 0 = 0% Speed, 65536=200% speed)
|
|
213
|
+
*/
|
|
214
|
+
pitchBend: number;
|
|
215
|
+
/**
|
|
216
|
+
* Track ID number of the track that is loaded on layer. This is usually the database ID number. (Used to reflect track selection changes)
|
|
217
|
+
*/
|
|
218
|
+
trackId: number;
|
|
219
|
+
};
|
|
220
|
+
type TCNetMetadataDataPacket = TCNetDataPacket<'METADATA'> & {
|
|
221
|
+
/**
|
|
222
|
+
* Layer number of layer sending data.
|
|
223
|
+
*
|
|
224
|
+
* see {@link LayerDataId}
|
|
225
|
+
*/
|
|
226
|
+
layer: LayerDataId;
|
|
227
|
+
trackArtist: string;
|
|
228
|
+
trackTitle: string;
|
|
229
|
+
trackKey: number;
|
|
230
|
+
/**
|
|
231
|
+
* @deprecated Note that ShowKontrol seems to not consistently send the correct
|
|
232
|
+
* trackID here, and you need to rely on the trackID from the status packet instead.
|
|
233
|
+
*/
|
|
234
|
+
trackId: number;
|
|
235
|
+
};
|
|
236
|
+
type TCNetMixerDataPacket = TCNetDataPacket<'MIXER_DATA'> & {
|
|
237
|
+
mixerId: number;
|
|
238
|
+
mixerType: TCNetMixerType;
|
|
239
|
+
mixerName: string;
|
|
240
|
+
micEqHi: number;
|
|
241
|
+
micEqLow: number;
|
|
242
|
+
masterAudioLevel: number;
|
|
243
|
+
masterFaderLevel: number;
|
|
244
|
+
};
|
|
245
|
+
type AnyTCNetDataPacket = TCNetMetricsDataPacket | TCNetMetadataDataPacket | TCNetMixerDataPacket;
|
|
246
|
+
/**
|
|
247
|
+
* 24=24FPS (FILM)
|
|
248
|
+
* 25=25FPS (EBU)
|
|
249
|
+
* 29=29.7FPS (DF)
|
|
250
|
+
* 30=30FPS (NTSC)
|
|
251
|
+
*/
|
|
252
|
+
type SMPTEFramerate = 24 | 25 | 29 | 30;
|
|
253
|
+
type TCNetTimePacketLayer = {
|
|
254
|
+
currentTimeMillis: number;
|
|
255
|
+
totalTimeMillis: number;
|
|
256
|
+
/**
|
|
257
|
+
* 0 = unknown, 1-4 = beat position in current bar
|
|
258
|
+
*/
|
|
259
|
+
beatMarker: number;
|
|
260
|
+
state: TCNetLayerState;
|
|
261
|
+
smpte: {
|
|
262
|
+
/**
|
|
263
|
+
* See {@link SMPTEFramerate}
|
|
264
|
+
*/
|
|
265
|
+
mode: null | SMPTEFramerate;
|
|
266
|
+
state: TCNetLayerTCState;
|
|
267
|
+
hours: number;
|
|
268
|
+
minutes: number;
|
|
269
|
+
seconds: number;
|
|
270
|
+
frames: number;
|
|
271
|
+
};
|
|
272
|
+
/**
|
|
273
|
+
* OnAir State & Fader Position (0-255) (Example: 0=Not on Air, >=1 =On Air)
|
|
274
|
+
*
|
|
275
|
+
* Note: ShowKontrol does not seem to include the actual mixer value here,
|
|
276
|
+
* and only sends 0 or 1.
|
|
277
|
+
*/
|
|
278
|
+
onAir: number;
|
|
279
|
+
};
|
|
280
|
+
type TCNetTimePacketLayers = [
|
|
281
|
+
TCNetTimePacketLayer,
|
|
282
|
+
TCNetTimePacketLayer,
|
|
283
|
+
TCNetTimePacketLayer,
|
|
284
|
+
TCNetTimePacketLayer,
|
|
285
|
+
TCNetTimePacketLayer,
|
|
286
|
+
TCNetTimePacketLayer,
|
|
287
|
+
TCNetTimePacketLayer,
|
|
288
|
+
TCNetTimePacketLayer
|
|
289
|
+
];
|
|
290
|
+
type TCNetTimePacket = TCNetBasePacket<'TIME'> & {
|
|
291
|
+
/**
|
|
292
|
+
* All layers:
|
|
293
|
+
* - 0 - L1
|
|
294
|
+
* - 1 - L2
|
|
295
|
+
* - 2 - L3
|
|
296
|
+
* - 3 - L4
|
|
297
|
+
* - 4 - LA
|
|
298
|
+
* - 5 - LB
|
|
299
|
+
* - 6 - MASTER
|
|
300
|
+
* - 7 - LC
|
|
301
|
+
*/
|
|
302
|
+
layers: TCNetTimePacketLayers;
|
|
303
|
+
generalSmpteFramerate: SMPTEFramerate;
|
|
304
|
+
};
|
|
305
|
+
type TCNetPacket = TCNetOptInPacket | TCNetOptOutPacket | TCNetStatusPacket | TCNetApplicationSpecificData1Packet | TCNetTimePacket | AnyTCNetDataPacket;
|
|
306
|
+
declare const parsePacket: (buffer: Buffer) => TCNetPacket;
|
|
307
|
+
|
|
308
|
+
export { type AnyTCNetDataPacket, type LayerDataId, MAX_NODE_ID, MGMT_HEADER_MINOR_VERSION, MGMT_HEADER_VERSION, type SMPTEFramerate, TCNET_LAYER_COUNT, type TCNetApplicationSpecificData1Packet, type TCNetDataPacket, type TCNetDataPacketType, type TCNetLayerState, type TCNetLayerTCState, type TCNetManagementHeader, type TCNetManagementHeaderWithoutMessageType, type TCNetMessageType, type TCNetMetadataDataPacket, type TCNetMetricsDataPacket, type TCNetMixerDataPacket, type TCNetMixerType, type TCNetNodeType, type TCNetOptInPacket, type TCNetOptOutPacket, type TCNetPacket, type TCNetPortNodeOption, type TCNetPortNodeOptions, type TCNetRequestPacket, type TCNetStatusPacket, type TCNetStatusPacketLayer, type TCNetTimePacket, type TCNetTimePacketLayer, generateTcNetPortNodeOptionsFlags, getTcNetDataPacketType, getTcNetLayerState, getTcNetLayerTCState, getTcNetMessageType, getTcNetMixerType, getTcNetNodeType, parseManagementHeader, parsePacket, parseTcNetPortNodeOptions, writeManagementHeader, writeOptInPacket, writeOptOutPacket, writeRequestPacket };
|