spessasynth_lib 3.24.13 → 3.24.15

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 (61) hide show
  1. package/midi_parser/basic_midi.js +457 -68
  2. package/midi_parser/midi_loader.js +18 -503
  3. package/midi_parser/midi_message.js +18 -5
  4. package/midi_parser/midi_sequence.js +2 -2
  5. package/package.json +1 -1
  6. package/sequencer/worklet_sequencer/process_event.js +1 -6
  7. package/synthetizer/synthetizer.js +9 -6
  8. package/synthetizer/worklet_processor.min.js +12 -12
  9. package/synthetizer/worklet_system/README.md +2 -2
  10. package/synthetizer/worklet_system/main_processor.js +106 -95
  11. package/synthetizer/worklet_system/message_protocol/handle_message.js +22 -17
  12. package/synthetizer/worklet_system/message_protocol/worklet_message.js +2 -1
  13. package/synthetizer/worklet_system/snapshot/apply_synthesizer_snapshot.js +14 -0
  14. package/synthetizer/worklet_system/snapshot/channel_snapshot.js +166 -0
  15. package/synthetizer/worklet_system/snapshot/send_synthesizer_snapshot.js +14 -0
  16. package/synthetizer/worklet_system/snapshot/synthesizer_snapshot.js +121 -0
  17. package/synthetizer/worklet_system/worklet_methods/controller_control/controller_change.js +196 -0
  18. package/synthetizer/worklet_system/worklet_methods/controller_control/master_parameters.js +34 -0
  19. package/synthetizer/worklet_system/worklet_methods/{reset_controllers.js → controller_control/reset_controllers.js} +33 -39
  20. package/synthetizer/worklet_system/worklet_methods/create_worklet_channel.js +26 -0
  21. package/synthetizer/worklet_system/worklet_methods/{data_entry.js → data_entry/data_entry_coarse.js} +38 -105
  22. package/synthetizer/worklet_system/worklet_methods/data_entry/data_entry_fine.js +64 -0
  23. package/synthetizer/worklet_system/worklet_methods/mute_channel.js +17 -0
  24. package/synthetizer/worklet_system/worklet_methods/note_on.js +36 -34
  25. package/synthetizer/worklet_system/worklet_methods/program_change.js +49 -0
  26. package/synthetizer/worklet_system/worklet_methods/{voice_control.js → render_voice.js} +37 -120
  27. package/synthetizer/worklet_system/worklet_methods/soundfont_management/clear_sound_font.js +35 -0
  28. package/synthetizer/worklet_system/worklet_methods/soundfont_management/get_preset.js +20 -0
  29. package/synthetizer/worklet_system/worklet_methods/soundfont_management/reload_sound_font.js +43 -0
  30. package/synthetizer/worklet_system/worklet_methods/soundfont_management/send_preset_list.js +31 -0
  31. package/synthetizer/worklet_system/worklet_methods/soundfont_management/set_embedded_sound_font.js +21 -0
  32. package/synthetizer/worklet_system/worklet_methods/stopping_notes/kill_note.js +19 -0
  33. package/synthetizer/worklet_system/worklet_methods/stopping_notes/note_off.js +51 -0
  34. package/synthetizer/worklet_system/worklet_methods/stopping_notes/stop_all_channels.js +16 -0
  35. package/synthetizer/worklet_system/worklet_methods/stopping_notes/stop_all_notes.js +30 -0
  36. package/synthetizer/worklet_system/worklet_methods/stopping_notes/voice_killing.js +63 -0
  37. package/synthetizer/worklet_system/worklet_methods/system_exclusive.js +31 -30
  38. package/synthetizer/worklet_system/worklet_methods/tuning_control/channel_pressure.js +24 -0
  39. package/synthetizer/worklet_system/worklet_methods/tuning_control/pitch_wheel.js +33 -0
  40. package/synthetizer/worklet_system/worklet_methods/tuning_control/poly_pressure.js +31 -0
  41. package/synthetizer/worklet_system/worklet_methods/tuning_control/set_master_tuning.js +15 -0
  42. package/synthetizer/worklet_system/worklet_methods/tuning_control/set_modulation_depth.js +27 -0
  43. package/synthetizer/worklet_system/worklet_methods/tuning_control/set_octave_tuning.js +15 -0
  44. package/synthetizer/worklet_system/worklet_methods/tuning_control/set_tuning.js +24 -0
  45. package/synthetizer/worklet_system/worklet_methods/tuning_control/set_tuning_semitones.js +19 -0
  46. package/synthetizer/worklet_system/worklet_methods/tuning_control/transpose_all_channels.js +15 -0
  47. package/synthetizer/worklet_system/worklet_methods/tuning_control/transpose_channel.js +31 -0
  48. package/synthetizer/worklet_system/worklet_utilities/controller_tables.js +10 -1
  49. package/synthetizer/worklet_system/worklet_utilities/lfo.js +2 -1
  50. package/synthetizer/worklet_system/worklet_utilities/modulation_envelope.js +4 -4
  51. package/synthetizer/worklet_system/worklet_utilities/modulator_curves.js +4 -5
  52. package/synthetizer/worklet_system/worklet_utilities/stereo_panner.js +18 -18
  53. package/synthetizer/worklet_system/worklet_utilities/wavetable_oscillator.js +210 -206
  54. package/synthetizer/worklet_system/worklet_utilities/worklet_processor_channel.js +354 -108
  55. package/synthetizer/worklet_system/worklet_utilities/worklet_voice.js +22 -9
  56. package/synthetizer/worklet_system/snapshot/snapshot.js +0 -311
  57. package/synthetizer/worklet_system/worklet_methods/controller_control.js +0 -260
  58. package/synthetizer/worklet_system/worklet_methods/note_off.js +0 -119
  59. package/synthetizer/worklet_system/worklet_methods/program_control.js +0 -282
  60. package/synthetizer/worklet_system/worklet_methods/tuning_control.js +0 -233
  61. package/synthetizer/worklet_system/worklet_methods/vibrato_control.js +0 -29
