private void buttonItUpsampleStart_Click(object sender, RoutedEventArgs e) { var args = new FirWorkerArgs(); args.inputPath = textBoxItUpsampleInputPath.Text; args.outputPath = textBoxItUpsampleOutputPath.Text; if (true == radioButtonItUpsampleSint16.IsChecked) { args.outputBitsPerSample = 16; args.valueRepresentationType = PcmData.ValueRepresentationType.SInt; } if (true == radioButtonItUpsampleSint24.IsChecked) { args.outputBitsPerSample = 24; args.valueRepresentationType = PcmData.ValueRepresentationType.SInt; } if (true == radioButtonItUpsampleSfloat32.IsChecked) { args.outputBitsPerSample = 32; args.valueRepresentationType = PcmData.ValueRepresentationType.SFloat; } mFreqMagnitude = 0; if (!Int32.TryParse(textBoxItUpsampleFreqMagnitude.Text, out mFreqMagnitude) || mFreqMagnitude <= 1) { MessageBox.Show("アップサンプル倍率は2以上の整数を入力して下さい"); return; } mItUpsampleType = ItUpsampleType.Unknown; if (true == radioButtonItUpsampleImpulse.IsChecked) { mItUpsampleType = ItUpsampleType.ImpulseTrain; } if (true == radioButtonItUpsampleSampleHold.IsChecked) { mItUpsampleType = ItUpsampleType.SampleHold; } if (true == radioButtonItUpsampleLinear.IsChecked) { mItUpsampleType = ItUpsampleType.Linear; } if (true == radioButtonItUpsampleCubic.IsChecked) { mItUpsampleType = ItUpsampleType.Cubic; } System.Diagnostics.Debug.Assert(mItUpsampleType != ItUpsampleType.Unknown); textBoxItUpsampleLog.Text += string.Format("開始。{0} ==> {1} {2}x\r\n", args.inputPath, args.outputPath, mFreqMagnitude); textBoxItUpsampleLog.ScrollToEnd(); buttonItUpsampleDo.IsEnabled = false; mItUpsampleWorker.RunWorkerAsync(args); }
/// <summary> /// FIR実行 /// </summary> private void buttonFirDo_Click(object sender, RoutedEventArgs e) { int filterLength; if (!Int32.TryParse(textBoxFirLength.Text, out filterLength) || (filterLength & 1) == 0 || filterLength <= 0) { MessageBox.Show("FIRフィルタ長は正の奇数の数値を半角数字で入力してください。処理中断。"); return; } m_firTapN = filterLength + 1; var args = new FirWorkerArgs(); args.inputPath = textBoxFirInputPath.Text; args.outputPath = textBoxFirOutputPath.Text; if (radioButtonFir16bit.IsChecked == true) { args.outputBitsPerSample = 16; args.valueRepresentationType = PcmData.ValueRepresentationType.SInt; } if (radioButtonFir24bit.IsChecked == true) { args.outputBitsPerSample = 24; args.valueRepresentationType = PcmData.ValueRepresentationType.SInt; } if (radioButtonFir32bitFloat.IsChecked == true) { args.outputBitsPerSample = 32; args.valueRepresentationType = PcmData.ValueRepresentationType.SFloat; } if (!Double.TryParse(textBoxKaiserAlpha.Text, out args.kaiserAlpha) || args.kaiserAlpha <= 4.0 || 9.0 <= args.kaiserAlpha) { MessageBox.Show("カイザー窓α値は4.0<α<9.0の範囲の数値を半角数字で入力してください。処理中断。"); return; } if (radioButtonFirBlackman.IsChecked == true) { args.windowFunc = WindowFuncType.Blackman; } else { args.windowFunc = WindowFuncType.Kaiser; } AddFirLog(string.Format("開始。{0} → {1}\r\n", args.inputPath, args.outputPath)); buttonFirDo.IsEnabled = false; groupBoxFirInputFile.IsEnabled = false; groupBoxFirEq.IsEnabled = false; groupBoxFirOutputFile.IsEnabled = false; groupBoxFirWindow.IsEnabled = false; m_FirWorker.RunWorkerAsync(args); }
void m_HilbWorker_DoWork(object sender, DoWorkEventArgs e) { FirWorkerArgs args = (FirWorkerArgs)e.Argument; Stopwatch sw = new Stopwatch(); sw.Start(); string result; if (!FirDoLoadConvertSave(args, HilbertDo, out result)) { e.Result = result; return; } sw.Stop(); e.Result = string.Format("{0}\r\n所要時間 {1}秒", result, sw.ElapsedMilliseconds / 1000); }
// この関数は音量制限を行わない。呼び出し側で必要に応じて音量を制限する。 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 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 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 ItUpsampleDo(FirWorkerArgs args, PcmData pcmDataIn, out PcmData pcmDataOut) { pcmDataOut = new PcmData(); pcmDataOut.SetFormat(pcmDataIn.NumChannels, 64, 64, pcmDataIn.SampleRate * mFreqMagnitude, PcmData.ValueRepresentationType.SFloat, pcmDataIn.NumFrames * mFreqMagnitude); pcmDataOut.SetSampleArray(new byte[pcmDataOut.NumFrames * pcmDataOut.BitsPerFrame / 8]); var pcm = pcmDataOut; switch (mItUpsampleType) { case ItUpsampleType.ImpulseTrain: Parallel.For(0, pcmDataIn.NumFrames, (pos) => { for (int ch=0; ch < pcmDataIn.NumChannels; ++ch) { var v = pcmDataIn.GetSampleValueInDouble(ch, pos); pcm.SetSampleValueInDouble(ch, pos * mFreqMagnitude, v); } }); break; case ItUpsampleType.SampleHold: Parallel.For(0, pcmDataIn.NumFrames, (pos) => { for (int ch=0; ch < pcmDataIn.NumChannels; ++ch) { var v = pcmDataIn.GetSampleValueInDouble(ch, pos); for (int i=0; i < mFreqMagnitude; ++i) { pcm.SetSampleValueInDouble(ch, pos * mFreqMagnitude+i, v); } } }); break; case ItUpsampleType.Linear: Parallel.For(0, pcmDataIn.NumFrames - 1, (pos) => { // 0 <= pos <= NumFrames-2まで実行する for (int ch=0; ch < pcmDataIn.NumChannels; ++ch) { var v0 = pcmDataIn.GetSampleValueInDouble(ch, pos); var v1 = pcmDataIn.GetSampleValueInDouble(ch, pos + 1); for (int i=0; i < mFreqMagnitude; ++i) { var ratio = (double)i / mFreqMagnitude; var v = v0 * (1 - ratio) + v1 * ratio; pcm.SetSampleValueInDouble(ch, pos * mFreqMagnitude + i, v); } } }); // 最後の1区間は0に向かう for (int ch=0; ch < pcmDataIn.NumChannels; ++ch) { var pos = pcmDataIn.NumFrames-1; var v = pcmDataIn.GetSampleValueInDouble(ch, pos); for (int i=0; i < mFreqMagnitude; ++i) { pcm.SetSampleValueInDouble(ch, pos * mFreqMagnitude + i, v * (mFreqMagnitude - i-1) / mFreqMagnitude); } } break; case ItUpsampleType.Cubic: { var h = new double[4 * mFreqMagnitude]; double a = -0.5; for (int i=0; i < mFreqMagnitude; ++i) { double t = (double)i/mFreqMagnitude; h[mFreqMagnitude * 2 + i] = (a + 2) * t * t * t - (a + 3) * t * t + 1; h[mFreqMagnitude * 2 - i] = h[mFreqMagnitude * 2 +i]; } for (int i=mFreqMagnitude; i < mFreqMagnitude * 2; ++i) { double t = (double)i / mFreqMagnitude; h[mFreqMagnitude * 2 + i] = a * t * t * t - 5 * a * t * t + 8 * a * t - 4 * a; h[mFreqMagnitude * 2 - i] = h[mFreqMagnitude * 2 + i]; } Parallel.For(0, pcmDataOut.NumFrames, (pos) => { for (int ch=0; ch < pcmDataIn.NumChannels; ++ch) { var x = new double[4 * mFreqMagnitude]; for (int i=0; i < 4 * mFreqMagnitude; ++i) { if (0 == (pos + i) % mFreqMagnitude) { x[i] = pcmDataIn.GetSampleValueInDouble(ch, (pos+i)/mFreqMagnitude); } } double v = 0; for (int i=0; i < 4* mFreqMagnitude; ++i) { v += h[i] * x[i]; } pcm.SetSampleValueInDouble(ch, pos, v); } }); } break; default: System.Diagnostics.Debug.Assert(false); break; } return true; }
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 FirDoLoadConvertSave(FirWorkerArgs args, FirDelegate Do, out string result) { // pcmファイルを読み込んでサンプル配列pcm1chを作成。 PcmData pcmDataIn = null; try { pcmDataIn = ReadWavFile(args.inputPath); } catch (IOException ex) { result = string.Format("WAVファイル {0} 読み込み失敗\r\n{1}", args.inputPath, ex); return(false); } if (null == pcmDataIn) { result = string.Format("WAVファイル {0} 読み込み失敗", args.inputPath); return(false); } var formatConv = new WasapiPcmUtil.PcmFormatConverter(pcmDataIn.NumChannels); pcmDataIn = formatConv.Convert(pcmDataIn, Wasapi.WasapiCS.SampleFormatType.Sdouble, null); PcmData pcmDataOutput; if (!Do(args, pcmDataIn, out pcmDataOutput)) { result = "FIR処理失敗"; return(false); } // サンプルフォーマットが整数型の時だけ音量制限処理する。 double scale = 1.0; double maxLevel = 0.0f; if (args.valueRepresentationType == PcmData.ValueRepresentationType.SInt) { scale = pcmDataOutput.LimitLevelOnDoubleRange(); } else { double maxV, minV; pcmDataOutput.FindMaxMinValueOnDoubleBuffer(out maxV, out minV); maxLevel = Math.Max(Math.Abs(maxV), Math.Abs(minV)); } PcmData pcmDataWrite = formatConv.Convert(pcmDataOutput, Wasapi.WasapiCS.BitAndFormatToSampleFormatType(args.outputBitsPerSample, args.outputBitsPerSample, (Wasapi.WasapiCS.BitFormatType)args.valueRepresentationType), null); bool writeResult = false; try { writeResult = WriteWavFile(pcmDataWrite, args.outputPath); } catch (IOException ex) { result = string.Format("WAVファイル書き込み失敗: {0}\r\n{1}", args.outputPath, ex); return(false); } if (!writeResult) { result = string.Format("WAVファイル書き込み失敗: {0}", args.outputPath); return(false); } if (args.valueRepresentationType == PcmData.ValueRepresentationType.SInt) { if (scale == 1.0) { result = string.Format("WAVファイル書き込み成功: {0}", args.outputPath); } else { result = string.Format("WAVファイル書き込み成功: {0}\r\nレベルオーバーのため音量を{1:0.######}倍しました({2:0.######}dB)", args.outputPath, scale, 20.0 * Math.Log10(scale)); } } else { result = string.Format("WAVファイル書き込み成功: {0}\r\nサンプル値の絶対値の最大値 {1:0.######}", args.outputPath, maxLevel); } 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); }
private bool ItUpsampleDo(FirWorkerArgs args, PcmData pcmDataIn, out PcmData pcmDataOut) { pcmDataOut = new PcmData(); pcmDataOut.SetFormat(pcmDataIn.NumChannels, 64, 64, pcmDataIn.SampleRate * mFreqMagnitude, PcmData.ValueRepresentationType.SFloat, pcmDataIn.NumFrames * mFreqMagnitude); pcmDataOut.SetSampleArray(new byte[pcmDataOut.NumFrames * pcmDataOut.BitsPerFrame / 8]); var pcm = pcmDataOut; switch (mItUpsampleType) { case ItUpsampleType.ImpulseTrain: Parallel.For(0, pcmDataIn.NumFrames, (pos) => { for (int ch = 0; ch < pcmDataIn.NumChannels; ++ch) { var v = pcmDataIn.GetSampleValueInDouble(ch, pos); pcm.SetSampleValueInDouble(ch, pos * mFreqMagnitude, v); } }); break; case ItUpsampleType.SampleHold: Parallel.For(0, pcmDataIn.NumFrames, (pos) => { for (int ch = 0; ch < pcmDataIn.NumChannels; ++ch) { var v = pcmDataIn.GetSampleValueInDouble(ch, pos); for (int i = 0; i < mFreqMagnitude; ++i) { pcm.SetSampleValueInDouble(ch, pos * mFreqMagnitude + i, v); } } }); break; case ItUpsampleType.Linear: Parallel.For(0, pcmDataIn.NumFrames - 1, (pos) => { // 0 <= pos <= NumFrames-2まで実行する for (int ch = 0; ch < pcmDataIn.NumChannels; ++ch) { var v0 = pcmDataIn.GetSampleValueInDouble(ch, pos); var v1 = pcmDataIn.GetSampleValueInDouble(ch, pos + 1); for (int i = 0; i < mFreqMagnitude; ++i) { var ratio = (double)i / mFreqMagnitude; var v = v0 * (1 - ratio) + v1 * ratio; pcm.SetSampleValueInDouble(ch, pos * mFreqMagnitude + i, v); } } }); // 最後の1区間は0に向かう for (int ch = 0; ch < pcmDataIn.NumChannels; ++ch) { var pos = pcmDataIn.NumFrames - 1; var v = pcmDataIn.GetSampleValueInDouble(ch, pos); for (int i = 0; i < mFreqMagnitude; ++i) { pcm.SetSampleValueInDouble(ch, pos * mFreqMagnitude + i, v * (mFreqMagnitude - i - 1) / mFreqMagnitude); } } break; case ItUpsampleType.Cubic: { var h = new double[4 * mFreqMagnitude]; double a = -0.5; for (int i = 0; i < mFreqMagnitude; ++i) { double t = (double)i / mFreqMagnitude; h[mFreqMagnitude * 2 + i] = (a + 2) * t * t * t - (a + 3) * t * t + 1; h[mFreqMagnitude * 2 - i] = h[mFreqMagnitude * 2 + i]; } for (int i = mFreqMagnitude; i < mFreqMagnitude * 2; ++i) { double t = (double)i / mFreqMagnitude; h[mFreqMagnitude * 2 + i] = a * t * t * t - 5 * a * t * t + 8 * a * t - 4 * a; h[mFreqMagnitude * 2 - i] = h[mFreqMagnitude * 2 + i]; } Parallel.For(0, pcmDataOut.NumFrames, (pos) => { for (int ch = 0; ch < pcmDataIn.NumChannels; ++ch) { var x = new double[4 * mFreqMagnitude]; for (int i = 0; i < 4 * mFreqMagnitude; ++i) { if (0 == (pos + i) % mFreqMagnitude) { x[i] = pcmDataIn.GetSampleValueInDouble(ch, (pos + i) / mFreqMagnitude); } } double v = 0; for (int i = 0; i < 4 * mFreqMagnitude; ++i) { v += h[i] * x[i]; } pcm.SetSampleValueInDouble(ch, pos, v); } }); } break; default: System.Diagnostics.Debug.Assert(false); break; } return(true); }
private bool FirDoLoadConvertSave(FirWorkerArgs args, FirDelegate Do, out string result) { // pcmファイルを読み込んでサンプル配列pcm1chを作成。 PcmData pcmDataIn = null; try { pcmDataIn = ReadWavFile(args.inputPath); } catch (IOException ex) { result = string.Format("WAVファイル {0} 読み込み失敗\r\n{1}", args.inputPath, ex); return false; } if (null == pcmDataIn) { result = string.Format("WAVファイル {0} 読み込み失敗", args.inputPath); return false; } var formatConv = new WasapiPcmUtil.PcmFormatConverter(pcmDataIn.NumChannels); pcmDataIn = formatConv.Convert(pcmDataIn, Wasapi.WasapiCS.SampleFormatType.Sdouble, null); PcmData pcmDataOutput; if (!Do(args, pcmDataIn, out pcmDataOutput)) { result = "FIR処理失敗"; return false; } // サンプルフォーマットが整数型の時だけ音量制限処理する。 double scale = 1.0; double maxLevel = 0.0f; if (args.valueRepresentationType == PcmData.ValueRepresentationType.SInt) { scale = pcmDataOutput.LimitLevelOnDoubleRange(); } else { double maxV, minV; pcmDataOutput.FindMaxMinValueOnDoubleBuffer(out maxV, out minV); maxLevel = Math.Max(Math.Abs(maxV), Math.Abs(minV)); } PcmData pcmDataWrite = formatConv.Convert(pcmDataOutput, Wasapi.WasapiCS.BitAndFormatToSampleFormatType(args.outputBitsPerSample, args.outputBitsPerSample, (Wasapi.WasapiCS.BitFormatType)args.valueRepresentationType), null); bool writeResult = false; try { writeResult = WriteWavFile(pcmDataWrite, args.outputPath); } catch (IOException ex) { result = string.Format("WAVファイル書き込み失敗: {0}\r\n{1}", args.outputPath, ex); return false; } if (!writeResult) { result = string.Format("WAVファイル書き込み失敗: {0}", args.outputPath); return false; } if (args.valueRepresentationType == PcmData.ValueRepresentationType.SInt) { if (scale == 1.0) { result = string.Format("WAVファイル書き込み成功: {0}", args.outputPath); } else { result = string.Format("WAVファイル書き込み成功: {0}\r\nレベルオーバーのため音量を{1:0.######}倍しました({2:0.######}dB)", args.outputPath, scale, 20.0 * Math.Log10(scale)); } } else { result = string.Format("WAVファイル書き込み成功: {0}\r\nサンプル値の絶対値の最大値 {1:0.######}", args.outputPath, maxLevel); } 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; }