private void m_AnalyticSignalWorker_DoWork(object sender, DoWorkEventArgs e) { AnalyticSignalWorkerArgs args = e.Argument as AnalyticSignalWorkerArgs; Stopwatch sw = new Stopwatch(); sw.Start(); // pcmファイルを読み込んでサンプル配列pcm1chを作成。 PcmData pcmDataIn = null; try { pcmDataIn = ReadWavFile(args.inputPath); } catch (IOException ex) { e.Result = string.Format("WAVファイル {0} 読み込み失敗\r\n{1}", args.inputPath, ex); } if (null == pcmDataIn) { e.Result = string.Format("WAVファイル {0} 読み込み失敗", args.inputPath); } var formatConv = new WasapiPcmUtil.PcmFormatConverter(pcmDataIn.NumChannels); PcmData pcmDataReal = formatConv.Convert(pcmDataIn, Wasapi.WasapiCS.SampleFormatType.Sdouble, null); PcmData pcmDataImaginary = new PcmData(); pcmDataImaginary.CopyFrom(pcmDataReal); var dft = new WWDirectComputeCS.WWDftCpu(); var hilb = WWHilbert.HilbertFirCoeff(args.hilbertFilterType, args.firLength); System.Diagnostics.Debug.Assert(hilb.Length == args.firLength); // 窓関数 double [] window; if (args.windowFunc == WindowFuncType.Blackman) { WWWindowFunc.BlackmanWindow(args.firLength, out window); } else { WWWindowFunc.KaiserWindow(args.firLength, args.kaiserAlpha, out window); } // FIR coeffの個数は、window.Length個。 // ヒルベルト変換パラメータは未来から過去の方向に並んでいるので左右をひっくり返す。 double [] coeff = new double[args.firLength]; for (int i=0; i < coeff.Length; ++i) { int pos = coeff.Length - i - 1; coeff[i] = hilb[pos] * window[i]; } for (int ch=0; ch < pcmDataImaginary.NumChannels; ++ch) { var pcm1ch = new double[pcmDataImaginary.NumFrames]; for (long i=0; i < pcm1ch.Length; ++i) { pcm1ch[i] = pcmDataImaginary.GetSampleValueInDouble(ch, i); } // 少しずつFIRする。 var fir = new WWFirCpu(); fir.Setup(coeff, pcm1ch); const int FIR_SAMPLE = 65536; for (int offs=0; offs < pcm1ch.Length; offs += FIR_SAMPLE) { int nSample = FIR_SAMPLE; if (pcm1ch.Length < offs + nSample) { nSample = pcm1ch.Length - offs; } var pcmFir = new double[nSample]; fir.Do(offs - window.Length / 2, nSample, pcmFir); // 結果を出力に書き込む。 for (long i=0; i < pcmFir.Length; ++i) { var re = pcmFir[i]; pcmDataImaginary.SetSampleValueInDouble(ch, i + offs, (float)(re)); } // 進捗Update。 int percentage = (int)( (100L * ch / pcmDataImaginary.NumChannels) + (100L * (offs + 1) / pcm1ch.Length / pcmDataImaginary.NumChannels)); m_ASWorker.ReportProgress(percentage); } fir.Unsetup(); } // 解析信号を出力。 m_analyticSignalList.Clear(); for (int ch=0; ch < pcmDataReal.NumChannels; ++ch) { double [] signal = new double[pcmDataImaginary.NumFrames * 2]; for (long pos=0; pos < pcmDataReal.NumFrames; ++pos) { signal[pos * 2 + 0] = pcmDataReal.GetSampleValueInDouble(ch, pos); signal[pos * 2 + 1] = pcmDataImaginary.GetSampleValueInDouble(ch, pos); } m_analyticSignalList.Add(signal); } sw.Stop(); e.Result = ""; }
private bool HilbertDo(FirWorkerArgs argsFir, PcmData pcmDataIn, out PcmData pcmDataOutput) { HilbertWorkerArgs args = argsFir as HilbertWorkerArgs; var dft = new WWDirectComputeCS.WWDftCpu(); var hilb = WWHilbert.HilbertFirCoeff(args.hilbertFilterType, args.firLength); System.Diagnostics.Debug.Assert(hilb.Length == args.firLength); // 窓関数 double [] window; if (args.windowFunc == WindowFuncType.Blackman) { WWWindowFunc.BlackmanWindow(args.firLength, out window); } else { WWWindowFunc.KaiserWindow(args.firLength, args.kaiserAlpha, out window); } // FIR coeffの個数は、window.Length個。 // ヒルベルト変換パラメータは未来から過去の方向に並んでいるので左右をひっくり返す。 double [] coeff = new double[args.firLength]; for (int i=0; i<coeff.Length; ++i) { int pos = coeff.Length - i - 1; coeff[i] = hilb[pos] * window[i]; } /* for (int i=0; i < coeff.Length; ++i) { System.Console.WriteLine("coeff {0:D2} {1}", i, coeff[i]); } System.Console.WriteLine(""); */ pcmDataOutput = new PcmData(); pcmDataOutput.CopyFrom(pcmDataIn); for (int ch=0; ch < pcmDataOutput.NumChannels; ++ch) { // 全てのチャンネルでループ。 var pcm1ch = new double[pcmDataOutput.NumFrames]; for (long i=0; i < pcm1ch.Length; ++i) { pcm1ch[i] = pcmDataOutput.GetSampleValueInDouble(ch, i); } // 少しずつFIRする。 var fir = new WWFirCpu(); fir.Setup(coeff, pcm1ch); const int FIR_SAMPLE = 65536; for (int offs=0; offs < pcm1ch.Length; offs += FIR_SAMPLE) { int nSample = FIR_SAMPLE; if (pcm1ch.Length < offs + nSample) { nSample = pcm1ch.Length - offs; } var pcmFir = new double[nSample]; fir.Do(offs - window.Length / 2, nSample, pcmFir); // 結果を出力に書き込む。 for (long i=0; i < pcmFir.Length; ++i) { var re = pcmFir[i]; pcmDataOutput.SetSampleValueInDouble(ch, i + offs, re); } // 進捗Update。 int percentage = (int)( ( 100L * ch / pcmDataOutput.NumChannels ) + ( 100L * ( offs + 1 ) / pcm1ch.Length / pcmDataOutput.NumChannels ) ); m_HilbWorker.ReportProgress(percentage); } fir.Unsetup(); } return true; }
// この関数は音量制限を行わない。呼び出し側で必要に応じて音量を制限する。 private bool PhaseRotationDo(FirWorkerArgs argsFWA, PcmData pcmDataIn, out PcmData pcmDataOutput) { PhaseRotationWorkerArgs args = argsFWA as PhaseRotationWorkerArgs; var conv = new WasapiPcmUtil.PcmFormatConverter(pcmDataIn.NumChannels); PcmData pcmDataReal = conv.Convert(pcmDataIn, Wasapi.WasapiCS.SampleFormatType.Sdouble, null); pcmDataOutput = new PcmData(); pcmDataOutput.CopyFrom(pcmDataIn); PcmData pcmDataImaginary = new PcmData(); pcmDataImaginary.CopyFrom(pcmDataReal); var dft = new WWDirectComputeCS.WWDftCpu(); var hilb = WWHilbert.HilbertFirCoeff(args.hilbertFilterType, args.firLength); System.Diagnostics.Debug.Assert(hilb.Length == args.firLength); // 窓関数 double [] window; if (args.windowFunc == WindowFuncType.Blackman) { WWWindowFunc.BlackmanWindow(args.firLength, out window); } else { WWWindowFunc.KaiserWindow(args.firLength, args.kaiserAlpha, out window); } // FIR coeffの個数は、window.Length個。 // ヒルベルト変換パラメータは未来から過去の方向に並んでいるので左右をひっくり返す。 double [] coeff = new double[args.firLength]; for (int i=0; i < coeff.Length; ++i) { int pos = coeff.Length - i - 1; coeff[i] = hilb[pos] * window[i]; } for (int ch=0; ch < pcmDataImaginary.NumChannels; ++ch) { var pcm1ch = new double[pcmDataImaginary.NumFrames]; for (long i=0; i < pcm1ch.Length; ++i) { pcm1ch[i] = pcmDataImaginary.GetSampleValueInDouble(ch, i); } // 少しずつFIRする。 var fir = new WWFirCpu(); fir.Setup(coeff, pcm1ch); const int FIR_SAMPLE = 65536; for (int offs=0; offs < pcm1ch.Length; offs += FIR_SAMPLE) { int nSample = FIR_SAMPLE; if (pcm1ch.Length < offs + nSample) { nSample = pcm1ch.Length - offs; } var pcmFir = new double[nSample]; fir.Do(offs - window.Length / 2, nSample, pcmFir); // 結果を出力に書き込む。 for (long i=0; i < pcmFir.Length; ++i) { var re = pcmFir[i]; pcmDataImaginary.SetSampleValueInDouble(ch, i + offs, (float)(re)); } // 進捗Update。 int percentage = (int)( (100L * ch / pcmDataImaginary.NumChannels) + (100L * (offs + 1) / pcm1ch.Length / pcmDataImaginary.NumChannels)); mPRWorker.ReportProgress(percentage); } fir.Unsetup(); } // 音の位相を回転。 for (int ch=0; ch < pcmDataReal.NumChannels; ++ch) { for (long pos=0; pos < pcmDataReal.NumFrames; ++pos) { // 解析信号の各サンプル値を極座標表現に変換。オリジナルの長さと位相を得る。 // 長さをそのままに位相を回転し、回転後の実数成分を出力する。 double x = pcmDataReal.GetSampleValueInDouble(ch, pos); double y = pcmDataImaginary.GetSampleValueInDouble(ch, pos); double norm = Math.Sqrt(x * x + y * y); double theta = Math.Atan2(y, x); double re = norm * Math.Cos(theta + args.phaseRadian); pcmDataOutput.SetSampleValueInDouble(ch, pos, re); } } return true; }
private bool FirDo(FirWorkerArgs args, PcmData pcmDataIn, out PcmData pcmDataOut) { var dft = new WWDirectComputeCS.WWDftCpu(); var from = FreqGraphToIdftInput(pcmDataIn.SampleRate); double [] idftResult; dft.Idft1d(from, out idftResult); // 窓関数の要素数は、IDFT結果の複素数の個数 -1個。 double [] window; if (args.windowFunc == WindowFuncType.Blackman) { WWWindowFunc.BlackmanWindow((idftResult.Length / 2) - 1, out window); } else { WWWindowFunc.KaiserWindow((idftResult.Length / 2) - 1, args.kaiserAlpha, out window); } // FIR coeffの個数は、window.Length個。 double [] coeff; dft.IdftComplexToFirCoeff1d(idftResult, window, out coeff); /* for (int i=0; i < coeff.Length; ++i) { System.Console.WriteLine("coeff {0:D2} {1}", i, coeff[i]); } System.Console.WriteLine(""); */ pcmDataOut = new PcmData(); pcmDataOut.CopyFrom(pcmDataIn); for (int ch=0; ch < pcmDataOut.NumChannels; ++ch) { // 全てのチャンネルでループ。 var pcm1ch = new double[pcmDataOut.NumFrames]; for (long i=0; i < pcm1ch.Length; ++i) { pcm1ch[i] = pcmDataOut.GetSampleValueInDouble(ch, i); } // 少しずつFIRする。 var fir = new WWFirCpu(); fir.Setup(coeff, pcm1ch); const int FIR_SAMPLE = 65536; for (int offs=0; offs < pcm1ch.Length; offs += FIR_SAMPLE) { int nSample = FIR_SAMPLE; if (pcm1ch.Length < offs + nSample) { nSample = pcm1ch.Length - offs; } var pcmFir = new double[nSample]; fir.Do(offs - window.Length / 2, nSample, pcmFir); // 結果を出力に書き込む。 for (long i=0; i < pcmFir.Length; ++i) { var re = pcmFir[i]; pcmDataOut.SetSampleValueInDouble(ch, i + offs, re); } // 進捗Update。 int percentage = (int)( ( 100L * ch / pcmDataOut.NumChannels ) + ( 100L * ( offs + 1 ) / pcm1ch.Length / pcmDataOut.NumChannels ) ); m_FirWorker.ReportProgress(percentage); } fir.Unsetup(); } return true; }