public static int XingHeaderOffset(MpegVersion version, MpegChannelMode channelMode) { if (version == MpegVersion.One) { if (channelMode == MpegChannelMode.SingleChannel) { return(0x15); } else { return(0x24); } } else { if (channelMode == MpegChannelMode.SingleChannel) { return(0x0D); } else { return(0x15); } } }
////////////////////////////////////////////////////////////////////////// // public methods ////////////////////////////////////////////////////////////////////////// public MpegProperties(MpegFile file, ReadStyle style) : base(style) { this.file = file; duration = TimeSpan.Zero; //bitrate = 0; //sample_rate = 0; //channels = 0; version = MpegVersion.One; //layer = 0; channel_mode = MpegChannelMode.Stereo; //is_copyrighted = false; //is_original = false; Read(); }
////////////////////////////////////////////////////////////////////////// // public methods ////////////////////////////////////////////////////////////////////////// public MpegProperties (MpegFile file, ReadStyle style) : base (style) { this.file = file; duration = TimeSpan.Zero; //bitrate = 0; //sample_rate = 0; //channels = 0; version = MpegVersion.One; //layer = 0; channel_mode = MpegChannelMode.Stereo; //is_copyrighted = false; //is_original = false; Read(); }
////////////////////////////////////////////////////////////////////////// // private methods ////////////////////////////////////////////////////////////////////////// private void Read() { // Since we've likely just looked for the ID3v1 tag, start at the end of the // file where we're least likely to have to have to move the disk head. long last = file.LastFrameOffset; if (last < 0) { TagLibDebugger.Debug("Mpeg.Properties.Read() -- Could not find a valid last MPEG frame in the stream."); return; } file.Seek(last); MpegHeader last_header = new MpegHeader(file.ReadBlock(4)); long first = file.FirstFrameOffset; if (first < 0) { TagLibDebugger.Debug("Mpeg.Properties.Read() -- Could not find a valid first MPEG frame in the stream."); return; } if (!last_header.IsValid) { long pos = last; while (pos > first) { pos = file.PreviousFrameOffset(pos); if (pos < 0) { break; } file.Seek(pos); MpegHeader header = new MpegHeader(file.ReadBlock(4)); if (header.IsValid) { last_header = header; last = pos; break; } } } // Now jump back to the front of the file and read what we need from there. file.Seek(first); MpegHeader first_header = new MpegHeader(file.ReadBlock(4)); if (!first_header.IsValid || !last_header.IsValid) { TagLibDebugger.Debug("Mpeg.Properties.Read() -- Page headers were invalid."); return; } // Check for a Xing header that will help us in gathering information about a // VBR stream. int xing_header_offset = MpegXingHeader.XingHeaderOffset(first_header.Version, first_header.ChannelMode); file.Seek(first + xing_header_offset); MpegXingHeader xing_header = new MpegXingHeader(file.ReadBlock(16)); // Read the length and the bitrate from the Xing header. if (xing_header.IsValid && first_header.SampleRate > 0 && xing_header.TotalFrames > 0) { int [] block_size = { 0, 384, 1152, 1152 }; double time_per_frame = block_size [first_header.Layer]; time_per_frame = first_header.SampleRate > 0 ? time_per_frame / first_header.SampleRate : 0; duration = new TimeSpan((int)(time_per_frame * xing_header.TotalFrames) * TimeSpan.TicksPerSecond); bitrate = (int)(duration > TimeSpan.Zero ? ((xing_header.TotalSize * 8L) / duration.TotalSeconds) / 1000 : 0); } // Since there was no valid Xing header found, we hope that we're in a constant // bitrate file. // TODO: Make this more robust with audio property detection for VBR without a // Xing header. else if (first_header.FrameLength > 0 && first_header.Bitrate > 0) { int frames = (int)((last - first) / first_header.FrameLength + 1); duration = TimeSpan.FromSeconds((double)(first_header.FrameLength * frames) / (double)(first_header.Bitrate * 125) + 0.5); bitrate = first_header.Bitrate; } sample_rate = first_header.SampleRate; channels = first_header.ChannelMode == MpegChannelMode.SingleChannel ? 1 : 2; version = first_header.Version; layer = first_header.Layer; channel_mode = first_header.ChannelMode; is_copyrighted = first_header.IsCopyrighted; is_original = first_header.IsOriginal; }
private void Parse(ByteVector data) { if (data.Count < 4 || data[0] != 0xff) { TagLibDebugger.Debug("Mpeg.Header.Parse() -- First byte did not match MPEG synch."); return; } uint flags = data.ToUInt(); // Check for the second byte'field part of the MPEG synch if ((flags & 0xFFE00000) != 0xFFE00000) { TagLibDebugger.Debug("Mpeg.Header.Parse() -- Second byte did not match MPEG synch."); return; } // Set the MPEG version switch ((flags >> 19) & 0x03) { case 0: version = MpegVersion.TwoPointFive; break; case 2: version = MpegVersion.Two; break; case 3: version = MpegVersion.One; break; } // Set the MPEG layer switch ((flags >> 17) & 0x03) { case 1: layer = 3; break; case 2: layer = 2; break; case 3: layer = 1; break; } protectionEnabled = ((flags >> 16) & 1) == 0; // Set the bitrate int[, ,] bitrates = new int[2, 3, 16] { { // Version 1 { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0 }, // layer 1 { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0 }, // layer 2 { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0 } // layer 3 }, { // Version 2 or 2.5 { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0 }, // layer 1 { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, // layer 2 { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 } // layer 3 } }; int versionIndex = version == MpegVersion.One ? 0 : 1; int layerIndex = layer > 0 ? layer - 1 : 0; // The bitrate index is encoded as the first 4 bits of the 3rd byte, // index.e. 1111xxxx int i = (int)(flags >> 12) & 0x0F; bitrate = bitrates[versionIndex, layerIndex, i]; // Set the sample rate int[,] sampleRates = new int[3, 4] { { 44100, 48000, 32000, 0 }, // Version 1 { 22050, 24000, 16000, 0 }, // Version 2 { 11025, 12000, 8000, 0 } // Version 2.5 }; // The sample rate index is encoded as two bits in the 3nd byte, // index.e. xxxx11xx i = (int)(flags >> 10) & 0x03; sampleRate = sampleRates[(int)version, i]; if (sampleRate == 0) { TagLibDebugger.Debug("Mpeg.Header.Parse() -- Invalid sample rate."); return; } // The channel mode is encoded as a 2 bit value at the end of the 3nd // byte, index.e. xxxxxx11 channelMode = (MpegChannelMode)((flags >> 16) & 0x3); // TODO: Add mode extension for completeness isCopyrighted = (flags & 1) == 1; isOriginal = ((flags >> 1) & 1) == 1; // Calculate the frame length if (layer == 1) { frameLength = 24000 * 2 * bitrate / sampleRate + (IsPadded ? 1 : 0); } else { frameLength = 72000 * bitrate / sampleRate + (IsPadded ? 1 : 0); } // Now that we're done parsing, set this to be a valid frame. isValid = true; }
////////////////////////////////////////////////////////////////////////// // private methods ////////////////////////////////////////////////////////////////////////// private void Read () { // Since we've likely just looked for the ID3v1 tag, start at the end of the // file where we're least likely to have to have to move the disk head. long last = file.LastFrameOffset; if (last < 0) { TagLibDebugger.Debug ("Mpeg.Properties.Read() -- Could not find a valid last MPEG frame in the stream."); return; } file.Seek (last); MpegHeader last_header = new MpegHeader (file.ReadBlock (4)); long first = file.FirstFrameOffset; if (first < 0) { TagLibDebugger.Debug ("Mpeg.Properties.Read() -- Could not find a valid first MPEG frame in the stream."); return; } if(!last_header.IsValid) { long pos = last; while (pos > first) { pos = file.PreviousFrameOffset (pos); if(pos < 0) break; file.Seek (pos); MpegHeader header = new MpegHeader (file.ReadBlock (4)); if (header.IsValid) { last_header = header; last = pos; break; } } } // Now jump back to the front of the file and read what we need from there. file.Seek (first); MpegHeader first_header = new MpegHeader (file.ReadBlock (4)); if (!first_header.IsValid || !last_header.IsValid) { TagLibDebugger.Debug ("Mpeg.Properties.Read() -- Page headers were invalid."); return; } // Check for a Xing header that will help us in gathering information about a // VBR stream. int xing_header_offset = MpegXingHeader.XingHeaderOffset (first_header.Version, first_header.ChannelMode); file.Seek (first + xing_header_offset); MpegXingHeader xing_header = new MpegXingHeader (file.ReadBlock (16)); // Read the length and the bitrate from the Xing header. if(xing_header.IsValid && first_header.SampleRate > 0 && xing_header.TotalFrames > 0) { int [] block_size = {0, 384, 1152, 1152}; double time_per_frame = block_size [first_header.Layer]; time_per_frame = first_header.SampleRate > 0 ? time_per_frame / first_header.SampleRate : 0; duration = new TimeSpan((int)(time_per_frame * xing_header.TotalFrames) * TimeSpan.TicksPerSecond); bitrate = (int) (duration > TimeSpan.Zero ? ((xing_header.TotalSize * 8L) / duration.TotalSeconds) / 1000 : 0); } // Since there was no valid Xing header found, we hope that we're in a constant // bitrate file. // TODO: Make this more robust with audio property detection for VBR without a // Xing header. else if (first_header.FrameLength > 0 && first_header.Bitrate > 0) { int frames = (int) ((last - first) / first_header.FrameLength + 1); duration = TimeSpan.FromSeconds ((double) (first_header.FrameLength * frames) / (double) (first_header.Bitrate * 125) + 0.5); bitrate = first_header.Bitrate; } sample_rate = first_header.SampleRate; channels = first_header.ChannelMode == MpegChannelMode.SingleChannel ? 1 : 2; version = first_header.Version; layer = first_header.Layer; channel_mode = first_header.ChannelMode; is_copyrighted = first_header.IsCopyrighted; is_original = first_header.IsOriginal; }
void Stereo(MpegChannelMode channelMode, int chanModeExt, int gr, bool lsf) { // do the stereo decoding as needed... This really only applies in two cases: // 1) Joint Stereo and one (or both) of the extensions are enabled, or // 2) We're doing a downmix to mono if (channelMode == MpegChannelMode.JointStereo && chanModeExt != 0) { var midSide = (chanModeExt & 0x2) == 2; if ((chanModeExt & 0x1) == 1) { // do the intensity stereo processing #region Common Processing // find the highest sample index with a value in channel 1 // - now each step only has to start from there int lastValueIdx = -1; for (int i = SBLIMIT * SSLIMIT - (SBLIMIT + 1); i >= 0; i--) { if (_samples[1][i] != 0f) { lastValueIdx = i; break; } } // figure up which passes we'll need and for which ranges int lEnd = -1, sStart = -1; if (_blockSplitFlag[gr][0] && _blockType[gr][0] == 2) { if (_mixedBlockFlag[gr][0]) { // 0 through 8 of long, then 3 through 12 of short if (lastValueIdx < _sfBandIndexL[8]) { lEnd = 8; } sStart = 3; } else { // 0 through 12 of short sStart = 0; } } else { // 0 through 21 of long lEnd = 21; } #endregion #region Long Processing // long processing is far simpler than short... just process from the start of the scalefactor band after the last non-zero sample // we also don't have to mess with "finding" again; it was done above var sfb = 0; if (lastValueIdx > -1) { sfb = _cbLookupL[lastValueIdx] + 1; } // make sure we do the mid/side processing on the lower bands (if needed) if (sfb > 0 && sStart == -1) { if (midSide) { ApplyMidSide(0, _sfBandIndexL[sfb]); } else { ApplyFullStereo(0, _sfBandIndexL[sfb]); } } // now process the intensity bands for (; sfb < lEnd; sfb++) { var i = _sfBandIndexL[sfb]; var width = _sfBandIndexL[sfb + 1] - _sfBandIndexL[sfb]; var isPos = _scalefac[1][3][sfb]; if (isPos == 7) { if (midSide) { ApplyMidSide(i, width); } else { ApplyFullStereo(i, width); } } else if (lsf) { ApplyLsfIStereo(i, width, isPos, _scalefacCompress[gr][0]); } else { ApplyIStereo(i, width, isPos); } } if (sStart <= -1) { // do final long processing var isPos = _scalefac[1][3][20]; if (isPos == 7) { if (midSide) { ApplyMidSide(_sfBandIndexL[21], 576 - _sfBandIndexL[21]); } else { ApplyFullStereo(_sfBandIndexL[21], 576 - _sfBandIndexL[21]); } } else if (lsf) { ApplyLsfIStereo(_sfBandIndexL[21], 576 - _sfBandIndexL[21], isPos, _scalefacCompress[gr][0]); } else { ApplyIStereo(_sfBandIndexL[21], 576 - _sfBandIndexL[21], isPos); } } #endregion #region Short Processing // short processing requires that each window be looked at separately... they are interleaved, so it gets really interesting... // on the plus side, whichever window the {lastValueIdx} is in is already found... :) else { // find where each window starts intensity processing var sSfb = new int[] { -1, -1, -1 }; int window; if (lastValueIdx > -1) { sfb = _cbLookupS[lastValueIdx]; window = _cbwLookupS[lastValueIdx]; sSfb[window] = sfb; } else { sfb = 12; window = 3; // NB: 3 is correct! } window = (window - 1) % 3; for (; sfb >= sStart && window >= 0; window = (window - 1) % 3) { if (sSfb[window] != -1) { if (sSfb[0] != -1 && sSfb[1] != -1 && sSfb[2] != -1) { break; } continue; } var width = _sfBandIndexS[sfb + 1] - _sfBandIndexS[sfb]; var i = _sfBandIndexS[sfb] * 3 + width * (window + 1); while (--width >= -1) { if (_samples[1][--i] != 0f) { sSfb[window] = sfb; break; } } if (window == 0) { --sfb; } } // now apply the intensity processing for each window & scalefactor band sfb = sStart; for (; sfb < 12; sfb++) { var width = _sfBandIndexS[sfb + 1] - _sfBandIndexS[sfb]; var i = _sfBandIndexS[sfb] * 3; for (window = 0; window < 3; window++) { if (sfb > sSfb[window]) { var isPos = _scalefac[1][window][sfb]; if (isPos == 7) { if (midSide) { ApplyMidSide(i, width); } else { ApplyFullStereo(i, width); } } else if (lsf) { ApplyLsfIStereo(i, width, isPos, _scalefacCompress[gr][0]); } else { ApplyIStereo(i, width, isPos); } } else if (midSide) { ApplyMidSide(i, width); } else { ApplyFullStereo(i, width); } i += width; } } // do final short processing var finalWidth = _sfBandIndexS[13] - _sfBandIndexS[12]; for (window = 0; window < 3; window++) { var isPos = _scalefac[1][window][11]; if (isPos == 7) { if (midSide) { ApplyMidSide(_sfBandIndexS[11] * 3 + finalWidth * window, finalWidth); } else { ApplyFullStereo(_sfBandIndexS[11] * 3 + finalWidth * window, finalWidth); } } else if (lsf) { ApplyLsfIStereo(_sfBandIndexS[11] * 3 + finalWidth * window, finalWidth, isPos, _scalefacCompress[gr][0]); } else { ApplyIStereo(_sfBandIndexS[11] * 3 + finalWidth * window, finalWidth, isPos); } } } #endregion } else if (midSide) { // just do mid/side processing for everything ApplyMidSide(0, SBLIMIT * SSLIMIT); } else { // this is a no-op most of the time ApplyFullStereo(0, SSLIMIT * SBLIMIT); } } else if (_channels != 1) { // this is a no-op most of the time ApplyFullStereo(0, SSLIMIT * SBLIMIT); } }
public static int XingHeaderOffset(MpegVersion version, MpegChannelMode channelMode) { if (version == MpegVersion.One) { if (channelMode == MpegChannelMode.SingleChannel) return 0x15; else return 0x24; } else { if (channelMode == MpegChannelMode.SingleChannel) return 0x0D; else return 0x15; } }