@@ -1,4 +1,4 @@
1
- ## This the worklet system synthesis folder.
1
+ ## This is the worklet system synthesis folder.
2
2
 
3
3
  The code here is responsible for a single midi channel, synthesizing the sound to it.
4
4
 
@@ -6,7 +6,7 @@ The code here is responsible for a single midi channel, synthesizing the sound t
6
6
  - `worklet_utilities` contains the various digital signal processing functions such as the wavetable oscillator, low
7
7
  pass filter, etc.
8
8
 
9
- For those interested, the `voice_control.js` contains the actual DSP synthesis code.
9
+ For those interested, `render_voice.js` file contains the actual DSP synthesis code.
10
10
 
11
11
  `minify_processor.js` uses esbuild to minify the processor code. Importing this instead of `worklet_processor.js` is
12
12
  recommended.
@@ -2,54 +2,31 @@ import { DEFAULT_PERCUSSION, DEFAULT_SYNTH_MODE, VOICE_CAP } from "../synthetize
2
2
  import { WorkletSequencer } from "../../sequencer/worklet_sequencer/worklet_sequencer.js";
3
3
  import { SpessaSynthInfo } from "../../utils/loggin.js";
4
4
  import { consoleColors } from "../../utils/other.js";
5
- import { releaseVoice, renderVoice, voiceKilling } from "./worklet_methods/voice_control.js";
5
+ import { voiceKilling } from "./worklet_methods/stopping_notes/voice_killing.js";
6
6
  import { ALL_CHANNELS_OR_DIFFERENT_ACTION, returnMessageType } from "./message_protocol/worklet_message.js";
7
7
  import { stbvorbis } from "../../externals/stbvorbis_sync/stbvorbis_sync.min.js";
8
8
  import { VOLUME_ENVELOPE_SMOOTHING_FACTOR } from "./worklet_utilities/volume_envelope.js";
9
9
  import { handleMessage } from "./message_protocol/handle_message.js";
10
10
  import { callEvent, sendChannelProperties } from "./message_protocol/message_sending.js";
11
11
  import { systemExclusive } from "./worklet_methods/system_exclusive.js";
