libadlmidi-js 1.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 (86) hide show
  1. package/LICENSE +165 -0
  2. package/README.md +126 -0
  3. package/dist/core.d.ts +276 -0
  4. package/dist/fm_banks/ail/MonopolyDeluxe.wopl +0 -0
  5. package/dist/fm_banks/ail/master_of_magic.wopl +0 -0
  6. package/dist/fm_banks/manifest.json +60 -0
  7. package/dist/fm_banks/wopl/Apogee-IMF-90.wopl +0 -0
  8. package/dist/fm_banks/wopl/DMXOPL3-by-sneakernets-GS.wopl +0 -0
  9. package/dist/fm_banks/wopl/GM-By-J.A.Nguyen-and-Wohlstand.wopl +0 -0
  10. package/dist/fm_banks/wopl/Wohlstand's-modded-FatMan.wopl +0 -0
  11. package/dist/fm_banks/wopl/fatman-2op.wopl +0 -0
  12. package/dist/fm_banks/wopl/fatman-4op.wopl +0 -0
  13. package/dist/fm_banks/wopl/msadlib.wopl +0 -0
  14. package/dist/libadlmidi.d.ts +453 -0
  15. package/dist/libadlmidi.dosbox.browser.js +2 -0
  16. package/dist/libadlmidi.dosbox.browser.wasm +0 -0
  17. package/dist/libadlmidi.dosbox.core.js +2 -0
  18. package/dist/libadlmidi.dosbox.core.wasm +0 -0
  19. package/dist/libadlmidi.dosbox.js +0 -0
  20. package/dist/libadlmidi.dosbox.processor.js +3226 -0
  21. package/dist/libadlmidi.dosbox.slim.browser.js +2 -0
  22. package/dist/libadlmidi.dosbox.slim.browser.wasm +0 -0
  23. package/dist/libadlmidi.dosbox.slim.core.js +2 -0
  24. package/dist/libadlmidi.dosbox.slim.core.wasm +0 -0
  25. package/dist/libadlmidi.dosbox.slim.js +0 -0
  26. package/dist/libadlmidi.dosbox.slim.processor.js +3226 -0
  27. package/dist/libadlmidi.full.browser.js +2 -0
  28. package/dist/libadlmidi.full.browser.wasm +0 -0
  29. package/dist/libadlmidi.full.core.js +2 -0
  30. package/dist/libadlmidi.full.core.wasm +0 -0
  31. package/dist/libadlmidi.full.js +0 -0
  32. package/dist/libadlmidi.full.processor.js +3226 -0
  33. package/dist/libadlmidi.full.slim.browser.js +2 -0
  34. package/dist/libadlmidi.full.slim.browser.wasm +0 -0
  35. package/dist/libadlmidi.full.slim.core.js +2 -0
  36. package/dist/libadlmidi.full.slim.core.wasm +0 -0
  37. package/dist/libadlmidi.full.slim.js +0 -0
  38. package/dist/libadlmidi.full.slim.processor.js +3226 -0
  39. package/dist/libadlmidi.js +445 -0
  40. package/dist/libadlmidi.js.map +7 -0
  41. package/dist/libadlmidi.light.browser.js +2 -0
  42. package/dist/libadlmidi.light.browser.wasm +0 -0
  43. package/dist/libadlmidi.light.core.js +2 -0
  44. package/dist/libadlmidi.light.core.wasm +0 -0
  45. package/dist/libadlmidi.light.js +0 -0
  46. package/dist/libadlmidi.light.processor.js +3226 -0
  47. package/dist/libadlmidi.light.slim.browser.js +2 -0
  48. package/dist/libadlmidi.light.slim.browser.wasm +0 -0
  49. package/dist/libadlmidi.light.slim.core.js +2 -0
  50. package/dist/libadlmidi.light.slim.core.wasm +0 -0
  51. package/dist/libadlmidi.light.slim.js +0 -0
  52. package/dist/libadlmidi.light.slim.processor.js +3226 -0
  53. package/dist/libadlmidi.nuked.browser.js +2 -0
  54. package/dist/libadlmidi.nuked.browser.wasm +0 -0
  55. package/dist/libadlmidi.nuked.core.js +2 -0
  56. package/dist/libadlmidi.nuked.core.wasm +0 -0
  57. package/dist/libadlmidi.nuked.js +0 -0
  58. package/dist/libadlmidi.nuked.processor.js +3226 -0
  59. package/dist/libadlmidi.nuked.slim.browser.js +2 -0
  60. package/dist/libadlmidi.nuked.slim.browser.wasm +0 -0
  61. package/dist/libadlmidi.nuked.slim.core.js +2 -0
  62. package/dist/libadlmidi.nuked.slim.core.wasm +0 -0
  63. package/dist/libadlmidi.nuked.slim.js +0 -0
  64. package/dist/libadlmidi.nuked.slim.processor.js +3226 -0
  65. package/dist/profiles/dosbox.d.ts +49 -0
  66. package/dist/profiles/dosbox.slim.d.ts +49 -0
  67. package/dist/profiles/full.d.ts +49 -0
  68. package/dist/profiles/full.slim.d.ts +49 -0
  69. package/dist/profiles/light.d.ts +49 -0
  70. package/dist/profiles/light.slim.d.ts +49 -0
  71. package/dist/profiles/nuked.d.ts +49 -0
  72. package/dist/profiles/nuked.slim.d.ts +49 -0
  73. package/dist/utils/struct.d.ts +209 -0
  74. package/package.json +103 -0
  75. package/src/core.js +591 -0
  76. package/src/libadlmidi.js +524 -0
  77. package/src/processor.js +517 -0
  78. package/src/profiles/dosbox.js +82 -0
  79. package/src/profiles/dosbox.slim.js +82 -0
  80. package/src/profiles/full.js +82 -0
  81. package/src/profiles/full.slim.js +82 -0
  82. package/src/profiles/light.js +82 -0
  83. package/src/profiles/light.slim.js +82 -0
  84. package/src/profiles/nuked.js +82 -0
  85. package/src/profiles/nuked.slim.js +82 -0
  86. package/src/utils/struct.js +288 -0
