带你分析wav音频文件结构(实例+代码)

您所在的位置:网站首页 录音格式wav是什么意思啊 带你分析wav音频文件结构(实例+代码)

带你分析wav音频文件结构(实例+代码)

2023-12-22 09:48| 来源: 网络整理| 查看: 265

转载请注明出处,谢谢您!https://blog.csdn.net/ljrsunshine/article/details/89320026对wav文件的一些探索,与大家一起学习

文章目录 RIFF的组织结构wav文件格式实例1. 标准的wav文件,如图1(小端模式):计算细节 2. 经过格式转换的wav文件,如图4(小端模式):计算细节 代码参考资料

RIFF的组织结构

引用自参考资料【1】RIFF文件浅析

  RIFF是Microsoft提出的一种多媒体文件的存储方式,不同编码的音频、视频文件,可以按照它定义的存储规则来保存、记录各自不同的数据,如:数据内容、采样信息、视窗大小、编码方式等。在播放器或者其他提取工具读取文件的时候,就可以根据RIFF的规则来分析文件,合理的解析出音频、视频信息,正确的播放它们。在现实中,常见的这类文件有WAV文件、AVI文件。它们都是遵循RIFF的方式保存自己的播放信息和播放数据的。

  在RIFF的文件存储规则中,主要有几个重要的概念需要理解。它们是FOURCC、RIFF文件头、CHUNK、LIST。RIFF的组成元素就是它们,分析一个按RIFF规则组织的文件,都可以把它划分成CHUNK、LIST、RIFF这几个部分。

  RIFF的数据存储形式是一种仿Microsoft文件系统的组织形式。在一个Microsoft的文件系统中,有盘符、目录、文件的概念。系统可以有几个盘符,每个盘符又可以有多个目录,在目录的下面则可以有子目录或是许多的文件。文件是保存数据的基本单元,而盘符目录是用来组织文件的。在RIFF的组织中,也借用了这些思想。在RIFF文件中,数据保存的基本单元是CHUNK,相当于Microsoft中的文件用他来保存一个一个代表实际意义的数据块。多个CHUNK可以用一个LIST组织起来。LIST相当于Microsoft中的目录。一个目录下面可以存放多个文件、子目录。同样,在一个LIST下面可以有几个CHUNK文件或子LIST。而多个CHUNK、LIST又由一个“RIFF头”来统领。在“RIFF头”中记录一个RIFF文件的各种信息。相当于Microsoft中的盘符。

  FOURCC

  一个FOURCC(four-character code)是一个占32位四个字节的数据,一般表示为4个ASCII字符。例如:一个FOURCC“abcd”在系统中就表示为 “x64636261”。FOURCC可以包括空格,所以“abc ”也是一个有效的FOURCC,在RIFF文件格式中,用FOURCC代码来标识数据流的格式、数据块的含义及其他信息。

  1)一个CHUNK数据块的数据结构如下:

  Chunkid chunkSize  ChunkData

  Chunkid是一个FOURCC,表示这个CHUNK记录的是那些内容,相似与Microsoft中的文件名,ChunkSize占用4字节,表示这个CHUNK中数据内容的大小。ChunkData则是这个CHUNK中实质性的东西,保存CHUNK的具体数据内容。一个CHUNK保存的数据可以是关于声音文件的编码方式、音频采样等信息。也可以是声音文件的声音数据。具体表示的是哪类数据则通过CHUNKID来标识。

  2)一个List数据块的数据结构如下:

  “LIST“ listSize listType listData

  在这里,“LIST”也是一个FOURCC,而且是固定的,每个LIST都是以“list”为开头,标识它是一个LIST,就像在Microsoft文件系统中有一个标志来标识 目录一样。ListType则是这个“目录”的名字,要求是一个FOURCC。listSize占有4字节,表示这个“目录”下保存的数据有多大。而listData则是这个目录下保存的数据,由chunk和list来组成,它们的个数和组成次序是可以不确定的。注意,listSize的值是listType的大小(即4个字节)加上listData的长度。不包括“LIST”和listSize的长度。

  3)RIFF文件头是数据结构如下:

  “RIFF” fileSize fileType fileData

  这里,“RIFF”是一个字符串,也是一个FOURCC,表示是一个RIFF格式文件。fileSize是一个4个字节的数据,给出文件的大小。fileType是一个FOURCC的数据,用来说明的文件类型,如:“WAV”、“AVI”等(WAV、AVI文件都是基于RIFF的文件格式的)。请注意,fileSize表示的文件大小,是不包括“RIFF”和它自己所占的8个字节的,是RIFF文件头后面跟的数据大小再加上fileType的4个字节。FileData部分是用来保存他统领的内容的,可以是LIST也可以是CHUNK。

