spessasynth_core 3.26.4 → 3.26.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spessasynth_core",
3
- "version": "3.26.4",
3
+ "version": "3.26.5",
4
4
  "description": "MIDI and SoundFont2/DLS library with no compromises",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -4,11 +4,12 @@ import { messageTypes, midiControllers } from "../midi_message.js";
4
4
  import { DEFAULT_PERCUSSION } from "../../synthetizer/synth_constants.js";
5
5
  import { chooseBank, isSystemXG, parseBankSelect } from "../../utils/xg_hacks.js";
6
6
  import { isGSDrumsOn, isXGOn } from "../../utils/sysex_detector.js";
7
+ import { SoundFontManager } from "../../synthetizer/audio_engine/engine_components/soundfont_manager.js";
7
8
 
8
9
  /**
9
10
  * Gets the used programs and keys for this MIDI file with a given sound bank
10
11
  * @this {BasicMIDI}
11
- * @param soundfont {BasicSoundBank|SoundFontManager} - the sound bank
12
+ * @param soundfont {SoundFontManager|BasicSoundBank} - the sound bank
12
13
  * @returns {Object<string, Set<string>>} Object<bank:program, Set<key-velocity>>
13
14
  */
14
15
  export function getUsedProgramsAndKeys(soundfont)
@@ -44,9 +45,27 @@ export function getUsedProgramsAndKeys(soundfont)
44
45
  {
45
46
  const bank = chooseBank(ch.bank, ch.bankLSB, ch.drums, isSystemXG(system));
46
47
  // check if this exists in the soundfont
47
- let exists = soundfont.getPreset(bank, ch.program, isSystemXG(system));
48
- ch.actualBank = exists.bank;
49
- ch.program = exists.program;
48
+ let existsBank, existsProgram;
49
+ if (soundfont instanceof SoundFontManager)
50
+ {
51
+ /**
52
+ * @type {{preset: BasicPreset, bankOffset: number}}
53
+ */
54
+ let exists = soundfont.getPreset(bank, ch.program, isSystemXG(system));
55
+ existsBank = exists.preset.bank + exists.bankOffset;
56
+ existsProgram = exists.preset.program;
57
+ }
58
+ else
59
+ {
60
+ /**
61
+ * @type {BasicPreset}
62
+ */
63
+ let exists = soundfont.getPreset(bank, ch.program, isSystemXG(system));
64
+ existsBank = exists.bank;
65
+ existsProgram = exists.program;
66
+ }
67
+ ch.actualBank = existsBank;
68
+ ch.program = existsProgram;
50
69
  ch.string = ch.actualBank + ":" + ch.program;
51
70
  if (!usedProgramsAndKeys[ch.string])
52
71
  {
@@ -147,19 +166,18 @@ export function getUsedProgramsAndKeys(soundfont)
147
166
  continue;
148
167
  }
149
168
  const bank = event.messageData[1];
150
- const realBank = Math.max(0, bank - mid.bankOffset);
151
169
  if (isLSB)
152
170
  {
153
- ch.bankLSB = realBank;
171
+ ch.bankLSB = bank;
154
172
  }
155
173
  else
156
174
  {
157
- ch.bank = realBank;
175
+ ch.bank = bank;
158
176
  }
159
177
  // interpret the bank
160
178
  const intepretation = parseBankSelect(
161
179
  ch.bank,
162
- realBank,
180
+ bank,
163
181
  system,
164
182
  isLSB,
165
183
  ch.drums,
@@ -57,41 +57,36 @@ export function loadNewSequence(parsedMidi, autoPlay = true)
57
57
  */
58
58
  this.midiData = parsedMidi;
59
59
 
60
+ // clear old embedded bank if exists
61
+ this.synth.clearEmbeddedBank();
62
+
60
63
  // check for embedded soundfont
61
64
  if (this.midiData.embeddedSoundFont !== undefined)
62
65
  {
63
66
  SpessaSynthInfo("%cEmbedded soundfont detected! Using it.", consoleColors.recognized);
64
67
  this.synth.setEmbeddedSoundFont(this.midiData.embeddedSoundFont, this.midiData.bankOffset);
65
68
  }
66
- else
69
+
70
+ SpessaSynthGroupCollapsed("%cPreloading samples...", consoleColors.info);
71
+ // smart preloading: load only samples used in the midi!
72
+ const used = this.midiData.getUsedProgramsAndKeys(this.synth.soundfontManager);
73
+ for (const [programBank, combos] of Object.entries(used))
67
74
  {
68
- if (this.synth.overrideSoundfont)
69
- {
70
- // clean up the embedded soundfont
71
- this.synth.clearSoundFont(true, true);
72
- }
73
- SpessaSynthGroupCollapsed("%cPreloading samples...", consoleColors.info);
74
- // smart preloading: load only samples used in the midi!
75
- const used = this.midiData.getUsedProgramsAndKeys(this.synth.soundfontManager);
76
- for (const [programBank, combos] of Object.entries(used))
75
+ const [bank, program] = programBank.split(":").map(Number);
76
+ const preset = this.synth.getPreset(bank, program);
77
+ SpessaSynthInfo(
78
+ `%cPreloading used samples on %c${preset.presetName}%c...`,
79
+ consoleColors.info,
80
+ consoleColors.recognized,
81
+ consoleColors.info
82
+ );
83
+ for (const combo of combos)
77
84
  {
78
- const bank = parseInt(programBank.split(":")[0]);
79
- const program = parseInt(programBank.split(":")[1]);
80
- const preset = this.synth.getPreset(bank, program);
81
- SpessaSynthInfo(
82
- `%cPreloading used samples on %c${preset.presetName}%c...`,
83
- consoleColors.info,
84
- consoleColors.recognized,
85
- consoleColors.info
86
- );
87
- for (const combo of combos)
88
- {
89
- const split = combo.split("-");
90
- preset.preloadSpecific(parseInt(split[0]), parseInt(split[1]));
91
- }
85
+ const [midiNote, velocity] = combo.split("-").map(Number);
86
+ this.synth.getVoicesForPreset(preset, bank, program, midiNote, velocity, midiNote);
92
87
  }
93
- SpessaSynthGroupEnd();
94
88
  }
89
+ SpessaSynthGroupEnd();
95
90
 
96
91
  /**
97
92
  * the midi track data
@@ -44,12 +44,6 @@ export class BasicPreset
44
44
  */
45
45
  presetZones = [];
46
46
 
47
- /**
48
- * Stores already found getSamplesAndGenerators for reuse
49
- * @type {SampleAndGenerators[][][]}
50
- */
51
- foundSamplesAndGenerators = [];
52
-
53
47
  /**
54
48
  * unused metadata
55
49
  * @type {number}
@@ -73,15 +67,6 @@ export class BasicPreset
73
67
  constructor(parentSoundBank)
74
68
  {
75
69
  this.parentSoundBank = parentSoundBank;
76
- for (let i = 0; i < 128; i++)
77
- {
78
- this.foundSamplesAndGenerators[i] = [];
79
- }
80
- }
81
-
82
- clearCache()
83
- {
84
- this.foundSamplesAndGenerators = [];
85
70
  }
86
71
 
87
72
  /**
@@ -136,34 +121,13 @@ export class BasicPreset
136
121
  }
137
122
 
138
123
  /**
139
- * Preloads a specific key/velocity combo
140
- * @param key {number}
141
- * @param velocity {number}
142
- */
143
- preloadSpecific(key, velocity)
144
- {
145
- this.getSamplesAndGenerators(key, velocity).forEach(samandgen =>
146
- {
147
- if (!samandgen.sample.isSampleLoaded)
148
- {
149
- samandgen.sample.getAudioData();
150
- }
151
- });
152
- }
153
-
154
- /**
155
- * Returns generatorTranslator and generators for given note
124
+ * Returns samples and generators for given note
156
125
  * @param midiNote {number}
157
126
  * @param velocity {number}
158
127
  * @returns {SampleAndGenerators[]}
159
128
  */
160
129
  getSamplesAndGenerators(midiNote, velocity)
161
130
  {
162
- const memorized = this.foundSamplesAndGenerators[midiNote][velocity];
163
- if (memorized)
164
- {
165
- return memorized;
166
- }
167
131
 
168
132
  if (this.presetZones.length < 1)
169
133
  {
@@ -333,9 +297,6 @@ export class BasicPreset
333
297
  });
334
298
  });
