libadlmidi-js 1.2.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/dist/core.d.ts +186 -4
  2. package/dist/fm_banks/manifest.json +1 -1
  3. package/dist/libadlmidi.d.ts +143 -65
  4. package/dist/libadlmidi.dosbox.browser.js +1 -1
  5. package/dist/libadlmidi.dosbox.browser.wasm +0 -0
  6. package/dist/libadlmidi.dosbox.core.js +1 -1
  7. package/dist/libadlmidi.dosbox.core.wasm +0 -0
  8. package/dist/libadlmidi.dosbox.js +0 -0
  9. package/dist/libadlmidi.dosbox.processor.js +242 -74
  10. package/dist/libadlmidi.dosbox.slim.browser.js +1 -1
  11. package/dist/libadlmidi.dosbox.slim.browser.wasm +0 -0
  12. package/dist/libadlmidi.dosbox.slim.core.js +1 -1
  13. package/dist/libadlmidi.dosbox.slim.core.wasm +0 -0
  14. package/dist/libadlmidi.dosbox.slim.js +0 -0
  15. package/dist/libadlmidi.dosbox.slim.processor.js +242 -74
  16. package/dist/libadlmidi.full.browser.js +1 -1
  17. package/dist/libadlmidi.full.browser.wasm +0 -0
  18. package/dist/libadlmidi.full.core.js +1 -1
  19. package/dist/libadlmidi.full.core.wasm +0 -0
  20. package/dist/libadlmidi.full.js +0 -0
  21. package/dist/libadlmidi.full.processor.js +242 -74
  22. package/dist/libadlmidi.full.slim.browser.js +1 -1
  23. package/dist/libadlmidi.full.slim.browser.wasm +0 -0
  24. package/dist/libadlmidi.full.slim.core.js +1 -1
  25. package/dist/libadlmidi.full.slim.core.wasm +0 -0
  26. package/dist/libadlmidi.full.slim.js +0 -0
  27. package/dist/libadlmidi.full.slim.processor.js +242 -74
  28. package/dist/libadlmidi.js +465 -21
  29. package/dist/libadlmidi.js.map +3 -3
  30. package/dist/libadlmidi.light.browser.js +1 -1
  31. package/dist/libadlmidi.light.browser.wasm +0 -0
  32. package/dist/libadlmidi.light.core.js +1 -1
  33. package/dist/libadlmidi.light.core.wasm +0 -0
  34. package/dist/libadlmidi.light.js +0 -0
  35. package/dist/libadlmidi.light.processor.js +242 -74
  36. package/dist/libadlmidi.light.slim.browser.js +1 -1
  37. package/dist/libadlmidi.light.slim.browser.wasm +0 -0
  38. package/dist/libadlmidi.light.slim.core.js +1 -1
  39. package/dist/libadlmidi.light.slim.core.wasm +0 -0
  40. package/dist/libadlmidi.light.slim.js +0 -0
  41. package/dist/libadlmidi.light.slim.processor.js +242 -74
  42. package/dist/libadlmidi.nuked.browser.js +1 -1
  43. package/dist/libadlmidi.nuked.browser.wasm +0 -0
  44. package/dist/libadlmidi.nuked.core.js +1 -1
  45. package/dist/libadlmidi.nuked.core.wasm +0 -0
  46. package/dist/libadlmidi.nuked.js +0 -0
  47. package/dist/libadlmidi.nuked.processor.js +242 -74
  48. package/dist/libadlmidi.nuked.slim.browser.js +1 -1
  49. package/dist/libadlmidi.nuked.slim.browser.wasm +0 -0
  50. package/dist/libadlmidi.nuked.slim.core.js +1 -1
  51. package/dist/libadlmidi.nuked.slim.core.wasm +0 -0
  52. package/dist/libadlmidi.nuked.slim.js +0 -0
  53. package/dist/libadlmidi.nuked.slim.processor.js +242 -74
  54. package/dist/profiles/dosbox.d.ts +1 -0
  55. package/dist/profiles/dosbox.slim.d.ts +1 -0
  56. package/dist/profiles/full.d.ts +1 -0
  57. package/dist/profiles/full.slim.d.ts +1 -0
  58. package/dist/profiles/light.d.ts +1 -0
  59. package/dist/profiles/light.slim.d.ts +1 -0
  60. package/dist/profiles/nuked.d.ts +1 -0
  61. package/dist/profiles/nuked.slim.d.ts +1 -0
  62. package/dist/utils/constants.d.ts +59 -0
  63. package/package.json +1 -1
  64. package/src/core.js +352 -4
  65. package/src/libadlmidi.js +374 -56
  66. package/src/processor.js +204 -12
  67. package/src/profiles/dosbox.js +7 -4
  68. package/src/profiles/dosbox.slim.js +7 -4
  69. package/src/profiles/full.js +7 -4
  70. package/src/profiles/full.slim.js +7 -4
  71. package/src/profiles/light.js +7 -4
  72. package/src/profiles/light.slim.js +7 -4
  73. package/src/profiles/nuked.js +7 -4
  74. package/src/profiles/nuked.slim.js +7 -4
  75. package/src/utils/constants.js +51 -0
