private bool ReadFieldInstance(StreamBuffer sb, long maximumFieldSize) { if (sb == null) throw new ArgumentNullException("sb"); string identifier = sb.ReadString(FieldIdentifierLength); string strFieldSize = sb.ReadString(FieldSizeLength); int fieldSize; if (!Int32.TryParse(strFieldSize, out fieldSize)) { #if DEBUG return false;//throw new InvalidDataException(String.Format("Size value for field {0} is not an int: {1}", identifier, strFieldSize)); #else return false; #endif } Identifier = identifier; _data = new byte[fieldSize]; sb.Read(_data, fieldSize); if (!IsValidData(_data)) return false; Data = _data; return true; }
/// <summary> /// Initializes a new instance of the <see cref="VbriHeader"/> class. /// </summary> /// <param name="firstFrame">The first frame.</param> /// <param name="firstFrameBuffer">The first frame buffer.</param> /// <param name="offset">The offset.</param> public VbriHeader(MpaFrame firstFrame, StreamBuffer firstFrameBuffer, long offset) : base(firstFrame, firstFrameBuffer, offset, VbrHeaderType.Vbri) { /* FhG VBRI Header size description 4 'VBRI' (ID) 2 version 2 delay 2 quality 4 # bytes 4 # frames 2 table size (for TOC) 2 table scale (for TOC) 2 size of a table entry (max. size = 4 byte (must be stored in an integer)) 2 frames per table entry ?? dynamic table consisting out of frames with size 1-4 whole length in table size! (for TOC) */ // name Name = firstFrameBuffer.ReadString(4); // version Version = (short)firstFrameBuffer.ReadBigEndianInt16(); // delay _delay = firstFrameBuffer.ReadBigEndianInt16(); // quality Quality = firstFrameBuffer.ReadBigEndianInt16(); // size of the file, in bytes, of all the data FileSize = firstFrameBuffer.ReadBigEndianInt32(); // amount of frames FrameCount = firstFrameBuffer.ReadBigEndianInt32(); // number of entries in the table (for TOC) TableEntries = (short)firstFrameBuffer.ReadBigEndianInt16(); // table scale (for TOC) TableScale = (short)firstFrameBuffer.ReadBigEndianInt16(); // size of a table entry (in bytes) TableEntrySize = (short)firstFrameBuffer.ReadBigEndianInt16(); // frames per table entry FramesPerTableEntry = (short)firstFrameBuffer.ReadBigEndianInt16(); // dynamic table consisting out of frames TableLength = TableEntries * TableEntrySize; Toc = new int[TableEntries + 1]; for (int i = 0; i <= TableEntries; i++) { int value = firstFrameBuffer.ReadBigEndianInt(TableEntrySize); Toc[i] = value * TableScale; } _totalLengthMilliseconds = firstFrame.AudioLength * FrameCount; }
////------------------------------------------------------------------------------------------------------------------------------ private static Lyrics3v2Field ReadField(StreamBuffer sb, long maximumFieldSize) { if (sb == null) throw new ArgumentNullException("sb"); string identifier = sb.ReadString(FieldIdentifierLength, false, false); Lyrics3v2Field field = GetField(identifier); return field.ReadFieldInstance(sb, maximumFieldSize) ? field : null; }
/// <summary> /// Initializes a new instance of the <see cref="XingHeader"/> class. /// </summary> /// <param name="firstFrame">The first frame.</param> /// <param name="firstFrameBuffer">The first frame buffer.</param> /// <param name="offset">The offset.</param> public XingHeader(MpaFrame firstFrame, StreamBuffer firstFrameBuffer, long offset) : base(firstFrame, firstFrameBuffer, offset, VbrHeaderType.Xing) { /* XING VBR-Header size description 4 'Xing' or 'Info' 4 flags (indicates which fields are used) 4 frames (optional) 4 bytes (optional) 100 toc (optional) 4 a VBR quality indicator: 0=best 100=worst (optional) --------- + 120 bytes * * NOTE: the frames (frameCount) in the XING header does not include its own frame. * So the total frames is actually XING framecount + 1 */ // name of the tag as found in the file Name = firstFrameBuffer.ReadString(4); // The flags indicate which fields are used in the XING header Flags = firstFrameBuffer.ReadBigEndianInt32(); // Extract total frames in the file (XING header excludes it's own frame) if ((Flags & XingHeaderFlags.FrameCountFlag) != 0) FrameCount = firstFrameBuffer.ReadBigEndianInt32(); // Extract size of the file, in bytes if ((Flags & XingHeaderFlags.FileSizeFlag) != 0) FileSize = firstFrameBuffer.ReadBigEndianInt32(); // Extract TOC (Table of Contents) for more accurate seeking if ((Flags & XingHeaderFlags.TocFlag) != 0) { Toc = new int[100]; for (int i = 0; i < 100; i++) Toc[i] = firstFrameBuffer.ReadByte(); } if ((Flags & XingHeaderFlags.VbrScaleFlag) != 0) Quality = firstFrameBuffer.ReadBigEndianInt32(); // The LAME tag is always 120 bytes after the XING header - regardless of which fields are used LameTag = LameTag.FindTag(firstFrameBuffer, offset + 120); }
/// <summary> /// Finds the <see cref="VbriHeader"/> header within the <paramref name="firstFrame"/>. /// </summary> /// <param name="firstFrame">The first frame.</param> /// <returns>The VBRI header if found; otherwise, null.</returns> /// <remarks> /// The VBRI header is located exactly 32 bytes after the end of the first MPEG audio header in the file. /// It will compare the first 4 bytes against the <see cref="HeaderIndicator"/> /// to see if the header contains a <see cref="VbriHeader"/> or not. /// </remarks> public static new VbriHeader FindHeader(MpaFrame firstFrame) { if (firstFrame == null) throw new ArgumentNullException("firstFrame"); using (StreamBuffer buffer = new StreamBuffer()) { byte[] data = firstFrame.ToByteArray(); buffer.Write(data); // 32 bytes = data indicating silence const long Offset = MpaFrame.FrameHeaderSize + SilenceDataSize; buffer.Seek(Offset, SeekOrigin.Begin); string tagName = buffer.ReadString(4, false, false); return String.Compare(tagName, HeaderIndicator, StringComparison.OrdinalIgnoreCase) == 0 ? new VbriHeader(firstFrame, buffer, Offset) : null; } }
/// <summary> /// Finds the <see cref="VbriHeader"/> header within the <paramref name="firstFrame"/>. /// </summary> /// <param name="firstFrame">The first frame.</param> /// <returns>The VBRI header if found; otherwise, null.</returns> /// <remarks> /// The XING header is located after the side information in Layer III in the first MPEG audio header in the file. /// It will compare the first 4 bytes against the <see cref="VbrHeaderIndicator"/> /// to see if the header contains a <see cref="XingHeader"/> or not. /// </remarks> public static new XingHeader FindHeader(MpaFrame firstFrame) { if (firstFrame == null) throw new ArgumentNullException("firstFrame"); long offset = MpaFrame.FrameHeaderSize + firstFrame.SideInfoSize; using (StreamBuffer buffer = new StreamBuffer()) { byte[] data = firstFrame.ToByteArray(); buffer.Write(data); buffer.Seek(offset, SeekOrigin.Begin); string tagName = buffer.ReadString(4, false, false); if ((String.Compare(tagName, VbrHeaderIndicator, StringComparison.OrdinalIgnoreCase) == 0) || (String.Compare(tagName, CbrHeaderIndicator, StringComparison.OrdinalIgnoreCase) == 0)) return new XingHeader(firstFrame, buffer, offset); } return null; }
private static string ReadTextField(StreamBuffer sb) { if (sb == null) throw new ArgumentNullException("sb"); int size = sb.ReadInt16(); return sb.ReadString(size); }
private static MusicMatchHeader ReadHeaderFooter(StreamBuffer stream, long startHeaderPosition, long endHeaderPosition, IList<byte> identifierBytes, TagOrigin tagOrigin) { if (stream == null) throw new ArgumentNullException("stream"); stream.Position = startHeaderPosition; while (startHeaderPosition < endHeaderPosition) { int y = 0; while (stream.ReadByte() == identifierBytes[y++]) { startHeaderPosition++; if (y != identifierBytes.Count) continue; if (tagOrigin == TagOrigin.Start) { MusicMatchHeader header = new MusicMatchHeader { Position = stream.Position - identifierBytes.Count, Padding1 = new byte[2], Padding2 = new byte[2], Padding3 = new byte[2], SpacePadding2 = new byte[226] }; // 0x00 0x00 Padding stream.Read(header.Padding1, header.Padding1.Length); // <8-byte numerical ASCII string> header.XingEncoderVersion = stream.ReadString(8); // 0x00 0x00 Padding stream.Read(header.Padding2, header.Padding2.Length); // Xing encoder version <8-byte numerical ASCII string> header.MusicMatchVersion = stream.ReadString(8); // 0x00 0x00 Padding stream.Read(header.Padding3, header.Padding3.Length); // Space padding <226 * 0x20 > stream.Read(header.SpacePadding2, header.SpacePadding2.Length); ValidateHeader(header, null); return header; } if (tagOrigin == TagOrigin.End) { MusicMatchHeader footer = new MusicMatchHeader { Position = stream.Position - identifierBytes.Count, SpacePadding1 = new byte[13], SpacePadding2 = new byte[12] }; // Space padding <13 * 0x20> stream.Read(footer.SpacePadding1, 13); // version <4-byte numerical ASCII string> e.g. 3.05 footer.MusicMatchVersion = stream.ReadString(4); // Space padding <12 * 0x20> stream.Read(footer.SpacePadding2, 12); ValidateHeader(null, footer); return footer; } } startHeaderPosition++; } return null; }
////------------------------------------------------------------------------------------------------------------------------------ private bool ReadStream(StreamBuffer sb) { if (sb == null) throw new ArgumentNullException("sb"); int length = sb.ReadInt32(); Vendor = sb.ReadString(length); length = sb.ReadInt32(); int commentsRead = 0; _comments.Clear(); while ((commentsRead < length) && (sb.Position <= sb.Length)) { _comments.Add(VorbisComment.ReadStream(sb)); commentsRead++; } return true; }
////------------------------------------------------------------------------------------------------------------------------------ private static ApeItem ReadItem(ApeVersion version, StreamBuffer sb, long maximumItemSize) { if (sb == null) throw new ArgumentNullException("sb"); // Find the item. long startPosition = sb.Position; long bytesToSkip = GetBytesUntilNextItem(version, sb, maximumItemSize); // Not found; return null. if (bytesToSkip == -1) return null; // Move the stream position to the start of the frame. sb.Position = startPosition + bytesToSkip; int valueSize = sb.ReadLittleEndianInt32(); if ((valueSize <= 0) || (valueSize > ApeTag.MaxAllowedSize)) return null; int flags = sb.ReadLittleEndianInt32(); maximumItemSize -= 8; int itemKeyLengthBytes = 0; long maximumBytesToRead = maximumItemSize; if (maximumBytesToRead > 0) { for (int y = 0; y < maximumBytesToRead; y++) { int character = sb.ReadByte(); if (character == 0x00) // 0x00 byte - string terminator. break; itemKeyLengthBytes++; } } // Name sb.Seek(-(itemKeyLengthBytes + 1), SeekOrigin.Current); string key = sb.ReadString(itemKeyLengthBytes, Encoding.UTF8); if ((key.Length < MinKeyLengthCharacters) || (itemKeyLengthBytes > MaxKeyLengthBytes)) return null; // Skip Item key terminator (0x00 byte) int keyTerminator = sb.ReadByte(); if (keyTerminator != 0x00) return null; ApeItem item = GetItem(version, key, flags); return item.ReadItem(version, valueSize, sb, maximumItemSize) ? item : null; }
public void ReadStringTestLengthBytesAndEncoding() { const string Value = "This is a test. 日本語が含まれてます。"; const string Expected = "This is a test. 日本語が含まれてます。"; Encoding encoding = Encoding.UTF8; int lengthBytes = encoding.GetByteCount(Value); StreamBuffer target = new StreamBuffer(encoding.GetBytes(Value)) { Position = 0 }; string actual = target.ReadString(lengthBytes, encoding); Assert.AreEqual(Expected, actual); }
public void ReadStringTestLengthBytes() { const string Value = "This is a test."; const string Expected = "This is a test."; int lengthBytes = Encoding.ASCII.GetByteCount(Value); StreamBuffer target = new StreamBuffer(Encoding.ASCII.GetBytes(Value)) { Position = 0 }; string actual = target.ReadString(lengthBytes); Assert.AreEqual(Expected, actual); }
public void ReadStringTestEncodingAndCustomStringTerminatorAsString() { const string Value = "This is a test.||日本語が含まれてます。"; const string Expected = "This is a test."; const string CustomStringTerminator = "||"; Encoding encoding = Encoding.UTF8; StreamBuffer target = new StreamBuffer(encoding.GetBytes(Value)) { Position = 0 }; string actual = target.ReadString(encoding, CustomStringTerminator); Assert.AreEqual(Expected, actual); }
////------------------------------------------------------------------------------------------------------------------------------ /// <summary> /// Finds a <see cref="LameTag"/> in the given stream. /// </summary> /// <param name="firstFrameBuffer">The first frame buffer.</param> /// <param name="offset">The offset.</param> /// <returns>The <see cref="LameTag"/> when found; otherwise, null.</returns> public static LameTag FindTag(StreamBuffer firstFrameBuffer, long offset) { if (firstFrameBuffer == null) throw new ArgumentNullException("firstFrameBuffer"); // If limiting the LAME string to 9 bytes "LAME X.YZu", the extension revision 0 could take 27 bytes and it would still fit a 64 kbit 48kHz frame. firstFrameBuffer.Seek(offset, SeekOrigin.Begin); string tagName = firstFrameBuffer.ReadString(4, false, false); return String.Compare(tagName, "LAME", StringComparison.OrdinalIgnoreCase) == 0 ? new LameTag(firstFrameBuffer) : null; }
////------------------------------------------------------------------------------------------------------------------------------ /// <summary> /// Initializes a new instance of the <see cref="LameTag"/> class. /// </summary> /// <param name="firstFrameBuffer">The first frame buffer containing a <see cref="LameTag"/>.</param> public LameTag(StreamBuffer firstFrameBuffer) { // string lameTag = "LAME" firstFrameBuffer.ReadString(4); float ver; float.TryParse(firstFrameBuffer.ReadString(4), out ver); Version = ver; firstFrameBuffer.Seek(-8, SeekOrigin.Current); if (Version < 3.90f) { // Initial LAME info, 20 bytes for LAME tag. for example, "LAME3.12 (beta 6)" // LAME prior to 3.90 writes only a 20 byte encoder string. EncoderVersion = firstFrameBuffer.ReadString(20); } else { EncoderVersion = firstFrameBuffer.ReadString(9); // Revision Information Tag + VBR Info int infoAndVbr = firstFrameBuffer.ReadByte(); // Revision information in 4 MSB InfoTagRevision = infoAndVbr >> 4; if (InfoTagRevision == Formats.InfoTagRevision.Reserved) throw new ArgumentException("InfoTagRevision bit is set to reserved (0xF)"); // VBR info in 4 LSB VbrMethod = infoAndVbr & 0x0F; // lowpass information, multiply by 100 to get hz LowpassFilterValue = firstFrameBuffer.ReadByte() * 100; // Radio replay gain fields // Peak signal amplitude PeakSignalAmplitude = firstFrameBuffer.ReadFloat(); // Radio Replay Gain RadioReplayGain = firstFrameBuffer.ReadInt16(); // Audiophile Replay Gain AudiophileReplayGain = firstFrameBuffer.ReadInt16(); // Encoding Flags + ATH type int encodingFlagsAndAthType = firstFrameBuffer.ReadByte(); // Encoding Flags in 4 MSB EncodingFlags = encodingFlagsAndAthType >> 4; // LAME ATH Type in 4 LSB AthType = encodingFlagsAndAthType & 0x0F; // If ABR, this will be the specified bitrate // Otherwise (CBR/VBR), the minimal bitrate (255 means 255 or bigger) BitRate = firstFrameBuffer.ReadByte(); // the 12 bit values (0-4095) of how many samples were added at start (encoder delay) // in X and how many 0-samples were padded at the end in Y to complete the last frame. EncoderDelays = firstFrameBuffer.ReadInt(3); EncoderDelaySamples = EncoderDelays & 0xFFF; EncoderDelayPaddingSamples = EncoderDelays >> 12; ////int numberSamplesInOriginalWav = frameCount Misc = firstFrameBuffer.ReadByte(); // Any mp3 can be amplified by a factor 2 ^ ( x * 0.25) in a lossless manner by a tool like eg. mp3gain // if done so, this 8-bit field can be used to log such transformation happened so that any given time it can be undone. Mp3Gain = firstFrameBuffer.ReadByte(); // Preset and surround info PresetSurroundInfo = firstFrameBuffer.ReadInt16(); // 32 bit integer filed containing the exact length in bytes of the mp3 file originally made by LAME excluded ID3 tag info at the end. MusicLength = firstFrameBuffer.ReadBigEndianInt32(); // Contains a CRC-16 of the complete mp3 music data as made originally by LAME. _musicCrc = (short)firstFrameBuffer.ReadBigEndianInt16(); // contains a CRC-16 of the first 190 bytes (0x00 - 0xBD) of the Info header frame. // This field is calculated at the end, once all other fields are completed. _infoTagCrc = (short)firstFrameBuffer.ReadBigEndianInt16(); } }