(以上内容引用自 参考资料【1】RIFF文件浅析)

wav文件格式

  wav文件是非常简单的一种RIFF文件,它的格式类型为"WAVE"。RIFF块包含两个子块,这两个子块的ID分别是"fmt"和"data"

偏移地址大小字节数据块类型内容00H~03H44字符资源交换文件标志(RIFF)04H~07H4长整数从下个地址开始到文件尾的总字节数08H~0BH44字符WAV文件标志(WAVE)0CH~0FH44字符波形格式标志(fmt ),最后一位空格。10H~13H4整数值表示的是后面有一段数据的总长度字节(见下面的实例解析的加粗部分)14H~15H2整数编码格式,值为1时,表示数据为线性PCM编码16H~17H2整数声道数,单声道为1,双声道为218H~1BH4长整数采样频率,00003E80 H =16000,0000AC44 H = 441001CH~1FH4长整数波形数据传输速率(每秒平均字节数),即(比特率) / 8 = (声道数×采样频率×每样本的数据位数) / 820H~21H2整数块对齐=声道数×每次采样得到的样本位数 / 822H~23H2整数样本数据的位数 或 位深度,16或824H~27H44字符“data”或“LIST”,当其他格式的音频经过格式转换成WAV文件时,这部分会是“LIST”28H~2BH4长整型如果前面4个字节是data,则这里表示DATA总数据长度字节,2CH开始是音频数据(见实例1);如果前面4个字节不是data,则后面记录一些格式转换的信息(见实例2)2CH……DATA数据块 实例

  笔者使用UltraEdit编辑器打开wav文件

1. 标准的wav文件,如图1(小端模式):

标准的wav文件

图1  标准的wav文件 图2  wav文件“属性-->大小”截图

偏移地址字节说明00H ~ 03H52 49 46 46资源交换文件标志符RIFF04H ~ 07H24 FA 00 00表示后面文件的大小,小端模式是0000FA24=64036,这个数字加上00H~07H的8个字节,等于64044字节,是wav文件的总大小,在右键“属性–>大小”里显示,如图208H ~ 0BH57 41 56 45标示符WAVE0CH ~ 0FH66 6D 74 20波形格式标示符fmt10H ~ 13H10 00 00 00小端模式是00000010 = 16,表示后面有一段数据的长度是16个字节,即,从14H到23H14H ~ 15H01 00小端模式是0001 = 1 ,表示线性的PCM编码16H ~ 17H01 00小端模式是0001 = 1 ,表示单声道,MONO18H ~ 1BH80 3E 00 00小端模式是00003E80 = 16000,表示采样率是16000Hz1CH ~ 1FH00 7D 00 00小端模式是00007D00 = 32000 Bps,波形数据传输率,字节每秒20H ~ 21H02 00小端模式是0002 = 2,块对齐22H ~ 23H10 00小端模式是0010 = 16,样本数据的位数,表示用16位表示一个样本24H ~ 27H64 61 74 61标志符data28H ~ 2BH00 FA 00 00小端模式是0000FA00 = 64000,表示data的数据大小是64000 B2CH ~ …音频数据 计算细节 波形数据传输率:采样率(16000) × 位深度(16) × 声道数(1) / 8 = 256(kbps) / 8 = 256000(bps) / 8 = 32000 Bps(字节每秒)块对齐:声道数(1) × 每次采样得到的样本位数(16) / 8 = 2播放时间 = dataSize字节 / (比特率/ 8) = 64000B / (32000 Bps) = 2秒(图3) 图3 播放时间、文件总大小(64044字节=62.5KB)、比特率

2. 经过格式转换的wav文件,如图4(小端模式):

经过格式转换的wav文件

图4 经过格式转换的wav文件 图5 wav文件“属性-->大小”截图

