⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mpaheader.cpp

📁 获取mp3信息, Xing header, ID3 tag, APE tag, VBR header
💻 CPP
字号:
#include "stdafx.h"
#include "mpaheader.h"
#include "mpaexception.h"
#include "mpaendoffileexception.h"
#include "mpastream.h"


// static variables
LPCTSTR CMPAHeader::m_szLayers[] = { _T("Layer I"), _T("Layer II"), _T("Layer III") };
LPCTSTR CMPAHeader::m_szMPEGVersions[] = {_T("MPEG 2.5"), _T(""), _T("MPEG 2"), _T("MPEG 1") };
LPCTSTR CMPAHeader::m_szChannelModes[] = { _T("Stereo"), _T("Joint Stereo"), _T("Dual Channel"), _T("Single Channel") };
LPCTSTR CMPAHeader::m_szEmphasis[] = { _T("None"), _T("50/15ms"), _T(""), _T("CCIT J.17") };

// sampling rates in hertz: 1. index = MPEG Version ID, 2. index = sampling rate index
const DWORD CMPAHeader::m_dwSamplingRates[4][3] = 
{ 
	{11025, 12000, 8000,  },	// MPEG 2.5
	{0,     0,     0,     },	// reserved
	{22050, 24000, 16000, },	// MPEG 2
	{44100, 48000, 32000  }		// MPEG 1
};

// bitrates: 1. index = LSF, 2. index = Layer, 3. index = bitrate index
const DWORD CMPAHeader::m_dwBitrates[2][3][15] =
{
	{	// MPEG 1
		{0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,},	// Layer1
		{0,32,48,56, 64, 80, 96,112,128,160,192,224,256,320,384,},	// Layer2
		{0,32,40,48, 56, 64, 80, 96,112,128,160,192,224,256,320,}	// Layer3
	},
	{	// MPEG 2, 2.5		
		{0,32,48,56,64,80,96,112,128,144,160,176,192,224,256,},		// Layer1
		{0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,},			// Layer2
		{0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,}			// Layer3
	}
};

// allowed combination of bitrate (1.index) and mono (2.index)
const bool CMPAHeader::m_bAllowedModes[15][2] =
{
	// {stereo, intensity stereo, dual channel allowed,single channel allowed}
	{true,true},		// free mode
	{false,true},		// 32
	{false,true},		// 48
	{false,true},		// 56
	{true,true},		// 64
	{false,true},		// 80
	{true,true},		// 96
	{true,true},		// 112
	{true,true},		// 128
	{true,true},		// 160
	{true,true},		// 192
	{true,false},		// 224
	{true,false},		// 256
	{true,false},		// 320
	{true,false}		// 384
};

// Samples per Frame: 1. index = LSF, 2. index = Layer
const DWORD CMPAHeader::m_dwSamplesPerFrames[2][3] =
{
	{	// MPEG 1
		384,	// Layer1
		1152,	// Layer2	
		1152	// Layer3
	},
	{	// MPEG 2, 2.5
		384,	// Layer1
		1152,	// Layer2
		576		// Layer3
	}	
};

// Samples per Frame / 8
const DWORD CMPAHeader::m_dwCoefficients[2][3] =
{
	{	// MPEG 1
		12,		// Layer1	(must be multiplied with 4, because of slot size)
		144,	// Layer2
		144		// Layer3
	},
	{	// MPEG 2, 2.5
		12,		// Layer1	(must be multiplied with 4, because of slot size)
		144,	// Layer2
		72		// Layer3
	}	
};

// slot size per layer
const DWORD CMPAHeader::m_dwSlotSizes[3] =
{
	4,			// Layer1
	1,			// Layer2
	1			// Layer3
};

// size of side information (only for Layer III)
// 1. index = LSF, 2. index = mono
const DWORD CMPAHeader::m_dwSideInfoSizes[2][2] =
{
	// MPEG 1
	{32,17},
	// MPEG 2/2.5
	{17,9}
};