@@ -6,8 +6,16 @@ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read fr
6
6
  var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
7
7
  var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
8
8
  var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
9
+ var __privateWrapper = (obj, member, setter, getter) => ({
10
+ set _(value) {
11
+ __privateSet(obj, member, value, setter);
12
+ },
13
+ get _() {
14
+ return __privateGet(obj, member, getter);
15
+ }
16
+ });
9
17
 
10
- // src/libadlmidi.js
18
+ // src/utils/constants.js
11
19
  var Emulator = Object.freeze({
12
20
  /** Nuked OPL3 v1.8 - Most accurate, higher CPU usage */
13
21
  NUKED: 0,
@@ -20,7 +28,7 @@ var Emulator = Object.freeze({
20
28
  /** Java OPL3 - Port of emu8950 */
21
29
  JAVA: 4,
22
30
  /** ESFMu - ESFM chip emulator */
23
- ESFMU: 5,
31
+ ESFMu: 5,
24
32
  /** MAME OPL2 */
25
33
  MAME_OPL2: 6,
26
34
  /** YMFM OPL2 */
@@ -34,7 +42,17 @@ var Emulator = Object.freeze({
34
42
  /** Nuked OPL2 Lite - Lightweight OPL2 emulation for AdLib-era music */
35
43
  NUKED_OPL2_LITE: 11
36
44
  });
37
- var _ready, _messageHandlers, _AdlMidi_instances, handleMessage_fn, onceMessage_fn, send_fn;
45
+ var TrackOption = Object.freeze({
46
+ /** Enable the track (default state) */
47
+ ON: 1,
48
+ /** Mute/disable the track */
49
+ OFF: 2,
50
+ /** Solo the track (mute all others) */
51
+ SOLO: 3
52
+ });
53
+
54
+ // src/libadlmidi.js
55
+ var _ready, _messageHandlers, _nextRequestId, _AdlMidi_instances, handleMessage_fn, onceMessage_fn, onceCorrelatedMessage_fn, send_fn;
38
56
  var AdlMidi = class {
39
57
  /**
40
58
  * Create a new AdlMidi instance
@@ -46,6 +64,8 @@ var AdlMidi = class {
46
64
  __privateAdd(this, _ready, false);
47
65
  /** @type {Map<string, Set<Function>>} */
48
66
  __privateAdd(this, _messageHandlers, /* @__PURE__ */ new Map());
67
+ /** @type {number} */
68
+ __privateAdd(this, _nextRequestId, 0);
49
69
  this.ctx = context || null;
50
70
  this.node = null;
51
71
  }
@@ -228,7 +248,7 @@ var AdlMidi = class {
228
248
  * @param {ArrayBuffer} arrayBuffer - Bank file data
229
249
  * @returns {Promise<void>}
230
250
  */
231
- async loadBank(arrayBuffer) {
251
+ async loadBankData(arrayBuffer) {
232
252
  return new Promise((resolve, reject) => {
233
253
  __privateMethod(this, _AdlMidi_instances, onceMessage_fn).call(
234
254
  this,
@@ -242,7 +262,7 @@ var AdlMidi = class {
242
262
  }
243
263
  }
244
264
  );
245
- __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "loadBank", data: arrayBuffer });
265
+ __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "loadBankData", data: arrayBuffer });
246
266
  });
247
267
  }
248
268
  /**
@@ -345,6 +365,23 @@ var AdlMidi = class {
345
365
  __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "getNumFourOpChannels" });
346
366
  });
347
367
  }
368
+ /**
369
+ * Get the number of 4-operator channels obtained
370
+ * @returns {Promise<number>}
371
+ */
372
+ async getNumFourOpChannelsObtained() {
373
+ return new Promise((resolve) => {
374
+ __privateMethod(this, _AdlMidi_instances, onceMessage_fn).call(
375
+ this,
376
+ "numFourOpChannelsObtained",
377
+ /** @param {{channels: number}} msg */
378
+ (msg) => {
379
+ resolve(msg.channels);
380
+ }
381
+ );
382
+ __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "getNumFourOpChannelsObtained" });
383
+ });
384
+ }
348
385
  /**
349
386
  * Enable/disable scaling of modulators by volume
350
387
  * @param {boolean} enabled
@@ -408,32 +445,66 @@ var AdlMidi = class {
408
445
  });
409
446
  }
410
447
  /**
411
- * Set the volume model
448
+ * Set the volume range model
412
449
  * @param {number} model - Volume model number
413
450
  */