@@ -0,0 +1,288 @@
1
+ /**
2
+ * OPL3 struct serialization utilities
3
+ * Shared between processor and tests
4
+ *
5
+ * @module utils/struct
6
+ */
7
+
8
+ // =============================================================================
9
+ // Structure Sizes (verified with offsetof() - WASM is 32-bit)
10
+ // =============================================================================
11
+
12
+ /** Size of ADL_Operator struct (5 register bytes) */
13
+ export const SIZEOF_ADL_OPERATOR = 5;
14
+
15
+ /** Size of ADL_Instrument struct */
16
+ export const SIZEOF_ADL_INSTRUMENT = 40; // Verified: ops at offset 14, delay at 34/36
17
+
18
+ /** Size of ADL_Bank struct (3 pointers × 4 bytes in 32-bit WASM) */
19
+ export const SIZEOF_ADL_BANK = 12;
20
+
21
+ /** Size of ADL_BankId struct (3 bytes + padding) */
22
+ export const SIZEOF_ADL_BANK_ID = 4;
23
+
24
+ /** Offset where operators start within ADL_Instrument */
25
+ export const OPERATOR_OFFSET = 14;
26
+
27
+ // =============================================================================
28
+ // Operator Encoding/Decoding
29
+ // =============================================================================
30
+
31
+ /**
32
+ * @typedef {Object} Operator
33
+ * @property {boolean} am - Amplitude modulation (tremolo)
34
+ * @property {boolean} vibrato - Vibrato (frequency modulation)
35
+ * @property {boolean} sustaining - Sustaining (EG type)
36
+ * @property {boolean} ksr - Key scale rate
37
+ * @property {number} freqMult - Frequency multiplier (0-15)
38
+ * @property {number} keyScaleLevel - Key scale level (0-3)
39
+ * @property {number} totalLevel - Total level / attenuation (0-63, 0 = loudest)
40
+ * @property {number} attack - Attack rate (0-15)
41
+ * @property {number} decay - Decay rate (0-15)
42
+ * @property {number} sustain - Sustain level (0-15, 0 = loudest)
43
+ * @property {number} release - Release rate (0-15)
44
+ * @property {number} waveform - Waveform select (0-7)
45
+ */
46
+
47
+ /**
48
+ * Decode an OPL3 operator from raw register bytes to named properties
49
+ * @param {Uint8Array | number[]} bytes - 5 bytes of operator register data
50
+ * @returns {Operator} Decoded operator with named properties
51
+ */
52
+ export function decodeOperator(bytes) {
53
+ const avekf = bytes[0];
54
+ const ksl_l = bytes[1];
55
+ const atdec = bytes[2];
56
+ const susrel = bytes[3];
57
+ const waveform = bytes[4];
58
+
59
+ return {
60
+ // Register 0x20: AM/Vib/EG-type/KSR/Mult
61
+ am: !!(avekf & 0x80),
62
+ vibrato: !!(avekf & 0x40),
63
+ sustaining: !!(avekf & 0x20),
64
+ ksr: !!(avekf & 0x10),
65
+ freqMult: avekf & 0x0F,
66
+
67
+ // Register 0x40: KSL/TL
68
+ keyScaleLevel: (ksl_l >> 6) & 0x03,
69
+ totalLevel: ksl_l & 0x3F,
70
+
71
+ // Register 0x60: AR/DR
72
+ attack: (atdec >> 4) & 0x0F,
73
+ decay: atdec & 0x0F,
74
+
75
+ // Register 0x80: SL/RR
76
+ sustain: (susrel >> 4) & 0x0F,
77
+ release: susrel & 0x0F,
78
+
79
+ // Register 0xE0: Waveform
80
+ waveform: waveform & 0x07
81
+ };
82
+ }
83
+
84
+ /**
85
+ * Encode named operator properties to raw register bytes
86
+ * @param {Operator} op - Operator with named properties
87
+ * @returns {Uint8Array} 5 bytes of operator register data
88
+ */
89
+ export function encodeOperator(op) {
90
+ const avekf =
91
+ (op.am ? 0x80 : 0) |
92
+ (op.vibrato ? 0x40 : 0) |
93
+ (op.sustaining ? 0x20 : 0) |
94
+ (op.ksr ? 0x10 : 0) |
95
+ (op.freqMult & 0x0F);
96
+
97
+ const ksl_l = ((op.keyScaleLevel & 0x03) << 6) | (op.totalLevel & 0x3F);
98
+ const atdec = ((op.attack & 0x0F) << 4) | (op.decay & 0x0F);
99
+ const susrel = ((op.sustain & 0x0F) << 4) | (op.release & 0x0F);
100
+ const waveform = op.waveform & 0x07;
101
+
102
+ return new Uint8Array([avekf, ksl_l, atdec, susrel, waveform]);
103
+ }
104
+
105
+ /**
106
+ * Default operator values (silent)
107
+ * @returns {Operator} A silent operator configuration
108
+ */
109
+ export function defaultOperator() {
110
+ return {
111
+ am: false,
112
+ vibrato: false,
113
+ sustaining: true,
114
+ ksr: false,
115
+ freqMult: 1,
116
+ keyScaleLevel: 0,
117
+ totalLevel: 63, // Max attenuation (silent)
118
+ attack: 15,
119
+ decay: 0,
120
+ sustain: 0,
121
+ release: 15,
122
+ waveform: 0
123
+ };
124
+ }
125
+
126
+ // =============================================================================
127
+ // Instrument Encoding/Decoding
128
+ // =============================================================================
129
+
130
+ /**
131
+ * Complete OPL3 instrument definition
132
+ * @typedef {Object} Instrument
133
+ * @property {number} [version] - Instrument version
134
+ * @property {number} [noteOffset1] - Note offset for voice 1
135
+ * @property {number} [noteOffset2] - Note offset for voice 2
136
+ * @property {number} [velocityOffset] - MIDI velocity offset
137
+ * @property {number} [secondVoiceDetune] - Detune for second voice
138
+ * @property {number} [percussionKey] - Percussion key number
139
+ * @property {boolean} [is4op] - 4-operator mode enabled
140
+ * @property {boolean} [isPseudo4op] - Pseudo 4-op (two 2-op voices)
141
+ * @property {boolean} [isBlank] - Blank/unused instrument
142
+ * @property {number} [rhythmMode] - Rhythm mode (0-7)
143
+ * @property {number} [feedback1] - Feedback for voice 1 (0-7)
144
+ * @property {number} [connection1] - Connection type for voice 1 (0-1)
145
+ * @property {number} [feedback2] - Feedback for voice 2 (0-7)
146
+ * @property {number} [connection2] - Connection type for voice 2 (0-1)
147
+ * @property {[Operator, Operator, Operator, Operator]} operators - Four operators
148
+ * @property {number} [delayOnMs] - Delay before note-on (ms)
149
+ * @property {number} [delayOffMs] - Delay before note-off (ms)
150
+ */
151
+
152
+ /**
153
+ * Decode an ADL_Instrument from raw bytes to JS object
154
+ * @param {Uint8Array} bytes - 40 bytes of instrument data
155
+ * @returns {Instrument} Decoded instrument with named properties
156
+ */
157
+ export function decodeInstrument(bytes) {
158
+ const view = new DataView(bytes.buffer, bytes.byteOffset, SIZEOF_ADL_INSTRUMENT);
159
+
160
+ // int version (4 bytes)
161
+ const version = view.getInt32(0, true);
162
+
163
+ // int16_t note_offset1, note_offset2 (2 bytes each)
164
+ const noteOffset1 = view.getInt16(4, true);
165
+ const noteOffset2 = view.getInt16(6, true);
166
+
167
+ // int8_t midi_velocity_offset, second_voice_detune (1 byte each)
168
+ const velocityOffset = view.getInt8(8);
169
+ const secondVoiceDetune = view.getInt8(9);
170
+
171
+ // uint8_t percussion_key_number, inst_flags, fb_conn1, fb_conn2
172
+ const percussionKey = bytes[10];
173
+ const instFlags = bytes[11];
174
+ const fbConn1 = bytes[12];
175
+ const fbConn2 = bytes[13];
176
+
177
+ // ADL_Operator operators[4] - 5 bytes each at offset 14
178
+ /** @type {[Operator, Operator, Operator, Operator]} */
179
+ const operators = /** @type {[Operator, Operator, Operator, Operator]} */ ([
180
+ decodeOperator(bytes.slice(OPERATOR_OFFSET, OPERATOR_OFFSET + SIZEOF_ADL_OPERATOR)),
181
+ decodeOperator(bytes.slice(OPERATOR_OFFSET + SIZEOF_ADL_OPERATOR, OPERATOR_OFFSET + 2 * SIZEOF_ADL_OPERATOR)),
182
+ decodeOperator(bytes.slice(OPERATOR_OFFSET + 2 * SIZEOF_ADL_OPERATOR, OPERATOR_OFFSET + 3 * SIZEOF_ADL_OPERATOR)),
183
+ decodeOperator(bytes.slice(OPERATOR_OFFSET + 3 * SIZEOF_ADL_OPERATOR, OPERATOR_OFFSET + 4 * SIZEOF_ADL_OPERATOR))
184
+ ]);
185
+
186
+ // uint16_t delay_on_ms, delay_off_ms at offset 34
187
+ const delayOnMs = view.getUint16(34, true);
188
+ const delayOffMs = view.getUint16(36, true);
189
+
190
+ return {
191
+ version,
192
+ noteOffset1,
193
+ noteOffset2,
194
+ velocityOffset,
195
+ secondVoiceDetune,
196
+ percussionKey,
197
+
198
+ // Decode flags
199
+ is4op: !!(instFlags & 0x01),
200
+ isPseudo4op: !!(instFlags & 0x02),
201
+ isBlank: !!(instFlags & 0x04),
202
+ rhythmMode: (instFlags >> 3) & 0x07,
203
+
204
+ // Decode feedback/connection
205
+ feedback1: (fbConn1 >> 1) & 0x07,
206
+ connection1: fbConn1 & 0x01,
207
+ feedback2: (fbConn2 >> 1) & 0x07,
208
+ connection2: fbConn2 & 0x01,
209
+
210
+ operators,
211
+ delayOnMs,
212
+ delayOffMs
213
+ };
214
+ }
215
+
216
+ /**
217
+ * Encode a JS instrument object to raw bytes
218
+ * @param {Instrument} inst - Instrument with named properties
219
+ * @returns {Uint8Array} 40 bytes of instrument data
220
+ */
221
+ export function encodeInstrument(inst) {
222
+ const bytes = new Uint8Array(SIZEOF_ADL_INSTRUMENT);
223
+ const view = new DataView(bytes.buffer);
224
+
225
+ // int version
226
+ view.setInt32(0, inst.version || 0, true);
227
+
228
+ // int16_t note_offset1, note_offset2
229
+ view.setInt16(4, inst.noteOffset1 || 0, true);
230
+ view.setInt16(6, inst.noteOffset2 || 0, true);
231
+
232
+ // int8_t midi_velocity_offset, second_voice_detune
233
+ view.setInt8(8, inst.velocityOffset || 0);
234
+ view.setInt8(9, inst.secondVoiceDetune || 0);
235
+
236
+ // uint8_t percussion_key_number
237
+ bytes[10] = inst.percussionKey || 0;
238
+
239
+ // uint8_t inst_flags
240
+ let flags = 0;
241
+ if (inst.is4op) flags |= 0x01;
242
+ if (inst.isPseudo4op) flags |= 0x02;
243
+ if (inst.isBlank) flags |= 0x04;
244
+ flags |= ((inst.rhythmMode || 0) & 0x07) << 3;
245
+ bytes[11] = flags;
246
+
247
+ // uint8_t fb_conn1, fb_conn2
248
+ bytes[12] = (((inst.feedback1 || 0) & 0x07) << 1) | ((inst.connection1 || 0) & 0x01);
249
+ bytes[13] = (((inst.feedback2 || 0) & 0x07) << 1) | ((inst.connection2 || 0) & 0x01);
250
+
251
+ // ADL_Operator operators[4]
252
+ for (let i = 0; i < 4; i++) {
253
+ const opBytes = encodeOperator(inst.operators?.[i] || defaultOperator());
254
+ bytes.set(opBytes, OPERATOR_OFFSET + i * SIZEOF_ADL_OPERATOR);
255
+ }
256
+
257
+ // uint16_t delay_on_ms, delay_off_ms
258
+ view.setUint16(34, inst.delayOnMs || 0, true);
259
+ view.setUint16(36, inst.delayOffMs || 0, true);
260
+
261
+ return bytes;
262
+ }
263
+
264
+ /**
265
+ * Default instrument values (blank/silent)
266
+ * @returns {Instrument} A blank instrument configuration
267
+ */
268
+ export function defaultInstrument() {
269
+ return {
270
+ version: 0,
271
+ noteOffset1: 0,
272
+ noteOffset2: 0,
273
+ velocityOffset: 0,
274
+ secondVoiceDetune: 0,
275
+ percussionKey: 0,
276
+ is4op: false,
277
+ isPseudo4op: false,
278
+ isBlank: true,
279
+ rhythmMode: 0,
280
+ feedback1: 0,
281
+ connection1: 0,
282
+ feedback2: 0,
283
+ connection2: 0,
284
+ operators: [defaultOperator(), defaultOperator(), defaultOperator(), defaultOperator()],
285
+ delayOnMs: 0,
286
+ delayOffMs: 0
287
+ };
288
+ }