public PcmData MonoToStereo() { System.Diagnostics.Debug.Assert(NumChannels == 1); // サンプルあたりビット数が8の倍数でないとこのアルゴリズムは使えない System.Diagnostics.Debug.Assert((BitsPerSample & 7) == 0); byte [] newSampleArray = new byte[mSampleArray.LongLength * 2]; { int bytesPerSample = BitsPerSample / 8; // NumFramesは総フレーム数。sampleArrayのフレーム数はこれよりも少ないことがある。 // 実際に存在するサンプル数sampleFramesだけ処理する。 long sampleFrames = mSampleArray.LongLength / bytesPerSample; long fromPosBytes = 0; for (long frame = 0; frame < sampleFrames; ++frame) { for (int offs = 0; offs < bytesPerSample; ++offs) { newSampleArray[fromPosBytes * 2 + offs] = mSampleArray[fromPosBytes + offs]; newSampleArray[fromPosBytes * 2 + bytesPerSample + offs] = mSampleArray[fromPosBytes + offs]; } fromPosBytes += bytesPerSample; } } PcmData newPcmData = new PcmData(); newPcmData.CopyHeaderInfoFrom(this); newPcmData.SetFormat(2, BitsPerSample, ValidBitsPerSample, SampleRate, SampleValueRepresentationType, NumFrames); newPcmData.SetSampleArray(newSampleArray); return(newPcmData); }
public static PcmData ReadWav(BinaryReader br) { var reader = new WavReader(); if (!reader.ReadHeaderAndSamples(br, 0, -1)) { return null; } var pcm = new PcmData(); pcm.AlbumTitle = reader.AlbumName; pcm.ArtistName = reader.ArtistName; pcm.DisplayName = reader.Title; pcm.SetFormat(reader.NumChannels, reader.BitsPerSample, reader.ValidBitsPerSample, reader.SampleRate, reader.SampleValueRepresentationType, reader.NumFrames); pcm.SetSampleArray(reader.GetSampleArray()); return pcm; }
/// <summary> /// Converts sample format to toFormat and returns new instance of PcmData. /// pcmFrom is not changed. /// </summary> /// <param name="toFormat">sample format to convert</param> /// <returns>Newly instanciated PcmData</returns> public PcmData Convert(PcmData pcmFrom, WasapiCS.SampleFormatType toFormat, BitsPerSampleConvArgs args) { if (args == null) { args = new BitsPerSampleConvArgs(NoiseShapingType.None); } var fromFormat = WasapiCS.BitAndFormatToSampleFormatType(pcmFrom.BitsPerSample, pcmFrom.ValidBitsPerSample, SampleFormatInfo.VrtToBft(pcmFrom.SampleValueRepresentationType)); if (fromFormat == WasapiCS.SampleFormatType.Unknown || toFormat == WasapiCS.SampleFormatType.Unknown) { return null; } var newSampleArray = mConvert[(int)fromFormat][(int)toFormat](pcmFrom, toFormat, args); PcmData newPcmData = new PcmData(); newPcmData.CopyHeaderInfoFrom(pcmFrom); newPcmData.SetFormat(pcmFrom.NumChannels, WasapiCS.SampleFormatTypeToUseBitsPerSample(toFormat), WasapiCS.SampleFormatTypeToValidBitsPerSample(toFormat), pcmFrom.SampleRate, SampleFormatInfo.BftToVrt(WasapiCS.SampleFormatTypeToBitFormatType(toFormat)), pcmFrom.NumFrames); newPcmData.SetSampleArray(newSampleArray); return newPcmData; }
public PcmData MonoToStereo() { System.Diagnostics.Debug.Assert(NumChannels == 1); // サンプルあたりビット数が8の倍数でないとこのアルゴリズムは使えない System.Diagnostics.Debug.Assert((BitsPerSample & 7) == 0); byte [] newSampleArray = new byte[mSampleArray.LongLength * 2]; { int bytesPerSample = BitsPerSample / 8; // NumFramesは総フレーム数。sampleArrayのフレーム数はこれよりも少ないことがある。 // 実際に存在するサンプル数sampleFramesだけ処理する。 long sampleFrames = mSampleArray.LongLength / bytesPerSample; long fromPosBytes = 0; for (long frame = 0; frame < sampleFrames; ++frame) { for (int offs = 0; offs < bytesPerSample; ++offs) { newSampleArray[fromPosBytes * 2 + offs] = mSampleArray[fromPosBytes + offs]; newSampleArray[fromPosBytes * 2 + bytesPerSample + offs] = mSampleArray[fromPosBytes + offs]; } fromPosBytes += bytesPerSample; } } PcmData newPcmData = new PcmData(); newPcmData.CopyHeaderInfoFrom(this); newPcmData.SetFormat(2, BitsPerSample, ValidBitsPerSample, SampleRate, SampleValueRepresentationType, NumFrames); newPcmData.SetSampleArray(newSampleArray); return newPcmData; }
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); }
public PcmData ConvertChannelCount(int newCh) { if (NumChannels == newCh) { // 既に希望のチャンネル数である。 return(this); } // サンプルあたりビット数が8の倍数でないとこのアルゴリズムは使えない System.Diagnostics.Debug.Assert((BitsPerSample & 7) == 0); // 新しいサンプルサイズ // NumFramesは総フレーム数。sampleArrayのフレーム数はこれよりも少ないことがある。 // 実際に存在するサンプル数sampleFramesだけ処理する。 int bytesPerSample = BitsPerSample / 8; long sampleFrames = mSampleArray.LongLength / (BitsPerFrame / 8); var newSampleArray = new byte[newCh * bytesPerSample * sampleFrames]; for (long frame = 0; frame < sampleFrames; ++frame) { int copyBytes = NumChannels * bytesPerSample; if (newCh < NumChannels) { // チャンネル数が減る場合。 copyBytes = newCh * bytesPerSample; } Array.Copy(mSampleArray, NumChannels * bytesPerSample * frame, newSampleArray, newCh * bytesPerSample * frame, copyBytes); if (SampleDataType == DataType.DoP && NumChannels < newCh) { // 追加したチャンネルにDSD無音をセットする。 switch (bytesPerSample) { case 3: for (int ch = NumChannels; ch < newCh; ++ch) { newSampleArray[(frame * newCh + ch) * bytesPerSample + 0] = 0x69; newSampleArray[(frame * newCh + ch) * bytesPerSample + 1] = 0x69; newSampleArray[(frame * newCh + ch) * bytesPerSample + 2] = (byte)((frame & 1) == 1 ? 0xfa : 0x05); } break; case 4: for (int ch = NumChannels; ch < newCh; ++ch) { newSampleArray[(frame * newCh + ch) * bytesPerSample + 1] = 0x69; newSampleArray[(frame * newCh + ch) * bytesPerSample + 2] = 0x69; newSampleArray[(frame * newCh + ch) * bytesPerSample + 3] = (byte)((frame & 1) == 1 ? 0xfa : 0x05); } break; } } } PcmData newPcmData = new PcmData(); newPcmData.CopyHeaderInfoFrom(this); newPcmData.SetFormat(newCh, BitsPerSample, ValidBitsPerSample, SampleRate, SampleValueRepresentationType, NumFrames); newPcmData.SetSampleArray(newSampleArray); return(newPcmData); }
private PcmData ReadWavFile(string path) { PcmData pcmData = new PcmData(); using (BinaryReader br = new BinaryReader( File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read))) { var wavR = new WavReader(); bool readSuccess = wavR.ReadHeaderAndSamples(br, 0, -1); if (!readSuccess) { return null; } pcmData.SetFormat(wavR.NumChannels, wavR.BitsPerSample, wavR.BitsPerSample, wavR.SampleRate, wavR.SampleValueRepresentationType, wavR.NumFrames); pcmData.SetSampleArray(wavR.GetSampleArray()); } return pcmData; }
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; }
public PcmData ConvertChannelCount(int newCh) { if (NumChannels == newCh) { // 既に希望のチャンネル数である。 return this; } // サンプルあたりビット数が8の倍数でないとこのアルゴリズムは使えない System.Diagnostics.Debug.Assert((BitsPerSample & 7) == 0); // 新しいサンプルサイズ // NumFramesは総フレーム数。sampleArrayのフレーム数はこれよりも少ないことがある。 // 実際に存在するサンプル数sampleFramesだけ処理する。 int bytesPerSample = BitsPerSample / 8; long sampleFrames = mSampleArray.LongLength / (BitsPerFrame / 8); var newSampleArray = new byte[newCh * bytesPerSample * sampleFrames]; for (long frame = 0; frame < sampleFrames; ++frame) { int copyBytes = NumChannels * bytesPerSample; if (newCh < NumChannels) { // チャンネル数が減る場合。 copyBytes = newCh * bytesPerSample; } Array.Copy(mSampleArray, NumChannels * bytesPerSample * frame, newSampleArray, newCh * bytesPerSample * frame, copyBytes); if (SampleDataType == DataType.DoP && NumChannels < newCh) { // 追加したチャンネルにDSD無音をセットする。 switch (bytesPerSample) { case 3: for (int ch = NumChannels; ch < newCh; ++ch) { newSampleArray[(frame * newCh + ch) * bytesPerSample + 0] = 0x69; newSampleArray[(frame * newCh + ch) * bytesPerSample + 1] = 0x69; newSampleArray[(frame * newCh + ch) * bytesPerSample + 2] = (byte)((frame & 1) == 1 ? 0xfa : 0x05); } break; case 4: for (int ch = NumChannels; ch < newCh; ++ch) { newSampleArray[(frame * newCh + ch) * bytesPerSample + 1] = 0x69; newSampleArray[(frame * newCh + ch) * bytesPerSample + 2] = 0x69; newSampleArray[(frame * newCh + ch) * bytesPerSample + 3] = (byte)((frame & 1) == 1 ? 0xfa : 0x05); } break; } } } PcmData newPcmData = new PcmData(); newPcmData.CopyHeaderInfoFrom(this); newPcmData.SetFormat(newCh, BitsPerSample, ValidBitsPerSample, SampleRate, SampleValueRepresentationType, NumFrames); newPcmData.SetSampleArray(newSampleArray); return newPcmData; }