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
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
  *
@@ -242,6 +245,16 @@ export class AdlMidiCore {
242
245
  return this._module._adl_getNumFourOpsChn(this._player);
243
246
  }
244
247
 
248
+ /**
249
+ * Get the number of 4-operator channels obtained.
250
+ *
251
+ * @returns {number} Count of channels obtained
252
+ */
253
+ getNumFourOpChannelsObtained() {
254
+ this._ensurePlayer();
255
+ return this._module._adl_getNumFourOpsChnObtained(this._player);
256
+ }
257
+
245
258
  /**
246
259
  * Enable/disable scaling of modulators by volume.
247
260
  *
@@ -307,7 +320,7 @@ export class AdlMidiCore {
307
320
  *
308
321
  * @param {boolean} enabled
309
322
  */
310
- setSoftPan(enabled) {
323
+ setSoftPanEnabled(enabled) {
311
324
  this._ensurePlayer();
312
325
  this._module._adl_setSoftPanEnabled(this._player, enabled ? 1 : 0);
313
326
  }
@@ -322,6 +335,16 @@ export class AdlMidiCore {
322
335
  this._module._adl_setHVibrato(this._player, enabled ? 1 : 0);
323
336
  }
324
337
 
338
+ /**
339
+ * Get deep vibrato state.
340
+ *
341
+ * @returns {boolean}
342
+ */
343
+ getDeepVibrato() {
344
+ this._ensurePlayer();
345
+ return this._module._adl_getHVibrato(this._player) !== 0;
346
+ }
347
+
325
348
  /**
326
349
  * Enable/disable deep tremolo.
327
350
  *
@@ -332,6 +355,16 @@ export class AdlMidiCore {
332
355
  this._module._adl_setHTremolo(this._player, enabled ? 1 : 0);
333
356
  }
334
357
 
358
+ /**
359
+ * Get deep tremolo state.
360
+ *
361
+ * @returns {boolean}
362
+ */
363
+ getDeepTremolo() {
364
+ this._ensurePlayer();
365
+ return this._module._adl_getHTremolo(this._player) !== 0;
366
+ }
367
+
335
368
  /**
336
369
  * Switch OPL3 emulator (if multiple are compiled in).
337
370
  *
@@ -354,6 +387,27 @@ export class AdlMidiCore {
354
387
  return this._module.UTF8ToString(ptr);
355
388
  }
356
389
 
390
+ /**
391
+ * Get the last global error string (static, no player needed).
392
+ *
393
+ * @returns {string} Error string or empty string
394
+ */
395
+ getErrorString() {
396
+ const ptr = this._module._adl_errorString();
397
+ return ptr ? this._module.UTF8ToString(ptr) : '';
398
+ }
399
+
400
+ /**
401
+ * Get the last error info for this player instance.
402
+ *
403
+ * @returns {string} Error info string or empty string
404
+ */
405
+ getErrorInfo() {
406
+ this._ensurePlayer();
407
+ const ptr = this._module._adl_errorInfo(this._player);
408
+ return ptr ? this._module.UTF8ToString(ptr) : '';
409
+ }
410
+
357
411
  /**
358
412
  * Get the version string of the linked libADLMIDI library.
359
413
  *
@@ -403,7 +457,7 @@ export class AdlMidiCore {
403
457
  *
404
458
  * @returns {number}
405
459
  */
406
- getVolumeModel() {
460
+ getVolumeRangeModel() {
407
461
  this._ensurePlayer();
408
462
  return this._module._adl_getVolumeRangeModel(this._player);
409
463
  }
@@ -413,7 +467,7 @@ export class AdlMidiCore {
413
467
  *
414
468
  * @param {number} model - Volume model type
415
469
  */
416
- setVolumeModel(model) {
470
+ setVolumeRangeModel(model) {
417
471
  this._ensurePlayer();
418
472
  this._module._adl_setVolumeRangeModel(this._player, model);
419
473
  }
@@ -625,6 +679,38 @@ export class AdlMidiCore {
625
679
  return ptr ? this._module.UTF8ToString(ptr) : '';
626
680
  }
627
681
 
682
+ /**
683
+ * Get the number of track titles in the loaded MIDI file.
684
+ *
685
+ * @returns {number} Number of track titles
686
+ */
687
+ getTrackTitleCount() {
688
+ this._ensurePlayer();
689
+ return this._module._adl_metaTrackTitleCount(this._player);
690
+ }
691
+
692
+ /**
693
+ * Get a track title by index.
694
+ *
695
+ * @param {number} index - Track title index
696
+ * @returns {string} Track title or empty string
697
+ */
698
+ getTrackTitle(index) {
699
+ this._ensurePlayer();
700
+ const ptr = this._module._adl_metaTrackTitle(this._player, index);
701
+ return ptr ? this._module.UTF8ToString(ptr) : '';
702
+ }
703
+
704
+ /**
705
+ * Get the number of MIDI markers in the loaded file.
706
+ *
707
+ * @returns {number} Number of markers
708
+ */
709
+ getMarkerCount() {
710
+ this._ensurePlayer();
711
+ return this._module._adl_metaMarkerCount(this._player);
712
+ }
713
+
628
714
  /**
629
715
  * Play MIDI file and generate audio.
630
716
  *
@@ -709,11 +795,107 @@ export class AdlMidiCore {
709
795
  *
710
796
  * @param {boolean} enabled
711
797
  */
712
- setLooping(enabled) {
798
+ setLoopEnabled(enabled) {
713
799
  this._ensurePlayer();
714
800
  this._module._adl_setLoopEnabled(this._player, enabled ? 1 : 0);
715
801
  }
716
802
 
803
+ /**
804
+ * Set the number of loop repetitions.
805
+ *
806
+ * @param {number} count - Loop count (-1 = infinite, 0 = no loops, 1+ = number of loops)
807
+ */
808
+ setLoopCount(count) {
809
+ this._ensurePlayer();
810
+ this._module._adl_setLoopCount(this._player, count);
811
+ }
812
+
813
+ /**
814
+ * Enable/disable loop hooks only mode.
815
+ *
816
+ * @param {boolean} enabled
817
+ */
818
+ setLoopHooksOnly(enabled) {
819
+ this._ensurePlayer();
820
+ this._module._adl_setLoopHooksOnly(this._player, enabled ? 1 : 0);
821
+ }
822
+
823
+ /**
824
+ * Get the loop start time in seconds.
825
+ *
826
+ * @returns {number} Loop start time in seconds
827
+ */
828
+ getLoopStartTime() {
829
+ this._ensurePlayer();
830
+ return this._module._adl_loopStartTime(this._player);
831
+ }
832
+
833
+ /**
834
+ * Get the loop end time in seconds.
835
+ *
836
+ * @returns {number} Loop end time in seconds
837
+ */
838
+ getLoopEndTime() {
839
+ this._ensurePlayer();
840
+ return this._module._adl_loopEndTime(this._player);
841
+ }
842
+
843
+ /**
844
+ * Select a song number for multi-song MIDI files.
845
+ *
846
+ * @param {number} num - Song number (0-based)
847
+ */
848
+ selectSongNum(num) {
849
+ this._ensurePlayer();
850
+ this._module._adl_selectSongNum(this._player, num);
851
+ }
852
+
853
+ /**
854
+ * Get the number of songs in the loaded MIDI file.
855
+ *
856
+ * @returns {number} Number of songs
857
+ */
858
+ getSongsCount() {
859
+ this._ensurePlayer();
860
+ return this._module._adl_getSongsCount(this._player);
861
+ }
862
+
863
+ /**
864
+ * Get the number of tracks in the loaded MIDI file.
865
+ *
866
+ * @returns {number} Number of tracks
867
+ */
868
+ getTrackCount() {
869
+ this._ensurePlayer();
870
+ return this._module._adl_trackCount(this._player);
871
+ }
872
+
873
+ /**
874
+ * Set track options (enable, mute, or solo).
875
+ * Use the TrackOption enum: TrackOption.ON (1), TrackOption.OFF (2), TrackOption.SOLO (3).
876
+ * Note: Passing 0 is a silent no-op that returns true without changing state.
877
+ *
878
+ * @param {number} track - Track index
879
+ * @param {number} options - Track option from TrackOption enum
880
+ * @returns {boolean} True if successful
881
+ */
882
+ setTrackOptions(track, options) {
883
+ this._ensurePlayer();
884
+ return this._module._adl_setTrackOptions(this._player, track, options) === 0;
885
+ }
886
+
887
+ /**
888
+ * Enable or disable a MIDI channel.
889
+ *
890
+ * @param {number} channel - MIDI channel (0-15)
891
+ * @param {boolean} enabled - Whether to enable the channel
892
+ * @returns {boolean} True if successful
893
+ */
894
+ setChannelEnabled(channel, enabled) {
895
+ this._ensurePlayer();
896
+ return this._module._adl_setChannelEnabled(this._player, channel, enabled ? 1 : 0) === 0;
897
+ }
898
+
717
899
  /**
718
900
  * Set playback tempo multiplier.
719
901
  *
@@ -724,6 +906,126 @@ export class AdlMidiCore {
724
906
  this._module._adl_setTempo(this._player, tempo);
725
907
  }
726
908
 
909
+ // =========================================================================
910
+ // Bank Management
911
+ // =========================================================================
912
+
913
+ /**
914
+ * Reserve a number of banks.
915
+ *
916
+ * @param {number} count - Number of banks to reserve
917
+ * @returns {boolean} True if successful
918
+ */
919
+ reserveBanks(count) {
920
+ this._ensurePlayer();
921
+ // adl_reserveBanks returns the resulting capacity (>= 0), not 0 on success
922
+ return this._module._adl_reserveBanks(this._player, count) >= 0;
923
+ }
924
+
925
+ /**
926
+ * Get the bank ID for a given bank identifier.
927
+ *
928
+ * @param {Object} bankId - Bank identifier
929
+ * @param {boolean|number} bankId.percussive - True/1 for percussion, false/0 for melodic
930
+ * @param {number} bankId.msb - Bank MSB
931
+ * @param {number} bankId.lsb - Bank LSB
932
+ * @returns {{percussive: number, msb: number, lsb: number}|null} Bank ID or null if not found
933
+ */
934
+ getBankId(bankId) {
935
+ this._ensurePlayer();
936
+ const bankIdPtr = this._module._malloc(SIZEOF_ADL_BANK_ID);
937
+ this._module.HEAPU8[bankIdPtr] = bankId.percussive ? 1 : 0;
938
+ this._module.HEAPU8[bankIdPtr + 1] = bankId.msb || 0;
939
+ this._module.HEAPU8[bankIdPtr + 2] = bankId.lsb || 0;
940
+
941
+ const bankPtr = this._module._malloc(SIZEOF_ADL_BANK);
942
+ const bankResult = this._module._adl_getBank(this._player, bankIdPtr, 0, bankPtr);
943
+
944
+ let result = null;
945
+ if (bankResult === 0) {
946
+ const outIdPtr = this._module._malloc(SIZEOF_ADL_BANK_ID);
947
+ const idResult = this._module._adl_getBankId(this._player, bankPtr, outIdPtr);
948
+ if (idResult === 0) {
949
+ result = {
950
+ percussive: this._module.HEAPU8[outIdPtr],
951
+ msb: this._module.HEAPU8[outIdPtr + 1],
952
+ lsb: this._module.HEAPU8[outIdPtr + 2],
953
+ };
954
+ }
955
+ this._module._free(outIdPtr);
956
+ }
957
+ this._module._free(bankIdPtr);
958
+ this._module._free(bankPtr);
959
+ return result;
960
+ }
961
+
962
+ /**
963
+ * Remove a bank by its identifier.
964
+ *
965
+ * @param {Object} bankId - Bank identifier
966
+ * @param {boolean|number} bankId.percussive - True/1 for percussion, false/0 for melodic
967
+ * @param {number} bankId.msb - Bank MSB
968
+ * @param {number} bankId.lsb - Bank LSB
969
+ * @returns {boolean} True if successfully removed
970
+ */
971
+ removeBank(bankId) {
972
+ this._ensurePlayer();
973
+ const bankIdPtr = this._module._malloc(SIZEOF_ADL_BANK_ID);
974
+ this._module.HEAPU8[bankIdPtr] = bankId.percussive ? 1 : 0;
975
+ this._module.HEAPU8[bankIdPtr + 1] = bankId.msb || 0;
976
+ this._module.HEAPU8[bankIdPtr + 2] = bankId.lsb || 0;
977
+
978
+ const bankPtr = this._module._malloc(SIZEOF_ADL_BANK);
979
+ const bankResult = this._module._adl_getBank(this._player, bankIdPtr, 0, bankPtr);
980
+
981
+ let success = false;
982
+ if (bankResult === 0) {
983
+ success = this._module._adl_removeBank(this._player, bankPtr) === 0;
984
+ }
985
+
986
+ this._module._free(bankIdPtr);
987
+ this._module._free(bankPtr);
988
+ return success;
989
+ }
990
+
991
+ /**
992
+ * Load an embedded bank into a custom bank slot.
993
+ *
994
+ * @param {Object} bankId - Target bank identifier
995
+ * @param {boolean|number} bankId.percussive - True/1 for percussion, false/0 for melodic
996
+ * @param {number} bankId.msb - Bank MSB
997
+ * @param {number} bankId.lsb - Bank LSB
998
+ * @param {number} num - Embedded bank number to load
999
+ * @returns {boolean} True if successful
1000
+ */
1001
+ loadEmbeddedBank(bankId, num) {
1002
+ this._ensurePlayer();
1003
+ const bankIdPtr = this._module._malloc(SIZEOF_ADL_BANK_ID);
1004
+ this._module.HEAPU8[bankIdPtr] = bankId.percussive ? 1 : 0;
1005
+ this._module.HEAPU8[bankIdPtr + 1] = bankId.msb || 0;
1006
+ this._module.HEAPU8[bankIdPtr + 2] = bankId.lsb || 0;
1007
+
1008
+ const bankPtr = this._module._malloc(SIZEOF_ADL_BANK);
1009
+
1010
+ // Check if bank already exists (flag=0) before creating
1011
+ const existed = this._module._adl_getBank(this._player, bankIdPtr, 0, bankPtr) === 0;
1012
+ // Get or create the bank slot (flag=1)
1013
+ const bankResult = existed ? 0 : this._module._adl_getBank(this._player, bankIdPtr, 1, bankPtr);
1014
+
1015
+ let success = false;
1016
+ if (bankResult === 0) {
1017
+ success = this._module._adl_loadEmbeddedBank(this._player, bankPtr, num) === 0;
1018
+ // Clean up: if we created a new slot but load failed, remove it
1019
+ if (!success && !existed) {
1020
+ this._module._adl_removeBank(this._player, bankPtr);
1021
+ }
1022
+ }
1023
+
1024
+ this._module._free(bankIdPtr);
1025
+ this._module._free(bankPtr);
1026
+ return success;
1027
+ }
1028
+
727
1029
  // =========================================================================
728
1030
  // Instrument Access
729
1031
  // =========================================================================
@@ -829,6 +1131,52 @@ export class AdlMidiCore {
829
1131
  return success;
830
1132
  }
831
1133
 
1134
+ // =========================================================================
1135
+ // Real-time SysEx
1136
+ // =========================================================================
1137
+
1138
+ /**
1139
+ * Send a System Exclusive (SysEx) message.
1140
+ *
1141
+ * @param {Uint8Array|ArrayBuffer} data - SysEx message data
1142
+ * @returns {boolean} True if successful
1143
+ */
1144
+ systemExclusive(data) {
1145
+ this._ensurePlayer();
1146
+ const bytes = data instanceof ArrayBuffer ? new Uint8Array(data) : data;
1147
+ const ptr = this._module._malloc(bytes.length);
1148
+ this._module.HEAPU8.set(bytes, ptr);
1149
+ const result = this._module._adl_rt_systemExclusive(this._player, ptr, bytes.length);
1150
+ this._module._free(ptr);
1151
+ // adl_rt_systemExclusive returns 1 when processed, 0 when rejected
1152
+ return result !== 0;
1153
+ }
1154
+
1155
+ // =========================================================================
1156
+ // Debug / Diagnostics
1157
+ // =========================================================================
1158
+
1159
+ /**
1160
+ * Describe the current state of all channels (debug utility).
1161
+ *
1162
+ * @returns {{text: string, attr: Uint8Array}} Channel state text and raw per-channel attribute bytes
1163
+ */
1164
+ describeChannels() {
1165
+ this._ensurePlayer();
1166
+ // Size buffers based on actual chip count (~23 channels per OPL3 chip)
1167
+ const numChips = this._module._adl_getNumChipsObtained(this._player);
1168
+ const size = Math.max(256, (numChips + 1) * 23);
1169
+ const textPtr = this._module._malloc(size);
1170
+ const attrPtr = this._module._malloc(size);
1171
+ this._module._adl_describeChannels(this._player, textPtr, attrPtr, size);
1172
+ const text = this._module.UTF8ToString(textPtr);
1173
+ // attr contains raw per-channel bytes; one byte per channel char in text
1174
+ const attr = this._module.HEAPU8.slice(attrPtr, attrPtr + text.length);
1175
+ this._module._free(textPtr);
1176
+ this._module._free(attrPtr);
1177
+ return { text, attr };
1178
+ }
1179
+
832
1180
  // =========================================================================
833
1181
  // Direct Module Access
834
1182
  // =========================================================================