12
- import { noteOn } from "./worklet_methods/note_on.js";
13
- import { killNote, noteOff, stopAll, stopAllChannels } from "./worklet_methods/note_off.js";
14
- import {
15
- channelPressure,
16
- pitchWheel,
17
- polyPressure,
18
- setChannelTuning,
19
- setChannelTuningSemitones,
20
- setMasterTuning,
21
- setModulationDepth,
22
- setOctaveTuning,
23
- transposeAllChannels,
24
- transposeChannel
25
- } from "./worklet_methods/tuning_control.js";
26
- import {
27
- controllerChange,
28
- muteChannel,
29
- setMasterGain,
30
- setMasterPan,
31
- setMIDIVolume
32
- } from "./worklet_methods/controller_control.js";
33
- import { disableAndLockGSNRPN, setVibrato } from "./worklet_methods/vibrato_control.js";
34
- import { dataEntryCoarse, dataEntryFine } from "./worklet_methods/data_entry.js";
35
- import { createWorkletChannel } from "./worklet_utilities/worklet_processor_channel.js";
36
- import { resetAllControllers, resetControllers, resetParameters } from "./worklet_methods/reset_controllers.js";
37
- import {
38
- clearSoundFont,
39
- getPreset,
40
- programChange,
41
- reloadSoundFont,
42
- sendPresetList,
43
- setDrums,
44
- setEmbeddedSoundFont,
45
- setPreset
46
- } from "./worklet_methods/program_control.js";
47
- import { applySynthesizerSnapshot, sendSynthesizerSnapshot } from "./snapshot/snapshot.js";
12
+ import { setMasterGain, setMasterPan, setMIDIVolume } from "./worklet_methods/controller_control/master_parameters.js";
13
+ import { resetAllControllers } from "./worklet_methods/controller_control/reset_controllers.js";
48
14
  import { WorkletSoundfontManager } from "./worklet_methods/worklet_soundfont_manager/worklet_soundfont_manager.js";
49
15
  import { interpolationTypes } from "./worklet_utilities/wavetable_oscillator.js";
50
16
  import { WorkletKeyModifierManager } from "./worklet_methods/worklet_key_modifier.js";
51
17
  import { getWorkletVoices } from "./worklet_utilities/worklet_voice.js";
52
- import { PAN_SMOOTHING_FACTOR, panVoice } from "./worklet_utilities/stereo_panner.js";
18
+ import { PAN_SMOOTHING_FACTOR } from "./worklet_utilities/stereo_panner.js";
19
+ import { stopAllChannels } from "./worklet_methods/stopping_notes/stop_all_channels.js";
20
+ import { setEmbeddedSoundFont } from "./worklet_methods/soundfont_management/set_embedded_sound_font.js";
21
+ import { reloadSoundFont } from "./worklet_methods/soundfont_management/reload_sound_font.js";
22
+ import { clearSoundFont } from "./worklet_methods/soundfont_management/clear_sound_font.js";
23
+ import { sendPresetList } from "./worklet_methods/soundfont_management/send_preset_list.js";
24
+ import { getPreset } from "./worklet_methods/soundfont_management/get_preset.js";
25
+ import { transposeAllChannels } from "./worklet_methods/tuning_control/transpose_all_channels.js";
26
+ import { setMasterTuning } from "./worklet_methods/tuning_control/set_master_tuning.js";
27
+ import { sendSynthesizerSnapshot } from "./snapshot/send_synthesizer_snapshot.js";
28
+ import { applySynthesizerSnapshot } from "./snapshot/apply_synthesizer_snapshot.js";
29
+ import { createWorkletChannel } from "./worklet_methods/create_worklet_channel.js";
53
30
 
54
31
 
