using Sandbox; using System; namespace SFXR; [Title( "Low Pass Filter" )] [Category( "SFXR Effects" )] [Icon( "align_horizontal_right" )] public class SFXRLowPass : SFXREffect { /// /// Cutoff value of the low-pass filter /// (Default: 1, Range: 0 - 1) /// [Property, Range( 0f, 12000f, 0.01f )] public float Cutoff { get; set; } = 4000; /// /// Amount by which the low-pass cutoff frequency is increased or decreased over time /// (Default: 0, Range: -1 - 1) /// [Property, Range( -12000f, 12000f, 0.01f )] public float Sweep { get; set; } = 0; /// /// Controls the low-pass filter's resonance (peak) /// (Default: 0, Range: 0 - 1) /// [Property, Range( 0f, 1f, 0.01f )] public float Resonance { get; set; } = 0; public override short[] Apply( short[] samples, SFXRComponent sound ) { short[] outputSamples = new short[samples.Length]; double currentCutoff = Cutoff; double previousOutput = 0; for ( int i = 0; i < samples.Length; i++ ) { // Update cutoff frequency based on Sweep currentCutoff += Sweep / (double)sound.SampleRate; double RC = 1.0 / (2 * Math.PI * currentCutoff); double dt = 1.0 / (double)sound.SampleRate; double alpha = dt / (RC + dt); double inputSample = samples[i]; double outputSample = alpha * inputSample + (1 - alpha) * previousOutput; // Apply resonance if ( Resonance > 0 ) { double feedback = Resonance + Resonance / (1 - currentCutoff); outputSample = (1 - feedback) * outputSample + feedback * previousOutput; } outputSamples[i] = (short)Math.Clamp( outputSample, short.MinValue, short.MaxValue ); previousOutput = outputSample; } return outputSamples; } }