public void ReadIntTest3Bytes() { const int Value = 0x123456; const int Expected = 0x123456; StreamBuffer target = new StreamBuffer(BitConverter.GetBytes(Value)) { Position = 0 }; int actual = target.ReadInt(3); Assert.AreEqual(Expected, actual); }
/// <summary> /// Reads the extended header. /// </summary> /// <param name="streamBuffer">The stream buffer.</param> /// <param name="tag">The tag.</param> /// <param name="crcData">The CRC data.</param> /// <returns> /// The extended header if used; otherwise, null. /// </returns> /// <exception cref="System.IO.InvalidDataException">Thrown if the extended header size does not match the amount of bytes read for the extended header.</exception> private static Id3v2ExtendedHeader ReadExtendedHeader(StreamBuffer streamBuffer, Id3v2Tag tag, out int crcData) { crcData = 0; if (!tag.UseExtendedHeader) return null; // The extended header contains information that can provide further insight in the structure of the tag, // but is not vital to the correct parsing of the tag information; hence the extended header is optional. int extendedHeaderSize = 0; long startPosition = streamBuffer.Position; Id3v2ExtendedHeader extendedHeader = null; if ((tag.Version >= Id3v2Version.Id3v230) && (tag.Version < Id3v2Version.Id3v240)) { // Where the 'Extended header size', currently 6 or 10 bytes, excludes itself. extendedHeaderSize = streamBuffer.ReadBigEndianInt32() + 4; int extendedFlags = streamBuffer.ReadBigEndianInt16(); extendedHeader = Id3v2ExtendedHeader.InitExtendedHeader(tag.Version, extendedFlags); extendedHeader.PaddingSize = streamBuffer.ReadBigEndianInt32(); if (extendedHeader.CrcDataPresent) crcData = streamBuffer.ReadBigEndianInt32(); } else if (tag.Version >= Id3v2Version.Id3v240) { // Where the 'Extended header size' is the size of the whole extended header, stored as a 32 bit synchsafe integer. // An extended header can thus never have a size of fewer than six bytes. extendedHeaderSize = streamBuffer.ReadBigEndianInt32(); extendedHeaderSize = Id3v2Tag.GetUnsynchedValue(extendedHeaderSize); int extendedFlagsFieldLength = streamBuffer.ReadByte(); // The extended flags field, with its size described by 'number of flag bytes', is defined as: %0bcd0000 int extendedFlags = streamBuffer.ReadInt(extendedFlagsFieldLength); extendedHeader = Id3v2ExtendedHeader.InitExtendedHeader(tag.Version, extendedFlags); extendedHeader.SetExtendedFlagsFieldLength(extendedFlagsFieldLength); // Each flag that is set in the extended header has data attached, // which comes in the order in which the flags are encountered (i.e. the data for flag 'b' comes before the data for flag 'c'). // Unset flags cannot have any attached data. All unknown flags MUST be unset and their corresponding data removed when a tag is modified. // // Every set flag's data starts with a length byte, which contains a value between 0 and 127 ($00 - $7f), // followed by data that has the field length indicated by the length byte. // If a flag has no attached data, the value $00 is used as length byte. // If this flag is set, the present tag is an update of a tag found earlier in the present file or stream. // If frames defined as unique are found in the present tag, they are to override any corresponding ones found in the earlier tag. // This flag has no corresponding data. if (extendedHeader.TagIsUpdate) { // If a flag has no attached data, the value $00 is used as length byte. streamBuffer.ReadByte(); } // If this flag is set, a CRC-32 [ISO-3309] data is included in the extended header. // The CRC is calculated on all the data between the header and footer as indicated by the header's tag length field, minus the extended header. // Note that this includes the padding (if there is any), but excludes the footer. // The CRC-32 is stored as an 35 bit synchsafe integer, leaving the upper four bits always zeroed. if (extendedHeader.CrcDataPresent) { long crcBytes = streamBuffer.ReadBigEndianInt64(5); crcData = (int)Id3v2Tag.GetUnsynchedValue(crcBytes, 5); } // For some applications it might be desired to restrict a tag in more ways than imposed by the ID3v2 specification. // Note that the presence of these restrictions does not affect how the tag is decoded, merely how it was restricted before encoding. // If this flag is set the tag is restricted as follows: if (extendedHeader.TagIsRestricted) { // Restrictions: %ppqrrstt byte tagRestrictions = (byte)streamBuffer.ReadByte(); extendedHeader.TagRestrictions = new Id3v2TagRestrictions { // p - Tag size restrictions TagSizeRestriction = (Id3v2TagSizeRestriction)(tagRestrictions & Id3v2TagRestrictions.TagSizeRestrictionFlags), // q - Text encoding restrictions TextEncodingRestriction = (Id3v2TextEncodingRestriction)(tagRestrictions & Id3v2TagRestrictions.TextEncodingRestrictionFlags), // r - Text fields size restrictions TextFieldsSizeRestriction = (Id3v2TextFieldsSizeRestriction)(tagRestrictions & Id3v2TagRestrictions.TextFieldsSizeRestrictionFlags), // s - Image encoding restrictions ImageEncodingRestriction = (Id3v2ImageEncodingRestriction)(tagRestrictions & Id3v2TagRestrictions.ImageEncodingRestrictionFlags), // t - Image size restrictions ImageSizeRestriction = (Id3v2ImageSizeRestriction)(tagRestrictions & Id3v2TagRestrictions.ImageSizeRestrictionFlags) }; } } if ((streamBuffer.Position - startPosition) != extendedHeaderSize) { throw new InvalidDataException( string.Format( "ExtendedHeaderSize does not match amount of bytes read: expected {0} but got {1} bytes.", extendedHeaderSize, streamBuffer.Position - startPosition)); } return extendedHeader; }
////------------------------------------------------------------------------------------------------------------------------------ /// <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(); } }