@djodjonx/x32-simulator 0.0.2 → 0.0.3
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/CHANGELOG.md +8 -0
- package/README.md +28 -0
- package/dist/{UdpNetworkGateway-BrroQ6-Q.mjs → SchemaRegistry-BRVgnyaA.mjs} +990 -2
- package/dist/{UdpNetworkGateway-Ccdd7Us5.cjs → SchemaRegistry-CfDtw84j.cjs} +1033 -3
- package/dist/index.cjs +160 -6
- package/dist/index.d.cts +61 -11
- package/dist/index.d.mts +61 -11
- package/dist/index.mjs +146 -2
- package/dist/server.cjs +8 -927
- package/dist/server.mjs +1 -920
- package/package.json +1 -1
- package/src/application/use-cases/SimulationService.ts +24 -0
- package/src/domain/entities/X32State.ts +1 -1
- package/src/domain/models/X32Address.ts +2 -8
- package/src/domain/models/types.ts +0 -10
- package/src/infrastructure/repositories/InMemoryStateRepository.ts +16 -0
- package/src/infrastructure/services/UdpNetworkGateway.ts +29 -0
- package/src/presentation/library/library.ts +131 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as os from "os";
|
|
2
2
|
import { EventEmitter } from "node:events";
|
|
3
3
|
import * as dgram from "node:dgram";
|
|
4
|
+
import { Buffer as Buffer$1 } from "node:buffer";
|
|
4
5
|
|
|
5
6
|
//#region src/application/use-cases/ProcessPacketUseCase.ts
|
|
6
7
|
var ProcessPacketUseCase = class {
|
|
@@ -832,6 +833,10 @@ function getLocalIp() {
|
|
|
832
833
|
for (const name of Object.keys(interfaces)) for (const iface of interfaces[name]) if (iface.family === "IPv4" && !iface.internal) return iface.address;
|
|
833
834
|
return "127.0.0.1";
|
|
834
835
|
}
|
|
836
|
+
/**
|
|
837
|
+
* The core service that manages the X32 simulation, including networking,
|
|
838
|
+
* state management, and OSC message handling.
|
|
839
|
+
*/
|
|
835
840
|
var SimulationService = class {
|
|
836
841
|
subscriptionManager;
|
|
837
842
|
messageHandler;
|
|
@@ -842,6 +847,17 @@ var SimulationService = class {
|
|
|
842
847
|
staticResponseService;
|
|
843
848
|
updateInterval = null;
|
|
844
849
|
cleanupInterval = null;
|
|
850
|
+
/**
|
|
851
|
+
* Creates a new SimulationService instance.
|
|
852
|
+
* @param gateway - The network gateway for OSC communication.
|
|
853
|
+
* @param logger - The logger service.
|
|
854
|
+
* @param stateRepo - The repository managing the mixer state.
|
|
855
|
+
* @param schemaRegistry - The registry for X32 OSC schema.
|
|
856
|
+
* @param port - UDP port to listen on (default 10023).
|
|
857
|
+
* @param ip - IP address to bind to (default '0.0.0.0').
|
|
858
|
+
* @param name - Reported console name.
|
|
859
|
+
* @param model - Reported console model.
|
|
860
|
+
*/
|
|
845
861
|
constructor(gateway, logger, stateRepo, schemaRegistry, port = 10023, ip = "0.0.0.0", name = "osc-server", model = "X32") {
|
|
846
862
|
this.gateway = gateway;
|
|
847
863
|
this.logger = logger;
|
|
@@ -864,6 +880,9 @@ var SimulationService = class {
|
|
|
864
880
|
});
|
|
865
881
|
this.gateway.onPacket((packet, source) => this.processPacket.execute(packet, source));
|
|
866
882
|
}
|
|
883
|
+
/**
|
|
884
|
+
* Starts the simulator server and internal loops.
|
|
885
|
+
*/
|
|
867
886
|
async start() {
|
|
868
887
|
await this.gateway.start(this.port, this.ip);
|
|
869
888
|
this.cleanupInterval = setInterval(() => {
|
|
@@ -873,11 +892,17 @@ var SimulationService = class {
|
|
|
873
892
|
this.broadcastUpdates.execute();
|
|
874
893
|
}, 100);
|
|
875
894
|
}
|
|
895
|
+
/**
|
|
896
|
+
* Stops the simulator server and stops all internal loops.
|
|
897
|
+
*/
|
|
876
898
|
async stop() {
|
|
877
899
|
if (this.updateInterval) clearInterval(this.updateInterval);
|
|
878
900
|
if (this.cleanupInterval) clearInterval(this.cleanupInterval);
|
|
879
901
|
await this.gateway.stop();
|
|
880
902
|
}
|
|
903
|
+
/**
|
|
904
|
+
* Resets the mixer state to default values.
|
|
905
|
+
*/
|
|
881
906
|
resetState() {
|
|
882
907
|
this.stateRepo.reset();
|
|
883
908
|
}
|
|
@@ -887,7 +912,7 @@ var SimulationService = class {
|
|
|
887
912
|
//#region src/domain/entities/X32State.ts
|
|
888
913
|
/**
|
|
889
914
|
* Manages the internal "Digital Twin" state of the X32 console.
|
|
890
|
-
* acts as a single source of truth for all parameters.
|
|
915
|
+
* It acts as a single source of truth for all parameters.
|
|
891
916
|
* Emits 'change' events whenever a value is updated.
|
|
892
917
|
*/
|
|
893
918
|
var X32State = class extends EventEmitter {
|
|
@@ -954,17 +979,76 @@ var X32State = class extends EventEmitter {
|
|
|
954
979
|
}
|
|
955
980
|
};
|
|
956
981
|
|
|
982
|
+
//#endregion
|
|
983
|
+
//#region src/domain/models/X32Node.ts
|
|
984
|
+
/**
|
|
985
|
+
* Metadata for a single state node in the X32 schema.
|
|
986
|
+
* Represents a "Knob" or "Variable" on the console.
|
|
987
|
+
*/
|
|
988
|
+
var X32Node = class X32Node {
|
|
989
|
+
/** Value type (f=float, i=int, s=string). */
|
|
990
|
+
type;
|
|
991
|
+
/** Default value for reset. */
|
|
992
|
+
default;
|
|
993
|
+
/**
|
|
994
|
+
* Creates a new X32Node.
|
|
995
|
+
* @param type - The OSC data type ('f', 'i', 's').
|
|
996
|
+
* @param defaultValue - The default value.
|
|
997
|
+
*/
|
|
998
|
+
constructor(type, defaultValue) {
|
|
999
|
+
this.type = type;
|
|
1000
|
+
this.default = defaultValue;
|
|
1001
|
+
}
|
|
1002
|
+
/**
|
|
1003
|
+
* Validates if a value is compatible with this node's type.
|
|
1004
|
+
* @param value - The value to check.
|
|
1005
|
+
* @returns True if valid.
|
|
1006
|
+
*/
|
|
1007
|
+
validate(value) {
|
|
1008
|
+
if (this.type === "f") return typeof value === "number";
|
|
1009
|
+
if (this.type === "i") return typeof value === "number";
|
|
1010
|
+
if (this.type === "s") return typeof value === "string";
|
|
1011
|
+
return false;
|
|
1012
|
+
}
|
|
1013
|
+
/**
|
|
1014
|
+
* Factory method to create from a plain object (for compatibility/migration).
|
|
1015
|
+
* @param obj - Plain object.
|
|
1016
|
+
* @param obj.type - OSC data type.
|
|
1017
|
+
* @param obj.default - Default value.
|
|
1018
|
+
* @returns A new X32Node instance.
|
|
1019
|
+
*/
|
|
1020
|
+
static from(obj) {
|
|
1021
|
+
return new X32Node(obj.type, obj.default);
|
|
1022
|
+
}
|
|
1023
|
+
};
|
|
1024
|
+
|
|
957
1025
|
//#endregion
|
|
958
1026
|
//#region src/infrastructure/repositories/InMemoryStateRepository.ts
|
|
1027
|
+
/**
|
|
1028
|
+
* In-memory implementation of the state repository.
|
|
1029
|
+
* Stores the mixer state in volatile memory during the simulator's execution.
|
|
1030
|
+
*/
|
|
959
1031
|
var InMemoryStateRepository = class {
|
|
960
1032
|
state;
|
|
1033
|
+
/**
|
|
1034
|
+
* Creates a new InMemoryStateRepository.
|
|
1035
|
+
* @param logger - Logger service.
|
|
1036
|
+
* @param schemaRegistry - Registry providing the initial state schema.
|
|
1037
|
+
*/
|
|
961
1038
|
constructor(logger, schemaRegistry) {
|
|
962
1039
|
this.logger = logger;
|
|
963
1040
|
this.state = new X32State(schemaRegistry.getSchema());
|
|
964
1041
|
}
|
|
1042
|
+
/**
|
|
1043
|
+
* Returns the current mixer state instance.
|
|
1044
|
+
* @returns The X32State entity.
|
|
1045
|
+
*/
|
|
965
1046
|
getState() {
|
|
966
1047
|
return this.state;
|
|
967
1048
|
}
|
|
1049
|
+
/**
|
|
1050
|
+
* Resets the entire state to its default values.
|
|
1051
|
+
*/
|
|
968
1052
|
reset() {
|
|
969
1053
|
this.logger.info(LogCategory.SYSTEM, "Resetting state to defaults");
|
|
970
1054
|
this.state.reset();
|
|
@@ -1125,18 +1209,37 @@ var ConsoleLogger = class ConsoleLogger {
|
|
|
1125
1209
|
|
|
1126
1210
|
//#endregion
|
|
1127
1211
|
//#region src/infrastructure/services/UdpNetworkGateway.ts
|
|
1212
|
+
/**
|
|
1213
|
+
* Node.js UDP implementation of the network gateway.
|
|
1214
|
+
* Handles the low-level socket communication and OSC packet routing.
|
|
1215
|
+
*/
|
|
1128
1216
|
var UdpNetworkGateway = class {
|
|
1129
1217
|
socket;
|
|
1130
1218
|
isRunning = false;
|
|
1131
1219
|
packetCallback = null;
|
|
1220
|
+
/**
|
|
1221
|
+
* Creates a new UdpNetworkGateway.
|
|
1222
|
+
* @param logger - Logger service.
|
|
1223
|
+
* @param codec - OSC codec for encoding/decoding.
|
|
1224
|
+
*/
|
|
1132
1225
|
constructor(logger, codec) {
|
|
1133
1226
|
this.logger = logger;
|
|
1134
1227
|
this.codec = codec;
|
|
1135
1228
|
this.socket = dgram.createSocket("udp4");
|
|
1136
1229
|
}
|
|
1230
|
+
/**
|
|
1231
|
+
* Registers a callback to be executed whenever a new OSC packet arrives.
|
|
1232
|
+
* @param callback - Function called with decoded packet and source info.
|
|
1233
|
+
*/
|
|
1137
1234
|
onPacket(callback) {
|
|
1138
1235
|
this.packetCallback = callback;
|
|
1139
1236
|
}
|
|
1237
|
+
/**
|
|
1238
|
+
* Starts the UDP server on the specified port and IP.
|
|
1239
|
+
* @param port - UDP port.
|
|
1240
|
+
* @param ip - IP address to bind to.
|
|
1241
|
+
* @returns Promise resolving when the socket is bound.
|
|
1242
|
+
*/
|
|
1140
1243
|
start(port, ip) {
|
|
1141
1244
|
return new Promise((resolve, reject) => {
|
|
1142
1245
|
this.socket.on("error", (err) => {
|
|
@@ -1162,6 +1265,10 @@ var UdpNetworkGateway = class {
|
|
|
1162
1265
|
});
|
|
1163
1266
|
});
|
|
1164
1267
|
}
|
|
1268
|
+
/**
|
|
1269
|
+
* Shuts down the UDP server.
|
|
1270
|
+
* @returns Promise resolving when the socket is closed.
|
|
1271
|
+
*/
|
|
1165
1272
|
stop() {
|
|
1166
1273
|
return new Promise((resolve) => {
|
|
1167
1274
|
if (!this.isRunning) return resolve();
|
|
@@ -1172,6 +1279,12 @@ var UdpNetworkGateway = class {
|
|
|
1172
1279
|
});
|
|
1173
1280
|
});
|
|
1174
1281
|
}
|
|
1282
|
+
/**
|
|
1283
|
+
* Sends an OSC message to a specific remote client.
|
|
1284
|
+
* @param target - Target client info (IP/port).
|
|
1285
|
+
* @param address - OSC address pattern.
|
|
1286
|
+
* @param args - Array of arguments.
|
|
1287
|
+
*/
|
|
1175
1288
|
send(target, address, args) {
|
|
1176
1289
|
const buf = this.codec.encode(address, args);
|
|
1177
1290
|
const cat = address.startsWith("/meters") ? LogCategory.METER : LogCategory.OSC_OUT;
|
|
@@ -1186,4 +1299,879 @@ var UdpNetworkGateway = class {
|
|
|
1186
1299
|
};
|
|
1187
1300
|
|
|
1188
1301
|
//#endregion
|
|
1189
|
-
|
|
1302
|
+
//#region node_modules/node-osc/lib/Message.mjs
|
|
1303
|
+
const typeTags = {
|
|
1304
|
+
s: "string",
|
|
1305
|
+
f: "float",
|
|
1306
|
+
i: "integer",
|
|
1307
|
+
b: "blob",
|
|
1308
|
+
m: "midi"
|
|
1309
|
+
};
|
|
1310
|
+
/**
|
|
1311
|
+
* Represents a typed argument for an OSC message.
|
|
1312
|
+
*
|
|
1313
|
+
* @class
|
|
1314
|
+
* @private
|
|
1315
|
+
*/
|
|
1316
|
+
var Argument = class {
|
|
1317
|
+
/**
|
|
1318
|
+
* @param {string} type - The type of the argument (string, float, integer, blob, boolean).
|
|
1319
|
+
* @param {*} value - The value of the argument.
|
|
1320
|
+
*/
|
|
1321
|
+
constructor(type, value) {
|
|
1322
|
+
this.type = type;
|
|
1323
|
+
this.value = value;
|
|
1324
|
+
}
|
|
1325
|
+
};
|
|
1326
|
+
/**
|
|
1327
|
+
* Represents an OSC message with an address and arguments.
|
|
1328
|
+
*
|
|
1329
|
+
* OSC messages consist of an address pattern (string starting with '/')
|
|
1330
|
+
* and zero or more arguments of various types.
|
|
1331
|
+
*
|
|
1332
|
+
* @class
|
|
1333
|
+
*
|
|
1334
|
+
* @example
|
|
1335
|
+
* // Create a message with constructor arguments
|
|
1336
|
+
* const msg = new Message('/test', 1, 2, 'hello');
|
|
1337
|
+
*
|
|
1338
|
+
* @example
|
|
1339
|
+
* // Create a message and append arguments
|
|
1340
|
+
* const msg = new Message('/test');
|
|
1341
|
+
* msg.append(1);
|
|
1342
|
+
* msg.append('hello');
|
|
1343
|
+
* msg.append(3.14);
|
|
1344
|
+
*/
|
|
1345
|
+
var Message = class {
|
|
1346
|
+
/**
|
|
1347
|
+
* Create an OSC Message.
|
|
1348
|
+
*
|
|
1349
|
+
* @param {string} address - The OSC address pattern (e.g., '/oscillator/frequency').
|
|
1350
|
+
* @param {...*} args - Optional arguments to include in the message.
|
|
1351
|
+
*
|
|
1352
|
+
* @example
|
|
1353
|
+
* const msg = new Message('/test');
|
|
1354
|
+
*
|
|
1355
|
+
* @example
|
|
1356
|
+
* const msg = new Message('/test', 1, 2, 3);
|
|
1357
|
+
*
|
|
1358
|
+
* @example
|
|
1359
|
+
* const msg = new Message('/synth', 'note', 60, 0.5);
|
|
1360
|
+
*/
|
|
1361
|
+
constructor(address, ...args) {
|
|
1362
|
+
this.oscType = "message";
|
|
1363
|
+
this.address = address;
|
|
1364
|
+
this.args = args;
|
|
1365
|
+
}
|
|
1366
|
+
/**
|
|
1367
|
+
* Append an argument to the message.
|
|
1368
|
+
*
|
|
1369
|
+
* Automatically detects the type based on the JavaScript type:
|
|
1370
|
+
* - Integers are encoded as OSC integers
|
|
1371
|
+
* - Floats are encoded as OSC floats
|
|
1372
|
+
* - Strings are encoded as OSC strings
|
|
1373
|
+
* - Booleans are encoded as OSC booleans
|
|
1374
|
+
* - Buffers are encoded as OSC blobs
|
|
1375
|
+
* - Arrays are recursively appended
|
|
1376
|
+
* - Objects with a 'type' property are used as-is
|
|
1377
|
+
*
|
|
1378
|
+
* @param {*} arg - The argument to append. Can be:
|
|
1379
|
+
* - A primitive value (number, string, boolean)
|
|
1380
|
+
* - A Buffer (encoded as blob)
|
|
1381
|
+
* - An array of values (will be recursively appended)
|
|
1382
|
+
* - An object with 'type' and 'value' properties for explicit type control
|
|
1383
|
+
*
|
|
1384
|
+
* @throws {Error} If the argument type cannot be encoded.
|
|
1385
|
+
*
|
|
1386
|
+
* @example
|
|
1387
|
+
* const msg = new Message('/test');
|
|
1388
|
+
* msg.append(42); // Integer
|
|
1389
|
+
* msg.append(3.14); // Float
|
|
1390
|
+
* msg.append('hello'); // String
|
|
1391
|
+
* msg.append(true); // Boolean
|
|
1392
|
+
*
|
|
1393
|
+
* @example
|
|
1394
|
+
* // Append multiple values at once
|
|
1395
|
+
* msg.append([1, 2, 3]);
|
|
1396
|
+
*
|
|
1397
|
+
* @example
|
|
1398
|
+
* // Explicitly specify type
|
|
1399
|
+
* msg.append({ type: 'float', value: 42 });
|
|
1400
|
+
* msg.append({ type: 'blob', value: Buffer.from('data') });
|
|
1401
|
+
*
|
|
1402
|
+
* @example
|
|
1403
|
+
* // MIDI messages (4 bytes: port, status, data1, data2)
|
|
1404
|
+
* msg.append({ type: 'midi', value: { port: 0, status: 144, data1: 60, data2: 127 } });
|
|
1405
|
+
* msg.append({ type: 'm', value: Buffer.from([0, 144, 60, 127]) });
|
|
1406
|
+
*/
|
|
1407
|
+
append(arg) {
|
|
1408
|
+
let argOut;
|
|
1409
|
+
switch (typeof arg) {
|
|
1410
|
+
case "object":
|
|
1411
|
+
if (Buffer.isBuffer(arg)) this.args.push(arg);
|
|
1412
|
+
else if (arg instanceof Array) arg.forEach((a) => this.append(a));
|
|
1413
|
+
else if (arg.type) {
|
|
1414
|
+
if (typeTags[arg.type]) arg.type = typeTags[arg.type];
|
|
1415
|
+
this.args.push(arg);
|
|
1416
|
+
} else throw new Error(`don't know how to encode object ${arg}`);
|
|
1417
|
+
break;
|
|
1418
|
+
case "number":
|
|
1419
|
+
if (Math.floor(arg) === arg) argOut = new Argument("integer", arg);
|
|
1420
|
+
else argOut = new Argument("float", arg);
|
|
1421
|
+
break;
|
|
1422
|
+
case "string":
|
|
1423
|
+
argOut = new Argument("string", arg);
|
|
1424
|
+
break;
|
|
1425
|
+
case "boolean":
|
|
1426
|
+
argOut = new Argument("boolean", arg);
|
|
1427
|
+
break;
|
|
1428
|
+
default: throw new Error(`don't know how to encode ${arg}`);
|
|
1429
|
+
}
|
|
1430
|
+
if (argOut) this.args.push(argOut);
|
|
1431
|
+
}
|
|
1432
|
+
};
|
|
1433
|
+
var Message_default = Message;
|
|
1434
|
+
|
|
1435
|
+
//#endregion
|
|
1436
|
+
//#region node_modules/node-osc/lib/osc.mjs
|
|
1437
|
+
function padString(str) {
|
|
1438
|
+
const nullTerminated = str + "\0";
|
|
1439
|
+
const padding = (4 - Buffer$1.byteLength(nullTerminated) % 4) % 4;
|
|
1440
|
+
return nullTerminated + "\0".repeat(padding);
|
|
1441
|
+
}
|
|
1442
|
+
function readString(buffer, offset) {
|
|
1443
|
+
let end = offset;
|
|
1444
|
+
while (end < buffer.length && buffer[end] !== 0) end++;
|
|
1445
|
+
if (end >= buffer.length) throw new Error("Malformed Packet: Missing null terminator for string");
|
|
1446
|
+
return {
|
|
1447
|
+
value: buffer.subarray(offset, end).toString("utf8"),
|
|
1448
|
+
offset: offset + Math.ceil((end - offset + 1) / 4) * 4
|
|
1449
|
+
};
|
|
1450
|
+
}
|
|
1451
|
+
function writeInt32(value) {
|
|
1452
|
+
const buffer = Buffer$1.alloc(4);
|
|
1453
|
+
buffer.writeInt32BE(value, 0);
|
|
1454
|
+
return buffer;
|
|
1455
|
+
}
|
|
1456
|
+
function readInt32(buffer, offset) {
|
|
1457
|
+
if (offset + 4 > buffer.length) throw new Error("Malformed Packet: Not enough bytes for int32");
|
|
1458
|
+
return {
|
|
1459
|
+
value: buffer.readInt32BE(offset),
|
|
1460
|
+
offset: offset + 4
|
|
1461
|
+
};
|
|
1462
|
+
}
|
|
1463
|
+
function writeFloat32(value) {
|
|
1464
|
+
const buffer = Buffer$1.alloc(4);
|
|
1465
|
+
buffer.writeFloatBE(value, 0);
|
|
1466
|
+
return buffer;
|
|
1467
|
+
}
|
|
1468
|
+
function readFloat32(buffer, offset) {
|
|
1469
|
+
if (offset + 4 > buffer.length) throw new Error("Malformed Packet: Not enough bytes for float32");
|
|
1470
|
+
return {
|
|
1471
|
+
value: buffer.readFloatBE(offset),
|
|
1472
|
+
offset: offset + 4
|
|
1473
|
+
};
|
|
1474
|
+
}
|
|
1475
|
+
function writeBlob(value) {
|
|
1476
|
+
const length = value.length;
|
|
1477
|
+
const lengthBuffer = writeInt32(length);
|
|
1478
|
+
const padding = 4 - length % 4;
|
|
1479
|
+
const paddingBuffer = Buffer$1.alloc(padding === 4 ? 0 : padding);
|
|
1480
|
+
return Buffer$1.concat([
|
|
1481
|
+
lengthBuffer,
|
|
1482
|
+
value,
|
|
1483
|
+
paddingBuffer
|
|
1484
|
+
]);
|
|
1485
|
+
}
|
|
1486
|
+
function readBlob(buffer, offset) {
|
|
1487
|
+
const lengthResult = readInt32(buffer, offset);
|
|
1488
|
+
const length = lengthResult.value;
|
|
1489
|
+
if (length < 0) throw new Error("Malformed Packet: Invalid blob length");
|
|
1490
|
+
if (lengthResult.offset + length > buffer.length) throw new Error("Malformed Packet: Not enough bytes for blob");
|
|
1491
|
+
const data = buffer.subarray(lengthResult.offset, lengthResult.offset + length);
|
|
1492
|
+
const padding = 4 - length % 4;
|
|
1493
|
+
const nextOffset = lengthResult.offset + length + (padding === 4 ? 0 : padding);
|
|
1494
|
+
if (nextOffset > buffer.length) throw new Error("Malformed Packet: Not enough bytes for blob padding");
|
|
1495
|
+
return {
|
|
1496
|
+
value: data,
|
|
1497
|
+
offset: nextOffset
|
|
1498
|
+
};
|
|
1499
|
+
}
|
|
1500
|
+
function writeTimeTag(value) {
|
|
1501
|
+
const buffer = Buffer$1.alloc(8);
|
|
1502
|
+
if (value === 0 || value === null || value === void 0) {
|
|
1503
|
+
buffer.writeUInt32BE(0, 0);
|
|
1504
|
+
buffer.writeUInt32BE(1, 4);
|
|
1505
|
+
} else if (typeof value === "number") {
|
|
1506
|
+
const seconds = Math.floor(value);
|
|
1507
|
+
const fraction = Math.floor((value - seconds) * 4294967296);
|
|
1508
|
+
buffer.writeUInt32BE(seconds + 2208988800, 0);
|
|
1509
|
+
buffer.writeUInt32BE(fraction, 4);
|
|
1510
|
+
} else {
|
|
1511
|
+
buffer.writeUInt32BE(0, 0);
|
|
1512
|
+
buffer.writeUInt32BE(1, 4);
|
|
1513
|
+
}
|
|
1514
|
+
return buffer;
|
|
1515
|
+
}
|
|
1516
|
+
function readTimeTag(buffer, offset) {
|
|
1517
|
+
if (offset + 8 > buffer.length) throw new Error("Malformed Packet: Not enough bytes for timetag");
|
|
1518
|
+
const seconds = buffer.readUInt32BE(offset);
|
|
1519
|
+
const fraction = buffer.readUInt32BE(offset + 4);
|
|
1520
|
+
let value;
|
|
1521
|
+
if (seconds === 0 && fraction === 1) value = 0;
|
|
1522
|
+
else value = seconds - 2208988800 + fraction / 4294967296;
|
|
1523
|
+
return {
|
|
1524
|
+
value,
|
|
1525
|
+
offset: offset + 8
|
|
1526
|
+
};
|
|
1527
|
+
}
|
|
1528
|
+
function writeMidi(value) {
|
|
1529
|
+
const buffer = Buffer$1.alloc(4);
|
|
1530
|
+
if (Buffer$1.isBuffer(value)) {
|
|
1531
|
+
if (value.length !== 4) throw new Error("MIDI message must be exactly 4 bytes");
|
|
1532
|
+
value.copy(buffer);
|
|
1533
|
+
} else if (typeof value === "object" && value !== null) {
|
|
1534
|
+
buffer.writeUInt8(value.port || 0, 0);
|
|
1535
|
+
buffer.writeUInt8(value.status || 0, 1);
|
|
1536
|
+
buffer.writeUInt8(value.data1 || 0, 2);
|
|
1537
|
+
buffer.writeUInt8(value.data2 || 0, 3);
|
|
1538
|
+
} else throw new Error("MIDI value must be a 4-byte Buffer or object with port, status, data1, data2 properties");
|
|
1539
|
+
return buffer;
|
|
1540
|
+
}
|
|
1541
|
+
function readMidi(buffer, offset) {
|
|
1542
|
+
if (offset + 4 > buffer.length) throw new Error("Not enough bytes for MIDI message");
|
|
1543
|
+
return {
|
|
1544
|
+
value: buffer.subarray(offset, offset + 4),
|
|
1545
|
+
offset: offset + 4
|
|
1546
|
+
};
|
|
1547
|
+
}
|
|
1548
|
+
function encodeArgument(arg) {
|
|
1549
|
+
if (typeof arg === "object" && arg.type && arg.value !== void 0) switch (arg.type) {
|
|
1550
|
+
case "i":
|
|
1551
|
+
case "integer": return {
|
|
1552
|
+
tag: "i",
|
|
1553
|
+
data: writeInt32(arg.value)
|
|
1554
|
+
};
|
|
1555
|
+
case "f":
|
|
1556
|
+
case "float": return {
|
|
1557
|
+
tag: "f",
|
|
1558
|
+
data: writeFloat32(arg.value)
|
|
1559
|
+
};
|
|
1560
|
+
case "s":
|
|
1561
|
+
case "string": return {
|
|
1562
|
+
tag: "s",
|
|
1563
|
+
data: Buffer$1.from(padString(arg.value))
|
|
1564
|
+
};
|
|
1565
|
+
case "b":
|
|
1566
|
+
case "blob": return {
|
|
1567
|
+
tag: "b",
|
|
1568
|
+
data: writeBlob(arg.value)
|
|
1569
|
+
};
|
|
1570
|
+
case "d":
|
|
1571
|
+
case "double": return {
|
|
1572
|
+
tag: "f",
|
|
1573
|
+
data: writeFloat32(arg.value)
|
|
1574
|
+
};
|
|
1575
|
+
case "T": return {
|
|
1576
|
+
tag: "T",
|
|
1577
|
+
data: Buffer$1.alloc(0)
|
|
1578
|
+
};
|
|
1579
|
+
case "F": return {
|
|
1580
|
+
tag: "F",
|
|
1581
|
+
data: Buffer$1.alloc(0)
|
|
1582
|
+
};
|
|
1583
|
+
case "boolean": return arg.value ? {
|
|
1584
|
+
tag: "T",
|
|
1585
|
+
data: Buffer$1.alloc(0)
|
|
1586
|
+
} : {
|
|
1587
|
+
tag: "F",
|
|
1588
|
+
data: Buffer$1.alloc(0)
|
|
1589
|
+
};
|
|
1590
|
+
case "m":
|
|
1591
|
+
case "midi": return {
|
|
1592
|
+
tag: "m",
|
|
1593
|
+
data: writeMidi(arg.value)
|
|
1594
|
+
};
|
|
1595
|
+
default: throw new Error(`Unknown argument type: ${arg.type}`);
|
|
1596
|
+
}
|
|
1597
|
+
switch (typeof arg) {
|
|
1598
|
+
case "number": if (Number.isInteger(arg)) return {
|
|
1599
|
+
tag: "i",
|
|
1600
|
+
data: writeInt32(arg)
|
|
1601
|
+
};
|
|
1602
|
+
else return {
|
|
1603
|
+
tag: "f",
|
|
1604
|
+
data: writeFloat32(arg)
|
|
1605
|
+
};
|
|
1606
|
+
case "string": return {
|
|
1607
|
+
tag: "s",
|
|
1608
|
+
data: Buffer$1.from(padString(arg))
|
|
1609
|
+
};
|
|
1610
|
+
case "boolean": return arg ? {
|
|
1611
|
+
tag: "T",
|
|
1612
|
+
data: Buffer$1.alloc(0)
|
|
1613
|
+
} : {
|
|
1614
|
+
tag: "F",
|
|
1615
|
+
data: Buffer$1.alloc(0)
|
|
1616
|
+
};
|
|
1617
|
+
default:
|
|
1618
|
+
if (Buffer$1.isBuffer(arg)) return {
|
|
1619
|
+
tag: "b",
|
|
1620
|
+
data: writeBlob(arg)
|
|
1621
|
+
};
|
|
1622
|
+
throw new Error(`Don't know how to encode argument: ${arg}`);
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
function decodeArgument(tag, buffer, offset) {
|
|
1626
|
+
switch (tag) {
|
|
1627
|
+
case "i": return readInt32(buffer, offset);
|
|
1628
|
+
case "f": return readFloat32(buffer, offset);
|
|
1629
|
+
case "s": return readString(buffer, offset);
|
|
1630
|
+
case "b": return readBlob(buffer, offset);
|
|
1631
|
+
case "T": return {
|
|
1632
|
+
value: true,
|
|
1633
|
+
offset
|
|
1634
|
+
};
|
|
1635
|
+
case "F": return {
|
|
1636
|
+
value: false,
|
|
1637
|
+
offset
|
|
1638
|
+
};
|
|
1639
|
+
case "N": return {
|
|
1640
|
+
value: null,
|
|
1641
|
+
offset
|
|
1642
|
+
};
|
|
1643
|
+
case "m": return readMidi(buffer, offset);
|
|
1644
|
+
default: throw new Error(`I don't understand the argument code ${tag}`);
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
/**
|
|
1648
|
+
* Encode an OSC message or bundle to a Buffer.
|
|
1649
|
+
*
|
|
1650
|
+
* This low-level function converts OSC messages and bundles into binary format
|
|
1651
|
+
* for transmission or storage. Useful for sending OSC over custom transports
|
|
1652
|
+
* (WebSocket, TCP, HTTP), storing to files, or implementing custom OSC routers.
|
|
1653
|
+
*
|
|
1654
|
+
* @param {Object} message - OSC message or bundle object with oscType property
|
|
1655
|
+
* @returns {Buffer} The encoded OSC data ready for transmission
|
|
1656
|
+
*
|
|
1657
|
+
* @example
|
|
1658
|
+
* // Encode a message
|
|
1659
|
+
* import { Message, encode } from 'node-osc';
|
|
1660
|
+
*
|
|
1661
|
+
* const message = new Message('/oscillator/frequency', 440);
|
|
1662
|
+
* const buffer = encode(message);
|
|
1663
|
+
* console.log('Encoded bytes:', buffer.length);
|
|
1664
|
+
*
|
|
1665
|
+
* @example
|
|
1666
|
+
* // Encode a bundle
|
|
1667
|
+
* import { Bundle, encode } from 'node-osc';
|
|
1668
|
+
*
|
|
1669
|
+
* const bundle = new Bundle(['/one', 1], ['/two', 2]);
|
|
1670
|
+
* const buffer = encode(bundle);
|
|
1671
|
+
*
|
|
1672
|
+
* @example
|
|
1673
|
+
* // Send over WebSocket
|
|
1674
|
+
* const buffer = encode(message);
|
|
1675
|
+
* websocket.send(buffer);
|
|
1676
|
+
*/
|
|
1677
|
+
function encode(message) {
|
|
1678
|
+
if (message.oscType === "bundle") return encodeBundleToBuffer(message);
|
|
1679
|
+
else return encodeMessageToBuffer(message);
|
|
1680
|
+
}
|
|
1681
|
+
function encodeMessageToBuffer(message) {
|
|
1682
|
+
const address = padString(message.address);
|
|
1683
|
+
const addressBuffer = Buffer$1.from(address);
|
|
1684
|
+
const encodedArgs = message.args.map(encodeArgument);
|
|
1685
|
+
const typeTags$1 = "," + encodedArgs.map((arg) => arg.tag).join("");
|
|
1686
|
+
const typeTagsBuffer = Buffer$1.from(padString(typeTags$1));
|
|
1687
|
+
const argumentBuffers = encodedArgs.map((arg) => arg.data);
|
|
1688
|
+
return Buffer$1.concat([
|
|
1689
|
+
addressBuffer,
|
|
1690
|
+
typeTagsBuffer,
|
|
1691
|
+
...argumentBuffers
|
|
1692
|
+
]);
|
|
1693
|
+
}
|
|
1694
|
+
function encodeBundleToBuffer(bundle) {
|
|
1695
|
+
const bundleString = padString("#bundle");
|
|
1696
|
+
const bundleStringBuffer = Buffer$1.from(bundleString);
|
|
1697
|
+
const timetagBuffer = writeTimeTag(bundle.timetag);
|
|
1698
|
+
const elementBuffers = bundle.elements.map((element) => {
|
|
1699
|
+
let elementBuffer;
|
|
1700
|
+
if (element.oscType === "bundle") elementBuffer = encodeBundleToBuffer(element);
|
|
1701
|
+
else elementBuffer = encodeMessageToBuffer(element);
|
|
1702
|
+
const sizeBuffer = writeInt32(elementBuffer.length);
|
|
1703
|
+
return Buffer$1.concat([sizeBuffer, elementBuffer]);
|
|
1704
|
+
});
|
|
1705
|
+
return Buffer$1.concat([
|
|
1706
|
+
bundleStringBuffer,
|
|
1707
|
+
timetagBuffer,
|
|
1708
|
+
...elementBuffers
|
|
1709
|
+
]);
|
|
1710
|
+
}
|
|
1711
|
+
/**
|
|
1712
|
+
* Decode a Buffer containing OSC data into a message or bundle object.
|
|
1713
|
+
*
|
|
1714
|
+
* This low-level function parses binary OSC data back into JavaScript objects.
|
|
1715
|
+
* Useful for receiving OSC over custom transports, reading from files,
|
|
1716
|
+
* or implementing custom OSC routers.
|
|
1717
|
+
*
|
|
1718
|
+
* @param {Buffer} buffer - The Buffer containing OSC data
|
|
1719
|
+
* @returns {Object} The decoded OSC message or bundle. Messages have
|
|
1720
|
+
* {oscType: 'message', address: string, args: Array}, bundles have
|
|
1721
|
+
* {oscType: 'bundle', timetag: number, elements: Array}
|
|
1722
|
+
* @throws {Error} If the buffer contains malformed OSC data
|
|
1723
|
+
*
|
|
1724
|
+
* @example
|
|
1725
|
+
* // Decode received data
|
|
1726
|
+
* import { decode } from 'node-osc';
|
|
1727
|
+
*
|
|
1728
|
+
* const decoded = decode(buffer);
|
|
1729
|
+
* if (decoded.oscType === 'message') {
|
|
1730
|
+
* console.log('Address:', decoded.address);
|
|
1731
|
+
* console.log('Arguments:', decoded.args);
|
|
1732
|
+
* }
|
|
1733
|
+
*
|
|
1734
|
+
* @example
|
|
1735
|
+
* // Round-trip encode/decode
|
|
1736
|
+
* import { Message, encode, decode } from 'node-osc';
|
|
1737
|
+
*
|
|
1738
|
+
* const original = new Message('/test', 42, 'hello');
|
|
1739
|
+
* const buffer = encode(original);
|
|
1740
|
+
* const decoded = decode(buffer);
|
|
1741
|
+
* console.log(decoded.address); // '/test'
|
|
1742
|
+
*/
|
|
1743
|
+
function decode(buffer) {
|
|
1744
|
+
if (buffer.length >= 8 && buffer.subarray(0, 8).toString() === "#bundle\0") return decodeBundleFromBuffer(buffer);
|
|
1745
|
+
else return decodeMessageFromBuffer(buffer);
|
|
1746
|
+
}
|
|
1747
|
+
function decodeMessageFromBuffer(buffer) {
|
|
1748
|
+
let offset = 0;
|
|
1749
|
+
const addressResult = readString(buffer, offset);
|
|
1750
|
+
const address = addressResult.value;
|
|
1751
|
+
offset = addressResult.offset;
|
|
1752
|
+
const typeTagsResult = readString(buffer, offset);
|
|
1753
|
+
const typeTags$1 = typeTagsResult.value;
|
|
1754
|
+
offset = typeTagsResult.offset;
|
|
1755
|
+
if (!typeTags$1.startsWith(",")) throw new Error("Malformed Packet");
|
|
1756
|
+
const tags = typeTags$1.slice(1);
|
|
1757
|
+
const args = [];
|
|
1758
|
+
for (const tag of tags) {
|
|
1759
|
+
const argResult = decodeArgument(tag, buffer, offset);
|
|
1760
|
+
args.push({ value: argResult.value });
|
|
1761
|
+
offset = argResult.offset;
|
|
1762
|
+
}
|
|
1763
|
+
return {
|
|
1764
|
+
oscType: "message",
|
|
1765
|
+
address,
|
|
1766
|
+
args
|
|
1767
|
+
};
|
|
1768
|
+
}
|
|
1769
|
+
function decodeBundleFromBuffer(buffer) {
|
|
1770
|
+
let offset = 8;
|
|
1771
|
+
const timetagResult = readTimeTag(buffer, offset);
|
|
1772
|
+
const timetag = timetagResult.value;
|
|
1773
|
+
offset = timetagResult.offset;
|
|
1774
|
+
const elements = [];
|
|
1775
|
+
while (offset < buffer.length) {
|
|
1776
|
+
const sizeResult = readInt32(buffer, offset);
|
|
1777
|
+
const size = sizeResult.value;
|
|
1778
|
+
offset = sizeResult.offset;
|
|
1779
|
+
if (size <= 0 || offset + size > buffer.length) throw new Error("Malformed Packet");
|
|
1780
|
+
const element = decode(buffer.subarray(offset, offset + size));
|
|
1781
|
+
elements.push(element);
|
|
1782
|
+
offset += size;
|
|
1783
|
+
}
|
|
1784
|
+
return {
|
|
1785
|
+
oscType: "bundle",
|
|
1786
|
+
timetag,
|
|
1787
|
+
elements
|
|
1788
|
+
};
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1791
|
+
//#endregion
|
|
1792
|
+
//#region src/infrastructure/mappers/OscCodec.ts
|
|
1793
|
+
/**
|
|
1794
|
+
* Handles encoding and decoding of X32 OSC messages.
|
|
1795
|
+
*/
|
|
1796
|
+
var OscCodec = class {
|
|
1797
|
+
constructor(schemaRegistry) {
|
|
1798
|
+
this.schemaRegistry = schemaRegistry;
|
|
1799
|
+
}
|
|
1800
|
+
/**
|
|
1801
|
+
* Decodes a binary OSC message into a packet object.
|
|
1802
|
+
* @param msg - Raw UDP buffer.
|
|
1803
|
+
* @returns Decoded OscPacket.
|
|
1804
|
+
*/
|
|
1805
|
+
decode(msg) {
|
|
1806
|
+
return decode(msg);
|
|
1807
|
+
}
|
|
1808
|
+
/**
|
|
1809
|
+
* Encodes an address and arguments into a binary OSC message.
|
|
1810
|
+
* @param address - OSC address pattern.
|
|
1811
|
+
* @param args - Array of arguments.
|
|
1812
|
+
* @returns Binary buffer.
|
|
1813
|
+
*/
|
|
1814
|
+
encode(address, args) {
|
|
1815
|
+
return encode(this.createMessage(address, args));
|
|
1816
|
+
}
|
|
1817
|
+
/**
|
|
1818
|
+
* Creates a typed node-osc Message based on X32 schema.
|
|
1819
|
+
* @param address - Target address.
|
|
1820
|
+
* @param args - Untyped arguments.
|
|
1821
|
+
* @returns Typed Message.
|
|
1822
|
+
*/
|
|
1823
|
+
createMessage(address, args) {
|
|
1824
|
+
return new Message_default(address, ...args.map((arg) => {
|
|
1825
|
+
if (typeof arg === "object" && arg !== null && "type" in arg && "value" in arg) return arg;
|
|
1826
|
+
if (Buffer.isBuffer(arg)) return arg;
|
|
1827
|
+
const node = this.schemaRegistry.getNode(address);
|
|
1828
|
+
if (node) {
|
|
1829
|
+
if (node.type === "f" && typeof arg === "number") return {
|
|
1830
|
+
type: "f",
|
|
1831
|
+
value: arg
|
|
1832
|
+
};
|
|
1833
|
+
if (node.type === "i" && typeof arg === "number") return {
|
|
1834
|
+
type: "i",
|
|
1835
|
+
value: Math.round(arg)
|
|
1836
|
+
};
|
|
1837
|
+
if (node.type === "s" && typeof arg === "string") return {
|
|
1838
|
+
type: "s",
|
|
1839
|
+
value: arg
|
|
1840
|
+
};
|
|
1841
|
+
}
|
|
1842
|
+
return arg;
|
|
1843
|
+
}));
|
|
1844
|
+
}
|
|
1845
|
+
};
|
|
1846
|
+
|
|
1847
|
+
//#endregion
|
|
1848
|
+
//#region src/domain/services/SchemaFactory.ts
|
|
1849
|
+
/**
|
|
1850
|
+
* Factory service responsible for constructing the X32 OSC Schema.
|
|
1851
|
+
* Encapsulates all the logic for generating channel strips, routing blocks, etc.
|
|
1852
|
+
*/
|
|
1853
|
+
var SchemaFactory = class {
|
|
1854
|
+
/**
|
|
1855
|
+
* Builds the complete X32 OSC Schema.
|
|
1856
|
+
* @returns The constructed schema map.
|
|
1857
|
+
*/
|
|
1858
|
+
createSchema() {
|
|
1859
|
+
const schema = {
|
|
1860
|
+
...this.generateNodes(32, "ch"),
|
|
1861
|
+
...this.generateNodes(16, "bus"),
|
|
1862
|
+
...this.generateNodes(8, "dca"),
|
|
1863
|
+
...this.generateNodes(8, "auxin"),
|
|
1864
|
+
...this.generateNodes(8, "fxrtn"),
|
|
1865
|
+
...this.generateNodes(6, "mtx"),
|
|
1866
|
+
...Object.fromEntries(Array.from({ length: 8 }, (_, i) => [`/fx/${i + 1}/type`, this.node("i", 0)])),
|
|
1867
|
+
...this.generateRange(8, "/fx", "/type", "i", 0),
|
|
1868
|
+
...this.generateRange(8, "/fx", "/source/l", "i", 0),
|
|
1869
|
+
...this.generateRange(8, "/fx", "/source/r", "i", 0),
|
|
1870
|
+
...Object.fromEntries(Array.from({ length: 8 }, (_, slot) => Object.entries(this.generateRange(64, `/fx/${slot + 1}/par`, "", "f", 0, 2, 1))).flat()),
|
|
1871
|
+
...this.generateRange(128, "/headamp", "/gain", "f", 0, 3, 0),
|
|
1872
|
+
...this.generateRange(128, "/headamp", "/phantom", "i", 0, 3, 0),
|
|
1873
|
+
...this.generateRange(6, "/config/mute", "", "i", 0),
|
|
1874
|
+
...this.generateRange(80, "/-stat/solosw", "", "i", 0),
|
|
1875
|
+
"/-stat/selidx": this.node("i", 0),
|
|
1876
|
+
"/-stat/sendsonfader": this.node("i", 0),
|
|
1877
|
+
"/-stat/bussendbank": this.node("i", 0),
|
|
1878
|
+
"/-stat/keysolo": this.node("i", 0),
|
|
1879
|
+
"/-stat/screen/screen": this.node("i", 0),
|
|
1880
|
+
"/-stat/screen/CHAN/page": this.node("i", 0),
|
|
1881
|
+
"/-stat/screen/METER/page": this.node("i", 0),
|
|
1882
|
+
"/-stat/screen/ROUTE/page": this.node("i", 0),
|
|
1883
|
+
"/-stat/screen/SETUP/page": this.node("i", 0),
|
|
1884
|
+
"/-stat/screen/LIBRARY/page": this.node("i", 0),
|
|
1885
|
+
"/-stat/screen/FX/page": this.node("i", 0),
|
|
1886
|
+
"/-stat/screen/MON/page": this.node("i", 0),
|
|
1887
|
+
"/-stat/screen/USB/page": this.node("i", 0),
|
|
1888
|
+
"/-stat/screen/SCENE/page": this.node("i", 0),
|
|
1889
|
+
"/-stat/screen/ASSIGN/page": this.node("i", 0),
|
|
1890
|
+
"/-stat/talk/A": this.node("i", 0),
|
|
1891
|
+
"/-stat/talk/B": this.node("i", 0),
|
|
1892
|
+
"/-stat/osc/on": this.node("i", 0),
|
|
1893
|
+
"/-prefs/autosel": this.node("i", 1),
|
|
1894
|
+
"/-action/setrtasrc": this.node("i", 0),
|
|
1895
|
+
"/-action/playtrack": this.node("i", 0),
|
|
1896
|
+
"/-action/goscene": this.node("i", 0),
|
|
1897
|
+
"/-action/setscene": this.node("i", 0),
|
|
1898
|
+
"/config/routing/AES50A/1-8": this.node("i", 0),
|
|
1899
|
+
"/config/routing/AES50B/1-8": this.node("i", 0),
|
|
1900
|
+
"/config/routing/CARD/1-8": this.node("i", 0),
|
|
1901
|
+
"/config/routing/OUT/1-4": this.node("i", 0),
|
|
1902
|
+
"/-prefs/invertmutes": this.node("i", 0),
|
|
1903
|
+
...this.generateChannelStrip("/main/st", 6, false),
|
|
1904
|
+
...this.generateChannelStrip("/main/m", 6, false),
|
|
1905
|
+
...Object.fromEntries(Array.from({ length: 16 }, (_, i) => [`/config/chlink/${i * 2 + 1}-${i * 2 + 2}`, this.node("i", 0)])),
|
|
1906
|
+
...Object.fromEntries(Array.from({ length: 8 }, (_, i) => [`/config/buslink/${i * 2 + 1}-${i * 2 + 2}`, this.node("i", 0)])),
|
|
1907
|
+
...Object.fromEntries(Array.from({ length: 4 }, (_, i) => [`/config/auxlink/${i * 2 + 1}-${i * 2 + 2}`, this.node("i", 0)])),
|
|
1908
|
+
...Object.fromEntries(Array.from({ length: 4 }, (_, i) => [`/config/fxlink/${i * 2 + 1}-${i * 2 + 2}`, this.node("i", 0)])),
|
|
1909
|
+
...this.generateRange(32, "/config/userrout/in", "", "i", 0),
|
|
1910
|
+
...this.generateRange(48, "/config/userrout/out", "", "i", 0),
|
|
1911
|
+
"/config/solo/level": this.node("f", 0),
|
|
1912
|
+
"/config/solo/source": this.node("i", 0),
|
|
1913
|
+
"/config/solo/sourcetrim": this.node("f", 0),
|
|
1914
|
+
"/config/solo/exclusive": this.node("i", 0),
|
|
1915
|
+
"/config/solo/dim": this.node("i", 0),
|
|
1916
|
+
"/config/solo/dimpfl": this.node("i", 0),
|
|
1917
|
+
"/config/solo/dimatt": this.node("f", 0),
|
|
1918
|
+
"/config/solo/mono": this.node("i", 0),
|
|
1919
|
+
"/config/solo/chmode": this.node("i", 0),
|
|
1920
|
+
"/config/solo/busmode": this.node("i", 0),
|
|
1921
|
+
"/config/solo/dcamode": this.node("i", 0),
|
|
1922
|
+
"/config/solo/masterctrl": this.node("i", 0),
|
|
1923
|
+
"/config/solo/delay": this.node("i", 0),
|
|
1924
|
+
"/config/solo/delaytime": this.node("f", 0),
|
|
1925
|
+
"/config/solo/followsel": this.node("i", 0),
|
|
1926
|
+
"/config/solo/followsolo": this.node("i", 0),
|
|
1927
|
+
"/config/talk/enable": this.node("i", 0),
|
|
1928
|
+
"/config/talk/source": this.node("i", 0),
|
|
1929
|
+
"/config/talk/A/level": this.node("f", 0),
|
|
1930
|
+
"/config/talk/B/level": this.node("f", 0),
|
|
1931
|
+
"/config/talk/A/dim": this.node("i", 0),
|
|
1932
|
+
"/config/talk/B/dim": this.node("i", 0),
|
|
1933
|
+
"/config/talk/A/latch": this.node("i", 0),
|
|
1934
|
+
"/config/talk/B/latch": this.node("i", 0),
|
|
1935
|
+
"/config/talk/A/destmap": this.node("i", 0),
|
|
1936
|
+
"/config/talk/B/destmap": this.node("i", 0),
|
|
1937
|
+
"/config/osc/on": this.node("i", 0),
|
|
1938
|
+
"/config/osc/type": this.node("i", 0),
|
|
1939
|
+
"/config/osc/fsel": this.node("i", 0),
|
|
1940
|
+
"/config/osc/f1": this.node("f", .5),
|
|
1941
|
+
"/config/osc/f2": this.node("f", .5),
|
|
1942
|
+
"/config/osc/level": this.node("f", 0),
|
|
1943
|
+
"/config/osc/dest": this.node("i", 0),
|
|
1944
|
+
...this.generateOutputs("main", 16),
|
|
1945
|
+
...this.generateOutputs("aux", 6),
|
|
1946
|
+
...this.generateOutputs("p16", 16),
|
|
1947
|
+
...this.generateOutputs("aes", 2),
|
|
1948
|
+
...this.generateOutputs("rec", 2)
|
|
1949
|
+
};
|
|
1950
|
+
Object.keys(STATIC_RESPONSES_DATA).forEach((key) => {
|
|
1951
|
+
if (key.startsWith("/-") || key.startsWith("/stat")) schema[key] = this.node("i", 0);
|
|
1952
|
+
});
|
|
1953
|
+
[
|
|
1954
|
+
"/config/routing/IN/1-8",
|
|
1955
|
+
"/config/routing/IN/9-16",
|
|
1956
|
+
"/config/routing/IN/17-24",
|
|
1957
|
+
"/config/routing/IN/25-32",
|
|
1958
|
+
"/config/routing/AUX/1-4",
|
|
1959
|
+
"/config/routing/OUT/1-4",
|
|
1960
|
+
"/config/routing/OUT/5-8",
|
|
1961
|
+
"/config/routing/OUT/9-12",
|
|
1962
|
+
"/config/routing/OUT/13-16",
|
|
1963
|
+
"/config/routing/P16/1-8",
|
|
1964
|
+
"/config/routing/P16/9-16",
|
|
1965
|
+
"/config/routing/CARD/1-8",
|
|
1966
|
+
"/config/routing/CARD/9-16",
|
|
1967
|
+
"/config/routing/CARD/17-24",
|
|
1968
|
+
"/config/routing/CARD/25-32",
|
|
1969
|
+
"/config/routing/AES50A/1-8",
|
|
1970
|
+
"/config/routing/AES50A/9-16",
|
|
1971
|
+
"/config/routing/AES50A/17-24",
|
|
1972
|
+
"/config/routing/AES50A/25-32",
|
|
1973
|
+
"/config/routing/AES50A/33-40",
|
|
1974
|
+
"/config/routing/AES50A/41-48",
|
|
1975
|
+
"/config/routing/AES50B/1-8",
|
|
1976
|
+
"/config/routing/AES50B/9-16",
|
|
1977
|
+
"/config/routing/AES50B/17-24",
|
|
1978
|
+
"/config/routing/AES50B/25-32",
|
|
1979
|
+
"/config/routing/AES50B/33-40",
|
|
1980
|
+
"/config/routing/AES50B/41-48",
|
|
1981
|
+
"/config/routing/PLAY/1-8",
|
|
1982
|
+
"/config/routing/PLAY/9-16",
|
|
1983
|
+
"/config/routing/PLAY/17-24",
|
|
1984
|
+
"/config/routing/PLAY/25-32"
|
|
1985
|
+
].forEach((path) => {
|
|
1986
|
+
schema[path] = this.node("i", 0);
|
|
1987
|
+
});
|
|
1988
|
+
return schema;
|
|
1989
|
+
}
|
|
1990
|
+
node(type, def) {
|
|
1991
|
+
return new X32Node(type, def);
|
|
1992
|
+
}
|
|
1993
|
+
generateChannelStrip(base, eqBands = 4, hasPreamp = false) {
|
|
1994
|
+
const nodes = {};
|
|
1995
|
+
nodes[`${base}/config/name`] = this.node("s", base.split("/").pop()?.toUpperCase() || "");
|
|
1996
|
+
nodes[`${base}/config/icon`] = this.node("i", 1);
|
|
1997
|
+
nodes[`${base}/config/color`] = this.node("i", 1);
|
|
1998
|
+
nodes[`${base}/config/source`] = this.node("i", 1);
|
|
1999
|
+
nodes[`${base}/mix/fader`] = this.node("f", .75);
|
|
2000
|
+
nodes[`${base}/mix/on`] = this.node("i", 0);
|
|
2001
|
+
nodes[`${base}/mix/pan`] = this.node("f", .5);
|
|
2002
|
+
nodes[`${base}/mix/mono`] = this.node("i", 0);
|
|
2003
|
+
nodes[`${base}/mix/mlevel`] = this.node("f", 0);
|
|
2004
|
+
nodes[`${base}/mix/st`] = this.node("i", 1);
|
|
2005
|
+
if (hasPreamp) {
|
|
2006
|
+
nodes[`${base}/preamp/trim`] = this.node("f", .5);
|
|
2007
|
+
nodes[`${base}/preamp/hpon`] = this.node("i", 0);
|
|
2008
|
+
nodes[`${base}/preamp/hpf`] = this.node("f", 0);
|
|
2009
|
+
nodes[`${base}/preamp/phantom`] = this.node("i", 0);
|
|
2010
|
+
nodes[`${base}/preamp/rtnsw`] = this.node("i", 0);
|
|
2011
|
+
nodes[`${base}/preamp/invert`] = this.node("i", 0);
|
|
2012
|
+
}
|
|
2013
|
+
nodes[`${base}/delay/on`] = this.node("i", 0);
|
|
2014
|
+
nodes[`${base}/delay/time`] = this.node("f", 0);
|
|
2015
|
+
nodes[`${base}/insert/on`] = this.node("i", 0);
|
|
2016
|
+
nodes[`${base}/insert/pos`] = this.node("i", 0);
|
|
2017
|
+
nodes[`${base}/insert/sel`] = this.node("i", 0);
|
|
2018
|
+
nodes[`${base}/gate/on`] = this.node("i", 0);
|
|
2019
|
+
nodes[`${base}/gate/mode`] = this.node("i", 0);
|
|
2020
|
+
nodes[`${base}/gate/thr`] = this.node("f", 0);
|
|
2021
|
+
nodes[`${base}/gate/range`] = this.node("f", 0);
|
|
2022
|
+
nodes[`${base}/gate/attack`] = this.node("f", 0);
|
|
2023
|
+
nodes[`${base}/gate/hold`] = this.node("f", 0);
|
|
2024
|
+
nodes[`${base}/gate/release`] = this.node("f", 0);
|
|
2025
|
+
nodes[`${base}/gate/keysrc`] = this.node("i", 0);
|
|
2026
|
+
nodes[`${base}/gate/filter/on`] = this.node("i", 0);
|
|
2027
|
+
nodes[`${base}/gate/filter/type`] = this.node("i", 0);
|
|
2028
|
+
nodes[`${base}/gate/filter/f`] = this.node("f", .5);
|
|
2029
|
+
nodes[`${base}/dyn/on`] = this.node("i", 0);
|
|
2030
|
+
nodes[`${base}/dyn/mode`] = this.node("i", 0);
|
|
2031
|
+
nodes[`${base}/dyn/pos`] = this.node("i", 0);
|
|
2032
|
+
nodes[`${base}/dyn/det`] = this.node("i", 0);
|
|
2033
|
+
nodes[`${base}/dyn/env`] = this.node("i", 0);
|
|
2034
|
+
nodes[`${base}/dyn/thr`] = this.node("f", 0);
|
|
2035
|
+
nodes[`${base}/dyn/ratio`] = this.node("i", 0);
|
|
2036
|
+
nodes[`${base}/dyn/knee`] = this.node("f", 0);
|
|
2037
|
+
nodes[`${base}/dyn/mgain`] = this.node("f", 0);
|
|
2038
|
+
nodes[`${base}/dyn/attack`] = this.node("f", 0);
|
|
2039
|
+
nodes[`${base}/dyn/hold`] = this.node("f", 0);
|
|
2040
|
+
nodes[`${base}/dyn/release`] = this.node("f", 0);
|
|
2041
|
+
nodes[`${base}/dyn/mix`] = this.node("f", 1);
|
|
2042
|
+
nodes[`${base}/dyn/auto`] = this.node("i", 0);
|
|
2043
|
+
nodes[`${base}/dyn/keysrc`] = this.node("i", 0);
|
|
2044
|
+
nodes[`${base}/dyn/filter/on`] = this.node("i", 0);
|
|
2045
|
+
nodes[`${base}/dyn/filter/type`] = this.node("i", 0);
|
|
2046
|
+
nodes[`${base}/dyn/filter/f`] = this.node("f", .5);
|
|
2047
|
+
nodes[`${base}/eq/on`] = this.node("i", 0);
|
|
2048
|
+
nodes[`${base}/eq/mode`] = this.node("i", 0);
|
|
2049
|
+
for (let b = 1; b <= eqBands; b++) {
|
|
2050
|
+
nodes[`${base}/eq/${b}/type`] = this.node("i", 0);
|
|
2051
|
+
nodes[`${base}/eq/${b}/f`] = this.node("f", .5);
|
|
2052
|
+
nodes[`${base}/eq/${b}/g`] = this.node("f", .5);
|
|
2053
|
+
nodes[`${base}/eq/${b}/q`] = this.node("f", .5);
|
|
2054
|
+
}
|
|
2055
|
+
nodes[`${base}/grp/dca`] = this.node("i", 0);
|
|
2056
|
+
nodes[`${base}/grp/mute`] = this.node("i", 0);
|
|
2057
|
+
return nodes;
|
|
2058
|
+
}
|
|
2059
|
+
generateNodes(count, prefix) {
|
|
2060
|
+
const nodes = {};
|
|
2061
|
+
const isChannelOrAux = prefix === "ch" || prefix === "auxin";
|
|
2062
|
+
const eqBands = prefix === "bus" || prefix === "mtx" ? 6 : 4;
|
|
2063
|
+
for (let i = 1; i <= count; i++) {
|
|
2064
|
+
const padId = i.toString().padStart(2, "0");
|
|
2065
|
+
[i.toString(), padId].forEach((id) => {
|
|
2066
|
+
const base = `/${prefix}/${id}`;
|
|
2067
|
+
if (prefix === "dca") {
|
|
2068
|
+
nodes[`${base}/config/name`] = this.node("s", `DCA ${id}`);
|
|
2069
|
+
nodes[`${base}/config/color`] = this.node("i", 1);
|
|
2070
|
+
nodes[`${base}/fader`] = this.node("f", .75);
|
|
2071
|
+
nodes[`${base}/on`] = this.node("i", 0);
|
|
2072
|
+
} else {
|
|
2073
|
+
Object.assign(nodes, this.generateChannelStrip(base, eqBands, isChannelOrAux));
|
|
2074
|
+
if (prefix === "ch" || prefix === "auxin" || prefix === "fxrtn" || prefix === "bus") for (let b = 1; b <= 16; b++) {
|
|
2075
|
+
const busId = b.toString().padStart(2, "0");
|
|
2076
|
+
nodes[`${base}/mix/${busId}/level`] = this.node("f", 0);
|
|
2077
|
+
nodes[`${base}/mix/${busId}/on`] = this.node("i", 0);
|
|
2078
|
+
nodes[`${base}/mix/${busId}/pan`] = this.node("f", .5);
|
|
2079
|
+
nodes[`${base}/mix/${busId}/type`] = this.node("i", 0);
|
|
2080
|
+
}
|
|
2081
|
+
}
|
|
2082
|
+
});
|
|
2083
|
+
}
|
|
2084
|
+
return nodes;
|
|
2085
|
+
}
|
|
2086
|
+
generateRange(count, prefix, suffix, type, def, pad = 2, start = 1) {
|
|
2087
|
+
const nodes = {};
|
|
2088
|
+
for (let i = start; i < start + count; i++) {
|
|
2089
|
+
const id = i.toString().padStart(pad, "0");
|
|
2090
|
+
nodes[`${prefix}/${i}${suffix}`] = this.node(type, def);
|
|
2091
|
+
nodes[`${prefix}/${id}${suffix}`] = this.node(type, def);
|
|
2092
|
+
}
|
|
2093
|
+
return nodes;
|
|
2094
|
+
}
|
|
2095
|
+
generateOutputs(prefix, count) {
|
|
2096
|
+
const nodes = {};
|
|
2097
|
+
for (let i = 1; i <= count; i++) {
|
|
2098
|
+
const base = `/outputs/${prefix}/${i.toString().padStart(2, "0")}`;
|
|
2099
|
+
nodes[`${base}/src`] = this.node("i", 0);
|
|
2100
|
+
nodes[`${base}/pos`] = this.node("i", 0);
|
|
2101
|
+
nodes[`${base}/invert`] = this.node("i", 0);
|
|
2102
|
+
nodes[`${base}/delay/on`] = this.node("i", 0);
|
|
2103
|
+
nodes[`${base}/delay/time`] = this.node("f", 0);
|
|
2104
|
+
if (prefix === "p16") {
|
|
2105
|
+
nodes[`${base}/iQ/group`] = this.node("i", 0);
|
|
2106
|
+
nodes[`${base}/iQ/model`] = this.node("i", 0);
|
|
2107
|
+
nodes[`${base}/iQ/eq`] = this.node("i", 0);
|
|
2108
|
+
nodes[`${base}/iQ/speaker`] = this.node("i", 0);
|
|
2109
|
+
}
|
|
2110
|
+
}
|
|
2111
|
+
return nodes;
|
|
2112
|
+
}
|
|
2113
|
+
};
|
|
2114
|
+
|
|
2115
|
+
//#endregion
|
|
2116
|
+
//#region src/domain/services/SchemaRegistry.ts
|
|
2117
|
+
/**
|
|
2118
|
+
* Service providing access to the X32 OSC Schema.
|
|
2119
|
+
* Acts as the "Registry" of all available console parameters.
|
|
2120
|
+
*/
|
|
2121
|
+
var SchemaRegistry = class {
|
|
2122
|
+
_schema;
|
|
2123
|
+
constructor(schemaFactory) {
|
|
2124
|
+
this.schemaFactory = schemaFactory;
|
|
2125
|
+
this._schema = this.schemaFactory.createSchema();
|
|
2126
|
+
}
|
|
2127
|
+
/**
|
|
2128
|
+
* Retrieves the entire schema map.
|
|
2129
|
+
* @returns The internal schema definition record.
|
|
2130
|
+
*/
|
|
2131
|
+
getSchema() {
|
|
2132
|
+
return this._schema;
|
|
2133
|
+
}
|
|
2134
|
+
/**
|
|
2135
|
+
* Retrieves the node definition for a given path.
|
|
2136
|
+
* @param path - The OSC path.
|
|
2137
|
+
* @returns The X32Node definition or undefined if not found.
|
|
2138
|
+
*/
|
|
2139
|
+
getNode(path) {
|
|
2140
|
+
return this._schema[path];
|
|
2141
|
+
}
|
|
2142
|
+
/**
|
|
2143
|
+
* Checks if a path exists in the schema.
|
|
2144
|
+
* @param path - The path to check.
|
|
2145
|
+
* @returns True if the path is registered.
|
|
2146
|
+
*/
|
|
2147
|
+
has(path) {
|
|
2148
|
+
return path in this._schema;
|
|
2149
|
+
}
|
|
2150
|
+
/**
|
|
2151
|
+
* Returns all paths in the schema.
|
|
2152
|
+
* @returns Array of all registered OSC paths.
|
|
2153
|
+
*/
|
|
2154
|
+
getAllPaths() {
|
|
2155
|
+
return Object.keys(this._schema);
|
|
2156
|
+
}
|
|
2157
|
+
/**
|
|
2158
|
+
* Maps an absolute X32 global index to its specific OSC root path.
|
|
2159
|
+
* Used for batch subscriptions where clients request ranges of channels.
|
|
2160
|
+
* @param index - The absolute integer index.
|
|
2161
|
+
* @returns The root path string or null if index is out of bounds.
|
|
2162
|
+
*/
|
|
2163
|
+
getRootFromIndex(index) {
|
|
2164
|
+
if (index >= 0 && index <= 31) return `/ch/${(index + 1).toString().padStart(2, "0")}`;
|
|
2165
|
+
if (index >= 32 && index <= 39) return `/auxin/${(index - 31).toString().padStart(2, "0")}`;
|
|
2166
|
+
if (index >= 40 && index <= 47) return `/fxrtn/${(index - 39).toString().padStart(2, "0")}`;
|
|
2167
|
+
if (index >= 48 && index <= 63) return `/bus/${(index - 47).toString().padStart(2, "0")}`;
|
|
2168
|
+
if (index >= 64 && index <= 69) return `/mtx/${(index - 63).toString().padStart(2, "0")}`;
|
|
2169
|
+
if (index === 70) return "/main/st";
|
|
2170
|
+
if (index === 71) return "/main/m";
|
|
2171
|
+
if (index >= 72 && index <= 79) return `/dca/${(index - 71).toString().padStart(2, "0")}`;
|
|
2172
|
+
return null;
|
|
2173
|
+
}
|
|
2174
|
+
};
|
|
2175
|
+
|
|
2176
|
+
//#endregion
|
|
2177
|
+
export { ConsoleLogger as a, X32Node as c, METER_COUNTS as d, MeterData as f, UdpNetworkGateway as i, X32State as l, LogCategory as m, SchemaFactory as n, LogLevel as o, SubscriptionManager as p, OscCodec as r, InMemoryStateRepository as s, SchemaRegistry as t, SimulationService as u };
|