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.
- package/README.md +8 -5
- package/dist/core.d.ts +191 -4
- package/dist/fm_banks/manifest.json +1 -1
- package/dist/libadlmidi.d.ts +146 -66
- package/dist/libadlmidi.dosbox.browser.js +1 -1
- package/dist/libadlmidi.dosbox.browser.wasm +0 -0
- package/dist/libadlmidi.dosbox.core.js +1 -1
- package/dist/libadlmidi.dosbox.core.wasm +0 -0
- package/dist/libadlmidi.dosbox.js +0 -0
- package/dist/libadlmidi.dosbox.processor.js +247 -74
- package/dist/libadlmidi.dosbox.slim.browser.js +1 -1
- package/dist/libadlmidi.dosbox.slim.browser.wasm +0 -0
- package/dist/libadlmidi.dosbox.slim.core.js +1 -1
- package/dist/libadlmidi.dosbox.slim.core.wasm +0 -0
- package/dist/libadlmidi.dosbox.slim.js +0 -0
- package/dist/libadlmidi.dosbox.slim.processor.js +247 -74
- package/dist/libadlmidi.full.browser.js +1 -1
- package/dist/libadlmidi.full.browser.wasm +0 -0
- package/dist/libadlmidi.full.core.js +1 -1
- package/dist/libadlmidi.full.core.wasm +0 -0
- package/dist/libadlmidi.full.js +0 -0
- package/dist/libadlmidi.full.processor.js +247 -74
- package/dist/libadlmidi.full.slim.browser.js +1 -1
- package/dist/libadlmidi.full.slim.browser.wasm +0 -0
- package/dist/libadlmidi.full.slim.core.js +1 -1
- package/dist/libadlmidi.full.slim.core.wasm +0 -0
- package/dist/libadlmidi.full.slim.js +0 -0
- package/dist/libadlmidi.full.slim.processor.js +247 -74
- package/dist/libadlmidi.js +473 -24
- package/dist/libadlmidi.js.map +3 -3
- package/dist/libadlmidi.light.browser.js +1 -1
- package/dist/libadlmidi.light.browser.wasm +0 -0
- package/dist/libadlmidi.light.core.js +1 -1
- package/dist/libadlmidi.light.core.wasm +0 -0
- package/dist/libadlmidi.light.js +0 -0
- package/dist/libadlmidi.light.processor.js +247 -74
- package/dist/libadlmidi.light.slim.browser.js +1 -1
- package/dist/libadlmidi.light.slim.browser.wasm +0 -0
- package/dist/libadlmidi.light.slim.core.js +1 -1
- package/dist/libadlmidi.light.slim.core.wasm +0 -0
- package/dist/libadlmidi.light.slim.js +0 -0
- package/dist/libadlmidi.light.slim.processor.js +247 -74
- package/dist/libadlmidi.nuked.browser.js +1 -1
- package/dist/libadlmidi.nuked.browser.wasm +0 -0
- package/dist/libadlmidi.nuked.core.js +1 -1
- package/dist/libadlmidi.nuked.core.wasm +0 -0
- package/dist/libadlmidi.nuked.js +0 -0
- package/dist/libadlmidi.nuked.processor.js +247 -74
- package/dist/libadlmidi.nuked.slim.browser.js +1 -1
- package/dist/libadlmidi.nuked.slim.browser.wasm +0 -0
- package/dist/libadlmidi.nuked.slim.core.js +1 -1
- package/dist/libadlmidi.nuked.slim.core.wasm +0 -0
- package/dist/libadlmidi.nuked.slim.js +0 -0
- package/dist/libadlmidi.nuked.slim.processor.js +247 -74
- package/dist/profiles/dosbox.d.ts +7 -2
- package/dist/profiles/dosbox.slim.d.ts +7 -2
- package/dist/profiles/full.d.ts +7 -2
- package/dist/profiles/full.slim.d.ts +7 -2
- package/dist/profiles/light.d.ts +7 -2
- package/dist/profiles/light.slim.d.ts +7 -2
- package/dist/profiles/nuked.d.ts +7 -2
- package/dist/profiles/nuked.slim.d.ts +7 -2
- package/dist/utils/constants.d.ts +61 -0
- package/package.json +30 -9
- package/src/core.js +361 -4
- package/src/libadlmidi.js +379 -58
- package/src/processor.js +210 -12
- package/src/profiles/dosbox.js +20 -10
- package/src/profiles/dosbox.slim.js +20 -10
- package/src/profiles/full.js +21 -11
- package/src/profiles/full.slim.js +21 -11
- package/src/profiles/light.js +21 -11
- package/src/profiles/light.slim.js +21 -11
- package/src/profiles/nuked.js +21 -11
- package/src/profiles/nuked.slim.js +21 -11
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
// =========================================================================
|