414
- setVolumeModel(model) {
415
- __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "setVolumeModel", model });
451
+ setVolumeRangeModel(model) {
452
+ __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "setVolumeRangeModel", model });
416
453
  }
417
454
  /**
418
- * Enable/disable rhythm mode (percussion)
455
+ * Enable/disable soft stereo panning
419
456
  * @param {boolean} enabled
420
457
  */
421
- setPercussionMode(enabled) {
422
- __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "setPercMode", enabled });
458
+ setSoftPanEnabled(enabled) {
459
+ __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "setSoftPanEnabled", enabled });
423
460
  }
424
461
  /**
425
462
  * Enable/disable deep vibrato
426
463
  * @param {boolean} enabled
427
464
  */
428
- setVibrato(enabled) {
429
- __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "setVibrato", enabled });
465
+ setDeepVibrato(enabled) {
466
+ __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "setDeepVibrato", enabled });
467
+ }
468
+ /**
469
+ * Get deep vibrato state
470
+ * @returns {Promise<boolean>}
471
+ */
472
+ async getDeepVibrato() {
473
+ return new Promise((resolve) => {
474
+ __privateMethod(this, _AdlMidi_instances, onceMessage_fn).call(
475
+ this,
476
+ "deepVibrato",
477
+ /** @param {{enabled: boolean}} msg */
478
+ (msg) => {
479
+ resolve(msg.enabled);
480
+ }
481
+ );
482
+ __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "getDeepVibrato" });
483
+ });
430
484
  }
431
485
  /**
432
486
  * Enable/disable deep tremolo
433
487
  * @param {boolean} enabled
434
488
  */
435
- setTremolo(enabled) {
436
- __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "setTremolo", enabled });
489
+ setDeepTremolo(enabled) {
490
+ __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "setDeepTremolo", enabled });
491
+ }
492
+ /**
493
+ * Get deep tremolo state
494
+ * @returns {Promise<boolean>}
495
+ */
496
+ async getDeepTremolo() {
497
+ return new Promise((resolve) => {
498
+ __privateMethod(this, _AdlMidi_instances, onceMessage_fn).call(
499
+ this,
500
+ "deepTremolo",
501
+ /** @param {{enabled: boolean}} msg */
502
+ (msg) => {
503
+ resolve(msg.enabled);
504
+ }
505
+ );
506
+ __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "getDeepTremolo" });
507
+ });
437
508
  }
438
509
  /**
439
510
  * Run emulator with PCM rate to reduce CPU usage
@@ -449,7 +520,7 @@ var AdlMidi = class {
449
520
  * - nuked profile: NUKED only
450
521
  * - dosbox profile: DOSBOX only
451
522
  * - light profile: NUKED, DOSBOX
452
- * - full profile: NUKED, DOSBOX, OPAL, JAVA, ESFMU, YMFM_OPL2, YMFM_OPL3
523
+ * - full profile: NUKED, DOSBOX, OPAL, JAVA, ESFMu, YMFM_OPL2, YMFM_OPL3
453
524
  *
454
525
  * @param {number} emulator - Emulator ID from the Emulator enum
455
526
  * @returns {Promise<void>} Resolves when emulator is switched, rejects if unavailable
@@ -494,6 +565,23 @@ var AdlMidi = class {
494
565
  __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "getEmulatorName" });
495
566
  });
496
567
  }
568
+ /**
569
+ * Get the last error info for the player instance
570
+ * @returns {Promise<string>}
571
+ */
572
+ async getErrorInfo() {
573
+ return new Promise((resolve) => {
574
+ __privateMethod(this, _AdlMidi_instances, onceMessage_fn).call(
575
+ this,
576
+ "errorInfo",
577
+ /** @param {{info: string}} msg */
578
+ (msg) => {
579
+ resolve(msg.info);
580
+ }
581
+ );
582
+ __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "getErrorInfo" });
583
+ });
584
+ }
497
585
  /**
498
586
  * Get the version string of the linked libADLMIDI library
499
587
  * @returns {Promise<string>}
@@ -566,17 +654,17 @@ var AdlMidi = class {
566
654
  * Get the volume range model
567
655
  * @returns {Promise<number>}
568
656
  */
