From cf7fbb8e36bf3d733b39daecfcf11379fa4b3700 Mon Sep 17 00:00:00 2001 From: VoidX Date: Sun, 7 Jan 2024 02:37:12 +0100 Subject: [PATCH] FIR crossover filter generation --- Cavern.QuickEQ/Crossover/BasicCrossover.cs | 4 +- Cavern.QuickEQ/Crossover/CavernCrossover.cs | 25 ++++++++---- Cavern.QuickEQ/Crossover/Crossover.cs | 36 ++++++++++++++++-- .../Crossover/SyntheticBiquadCrossover.cs | 38 +++++++++++++------ Cavern.QuickEQ/Equalization/EQGenerator.cs | 2 +- Cavern.QuickEQ/Utilities/FilterAnalyzer.cs | 3 +- Cavern/Filters/Allpass.cs | 2 +- Cavern/Filters/Bandpass.cs | 2 +- Cavern/Filters/BiquadFilter.cs | 14 +++---- Cavern/Filters/Crossover.cs | 1 - Cavern/Filters/HighShelf.cs | 2 +- Cavern/Filters/Highpass.cs | 2 +- Cavern/Filters/LowShelf.cs | 2 +- Cavern/Filters/Lowpass.cs | 2 +- Cavern/Filters/Notch.cs | 2 +- Cavern/Filters/PeakingEQ.cs | 2 +- .../Crossover/BasicCrossover_Tests.cs | 15 ++++++++ .../Crossover/CavernCrossover_Tests.cs | 15 ++++++++ .../SyntheticBiquadCrossover_Tests.cs | 15 ++++++++ Tests/Test.Cavern.QuickEQ/Crossover/_Utils.cs | 27 +++++++++++++ ...{IIRFilterSet.cs => IIRFilterSet_Tests.cs} | 0 21 files changed, 168 insertions(+), 43 deletions(-) create mode 100644 Tests/Test.Cavern.QuickEQ/Crossover/BasicCrossover_Tests.cs create mode 100644 Tests/Test.Cavern.QuickEQ/Crossover/CavernCrossover_Tests.cs create mode 100644 Tests/Test.Cavern.QuickEQ/Crossover/SyntheticBiquadCrossover_Tests.cs create mode 100644 Tests/Test.Cavern.QuickEQ/Crossover/_Utils.cs rename Tests/Test.Cavern.QuickEQ/FilterSet/BaseClasses/{IIRFilterSet.cs => IIRFilterSet_Tests.cs} (100%) diff --git a/Cavern.QuickEQ/Crossover/BasicCrossover.cs b/Cavern.QuickEQ/Crossover/BasicCrossover.cs index b56b0392..d304b834 100644 --- a/Cavern.QuickEQ/Crossover/BasicCrossover.cs +++ b/Cavern.QuickEQ/Crossover/BasicCrossover.cs @@ -15,9 +15,7 @@ public class BasicCrossover : Crossover { /// Channels to route bass to public BasicCrossover(float[] frequencies, bool[] subs) : base(frequencies, subs) { } - /// - /// Attach the crossover to an Equalizer APO configuration file in the making. - /// + /// public override void ExportToEqualizerAPO(List wipConfig) { (float frequency, string[] channelLabels)[] groups = GetCrossoverGroups(); string[] targets = GetSubLabels(); diff --git a/Cavern.QuickEQ/Crossover/CavernCrossover.cs b/Cavern.QuickEQ/Crossover/CavernCrossover.cs index 592f945e..a3bc6893 100644 --- a/Cavern.QuickEQ/Crossover/CavernCrossover.cs +++ b/Cavern.QuickEQ/Crossover/CavernCrossover.cs @@ -1,11 +1,13 @@ using System.Collections.Generic; using System.Globalization; +using Cavern.QuickEQ.Equalization; + namespace Cavern.QuickEQ.Crossover { /// /// A FIR brickwall crossover, first introduced in Cavern. /// - internal class CavernCrossover : BasicCrossover { + public class CavernCrossover : BasicCrossover { /// /// Creates a FIR brickwall crossover, first introduced in Cavern. /// @@ -13,24 +15,31 @@ internal class CavernCrossover : BasicCrossover { /// Channels to route bass to public CavernCrossover(float[] frequencies, bool[] subs) : base(frequencies, subs) { } - /// - /// Add the filter's interpretation of highpass to the previously selected channel in a WIP configuration file. - /// + /// public override void AddHighpass(List wipConfig, float frequency) { float offsetFreq = frequency * .967741875f; // Removes crossover notch caused by FIR resolution wipConfig.Add($"GraphicEQ: {(offsetFreq - 1).ToString(CultureInfo.InvariantCulture)} -48;" + $" {offsetFreq.ToString(CultureInfo.InvariantCulture)} 0"); } - /// - /// Add the filter's interpretation of lowpass to the previously selected channel in a WIP configuration file. - /// - /// Don't forget to call AddExtraOperations, this is generally the best place for it. + /// + public override float[] GetHighpass(int sampleRate, float frequency, int length) => new Equalizer(new List { + new Band(frequency * .967741875f, -48), // Removes crossover notch caused by FIR resolution + new Band(frequency, 0) + }, true).GetConvolution(sampleRate, length); + + /// public override void AddLowpass(List wipConfig, float frequency) { float offsetFreq = frequency * 1.032258f; // Removes crossover notch caused by FIR resolution wipConfig.Add($"GraphicEQ: {offsetFreq.ToString(CultureInfo.InvariantCulture)} 0;" + $" {(offsetFreq + 1).ToString(CultureInfo.InvariantCulture)} -48"); AddExtraOperations(wipConfig); } + + /// + public override float[] GetLowpass(int sampleRate, float frequency, int length) => new Equalizer(new List { + new Band(frequency, 0), + new Band(frequency * 1.032258f, -48) // Removes crossover notch caused by FIR resolution + }, true).GetConvolution(sampleRate, length); } } \ No newline at end of file diff --git a/Cavern.QuickEQ/Crossover/Crossover.cs b/Cavern.QuickEQ/Crossover/Crossover.cs index aeafe081..0b77e0e3 100644 --- a/Cavern.QuickEQ/Crossover/Crossover.cs +++ b/Cavern.QuickEQ/Crossover/Crossover.cs @@ -3,6 +3,7 @@ using System.Linq; using Cavern.Channels; +using Cavern.Filters; namespace Cavern.QuickEQ.Crossover { /// @@ -24,7 +25,7 @@ public enum CrossoverType { } /// - /// A crossover to modify an Equalizer APO configuration file with. + /// A crossover to be exported as FIR filters or written into an Equalizer APO configuration file. /// public abstract class Crossover { /// @@ -67,13 +68,24 @@ public static Crossover Create(CrossoverType type, float[] frequencies, bool[] s }; } + /// + /// Generate a 2nd order impulse response for a simple filter. + /// + static float[] Simulate(BiquadFilter filter, int length) { + float[] impulse = new float[length]; + impulse[0] = 1; + filter.Process(impulse); + ((BiquadFilter)filter.Clone()).Process(impulse); + return impulse; + } + /// /// Attach the crossover to an Equalizer APO configuration file in the making. /// public abstract void ExportToEqualizerAPO(List wipConfig); /// - /// Add the filter's interpretation of highpass to the previously selected channel in a WIP configuration file. + /// Add the filter's interpretation of highpass to the previously selected channel in an Equalizer APO configuration file. /// public virtual void AddHighpass(List wipConfig, float frequency) { string hpf = $"Filter: ON HP Fc {frequency} Hz"; @@ -82,7 +94,16 @@ public virtual void AddHighpass(List wipConfig, float frequency) { } /// - /// Add the filter's interpretation of lowpass to the previously selected channel in a WIP configuration file. + /// Get a FIR filter for the highpass part of the crossover. + /// + /// Filter sample rate + /// Highpass cutoff point + /// Filter length in samples + public virtual float[] GetHighpass(int sampleRate, float frequency, int length) => + Simulate(new Highpass(sampleRate, frequency), length); + + /// + /// Add the filter's interpretation of lowpass to the previously selected channel in an Equalizer APO configuration file. /// /// Don't forget to call , this is generally the best place for it. public virtual void AddLowpass(List wipConfig, float frequency) { @@ -92,6 +113,15 @@ public virtual void AddLowpass(List wipConfig, float frequency) { AddExtraOperations(wipConfig); } + /// + /// Get a FIR filter for the lowpass part of the crossover. + /// + /// Filter sample rate + /// Lowpass cutoff point + /// Filter length in samples + public virtual float[] GetLowpass(int sampleRate, float frequency, int length) => + Simulate(new Lowpass(sampleRate, frequency), length); + /// /// Get the labels of channels to route bass to. /// diff --git a/Cavern.QuickEQ/Crossover/SyntheticBiquadCrossover.cs b/Cavern.QuickEQ/Crossover/SyntheticBiquadCrossover.cs index fd6ee976..7b224b6b 100644 --- a/Cavern.QuickEQ/Crossover/SyntheticBiquadCrossover.cs +++ b/Cavern.QuickEQ/Crossover/SyntheticBiquadCrossover.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using Cavern.Filters; +using Cavern.QuickEQ.Equalization; using Cavern.QuickEQ.Utilities; namespace Cavern.QuickEQ.Crossover { @@ -16,25 +17,40 @@ public class SyntheticBiquadCrossover : BasicCrossover { public SyntheticBiquadCrossover(float[] frequencies, bool[] subs) : base(frequencies, subs) { } /// - /// Add the filter's interpretation of highpass to the previously selected channel in a WIP configuration file. + /// Get a instance for a 2nd order . /// - public override void AddHighpass(List wipConfig, float frequency) { + static FilterAnalyzer GetAnalyzer(BiquadFilter filter) { ComplexFilter bw = new ComplexFilter(); - bw.Filters.Add(new Highpass(48000, frequency)); - bw.Filters.Add(new Highpass(48000, frequency)); - wipConfig.Add(new FilterAnalyzer(bw, 48000).ToEqualizer(10, 480, 1 / 24.0).ExportToEqualizerAPO()); + bw.Filters.Add(filter); + bw.Filters.Add((BiquadFilter)filter.Clone()); + return new FilterAnalyzer(bw, filter.SampleRate); } /// - /// Add the filter's interpretation of lowpass to the previously selected channel in a WIP configuration file. + /// Get a FIR filter for a 2nd order biquad filter's response in minimum phase. /// - /// Don't forget to call AddExtraOperations, this is generally the best place for it. + static float[] GetImpulse(BiquadFilter filter, int length) { + FilterAnalyzer analyzer = GetAnalyzer(filter); + analyzer.Resolution = length; + return analyzer.ToEqualizer(10, 20000, 1 / 12.0).GetConvolution(filter.SampleRate, length); + } + + /// + public override void AddHighpass(List wipConfig, float frequency) => + wipConfig.Add(GetAnalyzer(new Highpass(48000, frequency)).ToEqualizer(10, 480, 1 / 24.0).ExportToEqualizerAPO()); + + /// + public override float[] GetHighpass(int sampleRate, float frequency, int length) => + GetImpulse(new Highpass(sampleRate, frequency), length); + + /// public override void AddLowpass(List wipConfig, float frequency) { - ComplexFilter bw = new ComplexFilter(); - bw.Filters.Add(new Lowpass(48000, frequency)); - bw.Filters.Add(new Lowpass(48000, frequency)); - wipConfig.Add(new FilterAnalyzer(bw, 48000).ToEqualizer(10, 480, 1 / 24.0).ExportToEqualizerAPO()); + wipConfig.Add(GetAnalyzer(new Lowpass(48000, frequency)).ToEqualizer(10, 480, 1 / 24.0).ExportToEqualizerAPO()); AddExtraOperations(wipConfig); } + + /// + public override float[] GetLowpass(int sampleRate, float frequency, int length) => + GetImpulse(new Lowpass(sampleRate, frequency), length); } } \ No newline at end of file diff --git a/Cavern.QuickEQ/Equalization/EQGenerator.cs b/Cavern.QuickEQ/Equalization/EQGenerator.cs index 1abbbfce..bc6f41d8 100644 --- a/Cavern.QuickEQ/Equalization/EQGenerator.cs +++ b/Cavern.QuickEQ/Equalization/EQGenerator.cs @@ -191,7 +191,7 @@ public static float[] GetConvolution(this Equalizer eq, int sampleRate, int leng } } eq.Apply(filter, sampleRate); - using (FFTCache cache = new FFTCache(length)) { + using (FFTCache cache = new ThreadSafeFFTCache(length)) { Measurements.MinimumPhaseSpectrum(filter, cache); filter.InPlaceIFFT(cache); } diff --git a/Cavern.QuickEQ/Utilities/FilterAnalyzer.cs b/Cavern.QuickEQ/Utilities/FilterAnalyzer.cs index bcbba7f7..c39a77fa 100644 --- a/Cavern.QuickEQ/Utilities/FilterAnalyzer.cs +++ b/Cavern.QuickEQ/Utilities/FilterAnalyzer.cs @@ -239,7 +239,8 @@ public Equalizer ToEqualizer(double startFreq, double endFreq, double resolution octaveRange = Math.Log(endFreq, 2) - Math.Log(startFreq, 2); int windowSize = (int)(graph.Length / (octaveRange / resolution + 1)); for (int pos = graph.Length - 1; pos >= 0; pos -= windowSize) { - bands.Add(new Band(Math.Pow(10, startPow + powRange * pos), 20 * Math.Log10(graph[pos]))); + double gain = graph[pos] != 0 ? 20 * Math.Log10(graph[pos]) : -150; // -150 dB is the lowest float value + bands.Add(new Band(Math.Pow(10, startPow + powRange * pos), gain)); } bands.Reverse(); return new Equalizer(bands, true); diff --git a/Cavern/Filters/Allpass.cs b/Cavern/Filters/Allpass.cs index 23783f35..cd732a1c 100644 --- a/Cavern/Filters/Allpass.cs +++ b/Cavern/Filters/Allpass.cs @@ -32,7 +32,7 @@ public Allpass(int sampleRate, double centerFreq, double q, double gain) : base( /// /// Create a copy of this filter. /// - public override object Clone() => new Allpass(sampleRate, centerFreq, q, gain); + public override object Clone() => new Allpass(SampleRate, centerFreq, q, gain); /// /// Create a copy of this filter with a changed sampleRate. diff --git a/Cavern/Filters/Bandpass.cs b/Cavern/Filters/Bandpass.cs index f9afdde9..ad7f32a4 100644 --- a/Cavern/Filters/Bandpass.cs +++ b/Cavern/Filters/Bandpass.cs @@ -32,7 +32,7 @@ public Bandpass(int sampleRate, double centerFreq, double q, double gain) : base /// /// Create a copy of this filter. /// - public override object Clone() => new Bandpass(sampleRate, centerFreq, q, gain); + public override object Clone() => new Bandpass(SampleRate, centerFreq, q, gain); /// /// Create a copy of this filter with a changed sampleRate. diff --git a/Cavern/Filters/BiquadFilter.cs b/Cavern/Filters/BiquadFilter.cs index 27c0395f..0b7fad34 100644 --- a/Cavern/Filters/BiquadFilter.cs +++ b/Cavern/Filters/BiquadFilter.cs @@ -8,6 +8,11 @@ namespace Cavern.Filters { /// Simple first-order biquad filter. /// public abstract class BiquadFilter : Filter, ICloneable { + /// + /// Sample rate of the filter. + /// + public int SampleRate { get; protected set; } + /// /// Center frequency (-3 dB point) of the filter. /// @@ -74,11 +79,6 @@ public double Gain { /// protected double gain; - /// - /// Cached sample rate. - /// - protected int sampleRate; - /// /// History sample. /// @@ -107,7 +107,7 @@ protected BiquadFilter(int sampleRate, double centerFreq, double q) : this(sampl /// Q-factor of the filter /// Gain of the filter in decibels protected BiquadFilter(int sampleRate, double centerFreq, double q, double gain) { - this.sampleRate = sampleRate; + SampleRate = sampleRate; Reset(centerFreq, q, gain); } @@ -144,7 +144,7 @@ public void Reset(double centerFreq, double q, double gain) { this.centerFreq = centerFreq; this.q = q; this.gain = gain; - float w0 = (float)(MathF.PI * 2 * centerFreq / sampleRate), cos = (float)Math.Cos(w0), + float w0 = (float)(MathF.PI * 2 * centerFreq / SampleRate), cos = (float)Math.Cos(w0), alpha = (float)(Math.Sin(w0) / (q + q)), divisor = 1 / (1 + alpha); Reset(cos, alpha, divisor); } diff --git a/Cavern/Filters/Crossover.cs b/Cavern/Filters/Crossover.cs index b25128cd..3360ca88 100644 --- a/Cavern/Filters/Crossover.cs +++ b/Cavern/Filters/Crossover.cs @@ -1,5 +1,4 @@ using System; -using System.Runtime.CompilerServices; namespace Cavern.Filters { /// diff --git a/Cavern/Filters/HighShelf.cs b/Cavern/Filters/HighShelf.cs index 5f959723..3e48c795 100644 --- a/Cavern/Filters/HighShelf.cs +++ b/Cavern/Filters/HighShelf.cs @@ -32,7 +32,7 @@ public HighShelf(int sampleRate, double centerFreq, double q, double gain) : bas /// /// Create a copy of this filter. /// - public override object Clone() => new HighShelf(sampleRate, centerFreq, q, gain); + public override object Clone() => new HighShelf(SampleRate, centerFreq, q, gain); /// /// Create a copy of this filter with a changed sampleRate. diff --git a/Cavern/Filters/Highpass.cs b/Cavern/Filters/Highpass.cs index 4c19947c..415a2c95 100644 --- a/Cavern/Filters/Highpass.cs +++ b/Cavern/Filters/Highpass.cs @@ -30,7 +30,7 @@ public Highpass(int sampleRate, double centerFreq, double q, double gain) : base /// /// Create a copy of this filter. /// - public override object Clone() => new Highpass(sampleRate, centerFreq, q, gain); + public override object Clone() => new Highpass(SampleRate, centerFreq, q, gain); /// /// Create a copy of this filter with a changed sampleRate. diff --git a/Cavern/Filters/LowShelf.cs b/Cavern/Filters/LowShelf.cs index 67c8b423..ec4cffb2 100644 --- a/Cavern/Filters/LowShelf.cs +++ b/Cavern/Filters/LowShelf.cs @@ -32,7 +32,7 @@ public LowShelf(int sampleRate, double centerFreq, double q, double gain) : base /// /// Create a copy of this filter. /// - public override object Clone() => new LowShelf(sampleRate, centerFreq, q, gain); + public override object Clone() => new LowShelf(SampleRate, centerFreq, q, gain); /// /// Create a copy of this filter with a changed sampleRate. diff --git a/Cavern/Filters/Lowpass.cs b/Cavern/Filters/Lowpass.cs index 86fc74fc..6456af35 100644 --- a/Cavern/Filters/Lowpass.cs +++ b/Cavern/Filters/Lowpass.cs @@ -30,7 +30,7 @@ public Lowpass(int sampleRate, double centerFreq, double q, double gain) : base( /// /// Create a copy of this filter. /// - public override object Clone() => new Lowpass(sampleRate, centerFreq, q, gain); + public override object Clone() => new Lowpass(SampleRate, centerFreq, q, gain); /// /// Create a copy of this filter with a changed sampleRate. diff --git a/Cavern/Filters/Notch.cs b/Cavern/Filters/Notch.cs index f69f90b3..30e42077 100644 --- a/Cavern/Filters/Notch.cs +++ b/Cavern/Filters/Notch.cs @@ -32,7 +32,7 @@ public Notch(int sampleRate, double centerFreq, double q, double gain) : base(sa /// /// Create a copy of this filter. /// - public override object Clone() => new Notch(sampleRate, centerFreq, q, gain); + public override object Clone() => new Notch(SampleRate, centerFreq, q, gain); /// /// Create a copy of this filter with a changed sampleRate. diff --git a/Cavern/Filters/PeakingEQ.cs b/Cavern/Filters/PeakingEQ.cs index 3927ab9f..084974d5 100644 --- a/Cavern/Filters/PeakingEQ.cs +++ b/Cavern/Filters/PeakingEQ.cs @@ -32,7 +32,7 @@ public PeakingEQ(int sampleRate, double centerFreq, double q, double gain) : bas /// /// Create a copy of this filter. /// - public override object Clone() => new PeakingEQ(sampleRate, centerFreq, q, gain); + public override object Clone() => new PeakingEQ(SampleRate, centerFreq, q, gain); /// /// Create a copy of this filter with a changed sampleRate. diff --git a/Tests/Test.Cavern.QuickEQ/Crossover/BasicCrossover_Tests.cs b/Tests/Test.Cavern.QuickEQ/Crossover/BasicCrossover_Tests.cs new file mode 100644 index 00000000..90654e1b --- /dev/null +++ b/Tests/Test.Cavern.QuickEQ/Crossover/BasicCrossover_Tests.cs @@ -0,0 +1,15 @@ +using Cavern.QuickEQ.Crossover; + +namespace Test.Cavern.QuickEQ.Crossover { + /// + /// Tests the class. + /// + [TestClass] + public class BasicCrossover_Tests { + /// + /// Tests if generates correct impulse responses. + /// + [TestMethod, Timeout(1000)] + public void ImpulseResponse() => Utils.ImpulseResponse(new BasicCrossover(null, null), 0.49152157f, 0.50847834f); + } +} \ No newline at end of file diff --git a/Tests/Test.Cavern.QuickEQ/Crossover/CavernCrossover_Tests.cs b/Tests/Test.Cavern.QuickEQ/Crossover/CavernCrossover_Tests.cs new file mode 100644 index 00000000..3d68ebf4 --- /dev/null +++ b/Tests/Test.Cavern.QuickEQ/Crossover/CavernCrossover_Tests.cs @@ -0,0 +1,15 @@ +using Cavern.QuickEQ.Crossover; + +namespace Test.Cavern.QuickEQ.Crossover { + /// + /// Tests the class. + /// + [TestClass] + public class CavernCrossover_Tests { + /// + /// Tests if generates correct impulse responses. + /// + [TestMethod, Timeout(1000)] + public void ImpulseResponse() => Utils.ImpulseResponse(new CavernCrossover(null, null), 0.4130373f, 0.979050338f); + } +} \ No newline at end of file diff --git a/Tests/Test.Cavern.QuickEQ/Crossover/SyntheticBiquadCrossover_Tests.cs b/Tests/Test.Cavern.QuickEQ/Crossover/SyntheticBiquadCrossover_Tests.cs new file mode 100644 index 00000000..0636a2d8 --- /dev/null +++ b/Tests/Test.Cavern.QuickEQ/Crossover/SyntheticBiquadCrossover_Tests.cs @@ -0,0 +1,15 @@ +using Cavern.QuickEQ.Crossover; + +namespace Test.Cavern.QuickEQ.Crossover { + /// + /// Tests the class. + /// + [TestClass] + public class SyntheticBiquadCrossover_Tests { + /// + /// Tests if generates correct impulse responses. + /// + [TestMethod, Timeout(1000)] + public void ImpulseResponse() => Utils.ImpulseResponse(new SyntheticBiquadCrossover(null, null), 0.47490564f, 0.5231629f); + } +} \ No newline at end of file diff --git a/Tests/Test.Cavern.QuickEQ/Crossover/_Utils.cs b/Tests/Test.Cavern.QuickEQ/Crossover/_Utils.cs new file mode 100644 index 00000000..116d608a --- /dev/null +++ b/Tests/Test.Cavern.QuickEQ/Crossover/_Utils.cs @@ -0,0 +1,27 @@ +using Cavern.Utilities; + +using CrossoverBase = Cavern.QuickEQ.Crossover.Crossover; + +namespace Test.Cavern.QuickEQ.Crossover { + /// + /// Helper functions for testing Crossover derivatives. + /// + class Utils { + public static void ImpulseResponse(CrossoverBase crossover, float expectedHighpassValue, float expectedLowpassValue) { + const int fftSize = 256, + sampleRate = 48000, + crossoverFreq = 10000, + crossoverBand = fftSize * crossoverFreq / sampleRate; + FFTCache cache = new FFTCache(fftSize); + Complex[] highpass = crossover.GetHighpass(sampleRate, crossoverFreq, fftSize).FFT(cache); + Complex[] lowpass = crossover.GetLowpass(sampleRate, crossoverFreq, fftSize).FFT(cache); + + float highpassAtCrossover = highpass[crossoverBand].Magnitude, + lowpassAtCrossover = lowpass[crossoverBand].Magnitude; + Assert.IsTrue(highpassAtCrossover - highpass[crossoverBand + 1].Magnitude < 0); + Assert.AreEqual(expectedHighpassValue, highpassAtCrossover); + Assert.IsTrue(lowpassAtCrossover - lowpass[crossoverBand + 1].Magnitude > 0); + Assert.AreEqual(expectedLowpassValue, lowpassAtCrossover); + } + } +} \ No newline at end of file diff --git a/Tests/Test.Cavern.QuickEQ/FilterSet/BaseClasses/IIRFilterSet.cs b/Tests/Test.Cavern.QuickEQ/FilterSet/BaseClasses/IIRFilterSet_Tests.cs similarity index 100% rename from Tests/Test.Cavern.QuickEQ/FilterSet/BaseClasses/IIRFilterSet.cs rename to Tests/Test.Cavern.QuickEQ/FilterSet/BaseClasses/IIRFilterSet_Tests.cs