【音频】WAV 格式详解

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 字节部分为扩展块
4 bytes FmtTag 编码格式代码,其值见下 常见编码格式 表,如果上述取值为 16,则此值通常为 1,代表该音频的编码方式是 PCM 编码
4 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
4 bytes BlockAilgn 块对齐,告知播放软件一次性需处理多少字节,公式为: BitsPerSample*FmtChannels/8
4 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%)

查看文件大小

 ls test.wav -l
-rw-rw-r-- 1 tyustli tyustli 1411248 729 15:03 test.wav

hd 工具查看原始数据

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 ***************/

猜你喜欢

转载自blog.csdn.net/tyustli/article/details/126240571