static void UpdateMetadata(NativeMetadataIterator iterator, NativeVorbisCommentBlock newComments, NativePictureBlock newPicture) { Contract.Requires(iterator != null); Contract.Requires(newComments != null); var metadataInserted = false; var pictureInserted = false; do { switch ((MetadataType)Marshal.ReadInt32(iterator.GetBlock())) { // Replace the existing Vorbis comment: case MetadataType.VorbisComment: if (!iterator.DeleteBlock(false)) { throw new IOException(Resources.MetadataEncoderDeleteError); } if (!iterator.InsertBlockAfter(newComments.Handle)) { throw new IOException(Resources.MetadataEncoderInsertBlockError); } metadataInserted = true; break; // Replace the existing Picture block: case MetadataType.Picture: if (!iterator.DeleteBlock(false)) { throw new IOException(Resources.MetadataEncoderDeleteError); } if (newPicture != null && !iterator.InsertBlockAfter(newPicture.Handle)) { throw new IOException(Resources.MetadataEncoderInsertBlockError); } pictureInserted = true; break; // Delete any padding: case MetadataType.Padding: if (!iterator.DeleteBlock(false)) { throw new IOException(Resources.MetadataEncoderDeleteError); } break; } } while (iterator.Next()); // If there was no existing metadata block to replace, insert it now: if (!metadataInserted && !iterator.InsertBlockAfter(newComments.Handle)) { throw new IOException(Resources.MetadataEncoderInsertBlockError); } // If there was no existing picture block to replace, and a new one is available, insert it now: if (newPicture != null && !pictureInserted && !iterator.InsertBlockAfter(newPicture.Handle)) { throw new IOException(Resources.MetadataEncoderInsertBlockError); } newComments.ReleaseHandleOwnership(); newPicture?.ReleaseHandleOwnership(); }
public void WriteMetadata(Stream stream, MetadataDictionary metadata, SettingsDictionary settings) { bool usePadding; if (string.IsNullOrEmpty(settings["UsePadding"])) { usePadding = false; } else if (!bool.TryParse(settings["UsePadding"], out usePadding)) { throw new InvalidSettingException(string.Format(CultureInfo.CurrentCulture, Resources.MetadataEncoderBadUsePadding, settings["UsePadding"])); } NativeVorbisCommentBlock vorbisCommentBlock = null; NativePictureBlock pictureBlock = null; try { using (var chain = new NativeMetadataChain(stream)) { if (!chain.Read()) { MetadataChainStatus chainStatus = chain.GetStatus(); if (chainStatus == MetadataChainStatus.NotAFlacFile) { throw new UnsupportedAudioException(Resources.MetadataEncoderNotFlacError); } else { throw new IOException(string.Format(CultureInfo.CurrentCulture, Resources.MetadataEncoderReadError, chainStatus)); } } // Create a Vorbis Comment block, even if empty: vorbisCommentBlock = new NativeVorbisCommentBlock(); foreach (var item in new MetadataToVorbisCommentAdapter(metadata)) { vorbisCommentBlock.Append(item.Key, item.Value); } // Create a Picture block if cover art is available: if (metadata.CoverArt != null) { pictureBlock = new CoverArtToPictureBlockAdapter(metadata.CoverArt); } // Iterate over the existing blocks, replacing and deleting as needed: using (var iterator = new NativeMetadataIterator(chain.Handle)) UpdateMetadata(iterator, vorbisCommentBlock, pictureBlock); if (chain.CheckIfTempFileNeeded(usePadding)) { // If FLAC requests a temporary file, use a MemoryStream instead. Then overwrite the original: using (var tempStream = new MemoryStream()) { if (!chain.WriteWithTempFile(usePadding, tempStream)) { throw new IOException(string.Format(CultureInfo.CurrentCulture, Resources.MetadataEncoderTempFileError, chain.GetStatus())); } // Clear the original stream, and copy the temporary one over it: stream.SetLength(tempStream.Length); stream.Position = 0; tempStream.WriteTo(stream); } } else if (!chain.Write(usePadding)) { throw new IOException(string.Format(CultureInfo.CurrentCulture, Resources.MetadataEncoderWriteError, chain.GetStatus())); } } } finally { vorbisCommentBlock?.Dispose(); pictureBlock?.Dispose(); } }
public void Initialize(Stream stream, AudioInfo audioInfo, MetadataDictionary metadata, SettingsDictionary settings) { Contract.Ensures(_encoder != null); Contract.Ensures(_encoder.GetState() == EncoderState.Ok); Contract.Ensures(_multiplier > 0); _encoder = InitializeEncoder(audioInfo, stream); _metadataBlocks = new List <NativeMetadataBlock>(3); // Assumes one metadata block, one picture and one seek table _multiplier = (float)Math.Pow(2, audioInfo.BitsPerSample - 1); uint compressionLevel; if (string.IsNullOrEmpty(settings["CompressionLevel"])) { compressionLevel = 5; } else if (!uint.TryParse(settings["CompressionLevel"], out compressionLevel) || compressionLevel > 8) { throw new InvalidSettingException(string.Format(CultureInfo.CurrentCulture, Resources.SampleEncoderBadCompressionLevel, settings["CompressionLevel"])); } _encoder.SetCompressionLevel(compressionLevel); var vorbisCommentBlock = new NativeVorbisCommentBlock(); foreach (var field in new MetadataToVorbisCommentAdapter(metadata)) { vorbisCommentBlock.Append(field.Key, field.Value); } _metadataBlocks.Add(vorbisCommentBlock); // Add a seek table, unless SeekPointInterval = 0: uint seekPointInterval; if (string.IsNullOrEmpty(settings["SeekPointInterval"])) { seekPointInterval = 10; } else if (!uint.TryParse(settings["SeekPointInterval"], out seekPointInterval)) { throw new InvalidSettingException(string.Format(CultureInfo.CurrentCulture, Resources.SampleEncoderBadSeekPointInterval, settings["SeekPointInterval"])); } if (seekPointInterval > 0) { _metadataBlocks.Add(new NativeSeekTableBlock( (int)Math.Ceiling(audioInfo.SampleCount / (double)audioInfo.SampleRate / seekPointInterval), audioInfo.SampleCount)); } // Add a picture block, if cover art is available: if (metadata.CoverArt != null) { _metadataBlocks.Add(new CoverArtToPictureBlockAdapter(metadata.CoverArt)); } _encoder.SetMetadata(_metadataBlocks); EncoderInitStatus initStatus = _encoder.Initialize(); if (initStatus != EncoderInitStatus.Ok) { throw new IOException(string.Format(CultureInfo.CurrentCulture, Resources.SampleEncoderInitializationError, initStatus)); } }