public override void tick() { if (fft == null) { fft = new FFTProcessor(FFTProcessor.ProcessorMode.Bidirectional, blockSize, owner.sampleRate, FFTProcessor.WindowType.Hann); } SignalBuffer dbout = getSignalOutputBuffer(ioO); SignalBuffer dbin = getSignalInputBuffer(ioI); if (!_active) { return; } if ((dbout == null) && (dbin == null)) { return; } if (buffIn == null) { buffIn = new double[blockSize]; buffOut = new double[blockSize]; fftOut = new double[blockSize]; re = new double[blockSize / 2]; im = new double[blockSize / 2]; buffInFill = 0; buffOutFill = 0; buffOutRead = 0; } if (dbin != null) { Array.Copy(dbin.data, 0, buffIn, buffInFill, owner.blockSize); buffInFill += owner.blockSize; if (buffInFill == blockSize) { fft.runFFT(ref buffIn, true, ref re, ref im); Array.Copy(buffIn, blockSize / 2, buffIn, 0, buffInFill - blockSize / 2); buffInFill -= blockSize / 2; // Process FFT int n1, n2; double s, c; switch (filterMode) { case FFTFilterMode.LowPass: n1 = (int)Math.Floor(f1 / (double)owner.sampleRate * blockSize); if (n1 < 0) { n1 = 0; } for (int i = n1; i < blockSize / 2; i++) { re[i] = im[i] = 0; } break; case FFTFilterMode.HighPass: n1 = (int)Math.Ceiling(f1 / (double)owner.sampleRate * blockSize); if (n1 >= blockSize / 2) { n1 = blockSize / 2 - 1; } for (int i = 0; i < n1; i++) { re[i] = im[i] = 0; } break; case FFTFilterMode.BandPass: n1 = (int)Math.Floor(f1 / (double)owner.sampleRate * blockSize); n2 = (int)Math.Ceiling(f2 / (double)owner.sampleRate * blockSize); if (n1 < 0) { n1 = 0; } if (n2 >= blockSize / 2) { n2 = blockSize / 2 - 1; } for (int i = 0; i < n1; i++) { re[i] = im[i] = 0; } for (int i = n2 + 1; i < blockSize / 2; i++) { re[i] = im[i] = 0; } break; case FFTFilterMode.BandStop: n1 = (int)Math.Floor(f1 / (double)owner.sampleRate * blockSize); n2 = (int)Math.Ceiling(f2 / (double)owner.sampleRate * blockSize); if (n1 < 0) { n1 = 0; } if (n2 >= blockSize / 2) { n2 = blockSize / 2 - 1; } for (int i = n1 + 1; i < n2; i++) { re[i] = im[i] = 0; } break; case FFTFilterMode.AllPass: s = Math.Sin(phi); c = Math.Cos(phi); for (int i = 1; i < blockSize / 2; i++) { double _re = re[i] * c - im[i] * s; double _im = re[i] * s + im[i] * c; re[i] = _re; im[i] = _im; } break; case FFTFilterMode.FrequencyShifter: n1 = (int)Math.Floor(f1 / (double)owner.sampleRate * blockSize + 0.5); if (n1 > 0) { // positive shift double df = (double)owner.sampleRate / (double)blockSize; // Bin Spacing Array.Copy(re, 1, re, 1 + n1, blockSize / 2 - 1 - n1); Array.Copy(im, 1, im, 1 + n1, blockSize / 2 - 1 - n1); Array.Clear(re, 1, n1); Array.Clear(im, 1, n1); for (int i = 1 + n1; i < blockSize / 2; i++) { double T = 1.0 / i / df; double T2 = 1.0 / (i + n1) / df; double dphi = -2.0 * Math.PI * (T - T2) * (i + n1) * df; s = Math.Sin(dphi); c = Math.Cos(dphi); double _re = re[i] * c - im[i] * s; double _im = re[i] * s + im[i] * c; re[i] = _re; im[i] = _im; } } if (n1 < 0) { n1 = -n1; Array.Copy(re, n1 + 1, re, 1, blockSize / 2 - n1 - 1); Array.Copy(im, n1 + 1, im, 1, blockSize / 2 - n1 - 1); Array.Clear(re, blockSize / 2 - 1 - n1, n1); Array.Clear(im, blockSize / 2 - 1 - n1, n1); } break; } // Transfer Back fft.runIFFT(ref re, ref im, ref fftOut); Array.Copy(buffOut, blockSize / 2, buffOut, 0, blockSize / 2); Array.Clear(buffOut, blockSize / 2, blockSize / 2); for (int i = 0; i < blockSize; i++) { buffOut[i] += fftOut[i]; } buffOutFill = blockSize / 2; buffOutRead = 0; } if (buffOutFill > 0) { if (dbout != null) { Array.Copy(buffOut, buffOutRead, dbout.data, 0, owner.blockSize); buffOutRead += owner.blockSize; } } } }