libadlmidi-js 1.1.0 → 1.2.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 (58) hide show
  1. package/README.md +36 -1
  2. package/dist/core.d.ts +148 -0
  3. package/dist/fm_banks/manifest.json +1 -1
  4. package/dist/libadlmidi.d.ts +119 -2
  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 +152 -53
  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 +152 -53
  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 +152 -53
  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 +152 -53
  29. package/dist/libadlmidi.js +258 -4
  30. package/dist/libadlmidi.js.map +2 -2
  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 +152 -53
  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 +152 -53
  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 +152 -53
  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 +152 -53
  55. package/package.json +6 -5
  56. package/src/core.js +289 -24
  57. package/src/libadlmidi.js +229 -4
  58. package/src/processor.js +101 -1
package/src/core.js CHANGED
@@ -11,6 +11,7 @@
11
11
  import {
12
12
  SIZEOF_ADL_INSTRUMENT,
13
13
  SIZEOF_ADL_BANK_ID,
14
+ SIZEOF_ADL_BANK,
14
15
  decodeInstrument,
15
16
  encodeInstrument,
16
17
  } from './utils/struct.js';
@@ -131,6 +132,14 @@ export class AdlMidiCore {
131
132
  this._module._adl_rt_resetState(this._player);
132
133
  }
133
134
 
135
+ /**
136
+ * Full reset to apply instrument/bank changes.
137
+ */
138
+ resetFull() {
139
+ this._ensurePlayer();
140
+ this._module._adl_reset(this._player);
141
+ }
142
+
134
143
  /**
135
144
  * Panic - immediately stop all notes.
136
145
  */
@@ -212,6 +221,87 @@ export class AdlMidiCore {
212
221
  return this._module._adl_setNumChips(this._player, count) === 0;
213
222
  }
214
223
 
