spessasynth_core 3.26.0 → 3.26.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 (22) hide show
  1. package/README.md +71 -67
  2. package/package.json +1 -1
  3. package/src/midi/midi_tools/used_keys_loaded.js +1 -1
  4. package/src/sequencer/process_event.js +1 -1
  5. package/src/synthetizer/audio_engine/engine_components/key_modifier_manager.js +1 -0
  6. package/src/synthetizer/audio_engine/engine_components/lowpass_filter.js +9 -9
  7. package/src/synthetizer/audio_engine/engine_components/midi_audio_channel.js +6 -1
  8. package/src/synthetizer/audio_engine/engine_components/soundfont_manager.js +32 -19
  9. package/src/synthetizer/audio_engine/engine_components/stereo_panner.js +4 -4
  10. package/src/synthetizer/audio_engine/engine_components/voice.js +11 -12
  11. package/src/synthetizer/audio_engine/engine_methods/create_midi_channel.js +1 -8
  12. package/src/synthetizer/audio_engine/engine_methods/render_voice.js +2 -2
  13. package/src/synthetizer/audio_engine/engine_methods/soundfont_management/clear_sound_font.js +6 -4
  14. package/src/synthetizer/audio_engine/engine_methods/soundfont_management/set_embedded_sound_font.js +13 -1
  15. package/src/synthetizer/audio_engine/engine_methods/soundfont_management/{send_preset_list.js → update_preset_list.js} +3 -1
  16. package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_master_tuning.js +1 -1
  17. package/src/synthetizer/audio_engine/main_processor.js +78 -103
  18. package/src/synthetizer/audio_engine/snapshot/apply_synthesizer_snapshot.js +1 -0
  19. package/src/synthetizer/audio_engine/snapshot/channel_snapshot.js +6 -6
  20. package/src/synthetizer/audio_engine/snapshot/synthesizer_snapshot.js +21 -21
  21. package/src/synthetizer/audio_engine/synth_processor_options.js +18 -0
  22. package/src/synthetizer/audio_engine/engine_methods/soundfont_management/reload_sound_font.js +0 -28
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,82 +14,20 @@ 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.
20
-
21
- ###
22
- ```js
23
- import * as fs from "node:fs";
24
- import { MIDI } from "../../src/midi/midi_loader.js";
25
- import { SpessaSynthProcessor } from "../../src/synthetizer/audio_engine/main_processor.js";
26
- import { SpessaSynthSequencer } from "../../src/sequencer/sequencer_engine.js";
27
- import { audioToWav } from "../../src/utils/buffer_to_wav.js";
28
-
29
- // process arguments
30
- const args = process.argv.slice(2);
31
- if (args.length !== 3)
32
- {
33
- console.log("Usage: node index.js <soundfont path> <midi path> <wav output path>");
34
- process.exit();
35
- }
36
- // load the files
37
- const sf = fs.readFileSync(args[0]);
38
- const mid = fs.readFileSync(args[1]);
39
-
40
- const midi = new MIDI(mid);
41
- const sampleRate = 44100;
42
- const sampleCount = 44100 * (midi.duration + 2);
43
-
44
- const synth = new SpessaSynthProcessor(
45
- sf,
46
- sampleRate,
47
- {},
48
- false,
49
- false
50
- );
51
- await synth.processorInitialized;
52
-
53
- const seq = new SpessaSynthSequencer(synth);
54
- seq.loadNewSongList([midi]);
55
- seq.loop = false;
56
-
57
- const outLeft = new Float32Array(sampleCount);
58
- const outRight = new Float32Array(sampleCount);
59
- const start = performance.now();
60
- let filledSamples = 0;
61
-
62
- const bufSize = 128;
63
- while (filledSamples + bufSize < sampleCount)
64
- {
65
- const bufLeft = new Float32Array(bufSize);
66
- const bufRight = new Float32Array(bufSize);
67
- seq.processTick();
68
- const arr = [bufLeft, bufRight];
69
- synth.renderAudio(arr, arr, arr);
70
- outLeft.set(bufLeft, filledSamples);
71
- outRight.set(bufRight, filledSamples);
72
- filledSamples += bufSize;
73
- }
74
- const wave = audioToWav({
75
- leftChannel: outLeft,
76
- rightChannel: outRight,
77
- sampleRate: sampleRate
78
- });
79
- fs.writeFileSync(args[2], new Buffer(wave));
80
- process.exit();
81
- ```
22
+ > Note: This is the new heart of the SpessaSynth library, after the repository has been split.
82
23
 