55
32
  /**
@@ -334,43 +311,39 @@ class SpessaSynthProcessor extends AudioWorkletProcessor
334
311
  return;
335
312
  }
336
313
  let outputIndex;
337
- let outputLeft;
338
- let outputRight;
339
- let reverbChannels;
340
- let chorusChannels;
314
+ let outputL;
315
+ let outputR;
316
+ let reverbL;
317
+ let reverbR;
318
+ let chorusL;
319
+ let chorusR;
341
320
  if (this.oneOutputMode)
342
321
  {
343
322
  // first output only
344
323
  const output = outputs[0];
345
324
  // reverb and chorus are disabled. 32 output channels: two for each midi channel
346
325
  outputIndex = (index % 16) * 2;
347
- outputLeft = output[outputIndex];
348
- outputRight = output[outputIndex + 1];
326
+ outputL = output[outputIndex];
327
+ outputR = output[outputIndex + 1];
349
328
  }
350
329
  else
351
330
  {
352
331
  // 2 first outputs are reverb and chorus, others are for channels
353
332
  outputIndex = (index % this._outputsAmount) + 2;
354
- outputLeft = outputs[outputIndex][0];
355
- outputRight = outputs[outputIndex][1];
356
- reverbChannels = outputs[0];
357
- chorusChannels = outputs[1];
333
+ outputL = outputs[outputIndex][0];
334
+ outputR = outputs[outputIndex][1];
335
+ reverbL = outputs[0][0];
336
+ reverbR = outputs[0][1];
337
+ chorusL = outputs[1][0];
338
+ chorusR = outputs[1][1];
358
339
  }
359
340
 
360
341
  // for every voice, render it
361
- channel.voices = channel.voices.filter(v =>
362
- {
363
- // render voice
364
- this.renderVoice(
365
- channel,
366
- v,
367
- outputLeft, outputRight,
368
- reverbChannels,
369
- chorusChannels
370
- );
371
-
372
- return !v.finished;
373
- });
342
+ channel.renderAudio(
343
+ outputL, outputR,
344
+ reverbL, reverbR,
345
+ chorusL, chorusR
346
+ );
374
347
 
375
348
  totalCurrentVoices += channel.voices.length;
376
349
  });
@@ -403,12 +376,81 @@ class SpessaSynthProcessor extends AudioWorkletProcessor
403
376
  this.soundfontManager.destroyManager();
404
377
  delete this.soundfontManager;
405
378
  }
379
+
380
+ /**
381
+ * @param channel {number}
382
+ * @param controllerNumber {number}
383
+ * @param controllerValue {number}
384
+ * @param force {boolean}
385
+ */
386
+ controllerChange(channel, controllerNumber, controllerValue, force = false)
387
+ {
388
+ this.workletProcessorChannels[channel].controllerChange(controllerNumber, controllerValue, force);
389
+ }
390
+
391
+ /**
392
+ * @param channel {number}
393
+ * @param midiNote {number}
394
+ * @param velocity {number}
395
+ * @param enableDebug {boolean}
396
+ * @param sendEvent {boolean}
397
+ */
398
+ noteOn(channel, midiNote, velocity, enableDebug = false, sendEvent = true)
399
+ {
400
+ this.workletProcessorChannels[channel].noteOn(midiNote, velocity, enableDebug, sendEvent);
401
+ }
402
+
403
+ /**
404
+ * @param channel {number}
405
+ * @param midiNote {number}
406
+ */
407
+ noteOff(channel, midiNote)
408
+ {
409
+ this.workletProcessorChannels[channel].noteOff(midiNote);
410
+ }
411
+
412
+ /**
413
+ * @param channel {number}
414
+ * @param midiNote {number}
415
+ * @param pressure {number}
416
+ */
417
+ polyPressure(channel, midiNote, pressure)
418
+ {
419
+ this.workletProcessorChannels[channel].polyPressure(midiNote, pressure);
420
+ }
421
+
422
+ /**
423
+ * @param channel {number}
424
+ * @param pressure {number}
425
+ */
426
+ channelPressure(channel, pressure)
427
+ {
428
+ this.workletProcessorChannels[channel].channelPressure(pressure);
429
+ }
430
+
431
+ /**
432
+ * @param channel {number}
433
+ * @param MSB {number}
434
+ * @param LSB {number}
435
+ */
436
+ pitchWheel(channel, MSB, LSB)
437
+ {
438
+ this.workletProcessorChannels[channel].pitchWheel(MSB, LSB);
439
+ }
440
+
441
+ /**
442
+ * @param channel {number}
443
+ * @param programNumber {number}
444
+ * @param userChange {boolean}
445
+ */
446
+ programChange(channel, programNumber, userChange = false)
447
+ {
448
+ this.workletProcessorChannels[channel].programChange(programNumber, userChange);
449
+ }
406
450
  }
407
451
 
408
452
  // include other methods
409
453
  // voice related
410
- SpessaSynthProcessor.prototype.renderVoice = renderVoice;
411
- SpessaSynthProcessor.prototype.releaseVoice = releaseVoice;
412
454
  SpessaSynthProcessor.prototype.voiceKilling = voiceKilling;
413
455
  SpessaSynthProcessor.prototype.getWorkletVoices = getWorkletVoices;
414
456
 
@@ -420,30 +462,10 @@ SpessaSynthProcessor.prototype.callEvent = callEvent;
420
462
  // system-exclusive related
421
463
  SpessaSynthProcessor.prototype.systemExclusive = systemExclusive;
422
464
 