335
299
  });
336
-
337
- // save and return
338
- this.foundSamplesAndGenerators[midiNote][velocity] = parsedGeneratorsAndSamples;
339
300
  return parsedGeneratorsAndSamples;
340
301
  }
341
302
  }
@@ -361,7 +361,6 @@ class MidiAudioChannel
361
361
  {
362
362
  this.drumChannel = false;
363
363
  }
364
- this.presetUsesOverride = false;
365
364
  this.synth.callEvent("drumchange", {
366
365
  channel: this.channelNumber,
367
366
  isDrumChannel: this.drumChannel
@@ -1,5 +1,6 @@
1
- import { SpessaSynthWarn } from "../../../utils/loggin.js";
1
+ import { SpessaSynthInfo, SpessaSynthWarn } from "../../../utils/loggin.js";
2
2
  import { isXGDrums } from "../../../utils/xg_hacks.js";
3
+ import { EMBEDDED_SOUND_BANK_ID } from "../../synth_constants.js";
3
4
 
4
5
  /**
5
6
  * @typedef {Object} SoundFontType
@@ -47,7 +48,8 @@ export class SoundFontManager
47
48
  const presets = new Set();
48
49
  for (const p of font.soundfont.presets)
49
50
  {
50
- const presetString = `${p.bank + font.bankOffset}-${p.program}`;
51
+ const bank = Math.min(128, p.bank + font.bankOffset);
52
+ const presetString = `${bank}-${p.program}`;
51
53
  if (presets.has(presetString))
52
54
  {
53
55
  continue;
@@ -86,11 +88,8 @@ export class SoundFontManager
86
88
  */
87
89
  reloadManager(soundFont)
88
90
  {
89
- /**
90
- * All the soundfonts, ordered from the most important to the least.
91
- * @type {SoundFontType[]}
92
- */
93
- this.soundfontList = [];
91
+ // do not clear the embedded bank
92
+ this.soundfontList = this.soundfontList.filter(sf => sf.id === EMBEDDED_SOUND_BANK_ID);
94
93
  this.soundfontList.push({
95
94
  id: "main",
96
95
  bankOffset: 0,
@@ -114,7 +113,7 @@ export class SoundFontManager
114
113
  const index = this.soundfontList.findIndex(s => s.id === id);
115
114
  if (index === -1)
116
115
  {
117
- SpessaSynthWarn(`No soundfont with id of "${id}" found. Aborting!`);
116
+ SpessaSynthInfo(`No soundfont with id of "${id}" found. Aborting!`);
118
117
  return;
119
118
  }
120
119
  delete this.soundfontList[index].soundfont.presets;
@@ -145,6 +144,15 @@ export class SoundFontManager
145
144
  this.generatePresetList();
146
145
  }
147
146
 
147
+ /**
148
+ * Gets the current soundfont order
149
+ * @returns {string[]}
150
+ */
151
+ getCurrentSoundFontOrder()
152
+ {
153
+ return this.soundfontList.map(s => s.id);
154
+ }
155
+
148
156
  // noinspection JSUnusedGlobalSymbols
149
157
  /**
150
158
  * Rearranges the soundfonts
@@ -163,7 +171,7 @@ export class SoundFontManager
163
171
  * @param bankNumber {number}
164
172
  * @param programNumber {number}
165
173
  * @param allowXGDrums {boolean} if true, allows XG drum banks (120, 126 and 127) as drum preset
166
- * @returns {BasicPreset} the preset
174
+ * @returns {{preset: BasicPreset, bankOffset: number}} the preset and its bank offset
167
175
  */
168
176
  getPreset(bankNumber, programNumber, allowXGDrums = false)
169
177
  {
@@ -171,21 +179,24 @@ export class SoundFontManager
171
179
  {
172
180
  throw new Error("No soundfonts! Did you forget to add one?");
173
181
  }
182
+ const isDrum = bankNumber === 128 || (allowXGDrums && isXGDrums(bankNumber));
174
183
  for (const sf of this.soundfontList)
175
184
  {
176
185
  // check for the preset (with given offset)
177
186
  const preset = sf.soundfont.getPresetNoFallback(
178
- bankNumber - sf.bankOffset,
187
+ bankNumber === 128 ? 128 : bankNumber - sf.bankOffset,
179
188
  programNumber,
180
189
  allowXGDrums
181
190
  );
182
191
  if (preset !== undefined)
183
192
  {
184
- return preset;
193
+ return {
194
+ preset: preset,
195
+ bankOffset: sf.bankOffset
196
+ };
185
197
  }
186
198
  // if not found, advance to the next soundfont
187
199
  }
188
- const isDrum = bankNumber === 128 || (allowXGDrums && isXGDrums(bankNumber));
189
200
  // if none found, return the first correct preset found
190
201
  if (!isDrum)
191
202
  {
@@ -195,11 +206,18 @@ export class SoundFontManager
195
206
  allowXGDrums));
196
207
  if (preset)
197
208
  {
198
- return preset;
209
+ return {
210
+ preset: preset,
211
+ bankOffset: sf.bankOffset
212
+ };
199
213
  }
200
214
  }
201
215
  // if nothing at all, use the first preset
202
- return this.soundfontList[0].soundfont.presets[0];
216
+ const sf = this.soundfontList[0];
217
+ return {
218
+ preset: sf.soundfont.presets[0],
219
+ bankOffset: sf.bankOffset
220
+ };
203
221
  }
204
222
  else
205
223
  {
@@ -209,17 +227,27 @@ export class SoundFontManager
209
227
  const p = sf.soundfont.presets.find(p => p.isDrumPreset(allowXGDrums) && p.program === programNumber);
210
228
  if (p)
211
229
  {
212
- return p;
230
+ return {
231
+ preset: p,
232
+ bankOffset: sf.bankOffset
233
+ };
213
234
  }
214
235
  // check for any drum preset
215
236
  const preset = sf.soundfont.presets.find(p => p.isDrumPreset(allowXGDrums));
216
237
  if (preset)
217
238
  {
218
- return preset;
239
+ return {
240
+ preset: preset,
241
+ bankOffset: sf.bankOffset
242
+ };
219
243
  }
220
244
  }
221
245
  // if nothing at all, use the first preset
222
- return this.soundfontList[0].soundfont.presets[0];
246
+ const sf = this.soundfontList[0];
247
+ return {
248
+ preset: sf.soundfont.presets[0],
249
+ bankOffset: sf.bankOffset
250
+ };
223
251
  }
224
252
  }
