public void TestFilterAppliedDirectly() { AssertFilterOutput(_filter.ApplyTo(_signal, FilteringMethod.DifferenceEquation)); }
/// <summary> /// Method creates overlapping ERB filters (ported from Malcolm Slaney's MATLAB code). /// </summary> /// <param name="erbFilterCount">Number of ERB filters</param> /// <param name="fftSize">Assumed size of FFT</param> /// <param name="samplingRate">Assumed sampling rate</param> /// <param name="lowFreq">Lower bound of the frequency range</param> /// <param name="highFreq">Upper bound of the frequency range</param> /// <param name="normalizeGain">True if gain should be normalized; false if all filters should have same height 1.0</param> /// <returns>Array of ERB filters</returns> public static float[][] Erb( int erbFilterCount, int fftSize, int samplingRate, double lowFreq = 0, double highFreq = 0, bool normalizeGain = true) { if (lowFreq < 0) { lowFreq = 0; } if (highFreq <= lowFreq) { highFreq = samplingRate / 2.0; } const double earQ = 9.26449; const double minBw = 24.7; const double bw = earQ * minBw; const int order = 1; var t = 1.0 / samplingRate; var frequencies = new double[erbFilterCount]; for (var i = 1; i <= erbFilterCount; i++) { frequencies[erbFilterCount - i] = -bw + Math.Exp(i * (-Math.Log(highFreq + bw) + Math.Log(lowFreq + bw)) / erbFilterCount) * (highFreq + bw); } var ucirc = new Complex[fftSize / 2 + 1]; for (var i = 0; i < ucirc.Length; i++) { ucirc[i] = Complex.Exp((2 * Complex.ImaginaryOne * i * Math.PI) / fftSize); } var rootPos = Math.Sqrt(3 + Math.Pow(2, 1.5)); var rootNeg = Math.Sqrt(3 - Math.Pow(2, 1.5)); var fft = new Fft(fftSize); var erbFilterBank = new float[erbFilterCount][]; for (var i = 0; i < erbFilterCount; i++) { var cf = frequencies[i]; var erb = Math.Pow(Math.Pow(cf / earQ, order) + Math.Pow(minBw, order), 1.0 / order); var b = 1.019 * 2 * Math.PI * erb; var theta = 2 * cf * Math.PI * t; var itheta = Complex.Exp(2 * Complex.ImaginaryOne * theta); var a0 = t; var a2 = 0.0; var b0 = 1.0; var b1 = -2 * Math.Cos(theta) / Math.Exp(b * t); var b2 = Math.Exp(-2 * b * t); var common = -t *Math.Exp(-b *t); var k1 = Math.Cos(theta) + rootPos * Math.Sin(theta); var k2 = Math.Cos(theta) - rootPos * Math.Sin(theta); var k3 = Math.Cos(theta) + rootNeg * Math.Sin(theta); var k4 = Math.Cos(theta) - rootNeg * Math.Sin(theta); var a11 = common * k1; var a12 = common * k2; var a13 = common * k3; var a14 = common * k4; var gainArg = Complex.Exp(Complex.ImaginaryOne * theta - b * t); var gain = Complex.Abs( (itheta - gainArg * k1) * (itheta - gainArg * k2) * (itheta - gainArg * k3) * (itheta - gainArg * k4) * Complex.Pow(t * Math.Exp(b * t) / (-1.0 / Math.Exp(b * t) + 1 + itheta * (1 - Math.Exp(b * t))), 4.0)); var filter1 = new IirFilter(new[] { a0, a11, a2 }, new[] { b0, b1, b2 }); var filter2 = new IirFilter(new[] { a0, a12, a2 }, new[] { b0, b1, b2 }); var filter3 = new IirFilter(new[] { a0, a13, a2 }, new[] { b0, b1, b2 }); var filter4 = new IirFilter(new[] { a0, a14, a2 }, new[] { b0, b1, b2 }); var ir = new double[fftSize]; ir[0] = 1.0; // for doubles the following code will work ok // (however there's a crucial lost of precision in case of floats): //var filter = filter1 * filter2 * filter3 * filter4; //ir = filter.ApplyTo(ir); // this code is ok both for floats and for doubles: ir = filter1.ApplyTo(ir); ir = filter2.ApplyTo(ir); ir = filter3.ApplyTo(ir); ir = filter4.ApplyTo(ir); var kernel = new DiscreteSignal(1, ir.Select(s => (float)(s / gain))); erbFilterBank[i] = fft.PowerSpectrum(kernel, false).Samples; } // normalize gain (by default) if (!normalizeGain) { return(erbFilterBank); } foreach (var filter in erbFilterBank) { var sum = 0.0; for (var j = 0; j < filter.Length; j++) { sum += Math.Abs(filter[j] * filter[j]); } var weight = Math.Sqrt(sum * samplingRate / fftSize); for (var j = 0; j < filter.Length; j++) { filter[j] = (float)(filter[j] / weight); } } return(erbFilterBank); }
public void OfflineFilterForLoop() { var output = _filter.ApplyTo(_signal); }
public void Run() { var output2 = new float[_signal.Length]; var output4 = new float[_signal.Length]; var output5 = new float[_signal.Length]; var outputZi = new float[_signal.Length]; var samples = _signal.Samples; for (var i = 0; i < samples.Length; i++) { output4[i] = _filterV4BiQuad.Process(samples[i]); output5[i] = _filterV5BiQuad.Process(samples[i]); outputZi[i] = _filterZiBiQuad.Process(samples[i]); } var diffAverageV4 = output5.Zip(output4, (o5, o4) => Math.Abs(o5 - o4)).Average(); var diffAverageZi = output5.Zip(outputZi, (o5, zi) => Math.Abs(o5 - zi)).Average(); Console.WriteLine($"Average difference Ver.0.9.5 vs. Ver.0.9.4 : {diffAverageV4}"); Console.WriteLine($"Average difference IirFilter vs. ZiFilter : {diffAverageZi}"); for (var i = 0; i < samples.Length; i++) { output4[i] = _filterV4Butterworth6.Process(samples[i]); output5[i] = _filterV5Butterworth6.Process(samples[i]); outputZi[i] = _filterZiButterworth6.Process(samples[i]); } diffAverageV4 = output5.Zip(output4, (o5, o4) => Math.Abs(o5 - o4)).Average(); diffAverageZi = output5.Zip(outputZi, (o5, zi) => Math.Abs(o5 - zi)).Average(); Console.WriteLine($"Average difference Ver.0.9.5 vs. Ver.0.9.4 : {diffAverageV4}"); Console.WriteLine($"Average difference IirFilter vs. ZiFilter : {diffAverageZi}"); // === MISC ==== var med = new MedianFilter(); var med2 = new MedianFilter2(); var medOut = med.ApplyTo(_signal).Samples; var medOut2 = med2.ApplyTo(_signal).Samples; var diffAverageMed = medOut.Zip(medOut, (m1, m2) => Math.Abs(m1 - m2)).Average(); Console.WriteLine($"Average difference MedianFilter vs. MedianFilter2 : {diffAverageMed}"); var ma = new MovingAverageFilter(); var maRec = new MovingAverageRecursiveFilter(); var maOut = ma.ApplyTo(_signal).Samples; var maRecOut = maRec.ApplyTo(_signal).Samples; var diffAverageMa = maOut.Zip(maRecOut, (m1, m2) => Math.Abs(m1 - m2)).Average(); Console.WriteLine($"Average difference MovingAverageFilter vs. MovingAverageRecursiveFilter : {diffAverageMa}"); // 32bit vs. 64bit var fir32 = new FirFilter(DesignFilter.FirWinLp(7, 0.1)); var fir64 = new FirFilter64(DesignFilter.FirWinLp(7, 0.1)); var fir32Out = fir32.ApplyTo(_signal).Samples; var fir64Out = fir64.ApplyTo(_signal.Samples.ToDoubles()); var diffAverageFir = fir64Out.Zip(fir32Out, (m1, m2) => Math.Abs(m1 - m2)).Average(); Console.WriteLine($"Average difference FirFilter vs. FirFilter64 : {diffAverageFir}"); var iir32 = new IirFilter(_filterV5Butterworth6.Tf); var iir64 = new IirFilter64(_filterV5Butterworth6.Tf); var iir32Out = iir32.ApplyTo(_signal).Samples; var iir64Out = iir64.ApplyTo(_signal.Samples.ToDoubles()); var diffAverageIir = iir64Out.Zip(iir32Out, (m1, m2) => Math.Abs(m1 - m2)).Average(); Console.WriteLine($"Average difference IirFilter vs. IirFilter64 : {diffAverageIir}"); var zi32 = new ZiFilter(_filterV5Butterworth6.Tf); var zi64 = new ZiFilter64(_filterV5Butterworth6.Tf); var zi32Out = zi32.ApplyTo(_signal).Samples; var zi64Out = zi64.ApplyTo(_signal.Samples.ToDoubles()); var diffAverageZis = zi64Out.Zip(zi32Out, (m1, m2) => Math.Abs(m1 - m2)).Average(); Console.WriteLine($"Average difference ZiFilter vs. ZiFilter64 : {diffAverageZis}"); zi32Out = zi32.ZeroPhase(_signal).Samples; zi64Out = zi64.ZeroPhase(_signal.Samples.ToDoubles()); var diffAverageZiZeroPhase = zi64Out.Zip(zi32Out, (m1, m2) => Math.Abs(m1 - m2)).Average(); Console.WriteLine($"Average difference ZiFilter vs. ZiFilter64 (zero-phase): {diffAverageZiZeroPhase}"); }