WAV 文件格式解析
概述
wav 文件支持多种不同的比特率、采样率、多声道音频。
WAV 文件格式是 Microsoft 的 RIFF 规范的一个子集,用于存储多媒体文件。RIFF(resource interchange file format 资源互换文件格式,以 chunk(块) 为单位组织文件)格式文件。在 windows 上,大部分多媒体文件都是 RIFF 文件。wav 文件由若干个 RIFF chunk 构成,分别为: RIFF WAVE Chunk,Format Chunk,Fact Chunk(可选),Data Chunk。另外,文件中还可能包含一些可选的区块,如:Fact chunk、Cue points chunk、Playlist chunk、Associated data list chunk 等。具体格式如下:
块解析
wav 文件都是由 chunk 组成,chunk 的格式如下
size | 内容 | 含义 |
---|---|---|
4 bytes | ID | 如:RIFF,fmt,data |
4 bytes | chund size | 如标准的 fmt chunk 为 16 字节 |
N bytes | data | chunk 的内容 |
RIFF chunk
typedef struct
{
char ChunkID[4]; //'R','I','F','F'
unsigned int ChunkSize;
char Format[4]; //'W','A','V','E'
}riff_chunk;
size | 内容 | 含义 |
---|---|---|
4 bytes | ChunkID | RIFF |
4 bytes | ChunkSize | 从下一个字段首地址开始到文件末尾的总字节数。该字段的数值加 8 为当前文件的实际长度 |
4 bytes | Format | WAVE |
其中 ChunkSize 代表的是整个 file_size 的大小减去 ChunkID 和 ChunkSize 的大小,即 file_size=ChunkSize+8。
fmt chunk
typedef struct
{
char FmtID[4];
unsigned int FmtSize;
unsigned short FmtTag;
unsigned short FmtChannels;
unsigned int SampleRate;
unsigned int ByteRate;
unsigned short BlockAilgn;
unsigned short BitsPerSample;
}fmt_chunk;
size | 内容 | 含义 |
---|---|---|
4 bytes | FmtID | fmt |
4 bytes | FmtSize | fmt chunk 的大小,一般有 16/18/20/22/40 字节 (也有超过 40 字节的情况,如果不知道后面部分的含义,直接跳过即可),超过 16 字节部分为扩展块 |
2 bytes | FmtTag | 编码格式代码,其值见下 常见编码格式 表,如果上述取值为 16,则此值通常为 1,代表该音频的编码方式是 PCM 编码 |
2 bytes | FmtChannels | 声道数目,1 代表单声道,2 代表双声道 |
4 bytes | SampleRate | 采样频率,8/11.025/12/16/22.05/24/32/44.1/48/64/88.2/96/176.4/192 kHZ |
4 bytes | ByteRate | 传输速率,每秒的字节数,计算公式为:SampleRate * FmtChannels * BitsPerSample/8 |
2 bytes | BlockAilgn | 块对齐,告知播放软件一次性需处理多少字节,公式为: BitsPerSample*FmtChannels/8 |
2 bytes | BitsPerSample | 采样位数,一般有8/16/24/32/64,值越大,对声音的还原度越高 |
常见编码格式
格式编码 | 格式名称 | fmt 块长度 | fact 块 |
---|---|---|---|
0x01 | PCM / 非压缩格式 | 16 | |
0x02 | Microsoft ADPCM | 18 | √ |
0x03 | IEEE float | 18 | √ |
0x06 | ITU G.711 a-law | 18 | √ |
0x07 | ITU G.711 μ-law | 18 | √ |
0x031 | GSM 6.10 | 20 | √ |
0x040 | ITU G.721 ADPCM | √ | |
0xFFFE | 见子格式块中的编码格式 | 40 |
data chunk
struct DATA_CHUNK
{
char DataID[4]; //'d','a','t','a'
unsigned int DataSize;
};
size | 内容 | 含义 |
---|---|---|
4 bytes | DataID | data |
4 bytes | DataSize | 原始音频数据的大小 |
示例分析
Linux 平台下使用 mediainfo 分析 wav 文件
安装
sudo apt-get install mediainfo
解析
执行命令:mediainfo test.wav
General
Complete name : test.wav
Format : Wave
File size : 1.35 MiB
Duration : 8 s 0 ms
Overall bit rate mode : Constant
Overall bit rate : 1 411 kb/s
Audio
Format : PCM
Format settings : Little / Signed
Codec ID : 1
Duration : 8 s 0 ms
Bit rate mode : Constant
Bit rate : 1 411.2 kb/s
Channel(s) : 2 channels
Sampling rate : 44.1 kHz
Bit depth : 16 bits
Stream size : 1.35 MiB (100%)
查看文件大小文章来源:https://www.toymoban.com/news/detail-698052.html
ls test.wav -l
-rw-rw-r-- 1 tyustli tyustli 1411248 7月 29 15:03 test.wav
hd
工具查看原始数据文章来源地址https://www.toymoban.com/news/detail-698052.html
hd test.wav -n 128
00000000 52 49 46 46 a8 88 15 00 57 41 56 45 66 6d 74 20 |RIFF....WAVEfmt |
00000010 10 00 00 00 01 00 02 00 44 ac 00 00 10 b1 02 00 |........D.......|
00000020 04 00 10 00 64 61 74 61 84 88 15 00 0e fc fa fe |....data........|
00000030 6c fb 8a fe c2 fa 19 fe 1e fa b5 fd 85 f9 64 fd |l.............d.|
00000040 f5 f8 20 fd 73 f8 df fc 09 f8 92 fc bb f7 28 fc |.. .s.........(.|
00000050 83 f7 9d fb 5f f7 fe fa 46 f7 55 fa 2e f7 a3 f9 |...._...F.U.....|
00000060 23 f7 fa f8 36 f7 6b f8 57 f7 ef f7 84 f7 83 f7 |#...6.k.W.......|
00000070 c7 f7 2d f7 1e f8 eb f6 88 f8 c3 f6 0b f9 c5 f6 |..-.............|
- 52 49 46 46:对应的 ASCII 字符为:RIFF
- a8 88 15 00:ChunkSize,对应的十六进制是 0x1588a8=1411240 +8 和上面的文件大小一致
- 57 41 56 45:对应的 ASCII 字符为 WAVE
- 66 6d 74 20:对应的 ASCII 字符为 fmt
- 10 00 00 00:FmtSize 0x10=16 代表PCM编码方式
- 01 00:对应为1,代表PCM编码方式
- 02 00:通道个数,通道数为2
- 44 ac 00 00:采样频率 0xac44=44100=44.1KHz
- 10 b1 02 00:每秒所需的字节数,转化为十六进制为: 0x2b110=176400 通过此值可以计算该音频的时长:1411240/176400 = 8s
- 04 00:数据对齐单位
- 10 00:采样位数 0x10=16
- 64 61 74 61:对应的 ASCII 字符为 data
- 84 88 15 00:对应该音频的raw数据的大小,转化为十六进制为 0x158884=1411204,此值等于 1411248-44
代码解析
/*
* Change Logs:
* Date Author Notes
* 2022-08-26 tyustli first version
*/
#include <stdio.h>
#include <stdint.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
typedef struct {
char ChunkID[4]; //'R','I','F','F'
unsigned int ChunkSize;
char Format[4]; //'W','A','V','E'
} riff_chunk;
typedef struct {
char FmtID[4];
unsigned int FmtSize;
unsigned short FmtTag;
unsigned short FmtChannels;
unsigned int SampleRate;
unsigned int ByteRate;
unsigned short BlockAilgn;
unsigned short BitsPerSample;
} fmt_chunk;
typedef struct {
char DataID[4]; //'d','a','t','a'
unsigned int DataSize;
} data_chunk;
typedef struct {
riff_chunk riff_region;
fmt_chunk fmt_region;
data_chunk data_region;
} wav_struct;
static void *map_file(const char *path);
static void data_dump(wav_struct *data);
int main(int argc, char *argv[]) {
assert(sizeof(wav_struct) == 44); /* defensive */
if (argc < 2) {
printf("usage: %s file_path\r\n", argv[0]);
exit(-1);
}
/* map file */
wav_struct *map_data = map_file(argv[1]);
/* data dump */
data_dump(map_data);
/* munmap file */
munmap(map_data, map_data->riff_region.ChunkSize + 8);
return 1;
}
static void *map_file(const char *path) {
assert(path != NULL);
int fd = open(path, O_RDWR);
if (fd == -1) {
goto __release;
}
#if 0
off_t size = lseek(fd, 0, SEEK_END);
if (size == -1) {
goto __release;
}
#endif
/* get file size */
struct stat stat;
int ret = fstat(fd, &stat);
if (ret == -1) {
goto __release;
}
size_t size = stat.st_size;
/* map file */
void *file_data = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
if(file_data == (void *)-1) {
goto __release;
}
close(fd);
return file_data;
/* error handle */
__release:
perror("map file");
if (fd > 0) {
close(fd);
}
exit(1);
}
static void data_dump(wav_struct *data) {
/* riff chunkid */
printf("riff chunk id :");
for(int i = 0; i < 4; i++) {
printf("%c", data->riff_region.ChunkID[i]);
}
printf("\r\n");
/* file size */
printf("wav file size :%d\r\n", data->riff_region.ChunkSize + 8);
/* riff Format */
printf("riff format id :");
for(int i = 0; i < 4; i++) {
printf("%c", data->riff_region.Format[i]);
}
printf("\r\n");
/* fmt chunkid */
printf("fmt chunk id :");
for(int i = 0; i < 4; i++) {
printf("%c", data->fmt_region.FmtID[i]);
}
printf("\r\n");
printf("FmtChannels :%d(1 单声道, 2 双声道)\r\n", data->fmt_region.FmtChannels);
printf("FmtTag :%d(1 PCM 编码)\r\n", data->fmt_region.FmtTag);
printf("SampleRate :%d\r\n", data->fmt_region.SampleRate);
printf("ByteRate :%d\r\n", data->fmt_region.ByteRate);
printf("BitsPerSample :%d\r\n", data->fmt_region.BitsPerSample);
/* data chunkid */
printf("fmt chunk id :");
for(int i = 0; i < 4; i++) {
printf("%c", data->data_region.DataID[i]);
}
printf("\r\n");
}
/**
* 编译:gcc wav_parse.c
* 运行:./a.out sample.wav
* 结果:
* riff chunk id :RIFF
* wav file size :497904
* riff format id :WAVE
* fmt chunk id :fmt
* FmtChannels :2(1 单声道, 2 双声道)
* FmtTag :1(1 PCM 编码)
* SampleRate :44100
* ByteRate :176400
* BitsPerSample :16
* fmt chunk id :data
*
*/
/*************** end of file ***************/
到了这里,关于音频——WAV 格式详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!