spessasynth_lib 3.22.2 → 3.22.4

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.
@@ -27,6 +27,7 @@
27
27
  /**
28
28
  * @typedef {Object} SynthesizerSnapshot
29
29
  * @property {ChannelSnapshot[]} channelSnapshots - the individual channel snapshots
30
+ * @property {KeyModifier[][]} keyMappings - key modifiers
30
31
  * @property {number} mainVolume - main synth volume (set by MIDI), from 0 to 1
31
32
  * @property {number} pan - master stereo panning, from -1 to 1
32
33
  * @property {interpolationTypes} interpolation - the synth's interpolation type
@@ -82,7 +83,8 @@ export function sendSynthesizerSnapshot()
82
83
  pan: this.pan,
83
84
  transposition: this.transposition,
84
85
  system: this.system,
85
- interpolation: this.interpolationType
86
+ interpolation: this.interpolationType,
87
+ keyMappings: this.keyModifierManager.getMappings()
86
88
  };
87
89
 
88
90
  this.post({
@@ -106,6 +108,7 @@ export function applySynthesizerSnapshot(snapshot)
106
108
  this.setMasterPan(snapshot.pan);
107
109
  this.transposeAllChannels(snapshot.transposition);
108
110
  this.interpolationType = snapshot.interpolation;
111
+ this.keyModifierManager.setMappings(snapshot.keyMappings);
109
112
 
110
113
  // add channels if more needed
111
114
  while (this.workletProcessorChannels.length < snapshot.channelSnapshots.length)
@@ -97,6 +97,22 @@ export class WorkletKeyModifierManager
97
97
  this._keyMappings = [];
98
98
  }
99
99
 
100
+ /**
101
+ * @param mappings {KeyModifier[][]}
102
+ */
103
+ setMappings(mappings)
104
+ {
105
+ this._keyMappings = mappings;
106
+ }
107
+
108
+ /**
109
+ * @returns {KeyModifier[][]}
110
+ */
111
+ getMappings()
112
+ {
113
+ return this._keyMappings;
114
+ }
115
+
100
116
  /**
101
117
  * @param channel {number}
102
118
  * @param midiNote {number}
@@ -87,7 +87,6 @@ export class WorkletModulationEnvelope
87
87
  */
88
88
  static startRelease(voice)
89
89
  {
90
- voice.modulationEnvelope.releaseStartLevel = voice.modulationEnvelope.currentValue;
91
90
  WorkletModulationEnvelope.recalculate(voice);
92
91
  }
93
92
 
@@ -98,6 +97,12 @@ export class WorkletModulationEnvelope
98
97
  {
99
98
  const env = voice.modulationEnvelope;
100
99
 
100
+ // in release? Might need to recalculate the value as it can be modulated
101
+ if (voice.isInRelease)
102
+ {
103
+ env.releaseStartLevel = WorkletModulationEnvelope.getValue(voice, voice.releaseStartTime, true);
104
+ }
105
+
101
106
  env.sustainLevel = 1 - (voice.modulatedGenerators[generatorTypes.sustainModEnv] / 1000);
102
107
 
103
108
  env.attackDuration = timecentsToSeconds(voice.modulatedGenerators[generatorTypes.attackModEnv]);
@@ -127,17 +132,19 @@ export class WorkletModulationEnvelope
127
132
  * Calculates the current modulation envelope value for the given time and voice
128
133
  * @param voice {WorkletVoice} the voice we're working on
129
134
  * @param currentTime {number} in seconds
135
+ * @param ignoreRelease {boolean} if true, it will compute the value as if the voice was not released
130
136
  * @returns {number} modenv value, from 0 to 1
131
137
  */
132
- static getValue(voice, currentTime)
138
+ static getValue(voice, currentTime, ignoreRelease = false)
133
139
  {
134
140
  const env = voice.modulationEnvelope;
135
- if (voice.isInRelease)
141
+ if (voice.isInRelease && !ignoreRelease)
136
142
  {
137
- if (env.releaseDuration < 0.001)
143
+ // if the voice is still in the delay phase,
144
+ // start level will be 0 which will result in divide by zero
145
+ if (env.releaseStartLevel === 0)
138
146
  {
139
- // prevent lowpass bugs if release is instant
140
- return env.releaseStartLevel;
147
+ return 0;
141
148
  }
142
149
  return Math.max(
143
150
  0,
@@ -130,11 +130,6 @@ export function computeModulators(voice, controllerTable, sourceUsesCC = -1, sou
130
130
  const generators = voice.generators;
131
131
  const modulatedGenerators = voice.modulatedGenerators;
132
132
 
133
- // Modulation envelope is cheap to recalculate
134
- // why here and not at the bottom?
135
- // I dunno, seems to work fine
136
- WorkletModulationEnvelope.recalculate(voice);
137
-
138
133
  if (sourceUsesCC === -1)
139
134
  {
140
135
  // All modulators mode: compute all modulators
@@ -153,6 +148,7 @@ export function computeModulators(voice, controllerTable, sourceUsesCC = -1, sou
153
148
  );
154
149
  });
155
150
  WorkletVolumeEnvelope.recalculate(voice);
151
+ WorkletModulationEnvelope.recalculate(voice);
156
152
  return;
157
153
  }
158
154
 
@@ -208,6 +204,8 @@ export function computeModulators(voice, controllerTable, sourceUsesCC = -1, sou
208
204
  {
209
205
  WorkletVolumeEnvelope.recalculate(voice);
210
206
  }
207
+
208
+ WorkletModulationEnvelope.recalculate(voice);
211
209
  }
212
210
 
213
211