【音频】WAV 格式详解_wav文件格式详解

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

WAV 文件格式解析

概述

wav 文件支持多种不同的比特率、采样率、多声道音频。

WAV 文件格式是 Microsoft 的 RIFF 规范的一个子集用于存储多媒体文件。RIFFresource interchange file format 资源互换文件格式以 chunk(块) 为单位组织文件格式文件。在 windows 上大部分多媒体文件都是 RIFF 文件。wav 文件由若干个 RIFF chunk 构成分别为: RIFF WAVE ChunkFormat ChunkFact Chunk(可选)Data Chunk。另外文件中还可能包含一些可选的区块如Fact chunk、Cue points chunk、Playlist chunk、Associated data list chunk 等。具体格式如下

请添加图片描述

块解析

wav 文件都是由 chunk 组成chunk 的格式如下

size内容含义
4 bytesID如RIFF,fmt,data
4 byteschund size如标准的 fmt chunk 为 16 字节
N bytesdatachunk 的内容

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 bytesChunkIDRIFF
4 bytesChunkSize从下一个字段首地址开始到文件末尾的总字节数。该字段的数值加 8 为当前文件的实际长度
4 bytesFormatWAVE

其中 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 bytesFmtIDfmt
4 bytesFmtSizefmt chunk 的大小一般有 16/18/20/22/40 字节 (也有超过 40 字节的情况如果不知道后面部分的含义直接跳过即可)超过 16 字节部分为扩展块
4 bytesFmtTag编码格式代码其值见下 常见编码格式 表如果上述取值为 16则此值通常为 1代表该音频的编码方式是 PCM 编码
4 bytesFmtChannels声道数目1 代表单声道2 代表双声道
4 bytesSampleRate采样频率8/11.025/12/16/22.05/24/32/44.1/48/64/88.2/96/176.4/192 kHZ
4 bytesByteRate传输速率每秒的字节数计算公式为:SampleRate * FmtChannels * BitsPerSample/8
4 bytesBlockAilgn块对齐告知播放软件一次性需处理多少字节公式为 BitsPerSample*FmtChannels/8
4 bytesBitsPerSample采样位数一般有8/16/24/32/64值越大对声音的还原度越高

常见编码格式

格式编码格式名称fmt 块长度fact 块
0x01PCM / 非压缩格式16
0x02Microsoft ADPCM18
0x03IEEE float18
0x06ITU G.711 a-law18
0x07ITU G.711 μ-law18
0x031GSM 6.1020
0x040ITU G.721 ADPCM
0xFFFE见子格式块中的编码格式40

data chunk

struct DATA_CHUNK
{
    char DataID[4]; //'d','a','t','a'
    unsigned int DataSize;
};
size内容含义
4 bytesDataIDdata
4 bytesDataSize原始音频数据的大小

示例分析

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 00ChunkSize对应的十六进制是 0x1588a8=1411240 +8 和上面的文件大小一致
  • 57 41 56 45对应的 ASCII 字符为 WAVE
  • 66 6d 74 20对应的 ASCII 字符为 fmt
  • 10 00 00 00FmtSize 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 ***************/

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6