569
- async getVolumeModel() {
657
+ async getVolumeRangeModel() {
570
658
  return new Promise((resolve) => {
571
659
  __privateMethod(this, _AdlMidi_instances, onceMessage_fn).call(
572
660
  this,
573
- "volumeModel",
661
+ "volumeRangeModel",
574
662
  /** @param {{model: number}} msg */
575
663
  (msg) => {
576
664
  resolve(msg.model);
577
665
  }
578
666
  );
579
- __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "getVolumeModel" });
667
+ __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "getVolumeRangeModel" });
580
668
  });
581
669
  }
582
670
  /**
@@ -600,6 +688,146 @@ var AdlMidi = class {
600
688
  __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "getEmbeddedBanks" });
601
689
  });
602
690
  }
691
+ // ================== Bank Management API ==================
692
+ /**
693
+ * Reserve a number of banks
694
+ * @param {number} count - Number of banks to reserve
695
+ * @returns {Promise<void>} Resolves on success, rejects on failure
696
+ */
697
+ async reserveBanks(count) {
698
+ const reqId = __privateWrapper(this, _nextRequestId)._++;
699
+ return new Promise((resolve, reject) => {
700
+ __privateMethod(this, _AdlMidi_instances, onceCorrelatedMessage_fn).call(
701
+ this,
702
+ "banksReserved",
703
+ reqId,
704
+ /** @param {{success: boolean}} msg */
705
+ (msg) => {
706
+ if (msg.success) {
707
+ resolve();
708
+ } else {
709
+ reject(new Error("Failed to reserve banks"));
710
+ }
711
+ }
712
+ );
713
+ __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "reserveBanks", count, reqId });
714
+ });
715
+ }
716
+ /**
717
+ * Get the bank ID for a given bank identifier
718
+ * @param {BankId} bankId - Bank identifier
719
+ * @returns {Promise<{percussive: number, msb: number, lsb: number}|null>} Bank ID or null if not found
720
+ */
721
+ async getBankId(bankId) {
722
+ const reqId = __privateWrapper(this, _nextRequestId)._++;
723
+ return new Promise((resolve) => {
724
+ __privateMethod(this, _AdlMidi_instances, onceCorrelatedMessage_fn).call(
725
+ this,
726
+ "bankId",
727
+ reqId,
728
+ /** @param {{id: {percussive: number, msb: number, lsb: number}|null}} msg */
729
+ (msg) => {
730
+ resolve(msg.id);
731
+ }
732
+ );
733
+ __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "getBankId", bankId, reqId });
734
+ });
735
+ }
736
+ /**
737
+ * Remove a bank by its identifier
738
+ * @param {BankId} bankId - Bank identifier
739
+ * @returns {Promise<void>} Resolves on success, rejects on failure
740
+ */
741
+ async removeBank(bankId) {
742
+ const reqId = __privateWrapper(this, _nextRequestId)._++;
743
+ return new Promise((resolve, reject) => {
744
+ __privateMethod(this, _AdlMidi_instances, onceCorrelatedMessage_fn).call(
745
+ this,
746
+ "bankRemoved",
747
+ reqId,
748
+ /** @param {{success: boolean}} msg */
749
+ (msg) => {
750
+ if (msg.success) {
751
+ resolve();
752
+ } else {
753
+ reject(new Error("Failed to remove bank"));
754
+ }
755
+ }
756
+ );
757
+ __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "removeBank", bankId, reqId });
758
+ });
759
+ }
760
+ /**
761
+ * Load an embedded bank into a custom bank slot
762
+ * @param {BankId} bankId - Target bank identifier
763
+ * @param {number} num - Embedded bank number to load
764
+ * @returns {Promise<void>} Resolves on success, rejects on failure
765
+ */
766
+ async loadEmbeddedBank(bankId, num) {
767
+ const reqId = __privateWrapper(this, _nextRequestId)._++;
768
+ return new Promise((resolve, reject) => {
769
+ __privateMethod(this, _AdlMidi_instances, onceCorrelatedMessage_fn).call(
770
+ this,
771
+ "embeddedBankLoaded",
772
+ reqId,
773
+ /** @param {{success: boolean}} msg */
774
+ (msg) => {
775
+ if (msg.success) {
776
+ resolve();
777
+ } else {
778
+ reject(new Error("Failed to load embedded bank"));
779
+ }
780
+ }
781
+ );
782
+ __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "loadEmbeddedBank", bankId, num, reqId });
783
+ });
784
+ }
785
+ // ================== SysEx API ==================
786
+ /**
787
+ * Send a System Exclusive (SysEx) message
788
+ * @param {Uint8Array|ArrayBuffer} data - SysEx message data
789
+ * @returns {Promise<void>} Resolves on success, rejects on failure
790
+ */
791
+ async systemExclusive(data) {
792
+ const bytes = data instanceof ArrayBuffer ? new Uint8Array(data) : data;
793
+ const reqId = __privateWrapper(this, _nextRequestId)._++;
794
+ return new Promise((resolve, reject) => {
795
+ __privateMethod(this, _AdlMidi_instances, onceCorrelatedMessage_fn).call(
796
+ this,
797
+ "systemExclusiveSent",
798
+ reqId,
799
+ /** @param {{success: boolean}} msg */
800
+ (msg) => {
801
+ if (msg.success) {
802
+ resolve();
803
+ } else {
804
+ reject(new Error("Failed to send system exclusive message"));
805
+ }
806
+ }
807
+ );
808
+ __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "systemExclusive", data: Array.from(bytes), reqId });
809
+ });
810
+ }
811
+ // ================== Debug / Diagnostics API ==================
812
+ /**
813
+ * Describe the current state of all channels (debug utility)
814
+ * @returns {Promise<{text: string, attr: Uint8Array}>} Channel state text and raw per-channel attribute bytes
815
+ */
816
+ async describeChannels() {
817
+ const reqId = __privateWrapper(this, _nextRequestId)._++;
818
+ return new Promise((resolve) => {
819
+ __privateMethod(this, _AdlMidi_instances, onceCorrelatedMessage_fn).call(
820
+ this,
821
+ "channelsDescribed",
822
+ reqId,
823
+ /** @param {{text: string, attr: number[]}} msg */
824
+ (msg) => {
825
+ resolve({ text: msg.text, attr: new Uint8Array(msg.attr) });
826
+ }
827
+ );
828
+ __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "describeChannels", reqId });
829
+ });
830
+ }
603
831
  /**
604
832
  * Reset the synthesizer
605
833
  * @returns {void}
@@ -664,6 +892,60 @@ var AdlMidi = class {
664
892
  __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "getMusicCopyright" });
665
893
  });
666
894
  }
895
+ /**
896
+ * Get the number of track titles in the loaded MIDI file
897
+ * @returns {Promise<number>}
898
+ */
899
+ async getTrackTitleCount() {
900
+ return new Promise((resolve) => {
901
+ __privateMethod(this, _AdlMidi_instances, onceMessage_fn).call(
902
+ this,
903
+ "trackTitleCount",
904
+ /** @param {{count: number}} msg */
905
+ (msg) => {
906
+ resolve(msg.count);
907
+ }
908
+ );
909
+ __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "getTrackTitleCount" });
910
+ });
911
+ }
912
+ /**
913
+ * Get a track title by index
914
+ * @param {number} index - Track title index
915
+ * @returns {Promise<string>}
916
+ */
917
+ async getTrackTitle(index) {
918
+ const reqId = __privateWrapper(this, _nextRequestId)._++;
919
+ return new Promise((resolve) => {
920
+ __privateMethod(this, _AdlMidi_instances, onceCorrelatedMessage_fn).call(
921
+ this,
922
+ "trackTitle",
923
+ reqId,
924
+ /** @param {{title: string}} msg */
925
+ (msg) => {
926
+ resolve(msg.title);
927
+ }
928
+ );
929
+ __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "getTrackTitle", index, reqId });
930
+ });
931
+ }
932
+ /**
933
+ * Get the number of MIDI markers in the loaded file
934
+ * @returns {Promise<number>}
935
+ */
936
+ async getMarkerCount() {
937
+ return new Promise((resolve) => {
938
+ __privateMethod(this, _AdlMidi_instances, onceMessage_fn).call(
939
+ this,
940
+ "markerCount",
941
+ /** @param {{count: number}} msg */
942
+ (msg) => {
943
+ resolve(msg.count);
944
+ }
945
+ );
946
+ __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "getMarkerCount" });
947
+ });
948
+ }
667
949
  /**
668
950
  * Start or resume MIDI file playback
669
951
  * @returns {void}
@@ -691,8 +973,149 @@ var AdlMidi = class {
691
973
  * @param {boolean} enabled - Whether to loop
692
974
  * @returns {void}
693
975
  */
