예제 #1
0
        ////------------------------------------------------------------------------------------------------------------------------------
        public static FlacRicePartition Read(StreamBuffer sb, int partitionNumber, int partitionOrder, int predictOrder, int blockSize, FlacResidualCodingMethod codingMethod)
        {
            FlacRicePartition ricePartition = new FlacRicePartition { _riceParameter = sb.ReadBigEndianInt32(), _codingMethod = codingMethod };
            int riceParameter = ricePartition._riceParameter & ((codingMethod == FlacResidualCodingMethod.PartitionedRice) ? 0x1F : 0xF);
            if (partitionOrder == 0)
                ricePartition.Samples = blockSize - predictOrder;
            else if (partitionNumber == 0)
                ricePartition.Samples = (blockSize >> partitionOrder) - predictOrder;
            else
                ricePartition.Samples = blockSize >> partitionOrder;

            ricePartition.Residuals = new int[ricePartition.Samples];
            if ((riceParameter < 0xF) || ((codingMethod == FlacResidualCodingMethod.PartitionedRice2) && (riceParameter < 0x1F)))
            {
                for (int i = 0; i < ricePartition.Samples; i++)
                {
                    long msbs = sb.ReadUnaryInt();
                    long lsbs = sb.ReadBigEndianInt32() & (0xFFFFFFFF >> (32 - riceParameter));
                    long value = (msbs << riceParameter) | lsbs;
                    ricePartition.Residuals[i] = ((value & 0x01) == 0x01) ? -((int)(value >> 1)) - 1 : (int)(value >> 1);
                }
            }
            else
            {
                // residuals in unencoded form, sample size read from the next 5
                // bits in the stream.
                int size = sb.ReadBigEndianInt32();
                for (int i = 0; i < ricePartition.Samples; i++)
                    ricePartition.Residuals[i] = sb.ReadBigEndianInt32();
            }
            return ricePartition;
        }
예제 #2
0
        /// <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);
        }
예제 #3
0
        /// <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;
        }
 ////------------------------------------------------------------------------------------------------------------------------------
 /// <summary>
 /// Reads the specified stream buffer.
 /// </summary>
 /// <param name="sb">The stream buffer.</param>
 /// <param name="sampeSize">Size of the sample.</param>
 /// <param name="blockSize">Size of the block.</param>
 protected override void Read(StreamBuffer sb, int sampeSize, int blockSize)
 {
     UnencodedSubblocks = new int[blockSize];
     for (int i = 0; i < blockSize; i++)
         UnencodedSubblocks[i] = sb.ReadBigEndianInt32();
 }
        ////------------------------------------------------------------------------------------------------------------------------------
        /// <summary>
        /// Reads the specified stream buffer.
        /// </summary>
        /// <param name="sb">The stream buffer.</param>
        /// <param name="sampeSize">Size of the sample.</param>
        /// <param name="blockSize">Size of the block.</param>
        protected override void Read(StreamBuffer sb, int sampeSize, int blockSize)
        {
            UnencodedWarmUpSamples = new int[Order];
            for (int i = 0; i < Order; i++)
                UnencodedWarmUpSamples[i] = sb.ReadBigEndianInt32();

            int other = sb.ReadBigEndianInt16();
            QuantizedCoefficientsPrecision = (other & 0xF) + 1;
            QuantizedCoefficientShift = (other >> 4) & 0x1F;

            UnencodedPredictorCoefficients = new int[Order];
            for (int i = 0; i < Order; i++)
                UnencodedPredictorCoefficients[i] = sb.ReadBigEndianInt32();

            Residual = FlacResidual.Read(sb, blockSize, Order);
        }
예제 #6
0
        /// <summary>
        /// Reads the header.
        /// </summary>
        /// <param name="sb">The <see cref="StreamBuffer"/>.</param>
        /// <returns>
        /// true if the header is read and valid; otherwise, false.
        /// </returns>
        /// <exception cref="System.ArgumentNullException">Thrown if <paramref name="sb"/> is null.</exception>
        private bool ReadHeader(StreamBuffer sb)
        {
            if (sb == null)
                throw new ArgumentNullException("sb");

            long startPosition = sb.Position;

            // Sync code '11111111111110'
            _header = sb.ReadBigEndianInt32();
            if (((_header >> 18) & FrameSync) != FrameSync)
                return false;

            /*
                Blocking strategy:
                    •0 : fixed-block size stream; frame header encodes the frame number
                    •1 : variable-block size stream; frame header encodes the sample number
            */
            long num = ReadBigEndianUtf8Int64(sb, out _sampleFrameNumberBytes);
            switch (BlockingStrategy)
            {
                case FlacBlockingStrategy.FixedBlocksize:
                    Samples = num;
                    FrameNumber = num
                                  *
                                  (FlacStream.StreamInfoMetadataBlocks.Any()
                                       ? FlacStream.StreamInfoMetadataBlocks.First().MinimumBlockSize
                                       : 1);
                    break;

                case FlacBlockingStrategy.VariableBlocksize:
                    Samples = num;
                    FrameNumber = -1;
                break;
            }

            /*
                Block size in inter-channel samples:
                    •0000 : reserved
                    •0001 : 192 samples
                    •0010-0101 : 576 * (2 ^ (n - 2)) samples, i.e. 576/1152/2304/4608
                    •0110 : get 8 bit (block size - 1) from end of header
                    •0111 : get 16 bit (block size - 1) from end of header
                    •1000-1111 : 256 * (2 ^ (n - 8)) samples, i.e. 256/512/1024/2048/4096/8192/16384/3276
            */
            int blockSize = (_header >> 12) & 0xF;
            switch (blockSize)
            {
                case 0x01:
                    BlockSize = 192;
                    break;

                case 0x02:
                case 0x03:
                case 0x04:
                case 0x05:
                    BlockSize = 576 << (blockSize - 2);
                    break;

                case 0x06:
                    BlockSize = sb.ReadByte() + 1;
                    break;

                case 0x07:
                    BlockSize = sb.ReadBigEndianInt16() + 1;
                    break;

                default:
                    BlockSize = 256 << (blockSize - 8);
                    break;
            }

            /*
                Sample rate:
                    •0000 : get from STREAMINFO metadata block
                    •0001 : 88.2kHz
                    •0010 : 176.4kHz
                    •0011 : 192kHz
                    •0100 : 8kHz
                    •0101 : 16kHz
                    •0110 : 22.05kHz
                    •0111 : 24kHz
                    •1000 : 32kHz
                    •1001 : 44.1kHz
                    •1010 : 48kHz
                    •1011 : 96kHz
                    •1100 : get 8 bit sample rate (in kHz) from end of header
                    •1101 : get 16 bit sample rate (in Hz) from end of header
                    •1110 : get 16 bit sample rate (in tens of Hz) from end of header
                    •1111 : invalid, to prevent sync-fooling string of 1s
            */
            int samplingRate = (_header >> 8) & 0xF;
            if (samplingRate == 0x00)
            {
                SamplingRate = FlacStream.StreamInfoMetadataBlocks.Any()
                                   ? FlacStream.StreamInfoMetadataBlocks.First().SampleRate
                                   : 0;
            }
            else if (samplingRate <= 0x0B)
            {
                SamplingRate = SampleRates[samplingRate];
            }
            else
            {
                switch (samplingRate)
                {
                    case 0x0C:
                        // Sample rate in kHz
                        SamplingRate = sb.ReadByte() * 1000;
                        break;

                    case 0x0D:
                        // Sample rate in Hz
                        SamplingRate = sb.ReadBigEndianInt16();
                        break;

                    case 0x0E:
                        // Sample rate in 10s of Hz
                        SamplingRate = sb.ReadBigEndianInt16() * 10;
                        break;
                }
            }

            /*
                Channel assignment
                    •0000-0111 : (number of independent channels)-1. Where defined, the channel order follows SMPTE/ITU-R recommendations.
                    The assignments are as follows:
                        ◦1 channel: mono
                        ◦2 channels: left, right
                        ◦3 channels: left, right, center
                        ◦4 channels: front left, front right, back left, back right
                        ◦5 channels: front left, front right, front center, back/surround left, back/surround right
                        ◦6 channels: front left, front right, front center, LFE, back/surround left, back/surround right
                        ◦7 channels: front left, front right, front center, LFE, back center, side left, side right
                        ◦8 channels: front left, front right, front center, LFE, back left, back right, side left, side right
                    •1000 : left/side stereo: channel 0 is the left channel, channel 1 is the side(difference) channel
                    •1001 : right/side stereo: channel 0 is the side(difference) channel, channel 1 is the right channel
                    •1010 : mid/side stereo: channel 0 is the mid(average) channel, channel 1 is the side(difference) channel
                    •1011-1111 : reserved
            */
            int channelAssignment = (_header >> 4) & 0xF;
            if (channelAssignment < 0x08)
            {
                ChannelAssignment = FlacChannelAssignment.Independent;
                Channels = channelAssignment + 1;
            }
            else
            {
                Channels = 2;
                switch (channelAssignment)
                {
                    case 0x08:
                        ChannelAssignment = FlacChannelAssignment.LeftSide;
                        break;

                    case 0x09:
                        ChannelAssignment = FlacChannelAssignment.RightSide;
                        break;

                    case 0x0A:
                        ChannelAssignment = FlacChannelAssignment.MidSide;
                        break;
                }
            }

            /*
                Sample size in bits:
                    •000 : get from STREAMINFO metadata block
                    •001 : 8 bits per sample
                    •010 : 12 bits per sample
                    •011 : reserved
                    •100 : 16 bits per sample
                    •101 : 20 bits per sample
                    •110 : 24 bits per sample
                    •111 : reserved
            */
            int sampleSize = (_header >> 0x01) & 0x07;
            SampleSize = (sampleSize == 0x00)
                             ? (FlacStream.StreamInfoMetadataBlocks.Any()
                                    ? FlacStream.StreamInfoMetadataBlocks.First().BitsPerSample
                                    : 0)
                             : SampleSizes[sampleSize];

            _crc8 = sb.ReadByte();

            long endPosition = sb.Position;
            long length = endPosition - startPosition;
            byte[] crcBytes = new byte[length];
            sb.Position -= length;
            sb.Read(crcBytes, (int)length);
            byte crc8 = Crc8.Calculate(crcBytes);
            if (_crc8 != crc8)
                throw new InvalidDataException("Corrupt CRC8.");

            return true;
        }