83
24
  ## Current Features
84
25
 
85
26
  ### Easy Integration
86
27
  - **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)*
28
+ - **[Detailed documentation:](https://github.com/spessasus/spessasynth_core/wiki/Home)** *With [examples!](https://github.com/spessasus/spessasynth_core/wiki/Getting-Started#examples)*
88
29
  - **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)*
30
+ - **Easy to Use:** *Basic setup is just [two lines of code!](https://github.com/spessasus/spessasynth_core/wiki/Getting-Started#minimal-setup)*
90
31
  - **No dependencies:** *Batteries included!*
91
32
 
92
33
  ### Powerful MIDI Synthesizer
@@ -205,6 +146,69 @@ process.exit();
205
146
 
206
147
  **If you like this project, consider giving it a star. It really helps out!**
207
148
 
149
+ ### Short example: MIDI to wav converter
150
+ ```js
151
+ import * as fs from "node:fs";
152
+ import { MIDI, SpessaSynthProcessor, SpessaSynthSequencer, audioToWav, loadSoundFont } from "spessasynth_core";
153
+
154
+ // process arguments
155
+ const args = process.argv.slice(2);
156
+ if (args.length !== 3)
157
+ {
158
+ console.log("Usage: node index.js <soundfont path> <midi path> <wav output path>");
159
+ process.exit();
160
+ }
161
+ const sf = fs.readFileSync(args[0]);
162
+ const mid = fs.readFileSync(args[1]);
163
+ const midi = new MIDI(mid);
164
+ const sampleRate = 44100;
165
+ const sampleCount = 44100 * (midi.duration + 2);
166
+ const synth = new SpessaSynthProcessor(sampleRate, {
167
+ enableEventSystem: false,
168
+ effectsEnabled: false
169
+ });
170
+ synth.soundfontManager.reloadManager(loadSoundFont(sf));
171
+ await synth.processorInitialized;
172
+ const seq = new SpessaSynthSequencer(synth);
173
+ seq.loadNewSongList([midi]);
174
+ seq.loop = false;
175
+ const outLeft = new Float32Array(sampleCount);
176
+ const outRight = new Float32Array(sampleCount);
177
+ const start = performance.now();
178
+ let filledSamples = 0;
179
+ // note: buffer size is recommended to be very small, as this is the interval between modulator updates and LFO updates
180
+ const bufSize = 128;
181
+ let i = 0;
182
+ while (filledSamples + bufSize < sampleCount)
183
+ {
184
+ const bufLeft = new Float32Array(bufSize);
185
+ const bufRight = new Float32Array(bufSize);
186
+ // process sequencer
187
+ seq.processTick();
188
+ const arr = [bufLeft, bufRight];
189
+ // render
190
+ synth.renderAudio(arr, arr, arr);
191
+ // write out
192
+ outLeft.set(bufLeft, filledSamples);
193
+ outRight.set(bufRight, filledSamples);
194
+ filledSamples += bufSize;
195
+ i++;
196
+ // log progress
197
+ if (i % 100 === 0)
198
+ {
199
+ console.log("Rendered", seq.currentTime, "/", midi.duration);
200
+ }
201
+ }
202
+ console.log("Rendered in", Math.floor(performance.now() - start), "ms");
203
+ const wave = audioToWav({
204
+ leftChannel: outLeft,
205
+ rightChannel: outRight,
206
+ sampleRate: sampleRate
207
+ });
208
+ fs.writeFileSync(args[2], new Buffer(wave));
209
+ process.exit();
210
+ ```
211
+
208
212
  # License
209
213
  Copyright © 2025 Spessasus
210
214
  Licensed under the Apache-2.0 License.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spessasynth_core",
3
- "version": "3.26.0",
3
+ "version": "3.26.2",
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
  }
@@ -370,6 +370,7 @@ class MidiAudioChannel
370
370
  this.sendChannelProperty();
371
371
  }
372
372
 
