spessasynth_lib 3.25.0 → 3.25.2
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 +50 -44
- package/midi_parser/rmidi_writer.js +90 -45
- package/midi_parser/used_keys_loaded.js +59 -39
- package/package.json +1 -1
- package/sequencer/sequencer.js +0 -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/synthetizer.js +48 -1
- package/synthetizer/worklet_processor.min.js +12 -12
- package/synthetizer/worklet_system/main_processor.js +46 -4
- package/synthetizer/worklet_system/message_protocol/handle_message.js +4 -1
- package/synthetizer/worklet_system/message_protocol/worklet_message.js +5 -6
- package/synthetizer/worklet_system/snapshot/channel_snapshot.js +18 -3
- package/synthetizer/worklet_system/snapshot/synthesizer_snapshot.js +1 -1
- package/synthetizer/worklet_system/worklet_methods/controller_control/controller_change.js +64 -135
- package/synthetizer/worklet_system/worklet_methods/controller_control/reset_controllers.js +8 -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 +2 -2
- package/synthetizer/worklet_system/worklet_methods/soundfont_management/reload_sound_font.js +1 -2
- package/synthetizer/worklet_system/worklet_methods/system_exclusive.js +6 -6
- 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 +70 -14
- package/synthetizer/worklet_system/worklet_utilities/worklet_voice.js +2 -3
- package/utils/sysex_detector.js +46 -0
- package/utils/xg_hacks.js +176 -0
|
@@ -2,7 +2,11 @@ import { WorkletSequencer } from "../../sequencer/worklet_sequencer/worklet_sequ
|
|
|
2
2
|
import { SpessaSynthInfo } from "../../utils/loggin.js";
|
|
3
3
|
import { consoleColors } from "../../utils/other.js";
|
|
4
4
|
import { voiceKilling } from "./worklet_methods/stopping_notes/voice_killing.js";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
ALL_CHANNELS_OR_DIFFERENT_ACTION,
|
|
7
|
+
masterParameterType,
|
|
8
|
+
returnMessageType
|
|
9
|
+
} from "./message_protocol/worklet_message.js";
|
|
6
10
|
import { stbvorbis } from "../../externals/stbvorbis_sync/stbvorbis_sync.min.js";
|
|
7
11
|
import { VOLUME_ENVELOPE_SMOOTHING_FACTOR } from "./worklet_utilities/volume_envelope.js";
|
|
8
12
|
import { handleMessage } from "./message_protocol/handle_message.js";
|
|
@@ -181,13 +185,28 @@ class SpessaSynthProcessor extends AudioWorkletProcessor
|
|
|
181
185
|
* @type {SynthSystem}
|
|
182
186
|
*/
|
|
183
187
|
system = DEFAULT_SYNTH_MODE;
|
|
184
|
-
|
|
185
188
|
/**
|
|
186
189
|
* Current total voices amount
|
|
187
190
|
* @type {number}
|
|
188
191
|
*/
|
|
189
192
|
totalVoicesAmount = 0;
|
|
190
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
|
+
|
|
191
210
|
/**
|
|
192
211
|
* Creates a new worklet synthesis system. contains all channels
|
|
193
212
|
* @param options {{
|
|
@@ -232,8 +251,7 @@ class SpessaSynthProcessor extends AudioWorkletProcessor
|
|
|
232
251
|
}
|
|
233
252
|
this.sendPresetList();
|
|
234
253
|
|
|
235
|
-
this.
|
|
236
|
-
this.drumPreset = this.getPreset(128, 0);
|
|
254
|
+
this.getDefaultPresets();
|
|
237
255
|
|
|
238
256
|
|
|
239
257
|
for (let i = 0; i < options.processorOptions.midiChannels; i++)
|
|
@@ -300,6 +318,30 @@ class SpessaSynthProcessor extends AudioWorkletProcessor
|
|
|
300
318
|
return this.masterGain * this.midiVolume;
|
|
301
319
|
}
|
|
302
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
|
+
|
|
333
|
+
/**
|
|
334
|
+
* @param value {SynthSystem}
|
|
335
|
+
*/
|
|
336
|
+
setSystem(value)
|
|
337
|
+
{
|
|
338
|
+
this.system = value;
|
|
339
|
+
this.post({
|
|
340
|
+
messageType: returnMessageType.masterParameterChange,
|
|
341
|
+
messageData: [masterParameterType.midiSystem, this.system]
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
|
|
303
345
|
/**
|
|
304
346
|
* @param bank {number}
|
|
305
347
|
* @param program {number}
|
|
@@ -158,6 +158,9 @@ export function handleMessage(message)
|
|
|
158
158
|
case masterParameterType.interpolationType:
|
|
159
159
|
this.interpolationType = value;
|
|
160
160
|
break;
|
|
161
|
+
|
|
162
|
+
case masterParameterType.midiSystem:
|
|
163
|
+
this.setSystem(value);
|
|
161
164
|
}
|
|
162
165
|
break;
|
|
163
166
|
|
|
@@ -183,7 +186,7 @@ export function handleMessage(message)
|
|
|
183
186
|
case workletMessageType.lockController:
|
|
184
187
|
if (data[0] === ALL_CHANNELS_OR_DIFFERENT_ACTION)
|
|
185
188
|
{
|
|
186
|
-
channelObject.
|
|
189
|
+
channelObject.setPresetLock(data[1]);
|
|
187
190
|
}
|
|
188
191
|
else
|
|
189
192
|
{
|
|
@@ -70,7 +70,8 @@ export const masterParameterType = {
|
|
|
70
70
|
mainVolume: 0,
|
|
71
71
|
masterPan: 1,
|
|
72
72
|
voicesCap: 2,
|
|
73
|
-
interpolationType: 3
|
|
73
|
+
interpolationType: 3,
|
|
74
|
+
midiSystem: 4
|
|
74
75
|
};
|
|
75
76
|
|
|
76
77
|
|
|
@@ -110,12 +111,11 @@ export const ALL_CHANNELS_OR_DIFFERENT_ACTION = -1;
|
|
|
110
111
|
*
|
|
111
112
|
* 0 - channel properties -> [...<ChannelProperty>] see message_sending.js line 29
|
|
112
113
|
* 1 - event call -> {eventName<string>, eventData:<the event's data>}
|
|
113
|
-
* 2 -
|
|
114
|
+
* 2 - master parameter change -> [parameter<masterParameterType>, value<string|number>]
|
|
114
115
|
* 3 - sequencer specific -> [messageType<WorkletSequencerReturnMessageType> messageData<any>] note: refer to sequencer_message.js
|
|
115
116
|
* 4 - synthesizer snapshot -> snapshot<SynthesizerSnapshot> note: refer to synthesizer_snapshot.js
|
|
116
117
|
* 5 - ready -> (no data)
|
|
117
118
|
* 6 - soundfontError -> errorMessage<string>
|
|
118
|
-
* 7 - idenfity -> version<string>
|
|
119
119
|
*/
|
|
120
120
|
|
|
121
121
|
/**
|
|
@@ -124,10 +124,9 @@ export const ALL_CHANNELS_OR_DIFFERENT_ACTION = -1;
|
|
|
124
124
|
export const returnMessageType = {
|
|
125
125
|
channelProperties: 0,
|
|
126
126
|
eventCall: 1,
|
|
127
|
-
|
|
127
|
+
masterParameterChange: 2,
|
|
128
128
|
sequencerSpecific: 3,
|
|
129
129
|
synthesizerSnapshot: 4,
|
|
130
130
|
ready: 5,
|
|
131
|
-
soundfontError: 6
|
|
132
|
-
identify: 7
|
|
131
|
+
soundfontError: 6
|
|
133
132
|
};
|
|
@@ -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
|
}
|
|
@@ -94,7 +94,7 @@ export class SynthesizerSnapshot
|
|
|
94
94
|
static applySnapshot(workletProcessor, snapshot)
|
|
95
95
|
{
|
|
96
96
|
// restore system
|
|
97
|
-
workletProcessor.
|
|
97
|
+
workletProcessor.setSystem(snapshot.system);
|
|
98
98
|
|
|
99
99
|
// restore pan and volume
|
|
100
100
|
workletProcessor.setMasterGain(snapshot.mainVolume);
|
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
import { SpessaSynthInfo } from "../../../../utils/loggin.js";
|
|
2
1
|
import { midiControllers } from "../../../../midi_parser/midi_message.js";
|
|
3
2
|
import { computeModulators } from "../../worklet_utilities/worklet_modulator.js";
|
|
4
|
-
import { consoleColors } from "../../../../utils/other.js";
|
|
5
3
|
import { channelConfiguration, dataEntryStates } from "../../worklet_utilities/controller_tables.js";
|
|
6
|
-
import { DEFAULT_PERCUSSION } from "../../../synth_constants.js";
|
|
7
4
|
|
|
8
5
|
/**
|
|
9
6
|
* @param controllerNumber {number}
|
|
@@ -56,144 +53,76 @@ export function controllerChange(controllerNumber, controllerValue, force = fals
|
|
|
56
53
|
this.midiControllers[controllerNumber] = controllerValue << 7;
|
|
57
54
|
|
|
58
55
|
// interpret special CCs
|
|
59
|
-
switch (controllerNumber)
|
|
60
56
|
{
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
if (this.drumChannel)
|
|
115
|
-
{
|
|
116
|
-
// 128 for percussion channel
|
|
117
|
-
bankNr = 128;
|
|
118
|
-
}
|
|
119
|
-
if (bankNr === 128 && !this.drumChannel)
|
|
57
|
+
switch (controllerNumber)
|
|
58
|
+
{
|
|
59
|
+
case midiControllers.allNotesOff:
|
|
60
|
+
this.stopAllNotes();
|
|
61
|
+
break;
|
|
62
|
+
|
|
63
|
+
case midiControllers.allSoundOff:
|
|
64
|
+
this.stopAllNotes(true);
|
|
65
|
+
break;
|
|
66
|
+
|
|
67
|
+
// special case: bank select
|
|
68
|
+
case midiControllers.bankSelect:
|
|
69
|
+
this.setBankSelect(controllerValue);
|
|
70
|
+
break;
|
|
71
|
+
|
|
72
|
+
case midiControllers.lsbForControl0BankSelect:
|
|
73
|
+
this.setBankSelect(controllerValue, true);
|
|
74
|
+
break;
|
|
75
|
+
|
|
76
|
+
// check for RPN and NPRN and data entry
|
|
77
|
+
case midiControllers.RPNLsb:
|
|
78
|
+
this.dataEntryState = dataEntryStates.RPFine;
|
|
79
|
+
break;
|
|
80
|
+
|
|
81
|
+
case midiControllers.RPNMsb:
|
|
82
|
+
this.dataEntryState = dataEntryStates.RPCoarse;
|
|
83
|
+
break;
|
|
84
|
+
|
|
85
|
+
case midiControllers.NRPNMsb:
|
|
86
|
+
this.dataEntryState = dataEntryStates.NRPCoarse;
|
|
87
|
+
break;
|
|
88
|
+
|
|
89
|
+
case midiControllers.NRPNLsb:
|
|
90
|
+
this.dataEntryState = dataEntryStates.NRPFine;
|
|
91
|
+
break;
|
|
92
|
+
|
|
93
|
+
case midiControllers.dataEntryMsb:
|
|
94
|
+
this.dataEntryCoarse(controllerValue);
|
|
95
|
+
break;
|
|
96
|
+
|
|
97
|
+
case midiControllers.lsbForControl6DataEntry:
|
|
98
|
+
this.dataEntryFine(controllerValue);
|
|
99
|
+
break;
|
|
100
|
+
|
|
101
|
+
case midiControllers.resetAllControllers:
|
|
102
|
+
this.resetControllersRP15Compliant();
|
|
103
|
+
break;
|
|
104
|
+
|
|
105
|
+
case midiControllers.sustainPedal:
|
|
106
|
+
if (controllerValue >= 64)
|
|
120
107
|
{
|
|
121
|
-
|
|
122
|
-
bankNr = this.getBankSelect();
|
|
108
|
+
this.holdPedal = true;
|
|
123
109
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
this.setBankSelect(bankNr);
|
|
127
|
-
break;
|
|
128
|
-
|
|
129
|
-
case midiControllers.lsbForControl0BankSelect:
|
|
130
|
-
if (this.synth.system === "xg")
|
|
131
|
-
{
|
|
132
|
-
if (!this.drumChannel)
|
|
110
|
+
else
|
|
133
111
|
{
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
if (controllerValue !== 127)
|
|
112
|
+
this.holdPedal = false;
|
|
113
|
+
this.sustainedVoices.forEach(v =>
|
|
137
114
|
{
|
|
138
|
-
|
|
139
|
-
}
|
|
115
|
+
v.release();
|
|
116
|
+
});
|
|
117
|
+
this.sustainedVoices = [];
|
|
140
118
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
// check for RPN and NPRN and data entry
|
|
149
|
-
case midiControllers.RPNLsb:
|
|
150
|
-
this.dataEntryState = dataEntryStates.RPFine;
|
|
151
|
-
break;
|
|
152
|
-
|
|
153
|
-
case midiControllers.RPNMsb:
|
|
154
|
-
this.dataEntryState = dataEntryStates.RPCoarse;
|
|
155
|
-
break;
|
|
156
|
-
|
|
157
|
-
case midiControllers.NRPNMsb:
|
|
158
|
-
this.dataEntryState = dataEntryStates.NRPCoarse;
|
|
159
|
-
break;
|
|
160
|
-
|
|
161
|
-
case midiControllers.NRPNLsb:
|
|
162
|
-
this.dataEntryState = dataEntryStates.NRPFine;
|
|
163
|
-
break;
|
|
164
|
-
|
|
165
|
-
case midiControllers.dataEntryMsb:
|
|
166
|
-
this.dataEntryCoarse(controllerValue);
|
|
167
|
-
break;
|
|
168
|
-
|
|
169
|
-
case midiControllers.lsbForControl6DataEntry:
|
|
170
|
-
this.dataEntryFine(controllerValue);
|
|
171
|
-
break;
|
|
172
|
-
|
|
173
|
-
case midiControllers.resetAllControllers:
|
|
174
|
-
this.resetControllersRP15Compliant();
|
|
175
|
-
break;
|
|
176
|
-
|
|
177
|
-
case midiControllers.sustainPedal:
|
|
178
|
-
if (controllerValue >= 64)
|
|
179
|
-
{
|
|
180
|
-
this.holdPedal = true;
|
|
181
|
-
}
|
|
182
|
-
else
|
|
183
|
-
{
|
|
184
|
-
this.holdPedal = false;
|
|
185
|
-
this.sustainedVoices.forEach(v =>
|
|
186
|
-
{
|
|
187
|
-
v.release();
|
|
188
|
-
});
|
|
189
|
-
this.sustainedVoices = [];
|
|
190
|
-
}
|
|
191
|
-
break;
|
|
192
|
-
|
|
193
|
-
// default: just compute modulators
|
|
194
|
-
default:
|
|
195
|
-
this.voices.forEach(v => computeModulators(v, this.midiControllers, 1, controllerNumber));
|
|
196
|
-
break;
|
|
119
|
+
break;
|
|
120
|
+
|
|
121
|
+
// default: just compute modulators
|
|
122
|
+
default:
|
|
123
|
+
this.voices.forEach(v => computeModulators(v, this.midiControllers, 1, controllerNumber));
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
197
126
|
}
|
|
198
127
|
this.synth.callEvent("controllerchange", {
|
|
199
128
|
channel: this.channelNumber,
|
|
@@ -25,6 +25,7 @@ export function resetAllControllers(log = true)
|
|
|
25
25
|
SpessaSynthInfo("%cResetting all controllers!", consoleColors.info);
|
|
26
26
|
}
|
|
27
27
|
this.callEvent("allcontrollerreset", undefined);
|
|
28
|
+
this.setSystem(DEFAULT_SYNTH_MODE);
|
|
28
29
|
for (let channelNumber = 0; channelNumber < this.workletProcessorChannels.length; channelNumber++)
|
|
29
30
|
{
|
|
30
31
|
this.workletProcessorChannels[channelNumber].resetControllers();
|
|
@@ -37,11 +38,11 @@ export function resetAllControllers(log = true)
|
|
|
37
38
|
// if preset is unlocked, switch to non-drums and call event
|
|
38
39
|
if (!ch.lockPreset)
|
|
39
40
|
{
|
|
40
|
-
ch.presetUsesOverride = true;
|
|
41
41
|
ch.setBankSelect(0);
|
|
42
42
|
if (channelNumber % 16 === DEFAULT_PERCUSSION)
|
|
43
43
|
{
|
|
44
|
-
|
|
44
|
+
ch.setPreset(this.drumPreset);
|
|
45
|
+
ch.presetUsesOverride = this.defaultDrumsUsesOverride;
|
|
45
46
|
ch.drumChannel = true;
|
|
46
47
|
this.callEvent("drumchange", {
|
|
47
48
|
channel: channelNumber,
|
|
@@ -51,6 +52,7 @@ export function resetAllControllers(log = true)
|
|
|
51
52
|
else
|
|
52
53
|
{
|
|
53
54
|
ch.drumChannel = false;
|
|
55
|
+
ch.presetUsesOverride = this.defaultDrumsUsesOverride;
|
|
54
56
|
ch.setPreset(this.defaultPreset);
|
|
55
57
|
this.callEvent("drumchange", {
|
|
56
58
|
channel: channelNumber,
|
|
@@ -66,11 +68,14 @@ export function resetAllControllers(log = true)
|
|
|
66
68
|
});
|
|
67
69
|
}
|
|
68
70
|
|
|
71
|
+
const presetBank = ch.preset.bank;
|
|
72
|
+
const sentBank = presetBank === 128 ? 128 : (ch.presetUsesOverride ? presetBank + this.soundfontBankOffset : presetBank);
|
|
73
|
+
|
|
69
74
|
// call program change
|
|
70
75
|
this.callEvent("programchange", {
|
|
71
76
|
channel: channelNumber,
|
|
72
77
|
program: ch.preset.program,
|
|
73
|
-
bank:
|
|
78
|
+
bank: sentBank,
|
|
74
79
|
userCalled: false
|
|
75
80
|
});
|
|
76
81
|
|
|
@@ -110,7 +115,6 @@ export function resetAllControllers(log = true)
|
|
|
110
115
|
}
|
|
111
116
|
|
|
112
117
|
this.setMIDIVolume(1);
|
|
113
|
-
this.system = DEFAULT_SYNTH_MODE;
|
|
114
118
|
}
|
|
115
119
|
|
|
116
120
|
/**
|
|
@@ -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
|
}
|
|
@@ -10,11 +10,11 @@ export function getPreset(bank, program)
|
|
|
10
10
|
{
|
|
11
11
|
// if override soundfont
|
|
12
12
|
const bankWithOffset = bank === 128 ? 128 : bank - this.soundfontBankOffset;
|
|
13
|
-
const preset = this.overrideSoundfont.getPresetNoFallback(bankWithOffset, program);
|
|
13
|
+
const preset = this.overrideSoundfont.getPresetNoFallback(bankWithOffset, program, this.system === "xg");
|
|
14
14
|
if (preset)
|
|
15
15
|
{
|
|
16
16
|
return preset;
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
|
-
return this.soundfontManager.getPreset(bank, program);
|
|
19
|
+
return this.soundfontManager.getPreset(bank, program, this.system === "xg");
|
|
20
20
|
}
|
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
|
);
|
|
@@ -148,17 +148,17 @@ export function systemExclusive(messageData, channelOffset = 0)
|
|
|
148
148
|
if (messageData[3] === 0x01)
|
|
149
149
|
{
|
|
150
150
|
SpessaSynthInfo("%cGM system on", consoleColors.info);
|
|
151
|
-
this.
|
|
151
|
+
this.setSystem("gm");
|
|
152
152
|
}
|
|
153
153
|
else if (messageData[3] === 0x03)
|
|
154
154
|
{
|
|
155
155
|
SpessaSynthInfo("%cGM2 system on", consoleColors.info);
|
|
156
|
-
this.
|
|
156
|
+
this.setSystem("gm2");
|
|
157
157
|
}
|
|
158
158
|
else
|
|
159
159
|
{
|
|
160
160
|
SpessaSynthInfo("%cGM system off, defaulting to GS", consoleColors.info);
|
|
161
|
-
this.
|
|
161
|
+
this.setSystem("gs");
|
|
162
162
|
}
|
|
163
163
|
break;
|
|
164
164
|
|
|
@@ -312,14 +312,14 @@ export function systemExclusive(messageData, channelOffset = 0)
|
|
|
312
312
|
// this is a GS reset
|
|
313
313
|
SpessaSynthInfo("%cGS Reset received!", consoleColors.info);
|
|
314
314
|
this.resetAllControllers(false);
|
|
315
|
-
this.
|
|
315
|
+
this.setSystem("gs");
|
|
316
316
|
}
|
|
317
317
|
else if (messageValue === 0x7F)
|
|
318
318
|
{
|
|
319
319
|
// GS mode off
|
|
320
320
|
SpessaSynthInfo("%cGS system off, switching to GM2", consoleColors.info);
|
|
321
321
|
this.resetAllControllers(false);
|
|
322
|
-
this.
|
|
322
|
+
this.setSystem("gm2");
|
|
323
323
|
}
|
|
324
324
|
return;
|
|
325
325
|
}
|
|
@@ -606,7 +606,7 @@ export function systemExclusive(messageData, channelOffset = 0)
|
|
|
606
606
|
case 0x7E:
|
|
607
607
|
SpessaSynthInfo("%cXG system on", consoleColors.info);
|
|
608
608
|
this.resetAllControllers(false);
|
|
609
|
-
this.
|
|
609
|
+
this.setSystem("xg");
|
|
610
610
|
break;
|
|
611
611
|
}
|
|
612
612
|
}
|
|
@@ -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;
|