예제 #7
0
        private static Id3v2Header ReadHeader(StreamBuffer stream, long startHeaderPosition, long endHeaderPosition, byte[] identifierBytes)
        {
            if (stream == null)
                throw new ArgumentNullException("stream");

            stream.Position = startHeaderPosition;

            // Look for a header/footer between the start position and end position.
            while (startHeaderPosition < endHeaderPosition)
            {
                int y = 0;

                // identifierBytes is the HeaderIdentifier or the FooterIdentifier encoded.
                while (stream.ReadByte() == identifierBytes[y++])
                {
                    startHeaderPosition++;
                    if (y != identifierBytes.Length)
                        continue;

                    // The first byte of ID3 version is it's major version, while the second byte is its revision number.
                    // All revisions are backwards compatible while major versions are not.
                    // If software with ID3v2 and below support should encounter version three or higher it should simply ignore the whole tag.
                    // Version and revision will never be $FF.
                    int majorRevision = stream.ReadByte();
                    int revisionNumber = stream.ReadByte();
                    if ((majorRevision >= 0x10) || (revisionNumber >= 0x10))
                    {
                        stream.Position -= 2;
                        break;
                    }

                    // Return header/footer found.
                    return new Id3v2Header
                               {
                                   Position = stream.Position - identifierBytes.Length - 2,
                                   Identifier = Encoding.ASCII.GetString(identifierBytes),
                                   Version = (Id3v2Version)((majorRevision * 10) + revisionNumber),

                                   // One byte of flags.
                                   // ID3v2.2.0: %xx000000
                                   Flags = (byte)stream.ReadByte(),

                                   // The ID3 tag size is encoded with four bytes where the first bit (bit 7) is set to zero in every byte, making a total of 28 bits.
                                   // The zeroed bits are ignored, so a 257 bytes long tag is represented as $00 00 02 01.
                                   // ID3v2.2.0: The ID3 tag size is the size of the complete tag after unsychronisation, including padding, excluding the header (total tag size - 10).
                                   Size = Id3v2Tag.GetUnsynchedValue(stream.ReadBigEndianInt32())
                               };
                }
                startHeaderPosition++;
            }
            return null;
        }
예제 #8
0
        /// <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;
        }
예제 #9
0
        ////------------------------------------------------------------------------------------------------------------------------------
        private static FlacSubFrame ReadSubFrame(StreamBuffer sb, int channel, FlacFrame flacFrame)
        {
            if (sb == null)
                throw new ArgumentNullException("sb");

            if (flacFrame == null)
                throw new ArgumentNullException("flacFrame");

            FlacSubFrame frame;
            int header = sb.ReadBigEndianInt32(false);
            int type = (header >> 1) & 0x7E;
            switch (type)
            {
                case 0x00:
                    frame = new FlacConstantSubFrame(flacFrame);
                    break;

                case 0x01:
                    frame = new FlacVerbatimSubFrame(flacFrame);
                    break;

                default:
                    if ((type >= 0x08) && (type <= 0x0C))
                        frame = new FlacFixedSubFrame(flacFrame);
                    else if (type >= 0x20)
                        frame = new FlacLinearPredictorSubFrame(flacFrame);
                    else
                        frame = new FlacSubFrame(flacFrame);
                    break;
            }
            frame.ReadSubFrame(sb, channel);
            return frame;
        }
예제 #10
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();
            }
        }