373
+ // noinspection JSUnusedGlobalSymbols
373
374
  /**
374
375
  * Sets a custom vibrato
375
376
  * @param depth {number} cents
@@ -387,6 +388,10 @@ class MidiAudioChannel
387
388
  this.channelVibrato.depth = depth;
388
389
  }
389
390
 
391
+ // noinspection JSUnusedGlobalSymbols
392
+ /**
393
+ * Yes
394
+ */
390
395
  disableAndLockGSNRPN()
391
396
  {
392
397
  this.lockGSNRPNParams = true;
@@ -431,7 +436,7 @@ class MidiAudioChannel
431
436
  bank: this.sentBank,
432
437
  program: this.preset.program
433
438
  };
434
- this.synth?.callbacks?.channelPropertyChange?.(data, this.channelNumber);
439
+ this.synth?.onChannelPropertyChange?.(data, this.channelNumber);
435
440
  }
436
441
  }
437
442
 
@@ -1,5 +1,4 @@
1
1
  import { SpessaSynthWarn } from "../../../utils/loggin.js";
2
- import { loadSoundFont } from "../../../soundfont/load_soundfont.js";
3
2
  import { isXGDrums } from "../../../utils/xg_hacks.js";
4
3
 
5
4
  /**
@@ -9,15 +8,24 @@ import { isXGDrums } from "../../../utils/xg_hacks.js";
9
8
  * @property {number} bankOffset - the soundfont's bank offset
10
9
  */
11
10
 
12
- export class WorkletSoundfontManager
11
+ export class SoundFontManager
13
12
  {
14
13
  /**
15
- * Creates a new instance of worklet soundfont manager (worklet scope)
16
- * @param initialSoundFontBuffer {ArrayBuffer} Array buffer of the soundfont. This soudfont always has the id "main"
14
+ * All the soundfonts, ordered from the most important to the least.
15
+ * @type {SoundFontType[]}
17
16
  */
18
- constructor(initialSoundFontBuffer)
17
+ soundfontList = [];
18
+ /**
19
+ * @type {{bank: number, presetName: string, program: number}[]}
20
+ */
21
+ presetList = [];
22
+
23
+ /**
24
+ * @param presetListChangeCallback {function} to call when stuff changes
25
+ */
26
+ constructor(presetListChangeCallback)
19
27
  {
20
- this.reloadManager(initialSoundFontBuffer);
28
+ this.presetListChangeCallback = presetListChangeCallback;
21
29
  }
22
30
 
23
31
  generatePresetList()
@@ -49,9 +57,6 @@ export class WorkletSoundfontManager
49
57
  }
50
58
  }
51
59
 
52
- /**
53
- * @type {{bank: number, presetName: string, program: number}[]}
54
- */
55
60
  this.presetList = [];
56
61
  for (const [string, name] of Object.entries(presetList))
57
62
  {
@@ -62,6 +67,7 @@ export class WorkletSoundfontManager
62
67
  bank: parseInt(pb[0])
63
68
  });
64
69
  }
70
+ this.presetListChangeCallback();
65
71
  }
66
72
 
67
73
  /**
@@ -73,13 +79,13 @@ export class WorkletSoundfontManager
73
79
  return this.presetList.slice();
74
80
  }
75
81
 
82
+ // noinspection JSUnusedGlobalSymbols
76
83
  /**
77
- * Clears all soundfonts and adds a new one
78
- * @param soundFontArrayBuffer {ArrayBuffer}
84
+ * Clears all soundfonts and adds a new one with an ID "main"
85
+ * @param soundFont {BasicSoundBank}
79
86
  */