694
- setLoop(enabled) {
695
- __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "setLoop", enabled });
976
+ setLoopEnabled(enabled) {
977
+ __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "setLoopEnabled", enabled });
978
+ }
979
+ /**
980
+ * Set the number of loop repetitions
981
+ * @param {number} count - Loop count (-1 = infinite, 0 = no loops, 1+ = number of loops)
982
+ */
983
+ setLoopCount(count) {
984
+ __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "setLoopCount", count });
985
+ }
986
+ /**
987
+ * Enable/disable loop hooks only mode
988
+ * @param {boolean} enabled
989
+ */
990
+ setLoopHooksOnly(enabled) {
991
+ __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "setLoopHooksOnly", enabled });
992
+ }
993
+ /**
994
+ * Get the loop start time in seconds
995
+ * @returns {Promise<number>}
996
+ */
997
+ async getLoopStartTime() {
998
+ return new Promise((resolve) => {
999
+ __privateMethod(this, _AdlMidi_instances, onceMessage_fn).call(
1000
+ this,
1001
+ "loopStartTime",
1002
+ /** @param {{time: number}} msg */
1003
+ (msg) => {
1004
+ resolve(msg.time);
1005
+ }
1006
+ );
1007
+ __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "getLoopStartTime" });
1008
+ });
1009
+ }
1010
+ /**
1011
+ * Get the loop end time in seconds
1012
+ * @returns {Promise<number>}
1013
+ */
1014
+ async getLoopEndTime() {
1015
+ return new Promise((resolve) => {
1016
+ __privateMethod(this, _AdlMidi_instances, onceMessage_fn).call(
1017
+ this,
1018
+ "loopEndTime",
1019
+ /** @param {{time: number}} msg */
1020
+ (msg) => {
1021
+ resolve(msg.time);
1022
+ }
1023
+ );
1024
+ __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "getLoopEndTime" });
1025
+ });
1026
+ }
1027
+ /**
1028
+ * Select a song number for multi-song MIDI files
1029
+ * @param {number} num - Song number (0-based)
1030
+ */
1031
+ selectSongNum(num) {
1032
+ __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "selectSongNum", num });
1033
+ }
1034
+ /**
1035
+ * Get the number of songs in the loaded MIDI file
1036
+ * @returns {Promise<number>}
1037
+ */
1038
+ async getSongsCount() {
1039
+ return new Promise((resolve) => {
1040
+ __privateMethod(this, _AdlMidi_instances, onceMessage_fn).call(
1041
+ this,
1042
+ "songsCount",
1043
+ /** @param {{count: number}} msg */
1044
+ (msg) => {
1045
+ resolve(msg.count);
1046
+ }
1047
+ );
1048
+ __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "getSongsCount" });
1049
+ });
1050
+ }
1051
+ /**
1052
+ * Get the number of tracks in the loaded MIDI file
1053
+ * @returns {Promise<number>}
1054
+ */
1055
+ async getTrackCount() {
1056
+ return new Promise((resolve) => {
1057
+ __privateMethod(this, _AdlMidi_instances, onceMessage_fn).call(
1058
+ this,
1059
+ "trackCount",
1060
+ /** @param {{count: number}} msg */
1061
+ (msg) => {
1062
+ resolve(msg.count);
1063
+ }
1064
+ );
1065
+ __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "getTrackCount" });
1066
+ });
1067
+ }
1068
+ /**
1069
+ * Set track options (enable, mute, or solo)
1070
+ * Use the TrackOption enum: TrackOption.ON (1), TrackOption.OFF (2), TrackOption.SOLO (3).
1071
+ * Note: Passing 0 is a silent no-op that resolves without changing state.
1072
+ * @param {number} track - Track index
1073
+ * @param {number} options - Track option from TrackOption enum
1074
+ * @returns {Promise<void>} Resolves on success, rejects on failure
1075
+ */
1076
+ async setTrackOptions(track, options) {
1077
+ const reqId = __privateWrapper(this, _nextRequestId)._++;
1078
+ return new Promise((resolve, reject) => {
1079
+ __privateMethod(this, _AdlMidi_instances, onceCorrelatedMessage_fn).call(
1080
+ this,
1081
+ "trackOptionsSet",
1082
+ reqId,
1083
+ /** @param {{success: boolean}} msg */
1084
+ (msg) => {
1085
+ if (msg.success) {
1086
+ resolve();
1087
+ } else {
1088
+ reject(new Error(`Failed to set track options for track ${track}`));
1089
+ }
1090
+ }
1091
+ );
1092
+ __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "setTrackOptions", track, options, reqId });
1093
+ });
1094
+ }
1095
+ /**
1096
+ * Enable or disable a MIDI channel
1097
+ * @param {number} channel - MIDI channel (0-15)
1098
+ * @param {boolean} enabled - Whether to enable the channel
1099
+ * @returns {Promise<void>} Resolves on success, rejects on failure
1100
+ */
1101
+ async setChannelEnabled(channel, enabled) {
1102
+ const reqId = __privateWrapper(this, _nextRequestId)._++;
1103
+ return new Promise((resolve, reject) => {
1104
+ __privateMethod(this, _AdlMidi_instances, onceCorrelatedMessage_fn).call(
1105
+ this,
1106
+ "channelEnabledSet",
1107
+ reqId,
1108
+ /** @param {{success: boolean}} msg */
1109
+ (msg) => {
1110
+ if (msg.success) {
1111
+ resolve();
1112
+ } else {
1113
+ reject(new Error(`Failed to set channel ${channel} enabled state`));
1114
+ }
1115
+ }
1116
+ );
1117
+ __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "setChannelEnabled", channel, enabled, reqId });
1118
+ });
696
1119
  }
