spessasynth_lib 3.25.1 → 3.25.3
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/midi_parser/midi_editor.js +48 -31
- package/midi_parser/rmidi_writer.js +57 -38
- package/midi_parser/used_keys_loaded.js +28 -23
- package/package.json +1 -1
- package/soundfont/basic_soundfont/basic_preset.js +12 -0
- package/soundfont/basic_soundfont/basic_soundfont.js +25 -15
- package/soundfont/dls/dls_preset.js +11 -1
- package/synthetizer/worklet_processor.min.js +12 -12
- package/synthetizer/worklet_system/main_processor.js +29 -2
- package/synthetizer/worklet_system/message_protocol/handle_message.js +1 -1
- package/synthetizer/worklet_system/snapshot/channel_snapshot.js +18 -3
- package/synthetizer/worklet_system/worklet_methods/controller_control/controller_change.js +2 -9
- package/synthetizer/worklet_system/worklet_methods/controller_control/reset_controllers.js +9 -4
- package/synthetizer/worklet_system/worklet_methods/program_change.js +10 -5
- package/synthetizer/worklet_system/worklet_methods/soundfont_management/clear_sound_font.js +2 -3
- package/synthetizer/worklet_system/worklet_methods/soundfont_management/get_preset.js +4 -2
- package/synthetizer/worklet_system/worklet_methods/soundfont_management/reload_sound_font.js +1 -2
- package/synthetizer/worklet_system/worklet_methods/system_exclusive.js +5 -4
- package/synthetizer/worklet_system/worklet_methods/worklet_soundfont_manager/worklet_soundfont_manager.js +20 -5
- package/synthetizer/worklet_system/worklet_utilities/worklet_processor_channel.js +43 -20
- package/synthetizer/worklet_system/worklet_utilities/worklet_voice.js +3 -3
- package/utils/sysex_detector.js +13 -1
- package/utils/xg_hacks.js +74 -9
|
@@ -191,6 +191,22 @@ class SpessaSynthProcessor extends AudioWorkletProcessor
|
|
|
191
191
|
*/
|
|
192
192
|
totalVoicesAmount = 0;
|
|
193
193
|
|
|
194
|
+
/**
|
|
195
|
+
* Synth's default (reset) preset
|
|
196
|
+
* @type {BasicPreset}
|
|
197
|
+
*/
|
|
198
|
+
defaultPreset;
|
|
199
|
+
|
|
200
|
+
defaultPresetUsesOverride = false;
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Synth's default (reset) drum preset
|
|
204
|
+
* @type {BasicPreset}
|
|
205
|
+
*/
|
|
206
|
+
drumPreset;
|
|
207
|
+
|
|
208
|
+
defaultDrumsUsesOverride = false;
|
|
209
|
+
|
|
194
210
|
/**
|
|
195
211
|
* Creates a new worklet synthesis system. contains all channels
|
|
196
212
|
* @param options {{
|
|
@@ -235,8 +251,7 @@ class SpessaSynthProcessor extends AudioWorkletProcessor
|
|
|
235
251
|
}
|
|
236
252
|
this.sendPresetList();
|
|
237
253
|
|
|
238
|
-
this.
|
|
239
|
-
this.drumPreset = this.getPreset(128, 0);
|
|
254
|
+
this.getDefaultPresets();
|
|
240
255
|
|
|
241
256
|
|
|
242
257
|
for (let i = 0; i < options.processorOptions.midiChannels; i++)
|
|
@@ -303,6 +318,18 @@ class SpessaSynthProcessor extends AudioWorkletProcessor
|
|
|
303
318
|
return this.masterGain * this.midiVolume;
|
|
304
319
|
}
|
|
305
320
|
|
|
321
|
+
getDefaultPresets()
|
|
322
|
+
{
|
|
323
|
+
// override this to XG, to set the default preset to NOT be XG drums!
|
|
324
|
+
const sys = this.system;
|
|
325
|
+
this.system = "xg";
|
|
326
|
+
this.defaultPreset = this.getPreset(0, 0);
|
|
327
|
+
this.defaultPresetUsesOverride = this.overrideSoundfont?.presets?.indexOf(this.defaultPreset) >= 0;
|
|
328
|
+
this.system = sys;
|
|
329
|
+
this.drumPreset = this.getPreset(128, 0);
|
|
330
|
+
this.defaultDrumsUsesOverride = this.overrideSoundfont?.presets?.indexOf(this.drumPreset) >= 0;
|
|
331
|
+
}
|
|
332
|
+
|
|
306
333
|
/**
|
|
307
334
|
* @param value {SynthSystem}
|
|
308
335
|
*/
|
|
@@ -15,6 +15,12 @@ export class ChannelSnapshot
|
|
|
15
15
|
*/
|
|
16
16
|
bank;
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* If the bank is LSB. For restoring.
|
|
20
|
+
* @type {boolean}
|
|
21
|
+
*/
|
|
22
|
+
isBankLSB;
|
|
23
|
+
|
|
18
24
|
/**
|
|
19
25
|
* The name of the patch currently loaded in the channel.
|
|
20
26
|
* @type {string}
|
|
@@ -27,6 +33,12 @@ export class ChannelSnapshot
|
|
|
27
33
|
*/
|
|
28
34
|
lockPreset;
|
|
29
35
|
|
|
36
|
+
/**
|
|
37
|
+
* Indicates the MIDI system when the preset was locked
|
|
38
|
+
* @type {SynthSystem}
|
|
39
|
+
*/
|
|
40
|
+
lockedSystem;
|
|
41
|
+
|
|
30
42
|
/**
|
|
31
43
|
* The array of all MIDI controllers (in 14-bit values) with the modulator sources at the end.
|
|
32
44
|
* @type {Int16Array}
|
|
@@ -103,7 +115,9 @@ export class ChannelSnapshot
|
|
|
103
115
|
// program data
|
|
104
116
|
channelSnapshot.program = channelObject.preset.program;
|
|
105
117
|
channelSnapshot.bank = channelObject.getBankSelect();
|
|
118
|
+
channelSnapshot.isBankLSB = channelSnapshot.bank !== channelObject.bank;
|
|
106
119
|
channelSnapshot.lockPreset = channelObject.lockPreset;
|
|
120
|
+
channelSnapshot.lockedSystem = channelObject.lockedSystem;
|
|
107
121
|
channelSnapshot.patchName = channelObject.preset.presetName;
|
|
108
122
|
|
|
109
123
|
// controller data
|
|
@@ -152,9 +166,10 @@ export class ChannelSnapshot
|
|
|
152
166
|
channelObject.velocityOverride = channelSnapshot.velocityOverride;
|
|
153
167
|
|
|
154
168
|
// restore preset and lock
|
|
155
|
-
channelObject.
|
|
156
|
-
channelObject.setBankSelect(channelSnapshot.bank,
|
|
169
|
+
channelObject.setPresetLock(false);
|
|
170
|
+
channelObject.setBankSelect(channelSnapshot.bank, channelSnapshot.isBankLSB);
|
|
157
171
|
channelObject.programChange(channelSnapshot.program);
|
|
158
|
-
channelObject.
|
|
172
|
+
channelObject.setPresetLock(channelSnapshot.lockPreset);
|
|
173
|
+
channelObject.lockedSystem = channelSnapshot.lockedSystem;
|
|
159
174
|
}
|
|
160
175
|
}
|
|
@@ -66,18 +66,11 @@ export function controllerChange(controllerNumber, controllerValue, force = fals
|
|
|
66
66
|
|
|
67
67
|
// special case: bank select
|
|
68
68
|
case midiControllers.bankSelect:
|
|
69
|
-
|
|
70
|
-
{
|
|
71
|
-
this.setBankSelect(controllerValue);
|
|
72
|
-
}
|
|
73
|
-
else
|
|
74
|
-
{
|
|
75
|
-
this.bank = controllerValue;
|
|
76
|
-
}
|
|
69
|
+
this.setBankSelect(controllerValue);
|
|
77
70
|
break;
|
|
78
71
|
|
|
79
72
|
case midiControllers.lsbForControl0BankSelect:
|
|
80
|
-
this.setBankSelect(controllerValue,
|
|
73
|
+
this.setBankSelect(controllerValue, true);
|
|
81
74
|
break;
|
|
82
75
|
|
|
83
76
|
// check for RPN and NPRN and data entry
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
} from "../../worklet_utilities/controller_tables.js";
|
|
12
12
|
import { midiControllers } from "../../../../midi_parser/midi_message.js";
|
|
13
13
|
import { DEFAULT_PERCUSSION, DEFAULT_SYNTH_MODE } from "../../../synth_constants.js";
|
|
14
|
+
import { getDefaultBank } from "../../../../utils/xg_hacks.js";
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
/**
|
|
@@ -38,11 +39,11 @@ export function resetAllControllers(log = true)
|
|
|
38
39
|
// if preset is unlocked, switch to non-drums and call event
|
|
39
40
|
if (!ch.lockPreset)
|
|
40
41
|
{
|
|
41
|
-
ch.
|
|
42
|
-
ch.setBankSelect(0, true);
|
|
42
|
+
ch.setBankSelect(getDefaultBank(this.system));
|
|
43
43
|
if (channelNumber % 16 === DEFAULT_PERCUSSION)
|
|
44
44
|
{
|
|
45
|
-
|
|
45
|
+
ch.setPreset(this.drumPreset);
|
|
46
|
+
ch.presetUsesOverride = this.defaultDrumsUsesOverride;
|
|
46
47
|
ch.drumChannel = true;
|
|
47
48
|
this.callEvent("drumchange", {
|
|
48
49
|
channel: channelNumber,
|
|
@@ -52,6 +53,7 @@ export function resetAllControllers(log = true)
|
|
|
52
53
|
else
|
|
53
54
|
{
|
|
54
55
|
ch.drumChannel = false;
|
|
56
|
+
ch.presetUsesOverride = this.defaultDrumsUsesOverride;
|
|
55
57
|
ch.setPreset(this.defaultPreset);
|
|
56
58
|
this.callEvent("drumchange", {
|
|
57
59
|
channel: channelNumber,
|
|
@@ -67,11 +69,14 @@ export function resetAllControllers(log = true)
|
|
|
67
69
|
});
|
|
68
70
|
}
|
|
69
71
|
|
|
72
|
+
const presetBank = ch.preset.bank;
|
|
73
|
+
const sentBank = presetBank === 128 ? 128 : (ch.presetUsesOverride ? presetBank + this.soundfontBankOffset : presetBank);
|
|
74
|
+
|
|
70
75
|
// call program change
|
|
71
76
|
this.callEvent("programchange", {
|
|
72
77
|
channel: channelNumber,
|
|
73
78
|
program: ch.preset.program,
|
|
74
|
-
bank:
|
|
79
|
+
bank: sentBank,
|
|
75
80
|
userCalled: false
|
|
76
81
|
});
|
|
77
82
|
|
|
@@ -11,31 +11,36 @@ export function programChange(programNumber, userChange = false)
|
|
|
11
11
|
return;
|
|
12
12
|
}
|
|
13
13
|
// always 128 for percussion
|
|
14
|
-
|
|
14
|
+
let bank = this.getBankSelect();
|
|
15
15
|
let sentBank;
|
|
16
16
|
let preset;
|
|
17
17
|
|
|
18
|
+
const isXG = this.isXGChannel;
|
|
18
19
|
// check if override
|
|
19
20
|
if (this.synth.overrideSoundfont)
|
|
20
21
|
{
|
|
21
22
|
const bankWithOffset = bank === 128 ? 128 : bank - this.synth.soundfontBankOffset;
|
|
22
|
-
const p = this.synth.overrideSoundfont.getPresetNoFallback(
|
|
23
|
+
const p = this.synth.overrideSoundfont.getPresetNoFallback(
|
|
24
|
+
bankWithOffset,
|
|
25
|
+
programNumber,
|
|
26
|
+
isXG
|
|
27
|
+
);
|
|
23
28
|
if (p)
|
|
24
29
|
{
|
|
25
|
-
sentBank = bank;
|
|
30
|
+
sentBank = p.bank === 128 ? 128 : p.bank + this.synth.soundfontBankOffset;
|
|
26
31
|
preset = p;
|
|
27
32
|
this.presetUsesOverride = true;
|
|
28
33
|
}
|
|
29
34
|
else
|
|
30
35
|
{
|
|
31
|
-
preset = this.synth.soundfontManager.getPreset(bank, programNumber);
|
|
36
|
+
preset = this.synth.soundfontManager.getPreset(bank, programNumber, isXG);
|
|
32
37
|
sentBank = preset.bank;
|
|
33
38
|
this.presetUsesOverride = false;
|
|
34
39
|
}
|
|
35
40
|
}
|
|
36
41
|
else
|
|
37
42
|
{
|
|
38
|
-
preset = this.synth.soundfontManager.getPreset(bank, programNumber);
|
|
43
|
+
preset = this.synth.soundfontManager.getPreset(bank, programNumber, isXG);
|
|
39
44
|
sentBank = preset.bank;
|
|
40
45
|
this.presetUsesOverride = false;
|
|
41
46
|
}
|
|
@@ -11,8 +11,7 @@ export function clearSoundFont(sendPresets = true, clearOverride = true)
|
|
|
11
11
|
delete this.overrideSoundfont;
|
|
12
12
|
this.overrideSoundfont = undefined;
|
|
13
13
|
}
|
|
14
|
-
this.
|
|
15
|
-
this.drumPreset = this.getPreset(128, 0);
|
|
14
|
+
this.getDefaultPresets();
|
|
16
15
|
this.cachedVoices = [];
|
|
17
16
|
|
|
18
17
|
for (let i = 0; i < this.workletProcessorChannels.length; i++)
|
|
@@ -20,7 +19,7 @@ export function clearSoundFont(sendPresets = true, clearOverride = true)
|
|
|
20
19
|
const channelObject = this.workletProcessorChannels[i];
|
|
21
20
|
if (!clearOverride || (clearOverride && channelObject.presetUsesOverride))
|
|
22
21
|
{
|
|
23
|
-
channelObject.
|
|
22
|
+
channelObject.setPresetLock(false);
|
|
24
23
|
}
|
|
25
24
|
channelObject.programChange(channelObject.preset.program);
|
|
26
25
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { isSystemXG } from "../../../../utils/xg_hacks.js";
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* @this {SpessaSynthProcessor}
|
|
3
5
|
* @param program {number}
|
|
@@ -10,11 +12,11 @@ export function getPreset(bank, program)
|
|
|
10
12
|
{
|
|
11
13
|
// if override soundfont
|
|
12
14
|
const bankWithOffset = bank === 128 ? 128 : bank - this.soundfontBankOffset;
|
|
13
|
-
const preset = this.overrideSoundfont.getPresetNoFallback(bankWithOffset, program);
|
|
15
|
+
const preset = this.overrideSoundfont.getPresetNoFallback(bankWithOffset, program, isSystemXG(this.system));
|
|
14
16
|
if (preset)
|
|
15
17
|
{
|
|
16
18
|
return preset;
|
|
17
19
|
}
|
|
18
20
|
}
|
|
19
|
-
return this.soundfontManager.getPreset(bank, program);
|
|
21
|
+
return this.soundfontManager.getPreset(bank, program, isSystemXG(this.system));
|
|
20
22
|
}
|
package/synthetizer/worklet_system/worklet_methods/soundfont_management/reload_sound_font.js
CHANGED
|
@@ -32,8 +32,7 @@ export function reloadSoundFont(buffer, isOverride = false)
|
|
|
32
32
|
});
|
|
33
33
|
return;
|
|
34
34
|
}
|
|
35
|
-
this.
|
|
36
|
-
this.drumPreset = this.getPreset(128, 0);
|
|
35
|
+
this.getDefaultPresets();
|
|
37
36
|
this.workletProcessorChannels.forEach(c =>
|
|
38
37
|
c.programChange(c.preset.program)
|
|
39
38
|
);
|
|
@@ -2,6 +2,7 @@ import { arrayToHexString, consoleColors } from "../../../utils/other.js";
|
|
|
2
2
|
import { SpessaSynthInfo, SpessaSynthWarn } from "../../../utils/loggin.js";
|
|
3
3
|
import { midiControllers } from "../../../midi_parser/midi_message.js";
|
|
4
4
|
import { ALL_CHANNELS_OR_DIFFERENT_ACTION } from "../message_protocol/worklet_message.js";
|
|
5
|
+
import { isSystemXG } from "../../../utils/xg_hacks.js";
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* KeyNum: tuning
|
|
@@ -147,7 +148,7 @@ export function systemExclusive(messageData, channelOffset = 0)
|
|
|
147
148
|
// gm system related
|
|
148
149
|
if (messageData[3] === 0x01)
|
|
149
150
|
{
|
|
150
|
-
SpessaSynthInfo("%
|
|
151
|
+
SpessaSynthInfo("%cGM1 system on", consoleColors.info);
|
|
151
152
|
this.setSystem("gm");
|
|
152
153
|
}
|
|
153
154
|
else if (messageData[3] === 0x03)
|
|
@@ -614,7 +615,7 @@ export function systemExclusive(messageData, channelOffset = 0)
|
|
|
614
615
|
// XG part parameter
|
|
615
616
|
if (messageData[3] === 0x08)
|
|
616
617
|
{
|
|
617
|
-
if (this.system
|
|
618
|
+
if (!isSystemXG(this.system))
|
|
618
619
|
{
|
|
619
620
|
return;
|
|
620
621
|
}
|
|
@@ -714,7 +715,7 @@ export function systemExclusive(messageData, channelOffset = 0)
|
|
|
714
715
|
}
|
|
715
716
|
);
|
|
716
717
|
}
|
|
717
|
-
else if (this.system
|
|
718
|
+
else if (isSystemXG(this.system))
|
|
718
719
|
{
|
|
719
720
|
SpessaSynthWarn(
|
|
720
721
|
`%cUnrecognized Yamaha XG SysEx: %c${arrayToHexString(messageData)}`,
|
|
@@ -726,7 +727,7 @@ export function systemExclusive(messageData, channelOffset = 0)
|
|
|
726
727
|
}
|
|
727
728
|
else
|
|
728
729
|
{
|
|
729
|
-
if (this.system
|
|
730
|
+
if (isSystemXG(this.system))
|
|
730
731
|
{
|
|
731
732
|
SpessaSynthWarn(
|
|
732
733
|
`%cUnrecognized Yamaha SysEx: %c${arrayToHexString(messageData)}`,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { SpessaSynthWarn } from "../../../../utils/loggin.js";
|
|
2
2
|
import { WorkletSoundfontManagerMessageType } from "./sfman_message.js";
|
|
3
3
|
import { loadSoundFont } from "../../../../soundfont/load_soundfont.js";
|
|
4
|
+
import { isXGDrums } from "../../../../utils/xg_hacks.js";
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* @typedef {Object} SoundFontType
|
|
@@ -194,9 +195,10 @@ export class WorkletSoundfontManager
|
|
|
194
195
|
* Gets a given preset from the soundfont stack
|
|
195
196
|
* @param bankNumber {number}
|
|
196
197
|
* @param programNumber {number}
|
|
198
|
+
* @param allowXGDrums {boolean} if true, allows XG drum banks (120, 126 and 127) as drum preset
|
|
197
199
|
* @returns {BasicPreset} the preset
|
|
198
200
|
*/
|
|
199
|
-
getPreset(bankNumber, programNumber)
|
|
201
|
+
getPreset(bankNumber, programNumber, allowXGDrums = false)
|
|
200
202
|
{
|
|
201
203
|
if (this.soundfontList.length < 1)
|
|
202
204
|
{
|
|
@@ -205,19 +207,25 @@ export class WorkletSoundfontManager
|
|
|
205
207
|
for (const sf of this.soundfontList)
|
|
206
208
|
{
|
|
207
209
|
// check for the preset (with given offset)
|
|
208
|
-
const preset = sf.soundfont.getPresetNoFallback(
|
|
210
|
+
const preset = sf.soundfont.getPresetNoFallback(
|
|
211
|
+
bankNumber - sf.bankOffset,
|
|
212
|
+
programNumber,
|
|
213
|
+
allowXGDrums
|
|
214
|
+
);
|
|
209
215
|
if (preset !== undefined)
|
|
210
216
|
{
|
|
211
217
|
return preset;
|
|
212
218
|
}
|
|
213
219
|
// if not found, advance to the next soundfont
|
|
214
220
|
}
|
|
221
|
+
const isDrum = bankNumber === 128 || (allowXGDrums && isXGDrums(bankNumber));
|
|
215
222
|
// if none found, return the first correct preset found
|
|
216
|
-
if (
|
|
223
|
+
if (!isDrum)
|
|
217
224
|
{
|
|
218
225
|
for (const sf of this.soundfontList)
|
|
219
226
|
{
|
|
220
|
-
const preset = sf.soundfont.presets.find(p => p.program === programNumber
|
|
227
|
+
const preset = sf.soundfont.presets.find(p => p.program === programNumber && !p.isDrumPreset(
|
|
228
|
+
allowXGDrums));
|
|
221
229
|
if (preset)
|
|
222
230
|
{
|
|
223
231
|
return preset;
|
|
@@ -230,7 +238,14 @@ export class WorkletSoundfontManager
|
|
|
230
238
|
{
|
|
231
239
|
for (const sf of this.soundfontList)
|
|
232
240
|
{
|
|
233
|
-
|
|
241
|
+
// check for any drum type (127/128) and matching program
|
|
242
|
+
const p = sf.soundfont.presets.find(p => p.isDrumPreset(allowXGDrums) && p.program === programNumber);
|
|
243
|
+
if (p)
|
|
244
|
+
{
|
|
245
|
+
return p;
|
|
246
|
+
}
|
|
247
|
+
// check for any drum preset
|
|
248
|
+
const preset = sf.soundfont.presets.find(p => p.isDrumPreset(allowXGDrums));
|
|
234
249
|
if (preset)
|
|
235
250
|
{
|
|
236
251
|
return preset;
|
|
@@ -27,7 +27,8 @@ import { channelPressure } from "../worklet_methods/tuning_control/channel_press
|
|
|
27
27
|
import { pitchWheel } from "../worklet_methods/tuning_control/pitch_wheel.js";
|
|
28
28
|
import { setOctaveTuning } from "../worklet_methods/tuning_control/set_octave_tuning.js";
|
|
29
29
|
import { programChange } from "../worklet_methods/program_change.js";
|
|
30
|
-
import { parseBankSelect } from "../../../utils/xg_hacks.js";
|
|
30
|
+
import { chooseBank, isSystemXG, parseBankSelect } from "../../../utils/xg_hacks.js";
|
|
31
|
+
import { DEFAULT_PERCUSSION } from "../../synth_constants.js";
|
|
31
32
|
|
|
32
33
|
/**
|
|
33
34
|
* This class represents a single MIDI Channel within the synthesizer.
|
|
@@ -114,6 +115,12 @@ class WorkletProcessorChannel
|
|
|
114
115
|
*/
|
|
115
116
|
bank = 0;
|
|
116
117
|
|
|
118
|
+
/**
|
|
119
|
+
* The bank LSB number of the channel (used for patch changes in XG mode).
|
|
120
|
+
* @type {number}
|
|
121
|
+
*/
|
|
122
|
+
bankLSB = 0;
|
|
123
|
+
|
|
117
124
|
/**
|
|
118
125
|
* The preset currently assigned to the channel.
|
|
119
126
|
* @type {BasicPreset}
|
|
@@ -126,6 +133,12 @@ class WorkletProcessorChannel
|
|
|
126
133
|
*/
|
|
127
134
|
lockPreset = false;
|
|
128
135
|
|
|
136
|
+
/**
|
|
137
|
+
* Indicates the MIDI system when the preset was locked.
|
|
138
|
+
* @type {SynthSystem}
|
|
139
|
+
*/
|
|
140
|
+
lockedSystem = "gs";
|
|
141
|
+
|
|
129
142
|
/**
|
|
130
143
|
* Indicates whether the channel uses a preset from the override soundfont.
|
|
131
144
|
* @type {boolean}
|
|
@@ -190,6 +203,11 @@ class WorkletProcessorChannel
|
|
|
190
203
|
this.channelNumber = channelNumber;
|
|
191
204
|
}
|
|
192
205
|
|
|
206
|
+
get isXGChannel()
|
|
207
|
+
{
|
|
208
|
+
return isSystemXG(this.synth.system) || (this.lockPreset && isSystemXG(this.lockedSystem));
|
|
209
|
+
}
|
|
210
|
+
|
|
193
211
|
/**
|
|
194
212
|
* @param type {customControllers|number}
|
|
195
213
|
* @param value {number}
|
|
@@ -231,32 +249,43 @@ class WorkletProcessorChannel
|
|
|
231
249
|
));
|
|
232
250
|
}
|
|
233
251
|
|
|
252
|
+
/**
|
|
253
|
+
* @param locked {boolean}
|
|
254
|
+
*/
|
|
255
|
+
setPresetLock(locked)
|
|
256
|
+
{
|
|
257
|
+
this.lockPreset = locked;
|
|
258
|
+
if (locked)
|
|
259
|
+
{
|
|
260
|
+
this.lockedSystem = this.synth.system;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
234
264
|
/**
|
|
235
265
|
* @param bank {number}
|
|
236
|
-
* @param force {boolean}
|
|
237
266
|
* @param isLSB {boolean}
|
|
238
267
|
*/
|
|
239
|
-
setBankSelect(bank,
|
|
268
|
+
setBankSelect(bank, isLSB = false)
|
|
240
269
|
{
|
|
241
270
|
if (this.lockPreset)
|
|
242
271
|
{
|
|
243
272
|
return;
|
|
244
273
|
}
|
|
245
|
-
if (
|
|
274
|
+
if (isLSB)
|
|
246
275
|
{
|
|
247
|
-
this.
|
|
276
|
+
this.bankLSB = bank;
|
|
248
277
|
}
|
|
249
278
|
else
|
|
250
279
|
{
|
|
280
|
+
this.bank = bank;
|
|
251
281
|
const bankLogic = parseBankSelect(
|
|
252
282
|
this.getBankSelect(),
|
|
253
283
|
bank,
|
|
254
284
|
this.synth.system,
|
|
255
|
-
|
|
285
|
+
false,
|
|
256
286
|
this.drumChannel,
|
|
257
287
|
this.channelNumber
|
|
258
288
|
);
|
|
259
|
-
this.bank = bankLogic.newBank;
|
|
260
289
|
switch (bankLogic.drumsStatus)
|
|
261
290
|
{
|
|
262
291
|
default:
|
|
@@ -264,7 +293,11 @@ class WorkletProcessorChannel
|
|
|
264
293
|
break;
|
|
265
294
|
|
|
266
295
|
case 1:
|
|
267
|
-
this.
|
|
296
|
+
if (this.channelNumber % 16 === DEFAULT_PERCUSSION)
|
|
297
|
+
{
|
|
298
|
+
// cannot disable drums on channel 9
|
|
299
|
+
this.bank = 127;
|
|
300
|
+
}
|
|
268
301
|
break;
|
|
269
302
|
|
|
270
303
|
case 2:
|
|
@@ -279,11 +312,7 @@ class WorkletProcessorChannel
|
|
|
279
312
|
*/
|
|
280
313
|
getBankSelect()
|
|
281
314
|
{
|
|
282
|
-
|
|
283
|
-
{
|
|
284
|
-
return 128;
|
|
285
|
-
}
|
|
286
|
-
return this.bank;
|
|
315
|
+
return chooseBank(this.bank, this.bankLSB, this.drumChannel, this.isXGChannel);
|
|
287
316
|
}
|
|
288
317
|
|
|
289
318
|
/**
|
|
@@ -319,23 +348,17 @@ class WorkletProcessorChannel
|
|
|
319
348
|
// clear transpose
|
|
320
349
|
this.channelTransposeKeyShift = 0;
|
|
321
350
|
this.drumChannel = true;
|
|
322
|
-
this.setPreset(this.synth.getPreset(this.getBankSelect(), this.preset.program));
|
|
323
351
|
}
|
|
324
352
|
else
|
|
325
353
|
{
|
|
326
354
|
this.drumChannel = false;
|
|
327
|
-
this.setPreset(
|
|
328
|
-
this.synth.getPreset(
|
|
329
|
-
this.getBankSelect(),
|
|
330
|
-
this.preset.program
|
|
331
|
-
)
|
|
332
|
-
);
|
|
333
355
|
}
|
|
334
356
|
this.presetUsesOverride = false;
|
|
335
357
|
this.synth.callEvent("drumchange", {
|
|
336
358
|
channel: this.channelNumber,
|
|
337
359
|
isDrumChannel: this.drumChannel
|
|
338
360
|
});
|
|
361
|
+
this.programChange(this.preset.program);
|
|
339
362
|
this.synth.sendChannelProperties();
|
|
340
363
|
}
|
|
341
364
|
|
|
@@ -10,6 +10,7 @@ import { WorkletVolumeEnvelope } from "./volume_envelope.js";
|
|
|
10
10
|
import { WorkletModulationEnvelope } from "./modulation_envelope.js";
|
|
11
11
|
import { addAndClampGenerator, generatorTypes } from "../../../soundfont/basic_soundfont/generator.js";
|
|
12
12
|
import { Modulator } from "../../../soundfont/basic_soundfont/modulator.js";
|
|
13
|
+
import { isSystemXG } from "../../../utils/xg_hacks.js";
|
|
13
14
|
|
|
14
15
|
const EXCLUSIVE_CUTOFF_TIME = -2320;
|
|
15
16
|
const EXCLUSIVE_MOD_CUTOFF_TIME = -1130; // less because filter shenanigans
|
|
@@ -408,7 +409,7 @@ export function getWorkletVoices(channel,
|
|
|
408
409
|
let preset = channelObject.preset;
|
|
409
410
|
if (overridePatch)
|
|
410
411
|
{
|
|
411
|
-
preset = this.soundfontManager.getPreset(bank, program);
|
|
412
|
+
preset = this.soundfontManager.getPreset(bank, program, isSystemXG(this.system));
|
|
412
413
|
}
|
|
413
414
|
/**
|
|
414
415
|
* @returns {WorkletVoice[]}
|
|
@@ -503,8 +504,7 @@ export function getWorkletVoices(channel,
|
|
|
503
504
|
targetKey,
|
|
504
505
|
realKey,
|
|
505
506
|
generators,
|
|
506
|
-
sampleAndGenerators.modulators.map(m => Modulator.copy(m))
|
|
507
|
-
this.filterSmoothingFactor
|
|
507
|
+
sampleAndGenerators.modulators.map(m => Modulator.copy(m))
|
|
508
508
|
)
|
|
509
509
|
);
|
|
510
510
|
return voices;
|
package/utils/sysex_detector.js
CHANGED
|
@@ -42,5 +42,17 @@ export function isGSOn(e)
|
|
|
42
42
|
export function isGMOn(e)
|
|
43
43
|
{
|
|
44
44
|
return e.messageData[0] === 0x7E // non realtime
|
|
45
|
-
&& e.messageData[2] === 0x09
|
|
45
|
+
&& e.messageData[2] === 0x09 // gm system
|
|
46
|
+
&& e.messageData[3] === 0x01; // gm1
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @param e {MIDIMessage}
|
|
51
|
+
* @returns boolean
|
|
52
|
+
*/
|
|
53
|
+
export function isGM2On(e)
|
|
54
|
+
{
|
|
55
|
+
return e.messageData[0] === 0x7E // non realtime
|
|
56
|
+
&& e.messageData[2] === 0x09 // gm system
|
|
57
|
+
&& e.messageData[3] === 0x03; // gm2
|
|
46
58
|
}
|