From 894976b33170336be3a83a6bef64a9d6ae4ba63f Mon Sep 17 00:00:00 2001 From: Jay D Zimmerman Date: Fri, 31 Aug 2012 01:29:19 -0400 Subject: [PATCH 1/4] Append a terminating null character to iTunes comments to ensure that iTunes respects them. --- .../ID3v2/Classes/FrameContainer.cs | 3 ++- .../ID3v2/Classes/Frames/Comments.cs | 25 +++++++++++++------ 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/IdSharp.Tagging/ID3v2/Classes/FrameContainer.cs b/IdSharp.Tagging/ID3v2/Classes/FrameContainer.cs index 4929b8c..0cb2422 100644 --- a/IdSharp.Tagging/ID3v2/Classes/FrameContainer.cs +++ b/IdSharp.Tagging/ID3v2/Classes/FrameContainer.cs @@ -310,7 +310,8 @@ internal byte[] GetBytes(ID3v2TagVersion tagVersion) { foreach (var frame in m_iTunesCommentsList) { - byte[] rawData = frame.GetBytes(tagVersion); + // iTunes will only respect comments that end with a terminating null character + byte[] rawData = ((Comments) frame).GetBytes(tagVersion, true); frameData.Write(rawData); } } diff --git a/IdSharp.Tagging/ID3v2/Classes/Frames/Comments.cs b/IdSharp.Tagging/ID3v2/Classes/Frames/Comments.cs index 14b23cd..e71da88 100644 --- a/IdSharp.Tagging/ID3v2/Classes/Frames/Comments.cs +++ b/IdSharp.Tagging/ID3v2/Classes/Frames/Comments.cs @@ -177,8 +177,18 @@ public override void Read(TagReadingInfo tagReadingInfo, Stream stream) } } - public override byte[] GetBytes(ID3v2TagVersion tagVersion) - { + public override byte[] GetBytes(ID3v2TagVersion tagVersion) { + return GetBytes(tagVersion, false); + } + + /// + /// Returns the byte array representation of the comment frame, allowing for the appending of a terminating + /// null character. iTunes will only respect comment frames expressly terminated by a null character. + /// + /// The tag version + /// Should a null terminator be appended + /// The byte array representation of the frame + public byte[] GetBytes(ID3v2TagVersion tagVersion, bool appendNullTerminator) { if (string.IsNullOrEmpty(Value)) return new byte[0]; @@ -188,18 +198,19 @@ public override byte[] GetBytes(ID3v2TagVersion tagVersion) byte[] descriptionData; byte[] valueData; - do - { + do { descriptionData = ID3v2Utils.GetStringBytes(tagVersion, TextEncoding, Description, true); valueData = ID3v2Utils.GetStringBytes(tagVersion, TextEncoding, Value, false); + + if (appendNullTerminator) + Array.Resize(ref valueData, valueData.Length + 1); } while ( this.RequiresFix(tagVersion, Description, descriptionData) || this.RequiresFix(tagVersion, Value, valueData) ); - using (MemoryStream frameData = new MemoryStream()) - { - frameData.WriteByte((byte)TextEncoding); + using (MemoryStream frameData = new MemoryStream()) { + frameData.WriteByte((byte) TextEncoding); frameData.Write(ByteUtils.ISO88591GetBytes(LanguageCode)); frameData.Write(descriptionData); frameData.Write(valueData); From f4846084f0177aceb49f7f25320e0814c4af7b67 Mon Sep 17 00:00:00 2001 From: Jay D Zimmerman Date: Fri, 31 Aug 2012 11:27:40 -0400 Subject: [PATCH 2/4] Moved ReplayGain TXXX frames to their own BindingList collection, much like iTunes comments are handled. --- .../Classes/FrameContainer.FrameCreation.cs | 3 +++ .../Classes/FrameContainer.FrameProperties.cs | 10 +++++++ .../ID3v2/Classes/FrameContainer.cs | 27 +++++++++++++++++++ .../ID3v2/Interfaces/IFrameContainer.cs | 6 +++++ 4 files changed, 46 insertions(+) diff --git a/IdSharp.Tagging/ID3v2/Classes/FrameContainer.FrameCreation.cs b/IdSharp.Tagging/ID3v2/Classes/FrameContainer.FrameCreation.cs index 971ef97..d783a54 100644 --- a/IdSharp.Tagging/ID3v2/Classes/FrameContainer.FrameCreation.cs +++ b/IdSharp.Tagging/ID3v2/Classes/FrameContainer.FrameCreation.cs @@ -31,6 +31,7 @@ public abstract partial class FrameContainer : IFrameContainer private readonly UrlBindingList m_ArtistUrlList; private readonly UrlBindingList m_CommercialInfoUrlList; private readonly UserDefinedTextBindingList m_UserDefinedTextList; + private readonly UserDefinedTextBindingList m_ReplayGainList; private readonly RelativeVolumeAdjustmentBindingList m_RelativeVolumeAdjustmentList; // TODO: this is a single occurrence in 2.3 and 2.2 private readonly UnsynchronizedLyricsBindingList m_UnsynchronizedLyricsList; private readonly GeneralEncapsulatedObjectBindingList m_GeneralEncapsulatedObjectList; @@ -155,6 +156,7 @@ internal FrameContainer() m_CommercialInfoUrlList = new UrlBindingList("WCOM", "WCOM", "WCM"); m_ArtistUrlList = new UrlBindingList("WOAR", "WOAR", "WAR"); m_UserDefinedTextList = new UserDefinedTextBindingList(); + m_ReplayGainList = new UserDefinedTextBindingList(); m_RelativeVolumeAdjustmentList = new RelativeVolumeAdjustmentBindingList(); m_UnsynchronizedLyricsList = new UnsynchronizedLyricsBindingList(); m_GeneralEncapsulatedObjectList = new GeneralEncapsulatedObjectBindingList(); @@ -182,6 +184,7 @@ internal FrameContainer() //"CommercialInfoUrl", new MethodInvoker(ValidateCommercialInfoUrl)); // TODO AddMultipleOccurrenceFrame("WOAR", "WOAR", "WAR", m_ArtistUrlList); AddMultipleOccurrenceFrame("TXXX", "TXXX", "TXX", m_UserDefinedTextList); + AddMultipleOccurrenceFrame(null, null, null, m_ReplayGainList); AddMultipleOccurrenceFrame("RVA2", "RVAD", "RVA", m_RelativeVolumeAdjustmentList); AddMultipleOccurrenceFrame("USLT", "USLT", "ULT", m_UnsynchronizedLyricsList); AddMultipleOccurrenceFrame("GEOB", "GEOB", "GEO", m_GeneralEncapsulatedObjectList); diff --git a/IdSharp.Tagging/ID3v2/Classes/FrameContainer.FrameProperties.cs b/IdSharp.Tagging/ID3v2/Classes/FrameContainer.FrameProperties.cs index 60cb553..7a9768d 100644 --- a/IdSharp.Tagging/ID3v2/Classes/FrameContainer.FrameProperties.cs +++ b/IdSharp.Tagging/ID3v2/Classes/FrameContainer.FrameProperties.cs @@ -526,6 +526,16 @@ public BindingList UserDefinedText get { return m_UserDefinedTextList; } } + /// + /// Gets the list of ReplayGain frames (stored in TXXX/TXX). + /// For example: REPLAYGAIN_TRACK_GAIN, REPLAYGAIN_TRACK_PEAK, REPLAYGAIN_ALBUM_GAIN, REPLAYGAIN_ALBUM_PEAK. + /// + /// The BindingList of ReplayGain frames. For example: REPLAYGAIN_TRACK_GAIN, REPLAYGAIN_TRACK_PEAK, REPLAYGAIN_ALBUM_GAIN, REPLAYGAIN_ALBUM_PEAK. + public BindingList ReplayGainList + { + get { return m_ReplayGainList; } + } + /// /// Gets the list of commercial info URLs. WCOM/WCM. /// diff --git a/IdSharp.Tagging/ID3v2/Classes/FrameContainer.cs b/IdSharp.Tagging/ID3v2/Classes/FrameContainer.cs index 0cb2422..38d90f7 100644 --- a/IdSharp.Tagging/ID3v2/Classes/FrameContainer.cs +++ b/IdSharp.Tagging/ID3v2/Classes/FrameContainer.cs @@ -176,6 +176,16 @@ internal void Read(Stream stream, ID3v2TagVersion tagVersion, TagReadingInfo tag } } + // Process ReplayGain frames + foreach (var frame in new List(m_UserDefinedTextList)) + { + if (frame.Description != null && frame.Description.ToUpper().StartsWith("REPLAYGAIN_")) + { + m_UserDefinedTextList.Remove(frame); + m_ReplayGainList.Add(frame); + } + } + // Process genre // TODO: may need cleanup if (!string.IsNullOrEmpty(m_Genre.Value)) { @@ -232,6 +242,13 @@ internal List