From 6edbca899a949a847d36ea501470be6284e32696 Mon Sep 17 00:00:00 2001 From: codengine <498798+codengine@users.noreply.github.com> Date: Mon, 17 Aug 2026 14:25:53 +0200 Subject: [PATCH] remove MVerb, IIR and Speex - Spice86 Core now uses external dependencies --- LICENSE | 13 +- README.md | 21 +- src/Directory.Build.props | 2 +- src/Directory.Packages.props | 1 + .../Filters/IirFilters/Common/Biquad.cs | 224 ----- .../IirFilters/Common/BiquadFilterBase.cs | 31 - .../Filters/IirFilters/Common/Cascade.cs | 189 ----- .../Filters/IirFilters/Common/Constants.cs | 6 - .../IirFilters/Common/Layout/ComplexTypes.cs | 75 -- .../IirFilters/Common/Layout/Layout.cs | 126 --- .../Filters/IirFilters/Common/MathEx.cs | 34 - .../IirFilters/Common/PoleFilterBase.cs | 39 - .../Common/State/DirectFormStates.cs | 75 -- .../IirFilters/Common/State/ISectionState.cs | 9 - .../Common/Transforms/PoleTransforms.cs | 261 ------ .../Filters/Butterworth/AnalogPrototypes.cs | 70 -- .../Filters/Butterworth/ButterworthFilters.cs | 97 --- .../Filters/Butterworth/PublicFilters.cs | 179 ---- .../Filters/ChebyshevI/AnalogPrototypes.cs | 125 --- .../Filters/ChebyshevI/ChebyshevIFilters.cs | 97 --- .../Filters/ChebyshevI/PublicFilters.cs | 172 ---- .../Filters/ChebyshevII/AnalogPrototypes.cs | 123 --- .../Filters/ChebyshevII/ChebyshevIIFilters.cs | 122 --- .../Filters/ChebyshevII/PublicFilters.cs | 167 ---- .../Filters/Custom/CustomFilters.cs | 76 -- .../Filters/Custom/PublicFilters.cs | 109 --- .../IirFilters/Filters/RBJ/PublicFilters.cs | 159 ---- .../IirFilters/Filters/RBJ/RbjFilters.cs | 158 ---- src/Spice86.Audio/Filters/IirFilters/LICENSE | 22 - .../Filters/IirFilters/README.md | 5 - src/Spice86.Audio/Filters/MVerb.cs | 657 --------------- src/Spice86.Audio/Filters/NoiseGate.cs | 4 +- .../Filters/Speex/SpeexResamplerCSharp.cs | 794 ------------------ src/Spice86.Audio/Spice86.Audio.csproj | 1 + 34 files changed, 21 insertions(+), 4222 deletions(-) delete mode 100644 src/Spice86.Audio/Filters/IirFilters/Common/Biquad.cs delete mode 100644 src/Spice86.Audio/Filters/IirFilters/Common/BiquadFilterBase.cs delete mode 100644 src/Spice86.Audio/Filters/IirFilters/Common/Cascade.cs delete mode 100644 src/Spice86.Audio/Filters/IirFilters/Common/Constants.cs delete mode 100644 src/Spice86.Audio/Filters/IirFilters/Common/Layout/ComplexTypes.cs delete mode 100644 src/Spice86.Audio/Filters/IirFilters/Common/Layout/Layout.cs delete mode 100644 src/Spice86.Audio/Filters/IirFilters/Common/MathEx.cs delete mode 100644 src/Spice86.Audio/Filters/IirFilters/Common/PoleFilterBase.cs delete mode 100644 src/Spice86.Audio/Filters/IirFilters/Common/State/DirectFormStates.cs delete mode 100644 src/Spice86.Audio/Filters/IirFilters/Common/State/ISectionState.cs delete mode 100644 src/Spice86.Audio/Filters/IirFilters/Common/Transforms/PoleTransforms.cs delete mode 100644 src/Spice86.Audio/Filters/IirFilters/Filters/Butterworth/AnalogPrototypes.cs delete mode 100644 src/Spice86.Audio/Filters/IirFilters/Filters/Butterworth/ButterworthFilters.cs delete mode 100644 src/Spice86.Audio/Filters/IirFilters/Filters/Butterworth/PublicFilters.cs delete mode 100644 src/Spice86.Audio/Filters/IirFilters/Filters/ChebyshevI/AnalogPrototypes.cs delete mode 100644 src/Spice86.Audio/Filters/IirFilters/Filters/ChebyshevI/ChebyshevIFilters.cs delete mode 100644 src/Spice86.Audio/Filters/IirFilters/Filters/ChebyshevI/PublicFilters.cs delete mode 100644 src/Spice86.Audio/Filters/IirFilters/Filters/ChebyshevII/AnalogPrototypes.cs delete mode 100644 src/Spice86.Audio/Filters/IirFilters/Filters/ChebyshevII/ChebyshevIIFilters.cs delete mode 100644 src/Spice86.Audio/Filters/IirFilters/Filters/ChebyshevII/PublicFilters.cs delete mode 100644 src/Spice86.Audio/Filters/IirFilters/Filters/Custom/CustomFilters.cs delete mode 100644 src/Spice86.Audio/Filters/IirFilters/Filters/Custom/PublicFilters.cs delete mode 100644 src/Spice86.Audio/Filters/IirFilters/Filters/RBJ/PublicFilters.cs delete mode 100644 src/Spice86.Audio/Filters/IirFilters/Filters/RBJ/RbjFilters.cs delete mode 100644 src/Spice86.Audio/Filters/IirFilters/LICENSE delete mode 100644 src/Spice86.Audio/Filters/IirFilters/README.md delete mode 100644 src/Spice86.Audio/Filters/MVerb.cs delete mode 100644 src/Spice86.Audio/Filters/Speex/SpeexResamplerCSharp.cs diff --git a/LICENSE b/LICENSE index dce2f3c..d76ef90 100644 --- a/LICENSE +++ b/LICENSE @@ -202,8 +202,11 @@ limitations under the License. - Third-party libraries are licensed as followed: - - Adlib Gold: Personal permission granted by John Novak from DosBox Staging - - Nuked-OPL3: LGPL-2.1 - - iir1: MIT - - YM7128B_emu: BSD 2-Clause \ No newline at end of file + Third-party components retain their respective licenses: + - SDL-derived audio backends: zlib; see LICENSE.SDL and CREDITS.SDL. + - DOSBox Staging-derived audio code: GPL-2.0-or-later; see + LICENSE.DOSBOXSTAGING and individual source headers. + - Iir1.Net and upstream iir1: MIT; the dependency package includes both + license notices and its third-party notice. + - TAL-NoiseMaker chorus code: GPL-2.0; see individual source headers. + - Master Tom Compressor: BSD-style terms; see Compressor.cs. diff --git a/README.md b/README.md index b4389a0..bd38328 100644 --- a/README.md +++ b/README.md @@ -513,7 +513,7 @@ dotnet test tests/Spice86.Audio.Tests ## License and Credits -The `Spice86.Audio` library itself is licensed under the [Apache License 2.0](LICENSE). +Project-authored `Spice86.Audio` code is licensed under the [Apache License 2.0](LICENSE). Ports and dependencies retain the licenses listed below. It incorporates C# ports of the following third-party components: @@ -523,17 +523,13 @@ It incorporates C# ports of the following third-party components: - Full thanks to the SDL team for their outstanding cross-platform multimedia library. ### DOSBox Staging -- **License:** [GNU GPL v2.0](LICENSE.DOSBOXSTAGING) -- The mixer architecture, `RWQueue`, `AudioFrame`, chorus engine (TAL-NoiseMaker integration), noise gate, MVerb reverb, compressor, and resampling pipeline are all faithful ports of DOSBox Staging's audio subsystem (`src/audio/`, `src/utils/rwqueue.*`). +- **License:** [GNU GPL v2.0 or later](LICENSE.DOSBOXSTAGING) +- `RWQueue`, `AudioFrame`, shared audio configuration types, the chorus engine, noise gate, and compressor are ports of DOSBox Staging's audio subsystem (`src/audio/`, `src/utils/rwqueue.*`). - Full thanks to the DOSBox Staging team and the original DOSBox team. -### IIR Filters (iir1) -- **License:** MIT / GPL v3 (see [`src/Spice86.Audio/Filters/IirFilters/LICENSE`](src/Spice86.Audio/Filters/IirFilters/LICENSE)) -- Port of [iir1](https://github.com/berndporr/iir1) by Vinnie Falco (original DSPFilters) and Bernd Porr. - -### Speex Resampler (libspeex) -- **License:** BSD-style (see libspeex source headers) -- Port of the Speex resampler by Jean-Marc Valin and the Speex project. +### Iir1.Net / iir1 +- **License:** MIT; the [`Iir1.Net`](https://github.com/codengine/Iir1.Net) package includes its own license and the preserved upstream iir1 license. +- `NoiseGate` uses `Iir1.Net`, a managed port of [iir1](https://github.com/berndporr/iir1) by Vinnie Falco and Bernd Porr. ### TAL-NoiseMaker (Chorus & DCBlock) - **License:** [GNU GPL v2.0](https://www.gnu.org/licenses/gpl-2.0.html) @@ -543,7 +539,4 @@ It incorporates C# ports of the following third-party components: - **License:** BSD-style (see `Compressor.cs` header) - `Compressor` is a port of Thomas Scott Stillwell's "Master Tom Compressor" JSFX effect. -### Additional contributors -Work from Patrick Kunz, Martin Eastwood, and John Novak has also been ported. See individual source file headers for details. - -Please see the respective LICENSE files at the root of this repository for the full license texts. +See the linked license files, dependency package notices, and individual source headers for the applicable terms and attribution. diff --git a/src/Directory.Build.props b/src/Directory.Build.props index e48faf3..5aa92ea 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -16,7 +16,7 @@ - 11.4.0 + 11.5.0 true + diff --git a/src/Spice86.Audio/Filters/IirFilters/Common/Biquad.cs b/src/Spice86.Audio/Filters/IirFilters/Common/Biquad.cs deleted file mode 100644 index 3e222a9..0000000 --- a/src/Spice86.Audio/Filters/IirFilters/Common/Biquad.cs +++ /dev/null @@ -1,224 +0,0 @@ -namespace Spice86.Audio.Filters.IirFilters.Common; - -using Spice86.Audio.Filters.IirFilters.Common.Layout; - -using System.Numerics; - -public sealed class Biquad { - internal double A0 = 1.0; - internal double A1; - internal double A2; - internal double B0 = 1.0; - internal double B1; - internal double B2; - - internal Complex Response(double normalizedFrequency) { - double a0 = GetA0(); - double a1 = GetA1(); - double a2 = GetA2(); - double b0 = GetB0(); - double b1 = GetB1(); - double b2 = GetB2(); - - double w = 2.0 * MathEx.DoublePi * normalizedFrequency; - var czn1 = Complex.FromPolarCoordinates(1.0, -w); - var czn2 = Complex.FromPolarCoordinates(1.0, -2.0 * w); - var ct = new Complex(b0 / a0, 0.0); - Complex cb = Complex.One; - ct = MathEx.AddMul(ct, b1 / a0, czn1); - ct = MathEx.AddMul(ct, b2 / a0, czn2); - cb = MathEx.AddMul(cb, a1 / a0, czn1); - cb = MathEx.AddMul(cb, a2 / a0, czn2); - return ct / cb; - } - - internal IReadOnlyList GetPoleZeroPairs() { - return [new BiquadPoleState(this)]; - } - - internal double GetA0() { - return A0; - } - - internal double GetA1() { - return A1 * A0; - } - - internal double GetA2() { - return A2 * A0; - } - - internal double GetB0() { - return B0 * A0; - } - - internal double GetB1() { - return B1 * A0; - } - - internal double GetB2() { - return B2 * A0; - } - - internal void SetCoefficients(double a0, double a1, double a2, double b0, double b1, double b2) { - if (double.IsNaN(a0)) { - throw new ArgumentException("a0 is NaN"); - } - - if (double.IsNaN(a1)) { - throw new ArgumentException("a1 is NaN"); - } - - if (double.IsNaN(a2)) { - throw new ArgumentException("a2 is NaN"); - } - - if (double.IsNaN(b0)) { - throw new ArgumentException("b0 is NaN"); - } - - if (double.IsNaN(b1)) { - throw new ArgumentException("b1 is NaN"); - } - - if (double.IsNaN(b2)) { - throw new ArgumentException("b2 is NaN"); - } - - A0 = a0; - A1 = a1 / a0; - A2 = a2 / a0; - B0 = b0 / a0; - B1 = b1 / a0; - B2 = b2 / a0; - } - - internal void SetOnePole(Complex pole, Complex zero) { - if (pole.Imaginary != 0.0) { - throw new ArgumentException("Imaginary part of pole is non-zero."); - } - - if (zero.Imaginary != 0.0) { - throw new ArgumentException("Imaginary part of zero is non-zero."); - } - - SetCoefficients(1.0, -pole.Real, 0.0, 1.0, -zero.Real, 0.0); - } - - internal void SetTwoPole(Complex pole1, Complex zero1, Complex pole2, Complex zero2) { - const string poleErr = "imaginary parts of both poles need to be 0 or complex conjugate"; - const string zeroErr = "imaginary parts of both zeros need to be 0 or complex conjugate"; - - double a1; - double a2; - - if (pole1.Imaginary != 0.0) { - if (pole2 != Complex.Conjugate(pole1)) { - throw new ArgumentException(poleErr); - } - - a1 = -2.0 * pole1.Real; - a2 = Complex.Abs(pole1) * Complex.Abs(pole1); - } else { - if (pole2.Imaginary != 0.0) { - throw new ArgumentException(poleErr); - } - - a1 = -(pole1.Real + pole2.Real); - a2 = pole1.Real * pole2.Real; - } - - double b1; - double b2; - - if (zero1.Imaginary != 0.0) { - if (zero2 != Complex.Conjugate(zero1)) { - throw new ArgumentException(zeroErr); - } - - b1 = -2.0 * zero1.Real; - b2 = Complex.Abs(zero1) * Complex.Abs(zero1); - } else { - if (zero2.Imaginary != 0.0) { - throw new ArgumentException(zeroErr); - } - - b1 = -(zero1.Real + zero2.Real); - b2 = zero1.Real * zero2.Real; - } - - SetCoefficients(1.0, a1, a2, 1.0, b1, b2); - } - - internal void SetPoleZeroPair(PoleZeroPair pair) { - if (pair.IsSinglePole()) { - SetOnePole(pair.Poles.First, pair.Zeros.First); - } else { - SetTwoPole(pair.Poles.First, pair.Zeros.First, pair.Poles.Second, pair.Zeros.Second); - } - } - - internal void SetPoleZeroForm(BiquadPoleState state) { - SetPoleZeroPair(state); - ApplyScale(state.Gain); - } - - internal void SetIdentity() { - SetCoefficients(1.0, 0.0, 0.0, 1.0, 0.0, 0.0); - } - - internal void ApplyScale(double scale) { - B0 *= scale; - B1 *= scale; - B2 *= scale; - } -} - -internal readonly struct BiquadPoleState { - internal readonly PoleZeroPair Pair; - internal readonly double Gain; - - internal BiquadPoleState(Biquad s) { - double a0 = s.GetA0(); - double a1 = s.GetA1(); - double a2 = s.GetA2(); - double b0 = s.GetB0(); - double b1 = s.GetB1(); - double b2 = s.GetB2(); - - Complex polesFirst; - Complex polesSecond; - Complex zerosFirst; - Complex zerosSecond; - - if (a2 == 0.0 && b2 == 0.0) { - polesFirst = -a1; - zerosFirst = -b0 / b1; - polesSecond = Complex.Zero; - zerosSecond = Complex.Zero; - } else { - var c = Complex.Sqrt(new Complex((a1 * a1) - (4.0 * a0 * a2), 0.0)); - double d = 2.0 * a0; - polesFirst = -(a1 + c) / d; - polesSecond = (c - a1) / d; - if (MathEx.IsNaN(polesFirst) || MathEx.IsNaN(polesSecond)) { - throw new ArgumentException("poles are NaN"); - } - - var cz = Complex.Sqrt(new Complex((b1 * b1) - (4.0 * b0 * b2), 0.0)); - double dz = 2.0 * b0; - zerosFirst = -(b1 + cz) / dz; - zerosSecond = (cz - b1) / dz; - if (MathEx.IsNaN(zerosFirst) || MathEx.IsNaN(zerosSecond)) { - throw new ArgumentException("zeros are NaN"); - } - } - - Pair = new PoleZeroPair(polesFirst, zerosFirst, polesSecond, zerosSecond); - Gain = b0 / a0; - } - - public static implicit operator PoleZeroPair(BiquadPoleState state) { - return state.Pair; - } -} \ No newline at end of file diff --git a/src/Spice86.Audio/Filters/IirFilters/Common/BiquadFilterBase.cs b/src/Spice86.Audio/Filters/IirFilters/Common/BiquadFilterBase.cs deleted file mode 100644 index 4530b11..0000000 --- a/src/Spice86.Audio/Filters/IirFilters/Common/BiquadFilterBase.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace Spice86.Audio.Filters.IirFilters.Common; - -using Spice86.Audio.Filters.IirFilters.Common.State; - -public abstract class BiquadFilterBase - where TState : struct, ISectionState { - // ReSharper disable once RedundantDefaultMemberInitializer - private readonly TState _state = default; - - public Biquad Coefficients { get; } = new(); - - protected void SetCoefficients(double a0, double a1, double a2, double b0, double b1, double b2) { - Coefficients.SetCoefficients(a0, a1, a2, b0, b1, b2); - } - - public void Reset() { - _state.Reset(); - } - - public double Filter(double sample) { - return _state.Process(sample, Coefficients); - } - - public float Filter(float sample) { - return (float)Filter((double)sample); - } - - public double Filter(double sample, ref TState state) { - return state.Process(sample, Coefficients); - } -} \ No newline at end of file diff --git a/src/Spice86.Audio/Filters/IirFilters/Common/Cascade.cs b/src/Spice86.Audio/Filters/IirFilters/Common/Cascade.cs deleted file mode 100644 index 643ea9f..0000000 --- a/src/Spice86.Audio/Filters/IirFilters/Common/Cascade.cs +++ /dev/null @@ -1,189 +0,0 @@ -namespace Spice86.Audio.Filters.IirFilters.Common; - -using Spice86.Audio.Filters.IirFilters.Common.Layout; -using Spice86.Audio.Filters.IirFilters.Common.State; - -using System.Numerics; - -internal readonly struct CascadeStorage { - internal CascadeStorage(int maxStages, Biquad[] stageArray) { - MaxStages = maxStages; - StageArray = stageArray; - } - - internal int MaxStages { get; } - - internal Biquad[] StageArray { get; } -} - -public class Cascade { - private int _maxStages; - private int _numStages; - private Biquad[] _stageArray = []; - - internal void SetCascadeStorage(CascadeStorage storage) { - _numStages = 0; - _maxStages = storage.MaxStages; - _stageArray = storage.StageArray; - } - - internal Complex Response(double normalizedFrequency) { - switch (normalizedFrequency) { - case > 0.5: - throw new ArgumentException("The normalised frequency needs to be =< 0.5."); - case < 0.0: - throw new ArgumentException("The normalised frequency needs to be >= 0."); - } - - double w = 2.0 * MathEx.DoublePi * normalizedFrequency; - var czn1 = Complex.FromPolarCoordinates(1.0, -w); - var czn2 = Complex.FromPolarCoordinates(1.0, -2.0 * w); - Complex numerator = Complex.One; - Complex denominator = Complex.One; - - for (int i = 0; i < _numStages; i++) { - Biquad stage = _stageArray[i]; - var ct = new Complex(stage.GetB0() / stage.GetA0(), 0.0); - Complex cb = Complex.One; - ct = MathEx.AddMul(ct, stage.GetB1() / stage.GetA0(), czn1); - ct = MathEx.AddMul(ct, stage.GetB2() / stage.GetA0(), czn2); - cb = MathEx.AddMul(cb, stage.GetA1() / stage.GetA0(), czn1); - cb = MathEx.AddMul(cb, stage.GetA2() / stage.GetA0(), czn2); - numerator *= ct; - denominator *= cb; - } - - return numerator / denominator; - } - - internal IReadOnlyList GetPoleZeroPairs() { - var result = new List(_numStages); - for (int i = 0; i < _numStages; i++) { - var state = new BiquadPoleState(_stageArray[i]); - result.Add(state); - } - - return result; - } - - internal void ApplyScale(double scale) { - if (_numStages < 1) { - return; - } - - _stageArray[0].ApplyScale(scale); - } - - internal void SetLayout(LayoutBase prototype) { - int numPoles = prototype.GetNumPoles(); - _numStages = (numPoles + 1) / 2; - if (_numStages > _maxStages) { - throw new ArgumentException("Number of stages is larger than the max stages."); - } - - for (int i = 0; i < _maxStages; i++) { - _stageArray[i].SetIdentity(); - } - - for (int i = 0; i < _numStages; i++) { - PoleZeroPair pair = prototype[i]; - _stageArray[i].SetPoleZeroPair(pair); - } - - double target = prototype.GetNormalGain(); - Complex response = Response(prototype.GetNormalW() / (2.0 * MathEx.DoublePi)); - ApplyScale(target / Complex.Abs(response)); - } -} - -internal sealed class CascadeStages - where TState : struct, ISectionState { - private readonly Biquad[] _stages; - private readonly TState[] _states; - - internal CascadeStages(int maxStages) { - _stages = new Biquad[maxStages]; - _states = new TState[maxStages]; - for (int i = 0; i < _stages.Length; i++) { - _stages[i] = new Biquad(); - } - } - - internal CascadeStorage GetCascadeStorage() { - return new CascadeStorage(_stages.Length, _stages); - } - - internal Biquad[] SnapshotStages() { - var copy = new Biquad[_stages.Length]; - for (int i = 0; i < _stages.Length; i++) { - Biquad source = _stages[i]; - var destination = new Biquad { - A0 = source.A0, - A1 = source.A1, - A2 = source.A2, - B0 = source.B0, - B1 = source.B1, - B2 = source.B2 - }; - copy[i] = destination; - } - - return copy; - } - - internal void Reset() { - for (int i = 0; i < _states.Length; i++) { - ref TState state = ref _states[i]; - state.Reset(); - } - } - - internal void Setup(ReadOnlySpan sosCoefficients) { - if (sosCoefficients.Length != _stages.Length * 6) { - throw new ArgumentException("SOS array must match the number of stages."); - } - - int offset = 0; - foreach (Biquad stage in _stages) { - double b0 = sosCoefficients[offset++]; - double b1 = sosCoefficients[offset++]; - double b2 = sosCoefficients[offset++]; - double a0 = sosCoefficients[offset++]; - double a1 = sosCoefficients[offset++]; - double a2 = sosCoefficients[offset++]; - stage.SetCoefficients(a0, a1, a2, b0, b1, b2); - } - } - - internal void Setup(double[,] sosCoefficients) { - if (sosCoefficients.GetLength(0) != _stages.Length || sosCoefficients.GetLength(1) != 6) { - throw new ArgumentException("SOS matrix must be [stageCount,6]."); - } - - for (int i = 0; i < _stages.Length; i++) { - double b0 = sosCoefficients[i, 0]; - double b1 = sosCoefficients[i, 1]; - double b2 = sosCoefficients[i, 2]; - double a0 = sosCoefficients[i, 3]; - double a1 = sosCoefficients[i, 4]; - double a2 = sosCoefficients[i, 5]; - _stages[i].SetCoefficients(a0, a1, a2, b0, b1, b2); - } - } - - internal double Filter(double input) { - double output = input; - for (int i = 0; i < _stages.Length; i++) { - ref TState state = ref _states[i]; - output = state.Process(output, _stages[i]); - } - - return output; - } - - internal float FilterSingle(float input) { - double sample = input; - double filtered = Filter(sample); - return (float)filtered; - } -} \ No newline at end of file diff --git a/src/Spice86.Audio/Filters/IirFilters/Common/Constants.cs b/src/Spice86.Audio/Filters/IirFilters/Common/Constants.cs deleted file mode 100644 index 641a339..0000000 --- a/src/Spice86.Audio/Filters/IirFilters/Common/Constants.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Spice86.Audio.Filters.IirFilters.Common; - -internal static class Constants { - internal const int DefaultFilterOrder = 4; - internal const string OrderTooHigh = "Requested order is too high. Provide a higher order for the template."; -} \ No newline at end of file diff --git a/src/Spice86.Audio/Filters/IirFilters/Common/Layout/ComplexTypes.cs b/src/Spice86.Audio/Filters/IirFilters/Common/Layout/ComplexTypes.cs deleted file mode 100644 index 914dd84..0000000 --- a/src/Spice86.Audio/Filters/IirFilters/Common/Layout/ComplexTypes.cs +++ /dev/null @@ -1,75 +0,0 @@ -namespace Spice86.Audio.Filters.IirFilters.Common.Layout; - -using Spice86.Audio.Filters.IirFilters.Common; - -using System.Numerics; - -public struct ComplexPair { - internal Complex First; - internal Complex Second; - - internal ComplexPair(Complex first, Complex second) { - First = first; - Second = second; - } - - internal ComplexPair(Complex single) { - double imag = Math.Abs(single.Imaginary); - if (imag < 1e-12) { - single = new Complex(single.Real, 0.0); - } - - if (single.Imaginary != 0.0) { - throw new ArgumentException("A single complex number needs to be real.", nameof(single)); - } - - First = single; - Second = Complex.Zero; - } - - internal bool IsReal() { - return Math.Abs(First.Imaginary) < 1e-12 && Math.Abs(Second.Imaginary) < 1e-12; - } - - internal bool IsMatchedPair() { - if (First.Imaginary != 0.0) { - return Second == Complex.Conjugate(First); - } - - return Math.Abs(Second.Imaginary) < 1e-12 && - Second.Real != 0.0 && - First.Real != 0.0; - } - - internal readonly bool IsNaN() { - return MathEx.IsNaN(First) || MathEx.IsNaN(Second); - } -} - -public struct PoleZeroPair { - internal ComplexPair Poles; - internal ComplexPair Zeros; - - internal PoleZeroPair(ComplexPair poles, ComplexPair zeros) { - Poles = poles; - Zeros = zeros; - } - - internal PoleZeroPair(Complex pole, Complex zero) { - Poles = new ComplexPair(pole); - Zeros = new ComplexPair(zero); - } - - internal PoleZeroPair(Complex p1, Complex z1, Complex p2, Complex z2) { - Poles = new ComplexPair(p1, p2); - Zeros = new ComplexPair(z1, z2); - } - - internal readonly bool IsSinglePole() { - return Poles.Second == Complex.Zero && Zeros.Second == Complex.Zero; - } - - internal readonly bool IsNaN() { - return Poles.IsNaN() || Zeros.IsNaN(); - } -} \ No newline at end of file diff --git a/src/Spice86.Audio/Filters/IirFilters/Common/Layout/Layout.cs b/src/Spice86.Audio/Filters/IirFilters/Common/Layout/Layout.cs deleted file mode 100644 index 63f8785..0000000 --- a/src/Spice86.Audio/Filters/IirFilters/Common/Layout/Layout.cs +++ /dev/null @@ -1,126 +0,0 @@ -namespace Spice86.Audio.Filters.IirFilters.Common.Layout; - -using Spice86.Audio.Filters.IirFilters.Common; - -using System.Numerics; - -public class LayoutBase { - private int _maxPoles; - private double _normalGain = 1.0; - private double _normalW; - private int _numPoles; - private PoleZeroPair[] _pairs = []; - - internal LayoutBase() { - } - - internal LayoutBase(int maxPoles, PoleZeroPair[] pairs) { - _maxPoles = maxPoles; - _pairs = pairs; - } - - internal ref readonly PoleZeroPair this[int pairIndex] => ref GetPair(pairIndex); - - internal void SetStorage(LayoutBase other) { - _numPoles = 0; - _maxPoles = other._maxPoles; - _pairs = other._pairs; - } - - internal void Reset() { - _numPoles = 0; - } - - internal int GetNumPoles() { - return _numPoles; - } - - internal int GetMaxPoles() { - return _maxPoles; - } - - internal void Add(Complex pole, Complex zero) { - if ((_numPoles & 1) != 0) { - throw new ArgumentException("Can't add 2nd order after a 1st order filter."); - } - - if (MathEx.IsNaN(pole)) { - throw new ArgumentException("Pole to add is NaN."); - } - - if (MathEx.IsNaN(zero)) { - throw new ArgumentException("Zero to add is NaN."); - } - - _pairs[_numPoles / 2] = new PoleZeroPair(pole, zero); - ++_numPoles; - } - - internal void AddPoleZeroConjugatePairs(Complex pole, Complex zero) { - if ((_numPoles & 1) != 0) { - throw new ArgumentException("Can't add 2nd order after a 1st order filter."); - } - - if (MathEx.IsNaN(pole)) { - throw new ArgumentException("Pole to add is NaN."); - } - - if (MathEx.IsNaN(zero)) { - throw new ArgumentException("Zero to add is NaN."); - } - - _pairs[_numPoles / 2] = new PoleZeroPair( - pole, - zero, - Complex.Conjugate(pole), - Complex.Conjugate(zero)); - _numPoles += 2; - } - - internal void Add(ComplexPair poles, ComplexPair zeros) { - if ((_numPoles & 1) != 0) { - throw new ArgumentException("Can't add 2nd order after a 1st order filter."); - } - - if (!poles.IsMatchedPair()) { - throw new ArgumentException("Poles not complex conjugate."); - } - - if (!zeros.IsMatchedPair()) { - throw new ArgumentException("Zeros not complex conjugate."); - } - - _pairs[_numPoles / 2] = new PoleZeroPair(poles.First, zeros.First, poles.Second, zeros.Second); - _numPoles += 2; - } - - internal ref readonly PoleZeroPair GetPair(int pairIndex) { - if (pairIndex < 0 || pairIndex >= (_numPoles + 1) / 2) { - throw new ArgumentOutOfRangeException(nameof(pairIndex), "Pair index out of bounds."); - } - - return ref _pairs[pairIndex]; - } - - internal double GetNormalW() { - return _normalW; - } - - internal double GetNormalGain() { - return _normalGain; - } - - internal void SetNormal(double w, double g) { - _normalW = w; - _normalGain = g; - } -} - -internal sealed class LayoutStorage { - internal LayoutStorage(int maxPoles) { - var pairs = new PoleZeroPair[(maxPoles + 1) / 2]; - Base = new LayoutBase(maxPoles, pairs); - } - - internal LayoutBase Base { get; } -} \ No newline at end of file diff --git a/src/Spice86.Audio/Filters/IirFilters/Common/MathEx.cs b/src/Spice86.Audio/Filters/IirFilters/Common/MathEx.cs deleted file mode 100644 index 61650a2..0000000 --- a/src/Spice86.Audio/Filters/IirFilters/Common/MathEx.cs +++ /dev/null @@ -1,34 +0,0 @@ -namespace Spice86.Audio.Filters.IirFilters.Common; - -using System.Numerics; - -internal static class MathEx { - internal const double DoublePi = 3.1415926535897932384626433832795028841971; - internal const double DoublePiOverTwo = 1.5707963267948966192313216916397514420986; - internal const double DoubleLn2 = 0.69314718055994530941723212145818; - internal const double DoubleLn10 = 2.3025850929940456840179914546844; - - internal static Complex Infinity() { - return new Complex(double.PositiveInfinity, 0.0); - } - - internal static Complex AddMul(Complex c, double v, Complex c1) { - return new Complex(c.Real + (v * c1.Real), c.Imaginary + (v * c1.Imaginary)); - } - - internal static double Asinh(double x) { - return Math.Log(x + Math.Sqrt((x * x) + 1.0)); - } - - internal static bool IsNaN(double v) { - return double.IsNaN(v); - } - - internal static bool IsNaN(Complex v) { - return IsNaN(v.Real) || IsNaN(v.Imaginary); - } - - internal static bool IsInfinity(Complex v) { - return double.IsInfinity(v.Real) || double.IsInfinity(v.Imaginary); - } -} \ No newline at end of file diff --git a/src/Spice86.Audio/Filters/IirFilters/Common/PoleFilterBase.cs b/src/Spice86.Audio/Filters/IirFilters/Common/PoleFilterBase.cs deleted file mode 100644 index 6ca0060..0000000 --- a/src/Spice86.Audio/Filters/IirFilters/Common/PoleFilterBase.cs +++ /dev/null @@ -1,39 +0,0 @@ -namespace Spice86.Audio.Filters.IirFilters.Common; - -using Spice86.Audio.Filters.IirFilters.Common.Layout; -using Spice86.Audio.Filters.IirFilters.Common.State; - -public abstract class PoleFilterBase : Cascade - where TAnalog : LayoutBase - where TState : struct, ISectionState { - private readonly CascadeStages _stages; - - protected PoleFilterBase(int maxAnalogPoles, int maxDigitalPoles, TAnalog analogPrototype) { - var analogStorage = new LayoutStorage(maxAnalogPoles); - var digitalStorage = new LayoutStorage(maxDigitalPoles); - - AnalogPrototype = analogPrototype; - AnalogPrototype.SetStorage(analogStorage.Base); - DigitalPrototype = digitalStorage.Base; - - _stages = new CascadeStages((maxDigitalPoles + 1) / 2); - SetCascadeStorage(_stages.GetCascadeStorage()); - _stages.Reset(); - } - - protected TAnalog AnalogPrototype { get; } - - protected LayoutBase DigitalPrototype { get; } - - public void Reset() { - _stages.Reset(); - } - - public double Filter(double input) { - return _stages.Filter(input); - } - - public float Filter(float input) { - return _stages.FilterSingle(input); - } -} \ No newline at end of file diff --git a/src/Spice86.Audio/Filters/IirFilters/Common/State/DirectFormStates.cs b/src/Spice86.Audio/Filters/IirFilters/Common/State/DirectFormStates.cs deleted file mode 100644 index a983a9d..0000000 --- a/src/Spice86.Audio/Filters/IirFilters/Common/State/DirectFormStates.cs +++ /dev/null @@ -1,75 +0,0 @@ -namespace Spice86.Audio.Filters.IirFilters.Common.State; - -using Spice86.Audio.Filters.IirFilters.Common; - -public struct DirectFormIState : ISectionState { - private double _x1; - private double _x2; - private double _y1; - private double _y2; - - public void Reset() { - _x1 = 0.0; - _x2 = 0.0; - _y1 = 0.0; - _y2 = 0.0; - } - - public double Process(double input, Biquad section) { - double output = (section.B0 * input) + - (section.B1 * _x1) + - (section.B2 * _x2) - - (section.A1 * _y1) - - (section.A2 * _y2); - - _x2 = _x1; - _y2 = _y1; - _x1 = input; - _y1 = output; - - return output; - } -} - -public struct DirectFormIiState : ISectionState { - private double _v1; - private double _v2; - - public void Reset() { - _v1 = 0.0; - _v2 = 0.0; - } - - public double Process(double input, Biquad section) { - double w = input - (section.A1 * _v1) - (section.A2 * _v2); - double output = (section.B0 * w) + (section.B1 * _v1) + (section.B2 * _v2); - - _v2 = _v1; - _v1 = w; - - return output; - } -} - -public struct TransposedDirectFormIiState : ISectionState { - private double _s1; - private double _s1Prev; - private double _s2; - private double _s2Prev; - - public void Reset() { - _s1 = 0.0; - _s1Prev = 0.0; - _s2 = 0.0; - _s2Prev = 0.0; - } - - public double Process(double input, Biquad section) { - double output = _s1Prev + (section.B0 * input); - _s1 = _s2Prev + (section.B1 * input) - (section.A1 * output); - _s2 = (section.B2 * input) - (section.A2 * output); - _s1Prev = _s1; - _s2Prev = _s2; - return output; - } -} \ No newline at end of file diff --git a/src/Spice86.Audio/Filters/IirFilters/Common/State/ISectionState.cs b/src/Spice86.Audio/Filters/IirFilters/Common/State/ISectionState.cs deleted file mode 100644 index 6f0b631..0000000 --- a/src/Spice86.Audio/Filters/IirFilters/Common/State/ISectionState.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Spice86.Audio.Filters.IirFilters.Common.State; - -using Spice86.Audio.Filters.IirFilters.Common; - -public interface ISectionState { - void Reset(); - - double Process(double input, Biquad coefficients); -} \ No newline at end of file diff --git a/src/Spice86.Audio/Filters/IirFilters/Common/Transforms/PoleTransforms.cs b/src/Spice86.Audio/Filters/IirFilters/Common/Transforms/PoleTransforms.cs deleted file mode 100644 index 123caf8..0000000 --- a/src/Spice86.Audio/Filters/IirFilters/Common/Transforms/PoleTransforms.cs +++ /dev/null @@ -1,261 +0,0 @@ -namespace Spice86.Audio.Filters.IirFilters.Common.Transforms; - -using Spice86.Audio.Filters.IirFilters.Common; -using Spice86.Audio.Filters.IirFilters.Common.Layout; - -using System.Numerics; - -internal sealed class LowPassTransform { - private readonly double _f; - - internal LowPassTransform(double fc, LayoutBase digital, LayoutBase analog) { - if (!(fc < 0.5)) { - throw new ArgumentException("The cutoff frequency needs to be below the Nyquist frequency."); - } - - if (fc < 0.0) { - throw new ArgumentException("Cutoff frequency is negative."); - } - - digital.Reset(); - _f = Math.Tan(MathEx.DoublePi * fc); - - int numPoles = analog.GetNumPoles(); - int pairs = numPoles / 2; - for (int i = 0; i < pairs; i++) { - ref readonly PoleZeroPair pair = ref analog[i]; - digital.AddPoleZeroConjugatePairs(Transform(pair.Poles.First), Transform(pair.Zeros.First)); - } - - if ((numPoles & 1) != 0) { - ref readonly PoleZeroPair pair = ref analog[pairs]; - digital.Add(Transform(pair.Poles.First), Transform(pair.Zeros.First)); - } - - digital.SetNormal(analog.GetNormalW(), analog.GetNormalGain()); - } - - private Complex Transform(Complex c) { - if (MathEx.IsInfinity(c)) { - return new Complex(-1.0, 0.0); - } - - c = _f * c; - return (Complex.One + c) / (Complex.One - c); - } -} - -internal sealed class HighPassTransform { - private readonly double _f; - - internal HighPassTransform(double fc, LayoutBase digital, LayoutBase analog) { - if (!(fc < 0.5)) { - throw new ArgumentException("The cutoff frequency needs to be below the Nyquist frequency."); - } - - if (fc < 0.0) { - throw new ArgumentException("Cutoff frequency is negative."); - } - - digital.Reset(); - _f = 1.0 / Math.Tan(MathEx.DoublePi * fc); - - int numPoles = analog.GetNumPoles(); - int pairs = numPoles / 2; - for (int i = 0; i < pairs; i++) { - ref readonly PoleZeroPair pair = ref analog[i]; - digital.AddPoleZeroConjugatePairs(Transform(pair.Poles.First), Transform(pair.Zeros.First)); - } - - if ((numPoles & 1) != 0) { - ref readonly PoleZeroPair pair = ref analog[pairs]; - digital.Add(Transform(pair.Poles.First), Transform(pair.Zeros.First)); - } - - digital.SetNormal(MathEx.DoublePi - analog.GetNormalW(), analog.GetNormalGain()); - } - - private Complex Transform(Complex c) { - if (MathEx.IsInfinity(c)) { - return Complex.One; - } - - c = _f * c; - return -(Complex.One + c) / (Complex.One - c); - } -} - -internal sealed class BandPassTransform { - private readonly double _a2; - private readonly double _ab2; - private readonly double _b; - private readonly double _b2; - - internal BandPassTransform(double fc, double fw, LayoutBase digital, LayoutBase analog) { - if (!(fc < 0.5)) { - throw new ArgumentException("The cutoff frequency needs to be below the Nyquist frequency."); - } - - if (fc < 0.0) { - throw new ArgumentException("Cutoff frequency is negative."); - } - - digital.Reset(); - - double ww = 2.0 * MathEx.DoublePi * fw; - double wc2 = (2.0 * MathEx.DoublePi * fc) - (ww / 2.0); - double wc = wc2 + ww; - - if (wc2 < 1e-8) { - wc2 = 1e-8; - } - - if (wc > MathEx.DoublePi - 1e-8) { - wc = MathEx.DoublePi - 1e-8; - } - - double a = Math.Cos((wc + wc2) * 0.5) / Math.Cos((wc - wc2) * 0.5); - _b = 1.0 / Math.Tan((wc - wc2) * 0.5); - _a2 = a * a; - _b2 = _b * _b; - double ab = a * _b; - _ab2 = 2.0 * ab; - - int numPoles = analog.GetNumPoles(); - int pairs = numPoles / 2; - for (int i = 0; i < pairs; i++) { - ref readonly PoleZeroPair pair = ref analog[i]; - ComplexPair pole = Transform(pair.Poles.First); - ComplexPair zero = Transform(pair.Zeros.First); - digital.AddPoleZeroConjugatePairs(pole.First, zero.First); - digital.AddPoleZeroConjugatePairs(pole.Second, zero.Second); - } - - if ((numPoles & 1) != 0) { - ComplexPair poles = Transform(analog[pairs].Poles.First); - ComplexPair zeros = Transform(analog[pairs].Zeros.First); - digital.Add(poles, zeros); - } - - double wn = analog.GetNormalW(); - double normalW = 2.0 * Math.Atan(Math.Sqrt(Math.Tan((wc + wn) * 0.5) * Math.Tan((wc2 + wn) * 0.5))); - digital.SetNormal(normalW, analog.GetNormalGain()); - } - - private ComplexPair Transform(Complex c) { - if (MathEx.IsInfinity(c)) { - return new ComplexPair(new Complex(-1.0, 0.0), new Complex(1.0, 0.0)); - } - - c = (Complex.One + c) / (Complex.One - c); - - Complex v = Complex.Zero; - v = MathEx.AddMul(v, 4.0 * ((_b2 * (_a2 - 1.0)) + 1.0), c); - v += 8.0 * ((_b2 * (_a2 - 1.0)) - 1.0); - v *= c; - v += 4.0 * ((_b2 * (_a2 - 1.0)) + 1.0); - v = Complex.Sqrt(v); - - Complex u = -v; - u = MathEx.AddMul(u, _ab2, c); - u += _ab2; - - v = MathEx.AddMul(v, _ab2, c); - v += _ab2; - - Complex d = Complex.Zero; - d = MathEx.AddMul(d, 2.0 * (_b - 1.0), c); - d += 2.0 * (1.0 + _b); - - Complex first = u / d; - Complex second = v / d; - return new ComplexPair(first, second); - } -} - -internal sealed class BandStopTransform { - private readonly double _a; - private readonly double _a2; - private readonly double _b; - private readonly double _b2; - - internal BandStopTransform(double fc, double fw, LayoutBase digital, LayoutBase analog) { - if (!(fc < 0.5)) { - throw new ArgumentException("The cutoff frequency needs to be below the Nyquist frequency."); - } - - if (fc < 0.0) { - throw new ArgumentException("Cutoff frequency is negative."); - } - - digital.Reset(); - - double ww = 2.0 * MathEx.DoublePi * fw; - double wc2 = (2.0 * MathEx.DoublePi * fc) - (ww / 2.0); - double wc = wc2 + ww; - - if (wc2 < 1e-8) { - wc2 = 1e-8; - } - - if (wc > MathEx.DoublePi - 1e-8) { - wc = MathEx.DoublePi - 1e-8; - } - - _a = Math.Cos((wc + wc2) * 0.5) / Math.Cos((wc - wc2) * 0.5); - _b = Math.Tan((wc - wc2) * 0.5); - _a2 = _a * _a; - _b2 = _b * _b; - - int numPoles = analog.GetNumPoles(); - int pairs = numPoles / 2; - for (int i = 0; i < pairs; i++) { - ref readonly PoleZeroPair pair = ref analog[i]; - ComplexPair poles = Transform(pair.Poles.First); - ComplexPair zeros = Transform(pair.Zeros.First); - - if (zeros.Second == zeros.First) { - zeros.Second = Complex.Conjugate(zeros.First); - } - - digital.AddPoleZeroConjugatePairs(poles.First, zeros.First); - digital.AddPoleZeroConjugatePairs(poles.Second, zeros.Second); - } - - if ((numPoles & 1) != 0) { - ComplexPair poles = Transform(analog[pairs].Poles.First); - ComplexPair zeros = Transform(analog[pairs].Zeros.First); - digital.Add(poles, zeros); - } - - digital.SetNormal(fc < 0.25 ? MathEx.DoublePi : 0.0, analog.GetNormalGain()); - } - - private ComplexPair Transform(Complex c) { - if (MathEx.IsInfinity(c)) { - c = new Complex(-1.0, 0.0); - } else { - c = (Complex.One + c) / (Complex.One - c); - } - - Complex u = Complex.Zero; - u = MathEx.AddMul(u, 4.0 * (_b2 + _a2 - 1.0), c); - u += 8.0 * (_b2 - _a2 + 1.0); - u *= c; - u += 4.0 * (_a2 + _b2 - 1.0); - u = Complex.Sqrt(u); - - Complex v = -0.5 * u; - v += _a; - v = MathEx.AddMul(v, -_a, c); - - u *= 0.5; - u += _a; - u = MathEx.AddMul(u, -_a, c); - - var d = new Complex(_b + 1.0, 0.0); - d = MathEx.AddMul(d, _b - 1.0, c); - - return new ComplexPair(u / d, v / d); - } -} \ No newline at end of file diff --git a/src/Spice86.Audio/Filters/IirFilters/Filters/Butterworth/AnalogPrototypes.cs b/src/Spice86.Audio/Filters/IirFilters/Filters/Butterworth/AnalogPrototypes.cs deleted file mode 100644 index a04531c..0000000 --- a/src/Spice86.Audio/Filters/IirFilters/Filters/Butterworth/AnalogPrototypes.cs +++ /dev/null @@ -1,70 +0,0 @@ -namespace Spice86.Audio.Filters.IirFilters.Filters.Butterworth; - -using Spice86.Audio.Filters.IirFilters.Common; -using Spice86.Audio.Filters.IirFilters.Common.Layout; - -using System.Numerics; - -public sealed class AnalogLowPass : LayoutBase { - private int _numPoles = -1; - - public AnalogLowPass() { - SetNormal(0.0, 1.0); - } - - public void Design(int numPoles) { - if (_numPoles == numPoles) { - return; - } - - _numPoles = numPoles; - Reset(); - - int n2 = 2 * numPoles; - int pairs = numPoles / 2; - for (int i = 0; i < pairs; i++) { - double angle = MathEx.DoublePiOverTwo + (((2 * i) + 1) * MathEx.DoublePi / n2); - var pole = Complex.FromPolarCoordinates(1.0, angle); - AddPoleZeroConjugatePairs(pole, MathEx.Infinity()); - } - - if ((numPoles & 1) != 0) { - Add(-1.0, MathEx.Infinity()); - } - } -} - -public sealed class AnalogLowShelf : LayoutBase { - private double _gainDb; - private int _numPoles = -1; - - public AnalogLowShelf() { - SetNormal(MathEx.DoublePi, 1.0); - } - - public void Design(int numPoles, double gainDb) { - if (_numPoles == numPoles && Math.Abs(_gainDb - gainDb) <= double.Epsilon) { - return; - } - - _numPoles = numPoles; - _gainDb = gainDb; - - Reset(); - - int n2 = numPoles * 2; - double g = Math.Pow(Math.Pow(10.0, gainDb / 20.0), 1.0 / n2); - double gp = -1.0 / g; - double gz = -g; - - int pairs = numPoles / 2; - for (int i = 1; i <= pairs; i++) { - double theta = MathEx.DoublePi * (0.5 - (((2.0 * i) - 1.0) / n2)); - AddPoleZeroConjugatePairs(Complex.FromPolarCoordinates(gp, theta), Complex.FromPolarCoordinates(gz, theta)); - } - - if ((numPoles & 1) != 0) { - Add(gp, gz); - } - } -} \ No newline at end of file diff --git a/src/Spice86.Audio/Filters/IirFilters/Filters/Butterworth/ButterworthFilters.cs b/src/Spice86.Audio/Filters/IirFilters/Filters/Butterworth/ButterworthFilters.cs deleted file mode 100644 index a929a3e..0000000 --- a/src/Spice86.Audio/Filters/IirFilters/Filters/Butterworth/ButterworthFilters.cs +++ /dev/null @@ -1,97 +0,0 @@ -namespace Spice86.Audio.Filters.IirFilters.Filters.Butterworth; - -using Spice86.Audio.Filters.IirFilters.Common; -using Spice86.Audio.Filters.IirFilters.Common.Layout; -using Spice86.Audio.Filters.IirFilters.Common.State; -using Spice86.Audio.Filters.IirFilters.Common.Transforms; - -public abstract class ButterworthFilterBase(int maxOrder, int maxDigitalPoles, TAnalog analogPrototype) - : PoleFilterBase(maxOrder, maxDigitalPoles, analogPrototype) - where TAnalog : LayoutBase - where TState : struct, ISectionState { - protected int MaxOrder { get; } = maxOrder; - - protected void ValidateOrder(int order) { - if (order > MaxOrder) { - throw new ArgumentException(Constants.OrderTooHigh); - } - } -} - -public abstract class ButterworthLowPassBase(int maxOrder) - : ButterworthFilterBase(maxOrder, maxOrder, new AnalogLowPass()) - where TState : struct, ISectionState { - protected void Setup(int order, double normalizedCutoff) { - ValidateOrder(order); - AnalogPrototype.Design(order); - _ = new LowPassTransform(normalizedCutoff, DigitalPrototype, AnalogPrototype); - SetLayout(DigitalPrototype); - } -} - -public abstract class ButterworthHighPassBase(int maxOrder) - : ButterworthFilterBase(maxOrder, maxOrder, new AnalogLowPass()) - where TState : struct, ISectionState { - protected void Setup(int order, double normalizedCutoff) { - ValidateOrder(order); - AnalogPrototype.Design(order); - _ = new HighPassTransform(normalizedCutoff, DigitalPrototype, AnalogPrototype); - SetLayout(DigitalPrototype); - } -} - -public abstract class ButterworthBandPassBase(int maxOrder) - : ButterworthFilterBase(maxOrder, maxOrder * 2, new AnalogLowPass()) - where TState : struct, ISectionState { - protected void Setup(int order, double centerFrequency, double widthFrequency) { - ValidateOrder(order); - AnalogPrototype.Design(order); - _ = new BandPassTransform(centerFrequency, widthFrequency, DigitalPrototype, AnalogPrototype); - SetLayout(DigitalPrototype); - } -} - -public abstract class ButterworthBandStopBase(int maxOrder) - : ButterworthFilterBase(maxOrder, maxOrder * 2, new AnalogLowPass()) - where TState : struct, ISectionState { - protected void Setup(int order, double centerFrequency, double widthFrequency) { - ValidateOrder(order); - AnalogPrototype.Design(order); - _ = new BandStopTransform(centerFrequency, widthFrequency, DigitalPrototype, AnalogPrototype); - SetLayout(DigitalPrototype); - } -} - -public abstract class ButterworthLowShelfBase(int maxOrder) - : ButterworthFilterBase(maxOrder, maxOrder, new AnalogLowShelf()) - where TState : struct, ISectionState { - protected void Setup(int order, double normalizedCutoff, double gainDb) { - ValidateOrder(order); - AnalogPrototype.Design(order, gainDb); - _ = new LowPassTransform(normalizedCutoff, DigitalPrototype, AnalogPrototype); - SetLayout(DigitalPrototype); - } -} - -public abstract class ButterworthHighShelfBase(int maxOrder) - : ButterworthFilterBase(maxOrder, maxOrder, new AnalogLowShelf()) - where TState : struct, ISectionState { - protected void Setup(int order, double normalizedCutoff, double gainDb) { - ValidateOrder(order); - AnalogPrototype.Design(order, gainDb); - _ = new HighPassTransform(normalizedCutoff, DigitalPrototype, AnalogPrototype); - SetLayout(DigitalPrototype); - } -} - -public abstract class ButterworthBandShelfBase(int maxOrder) - : ButterworthFilterBase(maxOrder, maxOrder * 2, new AnalogLowShelf()) - where TState : struct, ISectionState { - protected void Setup(int order, double centerFrequency, double widthFrequency, double gainDb) { - ValidateOrder(order); - AnalogPrototype.Design(order, gainDb); - _ = new BandPassTransform(centerFrequency, widthFrequency, DigitalPrototype, AnalogPrototype); - DigitalPrototype.SetNormal(centerFrequency < 0.25 ? MathEx.DoublePi : 0.0, 1.0); - SetLayout(DigitalPrototype); - } -} \ No newline at end of file diff --git a/src/Spice86.Audio/Filters/IirFilters/Filters/Butterworth/PublicFilters.cs b/src/Spice86.Audio/Filters/IirFilters/Filters/Butterworth/PublicFilters.cs deleted file mode 100644 index e578bf7..0000000 --- a/src/Spice86.Audio/Filters/IirFilters/Filters/Butterworth/PublicFilters.cs +++ /dev/null @@ -1,179 +0,0 @@ -namespace Spice86.Audio.Filters.IirFilters.Filters.Butterworth; - -using Spice86.Audio.Filters.IirFilters.Common; -using Spice86.Audio.Filters.IirFilters.Common.State; - -public class LowPass(int maxOrder = Constants.DefaultFilterOrder) : ButterworthLowPassBase(maxOrder) - where TState : struct, ISectionState { - private readonly int _maxOrder = maxOrder; - - public void Setup(double sampleRate, double cutoffFrequency) { - base.Setup(_maxOrder, cutoffFrequency / sampleRate); - } - - public void Setup(int order, double sampleRate, double cutoffFrequency) { - ValidateOrder(order); - base.Setup(order, cutoffFrequency / sampleRate); - } - - public void SetupN(double cutoffFrequency) { - base.Setup(_maxOrder, cutoffFrequency); - } - - public void SetupN(int order, double cutoffFrequency) { - ValidateOrder(order); - base.Setup(order, cutoffFrequency); - } -} - -public sealed class LowPass(int maxOrder = Constants.DefaultFilterOrder) : LowPass(maxOrder); - -public class HighPass(int maxOrder = Constants.DefaultFilterOrder) : ButterworthHighPassBase(maxOrder) - where TState : struct, ISectionState { - private readonly int _maxOrder = maxOrder; - - public void Setup(double sampleRate, double cutoffFrequency) { - base.Setup(_maxOrder, cutoffFrequency / sampleRate); - } - - public void Setup(int order, double sampleRate, double cutoffFrequency) { - ValidateOrder(order); - base.Setup(order, cutoffFrequency / sampleRate); - } - - public void SetupN(double cutoffFrequency) { - base.Setup(_maxOrder, cutoffFrequency); - } - - public void SetupN(int order, double cutoffFrequency) { - ValidateOrder(order); - base.Setup(order, cutoffFrequency); - } -} - -public sealed class HighPass(int maxOrder = Constants.DefaultFilterOrder) : HighPass(maxOrder); - -public class BandPass(int maxOrder = Constants.DefaultFilterOrder) : ButterworthBandPassBase(maxOrder) - where TState : struct, ISectionState { - private readonly int _maxOrder = maxOrder; - - public void Setup(double sampleRate, double centerFrequency, double widthFrequency) { - base.Setup(_maxOrder, centerFrequency / sampleRate, widthFrequency / sampleRate); - } - - public void Setup(int order, double sampleRate, double centerFrequency, double widthFrequency) { - ValidateOrder(order); - base.Setup(order, centerFrequency / sampleRate, widthFrequency / sampleRate); - } - - public void SetupN(double centerFrequency, double widthFrequency) { - base.Setup(_maxOrder, centerFrequency, widthFrequency); - } - - public void SetupN(int order, double centerFrequency, double widthFrequency) { - ValidateOrder(order); - base.Setup(order, centerFrequency, widthFrequency); - } -} - -public sealed class BandPass(int maxOrder = Constants.DefaultFilterOrder) : BandPass(maxOrder); - -public class BandStop(int maxOrder = Constants.DefaultFilterOrder) : ButterworthBandStopBase(maxOrder) - where TState : struct, ISectionState { - private readonly int _maxOrder = maxOrder; - - public void Setup(double sampleRate, double centerFrequency, double widthFrequency) { - base.Setup(_maxOrder, centerFrequency / sampleRate, widthFrequency / sampleRate); - } - - public void Setup(int order, double sampleRate, double centerFrequency, double widthFrequency) { - ValidateOrder(order); - base.Setup(order, centerFrequency / sampleRate, widthFrequency / sampleRate); - } - - public void SetupN(double centerFrequency, double widthFrequency) { - base.Setup(_maxOrder, centerFrequency, widthFrequency); - } - - public void SetupN(int order, double centerFrequency, double widthFrequency) { - ValidateOrder(order); - base.Setup(order, centerFrequency, widthFrequency); - } -} - -public sealed class BandStop(int maxOrder = Constants.DefaultFilterOrder) : BandStop(maxOrder); - -public class LowShelf(int maxOrder = Constants.DefaultFilterOrder) : ButterworthLowShelfBase(maxOrder) - where TState : struct, ISectionState { - private readonly int _maxOrder = maxOrder; - - public void Setup(double sampleRate, double cutoffFrequency, double gainDb) { - base.Setup(_maxOrder, cutoffFrequency / sampleRate, gainDb); - } - - public void Setup(int order, double sampleRate, double cutoffFrequency, double gainDb) { - ValidateOrder(order); - base.Setup(order, cutoffFrequency / sampleRate, gainDb); - } - - public void SetupN(double cutoffFrequency, double gainDb) { - base.Setup(_maxOrder, cutoffFrequency, gainDb); - } - - public void SetupN(int order, double cutoffFrequency, double gainDb) { - ValidateOrder(order); - base.Setup(order, cutoffFrequency, gainDb); - } -} - -public sealed class LowShelf(int maxOrder = Constants.DefaultFilterOrder) : LowShelf(maxOrder); - -public class HighShelf(int maxOrder = Constants.DefaultFilterOrder) : ButterworthHighShelfBase(maxOrder) - where TState : struct, ISectionState { - private readonly int _maxOrder = maxOrder; - - public void Setup(double sampleRate, double cutoffFrequency, double gainDb) { - base.Setup(_maxOrder, cutoffFrequency / sampleRate, gainDb); - } - - public void Setup(int order, double sampleRate, double cutoffFrequency, double gainDb) { - ValidateOrder(order); - base.Setup(order, cutoffFrequency / sampleRate, gainDb); - } - - public void SetupN(double cutoffFrequency, double gainDb) { - base.Setup(_maxOrder, cutoffFrequency, gainDb); - } - - public void SetupN(int order, double cutoffFrequency, double gainDb) { - ValidateOrder(order); - base.Setup(order, cutoffFrequency, gainDb); - } -} - -public sealed class HighShelf(int maxOrder = Constants.DefaultFilterOrder) : HighShelf(maxOrder); - -public class BandShelf(int maxOrder = Constants.DefaultFilterOrder) : ButterworthBandShelfBase(maxOrder) - where TState : struct, ISectionState { - private readonly int _maxOrder = maxOrder; - - public void Setup(double sampleRate, double centerFrequency, double widthFrequency, double gainDb) { - base.Setup(_maxOrder, centerFrequency / sampleRate, widthFrequency / sampleRate, gainDb); - } - - public void Setup(int order, double sampleRate, double centerFrequency, double widthFrequency, double gainDb) { - ValidateOrder(order); - base.Setup(order, centerFrequency / sampleRate, widthFrequency / sampleRate, gainDb); - } - - public void SetupN(double centerFrequency, double widthFrequency, double gainDb) { - base.Setup(_maxOrder, centerFrequency, widthFrequency, gainDb); - } - - public void SetupN(int order, double centerFrequency, double widthFrequency, double gainDb) { - ValidateOrder(order); - base.Setup(order, centerFrequency, widthFrequency, gainDb); - } -} - -public sealed class BandShelf(int maxOrder = Constants.DefaultFilterOrder) : BandShelf(maxOrder); \ No newline at end of file diff --git a/src/Spice86.Audio/Filters/IirFilters/Filters/ChebyshevI/AnalogPrototypes.cs b/src/Spice86.Audio/Filters/IirFilters/Filters/ChebyshevI/AnalogPrototypes.cs deleted file mode 100644 index 183af43..0000000 --- a/src/Spice86.Audio/Filters/IirFilters/Filters/ChebyshevI/AnalogPrototypes.cs +++ /dev/null @@ -1,125 +0,0 @@ -namespace Spice86.Audio.Filters.IirFilters.Filters.ChebyshevI; - -using Spice86.Audio.Filters.IirFilters.Common; -using Spice86.Audio.Filters.IirFilters.Common.Layout; - -using System.Numerics; - -public sealed class AnalogLowPass : LayoutBase { - private int _numPoles = -1; - private double _rippleDb; - - public AnalogLowPass() { - SetNormal(0.0, 1.0); - } - - public void Design(int numPoles, double rippleDb) { - if (_numPoles == numPoles && Math.Abs(_rippleDb - rippleDb) <= double.Epsilon) { - return; - } - - _numPoles = numPoles; - _rippleDb = rippleDb; - - Reset(); - - if (numPoles == 0) { - return; - } - - double eps = Math.Sqrt((1.0 / Math.Exp(-rippleDb * 0.1 * MathEx.DoubleLn10)) - 1.0); - double v0 = MathEx.Asinh(1.0 / eps) / numPoles; - double sinhV0 = -Math.Sinh(v0); - double coshV0 = Math.Cosh(v0); - - int n2 = 2 * numPoles; - int pairs = numPoles / 2; - for (int i = 0; i < pairs; i++) { - int k = (2 * i) + 1 - numPoles; - double angle = k * MathEx.DoublePi / n2; - double a = sinhV0 * Math.Cos(angle); - double b = coshV0 * Math.Sin(angle); - AddPoleZeroConjugatePairs(new Complex(a, b), MathEx.Infinity()); - } - - if ((numPoles & 1) != 0) { - Add(new Complex(sinhV0, 0.0), MathEx.Infinity()); - SetNormal(0.0, 1.0); - } else { - double gain = Math.Pow(10.0, -rippleDb / 20.0); - SetNormal(0.0, gain); - } - } -} - -public sealed class AnalogLowShelf : LayoutBase { - private double _gainDb; - private int _numPoles = -1; - private double _rippleDb; - - public AnalogLowShelf() { - SetNormal(MathEx.DoublePi, 1.0); - } - - public void Design(int numPoles, double gainDb, double rippleDb) { - if (_numPoles == numPoles && - Math.Abs(_gainDb - gainDb) <= double.Epsilon && - Math.Abs(_rippleDb - rippleDb) <= double.Epsilon) { - return; - } - - _numPoles = numPoles; - _gainDb = gainDb; - _rippleDb = rippleDb; - - Reset(); - - if (numPoles == 0) { - return; - } - - double localGainDb = -gainDb; - double localRippleDb = rippleDb; - - if (localRippleDb >= Math.Abs(localGainDb)) { - localRippleDb = Math.Abs(localGainDb); - } - - if (localGainDb < 0.0) { - localRippleDb = -localRippleDb; - } - - double g = Math.Pow(10.0, localGainDb / 20.0); - double gb = Math.Pow(10.0, (localGainDb - localRippleDb) / 20.0); - const double g0 = 1.0; - - double eps; - if (Math.Abs(gb - g0) > double.Epsilon) { - eps = Math.Sqrt(((g * g) - (gb * gb)) / ((gb * gb) - (g0 * g0))); - } else { - eps = g - 1.0; - } - - double b = Math.Pow((g / eps) + (gb * Math.Sqrt(1.0 + (1.0 / (eps * eps)))), 1.0 / numPoles); - double u = Math.Log(b / Math.Pow(g0, 1.0 / numPoles)); - double v = Math.Log(Math.Pow((1.0 / eps) + Math.Sqrt(1.0 + (1.0 / (eps * eps))), 1.0 / numPoles)); - - double sinhU = Math.Sinh(u); - double sinhV = Math.Sinh(v); - double coshU = Math.Cosh(u); - double coshV = Math.Cosh(v); - - int n2 = 2 * numPoles; - int pairs = numPoles / 2; - for (int i = 1; i <= pairs; i++) { - double angle = MathEx.DoublePi * ((2 * i) - 1) / n2; - double sn = Math.Sin(angle); - double cs = Math.Cos(angle); - AddPoleZeroConjugatePairs(new Complex(-sn * sinhU, cs * coshU), new Complex(-sn * sinhV, cs * coshV)); - } - - if ((numPoles & 1) != 0) { - Add(new Complex(-sinhU, 0.0), new Complex(-sinhV, 0.0)); - } - } -} \ No newline at end of file diff --git a/src/Spice86.Audio/Filters/IirFilters/Filters/ChebyshevI/ChebyshevIFilters.cs b/src/Spice86.Audio/Filters/IirFilters/Filters/ChebyshevI/ChebyshevIFilters.cs deleted file mode 100644 index 912ebff..0000000 --- a/src/Spice86.Audio/Filters/IirFilters/Filters/ChebyshevI/ChebyshevIFilters.cs +++ /dev/null @@ -1,97 +0,0 @@ -namespace Spice86.Audio.Filters.IirFilters.Filters.ChebyshevI; - -using Spice86.Audio.Filters.IirFilters.Common; -using Spice86.Audio.Filters.IirFilters.Common.Layout; -using Spice86.Audio.Filters.IirFilters.Common.State; -using Spice86.Audio.Filters.IirFilters.Common.Transforms; - -public abstract class ChebyshevIFilterBase(int maxOrder, int maxDigitalPoles, TAnalog analog) - : PoleFilterBase(maxOrder, maxDigitalPoles, analog) - where TAnalog : LayoutBase - where TState : struct, ISectionState { - protected int MaxOrder { get; } = maxOrder; - - protected void ValidateOrder(int order) { - if (order > MaxOrder) { - throw new ArgumentException(Constants.OrderTooHigh); - } - } -} - -public abstract class ChebyshevILowPassBase(int maxOrder) - : ChebyshevIFilterBase(maxOrder, maxOrder, new AnalogLowPass()) - where TState : struct, ISectionState { - protected void Setup(int order, double normalizedCutoff, double rippleDb) { - ValidateOrder(order); - AnalogPrototype.Design(order, rippleDb); - _ = new LowPassTransform(normalizedCutoff, DigitalPrototype, AnalogPrototype); - SetLayout(DigitalPrototype); - } -} - -public abstract class ChebyshevIHighPassBase(int maxOrder) - : ChebyshevIFilterBase(maxOrder, maxOrder, new AnalogLowPass()) - where TState : struct, ISectionState { - protected void Setup(int order, double normalizedCutoff, double rippleDb) { - ValidateOrder(order); - AnalogPrototype.Design(order, rippleDb); - _ = new HighPassTransform(normalizedCutoff, DigitalPrototype, AnalogPrototype); - SetLayout(DigitalPrototype); - } -} - -public abstract class ChebyshevIBandPassBase(int maxOrder) - : ChebyshevIFilterBase(maxOrder, maxOrder * 2, new AnalogLowPass()) - where TState : struct, ISectionState { - protected void Setup(int order, double centerFrequency, double widthFrequency, double rippleDb) { - ValidateOrder(order); - AnalogPrototype.Design(order, rippleDb); - _ = new BandPassTransform(centerFrequency, widthFrequency, DigitalPrototype, AnalogPrototype); - SetLayout(DigitalPrototype); - } -} - -public abstract class ChebyshevIBandStopBase(int maxOrder) - : ChebyshevIFilterBase(maxOrder, maxOrder * 2, new AnalogLowPass()) - where TState : struct, ISectionState { - protected void Setup(int order, double centerFrequency, double widthFrequency, double rippleDb) { - ValidateOrder(order); - AnalogPrototype.Design(order, rippleDb); - _ = new BandStopTransform(centerFrequency, widthFrequency, DigitalPrototype, AnalogPrototype); - SetLayout(DigitalPrototype); - } -} - -public abstract class ChebyshevILowShelfBase(int maxOrder) - : ChebyshevIFilterBase(maxOrder, maxOrder, new AnalogLowShelf()) - where TState : struct, ISectionState { - protected void Setup(int order, double normalizedCutoff, double gainDb, double rippleDb) { - ValidateOrder(order); - AnalogPrototype.Design(order, gainDb, rippleDb); - _ = new LowPassTransform(normalizedCutoff, DigitalPrototype, AnalogPrototype); - SetLayout(DigitalPrototype); - } -} - -public abstract class ChebyshevIHighShelfBase(int maxOrder) - : ChebyshevIFilterBase(maxOrder, maxOrder, new AnalogLowShelf()) - where TState : struct, ISectionState { - protected void Setup(int order, double normalizedCutoff, double gainDb, double rippleDb) { - ValidateOrder(order); - AnalogPrototype.Design(order, gainDb, rippleDb); - _ = new HighPassTransform(normalizedCutoff, DigitalPrototype, AnalogPrototype); - SetLayout(DigitalPrototype); - } -} - -public abstract class ChebyshevIBandShelfBase(int maxOrder) - : ChebyshevIFilterBase(maxOrder, maxOrder * 2, new AnalogLowShelf()) - where TState : struct, ISectionState { - protected void Setup(int order, double centerFrequency, double widthFrequency, double gainDb, double rippleDb) { - ValidateOrder(order); - AnalogPrototype.Design(order, gainDb, rippleDb); - _ = new BandPassTransform(centerFrequency, widthFrequency, DigitalPrototype, AnalogPrototype); - DigitalPrototype.SetNormal(centerFrequency < 0.25 ? MathEx.DoublePi : 0.0, 1.0); - SetLayout(DigitalPrototype); - } -} \ No newline at end of file diff --git a/src/Spice86.Audio/Filters/IirFilters/Filters/ChebyshevI/PublicFilters.cs b/src/Spice86.Audio/Filters/IirFilters/Filters/ChebyshevI/PublicFilters.cs deleted file mode 100644 index bceee79..0000000 --- a/src/Spice86.Audio/Filters/IirFilters/Filters/ChebyshevI/PublicFilters.cs +++ /dev/null @@ -1,172 +0,0 @@ -namespace Spice86.Audio.Filters.IirFilters.Filters.ChebyshevI; - -using Spice86.Audio.Filters.IirFilters.Common; -using Spice86.Audio.Filters.IirFilters.Common.State; - -public class LowPass : ChebyshevILowPassBase - where TState : struct, ISectionState { - private readonly int _maxOrder; - - protected LowPass(int maxOrder = Constants.DefaultFilterOrder) - : base(maxOrder) { - _maxOrder = maxOrder; - } - - public void Setup(double sampleRate, double cutoffFrequency, double rippleDb) { - base.Setup(_maxOrder, cutoffFrequency / sampleRate, rippleDb); - } - - public void Setup(int order, double sampleRate, double cutoffFrequency, double rippleDb) { - base.Setup(order, cutoffFrequency / sampleRate, rippleDb); - } - - public void SetupN(double cutoffFrequency, double rippleDb) { - base.Setup(_maxOrder, cutoffFrequency, rippleDb); - } - - public void SetupN(int order, double cutoffFrequency, double rippleDb) { - base.Setup(order, cutoffFrequency, rippleDb); - } -} - -public sealed class LowPass(int maxOrder = Constants.DefaultFilterOrder) : LowPass(maxOrder); - -public class HighPass(int maxOrder = Constants.DefaultFilterOrder) : ChebyshevIHighPassBase(maxOrder) - where TState : struct, ISectionState { - private readonly int _maxOrder = maxOrder; - - public void Setup(double sampleRate, double cutoffFrequency, double rippleDb) { - base.Setup(_maxOrder, cutoffFrequency / sampleRate, rippleDb); - } - - public void Setup(int order, double sampleRate, double cutoffFrequency, double rippleDb) { - base.Setup(order, cutoffFrequency / sampleRate, rippleDb); - } - - public void SetupN(double cutoffFrequency, double rippleDb) { - base.Setup(_maxOrder, cutoffFrequency, rippleDb); - } - - public void SetupN(int order, double cutoffFrequency, double rippleDb) { - base.Setup(order, cutoffFrequency, rippleDb); - } -} - -public sealed class HighPass(int maxOrder = Constants.DefaultFilterOrder) : HighPass(maxOrder); - -public class BandPass(int maxOrder = Constants.DefaultFilterOrder) : ChebyshevIBandPassBase(maxOrder) - where TState : struct, ISectionState { - private readonly int _maxOrder = maxOrder; - - public void Setup(double sampleRate, double centerFrequency, double widthFrequency, double rippleDb) { - base.Setup(_maxOrder, centerFrequency / sampleRate, widthFrequency / sampleRate, rippleDb); - } - - public void Setup(int order, double sampleRate, double centerFrequency, double widthFrequency, double rippleDb) { - base.Setup(order, centerFrequency / sampleRate, widthFrequency / sampleRate, rippleDb); - } - - public void SetupN(double centerFrequency, double widthFrequency, double rippleDb) { - base.Setup(_maxOrder, centerFrequency, widthFrequency, rippleDb); - } - - public void SetupN(int order, double centerFrequency, double widthFrequency, double rippleDb) { - base.Setup(order, centerFrequency, widthFrequency, rippleDb); - } -} - -public sealed class BandPass(int maxOrder = Constants.DefaultFilterOrder) : BandPass(maxOrder); - -public class BandStop(int maxOrder = Constants.DefaultFilterOrder) : ChebyshevIBandStopBase(maxOrder) - where TState : struct, ISectionState { - private readonly int _maxOrder = maxOrder; - - public void Setup(double sampleRate, double centerFrequency, double widthFrequency, double rippleDb) { - base.Setup(_maxOrder, centerFrequency / sampleRate, widthFrequency / sampleRate, rippleDb); - } - - public void Setup(int order, double sampleRate, double centerFrequency, double widthFrequency, double rippleDb) { - base.Setup(order, centerFrequency / sampleRate, widthFrequency / sampleRate, rippleDb); - } - - public void SetupN(double centerFrequency, double widthFrequency, double rippleDb) { - base.Setup(_maxOrder, centerFrequency, widthFrequency, rippleDb); - } - - public void SetupN(int order, double centerFrequency, double widthFrequency, double rippleDb) { - base.Setup(order, centerFrequency, widthFrequency, rippleDb); - } -} - -public sealed class BandStop(int maxOrder = Constants.DefaultFilterOrder) : BandStop(maxOrder); - -public class LowShelf(int maxOrder = Constants.DefaultFilterOrder) : ChebyshevILowShelfBase(maxOrder) - where TState : struct, ISectionState { - private readonly int _maxOrder = maxOrder; - - public void Setup(double sampleRate, double cutoffFrequency, double gainDb, double rippleDb) { - base.Setup(_maxOrder, cutoffFrequency / sampleRate, gainDb, rippleDb); - } - - public void Setup(int order, double sampleRate, double cutoffFrequency, double gainDb, double rippleDb) { - base.Setup(order, cutoffFrequency / sampleRate, gainDb, rippleDb); - } - - public void SetupN(double cutoffFrequency, double gainDb, double rippleDb) { - base.Setup(_maxOrder, cutoffFrequency, gainDb, rippleDb); - } - - public void SetupN(int order, double cutoffFrequency, double gainDb, double rippleDb) { - base.Setup(order, cutoffFrequency, gainDb, rippleDb); - } -} - -public sealed class LowShelf(int maxOrder = Constants.DefaultFilterOrder) : LowShelf(maxOrder); - -public class HighShelf(int maxOrder = Constants.DefaultFilterOrder) : ChebyshevIHighShelfBase(maxOrder) - where TState : struct, ISectionState { - private readonly int _maxOrder = maxOrder; - - public void Setup(double sampleRate, double cutoffFrequency, double gainDb, double rippleDb) { - base.Setup(_maxOrder, cutoffFrequency / sampleRate, gainDb, rippleDb); - } - - public void Setup(int order, double sampleRate, double cutoffFrequency, double gainDb, double rippleDb) { - base.Setup(order, cutoffFrequency / sampleRate, gainDb, rippleDb); - } - - public void SetupN(double cutoffFrequency, double gainDb, double rippleDb) { - base.Setup(_maxOrder, cutoffFrequency, gainDb, rippleDb); - } - - public void SetupN(int order, double cutoffFrequency, double gainDb, double rippleDb) { - base.Setup(order, cutoffFrequency, gainDb, rippleDb); - } -} - -public sealed class HighShelf(int maxOrder = Constants.DefaultFilterOrder) : HighShelf(maxOrder); - -public class BandShelf(int maxOrder = Constants.DefaultFilterOrder) : ChebyshevIBandShelfBase(maxOrder) - where TState : struct, ISectionState { - private readonly int _maxOrder = maxOrder; - - public void Setup(double sampleRate, double centerFrequency, double widthFrequency, double gainDb, - double rippleDb) { - base.Setup(_maxOrder, centerFrequency / sampleRate, widthFrequency / sampleRate, gainDb, rippleDb); - } - - public void Setup(int order, double sampleRate, double centerFrequency, double widthFrequency, double gainDb, - double rippleDb) { - base.Setup(order, centerFrequency / sampleRate, widthFrequency / sampleRate, gainDb, rippleDb); - } - - public void SetupN(double centerFrequency, double widthFrequency, double gainDb, double rippleDb) { - base.Setup(_maxOrder, centerFrequency, widthFrequency, gainDb, rippleDb); - } - - public void SetupN(int order, double centerFrequency, double widthFrequency, double gainDb, double rippleDb) { - base.Setup(order, centerFrequency, widthFrequency, gainDb, rippleDb); - } -} - -public sealed class BandShelf(int maxOrder = Constants.DefaultFilterOrder) : BandShelf(maxOrder); \ No newline at end of file diff --git a/src/Spice86.Audio/Filters/IirFilters/Filters/ChebyshevII/AnalogPrototypes.cs b/src/Spice86.Audio/Filters/IirFilters/Filters/ChebyshevII/AnalogPrototypes.cs deleted file mode 100644 index d1822a5..0000000 --- a/src/Spice86.Audio/Filters/IirFilters/Filters/ChebyshevII/AnalogPrototypes.cs +++ /dev/null @@ -1,123 +0,0 @@ -namespace Spice86.Audio.Filters.IirFilters.Filters.ChebyshevII; - -using Spice86.Audio.Filters.IirFilters.Common; -using Spice86.Audio.Filters.IirFilters.Common.Layout; - -using System.Numerics; - -public sealed class AnalogLowPass : LayoutBase { - private int _numPoles = -1; - private double _stopBandDb; - - public AnalogLowPass() { - SetNormal(0.0, 1.0); - } - - public void Design(int numPoles, double stopBandDb) { - if (_numPoles == numPoles && Math.Abs(_stopBandDb - stopBandDb) <= double.Epsilon) { - return; - } - - _numPoles = numPoles; - _stopBandDb = stopBandDb; - - Reset(); - - if (numPoles == 0) { - return; - } - - double eps = Math.Sqrt(1.0 / (Math.Exp(stopBandDb * 0.1 * MathEx.DoubleLn10) - 1.0)); - double v0 = MathEx.Asinh(1.0 / eps) / numPoles; - double sinhV0 = -Math.Sinh(v0); - double coshV0 = Math.Cosh(v0); - double fn = MathEx.DoublePi / (2.0 * numPoles); - - int k = 1; - for (int i = numPoles / 2; i > 0; i--, k += 2) { - double a = sinhV0 * Math.Cos((k - numPoles) * fn); - double b = coshV0 * Math.Sin((k - numPoles) * fn); - double d2 = (a * a) + (b * b); - double im = 1.0 / Math.Cos(k * fn); - var pole = new Complex(a / d2, b / d2); - var zero = new Complex(0.0, im); - AddPoleZeroConjugatePairs(pole, zero); - } - - if ((numPoles & 1) != 0) { - Add(new Complex(1.0 / sinhV0, 0.0), MathEx.Infinity()); - } - } -} - -public sealed class AnalogLowShelf : LayoutBase { - private double _gainDb; - private int _numPoles = -1; - private double _stopBandDb; - - public AnalogLowShelf() { - SetNormal(MathEx.DoublePi, 1.0); - } - - public void Design(int numPoles, double gainDb, double stopBandDb) { - if (_numPoles == numPoles && - Math.Abs(_gainDb - gainDb) <= double.Epsilon && - Math.Abs(_stopBandDb - stopBandDb) <= double.Epsilon) { - return; - } - - _numPoles = numPoles; - _gainDb = gainDb; - _stopBandDb = stopBandDb; - - Reset(); - - if (numPoles == 0) { - return; - } - - double localGainDb = -gainDb; - double localStopBandDb = stopBandDb; - - if (localStopBandDb >= Math.Abs(localGainDb)) { - localStopBandDb = Math.Abs(localGainDb); - } - - if (localGainDb < 0.0) { - localStopBandDb = -localStopBandDb; - } - - double g = Math.Pow(10.0, localGainDb / 20.0); - double gb = Math.Pow(10.0, (localGainDb - localStopBandDb) / 20.0); - const double g0 = 1.0; - - double eps; - if (Math.Abs(gb - g0) > double.Epsilon) { - eps = Math.Sqrt(((g * g) - (gb * gb)) / ((gb * gb) - (g0 * g0))); - } else { - eps = g - 1.0; - } - - double b = Math.Pow((g / eps) + (gb * Math.Sqrt(1.0 + (1.0 / (eps * eps)))), 1.0 / numPoles); - double u = Math.Log(b / Math.Pow(g0, 1.0 / numPoles)); - double v = Math.Log(Math.Pow((1.0 / eps) + Math.Sqrt(1.0 + (1.0 / (eps * eps))), 1.0 / numPoles)); - - double sinhU = Math.Sinh(u); - double sinhV = Math.Sinh(v); - double coshU = Math.Cosh(u); - double coshV = Math.Cosh(v); - - int n2 = 2 * numPoles; - int pairs = numPoles / 2; - for (int i = 1; i <= pairs; i++) { - double angle = MathEx.DoublePi * ((2 * i) - 1) / n2; - double sn = Math.Sin(angle); - double cs = Math.Cos(angle); - AddPoleZeroConjugatePairs(new Complex(-sn * sinhU, cs * coshU), new Complex(-sn * sinhV, cs * coshV)); - } - - if ((numPoles & 1) != 0) { - Add(new Complex(-sinhU, 0.0), new Complex(-sinhV, 0.0)); - } - } -} \ No newline at end of file diff --git a/src/Spice86.Audio/Filters/IirFilters/Filters/ChebyshevII/ChebyshevIIFilters.cs b/src/Spice86.Audio/Filters/IirFilters/Filters/ChebyshevII/ChebyshevIIFilters.cs deleted file mode 100644 index b3a47ce..0000000 --- a/src/Spice86.Audio/Filters/IirFilters/Filters/ChebyshevII/ChebyshevIIFilters.cs +++ /dev/null @@ -1,122 +0,0 @@ -namespace Spice86.Audio.Filters.IirFilters.Filters.ChebyshevII; - -using Spice86.Audio.Filters.IirFilters.Common; -using Spice86.Audio.Filters.IirFilters.Common.Layout; -using Spice86.Audio.Filters.IirFilters.Common.State; -using Spice86.Audio.Filters.IirFilters.Common.Transforms; - -public abstract class ChebyshevIiFilterBase : PoleFilterBase - where TAnalog : LayoutBase - where TState : struct, ISectionState { - protected ChebyshevIiFilterBase(int maxOrder, int maxDigitalPoles, TAnalog analog) - : base(maxOrder, maxDigitalPoles, analog) { - MaxOrder = maxOrder; - } - - protected int MaxOrder { get; } - - protected void ValidateOrder(int order) { - if (order > MaxOrder) { - throw new ArgumentException(Constants.OrderTooHigh); - } - } -} - -public abstract class ChebyshevIiLowPassBase : ChebyshevIiFilterBase - where TState : struct, ISectionState { - protected ChebyshevIiLowPassBase(int maxOrder) - : base(maxOrder, maxOrder, new AnalogLowPass()) { - } - - protected void Setup(int order, double normalizedCutoff, double stopBandDb) { - ValidateOrder(order); - AnalogPrototype.Design(order, stopBandDb); - _ = new LowPassTransform(normalizedCutoff, DigitalPrototype, AnalogPrototype); - SetLayout(DigitalPrototype); - } -} - -public abstract class ChebyshevIiHighPassBase : ChebyshevIiFilterBase - where TState : struct, ISectionState { - protected ChebyshevIiHighPassBase(int maxOrder) - : base(maxOrder, maxOrder, new AnalogLowPass()) { - } - - protected void Setup(int order, double normalizedCutoff, double stopBandDb) { - ValidateOrder(order); - AnalogPrototype.Design(order, stopBandDb); - _ = new HighPassTransform(normalizedCutoff, DigitalPrototype, AnalogPrototype); - SetLayout(DigitalPrototype); - } -} - -public abstract class ChebyshevIiBandPassBase : ChebyshevIiFilterBase - where TState : struct, ISectionState { - protected ChebyshevIiBandPassBase(int maxOrder) - : base(maxOrder, maxOrder * 2, new AnalogLowPass()) { - } - - protected void Setup(int order, double centerFrequency, double widthFrequency, double stopBandDb) { - ValidateOrder(order); - AnalogPrototype.Design(order, stopBandDb); - _ = new BandPassTransform(centerFrequency, widthFrequency, DigitalPrototype, AnalogPrototype); - SetLayout(DigitalPrototype); - } -} - -public abstract class ChebyshevIiBandStopBase : ChebyshevIiFilterBase - where TState : struct, ISectionState { - protected ChebyshevIiBandStopBase(int maxOrder) - : base(maxOrder, maxOrder * 2, new AnalogLowPass()) { - } - - protected void Setup(int order, double centerFrequency, double widthFrequency, double stopBandDb) { - ValidateOrder(order); - AnalogPrototype.Design(order, stopBandDb); - _ = new BandStopTransform(centerFrequency, widthFrequency, DigitalPrototype, AnalogPrototype); - SetLayout(DigitalPrototype); - } -} - -public abstract class ChebyshevIiLowShelfBase : ChebyshevIiFilterBase - where TState : struct, ISectionState { - protected ChebyshevIiLowShelfBase(int maxOrder) - : base(maxOrder, maxOrder, new AnalogLowShelf()) { - } - - protected void Setup(int order, double normalizedCutoff, double gainDb, double stopBandDb) { - ValidateOrder(order); - AnalogPrototype.Design(order, gainDb, stopBandDb); - _ = new LowPassTransform(normalizedCutoff, DigitalPrototype, AnalogPrototype); - SetLayout(DigitalPrototype); - } -} - -public abstract class ChebyshevIiHighShelfBase : ChebyshevIiFilterBase - where TState : struct, ISectionState { - protected ChebyshevIiHighShelfBase(int maxOrder) - : base(maxOrder, maxOrder, new AnalogLowShelf()) { - } - - protected void Setup(int order, double normalizedCutoff, double gainDb, double stopBandDb) { - ValidateOrder(order); - AnalogPrototype.Design(order, gainDb, stopBandDb); - _ = new HighPassTransform(normalizedCutoff, DigitalPrototype, AnalogPrototype); - SetLayout(DigitalPrototype); - } -} - -public abstract class ChebyshevIiBandShelfBase : ChebyshevIiFilterBase - where TState : struct, ISectionState { - protected ChebyshevIiBandShelfBase(int maxOrder) - : base(maxOrder, maxOrder * 2, new AnalogLowShelf()) { - } - - protected void Setup(int order, double centerFrequency, double widthFrequency, double gainDb, double stopBandDb) { - ValidateOrder(order); - AnalogPrototype.Design(order, gainDb, stopBandDb); - _ = new BandPassTransform(centerFrequency, widthFrequency, DigitalPrototype, AnalogPrototype); - DigitalPrototype.SetNormal(centerFrequency < 0.25 ? MathEx.DoublePi : 0.0, 1.0); - SetLayout(DigitalPrototype); - } -} \ No newline at end of file diff --git a/src/Spice86.Audio/Filters/IirFilters/Filters/ChebyshevII/PublicFilters.cs b/src/Spice86.Audio/Filters/IirFilters/Filters/ChebyshevII/PublicFilters.cs deleted file mode 100644 index efb2605..0000000 --- a/src/Spice86.Audio/Filters/IirFilters/Filters/ChebyshevII/PublicFilters.cs +++ /dev/null @@ -1,167 +0,0 @@ -namespace Spice86.Audio.Filters.IirFilters.Filters.ChebyshevII; - -using Spice86.Audio.Filters.IirFilters.Common; -using Spice86.Audio.Filters.IirFilters.Common.State; - -public class LowPass(int maxOrder = Constants.DefaultFilterOrder) : ChebyshevIiLowPassBase(maxOrder) - where TState : struct, ISectionState { - private readonly int _maxOrder = maxOrder; - - public void Setup(double sampleRate, double cutoffFrequency, double stopBandDb) { - base.Setup(_maxOrder, cutoffFrequency / sampleRate, stopBandDb); - } - - public void Setup(int order, double sampleRate, double cutoffFrequency, double stopBandDb) { - base.Setup(order, cutoffFrequency / sampleRate, stopBandDb); - } - - public void SetupN(double cutoffFrequency, double stopBandDb) { - base.Setup(_maxOrder, cutoffFrequency, stopBandDb); - } - - public void SetupN(int order, double cutoffFrequency, double stopBandDb) { - base.Setup(order, cutoffFrequency, stopBandDb); - } -} - -public sealed class LowPass(int maxOrder = Constants.DefaultFilterOrder) : LowPass(maxOrder); - -public class HighPass(int maxOrder = Constants.DefaultFilterOrder) : ChebyshevIiHighPassBase(maxOrder) - where TState : struct, ISectionState { - private readonly int _maxOrder = maxOrder; - - public void Setup(double sampleRate, double cutoffFrequency, double stopBandDb) { - base.Setup(_maxOrder, cutoffFrequency / sampleRate, stopBandDb); - } - - public void Setup(int order, double sampleRate, double cutoffFrequency, double stopBandDb) { - base.Setup(order, cutoffFrequency / sampleRate, stopBandDb); - } - - public void SetupN(double cutoffFrequency, double stopBandDb) { - base.Setup(_maxOrder, cutoffFrequency, stopBandDb); - } - - public void SetupN(int order, double cutoffFrequency, double stopBandDb) { - base.Setup(order, cutoffFrequency, stopBandDb); - } -} - -public sealed class HighPass(int maxOrder = Constants.DefaultFilterOrder) : HighPass(maxOrder); - -public class BandPass(int maxOrder = Constants.DefaultFilterOrder) : ChebyshevIiBandPassBase(maxOrder) - where TState : struct, ISectionState { - private readonly int _maxOrder = maxOrder; - - public void Setup(double sampleRate, double centerFrequency, double widthFrequency, double stopBandDb) { - base.Setup(_maxOrder, centerFrequency / sampleRate, widthFrequency / sampleRate, stopBandDb); - } - - public void Setup(int order, double sampleRate, double centerFrequency, double widthFrequency, double stopBandDb) { - base.Setup(order, centerFrequency / sampleRate, widthFrequency / sampleRate, stopBandDb); - } - - public void SetupN(double centerFrequency, double widthFrequency, double stopBandDb) { - base.Setup(_maxOrder, centerFrequency, widthFrequency, stopBandDb); - } - - public void SetupN(int order, double centerFrequency, double widthFrequency, double stopBandDb) { - base.Setup(order, centerFrequency, widthFrequency, stopBandDb); - } -} - -public sealed class BandPass(int maxOrder = Constants.DefaultFilterOrder) : BandPass(maxOrder); - -public class BandStop(int maxOrder = Constants.DefaultFilterOrder) : ChebyshevIiBandStopBase(maxOrder) - where TState : struct, ISectionState { - private readonly int _maxOrder = maxOrder; - - public void Setup(double sampleRate, double centerFrequency, double widthFrequency, double stopBandDb) { - base.Setup(_maxOrder, centerFrequency / sampleRate, widthFrequency / sampleRate, stopBandDb); - } - - public void Setup(int order, double sampleRate, double centerFrequency, double widthFrequency, double stopBandDb) { - base.Setup(order, centerFrequency / sampleRate, widthFrequency / sampleRate, stopBandDb); - } - - public void SetupN(double centerFrequency, double widthFrequency, double stopBandDb) { - base.Setup(_maxOrder, centerFrequency, widthFrequency, stopBandDb); - } - - public void SetupN(int order, double centerFrequency, double widthFrequency, double stopBandDb) { - base.Setup(order, centerFrequency, widthFrequency, stopBandDb); - } -} - -public sealed class BandStop(int maxOrder = Constants.DefaultFilterOrder) : BandStop(maxOrder); - -public class LowShelf(int maxOrder = Constants.DefaultFilterOrder) : ChebyshevIiLowShelfBase(maxOrder) - where TState : struct, ISectionState { - private readonly int _maxOrder = maxOrder; - - public void Setup(double sampleRate, double cutoffFrequency, double gainDb, double stopBandDb) { - base.Setup(_maxOrder, cutoffFrequency / sampleRate, gainDb, stopBandDb); - } - - public void Setup(int order, double sampleRate, double cutoffFrequency, double gainDb, double stopBandDb) { - base.Setup(order, cutoffFrequency / sampleRate, gainDb, stopBandDb); - } - - public void SetupN(double cutoffFrequency, double gainDb, double stopBandDb) { - base.Setup(_maxOrder, cutoffFrequency, gainDb, stopBandDb); - } - - public void SetupN(int order, double cutoffFrequency, double gainDb, double stopBandDb) { - base.Setup(order, cutoffFrequency, gainDb, stopBandDb); - } -} - -public sealed class LowShelf(int maxOrder = Constants.DefaultFilterOrder) : LowShelf(maxOrder); - -public class HighShelf(int maxOrder = Constants.DefaultFilterOrder) : ChebyshevIiHighShelfBase(maxOrder) - where TState : struct, ISectionState { - private readonly int _maxOrder = maxOrder; - - public void Setup(double sampleRate, double cutoffFrequency, double gainDb, double stopBandDb) { - base.Setup(_maxOrder, cutoffFrequency / sampleRate, gainDb, stopBandDb); - } - - public void Setup(int order, double sampleRate, double cutoffFrequency, double gainDb, double stopBandDb) { - base.Setup(order, cutoffFrequency / sampleRate, gainDb, stopBandDb); - } - - public void SetupN(double cutoffFrequency, double gainDb, double stopBandDb) { - base.Setup(_maxOrder, cutoffFrequency, gainDb, stopBandDb); - } - - public void SetupN(int order, double cutoffFrequency, double gainDb, double stopBandDb) { - base.Setup(order, cutoffFrequency, gainDb, stopBandDb); - } -} - -public sealed class HighShelf(int maxOrder = Constants.DefaultFilterOrder) : HighShelf(maxOrder); - -public class BandShelf(int maxOrder = Constants.DefaultFilterOrder) : ChebyshevIiBandShelfBase(maxOrder) - where TState : struct, ISectionState { - private readonly int _maxOrder = maxOrder; - - public void Setup(double sampleRate, double centerFrequency, double widthFrequency, double gainDb, - double stopBandDb) { - base.Setup(_maxOrder, centerFrequency / sampleRate, widthFrequency / sampleRate, gainDb, stopBandDb); - } - - public void Setup(int order, double sampleRate, double centerFrequency, double widthFrequency, double gainDb, - double stopBandDb) { - base.Setup(order, centerFrequency / sampleRate, widthFrequency / sampleRate, gainDb, stopBandDb); - } - - public void SetupN(double centerFrequency, double widthFrequency, double gainDb, double stopBandDb) { - base.Setup(_maxOrder, centerFrequency, widthFrequency, gainDb, stopBandDb); - } - - public void SetupN(int order, double centerFrequency, double widthFrequency, double gainDb, double stopBandDb) { - base.Setup(order, centerFrequency, widthFrequency, gainDb, stopBandDb); - } -} - -public sealed class BandShelf(int maxOrder = Constants.DefaultFilterOrder) : BandShelf(maxOrder); \ No newline at end of file diff --git a/src/Spice86.Audio/Filters/IirFilters/Filters/Custom/CustomFilters.cs b/src/Spice86.Audio/Filters/IirFilters/Filters/Custom/CustomFilters.cs deleted file mode 100644 index 1b7f472..0000000 --- a/src/Spice86.Audio/Filters/IirFilters/Filters/Custom/CustomFilters.cs +++ /dev/null @@ -1,76 +0,0 @@ -namespace Spice86.Audio.Filters.IirFilters.Filters.Custom; - -using Spice86.Audio.Filters.IirFilters.Common; -using Spice86.Audio.Filters.IirFilters.Common.State; - -using System.Numerics; - -internal sealed class OnePoleInternal : BiquadFilterBase - where TState : struct, ISectionState { - public void Setup(double scale, double pole, double zero) { - Coefficients.SetOnePole(new Complex(pole, 0.0), new Complex(zero, 0.0)); - Coefficients.ApplyScale(scale); - } -} - -internal sealed class TwoPoleInternal : BiquadFilterBase - where TState : struct, ISectionState { - public void Setup(double scale, double poleRho, double poleTheta, double zeroRho, double zeroTheta) { - var pole = Complex.FromPolarCoordinates(poleRho, poleTheta); - var zero = Complex.FromPolarCoordinates(zeroRho, zeroTheta); - Coefficients.SetTwoPole(pole, zero, Complex.Conjugate(pole), Complex.Conjugate(zero)); - Coefficients.ApplyScale(scale); - } -} - -internal sealed class SosCascadeInternal - where TState : struct, ISectionState { - private readonly CascadeStages _stages; - - public SosCascadeInternal(int stageCount) { - if (stageCount <= 0) { - throw new ArgumentException("Stage count must be positive.", nameof(stageCount)); - } - - StageCount = stageCount; - _stages = new CascadeStages(stageCount); - } - - public int StageCount { get; } - - public void Reset() { - _stages.Reset(); - } - - public double Filter(double sample) { - return _stages.Filter(sample); - } - - public float Filter(float sample) { - return _stages.FilterSingle(sample); - } - - public void Setup(ReadOnlySpan sosCoefficients) { - _stages.Setup(sosCoefficients); - } - - public void Setup(double[,] sosCoefficients) { - _stages.Setup(sosCoefficients); - } - - public double[,] GetCoefficientSnapshot() { - Biquad[] snapshot = _stages.SnapshotStages(); - double[,] result = new double[StageCount, 6]; - for (int i = 0; i < StageCount; i++) { - Biquad stage = snapshot[i]; - result[i, 0] = stage.B0; - result[i, 1] = stage.B1; - result[i, 2] = stage.B2; - result[i, 3] = stage.A0; - result[i, 4] = stage.A1; - result[i, 5] = stage.A2; - } - - return result; - } -} \ No newline at end of file diff --git a/src/Spice86.Audio/Filters/IirFilters/Filters/Custom/PublicFilters.cs b/src/Spice86.Audio/Filters/IirFilters/Filters/Custom/PublicFilters.cs deleted file mode 100644 index 6abdaf9..0000000 --- a/src/Spice86.Audio/Filters/IirFilters/Filters/Custom/PublicFilters.cs +++ /dev/null @@ -1,109 +0,0 @@ -namespace Spice86.Audio.Filters.IirFilters.Filters.Custom; - -using Spice86.Audio.Filters.IirFilters.Common; -using Spice86.Audio.Filters.IirFilters.Common.State; - -public class OnePole - where TState : struct, ISectionState { - private readonly OnePoleInternal _impl = new(); - - public Biquad Coefficients => _impl.Coefficients; - - public void Setup(double scale, double pole, double zero) { - _impl.Setup(scale, pole, zero); - } - - public void Reset() { - _impl.Reset(); - } - - public double Filter(double sample) { - return _impl.Filter(sample); - } - - public float Filter(float sample) { - return _impl.Filter(sample); - } - - public double Filter(double sample, ref TState state) { - return _impl.Filter(sample, ref state); - } -} - -public sealed class OnePole : OnePole; - -public class TwoPole - where TState : struct, ISectionState { - private readonly TwoPoleInternal _impl = new(); - - public Biquad Coefficients => _impl.Coefficients; - - public void Setup(double scale, double poleRho, double poleTheta, double zeroRho, double zeroTheta) { - _impl.Setup(scale, poleRho, poleTheta, zeroRho, zeroTheta); - } - - public void Reset() { - _impl.Reset(); - } - - public double Filter(double sample) { - return _impl.Filter(sample); - } - - public float Filter(float sample) { - return _impl.Filter(sample); - } - - public double Filter(double sample, ref TState state) { - return _impl.Filter(sample, ref state); - } -} - -public sealed class TwoPole : TwoPole; - -public class SosCascade - where TState : struct, ISectionState { - private readonly SosCascadeInternal _impl; - - protected SosCascade(int stageCount) { - _impl = new SosCascadeInternal(stageCount); - } - - protected SosCascade(double[,] sosCoefficients) { - int stageCount = sosCoefficients.GetLength(0); - _impl = new SosCascadeInternal(stageCount); - _impl.Setup(sosCoefficients); - } - - public int StageCount => _impl.StageCount; - - public void Reset() { - _impl.Reset(); - } - - public double Filter(double sample) { - return _impl.Filter(sample); - } - - public float Filter(float sample) { - return _impl.Filter(sample); - } - - public void Setup(ReadOnlySpan sosCoefficients) { - _impl.Setup(sosCoefficients); - } - - public void Setup(double[,] sosCoefficients) { - _impl.Setup(sosCoefficients); - } - - public double[,] GetCoefficientSnapshot() { - return _impl.GetCoefficientSnapshot(); - } -} - -public sealed class SosCascade : SosCascade { - public SosCascade(int stageCount) : base(stageCount) { } - - public SosCascade(double[,] sosCoefficients) : base(sosCoefficients) { } -} \ No newline at end of file diff --git a/src/Spice86.Audio/Filters/IirFilters/Filters/RBJ/PublicFilters.cs b/src/Spice86.Audio/Filters/IirFilters/Filters/RBJ/PublicFilters.cs deleted file mode 100644 index 874659a..0000000 --- a/src/Spice86.Audio/Filters/IirFilters/Filters/RBJ/PublicFilters.cs +++ /dev/null @@ -1,159 +0,0 @@ -namespace Spice86.Audio.Filters.IirFilters.Filters.RBJ; - -using Spice86.Audio.Filters.IirFilters.Common.State; - -public class LowPass : RbjFilterBase - where TState : struct, ISectionState { - public void Setup(double sampleRate, double cutoffFrequency) { - SetupLowPass(cutoffFrequency / sampleRate, OneOverSqrtTwo); - } - - public void Setup(double sampleRate, double cutoffFrequency, double q) { - SetupLowPass(cutoffFrequency / sampleRate, q); - } - - public void SetupN(double cutoffFrequency) { - SetupLowPass(cutoffFrequency, OneOverSqrtTwo); - } - - public void SetupN(double cutoffFrequency, double q) { - SetupLowPass(cutoffFrequency, q); - } -} - -public sealed class LowPass : LowPass; - -public class HighPass : RbjFilterBase - where TState : struct, ISectionState { - public void Setup(double sampleRate, double cutoffFrequency) { - SetupHighPass(cutoffFrequency / sampleRate, OneOverSqrtTwo); - } - - - public void Setup(double sampleRate, double cutoffFrequency, double q) { - SetupHighPass(cutoffFrequency / sampleRate, q); - } - - public void SetupN(double cutoffFrequency) { - SetupHighPass(cutoffFrequency, OneOverSqrtTwo); - } - - public void SetupN(double cutoffFrequency, double q) { - SetupHighPass(cutoffFrequency, q); - } -} - -public sealed class HighPass : HighPass; - -public class BandPass1 : RbjFilterBase - where TState : struct, ISectionState { - public void Setup(double sampleRate, double centerFrequency, double bandWidth) { - SetupBandPass1(centerFrequency / sampleRate, bandWidth); - } - - public void SetupN(double centerFrequency, double bandWidth) { - SetupBandPass1(centerFrequency, bandWidth); - } -} - -public sealed class BandPass1 : BandPass1; - -public class BandPass2 : RbjFilterBase - where TState : struct, ISectionState { - public void Setup(double sampleRate, double centerFrequency, double bandWidth) { - SetupBandPass2(centerFrequency / sampleRate, bandWidth); - } - - public void SetupN(double centerFrequency, double bandWidth) { - SetupBandPass2(centerFrequency, bandWidth); - } -} - -public sealed class BandPass2 : BandPass2; - -public class BandStop : RbjFilterBase - where TState : struct, ISectionState { - public void Setup(double sampleRate, double centerFrequency, double bandWidth) { - SetupBandStop(centerFrequency / sampleRate, bandWidth); - } - - public void SetupN(double centerFrequency, double bandWidth) { - SetupBandStop(centerFrequency, bandWidth); - } -} - -public sealed class BandStop : BandStop; - -public class IirNotch : RbjFilterBase - where TState : struct, ISectionState { - public void Setup(double sampleRate, double centerFrequency, double qFactor = 10.0) { - SetupNotch(centerFrequency / sampleRate, qFactor); - } - - public void SetupN(double centerFrequency, double qFactor = 10.0) { - SetupNotch(centerFrequency, qFactor); - } -} - -public sealed class IirNotch : IirNotch; - -public class LowShelf : RbjFilterBase - where TState : struct, ISectionState { - public void Setup(double sampleRate, double cutoffFrequency, double gainDb, double shelfSlope = 1.0) { - SetupLowShelf(cutoffFrequency / sampleRate, gainDb, shelfSlope); - } - - public void SetupN(double cutoffFrequency, double gainDb, double shelfSlope = 1.0) { - SetupLowShelf(cutoffFrequency, gainDb, shelfSlope); - } -} - -public sealed class LowShelf : LowShelf; - -public class HighShelf : RbjFilterBase - where TState : struct, ISectionState { - public void Setup(double sampleRate, double cutoffFrequency, double gainDb, double shelfSlope = 1.0) { - SetupHighShelf(cutoffFrequency / sampleRate, gainDb, shelfSlope); - } - - public void SetupN(double cutoffFrequency, double gainDb, double shelfSlope = 1.0) { - SetupHighShelf(cutoffFrequency, gainDb, shelfSlope); - } -} - -public sealed class HighShelf : HighShelf; - -public class BandShelf : RbjFilterBase - where TState : struct, ISectionState { - public void Setup(double sampleRate, double centerFrequency, double gainDb, double bandWidth) { - SetupBandShelf(centerFrequency / sampleRate, gainDb, bandWidth); - } - - public void SetupN(double centerFrequency, double gainDb, double bandWidth) { - SetupBandShelf(centerFrequency, gainDb, bandWidth); - } -} - -public sealed class BandShelf : BandShelf; - -public class AllPass : RbjFilterBase - where TState : struct, ISectionState { - public void Setup(double sampleRate, double phaseFrequency) { - SetupAllPass(phaseFrequency / sampleRate, OneOverSqrtTwo); - } - - - public void Setup(double sampleRate, double phaseFrequency, double q) { - SetupAllPass(phaseFrequency / sampleRate, q); - } - - public void SetupN(double phaseFrequency) { - SetupAllPass(phaseFrequency, OneOverSqrtTwo); - } - - public void SetupN(double phaseFrequency, double q) { - SetupAllPass(phaseFrequency, q); - } -} - -public sealed class AllPass : AllPass; \ No newline at end of file diff --git a/src/Spice86.Audio/Filters/IirFilters/Filters/RBJ/RbjFilters.cs b/src/Spice86.Audio/Filters/IirFilters/Filters/RBJ/RbjFilters.cs deleted file mode 100644 index 93d9e93..0000000 --- a/src/Spice86.Audio/Filters/IirFilters/Filters/RBJ/RbjFilters.cs +++ /dev/null @@ -1,158 +0,0 @@ -namespace Spice86.Audio.Filters.IirFilters.Filters.RBJ; - -using Spice86.Audio.Filters.IirFilters.Common; -using Spice86.Audio.Filters.IirFilters.Common.State; - -public abstract class RbjFilterBase : BiquadFilterBase - where TState : struct, ISectionState { - internal static double OneOverSqrtTwo => 1.0 / Math.Sqrt(2.0); - - protected void SetupLowPass(double cutoffFrequency, double q) { - double w0 = 2.0 * MathEx.DoublePi * cutoffFrequency; - double cs = Math.Cos(w0); - double sn = Math.Sin(w0); - double alpha = sn / (2.0 * q); - double b0 = (1.0 - cs) * 0.5; - double b1 = 1.0 - cs; - double b2 = (1.0 - cs) * 0.5; - double a0 = 1.0 + alpha; - double a1 = -2.0 * cs; - double a2 = 1.0 - alpha; - SetCoefficients(a0, a1, a2, b0, b1, b2); - } - - protected void SetupHighPass(double cutoffFrequency, double q) { - double w0 = 2.0 * MathEx.DoublePi * cutoffFrequency; - double cs = Math.Cos(w0); - double sn = Math.Sin(w0); - double alpha = sn / (2.0 * q); - double b0 = (1.0 + cs) * 0.5; - double b1 = -(1.0 + cs); - double b2 = (1.0 + cs) * 0.5; - double a0 = 1.0 + alpha; - double a1 = -2.0 * cs; - double a2 = 1.0 - alpha; - SetCoefficients(a0, a1, a2, b0, b1, b2); - } - - protected void SetupBandPass1(double centerFrequency, double bandWidth) { - double w0 = 2.0 * MathEx.DoublePi * centerFrequency; - double cs = Math.Cos(w0); - double sn = Math.Sin(w0); - double alpha = sn / (2.0 * bandWidth); - double gain = bandWidth * alpha; - const double b1 = 0.0; - double b2 = -gain; - double a0 = 1.0 + alpha; - double a1 = -2.0 * cs; - double a2 = 1.0 - alpha; - SetCoefficients(a0, a1, a2, gain, b1, b2); - } - - protected void SetupBandPass2(double centerFrequency, double bandWidth) { - double w0 = 2.0 * MathEx.DoublePi * centerFrequency; - double cs = Math.Cos(w0); - double sn = Math.Sin(w0); - double alpha = sn / (2.0 * bandWidth); - const double b1 = 0.0; - double b2 = -alpha; - double a0 = 1.0 + alpha; - double a1 = -2.0 * cs; - double a2 = 1.0 - alpha; - SetCoefficients(a0, a1, a2, alpha, b1, b2); - } - - protected void SetupBandStop(double centerFrequency, double bandWidth) { - double w0 = 2.0 * MathEx.DoublePi * centerFrequency; - double cs = Math.Cos(w0); - double sn = Math.Sin(w0); - double alpha = sn / (2.0 * bandWidth); - const double b0 = 1.0; - double b1 = -2.0 * cs; - const double b2 = 1.0; - double a0 = 1.0 + alpha; - double a1 = -2.0 * cs; - double a2 = 1.0 - alpha; - SetCoefficients(a0, a1, a2, b0, b1, b2); - } - - protected void SetupNotch(double centerFrequency, double q) { - double w0 = 2.0 * MathEx.DoublePi * centerFrequency; - double cs = Math.Cos(w0); - double r = Math.Exp(-(w0 / 2.0) / q); - const double b0 = 1.0; - double b1 = -2.0 * cs; - const double b2 = 1.0; - const double a0 = 1.0; - double a1 = -2.0 * r * cs; - double a2 = r * r; - SetCoefficients(a0, a1, a2, b0, b1, b2); - } - - protected void SetupLowShelf(double cutoffFrequency, double gainDb, double shelfSlope) { - double aGain = Math.Pow(10.0, gainDb / 40.0); - double w0 = 2.0 * MathEx.DoublePi * cutoffFrequency; - double cs = Math.Cos(w0); - double sn = Math.Sin(w0); - double alpha = sn / 2.0 * Math.Sqrt(((aGain + (1.0 / aGain)) * ((1.0 / shelfSlope) - 1.0)) + 2.0); - double sqrtA = Math.Sqrt(aGain); - double sq = 2.0 * sqrtA * alpha; - double b0 = aGain * (aGain + 1.0 - ((aGain - 1.0) * cs) + sq); - double b1 = 2.0 * aGain * (aGain - 1.0 - ((aGain + 1.0) * cs)); - double b2 = aGain * (aGain + 1.0 - ((aGain - 1.0) * cs) - sq); - double a0 = aGain + 1.0 + ((aGain - 1.0) * cs) + sq; - double a1 = -2.0 * (aGain - 1.0 + ((aGain + 1.0) * cs)); - double a2 = aGain + 1.0 + ((aGain - 1.0) * cs) - sq; - SetCoefficients(a0, a1, a2, b0, b1, b2); - } - - protected void SetupHighShelf(double cutoffFrequency, double gainDb, double shelfSlope) { - double aGain = Math.Pow(10.0, gainDb / 40.0); - double w0 = 2.0 * MathEx.DoublePi * cutoffFrequency; - double cs = Math.Cos(w0); - double sn = Math.Sin(w0); - double alpha = sn / 2.0 * Math.Sqrt(((aGain + (1.0 / aGain)) * ((1.0 / shelfSlope) - 1.0)) + 2.0); - double sqrtA = Math.Sqrt(aGain); - double sq = 2.0 * sqrtA * alpha; - double b0 = aGain * (aGain + 1.0 + ((aGain - 1.0) * cs) + sq); - double b1 = -2.0 * aGain * (aGain - 1.0 + ((aGain + 1.0) * cs)); - double b2 = aGain * (aGain + 1.0 + ((aGain - 1.0) * cs) - sq); - double a0 = aGain + 1.0 - ((aGain - 1.0) * cs) + sq; - double a1 = 2.0 * (aGain - 1.0 - ((aGain + 1.0) * cs)); - double a2 = aGain + 1.0 - ((aGain - 1.0) * cs) - sq; - SetCoefficients(a0, a1, a2, b0, b1, b2); - } - - protected void SetupBandShelf(double centerFrequency, double gainDb, double bandWidth) { - double aGain = Math.Pow(10.0, gainDb / 40.0); - double w0 = 2.0 * MathEx.DoublePi * centerFrequency; - double cs = Math.Cos(w0); - double sn = Math.Sin(w0); - double alpha = sn * Math.Sinh(MathEx.DoubleLn2 / 2.0 * bandWidth * w0 / sn); - if (double.IsNaN(alpha)) { - throw new ArgumentException("No solution available for these parameters."); - } - - double b0 = 1.0 + (alpha * aGain); - double b1 = -2.0 * cs; - double b2 = 1.0 - (alpha * aGain); - double a0 = 1.0 + (alpha / aGain); - double a1 = -2.0 * cs; - double a2 = 1.0 - (alpha / aGain); - SetCoefficients(a0, a1, a2, b0, b1, b2); - } - - protected void SetupAllPass(double phaseFrequency, double q) { - double w0 = 2.0 * MathEx.DoublePi * phaseFrequency; - double cs = Math.Cos(w0); - double sn = Math.Sin(w0); - double alpha = sn / (2.0 * q); - double b0 = 1.0 - alpha; - double b1 = -2.0 * cs; - double b2 = 1.0 + alpha; - double a0 = 1.0 + alpha; - double a1 = -2.0 * cs; - double a2 = 1.0 - alpha; - SetCoefficients(a0, a1, a2, b0, b1, b2); - } -} \ No newline at end of file diff --git a/src/Spice86.Audio/Filters/IirFilters/LICENSE b/src/Spice86.Audio/Filters/IirFilters/LICENSE deleted file mode 100644 index 066f57a..0000000 --- a/src/Spice86.Audio/Filters/IirFilters/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -MIT License - -Copyright (c) 2009 Vinnie Falco -Copyright (c) 2012 Bernd Porr - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file diff --git a/src/Spice86.Audio/Filters/IirFilters/README.md b/src/Spice86.Audio/Filters/IirFilters/README.md deleted file mode 100644 index 8f981af..0000000 --- a/src/Spice86.Audio/Filters/IirFilters/README.md +++ /dev/null @@ -1,5 +0,0 @@ -This C# implementation is a port of https://github.com/berndporr/iir1 by Vinnie Falco and Bernd Porr, -originally released under the MIT License. - -This version is © 2025 and is distributed under GPLv3. -See LICENSE for details. \ No newline at end of file diff --git a/src/Spice86.Audio/Filters/MVerb.cs b/src/Spice86.Audio/Filters/MVerb.cs deleted file mode 100644 index 0034a17..0000000 --- a/src/Spice86.Audio/Filters/MVerb.cs +++ /dev/null @@ -1,657 +0,0 @@ -// Copyright (c) 2010 Martin Eastwood -// This code is distributed under the terms of the GNU General Public License -// -// MVerb is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// at your option) any later version. -// -// MVerb is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this MVerb. If not, see . - -namespace Spice86.Audio.Filters; - -using System; - -public sealed class MVerb { - private const int MaxLength = 96000; - - private readonly Allpass[] _allpass = new Allpass[4]; - private readonly StaticAllpassFourTap[] _allpassFourTap = new StaticAllpassFourTap[4]; - private readonly StateVariable[] _bandwidthFilter = new StateVariable[2]; - private readonly StateVariable[] _damping = new StateVariable[2]; - private readonly StaticDelayLine _predelay = new(); - private readonly StaticDelayLineFourTap[] _staticDelayLine = new StaticDelayLineFourTap[4]; - private readonly StaticDelayLineEightTap[] _earlyReflectionsDelayLine = new StaticDelayLineEightTap[2]; - private float _sampleRate; - private float _maxFreq; - private float _dampingFreq; - private float _density1; - private float _density2; - private float _bandwidthFreq; - private float _preDelayTime; - private float _decay; - private float _gain; - private float _mix; - private float _earlyMix; - private float _size; - - private float _mixSmooth; - private float _earlyLateSmooth; - private float _bandwidthSmooth; - private float _dampingSmooth; - private float _predelaySmooth; - private float _sizeSmooth; - private float _densitySmooth; - private float _decaySmooth; - - private float _previousLeftTank; - private float _previousRightTank; - - private int _controlRate; - private int _controlRateCounter; - - public enum Parameter { - DampingFreq = 0, - Density, - BandwidthFreq, - Decay, - Predelay, - Size, - Gain, - Mix, - EarlyMix, - NumParams - } - - public MVerb() { - for (int i = 0; i < 4; i++) { - _allpass[i] = new Allpass(); - _allpassFourTap[i] = new StaticAllpassFourTap(); - _staticDelayLine[i] = new StaticDelayLineFourTap(); - } - - for (int i = 0; i < 2; i++) { - _bandwidthFilter[i] = new StateVariable(); - _damping[i] = new StateVariable(); - _earlyReflectionsDelayLine[i] = new StaticDelayLineEightTap(); - } - - _dampingFreq = 0.9f; - _bandwidthFreq = 0.9f; - _sampleRate = 44100.0f; - _maxFreq = 18400.0f; - _decay = 0.5f; - _gain = 1.0f; - _mix = 1.0f; - _size = 1.0f; - _earlyMix = 1.0f; - _previousLeftTank = 0.0f; - _previousRightTank = 0.0f; - _preDelayTime = 100.0f * (_sampleRate / 1000.0f); - _mixSmooth = _earlyLateSmooth = _bandwidthSmooth = _dampingSmooth = _predelaySmooth = _sizeSmooth = _decaySmooth = _densitySmooth = 0.0f; - _controlRate = (int)(_sampleRate / 1000); - _controlRateCounter = 0; - Reset(); - } - - /// - /// Processes a single audio frame through the reverb. - /// Matches DOSBox's per-frame mverb.process(in_buf, out_buf, 1) call pattern - /// where sampleFrames=1 causes parameter deltas to snap instantly. - /// - public void Process(ref float left, ref float right) { - _mixSmooth += _mix - _mixSmooth; - _earlyLateSmooth += _earlyMix - _earlyLateSmooth; - _bandwidthSmooth += ((_bandwidthFreq * _maxFreq) + 100.0f) - _bandwidthSmooth; - _dampingSmooth += ((_dampingFreq * _maxFreq) + 100.0f) - _dampingSmooth; - _predelaySmooth += (_preDelayTime * 200 * (_sampleRate / 1000)) - _predelaySmooth; - _sizeSmooth += _size - _sizeSmooth; - _decaySmooth += ((0.7995f * _decay) + 0.005f) - _decaySmooth; - _densitySmooth += ((0.7995f * _density1) + 0.005f) - _densitySmooth; - if (_controlRateCounter >= _controlRate) { - _controlRateCounter = 0; - _bandwidthFilter[0].Frequency(_bandwidthSmooth); - _bandwidthFilter[1].Frequency(_bandwidthSmooth); - _damping[0].Frequency(_dampingSmooth); - _damping[1].Frequency(_dampingSmooth); - } - ++_controlRateCounter; - _predelay.SetLength((int)_predelaySmooth); - _density2 = _decaySmooth + 0.15f; - if (_density2 > 0.5f) - _density2 = 0.5f; - if (_density2 < 0.25f) - _density2 = 0.25f; - _allpassFourTap[1].SetFeedback(_density2); - _allpassFourTap[3].SetFeedback(_density2); - _allpassFourTap[0].SetFeedback(_density1); - _allpassFourTap[2].SetFeedback(_density1); - float bandwidthLeft = _bandwidthFilter[0].Process(left); - float bandwidthRight = _bandwidthFilter[1].Process(right); - float earlyReflectionsL = _earlyReflectionsDelayLine[0].Process(bandwidthLeft * 0.5f + bandwidthRight * 0.3f) - + _earlyReflectionsDelayLine[0].GetIndex(2) * 0.6f - + _earlyReflectionsDelayLine[0].GetIndex(3) * 0.4f - + _earlyReflectionsDelayLine[0].GetIndex(4) * 0.3f - + _earlyReflectionsDelayLine[0].GetIndex(5) * 0.3f - + _earlyReflectionsDelayLine[0].GetIndex(6) * 0.1f - + _earlyReflectionsDelayLine[0].GetIndex(7) * 0.1f - + (bandwidthLeft * 0.4f + bandwidthRight * 0.2f) * 0.5f; - float earlyReflectionsR = _earlyReflectionsDelayLine[1].Process(bandwidthLeft * 0.3f + bandwidthRight * 0.5f) - + _earlyReflectionsDelayLine[1].GetIndex(2) * 0.6f - + _earlyReflectionsDelayLine[1].GetIndex(3) * 0.4f - + _earlyReflectionsDelayLine[1].GetIndex(4) * 0.3f - + _earlyReflectionsDelayLine[1].GetIndex(5) * 0.3f - + _earlyReflectionsDelayLine[1].GetIndex(6) * 0.1f - + _earlyReflectionsDelayLine[1].GetIndex(7) * 0.1f - + (bandwidthLeft * 0.2f + bandwidthRight * 0.4f) * 0.5f; - float predelayMonoInput = _predelay.Process((bandwidthRight + bandwidthLeft) * 0.5f); - float smearedInput = predelayMonoInput; - for (int j = 0; j < 4; j++) - smearedInput = _allpass[j].Process(smearedInput); - float leftTank = _allpassFourTap[0].Process(smearedInput + _previousRightTank); - leftTank = _staticDelayLine[0].Process(leftTank); - leftTank = _damping[0].Process(leftTank); - leftTank = _allpassFourTap[1].Process(leftTank); - leftTank = _staticDelayLine[1].Process(leftTank); - float rightTank = _allpassFourTap[2].Process(smearedInput + _previousLeftTank); - rightTank = _staticDelayLine[2].Process(rightTank); - rightTank = _damping[1].Process(rightTank); - rightTank = _allpassFourTap[3].Process(rightTank); - rightTank = _staticDelayLine[3].Process(rightTank); - _previousLeftTank = leftTank * _decaySmooth; - _previousRightTank = rightTank * _decaySmooth; - float accumulatorL = (0.6f * _staticDelayLine[2].GetIndex(1)) - + (0.6f * _staticDelayLine[2].GetIndex(2)) - - (0.6f * _allpassFourTap[3].GetIndex(1)) - + (0.6f * _staticDelayLine[3].GetIndex(1)) - - (0.6f * _staticDelayLine[0].GetIndex(1)) - - (0.6f * _allpassFourTap[1].GetIndex(1)) - - (0.6f * _staticDelayLine[1].GetIndex(1)); - float accumulatorR = (0.6f * _staticDelayLine[0].GetIndex(2)) - + (0.6f * _staticDelayLine[0].GetIndex(3)) - - (0.6f * _allpassFourTap[1].GetIndex(2)) - + (0.6f * _staticDelayLine[1].GetIndex(2)) - - (0.6f * _staticDelayLine[2].GetIndex(3)) - - (0.6f * _allpassFourTap[3].GetIndex(2)) - - (0.6f * _staticDelayLine[3].GetIndex(2)); - accumulatorL = ((accumulatorL * _earlyMix) + ((1 - _earlyMix) * earlyReflectionsL)); - accumulatorR = ((accumulatorR * _earlyMix) + ((1 - _earlyMix) * earlyReflectionsR)); - left = (left + _mixSmooth * (accumulatorL - left)) * _gain; - right = (right + _mixSmooth * (accumulatorR - right)) * _gain; - } - - public void Reset() { - _controlRateCounter = 0; - _bandwidthFilter[0].SetSampleRate(_sampleRate); - _bandwidthFilter[1].SetSampleRate(_sampleRate); - _bandwidthFilter[0].Reset(); - _bandwidthFilter[1].Reset(); - _damping[0].SetSampleRate(_sampleRate); - _damping[1].SetSampleRate(_sampleRate); - _damping[0].Reset(); - _damping[1].Reset(); - _predelay.Clear(); - _predelay.SetLength((int)_preDelayTime); - _allpass[0].Clear(); - _allpass[1].Clear(); - _allpass[2].Clear(); - _allpass[3].Clear(); - _allpass[0].SetLength((int)(0.0048 * _sampleRate)); - _allpass[1].SetLength((int)(0.0036 * _sampleRate)); - _allpass[2].SetLength((int)(0.0127 * _sampleRate)); - _allpass[3].SetLength((int)(0.0093 * _sampleRate)); - _allpass[0].SetFeedback(0.75f); - _allpass[1].SetFeedback(0.75f); - _allpass[2].SetFeedback(0.625f); - _allpass[3].SetFeedback(0.625f); - _allpassFourTap[0].Clear(); - _allpassFourTap[1].Clear(); - _allpassFourTap[2].Clear(); - _allpassFourTap[3].Clear(); - _allpassFourTap[0].SetLength((int)(0.020 * _sampleRate * _size)); - _allpassFourTap[1].SetLength((int)(0.060 * _sampleRate * _size)); - _allpassFourTap[2].SetLength((int)(0.030 * _sampleRate * _size)); - _allpassFourTap[3].SetLength((int)(0.089 * _sampleRate * _size)); - _allpassFourTap[0].SetFeedback(_density1); - _allpassFourTap[1].SetFeedback(_density2); - _allpassFourTap[2].SetFeedback(_density1); - _allpassFourTap[3].SetFeedback(_density2); - _allpassFourTap[0].SetIndex(0, 0, 0, 0); - _allpassFourTap[1].SetIndex(0, (int)(0.006 * _sampleRate * _size), (int)(0.041 * _sampleRate * _size), 0); - _allpassFourTap[2].SetIndex(0, 0, 0, 0); - _allpassFourTap[3].SetIndex(0, (int)(0.031 * _sampleRate * _size), (int)(0.011 * _sampleRate * _size), 0); - _staticDelayLine[0].Clear(); - _staticDelayLine[1].Clear(); - _staticDelayLine[2].Clear(); - _staticDelayLine[3].Clear(); - _staticDelayLine[0].SetLength((int)(0.15 * _sampleRate * _size)); - _staticDelayLine[1].SetLength((int)(0.12 * _sampleRate * _size)); - _staticDelayLine[2].SetLength((int)(0.14 * _sampleRate * _size)); - _staticDelayLine[3].SetLength((int)(0.11 * _sampleRate * _size)); - _staticDelayLine[0].SetIndex(0, (int)(0.067 * _sampleRate * _size), (int)(0.011 * _sampleRate * _size), (int)(0.121 * _sampleRate * _size)); - _staticDelayLine[1].SetIndex(0, (int)(0.036 * _sampleRate * _size), (int)(0.089 * _sampleRate * _size), 0); - _staticDelayLine[2].SetIndex(0, (int)(0.0089 * _sampleRate * _size), (int)(0.099 * _sampleRate * _size), 0); - _staticDelayLine[3].SetIndex(0, (int)(0.067 * _sampleRate * _size), (int)(0.0041 * _sampleRate * _size), 0); - _earlyReflectionsDelayLine[0].Clear(); - _earlyReflectionsDelayLine[1].Clear(); - _earlyReflectionsDelayLine[0].SetLength((int)(0.089 * _sampleRate)); - _earlyReflectionsDelayLine[0].SetIndex(0, (int)(0.0199 * _sampleRate), (int)(0.0219 * _sampleRate), (int)(0.0354 * _sampleRate), (int)(0.0389 * _sampleRate), (int)(0.0414 * _sampleRate), (int)(0.0692 * _sampleRate), 0); - _earlyReflectionsDelayLine[1].SetLength((int)(0.069 * _sampleRate)); - _earlyReflectionsDelayLine[1].SetIndex(0, (int)(0.0099 * _sampleRate), (int)(0.011 * _sampleRate), (int)(0.0182 * _sampleRate), (int)(0.0189 * _sampleRate), (int)(0.0213 * _sampleRate), (int)(0.0431 * _sampleRate), 0); - } - - public void SetParameter(int index, float value) { - switch (index) { - case (int)Parameter.DampingFreq: - _dampingFreq = 1.0f - value; - break; - case (int)Parameter.Density: - _density1 = value; - break; - case (int)Parameter.BandwidthFreq: - _bandwidthFreq = value; - break; - case (int)Parameter.Predelay: - _preDelayTime = value; - break; - case (int)Parameter.Size: - _size = (0.95f * value) + 0.05f; - _allpassFourTap[0].Clear(); - _allpassFourTap[1].Clear(); - _allpassFourTap[2].Clear(); - _allpassFourTap[3].Clear(); - _allpassFourTap[0].SetLength((int)(0.020 * _sampleRate * _size)); - _allpassFourTap[1].SetLength((int)(0.060 * _sampleRate * _size)); - _allpassFourTap[2].SetLength((int)(0.030 * _sampleRate * _size)); - _allpassFourTap[3].SetLength((int)(0.089 * _sampleRate * _size)); - _allpassFourTap[1].SetIndex(0, (int)(0.006 * _sampleRate * _size), (int)(0.041 * _sampleRate * _size), 0); - _allpassFourTap[3].SetIndex(0, (int)(0.031 * _sampleRate * _size), (int)(0.011 * _sampleRate * _size), 0); - _staticDelayLine[0].Clear(); - _staticDelayLine[1].Clear(); - _staticDelayLine[2].Clear(); - _staticDelayLine[3].Clear(); - _staticDelayLine[0].SetLength((int)(0.15 * _sampleRate * _size)); - _staticDelayLine[1].SetLength((int)(0.12 * _sampleRate * _size)); - _staticDelayLine[2].SetLength((int)(0.14 * _sampleRate * _size)); - _staticDelayLine[3].SetLength((int)(0.11 * _sampleRate * _size)); - _staticDelayLine[0].SetIndex(0, (int)(0.067 * _sampleRate * _size), (int)(0.011 * _sampleRate * _size), (int)(0.121 * _sampleRate * _size)); - _staticDelayLine[1].SetIndex(0, (int)(0.036 * _sampleRate * _size), (int)(0.089 * _sampleRate * _size), 0); - _staticDelayLine[2].SetIndex(0, (int)(0.0089 * _sampleRate * _size), (int)(0.099 * _sampleRate * _size), 0); - _staticDelayLine[3].SetIndex(0, (int)(0.067 * _sampleRate * _size), (int)(0.0041 * _sampleRate * _size), 0); - break; - case (int)Parameter.Decay: - _decay = value; - break; - case (int)Parameter.Gain: - _gain = value; - break; - case (int)Parameter.Mix: - _mix = value; - break; - case (int)Parameter.EarlyMix: - _earlyMix = value; - break; - } - } - - public float GetParameter(int index) { - return index switch { - (int)Parameter.DampingFreq => _dampingFreq * 100.0f, - (int)Parameter.Density => _density1 * 100.0f, - (int)Parameter.BandwidthFreq => _bandwidthFreq * 100.0f, - (int)Parameter.Predelay => _preDelayTime * 100.0f, - (int)Parameter.Size => ((0.95f * _size) + 0.05f) * 100.0f, - (int)Parameter.Decay => _decay * 100.0f, - (int)Parameter.Gain => _gain * 100.0f, - (int)Parameter.Mix => _mix * 100.0f, - (int)Parameter.EarlyMix => _earlyMix * 100.0f, - _ => 0.0f, - }; - } - - public void SetSampleRate(float sampleRate) { - _sampleRate = sampleRate; - _controlRate = (int)(_sampleRate / 1000); - _maxFreq = Math.Min(_sampleRate * 0.41723f, 18400.0f); - Reset(); - } - - private sealed class Allpass { - private readonly float[] _buffer = new float[MaxLength]; - private int _index; - private int _length; - private float _feedback; - - public Allpass() { - SetLength(MaxLength - 1); - Clear(); - _feedback = 0.5f; - } - - public float Process(float input) { - float output; - float bufout; - bufout = _buffer[_index]; - float temp = input * -_feedback; - output = bufout + temp; - _buffer[_index] = input + ((bufout + temp) * _feedback); - if (++_index >= _length) - _index = 0; - return output; - } - - public void SetLength(int length) { - if (length >= MaxLength) - length = MaxLength; - if (length < 0) - length = 0; - _length = length; - } - - public void SetFeedback(float feedback) { - _feedback = feedback; - } - - public void Clear() { - Array.Clear(_buffer, 0, _buffer.Length); - _index = 0; - } - } - - private sealed class StaticAllpassFourTap { - private readonly float[] _buffer = new float[MaxLength]; - private int _index1; - private int _index2; - private int _index3; - private int _index4; - private int _length; - private float _feedback; - - public StaticAllpassFourTap() { - SetLength(MaxLength - 1); - Clear(); - _feedback = 0.5f; - } - - public float Process(float input) { - float output; - float bufout; - bufout = _buffer[_index1]; - float temp = input * -_feedback; - output = bufout + temp; - _buffer[_index1] = input + ((bufout + temp) * _feedback); - - if (++_index1 >= _length) - _index1 = 0; - if (++_index2 >= _length) - _index2 = 0; - if (++_index3 >= _length) - _index3 = 0; - if (++_index4 >= _length) - _index4 = 0; - - return output; - } - - public void SetIndex(int index1, int index2, int index3, int index4) { - _index1 = index1; - _index2 = index2; - _index3 = index3; - _index4 = index4; - } - - public float GetIndex(int index) { - return index switch { - 0 => _buffer[_index1], - 1 => _buffer[_index2], - 2 => _buffer[_index3], - 3 => _buffer[_index4], - _ => _buffer[_index1], - }; - } - - public void SetLength(int length) { - if (length >= MaxLength) - length = MaxLength; - if (length < 0) - length = 0; - _length = length; - } - - public void Clear() { - Array.Clear(_buffer, 0, _buffer.Length); - _index1 = _index2 = _index3 = _index4 = 0; - } - - public void SetFeedback(float feedback) { - _feedback = feedback; - } - } - - private sealed class StaticDelayLine { - private readonly float[] _buffer = new float[MaxLength]; - private int _index; - private int _length; - - public StaticDelayLine() { - SetLength(MaxLength - 1); - Clear(); - } - - public float Process(float input) { - float output = _buffer[_index]; - _buffer[_index++] = input; - if (_index >= _length) - _index = 0; - return output; - } - - public void SetLength(int length) { - if (length >= MaxLength) - length = MaxLength; - if (length < 0) - length = 0; - _length = length; - } - - public void Clear() { - Array.Clear(_buffer, 0, _buffer.Length); - _index = 0; - } - } - - private sealed class StaticDelayLineFourTap { - private readonly float[] _buffer = new float[MaxLength]; - private int _index1; - private int _index2; - private int _index3; - private int _index4; - private int _length; - - public StaticDelayLineFourTap() { - SetLength(MaxLength - 1); - Clear(); - } - - public float Process(float input) { - float output = _buffer[_index1]; - _buffer[_index1++] = input; - if (_index1 >= _length) - _index1 = 0; - if (++_index2 >= _length) - _index2 = 0; - if (++_index3 >= _length) - _index3 = 0; - if (++_index4 >= _length) - _index4 = 0; - return output; - } - - public void SetIndex(int index1, int index2, int index3, int index4) { - _index1 = index1; - _index2 = index2; - _index3 = index3; - _index4 = index4; - } - - public float GetIndex(int index) { - return index switch { - 0 => _buffer[_index1], - 1 => _buffer[_index2], - 2 => _buffer[_index3], - 3 => _buffer[_index4], - _ => _buffer[_index1], - }; - } - - public void SetLength(int length) { - if (length >= MaxLength) - length = MaxLength; - if (length < 0) - length = 0; - _length = length; - } - - public void Clear() { - Array.Clear(_buffer, 0, _buffer.Length); - _index1 = _index2 = _index3 = _index4 = 0; - } - } - - private sealed class StaticDelayLineEightTap { - private readonly float[] _buffer = new float[MaxLength]; - private int _index1; - private int _index2; - private int _index3; - private int _index4; - private int _index5; - private int _index6; - private int _index7; - private int _index8; - private int _length; - - public StaticDelayLineEightTap() { - SetLength(MaxLength - 1); - Clear(); - } - - public float Process(float input) { - float output = _buffer[_index1]; - _buffer[_index1++] = input; - if (_index1 >= _length) - _index1 = 0; - if (++_index2 >= _length) - _index2 = 0; - if (++_index3 >= _length) - _index3 = 0; - if (++_index4 >= _length) - _index4 = 0; - if (++_index5 >= _length) - _index5 = 0; - if (++_index6 >= _length) - _index6 = 0; - if (++_index7 >= _length) - _index7 = 0; - if (++_index8 >= _length) - _index8 = 0; - return output; - } - - public void SetIndex(int index1, int index2, int index3, int index4, int index5, int index6, int index7, int index8) { - _index1 = index1; - _index2 = index2; - _index3 = index3; - _index4 = index4; - _index5 = index5; - _index6 = index6; - _index7 = index7; - _index8 = index8; - } - - public float GetIndex(int index) { - return index switch { - 0 => _buffer[_index1], - 1 => _buffer[_index2], - 2 => _buffer[_index3], - 3 => _buffer[_index4], - 4 => _buffer[_index5], - 5 => _buffer[_index6], - 6 => _buffer[_index7], - 7 => _buffer[_index8], - _ => _buffer[_index1], - }; - } - - public void SetLength(int length) { - if (length >= MaxLength) - length = MaxLength; - if (length < 0) - length = 0; - _length = length; - } - - public void Clear() { - Array.Clear(_buffer, 0, _buffer.Length); - _index1 = _index2 = _index3 = _index4 = _index5 = _index6 = _index7 = _index8 = 0; - } - } - - private sealed class StateVariable { - private const int OverSampleCount = 4; - - private float _sampleRate; - private float _frequency; - private float _q; - private float _f; - - private float _low; - private float _high; - private float _band; - private float _notch; - - public StateVariable() { - SetSampleRate(44100.0f); - Frequency(1000.0f); - Resonance(0); - Reset(); - } - - public float Process(float input) { - for (int i = 0; i < OverSampleCount; i++) { - _low += _f * _band + 1e-25f; - _high = input - _low - _q * _band; - _band += _f * _high; - _notch = _low + _high; - } - return _low; - } - - public void Reset() { - _low = _high = _band = _notch = 0; - } - - public void SetSampleRate(float sampleRate) { - _sampleRate = sampleRate * OverSampleCount; - UpdateCoefficient(); - } - - public void Frequency(float frequency) { - _frequency = frequency; - UpdateCoefficient(); - } - - public void Resonance(float resonance) { - _q = 2 - 2 * resonance; - } - - private void UpdateCoefficient() { - _f = (float)(2.0 * Math.Sin(Math.PI * _frequency / _sampleRate)); - } - } -} \ No newline at end of file diff --git a/src/Spice86.Audio/Filters/NoiseGate.cs b/src/Spice86.Audio/Filters/NoiseGate.cs index 7349643..ffd68f1 100644 --- a/src/Spice86.Audio/Filters/NoiseGate.cs +++ b/src/Spice86.Audio/Filters/NoiseGate.cs @@ -1,10 +1,12 @@ // SPDX-License-Identifier: GPL-2.0-or-later // Reference: src/audio/private/noise_gate.h and noise_gate.cpp +using Iir.Butterworth; + namespace Spice86.Audio.Filters; using Spice86.Audio.Common; -using Spice86.Audio.Filters.IirFilters.Filters.Butterworth; + /// /// Implements a simple noise gate that mutes the signal below a given threshold. diff --git a/src/Spice86.Audio/Filters/Speex/SpeexResamplerCSharp.cs b/src/Spice86.Audio/Filters/Speex/SpeexResamplerCSharp.cs deleted file mode 100644 index 21699df..0000000 --- a/src/Spice86.Audio/Filters/Speex/SpeexResamplerCSharp.cs +++ /dev/null @@ -1,794 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -/* Copyright (C) 2007-2008 Jean-Marc Valin - Copyright (C) 2008 Thorvald Natvig - C# Port: 2025 for Spice86 project - - Arbitrary resampling code - Faithful C# port from libspeexdsp/resample.c - - Redistribution and use in source and binary forms, with or without - modification, are permitted providing that the following conditions are - met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - 3. The name of the author may not be used to endorse or promote products - derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. -*/ - -namespace Spice86.Audio.Filters.Speex; - -/// -/// Pure C# port of Speex high-quality audio resampler from libspeexdsp. -/// This is a faithful, minimal-deviation port of the floating-point C implementation. -/// Maintains exact algorithm fidelity to the original resample.c. -/// -public sealed class SpeexResamplerCSharp { - private const int RESAMPLER_ERR_SUCCESS = 0; - private const int RESAMPLER_ERR_ALLOC_FAILED = 1; - private const int RESAMPLER_ERR_BAD_STATE = 2; - private const int RESAMPLER_ERR_INVALID_ARG = 3; - private const int RESAMPLER_ERR_PTR_OVERLAP = 4; - private const int RESAMPLER_ERR_OVERFLOW = 5; - - private const double M_PI = 3.14159265358979323846; - - private static readonly double[] kaiser12_table = new double[68] { - 0.99859849, 1.00000000, 0.99859849, 0.99440475, 0.98745105, 0.97779076, - 0.96549770, 0.95066529, 0.93340547, 0.91384741, 0.89213598, 0.86843014, - 0.84290116, 0.81573067, 0.78710866, 0.75723148, 0.72629970, 0.69451601, - 0.66208321, 0.62920216, 0.59606986, 0.56287762, 0.52980938, 0.49704014, - 0.46473455, 0.43304576, 0.40211431, 0.37206735, 0.34301800, 0.31506490, - 0.28829195, 0.26276832, 0.23854851, 0.21567274, 0.19416736, 0.17404546, - 0.15530766, 0.13794294, 0.12192957, 0.10723616, 0.09382272, 0.08164178, - 0.07063950, 0.06075685, 0.05193064, 0.04409466, 0.03718069, 0.03111947, - 0.02584161, 0.02127838, 0.01736250, 0.01402878, 0.01121463, 0.00886058, - 0.00691064, 0.00531256, 0.00401805, 0.00298291, 0.00216702, 0.00153438, - 0.00105297, 0.00069463, 0.00043489, 0.00025272, 0.00013031, 0.0000527734, - 0.00001000, 0.00000000 - }; - - private static readonly double[] kaiser10_table = new double[36] { - 0.99537781, 1.00000000, 0.99537781, 0.98162644, 0.95908712, 0.92831446, - 0.89005583, 0.84522401, 0.79486424, 0.74011713, 0.68217934, 0.62226347, - 0.56155915, 0.50119680, 0.44221549, 0.38553619, 0.33194107, 0.28205962, - 0.23636152, 0.19515633, 0.15859932, 0.12670280, 0.09935205, 0.07632451, - 0.05731132, 0.04193980, 0.02979584, 0.02044510, 0.01345224, 0.00839739, - 0.00488951, 0.00257636, 0.00115101, 0.00035515, 0.00000000, 0.00000000 - }; - - private static readonly double[] kaiser8_table = new double[36] { - 0.99635258, 1.00000000, 0.99635258, 0.98548012, 0.96759014, 0.94302200, - 0.91223751, 0.87580811, 0.83439927, 0.78875245, 0.73966538, 0.68797126, - 0.63451750, 0.58014482, 0.52566725, 0.47185369, 0.41941150, 0.36897272, - 0.32108304, 0.27619388, 0.23465776, 0.19672670, 0.16255380, 0.13219758, - 0.10562887, 0.08273982, 0.06335451, 0.04724088, 0.03412321, 0.02369490, - 0.01563093, 0.00959968, 0.00527363, 0.00233883, 0.00050000, 0.00000000 - }; - - private static readonly double[] kaiser6_table = new double[36] { - 0.99733006, 1.00000000, 0.99733006, 0.98935595, 0.97618418, 0.95799003, - 0.93501423, 0.90755855, 0.87598009, 0.84068475, 0.80211977, 0.76076565, - 0.71712752, 0.67172623, 0.62508937, 0.57774224, 0.53019925, 0.48295561, - 0.43647969, 0.39120616, 0.34752997, 0.30580127, 0.26632152, 0.22934058, - 0.19505503, 0.16360756, 0.13508755, 0.10953262, 0.08693120, 0.06722600, - 0.05031820, 0.03607231, 0.02432151, 0.01487334, 0.00752000, 0.00000000 - }; - - private sealed class FuncDef { - public readonly double[] table; - public readonly int oversample; - - public FuncDef(double[] table, int oversample) { - this.table = table; - this.oversample = oversample; - } - } - - private static readonly FuncDef KAISER12 = new(kaiser12_table, 64); - private static readonly FuncDef KAISER10 = new(kaiser10_table, 32); - private static readonly FuncDef KAISER8 = new(kaiser8_table, 32); - private static readonly FuncDef KAISER6 = new(kaiser6_table, 32); - - private sealed class QualityMapping { - public readonly int base_length; - public readonly int oversample; - public readonly float downsample_bandwidth; - public readonly float upsample_bandwidth; - public readonly FuncDef window_func; - - public QualityMapping(int base_length, int oversample, float downsample_bandwidth, - float upsample_bandwidth, FuncDef window_func) { - this.base_length = base_length; - this.oversample = oversample; - this.downsample_bandwidth = downsample_bandwidth; - this.upsample_bandwidth = upsample_bandwidth; - this.window_func = window_func; - } - } - - private static readonly QualityMapping[] quality_map = new QualityMapping[11] { - new( 8, 4, 0.830f, 0.860f, KAISER6), - new( 16, 4, 0.850f, 0.880f, KAISER6), - new( 32, 4, 0.882f, 0.910f, KAISER6), - new( 48, 8, 0.895f, 0.917f, KAISER8), - new( 64, 8, 0.921f, 0.940f, KAISER8), - new( 80, 16, 0.922f, 0.940f, KAISER10), - new( 96, 16, 0.940f, 0.945f, KAISER10), - new(128, 16, 0.950f, 0.950f, KAISER10), - new(160, 16, 0.960f, 0.960f, KAISER10), - new(192, 32, 0.968f, 0.968f, KAISER12), - new(256, 32, 0.975f, 0.975f, KAISER12) - }; - - private uint in_rate; - private uint out_rate; - private uint num_rate; - private uint den_rate; - private readonly int quality; - private readonly uint nb_channels; - private uint filt_len; - private uint mem_alloc_size; - private const uint buffer_size = 160; - private int int_advance; - private int frac_advance; - private float cutoff; - private uint oversample; - private int started; - - private readonly int[] last_sample; - private readonly uint[] samp_frac_num; - private readonly uint[] magic_samples; - private float[] mem; - private float[] sinc_table; - private uint sinc_table_length; - - private enum ResamplerType { - Zero, - DirectSingle, - DirectDouble, - InterpolateSingle, - InterpolateDouble - } - private ResamplerType resampler_type = ResamplerType.Zero; - - private int in_stride; - private int out_stride; - - public SpeexResamplerCSharp(uint channels, uint in_rate_init, uint out_rate_init, int quality) { - if (channels is 0 or > 256) { - throw new ArgumentException(null, nameof(channels)); - } - if (quality is < 0 or > 10) { - throw new ArgumentException(null, nameof(quality)); - } - - nb_channels = channels; - this.in_rate = in_rate_init; - this.out_rate = out_rate_init; - this.quality = quality; - this.num_rate = in_rate_init; - this.den_rate = out_rate_init; - - last_sample = new int[channels]; - samp_frac_num = new uint[channels]; - magic_samples = new uint[channels]; - mem = []; - sinc_table = []; - sinc_table_length = 0; - mem_alloc_size = 0; - - in_stride = 1; - out_stride = 1; - started = 0; - resampler_type = ResamplerType.Zero; - - if (UpdateFilter() != RESAMPLER_ERR_SUCCESS) { - throw new InvalidOperationException("Could not update speex resampler filter"); - } - } - - private static double ComputeFunc(float x, FuncDef func) { - float y = x * func.oversample; - int ind = (int)Math.Floor(y); - float frac = y - ind; - - double[] interp = new double[4]; - interp[3] = -0.1666666667 * frac + 0.1666666667 * (frac * frac * frac); - interp[2] = frac + 0.5 * (frac * frac) - 0.5 * (frac * frac * frac); - interp[0] = -0.3333333333 * frac + 0.5 * (frac * frac) - 0.1666666667 * (frac * frac * frac); - interp[1] = 1.0 - interp[3] - interp[2] - interp[0]; - - return interp[0] * func.table[ind] + interp[1] * func.table[ind + 1] + - interp[2] * func.table[ind + 2] + interp[3] * func.table[ind + 3]; - } - - private static float Sinc(float cutoff, float x, int N, FuncDef window_func) { - float xx = x * cutoff; - if (Math.Abs(x) < 1e-6) { - return cutoff; - } else if (Math.Abs(x) > 0.5f * N) { - return 0; - } - return (float)(cutoff * Math.Sin(M_PI * xx) / (M_PI * xx) * ComputeFunc(Math.Abs(2.0f * x / N), window_func)); - } - - private static void CubicCoef(float frac, float[] interp) { - interp[0] = -0.16667f * frac + 0.16667f * frac * frac * frac; - interp[1] = frac + 0.5f * frac * frac - 0.5f * frac * frac * frac; - interp[3] = -0.33333f * frac + 0.5f * frac * frac - 0.16667f * frac * frac * frac; - interp[2] = 1.0f - interp[0] - interp[1] - interp[3]; - } - - private int ResamplerBasicDirectSingle(uint channel_index, Span inBuf, int inBufOffset, ref uint in_len, Span outBuf, int outBufOffset, ref uint out_len, int outStride) { - int N = (int)filt_len; - int out_sample = 0; - int last_sample_val = last_sample[channel_index]; - uint samp_frac_num_val = samp_frac_num[channel_index]; - - while (!(last_sample_val >= (int)in_len || out_sample >= (int)out_len)) { - int sincIdx = (int)(samp_frac_num_val * N); - double sum = 0; - for (int j = 0; j < N; j++) { - sum += sinc_table[sincIdx + j] * inBuf[inBufOffset + last_sample_val + j]; - } - outBuf[outBufOffset + outStride * out_sample++] = (float)sum; - - last_sample_val += int_advance; - samp_frac_num_val += (uint)frac_advance; - if (samp_frac_num_val >= den_rate) { - samp_frac_num_val -= den_rate; - last_sample_val++; - } - } - - last_sample[channel_index] = last_sample_val; - samp_frac_num[channel_index] = samp_frac_num_val; - return out_sample; - } - - private int ResamplerBasicDirectDouble(uint channel_index, Span inBuf, int inBufOffset, ref uint in_len, Span outBuf, int outBufOffset, ref uint out_len, int outStride) { - int N = (int)filt_len; - int out_sample = 0; - int last_sample_val = last_sample[channel_index]; - uint samp_frac_num_val = samp_frac_num[channel_index]; - - while (!(last_sample_val >= (int)in_len || out_sample >= (int)out_len)) { - int sincIdx = (int)(samp_frac_num_val * N); - double sum = 0; - for (int j = 0; j < N; j += 4) { - sum += sinc_table[sincIdx + j] * inBuf[inBufOffset + last_sample_val + j]; - if (j + 1 < N) sum += sinc_table[sincIdx + j + 1] * inBuf[inBufOffset + last_sample_val + j + 1]; - if (j + 2 < N) sum += sinc_table[sincIdx + j + 2] * inBuf[inBufOffset + last_sample_val + j + 2]; - if (j + 3 < N) sum += sinc_table[sincIdx + j + 3] * inBuf[inBufOffset + last_sample_val + j + 3]; - } - outBuf[outBufOffset + outStride * out_sample++] = (float)sum; - - last_sample_val += int_advance; - samp_frac_num_val += (uint)frac_advance; - if (samp_frac_num_val >= den_rate) { - samp_frac_num_val -= den_rate; - last_sample_val++; - } - } - - last_sample[channel_index] = last_sample_val; - samp_frac_num[channel_index] = samp_frac_num_val; - return out_sample; - } - - private int ResamplerBasicInterpolateSingle(uint channel_index, Span inBuf, int inBufOffset, ref uint in_len, Span outBuf, int outBufOffset, ref uint out_len, int outStride) { - int N = (int)filt_len; - int out_sample = 0; - int last_sample_val = last_sample[channel_index]; - uint samp_frac_num_val = samp_frac_num[channel_index]; - - while (!(last_sample_val >= (int)in_len || out_sample >= (int)out_len)) { - int offset = (int)(samp_frac_num_val * oversample / den_rate); - float frac = ((float)((samp_frac_num_val * oversample) % den_rate)) / den_rate; - - float c0 = -0.16667f * frac + 0.16667f * frac * frac * frac; - float c1 = frac + 0.5f * frac * frac - 0.5f * frac * frac * frac; - float c3 = -0.33333f * frac + 0.5f * frac * frac - 0.16667f * frac * frac * frac; - float c2 = 1.0f - c0 - c1 - c3; - - double accum0 = 0, accum1 = 0, accum2 = 0, accum3 = 0; - for (int j = 0; j < N; j++) { - float curr = inBuf[inBufOffset + last_sample_val + j]; - int idx = 4 + (j + 1) * (int)oversample - offset; - accum0 += curr * sinc_table[idx - 2]; - accum1 += curr * sinc_table[idx - 1]; - accum2 += curr * sinc_table[idx]; - accum3 += curr * sinc_table[idx + 1]; - } - - double sum = c0 * accum0 + c1 * accum1 + c2 * accum2 + c3 * accum3; - outBuf[outBufOffset + outStride * out_sample++] = (float)sum; - - last_sample_val += int_advance; - samp_frac_num_val += (uint)frac_advance; - if (samp_frac_num_val >= den_rate) { - samp_frac_num_val -= den_rate; - last_sample_val++; - } - } - - last_sample[channel_index] = last_sample_val; - samp_frac_num[channel_index] = samp_frac_num_val; - return out_sample; - } - - private int ResamplerBasicInterpolateDouble(uint channel_index, Span inBuf, int inBufOffset, ref uint in_len, Span outBuf, int outBufOffset, ref uint out_len, int outStride) { - int N = (int)filt_len; - int out_sample = 0; - int last_sample_val = last_sample[channel_index]; - uint samp_frac_num_val = samp_frac_num[channel_index]; - - while (!(last_sample_val >= (int)in_len || out_sample >= (int)out_len)) { - int offset = (int)(samp_frac_num_val * oversample / den_rate); - float frac = ((float)((samp_frac_num_val * oversample) % den_rate)) / den_rate; - - float c0 = -0.16667f * frac + 0.16667f * frac * frac * frac; - float c1 = frac + 0.5f * frac * frac - 0.5f * frac * frac * frac; - float c3 = -0.33333f * frac + 0.5f * frac * frac - 0.16667f * frac * frac * frac; - float c2 = 1.0f - c0 - c1 - c3; - - double accum0 = 0, accum1 = 0, accum2 = 0, accum3 = 0; - for (int j = 0; j < N; j++) { - double curr = inBuf[inBufOffset + last_sample_val + j]; - int idx = 4 + (j + 1) * (int)oversample - offset; - accum0 += curr * sinc_table[idx - 2]; - accum1 += curr * sinc_table[idx - 1]; - accum2 += curr * sinc_table[idx]; - accum3 += curr * sinc_table[idx + 1]; - } - - double sum = c0 * accum0 + c1 * accum1 + c2 * accum2 + c3 * accum3; - outBuf[outBufOffset + outStride * out_sample++] = (float)sum; - - last_sample_val += int_advance; - samp_frac_num_val += (uint)frac_advance; - if (samp_frac_num_val >= den_rate) { - samp_frac_num_val -= den_rate; - last_sample_val++; - } - } - - last_sample[channel_index] = last_sample_val; - samp_frac_num[channel_index] = samp_frac_num_val; - return out_sample; - } - - private int ResamplerBasicZero(uint channel_index, Span inBuf, int inBufOffset, ref uint in_len, Span outBuf, int outBufOffset, ref uint out_len, int outStride) { - int out_sample = 0; - int last_sample_val = last_sample[channel_index]; - uint samp_frac_num_val = samp_frac_num[channel_index]; - - while (!(last_sample_val >= (int)in_len || out_sample >= (int)out_len)) { - outBuf[outBufOffset + outStride * out_sample++] = 0; - - last_sample_val += int_advance; - samp_frac_num_val += (uint)frac_advance; - if (samp_frac_num_val >= den_rate) { - samp_frac_num_val -= den_rate; - last_sample_val++; - } - } - - last_sample[channel_index] = last_sample_val; - samp_frac_num[channel_index] = samp_frac_num_val; - return out_sample; - } - - private static int MultiplyFrac(out uint result, uint value, uint num, uint den) { - result = 0; - uint major = value / den; - uint remain = value % den; - - if (remain > uint.MaxValue / num || major > uint.MaxValue / num || - major * num > uint.MaxValue - remain * num / den) { - return RESAMPLER_ERR_OVERFLOW; - } - result = remain * num / den + major * num; - return RESAMPLER_ERR_SUCCESS; - } - - private int UpdateFilter() { - uint old_length = filt_len; - uint old_alloc_size = mem_alloc_size; - bool use_direct; - uint min_sinc_table_length; - uint min_alloc_size; - - int_advance = (int)(num_rate / den_rate); - frac_advance = (int)(num_rate % den_rate); - oversample = (uint)quality_map[quality].oversample; - filt_len = (uint)quality_map[quality].base_length; - - if (num_rate > den_rate) { - cutoff = quality_map[quality].downsample_bandwidth * den_rate / num_rate; - if (MultiplyFrac(out filt_len, filt_len, num_rate, den_rate) != RESAMPLER_ERR_SUCCESS) { - goto fail; - } - filt_len = ((filt_len - 1) & (~0x7u)) + 8; - if (2 * den_rate < num_rate) oversample >>= 1; - if (4 * den_rate < num_rate) oversample >>= 1; - if (8 * den_rate < num_rate) oversample >>= 1; - if (16 * den_rate < num_rate) oversample >>= 1; - if (oversample < 1) oversample = 1; - } else { - cutoff = quality_map[quality].upsample_bandwidth; - } - - use_direct = filt_len * den_rate <= filt_len * oversample + 8 && - int.MaxValue / sizeof_float / den_rate >= filt_len; - - if (use_direct) { - min_sinc_table_length = filt_len * den_rate; - } else { - if ((int.MaxValue / sizeof_float - 8) / oversample < filt_len) { - goto fail; - } - min_sinc_table_length = filt_len * oversample + 8; - } - - if (sinc_table_length < min_sinc_table_length) { - sinc_table = new float[min_sinc_table_length]; - sinc_table_length = min_sinc_table_length; - } - - if (use_direct) { - for (uint i = 0; i < den_rate; i++) { - for (int j = 0; j < (int)filt_len; j++) { - sinc_table[i * filt_len + (uint)j] = - Sinc(cutoff, ((j - (int)filt_len / 2 + 1) - ((float)i) / den_rate), (int)filt_len, - quality_map[quality].window_func); - } - } - if (quality > 8) { - resampler_type = ResamplerType.DirectDouble; - } else { - resampler_type = ResamplerType.DirectSingle; - } - } else { - for (int i = -4; i < (int)(oversample * filt_len + 4); i++) { - sinc_table[i + 4] = Sinc(cutoff, i / (float)oversample - filt_len / 2.0f, (int)filt_len, - quality_map[quality].window_func); - } - if (quality > 8) { - resampler_type = ResamplerType.InterpolateDouble; - } else { - resampler_type = ResamplerType.InterpolateSingle; - } - } - - min_alloc_size = filt_len - 1 + buffer_size; - if (min_alloc_size > mem_alloc_size) { - if (int.MaxValue / sizeof_float / nb_channels < min_alloc_size) { - goto fail; - } - mem = new float[nb_channels * min_alloc_size * 2]; - mem_alloc_size = min_alloc_size; - } - - if (started == 0) { - for (uint i = 0; i < nb_channels * mem_alloc_size; i++) { - mem[i] = 0; - } - } else if (filt_len > old_length) { - for (uint i = nb_channels; i-- > 0;) { - uint j; - uint olen = old_length; - uint start = i * mem_alloc_size; - uint magic = magic_samples[i]; - - olen = old_length + 2 * magic; - for (j = old_length - 1 + magic; j-- > 0;) { - mem[start + j + magic] = mem[i * old_alloc_size + j]; - } - for (j = 0; j < magic; j++) { - mem[start + j] = 0; - } - magic_samples[i] = 0; - - if (filt_len > olen) { - for (j = 0; j < olen - 1; j++) { - mem[start + (filt_len - 2 - j)] = mem[start + (olen - 2 - j)]; - } - for (; j < filt_len - 1; j++) { - mem[start + (filt_len - 2 - j)] = 0; - } - last_sample[i] += (int)((filt_len - olen) / 2); - } else { - magic = (olen - filt_len) / 2; - for (j = 0; j < filt_len - 1 + magic; j++) { - mem[start + (filt_len - 2 - j)] = mem[start + (olen - 2 - j)]; - } - for (j = filt_len - 1 + magic; j < olen; j++) { - mem[start + (filt_len - 2 - j)] = mem[start + (olen - 2 - j)]; - } - for (j = 0; j < filt_len - 1; j++) { - mem[start + (filt_len - 2 - j)] = 0; - } - last_sample[i] += (int)((filt_len - olen) / 2); - } - } - } else if (filt_len < old_length) { - for (uint i = 0; i < nb_channels; i++) { - uint j; - uint old_magic = magic_samples[i]; - magic_samples[i] = (old_length - filt_len) / 2; - - for (j = 0; j < filt_len - 1 + magic_samples[i] + old_magic; j++) { - mem[i * mem_alloc_size + j] = mem[i * mem_alloc_size + j + magic_samples[i]]; - } - magic_samples[i] += old_magic; - } - } - - return RESAMPLER_ERR_SUCCESS; - -fail: - resampler_type = ResamplerType.Zero; - filt_len = old_length; - return RESAMPLER_ERR_ALLOC_FAILED; - } - - private int SpeexResamplerProcessNative(uint channel_index, ref uint in_len, Span outBuf, int outOffset, ref uint out_len) { - int N = (int)filt_len; - int memOffset = (int)(channel_index * mem_alloc_size); - - started = 1; - - int out_sample; - switch (resampler_type) { - case ResamplerType.DirectSingle: - out_sample = ResamplerBasicDirectSingle(channel_index, mem, memOffset, ref in_len, outBuf, outOffset, ref out_len, out_stride); - break; - case ResamplerType.DirectDouble: - out_sample = ResamplerBasicDirectDouble(channel_index, mem, memOffset, ref in_len, outBuf, outOffset, ref out_len, out_stride); - break; - case ResamplerType.InterpolateSingle: - out_sample = ResamplerBasicInterpolateSingle(channel_index, mem, memOffset, ref in_len, outBuf, outOffset, ref out_len, out_stride); - break; - case ResamplerType.InterpolateDouble: - out_sample = ResamplerBasicInterpolateDouble(channel_index, mem, memOffset, ref in_len, outBuf, outOffset, ref out_len, out_stride); - break; - case ResamplerType.Zero: - default: - out_sample = ResamplerBasicZero(channel_index, mem, memOffset, ref in_len, outBuf, outOffset, ref out_len, out_stride); - break; - } - - if (last_sample[channel_index] < (int)in_len) { - in_len = (uint)last_sample[channel_index]; - } - out_len = (uint)out_sample; - last_sample[channel_index] -= (int)in_len; - - uint ilen = in_len; - - for (int j = 0; j < N - 1; j++) { - mem[memOffset + j] = mem[memOffset + (int)ilen + j]; - } - - return RESAMPLER_ERR_SUCCESS; - } - - private int SpeexResamplerMagic(uint channel_index, Span outBuf, ref int outOffset, uint out_len) { - uint tmp_in_len = magic_samples[channel_index]; - int memOffset = (int)(channel_index * mem_alloc_size); - int N = (int)filt_len; - - uint out_len_temp = out_len; - SpeexResamplerProcessNative(channel_index, ref tmp_in_len, outBuf, outOffset, ref out_len_temp); - - magic_samples[channel_index] -= tmp_in_len; - - if (magic_samples[channel_index] != 0) { - for (uint i = 0; i < magic_samples[channel_index]; i++) { - mem[memOffset + N - 1 + (int)i] = mem[memOffset + N - 1 + (int)tmp_in_len + (int)i]; - } - } - - outOffset += (int)out_len_temp * out_stride; - return (int)out_len_temp; - } - - public int ProcessFloat(uint channel_index, ReadOnlySpan input, int inputOffset, - ref uint in_len, Span output, int outputOffset, ref uint out_len) { - - if (channel_index >= nb_channels) { - return RESAMPLER_ERR_INVALID_ARG; - } - - int memOffset = (int)(channel_index * mem_alloc_size); - int filt_offs = (int)filt_len - 1; - uint xlen = mem_alloc_size - (uint)filt_offs; - int istride = in_stride; - - uint ilen = in_len; - uint olen = out_len; - - int currentOutOffset = outputOffset; - int currentInOffset = inputOffset; - - if (magic_samples[channel_index] != 0) { - olen -= (uint)SpeexResamplerMagic(channel_index, output, ref currentOutOffset, olen); - } - - if (magic_samples[channel_index] == 0) { - while (ilen > 0 && olen > 0) { - uint ichunk = (ilen > xlen) ? xlen : ilen; - uint ochunk = olen; - - if (input.Length > 0) { - for (int j = 0; j < (int)ichunk; j++) { - int srcIdx = currentInOffset + j * istride; - if (srcIdx < input.Length) { - mem[memOffset + filt_offs + j] = input[srcIdx]; - } else { - mem[memOffset + filt_offs + j] = 0; - } - } - } else { - for (int j = 0; j < (int)ichunk; j++) { - mem[memOffset + filt_offs + j] = 0; - } - } - - SpeexResamplerProcessNative(channel_index, ref ichunk, output, currentOutOffset, ref ochunk); - - ilen -= ichunk; - olen -= ochunk; - currentOutOffset += (int)ochunk * out_stride; - if (input.Length > 0) { - currentInOffset += (int)ichunk * istride; - } - } - } - - in_len -= ilen; - out_len -= olen; - - return resampler_type == ResamplerType.Zero ? RESAMPLER_ERR_ALLOC_FAILED : RESAMPLER_ERR_SUCCESS; - } - - public int ProcessInterleavedFloat(ReadOnlySpan input, ref uint in_len, - Span output, ref uint out_len) { - - int istride_save = in_stride; - int ostride_save = out_stride; - uint bak_out_len = out_len; - uint bak_in_len = in_len; - - in_stride = (int)nb_channels; - out_stride = (int)nb_channels; - - for (uint i = 0; i < nb_channels; i++) { - out_len = bak_out_len; - in_len = bak_in_len; - - if (input.Length > 0) { - ProcessFloat(i, input, (int)i, ref in_len, output, (int)i, ref out_len); - } else { - ProcessFloat(i, [], 0, ref in_len, output, (int)i, ref out_len); - } - } - - in_stride = istride_save; - out_stride = ostride_save; - - return resampler_type == ResamplerType.Zero ? RESAMPLER_ERR_ALLOC_FAILED : RESAMPLER_ERR_SUCCESS; - } - - public void ProcessInterleavedFloat(ReadOnlySpan input, Span output, - out uint inputFramesConsumed, out uint outputFramesProduced) { - - if (nb_channels == 0) { - inputFramesConsumed = 0; - outputFramesProduced = 0; - return; - } - - uint inFrames = (uint)(input.Length / nb_channels); - uint outFrames = (uint)(output.Length / nb_channels); - - if (inFrames == 0 || outFrames == 0) { - inputFramesConsumed = 0; - outputFramesProduced = 0; - return; - } - - uint in_len = inFrames; - uint out_len = outFrames; - - ProcessInterleavedFloat(input, ref in_len, output, ref out_len); - - inputFramesConsumed = in_len; - outputFramesProduced = out_len; - } - - public void SetRate(uint in_rate_new, uint out_rate_new) { - uint old_den = den_rate; - in_rate = in_rate_new; - out_rate = out_rate_new; - num_rate = in_rate_new; - den_rate = out_rate_new; - - uint a = num_rate; - uint b = den_rate; - while (b != 0) { - uint t = a % b; - a = b; - b = t; - } - num_rate /= a; - den_rate /= a; - - if (old_den > 0) { - for (uint i = 0; i < nb_channels; i++) { - if (MultiplyFrac(out samp_frac_num[i], samp_frac_num[i], den_rate, old_den) != RESAMPLER_ERR_SUCCESS) { - return; - } - if (samp_frac_num[i] >= den_rate) { - samp_frac_num[i] = den_rate - 1; - } - } - } - - UpdateFilter(); - } - - public void Reset() { - for (uint i = 0; i < nb_channels; i++) { - last_sample[i] = 0; - samp_frac_num[i] = 0; - magic_samples[i] = 0; - } - for (uint i = 0; i < nb_channels * mem_alloc_size; i++) { - mem[i] = 0; - } - started = 0; - } - - public void SkipZeros() { - for (uint i = 0; i < nb_channels; i++) { - last_sample[i] = (int)(filt_len / 2); - } - started = 1; - } - - public bool IsInitialized => true; - - public void GetRatio(out uint num, out uint den) { - num = num_rate; - den = den_rate; - } - - public int GetInputLatency() { - return (int)(filt_len / 2); - } - - public int GetOutputLatency() { - return (int)(((filt_len / 2) * den_rate + (num_rate >> 1)) / num_rate); - } - - private const int sizeof_float = 4; -} diff --git a/src/Spice86.Audio/Spice86.Audio.csproj b/src/Spice86.Audio/Spice86.Audio.csproj index 4a3b703..441c11e 100644 --- a/src/Spice86.Audio/Spice86.Audio.csproj +++ b/src/Spice86.Audio/Spice86.Audio.csproj @@ -9,6 +9,7 @@ Spice86.Audio +