423
- // note messages related
424
- SpessaSynthProcessor.prototype.noteOn = noteOn;
425
- SpessaSynthProcessor.prototype.noteOff = noteOff;
426
- SpessaSynthProcessor.prototype.polyPressure = polyPressure;
427
- SpessaSynthProcessor.prototype.killNote = killNote;
428
- SpessaSynthProcessor.prototype.stopAll = stopAll;
429
- SpessaSynthProcessor.prototype.stopAllChannels = stopAllChannels;
430
- SpessaSynthProcessor.prototype.muteChannel = muteChannel;
431
-
432
- // custom vibrato related
433
- SpessaSynthProcessor.prototype.setVibrato = setVibrato;
434
- SpessaSynthProcessor.prototype.disableAndLockGSNRPN = disableAndLockGSNRPN;
435
-
436
- // data entry related
437
- SpessaSynthProcessor.prototype.dataEntryCoarse = dataEntryCoarse;
438
- SpessaSynthProcessor.prototype.dataEntryFine = dataEntryFine;
439
-
440
465
  // channel related
466
+ SpessaSynthProcessor.prototype.stopAllChannels = stopAllChannels;
441
467
  SpessaSynthProcessor.prototype.createWorkletChannel = createWorkletChannel;
442
- SpessaSynthProcessor.prototype.controllerChange = controllerChange;
443
- SpessaSynthProcessor.prototype.channelPressure = channelPressure;
444
468
  SpessaSynthProcessor.prototype.resetAllControllers = resetAllControllers;
445
- SpessaSynthProcessor.prototype.resetControllers = resetControllers;
446
- SpessaSynthProcessor.prototype.resetParameters = resetParameters;
447
469
 
448
470
  // master parameter related
449
471
  SpessaSynthProcessor.prototype.setMasterGain = setMasterGain;
@@ -452,19 +474,10 @@ SpessaSynthProcessor.prototype.setMIDIVolume = setMIDIVolume;
452
474
 
453
475
  // tuning related
454
476
  SpessaSynthProcessor.prototype.transposeAllChannels = transposeAllChannels;
455
- SpessaSynthProcessor.prototype.transposeChannel = transposeChannel;
456
- SpessaSynthProcessor.prototype.setChannelTuning = setChannelTuning;
457
- SpessaSynthProcessor.prototype.setChannelTuningSemitones = setChannelTuningSemitones;
458
477
  SpessaSynthProcessor.prototype.setMasterTuning = setMasterTuning;
459
- SpessaSynthProcessor.prototype.setModulationDepth = setModulationDepth;
460
- SpessaSynthProcessor.prototype.pitchWheel = pitchWheel;
461
- SpessaSynthProcessor.prototype.setOctaveTuning = setOctaveTuning;
462
478
 
463
479
  // program related
464
- SpessaSynthProcessor.prototype.programChange = programChange;
465
480
  SpessaSynthProcessor.prototype.getPreset = getPreset;
466
- SpessaSynthProcessor.prototype.setPreset = setPreset;
467
- SpessaSynthProcessor.prototype.setDrums = setDrums;
468
481
  SpessaSynthProcessor.prototype.reloadSoundFont = reloadSoundFont;
469
482
  SpessaSynthProcessor.prototype.clearSoundFont = clearSoundFont;
470
483
  SpessaSynthProcessor.prototype.setEmbeddedSoundFont = setEmbeddedSoundFont;
@@ -474,6 +487,4 @@ SpessaSynthProcessor.prototype.sendPresetList = sendPresetList;
474
487
  SpessaSynthProcessor.prototype.sendSynthesizerSnapshot = sendSynthesizerSnapshot;
475
488
  SpessaSynthProcessor.prototype.applySynthesizerSnapshot = applySynthesizerSnapshot;
476
489
 
477
- SpessaSynthProcessor.prototype.panVoice = panVoice;
478
-
479
490
  export { SpessaSynthProcessor };
@@ -17,27 +17,32 @@ export function handleMessage(message)
17
17
  /**
18
18
  * @type {WorkletProcessorChannel}
19
19
  */
20
- let channelObject = {};
20
+ let channelObject;
21
21
  if (channel >= 0)
22
22
  {
23
23
  channelObject = this.workletProcessorChannels[channel];
24
+ if (channelObject === undefined)
25
+ {
26
+ SpessaSynthWarn(`Trying to access channel ${channel} which does not exist... ignoring!`);
27
+ return;
28
+ }
24
29
  }
25
30
  switch (message.messageType)
