/// <summary> /// Linear Convolution x ** h を計算。 /// </summary> /// <param name="h">コンボリューションカーネル。左右反転される。</param> /// <param name="x">入力数列。</param> 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); for (int i = h.Length; i < h2.Length; ++i) { h2[i] = WWComplex.Zero(); } var x2 = new WWComplex[fftSize]; Array.Copy(x, 0, x2, 0, x.Length); for (int i = x.Length; i < x2.Length; ++i) { x2[i] = WWComplex.Zero(); } 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; }
/// <summary> /// 連続FFT オーバーラップアド法でLinear convolution x ** hする。 /// </summary> /// <param name="h">コンボリューションカーネル。左右反転される。</param> /// <param name="x">入力数列。</param> /// <param name="fragmentSz">個々のFFTに入力するxの断片サイズ。</param> public WWComplex[] ConvolutionContinuousFft(WWComplex[] h, WWComplex[] x, int fragmentSz) { System.Diagnostics.Debug.Assert(2 <= fragmentSz); if (x.Length < h.Length) { // swap x and h var tmp = h; h = x; x = tmp; } // h.Len <= x.Len int fullConvLen = h.Length + x.Length - 1; var r = new WWComplex[fullConvLen]; for (int i = 0; i < r.Length; ++i) { r[i] = WWComplex.Zero(); } // hをFFTしてHを得る。 int fragConvLen = h.Length + fragmentSz - 1; int fftSize = Functions.NextPowerOf2(fragConvLen); var h2 = new WWComplex[fftSize]; Array.Copy(h, 0, h2, 0, h.Length); for (int i = h.Length; i < h2.Length; ++i) { h2[i] = WWComplex.Zero(); } var fft = new WWRadix2Fft(fftSize); var H = fft.ForwardFft(h2); for (int offs = 0; offs < x.Length; offs += fragmentSz) { // xFをFFTしてXを得る。 var xF = WWComplex.ZeroArray(fftSize); for (int i=0; i<fragmentSz; ++i) { if (i + offs < x.Length) { xF[i] = x[offs + i]; } else { break; } } var X = fft.ForwardFft(xF); var Y = WWComplex.Mul(H, X); var y = fft.InverseFft(Y); // オーバーラップアド法。FFT結果を足し合わせる。 for (int i = 0; i <fragConvLen; ++i) { r[offs + i] = WWComplex.Add(r[offs + i], y[i]); } } return r; }
/// <summary> /// 周波数ドメインの値を入力。 /// 時間ドメインの値を出力。 /// </summary> /// <param name="X">周波数ドメインの値。サンプル数=processBlockSize</param> /// <returns>時間ドメインの値。</returns> private double[] Process1(WWComplex[] X) { System.Diagnostics.Debug.Assert(X.Length == WantSamples); var x = WWComplex.ToRealArray(mFFT.InverseFft(X)); if (0 == mProcessCounter) { // 1回目。 // 前半のデータは埋め草なので破棄。後半のみ意味があるデータ。 var r = new double[ProcessSize / 2]; Array.Copy(x, ProcessSize / 2, r, 0, ProcessSize / 2); mOverlapBuff = r; ++mProcessCounter; return(new double[0]); } { // 2回目以降。 var r = new double[ProcessSize / 2]; for (int i = 0; i < r.Length; ++i) { r[i] = x[i] + mOverlapBuff[i]; } // 次回計算用にオーバーラップ部分を保存。 for (int i = 0; i < r.Length; ++i) { mOverlapBuff[i] = x[ProcessSize / 2 + i]; } if (0 <= mNumSamples && mNumSamples < mProcessedSamples + r.Length) { // 必要サンプル数を超えて出力する必要はない。 var tmp = new double[mNumSamples - mProcessedSamples]; Array.Copy(r, 0, tmp, 0, tmp.Length); r = tmp; } ++mProcessCounter; mProcessedSamples += r.Length; return(r); } }