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;
        }
Exemple #3
0
        ////------------------------------------------------------------------------------------------------------------------------------
        /// <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();
            }
        }