偏移地址字节说明00H ~ 03H52 49 46 46资源交换文件标志符RIFF04H ~ 07H46 48 09 00表示后面文件的大小,小端模式是00094846 = 608326B,这个数字加上00H~07H的8个字节,等于608334字节,是wav文件的总大小,在右键“属性–>大小”里显示,如图508H ~ 0BH57 41 56 45标示符WAVE0CH ~ 0FH66 6D 74 20波形格式标示符fmt10H ~ 13H10 00 00 00小端模式是00000010 = 16,表示后面有一段数据的长度是16个字节,即,从14H到23H14H ~ 15H01 00小端模式是0001 = 1 ,表示线性的PCM编码16H ~ 17H02 00小端模式是0002 = 2 ,表示双声道18H ~ 1BH44 AC 00 00小端模式是0000AC44 = 44100,表示采样率是44100Hz1CH ~ 1FH10 B1 02 00小端模式是0002B110 = 176400 Bps,波形数据传输率,字节每秒20H ~ 21H04 00小端模式是0004 = 4,块对齐22H ~ 23H10 00小端模式是0010 = 16,样本数据的位数,表示用16位表示一个样本24H ~ 27H4C 49 53 54“LIST”28H ~ 2BH1A 00 00 00小端模式是0000001A=26,表示后面有一段数据的长度是26字节,即2CH ~ 45H。2CH ~ 45H49 4E 46 4F ~ 30 00这段数据记录的是格式转换的一些信息,具体没有研究过46H ~ 49H64 61 74 61标志符data4AH ~ 4DH00 48 09 00小端模式是00094800 = 608256,表示data的数据大小是608256 B4EH ~ …音频数据 计算细节 波形数据传输率:采样率(44.1k) × 位深度(16) × 声道数(2) / 8 = 1411(kbps) / 8 = 1411200 / 8 = 176400 Bps(字节每秒)块对齐:声道数(2) × 每次采样得到的样本位数(16) / 8 = 4播放时间 = dataSize字节 / (比特率/ 8) = 608256 B / (176400 Bps) = 3.4秒(图6) 图6 播放时间、文件总大小(608334字节=594KB)、比特率 代码

  在实例分析里我们讲到,如果wav文件是由其他格式的音频文件经过格式转换而来,那么转换后得到的wav文件头会记录一些转换的信息。如果你理解了引用资料和实例2,就会发现wav文件中信息排列是有规律可循的。笔者参考HTK源码,整理了读取wav文件信息的c代码:

(测试的wav文件是PCM编码,单声道,16位深)

void Load(char path[]) { FILE *file = fopen(path, "rb"); if (!file) { fprintf(stderr, "[Load] [%s not found]\n", path); return; } char magic[4]; int lng; char c; // RIFF fread(magic, 4, 1, file); if (strncmp("RIFF", magic, 4)) { fprintf(stderr, "[Load] [not RIFF]\n"); } //printf("sizeof(int):%d\n", sizeof(int)); // 4 fread(&lng, 4, 1, file); fread(magic, 4, 1, file); if (strncmp("WAVE", magic, 4)) { fprintf(stderr, "[Load] [not WAVE]\n"); } /* Look for "fmt " before end of file */ while (1) { if (feof(file)) { fprintf(stderr, "[Load] No data portion in WAVE file"); } fread(magic, 4, 1, file);// fmt 或 data fread(&length_wav, 4, 1, file);// chunk size if (strncmp("data", magic, 4) == 0) break; if (strncmp("fmt ", magic, 4) == 0) { fread(&wave->FormatType, 2, 1, file); // 十进制是1 线性的PCM编码 fread(&wave->Channels, 2, 1, file); /* Number of Channels */ fread(&wave->SamplesPerSec, 4, 1, file); /* Sample Rate */ fread(&wave->AvgBytesPerSec, 4, 1, file); /* Average bytes/second */ fread(&wave->BlockAlign, 2, 1, file); /* Block align */ fread(&wave->BitsPerSample, 2, 1, file); // 样本数据的位数 = 16 if (wave->BitsPerSample != 16 && wave->BitsPerSample != 8) { fprintf(stderr, "[Load] Only 8/16 bit audio supported"); } length_wav -= 16; } /* Skip chunk */ for (; length_wav > 0; length_wav--) fread(&c, 1, 1, file); } // 循环结束后,length_wav的值就是声音数据的长度 data = (unsigned char*)realloc(data, length_wav); fread(data, length_wav, 1, file); fclose(file); } 参考资料

【1】RIFF文件浅析(百度文库,豆丁网均有这篇文章)

【2】WAVE PCM声音文件格式

【3】WAV文件格式分析(这篇是转载,在2009年,最早的原创文章找不到了)

【4】wav文件详解

【5】音频码率及大小计算

【6】HTK源码 HWave.c文件 GetWAVHeaderInfo函数

感谢这些优秀的前辈!

2019.03.22

点赞是一种鼓励!


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3