libadlmidi-js 1.2.0 → 2.1.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 (76) hide show
  1. package/README.md +8 -5
  2. package/dist/core.d.ts +191 -4
  3. package/dist/fm_banks/manifest.json +1 -1
  4. package/dist/libadlmidi.d.ts +146 -66
  5. package/dist/libadlmidi.dosbox.browser.js +1 -1
  6. package/dist/libadlmidi.dosbox.browser.wasm +0 -0
  7. package/dist/libadlmidi.dosbox.core.js +1 -1
  8. package/dist/libadlmidi.dosbox.core.wasm +0 -0
  9. package/dist/libadlmidi.dosbox.js +0 -0
  10. package/dist/libadlmidi.dosbox.processor.js +247 -74
  11. package/dist/libadlmidi.dosbox.slim.browser.js +1 -1
  12. package/dist/libadlmidi.dosbox.slim.browser.wasm +0 -0
  13. package/dist/libadlmidi.dosbox.slim.core.js +1 -1
  14. package/dist/libadlmidi.dosbox.slim.core.wasm +0 -0
  15. package/dist/libadlmidi.dosbox.slim.js +0 -0
  16. package/dist/libadlmidi.dosbox.slim.processor.js +247 -74
  17. package/dist/libadlmidi.full.browser.js +1 -1
  18. package/dist/libadlmidi.full.browser.wasm +0 -0
  19. package/dist/libadlmidi.full.core.js +1 -1
  20. package/dist/libadlmidi.full.core.wasm +0 -0
  21. package/dist/libadlmidi.full.js +0 -0
  22. package/dist/libadlmidi.full.processor.js +247 -74
  23. package/dist/libadlmidi.full.slim.browser.js +1 -1
  24. package/dist/libadlmidi.full.slim.browser.wasm +0 -0
  25. package/dist/libadlmidi.full.slim.core.js +1 -1
  26. package/dist/libadlmidi.full.slim.core.wasm +0 -0
  27. package/dist/libadlmidi.full.slim.js +0 -0
  28. package/dist/libadlmidi.full.slim.processor.js +247 -74
  29. package/dist/libadlmidi.js +473 -24
  30. package/dist/libadlmidi.js.map +3 -3
  31. package/dist/libadlmidi.light.browser.js +1 -1
  32. package/dist/libadlmidi.light.browser.wasm +0 -0
  33. package/dist/libadlmidi.light.core.js +1 -1
  34. package/dist/libadlmidi.light.core.wasm +0 -0
  35. package/dist/libadlmidi.light.js +0 -0
  36. package/dist/libadlmidi.light.processor.js +247 -74
  37. package/dist/libadlmidi.light.slim.browser.js +1 -1
  38. package/dist/libadlmidi.light.slim.browser.wasm +0 -0
  39. package/dist/libadlmidi.light.slim.core.js +1 -1
  40. package/dist/libadlmidi.light.slim.core.wasm +0 -0
  41. package/dist/libadlmidi.light.slim.js +0 -0
  42. package/dist/libadlmidi.light.slim.processor.js +247 -74
  43. package/dist/libadlmidi.nuked.browser.js +1 -1
  44. package/dist/libadlmidi.nuked.browser.wasm +0 -0
  45. package/dist/libadlmidi.nuked.core.js +1 -1
  46. package/dist/libadlmidi.nuked.core.wasm +0 -0
  47. package/dist/libadlmidi.nuked.js +0 -0
  48. package/dist/libadlmidi.nuked.processor.js +247 -74
  49. package/dist/libadlmidi.nuked.slim.browser.js +1 -1
  50. package/dist/libadlmidi.nuked.slim.browser.wasm +0 -0
  51. package/dist/libadlmidi.nuked.slim.core.js +1 -1
  52. package/dist/libadlmidi.nuked.slim.core.wasm +0 -0
  53. package/dist/libadlmidi.nuked.slim.js +0 -0
  54. package/dist/libadlmidi.nuked.slim.processor.js +247 -74
  55. package/dist/profiles/dosbox.d.ts +7 -2
  56. package/dist/profiles/dosbox.slim.d.ts +7 -2
  57. package/dist/profiles/full.d.ts +7 -2
  58. package/dist/profiles/full.slim.d.ts +7 -2
  59. package/dist/profiles/light.d.ts +7 -2
  60. package/dist/profiles/light.slim.d.ts +7 -2
  61. package/dist/profiles/nuked.d.ts +7 -2
  62. package/dist/profiles/nuked.slim.d.ts +7 -2
  63. package/dist/utils/constants.d.ts +61 -0
  64. package/package.json +30 -9
  65. package/src/core.js +361 -4
  66. package/src/libadlmidi.js +379 -58
  67. package/src/processor.js +210 -12
  68. package/src/profiles/dosbox.js +20 -10
  69. package/src/profiles/dosbox.slim.js +20 -10
  70. package/src/profiles/full.js +21 -11
  71. package/src/profiles/full.slim.js +21 -11
  72. package/src/profiles/light.js +21 -11
  73. package/src/profiles/light.slim.js +21 -11
  74. package/src/profiles/nuked.js +21 -11
  75. package/src/profiles/nuked.slim.js +21 -11
  76. package/src/utils/constants.js +53 -0
