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.
Files changed (27) hide show
  1. package/midi_parser/midi_editor.js +50 -44
  2. package/midi_parser/rmidi_writer.js +90 -45
  3. package/midi_parser/used_keys_loaded.js +59 -39
  4. package/package.json +1 -1
  5. package/sequencer/sequencer.js +0 -1
  6. package/soundfont/basic_soundfont/basic_preset.js +12 -0
  7. package/soundfont/basic_soundfont/basic_soundfont.js +25 -15
  8. package/soundfont/dls/dls_preset.js +11 -1
  9. package/synthetizer/synthetizer.js +48 -1
  10. package/synthetizer/worklet_processor.min.js +12 -12
  11. package/synthetizer/worklet_system/main_processor.js +46 -4
  12. package/synthetizer/worklet_system/message_protocol/handle_message.js +4 -1
  13. package/synthetizer/worklet_system/message_protocol/worklet_message.js +5 -6
  14. package/synthetizer/worklet_system/snapshot/channel_snapshot.js +18 -3
  15. package/synthetizer/worklet_system/snapshot/synthesizer_snapshot.js +1 -1
  16. package/synthetizer/worklet_system/worklet_methods/controller_control/controller_change.js +64 -135
  17. package/synthetizer/worklet_system/worklet_methods/controller_control/reset_controllers.js +8 -4
  18. package/synthetizer/worklet_system/worklet_methods/program_change.js +10 -5
  19. package/synthetizer/worklet_system/worklet_methods/soundfont_management/clear_sound_font.js +2 -3
  20. package/synthetizer/worklet_system/worklet_methods/soundfont_management/get_preset.js +2 -2
  21. package/synthetizer/worklet_system/worklet_methods/soundfont_management/reload_sound_font.js +1 -2
  22. package/synthetizer/worklet_system/worklet_methods/system_exclusive.js +6 -6
  23. package/synthetizer/worklet_system/worklet_methods/worklet_soundfont_manager/worklet_soundfont_manager.js +20 -5
  24. package/synthetizer/worklet_system/worklet_utilities/worklet_processor_channel.js +70 -14
  25. package/synthetizer/worklet_system/worklet_utilities/worklet_voice.js +2 -3
  26. package/utils/sysex_detector.js +46 -0
  27. package/utils/xg_hacks.js +176 -0
@@ -5,6 +5,8 @@ import { consoleColors } from "../utils/other.js";
5
5
 
6
6
  import { customControllers } from "../synthetizer/worklet_system/worklet_utilities/controller_tables.js";
7
7
  import { DEFAULT_PERCUSSION } from "../synthetizer/synth_constants.js";
8
+ import { isGMOn, isGSOn, isXGOn } from "../utils/sysex_detector.js";
9
+ import { isXGDrums, XG_SFX_VOICE } from "../utils/xg_hacks.js";
8
10
 
9
11
  /**
10
12
  * @param ticks {number}
@@ -126,6 +128,11 @@ export function modifyMIDI(
126
128
  const midi = this;
127
129
  SpessaSynthGroupCollapsed("%cApplying changes to the MIDI file...", consoleColors.info);
128
130
 
131
+ SpessaSynthInfo("Desired program changes:", desiredProgramChanges);
132
+ SpessaSynthInfo("Desired CC changes:", desiredControllerChanges);
133
+ SpessaSynthInfo("Desired channels to clear:", desiredChannelsToClear);
134
+ SpessaSynthInfo("Desired channels to transpose:", desiredChannelsToTranspose);
135
+
129
136
  /**
130
137
  * @type {Set<number>}
131
138
  */