// tolerance range, look at expected offset +/- m_dwTolerance/2 for beginning of a frame
const DWORD CMPAHeader::m_dwTolerance = 6;	// +/-3 bytes

// max. range where to look for frame sync
const DWORD CMPAHeader::m_dwMaxRange = 16384;


// constructor (throws exception if header is invalid)
// if bExactOffset = true then look for frame at offset +/- tolerance, otherwise start looking at offset and go through file
CMPAHeader::CMPAHeader(CMPAStream* pStream, DWORD& dwOffset, bool bExactOffset, bool bReverse, CMPAHeader* pCompareHeader) :
	m_wAllocationTableIndex(0), m_wBound(32)
{
	// look for synchronisation
	DWORD dwStep = 1;

	// is new offset within valid range?
	bool bHeaderFound = false;
	while (BYTE* pHeader = pStream->ReadBytes(4, dwOffset, false, bReverse)) 
	{
		// sync bytes found?
		// for performance reasons check already that it is not data within an empty frame (all bits set)
		// therefore check wether the bits for bitrate are all set -> means that this is no header!
		if ((pHeader[0] == 0xFF) && ((pHeader[1] & 0xE0) == 0xE0) && ((pHeader[2] & 0xF0) != 0xF0) ) // first 11 bits should be 1

		{
			try 
			{
				Init(pHeader, pStream->GetFilename());	
				if (pCompareHeader)
				{
					// is this header compatible (which means that it resembles the previous header
					if (!(*this == *pCompareHeader))
						throw CMPAException(CMPAException::IncompatibleHeader, pStream->GetFilename());
				}
				bHeaderFound = true;
				break;
			}

			/*	
			An Exception either means, that a corrupt header was found or
			that there is no header at the position dwOffset (but there were incidentally the sync bytes found).
			The distinction between these to errors is made upon the value of bExactOffset

			*/
			catch (CMPAException& e)
			{
				OutputDebugString(_T("Exception at construction of MPAHeader."));
				
				if (bExactOffset)
					throw;
			}
		}

		// find the frame on a position greater or smaller than the current offset
		if (!bExactOffset)
		{	
			// just go forward or backward to find sync
			dwOffset += (bReverse?-1:+1);
			
			// check only within dwMaxRange
			if (dwStep > m_dwMaxRange)
				// out of tolerance range
				throw CMPAException(CMPAException::NoFrameInTolerance, pStream->GetFilename());	

			dwStep++;
		}
		// find the frame at the exact position (or within the tolerance around the position)
		else
		{
			if (dwStep > m_dwTolerance)
			{
				// out of tolerance range
				throw CMPAException(CMPAException::NoFrameInTolerance, pStream->GetFilename());
			}
			
			// look around dwExpectedOffset with increasing steps (+1,-2,+3,-4,...)
			if (dwStep%2 == 1)
				dwOffset += dwStep;
			else
				dwOffset -= dwStep;
			dwStep++;			
		}
	}
	if (!bHeaderFound)
	{
		// out of tolerance range
		throw CMPAEndOfFileException(pStream->GetFilename());
	}
}