26
31
  {
27
32
  case workletMessageType.noteOn:
28
- this.noteOn(channel, data[0], data[1], data[2]);
33
+ channelObject.noteOn(...data);
29
34
  break;
30
35
 
31
36
  case workletMessageType.noteOff:
32
- this.noteOff(channel, data);
37
+ channelObject.noteOff(data);
33
38
  break;
34
39
 
35
40
  case workletMessageType.pitchWheel:
36
- this.pitchWheel(channel, data[0], data[1]);
41
+ channelObject.pitchWheel(...data);
37
42
  break;
38
43
 
39
44
  case workletMessageType.ccChange:
40
- this.controllerChange(channel, data[0], data[1], data[2]);
45
+ channelObject.controllerChange(...data);
41
46
  break;
42
47
 
43
48
  case workletMessageType.customcCcChange:
@@ -46,7 +51,7 @@ export function handleMessage(message)
46
51
  break;
47
52
 
48
53
  case workletMessageType.killNote:
49
- this.killNote(channel, data);
54
+ channelObject.killNote(data);
50
55
  break;
51
56
 
52
57
  case workletMessageType.programChange:
@@ -54,11 +59,11 @@ export function handleMessage(message)
54
59
  break;
55
60
 
56
61
  case workletMessageType.channelPressure:
57
- this.channelPressure(channel, data);
62
+ channelObject.channelPressure(data);
58
63
  break;
59
64
 
60
65
  case workletMessageType.polyPressure:
61
- this.polyPressure(channel, data[0], data[1]);
66
+ channelObject.polyPressure(...data);
62
67
  break;
63
68
 
64
69
  case workletMessageType.ccReset:
@@ -68,7 +73,7 @@ export function handleMessage(message)
68
73
  }
69
74
  else
70
75
  {
71
- this.resetControllers(channel);
76
+ channelObject.resetControllers();
72
77
  }
73
78
  break;
74
79
 
@@ -83,21 +88,21 @@ export function handleMessage(message)
83
88
  {
84
89
  if (data.rate === -1)
85
90
  {
86
- this.disableAndLockGSNRPN(i);
91
+ channelObject.disableAndLockGSNRPN();
87
92
  }
88
93
  else
89
94
  {
90
- this.setVibrato(i, data.depth, data.rate, data.delay);
95
+ channelObject.setVibrato(data.depth, data.rate, data.delay);
91
96
  }
92
97
  }
93
98
  }
94
99
  else if (data.rate === -1)
95
100
  {
96
- this.disableAndLockGSNRPN(channel);
101
+ channelObject.disableAndLockGSNRPN();
97
102
  }
98
103
  else
99
104
  {
100
- this.setVibrato(channel, data.depth, data.rate, data.delay);
105
+ channelObject.setVibrato(data.depth, data.rate, data.delay);
101
106
  }
102
107
  break;
103
108
 
@@ -108,7 +113,7 @@ export function handleMessage(message)
108
113
  }
109
114
  else
110
115
  {
111
- this.stopAll(channel, data === 1);
116
+ channelObject.stopAllNotes(data === 1);
112
117
  }
113
118
  break;
114
119
 
@@ -117,7 +122,7 @@ export function handleMessage(message)
117
122
  break;
118
123
 
119
124
  case workletMessageType.muteChannel:
120
- this.muteChannel(channel, data);
125
+ channelObject.muteChannel(data);
121
126
  break;
122
127
 
123
128
  case workletMessageType.addNewChannel:
@@ -155,7 +160,7 @@ export function handleMessage(message)
155
160
  break;
156
161
 
157
162
  case workletMessageType.setDrums:
158
- this.setDrums(channel, data);
163
+ channelObject.setDrums(data);
159
164
  break;
160
165
 
161
166
  case workletMessageType.transpose:
@@ -165,7 +170,7 @@ export function handleMessage(message)
165
170
  }
166
171
  else
167
172
  {
168
- this.transposeChannel(channel, data[0], data[1]);
173
+ channelObject.transposeChannel(data[0], data[1]);
169
174
  }
170
175
  break;
171
176
 
@@ -81,6 +81,7 @@ export const ALL_CHANNELS_OR_DIFFERENT_ACTION = -1;
81
81
  * messageType: workletMessageType,
