spessasynth_core 3.26.0 → 3.26.1

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/README.md CHANGED
@@ -1,4 +1,7 @@
1
- # spessasynth_core
1
+ <!--suppress HtmlDeprecatedAttribute, HtmlRequiredAltAttribute, HtmlExtraClosingTag -->
2
+ <p align='center'>
3
+ <img src='https://raw.githubusercontent.com/spessasus/SpessaSynth/refs/heads/master/src/website/spessasynth_logo_rounded.png' width='300' alt='SpessaSynth logo'>
4
+ </p>
2
5
 
3
6
  **A powerful SF2/DLS/MIDI JavaScript library. It works with any modern JS environment that supports WebAssembly.**
4
7
 
@@ -11,12 +14,12 @@ npm install --save spessasynth_core
11
14
 
12
15
  ### [Project site (consider giving it a star!)](https://github.com/spessasus/SpessaSynth)
13
16
 
14
- ### [Demo (using the spessasynth_lib wrapper)](https://spessasus.github.io/spessasynth_core)
17
+ ### [Demo (using the spessasynth_lib wrapper)](https://spessasus.github.io/SpessaSynth)
15
18
 
16
19
  ### [Documentation (in progress!)](https://github.com/spessasus/spessasynth_core/wiki/Home)
17
20
 
18
21
 
19
- > Note: This is the new heart of the spessasynth_core library, after the repository has been split.
22
+ > Note: This is the new heart of the SpessaSynth library, after the repository has been split.
20
23
 
21
24
  ###
