public ByteVector ReadBlock(int length) { if (length == 0) { return(new ByteVector()); } try { Mode = FileAccessMode.Read; } catch (TagLibException) { TagLibDebugger.Debug(GetType().ToString() + ".ReadBlock() failed. Invalid File: " + Name); return(null); } if (length > bufferSize && (long)length > Length) { length = (int)Length; } byte[] buffer = new byte[length]; int count = fileStream.Read(buffer, 0, length); return(new ByteVector(buffer, count)); }
private void Parse(ByteVector data) { // Check to see if a valid Xing header is available. if (!data.StartsWith("Xing")) { return; } // If the XingHeader doesn'type contain the number of frames and the total stream // info it'field invalid. if ((data[7] & 0x02) == 0) { TagLibDebugger.Debug("MPEG::XingHeader::parse() -- Xing header doesn't contain the total number of frames."); return; } if ((data[7] & 0x04) == 0) { TagLibDebugger.Debug("MPEG::XingHeader::parse() -- Xing header doesn't contain the total stream size."); return; } frames = data.Mid(8, 4).ToUInt(); size = data.Mid(12, 4).ToUInt(); valid = true; }
public static OggPage[] Paginate(ByteVectorCollection packets, PaginationStrategy strategy, uint streamSerialNumber, int firstPage, bool firstPacketContinued, bool lastPacketCompleted, bool containsLastPacket) { ArrayList l = new ArrayList(); int totalSize = 0; foreach (ByteVector b in packets) { totalSize += b.Count; } if (strategy == PaginationStrategy.Repaginate || totalSize + packets.Count > 255 * 256) { TagLibDebugger.Debug("Ogg.Page.Paginate() -- Sorry! Repagination is not yet implemented."); return((OggPage[])l.ToArray(typeof(OggPage))); } // TODO: Handle creation of multiple pages here with appropriate pagination. OggPage p = new OggPage(packets, streamSerialNumber, firstPage, firstPacketContinued, lastPacketCompleted, containsLastPacket); l.Add(p); return((OggPage[])l.ToArray(typeof(OggPage))); }
public void Parse(ByteVector data) { if (data != null) { // 11 buffer is the minimum size for an APE item if (data.Count < 11) { TagLibDebugger.Debug("APE.Item.Parse() -- no data in item"); return; } uint value_length = data.Mid(0, 4).ToUInt(false); uint flags = data.Mid(4, 4).ToUInt(false); int pos = data.Find(new ByteVector(1), 8); key = data.Mid(8, pos - 8).ToString(StringType.UTF8); value = data.Mid(pos + 1, (int)value_length); ReadOnly = (flags & 1) == 1; Type = (ApeItemType)((flags >> 1) & 3); if (Type != ApeItemType.Binary) { text.Clear(); text = new StringCollection(ByteVectorCollection.Split(value, (byte)0), StringType.UTF8); } } else { throw new ArgumentNullException("data"); } }
public void SetPacket(uint index, ByteVector packet) { while (packetToPageMap.Count <= index) { if (!NextPage()) { TagLibDebugger.Debug("Ogg.File.SetPacket() -- Could not set the requested packet."); return; } } foreach (int page in (IntCollection)packetToPageMap[(int)index]) { dirtyPages.SortedInsert(page, true); } if (dirtyPackets.ContainsKey(index)) { dirtyPackets[index] = packet; } else { dirtyPackets.Add(index, packet); } }
protected void Parse(ByteVector data) { if (data.Count < Size) { return; } // do some sanity checking -- even in ID3v2.3.0 and less the tag size is a // synch-safe integer, so all buffer must be less than 128. If this is not // true then this is an invalid tag. // note that we're doing things a little out of order here -- the size is // later in the bytestream than the version ByteVector sizeData = data.Mid(6, 4); if (sizeData.Count != 4) { tagSize = 0; TagLibDebugger.Debug("ID3v2.Header.Parse () - The tag size as read was 0 bytes!"); return; } foreach (byte b in sizeData) { if (b >= 128) { tagSize = 0; TagLibDebugger.Debug("ID3v2.Header.Parse () - One of the size bytes in the id3v2 header was greater than the allowed 128."); return; } } // The first three buffer, data[0..2], are the File Identifier, "ID3". (structure 3.1 "file identifier") // Read the version number from the fourth and fifth buffer. majorVersion = data[3]; // (structure 3.1 "major version") revisionNumber = data[4]; // (structure 3.1 "revision number") // Read the flags, the first four bits of the sixth byte. byte flags = data[5]; desynchronization = ((flags >> 7) & 1) == 1; // (structure 3.1.a) extendedHeader = ((flags >> 6) & 1) == 1; // (structure 3.1.b) experimentalIndicator = ((flags >> 5) & 1) == 1; // (structure 3.1.channelMode) footerPresent = ((flags >> 4) & 1) == 1; // (structure 3.1.d) // Get the size from the remaining four buffer (read above) tagSize = Id3v2SynchData.ToUInt(sizeData); // (structure 3.1 "size") }
private void ParsePrivateFields(ByteVector data) { if (data.Count < 1) { TagLibDebugger.Debug("A private frame must contain at least 1 byte."); return; } ByteVectorCollection list = ByteVectorCollection.Split(data, TextDelimiter(StringType.Latin1), 1, 2); if (list.Count == 2) { owner = list[0].ToString(StringType.Latin1); data = list[1]; } }
//private void Read(ByteVector data, long streamLength, ReadStyle style) private void Read(ByteVector data, long streamLength) { if (data.Count < 18) { TagLibDebugger.Debug("FLAC.Properties.Read() - FLAC properties must contain at least 18 bytes."); return; } int pos = 0; // Minimum block size (in samples) pos += 2; // Maximum block size (in samples) pos += 2; // Minimum frame size (in buffer) pos += 3; // Maximum frame size (in buffer) pos += 3; uint flags = data.Mid(pos, 4).ToUInt(true); sampleRate = (int)(flags >> 12); channels = (int)(((flags >> 9) & 7) + 1); sampleWidth = (int)(((flags >> 4) & 31) + 1); // The last 4 bits are the most significant 4 bits for the 36 bit // stream length in samples. (Audio files measured in days) double high_length = (double)(sampleRate > 0 ? (((flags & 0xf) << 28) / sampleRate) << 4 : 0); pos += 4; duration = sampleRate > 0 ? TimeSpan.FromSeconds((double)data.Mid(pos, 4).ToUInt(true) / (double)sampleRate + high_length) : TimeSpan.Zero; pos += 4; // Uncompressed bitrate: //bitrate = ((sampleRate * channels) / 1000) * sample_width; // Real bitrate: bitrate = (int)(duration > TimeSpan.Zero ? ((streamLength * 8L) / duration.TotalSeconds) / 1000 : 0); }
////////////////////////////////////////////////////////////////////////// // private methods ////////////////////////////////////////////////////////////////////////// private void Read(ReadStyle propertiesStyle) { ByteVector comment_header_data = GetPacket(1); if (comment_header_data.Mid(0, 7) != vorbis_comment_header_id) { TagLibDebugger.Debug("Vorbis.File.Read() - Could not find the Vorbis comment header."); SetValid(false); return; } comment = new OggXiphComment(comment_header_data.Mid(7)); if (propertiesStyle != ReadStyle.None) { properties = new OggVorbisProperties(this, propertiesStyle); } }
protected void Read() { if (file != null && file.IsValid) { file.Seek(tagOffset); // read the tag -- always 128 buffer ByteVector data = file.ReadBlock(128); // some initial sanity checking if (data.Count == 128 && data.StartsWith(FileIdentifier)) //"TAG")) { Parse(data); } else { TagLibDebugger.Debug("ID3v1 tag is not valid or could not be read at the specified offset."); } } }
public void WriteBlock(ByteVector data) { if (data != null) { try { Mode = FileAccessMode.Write; } catch (TagLibException) { TagLibDebugger.Debug(GetType().ToString() + ".WriteBlock () failed. Read-only File: " + Name); return; } fileStream.Write(data.GetDataBuffer(), 0, data.Count); } else { throw new ArgumentNullException("data"); } }
// Overwrite the box'field header with a new header incorporating a size // change. public virtual void OverwriteHeader(long sizeChange) { // If we don'type have a header we can'type do anything. if (header == null || header.Position < 0) { TagLibDebugger.Debug("Box.OverWriteHeader() - No header to overwrite."); return; } // Make sure this alteration won'type screw up the reading of children. LoadChildren(); // Save the header'field original position and size. long position = header.Position; long oldSize = header.HeaderSize; // Update the data size. header.DataSize = (ulong)((long)header.DataSize + sizeChange); // Render the header onto the file. File.Insert(header.Render(), position, oldSize); }
public ByteVector Render() { ByteVector data = header.Render(); if (packets.IsEmpty) { if (file != null) { file.Seek(packetOffset); data.Add(file.ReadBlock(dataSize)); } else { TagLibDebugger.Debug("Ogg.Page.Render() -- this page is empty!"); } } else { foreach (ByteVector v in packets) { data.Add(v); } } // Compute and set the checksum for the Ogg page. The checksum is taken over // the entire page with the 4 buffer reserved for the checksum zeroed and then // inserted in buffer 22-25 of the page header. ByteVector checksum = ByteVector.FromUInt(data.Checksum, false); for (int i = 0; i < 4; i++) { data [i + 22] = checksum [i]; } return(data); }
private void ParseCommentsFields(ByteVector data) { if (data.Count < 5) { TagLibDebugger.Debug("A comment frame must contain at least 5 bytes."); return; } textEncoding = (StringType)data[0]; language = data.Mid(1, 3); int byte_align = textEncoding == StringType.Latin1 || textEncoding == StringType.UTF8 ? 1 : 2; ByteVectorCollection l = ByteVectorCollection.Split(data.Mid(4), TextDelimiter(textEncoding), byte_align, 2); if (l.Count == 2) { if (l[0].Data != null && l[0].Data.Count > 0) { description = l[0].ToString(textEncoding); } else { description = string.Empty; } if (l[1].Data != null && l[1].Data.Count > 0) { text = l[1].ToString(textEncoding); } else { text = string.Empty; } } }
//private void Read(OggVorbisFile file, ReadStyle style) private void Read(OggVorbisFile file) { // Get the identification header from the Ogg implementation. ByteVector data = file.GetPacket(0); int pos = 0; if (data.Mid(pos, 7) != vorbisCommentHeaderId) { TagLibDebugger.Debug("Vorbis.Properties.Read() -- invalid Vorbis identification header"); return; } pos += 7; vorbisVersion = (int)data.Mid(pos, 4).ToUInt(false); pos += 4; channels = data[pos]; pos += 1; sampleRate = (int)data.Mid(pos, 4).ToUInt(false); pos += 4; bitrateMaximum = (int)data.Mid(pos, 4).ToUInt(false); pos += 4; bitrateNominal = (int)data.Mid(pos, 4).ToUInt(false); pos += 4; bitrateMinimum = (int)data.Mid(pos, 4).ToUInt(false); // TODO: Later this should be only the "fast" mode. bitrate = bitrateNominal; // Find the length of the file. See http://wiki.xiph.org/VorbisStreamLength/ // for my notes on the topic. OggPageHeader first = file.FirstPageHeader; OggPageHeader last = file.LastPageHeader; if (first != null && last != null) { long start = first.AbsoluteGranularPosition; long end = last.AbsoluteGranularPosition; if (start >= 0 && end >= 0 && sampleRate > 0) { duration = TimeSpan.FromSeconds(((double)(end - start) / (double)sampleRate)); } else { TagLibDebugger.Debug("Vorbis.Properties.Read() -- Either the PCM " + "values for the start or end of this file was " + "incorrect or the sample rate is zero."); } } else { TagLibDebugger.Debug("Vorbis.Properties.Read() -- Could not find valid first and last Ogg pages."); } }
public void Save(TagTypes types, bool stripOthers) { if (types == TagTypes.None && stripOthers) { if (!Strip(TagTypes.AllTags)) { throw new TagLibException(TagLibError.MpegCouldNotStripTags); } return; } if (id3v2_tag == null && id3v1_tag == null && ape_tag == null) { if (stripOthers) { if (!Strip(TagTypes.AllTags)) { throw new TagLibException(TagLibError.MpegCouldNotStripTags); } } return; } if (IsReadOnly) { throw new ReadOnlyException(); } Mode = FileAccessMode.Write; // Create the tags if we've been asked to. Copy the values from the tag that // does exist into the new tag. if ((types & TagTypes.Id3v2) != 0 && id3v1_tag != null) { TagLib.Tag.Duplicate(id3v1_tag, FindTag(TagTypes.Id3v2, true), false); } if ((types & TagTypes.Id3v1) != 0 && id3v2_tag != null) { TagLib.Tag.Duplicate(id3v2_tag, FindTag(TagTypes.Id3v1, true), false); } bool success = true; if ((TagTypes.Id3v2 & types) != 0 && id3v2_tag != null && !id3v2_tag.IsEmpty) { long id3v2_location = FindId3v2(); int id3v2_size = 0; if (id3v2_location < 0) { id3v2_location = 0; } else { Seek(id3v2_location); Id3v2Header header = new Id3v2Header(ReadBlock((int)Id3v2Header.Size)); if (header.TagSize == 0) { TagLibDebugger.Debug("Mpc.File.Save() -- Id3v2 header is broken. Ignoring."); } else { id3v2_size = (int)header.CompleteTagSize; } } Insert(id3v2_tag.Render(), id3v2_location, id3v2_size); } else if (stripOthers) { success = Strip(TagTypes.Id3v2) && success; } if ((TagTypes.Id3v1 & types) != 0 && id3v1_tag != null && !id3v1_tag.IsEmpty) { long id3v1_location = FindId3v1(); if (id3v1_location < 0) { Seek(0, System.IO.SeekOrigin.End); } else { Seek(id3v1_location); } WriteBlock(id3v1_tag.Render()); } else if (stripOthers) { success = (Strip(TagTypes.Id3v1, false) && success); } // Dont save an APE-tag unless one has been created if ((TagTypes.Ape & types) != 0 && ape_tag != null && !ape_tag.IsEmpty) { long ape_location = FindApe(FindId3v1() >= 0); long ape_size = 0; if (ape_location < 0) { ape_location = Length; } else { Seek(ape_location); ape_size = (new ApeFooter(ReadBlock((int)ApeFooter.Size))).CompleteTagSize; ape_location = ape_location + ApeFooter.Size - ape_size; } Insert(ape_tag.Render(), ape_location, ape_size); } else if (stripOthers) { success = (Strip(TagTypes.Ape, false) && success); } Mode = FileAccessMode.Closed; if (!success) { throw new TagLibException(TagLibError.MpegCouldNotWriteTags); } }
private void Parse(ByteVector data) { if (data.Count < 4 || data[0] != 0xff) { TagLibDebugger.Debug("Mpeg.Header.Parse() -- First byte did not match MPEG synch."); return; } uint flags = data.ToUInt(); // Check for the second byte'field part of the MPEG synch if ((flags & 0xFFE00000) != 0xFFE00000) { TagLibDebugger.Debug("Mpeg.Header.Parse() -- Second byte did not match MPEG synch."); return; } // Set the MPEG version switch ((flags >> 19) & 0x03) { case 0: version = MpegVersion.TwoPointFive; break; case 2: version = MpegVersion.Two; break; case 3: version = MpegVersion.One; break; } // Set the MPEG layer switch ((flags >> 17) & 0x03) { case 1: layer = 3; break; case 2: layer = 2; break; case 3: layer = 1; break; } protectionEnabled = ((flags >> 16) & 1) == 0; // Set the bitrate int[, ,] bitrates = new int[2, 3, 16] { { // Version 1 { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0 }, // layer 1 { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0 }, // layer 2 { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0 } // layer 3 }, { // Version 2 or 2.5 { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0 }, // layer 1 { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, // layer 2 { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 } // layer 3 } }; int versionIndex = version == MpegVersion.One ? 0 : 1; int layerIndex = layer > 0 ? layer - 1 : 0; // The bitrate index is encoded as the first 4 bits of the 3rd byte, // index.e. 1111xxxx int i = (int)(flags >> 12) & 0x0F; bitrate = bitrates[versionIndex, layerIndex, i]; // Set the sample rate int[,] sampleRates = new int[3, 4] { { 44100, 48000, 32000, 0 }, // Version 1 { 22050, 24000, 16000, 0 }, // Version 2 { 11025, 12000, 8000, 0 } // Version 2.5 }; // The sample rate index is encoded as two bits in the 3nd byte, // index.e. xxxx11xx i = (int)(flags >> 10) & 0x03; sampleRate = sampleRates[(int)version, i]; if (sampleRate == 0) { TagLibDebugger.Debug("Mpeg.Header.Parse() -- Invalid sample rate."); return; } // The channel mode is encoded as a 2 bit value at the end of the 3nd // byte, index.e. xxxxxx11 channelMode = (MpegChannelMode)((flags >> 16) & 0x3); // TODO: Add mode extension for completeness isCopyrighted = (flags & 1) == 1; isOriginal = ((flags >> 1) & 1) == 1; // Calculate the frame length if (layer == 1) { frameLength = 24000 * 2 * bitrate / sampleRate + (IsPadded ? 1 : 0); } else { frameLength = 72000 * bitrate / sampleRate + (IsPadded ? 1 : 0); } // Now that we're done parsing, set this to be a valid frame. isValid = true; }
public ByteVector GetPacket(uint index) { // Check to see if we're called setPacket() for this packet since the last // save: if (dirtyPackets.ContainsKey(index)) { return(dirtyPackets[index]); } // If we haven'type indexed the page where the packet we're interested in starts, // begin reading pages until we have. while (packetToPageMap.Count <= index) { if (!NextPage()) { TagLibDebugger.Debug("Ogg.File.Packet() -- Could not find the requested packet."); return(null); } } // Start reading at the first page that contains part (or all) of this packet. // If the last read stopped at the packet that we're interested in, don'type // reread its packet text. (This should make sequential packet reads fast.) int pageIndex = ((IntCollection)packetToPageMap[(int)index])[0]; if (currentPacketPage != pages[pageIndex]) { currentPacketPage = pages[pageIndex]; currentPackets = currentPacketPage.Packets; } // If the packet is completely contained in the first page that it'field in, then // just return it now. if ((currentPacketPage.ContainsPacket((int)index) & ContainsPacketSettings.CompletePacket) != 0) { return(currentPackets[(int)(index - currentPacketPage.FirstPacketIndex)]); } // If the packet is *not* completely contained in the first page that it'field a // part of then that packet trails off the end of the page. Continue appending // the pages' packet data until we hit a page that either does not end with the // packet that we're fetching or where the last packet is complete. ByteVector packet = currentPackets[currentPackets.Count - 1]; while ((currentPacketPage.ContainsPacket((int)index) & ContainsPacketSettings.EndsWithPacket) != 0 && !currentPacketPage.Header.LastPacketCompleted) { pageIndex++; if (pageIndex == pages.Count && !NextPage()) { TagLibDebugger.Debug("Ogg.File.Packet() -- Could not find the requested packet."); return(null); } currentPacketPage = (OggPage)pages[pageIndex]; currentPackets = currentPacketPage.Packets; packet.Add(currentPackets[0]); } return(packet); }
private static bool UpdateFrame(Id3v2FrameHeader header) { ByteVector frameId = header.FrameId; switch (header.Version) { case 2: // ID3v2.2 { if (frameId == "CRM" || frameId == "EQU" || frameId == "LNK" || frameId == "RVA" || frameId == "TIM" || frameId == "TSI") { TagLibDebugger.Debug("ID3v2.4 no longer supports the frame type " + frameId.ToString() + ". It will be discarded from the tag."); return(false); } // ID3v2.2 only used 3 buffer for the frame ID, so we need to convert all of // the frames to their 4 byte ID3v2.4 equivalent. ConvertFrame("BUF", "RBUF", header); ConvertFrame("CNT", "PCNT", header); ConvertFrame("COM", "COMM", header); ConvertFrame("CRA", "AENC", header); ConvertFrame("ETC", "ETCO", header); ConvertFrame("GEO", "GEOB", header); ConvertFrame("IPL", "TIPL", header); ConvertFrame("MCI", "MCDI", header); ConvertFrame("MLL", "MLLT", header); ConvertFrame("PIC", "APIC", header); ConvertFrame("POP", "POPM", header); ConvertFrame("REV", "RVRB", header); ConvertFrame("SLT", "SYLT", header); ConvertFrame("STC", "SYTC", header); ConvertFrame("TAL", "TALB", header); ConvertFrame("TBP", "TBPM", header); ConvertFrame("TCM", "TCOM", header); ConvertFrame("TCO", "TCON", header); ConvertFrame("TCR", "TCOP", header); ConvertFrame("TDA", "TDRC", header); ConvertFrame("TDY", "TDLY", header); ConvertFrame("TEN", "TENC", header); ConvertFrame("TFT", "TFLT", header); ConvertFrame("TKE", "TKEY", header); ConvertFrame("TLA", "TLAN", header); ConvertFrame("TLE", "TLEN", header); ConvertFrame("TMT", "TMED", header); ConvertFrame("TOA", "TOAL", header); ConvertFrame("TOF", "TOFN", header); ConvertFrame("TOL", "TOLY", header); ConvertFrame("TOR", "TDOR", header); ConvertFrame("TOT", "TOAL", header); ConvertFrame("TP1", "TPE1", header); ConvertFrame("TP2", "TPE2", header); ConvertFrame("TP3", "TPE3", header); ConvertFrame("TP4", "TPE4", header); ConvertFrame("TPA", "TPOS", header); ConvertFrame("TPB", "TPUB", header); ConvertFrame("TRC", "TSRC", header); ConvertFrame("TRD", "TDRC", header); ConvertFrame("TRK", "TRCK", header); ConvertFrame("TSS", "TSSE", header); ConvertFrame("TT1", "TIT1", header); ConvertFrame("TT2", "TIT2", header); ConvertFrame("TT3", "TIT3", header); ConvertFrame("TXT", "TOLY", header); ConvertFrame("TXX", "TXXX", header); ConvertFrame("TYE", "TDRC", header); ConvertFrame("UFI", "UFID", header); ConvertFrame("ULT", "USLT", header); ConvertFrame("WAF", "WOAF", header); ConvertFrame("WAR", "WOAR", header); ConvertFrame("WAS", "WOAS", header); ConvertFrame("WCM", "WCOM", header); ConvertFrame("WCP", "WCOP", header); ConvertFrame("WPB", "WPUB", header); ConvertFrame("WXX", "WXXX", header); } break; case 3: // ID3v2.3 { if (frameId == "EQUA" || frameId == "RVAD" || frameId == "TIME" || frameId == "TRDA" || frameId == "TSIZ" || frameId == "TDAT") { TagLibDebugger.Debug("ID3v2.4 no longer supports the frame type " + frameId.ToString() + ". It will be discarded from the tag."); return(false); } ConvertFrame("TORY", "TDOR", header); ConvertFrame("TYER", "TDRC", header); } break; default: { // This should catch a typo that existed in TagLib up to and including // version 1.1 where TRDC was used for the year rather than TDRC. ConvertFrame("TRDC", "TDRC", header); } break; } return(true); }
////////////////////////////////////////////////////////////////////////// // private methods ////////////////////////////////////////////////////////////////////////// private void Read() { // Since we've likely just looked for the ID3v1 tag, start at the end of the // file where we're least likely to have to have to move the disk head. long last = file.LastFrameOffset; if (last < 0) { TagLibDebugger.Debug("Mpeg.Properties.Read() -- Could not find a valid last MPEG frame in the stream."); return; } file.Seek(last); MpegHeader last_header = new MpegHeader(file.ReadBlock(4)); long first = file.FirstFrameOffset; if (first < 0) { TagLibDebugger.Debug("Mpeg.Properties.Read() -- Could not find a valid first MPEG frame in the stream."); return; } if (!last_header.IsValid) { long pos = last; while (pos > first) { pos = file.PreviousFrameOffset(pos); if (pos < 0) { break; } file.Seek(pos); MpegHeader header = new MpegHeader(file.ReadBlock(4)); if (header.IsValid) { last_header = header; last = pos; break; } } } // Now jump back to the front of the file and read what we need from there. file.Seek(first); MpegHeader first_header = new MpegHeader(file.ReadBlock(4)); if (!first_header.IsValid || !last_header.IsValid) { TagLibDebugger.Debug("Mpeg.Properties.Read() -- Page headers were invalid."); return; } // Check for a Xing header that will help us in gathering information about a // VBR stream. int xing_header_offset = MpegXingHeader.XingHeaderOffset(first_header.Version, first_header.ChannelMode); file.Seek(first + xing_header_offset); MpegXingHeader xing_header = new MpegXingHeader(file.ReadBlock(16)); // Read the length and the bitrate from the Xing header. if (xing_header.IsValid && first_header.SampleRate > 0 && xing_header.TotalFrames > 0) { int [] block_size = { 0, 384, 1152, 1152 }; double time_per_frame = block_size [first_header.Layer]; time_per_frame = first_header.SampleRate > 0 ? time_per_frame / first_header.SampleRate : 0; duration = new TimeSpan((int)(time_per_frame * xing_header.TotalFrames) * TimeSpan.TicksPerSecond); bitrate = (int)(duration > TimeSpan.Zero ? ((xing_header.TotalSize * 8L) / duration.TotalSeconds) / 1000 : 0); } // Since there was no valid Xing header found, we hope that we're in a constant // bitrate file. // TODO: Make this more robust with audio property detection for VBR without a // Xing header. else if (first_header.FrameLength > 0 && first_header.Bitrate > 0) { int frames = (int)((last - first) / first_header.FrameLength + 1); duration = TimeSpan.FromSeconds((double)(first_header.FrameLength * frames) / (double)(first_header.Bitrate * 125) + 0.5); bitrate = first_header.Bitrate; } sample_rate = first_header.SampleRate; channels = first_header.ChannelMode == MpegChannelMode.SingleChannel ? 1 : 2; version = first_header.Version; layer = first_header.Layer; channel_mode = first_header.ChannelMode; is_copyrighted = first_header.IsCopyrighted; is_original = first_header.IsOriginal; }
public override void Save() { if (IsReadOnly) { throw new ReadOnlyException(); } Mode = FileAccessMode.Write; long flac_data_begin; long flac_data_end; // Update ID3 tags if (id3v2_tag != null) { ByteVector id3v2_tag_data = id3v2_tag.Render(); long id3v2_location = FindId3v2(); if (id3v2_location >= 0) { int id3v2_size = 0; Seek(id3v2_location); Id3v2Header header = new Id3v2Header(ReadBlock((int)Id3v2Header.Size)); if (header.TagSize == 0) { TagLibDebugger.Debug("Flac.File.Save() -- Id3v2 header is broken. Ignoring."); } else { id3v2_size = (int)header.CompleteTagSize; } Insert(id3v2_tag_data, id3v2_location, id3v2_size); System.Console.WriteLine("ID3v2: " + id3v2_size + " " + id3v2_tag_data.Count); flac_data_begin = id3v2_location + id3v2_tag_data.Count; } else { Insert(id3v2_tag_data, 0, 0); flac_data_begin = id3v2_tag_data.Count; } } else { flac_data_begin = 0; } if (id3v1_tag != null) { long id3v1_location = FindId3v1(); if (id3v1_location >= 0) { Seek(id3v1_location); } else { Seek(0, System.IO.SeekOrigin.End); } flac_data_end = Tell; WriteBlock(id3v1_tag.Render()); } else { flac_data_end = Length; } // Create new vorbis comments is they don'type exist. FindTag(TagTypes.Xiph, true); xiph_comment_data = comment.Render(false); ByteVector v = ByteVector.FromUInt((uint)xiph_comment_data.Count); // Set the type of the comment to be a Xiph / Vorbis comment // (See scan() for comments on header-format) v[0] = 4; v.Add(xiph_comment_data); // If file already have comment => find and update it // if not => insert one scanned = false; if (Scan(flac_data_begin, flac_data_end) != null) { long next_page_offset = flac_start; Seek(next_page_offset); ByteVector header = ReadBlock(4); uint length = header.Mid(1, 3).ToUInt(); next_page_offset += length + 4; // Search through the remaining metadata byte block_type = (byte)(header[0] & 0x7f); bool last_block = (header[0] & 0x80) != 0; while (!last_block) { Seek(next_page_offset); header = ReadBlock(4); block_type = (byte)(header[0] & 0x7f); last_block = (header[0] & 0x80) != 0; length = header.Mid(1, 3).ToUInt(); // Type is vorbiscomment if (block_type == 4) { long next_keep = (last_block ? 0 : FindPaddingBreak(next_page_offset + length + 4, next_page_offset + XiphCommentData.Count + 8, ref last_block)); uint padding_length; if (next_keep != 0) { // There is space for comment and padding blocks without rewriting the whole file. // Note this can not overflow. padding_length = (uint)(next_keep - (next_page_offset + XiphCommentData.Count + 8)); } else { // Not enough space, so we will have to rewrite the whole file following this block padding_length = (uint)XiphCommentData.Count; if (padding_length < 4096) { padding_length = 4096; } next_keep = next_page_offset + length + 4; } ByteVector padding = ByteVector.FromUInt(padding_length); padding[0] = 1; if (last_block) { padding[0] = (byte)(padding[0] | 0x80); } padding.Resize((int)(padding_length + 4)); Insert(v + padding, next_page_offset, next_keep - next_page_offset); //System.Console.WriteLine ("OGG: " + (next_keep - next_page_offset) + " " + (vector.Count + padding.Count)); break; } next_page_offset += length + 4; } } else { long next_page_offset = flac_start; Seek(next_page_offset); ByteVector header = ReadBlock(4); bool last_block = (header[0] & 0x80) != 0; uint length = header.Mid(1, 3).ToUInt(); // If last block was last, make this one last if (last_block) { // Copy the bottom seven bits into the new value ByteVector h = (byte)(header[0] & 0x7F); Insert(h, next_page_offset, 1); // Set the last bit v[0] = (byte)(v[0] | 0x80); } Insert(v, next_page_offset + length + 4, 0); } Mode = FileAccessMode.Closed; }
private ByteVector Scan(long begin, long end) { ByteVector xiph_comment_data = null; // Scan the metadata pages if (scanned || !IsValid) { return(null); } long next_page_offset; long file_size = Length; next_page_offset = Find("fLaC", begin); if (next_page_offset < 0) { TagLibDebugger.Debug("Flac.File.Scan () -- FLAC stream not found"); SetValid(false); return(null); } next_page_offset += 4; flac_start = next_page_offset; Seek(next_page_offset); ByteVector header = ReadBlock(4); // Header format (from spec): // <1> Last-metadata-block flag // <7> BLOCK_TYPE // 0 : STREAMINFO // 1 : PADDING // .. // 4 : VORBIS_COMMENT // .. // <24> Length of metadata to follow byte block_type = (byte)(header[0] & 0x7f); bool last_block = (header[0] & 0x80) != 0; uint length = header.Mid(1, 3).ToUInt(); // First block should be the stream_info metadata if (block_type != 0) { TagLibDebugger.Debug("Flac.File.Scan() -- invalid FLAC stream"); SetValid(false); return(null); } stream_info_data = ReadBlock((int)length); next_page_offset += length + 4; // Search through the remaining metadata while (!last_block) { header = ReadBlock(4); block_type = (byte)(header[0] & 0x7f); last_block = (header[0] & 0x80) != 0; length = header.Mid(1, 3).ToUInt(); // Found the vorbis-comment if (block_type == 4) { xiph_comment_data = ReadBlock((int)length); } next_page_offset += length + 4; if (next_page_offset >= file_size) { TagLibDebugger.Debug("Flac.File.Scan() -- FLAC stream corrupted"); SetValid(false); return(null); } Seek(next_page_offset); } // End of metadata, now comes the datastream stream_start = next_page_offset; stream_length = end - stream_start; scanned = true; return(xiph_comment_data); }
public void SetData(ByteVector data, uint version) { if (data != null) { this.version = version; if (version < 3) { // ID3v2.2 if (data.Count < 3) { TagLibDebugger.Debug("You must at least specify a frame ID."); return; } // Set the frame ID -- the first three buffer frameId = data.Mid(0, 3); // If the full header information was not passed in, do not continue to the // steps to parse the frame size and flags. if (data.Count < 6) { frameSize = 0; return; } frameSize = data.Mid(3, 3).ToUInt(); } else if (version == 3) { // ID3v2.3 if (data.Count < 4) { TagLibDebugger.Debug("You must at least specify a frame ID."); return; } // Set the frame ID -- the first four buffer frameId = data.Mid(0, 4); // If the full header information was not passed in, do not continue to the // steps to parse the frame size and flags. if (data.Count < 10) { frameSize = 0; return; } // Set the size -- the frame size is the four buffer starting at byte four in // the frame header (structure 4) frameSize = data.Mid(4, 4).ToUInt(); // read the first byte of flags tagAlterPreservation = ((data[8] >> 7) & 1) == 1; // (structure 3.3.1.a) fileAlterPreservation = ((data[8] >> 6) & 1) == 1; // (structure 3.3.1.b) readOnly = ((data[8] >> 5) & 1) == 1; // (structure 3.3.1.channelMode) // read the second byte of flags compression = ((data[9] >> 7) & 1) == 1; // (structure 3.3.1.index) encryption = ((data[9] >> 6) & 1) == 1; // (structure 3.3.1.j) groupingIdentity = ((data[9] >> 5) & 1) == 1; // (structure 3.3.1.k) } else { // ID3v2.4 if (data.Count < 4) { TagLibDebugger.Debug("You must at least specify a frame ID."); return; } // Set the frame ID -- the first four buffer frameId = data.Mid(0, 4); // If the full header information was not passed in, do not continue to the // steps to parse the frame size and flags. if (data.Count < 10) { frameSize = 0; return; } // Set the size -- the frame size is the four buffer starting at byte four in // the frame header (structure 4) frameSize = Id3v2SynchData.ToUInt(data.Mid(4, 4)); // read the first byte of flags tagAlterPreservation = ((data[8] >> 6) & 1) == 1; // (structure 4.1.1.a) fileAlterPreservation = ((data[8] >> 5) & 1) == 1; // (structure 4.1.1.b) readOnly = ((data[8] >> 4) & 1) == 1; // (structure 4.1.1.channelMode) // read the second byte of flags groupingIdentity = ((data[9] >> 6) & 1) == 1; // (structure 4.1.2.header) compression = ((data[9] >> 3) & 1) == 1; // (structure 4.1.2.k) encryption = ((data[9] >> 2) & 1) == 1; // (structure 4.1.2.m) desynchronization = ((data[9] >> 1) & 1) == 1; // (structure 4.1.2.n) dataLengthIndicator = (data[9] & 1) == 1; // (structure 4.1.2.position) } } else { throw new ArgumentNullException("data"); } }
public override void Save() { if (IsReadOnly) { throw new ReadOnlyException(); } Mode = FileAccessMode.Write; // Update ID3v2 tag long id3v2_location = FindId3v2(); int id3v2_size = 0; if (id3v2_location != -1) { Seek(id3v2_location); Id3v2Header header = new Id3v2Header(ReadBlock((int)Id3v2Header.Size)); if (header.TagSize == 0) { TagLibDebugger.Debug("Mpc.File.Save() -- Id3v2 header is broken. Ignoring."); id3v2_location = -1; } else { id3v2_size = (int)header.CompleteTagSize; } } if (id3v2Tag != null) { if (id3v2_location >= 0) { Insert(id3v2Tag.Render(), id3v2_location, id3v2_size); } else { Insert(id3v2Tag.Render(), 0, 0); } } else if (id3v2_location >= 0) { RemoveBlock(id3v2_location, id3v2_size); } // Update ID3v1 tag long id3v1_location = FindId3v1(); if (id3v1Tag != null) { if (id3v1_location >= 0) { Insert(id3v1Tag.Render(), id3v1_location, 128); } else { Seek(0, System.IO.SeekOrigin.End); id3v1_location = Tell; WriteBlock(id3v1Tag.Render()); } } else if (id3v1_location >= 0) { RemoveBlock(id3v1_location, 128); id3v1_location = -1; } // Update APE tag long ape_location = FindApe(id3v1_location != -1); long ape_size = 0; if (ape_location >= 0) { Seek(ape_location); ape_size = (new ApeFooter(ReadBlock((int)ApeFooter.Size))).CompleteTagSize; ape_location = ape_location + ApeFooter.Size - ape_size; } if (apeTag != null) { if (ape_location >= 0) { Insert(apeTag.Render(), ape_location, ape_size); } else { if (id3v1_location >= 0) { Insert(apeTag.Render(), id3v1_location, 0); } else { Seek(0, System.IO.SeekOrigin.End); WriteBlock(apeTag.Render()); } } } else if (ape_location >= 0) { RemoveBlock(ape_location, ape_size); } Mode = FileAccessMode.Closed; }
private void Read(OggFile file, long file_offset) { file.Seek(file_offset); // An Ogg page header is at least 27 buffer, so we'll go ahead and read that // much and then get the rest when we're ready for it. ByteVector data = file.ReadBlock(27); // Sanity check -- make sure that we were in fact able to read as much data as // we asked for and that the page begins with "OggS". if (data.Count != 27 || !data.StartsWith("OggS")) { TagLibDebugger.Debug("Ogg.PageHeader.Read() -- error reading page header"); return; } byte flags = data[5]; firstPacketContinued = (flags & 1) != 0; firstPageOfStream = ((flags >> 1) & 1) != 0; lastPageOfStream = ((flags >> 2) & 1) != 0; absoluteGranularPosition = data.Mid(6, 8).ToLong(false); streamSerialNumber = data.Mid(14, 4).ToUInt(false); pageSequenceNumber = (int)data.Mid(18, 4).ToUInt(false); // Byte number 27 is the number of page segments, which is the only variable // length portion of the page header. After reading the number of page // segments we'll then read in the coresponding data for this count. int pageSegmentCount = data[26]; ByteVector page_segments = file.ReadBlock(pageSegmentCount); // Another sanity check. if (pageSegmentCount < 1 || page_segments.Count != pageSegmentCount) { return; } // The base size of an Ogg page 27 buffer plus the number of lacing values. size = 27 + pageSegmentCount; int packetSize = 0; for (int i = 0; i < pageSegmentCount; i++) { dataSize += page_segments[i]; packetSize += page_segments[i]; if (page_segments[i] < 255) { packetSizes.Add(packetSize); packetSize = 0; } } if (packetSize > 0) { packetSizes.Add(packetSize); lastPacketCompleted = false; } else { lastPacketCompleted = true; } isValid = true; }
protected void Parse(ByteVector data) { if (data != null) { try { int frameDataPosition = 0; int frameDataLength = data.Count; // check for extended header if (header.ExtendedHeader) { if (ExtendedHeader == null) { extendedHeader = new Id3v2ExtendedHeader(); } ExtendedHeader.SetData(data); if (ExtendedHeader.Size <= data.Count) { frameDataPosition += (int)ExtendedHeader.Size; frameDataLength -= (int)ExtendedHeader.Size; } } // check for footer -- we don'type actually need to parse it, as it *must* // contain the same data as the header, but we do need to account for its // size. if (header.FooterPresent && Id3v2Footer.Size <= frameDataLength) { frameDataLength -= (int)Id3v2Footer.Size; } // parse frames // Make sure that there is at least enough room in the remaining frame data for // a frame header. while (frameDataPosition < frameDataLength - Id3v2FrameHeader.Size(header.MajorVersion)) { // If the next data is position is 0, assume that we've hit the padding // portion of the frame data. if (data[frameDataPosition] == 0) { if (header.FooterPresent) { TagLibDebugger.Debug("Padding *and* a footer found. This is not allowed by the spec."); } return; } Id3v2Frame frame = Id3v2FrameFactory.CreateFrame(data.Mid(frameDataPosition), header.MajorVersion); if (frame == null) { return; } // Checks to make sure that frame parsed correctly. if (frame.Size < 0) { return; } frameDataPosition += (int)(frame.Size + Id3v2FrameHeader.Size(header.MajorVersion)); // Only add frames with content so we don'type send out just we got in. if (frame.Size > 0) { AddFrame(frame); } } } catch (Exception ex) { throw new ApplicationException("There was an error parsing this ID3 tag", ex); } } else { throw new ArgumentNullException("data"); } }
private void Scan() { // Scan the metadata pages if (scanned || !IsValid) { return; } uint ipacket = 0; long overhead = 0; ByteVector metadata_header = GetPacket(ipacket++); if (metadata_header == null) { return; } ByteVector header; if (!metadata_header.StartsWith("fLaC")) { // FLAC 1.1.2+ if (metadata_header.Mid(1, 4) != "FLAC") { return; } if (metadata_header[5] != 1) { return; // not version 1 } metadata_header = metadata_header.Mid(13); } else { // FLAC 1.1.0 & 1.1.1 metadata_header = GetPacket(ipacket++); if (metadata_header == null) { return; } } header = metadata_header.Mid(0, 4); // Header format (from spec): // <1> Last-metadata-block flag // <7> BLOCK_TYPE // 0 : STREAMINFO // 1 : PADDING // .. // 4 : VORBIS_COMMENT // .. // <24> Length of metadata to follow byte block_type = (byte)(header[0] & 0x7f); bool last_block = (header[0] & 0x80) != 0; uint length = header.Mid(1, 3).ToUInt(); overhead += length; // Sanity: First block should be the stream_info metadata if (block_type != 0) { TagLibDebugger.Debug("Ogg.Flac.File.Scan() -- Invalid Ogg/FLAC stream"); return; } stream_info_data = metadata_header.Mid(4, (int)length); // Search through the remaining metadata while (!last_block) { metadata_header = GetPacket(ipacket++); if (metadata_header == null) { return; } header = metadata_header.Mid(0, 4); block_type = (byte)(header[0] & 0x7f); last_block = (header[0] & 0x80) != 0; length = header.Mid(1, 3).ToUInt(); overhead += length; if (block_type == 4) { xiph_comment_data = metadata_header.Mid(4, (int)length); has_xiph_comment = true; comment_packet = ipacket; } else if (block_type > 5) { TagLibDebugger.Debug("Ogg.Flac.File.Scan() -- Unknown metadata block"); } } // End of metadata, now comes the datastream stream_start = overhead; stream_length = Length - stream_start; scanned = true; }
public bool Strip(TagTypes types, bool freeMemory) { FileAccessMode original_mode = Mode; if (IsReadOnly) { TagLibDebugger.Debug("Mpeg.File.Strip() - Cannot strip tags from a read only file."); return(false); } try { Mode = FileAccessMode.Write; } catch (TagLibException) { TagLibDebugger.Debug("Mpeg.File.Strip() - Cannot strip tags from a read only file."); return(false); } if ((types & TagTypes.Id3v2) != 0) { long id3v2_location = FindId3v2(); if (id3v2_location >= 0) { Seek(id3v2_location); Id3v2Header header = new Id3v2Header(ReadBlock((int)Id3v2Header.Size)); if (header.TagSize == 0) { TagLibDebugger.Debug("Mpc.File.Save() -- Id3v2 header is broken. Ignoring."); } else { RemoveBlock(id3v2_location, (int)header.CompleteTagSize); } } if (freeMemory) { id3v2_tag = null; } } long id3v1_location = FindId3v1(); if ((types & TagTypes.Id3v1) != 0) { if (id3v1_location >= 0) { Truncate(id3v1_location); id3v1_location = -1; } if (freeMemory) { id3v1_tag = null; } } if ((types & TagTypes.Ape) != 0) { long ape_location = FindApe(id3v1_location >= 0); if (ape_location != -1) { Seek(ape_location); int ape_size = (int)(new ApeFooter(ReadBlock((int)ApeFooter.Size))).CompleteTagSize; ape_location = ape_location + ApeFooter.Size - ape_size; RemoveBlock(ape_location, ape_size); } if (freeMemory) { ape_tag = null; } } tag.SetTags(id3v2_tag, ape_tag, id3v1_tag); Mode = original_mode; return(true); }
public Mpeg4AppleElementaryStreamDescriptor(Mpeg4BoxHeader header, Mpeg4Box parent) : base(header, parent) { //WARNING: this was changed from accessing the Data property directy to instead // use LoadBoxData which returns a reference to the underlying data. This change // was required to avoid accessing the virtual methods that the Data property uses // Everything should still work but if it doesn't then this change is the culprit... decoderConfiguration = new ByteVector(); uint length; // This box contains a ton of information. int offset = 0; // This is a safe alternative to the Data property // it will be a reference to the same underlying structure and so should work identically to Data ByteVector boxData = LoadBoxData(); // Elementary Stream Descriptor Tag if (boxData[offset++] == 3) { // We have a descriptor tag. Check that it'field at least 20 long. if ((length = ReadLength(boxData, offset)) < 20) { TagLibDebugger.Debug("TagLib.Mpeg4.AppleElementaryStreamDescriptor () - Could not read data. Too small."); return; } offset += 4; streamId = boxData.Mid(offset, 2).ToShort(); offset += 2; streamPriority = boxData[offset++]; } else { // The tag wasn'type found, so the next two byte are the ID, and // after that, business as usual. streamId = boxData.Mid(offset, 2).ToShort(); offset += 2; } // Verify that the next data is the Decoder Configuration Descriptor // Tag and escape if it won'type work out. if (boxData[offset++] != 4) { TagLibDebugger.Debug("TagLib.Mpeg4.AppleElementaryStreamDescriptor () - Could not identify decoder configuration descriptor."); return; } // Check that it'field at least 15 long. if ((length = ReadLength(boxData, offset)) < 15) { TagLibDebugger.Debug("TagLib.Mpeg4.AppleElementaryStreamDescriptor () - Could not read data. Too small."); return; } offset += 4; // Read a lot of good info. objectTypeId = boxData[offset++]; streamType = boxData[offset++]; bufferSize = boxData.Mid(offset, 3).ToUInt(); offset += 3; maximumBitrate = boxData.Mid(offset, 4).ToUInt(); offset += 4; averageBitrate = boxData.Mid(offset, 4).ToUInt(); offset += 4; // Verify that the next data is the Decoder Specific Descriptor // Tag and escape if it won'type work out. if (boxData[offset++] != 5) { TagLibDebugger.Debug("TagLib.Mpeg4.AppleElementaryStreamDescriptor () - Could not identify decoder specific descriptor."); return; } // The rest of the info is decoder specific. length = ReadLength(boxData, offset); offset += 4; decoderConfiguration = boxData.Mid(offset, (int)length); }
public static Id3v2Frame CreateFrame(ByteVector data, uint version) { Id3v2FrameHeader header = new Id3v2FrameHeader(data, version); ByteVector frameId = header.FrameId; // A quick sanity check -- make sure that the frameId is 4 uppercase // Latin1 characters. Also make sure that there is data in the frame. if (frameId == null || frameId.Count != (version < 3 ? 3 : 4) || header.FrameSize < 0) { return(null); } foreach (byte b in frameId) { char c = (char)b; if ((c < 'A' || c > 'Z') && (c < '1' || c > '9')) { return(null); } } // Windows Media Player may create zero byte frames. Just send them // off as unknown. if (header.FrameSize == 0) { return(new Id3v2UnknownFrame(data, header)); } // TagLib doesn'type mess with encrypted frames, so just treat them // as unknown frames. if (header.Compression) { TagLibDebugger.Debug("Compressed frames are currently not supported."); return(new Id3v2UnknownFrame(data, header)); } if (header.Encryption) { TagLibDebugger.Debug("Encrypted frames are currently not supported."); return(new Id3v2UnknownFrame(data, header)); } if (!UpdateFrame(header)) { header.TagAlterPreservation = true; return(new Id3v2UnknownFrame(data, header)); } foreach (FrameCreator creator in frameCreators) { Id3v2Frame frame = creator(data, header); if (frame != null) { return(frame); } } // UpdateFrame() might have updated the frame ID. frameId = header.FrameId; // This is where things get necissarily nasty. Here we determine which // Frame subclass (or if none is found simply an Frame) based // on the frame ID. Since there are a lot of possibilities, that means // a lot of if blocks. // Text Identification (frames 4.2) if (frameId.StartsWith("T")) { Id3v2TextIdentificationFrame frame = frameId != "TXXX" ? new Id3v2TextIdentificationFrame(data, header) : new Id3v2UserTextIdentificationFrame(data, header); if (useDefaultEncoding) { frame.TextEncoding = defaultEncoding; } return(frame); } // Comments (frames 4.10) if (frameId == "COMM") { Id3v2CommentsFrame frame = new Id3v2CommentsFrame(data, header); if (useDefaultEncoding) { frame.TextEncoding = defaultEncoding; } return(frame); } // Attached Picture (frames 4.14) if (frameId == "APIC") { Id3v2AttachedPictureFrame f = new Id3v2AttachedPictureFrame(data, header); if (useDefaultEncoding) { f.TextEncoding = defaultEncoding; } return(f); } // Relative Volume Adjustment (frames 4.11) if (frameId == "RVA2") { return(new Id3v2RelativeVolumeFrame(data, header)); } // Unique File Identifier (frames 4.1) if (frameId == "UFID") { return(new Id3v2UniqueFileIdentifierFrame(data, header)); } // Private (frames 4.27) if (frameId == "PRIV") { return(new Id3v2PrivateFrame(data, header)); } return(new Id3v2UnknownFrame(data, header)); }