spessasynth_core 3.27.3 → 3.27.5

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.5",
4
4
  "description": "MIDI and SoundFont2/DLS library with no compromises",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -34,6 +34,20 @@ export const modulatorCurveTypes = {
34
34
  switch: 3
35
35
  };
36
36
 
37
+
38
+ export function getModSourceEnum(curveType, polarity, direction, isCC, index)
39
+ {
40
+ return (curveType << 10) | (polarity << 9) | (direction << 8) | (isCC << 7) | index;
41
+ }
42
+
43
+ const defaultResonantModSource = getModSourceEnum(
44
+ modulatorCurveTypes.linear,
45
+ 1,
46
+ 0,
47
+ 1,
48
+ midiControllers.filterResonance
49
+ ); // linear forwards bipolar cc 74
50
+
37
51
  export class Modulator
38
52
  {
39
53
  /**
@@ -70,9 +84,18 @@ export class Modulator
70
84
  * - still can be disabled if the soundfont has its own modulator curve
71
85
  * - this fixes the very low amount of reverb by default and doesn't break soundfonts
72
86
  * @type {boolean}
87
+ * @readonly
73
88
  */
74
89
  isEffectModulator = false;
75
90
 
91
+ /**
92
+ * The default resonant modulator does not affect the filter gain.
93
+ * Neither XG nor GS responded to cc #74 in that way.
94
+ * @type {boolean}
95
+ * @readonly
96
+ */
97
+ isDefaultResonantModulator = false;
98
+
76
99
  /**
77
100
  * 1 if the source is bipolar (min is -1, max is 1)
78
101
  * otherwise min is 0 and max is 1
@@ -151,6 +174,7 @@ export class Modulator
151
174
  * @param amount {number}
152
175
  * @param transformType {0|2}
153
176
  * @param isEffectModulator {boolean}
177
+ * @param isDefaultResonantModulator {boolean}
154
178
  */
155
179
  constructor(sourceIndex,
156
180
  sourceCurveType,
@@ -165,7 +189,8 @@ export class Modulator
165
189
  destination,
166
190
  amount,
167
191
  transformType,
168
- isEffectModulator = false)
192
+ isEffectModulator = false,
193
+ isDefaultResonantModulator = false)
169
194
  {
170
195
  this.sourcePolarity = sourcePolarity;
171
196
  this.sourceDirection = sourceDirection;
@@ -183,6 +208,7 @@ export class Modulator
183
208
  this.transformAmount = amount;
184
209
  this.transformType = transformType;
185
210
  this.isEffectModulator = isEffectModulator;
211
+ this.isDefaultResonantModulator = isDefaultResonantModulator;
186
212
 
187
213
 
188
214
  if (this.modulatorDestination > MAX_GENERATOR)
@@ -212,7 +238,8 @@ export class Modulator
212
238
  modulator.modulatorDestination,
213
239
  modulator.transformAmount,
214
240
  modulator.transformType,
215
- modulator.isEffectModulator
241
+ modulator.isEffectModulator,
242
+ modulator.isDefaultResonantModulator
216
243
  );
217
244
  }
218
245
 
@@ -327,7 +354,8 @@ export class Modulator
327
354
  this.modulatorDestination,
328
355
  this.transformAmount + modulator.transformAmount,
329
356
  this.transformType,
330
- this.isEffectModulator
357
+ this.isEffectModulator,
358
+ this.isDefaultResonantModulator
331
359
  );
332
360
  }
333
361
  }
@@ -385,16 +413,19 @@ export class DecodedModulator extends Modulator
385
413
  this.modulatorDestination === generatorTypes.reverbEffectsSend
386
414
  || this.modulatorDestination === generatorTypes.chorusEffectsSend
387
415
  );
416
+
417
+
418
+ this.isDefaultResonantModulator = (
419
+ sourceEnum === defaultResonantModSource
420
+ && secondarySourceEnum === 0x0
421
+ && this.modulatorDestination === generatorTypes.initialFilterQ
422
+ );
388
423
  }
389
424
  }
390
425
 
391
426
  export const DEFAULT_ATTENUATION_MOD_AMOUNT = 960;
392
427
  export const DEFAULT_ATTENUATION_MOD_CURVE_TYPE = modulatorCurveTypes.concave;
393
428
 
394
- export function getModSourceEnum(curveType, polarity, direction, isCC, index)
395
- {
396
- return (curveType << 10) | (polarity << 9) | (direction << 8) | (isCC << 7) | index;
397
- }
398
429
 
399
430
  const soundFontModulators = [
400
431
  // vel to attenuation
@@ -533,20 +564,15 @@ const customModulators = [
533
564
  0
534
565
  ),
535
566
 
536
- // cc 71 (filter Q) to filter Q
567
+ // cc 71 (filter Q) to filter Q (default resonant modulator)
537
568
  new DecodedModulator(
538
- getModSourceEnum(
539
- modulatorCurveTypes.linear,
540
- 1,
541
- 0,
542
- 1,
543
- midiControllers.filterResonance
544
- ), // linear forwards bipolar cc 74
569
+ defaultResonantModSource,
545
570
  0x0, // no controller
546
571
  generatorTypes.initialFilterQ,
547
572
  250,
548
573
  0
549
574
  )
575
+
550
576
  ];
551
577
 
552
578
  /**
@@ -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.isDefaultResonantModulator)
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)