// the bit information refers to bit 0 as the most significant bit (MSB) of Byte 0
// decodes the header in pHeader
void CMPAHeader::Init(BYTE* pHeader, LPCTSTR szFilename) 
{
	// get MPEG version [bit 11,12]
	m_Version = (MPAVersion)((pHeader[1] >> 3) & 0x03);	// mask only the rightmost 2 bits
	if (m_Version == MPEGReserved)
		throw CMPAException(CMPAException::HeaderCorrupt, szFilename);

	if (m_Version == MPEG1)
		m_bLSF = false;
	else
		m_bLSF = true;

	// get layer (0 = layer1, 2 = layer2, ...)  [bit 13,14]
	m_Layer = (MPALayer)(3 - ((pHeader[1] >> 1) & 0x03));	
	if (m_Layer == LayerReserved)
		throw CMPAException(CMPAException::HeaderCorrupt, szFilename);

	// protection bit (inverted) [bit 15]
	m_bCRC = !((pHeader[1]) & 0x01);

	// bitrate [bit 16..19]
	BYTE bBitrateIndex = (BYTE)((pHeader[2] >> 4) & 0x0F);
	if (bBitrateIndex == 0x0F)		// all bits set is reserved
		throw CMPAException(CMPAException::HeaderCorrupt);
	m_dwBitrate = m_dwBitrates[m_bLSF][m_Layer][bBitrateIndex] * 1000; // convert from kbit to bit

	if (m_dwBitrate == 0)	// means free bitrate (is unsupported yet)
		throw CMPAException(CMPAException::FreeBitrate, szFilename);

	// sampling rate [bit 20,21]
	BYTE bIndex = (BYTE)((pHeader[2] >> 2) & 0x03);
	if (bIndex == 0x03)		// all bits set is reserved
		throw CMPAException(CMPAException::HeaderCorrupt, szFilename);
	m_dwSamplesPerSec = m_dwSamplingRates[m_Version][bIndex];

	// padding bit [bit 22]
	m_dwPaddingSize = 1 * ((pHeader[2] >> 1) & 0x01);	// in Slots (always 1)
	
	m_dwSamplesPerFrame = m_dwSamplesPerFrames[m_bLSF][m_Layer];

	// private bit [bit 23]
	m_bPrivate = (pHeader[2]) & 0x01;

	// channel mode [bit 24,25]
	m_ChannelMode = (ChannelMode)((pHeader[3] >> 6) & 0x03);

	// mode extension [bit 26,27]
	m_ModeExt = (BYTE)((pHeader[3] >> 4) & 0x03);

	// determine the bound for intensity stereo
	if (m_ChannelMode == JointStereo)
		m_wBound = 4 + m_ModeExt * 4;

	// copyright bit [bit 28]
	m_bCopyright = (pHeader[3] >> 3) & 0x01;
	
	// original bit [bit 29]
	m_bOriginal = (pHeader[3] >> 2) & 0x01;

	// emphasis [bit 30,31]
	m_Emphasis = (Emphasis)((pHeader[3]) & 0x03);
	if (m_Emphasis == EmphReserved)
		throw CMPAException(CMPAException::HeaderCorrupt, szFilename);

	// extended check for Layer II
	if (m_Layer == Layer2)
	{
		// MPEG 1
		if (m_Version == MPEG1)
		{
			if (!m_bAllowedModes[bBitrateIndex][IsMono()])
				throw CMPAException(CMPAException::HeaderCorrupt, szFilename);	

			// which allocation table is used
			switch (m_dwBitrate/1000/(IsMono()?1:2))
			{
				case 32:
				case 48:
					if (m_dwSamplesPerSec == 32000)
						m_wAllocationTableIndex = 3;	// table d
					else
						m_wAllocationTableIndex = 2;	// table c
					break;
				case 56:
				case 64:
				case 80:
					if (m_dwSamplesPerSec != 48000)
					{
						m_wAllocationTableIndex = 0;	// table a
						break;
					}
				case 96:
				case 112:
				case 128:
				case 160:
				case 192:
					if (m_dwSamplesPerSec != 48000)
					{
						m_wAllocationTableIndex = 1;	// table b
						break;
					}
					else
						m_wAllocationTableIndex = 0;	// table a
					break;
			}
		}
		else	// MPEG 2/2.5
			m_wAllocationTableIndex = 4;
	}
}

// destructor
CMPAHeader::~CMPAHeader()
{

}


// compare headers
// return true if identical or related
// return false if no similarities

bool CMPAHeader::operator==(CMPAHeader& DestHeader) const
{
	// version change never possible
	if (DestHeader.m_Version != m_Version)
		return false;

	// layer change never possible
	if (DestHeader.m_Layer != m_Layer)
		return false;

	// sampling rate change never possible
	if (DestHeader.m_dwSamplesPerSec != m_dwSamplesPerSec)
		return false;

	// from mono to stereo never possible
	if (DestHeader.IsMono() != IsMono())
		return false;

	if (DestHeader.m_Emphasis != m_Emphasis)
		return false;

	return true;
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -