public bool Set( int numChannels, int bitsPerSample, int validBitsPerSample, int sampleRate, PcmDataLib.PcmData.ValueRepresentationType sampleValueRepresentation, long numFrames, byte[] sampleArray) { mRcd = new RiffChunkDescriptor(); if (0xffffffffL < sampleArray.LongLength + 36) { System.Diagnostics.Debug.Assert(false); return false; } mRcd.Create((uint)(36 + sampleArray.LongLength)); mFsc = new FmtSubChunk(); mFsc.Create(numChannels, sampleRate, bitsPerSample, validBitsPerSample, sampleValueRepresentation); var dsc = new WavDataSubChunk(); dsc.Create(numFrames, sampleArray); mDscList.Clear(); mDscList.Add(dsc); return true; }
/// <summary> /// writes PCM data onto WAV file /// </summary> public bool Write(BinaryWriter bw, int numChannels, int bitsPerSample, int validBitsPerSample, int sampleRate, PcmDataLib.PcmData.ValueRepresentationType sampleValueRepresentation, long numFrames, byte[] sampleArray) { if (0xffffffffL < sampleArray.LongLength + 36) { System.Diagnostics.Debug.Assert(false); return false; } int dwChannelMask = 0; if (numChannels == 2) { dwChannelMask = 3; } mLL.RiffChunkWrite(bw, (int)(36 + sampleArray.LongLength)); mLL.FmtChunkWriteExtensible(bw, (short)numChannels, (int)sampleRate, (short)bitsPerSample, (short)validBitsPerSample, sampleValueRepresentation, dwChannelMask); mLL.DataChunkWrite(bw, false, sampleArray); return true; }
public void FmtChunkWriteExtensible(BinaryWriter bw, short numChannels, int sampleRate, short bitsPerSample, short validBitsPerSample, PcmDataLib.PcmData.ValueRepresentationType sampleValueRepresentation, int dwChannelMask) { FmtChunkWriteInternal(bw, numChannels, sampleRate, bitsPerSample, 40, WAVE_FORMAT_EXTENSIBLE); ushort cbSize = 22; bw.Write(cbSize); bw.Write(validBitsPerSample); bw.Write(dwChannelMask); byte[] guidByteArray = null; if (sampleValueRepresentation == PcmDataLib.PcmData.ValueRepresentationType.SInt) { var pcmGuid = Guid.Parse("00000001-0000-0010-8000-00aa00389b71"); guidByteArray = pcmGuid.ToByteArray(); } else if (sampleValueRepresentation == PcmDataLib.PcmData.ValueRepresentationType.SFloat) { var floatGuid = Guid.Parse("00000003-0000-0010-8000-00aa00389b71"); guidByteArray = floatGuid.ToByteArray(); } else { throw new ArgumentException("sampleValueRepresentation"); } bw.Write(guidByteArray); }
public int ReadHeader(string flacFilePath, out PcmDataLib.PcmData pcmData_return, out List<FlacCuesheetTrackInfo> cueSheet_return) { int rv = ReadStartCommon(ReadMode.Header, flacFilePath, 0, 0, out pcmData_return, out cueSheet_return); StopChildProcess(); if (rv != 0) { return rv; } return 0; }
public ResultType ReadStreamBegin(BinaryReader br, out PcmDataLib.PcmData pcmData) { ResultType rt = ResultType.Success; rt = ReadHeader1(br, out pcmData, ReadHeaderMode.ReadStopBeforeSoundData); mPosFrame = 0; return rt; }
private ResultType ReadHeader1(BinaryReader br, out PcmDataLib.PcmData pcmData, ReadHeaderMode mode) { pcmData = new PcmDataLib.PcmData(); ResultType result = ReadDsfChunk(br); if (result != ResultType.Success) { return result; } result = ReadFmtChunk(br); if (ResultType.Success != result) { return result; } result = ReadDataChunkHeader(br); if (ResultType.Success != result) { return result; } // 読み込めるデータのフレーム数DataFramesと出力するデータのフレーム数OutputFrames。 // PCMデータのフレーム数OutputFramesは偶数個にする。 OutputFrames = mDataFrames; if (0 != (1 & OutputFrames)) { // OutputFrames must be even number ++OutputFrames; } pcmData.SetFormat( NumChannels, 24, 24, SampleRate/16, PcmDataLib.PcmData.ValueRepresentationType.SInt, OutputFrames); pcmData.SampleDataType = PcmDataLib.PcmData.DataType.DoP; if (mode == ReadHeaderMode.AllHeadersWithID3 && mMetadataOffset != 0) { PcmDataLib.Util.BinaryReaderSkip(br, (long)mMetadataOffset - STREAM_DATA_OFFSET); result = ReadID3Chunk(br); if (ResultType.Success == result) { // ID3読み込み成功 pcmData.DisplayName = TitleName; pcmData.AlbumTitle = AlbumName; pcmData.ArtistName = ArtistName; if (0 < PictureBytes) { pcmData.SetPicture(PictureBytes, PictureData); } } } return 0; }
private ResultType ReadHeader1(BinaryReader br, out PcmDataLib.PcmData pcmData, ReadHeaderMode mode) { pcmData = new PcmDataLib.PcmData(); ResultType result = ReadFormChunkHeader(br); if (result != ResultType.Success) { return result; } if (mIsAIFC) { // AIFCの場合、FVERチャンクが来る(required) result = ReadFverChunk(br); if (ResultType.Success != result) { return result; } } result = ReadCommonChunk(br); if (ResultType.Success != result) { return result; } result = ReadSoundDataChunk(br, mode); if (ResultType.Success != result) { return result; } if (16 != BitsPerSample && 24 != BitsPerSample) { return ResultType.NotSupportBitsPerSample; } pcmData.SetFormat( NumChannels, BitsPerSample, BitsPerSample, SampleRate, PcmDataLib.PcmData.ValueRepresentationType.SInt, NumFrames); if (mode == ReadHeaderMode.AllHeadersWithID3) { result = ReadID3Chunk(br); if (ResultType.Success == result) { // ID3読み込み成功 pcmData.DisplayName = TitleName; pcmData.AlbumTitle = AlbumName; pcmData.ArtistName = ArtistName; if (0 < PictureBytes) { pcmData.SetPicture(PictureBytes, PictureData); } } } return 0; }
public ResultType ReadHeader(BinaryReader br, out PcmDataLib.PcmData pcmData) { return ReadHeader1(br, out pcmData, ReadHeaderMode.AllHeadersWithID3); }
private ResultType ReadHeader1(BinaryReader br, ReadHeaderMode mode, out PcmDataLib.PcmData pcmData) { pcmData = new PcmDataLib.PcmData(); bool done = false; try { while (!done) { ResultType rt = ResultType.Success; uint fourCC = br.ReadUInt32(); switch (fourCC) { case FOURCC_FRM8: rt = ReadDsdChunk(br); break; case FOURCC_FVER: rt = ReadFormVersionChunk(br); break; case FOURCC_PROP: rt = ReadPropertyChunk(br); break; case FOURCC_FS: rt = ReadSampleRateChunk(br); break; case FOURCC_CHNL: rt = ReadChannelsChunk(br); break; case FOURCC_CMPR: rt = ReadCompressionTypeChunk(br); break; case FOURCC_DSD: rt = ReadSoundDataChunkHeader(br, mode); switch (mode) { case ReadHeaderMode.ReadStopBeforeSoundData: done = true; break; case ReadHeaderMode.AllHeadersWithID3: break; } break; case FOURCC_ID3: rt = ReadID3Chunk(br); break; default: rt = SkipUnknownChunk(br); break; } if (rt != ResultType.Success) { return rt; } } } catch (EndOfStreamException ex) { // this is only way to exit from the while loop above System.Console.WriteLine(ex); } if (0 == SampleRate || // SampleRateChunkが存在しないとき。 2 != NumChannels || 0 == mDataFrames) { return ResultType.ReadError; } // 読み込めるデータのフレーム数DataFramesと出力するデータのフレーム数OutputFrames。 // PCMデータのフレーム数OutputFramesは偶数個にする。 OutputFrames = mDataFrames; if (0 != (1 & OutputFrames)) { // OutputFrames must be even number ++OutputFrames; } pcmData.SetFormat( NumChannels, 24, 24, SampleRate/16, PcmDataLib.PcmData.ValueRepresentationType.SInt, OutputFrames); pcmData.SampleDataType = PcmDataLib.PcmData.DataType.DoP; if (null != TitleName) { pcmData.DisplayName = TitleName; } if (null != AlbumName) { pcmData.AlbumTitle = AlbumName; } if (null != ArtistName) { pcmData.ArtistName = ArtistName; } if (0 < PictureBytes) { pcmData.SetPicture(PictureBytes, PictureData); } return 0; }
/// <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; }
public PlayListItemInfo(PcmDataLib.PcmData pcmData) { mPcmData = pcmData; mRowId = mNextRowId++; }
public bool Create( int numChannels, int sampleRate, int bitsPerSample, int validBitsPerSample, PcmDataLib.PcmData.ValueRepresentationType sampleValueRepresentation) { m_subChunk1Id = new byte[4]; m_subChunk1Id[0] = (byte)'f'; m_subChunk1Id[1] = (byte)'m'; m_subChunk1Id[2] = (byte)'t'; m_subChunk1Id[3] = (byte)' '; m_subChunk1Size = 16; m_audioFormat = 1; System.Diagnostics.Debug.Assert(0 < numChannels); NumChannels = (ushort)numChannels; SampleRate = (uint)sampleRate; m_byteRate = (uint)(sampleRate * numChannels * bitsPerSample / 8); m_blockAlign = (ushort)(numChannels * bitsPerSample / 8); BitsPerSample = (ushort)bitsPerSample; ValidBitsPerSample = (ushort)validBitsPerSample; ChannelMask = 0; SampleValueRepresentationType = sampleValueRepresentation; if (sampleValueRepresentation == PcmDataLib.PcmData.ValueRepresentationType.SInt) { m_audioFormat = 1; } else if (sampleValueRepresentation == PcmDataLib.PcmData.ValueRepresentationType.SFloat) { m_audioFormat = 3; } else { System.Diagnostics.Debug.Assert(false); } return true; }
/// <summary> /// PcmDataの形式と、(共有・排他)、フォーマット固定設定から、 /// デバイスに設定されるビットフォーマットを取得。 /// /// これは、内容的にテーブルなので、テーブルにまとめたほうが良い。 /// </summary> /// <returns>デバイスに設定されるビットフォーマット</returns> public static SampleFormatInfo CreateSetupSampleFormat( WasapiSharedOrExclusiveType sharedOrExclusive, BitsPerSampleFixType bitsPerSampleFixType, int bitsPerSample, int validBitsPerSample, PcmDataLib.PcmData.ValueRepresentationType vrt, int candidateId) { SampleFormatInfo sf = new SampleFormatInfo(); if (sharedOrExclusive == WasapiSharedOrExclusiveType.Shared) { // 共有モード sf.bitsPerSample = bitsPerSample; sf.validBitsPerSample = validBitsPerSample; sf.bitFormatType = SampleFormatInfo.VrtToBft(vrt); return sf; } // 排他モード switch (bitsPerSampleFixType) { case BitsPerSampleFixType.Sint16: sf.bitFormatType = WasapiCS.BitFormatType.SInt; sf.bitsPerSample = 16; sf.validBitsPerSample = 16; break; case BitsPerSampleFixType.Sint24: sf.bitFormatType = WasapiCS.BitFormatType.SInt; sf.bitsPerSample = 24; sf.validBitsPerSample = 24; break; case BitsPerSampleFixType.Sint32: sf.bitFormatType = WasapiCS.BitFormatType.SInt; sf.bitsPerSample = 32; sf.validBitsPerSample = 32; break; case BitsPerSampleFixType.Sint32V24: sf.bitFormatType = WasapiCS.BitFormatType.SInt; sf.bitsPerSample = 32; sf.validBitsPerSample = 24; break; case BitsPerSampleFixType.Sfloat32: sf.bitFormatType = WasapiCS.BitFormatType.SFloat; sf.bitsPerSample = 32; sf.validBitsPerSample = 32; break; case BitsPerSampleFixType.AutoSelect: WasapiCS.SampleFormatType sampleFormat = WasapiCS.SampleFormatType.Sint16; switch (validBitsPerSample) { case 16: sampleFormat = mTryFormat16[candidateId]; break; case 24: default: /* ? */ sampleFormat = mTryFormat24[candidateId]; break; case 32: sampleFormat = mTryFormat32[candidateId]; break; } sf.bitFormatType = WasapiCS.BitFormatType.SInt; sf.bitsPerSample = WasapiCS.SampleFormatTypeToUseBitsPerSample(sampleFormat); sf.validBitsPerSample = WasapiCS.SampleFormatTypeToValidBitsPerSample(sampleFormat); break; default: System.Diagnostics.Debug.Assert(false); break; } return sf; }
/// <summary> /// フォーマット設定から、 /// Setup()に設定されうるビットフォーマットの候補の数を数えて戻す。 /// </summary> /// <returns>Setup()に設定されうるビットフォーマットの候補の数</returns> public static int GetSetupSampleFormatCandidateNum( WasapiSharedOrExclusiveType sharedOrExclusive, BitsPerSampleFixType bitsPerSampleFixType, int validBitsPerSample, PcmDataLib.PcmData.ValueRepresentationType vrt) { if (bitsPerSampleFixType != BitsPerSampleFixType.AutoSelect || sharedOrExclusive == WasapiSharedOrExclusiveType.Shared) { // 共有モードの場合 1通り // 排他モードで自動選択以外の選択肢の場合 1通り return 1; } // 排他モードのAutoSelect switch (validBitsPerSample) { case 16: return mTryFormat16.Length; case 24: default: return mTryFormat24.Length; case 32: return mTryFormat32.Length; } }
/// <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) { 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; } } bool readSeparatorAfter = false; if (mPlis != null) { // PPWプレイリストの情報で上書きする pcmData.DisplayName = mPlis.Title; pcmData.AlbumTitle = mPlis.AlbumName; pcmData.ArtistName = mPlis.ArtistName; pcmData.StartTick = mPlis.StartTick; pcmData.EndTick = mPlis.EndTick; pcmData.CueSheetIndex = mPlis.CueSheetIndex; readSeparatorAfter = mPlis.ReadSeparaterAfter; } // カバーアート画像を追加する AddCoverart(path, pcmData); mAddPcmData(pcmData, readSeparatorAfter, mPlis != null); return true; }
/// <summary> /// FLACファイルからPCMデータを取り出し開始。 /// </summary> /// <param name="flacFilePath">読み込むファイルパス。</param> /// <param name="skipFrames">ファイルの先頭からのスキップするフレーム数。0以外の値を指定するとMD5のチェックが行われなくなる。</param> /// <param name="wantFrames">取得するフレーム数。</param> /// <param name="pcmData">出てきたデコード後のPCMデータ。</param> /// <returns>0: 成功。負: 失敗。</returns> public int ReadStreamBegin(string flacFilePath, long skipFrames, long wantFrames, int typicalReadFrames, out PcmDataLib.PcmData pcmData_return) { List<FlacCuesheetTrackInfo> cti; int rv = ReadStartCommon(ReadMode.HeadereAndData, flacFilePath, skipFrames, wantFrames, out pcmData_return, out cti); if (rv != 0) { StopChildProcess(); mBytesPerFrame = 0; return rv; } mBytesPerFrame = pcmData_return.BitsPerFrame / 8; if (CalcMD5 && skipFrames == 0 && wantFrames == mNumFrames) { md5 = new MD5CryptoServiceProvider(); mMD5SumOfPcm = new byte[MD5_BYTES]; mMD5TmpBuffer = new byte[mBytesPerFrame * typicalReadFrames]; } return 0; }
private int WasapiSetup( int deviceId, bool isExclusive, bool isEventDriven, int sampleRate, int pcmDataBitsPerSample, int pcmDataValidBitsPerSample, PcmDataLib.PcmData.ValueRepresentationType vrt, int latencyMillisec) { int num = SampleFormatInfo.GetSetupSampleFormatCandidateNum( isExclusive ? WasapiSharedOrExclusiveType.Exclusive : WasapiSharedOrExclusiveType.Shared, BitsPerSampleFixType.AutoSelect, pcmDataValidBitsPerSample, vrt); int hr = -1; for (int i = 0; i < num; ++i) { SampleFormatInfo sf = SampleFormatInfo.CreateSetupSampleFormat( isExclusive ? WasapiSharedOrExclusiveType.Exclusive : WasapiSharedOrExclusiveType.Shared, BitsPerSampleFixType.AutoSelect, pcmDataBitsPerSample, pcmDataValidBitsPerSample, vrt, i); hr = wasapi.Setup(deviceId, WasapiCS.DeviceType.Play, WasapiCS.StreamType.PCM, sampleRate, sf.GetSampleFormatType(), 2, WasapiCS.MMCSSCallType.Enable, WasapiCS.MMThreadPriorityType.None, WasapiCS.SchedulerTaskType.ProAudio, WasapiCS.ShareMode.Exclusive, isEventDriven ? WasapiCS.DataFeedMode.EventDriven : WasapiCS.DataFeedMode.TimerDriven, latencyMillisec, 500, 10000); if (0 <= hr) { m_sampleFormat = sf; return hr; } } wasapi.Unsetup(); return hr; }
private int ReadStartCommon(ReadMode mode, string flacFilePath, long skipFrames, long wantFrames, out PcmDataLib.PcmData pcmData_return, out List<FlacCuesheetTrackInfo> cueSheet_return) { pcmData_return = new PcmDataLib.PcmData(); cueSheet_return = new List<FlacCuesheetTrackInfo>(); StartChildProcess(); switch (mode) { case ReadMode.Header: SendString("H"); SendBase64(flacFilePath); break; case ReadMode.HeadereAndData: SendString("A"); SendBase64(flacFilePath); SendString(skipFrames.ToString(CultureInfo.InvariantCulture)); SendString(wantFrames.ToString(CultureInfo.InvariantCulture)); break; default: System.Diagnostics.Debug.Assert(false); break; } int rv = mBinaryReader.ReadInt32(); if (rv != 0) { return rv; } int nChannels = mBinaryReader.ReadInt32(); int bitsPerSample = mBinaryReader.ReadInt32(); int sampleRate = mBinaryReader.ReadInt32(); mNumFrames = mBinaryReader.ReadInt64(); int numFramesPerBlock = mBinaryReader.ReadInt32(); string titleStr = mBinaryReader.ReadString(); string albumStr = mBinaryReader.ReadString(); string artistStr = mBinaryReader.ReadString(); byte md5Available = mBinaryReader.ReadByte(); md5MetaAvailable = md5Available != 0; mMD5SumInMetadata = mBinaryReader.ReadBytes(MD5_BYTES); mPictureBytes = mBinaryReader.ReadInt32(); mPictureData = new byte[0]; if (0 < mPictureBytes) { mPictureData = mBinaryReader.ReadBytes(mPictureBytes); } { int numCuesheetTracks = mBinaryReader.ReadInt32(); for (int trackId=0; trackId < numCuesheetTracks; ++trackId) { var cti = new FlacCuesheetTrackInfo(); cti.trackNr = mBinaryReader.ReadInt32(); cti.offsetSamples = mBinaryReader.ReadInt64(); int numCuesheetTrackIndices = mBinaryReader.ReadInt32(); for (int indexId=0; indexId < numCuesheetTrackIndices; ++indexId) { var indexInfo = new FlacCuesheetTrackIndexInfo(); indexInfo.indexNr = mBinaryReader.ReadInt32(); indexInfo.offsetSamples = mBinaryReader.ReadInt64(); cti.indices.Add(indexInfo); } cueSheet_return.Add(cti); } } pcmData_return.SetFormat( nChannels, bitsPerSample, bitsPerSample, sampleRate, PcmDataLib.PcmData.ValueRepresentationType.SInt, mNumFrames); pcmData_return.DisplayName = titleStr; pcmData_return.AlbumTitle = albumStr; pcmData_return.ArtistName = artistStr; pcmData_return.SetPicture(mPictureBytes, mPictureData); return 0; }
public bool ReadStreamBegin(BinaryReader br, out PcmDataLib.PcmData pcmData) { if (!ReadHeader(br)) { pcmData = new PcmDataLib.PcmData(); return false; } pcmData = new PcmDataLib.PcmData(); pcmData.SetFormat(NumChannels, BitsPerSample, BitsPerSample, (int)SampleRate, SampleValueRepresentationType, NumFrames); mCurrentDsc = -1; mDscPosFrame = 0; // 最初のDSCまでシークする。 return ReadStreamSkip(br, 0); }