private void Run() { int nFFT = WWRadix2Fft.NextPowerOf2(mLpfTime.Count()); nFFT *= 8; Console.WriteLine("LPF coeffs"); for (int i = 0; i < mLpfTime.Count(); ++i) { Console.WriteLine("{0}, {1:R}", i, mLpfTime[i]); } Console.WriteLine(""); var complementary = BuildDelayComplementaryFilter(mLpfTime); Console.WriteLine("Delay complementary HPF coeffs"); for (int i = 0; i < complementary.Count(); ++i) { Console.WriteLine("{0}, {1:R}", i, complementary[i]); } Console.WriteLine(""); Console.WriteLine("LPF Freq phase(deg)"); InspectFilter(mLpfTime, nFFT); Console.WriteLine(""); Console.WriteLine("HPF Freq phase(deg)"); InspectFilter(complementary, nFFT); }
private void InspectFilter(double[] coeff, int nFFT) { var time = new WWComplex[nFFT]; for (int i = 0; i < time.Count(); ++i) { if (i < coeff.Count()) { time[i] = new WWComplex(coeff[i], 0.0); } else { time[i] = new WWComplex(0.0, 0.0); } } var freq = new WWComplex[nFFT]; var fft = new WWRadix2Fft(nFFT); fft.ForwardFft(time, freq); for (int i = 0; i < freq.Count() / 2; ++i) { Console.WriteLine("{0}, {1}, {2}", i, freq[i].Magnitude(), freq[i].Phase()); } }
private bool CompareFFTroundTrip(double[] x) { int N = x.Length; var xC = WWComplex.FromRealArray(x); var fft = new WWRadix2Fft(N); var X = fft.ForwardFft(xC); var xR = fft.InverseFft(X); return(WWComplex.AverageDistance(xC, xR) < 1e-8); }
// //You can use the following additional attributes as you write your tests: // //Use ClassInitialize to run code before running the first test in the class //[ClassInitialize()] //public static void MyClassInitialize(TestContext testContext) //{ //} // //Use ClassCleanup to run code after all tests in a class have run //[ClassCleanup()] //public static void MyClassCleanup() //{ //} // //Use TestInitialize to run code before running each test //[TestInitialize()] //public void MyTestInitialize() //{ //} // //Use TestCleanup to run code after each test has run //[TestCleanup()] //public void MyTestCleanup() //{ //} // #endregion private bool CompareFFT(double[] x) { int N = x.Length; var xC = WWComplex.FromRealArray(x); var Xexpected = WWDftCpu.Dft1d(xC); var fft = new WWRadix2Fft(N); var Xactual = fft.ForwardFft(xC, 1.0 / N); return(WWComplex.AverageDistance(Xactual, Xexpected) < 1e-8); }
public WWComplex[] ConvolutionContinuousFft(WWComplex[] h, WWComplex[] x) { int fftSize = Functions.NextPowerOf2(h.Length * 4); int fragmentSize = fftSize - h.Length + 1; if (x.Length <= fragmentSize) { // 1回のFFTで計算する。 return(ConvolutionFft(h, x)); } var fft = new WWRadix2Fft(fftSize); var r = new WWComplex[h.Length + x.Length - 1]; var h2 = new WWComplex[fftSize]; Array.Copy(h, 0, h2, 0, h.Length); var Hf = fft.ForwardFft(h2); // x(n)をfragmentSize要素ごとのデータ列に分解し、 // それぞれ長さfftSizeになるように末尾に0を追加してx0(n)、x1(n)を得る。 //Parallel.For(0, x.Length / fragmentSize, i => { for (int i = 0; i < (x.Length + fragmentSize - 1) / fragmentSize; ++i) { var xf = new WWComplex[fftSize]; int count = fragmentSize; if (x.Length < (i + 1) * fragmentSize) { count = x.Length - i * fragmentSize; } Array.Copy(x, i * fragmentSize, xf, 0, count); var Xf = fft.ForwardFft(xf); var Yf = WWComplex.Mul(Hf, Xf); var yf = fft.InverseFft(Yf); for (int j = 0; j < fftSize; ++j) { if (r.Length <= i * fragmentSize + j) { break; } r[i * fragmentSize + j] = WWComplex.Add(r[i * fragmentSize + j], yf[j]); } } ; return(r); }
// 開始ボタンを押すと以下の順に実行される。 // BwStartTesting_DoWork() // └PreparePcmData() // BwStartTesting_RunWorkerCompleted() // ↓ ↓ // mPlayWorker.RunWorkerAsync() mRecWorker.RunWorkerAsync() // PlayDoWork() RecDoWork() → CaptureDataArrived() // (リピート再生) │ ├CaptureSync() // │ └CaptureRunning() // └ProcessCapturedData() → RecWorkerProgressChanged() // // ユーザーがStopボタン押下 // ↓ // PlayRunWorkerCompleted() ←──────────── mWasapiPlay.Stop() // mWasapiRec.Stop() // RecRunWorkerCompleted() private void buttonStart_Click(object sender, RoutedEventArgs e) { if (!UpdateTestParamsFromUI()) { return; } //Console.WriteLine("buttonStart_Click()"); groupBoxPcmDataSettings.IsEnabled = false; groupBoxPlayback.IsEnabled = false; groupBoxRecording.IsEnabled = false; UpdateButtonStartStop(ButtonStartStopState.Disable); textBoxLog.Text += "Preparing data.\n"; textBoxLog.ScrollToEnd(); int numCh = mPref.NumOfChannels; int playDwChMask = WasapiCS.GetTypicalChannelMask(numCh); int recDwChMask = 0; if (mPref.SetDwChannelMask) { recDwChMask = WasapiCS.GetTypicalChannelMask(numCh); } mLevelMeterUC.UpdateNumOfChannels(mPref.NumOfChannels); mTimeDomainPlot.Clear(); mFreqResponse.SamplingFrequency = mPref.SampleRate; mFreqResponse.TransferFunction = (WWComplex z) => { return(new WWComplex(1, 0)); }; mFreqResponse.Update(); Directory.CreateDirectory(textboxOutputFolder.Text); int numSamples = 1 << mPref.MLSOrder; mFFT = new WWRadix2Fft(numSamples); mStartTestingArgs = new StartTestingArgs(mPref.MLSOrder, numCh, mPref.TestChannel, playDwChMask, recDwChMask, textboxOutputFolder.Text); mBwStartTesting.RunWorkerAsync(mStartTestingArgs); }
public WWComplex[] ConvolutionFft(WWComplex[] h, WWComplex[] x) { var r = new WWComplex[h.Length + x.Length - 1]; int fftSize = Functions.NextPowerOf2(r.Length); var h2 = new WWComplex[fftSize]; Array.Copy(h, 0, h2, 0, h.Length); var x2 = new WWComplex[fftSize]; Array.Copy(x, 0, x2, 0, x.Length); var fft = new WWRadix2Fft(fftSize); var H = fft.ForwardFft(h2); var X = fft.ForwardFft(x2); var Y = WWComplex.Mul(H, X); var y = fft.InverseFft(Y); Array.Copy(y, 0, r, 0, r.Length); return(r); }
public override WWUtil.LargeArray <double> FilterDo(WWUtil.LargeArray <double> inPcmLA) { System.Diagnostics.Debug.Assert(inPcmLA.LongLength <= NumOfSamplesNeeded()); var inPcm = inPcmLA.ToArray(); var fft = new WWRadix2Fft(FFT_LEN); // Overlap and add continuous FFT var inTime = new WWComplex[FFT_LEN]; for (int i = 0; i < inPcm.Length; ++i) { inTime[i] = new WWComplex(inPcm[i], 0); } // FFTでinTimeをinFreqに変換 var inFreq = fft.ForwardFft(inTime); inTime = null; // FFT後、フィルターHの周波数ドメインデータを掛ける var mulFreq = WWComplex.Mul(inFreq, mFilterFreq); inFreq = null; // inFreqをIFFTしてoutTimeに変換 var outTime = fft.InverseFft(mulFreq); mulFreq = null; double [] outReal; if (mFirstFilterDo) { // 最初のFilterDo()のとき、フィルタの遅延サンプル数だけ先頭サンプルを削除する。 outReal = new double[NumOfSamplesNeeded() - FILTER_DELAY]; for (int i = 0; i < outReal.Length; ++i) { outReal[i] = outTime[i + FILTER_DELAY].real; } mFirstFilterDo = false; } else { outReal = new double[NumOfSamplesNeeded()]; for (int i = 0; i < outReal.Length; ++i) { outReal[i] = outTime[i].real; } } // 前回のIFFT結果の最後のFILTER_LENGTH-1サンプルを先頭に加算する if (null != mIfftAddBuffer) { for (int i = 0; i < mIfftAddBuffer.Length; ++i) { outReal[i] += mIfftAddBuffer[i]; } } // 今回のIFFT結果の最後のFILTER_LENGTH-1サンプルをmIfftAddBufferとして保存する mIfftAddBuffer = new double[FILTER_LENP1]; for (int i = 0; i < mIfftAddBuffer.Length; ++i) { mIfftAddBuffer[i] = outTime[outTime.Length - mIfftAddBuffer.Length + i].real; } outTime = null; return(new WWUtil.LargeArray <double>(outReal)); }
/// <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] = WWComplex.Unity(); 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] = new WWComplex(v, 0); } for (int i = 1; i < filterLenP1 / 2; ++i) { fromF[filterLenP1 - i] = fromF[i]; } // 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] = new WWComplex( 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]; delayT[0] = WWComplex.Zero(); 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] = WWComplex.Mul(delayT[i], w[i]); } var delayTL = new WWComplex[fftLength]; for (int i = 0; i < delayT.Length; ++i) { delayTL[i] = delayT[i]; } for (int i = delayT.Length; i < delayTL.Length; ++i) { delayTL[i] = WWComplex.Zero(); } delayT = null; // できたフィルタをFFTする WWComplex[] delayF; { var fft = new WWRadix2Fft(fftLength); delayF = fft.ForwardFft(delayTL); for (int i = 0; i < fftLength; ++i) { delayF[i] = WWComplex.Mul(delayF[i], cutoffRatio); } } delayTL = null; return(delayF); }