Пример #1
0
        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);
        }
Пример #2
0
        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());
            }
        }
Пример #3
0
        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);
        }
Пример #4
0
        //
        //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);
        }
Пример #5
0
        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);
        }
Пример #6
0
        // 開始ボタンを押すと以下の順に実行される。
        //                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);
        }
Пример #7
0
        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);
        }
Пример #8
0
        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));
        }
Пример #9
0
        /// <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);
        }