如标题所示,但是仅实现了数据归类,包含解析文件头,文件尾,文件数据帧,数据帧头等内容,缺少解析vbr cbr数据的实现
需要先说明一下,MP3文件本身数据结构就是一堆帧构成,从上至下依次是
文件帧头,标签帧,填充空白帧,数据帧,文件信息帧
1.mp3文件头
这一段内容包含文件的各种信息,内容如下
// 文件头
typedef struct
{
uint8_t header[3]; // 文件头
uint8_t ver; // 文件版本
uint8_t revision; // 子版本
uint8_t flag; // 标记
uint8_t size[4]; // 大小
} mp3FileHeader_t;
2.mp3标签帧
这一段包含各种标签,用来展示这个曲子的多数内容,每个帧大小不同,包含的内容也不同,而且数量不定,或多或少,其中标签属性可以分解为下面的结构体内容,说明了一个标签的属性定义
// 标签头
typedef struct
{
uint8_t header[4]; // 头
uint8_t size[4]; // 标签大侠
uint8_t flag[2]; // 标签属性
} mp3LableHeader_t;
// 标签头标志位的解析
typedef struct
{
bool tagProtection; // 标签保护
bool fileProtection; // 文件保护
bool readOnly; // 只读
bool compression; // 压缩
bool encrypt; // 加密
bool group; // 属于某一组
} mp3LableHeaderFlag_t;
3.空白帧
无意义,跳过即可
4.数据帧的帧头
这块内容需要自己手动填充,需要对数据解析,包含了之后该怎么解析这个MP3的音频所需参数,包括
协议版本,协议层,是否CRC校验,位率,声道等
协议版本和协议层:这个决定了你要采取采样率,采样数,帧长度,这些东西有固定的计算方式和对应关系表,在计算时需要查表对照使用,如果你不想计算的话,可以采取其他博主的按字节查找FFFB这个,我个人觉得这很不靠谱.所以我没有采用
// 参考文档 https://blog.csdn.net/u010650845/article/details/53520426
typedef struct
{
uint16_t syn; // 同步信息,所有位均为1
uint8_t ver; // 版本,MPEG1
uint8_t layer; // 层,Layer3
uint8_t existCRC; // 无CRC校验
uint16_t bitRate; // 位率,单位是kbps,320
uint8_t samplingRate; // 采样频率,44.1K
uint8_t lengthAdjustment; // 用来调整文件头长度,无需调整
uint8_t reserve; // 保留字,未使用
uint8_t soundChannel; // 表示声道模式,立体声Stereo
uint8_t stereo; // 声道模式为Joint Stereo时才使用
uint8_t validityDetection; // 文件是否合法,不合法
uint8_t originalVersion; // 是否原版,非原版
uint8_t noiseReductionCompensation; // 用于声音经降噪压缩后再补偿的分类,未定义
} mp3DataHeader_t;
4.1.数据帧的通道信息
这一块没找到参考信息,故本文跳过
5. 代码实现
5.1 解析数据MP3文件头
// 文件帧
mp3FileHeader_t fileHeader = {0};
fread(&fileHeader, 1, sizeof(mp3FileHeader_t), file);
parseMp3FileHeader(fileHeader);
5.2 解析标签帧
// 标签帧
mp3LableHeader_t labheader = {0};
fread(&labheader, 1, sizeof(mp3LableHeader_t), file);
uint32_t labelLen = parseMp3LabelHeader(labheader);
5.3 解析数据帧
5.3.1 数据帧头
mp3DataHeader_t mp3DataHeader = {0};
uint32_t frameLength = mp3DataHeaderParse(&mp3DataHeader, buf);
5.3.2 数据帧通道
mp3Sideinfo_t channelInfo = {0};
// printf("channel info size %d\n", sizeof(mp3Sideinfo_t));
// fread(&channelInfo, 1, sizeof(mp3Sideinfo_t), file);
fread(&channelInfo, 1, 32, file);
// mp3ChannelInfoParse(channelInfo);
5.3.3 数据帧内容-VBR帧头
mp3VBRHeader_t mp3VBRHeader = {0};
fread(&mp3VBRHeader, 1, sizeof(mp3VBRHeader_t), file);
printf(
"mp3VBRHeader header [%#x] fileFlag [%d] frameNumber [%d] fileLength [%d]\n",
mp3VBRHeader.header,
mp3VBRHeader.fileFlag,
mp3VBRHeader.frameNumber,
mp3VBRHeader.fileLength);
5.3.4 数据帧数据-VBR数据
frameLength = (frameLength - 32 - 4 - sizeof(mp3VBRHeader_t));
printf("mp3 VBR OR CBR data addr index [%#x] data len [%d]\n", ftell(file), frameLength);
uint8_t *rawData = (uint8_t *)malloc(frameLength);
if (rawData == NULL)
{
isdataParam = false;
printf("malloc fail!!\n");
break;
}
memset(rawData, 0, frameLength);
fread(rawData, 1, frameLength, file);
在此之后就需要对数据帧进行解析了,其中涉及到一些比较复杂的算法,暂未实现,数据内容是rawData
源代码查看 代码仓库https://gitee.com/icandang/learn.git
项目下 test\mp3parse\mp3Parse.c 这个文件就是
6. 编译使用
编译--> gcc .\mp3Parse.c -o .\mp3Parse
使用--> .\mp3Parse.exe '.\余天易,徐梦圆 - 鹿鸣(Instrumental)Remix.mp3' > log.log