@@ -336,7 +343,7 @@ export function modifyMIDI(
336
343
  if (channelsToChangeProgram.has(channel))
337
344
  {
338
345
  const change = desiredProgramChanges.find(c => c.channel === channel);
339
- let desiredBank = change.bank;
346
+ let desiredBank = Math.max(0, Math.min(change.bank, 127));
340
347
  const desiredProgram = change.program;
341
348
  SpessaSynthInfo(
342
349
  `%cSetting %c${change.channel}%c to %c${desiredBank}:${desiredProgram}%c. Track num: %c${trackNum}`,
@@ -361,50 +368,61 @@ export function modifyMIDI(
361
368
  );
362
369
  addEventBefore(programChange);
363
370
 
364
- // on xg, add lsb
365
- if (!change.isDrum && system === "xg")
371
+ const addBank = (isLSB, v) =>
366
372
  {
367
- const bankChangeLSB = getControllerChange(
373
+ const bankChange = getControllerChange(
368
374
  midiChannel,
369
- midiControllers.lsbForControl0BankSelect,
370
- desiredBank,
375
+ isLSB ? midiControllers.lsbForControl0BankSelect : midiControllers.bankSelect,
376
+ v,
371
377
  e.ticks
372
378
  );
373
- addEventBefore(bankChangeLSB);
374
- }
375
-
376
- // add the bank MSB
377
- const bankChange = getControllerChange(
378
- midiChannel,
379
- midiControllers.bankSelect,
380
- desiredBank,
381
- e.ticks
382
- );
383
- addEventBefore(bankChange);
379
+ addEventBefore(bankChange);
380
+ };
384
381
 
385
- // is drums?
386
- // if so, adjust
387
- // do not add gs drum change on the drum channel
388
- if (change.isDrum)
382
+ // on xg, add lsb
383
+ if (system === "xg")
389
384
  {
390
- if (system === "gs" && midiChannel !== DEFAULT_PERCUSSION)
385
+ // xg drums: msb can be 120, 126 or 127
386
+ if (change.isDrum)
391
387
  {
392
388
  SpessaSynthInfo(
393
- `%cAdding GS Drum change on track %c${trackNum}`,
389
+ `%cAdding XG Drum change on track %c${trackNum}`,
394
390
  consoleColors.recognized,
395
391
  consoleColors.value
396
392
  );
397
- addEventBefore(getDrumChange(midiChannel, e.ticks));
393
+ addBank(false, isXGDrums(desiredBank) ? desiredBank : 127);
394
+ addBank(true, 0);
395
+ }
396
+ else
397
+ {
398
+ // sfx voice is set via MSB
399
+ if (desiredBank === XG_SFX_VOICE)
400
+ {
401
+ addBank(false, XG_SFX_VOICE);
402
+ addBank(true, 0);
403
+ }
404
+ else
405
+ {
406
+ // add variation as LSB
407
+ addBank(false, 0);
408
+ addBank(true, desiredBank);
409
+ }
398
410
  }
399
- else if (system === "xg")
411
+ }
412
+ else
413
+ {
414
+ // add just msb
415
+ addBank(false, desiredBank);
416
+
417
+ if (change.isDrum && midiChannel !== DEFAULT_PERCUSSION)
400
418
  {
419
+ // add gs drum change
401
420
  SpessaSynthInfo(
402
- `%cAdding XG Drum change on track %c${trackNum}`,
421
+ `%cAdding GS Drum change on track %c${trackNum}`,
403
422
  consoleColors.recognized,
404
423
  consoleColors.value
405
424
  );
406
- // the system is xg. drums are on msb bank 127.
407
- desiredBank = 127;
425
+ addEventBefore(getDrumChange(midiChannel, e.ticks));
408
426
  }
409
427
  }
410
428
  }
@@ -450,12 +468,7 @@ export function modifyMIDI(
450
468
 
451
469
  case messageTypes.systemExclusive:
452
470
  // check for xg on
453
- if (
454
- e.messageData[0] === 0x43 && // Yamaha
455
- e.messageData[2] === 0x4C && // XG ON
456
- e.messageData[5] === 0x7E &&
457
- e.messageData[6] === 0x00
458
- )
471
+ if (isXGOn(e))
459
472
  {
460
473
  SpessaSynthInfo("%cXG system on detected", consoleColors.info);
461
474
  system = "xg";
@@ -479,11 +492,7 @@ export function modifyMIDI(
479
492
  }
480
493
  else
481
494
  // check for GS on
482
- if (
483
- e.messageData[0] === 0x41 // roland
484
- && e.messageData[2] === 0x42 // GS
485
- && e.messageData[6] === 0x7F // Mode set
486
- )
495
+ if (isGSOn(e))
487
496
  {
488
497
  // that's a GS on, we're done here
489
498
  addedGs = true;
@@ -495,10 +504,7 @@ export function modifyMIDI(
495
504
  }
496
505
  else
497
506
  // check for GM/2 on
498
- if (
499
- e.messageData[0] === 0x7E // non realtime
500
- && e.messageData[2] === 0x09 // gm system
501
- )
507
+ if (isGMOn(e))
502
508
  {
503
509
  // that's a GM/2 system change, remove it!
504
510
  SpessaSynthInfo(
@@ -511,7 +517,7 @@ export function modifyMIDI(
511
517
  }
512
518
  }
513
519
  // check for gs
514
- if (!addedGs)
520
+ if (!addedGs && desiredProgramChanges.length > 0)
515
521
  {
516
522
  // gs is not on, add it on the first track at index 0 (or 1 if track name is first)
517
523
  let index = 0;
@@ -7,6 +7,8 @@ import { SpessaSynthGroup, SpessaSynthGroupEnd, SpessaSynthInfo } from "../utils
7
7
  import { consoleColors } from "../utils/other.js";
8
8
  import { writeLittleEndian } from "../utils/byte_functions/little_endian.js";
9
9
  import { DEFAULT_PERCUSSION } from "../synthetizer/synth_constants.js";
10
+ import { chooseBank, parseBankSelect } from "../utils/xg_hacks.js";
11
+ import { isGMOn, isGSDrumsOn, isGSOn, isXGOn } from "../utils/sysex_detector.js";
10
12
 
11
13
  /**
12
14
  * @enum {string}
@@ -122,6 +124,7 @@ export function writeRMIDI(
122
124
  * program: number,
123
125
  * drums: boolean,
124
126
  * lastBank: MIDIMessage,
127
+ * lastBankLSB: MIDIMessage,
125
128
  * hasBankSelect: boolean
126
129
  * }[]}
127
130
  */
@@ -132,6 +135,7 @@ export function writeRMIDI(
132
135
  program: 0,
133
136
  drums: i % 16 === DEFAULT_PERCUSSION, // drums appear on 9 every 16 channels,
134
137
  lastBank: undefined,
138
+ lastBankLSB: undefined,
135
139
  hasBankSelect: false
136
140
  });
137
141
  }
@@ -166,37 +170,18 @@ export function writeRMIDI(
166
170
  if (status === messageTypes.systemExclusive)
167
171
  {
168
172
  // check for drum sysex
169
- if (
170
- e.messageData[0] !== 0x41 || // roland
171
- e.messageData[2] !== 0x42 || // GS
172
- e.messageData[3] !== 0x12 || // GS
173
- e.messageData[4] !== 0x40 || // system parameter
174
- (e.messageData[5] & 0x10) === 0 || // part parameter
175
- e.messageData[6] !== 0x15 // drum part
176
- )
173
+ if (!isGSDrumsOn(e))
177
174
  {
178
175
  // check for XG
179
- if (
180
- e.messageData[0] === 0x43 && // yamaha
181
- e.messageData[2] === 0x4C && // sXG ON
182
- e.messageData[5] === 0x7E &&
183
- e.messageData[6] === 0x00
184
- )
176
+ if (isXGOn(e))
185
177
  {
186
178
  system = "xg";
187
179
  }
188
- else if (
189
- e.messageData[0] === 0x41 // roland
190
- && e.messageData[2] === 0x42 // GS
191
- && e.messageData[6] === 0x7F // Mode set
192
- )
180
+ else if (isGSOn(e))
193
181
  {
194
182
  system = "gs";
195
183
  }
196
- else if (
197
- e.messageData[0] === 0x7E // non realtime
198
- && e.messageData[2] === 0x09 // gm system
199
- )
184
+ else if (isGMOn(e))
200
185
  {
201
186
  system = "gm";
202
187
  unwantedSystems.push({
@@ -213,47 +198,73 @@ export function writeRMIDI(
213
198
 
214
199
  // program change
215
200
  const chNum = (e.messageStatusByte & 0xF) + portOffset;
201
+ /**
202
+ * @type {{program: number, drums: boolean, lastBank: MIDIMessage, lastBankLSB: MIDIMessage, hasBankSelect: boolean}}
203
+ */
216
204
  const channel = channelsInfo[chNum];
217
205
  if (status === messageTypes.programChange)
218
206
  {
207
+ const isXG = system === "xg";
219
208
  // check if the preset for this program exists
209
+ const initialProgram = e.messageData[0];
220
210
  if (channel.drums)
221
211
  {
222
- if (soundfont.presets.findIndex(p => p.program === e.messageData[0] && p.bank === 128) === -1)
212
+ if (soundfont.presets.findIndex(p => p.program === initialProgram && p.isDrumPreset(
213
+ isXG,
214
+ true
215
+ )) === -1)
223
216
  {
224
217
  // doesn't exist. pick any preset that has bank 128.
225
- e.messageData[0] = soundfont.presets.find(p => p.bank === 128)?.program || 0;
218
+ e.messageData[0] = soundfont.presets.find(p => p.isDrumPreset(isXG))?.program || 0;
219
+ SpessaSynthInfo(
220
+ `%cNo drum preset %c${initialProgram}%c. Channel %c${chNum}%c. Changing program to ${e.messageData[0]}.`,
221
+ consoleColors.info,
222
+ consoleColors.unrecognized,
223
+ consoleColors.info,
224
+ consoleColors.recognized,
225
+ consoleColors.info
226
+ );
226
227
  }
227
228
  }
228
229
  else
229
230
  {
230
- if (soundfont.presets.findIndex(p => p.program === e.messageData[0] && p.bank !== 128) === -1)
231
+ if (soundfont.presets.findIndex(p => p.program === initialProgram && !p.isDrumPreset(isXG)) === -1)
231
232
  {
232
233
  // doesn't exist. pick any preset that does not have bank 128.
233
- e.messageData[0] = soundfont.presets.find(p => p.bank !== 128)?.program || 0;
234
+ e.messageData[0] = soundfont.presets.find(p => !p.isDrumPreset(isXG))?.program || 0;
235
+ SpessaSynthInfo(
236
+ `%cNo preset %c${initialProgram}%c. Channel %c${chNum}%c. Changing program to ${e.messageData[0]}.`,
237
+ consoleColors.info,
238
+ consoleColors.unrecognized,
239
+ consoleColors.info,
240
+ consoleColors.recognized,
241
+ consoleColors.info
242
+ );
234
243
  }
235
244
  }
236
245
  channel.program = e.messageData[0];
237
246
  // check if this preset exists for program and bank
238
247
  const realBank = Math.max(0, channel.lastBank?.messageData[1] - mid.bankOffset); // make sure to take the previous bank offset into account
239
- const bank = channel.drums ? 128 : realBank;
248
+ const bankLSB = (channel?.lastBankLSB?.messageData[1] - mid.bankOffset) || 0;
240
249
  if (channel.lastBank === undefined)
241
250
  {
242
251
  continue;
243
252
  }
244
- if (system === "xg" && channel.drums)
245
- {
246
- // drums override: set the bank to 127
247
- channelsInfo[chNum].lastBank.messageData[1] = 127;
248
- }
249
-
253
+ // adjust bank for XG
254
+ let bank = chooseBank(realBank, bankLSB, channel.drums, isXG);
250
255
  if (soundfont.presets.findIndex(p => p.bank === bank && p.program === e.messageData[0]) === -1)
251
256
  {
252
257
  // no preset with this bank. find this program with any bank
253
258
  const targetBank = (soundfont.presets.find(p => p.program === e.messageData[0])?.bank + bankOffset) || bankOffset;
254
259
  channel.lastBank.messageData[1] = targetBank;
260
+ if (channel?.lastBankLSB?.messageData)
261
+ {
262
+ channel.lastBankLSB.messageData[1] = targetBank;
263
+ }
255
264
  SpessaSynthInfo(
256
- `%cNo preset %c${bank}:${e.messageData[0]}%c. Changing bank to ${targetBank}.`,
265
+ `%cNo preset %c${bank}:${e.messageData[0]}%c. Channel %c${chNum}%c. Changing bank to ${targetBank}.`,
266
+ consoleColors.info,
267
+ consoleColors.unrecognized,
257
268
  consoleColors.info,
258
269
  consoleColors.recognized,
259
270
  consoleColors.info
@@ -262,11 +273,21 @@ export function writeRMIDI(
262
273
  else
263
274
  {
264
275
  // There is a preset with this bank. Add offset. For drums add the normal offset.
265
- let drumBank = system === "xg" ? 127 : 0;
266
- const newBank = (bank === 128 ? drumBank : realBank) + bankOffset;
276
+ let drumBank = bank;
277
+ if (system === "xg" && bank === 128)
278
+ {
279
+ bank = 127;
280
+ }
281
+ const newBank = (bank === 128 ? 128 : drumBank) + bankOffset;
267
282
  channel.lastBank.messageData[1] = newBank;
283
+ if (channel?.lastBankLSB?.messageData && !channel.drums)
284
+ {
285
+ channel.lastBankLSB.messageData[1] = channel.lastBankLSB.messageData[1] - mid.bankOffset + bankOffset;
286
+ }
268
287
  SpessaSynthInfo(
269
- `%cPreset %c${bank}:${e.messageData[0]}%c exists. Changing bank to ${newBank}.`,
288
+ `%cPreset %c${bank}:${e.messageData[0]}%c exists. Channel %c${chNum}%c. Changing bank to ${newBank}.`,
289
+ consoleColors.info,
290
+ consoleColors.recognized,
270
291
  consoleColors.info,
271
292
  consoleColors.recognized,
272
293
  consoleColors.info
@@ -274,19 +295,42 @@ export function writeRMIDI(
274
295
  }
275
296
  continue;
276
297
  }
277
- // we only care about bank select
278
- if (e.messageData[0] !== midiControllers.bankSelect)
298
+
299
+ // controller change
300
+ // we only care about bank-selects
301
+ const isLSB = e.messageData[0] === midiControllers.lsbForControl0BankSelect;
302
+ if (e.messageData[0] !== midiControllers.bankSelect && !isLSB)
279
303
  {
280
304
  continue;
281
305
  }
282
306
  // bank select
283
307
  channel.hasBankSelect = true;
284
- if (system === "xg")
308
+ const bankNumber = e.messageData[1];
309
+ // interpret
310
+ const intepretation = parseBankSelect(
311
+ channel?.lastBank?.messageData[1] || 0,
312
+ bankNumber,
313
+ system,
314
+ isLSB,
315
+ channel.drums,
316
+ chNum
317
+ );
318
+ if (intepretation.drumsStatus === 2)
319
+ {
320
+ channel.drums = true;
321
+ }
322
+ else if (intepretation.drumsStatus === 1)
323
+ {
324
+ channel.drums = false;
325
+ }
326
+ if (isLSB)
327
+ {
328
+ channel.lastBankLSB = e;
329
+ }
330
+ else
285
331
  {
286
- // check for xg drums
287
- channel.drums = e.messageData[1] === 120 || e.messageData[1] === 126 || e.messageData[1] === 127;
332
+ channel.lastBank = e;
288
333
  }
289
- channel.lastBank = e;
290
334
  }
291
335
 
292
336
  // add missing bank selects
@@ -339,7 +383,8 @@ export function writeRMIDI(
339
383
  const ticks = track[indexToAdd].ticks;
340
384
  const targetBank = (soundfont.getPreset(
341
385
  0,
342
- has.program
386
+ has.program,
387
+ system === "xg"
343
388
  )?.bank + bankOffset) || bankOffset;
344
389
  track.splice(indexToAdd, 0, new MIDIMessage(
345
390
  ticks,
@@ -2,11 +2,13 @@ import { SpessaSynthGroupCollapsed, SpessaSynthGroupEnd, SpessaSynthInfo } from
2
2
  import { consoleColors } from "../utils/other.js";
3
3
  import { messageTypes, midiControllers } from "./midi_message.js";
4
4
  import { DEFAULT_PERCUSSION } from "../synthetizer/synth_constants.js";
5
+ import { chooseBank, parseBankSelect } from "../utils/xg_hacks.js";
6
+ import { isGSDrumsOn, isXGOn } from "../utils/sysex_detector.js";
5
7
 
6
8
  /**
7
9
  * Gets the used programs and keys for this MIDI file with a given sound bank
8
10
  * @this {BasicMIDI}
9
- * @param soundfont {{getPreset: function(number, number): BasicPreset}} - the sound bank
11
+ * @param soundfont {BasicSoundBank|WorkletSoundfontManager} - the sound bank
10
12
  * @returns {Object<string, Set<string>>}
11
13
  */
12
14
  export function getUsedProgramsAndKeys(soundfont)
@@ -19,7 +21,7 @@ export function getUsedProgramsAndKeys(soundfont)
19
21
  // Find every bank:program combo and every key:velocity for each. Make sure to care about ports and drums
20
22
  const channelsAmount = 16 + mid.midiPortChannelOffsets.reduce((max, cur) => cur > max ? cur : max);
21
23
  /**
22
- * @type {{program: number, bank: number, drums: boolean, string: string}[]}
24
+ * @type {{program: number, bank: number, bankLSB: number, drums: boolean, string: string, actualBank: number}[]}
23
25
  */
24
26
  const channelPresets = [];
25
27
  for (let i = 0; i < channelsAmount; i++)
@@ -28,18 +30,24 @@ export function getUsedProgramsAndKeys(soundfont)
28
30
  channelPresets.push({
29
31
  program: 0,
30
32
  bank: bank,
33
+ bankLSB: 0,
34
+ actualBank: bank,
31
35
  drums: i % 16 === DEFAULT_PERCUSSION, // drums appear on 9 every 16 channels,
32
36
  string: `${bank}:0`
33
37
  });
34
38
  }
35
39
 
40
+ // check for xg
41
+ let system = "gs";
42
+
36
43
  function updateString(ch)
37
44
  {
45
+ const bank = chooseBank(ch.bank, ch.bankLSB, ch.drums, system === "xg");
38
46
  // check if this exists in the soundfont
39
- let exists = soundfont.getPreset(ch.bank, ch.program);
40
- ch.bank = exists.bank;
47
+ let exists = soundfont.getPreset(bank, ch.program, system === "xg");
48
+ ch.actualBank = exists.bank;
41
49
  ch.program = exists.program;
42
- ch.string = ch.bank + ":" + ch.program;
50
+ ch.string = ch.actualBank + ":" + ch.program;
43
51
  if (!usedProgramsAndKeys[ch.string])
44
52
  {
45
53
  SpessaSynthInfo(
@@ -85,8 +93,11 @@ export function getUsedProgramsAndKeys(soundfont)
85
93
  }
86
94
 
87
95
  const ports = mid.midiPorts.slice();
88
- // check for xg
89
- let system = "gs";
96
+ // initialize
97
+ channelPresets.forEach(c =>
98
+ {
99
+ updateString(c);
100
+ });
90
101
  while (remainingTracks > 0)
91
102
  {
92
103
  let trackNum = findFirstEventIndex();
@@ -124,7 +135,8 @@ export function getUsedProgramsAndKeys(soundfont)
124
135
  break;
125
136
 
126
137
  case messageTypes.controllerChange:
127
- if (event.messageData[0] !== midiControllers.bankSelect)
138
+ const isLSB = event.messageData[0] === midiControllers.lsbForControl0BankSelect;
139
+ if (event.messageData[0] !== midiControllers.bankSelect && !isLSB)
128
140
  {
129
141
  // we only care about bank select
130
142
  continue;
@@ -136,24 +148,43 @@ export function getUsedProgramsAndKeys(soundfont)
136
148
  }
137
149
  const bank = event.messageData[1];
138
150
  const realBank = Math.max(0, bank - mid.bankOffset);
139
- if (system === "xg")
151
+ if (isLSB)
140
152
  {
141
- // check for xg drums
142
- const drumsBool = bank === 120 || bank === 126 || bank === 127;
143
- if (drumsBool !== ch.drums)
144
- {
153
+ ch.bankLSB = realBank;
154
+ }
155
+ else
156
+ {
157
+ ch.bank = realBank;
158
+ }
159
+ // interpret the bank
160
+ const intepretation = parseBankSelect(
161
+ ch.bank,
162
+ realBank,
163
+ system,
164
+ isLSB,
165
+ ch.drums,
166
+ channel
167
+ );
168
+ switch (intepretation.drumsStatus)
169
+ {
170
+ case 0:
171
+ // no change
172
+ break;
173
+
174
+ case 1:
175
+ // drums changed to off
145
176
  // drum change is a program change
146
- ch.drums = drumsBool;
147
- ch.bank = ch.drums ? 128 : realBank;
177
+ ch.drums = false;
148
178
  updateString(ch);
149
- }
150
- else
151
- {
152
- ch.bank = ch.drums ? 128 : realBank;
153
- }
154
- continue;
179
+ break;
180
+
181
+ case 2:
182
+ // drums changed to on
183
+ // drum change is a program change
184
+ ch.drums = true;
185
+ updateString(ch);
186
+ break;
155
187
  }
156
- channelPresets[channel].bank = realBank;
157
188
  // do not update the data, bank change doesn't change the preset
158
189
  break;
159
190
 
@@ -163,31 +194,21 @@ export function getUsedProgramsAndKeys(soundfont)
163
194
  // that's a note off
164
195
  continue;
165
196
  }
166
- updateString(ch);
167
197
  usedProgramsAndKeys[ch.string].add(`${event.messageData[0]}-${event.messageData[1]}`);
168
198
  break;
169
199
 
170
200
  case messageTypes.systemExclusive:
171
201
  // check for drum sysex
172
- if (
173
- event.messageData[0] !== 0x41 || // roland
174
- event.messageData[2] !== 0x42 || // GS
175
- event.messageData[3] !== 0x12 || // GS
176
- event.messageData[4] !== 0x40 || // system parameter
177
- (event.messageData[5] & 0x10) === 0 || // part parameter
178
- event.messageData[6] !== 0x15 // drum pars
179
-
180
- )
202
+ if (!isGSDrumsOn(event))
181
203
  {
182
204
  // check for XG
183
- if (
184
- event.messageData[0] === 0x43 && // yamaha
185
- event.messageData[2] === 0x4C && // sXG ON
186
- event.messageData[5] === 0x7E &&
187
- event.messageData[6] === 0x00
188
- )
205
+ if (isXGOn(event))
189
206
  {
190
207
  system = "xg";
208
+ SpessaSynthInfo(
209
+ "%cXG on detected!",
210
+ consoleColors.recognized
211
+ );
191
212
  }
192
213
  continue;
193
214
  }
@@ -195,7 +216,6 @@ export function getUsedProgramsAndKeys(soundfont)
195
216
  const isDrum = !!(event.messageData[7] > 0 && event.messageData[5] >> 4);
196
217
  ch = channelPresets[sysexChannel];
197
218
  ch.drums = isDrum;
198
- ch.bank = isDrum ? 128 : 0;
199
219
  updateString(ch);
200
220
  break;
201
221
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spessasynth_lib",
3
- "version": "3.25.0",
3
+ "version": "3.25.2",
4
4
  "description": "MIDI and SoundFont2/DLS library with no compromises",
5
5
  "browser": "index.js",
6
6
  "type": "module",
@@ -1,4 +1,3 @@
1
- import { MIDI } from "../midi_parser/midi_loader.js";
2
1
  import { Synthetizer } from "../synthetizer/synthetizer.js";
3
2
  import { messageTypes } from "../midi_parser/midi_message.js";
4
3
  import { workletMessageType } from "../synthetizer/worklet_system/message_protocol/worklet_message.js";
@@ -9,6 +9,7 @@
9
9
  */
10
10
  import { generatorTypes } from "./generator.js";
11
11
  import { Modulator } from "./modulator.js";
12
+ import { isXGDrums } from "../../utils/xg_hacks.js";
12
13
 
13
14
  export class BasicPreset
14
15
  {
@@ -78,6 +79,17 @@ export class BasicPreset
78
79
  this.defaultModulators = modulators;
79
80
  }
80
81
 
82
+ /**
83
+ * @param allowXG {boolean}
84
+ * @param allowSFX {boolean}
85
+ * @returns {boolean}
86
+ */
87
+ isDrumPreset(allowXG, allowSFX = false)
88
+ {
89
+ // sfx is not cool
90
+ return this.bank === 128 || (allowXG && isXGDrums(this.bank) && (this.bank !== 126 || allowSFX));
91
+ }
92
+
81
93
  deletePreset()
82
94
  {
83
95
  this.presetZones.forEach(z => z.deleteZone());