public PcmData MonoToStereo() { System.Diagnostics.Debug.Assert(NumChannels == 1); // サンプルあたりビット数が8の倍数でないとこのアルゴリズムは使えない System.Diagnostics.Debug.Assert((BitsPerSample & 7) == 0); var newSampleArray = new WWUtil.LargeArray <byte>(mSampleLargeArray.LongLength * 2); { int bytesPerSample = BitsPerSample / 8; // sampleArrayのフレーム数はこれよりも少ないことがある。 // 実際に存在するサンプル数sampleFramesだけ処理する。 long sampleFrames = mSampleLargeArray.LongLength / bytesPerSample; // NumChannels==1なので。 long fromPosBytes = 0; for (long frame = 0; frame < sampleFrames; ++frame) { for (int offs = 0; offs < bytesPerSample; ++offs) { byte b = mSampleLargeArray.At(fromPosBytes + offs); newSampleArray.Set(fromPosBytes * 2 + offs, b); newSampleArray.Set(fromPosBytes * 2 + bytesPerSample + offs, b); } fromPosBytes += bytesPerSample; } } PcmData newPcmData = new PcmData(); newPcmData.CopyHeaderInfoFrom(this); newPcmData.SetFormat(2, BitsPerSample, ValidBitsPerSample, SampleRate, SampleValueRepresentationType, NumFrames); newPcmData.SetSampleLargeArray(newSampleArray); return(newPcmData); }
/// <summary> /// rhsの内容をコピーする。PCMデータ配列だけはコピーしない。(nullをセットする) /// PCMデータ配列は、SetSampleArrayで別途設定する。 /// </summary> /// <param name="rhs">コピー元</param> public void CopyHeaderInfoFrom(PcmData rhs) { NumChannels = rhs.NumChannels; SampleRate = rhs.SampleRate; BitsPerSample = rhs.BitsPerSample; ValidBitsPerSample = rhs.ValidBitsPerSample; SampleValueRepresentationType = rhs.SampleValueRepresentationType; mNumFrames = rhs.mNumFrames; mSampleLargeArray = null; Id = rhs.Id; Ordinal = rhs.Ordinal; GroupId = rhs.GroupId; FileName = rhs.FileName; FullPath = rhs.FullPath; DisplayName = rhs.DisplayName; StartTick = rhs.StartTick; EndTick = rhs.EndTick; AlbumTitle = rhs.AlbumTitle; ArtistName = rhs.ArtistName; ComposerName = rhs.ComposerName; CueSheetIndex = rhs.CueSheetIndex; PictureBytes = rhs.PictureBytes; PictureData = rhs.PictureData; SampleDataType = rhs.SampleDataType; LastWriteTime = rhs.LastWriteTime; TrackId = rhs.TrackId; }
/// <summary> /// サンプリング周波数と量子化ビット数、有効なビット数、チャンネル数、データ形式が同じならtrue /// </summary> public bool IsSameFormat(PcmData other) { return(BitsPerSample == other.BitsPerSample && ValidBitsPerSample == other.ValidBitsPerSample && SampleRate == other.SampleRate && NumChannels == other.NumChannels && SampleValueRepresentationType == other.SampleValueRepresentationType && SampleDataType == other.SampleDataType); }
/// <summary> /// ヘッダー情報、サンプルデータ領域をクローンする。 /// </summary> public void CopyFrom(PcmData rhs) { CopyHeaderInfoFrom(rhs); mSampleLargeArray = null; if (rhs.mSampleLargeArray != null) { mSampleLargeArray = rhs.mSampleLargeArray.Clone(); } }
/// <summary> /// WAVファイルのヘッダ部分を読み込む。 /// </summary> /// <returns>読めたらtrue</returns> private bool ReadWavFileHeader(string path) { bool result = false; var pd = new PcmDataLib.PcmData(); var wavR = new WavReader(); using (BinaryReader br = new BinaryReader(File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read))) { if (wavR.ReadHeader(br)) { // WAVヘッダ読み込み成功。PcmDataを作って再生リストに足す。 pd.SetFormat(wavR.NumChannels, wavR.BitsPerSample, wavR.ValidBitsPerSample, (int)wavR.SampleRate, wavR.SampleValueRepresentationType, wavR.NumFrames); if ("RIFFINFO_INAM".Equals(wavR.Title) && "RIFFINFO_IART".Equals(wavR.ArtistName)) { // Issue 79 workaround } else { if (wavR.Title != null) { pd.DisplayName = wavR.Title; } if (wavR.AlbumName != null) { pd.AlbumTitle = wavR.AlbumName; } if (wavR.ArtistName != null) { pd.ArtistName = wavR.ArtistName; } } pd.SetPicture(wavR.PictureBytes, wavR.PictureData); result = CheckAddPcmData(path, pd, true); } else { LoadErrorMessageAdd(string.Format(CultureInfo.InvariantCulture, Properties.Resources.ReadFileFailed + ": {1} : {2}{3}", "WAV", path, wavR.ErrorReason, Environment.NewLine)); } } return(result); }
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; }
private bool ReadMp3FileHeader(string path) { bool result = false; var pd = new PcmDataLib.PcmData(); WWMFReader.Metadata meta; int rv = WWMFReader.ReadHeader(path, out meta); if (0 <= rv) { pd.SetFormat(meta.numChannels, meta.bitsPerSample, meta.bitsPerSample, meta.sampleRate, PcmData.ValueRepresentationType.SInt, meta.numApproxFrames); pd.SampleDataType = PcmDataLib.PcmData.DataType.PCM; pd.DisplayName = meta.title; pd.AlbumTitle = meta.album; pd.ArtistName = meta.artist; pd.ComposerName = meta.composer; if (null != meta.picture) { pd.SetPicture(meta.picture.Length, meta.picture); } pd.BitRate = meta.bitRate; pd.IsLossyCompressed = true; if (CheckAddPcmData(path, pd, true)) { result = true; } } else { LoadErrorMessageAdd(string.Format(CultureInfo.InvariantCulture, Properties.Resources.ReadFileFailed + " {1}: {2}{3}", "MP3", rv, path, Environment.NewLine)); } return(result); }
/// <summary> /// カバーアート画像を追加する。 /// </summary> /// <returns>true: カバーアート画像が付いている。false: カバーアート画像がついていない。</returns> private bool AddCoverart(string path, PcmDataLib.PcmData pcmData) { if (0 < pcmData.PictureBytes) { // 既に追加されている。 return(true); } try { var dirPath = System.IO.Path.GetDirectoryName(path); var pictureData = ReadWholeFile(string.Format(CultureInfo.InvariantCulture, "{0}\\{1}.jpg", dirPath, System.IO.Path.GetFileNameWithoutExtension(path))); if (0 < pictureData.Length) { // ファイル名.jpgが存在。 pcmData.SetPicture(pictureData.Length, pictureData); return(true); } foreach (string albumImageFilename in ALBUM_IMAGE_FILENAMES) { pictureData = ReadWholeFile(string.Format(CultureInfo.InvariantCulture, "{0}\\{1}", dirPath, albumImageFilename)); if (0 < pictureData.Length) { // アルバムのカバーアート画像(folder.jpg等)が存在。 pcmData.SetPicture(pictureData.Length, pictureData); return(true); } } } catch (Exception ex) { // エラーが起きたら読まない。 LoadErrorMessageAdd(string.Format(CultureInfo.InvariantCulture, "W: coverart image read failed: {0}", ex)); } return(false); }
/// <summary> /// rhsの内容をコピーする。PCMデータ配列だけはコピーしない。(nullをセットする) /// PCMデータ配列は、SetSampleArrayで別途設定する。 /// </summary> /// <param name="rhs">コピー元</param> public void CopyHeaderInfoFrom(PcmData rhs) { NumChannels = rhs.NumChannels; SampleRate = rhs.SampleRate; BitsPerSample = rhs.BitsPerSample; ValidBitsPerSample = rhs.ValidBitsPerSample; SampleValueRepresentationType = rhs.SampleValueRepresentationType; mNumFrames = rhs.mNumFrames; mSampleArray = null; Id = rhs.Id; Ordinal = rhs.Ordinal; GroupId = rhs.GroupId; FileName = rhs.FileName; FullPath = rhs.FullPath; DisplayName = rhs.DisplayName; StartTick = rhs.StartTick; EndTick = rhs.EndTick; AlbumTitle = rhs.AlbumTitle; ArtistName = rhs.ArtistName; CueSheetIndex = rhs.CueSheetIndex; PictureBytes = rhs.PictureBytes; PictureData = rhs.PictureData; SampleDataType = rhs.SampleDataType; LastWriteTime = rhs.LastWriteTime; }
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 byte[] ConvI32toI24orI32V24(PcmData pcmFrom, WasapiCS.SampleFormatType toFormat, BitsPerSampleConvArgs args) { return ConvCommon(pcmFrom, toFormat, args, (from, to, nSample, noiseShaping) => { int fromPos = 0; int toPos = 0; bool writePad = toFormat == WasapiCS.SampleFormatType.Sint32V24; switch (noiseShaping) { case NoiseShapingType.None: { for (int i = 0; i < nSample; ++i) { // discard lower 8-bit if (writePad) { to[toPos++] = 0; } to[toPos++] = from[fromPos + 1]; to[toPos++] = from[fromPos + 2]; to[toPos++] = from[fromPos + 3]; fromPos += 4; } } break; case NoiseShapingType.AddDither: { for (int i = 0; i < nSample; ++i) { long v = (int)((from[fromPos + 0] << 0) + (from[fromPos + 1] << 8) + (from[fromPos + 2] << 16) + (from[fromPos + 3] << 24)); // TPDF dither (width 512, center 128, left edge -128, right edge 384) int dither = (int)((mRand.NextDouble() + mRand.NextDouble() - 0.5) * 256); v += dither; int vOut; if (Int32.MaxValue < v) { vOut = Int32.MaxValue; } else if (v < Int32.MinValue) { vOut = Int32.MinValue; } else { vOut = (int)v; } if (writePad) { to[toPos++] = 0; } to[toPos++] = (byte)((vOut >> 8) & 0xff); to[toPos++] = (byte)((vOut >> 16) & 0xff); to[toPos++] = (byte)((vOut >> 24) & 0xff); fromPos += 4; } args.noiseShapingOrDitherPerformed = true; } break; case NoiseShapingType.NoiseShaping1stOrder: { long nFrame = nSample / pcmFrom.NumChannels; System.Diagnostics.Debug.Assert(mErr != null && mErr.Length == pcmFrom.NumChannels); for (int i = 0; i < nFrame; ++i) { for (int ch=0; ch < pcmFrom.NumChannels; ++ch) { int v = (short)(from[fromPos + 1] + (from[fromPos + 2] << 8) + (from[fromPos + 3] << 16)); v += mErr[ch] >> 8; if (8388607 < v) { v = 8388607; } mErr[ch] -= ((mErr[ch]) >> 8) << 8; mErr[ch] += from[fromPos]; if (writePad) { to[toPos++] = 0; } to[toPos++] = (byte)(v & 0xff); to[toPos++] = (byte)((v >> 8) & 0xff); to[toPos++] = (byte)((v >> 16) & 0xff); fromPos += 4; } } args.noiseShapingOrDitherPerformed = true; } break; case NoiseShapingType.DitheredNoiseShaping1stOrder: { long nFrame = nSample / pcmFrom.NumChannels; System.Diagnostics.Debug.Assert(mErr != null && mErr.Length == pcmFrom.NumChannels); for (int i = 0; i < nFrame; ++i) { for (int ch=0; ch < pcmFrom.NumChannels; ++ch) { long v = (int)((from[fromPos + 0] << 0) + (from[fromPos + 1] << 8) + (from[fromPos + 2] << 16) + (from[fromPos + 3] << 24)); // RPDF int dither = (int)((mRand.NextDouble() - 0.5) * 256 / 4); v += mErr[ch] + dither; int vOut; if (Int32.MaxValue < v) { vOut = Int32.MaxValue; } else if (v < Int32.MinValue) { vOut = Int32.MinValue; } else { vOut = (int)v; } vOut = (int)(vOut & 0xffffff00); mErr[ch] = (int)(v - vOut); if (writePad) { to[toPos++] = 0; } to[toPos++] = (byte)((vOut >> 8) & 0xff); to[toPos++] = (byte)((vOut >> 16) & 0xff); to[toPos++] = (byte)((vOut >> 24) & 0xff); fromPos += 4; } } args.noiseShapingOrDitherPerformed = true; } break; } }); }
private byte[] ConvI32toF32(PcmData pcmFrom, WasapiCS.SampleFormatType toFormat, BitsPerSampleConvArgs args) { return ConvCommon(pcmFrom, toFormat, args, (from, to, nSample, noiseShaping) => { int fromPos = 0; int toPos = 0; for (int i = 0; i < nSample; ++i) { int iv = ((int)from[fromPos + 1] << 8) + ((int)from[fromPos + 2] << 16) + ((int)from[fromPos + 3] << 24); float fv = ((float)iv) * (1.0f / 2147483648.0f); byte [] b = System.BitConverter.GetBytes(fv); to[toPos++] = b[0]; to[toPos++] = b[1]; to[toPos++] = b[2]; to[toPos++] = b[3]; fromPos += 4; } }); }
private byte[] ConvI24or32toI16(PcmData pcmFrom, WasapiCS.SampleFormatType toFormat, BitsPerSampleConvArgs args) { return ConvCommon(pcmFrom, toFormat, args, (from, to, nSample, noiseShaping) => { int fromPos = 0; int toPos = 0; int fromBytesPerSample = pcmFrom.BitsPerSample / 8; switch (noiseShaping) { case NoiseShapingType.None: { int fromSkipCount = 1; if (pcmFrom.BitsPerSample == 32) { fromSkipCount = 2; } for (int i = 0; i < nSample; ++i) { // discard lower bits fromPos += fromSkipCount; to[toPos++] = from[fromPos++]; to[toPos++] = from[fromPos++]; } } break; case NoiseShapingType.AddDither: { if (pcmFrom.BitsPerSample == 32) { // discard lower 8-bit ++fromPos; } for (int i = 0; i < nSample; ++i) { long v = (int)((from[fromPos] << 8) + (from[fromPos + 1] << 16) + (from[fromPos + 2] << 24)); // TPDF dither (width 131072, center 32768, left edge -32768, right edge 98304) int dither = (int)((mRand.NextDouble() + mRand.NextDouble() - 0.5) * 65536); v += dither; int vOut; if (Int32.MaxValue < v) { vOut = Int32.MaxValue; } else if (v < Int32.MinValue) { vOut = Int32.MinValue; } else { vOut = (int)v; } to[toPos++] = (byte)((vOut >> 16) & 0xff); to[toPos++] = (byte)((vOut >> 24) & 0xff); fromPos += fromBytesPerSample; } args.noiseShapingOrDitherPerformed = true; } break; case NoiseShapingType.NoiseShaping1stOrder: { long nFrame = nSample / pcmFrom.NumChannels; System.Diagnostics.Debug.Assert(mErr != null && mErr.Length == pcmFrom.NumChannels); if (pcmFrom.BitsPerSample == 32) { // discard lower 8-bit ++fromPos; } for (int i = 0; i < nFrame; ++i) { for (int ch=0; ch < pcmFrom.NumChannels; ++ch) { int v = (short)(from[fromPos + 1] + (from[fromPos + 2] << 8)); v += mErr[ch] >> 8; if (32767 < v) { v = 32767; } mErr[ch] -= ((mErr[ch]) >> 8) << 8; mErr[ch] += from[fromPos]; to[toPos++] = (byte)(v & 0xff); to[toPos++] = (byte)((v >> 8) & 0xff); fromPos += fromBytesPerSample; } } args.noiseShapingOrDitherPerformed = true; } break; case NoiseShapingType.DitheredNoiseShaping1stOrder: { long nFrame = nSample / pcmFrom.NumChannels; System.Diagnostics.Debug.Assert(mErr != null && mErr.Length == pcmFrom.NumChannels); if (pcmFrom.BitsPerSample == 32) { // discard lower 8-bit ++fromPos; } for (int i = 0; i < nFrame; ++i) { for (int ch=0; ch < pcmFrom.NumChannels; ++ch) { long v = (int)((from[fromPos] << 8) + (from[fromPos + 1] << 16) + (from[fromPos + 2] << 24)); // RPDF int dither = (int)((mRand.NextDouble() - 0.5) * 65536 / 4); v += mErr[ch] + dither; int vOut; if (Int32.MaxValue < v) { vOut = Int32.MaxValue; } else if (v < Int32.MinValue) { vOut = Int32.MinValue; } else { vOut = (int)v; } vOut = (int)(vOut & 0xffff0000); mErr[ch] = (int)(v - vOut); to[toPos++] = (byte)((vOut >> 16) & 0xff); to[toPos++] = (byte)((vOut >> 24) & 0xff); fromPos += fromBytesPerSample; } } args.noiseShapingOrDitherPerformed = true; } break; } }); }
private byte[] ConvI16toF32(PcmData pcmFrom, WasapiCS.SampleFormatType toFormat, BitsPerSampleConvArgs args) { return ConvCommon(pcmFrom, toFormat, args, (from, to, nSample, noiseShaping) => { int fromPos = 0; int toPos = 0; for (int i = 0; i < nSample; ++i) { short iv = (short)(from[fromPos] + (from[fromPos + 1] << 8)); float fv = ((float)iv) * (1.0f / 32768.0f); byte [] b = System.BitConverter.GetBytes(fv); to[toPos++] = b[0]; to[toPos++] = b[1]; to[toPos++] = b[2]; to[toPos++] = b[3]; fromPos += 2; } }); }
private byte[] ConvF64toI24orI32V24(PcmData pcmFrom, WasapiCS.SampleFormatType toFormat, BitsPerSampleConvArgs args) { return ConvCommon(pcmFrom, toFormat, args, (from, to, nSample, noiseShaping) => { int fromPos = 0; int toPos = 0; bool writePad = toFormat == WasapiCS.SampleFormatType.Sint32V24; for (int i = 0; i < nSample; ++i) { double dv = System.BitConverter.ToDouble(from, fromPos); if (SAMPLE_VALUE_MAX_DOUBLE <= dv) { dv = SAMPLE_VALUE_MAX_DOUBLE_TO_I24; IncrementClippedCounter(); } if (dv < SAMPLE_VALUE_MIN_DOUBLE) { dv = SAMPLE_VALUE_MIN_DOUBLE; IncrementClippedCounter(); } int iv = (int)(dv * 8388608.0); if (writePad) { to[toPos++] = 0; } to[toPos++] = (byte)(iv & 0xff); to[toPos++] = (byte)((iv >> 8) & 0xff); to[toPos++] = (byte)((iv >> 16) & 0xff); fromPos += 8; } }); }
/// <summary> /// メタ情報更新。PcmData読み込み成功後に行う。 /// FLACとWAVとAIFFで共通。 /// </summary> private bool CheckAddPcmData(string path, PcmDataLib.PcmData pcmData, bool bUsePlaylistTrackInfo) { if (31 < pcmData.NumChannels) { LoadErrorMessageAdd(string.Format(CultureInfo.InvariantCulture, "{0}: {1} {2}ch{3}", Properties.Resources.TooManyChannels, path, pcmData.NumChannels, Environment.NewLine)); return(false); } if (pcmData.BitsPerSample != 16 && pcmData.BitsPerSample != 24 && pcmData.BitsPerSample != 32 && pcmData.BitsPerSample != 64) { LoadErrorMessageAdd(string.Format(CultureInfo.InvariantCulture, "{0}: {1} {2}bit{3}", Properties.Resources.NotSupportedQuantizationBitRate, path, pcmData.BitsPerSample, Environment.NewLine)); return(false); } pcmData.FullPath = path; pcmData.FileName = System.IO.Path.GetFileName(path); pcmData.LastWriteTime = System.IO.File.GetLastWriteTimeUtc(path).Ticks; // PCMファイルにタイトル名が埋め込まれていない時、ファイル名をタイトル名にする。 if (pcmData.DisplayName == null || pcmData.DisplayName.Length == 0) { pcmData.DisplayName = pcmData.FileName; } if (!bUsePlaylistTrackInfo || null == mPlaylistTrackMeta) { // startTickとendTickは、既にセットされていることもあるので、ここではセットしない。 // pcmData.StartTick = 0; // pcmData.EndTick = -1; // pcmData.CueSheetIndex = 1; } else { pcmData.StartTick = mPlaylistTrackMeta.startTick; pcmData.EndTick = mPlaylistTrackMeta.endTick; pcmData.CueSheetIndex = mPlaylistTrackMeta.indexId; // 再生リストにタイトル名情報がある時は、再生リストのタイトル名をタイトル名にする。 if (mPlaylistTrackMeta.title != null && 0 < mPlaylistTrackMeta.title.Length) { pcmData.DisplayName = mPlaylistTrackMeta.title; } if (mPlaylistTrackMeta.performer != null && 0 < mPlaylistTrackMeta.performer.Length) { pcmData.ArtistName = mPlaylistTrackMeta.performer; } if (mPlaylistTrackMeta.albumTitle != null && 0 < mPlaylistTrackMeta.albumTitle.Length) { pcmData.AlbumTitle = mPlaylistTrackMeta.albumTitle; } pcmData.TrackId = mPlaylistTrackMeta.trackId; } bool readSeparatorAfter = false; if (mPlis != null) { // PPWプレイリストの情報で上書きする pcmData.DisplayName = mPlis.Title; pcmData.AlbumTitle = mPlis.AlbumName; pcmData.ArtistName = mPlis.ArtistName; pcmData.ComposerName = mPlis.ComposerName; pcmData.StartTick = mPlis.StartTick; pcmData.EndTick = mPlis.EndTick; pcmData.TrackId = mPlis.TrackId; pcmData.CueSheetIndex = mPlis.CueSheetIndex; readSeparatorAfter = mPlis.ReadSeparaterAfter; } // カバーアート画像を追加する AddCoverart(path, pcmData); mAddPcmData(pcmData, readSeparatorAfter, mPlis != null); return(true); }
public void Insert(int idx, PcmData pd) { mPcmDataList.Insert(idx, pd); }
public void Add(PcmData pd) { mPcmDataList.Add(pd); }
/// <summary> /// 読み込み処理を通常終了する。 /// </summary> /// <returns>Error code</returns> public int StreamEnd() { int rv = 0; mMD5SumInMetadata = null; mMD5SumOfPcm = null; switch (m_format) { case Format.FLAC: rv = mFlacR.ReadStreamEnd(); mMD5SumInMetadata = mFlacR.MD5SumInMetadata; mMD5SumOfPcm = mFlacR.MD5SumOfPcm; break; case Format.AIFF: mAiffR.ReadStreamEnd(); break; case Format.WAVE: mWaveR.ReadStreamEnd(); break; case Format.DSF: mDsfR.ReadStreamEnd(); break; case Format.DSDIFF: mDsdiffR.ReadStreamEnd(); break; default: System.Diagnostics.Debug.Assert(false); break; } if (null != mBr) { mBr.Close(); mBr = null; } mPcmData = null; mFlacR = null; mAiffR = null; mWaveR = null; mDsfR = null; return rv; }
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 byte[] ConvI32V24toI32(PcmData pcmFrom, WasapiCS.SampleFormatType toFormat, BitsPerSampleConvArgs args) { return ConvCommon(pcmFrom, toFormat, args, (from, to, nSample, noiseShaping) => { int fromPos = 0; int toPos = 0; for (int i = 0; i < nSample; ++i) { // discard lower 8-bit because it is garbage to[toPos++] = 0; to[toPos++] = from[fromPos + 1]; to[toPos++] = from[fromPos + 2]; to[toPos++] = from[fromPos + 3]; fromPos += 4; } }); }
public void StreamAbort() { switch (m_format) { case Format.FLAC: mFlacR.ReadStreamAbort(); break; case Format.AIFF: mAiffR.ReadStreamEnd(); break; case Format.WAVE: mWaveR.ReadStreamEnd(); break; case Format.DSF: mDsfR.ReadStreamEnd(); break; case Format.DSDIFF: mDsdiffR.ReadStreamEnd(); break; default: System.Diagnostics.Debug.Assert(false); break; } if (null != mBr) { mBr.Close(); mBr = null; } mPcmData = null; mFlacR = null; mAiffR = null; mWaveR = null; mDsfR = null; }
/// <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; }
private byte[] ConvF64toI32(PcmData pcmFrom, WasapiCS.SampleFormatType toFormat, BitsPerSampleConvArgs args) { return ConvCommon(pcmFrom, toFormat, args, (from, to, nSample, noiseShaping) => { int fromPos = 0; int toPos = 0; for (int i = 0; i < nSample; ++i) { double dv = System.BitConverter.ToDouble(from, fromPos); int iv = 0; if ((long)Int32.MaxValue < (long)(dv * Int32.MaxValue)) { iv = Int32.MaxValue; IncrementClippedCounter(); } else if ((long)(-dv * Int32.MinValue) < (long)Int32.MinValue) { iv = Int32.MinValue; IncrementClippedCounter(); } else { iv = (int)(-dv * Int32.MinValue); } to[toPos++] = (byte)((iv >> 0) & 0xff); to[toPos++] = (byte)((iv >> 8) & 0xff); to[toPos++] = (byte)((iv >> 16) & 0xff); to[toPos++] = (byte)((iv >> 24) & 0xff); fromPos += 8; } }); }
private byte[] ConvClone(PcmData from, WasapiCS.SampleFormatType toFormat, BitsPerSampleConvArgs args) { return (byte[])from.GetSampleArray().Clone(); }
private byte[] ConvI16toF64(PcmData pcmFrom, WasapiCS.SampleFormatType toFormat, BitsPerSampleConvArgs args) { return ConvCommon(pcmFrom, toFormat, args, (from, to, nSample, noiseShaping) => { int fromPos = 0; int toPos = 0; for (int i = 0; i < nSample; ++i) { short iv = (short)(from[fromPos] + (from[fromPos + 1] << 8)); double dv = ((double)iv) * (1.0 / 32768.0); byte [] b = System.BitConverter.GetBytes(dv); for (int j=0; j < 8; ++j) { to[toPos++] = b[j]; } fromPos += 2; } }); }
private byte[] ConvCommon(PcmData pcmFrom, WasapiCS.SampleFormatType toFormat, BitsPerSampleConvArgs args, ConversionLoop convLoop) { var from = pcmFrom.GetSampleArray(); long nSample = from.LongLength * 8 / pcmFrom.BitsPerSample; var to = new byte[nSample * WasapiCS.SampleFormatTypeToUseBitsPerSample(toFormat) / 8]; convLoop(from, to, nSample, args.noiseShaping); return to; }
private byte[] ConvI24toI32(PcmData pcmFrom, WasapiCS.SampleFormatType toFormat, BitsPerSampleConvArgs args) { return ConvCommon(pcmFrom, toFormat, args, (from, to, nSample, noiseShaping) => { int fromPos = 0; int toPos = 0; for (int i = 0; i < nSample; ++i) { // Lower 8-bit: fill 0s to[toPos++] = 0; // Higher 24-bit: copy PCM to[toPos++] = from[fromPos++]; to[toPos++] = from[fromPos++]; to[toPos++] = from[fromPos++]; } }); }
private byte[] ConvError(PcmData from, WasapiCS.SampleFormatType toFormat, BitsPerSampleConvArgs args) { System.Diagnostics.Debug.Assert(false); return null; }
private byte[] ConvI32toF64(PcmData pcmFrom, WasapiCS.SampleFormatType toFormat, BitsPerSampleConvArgs args) { return ConvCommon(pcmFrom, toFormat, args, (from, to, nSample, noiseShaping) => { int fromPos = 0; int toPos = 0; for (int i = 0; i < nSample; ++i) { int iv = ((int)from[fromPos + 1] << 8) + ((int)from[fromPos + 2] << 16) + ((int)from[fromPos + 3] << 24); double dv = ((double)iv) * (1.0 / 2147483648.0); byte [] b = System.BitConverter.GetBytes(dv); for (int j=0; j < 8; ++j) { to[toPos++] = b[j]; } fromPos += 4; } }); }
/// <summary> /// サンプリング周波数と量子化ビット数、有効なビット数、チャンネル数、データ形式が同じならtrue /// </summary> public bool IsSameFormat(PcmData other) { return BitsPerSample == other.BitsPerSample && ValidBitsPerSample == other.ValidBitsPerSample && SampleRate == other.SampleRate && NumChannels == other.NumChannels && SampleValueRepresentationType == other.SampleValueRepresentationType && SampleDataType == other.SampleDataType; }
private byte[] ConvI32V24toI24(PcmData pcmFrom, WasapiCS.SampleFormatType toFormat, BitsPerSampleConvArgs args) { return ConvCommon(pcmFrom, toFormat, args, (from, to, nSample, noiseShaping) => { int fromPos = 0; int toPos = 0; for (int i = 0; i < nSample; ++i) { // truncate lower 8-bit of 32-bit PCM to create 24-bit PCM to[toPos++] = from[fromPos + 1]; to[toPos++] = from[fromPos + 2]; to[toPos++] = from[fromPos + 3]; fromPos += 4; } }); }
private byte[] ConvF32toI16(PcmData pcmFrom, WasapiCS.SampleFormatType toFormat, BitsPerSampleConvArgs args) { return ConvCommon(pcmFrom, toFormat, args, (from, to, nSample, noiseShaping) => { int fromPos = 0; int toPos = 0; switch (noiseShaping) { case NoiseShapingType.None: for (int i = 0; i < nSample; ++i) { float fv = System.BitConverter.ToSingle(from, fromPos); if (SAMPLE_VALUE_MAX_FLOAT <= fv) { fv = SAMPLE_VALUE_MAX_FLOAT_TO_I16; IncrementClippedCounter(); } if (fv < SAMPLE_VALUE_MIN_FLOAT) { fv = SAMPLE_VALUE_MIN_FLOAT; IncrementClippedCounter(); } int iv = (int)(fv * 32768.0f); to[toPos++] = (byte)(iv & 0xff); to[toPos++] = (byte)((iv >> 8) & 0xff); fromPos += 4; } break; case NoiseShapingType.AddDither: { for (int i = 0; i < nSample; ++i) { float fv = System.BitConverter.ToSingle(from, fromPos); if (SAMPLE_VALUE_MAX_FLOAT <= fv) { fv = SAMPLE_VALUE_MAX_FLOAT_TO_I16; IncrementClippedCounter(); } if (fv < SAMPLE_VALUE_MIN_FLOAT) { fv = SAMPLE_VALUE_MIN_FLOAT; IncrementClippedCounter(); } long iv = (long)(fv * Int32.MaxValue); // TPDF int dither = (int)((mRand.NextDouble() + mRand.NextDouble() - 0.5) * 65536); iv += dither; int vOut; if (Int32.MaxValue < iv) { vOut = Int32.MaxValue; } else if (iv < Int32.MinValue) { vOut = Int32.MinValue; } else { vOut = (int)iv; } to[toPos++] = (byte)((vOut >> 16) & 0xff); to[toPos++] = (byte)((vOut >> 24) & 0xff); fromPos += 4; } args.noiseShapingOrDitherPerformed = true; } break; case NoiseShapingType.NoiseShaping1stOrder: { long nFrame = nSample / pcmFrom.NumChannels; System.Diagnostics.Debug.Assert(mErr != null && mErr.Length == pcmFrom.NumChannels); for (int i = 0; i < nFrame; ++i) { for (int ch=0; ch < pcmFrom.NumChannels; ++ch) { float fv = System.BitConverter.ToSingle(from, fromPos); if (SAMPLE_VALUE_MAX_FLOAT <= fv) { fv = SAMPLE_VALUE_MAX_FLOAT_TO_I16; IncrementClippedCounter(); } if (fv < SAMPLE_VALUE_MIN_FLOAT) { fv = SAMPLE_VALUE_MIN_FLOAT; IncrementClippedCounter(); } long lv = (long)((double)fv * ((long)Int32.MaxValue + 1L) + mErr[ch]); int iv; if (Int32.MaxValue < lv) { iv = Int32.MaxValue; } else if (lv < Int32.MinValue) { iv = Int32.MinValue; } else { iv = (int)lv; } iv = (int)(iv & 0xffff0000); mErr[ch] = (int)(lv - iv); to[toPos++] = (byte)((iv >> 16) & 0xff); to[toPos++] = (byte)((iv >> 24) & 0xff); fromPos += 4; } } args.noiseShapingOrDitherPerformed = true; } break; case NoiseShapingType.DitheredNoiseShaping1stOrder: { long nFrame = nSample / pcmFrom.NumChannels; System.Diagnostics.Debug.Assert(mErr != null && mErr.Length == pcmFrom.NumChannels); for (int i = 0; i < nFrame; ++i) { for (int ch=0; ch < pcmFrom.NumChannels; ++ch) { float fv = System.BitConverter.ToSingle(from, fromPos); if (SAMPLE_VALUE_MAX_FLOAT <= fv) { fv = SAMPLE_VALUE_MAX_FLOAT_TO_I16; IncrementClippedCounter(); } if (fv < SAMPLE_VALUE_MIN_FLOAT) { fv = SAMPLE_VALUE_MIN_FLOAT; IncrementClippedCounter(); } // RPDF double dither = (mRand.NextDouble() - 0.5) * 65536 / 4; long lv = (long)((double)fv * ((long)Int32.MaxValue + 1L) + mErr[ch] + dither); int iv; if (Int32.MaxValue < lv) { iv = Int32.MaxValue; } else if (lv < Int32.MinValue) { iv = Int32.MinValue; } else { iv = (int)lv; } iv = (int)(iv & 0xffff0000); mErr[ch] = (int)(lv - iv); to[toPos++] = (byte)((iv >> 16) & 0xff); to[toPos++] = (byte)((iv >> 24) & 0xff); fromPos += 4; } } args.noiseShapingOrDitherPerformed = true; } break; } }); }
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 = mSampleLargeArray.LongLength / (BitsPerFrame / 8); var newSampleArray = new WWUtil.LargeArray <byte>((long)newCh * bytesPerSample * sampleFrames); for (long frame = 0; frame < sampleFrames; ++frame) { int copyBytes = NumChannels * bytesPerSample; if (newCh < NumChannels) { // チャンネル数が減る場合。 copyBytes = newCh * bytesPerSample; } newSampleArray.CopyFrom(mSampleLargeArray, (long)NumChannels * bytesPerSample * frame, (long)newCh * bytesPerSample * frame, copyBytes); if (SampleDataType == DataType.DoP && NumChannels < newCh) { // 追加したチャンネルにDSD無音をセットする。 switch (bytesPerSample) { case 3: for (int ch = NumChannels; ch < newCh; ++ch) { newSampleArray.Set((frame * newCh + ch) * bytesPerSample + 0, 0x69); newSampleArray.Set((frame * newCh + ch) * bytesPerSample + 1, 0x69); newSampleArray.Set((frame * newCh + ch) * bytesPerSample + 2, (byte)((frame & 1) == 1 ? 0xfa : 0x05)); } break; case 4: for (int ch = NumChannels; ch < newCh; ++ch) { newSampleArray.Set((frame * newCh + ch) * bytesPerSample + 1, 0x69); newSampleArray.Set((frame * newCh + ch) * bytesPerSample + 2, 0x69); newSampleArray.Set((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.SetSampleLargeArray(newSampleArray); return(newPcmData); }
private byte[] ConvF32toI32(PcmData pcmFrom, WasapiCS.SampleFormatType toFormat, BitsPerSampleConvArgs args) { return ConvCommon(pcmFrom, toFormat, args, (from, to, nSample, noiseShaping) => { int fromPos = 0; int toPos = 0; for (int i = 0; i < nSample; ++i) { float fv = System.BitConverter.ToSingle(from, fromPos); if (SAMPLE_VALUE_MAX_FLOAT <= fv) { fv = SAMPLE_VALUE_MAX_FLOAT_TO_I24; IncrementClippedCounter(); } if (fv < SAMPLE_VALUE_MIN_FLOAT) { fv = SAMPLE_VALUE_MIN_FLOAT; IncrementClippedCounter(); } int iv = (int)(fv * 8388608.0f); to[toPos++] = 0; to[toPos++] = (byte)(iv & 0xff); to[toPos++] = (byte)((iv >> 8) & 0xff); to[toPos++] = (byte)((iv >> 16) & 0xff); fromPos += 4; } }); }
/// <summary> /// ヘッダー情報、サンプルデータ領域をクローンする。 /// </summary> public void CopyFrom(PcmData rhs) { CopyHeaderInfoFrom(rhs); mSampleArray = null; if (rhs.mSampleArray != null) { mSampleArray = (byte[])rhs.mSampleArray.Clone(); } }
private byte[] ConvF64toF32(PcmData pcmFrom, WasapiCS.SampleFormatType toFormat, BitsPerSampleConvArgs args) { return ConvCommon(pcmFrom, toFormat, args, (from, to, nSample, noiseShaping) => { int fromPos = 0; int toPos = 0; for (int i = 0; i < nSample; ++i) { double dv = System.BitConverter.ToDouble(from, fromPos); float fv = (float)dv; if (SAMPLE_VALUE_MAX_FLOAT <= fv) { fv = SAMPLE_VALUE_MAX_FLOAT_TO_I24; IncrementClippedCounter(); } if (fv < SAMPLE_VALUE_MIN_FLOAT) { fv = SAMPLE_VALUE_MIN_FLOAT; IncrementClippedCounter(); } byte [] b = System.BitConverter.GetBytes(fv); to[toPos++] = b[0]; to[toPos++] = b[1]; to[toPos++] = b[2]; to[toPos++] = b[3]; fromPos += 8; } }); }
private byte[] ConvF32toF64(PcmData pcmFrom, WasapiCS.SampleFormatType toFormat, BitsPerSampleConvArgs args) { return ConvCommon(pcmFrom, toFormat, args, (from, to, nSample, noiseShaping) => { int fromPos = 0; int toPos = 0; for (int i = 0; i < nSample; ++i) { float fv = System.BitConverter.ToSingle(from, fromPos); double dv = (double)fv; byte [] b = System.BitConverter.GetBytes(dv); for (int j=0; j < 8; ++j) { to[toPos++] = b[j]; } fromPos += 4; } }); }
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; }