80
- reloadManager(soundFontArrayBuffer)
87
+ reloadManager(soundFont)
81
88
  {
82
- const font = loadSoundFont(soundFontArrayBuffer);
83
89
  /**
84
90
  * All the soundfonts, ordered from the most important to the least.
85
91
  * @type {SoundFontType[]}
@@ -88,11 +94,16 @@ export class WorkletSoundfontManager
88
94
  this.soundfontList.push({
89
95
  id: "main",
90
96
  bankOffset: 0,
91
- soundfont: font
97
+ soundfont: soundFont
92
98
  });
93
99
  this.generatePresetList();
94
100
  }
95
101
 
102
+ // noinspection JSUnusedGlobalSymbols
103
+ /**
104
+ * Deletes a given soundfont.
105
+ * @param id {string}
106
+ */
96
107
  deleteSoundFont(id)
97
108
  {
98
109
  if (this.soundfontList.length === 0)
@@ -113,13 +124,14 @@ export class WorkletSoundfontManager
113
124
  this.generatePresetList();
114
125
  }
115
126
 
127
+ // noinspection JSUnusedGlobalSymbols
116
128
  /**
117
- * Adds a new soundfont buffer with a given ID
118
- * @param buffer {ArrayBuffer}
129
+ * Adds a new soundfont with a given ID
130
+ * @param font {BasicSoundBank}
119
131
  * @param id {string}
120
132
  * @param bankOffset {number}
121
133
  */
122
- addNewSoundFont(buffer, id, bankOffset)
134
+ addNewSoundFont(font, id, bankOffset)
123
135
  {
124
136
  if (this.soundfontList.find(s => s.id === id) !== undefined)
125
137
  {
@@ -127,12 +139,13 @@ export class WorkletSoundfontManager
127
139
  }
128
140
  this.soundfontList.push({
129
141
  id: id,
130
- soundfont: loadSoundFont(buffer),
142
+ soundfont: font,
131
143
  bankOffset: bankOffset
132
144
  });
133
145
  this.generatePresetList();
134
146
  }
135
147
 
148
+ // noinspection JSUnusedGlobalSymbols
136
149
  /**
137
150
  * Rearranges the soundfonts
138
151
  * @param newList {string[]} the order of soundfonts, a list of strings, first overwrites second
@@ -156,7 +169,7 @@ export class WorkletSoundfontManager
156
169
  {
157
170
  if (this.soundfontList.length < 1)
158
171
  {
159
- throw new Error("No soundfonts! This should never happen.");
172
+ throw new Error("No soundfonts! Did you forget to add one?");
160
173
  }
161
174
  for (const sf of this.soundfontList)
162
175
  {
@@ -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,
@@ -1,7 +1,5 @@
1
1
  import { MidiAudioChannel } from "../engine_components/midi_audio_channel.js";
2
2
 
3
- import { DEFAULT_PERCUSSION } from "../../synth_constants.js";
4
-
5
3
  /**
6
4
  * @param sendEvent {boolean}
7
5
  * @this {SpessaSynthProcessor}
@@ -13,15 +11,10 @@ export function createMidiChannel(sendEvent = false)
13
11
  */
14
12
  const channel = new MidiAudioChannel(this, this.defaultPreset, this.midiAudioChannels.length);
15
13
  this.midiAudioChannels.push(channel);
16
- channel.resetControllers();
17
- channel.sendChannelProperty();
18
14
  if (sendEvent)
19
15
  {
20
16
  this.callEvent("newchannel", undefined);
21
- }
22
-
23
- if (channel.channelNumber % 16 === DEFAULT_PERCUSSION)
24
- {
17
+ channel.sendChannelProperty();
25
18
  this.midiAudioChannels[this.midiAudioChannels.length - 1].setDrums(true);
26
19
  }
27
20
  }
@@ -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);
@@ -14,6 +14,11 @@ export function clearSoundFont(sendPresets = true, clearOverride = true)
14
14
  this.getDefaultPresets();
15
15
  this.cachedVoices = [];
16
16
 
17
+ if (sendPresets)
18
+ {
19
+ this.updatePresetList();
20
+ }
21
+
17
22
  for (let i = 0; i < this.midiAudioChannels.length; i++)
18
23
  {
19
24
  const channelObject = this.midiAudioChannels[i];
@@ -23,8 +28,5 @@ export function clearSoundFont(sendPresets = true, clearOverride = true)
23
28
  }
24
29
  channelObject.programChange(channelObject.preset.program);
25
30
  }
26
- if (sendPresets)
27
- {
28
- this.sendPresetList();
29
- }
31
+
30
32
  }