22
25
  ```js
@@ -84,9 +87,9 @@ process.exit();
84
87
 
85
88
  ### Easy Integration
86
89
  - **Modular design:** *Easy integration into other projects (load what you need)*
87
- - **[Detailed documentation:](https://github.com/spessasus/spessasynth_core/wiki/Home)** *With [examples!](https://github.com/spessasus/spessasynth_core/wiki/Usage-As-Library#examples)*
90
+ - **[Detailed documentation:](https://github.com/spessasus/spessasynth_core/wiki/Home)** *With [examples!](https://github.com/spessasus/spessasynth_core/wiki/Getting-Started#examples)*
88
91
  - **Flexible:** *It's not just a MIDI player!*
89
- - **Easy to Use:** *Basic setup is just [two lines of code!](https://github.com/spessasus/spessasynth_core/wiki/Usage-As-Library#minimal-setup)*
92
+ - **Easy to Use:** *Basic setup is just [two lines of code!](https://github.com/spessasus/spessasynth_core/wiki/Getting-Started#minimal-setup)*
90
93
  - **No dependencies:** *Batteries included!*
91
94
 
92
95
  ### Powerful MIDI Synthesizer
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spessasynth_core",
3
- "version": "3.26.0",
3
+ "version": "3.26.1",
4
4
  "description": "MIDI and SoundFont2/DLS library with no compromises",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -8,7 +8,7 @@ import { isGSDrumsOn, isXGOn } from "../../utils/sysex_detector.js";
8
8
  /**
9
9
  * Gets the used programs and keys for this MIDI file with a given sound bank
10
10
  * @this {BasicMIDI}
11
- * @param soundfont {BasicSoundBank|WorkletSoundfontManager} - the sound bank
11
+ * @param soundfont {BasicSoundBank|SoundFontManager} - the sound bank
12
12
  * @returns {Object<string, Set<string>>} Object<bank:program, Set<key-velocity>>
13
13
  */
14
14
  export function getUsedProgramsAndKeys(soundfont)
@@ -160,6 +160,6 @@ export function _addNewMidiPort()
160
160
  {
161
161
  for (let i = 0; i < 16; i++)
162
162
  {
163
- this.synth.createWorkletChannel(true);
163
+ this.synth.createMidiChannel(true);
164
164
  }
165
165
  }
@@ -42,6 +42,7 @@ export class KeyModifierManager
42
42
  {
43
43
  /**
44
44
  * The velocity override mappings for MIDI keys
45
+ * stored as [channelNumber][midiNote]
45
46
  * @type {KeyModifier[][]}
46
47
  * @private
47
48
  */
@@ -22,7 +22,7 @@ export const FILTER_SMOOTHING_FACTOR = 0.1;
22
22
  * @property {number} a4 - Filter coefficient 5
23
23
  */
24
24
 
25
- export class WorkletLowpassFilter
25
+ export class LowpassFilter
26
26
  {
27
27
  /**
28
28
  * Cached coefficient calculations
@@ -175,7 +175,7 @@ export class WorkletLowpassFilter
175
175
  {
176
176
  filter.lastTargetCutoff = targetCutoff;
177
177
  filter.resonanceCb = modulatedResonance;
178
- WorkletLowpassFilter.calculateCoefficients(filter, targetCutoff);
178
+ LowpassFilter.calculateCoefficients(filter, targetCutoff);
179
179
  }
180
180
 
181
181
  // filter the input
@@ -200,7 +200,7 @@ export class WorkletLowpassFilter
200
200
  }
201
201
 
202
202
  /**
203
- * @param filter {WorkletLowpassFilter}
203
+ * @param filter {LowpassFilter}
204
204
  * @param cutoffCents {number}
205
205
  */
206
206
  static calculateCoefficients(filter, cutoffCents)
@@ -208,7 +208,7 @@ export class WorkletLowpassFilter
208
208
  cutoffCents = ~~cutoffCents; // Math.floor
209
209
  const qCb = filter.resonanceCb;
210
210
  // check if these coefficients were already cached
211
- const cached = WorkletLowpassFilter.cachedCoefficients?.[qCb]?.[cutoffCents];
211
+ const cached = LowpassFilter.cachedCoefficients?.[qCb]?.[cutoffCents];
212
212
  if (cached !== undefined)
213
213
  {
214
214
  filter.a0 = cached.a0;
@@ -263,20 +263,20 @@ export class WorkletLowpassFilter
263
263
  filter.a3 = toCache.a3;
264
264
  filter.a4 = toCache.a4;
265
265
 
266
- if (WorkletLowpassFilter.cachedCoefficients[qCb] === undefined)
266
+ if (LowpassFilter.cachedCoefficients[qCb] === undefined)
267
267
  {
268
- WorkletLowpassFilter.cachedCoefficients[qCb] = [];
268
+ LowpassFilter.cachedCoefficients[qCb] = [];
269
269
  }
270
- WorkletLowpassFilter.cachedCoefficients[qCb][cutoffCents] = toCache;
270
+ LowpassFilter.cachedCoefficients[qCb][cutoffCents] = toCache;
271
271
  }
272
272
  }
273
273
 
274
274
  // precompute all the cutoffs for 0q (most common)
275
- const dummy = new WorkletLowpassFilter(44100);
275
+ const dummy = new LowpassFilter(44100);
276
276
  dummy.resonanceCb = 0;
277
277
  // sfspec section 8.1.3: initialFilterFc ranges from 1500 to 13,500 cents
278
278
  for (let i = 1500; i < 13500; i++)
279
279
  {
280
280
  dummy.currentInitialFc = i;
281
- WorkletLowpassFilter.calculateCoefficients(dummy, i);
281
+ LowpassFilter.calculateCoefficients(dummy, i);
282
282
  }
@@ -9,10 +9,10 @@ import { isXGDrums } from "../../../utils/xg_hacks.js";
9
9
  * @property {number} bankOffset - the soundfont's bank offset
10
10
  */
11
11
 
12
- export class WorkletSoundfontManager
12
+ export class SoundFontManager
13
13
  {
14
14
  /**
15
- * Creates a new instance of worklet soundfont manager (worklet scope)
15
+ * Creates a new instance of soundfont manager
16
16
  * @param initialSoundFontBuffer {ArrayBuffer} Array buffer of the soundfont. This soudfont always has the id "main"
17
17
  */
18
18
  constructor(initialSoundFontBuffer)
@@ -7,8 +7,8 @@ import { generatorTypes } from "../../../soundfont/basic_soundfont/generator.js"
7
7
 
8
8
  export const PAN_SMOOTHING_FACTOR = 0.05;
9
9
 
10
- export const WORKLET_SYSTEM_REVERB_DIVIDER = 4600;
11
- export const WORKLET_SYSTEM_CHORUS_DIVIDER = 2000;
10
+ export const REVERB_DIVIDER = 4600;
11
+ export const CHORUS_DIVIDER = 2000;
12
12
  const HALF_PI = Math.PI / 2;
13
13
 
14
14
  const MIN_PAN = -500;
@@ -78,7 +78,7 @@ export function panVoice(voice,
78
78
  if (reverbSend > 0)
79
79
  {
80
80
  // reverb is mono so we need to multiply by gain
81
- const reverbGain = this.synth.reverbGain * gain * (reverbSend / WORKLET_SYSTEM_REVERB_DIVIDER);
81
+ const reverbGain = this.synth.reverbGain * gain * (reverbSend / REVERB_DIVIDER);
82
82
  for (let i = 0; i < inputBuffer.length; i++)
83
83
  {
84
84
  reverbLeft[i] += reverbGain * inputBuffer[i];
@@ -91,7 +91,7 @@ export function panVoice(voice,
91
91
  if (chorusSend > 0)
92
92
  {
93
93
  // chorus is stereo so we do not need to
94
- const chorusGain = this.synth.chorusGain * chorusSend / WORKLET_SYSTEM_CHORUS_DIVIDER;
94
+ const chorusGain = this.synth.chorusGain * chorusSend / CHORUS_DIVIDER;
95
95
  const chorusLeftGain = gainLeft * chorusGain;
96
96
  const chorusRightGain = gainRight * chorusGain;
97
97
  for (let i = 0; i < inputBuffer.length; i++)
@@ -1,11 +1,10 @@
1
1
  /**
2
2
  * voice.js
3
- * purpose: prepares Voices from sample and generator data and manages sample dumping
4
- * note: sample dumping means sending it over to the AudioWorkletGlobalScope
3
+ * purpose: prepares Voices from sample and generator data
5
4
  */
6
5
  import { MIN_EXCLUSIVE_LENGTH, MIN_NOTE_LENGTH } from "../main_processor.js";
7
6
  import { SpessaSynthWarn } from "../../../utils/loggin.js";
8
- import { WorkletLowpassFilter } from "./lowpass_filter.js";
7
+ import { LowpassFilter } from "./lowpass_filter.js";
9
8
  import { VolumeEnvelope } from "./volume_envelope.js";
10
9
  import { ModulationEnvelope } from "./modulation_envelope.js";
11
10
  import { addAndClampGenerator, generatorTypes } from "../../../soundfont/basic_soundfont/generator.js";
@@ -122,7 +121,7 @@ class Voice
122
121
 
123
122
  /**
124
123
  * Lowpass filter applied to the voice.
125
- * @type {WorkletLowpassFilter}
124
+ * @type {LowpassFilter}
126
125
  */
127
126
  filter;
128
127
 
@@ -270,7 +269,7 @@ class Voice
270
269
  /**
271
270
  * Creates a Voice
272
271
  * @param sampleRate {number}
273
- * @param workletSample {AudioSample}
272
+ * @param audioSample {AudioSample}
274
273
  * @param midiNote {number}
275
274
  * @param velocity {number}
276
275
  * @param channel {number}
@@ -282,7 +281,7 @@ class Voice
282
281
  */
283
282
  constructor(
284
283
  sampleRate,
285
- workletSample,
284
+ audioSample,
286
285
  midiNote,
287
286
  velocity,
288
287
  channel,
@@ -293,12 +292,12 @@ class Voice
293
292
  modulators
294
293
  )
295
294
  {
296
- this.sample = workletSample;
295
+ this.sample = audioSample;
297
296
  this.generators = generators;
298
297
  this.exclusiveClass = this.generators[generatorTypes.exclusiveClass];
299
298
  this.modulatedGenerators = new Int16Array(generators);
300
299
  this.modulators = modulators;
301
- this.filter = new WorkletLowpassFilter(sampleRate);
300
+ this.filter = new LowpassFilter(sampleRate);
302
301
 
303
302
  this.velocity = velocity;
304
303
  this.midiNote = midiNote;
@@ -461,11 +460,11 @@ export function getVoices(channel,
461
460
  let loopEnd = sampleAndGenerators.sample.sampleLoopEndIndex;
462
461
  let loopingMode = generators[generatorTypes.sampleModes];
463
462
  /**
464
- * create the worklet sample
463
+ * create the sample
465
464
  * offsets are calculated at note on time (to allow for modulation of them)
466
465
  * @type {AudioSample}
467
466
  */
468
- const workletSample = new AudioSample(
467
+ const audioSample = new AudioSample(
469
468
  sampleAndGenerators.sample.sampleData,
470
469
  (sampleAndGenerators.sample.sampleRate / this.sampleRate) * Math.pow(
471
470
  2,
@@ -492,14 +491,14 @@ export function getVoices(channel,
492
491
  // Velocity: velocity,
493
492
  // TargetKey: targetKey,
494
493
  // MidiNote: midiNote,
495
- // AudioSample: workletSample
494
+ // AudioSample: audioSample
496
495
  // }]);
497
496
 
498
497
 
499
498
  voices.push(
500
499
  new Voice(
501
500
  this.sampleRate,
502
- workletSample,
501
+ audioSample,
503
502
  midiNote,
504
503
  velocity,
505
504
  channel,
@@ -5,7 +5,7 @@ import { customControllers } from "../engine_components/controller_tables.js";
5
5
  import { absCentsToHz, timecentsToSeconds } from "../engine_components/unit_converter.js";
6
6
  import { getLFOValue } from "../engine_components/lfo.js";
7
7
  import { WavetableOscillator } from "../engine_components/wavetable_oscillator.js";
8
- import { WorkletLowpassFilter } from "../engine_components/lowpass_filter.js";
8
+ import { LowpassFilter } from "../engine_components/lowpass_filter.js";
9
9
  import { interpolationTypes } from "../engine_components/enums.js";
10
10
 
11
11
  /**
@@ -181,7 +181,7 @@ export function renderVoice(
181
181
  }
182
182
 
183
183
  // low pass filter
184
- WorkletLowpassFilter.apply(voice, bufferOut, lowpassExcursion, this.synth.filterSmoothingFactor);
184
+ LowpassFilter.apply(voice, bufferOut, lowpassExcursion, this.synth.filterSmoothingFactor);
185
185
 
186
186
  // vol env
187
187
  VolumeEnvelope.apply(voice, bufferOut, modLfoCentibels, this.synth.volumeEnvelopeSmoothingFactor);
@@ -1,7 +1,7 @@
1
1
  import { customControllers } from "../../engine_components/controller_tables.js";
2
2
 
3
3
  /**
4
- * Sets the worklet's primary tuning
4
+ * Sets the synth's primary tuning
5
5
  * @this {SpessaSynthProcessor}
6
6
  * @param cents {number}
7
7
  */
@@ -12,7 +12,7 @@ import { VOLUME_ENVELOPE_SMOOTHING_FACTOR } from "./engine_components/volume_env
12
12
  import { systemExclusive } from "./engine_methods/system_exclusive.js";
13
13
  import { masterParameterType, setMasterParameter } from "./engine_methods/controller_control/master_parameters.js";
14
14
  import { resetAllControllers } from "./engine_methods/controller_control/reset_controllers.js";
15
- import { WorkletSoundfontManager } from "./engine_components/soundfont_manager.js";
15
+ import { SoundFontManager } from "./engine_components/soundfont_manager.js";
16
16
  import { KeyModifierManager } from "./engine_components/key_modifier_manager.js";
17
17
  import { getVoices } from "./engine_components/voice.js";
18
18
  import { PAN_SMOOTHING_FACTOR } from "./engine_components/stereo_panner.js";
@@ -314,7 +314,7 @@ class SpessaSynthProcessor
314
314
  defaultDrumsUsesOverride = false;
315
315
 
316
316
  /**
317
- * Controls if the worklet processor is fully initialized
317
+ * Controls if the processor is fully initialized
318
318
  * @type {Promise<boolean>}
319
319
  */
320
320
  processorInitialized = stbvorbis.isInitialized;
@@ -340,7 +340,7 @@ class SpessaSynthProcessor
340
340
  */
341
341
 
342
342
  /**
343
- * Creates a new worklet synthesis system. contains all channels
343
+ * Creates a new synthesizer engine.
344
344
  * @param midiChannels {number}
345
345
  * @param soundfont {ArrayBuffer}
346
346
  * @param enableEventSystem {boolean}
@@ -380,12 +380,6 @@ class SpessaSynthProcessor
380
380
  this.currentSynthTime = initialTime;
381
381
  this.sampleRate = sampleRate;
382
382
 
383
- this.isFullyReady = false;
384
- this.processorInitialized.then(() =>
385
- {
386
- this.isFullyReady = true;
387
- });
388
-
389
383
  /**
390
384
  * Sample time in seconds
391
385
  * @type {number}
@@ -401,9 +395,9 @@ class SpessaSynthProcessor
401
395
  }
402
396
 
403
397
  /**
404
- * @type {WorkletSoundfontManager}
398
+ * @type {SoundFontManager}
405
399
  */
406
- this.soundfontManager = new WorkletSoundfontManager(
400
+ this.soundfontManager = new SoundFontManager(
407
401
  soundfont
408
402
  );
409
403
  this.sendPresetList();
@@ -412,7 +406,7 @@ class SpessaSynthProcessor
412
406
 
413
407
  for (let i = 0; i < initialChannelCount; i++)
414
408
  {
415
- this.createWorkletChannel(false);
409
+ this.createMidiChannel(false);
416
410
  }
417
411
 
418
412
  this.midiAudioChannels[DEFAULT_PERCUSSION].preset = this.drumPreset;
@@ -764,7 +758,7 @@ class SpessaSynthProcessor
764
758
  }
765
759
 
766
760
  /**
767
- * Calls synth event from the worklet side
761
+ * Calls synth event
768
762
  * @param eventName {EventTypes} the event name
769
763
  * @param eventData {EventCallbackData}
770
764
  * @this {SpessaSynthProcessor}
@@ -785,7 +779,7 @@ SpessaSynthProcessor.prototype.systemExclusive = systemExclusive;
785
779
 
786
780
  // channel related
787
781
  SpessaSynthProcessor.prototype.stopAllChannels = stopAllChannels;
788
- SpessaSynthProcessor.prototype.createWorkletChannel = createMidiChannel;
782
+ SpessaSynthProcessor.prototype.createMidiChannel = createMidiChannel;
789
783
  SpessaSynthProcessor.prototype.resetAllControllers = resetAllControllers;
790
784
 
791
785
  // master parameter related
@@ -104,13 +104,13 @@ export class ChannelSnapshot
104
104
 
105
105
  /**
106
106
  * Creates a snapshot of a single channel's state in the synthesizer.
107
- * @param workletProcessor {SpessaSynthProcessor}
107
+ * @param spessaSynthProcessor {SpessaSynthProcessor}
108
108
  * @param channelNumber {number}
109
109
  * @returns {ChannelSnapshot}
110
110
  */
111
- static getChannelSnapshot(workletProcessor, channelNumber)
111
+ static getChannelSnapshot(spessaSynthProcessor, channelNumber)
112
112
  {
113
- const channelObject = workletProcessor.midiAudioChannels[channelNumber];
113
+ const channelObject = spessaSynthProcessor.midiAudioChannels[channelNumber];
114
114
  const channelSnapshot = new ChannelSnapshot();
115
115
  // program data
116
116
  channelSnapshot.program = channelObject.preset.program;
@@ -142,13 +142,13 @@ export class ChannelSnapshot
142
142
 
143
143
  /**
144
144
  * Applies the snapshot to the specified channel.
145
- * @param workletProcessor {SpessaSynthProcessor}
145
+ * @param spessaSynthProcessor {SpessaSynthProcessor}
146
146
  * @param channelNumber {number}
147
147
  * @param channelSnapshot {ChannelSnapshot}
148
148
  */
149
- static applyChannelSnapshot(workletProcessor, channelNumber, channelSnapshot)
149
+ static applyChannelSnapshot(spessaSynthProcessor, channelNumber, channelSnapshot)
150
150
  {
151
- const channelObject = workletProcessor.midiAudioChannels[channelNumber];
151
+ const channelObject = spessaSynthProcessor.midiAudioChannels[channelNumber];
152
152
  channelObject.muteChannel(channelSnapshot.isMuted);
153
153
  channelObject.setDrums(channelSnapshot.drumChannel);
154
154
 
@@ -53,27 +53,27 @@ export class SynthesizerSnapshot
53
53
 
54
54
  /**
55
55
  * Creates a snapshot of the synthesizer's state.
56
- * @param workletProcessor {SpessaSynthProcessor}
56
+ * @param spessaSynthProcessor {SpessaSynthProcessor}
57
57
  * @returns {SynthesizerSnapshot}
58
58
  */
59
- static createSynthesizerSnapshot(workletProcessor)
59
+ static createSynthesizerSnapshot(spessaSynthProcessor)
60
60
  {
61
61
  const snapshot = new SynthesizerSnapshot();
62
62
  // channel snapshots
63
63
  snapshot.channelSnapshots =
64
- workletProcessor.midiAudioChannels.map((_, i) =>
65
- ChannelSnapshot.getChannelSnapshot(workletProcessor, i));
64
+ spessaSynthProcessor.midiAudioChannels.map((_, i) =>
65
+ ChannelSnapshot.getChannelSnapshot(spessaSynthProcessor, i));
66
66
 
67
67
  // key mappings
68
- snapshot.keyMappings = workletProcessor.keyModifierManager.getMappings();
68
+ snapshot.keyMappings = spessaSynthProcessor.keyModifierManager.getMappings();
69
69
  // pan and volume
70
- snapshot.mainVolume = workletProcessor.midiVolume;
71
- snapshot.pan = workletProcessor.pan;
70
+ snapshot.mainVolume = spessaSynthProcessor.midiVolume;
71
+ snapshot.pan = spessaSynthProcessor.pan;
72
72
 
73
73
  // others
74
- snapshot.system = workletProcessor.system;
75
- snapshot.interpolation = workletProcessor.interpolationType;
76
- snapshot.transposition = workletProcessor.transposition;
74
+ snapshot.system = spessaSynthProcessor.system;
75
+ snapshot.interpolation = spessaSynthProcessor.interpolationType;
76
+ snapshot.transposition = spessaSynthProcessor.transposition;
77
77
 
78
78
  // effect config is stored on the main thread, leave it empty
79
79
  snapshot.effectsConfig = {};
@@ -83,31 +83,31 @@ export class SynthesizerSnapshot
83
83
 
84
84
  /**
85
85
  * Applies the snapshot to the synthesizer.
86
- * @param workletProcessor {SpessaSynthProcessor}
86
+ * @param spessaSynthProcessor {SpessaSynthProcessor}
87
87
  * @param snapshot {SynthesizerSnapshot}
88
88
  */
89
- static applySnapshot(workletProcessor, snapshot)
89
+ static applySnapshot(spessaSynthProcessor, snapshot)
90
90
  {
91
91
  // restore system
92
- workletProcessor.setSystem(snapshot.system);
92
+ spessaSynthProcessor.setSystem(snapshot.system);
93
93
 
94
94
  // restore pan and volume
95
- workletProcessor.setMasterParameter(masterParameterType.mainVolume, snapshot.mainVolume);
96
- workletProcessor.setMasterParameter(masterParameterType.masterPan, snapshot.pan);
97
- workletProcessor.transposeAllChannels(snapshot.transposition);
98
- workletProcessor.interpolationType = snapshot.interpolation;
99
- workletProcessor.keyModifierManager.setMappings(snapshot.keyMappings);
95
+ spessaSynthProcessor.setMasterParameter(masterParameterType.mainVolume, snapshot.mainVolume);
96
+ spessaSynthProcessor.setMasterParameter(masterParameterType.masterPan, snapshot.pan);
97
+ spessaSynthProcessor.transposeAllChannels(snapshot.transposition);
98
+ spessaSynthProcessor.interpolationType = snapshot.interpolation;
99
+ spessaSynthProcessor.keyModifierManager.setMappings(snapshot.keyMappings);
100
100
 
101
101
  // add channels if more needed
102
- while (workletProcessor.midiAudioChannels.length < snapshot.channelSnapshots.length)
102
+ while (spessaSynthProcessor.midiAudioChannels.length < snapshot.channelSnapshots.length)
103
103
  {
104
- workletProcessor.createWorkletChannel();
104
+ spessaSynthProcessor.createMidiChannel();
105
105
  }
106
106
 
107
107
  // restore channels
108
108
  snapshot.channelSnapshots.forEach((channelSnapshot, index) =>
109
109
  {
110
- ChannelSnapshot.applyChannelSnapshot(workletProcessor, index, channelSnapshot);
110
+ ChannelSnapshot.applyChannelSnapshot(spessaSynthProcessor, index, channelSnapshot);
111
111
  });
112
112
 
113
113
  SpessaSynthInfo("%cFinished restoring controllers!", consoleColors.info);