mp3信息解析源代码

本文档详细介绍了如何使用API和结构体解析MP3文件信息,包括DLL的调用和多层解码原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

#pragma once

class File
{
public:
    File(
void);
public:
    
~File(void);

    BOOL Open(LPCTSTR lpFileName,DWORD dwDesiredAccess
/*参考CreateFile*/);
    DWORD Seek(LONG lDistanceToMove,DWORD dwMoveMethod
/*参考SetFilePointer*/);
    DWORD SeekToBegin( );
    DWORD SeekToEnd( );
    DWORD GetLength( ) 
const;
    DWORD Read( LPVOID lpBuffer,DWORD nNumberOfBytesToRead);
    DWORD Write(LPVOID lpBuffer,DWORD nNumberOfBytesToWrite);
private:
    HANDLE m_hFile; 
}
;

#include 
"StdAfx.h"
#include 
"File.h"
extern DWORD g_dwLastError;
File::File(
void)
{
    m_hFile
=INVALID_HANDLE_VALUE;
}


File::
~File(void)
{
    
if (m_hFile!=INVALID_HANDLE_VALUE)
    
{
        CloseHandle(m_hFile);
    }

}

BOOL File::Open(LPCTSTR lpFileName,DWORD dwDesiredAccess)
{
    m_hFile 
= CreateFile(lpFileName,dwDesiredAccess,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
    
return m_hFile!=INVALID_HANDLE_VALUE ? TRUE:FALSE;
}


DWORD File::Seek(LONG lDistanceToMove,DWORD dwMoveMethod
/*参考SetFilePointer*/)
{
    assert(m_hFile
!=INVALID_HANDLE_VALUE);
    
return SetFilePointer(m_hFile,lDistanceToMove,NULL,dwMoveMethod);
}


DWORD File::SeekToBegin( )
{
    assert(m_hFile
!=INVALID_HANDLE_VALUE);
    
return SetFilePointer(m_hFile,0,NULL,FILE_BEGIN);
}

DWORD File::SeekToEnd( )
{
    assert(m_hFile
!=INVALID_HANDLE_VALUE);
    
return SetFilePointer(m_hFile,0,NULL,FILE_END);
}


DWORD File::GetLength( ) 
const
{
    assert(m_hFile
!=INVALID_HANDLE_VALUE);
    
return GetFileSize(m_hFile,NULL);
}


DWORD File::Read( LPVOID lpBuffer,DWORD nNumberOfBytesToRead)
{
    assert(m_hFile
!=INVALID_HANDLE_VALUE);
    DWORD dwNumberOfBytesRead; 
    ReadFile(m_hFile,lpBuffer,nNumberOfBytesToRead,
&dwNumberOfBytesRead,NULL);
    
return dwNumberOfBytesRead;
}


DWORD File::Write(LPVOID lpBuffer,DWORD nNumberOfBytesToWrite)
{
    assert(m_hFile
!=INVALID_HANDLE_VALUE);
    DWORD dwNumberOfBytesWrite; 
    WriteFile(m_hFile,lpBuffer,nNumberOfBytesToWrite,
&dwNumberOfBytesWrite,NULL);
    
return dwNumberOfBytesWrite;
}

#ifdef MUSICINFO_EXPORTS
#define MUSICINFO_API __declspec(dllexport)
#else
#define MUSICINFO_API __declspec(dllimport)
#endif
extern DWORD g_dwLastError=0;
typedef struct tagID3V2Head
{
 char Header[3]; /*必须为"ID3"否则认为标签不存在*/
 char Ver; /*版本号ID3V2.3 就记录3*/
 char Revision; /*副版本号此版本记录为0*/
 char Flag; /*存放标志的字节,这个版本只定义了三位,稍后详细解说*/
 char Size[4]; /*标签大小,包括标签头的10 个字节和所有的标签帧的大小*/
} ID3V2Head,*PID3V2Head;
typedef struct  tagID3V2Frame
{
 char FrameID[4];
 /*
 用四个字符标识一个帧,说明一个帧的内容含义,常用的对照如下:
 TIT2=标题 表示内容为这首歌的标题,下同
 TPE1=作者
 TALB=专集
 TRCK=音轨 格式:N/M 其中N 为专集中的第N 首,M为专集中共M 首,N和M 为
 ASCII 码表示的数字
 TYER=年代 是用ASCII 码表示的数字
 TCON=类型 直接用字符串表示
 COMM=备注 格式:"eng/0备注内容",其中eng 表示备注所使用的自然语言
 */
 char Size[4]; /*帧内容的大小,不包括帧头,不得小于1*/
 char Flags[2];
 /*只定义了6 位,另外的10 位为0,但大部分的情况下16 位都为0 就可以了。
 格式如下:
 abc00000 ijk00000
 a -- 标签保护标志,设置时认为此帧作废
 b -- 文件保护标志,设置时认为此帧作废
 c -- 只读标志,设置时认为此帧不能修改(但我没有找到一个软件理会这个标志)
 i -- 压缩标志,设置时一个字节存放两个BCD 码表示数字
 j -- 加密标志(没有见过哪个MP3 文件的标签用了加密)
 k -- 组标志,设置时说明此帧和其他的某帧是一组
 */
 char data[100];
} ID3V2Frame,*PID3V2Frame;
typedef struct tagID3V1
{
 char Header[3]; /*标签头必须是"TAG"否则认为没有标签*/
 char Title[30]; /*标题*/
 char Artist[30]; /*作者*/
 char Album[30]; /*专集*/
 char Year[4]; /*出品年代*/
 char Comment[28]; /*备注*/
 char reserve; /*保留*/
 char track;; /*音轨*/
 char Genre; /*类型*/
}ID3V1,*PID3V1;

typedef struct tagFrameHeader
{
 unsigned char sync1:8; //同步信息1   0xFF
 unsigned char crc:1; //CRC校验  0x01/0x00
 unsigned char layer:2; //层          0x01
 unsigned char version:2; //版本      0x03
 unsigned char sync2:3; //同步信息2   0x07
 unsigned char privatebit:1; //私有bit,可以用来做特殊应用 0x00
 unsigned char padding:1; //填充空白字    0x00/0x01
 unsigned char sample_rate_index:2; //采样率索引  0x00
 unsigned char bit_rate_index:4; //位率索引  0x0A
 unsigned char emphasis:2; //强调方式  0x00
 unsigned char original:1; //原始媒体 0x01
 unsigned char copyright:1; //版权标志 0x00
 unsigned char mode_extension:2; //扩展模式,仅用于联合立体声 0x00
 unsigned char channel_mode:2; //声道模式   0x00
}FHEADER, *PFHEADER;

MUSICINFO_API int miGetMp3ID3V1Info(LPTSTR pFileName,PID3V1 pData);
MUSICINFO_API int miGetMp3ID3V2Head(LPTSTR pFileName,PID3V2Head ptagHead);
MUSICINFO_API int miGetMp3ID3V2Size(LPTSTR pFileName);//包括标签头大小
MUSICINFO_API int miEnumMp3ID3V2TagFrame(LPTSTR pFileName,PID3V2Frame pTagFrame);
MUSICINFO_API int miGetMp3FirstFrameHeadInfo(LPTSTR pFileName,PFHEADER pFrameHead);//不用
MUSICINFO_API int miEnumMp3FrameHeadInfo(LPTSTR pFileName,PFHEADER pTagFrame);
MUSICINFO_API int miGetMp3FrameSize(PFHEADER pTagFrame);

MUSICINFO_API double miGetMp3Duration(LPTSTR pFileName);

#include "stdafx.h"
#include "MusicInfo.h"
#include "File.h"
#include <malloc.h>
#ifdef _MANAGED
#pragma managed(push, off)
#endif
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
      )
{
 switch (ul_reason_for_call)
 {
 case DLL_PROCESS_ATTACH:
 case DLL_THREAD_ATTACH:
 case DLL_THREAD_DETACH:
 case DLL_PROCESS_DETACH:
  break;
 }
    return TRUE;
}
#ifdef _MANAGED
#pragma managed(pop)
#endif
MUSICINFO_API int miGetMp3ID3V1Info(LPTSTR pFileName,PID3V1 pData)
{
 assert(pData&&sizeof(ID3V1)==128);
 File file;
 if(file.Open(pFileName,FILE_READ_DATA))
 {
  file.Seek(-128,FILE_END);
  file.Read((LPVOID)pData,128);
 }
 return 0;
}
MUSICINFO_API int miGetMp3ID3V2Head(LPTSTR pFileName,PID3V2Head ptagHead)
{
 assert(ptagHead);
 File file;
 if(file.Open(pFileName,FILE_READ_DATA))
 {
  file.SeekToBegin();
  file.Read((LPVOID)ptagHead,sizeof(ID3V2Head));
 }
 return 0;
}
MUSICINFO_API int miGetMp3ID3V2Size(LPTSTR pFileName)
{
 ID3V2Head tagHead;
 miGetMp3ID3V2Head(pFileName,&tagHead);
 if (memcmp(tagHead.Header,"ID3",3)!=0)
 {
  return 0;
 }
 return sizeof(ID3V2Head)+(tagHead.Size[0]&0x7F)*0x200000 +(tagHead.Size[1]&0x7F)*0x400+(tagHead.Size[2]&0x7F)*0x80+(tagHead.Size[3]&0x7F) ;
}
MUSICINFO_API int miEnumMp3ID3V2TagFrame(LPTSTR pFileName,PID3V2Frame pTagFrame)
{
 const int TAG_HEAD_SIZE = 10;
 static int sizecount = 0;
 ID3V2Head head;
 File file;
 if(file.Open(pFileName,FILE_READ_DATA))
 {
  file.SeekToBegin();
  file.Read((LPVOID)&head,sizeof(ID3V2Head));
  /*必须为"ID3"否则认为标签不存在*/
  if (memcmp(head.Header,"ID3",3)!=0)
  {
   return 0;
  }
  int tagsize = (head.Size[0]&0x7F)*0x200000 +(head.Size[1]&0x7F)*0x400+(head.Size[2]&0x7F)*0x80 +(head.Size[3]&0x7F) ;
  if (sizecount>=tagsize)
  {
   sizecount = 0;
   return 0;
  }
  file.Seek(sizecount+sizeof(ID3V2Head),FILE_BEGIN);
  ID3V2Frame frame;
  file.Read((LPVOID)&frame,sizeof(ID3V2Frame));
  int FSize = frame.Size[0]*0x100000000+frame.Size[1]*0x10000+frame.Size[2]*0x100+frame.Size[3];
  if (FSize==0)
  {
   sizecount = 0;
   return 0;
  }
  file.Seek(sizecount+sizeof(ID3V2Head),FILE_BEGIN);
  file.Read((LPVOID)pTagFrame,TAG_HEAD_SIZE+FSize);
  sizecount+=TAG_HEAD_SIZE+FSize;
  return 1;
 }
 return 0;
}
MUSICINFO_API int miGetMp3FirstFrameHeadInfo(LPTSTR pFileName,PFHEADER pFrameHead)
{
 int tagSize = miGetMp3ID3V2Size(pFileName);
 File file;
 if(file.Open(pFileName,FILE_READ_DATA))
 {
  file.Seek(tagSize,FILE_BEGIN);
  file.Read(pFrameHead,sizeof(FHEADER));
  return 1;
 }
 return 0;
}
MUSICINFO_API int miEnumMp3FrameHeadInfo(LPTSTR pFileName,PFHEADER pFrameHead)
{
 static int nSeekTo = 0;
 int tagSize = miGetMp3ID3V2Size(pFileName);
 
 File file;
 if(file.Open(pFileName,FILE_READ_DATA))
 {
  if (tagSize+nSeekTo+128 >=file.GetLength())
  {
   nSeekTo = 0;
   return 0;
  }
  file.Seek(tagSize+nSeekTo,FILE_BEGIN);
  file.Read(pFrameHead,sizeof(FHEADER));
//   int tmp = tagSize+nSeekTo;
//   while (pFrameHead->sync1!=0xFF || pFrameHead->sync2!=0x07 )
//   {
//    tmp++;
//    file.Seek(tmp,FILE_BEGIN);
//    file.Read(pFrameHead,sizeof(FHEADER));
//   }
  int fsize = miGetMp3FrameSize(pFrameHead);
  if (fsize==0)
  {
   nSeekTo = 0;
   return 0;
  }
  nSeekTo += fsize;
  return 1;
 }
 return 0;
}
//帧长度(字节)= (( 每帧采样数(1152) / 8 * 比特率 ) / 采样频率 ) + 填充
MUSICINFO_API int miGetMp3FrameSize(PFHEADER pFrameHead)
{
 
 int samplenum = 0;
 int bitrate=0;
 int samplerate=0;
//     MPEG 1 MPEG 2 MPEG 2.5
//  Layer I  384  384  384
//  Layer II 1152 1152 1152
//  Layer III 1152 576  576
 
 switch(pFrameHead->layer)
 {
 case 0x01://Layer III
  switch(pFrameHead->version)
  {
  case 0x00://MPEG 2.5
   samplenum = 576;
   break;
  case 0x02://MPEG 2
   samplenum = 576;
   break;
  case 0x03://MPEG 1
   samplenum = 1152;
   break;
  }
  break;
 case 0x02://Layer II
  switch(pFrameHead->version)
  {
  case 0x00://MPEG 2.5
   samplenum = 1152;
   break;
  case 0x02://MPEG 2
   samplenum = 1152;
   break;
  case 0x03://MPEG 1
   samplenum = 1152;
   break;
  }
  break;
 case 0x03://Layer I
  switch(pFrameHead->version)
  {
  case 0x00://MPEG 2.5
   samplenum = 384;
   break;
  case 0x02://MPEG 2
   samplenum = 384;
   break;
  case 0x03://MPEG 1
   samplenum = 384;
   break;
  }
  break;
 default:
  return 0;
 }
 switch(pFrameHead->bit_rate_index)
 {
 case 0x01:
  bitrate = 32;
  break;
 case 0x02:
  bitrate = 40;
  break;
 case 0x03:
  bitrate = 48;
  break;
 case 0x04:
  bitrate = 56;
  break;
 case 0x05:
  bitrate = 64;
  break;
 case 0x06:
  bitrate = 80;
  break;
 case 0x07:
  bitrate = 96;
  break;
 case 0x08:
  bitrate = 112;//注意:测试多首音乐发现它设置的实际值是128而不是112
  break;
 case 0x09:
  bitrate = 128;
  break;
 case 0x0a:
  bitrate = 160;
  break;
 case 0x0b:
  bitrate = 192;
  break;
 case 0x0c:
  bitrate = 224;
  break;
 case 0x0d:
  bitrate = 256;
  break;
 case 0x0e:
  bitrate = 320;
  break;
 default:
  return 0;
 }
 switch(pFrameHead->sample_rate_index)
 {
 case 0x00:
  switch(pFrameHead->version)
  {
  case 0x00:
   samplerate = 11025;
   break;
  case 0x02:
   samplerate = 22050;
   break;
  case 0x03:
   samplerate = 44100;
   break;
  }
  break;
 case 0x01:
  switch(pFrameHead->version)
  {
  case 0x00:
   samplerate = 12000;
   break;
  case 0x02:
   samplerate = 24000;
   break;
  case 0x03:
   samplerate = 48000;
   break;
  }
  break;
 case 0x02:
  switch(pFrameHead->version)
  {
  case 0x00:
   samplerate = 8000;
   break;
  case 0x02:
   samplerate = 16000;
   break;
  case 0x03:
   samplerate = 32000;
   break;
  }
  break;
 default:
  return 0;
 }
 assert(samplenum!=0&&samplerate!=0&&bitrate!=0);
 return (( samplenum/8*bitrate*1000 )/samplerate ) + (pFrameHead->padding?1:0);
}
MUSICINFO_API double miGetMp3Duration(LPTSTR pFileName)
{
 FHEADER fh;
 int framenum = 0;
 double spf = 0.0f;
 while(miEnumMp3FrameHeadInfo(pFileName,&fh))
 {
  if (framenum==0)
  {
   int samplenum = 0;
   int samplerate=0;

   switch(fh.layer)
   {
   case 1://Layer III
    switch(fh.version)
    {
    case 0://MPEG 2.5
     samplenum = 576;
     break;
    case 2://MPEG 2
     samplenum = 576;
     break;
    case 3://MPEG 1
     samplenum = 1152;
     break;
    }
    break;
   case 2://Layer II
    switch(fh.version)
    {
    case 0://MPEG 2.5
     samplenum = 1152;
     break;
    case 2://MPEG 2
     samplenum = 1152;
     break;
    case 3://MPEG 1
     samplenum = 1152;
     break;
    }
    break;
   case 3://Layer I
    switch(fh.version)
    {
    case 0://MPEG 2.5
     samplenum = 384;
     break;
    case 2://MPEG 2
     samplenum = 384;
     break;
    case 3://MPEG 1
     samplenum = 384;
     break;
    }
    break;
   }
  
   switch(fh.sample_rate_index)
   {
   case 0x00:
    switch(fh.version)
    {
    case 0x00:
     samplerate = 11025;
     break;
    case 0x02:
     samplerate = 22050;
     break;
    case 0x03:
     samplerate = 44100;
     break;
    }
    break;
   case 0x01:
    switch(fh.version)
    {
    case 0x00:
     samplerate = 12000;
     break;
    case 0x02:
     samplerate = 24000;
     break;
    case 0x03:
     samplerate = 48000;
     break;
    }
    break;
   case 0x02:
    switch(fh.version)
    {
    case 0x00:
     samplerate = 8000;
     break;
    case 0x02:
     samplerate = 16000;
     break;
    case 0x03:
     samplerate = 32000;
     break;
    }
    break;
   }
   assert(samplenum&&samplerate);
   spf = (double)samplenum/(double)samplerate;
  }
  framenum++;
  
 }
 return spf*(double)framenum;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值