224
+ /**
225
+ * Set the number of 4-operator channels.
226
+ *
227
+ * @param {number} count - Number of channels (-1 for auto)
228
+ * @returns {boolean} True if successful
229
+ */
230
+ setNumFourOpChannels(count) {
231
+ this._ensurePlayer();
232
+ return this._module._adl_setNumFourOpsChn(this._player, count) === 0;
233
+ }
234
+
235
+ /**
236
+ * Get the number of 4-operator channels.
237
+ *
238
+ * @returns {number} Count of channels
239
+ */
240
+ getNumFourOpChannels() {
241
+ this._ensurePlayer();
242
+ return this._module._adl_getNumFourOpsChn(this._player);
243
+ }
244
+
245
+ /**
246
+ * Enable/disable scaling of modulators by volume.
247
+ *
248
+ * @param {boolean} enabled
249
+ */
250
+ setScaleModulators(enabled) {
251
+ this._ensurePlayer();
252
+ this._module._adl_setScaleModulators(this._player, enabled ? 1 : 0);
253
+ }
254
+
255
+ /**
256
+ * Enable/disable full-range brightness (0-127).
257
+ *
258
+ * @param {boolean} enabled
259
+ */
260
+ setFullRangeBrightness(enabled) {
261
+ this._ensurePlayer();
262
+ this._module._adl_setFullRangeBrightness(this._player, enabled ? 1 : 0);
263
+ }
264
+
265
+ /**
266
+ * Enable/disable automatic arpeggio.
267
+ *
268
+ * @param {boolean} enabled
269
+ */
270
+ setAutoArpeggio(enabled) {
271
+ this._ensurePlayer();
272
+ this._module._adl_setAutoArpeggio(this._player, enabled ? 1 : 0);
273
+ }
274
+
275
+ /**
276
+ * Get automatic arpeggio state.
277
+ *
278
+ * @returns {boolean}
279
+ */
280
+ getAutoArpeggio() {
281
+ this._ensurePlayer();
282
+ return this._module._adl_getAutoArpeggio(this._player) !== 0;
283
+ }
284
+
285
+ /**
286
+ * Set channel allocation mode.
287
+ *
288
+ * @param {number} mode - Mode ID
289
+ */
290
+ setChannelAllocMode(mode) {
291
+ this._ensurePlayer();
292
+ this._module._adl_setChannelAllocMode(this._player, mode);
293
+ }
294
+
295
+ /**
296
+ * Get channel allocation mode.
297
+ *
298
+ * @returns {number} Mode ID
299
+ */
300
+ getChannelAllocMode() {
301
+ this._ensurePlayer();
302
+ return this._module._adl_getChannelAllocMode(this._player);
303
+ }
304
+
215
305
  /**
216
306
  * Enable/disable soft stereo panning.
217
307
  *
@@ -264,6 +354,81 @@ export class AdlMidiCore {
264
354
  return this._module.UTF8ToString(ptr);
265
355
  }
266
356
 
357
+ /**
358
+ * Get the version string of the linked libADLMIDI library.
359
+ *
360
+ * @returns {string} Version string (e.g., "1.5.1")
361
+ */
362
+ getLibraryVersion() {
363
+ const ptr = this._module._adl_linkedLibraryVersion();
364
+ return this._module.UTF8ToString(ptr);
365
+ }
366
+
367
+ /**
368
+ * Get the version of the linked libADLMIDI library as an object.
369
+ *
370
+ * @returns {{major: number, minor: number, patch: number}}
371
+ */
372
+ getVersion() {
373
+ const ptr = this._module._adl_linkedVersion();
374
+ return {
375
+ major: this._module.getValue(ptr, 'i16'),
376
+ minor: this._module.getValue(ptr + 2, 'i16'),
377
+ patch: this._module.getValue(ptr + 4, 'i16')
378
+ };
379
+ }
380
+
381
+ /**
382
+ * Get the number of emulated chips.
383
+ *
384
+ * @returns {number}
385
+ */
386
+ getNumChips() {
387
+ this._ensurePlayer();
388
+ return this._module._adl_getNumChips(this._player);
389
+ }
390
+
391
+ /**
392
+ * Get the number of emulated chips obtained.
393
+ *
394
+ * @returns {number}
395
+ */
396
+ getNumChipsObtained() {
397
+ this._ensurePlayer();
398
+ return this._module._adl_getNumChipsObtained(this._player);
399
+ }
400
+
401
+ /**
402
+ * Get the volume range model.
403
+ *
404
+ * @returns {number}
405
+ */
406
+ getVolumeModel() {
407
+ this._ensurePlayer();
408
+ return this._module._adl_getVolumeRangeModel(this._player);
409
+ }
410
+
411
+ /**
412
+ * Set the volume range model.
413
+ *
414
+ * @param {number} model - Volume model type
415
+ */
416
+ setVolumeModel(model) {
417
+ this._ensurePlayer();
418
+ this._module._adl_setVolumeRangeModel(this._player, model);
419
+ }
420
+
421
+ /**
422
+ * Run emulator with PCM rate to reduce CPU usage.
423
+ *
424
+ * @param {boolean} enabled
425
+ * @returns {boolean} True if successful
426
+ */
427
+ setRunAtPcmRate(enabled) {
428
+ this._ensurePlayer();
429
+ return this._module._adl_setRunAtPcmRate(this._player, enabled ? 1 : 0) === 0;
430
+ }
431
+
267
432
  // =========================================================================
268
433
  // Real-time Synthesis
269
434
  // =========================================================================
@@ -291,6 +456,29 @@ export class AdlMidiCore {
291
456
  this._module._adl_rt_noteOff(this._player, channel, note);
292
457
  }
293
458
 