697
1120
  /**
698
1121
  * Set the playback tempo multiplier
@@ -785,6 +1208,7 @@ var AdlMidi = class {
785
1208
  };
786
1209
  _ready = new WeakMap();
787
1210
  _messageHandlers = new WeakMap();
1211
+ _nextRequestId = new WeakMap();
788
1212
  _AdlMidi_instances = new WeakSet();
789
1213
  /**
790
1214
  * Internal message handler
@@ -814,6 +1238,25 @@ onceMessage_fn = function(type, handler) {
814
1238
  };
815
1239
  __privateGet(this, _messageHandlers).get(type)?.add(wrappedHandler);
816
1240
  };
1241
+ /**
1242
+ * Register a one-time handler correlated by request ID.
1243
+ * Allows concurrent operations of the same type without reply misrouting.
1244
+ * @param {string} type - Message type
1245
+ * @param {number} reqId - Request ID to match against
1246
+ * @param {Function} handler - Handler function
1247
+ */
1248
+ onceCorrelatedMessage_fn = function(type, reqId, handler) {
1249
+ if (!__privateGet(this, _messageHandlers).has(type)) {
1250
+ __privateGet(this, _messageHandlers).set(type, /* @__PURE__ */ new Set());
1251
+ }
1252
+ const filteredHandler = (msg) => {
1253
+ if (msg.reqId === reqId) {
1254
+ __privateGet(this, _messageHandlers).get(type)?.delete(filteredHandler);
1255
+ handler(msg);
1256
+ }
1257
+ };
1258
+ __privateGet(this, _messageHandlers).get(type)?.add(filteredHandler);
1259
+ };
817
1260
  /**
818
1261
  * Send a message to the processor
819
1262
  * @param {Object} msg - Message to send
@@ -827,6 +1270,7 @@ var libadlmidi_default = AdlMidi;
827
1270
  export {
828
1271
  AdlMidi,
829
1272
  Emulator,
1273
+ TrackOption,
830
1274
  libadlmidi_default as default
831
1275
  };
832
1276
  //# sourceMappingURL=libadlmidi.js.map