@@ -1,3 +1,7 @@
1
+ import { loadSoundFont } from "../../../../soundfont/load_soundfont.js";
2
+ import { SpessaSynthInfo } from "../../../../utils/loggin.js";
3
+ import { consoleColors } from "../../../../utils/other.js";
4
+
1
5
  /**
2
6
  * Sets the embedded (RMI soundfont)
3
7
  * @param font {ArrayBuffer}
@@ -8,14 +12,22 @@ export function setEmbeddedSoundFont(font, offset)
8
12
  {
9
13
  // set offset
10
14
  this.soundfontBankOffset = offset;
11
- this.reloadSoundFont(font, true);
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
+ );
12
22
  // preload all samples
13
23
  this.overrideSoundfont.samples.forEach(s => s.getAudioData());
14
24
 
25
+
15
26
  // apply snapshot again if applicable
16
27
  if (this._snapshot !== undefined)
17
28
  {
18
29
  this.applySynthesizerSnapshot(this._snapshot);
19
30
  this.resetAllControllers();
20
31
  }
32
+ SpessaSynthInfo("%cSpessaSynth is ready!", consoleColors.recognized);
21
33
  }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @this {SpessaSynthProcessor}
3
3
  */
4
- export function sendPresetList()
4
+ export function updatePresetList()
5
5
  {
6
6
  /**
7
7
  * @type {{bank: number, presetName: string, program: number}[]}
@@ -28,4 +28,6 @@ export function sendPresetList()
28
28
  });
29
29
  }
30
30
  this.callEvent("presetlistchange", mainFont);
31
+ this.getDefaultPresets();
32
+ this.resetAllControllers(false);
31
33
  }
@@ -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
  */
@@ -1,26 +1,20 @@
1
1
  import { SpessaSynthInfo } from "../../utils/loggin.js";
2
2
  import { consoleColors } from "../../utils/other.js";
3
3
  import { voiceKilling } from "./engine_methods/stopping_notes/voice_killing.js";
4
- import {
5
- ALL_CHANNELS_OR_DIFFERENT_ACTION,
6
- DEFAULT_PERCUSSION,
7
- DEFAULT_SYNTH_MODE,
8
- VOICE_CAP
9
- } from "../synth_constants.js";
4
+ import { ALL_CHANNELS_OR_DIFFERENT_ACTION, DEFAULT_SYNTH_MODE, VOICE_CAP } from "../synth_constants.js";
10
5
  import { stbvorbis } from "../../externals/stbvorbis_sync/stbvorbis_sync.min.js";
11
6
  import { VOLUME_ENVELOPE_SMOOTHING_FACTOR } from "./engine_components/volume_envelope.js";
12
7
  import { systemExclusive } from "./engine_methods/system_exclusive.js";
13
8
  import { masterParameterType, setMasterParameter } from "./engine_methods/controller_control/master_parameters.js";
14
9
  import { resetAllControllers } from "./engine_methods/controller_control/reset_controllers.js";
15
- import { WorkletSoundfontManager } from "./engine_components/soundfont_manager.js";
10
+ import { SoundFontManager } from "./engine_components/soundfont_manager.js";
16
11
  import { KeyModifierManager } from "./engine_components/key_modifier_manager.js";
17
12
  import { getVoices } from "./engine_components/voice.js";
18
13
  import { PAN_SMOOTHING_FACTOR } from "./engine_components/stereo_panner.js";
19
14
  import { stopAllChannels } from "./engine_methods/stopping_notes/stop_all_channels.js";
20
15
  import { setEmbeddedSoundFont } from "./engine_methods/soundfont_management/set_embedded_sound_font.js";
21
- import { reloadSoundFont } from "./engine_methods/soundfont_management/reload_sound_font.js";
22
16
  import { clearSoundFont } from "./engine_methods/soundfont_management/clear_sound_font.js";
23
- import { sendPresetList } from "./engine_methods/soundfont_management/send_preset_list.js";
17
+ import { updatePresetList } from "./engine_methods/soundfont_management/update_preset_list.js";
24
18
  import { getPreset } from "./engine_methods/soundfont_management/get_preset.js";
25
19
  import { transposeAllChannels } from "./engine_methods/tuning_control/transpose_all_channels.js";
26
20
  import { setMasterTuning } from "./engine_methods/tuning_control/set_master_tuning.js";
@@ -30,6 +24,8 @@ import { FILTER_SMOOTHING_FACTOR } from "./engine_components/lowpass_filter.js";
30
24
  import { getEvent, messageTypes } from "../../midi/midi_message.js";
31
25
  import { IndexedByteArray } from "../../utils/indexed_array.js";
32
26
  import { interpolationTypes } from "./engine_components/enums.js";
27
+ import { DEFAULT_SYNTH_OPTIONS } from "./synth_processor_options.js";
28
+ import { fillWithDefaults } from "../../utils/fill_with_defaults.js";
33
29
 
34
30
 
35
31
  /**
@@ -314,7 +310,7 @@ class SpessaSynthProcessor
314
310
  defaultDrumsUsesOverride = false;
315
311
 
316
312
  /**
317
- * Controls if the worklet processor is fully initialized
313
+ * Controls if the processor is fully initialized
318
314
  * @type {Promise<boolean>}
319
315
  */
320
316
  processorInitialized = stbvorbis.isInitialized;
@@ -332,67 +328,73 @@ class SpessaSynthProcessor
332
328
  sampleRate;
333
329
 
334
330
  /**
335
- * @typedef {Object} CallbacksTypedef
336
- * @property {function?} ready
337
- * @property {function(EventTypes, EventCallbackData)?} eventCall
338
- * @property {function(ChannelProperty, number)?} channelPropertyChange
339
- * @property {function(masterParameterType, number|string)?} masterParameterChange
340
- */
341
-
342
- /**
343
- * Creates a new worklet synthesis system. contains all channels
344
- * @param midiChannels {number}
345
- * @param soundfont {ArrayBuffer}
346
- * @param enableEventSystem {boolean}
347
- * @param callbacks {CallbacksTypedef}
348
- * @param sampleRate {number}
349
- * @param initialTime {number}
350
- * @param effectsEnabled {boolean}
351
- * @param snapshot {SynthesizerSnapshot}
352
- */
353
- constructor(
354
- soundfont,
355
- sampleRate,
356
- callbacks,
357
- effectsEnabled = true,
358
- enableEventSystem = true,
359
- initialTime = 0,
360
- midiChannels = 16,
361
- snapshot = undefined)
331
+ * Sample time in seconds
332
+ * @type {number}
333
+ */
334
+ sampleTime;
335
+
336
+ /**
337
+ * are the chorus and reverb effects enabled?
338
+ * @type {boolean}
339
+ */
340
+ effectsEnabled;
341
+
342
+ /**
343
+ * for applying the snapshot after an override sound bank too
344
+ * @type {SynthesizerSnapshot}
345
+ * @private
346
+ */
347
+ _snapshot;
348
+
349
+ /**
350
+ * Calls when an event occurs.
351
+ * @type {function}
352
+ * @param {EventTypes} eventType - the event type.
353
+ * @param {EventCallbackData} eventData - the event data.
354
+ */
355
+ onEventCall;
356
+
357
+ /**
358
+ * Calls when a channel property is changed.
359
+ * @type {function}
360
+ * @param {ChannelProperty} property - the updated property.
361
+ * @param {number} channelNumber - the channel number of the said property.
362
+ */
363
+ onChannelPropertyChange;
364
+
365
+ /**
366
+ * Calls when a master parameter is changed.
367
+ * @type {function}
368
+ * @param {masterParameterType} parameter - the parameter type
369
+ * @param {number|string} value - the new value.
370
+ */
371
+ onMasterParameterChange;
372
+
373
+
374
+ /**
375
+ * Creates a new synthesizer engine.
376
+ * @param sampleRate {number} - sample rate, in Hertz.
377
+ * @param options {SynthProcessorOptions} - the processor's options.
378
+ */
379
+ constructor(sampleRate,
380
+ options = DEFAULT_SYNTH_OPTIONS)
362
381
  {
382
+ options = fillWithDefaults(options, DEFAULT_SYNTH_OPTIONS);
363
383
  /**
364
384
  * Midi output count
365
385
  * @type {number}
366
386
  */
367
- this.midiOutputsCount = midiChannels;
368
- /**
369
- * are the chorus and reverb effects enabled?
370
- * @type {boolean}
371
- */
372
- this.effectsEnabled = effectsEnabled;
373
- let initialChannelCount = this.midiOutputsCount;
374
-
375
- /**
376
- * @type {CallbacksTypedef}
377
- */
378
- this.callbacks = callbacks;
379
-
380
- this.currentSynthTime = initialTime;
381
- this.sampleRate = sampleRate;
382
-
383
- this.isFullyReady = false;
384
- this.processorInitialized.then(() =>
385
- {
386
- this.isFullyReady = true;
387
- });
388
-
389
- /**
390
- * Sample time in seconds
391
- * @type {number}
392
- */
387
+ this.midiOutputsCount = options.midiChannels;
388
+ this.effectsEnabled = options.effectsEnabled;
389
+ this.enableEventSystem = options.enableEventSystem;
390
+ this.currentSynthTime = options.midiChannels;
393
391
  this.sampleTime = 1 / sampleRate;
392
+ this.sampleRate = sampleRate;
394
393
 
395
- this.enableEventSystem = enableEventSystem;
394
+ // these smoothing factors were tested on 44,100 Hz, adjust them to target sample rate here
395
+ this.volumeEnvelopeSmoothingFactor = VOLUME_ENVELOPE_SMOOTHING_FACTOR * (44100 / sampleRate);
396
+ this.panSmoothingFactor = PAN_SMOOTHING_FACTOR * (44100 / sampleRate);
397
+ this.filterSmoothingFactor = FILTER_SMOOTHING_FACTOR * (44100 / sampleRate);
396
398
 
397
399
 
398
400
  for (let i = 0; i < 128; i++)
@@ -401,34 +403,18 @@ class SpessaSynthProcessor
401
403
  }
402
404
 
403
405
  /**
404
- * @type {WorkletSoundfontManager}
406
+ * @type {SoundFontManager}
405
407
  */
406
- this.soundfontManager = new WorkletSoundfontManager(
407
- soundfont
408
- );
409
- this.sendPresetList();
410
- this.getDefaultPresets();
411
-
408
+ this.soundfontManager = new SoundFontManager(this.updatePresetList.bind(this));
412
409
 
413
- for (let i = 0; i < initialChannelCount; i++)
410
+ for (let i = 0; i < this.midiOutputsCount; i++)
414
411
  {
415
- this.createWorkletChannel(false);
412
+ this.createMidiChannel(false);
416
413
  }
417
-
418
- this.midiAudioChannels[DEFAULT_PERCUSSION].preset = this.drumPreset;
419
- this.midiAudioChannels[DEFAULT_PERCUSSION].drumChannel = true;
420
-
421
- // these smoothing factors were tested on 44,100 Hz, adjust them to target sample rate here
422
- this.volumeEnvelopeSmoothingFactor = VOLUME_ENVELOPE_SMOOTHING_FACTOR * (44100 / sampleRate);
423
- this.panSmoothingFactor = PAN_SMOOTHING_FACTOR * (44100 / sampleRate);
424
- this.filterSmoothingFactor = FILTER_SMOOTHING_FACTOR * (44100 / sampleRate);
425
-
426
- this._snapshot = snapshot;
427
- if (this?._snapshot)
414
+ this.processorInitialized.then(() =>
428
415
  {
429
- this.applySynthesizerSnapshot(snapshot);
430
- }
431
- this.postReady();
416
+ SpessaSynthInfo("%cSpessaSynth is ready!", consoleColors.recognized);
417
+ });
432
418
  }
