spessasynth_core 4.4.6 → 4.4.7

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/dist/index.js CHANGED
@@ -2246,6 +2246,7 @@ function resetChannelInternal(sendCCEvents = true) {
2246
2246
  this.resetMIDIParameter("modulationDepth");
2247
2247
  this.resetMIDIParameter("rxChannel");
2248
2248
  this.resetMIDIParameter("efxAssign");
2249
+ this.resetMIDIParameter("eqEnabled");
2249
2250
  this.resetMIDIParameter("polyMode");
2250
2251
  this.resetMIDIParameter("keyShift");
2251
2252
  this.resetMIDIParameter("fineTune");
@@ -2467,6 +2468,7 @@ function applySnapshotInternal(midi, snapshot) {
2467
2468
  gsReverbParams: snapshot.systemParameters.gsReverbLock ? snapshot.gsReverbProcessor : void 0,
2468
2469
  gsChorusParams: snapshot.systemParameters.gsChorusLock ? snapshot.gsChorusProcessor : void 0,
2469
2470
  gsDelayParams: snapshot.systemParameters.gsDelayLock ? snapshot.gsDelayProcessor : void 0,
2471
+ gsEqualizerParams: snapshot.systemParameters.gsEqualizerLock ? snapshot.gsEqualizerProcessor : void 0,
2470
2472
  gsInsertionParams: snapshot.systemParameters.gsInsertionLock ? snapshot.gsInsertionBlock : void 0,
2471
2473
  xgReverbParams: snapshot.systemParameters.xgReverbLock ? snapshot.xgReverbBlock : void 0,
2472
2474
  xgChorusParams: snapshot.systemParameters.xgChorusLock ? snapshot.xgChorusBlock : void 0,
@@ -2779,7 +2781,8 @@ const GSInsertionAddressMap = {
2779
2781
  controlSource1: 27,
2780
2782
  controlDepth1: 28,
2781
2783
  controlSource2: 29,
2782
- controlDepth2: 30
2784
+ controlDepth2: 30,
2785
+ sendEQSwitch: 31
2783
2786
  };
2784
2787
  const GSChorusAddressMap = {
2785
2788
  macro: 56,
@@ -2805,6 +2808,12 @@ const GSDelayAddressMap = {
2805
2808
  feedback: 89,
2806
2809
  sendLevelToReverb: 90
2807
2810
  };
2811
+ const GSEqualizerAddressMap = {
2812
+ lowFrequency: 0,
2813
+ lowGain: 1,
2814
+ highFrequency: 2,
2815
+ highGain: 3
2816
+ };
2808
2817
  /**
2809
2818
  * The SysEx addresses of the channel MIDI parameters that GS and XG both set per part.
2810
2819
  * `gs` is the third address byte of GS PATCH PART (`40 1x xx`) and `xg` is the third address byte of
@@ -2875,6 +2884,10 @@ const NRPNDrumParamMap = {
2875
2884
  const NRPNDrumParamByMSB = new Map(Object.entries(NRPNDrumParamMap).map(([parameter, msb]) => [msb, parameter]));
2876
2885
  // src/midi/midi_tools/midi_utils.ts
2877
2886
  /**
2887
+ * The GS equalizer parameters by their address (`40 02 xx`).
2888
+ */
2889
+ const GS_EQUALIZER_PARAMETER_BY_ADDRESS = new Map(Object.entries(GSEqualizerAddressMap).map(([name, address]) => [address, name]));
2890
+ /**
2878
2891
  * The named GS insertion parameters by their address (`40 03 xx`).
2879
2892
  */
2880
2893
  const GS_INSERTION_PARAMETER_BY_ADDRESS = new Map(Object.entries(GSInsertionAddressMap).map(([name, address]) => [address, name]));
@@ -3017,20 +3030,33 @@ var MIDIUtils = class MIDIUtils {
3017
3030
  return this.gsMessage(ticks, 64, 1, a3, [value]);
3018
3031
  }
3019
3032
  /**
3033
+ * Returns a MIDI event needed to set the given GS Equalizer Parameter.
3034
+ * @param ticks The MIDI tick time for the output event.
3035
+ * @param parameter The parameter to set.
3036
+ * @param value The value to set it to.
3037
+ * @returns The {@link MIDIMessage} needed to set this GS Equalizer Parameter.
3038
+ */
3039
+ static setGSEqualizerParameter(ticks, parameter, value) {
3040
+ const a3 = GSEqualizerAddressMap[parameter];
3041
+ if (a3 === void 0) throw new Error(`Invalid equalizer parameter: ${parameter}`);
3042
+ return this.gsMessage(ticks, 64, 2, a3, [value]);
3043
+ }
3044
+ /**
3020
3045
  * Returns a MIDI event needed to set the given GS Insertion Parameter.
3021
3046
  * @param ticks The MIDI tick time for the output event.
3022
3047
  * @param parameter The parameter to set: `"type"`, a send level or EFX control name (see {@link GSInsertionParameter}),
3023
3048
  * or a 0-based effect-specific parameter number (0-19).
3024
- * @param value The value to set it to.
3049
+ * @param value The value to set it to. A boolean for `"sendEQSwitch"`.
3025
3050
  * @returns The {@link MIDIMessage} needed to set this GS Insertion Parameter.
3026
3051
  */
3027
3052
  static setGSInsertionParameter(ticks, parameter, value) {
3053
+ const data = typeof value === "boolean" ? value ? 1 : 0 : value;
3028
3054
  switch (parameter) {
3029
- case "type": return this.gsMessage(ticks, 64, 3, 0, [value >> 8 & 127, value & 127]);
3055
+ case "type": return this.gsMessage(ticks, 64, 3, 0, [data >> 8 & 127, data & 127]);
3030
3056
  default:
3031
- if (typeof parameter === "string") return this.gsMessage(ticks, 64, 3, GSInsertionAddressMap[parameter], [value]);
3057
+ if (typeof parameter === "string") return this.gsMessage(ticks, 64, 3, GSInsertionAddressMap[parameter], [data]);
3032
3058
  if (!Number.isInteger(parameter) || parameter < 0 || parameter > 19) throw new Error(`Invalid insertion parameter: ${parameter}`);
3033
- return this.gsMessage(ticks, 64, 3, parameter + 3, [value]);
3059
+ return this.gsMessage(ticks, 64, 3, parameter + 3, [data]);
3034
3060
  }
3035
3061
  }
3036
3062
  /**
@@ -3238,6 +3264,7 @@ var MIDIUtils = class MIDIUtils {
3238
3264
  case "drumMap":
3239
3265
  case "velocitySenseDepth":
3240
3266
  case "velocitySenseOffset": return [MIDIUtils.partParamMessage(ticks, channel, system, parameter, value)];
3267
+ case "eqEnabled": return [MIDIUtils.gsMessage(ticks, 64, 64 | MIDIUtils.channelToGSPart(channel), 32, [value ? 1 : 0])];
3241
3268
  case "efxAssign": return [MIDIUtils.gsMessage(ticks, 64, 64 | MIDIUtils.channelToGSPart(channel), 34, [value ? 1 : 0])];
3242
3269
  case "dryLevel": return [MIDIUtils.xgMessage(ticks, 8, channel, 17, [value])];
3243
3270
  }
@@ -4438,6 +4465,15 @@ var MIDIUtils = class MIDIUtils {
4438
4465
  value
4439
4466
  }];
4440
4467
  }
4468
+ if (a2 === 2) {
4469
+ const parameter = GS_EQUALIZER_PARAMETER_BY_ADDRESS.get(a3);
4470
+ if (parameter === void 0) return [OTHER];
4471
+ return [{
4472
+ type: "GS Equalizer Param",
4473
+ parameter,
4474
+ value
4475
+ }];
4476
+ }
4441
4477
  if (a2 === 3) {
4442
4478
  switch (a3) {
4443
4479
  case 0: return [{
@@ -4447,6 +4483,11 @@ var MIDIUtils = class MIDIUtils {
4447
4483
  }];
4448
4484
  }
4449
4485
  const named = GS_INSERTION_PARAMETER_BY_ADDRESS.get(a3);
4486
+ if (named === "sendEQSwitch") return [{
4487
+ type: "GS Insertion Param",
4488
+ parameter: named,
4489
+ value: value !== 0
4490
+ }];
4450
4491
  if (named !== void 0) return [{
4451
4492
  type: "GS Insertion Param",
4452
4493
  parameter: named,
@@ -4650,6 +4691,12 @@ var MIDIUtils = class MIDIUtils {
4650
4691
  controller: MIDIControllers.bankSelectLSB,
4651
4692
  value
4652
4693
  }];
4694
+ case 32: return [{
4695
+ type: "Channel MIDI Param",
4696
+ channel,
4697
+ parameter: "eqEnabled",
4698
+ value: value === 1
4699
+ }];
4653
4700
  case 34: return [{
4654
4701
  type: "Channel MIDI Param",
4655
4702
  channel,
@@ -5023,6 +5070,7 @@ var MIDIEditor = class {
5023
5070
  gsReverbParams;
5024
5071
  gsChorusParams;
5025
5072
  gsDelayParams;
5073
+ gsEqualizerParams;
5026
5074
  gsInsertionParams;
5027
5075
  xgReverbParams;
5028
5076
  xgChorusParams;
@@ -5050,10 +5098,11 @@ var MIDIEditor = class {
5050
5098
  constructor(midi, opts) {
5051
5099
  this.midi = midi;
5052
5100
  SpessaLog.groupCollapsed("%cApplying changes to the MIDI file...", ConsoleColors.info);
5053
- const { channels, gsReverbParams, gsChorusParams, gsDelayParams, gsInsertionParams, xgReverbParams, xgChorusParams, xgVariationParams, xgInsertionParams, userDrumParams, midiParams } = opts;
5101
+ const { channels, gsReverbParams, gsChorusParams, gsDelayParams, gsEqualizerParams, gsInsertionParams, xgReverbParams, xgChorusParams, xgVariationParams, xgInsertionParams, userDrumParams, midiParams } = opts;
5054
5102
  this.gsReverbParams = gsReverbParams;
5055
5103
  this.gsChorusParams = gsChorusParams;
5056
5104
  this.gsDelayParams = gsDelayParams;
5105
+ this.gsEqualizerParams = gsEqualizerParams;
5057
5106
  this.gsInsertionParams = gsInsertionParams;
5058
5107
  this.xgReverbParams = xgReverbParams;
5059
5108
  this.xgChorusParams = xgChorusParams;
@@ -5126,12 +5175,12 @@ var MIDIEditor = class {
5126
5175
  }
5127
5176
  if (this.lastReset !== void 0 && replacedEvents.has(this.lastReset)) this.system = this.fallbackReset ?? "gs";
5128
5177
  }
5129
- if (!this.addedReset && this.midiParams?.system !== "clear" && (this.midiParams?.system !== void 0 || this.fallbackReset !== void 0 || this.gsReverbParams && this.gsReverbParams !== "clear" || this.gsChorusParams && this.gsChorusParams !== "clear" || this.gsDelayParams && this.gsDelayParams !== "clear" || this.gsInsertionParams && this.gsInsertionParams !== "clear" || this.xgReverbParams && this.xgReverbParams !== "clear" || this.xgChorusParams && this.xgChorusParams !== "clear" || this.xgVariationParams && this.xgVariationParams !== "clear" || [...this.xgInsertionParams?.values() ?? []].some((p) => p !== "clear") || this.userDrumSetParams?.size || [...this.channelChanges.values()].some((c) => c.patch && c.patch !== "clear"))) {
5178
+ if (!this.addedReset && this.midiParams?.system !== "clear" && (this.midiParams?.system !== void 0 || this.fallbackReset !== void 0 || this.gsReverbParams && this.gsReverbParams !== "clear" || this.gsChorusParams && this.gsChorusParams !== "clear" || this.gsDelayParams && this.gsDelayParams !== "clear" || this.gsEqualizerParams && this.gsEqualizerParams !== "clear" || this.gsInsertionParams && this.gsInsertionParams !== "clear" || this.xgReverbParams && this.xgReverbParams !== "clear" || this.xgChorusParams && this.xgChorusParams !== "clear" || this.xgVariationParams && this.xgVariationParams !== "clear" || [...this.xgInsertionParams?.values() ?? []].some((p) => p !== "clear") || this.userDrumSetParams?.size || [...this.channelChanges.values()].some((c) => c.patch && c.patch !== "clear"))) {
5130
5179
  let index = 0;
5131
5180
  const firstTrack = this.midi.tracks[0];
5132
5181
  while (index < firstTrack.events.length && firstTrack.events[index].statusByte < MIDIMessageTypes.noteOff) index++;
5133
5182
  const hasXG = this.xgReverbParams !== void 0 && this.xgReverbParams !== "clear" || this.xgChorusParams !== void 0 && this.xgChorusParams !== "clear" || this.xgVariationParams !== void 0 && this.xgVariationParams !== "clear" || [...this.xgInsertionParams?.values() ?? []].some((p) => p !== "clear");
5134
- const hasGS = this.gsReverbParams !== void 0 && this.gsReverbParams !== "clear" || this.gsChorusParams !== void 0 && this.gsChorusParams !== "clear" || this.gsDelayParams !== void 0 && this.gsDelayParams !== "clear" || this.gsInsertionParams !== void 0 && this.gsInsertionParams !== "clear";
5183
+ const hasGS = this.gsReverbParams !== void 0 && this.gsReverbParams !== "clear" || this.gsChorusParams !== void 0 && this.gsChorusParams !== "clear" || this.gsDelayParams !== void 0 && this.gsDelayParams !== "clear" || this.gsEqualizerParams !== void 0 && this.gsEqualizerParams !== "clear" || this.gsInsertionParams !== void 0 && this.gsInsertionParams !== "clear";
5135
5184
  const targetSystem = this.midiParams?.system ?? this.fallbackReset ?? (hasXG && !hasGS ? "xg" : "gs");
5136
5185
  const event = MIDIUtils.reset(0, targetSystem);
5137
5186
  firstTrack.addEvents(index, event);
@@ -5371,6 +5420,12 @@ var MIDIEditor = class {
5371
5420
  return;
5372
5421
  }
5373
5422
  break;
5423
+ case "GS Equalizer Param":
5424
+ if (this.gsEqualizerParams) {
5425
+ this.deleteCurrentEvent();
5426
+ return;
5427
+ }
5428
+ break;
5374
5429
  case "GS Insertion Param":
5375
5430
  if (this.gsInsertionParams) {
5376
5431
  this.deleteCurrentEvent();
@@ -5785,6 +5840,10 @@ var MIDIEditor = class {
5785
5840
  const p = this.gsDelayParams;
5786
5841
  output.push(MIDIUtils.setGSDelayParameter(targetTicks, "level", p.level), MIDIUtils.setGSDelayParameter(targetTicks, "preLowpass", p.preLowpass), MIDIUtils.setGSDelayParameter(targetTicks, "timeCenter", p.timeCenter), MIDIUtils.setGSDelayParameter(targetTicks, "timeRatioLeft", p.timeRatioLeft), MIDIUtils.setGSDelayParameter(targetTicks, "timeRatioRight", p.timeRatioRight), MIDIUtils.setGSDelayParameter(targetTicks, "levelCenter", p.levelCenter), MIDIUtils.setGSDelayParameter(targetTicks, "levelLeft", p.levelLeft), MIDIUtils.setGSDelayParameter(targetTicks, "levelRight", p.levelRight), MIDIUtils.setGSDelayParameter(targetTicks, "feedback", p.feedback), MIDIUtils.setGSDelayParameter(targetTicks, "sendLevelToReverb", p.sendLevelToReverb));
5787
5842
  }
5843
+ if (this.gsEqualizerParams && this.gsEqualizerParams !== "clear") {
5844
+ const p = this.gsEqualizerParams;
5845
+ for (const name of Object.keys(GSEqualizerAddressMap)) output.push(MIDIUtils.setGSEqualizerParameter(targetTicks, name, p[name]));
5846
+ }
5788
5847
  if (this.gsInsertionParams && this.gsInsertionParams !== "clear") {
5789
5848
  const p = this.gsInsertionParams;
5790
5849
  output.push(MIDIUtils.setGSInsertionParameter(targetTicks, "type", p.type));
@@ -32584,6 +32643,148 @@ var DefaultGSDelay = class {
32584
32643
  this.gainRight = this._levelRight / 127;
32585
32644
  }
32586
32645
  };
32646
+ // src/synthesizer/audio_engine/effects/gs/implementation/equalizer.ts
32647
+ const LOW_FREQUENCIES = [225, 425];
32648
+ const HIGH_FREQUENCIES = [3e3, 6e3];
32649
+ /**
32650
+ * The default GS Equalizer implementation for {@link SpessaSynthProcessor}.
32651
+ *
32652
+ * @group Synthesizer.GS Effects
32653
+ */
32654
+ var DefaultGSEqualizer = class {
32655
+ sampleRate;
32656
+ lowB0 = 1;
32657
+ lowB1 = 0;
32658
+ lowA1 = 0;
32659
+ highB0 = 1;
32660
+ highB1 = 0;
32661
+ highA1 = 0;
32662
+ lowZL = 0;
32663
+ lowZR = 0;
32664
+ highZL = 0;
32665
+ highZR = 0;
32666
+ /**
32667
+ * Constructs a new default GS equalizer processor.
32668
+ * @param sampleRate The sample rate, in Hertz.
32669
+ */
32670
+ constructor(sampleRate) {
32671
+ this.sampleRate = sampleRate;
32672
+ this.updateLow();
32673
+ this.updateHigh();
32674
+ }
32675
+ _lowFrequency = 0;
32676
+ get lowFrequency() {
32677
+ return this._lowFrequency;
32678
+ }
32679
+ set lowFrequency(value) {
32680
+ this._lowFrequency = value;
32681
+ this.updateLow();
32682
+ }
32683
+ _lowGain = 64;
32684
+ get lowGain() {
32685
+ return this._lowGain;
32686
+ }
32687
+ set lowGain(value) {
32688
+ this._lowGain = value;
32689
+ this.updateLow();
32690
+ }
32691
+ _highFrequency = 0;
32692
+ get highFrequency() {
32693
+ return this._highFrequency;
32694
+ }
32695
+ set highFrequency(value) {
32696
+ this._highFrequency = value;
32697
+ this.updateHigh();
32698
+ }
32699
+ _highGain = 64;
32700
+ get highGain() {
32701
+ return this._highGain;
32702
+ }
32703
+ set highGain(value) {
32704
+ this._highGain = value;
32705
+ this.updateHigh();
32706
+ }
32707
+ process(inputLeft, inputRight, outputLeft, outputRight, startIndex, sampleCount) {
32708
+ const { lowB0, lowB1, lowA1, highB0, highB1, highA1 } = this;
32709
+ let { lowZL, lowZR, highZL, highZR } = this;
32710
+ for (let i = 0; i < sampleCount; i++) {
32711
+ const o = startIndex + i;
32712
+ let x = inputLeft[i];
32713
+ let y = lowB0 * x + lowZL;
32714
+ lowZL = lowB1 * x - lowA1 * y;
32715
+ x = y;
32716
+ y = highB0 * x + highZL;
32717
+ highZL = highB1 * x - highA1 * y;
32718
+ outputLeft[o] += y;
32719
+ x = inputRight[i];
32720
+ y = lowB0 * x + lowZR;
32721
+ lowZR = lowB1 * x - lowA1 * y;
32722
+ x = y;
32723
+ y = highB0 * x + highZR;
32724
+ highZR = highB1 * x - highA1 * y;
32725
+ outputRight[o] += y;
32726
+ }
32727
+ this.lowZL = lowZL;
32728
+ this.lowZR = lowZR;
32729
+ this.highZL = highZL;
32730
+ this.highZR = highZR;
32731
+ }
32732
+ getSnapshot() {
32733
+ return {
32734
+ lowFrequency: this._lowFrequency,
32735
+ lowGain: this._lowGain,
32736
+ highFrequency: this._highFrequency,
32737
+ highGain: this._highGain
32738
+ };
32739
+ }
32740
+ applySnapshot(snapshot) {
32741
+ this.lowFrequency = snapshot.lowFrequency;
32742
+ this.lowGain = snapshot.lowGain;
32743
+ this.highFrequency = snapshot.highFrequency;
32744
+ this.highGain = snapshot.highGain;
32745
+ }
32746
+ getBand(frequency, gain) {
32747
+ const fc = Math.min(frequency, this.sampleRate * .45);
32748
+ const dB = Math.max(-12, Math.min(12, gain - 64));
32749
+ return {
32750
+ k: Math.tan(Math.PI * fc / this.sampleRate),
32751
+ g: 10 ** (Math.abs(dB) / 20),
32752
+ cut: dB < 0
32753
+ };
32754
+ }
32755
+ updateLow() {
32756
+ const { k, g, cut } = this.getBand(LOW_FREQUENCIES[this._lowFrequency & 1], this._lowGain);
32757
+ let b0 = 1 + k;
32758
+ let b1 = k - 1;
32759
+ let a0 = 1 + k / g;
32760
+ let a1 = k / g - 1;
32761
+ if (cut) [b0, b1, a0, a1] = [
32762
+ a0,
32763
+ a1,
32764
+ b0,
32765
+ b1
32766
+ ];
32767
+ this.lowB0 = b0 / a0;
32768
+ this.lowB1 = b1 / a0;
32769
+ this.lowA1 = a1 / a0;
32770
+ }
32771
+ updateHigh() {
32772
+ const { k, g, cut } = this.getBand(HIGH_FREQUENCIES[this._highFrequency & 1], this._highGain);
32773
+ let b0 = g * (1 + k);
32774
+ let b1 = g * (k - 1);
32775
+ let a0 = 1 + g * k;
32776
+ let a1 = g * k - 1;
32777
+ if (cut) [b0, b1, a0, a1] = [
32778
+ a0,
32779
+ a1,
32780
+ b0,
32781
+ b1
32782
+ ];
32783
+ this.highB0 = b0 / a0;
32784
+ this.highB1 = b1 / a0;
32785
+ this.highA1 = a1 / a0;
32786
+ }
32787
+ };
32587
32788
  // src/synthesizer/audio_engine/effects/gs/implementation/dsp/gs_effect.ts
32588
32789
  /**
32589
32790
  * The number of EFX parameters (1-20 in the manuals, 0-19 here).
@@ -52388,6 +52589,7 @@ var DefaultGSInsertion = class {
52388
52589
  sendLevelToReverb = DEFAULT_SEND_LEVEL_TO_REVERB;
52389
52590
  sendLevelToChorus = 0;
52390
52591
  sendLevelToDelay = 0;
52592
+ sendEQSwitch = true;
52391
52593
  controlSource1 = GSInsertionControlSources.off;
52392
52594
  controlDepth1 = DEFAULT_CONTROL_DEPTH;
52393
52595
  controlSource2 = GSInsertionControlSources.off;
@@ -52457,6 +52659,7 @@ var DefaultGSInsertion = class {
52457
52659
  this.sendLevelToReverb = DEFAULT_SEND_LEVEL_TO_REVERB;
52458
52660
  this.sendLevelToChorus = 0;
52459
52661
  this.sendLevelToDelay = 0;
52662
+ this.sendEQSwitch = true;
52460
52663
  }
52461
52664
  getType() {
52462
52665
  return this.type;
@@ -52513,6 +52716,7 @@ var DefaultGSInsertion = class {
52513
52716
  sendLevelToReverb: this.sendLevelToReverb,
52514
52717
  sendLevelToChorus: this.sendLevelToChorus,
52515
52718
  sendLevelToDelay: this.sendLevelToDelay,
52719
+ sendEQSwitch: this.sendEQSwitch,
52516
52720
  controlSource1: this.controlSource1,
52517
52721
  controlDepth1: this.controlDepth1,
52518
52722
  controlSource2: this.controlSource2,
@@ -52533,6 +52737,7 @@ var DefaultGSInsertion = class {
52533
52737
  this.sendLevelToReverb = snapshot.sendLevelToReverb;
52534
52738
  this.sendLevelToChorus = snapshot.sendLevelToChorus;
52535
52739
  this.sendLevelToDelay = snapshot.sendLevelToDelay;
52740
+ this.sendEQSwitch = snapshot.sendEQSwitch ?? true;
52536
52741
  }
52537
52742
  /**
52538
52743
  * Sends the offsets of both control sources to the processor.
@@ -54365,6 +54570,7 @@ const DEFAULT_CHANNEL_MIDI_PARAMETERS = {
54365
54570
  randomPan: false,
54366
54571
  assignMode: 2,
54367
54572
  efxAssign: false,
54573
+ eqEnabled: true,
54368
54574
  cc1: 16,
54369
54575
  cc2: 17,
54370
54576
  drumMap: 0,
@@ -55239,6 +55445,7 @@ const DEFAULT_GLOBAL_SYSTEM_PARAMETERS = {
55239
55445
  xgChorusLock: false,
55240
55446
  gsDelayGain: 1,
55241
55447
  gsDelayLock: false,
55448
+ gsEqualizerLock: false,
55242
55449
  xgVariationGain: 1,
55243
55450
  xgVariationLock: false,
55244
55451
  gsInsertionLock: false,
@@ -57251,6 +57458,27 @@ function rolandSystemExclusive(syx, channelOffset = 0) {
57251
57458
  }
57252
57459
  break;
57253
57460
  }
57461
+ if (a2 === 2) {
57462
+ if (this.systemParameters.gsEqualizerLock) return;
57463
+ const parameter = [
57464
+ "lowFrequency",
57465
+ "lowGain",
57466
+ "highFrequency",
57467
+ "highGain"
57468
+ ][a3];
57469
+ if (parameter === void 0) {
57470
+ SpessaLog.gsFail("Equalizer", [a3]);
57471
+ return;
57472
+ }
57473
+ this.gsEqualizerProcessor[parameter] = data;
57474
+ SpessaLog.gsInfo(`EQ ${parameter}`, data);
57475
+ this.callEvent("effectChange", {
57476
+ effect: "gsEqualizer",
57477
+ parameter,
57478
+ value: data
57479
+ });
57480
+ return;
57481
+ }
57254
57482
  if (a2 === 3) {
57255
57483
  if (this.systemParameters.gsInsertionLock) return;
57256
57484
  const insertion = this.gsInsertionBlock;
@@ -57327,6 +57555,17 @@ function rolandSystemExclusive(syx, channelOffset = 0) {
57327
57555
  });
57328
57556
  return;
57329
57557
  }
57558
+ case 31: {
57559
+ const on = data !== 0;
57560
+ insertion.sendEQSwitch = on;
57561
+ SpessaLog.gsInfo("EFX Send EQ Switch", on ? "ON" : "OFF");
57562
+ this.callEvent("effectChange", {
57563
+ effect: "gsInsertion",
57564
+ parameter: "sendEQSwitch",
57565
+ value: on
57566
+ });
57567
+ return;
57568
+ }
57330
57569
  }
57331
57570
  }
57332
57571
  if (a2 >> 4 === 1) {
@@ -57513,6 +57752,12 @@ function rolandSystemExclusive(syx, channelOffset = 0) {
57513
57752
  case 1:
57514
57753
  ch.controllerChange(MIDIControllers.bankSelectLSB, data);
57515
57754
  break;
57755
+ case 32: {
57756
+ const eq = data === 1;
57757
+ ch.setMIDIParameter("eqEnabled", eq);
57758
+ SpessaLog.gsInfo(`EQ for ${channel}`, eq ? "ON" : "OFF");
57759
+ break;
57760
+ }
57516
57761
  case 34: {
57517
57762
  const efx = data === 1;
57518
57763
  ch.setMIDIParameter("efxAssign", efx);
@@ -58860,6 +59105,7 @@ function applySnapshot(snapshot) {
58860
59105
  this.gsReverbProcessor.applySnapshot(snapshot.gsReverbProcessor);
58861
59106
  this.gsChorusProcessor.applySnapshot(snapshot.gsChorusProcessor);
58862
59107
  this.gsDelayProcessor.applySnapshot(snapshot.gsDelayProcessor);
59108
+ if (snapshot.gsEqualizerProcessor) this.gsEqualizerProcessor.applySnapshot(snapshot.gsEqualizerProcessor);
58863
59109
  this.gsInsertionBlock.applySnapshot(snapshot.gsInsertionBlock);
58864
59110
  this.xgReverbBlock.applySnapshot(snapshot.xgReverbBlock);
58865
59111
  this.xgChorusBlock.applySnapshot(snapshot.xgChorusBlock);
@@ -58888,6 +59134,7 @@ function getSynthesizerSnapshot() {
58888
59134
  gsReverbProcessor: this.gsReverbProcessor.getSnapshot(),
58889
59135
  gsChorusProcessor: this.gsChorusProcessor.getSnapshot(),
58890
59136
  gsDelayProcessor: this.gsDelayProcessor.getSnapshot(),
59137
+ gsEqualizerProcessor: this.gsEqualizerProcessor.getSnapshot(),
58891
59138
  gsInsertionBlock: this.gsInsertionBlock.getSnapshot(),
58892
59139
  xgReverbBlock: this.xgReverbBlock.getSnapshot(),
58893
59140
  xgChorusBlock: this.xgChorusBlock.getSnapshot(),
@@ -59002,6 +59249,16 @@ const GS_CHORUS_MACRO_PARAMETERS = [
59002
59249
  "sendLevelToDelay"
59003
59250
  ];
59004
59251
  /**
59252
+ * The default GS equalizer parameters (SC-8850 manual p.236).
59253
+ */
59254
+ const DEFAULT_GS_EQUALIZER = {
59255
+ lowFrequency: 0,
59256
+ lowGain: 64,
59257
+ highFrequency: 0,
59258
+ highGain: 64
59259
+ };
59260
+ const GS_EQUALIZER_PARAMETERS = Object.keys(DEFAULT_GS_EQUALIZER);
59261
+ /**
59005
59262
  * The GS delay parameters set by the delay macro.
59006
59263
  */
59007
59264
  const GS_DELAY_MACRO_PARAMETERS = [
@@ -59104,6 +59361,16 @@ var SpessaSynthProcessor = class {
59104
59361
  */
59105
59362
  insertionInputR;
59106
59363
  /**
59364
+ * The GS equalizer's left input buffer.
59365
+ * @internal
59366
+ */
59367
+ equalizerInputL;
59368
+ /**
59369
+ * The GS equalizer's right input buffer.
59370
+ * @internal
59371
+ */
59372
+ equalizerInputR;
59373
+ /**
59107
59374
  * The GS reverb processor's input buffer.
59108
59375
  * @internal
59109
59376
  */
@@ -59347,6 +59614,13 @@ var SpessaSynthProcessor = class {
59347
59614
  */
59348
59615
  gsDelayProcessor;
59349
59616
  /**
59617
+ * The synthesizer's GS equalizer processor.
59618
+ *
59619
+ * Used when {@link GlobalMIDIParameter.system} is `gm` `gm2` or `gs`.
59620
+ * @internal
59621
+ */
59622
+ gsEqualizerProcessor;
59623
+ /**
59350
59624
  * The synthesizer's XG reverb block.
59351
59625
  *
59352
59626
  * Used when {@link GlobalMIDIParameter.system} is `xg`.
@@ -59430,6 +59704,7 @@ var SpessaSynthProcessor = class {
59430
59704
  this.gsReverbProcessor = options.gsReverbProcessor ?? new DefaultGSReverb(sampleRate, bufSize);
59431
59705
  this.gsChorusProcessor = options.gsChorusProcessor ?? new DefaultGSChorus(sampleRate, bufSize);
59432
59706
  this.gsDelayProcessor = options.gsDelayProcessor ?? new DefaultGSDelay(sampleRate, bufSize);
59707
+ this.gsEqualizerProcessor = options.gsEqualizerProcessor ?? new DefaultGSEqualizer(sampleRate);
59433
59708
  this.gsInsertionBlock = options.gsInsertionBlock ?? new DefaultGSInsertion(sampleRate, bufSize);
59434
59709
  this.xgReverbBlock = options.xgReverbBlock ?? new DefaultXGReverb(sampleRate, bufSize);
59435
59710
  this.xgChorusBlock = options.xgChorusBlock ?? new DefaultXGChorus(sampleRate, bufSize);
@@ -59439,6 +59714,8 @@ var SpessaSynthProcessor = class {
59439
59714
  this.sharedVoiceSendBuffer = new VoiceSendBuffer(bufSize);
59440
59715
  this.insertionInputL = new Float32Array(bufSize);
59441
59716
  this.insertionInputR = new Float32Array(bufSize);
59717
+ this.equalizerInputL = new Float32Array(bufSize);
59718
+ this.equalizerInputR = new Float32Array(bufSize);
59442
59719
  this.gsReverbInput = new Float32Array(bufSize);
59443
59720
  this.gsChorusInput = new Float32Array(bufSize);
59444
59721
  this.gsDelayInput = new Float32Array(bufSize);
@@ -59833,6 +60110,17 @@ var SpessaSynthProcessor = class {
59833
60110
  this.setReverbMacro(4);
59834
60111
  this.setChorusMacro(2);
59835
60112
  this.setDelayMacro(0);
60113
+ if (!this.systemParameters.gsEqualizerLock) {
60114
+ const eq = this.gsEqualizerProcessor;
60115
+ for (const parameter of GS_EQUALIZER_PARAMETERS) {
60116
+ eq[parameter] = DEFAULT_GS_EQUALIZER[parameter];
60117
+ this.callEvent("effectChange", {
60118
+ effect: "gsEqualizer",
60119
+ parameter,
60120
+ value: eq[parameter]
60121
+ });
60122
+ }
60123
+ }
59836
60124
  if (!this.systemParameters.gsInsertionLock) {
59837
60125
  this.gsInsertionBlock.reset();
59838
60126
  this.callEvent("effectChange", {
@@ -59989,10 +60277,10 @@ var SpessaSynthProcessor = class {
59989
60277
  * │ │ │ Chorus │ │
59990
60278
  * │ │ │ Processor ├──────────┐ │
59991
60279
  * │ │ └─┬──────────┬──┘ │ │
59992
- * │ │ │ │ │ │
59993
- * │ │ │ │ │ │
59994
- * │ │ │ │ │ │
59995
- * │ │ │ │ │ │
60280
+ * ┌────┴────┐ │ │ │ │ │
60281
+ * │Equalizer│ │ │ │ │ │
60282
+ * │or Bypass│ │ │ │ │ │
60283
+ * └────┬────┘ │ │ │ │ │
59996
60284
  * │ │ │ 𜸊 𜸊 𜸊
59997
60285
  * │ │ │ ┌────────┴───────┐ ┌─┴─────┴────────┐
59998
60286
  * │ └───┼>┤ Delay ├─>>┤ Reverb │
@@ -60021,6 +60309,7 @@ var SpessaSynthProcessor = class {
60021
60309
  const isXG = this.midiParameters.system === "xg";
60022
60310
  const variationInsertion = this.xgVariationBlock.insertionMode;
60023
60311
  const fx = this.systemParameters.effectsEnabled;
60312
+ const eq = fx && !isXG;
60024
60313
  if (isXG && fx) {
60025
60314
  for (const channel of this.midiChannels) channel.xgInsertionAssigned = false;
60026
60315
  for (const insertion of this.xgInsertionBlocks) {
@@ -60036,6 +60325,10 @@ var SpessaSynthProcessor = class {
60036
60325
  this.insertionInputL.fill(0);
60037
60326
  this.insertionInputR.fill(0);
60038
60327
  }
60328
+ if (eq) {
60329
+ this.equalizerInputL.fill(0);
60330
+ this.equalizerInputR.fill(0);
60331
+ }
60039
60332
  for (const c of this.midiChannels) {
60040
60333
  c.clearVoiceCount();
60041
60334
  if (c.outputDirty) {
@@ -60121,6 +60414,15 @@ var SpessaSynthProcessor = class {
60121
60414
  }
60122
60415
  continue;
60123
60416
  }
60417
+ if (eq && midiParameters.eqEnabled) {
60418
+ const equalizerL = this.equalizerInputL;
60419
+ const equalizerR = this.equalizerInputR;
60420
+ for (let i = 0; i < sampleCount; i++) {
60421
+ equalizerL[i] += outputLeft[i];
60422
+ equalizerR[i] += outputRight[i];
60423
+ }
60424
+ continue;
60425
+ }
60124
60426
  if (isXG && !variationInsertion) {
60125
60427
  const gain = midiParameters.dryLevel / 127;
60126
60428
  for (let i = 0; i < sampleCount; i++) {
@@ -60141,7 +60443,12 @@ var SpessaSynthProcessor = class {
60141
60443
  this.xgChorusBlock.process(this.xgChorusInputL, this.xgChorusInputR, left, right, this.xgReverbInputL, this.xgReverbInputR, startIndex, sampleCount);
60142
60444
  this.xgReverbBlock.process(this.xgReverbInputL, this.xgReverbInputR, left, right, startIndex, sampleCount);
60143
60445
  } else {
60144
- if (this.gsInsertionActive) this.gsInsertionBlock.process(insertionInputL, insertionInputR, left, right, gsReverbInput, gsChorusInput, gsDelayInput, startIndex, sampleCount);
60446
+ if (this.gsInsertionActive) {
60447
+ const insertion = this.gsInsertionBlock;
60448
+ const toEqualizer = eq && insertion.sendEQSwitch;
60449
+ insertion.process(insertionInputL, insertionInputR, toEqualizer ? this.equalizerInputL : left, toEqualizer ? this.equalizerInputR : right, gsReverbInput, gsChorusInput, gsDelayInput, toEqualizer ? 0 : startIndex, sampleCount);
60450
+ }
60451
+ if (eq) this.gsEqualizerProcessor.process(this.equalizerInputL, this.equalizerInputR, left, right, startIndex, sampleCount);
60145
60452
  this.gsChorusProcessor.process(gsChorusInput, left, right, gsReverbInput, gsDelayInput, startIndex, sampleCount);
60146
60453
  if (this.gsDelayActive) this.gsDelayProcessor.process(gsDelayInput, left, right, gsReverbInput, startIndex, sampleCount);
60147
60454
  this.gsReverbProcessor.process(gsReverbInput, left, right, startIndex, sampleCount);
@@ -60716,6 +61023,6 @@ var SpessaSynthProcessor = class {
60716
61023
  return (isGMGSDrum ? 128 + patch.bankMSB : patch.bankMSB) + bankLSB * 256 + program * 32768 + 4194304 * midiNote + 536870912 * velocity;
60717
61024
  }
60718
61025
  };
60719
- export { BasicInstrument, BasicInstrumentZone, BasicLinkedZone, BasicMIDI, BasicPreset, BasicPresetZone, BasicSample, BasicSoundBank, BasicZone, DEFAULT_CHANNEL_MIDI_PARAMETERS, DEFAULT_CHANNEL_SYSTEM_PARAMETERS, DEFAULT_DRUM_REVERB, DEFAULT_GLOBAL_MIDI_PARAMETERS, DEFAULT_GLOBAL_SYSTEM_PARAMETERS, DEFAULT_MIDI_CONTROLLERS, DEFAULT_SOUND_BANK_CLONE_OPTIONS, DEFAULT_SOUND_BANK_LOAD_OPTIONS, DEFAULT_SYNTH_MODE, DefaultGSChorus, DefaultGSDelay, DefaultGSInsertion, DefaultGSReverb, DefaultXGChorus, DefaultXGEffect, DefaultXGInsertion, DefaultXGReverb, DefaultXGSystemEffect, DefaultXGVariation, DrumParameterUtils, EmptySample, GENERATORS_AMOUNT, GSInsertionControlSources, Generator, GeneratorLimits, GeneratorTypes, INSTRUMENT_ONLY_GENERATORS, IndexedByteArray, InterpolationTypes, MAX_GENERATOR, MIDIBuilder, MIDIChannel, MIDIControllers, MIDIMessage, MIDIMessageTypes, MIDIPatchTools, MIDITrack, MIDIUtils, MIDI_DRUM_CHANNEL, Modulator, ModulatorControllerSources, ModulatorCurveTypes, ModulatorSource, ModulatorTransformTypes, NonRegisteredParameterTypesLSB, NonRegisteredParameterTypesMSB, RegisteredParameterTypes, SPESSASYNTH_GAIN_FACTOR, SPESSA_BUFSIZE, SampleTypes, SoundBankCloner, SoundBankLoader, SoundBankManager, SpessaLog, SpessaSynthCoreUtils, SpessaSynthProcessor, SpessaSynthSequencer, VOICE_CAP, audioToWav };
61026
+ export { BasicInstrument, BasicInstrumentZone, BasicLinkedZone, BasicMIDI, BasicPreset, BasicPresetZone, BasicSample, BasicSoundBank, BasicZone, DEFAULT_CHANNEL_MIDI_PARAMETERS, DEFAULT_CHANNEL_SYSTEM_PARAMETERS, DEFAULT_DRUM_REVERB, DEFAULT_GLOBAL_MIDI_PARAMETERS, DEFAULT_GLOBAL_SYSTEM_PARAMETERS, DEFAULT_MIDI_CONTROLLERS, DEFAULT_SOUND_BANK_CLONE_OPTIONS, DEFAULT_SOUND_BANK_LOAD_OPTIONS, DEFAULT_SYNTH_MODE, DefaultGSChorus, DefaultGSDelay, DefaultGSEqualizer, DefaultGSInsertion, DefaultGSReverb, DefaultXGChorus, DefaultXGEffect, DefaultXGInsertion, DefaultXGReverb, DefaultXGSystemEffect, DefaultXGVariation, DrumParameterUtils, EmptySample, GENERATORS_AMOUNT, GSInsertionControlSources, Generator, GeneratorLimits, GeneratorTypes, INSTRUMENT_ONLY_GENERATORS, IndexedByteArray, InterpolationTypes, MAX_GENERATOR, MIDIBuilder, MIDIChannel, MIDIControllers, MIDIMessage, MIDIMessageTypes, MIDIPatchTools, MIDITrack, MIDIUtils, MIDI_DRUM_CHANNEL, Modulator, ModulatorControllerSources, ModulatorCurveTypes, ModulatorSource, ModulatorTransformTypes, NonRegisteredParameterTypesLSB, NonRegisteredParameterTypesMSB, RegisteredParameterTypes, SPESSASYNTH_GAIN_FACTOR, SPESSA_BUFSIZE, SampleTypes, SoundBankCloner, SoundBankLoader, SoundBankManager, SpessaLog, SpessaSynthCoreUtils, SpessaSynthProcessor, SpessaSynthSequencer, VOICE_CAP, audioToWav };
60720
61027
 
60721
61028
  //# sourceMappingURL=index.js.map