/// <summary> /// Loads the first meta data block found on the stream (first byte is supposed to be the start of the meta data block)... the stream will be forwarded to the start /// of the next meta data block (or the start of the audio frames) /// </summary> /// <param name="data"></param> public static MetadataBlock Create(Stream data) { // Step 1: Get header byte[] headerData = new byte[4]; // always 4 bytes data.Read(headerData, 0, 4); MetadataBlockHeader header = new MetadataBlockHeader(headerData); // Step 2: Get instance of meta data block by type MetadataBlock metaDataBlock = GetInstanceByBlockType(header.Type); if (metaDataBlock != null) { metaDataBlock.header = header; // Step 3: Read block of meta data (according to header) byte[] blockData = new byte[header.MetaDataBlockLength]; data.Read(blockData, 0, (Int32)header.MetaDataBlockLength); // Step 4: Load the meta data into the meta data block instance metaDataBlock.LoadBlockData(blockData); } // Step 5: Return Meta Data Block... return(metaDataBlock); }
/// <summary> /// Parses all the metadata blocks available in the file. /// </summary> protected void ReadMetadata() { bool foundStreamInfo = false; MetadataBlock lastMetaDataBlock = null; do { lastMetaDataBlock = MetadataBlock.Create(this.dataStream); this.Metadata.Add(lastMetaDataBlock); switch (lastMetaDataBlock.Header.Type) { case MetadataBlockHeader.MetadataBlockType.StreamInfo: foundStreamInfo = true; this.streamInfo = (StreamInfo)lastMetaDataBlock; break; case MetadataBlockHeader.MetadataBlockType.Application: this.applicationInfo = (ApplicationInfo)lastMetaDataBlock; break; case MetadataBlockHeader.MetadataBlockType.CueSheet: this.cueSheet = (CueSheet)lastMetaDataBlock; break; case MetadataBlockHeader.MetadataBlockType.Seektable: this.seekTable = (SeekTable)lastMetaDataBlock; break; case MetadataBlockHeader.MetadataBlockType.VorbisComment: this.vorbisComment = (VorbisComment)lastMetaDataBlock; break; case MetadataBlockHeader.MetadataBlockType.Padding: this.padding = (Padding)lastMetaDataBlock; break; } } while (!lastMetaDataBlock.Header.IsLastMetaDataBlock); if (!foundStreamInfo) { throw new Exceptions.FlacLibSharpStreamInfoMissing(); } // Remember where the frame data starts frameStart = this.dataStream.Position; }
/// <summary> /// Writes the metadata back to the flacfile, overwriting the original file. /// </summary> public void Save() { if (String.IsNullOrEmpty(this.filePath) || !this.dataStream.CanSeek) { throw new FlacLibSharpSaveNotSupportedException(); } string bufferFile = Path.GetTempFileName(); using (var fs = new FileStream(bufferFile, FileMode.Create)) { // First write the magic flac bytes ... fs.Write(magicFlacMarker, 0, magicFlacMarker.Length); for (var i = 0; i < this.Metadata.Count; i++) { MetadataBlock block = this.Metadata[i]; // We have to make sure to set the last metadata bit correctly. if (i == this.Metadata.Count - 1) { block.Header.IsLastMetaDataBlock = true; } else { block.Header.IsLastMetaDataBlock = false; } try { var startLength = fs.Length; block.WriteBlockData(fs); var writtenBytes = fs.Length - startLength; // minus 4 bytes because the MetaDataBlockLength excludes the size of the header if (writtenBytes - 4 != block.Header.MetaDataBlockLength) { throw new Exception(String.Format("The header of metadata block of type {0} claims a length of {1} bytes but the total amount of data written was {2} + 4 bytes", block.Header.Type, block.Header.MetaDataBlockLength, writtenBytes)); } } catch (NotImplementedException) { // Ignore for now (testing) - we'll remove this handler later! } } // Metadata is written back to the new file stream, now we can copy the rest of the frames byte[] dataBuffer = new byte[4096]; this.dataStream.Seek(this.frameStart, SeekOrigin.Begin); int read = 0; do { read = this.dataStream.Read(dataBuffer, 0, dataBuffer.Length); fs.Write(dataBuffer, 0, read); } while (read > 0); } this.dataStream.Dispose(); // Issue #35: Cannot use "File.Move" because it does not retain the original file's attributes. // File.Copy does: https://docs.microsoft.com/en-us/dotnet/api/system.io.file.copy?view=netframework-4.7#System_IO_File_Copy_System_String_System_String_System_Boolean_ File.Copy(bufferFile, this.filePath, true); File.Delete(bufferFile); }