libadlmidi-js 1.1.1 → 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 -63
  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 +468 -22
  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 -54
  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 */
@@ -30,9 +38,21 @@ var Emulator = Object.freeze({
30
38
  /** Nuked OPL2 LLE - Transistor-level emulation */
31
39
  NUKED_OPL2_LLE: 9,
32
40
  /** Nuked OPL3 LLE - Transistor-level emulation */
33
- NUKED_OPL3_LLE: 10
41
+ NUKED_OPL3_LLE: 10,
42
+ /** Nuked OPL2 Lite - Lightweight OPL2 emulation for AdLib-era music */
43
+ NUKED_OPL2_LITE: 11
34
44
  });
35
- 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;
36
56
  var AdlMidi = class {
37
57
  /**
38
58
  * Create a new AdlMidi instance
@@ -44,6 +64,8 @@ var AdlMidi = class {
44
64
  __privateAdd(this, _ready, false);
45
65
  /** @type {Map<string, Set<Function>>} */
46
66
  __privateAdd(this, _messageHandlers, /* @__PURE__ */ new Map());
67
+ /** @type {number} */
68
+ __privateAdd(this, _nextRequestId, 0);
47
69
  this.ctx = context || null;
48
70
  this.node = null;
49
71
  }
@@ -226,7 +248,7 @@ var AdlMidi = class {
226
248
  * @param {ArrayBuffer} arrayBuffer - Bank file data
227
249
  * @returns {Promise<void>}
228
250
  */
229
- async loadBank(arrayBuffer) {
251
+ async loadBankData(arrayBuffer) {
230
252
  return new Promise((resolve, reject) => {
231
253
  __privateMethod(this, _AdlMidi_instances, onceMessage_fn).call(
232
254
  this,
@@ -240,7 +262,7 @@ var AdlMidi = class {
240
262
  }
241
263
  }
242
264
  );
243
- __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 });
244
266
  });
245
267
  }
246
268
  /**
@@ -343,6 +365,23 @@ var AdlMidi = class {
343
365
  __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "getNumFourOpChannels" });
344
366
  });
345
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
+ }
346
385
  /**
347
386
  * Enable/disable scaling of modulators by volume
348
387
  * @param {boolean} enabled
@@ -406,32 +445,66 @@ var AdlMidi = class {
406
445
  });
407
446
  }
408
447
  /**
409
- * Set the volume model
448
+ * Set the volume range model
410
449
  * @param {number} model - Volume model number
411
450
  */
412
- setVolumeModel(model) {
413
- __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 });
414
453
  }
415
454
  /**
416
- * Enable/disable rhythm mode (percussion)
455
+ * Enable/disable soft stereo panning
417
456
  * @param {boolean} enabled
418
457
  */
419
- setPercussionMode(enabled) {
420
- __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 });
421
460
  }
422
461
  /**
423
462
  * Enable/disable deep vibrato
424
463
  * @param {boolean} enabled
425
464
  */
426
- setVibrato(enabled) {
427
- __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
+ });
428
484
  }
429
485
  /**
430
486
  * Enable/disable deep tremolo
431
487
  * @param {boolean} enabled
432
488
  */
433
- setTremolo(enabled) {
434
- __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
+ });
435
508
  }
436
509
  /**
437
510
  * Run emulator with PCM rate to reduce CPU usage
@@ -447,7 +520,7 @@ var AdlMidi = class {
447
520
  * - nuked profile: NUKED only
448
521
  * - dosbox profile: DOSBOX only
449
522
  * - light profile: NUKED, DOSBOX
450
- * - full profile: NUKED, DOSBOX, OPAL, JAVA, ESFMU, YMFM_OPL2, YMFM_OPL3
523
+ * - full profile: NUKED, DOSBOX, OPAL, JAVA, ESFMu, YMFM_OPL2, YMFM_OPL3
451
524
  *
452
525
  * @param {number} emulator - Emulator ID from the Emulator enum
453
526
  * @returns {Promise<void>} Resolves when emulator is switched, rejects if unavailable
@@ -492,6 +565,23 @@ var AdlMidi = class {
492
565
  __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "getEmulatorName" });
493
566
  });
494
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
+ }
495
585
  /**
496
586
  * Get the version string of the linked libADLMIDI library
497
587
  * @returns {Promise<string>}
@@ -564,17 +654,17 @@ var AdlMidi = class {
564
654
  * Get the volume range model
565
655
  * @returns {Promise<number>}
566
656
  */
567
- async getVolumeModel() {
657
+ async getVolumeRangeModel() {
568
658
  return new Promise((resolve) => {
569
659
  __privateMethod(this, _AdlMidi_instances, onceMessage_fn).call(
570
660
  this,
571
- "volumeModel",
661
+ "volumeRangeModel",
572
662
  /** @param {{model: number}} msg */
573
663
  (msg) => {
574
664
  resolve(msg.model);
575
665
  }
576
666
  );
577
- __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "getVolumeModel" });
667
+ __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "getVolumeRangeModel" });
578
668
  });
579
669
  }
580
670
  /**
@@ -598,6 +688,146 @@ var AdlMidi = class {
598
688
  __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "getEmbeddedBanks" });
599
689
  });
600
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
+ }
601
831
  /**
602
832
  * Reset the synthesizer
603
833
  * @returns {void}
@@ -662,6 +892,60 @@ var AdlMidi = class {
662
892
  __privateMethod(this, _AdlMidi_instances, send_fn).call(this, { type: "getMusicCopyright" });
663
893
  });
664
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
+ }
665
949
  /**
666
950
  * Start or resume MIDI file playback
667
951
  * @returns {void}
@@ -689,8 +973,149 @@ var AdlMidi = class {
689
973
  * @param {boolean} enabled - Whether to loop
690
974
  * @returns {void}
691
975
  */
692
- setLoop(enabled) {
693
- __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
+ });
694
1119
  }
695
1120
  /**
696
1121
  * Set the playback tempo multiplier
@@ -783,6 +1208,7 @@ var AdlMidi = class {
783
1208
  };
784
1209
  _ready = new WeakMap();
785
1210
  _messageHandlers = new WeakMap();
1211
+ _nextRequestId = new WeakMap();
786
1212
  _AdlMidi_instances = new WeakSet();
787
1213
  /**
788
1214
  * Internal message handler
@@ -812,6 +1238,25 @@ onceMessage_fn = function(type, handler) {
812
1238
  };
813
1239
  __privateGet(this, _messageHandlers).get(type)?.add(wrappedHandler);
814
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
+ };
815
1260
  /**
816
1261
  * Send a message to the processor
817
1262
  * @param {Object} msg - Message to send
@@ -825,6 +1270,7 @@ var libadlmidi_default = AdlMidi;
825
1270
  export {
826
1271
  AdlMidi,
827
1272
  Emulator,
1273
+ TrackOption,
828
1274
  libadlmidi_default as default
829
1275
  };
830
1276
  //# sourceMappingURL=libadlmidi.js.map