libadlmidi-js 2.0.0 → 2.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.
- package/README.md +8 -5
- package/dist/core.d.ts +39 -0
- package/dist/fm_banks/manifest.json +1 -1
- package/dist/libadlmidi.d.ts +34 -1
- 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 +29 -7
- 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 +29 -7
- 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 +29 -7
- 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 +29 -7
- package/dist/libadlmidi.js +74 -3
- package/dist/libadlmidi.js.map +2 -2
- 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 +29 -7
- 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 +29 -7
- 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 +29 -7
- 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 +29 -7
- 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 +3 -1
- package/dist/utils/opl3.d.ts +157 -0
- package/package.json +36 -11
- package/src/core.js +63 -0
- package/src/libadlmidi.js +63 -2
- package/src/processor.js +24 -0
- package/src/profiles/dosbox.js +34 -6
- package/src/profiles/dosbox.slim.js +34 -6
- package/src/profiles/full.js +35 -7
- package/src/profiles/full.slim.js +35 -7
- package/src/profiles/light.js +35 -7
- package/src/profiles/light.slim.js +35 -7
- package/src/profiles/nuked.js +35 -7
- package/src/profiles/nuked.slim.js +35 -7
- package/src/utils/constants.js +3 -1
- package/src/utils/opl3.js +250 -0
|
@@ -16,9 +16,10 @@ export class AdlMidi extends BaseAdlMidi {
|
|
|
16
16
|
*
|
|
17
17
|
* @param {string} [processorUrl] - Override processor URL (optional)
|
|
18
18
|
* @param {string} [wasmUrl] - Override WASM URL (optional)
|
|
19
|
+
* @param {object} [defaultSettings] - Override profile's default synth settings
|
|
19
20
|
* @returns {Promise<void>}
|
|
20
21
|
*/
|
|
21
|
-
init(processorUrl?: string, wasmUrl?: string): Promise<void>;
|
|
22
|
+
init(processorUrl?: string, wasmUrl?: string, defaultSettings?: object): Promise<void>;
|
|
22
23
|
}
|
|
23
24
|
/**
|
|
24
25
|
* Pre-configured AdlMidiCore for light slim profile.
|
|
@@ -37,14 +38,18 @@ export class AdlMidiCore {
|
|
|
37
38
|
/**
|
|
38
39
|
* Create a new AdlMidiCore instance with this profile's WASM.
|
|
39
40
|
*
|
|
40
|
-
* @param {{corePath?: string}} [options]
|
|
41
|
+
* @param {{corePath?: string, defaultEmulator?: number, wasmBinary?: ArrayBuffer}} [options]
|
|
42
|
+
* Override profile defaults. corePath and defaultEmulator are pre-configured.
|
|
41
43
|
* @returns {Promise<BaseAdlMidiCore>}
|
|
42
44
|
*/
|
|
43
45
|
static create(options?: {
|
|
44
46
|
corePath?: string;
|
|
47
|
+
defaultEmulator?: number;
|
|
48
|
+
wasmBinary?: ArrayBuffer;
|
|
45
49
|
}): Promise<BaseAdlMidiCore>;
|
|
46
50
|
}
|
|
47
51
|
import { AdlMidi as BaseAdlMidi } from '../libadlmidi.js';
|
|
48
52
|
import { AdlMidiCore as BaseAdlMidiCore } from '../core.js';
|
|
49
53
|
export { encodeInstrument, decodeInstrument, defaultInstrument, encodeOperator, decodeOperator, defaultOperator } from "../utils/struct.js";
|
|
50
54
|
export { Emulator, TrackOption } from "../utils/constants.js";
|
|
55
|
+
export { CHANNELS_PER_CHIP, CHANNELS_STANDARD, CHANNELS_RHYTHM, OPL3_SAMPLE_RATE, CHANNEL_OPERATORS, channelBank, fnumLoReg, keyOnBlockReg, feedbackConnReg, operatorReg, noteToFnumBlock, encodeFnumBlock, keyOn, keyOff, encodeOperatorRegisters, encodeChannelVoice, channelMask } from "../utils/opl3.js";
|
package/dist/profiles/nuked.d.ts
CHANGED
|
@@ -16,9 +16,10 @@ export class AdlMidi extends BaseAdlMidi {
|
|
|
16
16
|
*
|
|
17
17
|
* @param {string} [processorUrl] - Override processor URL (optional)
|
|
18
18
|
* @param {string} [wasmUrl] - Override WASM URL (optional)
|
|
19
|
+
* @param {object} [defaultSettings] - Override profile's default synth settings
|
|
19
20
|
* @returns {Promise<void>}
|
|
20
21
|
*/
|
|
21
|
-
init(processorUrl?: string, wasmUrl?: string): Promise<void>;
|
|
22
|
+
init(processorUrl?: string, wasmUrl?: string, defaultSettings?: object): Promise<void>;
|
|
22
23
|
}
|
|
23
24
|
/**
|
|
24
25
|
* Pre-configured AdlMidiCore for nuked profile.
|
|
@@ -37,14 +38,18 @@ export class AdlMidiCore {
|
|
|
37
38
|
/**
|
|
38
39
|
* Create a new AdlMidiCore instance with this profile's WASM.
|
|
39
40
|
*
|
|
40
|
-
* @param {{corePath?: string}} [options]
|
|
41
|
+
* @param {{corePath?: string, defaultEmulator?: number, wasmBinary?: ArrayBuffer}} [options]
|
|
42
|
+
* Override profile defaults. corePath and defaultEmulator are pre-configured.
|
|
41
43
|
* @returns {Promise<BaseAdlMidiCore>}
|
|
42
44
|
*/
|
|
43
45
|
static create(options?: {
|
|
44
46
|
corePath?: string;
|
|
47
|
+
defaultEmulator?: number;
|
|
48
|
+
wasmBinary?: ArrayBuffer;
|
|
45
49
|
}): Promise<BaseAdlMidiCore>;
|
|
46
50
|
}
|
|
47
51
|
import { AdlMidi as BaseAdlMidi } from '../libadlmidi.js';
|
|
48
52
|
import { AdlMidiCore as BaseAdlMidiCore } from '../core.js';
|
|
49
53
|
export { encodeInstrument, decodeInstrument, defaultInstrument, encodeOperator, decodeOperator, defaultOperator } from "../utils/struct.js";
|
|
50
54
|
export { Emulator, TrackOption } from "../utils/constants.js";
|
|
55
|
+
export { CHANNELS_PER_CHIP, CHANNELS_STANDARD, CHANNELS_RHYTHM, OPL3_SAMPLE_RATE, CHANNEL_OPERATORS, channelBank, fnumLoReg, keyOnBlockReg, feedbackConnReg, operatorReg, noteToFnumBlock, encodeFnumBlock, keyOn, keyOff, encodeOperatorRegisters, encodeChannelVoice, channelMask } from "../utils/opl3.js";
|
|
@@ -16,9 +16,10 @@ export class AdlMidi extends BaseAdlMidi {
|
|
|
16
16
|
*
|
|
17
17
|
* @param {string} [processorUrl] - Override processor URL (optional)
|
|
18
18
|
* @param {string} [wasmUrl] - Override WASM URL (optional)
|
|
19
|
+
* @param {object} [defaultSettings] - Override profile's default synth settings
|
|
19
20
|
* @returns {Promise<void>}
|
|
20
21
|
*/
|
|
21
|
-
init(processorUrl?: string, wasmUrl?: string): Promise<void>;
|
|
22
|
+
init(processorUrl?: string, wasmUrl?: string, defaultSettings?: object): Promise<void>;
|
|
22
23
|
}
|
|
23
24
|
/**
|
|
24
25
|
* Pre-configured AdlMidiCore for nuked slim profile.
|
|
@@ -37,14 +38,18 @@ export class AdlMidiCore {
|
|
|
37
38
|
/**
|
|
38
39
|
* Create a new AdlMidiCore instance with this profile's WASM.
|
|
39
40
|
*
|
|
40
|
-
* @param {{corePath?: string}} [options]
|
|
41
|
+
* @param {{corePath?: string, defaultEmulator?: number, wasmBinary?: ArrayBuffer}} [options]
|
|
42
|
+
* Override profile defaults. corePath and defaultEmulator are pre-configured.
|
|
41
43
|
* @returns {Promise<BaseAdlMidiCore>}
|
|
42
44
|
*/
|
|
43
45
|
static create(options?: {
|
|
44
46
|
corePath?: string;
|
|
47
|
+
defaultEmulator?: number;
|
|
48
|
+
wasmBinary?: ArrayBuffer;
|
|
45
49
|
}): Promise<BaseAdlMidiCore>;
|
|
46
50
|
}
|
|
47
51
|
import { AdlMidi as BaseAdlMidi } from '../libadlmidi.js';
|
|
48
52
|
import { AdlMidiCore as BaseAdlMidiCore } from '../core.js';
|
|
49
53
|
export { encodeInstrument, decodeInstrument, defaultInstrument, encodeOperator, decodeOperator, defaultOperator } from "../utils/struct.js";
|
|
50
54
|
export { Emulator, TrackOption } from "../utils/constants.js";
|
|
55
|
+
export { CHANNELS_PER_CHIP, CHANNELS_STANDARD, CHANNELS_RHYTHM, OPL3_SAMPLE_RATE, CHANNEL_OPERATORS, channelBank, fnumLoReg, keyOnBlockReg, feedbackConnReg, operatorReg, noteToFnumBlock, encodeFnumBlock, keyOn, keyOff, encodeOperatorRegisters, encodeChannelVoice, channelMask } from "../utils/opl3.js";
|
|
@@ -17,7 +17,9 @@ export type Emulator = number;
|
|
|
17
17
|
export const Emulator: Readonly<{
|
|
18
18
|
/** Nuked OPL3 v1.8 - Most accurate, higher CPU usage */
|
|
19
19
|
NUKED: 0;
|
|
20
|
-
/** Nuked
|
|
20
|
+
/** Optimized Nuked 1.8 fork by tgies with identical output */
|
|
21
|
+
NUKED_FAST: 1;
|
|
22
|
+
/** @deprecated Use NUKED_FAST */
|
|
21
23
|
NUKED_174: 1;
|
|
22
24
|
/** DosBox OPL3 - Good accuracy, lower CPU usage */
|
|
23
25
|
DOSBOX: 2;
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get the register bank bit for a channel.
|
|
3
|
+
*
|
|
4
|
+
* @param {number} channel - Per-chip channel (0-17)
|
|
5
|
+
* @returns {number} 0x000 for channels 0-8, 0x100 for channels 9-17
|
|
6
|
+
*/
|
|
7
|
+
export function channelBank(channel: number): number;
|
|
8
|
+
/**
|
|
9
|
+
* Get the frequency-number low register address (0xA0 series).
|
|
10
|
+
*
|
|
11
|
+
* @param {number} channel - Per-chip channel (0-17)
|
|
12
|
+
* @returns {number} Register address
|
|
13
|
+
*/
|
|
14
|
+
export function fnumLoReg(channel: number): number;
|
|
15
|
+
/**
|
|
16
|
+
* Get the key-on / block / fnum-high register address (0xB0 series).
|
|
17
|
+
*
|
|
18
|
+
* @param {number} channel - Per-chip channel (0-17)
|
|
19
|
+
* @returns {number} Register address
|
|
20
|
+
*/
|
|
21
|
+
export function keyOnBlockReg(channel: number): number;
|
|
22
|
+
/**
|
|
23
|
+
* Get the feedback / connection register address (0xC0 series).
|
|
24
|
+
*
|
|
25
|
+
* @param {number} channel - Per-chip channel (0-17)
|
|
26
|
+
* @returns {number} Register address
|
|
27
|
+
*/
|
|
28
|
+
export function feedbackConnReg(channel: number): number;
|
|
29
|
+
/**
|
|
30
|
+
* Get the register address for an operator parameter.
|
|
31
|
+
*
|
|
32
|
+
* @param {number} baseReg - Register group base: 0x20 (AM/VIB/EG/KSR/MULT),
|
|
33
|
+
* 0x40 (KSL/TL), 0x60 (AR/DR), 0x80 (SL/RR), or 0xE0 (waveform)
|
|
34
|
+
* @param {number} channel - Per-chip channel (0-17)
|
|
35
|
+
* @param {number} operatorSlot - 0 for modulator, 1 for carrier
|
|
36
|
+
* @returns {number} Register address (0x000-0x1FF)
|
|
37
|
+
*/
|
|
38
|
+
export function operatorReg(baseReg: number, channel: number, operatorSlot: number): number;
|
|
39
|
+
/**
|
|
40
|
+
* Convert a MIDI note number to OPL3 fnum and block values.
|
|
41
|
+
*
|
|
42
|
+
* Uses the standard OPL3 frequency formula:
|
|
43
|
+
* freq = fnum * OPL3_SAMPLE_RATE / 2^(20 - block)
|
|
44
|
+
*
|
|
45
|
+
* Picks the lowest block where fnum fits in 10 bits (maximum resolution).
|
|
46
|
+
* Notes above ~6.2 kHz (above MIDI 110 or so) will saturate.
|
|
47
|
+
*
|
|
48
|
+
* @param {number} note - MIDI note number (0-127)
|
|
49
|
+
* @returns {{fnum: number, block: number}}
|
|
50
|
+
*/
|
|
51
|
+
export function noteToFnumBlock(note: number): {
|
|
52
|
+
fnum: number;
|
|
53
|
+
block: number;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Encode fnum and block into the two register byte values.
|
|
57
|
+
*
|
|
58
|
+
* @param {number} fnum - Frequency number (0-1023)
|
|
59
|
+
* @param {number} block - Block / octave (0-7)
|
|
60
|
+
* @returns {{fnumLo: number, fnumHiBlock: number}} Values for A0h and B0h
|
|
61
|
+
* registers. The B0h value does NOT include the key-on bit.
|
|
62
|
+
*/
|
|
63
|
+
export function encodeFnumBlock(fnum: number, block: number): {
|
|
64
|
+
fnumLo: number;
|
|
65
|
+
fnumHiBlock: number;
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Encode a key-on register write for a channel.
|
|
69
|
+
*
|
|
70
|
+
* @param {number} channel - Per-chip channel (0-17)
|
|
71
|
+
* @param {number} fnum - Frequency number (0-1023)
|
|
72
|
+
* @param {number} block - Block / octave (0-7)
|
|
73
|
+
* @returns {{reg: number, value: number}}
|
|
74
|
+
*/
|
|
75
|
+
export function keyOn(channel: number, fnum: number, block: number): {
|
|
76
|
+
reg: number;
|
|
77
|
+
value: number;
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Encode a key-off register write for a channel.
|
|
81
|
+
*
|
|
82
|
+
* @param {number} channel - Per-chip channel (0-17)
|
|
83
|
+
* @param {number} fnum - Frequency number (0-1023)
|
|
84
|
+
* @param {number} block - Block / octave (0-7)
|
|
85
|
+
* @returns {{reg: number, value: number}}
|
|
86
|
+
*/
|
|
87
|
+
export function keyOff(channel: number, fnum: number, block: number): {
|
|
88
|
+
reg: number;
|
|
89
|
+
value: number;
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Encode an Operator's properties into OPL3 register writes.
|
|
93
|
+
*
|
|
94
|
+
* @param {import('./struct.js').Operator} op
|
|
95
|
+
* @param {number} channel - Per-chip channel (0-17)
|
|
96
|
+
* @param {number} operatorSlot - 0 for modulator, 1 for carrier
|
|
97
|
+
* @returns {Array<{reg: number, value: number}>}
|
|
98
|
+
*/
|
|
99
|
+
export function encodeOperatorRegisters(op: import("./struct.js").Operator, channel: number, operatorSlot: number): Array<{
|
|
100
|
+
reg: number;
|
|
101
|
+
value: number;
|
|
102
|
+
}>;
|
|
103
|
+
/**
|
|
104
|
+
* Generate all register writes to program a 2-op channel voice.
|
|
105
|
+
*
|
|
106
|
+
* Writes both operators and the feedback/connection register. Follows
|
|
107
|
+
* the ADL_Instrument convention: operators[0] = carrier (slot 1),
|
|
108
|
+
* operators[1] = modulator (slot 0). Sets stereo output bits (L+R)
|
|
109
|
+
* on the C0h register, which is required for audible output in OPL3 mode.
|
|
110
|
+
*
|
|
111
|
+
* Only handles 2-op instruments. Throws on 4-op or pseudo-4-op
|
|
112
|
+
* instruments, which require two paired channels.
|
|
113
|
+
*
|
|
114
|
+
* @param {import('./struct.js').Instrument} instrument
|
|
115
|
+
* @param {number} channel - Per-chip channel (0-17)
|
|
116
|
+
* @returns {Array<{reg: number, value: number}>}
|
|
117
|
+
*/
|
|
118
|
+
export function encodeChannelVoice(instrument: import("./struct.js").Instrument, channel: number): Array<{
|
|
119
|
+
reg: number;
|
|
120
|
+
value: number;
|
|
121
|
+
}>;
|
|
122
|
+
/**
|
|
123
|
+
* Create a channel reservation bitmask from channel indices.
|
|
124
|
+
*
|
|
125
|
+
* @param {...number} channels - Per-chip channel indices (0-22)
|
|
126
|
+
* @returns {number} Bitmask for use with reserveChipChannels()
|
|
127
|
+
*/
|
|
128
|
+
export function channelMask(...channels: number[]): number;
|
|
129
|
+
/**
|
|
130
|
+
* OPL3 register helpers for raw chip access.
|
|
131
|
+
*
|
|
132
|
+
* Pure-JS utilities for working with the OPL3 register layout. No WASM
|
|
133
|
+
* dependency. Use alongside AdlMidiCore.rawOPL3() or AdlMidi.rawOPL3()
|
|
134
|
+
* to program the chip directly.
|
|
135
|
+
*
|
|
136
|
+
* @module opl3
|
|
137
|
+
*/
|
|
138
|
+
/** Total per-chip channels including rhythm-mode percussion */
|
|
139
|
+
export const CHANNELS_PER_CHIP: 23;
|
|
140
|
+
/** Standard 2-op melodic channels per chip */
|
|
141
|
+
export const CHANNELS_STANDARD: 18;
|
|
142
|
+
/** Rhythm-mode percussion channels (indices 18-22) */
|
|
143
|
+
export const CHANNELS_RHYTHM: 5;
|
|
144
|
+
/** OPL3 internal sample rate (Hz), used for fnum calculation. 14.31818 MHz / 288. */
|
|
145
|
+
export const OPL3_SAMPLE_RATE: 49716;
|
|
146
|
+
/**
|
|
147
|
+
* Operator register offsets per channel.
|
|
148
|
+
*
|
|
149
|
+
* Maps per-chip channel index (0-17) to [modulator, carrier] register
|
|
150
|
+
* offsets. These are the low byte of the register address; for channels
|
|
151
|
+
* 9-17, add 0x100 (use {@link channelBank}).
|
|
152
|
+
*
|
|
153
|
+
* Derived from Nuked OPL3's `ad_slot` and `ch_slot` tables.
|
|
154
|
+
*
|
|
155
|
+
* @type {ReadonlyArray<readonly [number, number]>}
|
|
156
|
+
*/
|
|
157
|
+
export const CHANNEL_OPERATORS: ReadonlyArray<readonly [number, number]>;
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "libadlmidi-js",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"libadlmidi": {
|
|
5
5
|
"version": "1.6.2",
|
|
6
|
-
"commit": "
|
|
6
|
+
"commit": "ad001c83551779934bacd642fce801c51a5b7b4c"
|
|
7
7
|
},
|
|
8
8
|
"description": "WebAssembly build of libADLMIDI - OPL3/FM synthesis for the browser",
|
|
9
9
|
"main": "dist/libadlmidi.js",
|
|
@@ -11,35 +11,47 @@
|
|
|
11
11
|
"exports": {
|
|
12
12
|
".": "./dist/libadlmidi.js",
|
|
13
13
|
"./core": {
|
|
14
|
-
"
|
|
15
|
-
"
|
|
14
|
+
"types": "./dist/core.d.ts",
|
|
15
|
+
"import": "./src/core.js"
|
|
16
16
|
},
|
|
17
17
|
"./structs": {
|
|
18
|
-
"
|
|
19
|
-
"
|
|
18
|
+
"types": "./dist/utils/struct.d.ts",
|
|
19
|
+
"import": "./src/utils/struct.js"
|
|
20
|
+
},
|
|
21
|
+
"./opl3": {
|
|
22
|
+
"types": "./dist/utils/opl3.d.ts",
|
|
23
|
+
"import": "./src/utils/opl3.js"
|
|
20
24
|
},
|
|
21
25
|
"./nuked": {
|
|
26
|
+
"types": "./dist/profiles/nuked.d.ts",
|
|
22
27
|
"import": "./src/profiles/nuked.js"
|
|
23
28
|
},
|
|
24
29
|
"./nuked/slim": {
|
|
30
|
+
"types": "./dist/profiles/nuked.slim.d.ts",
|
|
25
31
|
"import": "./src/profiles/nuked.slim.js"
|
|
26
32
|
},
|
|
27
33
|
"./dosbox": {
|
|
34
|
+
"types": "./dist/profiles/dosbox.d.ts",
|
|
28
35
|
"import": "./src/profiles/dosbox.js"
|
|
29
36
|
},
|
|
30
37
|
"./dosbox/slim": {
|
|
38
|
+
"types": "./dist/profiles/dosbox.slim.d.ts",
|
|
31
39
|
"import": "./src/profiles/dosbox.slim.js"
|
|
32
40
|
},
|
|
33
41
|
"./light": {
|
|
42
|
+
"types": "./dist/profiles/light.d.ts",
|
|
34
43
|
"import": "./src/profiles/light.js"
|
|
35
44
|
},
|
|
36
45
|
"./light/slim": {
|
|
46
|
+
"types": "./dist/profiles/light.slim.d.ts",
|
|
37
47
|
"import": "./src/profiles/light.slim.js"
|
|
38
48
|
},
|
|
39
49
|
"./full": {
|
|
50
|
+
"types": "./dist/profiles/full.d.ts",
|
|
40
51
|
"import": "./src/profiles/full.js"
|
|
41
52
|
},
|
|
42
53
|
"./full/slim": {
|
|
54
|
+
"types": "./dist/profiles/full.slim.d.ts",
|
|
43
55
|
"import": "./src/profiles/full.slim.js"
|
|
44
56
|
},
|
|
45
57
|
"./dist/*": "./dist/*"
|
|
@@ -66,7 +78,10 @@
|
|
|
66
78
|
"lint:fix": "eslint . --fix",
|
|
67
79
|
"check": "npm run lint && npm run typecheck && npm run sync-version -- --check",
|
|
68
80
|
"sync-version": "node scripts/sync-libadlmidi-version.js",
|
|
69
|
-
"prepare": "
|
|
81
|
+
"prepare": "husky"
|
|
82
|
+
},
|
|
83
|
+
"lint-staged": {
|
|
84
|
+
"*.js": "eslint --fix"
|
|
70
85
|
},
|
|
71
86
|
"types": "dist/libadlmidi.d.ts",
|
|
72
87
|
"keywords": [
|
|
@@ -86,15 +101,22 @@
|
|
|
86
101
|
"url": "https://github.com/libadlmidi-js/libadlmidi-js.git"
|
|
87
102
|
},
|
|
88
103
|
"devDependencies": {
|
|
104
|
+
"@arethetypeswrong/cli": "^0.18.2",
|
|
105
|
+
"@commitlint/cli": "^21.0.1",
|
|
106
|
+
"@commitlint/config-conventional": "^21.0.1",
|
|
107
|
+
"@emnapi/core": "1.11.1",
|
|
108
|
+
"@emnapi/runtime": "1.11.1",
|
|
89
109
|
"@eslint/js": "^10.0.1",
|
|
90
110
|
"@playwright/test": "^1.57.0",
|
|
91
111
|
"@types/node": "^25.0.3",
|
|
92
|
-
"esbuild": "^0.
|
|
112
|
+
"esbuild": "^0.28.0",
|
|
93
113
|
"eslint": "^10.0.3",
|
|
94
114
|
"globals": "^17.4.0",
|
|
115
|
+
"husky": "^9.1.7",
|
|
116
|
+
"lint-staged": "^17.0.5",
|
|
95
117
|
"playwright": "^1.57.0",
|
|
96
|
-
"typescript": "^
|
|
97
|
-
"vitest": "^4.
|
|
118
|
+
"typescript": "^6.0.3",
|
|
119
|
+
"vitest": "^4.1.5"
|
|
98
120
|
},
|
|
99
121
|
"files": [
|
|
100
122
|
"dist/",
|
|
@@ -109,5 +131,8 @@
|
|
|
109
131
|
"light-slim.js",
|
|
110
132
|
"full-slim.js",
|
|
111
133
|
"README.md"
|
|
112
|
-
]
|
|
134
|
+
],
|
|
135
|
+
"overrides": {
|
|
136
|
+
"fflate": "0.8.2"
|
|
137
|
+
}
|
|
113
138
|
}
|
package/src/core.js
CHANGED
|
@@ -51,6 +51,8 @@ export class AdlMidiCore {
|
|
|
51
51
|
* @param {Object} options
|
|
52
52
|
* @param {string} options.corePath - Path to the .core.js WASM loader module
|
|
53
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.
|
|
54
56
|
* @returns {Promise<AdlMidiCore>}
|
|
55
57
|
*/
|
|
56
58
|
static async create(options) {
|
|
@@ -73,6 +75,7 @@ export class AdlMidiCore {
|
|
|
73
75
|
core._sampleRate = 44100;
|
|
74
76
|
core._audioBuffer = null;
|
|
75
77
|
core._audioBufferPtr = null;
|
|
78
|
+
core._defaultEmulator = options.defaultEmulator;
|
|
76
79
|
|
|
77
80
|
return core;
|
|
78
81
|
}
|
|
@@ -88,6 +91,8 @@ export class AdlMidiCore {
|
|
|
88
91
|
this._audioBuffer = null;
|
|
89
92
|
/** @private @type {number|null} */
|
|
90
93
|
this._audioBufferPtr = null;
|
|
94
|
+
/** @private @type {number|undefined} */
|
|
95
|
+
this._defaultEmulator = undefined;
|
|
91
96
|
}
|
|
92
97
|
|
|
93
98
|
/**
|
|
@@ -108,6 +113,10 @@ export class AdlMidiCore {
|
|
|
108
113
|
throw new Error('Failed to initialize ADL MIDI player');
|
|
109
114
|
}
|
|
110
115
|
|
|
116
|
+
if (this._defaultEmulator !== undefined) {
|
|
117
|
+
this._module._adl_switchEmulator(this._player, this._defaultEmulator);
|
|
118
|
+
}
|
|
119
|
+
|
|
111
120
|
return true;
|
|
112
121
|
}
|
|
113
122
|
|
|
@@ -600,6 +609,60 @@ export class AdlMidiCore {
|
|
|
600
609
|
this._module._adl_rt_bankChangeLSB(this._player, channel, lsb);
|
|
601
610
|
}
|
|
602
611
|
|
|
612
|
+
// =========================================================================
|
|
613
|
+
// Raw OPL3 Access
|
|
614
|
+
// =========================================================================
|
|
615
|
+
|
|
616
|
+
/**
|
|
617
|
+
* Write a raw OPL3 register on a specific chip.
|
|
618
|
+
*
|
|
619
|
+
* Bypasses the MIDI voice allocator. The caller is responsible for the
|
|
620
|
+
* state of targeted channels. Reserve channels first via
|
|
621
|
+
* {@link reserveChipChannels} to prevent the MIDI driver from
|
|
622
|
+
* overwriting your register state.
|
|
623
|
+
*
|
|
624
|
+
* @param {number} chipId - Zero-based chip index (0 to getNumChipsObtained()-1)
|
|
625
|
+
* @param {number} reg - OPL3 register address (0x000-0x1FF, bit 8 selects bank)
|
|
626
|
+
* @param {number} value - Register value (0-255)
|
|
627
|
+
* @returns {boolean} True on success, false if chipId is out of range
|
|
628
|
+
*/
|
|
629
|
+
rawOPL3(chipId, reg, value) {
|
|
630
|
+
this._ensurePlayer();
|
|
631
|
+
return this._module._adl_rt_rawOPL3(this._player, chipId, reg, value) === 1;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* Reserve chip channels so the MIDI voice allocator will not use them.
|
|
636
|
+
*
|
|
637
|
+
* After reservation, use {@link rawOPL3} to drive those channels
|
|
638
|
+
* directly. Pass channelMask = 0 to release all reservations.
|
|
639
|
+
*
|
|
640
|
+
* @param {number} chipId - Zero-based chip index (0 to getNumChipsObtained()-1)
|
|
641
|
+
* @param {number} channelMask - Bitmask of per-chip channels to reserve
|
|
642
|
+
* (bits 0-22, where bit N reserves per-chip channel N)
|
|
643
|
+
* @returns {boolean} True on success
|
|
644
|
+
* @throws {Error} If the device is invalid
|
|
645
|
+
*/
|
|
646
|
+
reserveChipChannels(chipId, channelMask) {
|
|
647
|
+
this._ensurePlayer();
|
|
648
|
+
const result = this._module._adl_reserveChipChannels(this._player, chipId, channelMask);
|
|
649
|
+
if (result === -1) {
|
|
650
|
+
throw new Error('Invalid device');
|
|
651
|
+
}
|
|
652
|
+
return result === 0;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
/**
|
|
656
|
+
* Read back the chip-channel reservation mask.
|
|
657
|
+
*
|
|
658
|
+
* @param {number} chipId - Zero-based chip index
|
|
659
|
+
* @returns {number} Reservation bitmask (0 if invalid chipId)
|
|
660
|
+
*/
|
|
661
|
+
getReservedChipChannels(chipId) {
|
|
662
|
+
this._ensurePlayer();
|
|
663
|
+
return this._module._adl_getReservedChipChannels(this._player, chipId);
|
|
664
|
+
}
|
|
665
|
+
|
|
603
666
|
/**
|
|
604
667
|
* Generate audio samples (real-time synthesis).
|
|
605
668
|
*
|
package/src/libadlmidi.js
CHANGED
|
@@ -114,9 +114,11 @@ export class AdlMidi {
|
|
|
114
114
|
* @param {string} processorUrl - URL to the bundled processor JavaScript file
|
|
115
115
|
* @param {string | null} [wasmUrl=null] - Optional URL to the .wasm file for split builds.
|
|
116
116
|
* If not provided, assumes bundled version with embedded WASM.
|
|
117
|
+
* @param {object} [defaultSettings={}] - Initial synth settings applied before ready.
|
|
118
|
+
* Profile wrappers use this to set a default emulator.
|
|
117
119
|
* @returns {Promise<void>}
|
|
118
120
|
*/
|
|
119
|
-
async init(processorUrl, wasmUrl = null) {
|
|
121
|
+
async init(processorUrl, wasmUrl = null, defaultSettings = {}) {
|
|
120
122
|
if (!this.ctx) {
|
|
121
123
|
this.ctx = new AudioContext({ sampleRate: 44100 });
|
|
122
124
|
}
|
|
@@ -143,7 +145,8 @@ export class AdlMidi {
|
|
|
143
145
|
this.node = new AudioWorkletNode(this.ctx, 'adl-midi-processor', {
|
|
144
146
|
processorOptions: {
|
|
145
147
|
sampleRate: this.ctx.sampleRate,
|
|
146
|
-
wasmBinary: wasmBinary // null for bundled, ArrayBuffer for split
|
|
148
|
+
wasmBinary: wasmBinary, // null for bundled, ArrayBuffer for split
|
|
149
|
+
settings: defaultSettings
|
|
147
150
|
}
|
|
148
151
|
});
|
|
149
152
|
|
|
@@ -346,6 +349,64 @@ export class AdlMidi {
|
|
|
346
349
|
this.#send({ type: 'panic' });
|
|
347
350
|
}
|
|
348
351
|
|
|
352
|
+
// ================== Raw OPL3 API ==================
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Write a raw OPL3 register on a specific chip (fire-and-forget).
|
|
356
|
+
*
|
|
357
|
+
* Bypasses the MIDI voice allocator. Reserve channels first via
|
|
358
|
+
* {@link reserveChipChannels} to prevent the MIDI driver from
|
|
359
|
+
* overwriting your register state.
|
|
360
|
+
*
|
|
361
|
+
* @param {number} chipId - Zero-based chip index (0 to getNumChipsObtained()-1)
|
|
362
|
+
* @param {number} reg - OPL3 register address (0x000-0x1FF, bit 8 selects bank)
|
|
363
|
+
* @param {number} value - Register value (0-255)
|
|
364
|
+
*/
|
|
365
|
+
rawOPL3(chipId, reg, value) {
|
|
366
|
+
this.#send({ type: 'rawOPL3', chipId, reg, value });
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Reserve chip channels so the MIDI voice allocator will not use them.
|
|
371
|
+
*
|
|
372
|
+
* After reservation, use {@link rawOPL3} to drive those channels
|
|
373
|
+
* directly. Pass channelMask = 0 to release all reservations.
|
|
374
|
+
*
|
|
375
|
+
* @param {number} chipId - Zero-based chip index (0 to getNumChipsObtained()-1)
|
|
376
|
+
* @param {number} channelMask - Bitmask of per-chip channels to reserve
|
|
377
|
+
* (bits 0-22, where bit N reserves per-chip channel N)
|
|
378
|
+
* @returns {Promise<void>}
|
|
379
|
+
*/
|
|
380
|
+
async reserveChipChannels(chipId, channelMask) {
|
|
381
|
+
const reqId = this.#nextRequestId++;
|
|
382
|
+
return new Promise((resolve, reject) => {
|
|
383
|
+
this.#onceCorrelatedMessage('chipChannelsReserved', reqId, /** @param {{success: boolean}} msg */ (msg) => {
|
|
384
|
+
if (msg.success) {
|
|
385
|
+
resolve();
|
|
386
|
+
} else {
|
|
387
|
+
reject(new Error(`Failed to reserve chip channels on chip ${chipId}`));
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
this.#send({ type: 'reserveChipChannels', chipId, channelMask, reqId });
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Read back the chip-channel reservation mask.
|
|
396
|
+
*
|
|
397
|
+
* @param {number} chipId - Zero-based chip index
|
|
398
|
+
* @returns {Promise<number>} Reservation bitmask (0 if invalid chipId)
|
|
399
|
+
*/
|
|
400
|
+
async getReservedChipChannels(chipId) {
|
|
401
|
+
const reqId = this.#nextRequestId++;
|
|
402
|
+
return new Promise((resolve) => {
|
|
403
|
+
this.#onceCorrelatedMessage('reservedChipChannels', reqId, /** @param {{mask: number}} msg */ (msg) => {
|
|
404
|
+
resolve(msg.mask);
|
|
405
|
+
});
|
|
406
|
+
this.#send({ type: 'getReservedChipChannels', chipId, reqId });
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
|
|
349
410
|
/**
|
|
350
411
|
* Configure synth settings at runtime
|
|
351
412
|
* @param {ConfigureSettings} settings - Settings object
|
package/src/processor.js
CHANGED
|
@@ -45,6 +45,7 @@ class AdlMidiProcessor extends AudioWorkletProcessor {
|
|
|
45
45
|
softPan: true, // Soft stereo panning
|
|
46
46
|
deepVibrato: false, // Deep vibrato
|
|
47
47
|
deepTremolo: false, // Deep tremolo
|
|
48
|
+
emulator: undefined, // Emulator core (undefined = libADLMIDI default)
|
|
48
49
|
...options.processorOptions?.settings
|
|
49
50
|
};
|
|
50
51
|
|
|
@@ -104,6 +105,11 @@ class AdlMidiProcessor extends AudioWorkletProcessor {
|
|
|
104
105
|
applySettings(settings) {
|
|
105
106
|
if (!this.midi) return;
|
|
106
107
|
|
|
108
|
+
// Switch emulator first; adl_switchEmulator does a partialReset and a
|
|
109
|
+
// wrong-order subsequent setBank etc. would otherwise need re-applying.
|
|
110
|
+
if (settings.emulator !== undefined) {
|
|
111
|
+
this.adl._adl_switchEmulator(this.midi, settings.emulator);
|
|
112
|
+
}
|
|
107
113
|
if (settings.numChips !== undefined) {
|
|
108
114
|
this.adl._adl_setNumChips(this.midi, settings.numChips);
|
|
109
115
|
}
|
|
@@ -695,6 +701,24 @@ class AdlMidiProcessor extends AudioWorkletProcessor {
|
|
|
695
701
|
break;
|
|
696
702
|
}
|
|
697
703
|
|
|
704
|
+
// ================== Raw OPL3 ==================
|
|
705
|
+
|
|
706
|
+
case 'rawOPL3':
|
|
707
|
+
this.adl._adl_rt_rawOPL3(this.midi, msg.chipId, msg.reg, msg.value);
|
|
708
|
+
break;
|
|
709
|
+
|
|
710
|
+
case 'reserveChipChannels': {
|
|
711
|
+
const result = this.adl._adl_reserveChipChannels(this.midi, msg.chipId, msg.channelMask);
|
|
712
|
+
this.port.postMessage({ type: 'chipChannelsReserved', success: result === 0, chipId: msg.chipId, reqId: msg.reqId });
|
|
713
|
+
break;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
case 'getReservedChipChannels': {
|
|
717
|
+
const mask = this.adl._adl_getReservedChipChannels(this.midi, msg.chipId);
|
|
718
|
+
this.port.postMessage({ type: 'reservedChipChannels', mask, chipId: msg.chipId, reqId: msg.reqId });
|
|
719
|
+
break;
|
|
720
|
+
}
|
|
721
|
+
|
|
698
722
|
// ================== Debug / Diagnostics ==================
|
|
699
723
|
|
|
700
724
|
case 'describeChannels': {
|