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