spessasynth_core 3.27.3 → 3.27.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spessasynth_core",
3
- "version": "3.27.3",
3
+ "version": "3.27.4",
4
4
  "description": "MIDI and SoundFont2/DLS library with no compromises",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -73,6 +73,13 @@ export class Modulator
73
73
  */
74
74
  isEffectModulator = false;
75
75
 
76
+ /**
77
+ * The default resonant modulator does not affect the filter gain.
78
+ * Neither XG nor GS responded to cc #74 in that way.
79
+ * @type {boolean}
80
+ */
81
+ isDefaultResonanceModulator = false;
82
+
76
83
  /**
77
84
  * 1 if the source is bipolar (min is -1, max is 1)
78
85
  * otherwise min is 0 and max is 1
@@ -198,7 +205,7 @@ export class Modulator
198
205
  */
199
206
  static copy(modulator)
200
207
  {
201
- return new Modulator(
208
+ const m = new Modulator(
202
209
  modulator.sourceIndex,
203
210
  modulator.sourceCurveType,
204
211
  modulator.sourceUsesCC,
@@ -214,6 +221,8 @@ export class Modulator
214
221
  modulator.transformType,
215
222
  modulator.isEffectModulator
216
223
  );
224
+ m.isDefaultResonanceModulator = modulator.isDefaultResonanceModulator;
225
+ return m;
217
226
  }
218
227
 
219
228
  /**
@@ -313,7 +322,7 @@ export class Modulator
313
322
  */
314
323
  sumTransform(modulator)
315
324
  {
316
- return new Modulator(
325
+ const m = new Modulator(
317
326
  this.sourceIndex,
318
327
  this.sourceCurveType,
319
328
  this.sourceUsesCC,
@@ -329,6 +338,8 @@ export class Modulator
329
338
  this.transformType,
330
339
  this.isEffectModulator
331
340
  );
341
+ m.isDefaultResonanceModulator = modulator.isDefaultResonanceModulator;
342
+ return m;
332
343
  }
333
344
  }
334
345
 
@@ -531,23 +542,26 @@ const customModulators = [
531
542
  generatorTypes.initialFilterFc,
532
543
  6000,
533
544
  0
534
- ),
535
-
536
- // cc 71 (filter Q) to filter Q
537
- new DecodedModulator(
538
- getModSourceEnum(
539
- modulatorCurveTypes.linear,
540
- 1,
541
- 0,
542
- 1,
543
- midiControllers.filterResonance
544
- ), // linear forwards bipolar cc 74
545
- 0x0, // no controller
546
- generatorTypes.initialFilterQ,
547
- 250,
548
- 0
549
545
  )
546
+
550
547
  ];
548
+ // cc 71 (filter Q) to filter Q
549
+ const resonanceModulator = new DecodedModulator(
550
+ getModSourceEnum(
551
+ modulatorCurveTypes.linear,
552
+ 1,
553
+ 0,
554
+ 1,
555
+ midiControllers.filterResonance
556
+ ), // linear forwards bipolar cc 74
557
+ 0x0, // no controller
558
+ generatorTypes.initialFilterQ,
559
+ 250,
560
+ 0
561
+ );
562
+
563
+ resonanceModulator.isDefaultResonanceModulator = true;
564
+ customModulators.push(resonanceModulator);
551
565
 
552
566
  /**
553
567
  * @type {Modulator[]}
@@ -113,6 +113,13 @@ export function computeModulator(controllerTable, modulator, voice)
113
113
  computedValue = Math.abs(computedValue);
114
114
  }
115
115
 
116
+ // resonant modulator: take its value and ensure that it won't change the final gain
117
+ if (modulator.isDefaultResonanceModulator)
118
+ {
119
+ // half the gain, negates the filter
120
+ voice.resonanceOffset = Math.max(0, computedValue / 2);
121
+ }
122
+
116
123
  modulator.currentValue = computedValue;
117
124
  return computedValue;
118
125
  }
@@ -143,6 +143,12 @@ class Voice
143
143
  */
144
144
  modulators = [];
145
145
 
146
+ /**
147
+ * Resonance offset, it is affected by the default resonant modulator
148
+ * @type {number}
149
+ */
150
+ resonanceOffset = 0;
151
+
146
152
  /**
147
153
  * The generators in real-time, affected by modulators.
148
154
  * This is used during rendering.
@@ -290,7 +296,6 @@ class Voice
290
296
  this.modulatedGenerators = new Int16Array(generators);
291
297
  this.modulators = modulators;
292
298
  this.filter = new LowpassFilter(sampleRate);
293
-
294
299
  this.velocity = velocity;
295
300
  this.midiNote = midiNote;
296
301
  this.startTime = currentTime;
@@ -452,8 +457,6 @@ export function getVoicesForPreset(preset, bank, program, midiNote, velocity, re
452
457
  // MidiNote: midiNote,
453
458
  // AudioSample: audioSample
454
459
  // }]);
455
-
456
-
457
460
  voices.push(
458
461
  new Voice(
459
462
  this.sampleRate,
@@ -160,6 +160,9 @@ export function renderVoice(
160
160
  cents += modEnv * modEnvPitchDepth;
161
161
  }
162
162
 
163
+ // default resonant modulator: it does not affect the filter gain (neither XG nor GS did that)
164
+ volumeExcursionCentibels -= voice.resonanceOffset;
165
+
163
166
  // finally, calculate the playback rate
164
167
  const centsTotal = ~~(cents + semitones * 100);
165
168
  if (centsTotal !== voice.currentTuningCents)