Пример #1
0
        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];
            }
        }
Пример #2
0
 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;
 }
Пример #3
0
        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;
            }
        }
Пример #4
0
        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;
        }
Пример #5
0
        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);
        }
Пример #6
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].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);
        }