private static void ParsePicture(Stream fs, ref FlacInfo info) { var pictureType = fs.BEInt32(); var mimeStringLength = (int)fs.BEInt32(); var mimeType = Encoding.ASCII.GetString(fs.ReadBytes(mimeStringLength), 0, mimeStringLength); var descriptionLength = (int)fs.BEInt32(); var description = Encoding.UTF8.GetString(fs.ReadBytes(descriptionLength), 0, descriptionLength); var pictureWidth = fs.BEInt32(); var pictureHeight = fs.BEInt32(); var colorDepth = fs.BEInt32(); var indexedColorCount = fs.BEInt32(); var pictureDataLength = fs.BEInt32(); fs.Seek(pictureDataLength, SeekOrigin.Current); info.HasCover = true; if (pictureType > 20) { pictureType = 21; } Logger.Log($" | picture type: {PictureTypeName[pictureType]}"); Logger.Log($" | picture format type: {mimeType}"); if (descriptionLength > 0) { Logger.Log($" | description: {description}"); } Logger.Log($" | attribute: {pictureWidth}px*{pictureHeight}px@{colorDepth}-bit"); if (indexedColorCount != 0) { Logger.Log($" | indexed-color color: {indexedColorCount}"); } }
private static void ParseStreamInfo(Stream fs, ref FlacInfo info) { var minBlockSize = fs.BEInt16(); var maxBlockSize = fs.BEInt16(); var minFrameSize = fs.BEInt24(); var maxFrameSize = fs.BEInt24(); var buffer = fs.ReadBytes(8); var br = new BitReader(buffer); var sampleRate = br.GetBits(20); var channelCount = br.GetBits(3) + 1; var bitPerSample = br.GetBits(5) + 1; var totalSample = br.GetBits(36); var md5 = fs.ReadBytes(16); info.RawLength = channelCount * bitPerSample / 8 * totalSample; info.SampleRate = sampleRate; info.BitPerSample = bitPerSample; Logger.Log($" | minimum block size: {minBlockSize}, maximum block size: {maxBlockSize}"); Logger.Log($" | minimum frame size: {minFrameSize}, maximum frame size: {maxFrameSize}"); Logger.Log($" | Sample rate: {sampleRate}Hz, bits per sample: {bitPerSample}-bit"); Logger.Log($" | Channel count: {channelCount}"); var md5String = md5.Aggregate("", (current, item) => current + $"{item:X2}"); Logger.Log($" | MD5: {md5String}"); }
private static void ParseVorbisComment(Stream fs, ref FlacInfo info) { //only here in flac use little-endian var vendorLength = (int)fs.LEInt32(); var vendorRawStringData = fs.ReadBytes(vendorLength); var vendor = Encoding.UTF8.GetString(vendorRawStringData, 0, vendorLength); info.Encoder = vendor; Logger.Log($" | Vendor: {vendor}"); var userCommentListLength = fs.LEInt32(); for (var i = 0; i < userCommentListLength; ++i) { var commentLength = (int)fs.LEInt32(); var commentRawStringData = fs.ReadBytes(commentLength); var comment = Encoding.UTF8.GetString(commentRawStringData, 0, commentLength); var splitterIndex = comment.IndexOf('='); var key = comment.Substring(0, splitterIndex); var value = comment.Substring(splitterIndex + 1, comment.Length - 1 - splitterIndex); info.VorbisComment[key] = value; var summary = value.Length > 25 ? value.Substring(0, 25) + "..." : value; Logger.Log($" | [{key}] = '{summary.Replace('\n', ' ')}'"); } }
public static FlacInfo GetMetadataFromFlac(string flacPath) { Logger.Log(flacPath); using (var fs = File.OpenRead(flacPath)) { var info = new FlacInfo(); var id3Header = Encoding.ASCII.GetString(fs.ReadBytes(3), 0, 3); fs.Seek(0, SeekOrigin.Begin); if (id3Header == "ID3") { SkipID3Block(fs); Logger.Log(Logger.Level.Warning, $"{flacPath} 文件在头部包含ID3v2标签!"); } var flacBeginPosition = fs.Position; var header = Encoding.ASCII.GetString(fs.ReadBytes(4), 0, 4); if (header != "fLaC") { throw new InvalidDataException($"Except an flac but get an {header}" + $"{Environment.NewLine}File name: {Path.GetFileName(flacPath)}"); } //METADATA_BLOCK_HEADER //1-bit Last-metadata-block flag //7-bit BLOCK_TYPE //24-bit Length long metaLength = 4 /*header*/; while (fs.Position < fs.Length) { var blockHeader = fs.BEInt32(); var lastMetadataBlock = blockHeader >> 31 == 0x1; var blockType = (BlockType)((blockHeader >> 24) & 0x7f); var length = blockHeader & 0xffffff; var prePos = fs.Position; metaLength += length + 4 /*length of METADATA_BLOCK_HEADER*/; Logger.Log($"|+{blockType} with Length: {length}"); switch (blockType) { case BlockType.STREAMINFO: Debug.Assert(length == 34); ParseStreamInfo(fs, ref info); break; case BlockType.VORBIS_COMMENT: ParseVorbisComment(fs, ref info); break; case BlockType.PICTURE: ParsePicture(fs, ref info); break; case BlockType.PADDING: case BlockType.APPLICATION: case BlockType.SEEKTABLE: case BlockType.CUESHEET: fs.Seek(length, SeekOrigin.Current); break; default: throw new ArgumentOutOfRangeException($"Invalid BLOCK_TYPE: 0x{blockType:X}"); } Debug.Assert(fs.Position - prePos == length); if (lastMetadataBlock) { break; } } Debug.Assert(fs.Position == metaLength + flacBeginPosition); info.TrueLength = fs.Length - fs.Position; return(info); } }