热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

WAV文件的二进制格式解析

文章目录第一个子chunk另一个子chunk实例:用C语言解析wav文件运行结果:通过WinHex验证结果根据网络上的各种资料可以得知WAVE文件本质上


文章目录

    • 第一个子chunk
    • 另一个子chunk
    • 实例:用C语言解析wav文件
    • 运行结果:
    • 通过WinHex验证结果




在这里插入图片描述

根据网络上的各种资料可以得知WAVE文件本质上就是一种RIFF格式,它可以抽象成一颗树(数据结构的一种)来看。

ss

我们看到这张图上面,从上到下分别对应着二进制数据在文件中相对于起始位置的偏移量。每一个格子对应一个字段,field size表示每个字段所占据的大小,根据这个大小以及当前的偏移量,我们也可以计算出下一个字段的起始地址(偏移量)。

一个chunk结构其实就是三个部分,第一部分标识符用于说明这个chunk是存什么内容,第二部分是说明这个chunk的内容有多大,用于让程序知道如果要找到下一个chunk该把地址偏移多少去读取,第三部分则是实际内容

顶级chunk: 说明这个chunk是存什么内容

子chunk


第一个子chunk

Subchunk1ID 在WAV文件中恒定为fmt,表示该subchunk的内容为该WAV音频文件的一些元数据,即WAV音频的一些格式信息

AudioFormat这个字段一般为1,表示这个WAV音频为PCM编码

NumChannels则是该WAV音频文件的声道数量。1为单声道,2为双声道。

SampleRate 则为采样率

ByteRate 为数据传输速率。其值为:通道数*每秒数据位数*每样本的数据位数/8

BlockAlign 则是每个block的平均大小,它等于NumChannels * BitsPerSample/8,至于block是什么,以及它的计算公式是怎么得来的需要来看看另一个Subchunk。BitsPerSample则为每秒采样比特,有的地方称它为量化精度或者PCM位宽。


另一个子chunk

Subchunk2ID 是在WAV文件中恒定为data,即WAV音频文件的实际音频数据,里面存储的是音频的采样数据。但是我们的音频如果是双声道,那么实际上某一个采样时刻采样的数据是由左声道和右声道共同组成的。而这个共同组成的采样我们把他成为block。前面有讲到BlockAlign = NumChannels * BitsPerSample / 8,这个现在就很好理解了,至于为什么末尾要除以8,这是因为计算机中是以8个二进制数表示一个字节,所以要除以8来求出字节数。

音频的持续长度,我们可以通过Subchunk2Size除以ByteRate,也就是实际音频data的chunk总长度除以每秒字节数得到持续多少秒。


实例:用C语言解析wav文件

#include
#include /*struct类型里面我用的是uint32_t等类型,而不是传统的int,short等等这些类型是由stdint.h头文件提供*/
#include typedef struct wave_tag //声明结构体的相关参数 {char ChunkID[4]; // "RIFF"标志unsigned int ChunkSize; // 文件长度(WAVE文件的大小, 不含前8个字节)char Format[4]; // "WAVE"标志char SubChunk1ID[4]; // "fmt "标志unsigned long int SubChunk1Size; // 过渡字节(不定)unsigned short int AudioFormat; // 格式类别(10H为PCM格式的声音数据)unsigned short int NumChannels; // 通道数(单声道为1, 双声道为2)unsigned short int SampleRate; // 采样率(每秒样本数), 表示每个通道的播放速度unsigned int ByteRate; // 波形音频数据传输速率, 其值为:通道数*每秒数据位数*每样本的数据位数/8unsigned short int BlockAlign; // 每个样点的Byte数(按字节算), 其值为:通道数*每样本的数据位数/8unsigned short int BitsPerSample; // 每个样点的数据位数, 表示每个声道中各个样本的数据位数.char SubChunk2ID[4]; // 数据标记"data"unsigned long int SubChunk2Size; // 语音数据的长度} WAVE;int main(int argc, char *argv[]){FILE *fp; //定义指针型文件 WAVE wav; //调用结构体wav fp=fopen("E:\\test_wave\\2.wav","rb"); /*以二进制方式打开并读取(rb)wav文件,通常r是正常方式读取文件 */ fread(&wav, sizeof(WAVE), 1, fp); //读取文件信息 //打印输出wave文件头的相关信息
printf("ChunkID=\t%c%c%c%c \n",wav.ChunkID[0],wav.ChunkID[1],wav.ChunkID[2],wav.ChunkID[3]); //输出"RIFF"标志printf("ChunkSize=\t%d\n",wav.ChunkSize); //输出文件长度,即WAVE文件的大小printf("Format=\t\t%c%c%c%c\n",wav.Format[0],wav.Format[1],wav.Format[2],wav.Format[3]);//输出"WAVE"标志 printf("SubChunk1ID=\t%c%c%c%c\n",wav.SubChunk1ID[0],wav.SubChunk1ID[1],wav.SubChunk1ID[2],wav.SubChunk1ID[3]);// 输出"fmt "标志printf("SubChunk1Size=\t%ld\n",wav.SubChunk1Size);// 输出过渡字节(不定)printf("AudioFormat=\t%x\n",wav.AudioFormat); // 输出格式类别(10H为PCM格式的声音数据)printf("NumChannels=\t%d\n",wav.NumChannels);//输出通道数 printf("SampleRate=\t%d\n",wav.SampleRate);//输出采样率 printf("ByteRate=\t%d\n",wav.ByteRate);// 输出波形音频数据传输速率printf("BlockAlign=\t%d\n",wav.BlockAlign);//输出每个样点的Byte数printf("BitsPerSample=\t%d\n",wav.BitsPerSample);// 输出每个样点的数据位数printf("SubChunk2ID=\t%c%c%c%c \n",wav.SubChunk2ID[0],wav.SubChunk2ID[1],wav.SubChunk2ID[2],wav.SubChunk2ID[3]);//输出data标志 printf("SubChunk2Size=\t%ld\n",wav.SubChunk2Size);//输出数据的长度 return 0;}

运行结果:

image-20200110113424163.png


通过WinHex验证结果

WinHex 是一款以通用的 16 进制编辑器为核心,专门用来对付计算机取证、数据恢复、低级数据处理、以及 IT 安全性、各种日常紧急情况的高级工具: 用来检查和修复各种文件、恢复删除文件、硬盘损坏、数码相机卡损坏造成的数据丢失等。可进行 2 进制、16 进制 ASCII, Intel 16 进制, 和 Motorola S 转换。
image-20200110113930082.png


推荐阅读
author-avatar
多盟乄丶
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有