433
419
 
434
420
  /**
@@ -457,7 +443,7 @@ class SpessaSynthProcessor
457
443
  setSystem(value)
458
444
  {
459
445
  this.system = value;
460
- this?.callbacks?.masterParameterChange?.(masterParameterType.midiSystem, this.system);
446
+ this?.onMasterParameterChange?.(masterParameterType.midiSystem, this.system);
461
447
  }
462
448
 
463
449
  /**
@@ -503,16 +489,6 @@ class SpessaSynthProcessor
503
489
  this.cachedVoices[bank][program][midiNote][velocity] = voices;
504
490
  }
505
491
 
506
- postReady()
507
- {
508
- // ensure stbvorbis is fully initialized
509
- this.processorInitialized.then(() =>
510
- {
511
- SpessaSynthInfo("%cSpessaSynth is ready!", consoleColors.recognized);
512
- this?.callbacks?.ready?.();
513
- });
514
- }
515
-
516
492
  // noinspection JSUnusedGlobalSymbols
517
493
  /**
518
494
  * Renders float32 audio data to stereo outputs; buffer size of 128 is recommended
@@ -764,14 +740,14 @@ class SpessaSynthProcessor
764
740
  }
765
741
 
766
742
  /**
767
- * Calls synth event from the worklet side
743
+ * Calls synth event
768
744
  * @param eventName {EventTypes} the event name
769
745
  * @param eventData {EventCallbackData}
770
746
  * @this {SpessaSynthProcessor}
771
747
  */