225
253
 
@@ -9,7 +9,6 @@ import { VolumeEnvelope } from "./volume_envelope.js";
9
9
  import { ModulationEnvelope } from "./modulation_envelope.js";
10
10
  import { addAndClampGenerator, generatorTypes } from "../../../soundfont/basic_soundfont/generator.js";
11
11
  import { Modulator } from "../../../soundfont/basic_soundfont/modulator.js";
12
- import { isSystemXG } from "../../../utils/xg_hacks.js";
13
12
 
14
13
  const EXCLUSIVE_CUTOFF_TIME = -2320;
15
14
  const EXCLUSIVE_MOD_CUTOFF_TIME = -1130; // less because filter shenanigans
@@ -162,12 +161,6 @@ class Voice
162
161
  */
163
162
  isInRelease = false;
164
163
 
165
- /**
166
- * MIDI channel number.
167
- * @type {number}
168
- */
169
- channelNumber = 0;
170
-
171
164
  /**
172
165
  * Velocity of the note.
173
166
  * @type {number}
@@ -272,7 +265,6 @@ class Voice
272
265
  * @param audioSample {AudioSample}
273
266
  * @param midiNote {number}
274
267
  * @param velocity {number}
275
- * @param channel {number}
276
268
  * @param currentTime {number}
277
269
  * @param targetKey {number}
278
270
  * @param realKey {number}
@@ -284,7 +276,6 @@ class Voice
284
276
  audioSample,
285
277
  midiNote,
286
278
  velocity,
287
- channel,
288
279
  currentTime,
289
280
  targetKey,
290
281
  realKey,
@@ -301,7 +292,6 @@ class Voice
301
292
 
302
293
  this.velocity = velocity;
303
294
  this.midiNote = midiNote;
304
- this.channelNumber = channel;
305
295
  this.startTime = currentTime;
306
296
  this.targetKey = targetKey;
307
297
  this.realKey = realKey;
@@ -333,7 +323,6 @@ class Voice
333
323
  sample,
334
324
  voice.midiNote,
335
325
  voice.velocity,
336
- voice.channelNumber,
337
326
  currentTime,
338
327
  voice.targetKey,
339
328
  realKey,
@@ -372,53 +361,21 @@ class Voice
372
361
  }
373
362
 
374
363
  /**
375
- * @param channel {number} a hint for the processor to recalculate sample cursors when sample dumping
364
+ * @param preset {BasicPreset} the preset to get voices for
365
+ * @param bank {number} the bank to cache the voices in
366
+ * @param program {number} program to cache the voices in
376
367
  * @param midiNote {number} the MIDI note to use
377
368
  * @param velocity {number} the velocity to use
378
369
  * @param realKey {number} the real MIDI note if the "midiNote" was changed by MIDI Tuning Standard
379
370
  * @this {SpessaSynthProcessor}
380
371
  * @returns {Voice[]} output is an array of Voices
381
372
  */