459
+ /**
460
+ * Send a note aftertouch message.
461
+ *
462
+ * @param {number} channel - MIDI channel (0-15)
463
+ * @param {number} note - MIDI note (0-127)
464
+ * @param {number} pressure - Aftertouch pressure (0-127)
465
+ */
466
+ noteAfterTouch(channel, note, pressure) {
467
+ this._ensurePlayer();
468
+ this._module._adl_rt_noteAfterTouch(this._player, channel, note, pressure);
469
+ }
470
+
471
+ /**
472
+ * Send a channel aftertouch message.
473
+ *
474
+ * @param {number} channel - MIDI channel (0-15)
475
+ * @param {number} pressure - Aftertouch pressure (0-127)
476
+ */
477
+ channelAfterTouch(channel, pressure) {
478
+ this._ensurePlayer();
479
+ this._module._adl_rt_channelAfterTouch(this._player, channel, pressure);
480
+ }
481
+
294
482
  /**
295
483
  * Send a pitch bend message.
296
484
  *
@@ -325,6 +513,39 @@ export class AdlMidiCore {
325
513
  this._module._adl_rt_patchChange(this._player, channel, program);
326
514
  }
327
515
 
516
+ /**
517
+ * Send a bank change message (16-bit).
518
+ *
519
+ * @param {number} channel - MIDI channel (0-15)
520
+ * @param {number} bank - Bank number
521
+ */
522
+ bankChange(channel, bank) {
523
+ this._ensurePlayer();
524
+ this._module._adl_rt_bankChange(this._player, channel, bank);
525
+ }
526
+
527
+ /**
528
+ * Send a bank change MSB message.
529
+ *
530
+ * @param {number} channel - MIDI channel (0-15)
531
+ * @param {number} msb - Bank MSB (0-127)
532
+ */
533
+ bankChangeMSB(channel, msb) {
534
+ this._ensurePlayer();
535
+ this._module._adl_rt_bankChangeMSB(this._player, channel, msb);
536
+ }
537
+
538
+ /**
539
+ * Send a bank change LSB message.
540
+ *
541
+ * @param {number} channel - MIDI channel (0-15)
542
+ * @param {number} lsb - Bank LSB (0-127)
543
+ */
544
+ bankChangeLSB(channel, lsb) {
545
+ this._ensurePlayer();
546
+ this._module._adl_rt_bankChangeLSB(this._player, channel, lsb);
547
+ }
548
+
328
549
  /**
329
550
  * Generate audio samples (real-time synthesis).
330
551
  *
@@ -382,6 +603,28 @@ export class AdlMidiCore {
382
603
  return result === 0;
383
604
  }
384
605
 
606
+ /**
607
+ * Get the music title of the loaded MIDI file.
608
+ *
609
+ * @returns {string} Title or empty string
610
+ */
611
+ getMusicTitle() {
612
+ this._ensurePlayer();
613
+ const ptr = this._module._adl_metaMusicTitle(this._player);
614
+ return ptr ? this._module.UTF8ToString(ptr) : '';
615
+ }
616
+
617
+ /**
618
+ * Get the copyright notice of the loaded MIDI file.
619
+ *
620
+ * @returns {string} Copyright or empty string
621
+ */
622
+ getMusicCopyright() {
623
+ this._ensurePlayer();
624
+ const ptr = this._module._adl_metaMusicCopyright(this._player);
625
+ return ptr ? this._module.UTF8ToString(ptr) : '';
626
+ }
627
+
385
628
  /**
386
629
  * Play MIDI file and generate audio.
387
630
  *
@@ -504,24 +747,34 @@ export class AdlMidiCore {
504
747
  this._module.HEAPU8[bankIdPtr + 1] = bankId.msb || 0;
505
748
  this._module.HEAPU8[bankIdPtr + 2] = bankId.lsb || 0;
506
749
 
507
- // Allocate instrument struct
508
- const instPtr = this._module._malloc(SIZEOF_ADL_INSTRUMENT);
750
+ // Allocate bank struct
751
+ const bankPtr = this._module._malloc(SIZEOF_ADL_BANK);
509
752
 
510
- const result = this._module._adl_getInstrument(
511
- this._player,
512
- bankIdPtr,
513
- program,
514
- instPtr
515
- );
753
+ // Get bank (create if needed)
754
+ const bankResult = this._module._adl_getBank(this._player, bankIdPtr, 1, bankPtr);
516
755
 
517
756
  let instrument = null;
518
- if (result === 0) {
519
- const bytes = this._module.HEAPU8.slice(instPtr, instPtr + SIZEOF_ADL_INSTRUMENT);
520
- instrument = decodeInstrument(bytes);
757
+ if (bankResult === 0) {
758
+ // Allocate instrument struct
759
+ const instPtr = this._module._malloc(SIZEOF_ADL_INSTRUMENT);
760
+
761
+ const result = this._module._adl_getInstrument(
762
+ this._player,
763
+ bankPtr,
764
+ program,
765
+ instPtr
766
+ );
767
+
768
+ if (result === 0) {
769
+ const bytes = this._module.HEAPU8.slice(instPtr, instPtr + SIZEOF_ADL_INSTRUMENT);
770
+ instrument = decodeInstrument(bytes);
771
+ }
772
+
773
+ this._module._free(instPtr);
521
774
  }
522
775
 
523
776
  this._module._free(bankIdPtr);
524
- this._module._free(instPtr);
777
+ this._module._free(bankPtr);
525
778
 
526
779
  return instrument;
527
780
  }
@@ -546,22 +799,34 @@ export class AdlMidiCore {
546
799
  this._module.HEAPU8[bankIdPtr + 1] = bankId.msb || 0;
547
800
  this._module.HEAPU8[bankIdPtr + 2] = bankId.lsb || 0;
548
801
 
549
- // Encode and write instrument
550
- const bytes = encodeInstrument(instrument);
551
- const instPtr = this._module._malloc(SIZEOF_ADL_INSTRUMENT);
552
- this._module.HEAPU8.set(bytes, instPtr);
802
+ // Allocate bank struct
803
+ const bankPtr = this._module._malloc(SIZEOF_ADL_BANK);
804
+
805
+ // Get bank (create if needed)
806
+ const bankResult = this._module._adl_getBank(this._player, bankIdPtr, 1, bankPtr);
553
807
 
554
- const result = this._module._adl_setInstrument(
555
- this._player,
556
- bankIdPtr,
557
- program,
558
- instPtr
559
- );
808
+ let success = false;
809
+ if (bankResult === 0) {
810
+ // Encode and write instrument
811
+ const bytes = encodeInstrument(instrument);
812
+ const instPtr = this._module._malloc(SIZEOF_ADL_INSTRUMENT);
813
+ this._module.HEAPU8.set(bytes, instPtr);
814
+
815
+ const result = this._module._adl_setInstrument(
816
+ this._player,
817
+ bankPtr,
818
+ program,
819
+ instPtr
820
+ );
821
+
822
+ success = result === 0;
823
+ this._module._free(instPtr);
824
+ }
560
825
 
561
826
  this._module._free(bankIdPtr);
562
- this._module._free(instPtr);
827
+ this._module._free(bankPtr);
563
828
 
564
- return result === 0;
829
+ return success;
565
830
  }
566
831
 
567
832
  // =========================================================================
package/src/libadlmidi.js CHANGED
@@ -74,7 +74,7 @@
74
74
  */