772
748
  callEvent(eventName, eventData)
773
749
  {
774
- this?.callbacks?.eventCall?.(eventName, eventData);
750
+ this?.onEventCall?.(eventName, eventData);
775
751
  }
776
752
  }
777
753
 
@@ -785,7 +761,7 @@ SpessaSynthProcessor.prototype.systemExclusive = systemExclusive;
785
761
 
786
762
  // channel related
787
763
  SpessaSynthProcessor.prototype.stopAllChannels = stopAllChannels;
788
- SpessaSynthProcessor.prototype.createWorkletChannel = createMidiChannel;
764
+ SpessaSynthProcessor.prototype.createMidiChannel = createMidiChannel;
789
765
  SpessaSynthProcessor.prototype.resetAllControllers = resetAllControllers;
790
766
 
791
767
  // master parameter related
@@ -797,10 +773,9 @@ SpessaSynthProcessor.prototype.setMasterTuning = setMasterTuning;
797
773
 
798
774
  // program related
799
775
  SpessaSynthProcessor.prototype.getPreset = getPreset;
800
- SpessaSynthProcessor.prototype.reloadSoundFont = reloadSoundFont;
801
776
  SpessaSynthProcessor.prototype.clearSoundFont = clearSoundFont;
802
777
  SpessaSynthProcessor.prototype.setEmbeddedSoundFont = setEmbeddedSoundFont;