package/src/core.js CHANGED
@@ -16,6 +16,9 @@ import {
16
16
  encodeInstrument,
17
17
  } from './utils/struct.js';
18
18
 
19
+ import { Emulator, TrackOption } from './utils/constants.js';
20
+ export { Emulator, TrackOption };
21
+
19
22
  /**
20
23
  * Low-level OPL3 synthesis interface.
21
24
  *
@@ -48,6 +51,8 @@ export class AdlMidiCore {
48
51
  * @param {Object} options
49
52
  * @param {string} options.corePath - Path to the .core.js WASM loader module
50
53
  * @param {ArrayBuffer} [options.wasmBinary] - Pre-loaded WASM binary (optional)
54
+ * @param {number} [options.defaultEmulator] - Emulator to switch to after init.
55
+ * Profile wrappers use this to default to NUKED_FAST.
51
56
  * @returns {Promise<AdlMidiCore>}
52
57
  */
53
58
  static async create(options) {
@@ -70,6 +75,7 @@ export class AdlMidiCore {
70
75
  core._sampleRate = 44100;
71
76
  core._audioBuffer = null;
72
77
  core._audioBufferPtr = null;
78
+ core._defaultEmulator = options.defaultEmulator;
73
79
 
74
80
  return core;
75
81
  }
@@ -85,6 +91,8 @@ export class AdlMidiCore {
85
91
  this._audioBuffer = null;
86
92
  /** @private @type {number|null} */
87
93
  this._audioBufferPtr = null;
94
+ /** @private @type {number|undefined} */
95
+ this._defaultEmulator = undefined;
88
96
  }
89
97
 
90
98
  /**
@@ -105,6 +113,10 @@ export class AdlMidiCore {
105
113
  throw new Error('Failed to initialize ADL MIDI player');
106
114
  }
107
115
 
116
+ if (this._defaultEmulator !== undefined) {
117
+ this._module._adl_switchEmulator(this._player, this._defaultEmulator);
118
+ }
119
+
108
120
  return true;
109
121
  }
110
122
 
@@ -242,6 +254,16 @@ export class AdlMidiCore {
242
254
  return this._module._adl_getNumFourOpsChn(this._player);
243
255
  }
244
256
 
257
+ /**
258
+ * Get the number of 4-operator channels obtained.
259
+ *
260
+ * @returns {number} Count of channels obtained
261
+ */
262
+ getNumFourOpChannelsObtained() {
263
+ this._ensurePlayer();
264
+ return this._module._adl_getNumFourOpsChnObtained(this._player);
265
+ }
266
+
245
267
  /**
246
268
  * Enable/disable scaling of modulators by volume.
247
269
  *
@@ -307,7 +329,7 @@ export class AdlMidiCore {
307
329
  *
308
330
  * @param {boolean} enabled
309
331
  */
310
- setSoftPan(enabled) {
332
+ setSoftPanEnabled(enabled) {
311
333
  this._ensurePlayer();
312
334
  this._module._adl_setSoftPanEnabled(this._player, enabled ? 1 : 0);
313
335
  }
@@ -322,6 +344,16 @@ export class AdlMidiCore {
322
344
  this._module._adl_setHVibrato(this._player, enabled ? 1 : 0);
323
345
  }
324
346
 
347
+ /**
348
+ * Get deep vibrato state.
349
+ *
350
+ * @returns {boolean}
351
+ */
352
+ getDeepVibrato() {
353
+ this._ensurePlayer();
354
+ return this._module._adl_getHVibrato(this._player) !== 0;
355
+ }
356
+
325
357
  /**
326
358
  * Enable/disable deep tremolo.
327
359
  *
@@ -332,6 +364,16 @@ export class AdlMidiCore {
332
364
  this._module._adl_setHTremolo(this._player, enabled ? 1 : 0);
333
365
  }
334
366
 
367
+ /**
368
+ * Get deep tremolo state.
369
+ *
370
+ * @returns {boolean}
371
+ */
372
+ getDeepTremolo() {
373
+ this._ensurePlayer();
374
+ return this._module._adl_getHTremolo(this._player) !== 0;
375
+ }
376
+
335
377
  /**
336
378
  * Switch OPL3 emulator (if multiple are compiled in).
337
379
  *
@@ -354,6 +396,27 @@ export class AdlMidiCore {
354
396
  return this._module.UTF8ToString(ptr);
355
397
  }
356
398
 
399
+ /**
400
+ * Get the last global error string (static, no player needed).
401
+ *
402
+ * @returns {string} Error string or empty string
403
+ */
404
+ getErrorString() {
405
+ const ptr = this._module._adl_errorString();
406
+ return ptr ? this._module.UTF8ToString(ptr) : '';
407
+ }
408
+
409
+ /**
410
+ * Get the last error info for this player instance.
411
+ *
412
+ * @returns {string} Error info string or empty string
413
+ */
414
+ getErrorInfo() {
415
+ this._ensurePlayer();
416
+ const ptr = this._module._adl_errorInfo(this._player);
417
+ return ptr ? this._module.UTF8ToString(ptr) : '';
418
+ }
419
+
357
420
  /**
358
421
  * Get the version string of the linked libADLMIDI library.
359
422
  *
@@ -403,7 +466,7 @@ export class AdlMidiCore {
403
466
  *
404
467
  * @returns {number}
405
468
  */
406
- getVolumeModel() {
469
+ getVolumeRangeModel() {
407
470
  this._ensurePlayer();
408
471
  return this._module._adl_getVolumeRangeModel(this._player);
409
472
  }
@@ -413,7 +476,7 @@ export class AdlMidiCore {
413
476
  *
414
477
  * @param {number} model - Volume model type
415
478
  */
416
- setVolumeModel(model) {
479
+ setVolumeRangeModel(model) {
417
480
  this._ensurePlayer();
418
481
  this._module._adl_setVolumeRangeModel(this._player, model);
419
482
  }
@@ -625,6 +688,38 @@ export class AdlMidiCore {
625
688
  return ptr ? this._module.UTF8ToString(ptr) : '';
626
689
  }
627
690
 
691
+ /**
692
+ * Get the number of track titles in the loaded MIDI file.
693
+ *
694
+ * @returns {number} Number of track titles
695
+ */
696
+ getTrackTitleCount() {
697
+ this._ensurePlayer();
698
+ return this._module._adl_metaTrackTitleCount(this._player);
699
+ }
700
+
701
+ /**
702
+ * Get a track title by index.
703
+ *
704
+ * @param {number} index - Track title index
705
+ * @returns {string} Track title or empty string
706
+ */
707
+ getTrackTitle(index) {
708
+ this._ensurePlayer();
709
+ const ptr = this._module._adl_metaTrackTitle(this._player, index);
710
+ return ptr ? this._module.UTF8ToString(ptr) : '';
711
+ }
712
+
713
+ /**
714
+ * Get the number of MIDI markers in the loaded file.
715
+ *
716
+ * @returns {number} Number of markers
717
+ */
718
+ getMarkerCount() {
719
+ this._ensurePlayer();
720
+ return this._module._adl_metaMarkerCount(this._player);
721
+ }
722
+
628
723
  /**
629
724
  * Play MIDI file and generate audio.
630
725
  *
@@ -709,11 +804,107 @@ export class AdlMidiCore {
709
804
  *
710
805
  * @param {boolean} enabled
711
806
  */
712
- setLooping(enabled) {
807
+ setLoopEnabled(enabled) {
713
808
  this._ensurePlayer();
714
809
  this._module._adl_setLoopEnabled(this._player, enabled ? 1 : 0);
715
810
  }
716
811
 
812
+ /**
813
+ * Set the number of loop repetitions.
814
+ *
815
+ * @param {number} count - Loop count (-1 = infinite, 0 = no loops, 1+ = number of loops)
816
+ */
817
+ setLoopCount(count) {
818
+ this._ensurePlayer();
819
+ this._module._adl_setLoopCount(this._player, count);
820
+ }
821
+
822
+ /**
823
+ * Enable/disable loop hooks only mode.
824
+ *
825
+ * @param {boolean} enabled
826
+ */
827
+ setLoopHooksOnly(enabled) {
828
+ this._ensurePlayer();
829
+ this._module._adl_setLoopHooksOnly(this._player, enabled ? 1 : 0);
830
+ }
831
+
832
+ /**
833
+ * Get the loop start time in seconds.
834
+ *
835
+ * @returns {number} Loop start time in seconds
836
+ */
837
+ getLoopStartTime() {
838
+ this._ensurePlayer();
839
+ return this._module._adl_loopStartTime(this._player);
840
+ }
841
+
842
+ /**
843
+ * Get the loop end time in seconds.
844
+ *
845
+ * @returns {number} Loop end time in seconds
846
+ */
847
+ getLoopEndTime() {
848
+ this._ensurePlayer();
849
+ return this._module._adl_loopEndTime(this._player);
850
+ }
851
+
852
+ /**
853
+ * Select a song number for multi-song MIDI files.
854
+ *
855
+ * @param {number} num - Song number (0-based)
856
+ */
857
+ selectSongNum(num) {
858
+ this._ensurePlayer();
859
+ this._module._adl_selectSongNum(this._player, num);
860
+ }
861
+
862
+ /**
863
+ * Get the number of songs in the loaded MIDI file.
864
+ *
865
+ * @returns {number} Number of songs
866
+ */
867
+ getSongsCount() {
868
+ this._ensurePlayer();
869
+ return this._module._adl_getSongsCount(this._player);
870
+ }
871
+
872
+ /**
873
+ * Get the number of tracks in the loaded MIDI file.
874
+ *
875
+ * @returns {number} Number of tracks
876
+ */
877
+ getTrackCount() {
878
+ this._ensurePlayer();
879
+ return this._module._adl_trackCount(this._player);
880
+ }
881
+
882
+ /**
883
+ * Set track options (enable, mute, or solo).
884
+ * Use the TrackOption enum: TrackOption.ON (1), TrackOption.OFF (2), TrackOption.SOLO (3).
885
+ * Note: Passing 0 is a silent no-op that returns true without changing state.
886
+ *
887
+ * @param {number} track - Track index
888
+ * @param {number} options - Track option from TrackOption enum
889
+ * @returns {boolean} True if successful
890
+ */
891
+ setTrackOptions(track, options) {
892
+ this._ensurePlayer();
893
+ return this._module._adl_setTrackOptions(this._player, track, options) === 0;
894
+ }
895
+
896
+ /**
897
+ * Enable or disable a MIDI channel.
898
+ *
899
+ * @param {number} channel - MIDI channel (0-15)
900
+ * @param {boolean} enabled - Whether to enable the channel
901
+ * @returns {boolean} True if successful
902
+ */
903
+ setChannelEnabled(channel, enabled) {
904
+ this._ensurePlayer();
905
+ return this._module._adl_setChannelEnabled(this._player, channel, enabled ? 1 : 0) === 0;
906
+ }
907
+
717
908
  /**
718
909
  * Set playback tempo multiplier.
719
910
  *
@@ -724,6 +915,126 @@ export class AdlMidiCore {
724
915
  this._module._adl_setTempo(this._player, tempo);
725
916
  }
726
917
 
918
+ // =========================================================================
919
+ // Bank Management
920
+ // =========================================================================
921
+
922
+ /**
923
+ * Reserve a number of banks.
924
+ *
925
+ * @param {number} count - Number of banks to reserve
926
+ * @returns {boolean} True if successful
927
+ */
928
+ reserveBanks(count) {
929
+ this._ensurePlayer();
930
+ // adl_reserveBanks returns the resulting capacity (>= 0), not 0 on success
931
+ return this._module._adl_reserveBanks(this._player, count) >= 0;
932
+ }
933
+
934
+ /**
935
+ * Get the bank ID for a given bank identifier.
936
+ *
937
+ * @param {Object} bankId - Bank identifier
938
+ * @param {boolean|number} bankId.percussive - True/1 for percussion, false/0 for melodic
939
+ * @param {number} bankId.msb - Bank MSB
940
+ * @param {number} bankId.lsb - Bank LSB
941
+ * @returns {{percussive: number, msb: number, lsb: number}|null} Bank ID or null if not found
942
+ */
943
+ getBankId(bankId) {
944
+ this._ensurePlayer();
945
+ const bankIdPtr = this._module._malloc(SIZEOF_ADL_BANK_ID);
946
+ this._module.HEAPU8[bankIdPtr] = bankId.percussive ? 1 : 0;
947
+ this._module.HEAPU8[bankIdPtr + 1] = bankId.msb || 0;
948
+ this._module.HEAPU8[bankIdPtr + 2] = bankId.lsb || 0;
949
+
950
+ const bankPtr = this._module._malloc(SIZEOF_ADL_BANK);
951
+ const bankResult = this._module._adl_getBank(this._player, bankIdPtr, 0, bankPtr);
952
+
953
+ let result = null;
954
+ if (bankResult === 0) {
955
+ const outIdPtr = this._module._malloc(SIZEOF_ADL_BANK_ID);
956
+ const idResult = this._module._adl_getBankId(this._player, bankPtr, outIdPtr);
957
+ if (idResult === 0) {
958
+ result = {
959
+ percussive: this._module.HEAPU8[outIdPtr],
960
+ msb: this._module.HEAPU8[outIdPtr + 1],
961
+ lsb: this._module.HEAPU8[outIdPtr + 2],
962
+ };
963
+ }
964
+ this._module._free(outIdPtr);
965
+ }
966
+ this._module._free(bankIdPtr);
967
+ this._module._free(bankPtr);
968
+ return result;
969
+ }
970
+
971
+ /**
972
+ * Remove a bank by its identifier.
973
+ *
974
+ * @param {Object} bankId - Bank identifier
975
+ * @param {boolean|number} bankId.percussive - True/1 for percussion, false/0 for melodic
976
+ * @param {number} bankId.msb - Bank MSB
977
+ * @param {number} bankId.lsb - Bank LSB
978
+ * @returns {boolean} True if successfully removed
979
+ */
980
+ removeBank(bankId) {
981
+ this._ensurePlayer();
982
+ const bankIdPtr = this._module._malloc(SIZEOF_ADL_BANK_ID);
983
+ this._module.HEAPU8[bankIdPtr] = bankId.percussive ? 1 : 0;
984
+ this._module.HEAPU8[bankIdPtr + 1] = bankId.msb || 0;
985
+ this._module.HEAPU8[bankIdPtr + 2] = bankId.lsb || 0;
986
+
987
+ const bankPtr = this._module._malloc(SIZEOF_ADL_BANK);
988
+ const bankResult = this._module._adl_getBank(this._player, bankIdPtr, 0, bankPtr);
989
+
990
+ let success = false;
991
+ if (bankResult === 0) {
992
+ success = this._module._adl_removeBank(this._player, bankPtr) === 0;
993
+ }
994
+
995
+ this._module._free(bankIdPtr);
996
+ this._module._free(bankPtr);
997
+ return success;
998
+ }
999
+
1000
+ /**
1001
+ * Load an embedded bank into a custom bank slot.
1002
+ *
1003
+ * @param {Object} bankId - Target bank identifier
1004
+ * @param {boolean|number} bankId.percussive - True/1 for percussion, false/0 for melodic
1005
+ * @param {number} bankId.msb - Bank MSB
1006
+ * @param {number} bankId.lsb - Bank LSB
1007
+ * @param {number} num - Embedded bank number to load
1008
+ * @returns {boolean} True if successful
1009
+ */
1010
+ loadEmbeddedBank(bankId, num) {
1011
+ this._ensurePlayer();
1012
+ const bankIdPtr = this._module._malloc(SIZEOF_ADL_BANK_ID);
1013
+ this._module.HEAPU8[bankIdPtr] = bankId.percussive ? 1 : 0;
1014
+ this._module.HEAPU8[bankIdPtr + 1] = bankId.msb || 0;
1015
+ this._module.HEAPU8[bankIdPtr + 2] = bankId.lsb || 0;
1016
+
1017
+ const bankPtr = this._module._malloc(SIZEOF_ADL_BANK);
1018
+
1019
+ // Check if bank already exists (flag=0) before creating
1020
+ const existed = this._module._adl_getBank(this._player, bankIdPtr, 0, bankPtr) === 0;
1021
+ // Get or create the bank slot (flag=1)
1022
+ const bankResult = existed ? 0 : this._module._adl_getBank(this._player, bankIdPtr, 1, bankPtr);
1023
+
1024
+ let success = false;
1025
+ if (bankResult === 0) {
1026
+ success = this._module._adl_loadEmbeddedBank(this._player, bankPtr, num) === 0;
1027
+ // Clean up: if we created a new slot but load failed, remove it
1028
+ if (!success && !existed) {
1029
+ this._module._adl_removeBank(this._player, bankPtr);
1030
+ }
1031
+ }
1032
+
1033
+ this._module._free(bankIdPtr);
1034
+ this._module._free(bankPtr);
1035
+ return success;
1036
+ }
1037
+
727
1038
  // =========================================================================
728
1039
  // Instrument Access
729
1040
  // =========================================================================
@@ -829,6 +1140,52 @@ export class AdlMidiCore {
829
1140
  return success;
830
1141
  }
831
1142
 
1143
+ // =========================================================================
1144
+ // Real-time SysEx
1145
+ // =========================================================================
1146
+
1147
+ /**
1148
+ * Send a System Exclusive (SysEx) message.
1149
+ *
1150
+ * @param {Uint8Array|ArrayBuffer} data - SysEx message data
1151
+ * @returns {boolean} True if successful
1152
+ */
1153
+ systemExclusive(data) {
1154
+ this._ensurePlayer();
1155
+ const bytes = data instanceof ArrayBuffer ? new Uint8Array(data) : data;
1156
+ const ptr = this._module._malloc(bytes.length);
1157
+ this._module.HEAPU8.set(bytes, ptr);
1158
+ const result = this._module._adl_rt_systemExclusive(this._player, ptr, bytes.length);
1159
+ this._module._free(ptr);
1160
+ // adl_rt_systemExclusive returns 1 when processed, 0 when rejected
1161
+ return result !== 0;
1162
+ }
1163
+
1164
+ // =========================================================================
1165
+ // Debug / Diagnostics
1166
+ // =========================================================================
1167
+
1168
+ /**
1169
+ * Describe the current state of all channels (debug utility).
1170
+ *
1171
+ * @returns {{text: string, attr: Uint8Array}} Channel state text and raw per-channel attribute bytes
1172
+ */
1173
+ describeChannels() {
1174
+ this._ensurePlayer();
1175
+ // Size buffers based on actual chip count (~23 channels per OPL3 chip)
1176
+ const numChips = this._module._adl_getNumChipsObtained(this._player);
1177
+ const size = Math.max(256, (numChips + 1) * 23);
1178
+ const textPtr = this._module._malloc(size);
1179
+ const attrPtr = this._module._malloc(size);
1180
+ this._module._adl_describeChannels(this._player, textPtr, attrPtr, size);
1181
+ const text = this._module.UTF8ToString(textPtr);
1182
+ // attr contains raw per-channel bytes; one byte per channel char in text
1183
+ const attr = this._module.HEAPU8.slice(attrPtr, attrPtr + text.length);
1184
+ this._module._free(textPtr);
1185
+ this._module._free(attrPtr);
1186
+ return { text, attr };
1187
+ }
1188
+
832
1189
  // =========================================================================
833
1190
  // Direct Module Access
834
1191
  // =========================================================================