/// <summary> /// ジッター発生。 /// </summary> private double GenerateJitter(USWorkerArgs args, int offs) { double seqJitter = args.ampSeqJitter * Math.Sin((args.thetaCoefficientSeqJitter * offs) % (2.0 * Math.PI)); double tpdfJitter = 0.0; double rpdfJitter = 0.0; if (0.0 < args.tpdfJitterPicoseconds) { double r = GenRandom0to1(gen) + GenRandom0to1(gen) - 1.0; tpdfJitter = args.ampTpdfJitter * r; } if (0.0 < args.rpdfJitterPicoseconds) { rpdfJitter = args.ampRpdfJitter * (GenRandom0to1(gen) * 2.0 - 1.0); } double jitter = seqJitter + tpdfJitter + rpdfJitter; return(jitter); }
private void PrepareResamplePosArray( USWorkerArgs args, int sampleRateFrom, int sampleRateTo, int sampleTotalFrom, int sampleTotalTo, int[] resamplePosArray, double[] fractionArray) { // resamplePosArrayとfractionArrayにジッターを付加する for (int i = 0; i < sampleTotalTo; ++i) { double resamplePos = (double)i * sampleRateFrom / sampleRateTo + GenerateJitter(args, i); /* -0.5 <= fraction<+0.5になるようにresamplePosを選ぶ。 * 最後のほうで範囲外を指さないようにする。 */ int resamplePosI = (int)(resamplePos + 0.5); if (resamplePosI < 0) { resamplePosI = 0; } if (sampleTotalFrom <= resamplePosI) { resamplePosI = sampleTotalFrom - 1; } double fraction = resamplePos - resamplePosI; resamplePosArray[i] = resamplePosI; fractionArray[i] = fraction; } }
private void buttonAQOutputStart_Click(object sender, RoutedEventArgs e) { USWorkerArgs args = new USWorkerArgs(); args.inputPath = textBoxAQInputFilePath.Text; args.outputPath = textBoxAQOutputFilePath.Text; if (!System.IO.File.Exists(args.inputPath)) { MessageBox.Show("エラー。入力ファイルが存在しません"); return; } if (!Double.TryParse(textBoxSequentialJitterFrequency.Text, out args.sequentialJitterFrequency) || args.sequentialJitterFrequency < 0.0) { MessageBox.Show("エラー。周期ジッター周波数に0以上の数値を入力してください"); return; } if (!Double.TryParse(textBoxSequentialJitterPicoseconds.Text, out args.sequentialJitterPicoseconds) || args.sequentialJitterPicoseconds < 0.0) { MessageBox.Show("エラー。周期ジッター最大ずれ量に0以上の数値を入力してください"); return; } // sequential jitter RMS⇒peak 正弦波なので√2倍する args.sequentialJitterPicoseconds *= Math.Sqrt(2.0); if (!Double.TryParse(textBoxTpdfJitterPicoseconds.Text, out args.tpdfJitterPicoseconds) || args.tpdfJitterPicoseconds < 0.0) { MessageBox.Show("エラー。三角分布ジッター最大ずれ量に0以上の数値を入力してください"); return; } if (!Double.TryParse(textBoxRpdfJitterPicoseconds.Text, out args.rpdfJitterPicoseconds) || args.rpdfJitterPicoseconds < 0.0) { MessageBox.Show("エラー。一様分布ジッター最大ずれ量に0以上の数値を入力してください"); return; } args.convolutionN = 256; args.device = ProcessDevice.Cpu; args.sampleSlice = 32768; if (radioButtonAQCpu16.IsChecked == true) { args.convolutionN = 65536; } if (radioButtonAQGpu16.IsChecked == true) { args.convolutionN = 65536; args.device = ProcessDevice.Gpu; } if (radioButtonAQGpu20.IsChecked == true) { args.convolutionN = 1048576; args.device = ProcessDevice.Gpu; // 重いので減らす。 args.sampleSlice = 256; } if (radioButtonAQGpu24.IsChecked == true) { args.convolutionN = 16777216; args.device = ProcessDevice.Gpu; // この条件では一度に32768個処理できない。 // 16384以下の値をセットする。 args.sampleSlice = 16; } args.addJitter = true; // 出力フォーマット args.outputBitsPerSample = 32; args.outputVRT = PcmData.ValueRepresentationType.SFloat; buttonAQOutputStart.IsEnabled = false; buttonAQBrowseOpen.IsEnabled = false; buttonAQBrowseSaveAs.IsEnabled = false; buttonAQAbort.IsEnabled = true; progressBarAQ.Value = 0; textBoxAQResult.Text += string.Format("処理中 {0}⇒{1}……処理中はPCの動作が重くなります!\r\n", args.inputPath, args.outputPath); textBoxAQResult.ScrollToEnd(); m_USAQworker.RunWorkerAsync(args); }
private int GpuUpsample(USWorkerArgs args, PcmData pcmDataIn, PcmData pcmDataOut) { int hr = 0; int sampleTotalTo = (int)pcmDataOut.NumFrames; float[] sampleData = new float[pcmDataIn.NumFrames]; for (int ch = 0; ch < pcmDataIn.NumChannels; ++ch) { for (int i = 0; i < pcmDataIn.NumFrames; ++i) { sampleData[i] = pcmDataIn.GetSampleValueInFloat(ch, i); } WWUpsampleGpu us = new WWUpsampleGpu(); if (args.addJitter) { hr = us.Init(args.convolutionN, sampleData, (int)pcmDataIn.NumFrames, pcmDataIn.SampleRate, args.resampleFrequency, sampleTotalTo, args.resamplePosArray, args.fractionArray); } else { hr = us.Init(args.convolutionN, sampleData, (int)pcmDataIn.NumFrames, pcmDataIn.SampleRate, args.resampleFrequency, sampleTotalTo); } if (hr < 0) { us.Term(); return hr; } int sampleRemain = sampleTotalTo; int offs = 0; while (0 < sampleRemain) { int sample1 = args.sampleSlice; if (sampleRemain < sample1) { sample1 = sampleRemain; } hr = us.ProcessPortion(offs, sample1); if (hr < 0) { break; } if (m_USAQworker.CancellationPending) { us.Term(); return -1; } sampleRemain -= sample1; offs += sample1; // 10%~99% m_USAQworker.ReportProgress( 10 + (int)(89L * offs / sampleTotalTo + 89L * ch) / pcmDataIn.NumChannels); } if (0 <= hr) { float[] output = new float[sampleTotalTo]; hr = us.GetResultFromGpuMemory(ref output, sampleTotalTo); if (0 <= hr) { // すべて成功。 for (int i = 0; i < pcmDataOut.NumFrames; ++i) { pcmDataOut.SetSampleValueInFloat(ch, i, output[i]); } } output = null; } us.Term(); if (hr < 0) { break; } } sampleData = null; return hr; }
/// <summary> /// ジッター発生。 /// </summary> private double GenerateJitter(USWorkerArgs args, int offs) { double seqJitter = args.ampSeqJitter * Math.Sin((args.thetaCoefficientSeqJitter * offs) % (2.0 * Math.PI)); double tpdfJitter = 0.0; double rpdfJitter = 0.0; if (0.0 < args.tpdfJitterPicoseconds) { double r = GenRandom0to1(gen) + GenRandom0to1(gen) - 1.0; tpdfJitter = args.ampTpdfJitter * r; } if (0.0 < args.rpdfJitterPicoseconds) { rpdfJitter = args.ampRpdfJitter * (GenRandom0to1(gen) * 2.0 - 1.0); } double jitter = seqJitter + tpdfJitter + rpdfJitter; return jitter; }
//////////////////////////////////////////////////////////////////// // US AQ共用ワーカースレッド private int CpuUpsample(USWorkerArgs args, PcmData pcmDataIn, PcmData pcmDataOut) { int hr = 0; int sampleTotalTo = (int)pcmDataOut.NumFrames; float[] sampleData = new float[pcmDataIn.NumFrames]; for (int ch = 0; ch < pcmDataIn.NumChannels; ++ch) { for (int i = 0; i < pcmDataIn.NumFrames; ++i) { sampleData[i] = pcmDataIn.GetSampleValueInFloat(ch, i); } WWUpsampleCpu us = new WWUpsampleCpu(); if (args.addJitter) { hr = us.Setup(args.convolutionN, sampleData, sampleData.Length, pcmDataIn.SampleRate, pcmDataOut.SampleRate, sampleTotalTo, args.resamplePosArray, args.fractionArray); } else { hr = us.Setup(args.convolutionN, sampleData, sampleData.Length, pcmDataIn.SampleRate, pcmDataOut.SampleRate, sampleTotalTo); } if (hr < 0) { break; } for (int offs = 0; offs < sampleTotalTo; offs += args.sampleSlice) { int sample1 = args.sampleSlice; if (sampleTotalTo - offs < sample1) { sample1 = sampleTotalTo - offs; } if (sample1 < 1) { break; } float[] outFragment = new float[sample1]; hr = us.Do(offs, sample1, outFragment); if (hr < 0) { // ここからbreakしても外のfor文までしか行かない us.Unsetup(); sampleData = null; break; } if (m_USAQworker.CancellationPending) { // ここからbreakしても外のfor文までしか行かない us.Unsetup(); sampleData = null; hr = -1; break; } if (0 <= hr) { // 成功。出てきたデータをpcmDataOutに詰める。 for (int j = 0; j < sample1; ++j) { pcmDataOut.SetSampleValueInFloat(ch, offs + j, outFragment[j]); } } outFragment = null; // 10%~99% m_USAQworker.ReportProgress( 10 + (int)(89L * offs / sampleTotalTo + 89L * ch) / pcmDataIn.NumChannels); } if (m_USAQworker.CancellationPending) { break; } if (hr < 0) { break; } us.Unsetup(); } sampleData = null; return hr; }
private void buttonUSOutputStart_Click(object sender, RoutedEventArgs e) { USWorkerArgs args = new USWorkerArgs(); args.inputPath = textBoxUSInputFilePath.Text; args.outputPath = textBoxUSOutputFilePath.Text; if (!System.IO.File.Exists(args.inputPath)) { MessageBox.Show("エラー。入力ファイルが存在しません"); return; } if (!Int32.TryParse(textBoxUSFrequency.Text, out args.resampleFrequency) || args.resampleFrequency < 0.0) { MessageBox.Show("エラー。リサンプル周波数に0以上の数値を入力してください"); return; } args.convolutionN = 256; args.device = ProcessDevice.Cpu; args.sampleSlice = 1; if (radioButtonUSCpu16.IsChecked == true) { args.convolutionN = 65536; } if (radioButtonUSGpu16.IsChecked == true) { args.convolutionN = 65536; args.device = ProcessDevice.Gpu; args.sampleSlice = 256; } if (radioButtonUSGpu20.IsChecked == true) { args.convolutionN = 1048576; args.device = ProcessDevice.Gpu; // 重いので減らす。 args.sampleSlice = 16; } if (radioButtonUSGpu24.IsChecked == true) { args.convolutionN = 16777216; args.device = ProcessDevice.Gpu; // この条件では一度に32768個処理できない。 // 16384以下の値をセットする。 args.sampleSlice = 1; } args.addJitter = false; // 出力フォーマット args.outputBitsPerSample = 16; args.outputVRT = PcmData.ValueRepresentationType.SInt; if (radioButtonOutputSint24.IsChecked == true) { args.outputBitsPerSample = 24; } if (radioButtonOutputFloat32.IsChecked == true) { args.outputBitsPerSample = 32; args.outputVRT = PcmData.ValueRepresentationType.SFloat; } buttonUSOutputStart.IsEnabled = false; buttonUSBrowseOpen.IsEnabled = false; buttonUSBrowseSaveAs.IsEnabled = false; buttonUSAbort.IsEnabled = true; progressBarUS.Value = 0; textBoxUSResult.Text += string.Format("処理中 {0}⇒{1}……処理中はPCの動作が重くなります!\r\n", args.inputPath, args.outputPath); textBoxUSResult.ScrollToEnd(); m_USAQworker.RunWorkerAsync(args); }
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 int GpuUpsample(USWorkerArgs args, PcmData pcmDataIn, PcmData pcmDataOut) { int hr = 0; int sampleTotalTo = (int)pcmDataOut.NumFrames; float[] sampleData = new float[pcmDataIn.NumFrames]; for (int ch = 0; ch < pcmDataIn.NumChannels; ++ch) { for (int i = 0; i < pcmDataIn.NumFrames; ++i) { sampleData[i] = pcmDataIn.GetSampleValueInFloat(ch, i); } WWUpsampleGpu us = new WWUpsampleGpu(); if (args.addJitter) { hr = us.Init(args.convolutionN, sampleData, (int)pcmDataIn.NumFrames, pcmDataIn.SampleRate, args.resampleFrequency, sampleTotalTo, args.resamplePosArray, args.fractionArray); } else { hr = us.Init(args.convolutionN, sampleData, (int)pcmDataIn.NumFrames, pcmDataIn.SampleRate, args.resampleFrequency, sampleTotalTo); } if (hr < 0) { us.Term(); return(hr); } int sampleRemain = sampleTotalTo; int offs = 0; while (0 < sampleRemain) { int sample1 = args.sampleSlice; if (sampleRemain < sample1) { sample1 = sampleRemain; } hr = us.ProcessPortion(offs, sample1); if (hr < 0) { break; } if (m_USAQworker.CancellationPending) { us.Term(); return(-1); } sampleRemain -= sample1; offs += sample1; // 10%~99% m_USAQworker.ReportProgress( 10 + (int)(89L * offs / sampleTotalTo + 89L * ch) / pcmDataIn.NumChannels); } if (0 <= hr) { float[] output = new float[sampleTotalTo]; hr = us.GetResultFromGpuMemory(ref output, sampleTotalTo); if (0 <= hr) { // すべて成功。 for (int i = 0; i < pcmDataOut.NumFrames; ++i) { pcmDataOut.SetSampleValueInFloat(ch, i, output[i]); } } output = null; } us.Term(); if (hr < 0) { break; } } sampleData = null; return(hr); }
//////////////////////////////////////////////////////////////////// // US AQ共用ワーカースレッド private int CpuUpsample(USWorkerArgs args, PcmData pcmDataIn, PcmData pcmDataOut) { int hr = 0; int sampleTotalTo = (int)pcmDataOut.NumFrames; float[] sampleData = new float[pcmDataIn.NumFrames]; for (int ch = 0; ch < pcmDataIn.NumChannels; ++ch) { for (int i = 0; i < pcmDataIn.NumFrames; ++i) { sampleData[i] = pcmDataIn.GetSampleValueInFloat(ch, i); } WWUpsampleCpu us = new WWUpsampleCpu(); if (args.addJitter) { hr = us.Setup(args.convolutionN, sampleData, sampleData.Length, pcmDataIn.SampleRate, pcmDataOut.SampleRate, sampleTotalTo, args.resamplePosArray, args.fractionArray); } else { hr = us.Setup(args.convolutionN, sampleData, sampleData.Length, pcmDataIn.SampleRate, pcmDataOut.SampleRate, sampleTotalTo); } if (hr < 0) { break; } for (int offs = 0; offs < sampleTotalTo; offs += args.sampleSlice) { int sample1 = args.sampleSlice; if (sampleTotalTo - offs < sample1) { sample1 = sampleTotalTo - offs; } if (sample1 < 1) { break; } float[] outFragment = new float[sample1]; hr = us.Do(offs, sample1, outFragment); if (hr < 0) { // ここからbreakしても外のfor文までしか行かない us.Unsetup(); sampleData = null; break; } if (m_USAQworker.CancellationPending) { // ここからbreakしても外のfor文までしか行かない us.Unsetup(); sampleData = null; hr = -1; break; } if (0 <= hr) { // 成功。出てきたデータをpcmDataOutに詰める。 for (int j = 0; j < sample1; ++j) { pcmDataOut.SetSampleValueInFloat(ch, offs + j, outFragment[j]); } } outFragment = null; // 10%~99% m_USAQworker.ReportProgress( 10 + (int)(89L * offs / sampleTotalTo + 89L * ch) / pcmDataIn.NumChannels); } if (m_USAQworker.CancellationPending) { break; } if (hr < 0) { break; } us.Unsetup(); } sampleData = null; return(hr); }