/// <summary> /// 排他モード用。ビットデプスとチャンネル数を変更する。 /// </summary> private byte[] PcmDepthChannelConvert(byte[] from, int fromOffs, int fromBytes, DeviceFormat fromFormat, DeviceFormat toFormat) { System.Diagnostics.Debug.Assert(fromFormat.SampleRate == toFormat.SampleRate); int numFrames = fromBytes / (fromFormat.NumChannels * WasapiCS.SampleFormatTypeToUseBitsPerSample(fromFormat.SampleFormat) / 8); var pcmFrom = new PcmDataLib.PcmData(); pcmFrom.SetFormat(fromFormat.NumChannels, WasapiCS.SampleFormatTypeToUseBitsPerSample(fromFormat.SampleFormat), WasapiCS.SampleFormatTypeToValidBitsPerSample(fromFormat.SampleFormat), fromFormat.SampleRate, PcmDataLib.PcmData.ValueRepresentationType.SInt, numFrames); pcmFrom.SetSampleLargeArray(new WWUtil.LargeArray <byte>(from, fromOffs, fromBytes)); // ビットデプスを変更する。 var conv = new WasapiPcmUtil.PcmFormatConverter(fromFormat.NumChannels); var pcmTo = conv.Convert(pcmFrom, toFormat.SampleFormat, new WasapiPcmUtil.PcmFormatConverter.BitsPerSampleConvArgs(WasapiPcmUtil.NoiseShapingType.None)); // チャンネル数を変更する。 var toBytes = WasapiPcmUtil.PcmFormatConverter.ChangeChannelCount( fromFormat.SampleFormat, WasapiCS.StreamType.PCM, fromFormat.NumChannels, pcmTo.GetSampleLargeArray().ToArray(), toFormat.NumChannels); return(toBytes); }
// この関数は音量制限を行わない。呼び出し側で必要に応じて音量を制限する。 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 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 void m_USAQworker_DoWork(object sender, DoWorkEventArgs e) { // System.Threading.Thread.CurrentThread.Priority = System.Threading.ThreadPriority.Lowest; USWorkerArgs args = (USWorkerArgs)e.Argument; PcmData pcmDataIn = ReadWavFile(args.inputPath); if (null == pcmDataIn) { e.Result = string.Format("WAVファイル 読み込み失敗: {0}", args.inputPath); return; } // ファイル読み込み完了。 if (args.addJitter) { // ジッター負荷の場合、サンプリング周波数は変更しない。 args.resampleFrequency = pcmDataIn.SampleRate; } if (args.resampleFrequency < pcmDataIn.SampleRate) { e.Result = string.Format("エラー: ダウンサンプルは対応していません {0} from={1} to={2}", args.inputPath, pcmDataIn.SampleRate, args.resampleFrequency); return; } if (0x7fff0000L < pcmDataIn.NumFrames * 4 * pcmDataIn.NumChannels * args.resampleFrequency / pcmDataIn.SampleRate) { e.Result = string.Format("エラー: リサンプル後のファイルサイズが2GBを超えそうなので中断しました {0}", args.inputPath); return; } m_USAQworker.ReportProgress(1); var conv = new WasapiPcmUtil.PcmFormatConverter(pcmDataIn.NumChannels); pcmDataIn = conv.Convert(pcmDataIn, WasapiCS.BitAndFormatToSampleFormatType(32, 32, WasapiCS.BitFormatType.SFloat), null); PcmData pcmDataOut = new PcmData(); pcmDataOut.CopyFrom(pcmDataIn); int sampleTotalTo = (int)(args.resampleFrequency * pcmDataIn.NumFrames / pcmDataIn.SampleRate); { // PcmDataOutのサンプルレートとサンプル数を更新する。 byte[] outSampleArray = new byte[(long)sampleTotalTo * pcmDataOut.NumChannels * 4]; pcmDataOut.SetSampleArray(sampleTotalTo, outSampleArray); pcmDataOut.SampleRate = args.resampleFrequency; outSampleArray = null; } // 再サンプルテーブル作成 args.resamplePosArray = null; args.fractionArray = null; if (args.addJitter) { // ジッター付加の場合、サンプルレートは変更しない。 args.resamplePosArray = new int[pcmDataIn.NumFrames]; args.fractionArray = new double[pcmDataIn.NumFrames]; /* sampleRate == 96000 Hz jitterFrequency == 50 Hz jitterPicoseconds == 1 ps の場合 サンプル位置posのθ= 2 * PI * pos * 50 / 96000 (ラジアン) サンプル間隔= 1/96000秒 = 10.4 μs 1ms = 10^-3秒 1μs= 10^-6秒 1ns = 10^-9秒 1ps = 10^-12秒 1psのずれ x サンプルのずれ ───────────── = ───────── 10.4 μs(1/96000)sのずれ 1 サンプルのずれ 1psのサンプルずれA = 10^-12 ÷ (1/96000) (サンプルのずれ) サンプルを採取する位置= pos + Asin(θ) */ args.thetaCoefficientSeqJitter = 2.0 * Math.PI * args.sequentialJitterFrequency / pcmDataIn.SampleRate; args.ampSeqJitter = 1.0e-12 * pcmDataIn.SampleRate * args.sequentialJitterPicoseconds; args.ampTpdfJitter = 1.0e-12 * pcmDataIn.SampleRate * args.tpdfJitterPicoseconds; args.ampRpdfJitter = 1.0e-12 * pcmDataIn.SampleRate * args.rpdfJitterPicoseconds; PrepareResamplePosArray( args, pcmDataIn.SampleRate, pcmDataOut.SampleRate, (int)pcmDataIn.NumFrames, sampleTotalTo, args.resamplePosArray, args.fractionArray); } System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); sw.Start(); int hr = 0; if (args.device == ProcessDevice.Gpu) { hr = GpuUpsample(args, pcmDataIn, pcmDataOut); } else { hr = CpuUpsample(args, pcmDataIn, pcmDataOut); } // args.resamplePosArrayは中でコピーされるのでここで不要になる。 args.resamplePosArray = null; args.fractionArray = null; if (m_USAQworker.CancellationPending) { e.Result = string.Format("キャンセル完了。"); e.Cancel = true; return; } if (hr < 0) { e.Result = string.Format("Upsample エラー 0x{0:X8}", hr); return; } sw.Stop(); // 成功した。レベル制限する。 float scale = pcmDataOut.LimitLevelOnFloatRange(); if (args.outputVRT != PcmData.ValueRepresentationType.SFloat) { // ビットフォーマット変更。 var formatConv = new WasapiPcmUtil.PcmFormatConverter(pcmDataOut.NumChannels); pcmDataOut = formatConv.Convert(pcmDataOut, WasapiCS.BitAndFormatToSampleFormatType(args.outputBitsPerSample, args.outputBitsPerSample, (WasapiCS.BitFormatType)args.outputVRT), null); } try { WriteWavFile(pcmDataOut, args.outputPath); } catch (IOException ex) { // 書き込みエラー。 e.Result = ex.ToString(); return; } e.Result = string.Format("書き込み成功。処理時間 {0}秒\r\n", sw.ElapsedMilliseconds * 0.001); if (scale < 1.0f) { e.Result = string.Format("書き込み成功。処理時間 {0}秒。" + "レベルオーバーのため音量調整{1}dB({2}倍)しました。\r\n", sw.ElapsedMilliseconds * 0.001, 20.0 * Math.Log10(scale), scale); } m_USAQworker.ReportProgress(100); }
// この関数は音量制限を行わない。呼び出し側で必要に応じて音量を制限する。 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 void m_USAQworker_DoWork(object sender, DoWorkEventArgs e) { // System.Threading.Thread.CurrentThread.Priority = System.Threading.ThreadPriority.Lowest; USWorkerArgs args = (USWorkerArgs)e.Argument; PcmData pcmDataIn = ReadWavFile(args.inputPath); if (null == pcmDataIn) { e.Result = string.Format("WAVファイル 読み込み失敗: {0}", args.inputPath); return; } // ファイル読み込み完了。 if (args.addJitter) { // ジッター負荷の場合、サンプリング周波数は変更しない。 args.resampleFrequency = pcmDataIn.SampleRate; } if (args.resampleFrequency < pcmDataIn.SampleRate) { e.Result = string.Format("エラー: ダウンサンプルは対応していません {0} from={1} to={2}", args.inputPath, pcmDataIn.SampleRate, args.resampleFrequency); return; } if (0x7fff0000L < pcmDataIn.NumFrames * 4 * pcmDataIn.NumChannels * args.resampleFrequency / pcmDataIn.SampleRate) { e.Result = string.Format("エラー: リサンプル後のファイルサイズが2GBを超えそうなので中断しました {0}", args.inputPath); return; } m_USAQworker.ReportProgress(1); var conv = new WasapiPcmUtil.PcmFormatConverter(pcmDataIn.NumChannels); pcmDataIn = conv.Convert(pcmDataIn, WasapiCS.BitAndFormatToSampleFormatType(32, 32, WasapiCS.BitFormatType.SFloat), null); PcmData pcmDataOut = new PcmData(); pcmDataOut.CopyFrom(pcmDataIn); int sampleTotalTo = (int)(args.resampleFrequency * pcmDataIn.NumFrames / pcmDataIn.SampleRate); { // PcmDataOutのサンプルレートとサンプル数を更新する。 byte[] outSampleArray = new byte[(long)sampleTotalTo * pcmDataOut.NumChannels * 4]; pcmDataOut.SetSampleArray(sampleTotalTo, outSampleArray); pcmDataOut.SampleRate = args.resampleFrequency; outSampleArray = null; } // 再サンプルテーブル作成 args.resamplePosArray = null; args.fractionArray = null; if (args.addJitter) { // ジッター付加の場合、サンプルレートは変更しない。 args.resamplePosArray = new int[pcmDataIn.NumFrames]; args.fractionArray = new double[pcmDataIn.NumFrames]; /* * sampleRate == 96000 Hz * jitterFrequency == 50 Hz * jitterPicoseconds == 1 ps の場合 * * サンプル位置posのθ= 2 * PI * pos * 50 / 96000 (ラジアン) * * サンプル間隔= 1/96000秒 = 10.4 μs * * 1ms = 10^-3秒 * 1μs= 10^-6秒 * 1ns = 10^-9秒 * 1ps = 10^-12秒 * * 1psのずれ x サンプルのずれ * ───────────── = ───────── * 10.4 μs(1/96000)sのずれ 1 サンプルのずれ * * 1psのサンプルずれA = 10^-12 ÷ (1/96000) (サンプルのずれ) * * サンプルを採取する位置= pos + Asin(θ) * */ args.thetaCoefficientSeqJitter = 2.0 * Math.PI * args.sequentialJitterFrequency / pcmDataIn.SampleRate; args.ampSeqJitter = 1.0e-12 * pcmDataIn.SampleRate * args.sequentialJitterPicoseconds; args.ampTpdfJitter = 1.0e-12 * pcmDataIn.SampleRate * args.tpdfJitterPicoseconds; args.ampRpdfJitter = 1.0e-12 * pcmDataIn.SampleRate * args.rpdfJitterPicoseconds; PrepareResamplePosArray( args, pcmDataIn.SampleRate, pcmDataOut.SampleRate, (int)pcmDataIn.NumFrames, sampleTotalTo, args.resamplePosArray, args.fractionArray); } System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); sw.Start(); int hr = 0; if (args.device == ProcessDevice.Gpu) { hr = GpuUpsample(args, pcmDataIn, pcmDataOut); } else { hr = CpuUpsample(args, pcmDataIn, pcmDataOut); } // args.resamplePosArrayは中でコピーされるのでここで不要になる。 args.resamplePosArray = null; args.fractionArray = null; if (m_USAQworker.CancellationPending) { e.Result = string.Format("キャンセル完了。"); e.Cancel = true; return; } if (hr < 0) { e.Result = string.Format("Upsample エラー 0x{0:X8}", hr); return; } sw.Stop(); // 成功した。レベル制限する。 float scale = pcmDataOut.LimitLevelOnFloatRange(); if (args.outputVRT != PcmData.ValueRepresentationType.SFloat) { // ビットフォーマット変更。 var formatConv = new WasapiPcmUtil.PcmFormatConverter(pcmDataOut.NumChannels); pcmDataOut = formatConv.Convert(pcmDataOut, WasapiCS.BitAndFormatToSampleFormatType(args.outputBitsPerSample, args.outputBitsPerSample, (WasapiCS.BitFormatType)args.outputVRT), null); } try { WriteWavFile(pcmDataOut, args.outputPath); } catch (IOException ex) { // 書き込みエラー。 e.Result = ex.ToString(); return; } e.Result = string.Format("書き込み成功。処理時間 {0}秒\r\n", sw.ElapsedMilliseconds * 0.001); if (scale < 1.0f) { e.Result = string.Format("書き込み成功。処理時間 {0}秒。" + "レベルオーバーのため音量調整{1}dB({2}倍)しました。\r\n", sw.ElapsedMilliseconds * 0.001, 20.0 * Math.Log10(scale), scale); } m_USAQworker.ReportProgress(100); }
private void PreparePcmData() { // mPcmSync : 長さ2秒、同期をするために頭に1回小さいクリック音を入れる。これを連続再生する。 // mPcmReady : 長さ2秒、頭に小さいクリック音がある。このPCMの直後にテストデータを再生する。 const int SYNC_PCM_SECONDS = 1; { mPcmSync = new PcmDataLib.PcmData(); mPcmSync.SetFormat(NUM_CHANNELS, WasapiCS.SampleFormatTypeToUseBitsPerSample(mPlaySampleFormat), WasapiCS.SampleFormatTypeToValidBitsPerSample(mPlaySampleFormat), mSampleRate, PcmDataLib.PcmData.ValueRepresentationType.SInt, SYNC_PCM_SECONDS * mSampleRate); var syncData = new LargeArray <byte>((WasapiCS.SampleFormatTypeToUseBitsPerSample(mPlaySampleFormat) / 8) * NUM_CHANNELS * mPcmSync.NumFrames); mPcmSync.SetSampleLargeArray(syncData); } { mPcmReady = new PcmDataLib.PcmData(); mPcmReady.CopyFrom(mPcmSync); var readyData = new LargeArray <byte>((WasapiCS.SampleFormatTypeToUseBitsPerSample(mPlaySampleFormat) / 8) * NUM_CHANNELS * mPcmSync.NumFrames); mPcmReady.SetSampleLargeArray(readyData); } switch (mPlaySampleFormat) { case WasapiCS.SampleFormatType.Sint16: mPcmSync.SetSampleValueInInt32(0, 0, 0x00040000); mPcmReady.SetSampleValueInInt32(0, 0, 0x00030000); break; case WasapiCS.SampleFormatType.Sint24: case WasapiCS.SampleFormatType.Sint32V24: mPcmSync.SetSampleValueInInt32(0, 0, 0x00000400); mPcmReady.SetSampleValueInInt32(0, 0, 0x00000300); break; default: System.Diagnostics.Debug.Assert(false); break; } // mPcmTest : テストデータ。このPCMデータを再生し、再生データと録音データが一致するかどうかを調べる。 if (mUseFile) { var conv = new WasapiPcmUtil.PcmFormatConverter(NUM_CHANNELS); mPcmTest = conv.Convert(mPlayPcmData, mPlaySampleFormat, new WasapiPcmUtil.PcmFormatConverter.BitsPerSampleConvArgs(WasapiPcmUtil.NoiseShapingType.None)); mNumTestFrames = mPcmTest.NumFrames; } else { mPcmTest = new PcmDataLib.PcmData(); mPcmTest.CopyHeaderInfoFrom(mPcmSync); var randData = new LargeArray <byte>((long)(WasapiCS.SampleFormatTypeToUseBitsPerSample(mPlaySampleFormat) / 8) * NUM_CHANNELS * mNumTestFrames); var fragment = new byte[4096]; for (long i = 0; i < randData.LongLength; i += fragment.Length) { long count = fragment.Length; if (randData.LongLength < i + count) { count = randData.LongLength - i; } mRand.NextBytes(fragment); randData.CopyFrom(fragment, 0, i, (int)count); } mPcmTest.SetSampleLargeArray(mNumTestFrames, randData); } // 録音データ置き場。 mCapturedPcmData = new LargeArray <byte>((long)(WasapiCS.SampleFormatTypeToUseBitsPerSample(mRecSampleFormat) / 8) * NUM_CHANNELS * (mNumTestFrames + (4 * SYNC_PCM_SECONDS) * mSampleRate)); }
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 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 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; }