803
- SpessaSynthProcessor.prototype.sendPresetList = sendPresetList;
778
+ SpessaSynthProcessor.prototype.updatePresetList = updatePresetList;
804
779
 
805
780
  // snapshot related
806
781
  SpessaSynthProcessor.prototype.applySynthesizerSnapshot = applySynthesizerSnapshot;
@@ -9,6 +9,7 @@ import { SynthesizerSnapshot } from "./synthesizer_snapshot.js";
9
9
  */
10
10
  export function applySynthesizerSnapshot(snapshot)
11
11
  {
12
+ this._snapshot = snapshot;
12
13
  SynthesizerSnapshot.applySnapshot(this, snapshot);
13
14
  SpessaSynthInfo("%cFinished applying snapshot!", consoleColors.info);
14
15
  this.resetAllControllers();
@@ -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);
@@ -0,0 +1,18 @@
1
+ /**
2
+ * @typedef {Object} SynthProcessorOptions
3
+ * @property {boolean?} enableEventSystem - if the event system is enabled.
4
+ * @property {number?} initialTime - initial synth time, in seconds.
5
+ * @property {boolean?} effectsEnabled - if the processor should route audio to the effect channels.
6
+ * @property {number?} midiChannels - the default MIDI channel count.
7
+ */
8
+
9
+
10
+ /**
11
+ * @type {SynthProcessorOptions}
12
+ */
13
+ export const DEFAULT_SYNTH_OPTIONS = {
14
+ enableEventSystem: true,
15
+ initialTime: 0,
16
+ effectsEnabled: true,
17
+ midiChannels: 16
18
+ };
@@ -1,28 +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
- * @param buffer {ArrayBuffer}
7
- * @param isOverride {Boolean}
8
- * @this {SpessaSynthProcessor}
9
- */
10
- export function reloadSoundFont(buffer, isOverride = false)
11
- {
12
- this.clearSoundFont(false, isOverride);
13
- if (isOverride)
14
- {
15
- this.overrideSoundfont = loadSoundFont(buffer);
16
- }
17
- else
18
- {
19
- this.soundfontManager.reloadManager(buffer);
20
- }
21
- this.getDefaultPresets();
22
- this.midiAudioChannels.forEach(c =>
23
- c.programChange(c.preset.program)
24
- );
25
- this.postReady();
26
- this.sendPresetList();
27
- SpessaSynthInfo("%cSpessaSynth is ready!", consoleColors.recognized);
28
- }