75
75
 
76
76
  /**
77
- * Available OPL3 emulator cores.
77
+ * Available OPL2/OPL3 emulator cores.
78
78
  * Use with switchEmulator() to change the synthesis engine at runtime.
79
79
  * Note: Only emulators compiled into the current profile are available.
80
80
  * @readonly
@@ -103,6 +103,8 @@ export const Emulator = Object.freeze({
103
103
  NUKED_OPL2_LLE: 9,
104
104
  /** Nuked OPL3 LLE - Transistor-level emulation */
105
105
  NUKED_OPL3_LLE: 10,
106
+ /** Nuked OPL2 Lite - Lightweight OPL2 emulation for AdLib-era music */
107
+ NUKED_OPL2_LITE: 11,
106
108
  });
107
109
 
108
110
  export class AdlMidi {
@@ -156,13 +158,12 @@ export class AdlMidi {
156
158
  // For split builds, fetch the WASM binary from main thread
157
159
  // (AudioWorklet doesn't have fetch access)
158
160
  // If wasmUrl not provided, derive it from processorUrl
159
- let wasmBinary = null;
160
161
  const effectiveWasmUrl = wasmUrl || processorUrl.replace('.processor.js', '.core.wasm');
161
162
  const response = await fetch(effectiveWasmUrl);
162
163
  if (!response.ok) {
163
164
  throw new Error(`Failed to fetch WASM: ${response.status}`);
164
165
  }
165
- wasmBinary = await response.arrayBuffer();
166
+ const wasmBinary = await response.arrayBuffer();
166
167
 
167
168
  // Add the AudioWorklet module
168
169
  await this.ctx.audioWorklet.addModule(processorUrl);
@@ -289,6 +290,52 @@ export class AdlMidi {
289
290
  this.#send({ type: 'programChange', channel, program });
290
291
  }
291
292
 
293
+ /**
294
+ * Send note aftertouch
295
+ * @param {number} channel - MIDI channel (0-15)
296
+ * @param {number} note - Note number (0-127)
297
+ * @param {number} pressure - Pressure (0-127)
298
+ */
299
+ noteAfterTouch(channel, note, pressure) {
300
+ this.#send({ type: 'noteAfterTouch', channel, note, pressure });
301
+ }
302
+
303
+ /**
304
+ * Send channel aftertouch
305
+ * @param {number} channel - MIDI channel (0-15)
306
+ * @param {number} pressure - Pressure (0-127)
307
+ */
308
+ channelAfterTouch(channel, pressure) {
309
+ this.#send({ type: 'channelAfterTouch', channel, pressure });
310
+ }
311
+
312
+ /**
313
+ * Change bank (16-bit)
314
+ * @param {number} channel - MIDI channel (0-15)
315
+ * @param {number} bank - Bank number
316
+ */
317
+ bankChange(channel, bank) {
318
+ this.#send({ type: 'bankChange', channel, bank });
319
+ }
320
+
321
+ /**
322
+ * Change bank MSB
323
+ * @param {number} channel - MIDI channel (0-15)
324
+ * @param {number} msb - Bank MSB (0-127)
325
+ */
326
+ bankChangeMSB(channel, msb) {
327
+ this.#send({ type: 'bankChangeMSB', channel, msb });
328
+ }
329
+
330
+ /**
331
+ * Change bank LSB
332
+ * @param {number} channel - MIDI channel (0-15)
333
+ * @param {number} lsb - Bank LSB (0-127)
334
+ */
335
+ bankChangeLSB(channel, lsb) {
336
+ this.#send({ type: 'bankChangeLSB', channel, lsb });
337
+ }
338
+
292
339
  /**
293
340
  * Reset the real-time state (stops all notes, resets controllers)
294
341
  * @returns {void}
@@ -404,6 +451,85 @@ export class AdlMidi {
404
451
  this.#send({ type: 'setNumChips', chips });
405
452
  }
406
453
 
454
+ /**
455
+ * Set the number of 4-operator channels
456
+ * @param {number} channels - Number of channels (-1 for auto)
457
+ */
458
+ setNumFourOpChannels(channels) {
459
+ this.#send({ type: 'setNumFourOpChannels', channels });
460
+ }
461
+
462
+ /**
463
+ * Get the number of 4-operator channels
464
+ * @returns {Promise<number>}
465
+ */
466
+ async getNumFourOpChannels() {
467
+ return new Promise((resolve) => {
468
+ this.#onceMessage('numFourOpChannels', /** @param {{channels: number}} msg */(msg) => {
469
+ resolve(msg.channels);
470
+ });
471
+ this.#send({ type: 'getNumFourOpChannels' });
472
+ });
473
+ }
474
+
475
+ /**
476
+ * Enable/disable scaling of modulators by volume
477
+ * @param {boolean} enabled
478
+ */
479
+ setScaleModulators(enabled) {
480
+ this.#send({ type: 'setScaleModulators', enabled });
481
+ }
482
+
483
+ /**
484
+ * Enable/disable full-range brightness
485
+ * @param {boolean} enabled
486
+ */
487
+ setFullRangeBrightness(enabled) {
488
+ this.#send({ type: 'setFullRangeBrightness', enabled });
489
+ }
490
+
491
+ /**
492
+ * Enable/disable automatic arpeggio
493
+ * @param {boolean} enabled
494
+ */
495
+ setAutoArpeggio(enabled) {
496
+ this.#send({ type: 'setAutoArpeggio', enabled });
497
+ }
498
+
499
+ /**
500
+ * Get automatic arpeggio state
501
+ * @returns {Promise<boolean>}
502
+ */
503
+ async getAutoArpeggio() {
504
+ return new Promise((resolve) => {
505
+ this.#onceMessage('autoArpeggio', /** @param {{enabled: boolean}} msg */(msg) => {
506
+ resolve(msg.enabled);
507
+ });
508
+ this.#send({ type: 'getAutoArpeggio' });
509
+ });
510
+ }
511
+
512
+ /**
513
+ * Set channel allocation mode
514
+ * @param {number} mode - Mode ID
515
+ */
516
+ setChannelAllocMode(mode) {
517
+ this.#send({ type: 'setChannelAllocMode', mode });
518
+ }
519
+
520
+ /**
521
+ * Get channel allocation mode
522
+ * @returns {Promise<number>}
523
+ */
524
+ async getChannelAllocMode() {
525
+ return new Promise((resolve) => {
526
+ this.#onceMessage('channelAllocMode', /** @param {{mode: number}} msg */(msg) => {
527
+ resolve(msg.mode);
528
+ });
529
+ this.#send({ type: 'getChannelAllocMode' });
530
+ });
531
+ }
532
+
407
533
  /**
408
534
  * Set the volume model
409
535
  * @param {number} model - Volume model number
@@ -436,6 +562,14 @@ export class AdlMidi {
436
562
  this.#send({ type: 'setTremolo', enabled });
437
563
  }
438
564
 
565
+ /**
566
+ * Run emulator with PCM rate to reduce CPU usage
567
+ * @param {boolean} enabled
568
+ */
569
+ setRunAtPcmRate(enabled) {
570
+ this.#send({ type: 'setRunAtPcmRate', enabled });
571
+ }
572
+
439
573
  /**
440
574
  * Switch the OPL3 emulator core at runtime
441
575
  *
@@ -480,6 +614,71 @@ export class AdlMidi {
480
614
  });
481
615
  }
482
616
 
617
+ /**
618
+ * Get the version string of the linked libADLMIDI library
619
+ * @returns {Promise<string>}
620
+ */
621
+ async getLibraryVersion() {
622
+ return new Promise((resolve) => {
623
+ this.#onceMessage('libraryVersion', /** @param {{version: string}} msg */(msg) => {
624
+ resolve(msg.version);
625
+ });
626
+ this.#send({ type: 'getLibraryVersion' });
627
+ });
628
+ }
629
+
630
+ /**
631
+ * Get the version of the linked libADLMIDI library as an object
632
+ * @returns {Promise<{major: number, minor: number, patch: number}>}
633
+ */
634
+ async getVersion() {
635
+ return new Promise((resolve) => {
636
+ this.#onceMessage('version', /** @param {{version: {major: number, minor: number, patch: number}}} msg */(msg) => {
637
+ resolve(msg.version);
638
+ });
639
+ this.#send({ type: 'getVersion' });
640
+ });
641
+ }
642
+
643
+ /**
644
+ * Get the number of emulated chips
645
+ * @returns {Promise<number>}
646
+ */
647
+ async getNumChips() {
648
+ return new Promise((resolve) => {
649
+ this.#onceMessage('numChips', /** @param {{chips: number}} msg */(msg) => {
650
+ resolve(msg.chips);
651
+ });
652
+ this.#send({ type: 'getNumChips' });
653
+ });
654
+ }
655
+
656
+ /**
657
+ * Get the number of emulated chips obtained
658
+ * @returns {Promise<number>}
659
+ */
660
+ async getNumChipsObtained() {
661
+ return new Promise((resolve) => {
662
+ this.#onceMessage('numChipsObtained', /** @param {{chips: number}} msg */(msg) => {
663
+ resolve(msg.chips);
664
+ });
665
+ this.#send({ type: 'getNumChipsObtained' });
666
+ });
667
+ }
668
+
669
+ /**
670
+ * Get the volume range model
671
+ * @returns {Promise<number>}
672
+ */
673
+ async getVolumeModel() {
674
+ return new Promise((resolve) => {
675
+ this.#onceMessage('volumeModel', /** @param {{model: number}} msg */(msg) => {
676
+ resolve(msg.model);
677
+ });
678
+ this.#send({ type: 'getVolumeModel' });
679
+ });
680
+ }
681
+
483
682
  /**
484
683
  * Get list of embedded banks available in this build
485
684
  * Note: Slim builds have no embedded banks and will return an empty array
@@ -518,7 +717,7 @@ export class AdlMidi {
518
717
  if (msg.success) {
519
718
  resolve({ duration: msg.duration });
520
719
  } else {
521
- reject(new Error(msg.error || 'Failed to load MIDI data'));
720
+ reject(new Error(msg.error || 'Failed to parse MIDI data'));
522
721
  }
523
722
  });
524
723
 
@@ -526,6 +725,32 @@ export class AdlMidi {
526
725
  });
527
726
  }
528
727
 
728
+ /**
729
+ * Get the music title of the loaded MIDI file
730
+ * @returns {Promise<string>}
731
+ */
732
+ async getMusicTitle() {
733
+ return new Promise((resolve) => {
734
+ this.#onceMessage('musicTitle', /** @param {{title: string}} msg */(msg) => {
735
+ resolve(msg.title);
736
+ });
737
+ this.#send({ type: 'getMusicTitle' });
738
+ });
739
+ }
740
+
741
+ /**
742
+ * Get the copyright notice of the loaded MIDI file
743
+ * @returns {Promise<string>}
744
+ */
745
+ async getMusicCopyright() {
746
+ return new Promise((resolve) => {
747
+ this.#onceMessage('musicCopyright', /** @param {{copyright: string}} msg */(msg) => {
748
+ resolve(msg.copyright);
749
+ });
750
+ this.#send({ type: 'getMusicCopyright' });
751
+ });
752
+ }
753
+
529
754
  /**
530
755
  * Start or resume MIDI file playback
531
756
  * @returns {void}