Files
ichni_Creator_Studio/Assets/NLayer/Decoder/MpegFrame.cs
2025-07-12 18:27:10 +08:00

631 lines
19 KiB
C#

using System;
using System.Collections.Generic;
using System.Text;
namespace NLayer.Decoder
{
class MpegFrame : FrameBase, IMpegFrame
{
static readonly int[][][] _bitRateTable =
{
new int[][]
{
new int[] { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448 },
new int[] { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384 },
new int[] { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320 }
},
new int[][]
{
new int[] { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256 },
new int[] { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160 },
new int[] { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160 }
},
};
internal static MpegFrame TrySync(uint syncMark)
{
if ((syncMark & 0xFFE00000) == 0xFFE00000 // frame sync
&& (syncMark & 0x00180000) != 0x00080000 // MPEG version != reserved
&& (syncMark & 0x00060000) != 0x00000000 // layer version != reserved
&& (syncMark & 0x0000F000) != 0x0000F000 // bitrate != bad
&& (syncMark & 0x00000C00) != 0x00000C00 // sample rate != reserved
)
{
// now check the stereo modes
switch ((syncMark >> 4) & 0xF)
{
case 0x0: // stereo
case 0x4: // joint stereo
case 0x5: // "
case 0x6: // "
case 0x7: // "
case 0x8: // dual channel
case 0xC: // mono
return new MpegFrame { _syncBits = (int)syncMark };
}
}
return null;
}
// base: 2xIntPtr, 1x8, 1x4
// total: 3xIntPtr, 4x8, 4x4
// Long Mode: 72 bytes per instance
// Prot Mode: 60 bytes per instance
// IntPtr
internal MpegFrame Next;
// 4
internal int Number;
// 4
int _syncBits;
// 8
int _readOffset, _bitsRead;
// 8
ulong _bitBucket = 0UL;
// 8
long _offset;
// 4
bool _isMuted;
MpegFrame()
{
}
protected override int Validate()
{
// TrySync has already validated version, layer, bitrate, and samplerate
// if layer2, we have to verify the channel mode is valid for the bitrate selected
if (Layer == MpegLayer.LayerII)
{
switch (BitRate)
{
case 32000:
case 48000:
case 56000:
case 80000:
// don't allow anything except mono
if (ChannelMode != MpegChannelMode.Mono) return -1;
break;
case 224000:
case 256000:
case 320000:
case 384000:
// don't allow mono
if (ChannelMode == MpegChannelMode.Mono) return -1;
break;
}
}
// calculate the frame's length
int frameSize;
if (BitRateIndex > 0)
{
if (Layer == MpegLayer.LayerI)
{
frameSize = (12 * BitRate / SampleRate + Padding) * 4;
}
else
{
frameSize = 144 * BitRate / SampleRate + Padding;
}
}
else
{
// "free" frame... we have to calculate it later
frameSize = _readOffset + GetSideDataSize() + Padding; // we know the frame will be at least this big...
}
// now check the crc if one is present
if (HasCrc)
{
// prep for CRC reading
_readOffset = 4 + (HasCrc ? 2 : 0);
// check the CRC
if (!ValidateCRC())
{
// mute this frame
_isMuted = true;
return 6; // header + crc... force the reader to re-sync
}
}
// prep for reading
Reset();
// finally, let our caller know how big this frame is (including the sync header)
return frameSize;
}
internal int GetSideDataSize()
{
switch (Layer)
{
case MpegLayer.LayerI:
if (ChannelMode == MpegChannelMode.Mono)
{
// mono
return 16;
}
else if (ChannelMode == MpegChannelMode.Stereo || ChannelMode == MpegChannelMode.DualChannel)
{
// full stereo / dual channel
return 32;
}
else
{
// joint stereo... ugh...
switch (ChannelModeExtension)
{
case 0:
return 18;
case 1:
return 20;
case 2:
return 22;
case 3:
return 24;
}
}
break;
case MpegLayer.LayerII:
return 0;
case MpegLayer.LayerIII:
if (ChannelMode == MpegChannelMode.Mono && Version >= MpegVersion.Version2)
{
return 9;
}
else if (ChannelMode != MpegChannelMode.Mono && Version < MpegVersion.Version2)
{
return 32;
}
else
{
return 17;
}
}
return 0;
}
bool ValidateCRC()
{
var crc = 0xFFFFU;
// process the common bits...
UpdateCRC(_syncBits, 16, ref crc);
var apply = false;
switch (Layer)
{
case MpegLayer.LayerI:
apply = LayerIDecoder.GetCRC(this, ref crc);
break;
case MpegLayer.LayerII:
apply = LayerIIDecoder.GetCRC(this, ref crc);
break;
case MpegLayer.LayerIII:
apply = LayerIIIDecoder.GetCRC(this, ref crc);
break;
}
if (apply)
{
var checkCrc = ReadByte(4) << 8 | ReadByte(5);
return checkCrc == crc;
}
return true;
}
static internal void UpdateCRC(int data, int length, ref uint crc)
{
var masking = 1U << length;
while ((masking >>= 1) != 0)
{
var carry = crc & 0x8000;
crc <<= 1;
if ((carry == 0) ^ ((data & masking) == 0))
{
crc ^= 0x8005;
}
}
crc &= 0xFFFF;
}
internal VBRInfo ParseVBR()
{
var buf = new byte[4];
// Xing first
int offset;
if (Version == MpegVersion.Version1 && ChannelMode != MpegChannelMode.Mono)
{
offset = 32 + 4;
}
else if (Version > MpegVersion.Version1 && ChannelMode == MpegChannelMode.Mono)
{
offset = 9 + 4;
}
else
{
offset = 17 + 4;
}
if (Read(offset, buf) != 4) return null;
if (buf[0] == 'X' && buf[1] == 'i' && buf[2] == 'n' && buf[3] == 'g'
|| buf[0] == 'I' && buf[1] == 'n' && buf[2] == 'f' && buf[3] == 'o')
{
return ParseXing(offset + 4);
}
// then VBRI (kinda rare)
if (Read(36, buf) != 4) return null;
if (buf[0] == 'V' && buf[1] == 'B' && buf[2] == 'R' && buf[3] == 'I')
{
return ParseVBRI();
}
return null;
}
VBRInfo ParseXing(int offset)
{
VBRInfo info = new VBRInfo();
info.Channels = Channels;
info.SampleRate = SampleRate;
info.SampleCount = SampleCount;
var buf = new byte[100];
if (Read(offset, buf, 0, 4) != 4) return null;
offset += 4;
var flags = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
// frame count
if ((flags & 0x1) != 0)
{
if (Read(offset, buf, 0, 4) != 4) return null;
offset += 4;
info.VBRFrames = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
}
// byte count
if ((flags & 0x2) != 0)
{
if (Read(offset, buf, 0, 4) != 4) return null;
offset += 4;
info.VBRBytes = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
}
// TOC
if ((flags & 0x4) != 0)
{
// we're not using the TOC, so just discard it
if (Read(offset, buf) != 100) return null;
offset += 100;
}
// scale
if ((flags & 0x8) != 0)
{
if (Read(offset, buf, 0, 4) != 4) return null;
offset += 4;
info.VBRQuality = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
}
//// now look for a LAME header (note: if it isn't found, it doesn't fail the VBR parse)
//do
//{
// if (Read(offset, buf, 0, 20) != 20) break;
// offset += 20;
// // LAME tag revision: only 0 and 1 are valid
// if ((buf[9] & 0xF0) > 0x10) break;
// // VBR mode: 0-6, 8 & 9 are valid
// var mode = buf[9] & 0xF;
// if (mode == 7 || mode > 9) break;
// // Lowpass filter value
// var lowpass = buf[10] / 100.0;
// // Replay Gain
// var rgPeak = BitConverter.ToSingle(buf, 11);
// var rgGainRadio = buf[15] << 8 | buf[16];
// var rgGain = buf[17] << 8 | buf[18];
//} while (false);
return info;
}
VBRInfo ParseVBRI()
{
VBRInfo info = new VBRInfo();
info.Channels = Channels;
info.SampleRate = SampleRate;
info.SampleCount = SampleCount;
// VBRI is "fixed" size... Yay. :)
var buf = new byte[26];
if (Read(36, buf) != 26) return null;
// Version
var version = buf[4] << 8 | buf[5];
// Delay
info.VBRDelay = buf[6] << 8 | buf[7];
// Quality
info.VBRQuality = buf[8] << 8 | buf[9];
// Bytes
info.VBRBytes = buf[10] << 24 | buf[11] << 16 | buf[12] << 8 | buf[13];
// Frames
info.VBRFrames = buf[14] << 24 | buf[15] << 16 | buf[16] << 8 | buf[17];
// TOC
// entries
var tocEntries = buf[18] << 8 | buf[19];
var tocScale = buf[20] << 8 | buf[21];
var tocEntrySize = buf[22] << 8 | buf[23];
var tocFramesPerEntry = buf[24] << 8 | buf[25];
var tocSize = tocEntries * tocEntrySize;
var toc = new byte[tocSize];
if (Read(62, toc) != tocSize) return null;
return info;
}
public int FrameLength
{
get { return base.Length; }
}
public MpegVersion Version
{
get
{
switch ((_syncBits >> 19) & 3)
{
case 0:
return MpegVersion.Version25;
case 2:
return MpegVersion.Version2;
case 3:
return MpegVersion.Version1;
}
return MpegVersion.Unknown;
}
}
public MpegLayer Layer
{
get
{
// the order is backwards, and "0" is invalid
return (MpegLayer)((4 - ((_syncBits >> 17) & 3)) % 4);
}
}
public bool HasCrc
{
get { return (_syncBits & 0x10000) == 0; }
}
public int BitRate
{
get
{
if (BitRateIndex > 0)
{
return _bitRateTable[(int)Version / 10 - 1][(int)Layer - 1][BitRateIndex] * 1000;
}
else
{
// bitrate is always an even multiple of 1000, so round
return ((((FrameLength * 8) * SampleRate) / SampleCount + 499) + 500) / 1000 * 1000;
}
}
}
public int BitRateIndex
{
get { return (_syncBits >> 12) & 0xF; }
}
public int SampleRate
{
get
{
int sr;
switch (SampleRateIndex)
{
case 0: sr = 44100; break;
case 1: sr = 48000; break;
case 2: sr = 32000; break;
default: sr = 0; break;
}
if (Version > MpegVersion.Version1)
{
if (Version == MpegVersion.Version25)
{
sr /= 4;
}
else
{
sr /= 2;
}
}
return sr;
}
}
public int SampleRateIndex
{
get { return (_syncBits >> 10) & 0x3; }
}
private int Padding
{
get { return (_syncBits >> 9) & 0x1; }
}
public MpegChannelMode ChannelMode
{
get { return (MpegChannelMode)((_syncBits >> 6) & 0x3); }
}
public int ChannelModeExtension
{
get { return (_syncBits >> 4) & 0x3; }
}
internal int Channels
{
get { return (ChannelMode == MpegChannelMode.Mono ? 1 : 2); }
}
public bool IsCopyrighted
{
get { return (_syncBits & 0x8) == 0x8; }
}
internal bool IsOriginal
{
get { return (_syncBits & 0x4) == 0x4; }
}
internal int EmphasisMode
{
get { return (_syncBits & 0x3); }
}
public bool IsCorrupted
{
get { return _isMuted; }
}
public int SampleCount
{
get
{
if (Layer == MpegLayer.LayerI) return 384;
if (Layer == MpegLayer.LayerIII && Version > MpegVersion.Version1) return 576;
return 1152;
}
}
internal long SampleOffset
{
get { return _offset; }
set { _offset = value; }
}
public void Reset()
{
_readOffset = 4 + (HasCrc ? 2 : 0);
_bitBucket = 0UL;
_bitsRead = 0;
}
public int ReadBits(int bitCount)
{
if (bitCount < 1 || bitCount > 32) throw new ArgumentOutOfRangeException("bitCount");
if (_isMuted) return 0;
while (_bitsRead < bitCount)
{
var b = ReadByte(_readOffset);
if (b == -1) throw new System.IO.EndOfStreamException();
++_readOffset;
_bitBucket <<= 8;
_bitBucket |= (byte)(b & 0xFF);
_bitsRead += 8;
}
var temp = (int)((_bitBucket >> (_bitsRead - bitCount)) & ((1UL << bitCount) - 1));
_bitsRead -= bitCount;
return temp;
}
#if DEBUG
public override string ToString()
{
// version
var sb = new StringBuilder("MPEG");
switch (Version)
{
case MpegVersion.Version1: sb.Append("1"); break;
case MpegVersion.Version2: sb.Append("2"); break;
case MpegVersion.Version25: sb.Append("2.5"); break;
}
// layer
sb.Append(" Layer ");
switch (Layer)
{
case MpegLayer.LayerI: sb.Append("I"); break;
case MpegLayer.LayerII: sb.Append("II"); break;
case MpegLayer.LayerIII: sb.Append("III"); break;
}
// bitrate
sb.AppendFormat(" {0} kbps ", BitRate / 1000);
// channel mode
switch (ChannelMode)
{
case MpegChannelMode.Stereo:
sb.Append("Stereo");
break;
case MpegChannelMode.JointStereo:
sb.Append("Joint Stereo");
switch (ChannelModeExtension)
{
case 1: sb.Append(" (I)"); break;
case 2: sb.Append(" (M/S)"); break;
case 3: sb.Append(" (M/S,I)"); break;
}
break;
case MpegChannelMode.DualChannel:
sb.Append("Dual Channel");
break;
case MpegChannelMode.Mono:
sb.Append("Mono");
break;
}
// sample rate
sb.AppendFormat(" {0} KHz", (float)SampleRate / 1000);
var flagList = new List<string>();
// protection
if (HasCrc) flagList.Add("CRC");
// copyright
if (IsCopyrighted) flagList.Add("Copyright");
// original
if (IsOriginal) flagList.Add("Original");
// emphasis
switch (EmphasisMode)
{
case 1:
flagList.Add("50/15 ms");
break;
case 2:
flagList.Add("Invalid Emphasis");
break;
case 3:
flagList.Add("CCIT J.17");
break;
}
if (flagList.Count > 0)
{
sb.AppendFormat(" ({0})", string.Join(",", flagList.ToArray()));
}
return sb.ToString();
}
#endif
}
}