382
- export function getVoices(channel,
383
- midiNote,
384
- velocity,
385
- realKey)
373
+ export function getVoicesForPreset(preset, bank, program, midiNote, velocity, realKey)
386
374
  {
387
375
  /**
388
376
  * @type {Voice[]}
389
377
  */
390
- let voices;
391
- const channelObject = this.midiAudioChannels[channel];
392
-
393
- // override patch
394
- const overridePatch = this.keyModifierManager.hasOverridePatch(channel, midiNote);
395
-
396
- let bank = channelObject.getBankSelect();
397
- let program = channelObject.preset.program;
398
- if (overridePatch)
399
- {
400
- const override = this.keyModifierManager.getPatch(channel, midiNote);
401
- bank = override.bank;
402
- program = override.program;
403
- }
404
-
405
- const cached = this.getCachedVoice(bank, program, midiNote, velocity);
406
- // if cached, return it!
407
- if (cached !== undefined)
408
- {
409
- return cached.map(v => Voice.copy(v, this.currentSynthTime, realKey));
410
- }
411
-
412
- // not cached...
413
- let preset = channelObject.preset;
414
- if (overridePatch)
415
- {
416
- preset = this.soundfontManager.getPreset(bank, program, isSystemXG(this.system));
417
- }
418
- /**
419
- * @returns {Voice[]}
420
- */
421
- voices = preset.getSamplesAndGenerators(midiNote, velocity)
378
+ const voices = preset.getSamplesAndGenerators(midiNote, velocity)
422
379
  .reduce((voices, sampleAndGenerators) =>
423
380
  {
424
381
  if (sampleAndGenerators.sample.getAudioData() === undefined)
@@ -501,7 +458,6 @@ export function getVoices(channel,
501
458
  audioSample,
502
459
  midiNote,
503
460
  velocity,
504
- channel,
505
461
  this.currentSynthTime,
506
462
  targetKey,
507
463
  realKey,
@@ -515,4 +471,44 @@ export function getVoices(channel,
515
471
  this.setCachedVoice(bank, program, midiNote, velocity, voices.map(v =>
516
472
  Voice.copy(v, this.currentSynthTime, realKey)));
517
473
  return voices;
474
+ }
475
+
476
+ /**
477
+ * @param channel {number} a hint for the processor to recalculate sample cursors when sample dumping
478
+ * @param midiNote {number} the MIDI note to use
479
+ * @param velocity {number} the velocity to use
480
+ * @param realKey {number} the real MIDI note if the "midiNote" was changed by MIDI Tuning Standard
481
+ * @this {SpessaSynthProcessor}
482
+ * @returns {Voice[]} output is an array of Voices
483
+ */
484
+ export function getVoices(channel, midiNote, velocity, realKey)
485
+ {
486
+ const channelObject = this.midiAudioChannels[channel];
487
+
488
+ // override patch
489
+ const overridePatch = this.keyModifierManager.hasOverridePatch(channel, midiNote);
490
+
491
+ let bank = channelObject.getBankSelect();
492
+ let program = channelObject.preset.program;
493
+ if (overridePatch)
494
+ {
495
+ const override = this.keyModifierManager.getPatch(channel, midiNote);
496
+ bank = override.bank;
497
+ program = override.program;
498
+ }
499
+
500
+ const cached = this.getCachedVoice(bank, program, midiNote, velocity);
501
+ // if cached, return it!
502
+ if (cached !== undefined)
503
+ {
504
+ return cached.map(v => Voice.copy(v, this.currentSynthTime, realKey));
505
+ }
506
+
507
+ // not cached...
508
+ let preset = channelObject.preset;
509
+ if (overridePatch)
510
+ {
511
+ preset = this.getPreset(bank, program);
512
+ }
513
+ return this.getVoicesForPreset(preset, bank, program, midiNote, velocity, realKey);
518
514
  }
@@ -43,7 +43,6 @@ export function resetAllControllers(log = true)
43
43
  if (channelNumber % 16 === DEFAULT_PERCUSSION)
44
44
  {
45
45
  ch.setPreset(this.drumPreset);
46
- ch.presetUsesOverride = this.defaultDrumsUsesOverride;
47
46
  ch.drumChannel = true;
48
47
  this.callEvent("drumchange", {
49
48
  channel: channelNumber,
@@ -53,7 +52,6 @@ export function resetAllControllers(log = true)
53
52
  else
54
53
  {
55
54
  ch.drumChannel = false;
56
- ch.presetUsesOverride = this.defaultDrumsUsesOverride;
57
55
  ch.setPreset(this.defaultPreset);
58
56
  this.callEvent("drumchange", {
59
57
  channel: channelNumber,
@@ -70,13 +68,11 @@ export function resetAllControllers(log = true)
70
68
  }
71
69
 
72
70
  const presetBank = ch.preset.bank;
73
- const sentBank = presetBank === 128 ? 128 : (ch.presetUsesOverride ? presetBank + this.soundfontBankOffset : presetBank);
74
-
75
71
  // call program change
76
72
  this.callEvent("programchange", {
77
73
  channel: channelNumber,
78
74
  program: ch.preset.program,
79
- bank: sentBank
75
+ bank: presetBank
80
76
  });
81
77
 
82
78
  for (let ccNum = 0; ccNum < 128; ccNum++)
@@ -11,51 +11,16 @@ export function programChange(programNumber)
11
11
  }
12
12
  // always 128 for percussion
13
13
  let bank = this.getBankSelect();
14
- let sentBank;
15
- /**
16
- * @type {BasicPreset}
17
- */
18
- let preset;
19
14
 
20
15
  const isXG = this.isXGChannel;
21
- // check if override
22
- if (this.synth.overrideSoundfont)
23
- {
24
- const bankWithOffset = bank === 128 ? 128 : bank - this.synth.soundfontBankOffset;
25
- const p = this.synth.overrideSoundfont.getPresetNoFallback(
26
- bankWithOffset,
27
- programNumber,
28
- isXG
29
- );
30
- if (p)
31
- {
32
- sentBank = p.bank === 128 ? 128 : p.bank + this.synth.soundfontBankOffset;
33
- preset = p;
34
- this.presetUsesOverride = true;
35
- }
36
- else
37
- {
38
- preset = this.synth.soundfontManager.getPreset(bank, programNumber, isXG);
39
- const offset = this.synth.soundfontManager.soundfontList
40
- .find(s => s.soundfont === preset.parentSoundBank).bankOffset;
41
- sentBank = preset.bank - offset;
42
- this.presetUsesOverride = false;
43
- }
44
- }
45
- else
46
- {
47
- preset = this.synth.soundfontManager.getPreset(bank, programNumber, isXG);
48
- const offset = this.synth.soundfontManager.soundfontList
49
- .find(s => s.soundfont === preset.parentSoundBank).bankOffset;
50
- sentBank = preset.bank - offset;
51
- this.presetUsesOverride = false;
52
- }
16
+ const p = this.synth.soundfontManager.getPreset(bank, programNumber, isXG);
17
+ const preset = p.preset;
53
18
  this.setPreset(preset);
54
- this.sentBank = sentBank;
19
+ this.sentBank = Math.min(128, preset.bank + p.bankOffset);
55
20
  this.synth.callEvent("programchange", {
56
21
  channel: this.channelNumber,
57
22
  program: preset.program,
58
- bank: sentBank
23
+ bank: this.sentBank
59
24
  });
60
25
  this.sendChannelProperty();
61
26
  }
@@ -0,0 +1,43 @@
1
+ import { loadSoundFont } from "../../../../soundfont/load_soundfont.js";
2
+ import { SpessaSynthInfo } from "../../../../utils/loggin.js";
3
+ import { consoleColors } from "../../../../utils/other.js";
4
+ import { EMBEDDED_SOUND_BANK_ID } from "../../../synth_constants.js";
5
+
6
+ /**
7
+ * @this {SpessaSynthProcessor}
8
+ */
9
+ export function clearEmbeddedBank()
10
+ {
11
+ if (this.soundfontManager.soundfontList.some(s => s.id === EMBEDDED_SOUND_BANK_ID))
12
+ {
13
+ this.soundfontManager.deleteSoundFont(EMBEDDED_SOUND_BANK_ID);
14
+ }
15
+ }
16
+
17
+ /**
18
+ * Sets the embedded (RMI soundfont)
19
+ * @param font {ArrayBuffer}
20
+ * @param offset {number}
21
+ * @this {SpessaSynthProcessor}
22
+ */
23
+ export function setEmbeddedSoundFont(font, offset)
24
+ {
25
+ // the embedded bank is set as the first bank in the manager,
26
+ // with a special ID that does not clear when reloadManager is performed.
27
+ this.soundfontBankOffset = offset;
28
+ const loadedFont = loadSoundFont(font);
29
+ this.soundfontManager.addNewSoundFont(loadedFont, EMBEDDED_SOUND_BANK_ID, offset);
30
+ // rearrange so the embedded is first (most important as it overrides all others)
31
+ const order = this.soundfontManager.getCurrentSoundFontOrder();
32
+ order.pop();
33
+ order.unshift(EMBEDDED_SOUND_BANK_ID);
34
+ this.soundfontManager.rearrangeSoundFonts(order);
35
+
36
+
37
+ // apply snapshot again if applicable
38
+ if (this._snapshot !== undefined)
39
+ {
40
+ this.applySynthesizerSnapshot(this._snapshot);
41
+ }
42
+ SpessaSynthInfo(`%cEmbedded sound bank set at offset %c${offset}`, consoleColors.recognized, consoleColors.value);
43
+ }
@@ -1,22 +0,0 @@
1
- import { isSystemXG } from "../../../../utils/xg_hacks.js";
2
-
3
- /**
4
- * @this {SpessaSynthProcessor}
5
- * @param program {number}
6
- * @param bank {number}
7
- * @returns {BasicPreset}
8
- */
9
- export function getPreset(bank, program)
10
- {
11
- if (this.overrideSoundfont)
12
- {
13
- // if override soundfont
14
- const bankWithOffset = bank === 128 ? 128 : bank - this.soundfontBankOffset;
15
- const preset = this.overrideSoundfont.getPresetNoFallback(bankWithOffset, program, isSystemXG(this.system));
16
- if (preset)
17
- {
18
- return preset;
19
- }
20
- }
21
- return this.soundfontManager.getPreset(bank, program, isSystemXG(this.system));
22
- }
@@ -7,28 +7,13 @@ export function updatePresetList()
7
7
  * @type {{bank: number, presetName: string, program: number}[]}
8
8
  */
9
9
  const mainFont = this.soundfontManager.getPresetList();
10
- if (this.overrideSoundfont !== undefined)
11
- {
12
- this.overrideSoundfont.presets.forEach(p =>
13
- {
14
- const bankCheck = p.bank === 128 ? 128 : p.bank + this.soundfontBankOffset;
15
- const exists = mainFont.find(pr => pr.bank === bankCheck && pr.program === p.program);
16
- if (exists !== undefined)
17
- {
18
- exists.presetName = p.presetName;
19
- }
20
- else
21
- {
22
- mainFont.push({
23
- presetName: p.presetName,
24
- bank: bankCheck,
25
- program: p.program
26
- });
27
- }
28
- });
29
- }
30
10
  this.clearCache();
31
11
  this.callEvent("presetlistchange", mainFont);
32
12
  this.getDefaultPresets();
13
+ // unlock presets
14
+ this.midiAudioChannels.forEach(c =>
15
+ {
16
+ c.setPresetLock(false);
17
+ });
33
18
  this.resetAllControllers(false);
34
19
  }
@@ -9,13 +9,11 @@ import { masterParameterType, setMasterParameter } from "./engine_methods/contro
9
9
  import { resetAllControllers } from "./engine_methods/controller_control/reset_controllers.js";
10
10
  import { SoundFontManager } from "./engine_components/soundfont_manager.js";
11
11
  import { KeyModifierManager } from "./engine_components/key_modifier_manager.js";
12
- import { getVoices } from "./engine_components/voice.js";
12
+ import { getVoices, getVoicesForPreset } from "./engine_components/voice.js";
13
13
  import { PAN_SMOOTHING_FACTOR } from "./engine_components/stereo_panner.js";
14
14
  import { stopAllChannels } from "./engine_methods/stopping_notes/stop_all_channels.js";
15
- import { setEmbeddedSoundFont } from "./engine_methods/soundfont_management/set_embedded_sound_font.js";
16
- import { clearSoundFont } from "./engine_methods/soundfont_management/clear_sound_font.js";
15
+ import { clearEmbeddedBank, setEmbeddedSoundFont } from "./engine_methods/soundfont_management/embedded_sound_bank.js";
17
16
  import { updatePresetList } from "./engine_methods/soundfont_management/update_preset_list.js";
18
- import { getPreset } from "./engine_methods/soundfont_management/get_preset.js";
19
17
  import { transposeAllChannels } from "./engine_methods/tuning_control/transpose_all_channels.js";
20
18
  import { setMasterTuning } from "./engine_methods/tuning_control/set_master_tuning.js";
21
19
  import { applySynthesizerSnapshot } from "./snapshot/apply_synthesizer_snapshot.js";
@@ -26,6 +24,7 @@ import { IndexedByteArray } from "../../utils/indexed_array.js";
26
24
  import { interpolationTypes } from "./engine_components/enums.js";
27
25
  import { DEFAULT_SYNTH_OPTIONS } from "./synth_processor_options.js";
28
26
  import { fillWithDefaults } from "../../utils/fill_with_defaults.js";
27
+ import { isSystemXG } from "../../utils/xg_hacks.js";
29
28
 
30
29
 
31
30
  /**
@@ -169,7 +168,7 @@ class SpessaSynthProcessor
169
168
  /**
170
169
  * Cached voices for all presets for this synthesizer.
171
170
  * Nesting goes like this:
172
- * this.cachedVoices[bankNumber][programNumber][midiNote][velocity] = a list of Voices for that.
171
+ * this.cachedVoices[bankNumber][programNumber][midiNote][velocity] = a list of voices for that.
173
172
  * @type {Voice[][][][][]}
174
173
  */
175
174
  cachedVoices = [];
@@ -211,7 +210,6 @@ class SpessaSynthProcessor
211
210
  */
212
211
  soundfontBankOffset = 0;
213
212
 
214
-
215
213
  /**
216
214
  * The volume gain, set by user
217
215
  * @type {number}
@@ -270,12 +268,6 @@ class SpessaSynthProcessor
270
268
  */
271
269
  keyModifierManager = new KeyModifierManager();
272
270
 
273
- /**
274
- * Overrides the main soundfont (embedded, for example)
275
- * @type {BasicSoundBank}
276
- */
277
- overrideSoundfont = undefined;
278
-
279
271
  /**
280
272
  * contains all the channels with their voices on the processor size
281
273
  * @type {MidiAudioChannel[]}
@@ -299,16 +291,12 @@ class SpessaSynthProcessor
299
291
  */
300
292
  defaultPreset;
301
293
 
302
- defaultPresetUsesOverride = false;
303
-
304
294
  /**
305
295
  * Synth's default (reset) drum preset
306
296
  * @type {BasicPreset}
307
297
  */
308
298
  drumPreset;
309
299
 
310
- defaultDrumsUsesOverride = false;
311
-
312
300
  /**
313
301
  * Controls if the processor is fully initialized
314
302
  * @type {Promise<boolean>}
@@ -431,10 +419,8 @@ class SpessaSynthProcessor
431
419
  const sys = this.system;
432
420
  this.system = "xg";
433
421
  this.defaultPreset = this.getPreset(0, 0);
434
- this.defaultPresetUsesOverride = this.overrideSoundfont?.presets?.indexOf(this.defaultPreset) >= 0;
435
422
  this.system = sys;
436
423
  this.drumPreset = this.getPreset(128, 0);
437
- this.defaultDrumsUsesOverride = this.overrideSoundfont?.presets?.indexOf(this.drumPreset) >= 0;
438
424
  }
439
425
 
440
426
  /**
@@ -750,11 +736,22 @@ class SpessaSynthProcessor
750
736
  {
751
737
  this.cachedVoices = [];
752
738
  }
739
+
740
+ /**
741
+ * @param program {number}
742
+ * @param bank {number}
743
+ * @returns {BasicPreset}
744
+ */
745
+ getPreset(bank, program)
746
+ {
747
+ return this.soundfontManager.getPreset(bank, program, isSystemXG(this.system)).preset;
748
+ }
753
749
  }
754
750
 
755
751
  // include other methods
756
752
  // voice related
757
753
  SpessaSynthProcessor.prototype.voiceKilling = voiceKilling;
754
+ SpessaSynthProcessor.prototype.getVoicesForPreset = getVoicesForPreset;
758
755
  SpessaSynthProcessor.prototype.getVoices = getVoices;
759
756
 
760
757
  // system-exclusive related
@@ -773,8 +770,7 @@ SpessaSynthProcessor.prototype.transposeAllChannels = transposeAllChannels;
773
770
  SpessaSynthProcessor.prototype.setMasterTuning = setMasterTuning;
774
771
 
775
772
  // program related
776
- SpessaSynthProcessor.prototype.getPreset = getPreset;
777
- SpessaSynthProcessor.prototype.clearSoundFont = clearSoundFont;
773
+ SpessaSynthProcessor.prototype.clearEmbeddedBank = clearEmbeddedBank;
778
774
  SpessaSynthProcessor.prototype.setEmbeddedSoundFont = setEmbeddedSoundFont;
779
775
  SpessaSynthProcessor.prototype.updatePresetList = updatePresetList;
780
776
 
@@ -19,4 +19,6 @@ export const MIDI_CHANNEL_COUNT = 16;
19
19
  */
20
20
  export const DEFAULT_SYNTH_MODE = "gs";
21
21
 
22
- export const ALL_CHANNELS_OR_DIFFERENT_ACTION = -1;
22
+ export const ALL_CHANNELS_OR_DIFFERENT_ACTION = -1;
23
+
24
+ export const EMBEDDED_SOUND_BANK_ID = `SPESSASYNTH_EMBEDDED_BANK_${Math.random()}`;
@@ -1,32 +0,0 @@
1
- /**
2
- * @this {SpessaSynthProcessor}
3
- * @param sendPresets {boolean}
4
- * @param clearOverride {boolean}
5
- */
6
- export function clearSoundFont(sendPresets = true, clearOverride = true)
7
- {
8
- this.stopAllChannels(true);
9
- if (clearOverride)
10
- {
11
- delete this.overrideSoundfont;
12
- this.overrideSoundfont = undefined;
13
- }
14
- this.getDefaultPresets();
15
- this.clearCache();
16
-
17
- if (sendPresets)
18
- {
19
- this.updatePresetList();
20
- }
21
-
22
- for (let i = 0; i < this.midiAudioChannels.length; i++)
23
- {
24
- const channelObject = this.midiAudioChannels[i];
25
- if (!clearOverride || (clearOverride && channelObject.presetUsesOverride))
26
- {
27
- channelObject.setPresetLock(false);
28
- }
29
- channelObject.programChange(channelObject.preset.program);
30
- }
31
-
32
- }
@@ -1,33 +0,0 @@
1
- import { loadSoundFont } from "../../../../soundfont/load_soundfont.js";
2
- import { SpessaSynthInfo } from "../../../../utils/loggin.js";
3
- import { consoleColors } from "../../../../utils/other.js";
4
-
5
- /**
6
- * Sets the embedded (RMI soundfont)
7
- * @param font {ArrayBuffer}
8
- * @param offset {number}
9
- * @this {SpessaSynthProcessor}
10
- */
11
- export function setEmbeddedSoundFont(font, offset)
12
- {
13
- // set offset
14
- this.soundfontBankOffset = offset;
15
- this.clearSoundFont(false, true);
16
- this.overrideSoundfont = loadSoundFont(font);
17
- this.updatePresetList();
18
- this.getDefaultPresets();
19
- this.midiAudioChannels.forEach(c =>
20
- c.programChange(c.preset.program)
21
- );
22
- // preload all samples
23
- this.overrideSoundfont.samples.forEach(s => s.getAudioData());
24
-
25
-
26
- // apply snapshot again if applicable
27
- if (this._snapshot !== undefined)
28
- {
29
- this.applySynthesizerSnapshot(this._snapshot);
30
- this.resetAllControllers();
31
- }
32
- SpessaSynthInfo("%cSpessaSynth is ready!", consoleColors.recognized);
33
- }