static OggPacket GetCommentPacket(MetadataDictionary metadata) { Contract.Requires(metadata != null); var comment = new VorbisComment(); try { SafeNativeMethods.VorbisCommentInitialize(out comment); foreach (var item in new MetadataToVorbisCommentAdapter(metadata)) { // The key and value need to be marshaled as null-terminated UTF-8 strings: var keyBytes = new byte[Encoding.UTF8.GetByteCount(item.Key) + 1]; Encoding.UTF8.GetBytes(item.Key, 0, item.Key.Length, keyBytes, 0); var valueBytes = new byte[Encoding.UTF8.GetByteCount(item.Value) + 1]; Encoding.UTF8.GetBytes(item.Value, 0, item.Value.Length, valueBytes, 0); SafeNativeMethods.VorbisCommentAddTag(ref comment, keyBytes, valueBytes); } OggPacket result; if (SafeNativeMethods.VorbisCommentHeaderOut(ref comment, out result) != 0) { throw new IOException(Resources.MetadataEncoderHeaderOutError); } return(result); } finally { SafeNativeMethods.VorbisCommentClear(ref comment); } }
internal void HeaderOut(ref VorbisComment comment, out OggPacket first, out OggPacket second, out OggPacket third) { Result result = SafeNativeMethods.VorbisAnalysisHeaderOut(_dspState, ref comment, out first, out second, out third); if (result != Result.Ok) { throw new IOException(string.Format(CultureInfo.CurrentCulture, Resources.NativeVorbisEncoderHeaderOutError, result)); } }
internal void HeaderIn(ref VorbisComment comment, ref OggPacket packet) { Result result = SafeNativeMethods.VorbisSynthesisHeaderIn(_info, ref comment, ref packet); switch (result) { case Result.Ok: return; case Result.NotVorbisError: throw new UnsupportedAudioException(Resources.NativeVorbisDecoderNotVorbisError); default: throw new IOException(string.Format(CultureInfo.CurrentCulture, Resources.NativeVorbisDecoderHeaderInError, result)); } }
void WriteHeader(MetadataDictionary metadata, Stream stream) { Contract.Requires(metadata != null); Contract.Requires(stream != null); Contract.Requires(stream.CanWrite); var vorbisComment = new VorbisComment(); try { SafeNativeMethods.VorbisCommentInitialize(out vorbisComment); foreach (var item in new MetadataToVorbisCommentAdapter(metadata)) { // The key and value need to be marshaled as null-terminated UTF-8 strings: var keyBytes = new byte[Encoding.UTF8.GetByteCount(item.Key) + 1]; Encoding.UTF8.GetBytes(item.Key, 0, item.Key.Length, keyBytes, 0); var valueBytes = new byte[Encoding.UTF8.GetByteCount(item.Value) + 1]; Encoding.UTF8.GetBytes(item.Value, 0, item.Value.Length, valueBytes, 0); SafeNativeMethods.VorbisCommentAddTag(ref vorbisComment, keyBytes, valueBytes); } OggPacket first; OggPacket second; OggPacket third; _encoder.HeaderOut(ref vorbisComment, out first, out second, out third); _oggStream.PacketIn(ref first); _oggStream.PacketIn(ref second); _oggStream.PacketIn(ref third); } finally { SafeNativeMethods.VorbisCommentClear(ref vorbisComment); } OggPage page; while (_oggStream.Flush(out page)) { WritePage(page, stream); } }
internal VorbisCommentToMetadataAdapter(VorbisComment vorbisComment) { var commentLengths = new int[vorbisComment.Comments]; Marshal.Copy(vorbisComment.CommentLengths, commentLengths, 0, commentLengths.Length); var commentPtrs = new IntPtr[vorbisComment.Comments]; Marshal.Copy(vorbisComment.UserComments, commentPtrs, 0, commentPtrs.Length); for (var i = 0; i < vorbisComment.Comments; i++) { var commentBytes = new byte[commentLengths[i]]; Marshal.Copy(commentPtrs[i], commentBytes, 0, commentLengths[i]); string[] comment = Encoding.UTF8.GetString(commentBytes).Split(new[] { '=' }, 2); Contract.Assert(comment.Length == 2); // The track number and count may be packed into the same comment: switch (comment[0]) { case "TRACKNUMBER": string[] segments = comment[1].Split('/'); base["TrackNumber"] = segments[0]; if (segments.Length > 1) { base["TrackCount"] = segments[1]; } break; case "DATE": case "YEAR": // The DATE comment may contain a full date, or only the year: DateTime result; if (DateTime.TryParse(comment[1], CultureInfo.CurrentCulture, DateTimeStyles.NoCurrentDateDefault, out result) && result.Year >= 1000) { base["Day"] = result.Day.ToString(CultureInfo.InvariantCulture); base["Month"] = result.Month.ToString(CultureInfo.InvariantCulture); base["Year"] = result.Year.ToString(CultureInfo.InvariantCulture); } else { base["Year"] = comment[1]; } break; case "METADATA_BLOCK_PICTURE": var picture = new MetadataBlockPicture(comment[1]); if (picture.Type == PictureType.CoverFront || picture.Type == PictureType.Other) { try { CoverArt = new CoverArt(picture.Data); } catch (UnsupportedCoverArtException) { } } break; case "COVERART": try { // Deprecated way to store cover art: CoverArt = new CoverArt(Convert.FromBase64String(comment[1])); } catch (UnsupportedCoverArtException) { } break; default: string mappedKey; if (_map.TryGetValue(comment[0], out mappedKey)) { base[mappedKey] = comment[1]; } break; } } }
public AudioInfo ReadAudioInfo(Stream stream) { Contract.Ensures(Contract.Result <AudioInfo>() != null); var buffer = new byte[4096]; using (var decoder = new NativeVorbisDecoder()) { NativeOggStream oggStream = null; var vorbisComment = new VorbisComment(); try { SafeNativeMethods.VorbisCommentInitialize(out vorbisComment); using (var sync = new NativeOggSync()) { OggPage page; do { // Read from the buffer into a page: while (sync.PageOut(out page) != 1) { int bytesRead = stream.Read(buffer, 0, buffer.Length); if (bytesRead == 0) { throw new IOException(Resources.ReadError); } IntPtr nativeBuffer = sync.Buffer(bytesRead); Marshal.Copy(buffer, 0, nativeBuffer, bytesRead); sync.Wrote(bytesRead); } if (oggStream == null) { oggStream = new NativeOggStream(SafeNativeMethods.OggPageGetSerialNumber(ref page)); } oggStream.PageIn(ref page); OggPacket packet; while (oggStream.PacketOut(out packet) == 1) { decoder.HeaderIn(ref vorbisComment, ref packet); VorbisInfo info = decoder.GetInfo(); return(new AudioInfo(string.Format(CultureInfo.CurrentCulture, "{0}kbps Ogg Vorbis", info.BitrateNominal / 1000), info.Channels, 0, info.Rate, 0)); } } while (SafeNativeMethods.OggPageEndOfStream(ref page) == 0); throw new IOException(Resources.EndOfStreamError); } } finally { oggStream?.Dispose(); SafeNativeMethods.VorbisCommentClear(ref vorbisComment); } } }
internal static extern void VorbisCommentInitialize(out VorbisComment comment);
internal static extern Result VorbisAnalysisHeaderOut(IntPtr dspState, ref VorbisComment comment, out OggPacket first, out OggPacket second, out OggPacket third);
internal static extern Result VorbisSynthesisHeaderIn(IntPtr info, ref VorbisComment comment, ref OggPacket packet);
internal static extern void VorbisCommentClear(ref VorbisComment comment);
internal static extern int VorbisCommentHeaderOut(ref VorbisComment comment, out OggPacket packet);
internal static extern void VorbisCommentAddTag(ref VorbisComment comment, byte[] tag, byte[] contents);
public MetadataDictionary ReadMetadata(Stream stream) { Contract.Ensures(Contract.Result <MetadataDictionary>() != null); var buffer = new byte[4096]; using (var decoder = new NativeVorbisDecoder()) { NativeOggStream oggStream = null; var vorbisComment = new VorbisComment(); try { SafeNativeMethods.VorbisCommentInitialize(out vorbisComment); using (var sync = new NativeOggSync()) { OggPage page; do { // Read from the buffer into a page: while (sync.PageOut(out page) != 1) { int bytesRead = stream.Read(buffer, 0, buffer.Length); if (bytesRead == 0) { throw new IOException(Resources.ReadError); } IntPtr nativeBuffer = sync.Buffer(bytesRead); Marshal.Copy(buffer, 0, nativeBuffer, bytesRead); sync.Wrote(bytesRead); } if (oggStream == null) { oggStream = new NativeOggStream(SafeNativeMethods.OggPageGetSerialNumber(ref page)); } oggStream.PageIn(ref page); OggPacket packet; while (oggStream.PacketOut(out packet) == 1) { decoder.HeaderIn(ref vorbisComment, ref packet); if (packet.PacketNumber == 1) { return(new VorbisCommentToMetadataAdapter(vorbisComment)); } } } while (SafeNativeMethods.OggPageEndOfStream(ref page) == 0); throw new IOException(Resources.EndOfStreamError); } } finally { oggStream?.Dispose(); SafeNativeMethods.VorbisCommentClear(ref vorbisComment); } } }