82
82
  * messageData: (
83
83
  * number[]
84
+ * |undefined
84
85
  * |boolean[]
85
86
  * |WorkletVoice[]
86
87
  * |number
@@ -111,7 +112,7 @@ export const ALL_CHANNELS_OR_DIFFERENT_ACTION = -1;
111
112
  * 1 - event call -> {eventName<string>, eventData:<the event's data>}
112
113
  * 2 - reported current time -> currentTime<number>
113
114
  * 3 - sequencer specific -> [messageType<WorkletSequencerReturnMessageType> messageData<any>] note: refer to sequencer_message.js
114
- * 4 - synthesizer snapshot -> snapshot<SynthesizerSnapshot> note: refer to snapshot.js
115
+ * 4 - synthesizer snapshot -> snapshot<SynthesizerSnapshot> note: refer to synthesizer_snapshot.js
115
116
  * 5 - ready -> (no data)
116
117
  * 6 - soundfontError -> errorMessage<string>
117
118
  * 7 - idenfity -> version<string>
@@ -0,0 +1,14 @@
1
+ import { SpessaSynthInfo } from "../../../utils/loggin.js";
2
+ import { consoleColors } from "../../../utils/other.js";
3
+ import { SynthesizerSnapshot } from "./synthesizer_snapshot.js";
4
+
5
+ /**
6
+ * Applies the snapshot to the synth
7
+ * @param snapshot {SynthesizerSnapshot}
8
+ * @this {SpessaSynthProcessor}
9
+ */
10
+ export function applySynthesizerSnapshot(snapshot)
11
+ {
12
+ SynthesizerSnapshot.applySnapshot(this, snapshot);
13
+ SpessaSynthInfo("%cFinished applying snapshot!", consoleColors.info);
14
+ }
@@ -0,0 +1,166 @@
1
+ /**
2
+ * Represents a snapshot of a single channel's state in the synthesizer.
3
+ */
4
+ export class ChannelSnapshot
5
+ {
6
+ /**
7
+ * The channel's MIDI program number.
8
+ * @type {number}
9
+ */
10
+ program;
11
+
12
+ /**
13
+ * The channel's bank number.
14
+ * @type {number}
15
+ */
16
+ bank;
17
+
18
+ /**
19
+ * The name of the patch currently loaded in the channel.
20
+ * @type {string}
21
+ */
22
+ patchName;
23
+
24
+ /**
25
+ * Indicates whether the channel's program change is disabled.
26
+ * @type {boolean}
27
+ */
28
+ lockPreset;
29
+
30
+ /**
31
+ * The array of all MIDI controllers (in 14-bit values) with the modulator sources at the end.
32
+ * @type {Int16Array}
33
+ */
34
+ midiControllers;
35
+
36
+ /**
37
+ * An array of booleans, indicating if the controller with a current index is locked.
38
+ * @type {boolean[]}
39
+ */
40
+ lockedControllers;
41
+
42
+ /**
43
+ * Array of custom (not SF2) control values such as RPN pitch tuning, transpose, modulation depth, etc.
44
+ * @type {Float32Array}
45
+ */
46
+ customControllers;
47
+
48
+ /**
49
+ * Indicates whether the channel vibrato is locked.
50
+ * @type {boolean}
51
+ */
52
+ lockVibrato;
53
+
54
+ /**
55
+ * The channel's vibrato settings.
56
+ * @type {Object}
57
+ * @property {number} depth - Vibrato depth, in gain.
58
+ * @property {number} delay - Vibrato delay from note on in seconds.
59
+ * @property {number} rate - Vibrato rate in Hz.
60
+ */
61
+ channelVibrato;
62
+
63
+ /**
64
+ * Key shift for the channel.
65
+ * @type {number}
66
+ */
67
+ channelTransposeKeyShift;
68
+
69
+ /**
70
+ * The channel's octave tuning in cents.
71
+ * @type {Int8Array}
72
+ */
73
+ channelOctaveTuning;
74
+
75
+ /**
76
+ * Tuning of individual keys in cents.
77
+ * @type {Int16Array}
78
+ */
79
+ keyCentTuning;
80
+
81
+ /**
82
+ * Indicates whether the channel is muted.
83
+ * @type {boolean}
84
+ */
85
+ isMuted;
86
+
87
+ /**
88
+ * Overrides velocity if greater than 0, otherwise disabled.
89
+ * @type {number}
90
+ */
91
+ velocityOverride;
92
+
93
+ /**
94
+ * Indicates whether the channel is a drum channel.
95
+ * @type {boolean}
96
+ */
97
+ drumChannel;
98
+
99
+ /**
100
+ * Creates a snapshot of a single channel's state in the synthesizer.
101
+ * @param workletProcessor {SpessaSynthProcessor}
102
+ * @param channelNumber {number}
103
+ * @returns {ChannelSnapshot}
104
+ */
105
+ static getChannelSnapshot(workletProcessor, channelNumber)
106
+ {
107
+ const channelObject = workletProcessor.workletProcessorChannels[channelNumber];
108
+ const channelSnapshot = new ChannelSnapshot();
109
+ // program data
110
+ channelSnapshot.program = channelObject.preset.program;
111
+ channelSnapshot.bank = channelObject.getBankSelect();
112
+ channelSnapshot.lockPreset = channelObject.lockPreset;
113
+ channelSnapshot.patchName = channelObject.preset.presetName;
114
+
115
+ // controller data
116
+ channelSnapshot.midiControllers = channelObject.midiControllers;
117
+ channelSnapshot.lockedControllers = channelObject.lockedControllers;
118
+ channelSnapshot.customControllers = channelObject.customControllers;
119
+
120
+ // vibrato data
121
+ channelSnapshot.channelVibrato = channelObject.channelVibrato;
122
+ channelSnapshot.lockVibrato = channelObject.lockGSNRPNParams;
123
+
124
+ // tuning and transpose data
125
+ channelSnapshot.channelTransposeKeyShift = channelObject.channelTransposeKeyShift;
126
+ channelSnapshot.channelOctaveTuning = channelObject.channelOctaveTuning;
127
+ channelSnapshot.keyCentTuning = channelObject.keyCentTuning;
128
+
129
+ // other data
130
+ channelSnapshot.isMuted = channelObject.isMuted;
131
+ channelSnapshot.velocityOverride = channelObject.velocityOverride;
132
+ channelSnapshot.drumChannel = channelObject.drumChannel;
133
+ return channelSnapshot;
134
+ }
135
+
136
+ /**
137
+ * Applies the snapshot to the specified channel.
138
+ * @param workletProcessor {SpessaSynthProcessor}
139
+ * @param channelNumber {number}
140
+ * @param channelSnapshot {ChannelSnapshot}
141
+ */
142
+ static applyChannelSnapshot(workletProcessor, channelNumber, channelSnapshot)
143
+ {
144
+ const channelObject = workletProcessor.workletProcessorChannels[channelNumber];
145
+ channelObject.muteChannel(channelSnapshot.isMuted);
146
+ channelObject.setDrums(channelSnapshot.drumChannel);
147
+
148
+ // restore controllers
149
+ channelObject.midiControllers = channelSnapshot.midiControllers;
150
+ channelObject.lockedControllers = channelSnapshot.lockedControllers;
151
+ channelObject.customControllers = channelSnapshot.customControllers;
152
+
153
+ // restore vibrato and transpose
154
+ channelObject.channelVibrato = channelSnapshot.channelVibrato;
155
+ channelObject.lockGSNRPNParams = channelSnapshot.lockVibrato;
156
+ channelObject.channelTransposeKeyShift = channelSnapshot.channelTransposeKeyShift;
157
+ channelObject.channelOctaveTuning = channelSnapshot.channelOctaveTuning;
158
+ channelObject.velocityOverride = channelSnapshot.velocityOverride;
159
+
160
+ // restore preset and lock
161
+ channelObject.lockPreset = false;
162
+ channelObject.setBankSelect(channelSnapshot.bank);
163
+ channelObject.programChange(channelSnapshot.program);
164
+ channelObject.lockPreset = channelSnapshot.lockPreset;
165
+ }
166
+ }
@@ -0,0 +1,14 @@
1
+ import { returnMessageType } from "../message_protocol/worklet_message.js";
2
+ import { SynthesizerSnapshot } from "./synthesizer_snapshot.js";
3
+
4
+ /**
5
+ * sends a snapshot of the current controller values of the synth (used to copy that data to OfflineAudioContext when rendering)
6
+ * @this {SpessaSynthProcessor}
7
+ */
8
+ export function sendSynthesizerSnapshot()
9
+ {
10
+ this.post({
11
+ messageType: returnMessageType.synthesizerSnapshot,
12
+ messageData: SynthesizerSnapshot.createSynthesizerSnapshot(this)
13
+ });
14
+ }