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.
Files changed (23) hide show
  1. package/midi_parser/midi_editor.js +48 -31
  2. package/midi_parser/rmidi_writer.js +57 -38
  3. package/midi_parser/used_keys_loaded.js +28 -23
  4. package/package.json +1 -1
  5. package/soundfont/basic_soundfont/basic_preset.js +12 -0
  6. package/soundfont/basic_soundfont/basic_soundfont.js +25 -15
  7. package/soundfont/dls/dls_preset.js +11 -1
  8. package/synthetizer/worklet_processor.min.js +12 -12
  9. package/synthetizer/worklet_system/main_processor.js +29 -2
  10. package/synthetizer/worklet_system/message_protocol/handle_message.js +1 -1
  11. package/synthetizer/worklet_system/snapshot/channel_snapshot.js +18 -3
  12. package/synthetizer/worklet_system/worklet_methods/controller_control/controller_change.js +2 -9
  13. package/synthetizer/worklet_system/worklet_methods/controller_control/reset_controllers.js +9 -4
  14. package/synthetizer/worklet_system/worklet_methods/program_change.js +10 -5
  15. package/synthetizer/worklet_system/worklet_methods/soundfont_management/clear_sound_font.js +2 -3
  16. package/synthetizer/worklet_system/worklet_methods/soundfont_management/get_preset.js +4 -2
  17. package/synthetizer/worklet_system/worklet_methods/soundfont_management/reload_sound_font.js +1 -2
  18. package/synthetizer/worklet_system/worklet_methods/system_exclusive.js +5 -4
  19. package/synthetizer/worklet_system/worklet_methods/worklet_soundfont_manager/worklet_soundfont_manager.js +20 -5
  20. package/synthetizer/worklet_system/worklet_utilities/worklet_processor_channel.js +43 -20
  21. package/synthetizer/worklet_system/worklet_utilities/worklet_voice.js +3 -3
  22. package/utils/sysex_detector.js +13 -1
  23. 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.defaultPreset = this.getPreset(0, 0);
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
  */
@@ -186,7 +186,7 @@ export function handleMessage(message)
186
186
  case workletMessageType.lockController:
187
187
  if (data[0] === ALL_CHANNELS_OR_DIFFERENT_ACTION)
188
188
  {
189
- channelObject.lockPreset = data[1];
189
+ channelObject.setPresetLock(data[1]);
190
190
  }
191
191
  else
192
192
  {
@@ -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.lockPreset = false;
156
- channelObject.setBankSelect(channelSnapshot.bank, true);
169
+ channelObject.setPresetLock(false);
170
+ channelObject.setBankSelect(channelSnapshot.bank, channelSnapshot.isBankLSB);
157
171
  channelObject.programChange(channelSnapshot.program);
158
- channelObject.lockPreset = channelSnapshot.lockPreset;
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
- if (!force)
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, false, true);
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.presetUsesOverride = true;
42
- ch.setBankSelect(0, true);
42
+ ch.setBankSelect(getDefaultBank(this.system));
43
43
  if (channelNumber % 16 === DEFAULT_PERCUSSION)
44
44
  {
45
- this.workletProcessorChannels[channelNumber].setPreset(this.drumPreset);
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: ch.getBankSelect(),
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
- const bank = this.getBankSelect();
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(bankWithOffset, programNumber);
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.defaultPreset = this.getPreset(0, 0);
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.lockPreset = false;
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
  }
@@ -32,8 +32,7 @@ export function reloadSoundFont(buffer, isOverride = false)
32
32
  });
33
33
  return;
34
34
  }
35
- this.defaultPreset = this.getPreset(0, 0);
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("%cGM system on", consoleColors.info);
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 !== "xg")
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 === "xg")
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 === "xg")
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(bankNumber - sf.bankOffset, programNumber);
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 (bankNumber !== 128)
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
- const preset = sf.soundfont.presets.find(p => p.bank === 128);
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, force = false, isLSB = false)
268
+ setBankSelect(bank, isLSB = false)
240
269
  {
241
270
  if (this.lockPreset)
242
271
  {
243
272
  return;
244
273
  }
245
- if (force)
274
+ if (isLSB)
246
275
  {
247
- this.bank = bank;
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
- isLSB,
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.setDrums(false);
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
- if (this.drumChannel)
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;
@@ -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; // gm system
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
  }