private void SetupCoeffs() { var window = WWWindowFunc.BlackmanWindow(WindowLength); // ループ処理を簡単にするため最初と最後に0を置く。 mCoeffs = new double[1 + WindowLength + 1]; int center = WindowLength / 2; for (int i = 0; i < WindowLength / 2 + 1; ++i) { int numerator = i; int denominator = Factor; int numeratorReminder = numerator % (denominator * 2); if (numerator == 0) { mCoeffs[1 + center + i] = 1.0f; } else if (numerator % denominator == 0) { // sinc(180 deg) == 0, sinc(360 deg) == 0, ... mCoeffs[1 + center + i] = 0.0f; } else { mCoeffs[1 + center + i] = Math.Sin(Math.PI * numeratorReminder / denominator) / (Math.PI * numerator / denominator) * window[center + i]; } mCoeffs[1 + center - i] = mCoeffs[1 + center + i]; } }
public OverlappedFft(int fftLength) { mFftLength = fftLength; mFft = new WWRadix2Fft(mFftLength); mOverlapInputSamples = new double[mFftLength / 2]; mWindow = WWWindowFunc.BlackmanWindow(mFftLength / 2 - 1); mLastOutputSamplesTail = new double[mFftLength / mSpliceDenominator]; mFirstTime = true; }
private void DesignFilter() { mFilterCoeffs = new double[FilterLength]; var sine90Table = new double[] { 0.0, 1.0, 0.0, -1.0 }; for (int i = 0; i < FILTER_DELAY; ++i) { if (i != 0 && 0 == (i & 1)) { // coefficient is 0 continue; } double theta = Math.PI * (i * 90.0) / 180.0f; double v = 1.0; if (Double.Epsilon < Math.Abs(theta)) { v = sine90Table[i & 3] / theta; } mFilterCoeffs[FILTER_DELAY - 1 - i] = v; mFilterCoeffs[FILTER_DELAY - 1 + i] = v; } // Kaiser窓(α==9)をかける var w = WWWindowFunc.KaiserWindow(FilterLength, 9.0); for (int i = 0; i < FilterLength; ++i) { mFilterCoeffs[i] *= w[i]; } // 0.5倍する for (int i = 0; i < FilterLength; ++i) { mFilterCoeffs[i] *= 0.5; } }
private void DesignCutoffFilter() { var fromF = new WWComplex[FILTER_LENP1]; // バターワースフィルター // 1次 = 6dB/oct // 2次 = 12dB/oct double orderX2 = 2.0 * (FilterSlopeDbOct / 6.0); double cutoffRatio = CutoffFrequency / (SampleRate / 2); // フィルタのF特 fromF[0].real = 1.0f; for (int i = 1; i <= FILTER_LENP1 / 2; ++i) { double omegaRatio = i * (1.0 / (FILTER_LENP1 / 2)); double v = Math.Sqrt(1.0 / (1.0 + Math.Pow(omegaRatio / cutoffRatio, orderX2))); if (Math.Abs(v) < Math.Pow(0.5, 24)) { v = 0.0; } fromF[i].real = v; } for (int i = 1; i < FILTER_LENP1 / 2; ++i) { fromF[FILTER_LENP1 - i].real = fromF[i].real; } // IFFTでfromFをfromTに変換 var fromT = new WWComplex[FILTER_LENP1]; { var fft = new WWRadix2Fft(FILTER_LENP1); fft.ForwardFft(fromF, fromT); double compensation = 1.0 / (FILTER_LENP1 * cutoffRatio); for (int i = 0; i < FILTER_LENP1; ++i) { fromT[i].Set( fromT[i].real * compensation, fromT[i].imaginary * compensation); } } fromF = null; // fromTの中心がFILTER_LENGTH/2番に来るようにする。 // delayT[0]のデータはfromF[FILTER_LENGTH/2]だが、非対称になるので入れない // このフィルタの遅延はFILTER_LENGTH/2サンプルある var delayT = new WWComplex[FILTER_LENP1]; for (int i = 1; i < FILTER_LENP1 / 2; ++i) { delayT[i] = fromT[i + FILTER_LENP1 / 2]; } for (int i = 0; i < FILTER_LENP1 / 2; ++i) { delayT[i + FILTER_LENP1 / 2] = fromT[i]; } fromT = null; // Kaiser窓をかける var w = WWWindowFunc.KaiserWindow(FILTER_LENP1 + 1, 9.0); for (int i = 0; i < FILTER_LENP1; ++i) { delayT[i].Mul(w[i]); } var delayTL = new WWComplex[FFT_LEN]; for (int i = 0; i < delayT.Length; ++i) { delayTL[i] = delayT[i]; } delayT = null; // できたフィルタをFFTする var delayF = new WWComplex[FFT_LEN]; { var fft = new WWRadix2Fft(FFT_LEN); fft.ForwardFft(delayTL, delayF); for (int i = 0; i < FFT_LEN; ++i) { delayF[i].Mul(cutoffRatio); } } delayTL = null; mFilterFreq = delayF; }
public override double[] FilterDo(double[] inPcm) { System.Diagnostics.Debug.Assert(inPcm.Length == NumOfSamplesNeeded()); var inPcmR = new double[FftLength]; if (mFirst) { Array.Copy(inPcm, 0, inPcmR, HalfOverlapLength, inPcm.Length); } else { System.Diagnostics.Debug.Assert(mOverlapSamples != null); System.Diagnostics.Debug.Assert(mOverlapSamples.Length == HalfOverlapLength * 2); Array.Copy(mOverlapSamples, 0, inPcmR, 0, HalfOverlapLength * 2); mOverlapSamples = null; Array.Copy(inPcm, 0, inPcmR, HalfOverlapLength * 2, inPcm.Length); } var inPcmT = new WWComplex[FftLength]; for (int i = 0; i < inPcmT.Length; ++i) { inPcmT[i] = new WWComplex(inPcmR[i], 0); } { // inPcmTの出力されず捨てられる領域に窓関数を掛ける。 // Kaiser窓(α==9)をかける var w = WWWindowFunc.KaiserWindow(HalfOverlapLength * 2, 9.0); for (int i = 0; i < HalfOverlapLength; ++i) { inPcmT[i].Mul(w[i]); inPcmT[FftLength - i - 1].Mul(w[i]); } } // inPcmTをFFTしてinPcmFを得る。 WWComplex[] inPcmF; { var fft = new WWRadix2Fft(FftLength); inPcmF = fft.ForwardFft(inPcmT); } inPcmT = null; // inPcmFを0で水増ししたデータoutPcmFを作ってローパスフィルターを通し逆FFTしoutPcmTを得る。 var outPcmF = new WWComplex[UpsampleFftLength]; for (int i = 0; i < outPcmF.Length; ++i) { if (i <= FftLength / 2) { outPcmF[i].CopyFrom(inPcmF[i]); } else if (UpsampleFftLength - FftLength / 2 <= i) { int pos = i + FftLength - UpsampleFftLength; outPcmF[i].CopyFrom(inPcmF[pos]); } else { // do nothing } outPcmF[i].Mul(mFreqFilter[i]); } inPcmF = null; WWComplex[] outPcmT; { var fft = new WWRadix2Fft(UpsampleFftLength); outPcmT = fft.InverseFft(outPcmF, 1.0 / FftLength); } outPcmF = null; // outPcmTの実数成分を戻り値とする。 var outPcm = new double[Factor * (FftLength - HalfOverlapLength * 2)]; for (int i = 0; i < outPcm.Length; ++i) { outPcm[i] = outPcmT[i + Factor * HalfOverlapLength].real; } outPcmT = null; // 次回計算に使用するオーバーラップ部分のデータをmOverlapSamplesに保存。 // オーバラップ部分==inPcmRの最後の方 mOverlapSamples = new double[HalfOverlapLength * 2]; Array.Copy(inPcmR, inPcmR.Length - HalfOverlapLength * 2, mOverlapSamples, 0, HalfOverlapLength * 2); if (mFirst) { mFirst = false; } return(outPcm); }
/// <summary> /// バターワースフィルターのFFT coeffs /// </summary> /// <param name="filterSlopeDbOct">1次 = 6(dB/oct), 2次 = 12(dB/oct)</param> /// <returns></returns> public static WWComplex[] Design(int sampleRate, double cutoffFrequency, int fftLength, int filterSlopeDbOct) { int filterLenP1 = fftLength / 4; int filterLength = filterLenP1 - 1; var fromF = new WWComplex[filterLenP1]; double orderX2 = 2.0 * (filterSlopeDbOct / 6.0); double cutoffRatio = cutoffFrequency / (sampleRate / 2); // フィルタのF特 fromF[0].real = 1.0f; for (int i = 1; i <= filterLenP1 / 2; ++i) { double omegaRatio = i * (1.0 / (filterLenP1 / 2)); double v = Math.Sqrt(1.0 / (1.0 + Math.Pow(omegaRatio / cutoffRatio, orderX2))); if (Math.Abs(v) < Math.Pow(0.5, 24)) { v = 0.0; } fromF[i].real = v; } for (int i = 1; i < filterLenP1 / 2; ++i) { fromF[filterLenP1 - i].real = fromF[i].real; } // IFFTでfromFをfromTに変換 WWComplex[] fromT; { var fft = new WWRadix2Fft(filterLenP1); fromT = fft.ForwardFft(fromF); double compensation = 1.0 / (filterLenP1 * cutoffRatio); for (int i = 0; i < filterLenP1; ++i) { fromT[i].Set( fromT[i].real * compensation, fromT[i].imaginary * compensation); } } fromF = null; // fromTの中心がFILTER_LENGTH/2番に来るようにする。 // delayT[0]のデータはfromF[FILTER_LENGTH/2]だが、非対称になるので入れない // このフィルタの遅延はFILTER_LENGTH/2サンプルある var delayT = new WWComplex[filterLenP1]; for (int i = 1; i < filterLenP1 / 2; ++i) { delayT[i] = fromT[i + filterLenP1 / 2]; } for (int i = 0; i < filterLenP1 / 2; ++i) { delayT[i + filterLenP1 / 2] = fromT[i]; } fromT = null; // Kaiser窓をかける var w = WWWindowFunc.KaiserWindow(filterLenP1 + 1, 9.0); for (int i = 0; i < filterLenP1; ++i) { delayT[i].Mul(w[i]); } var delayTL = new WWComplex[fftLength]; for (int i = 0; i < delayT.Length; ++i) { delayTL[i] = delayT[i]; } delayT = null; // できたフィルタをFFTする WWComplex[] delayF; { var fft = new WWRadix2Fft(fftLength); delayF = fft.ForwardFft(delayTL); for (int i = 0; i < fftLength; ++i) { delayF[i].Mul(cutoffRatio); } } delayTL = null; return(delayF); }