////------------------------------------------------------------------------------------------------------------------------------ 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; }
/// <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> /// 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); }
/// <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 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; }
/// <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 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; }
////------------------------------------------------------------------------------------------------------------------------------ /// <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(); } }