////------------------------------------------------------------------------------------------------------------------------------ public static FlacResidual Read(StreamBuffer sb, int blockSize, int order) { FlacResidual residual = new FlacResidual { _values = sb.ReadByte() }; int partitions = 1 << residual.PartitionOrder; residual.RicePartitions = new FlacRicePartition[partitions]; for (int i = 0; i < partitions; i++) residual.RicePartitions[i] = FlacRicePartition.Read(sb, i, residual.PartitionOrder, order, blockSize, residual.CodingMethod); return residual; }
/// <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); }
private static ApeHeader ReadHeader(StreamBuffer stream, long startHeaderPosition, long endHeaderPosition) { if (stream == null) throw new ArgumentNullException("stream"); stream.Position = startHeaderPosition; while (startHeaderPosition < endHeaderPosition) { int y = 0; while (stream.ReadByte() == TagIdentifierBytes[y++]) { startHeaderPosition++; if (y != TagIdentifierBytes.Length) continue; ApeHeader hdr = new ApeHeader { Position = stream.Position - TagIdentifierBytes.Length, Version = (ApeVersion)(stream.ReadLittleEndianInt32() / 1000), Size = stream.ReadLittleEndianInt32(), FrameCount = stream.ReadLittleEndianInt32(), Flags = stream.ReadLittleEndianInt32(), ReservedBytes = new byte[8] }; stream.Read(hdr.ReservedBytes, 8); if (IsValidTag(hdr)) return hdr; startHeaderPosition -= ApeTag.HeaderSize; stream.Position = startHeaderPosition + 1; break; } startHeaderPosition++; } return null; }
/// <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; }
////------------------------------------------------------------------------------------------------------------------------------ private static long ReadBigEndianUtf8Int64(StreamBuffer sb, out byte[] utf8Bytes) { //// Decoded long range Coded value //// 0000 0000 ・00 0000 007F 0xxxxxxx //// 0000 0080 ・00 0000 07FF 110xxxxx 10xxxxxx //// 0000 0800 ・00 0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx //// 0001 0000 ・00 001F FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx //// 0020 0000 ・00 03FF FFFF 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx //// 0400 0000 ・00 7FFF FFFF 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx //// 8000 0000 ・0F FFFF FFFF 11111110 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx // Where xxx represent the bits in the uncoded value, in the same order. const int ByteCountMask = 0x80; // 10000000 const int ByteMask = 0x3F; // 00111111 int byteCount = 1; long value = sb.ReadByte(); if (value > ByteCountMask) { int bytes = 0; while (((value << bytes) & ByteCountMask) == ByteCountMask) bytes++; value &= ByteMask >> (bytes - 1); for (int i = bytes - 1; i > 0; i--) value = (value << (6 * i)) | ((long)sb.ReadByte() & ByteMask); byteCount += bytes - 1; } sb.Position -= byteCount; utf8Bytes = new byte[byteCount]; sb.Read(utf8Bytes, byteCount); return value; }
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; }
/// <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; }
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; }
////------------------------------------------------------------------------------------------------------------------------------ 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; }
////------------------------------------------------------------------------------------------------------------------------------ private static FlacMetadataBlock ReadBlock(StreamBuffer stream) { int flags = stream.ReadByte(); // Length int length = stream.ReadBigEndianInt(3); if (length >= stream.Length) return null; byte[] data = new byte[length]; stream.Read(data, length); FlacMetadataBlock metadataBlock; FlacMetadataBlockType blockType = (FlacMetadataBlockType)(flags & HeaderFlags.BlockType); switch (blockType) { case FlacMetadataBlockType.Padding: metadataBlock = new FlacPaddingMetadataBlock(); break; case FlacMetadataBlockType.Application: metadataBlock = new FlacApplicationMetadataBlock(); break; case FlacMetadataBlockType.StreamInfo: metadataBlock = new FlacStreamInfoMetadataBlock(); break; case FlacMetadataBlockType.SeekTable: metadataBlock = new FlacSeekTableMetadataBlock(); break; case FlacMetadataBlockType.VorbisComment: metadataBlock = new FlacVorbisCommentsMetadataBlock(); break; case FlacMetadataBlockType.CueSheet: metadataBlock = new FlacCueSheetMetadataBlock(); break; case FlacMetadataBlockType.Picture: metadataBlock = new FlacPictureMetadataBlock(); break; default: metadataBlock = new FlacMetadataBlock(); break; } metadataBlock.Flags = flags; metadataBlock.Data = data; return metadataBlock; }
/// <summary> /// Reads the <see cref="Id3v2FrameEncodingType"/> from the stream. /// </summary> /// <param name="streamBuffer">The stream buffer.</param> /// <returns> /// The <see cref="Id3v2FrameEncodingType"/>. /// </returns> /// <remarks> /// Since the encoding type is first read from the stream, the position should be set to the byte containing the encoding type. /// If the encoding type is <see cref="Id3v2FrameEncodingType.UTF16LittleEndian"/>, /// it will try to read the byte order marker from the stream /// to see if the encoding type is <see cref="Id3v2FrameEncodingType.UTF16BigEndian"/>. /// If no byte order marker is found or if the byte order marker is not recognized, /// it will return <see cref="Id3v2FrameEncodingType.UTF16LittleEndian"/> as default encoding. /// <para /> /// The position of the stream will not be modified. /// </remarks> public static Id3v2FrameEncodingType ReadEncodingTypeFromStream(StreamBuffer streamBuffer) { int encodingByte = streamBuffer.ReadByte(); if ((encodingByte == -1) || !IsValidEncodingType(encodingByte)) return Id3v2FrameEncodingType.Default; if (encodingByte > (byte)Id3v2FrameEncodingType.UTF16LittleEndian) encodingByte += 1; // Cast the encoding type. Id3v2FrameEncodingType encodingType = (Id3v2FrameEncodingType)encodingByte; // If it's not little endian, then we don't need to figure out if it's little or big; return here. if (encodingType != Id3v2FrameEncodingType.UTF16LittleEndian) return encodingType; // See which byte order the frame encoding is. byte[] byteOrderMarker = new byte[2]; // Not enough bytes available; probably little endian then. if (streamBuffer.Read(byteOrderMarker, 2, false) < 2) return Id3v2FrameEncodingType.UTF16LittleEndian; // Little endian byte order marker? if (((byteOrderMarker[0] == 0xFF) && (byteOrderMarker[1] == 0xFE)) || ((byteOrderMarker[0] == 0x00) && (byteOrderMarker[1] == 0x00))) return Id3v2FrameEncodingType.UTF16LittleEndian; // Big endian byte order marker? if ((byteOrderMarker[0] == 0xFE) && (byteOrderMarker[1] == 0xFF)) return Id3v2FrameEncodingType.UTF16BigEndian; // Default to little endian. return Id3v2FrameEncodingType.UTF16LittleEndian; }
public void ReadByteTestMovePositionFalse() { const byte Value = 0xEF; const byte Expected = 0xEF; StreamBuffer target = new StreamBuffer(BitConverter.GetBytes(Value)) { Position = 0 }; int actual = target.ReadByte(false); Assert.AreEqual(Expected, actual); Assert.IsTrue(target.Position == 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(); } }