音频——WAV 格式详解

2023-11-06

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%)

查看文件大小

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

音频——WAV 格式详解 的相关文章

  • lzma算法分析

    lzma算法分析 这几天在公司主要在做压缩相关 记录一下所得 目前业界主流的压缩算法感觉并不多 好用的就Huffman lz系列 其他的像差分编码 vlq编码 感觉只能做个数据预处理 或者一种小范围的压缩 lz系列有很多 主要有lz77 l
  • 禁用系统【快应用】,停止【快应用】自动弹出

    快应用 九大厂商同时宣布建立即时应用生态发展联盟 通过统一标准让开发者低成本接入 快应用 在研发接口 场景接入 服务能力和接入方式上建设标准平台 以平台化的生态模式对个人开发者和企业开发者全品类开放 此次九大厂商共建 快应用 标准和平台 最
  • C语言:操作符以及部分表达式介绍

    目录 1 操作符 1 1 算数操作符 1 2 移位操作符 1 3 位操作符 1 4 1 赋值操作符 1 4 2 复合赋值符 1 5 单目操作符 1 6 关系操作符 1 7 逻辑操作符 1 8 条件操作符 1 9 逗号操作符 1 10 下标引

随机推荐

  • udp发包结合tkinter

    import socket import tkinter from tkinter import filedialog from tkinter filedialog import def action 获取输入框内容 date entry
  • 深度学习双显卡配置_更新深度学习装备:双(1080Ti)显卡装机实录

    前言 之前一直在装有一张1080Ti的服务器上跑代码 但是当数据量超过10W 图像数据集 的时候 训练时就稍微有点吃力了 速度慢是一方面 关键显存存在瓶颈 导致每次训练的batch size不敢调的过高 batch size与训练结果存在一
  • 播放器实战22 解决花屏与卡顿问题

    1 内存对齐 1 1什么是内存对齐 在C语言中 结构是一种复合数据类型 其构成元素既可以是基本数据类型 如int long float等 的变量 也可以是一些复合数据类型 如数组 结构 联合等 的数据单元 在结构中 编译器为结构的每个成员按
  • 小熊派笔记2

    GPIO案例 GPIO接口函数 初始化 wifiiot gpio h接口 wifiiot gpio ex h接口 扩展函数 设置GPIO拉力和驱动器强度 LED对应的gpio引脚是gpio2通过控制gpio2输出的电平信号来实现闪烁 设置
  • 三极管和场效应管-易错点

    NPN三极管是电流控制器件 共发射极电路中 放大区 Ice Ib x Vbe正偏 Vbc反偏 电势 Vc Vb Ve 饱和区 Ice Ib x 两个都正偏 电势 Vb Vc Ve Vce之间是饱和管压降Vces 截止区 Ice 0 两个都反
  • VerilogHDL实现除法操作

    硬件电路中实现除法操作一般基于两种方式 乘法操作和减法操作 基于减法的除法器 对于32位的无符号除法 被除数a除以除数b 他们的商和余数位数一定不超过32位 首先将a转换为32位 b也转换为32位 在每周期的开始 先将a左移一位 末尾补0
  • 如何知道你的Linux内核占用的内存大小?

    如何知道你的Linux内核占用的内存大小 1 代码段等 2 kernel heap 2 1 kmalloc 2 2 vmalloc 3 进程的页表 4 内核占用内存大小总和 1 代码段等 内核所需的代码段 bss段 内核栈等 dmesg g
  • 双调排序算法的实现(C++)

    双调排序算法的实现 C 双调排序 Bitonic Sort 是一种并行排序算法 它在某些情况下比传统的排序算法具有更好的性能 该算法的特点是可以通过并行比较和交换操作来对元素进行排序 适用于并行计算环境 本文将介绍双调排序算法的实现 并提供
  • subList截图数据集合,便于分页或分批次保存至数据库

    import java util ArrayList import java util List public class subList param list 待切割集合 param len 集合按照多大size来切割 param
  • ZNYQ初体验,持续记录中...

    首先在VIVADO中创建zynq PS核 步骤如下 如下新建工程 新建 block design add ip 选择zynq7 processing system 如下图所示 双击打开配置界面如下 下面我们简要地介绍一下页面导航面板中各个页
  • 解决页面禁止拷贝的问题

    众所周知 像这种禁止的动作一般都是网页中的javascript在作怪 那么 我们直接把他禁用就好了吖 其实 最简单的 在你前面网页的导航栏里面输入javascript void 就可以尽情的白嫖了
  • go语言 FindWindow

    Go 语言中的 FindWindow 函数是用于查找窗口句柄的函数 它是由 Windows API 提供的 在 Go 中通过 syscall 包来调用 用法示例 packagemain import fmt syscall unsafe v
  • 有趣的数据结构算法5——利用循环链表解决Josephus问题

    有趣的数据结构算法5 利用循环链表解决Josephus问题 题目复述 解题思路 实现代码 GITHUB下载连接 本次教程主要讲述如何利用循环链表解决Josephus问题 题目复述 据说著名犹太历史学家 Josephus有过以下的故事 在罗马
  • Selenium碰到的异常记录

    Java版本的Selenium异常记录 1 没有找到类的异常 NoClassDefFoundError 异常如下 Exception in thread main java lang NoClassDefFoundError com goo
  • 记录Android开发中SELINUX权限问题

    记录Android开发中SELINUX权限和用户权限问题 在安卓开发中 当linux内核中配置了SELINUX权限管理 访问硬件相关的设备文件 led tty等 时 如果没有对文件和访问文件的程序设置selinux的权限 就有可能报如下错误
  • 编写java程序151条建议读书笔记(20)

    建议139 大胆采用开源工具 MVC框架有Structs 也有Spring MVC WebWorker IoC容器有Spring 也有Coolgle Guice ORM有Hibernate MyBatis 日志有经典的log4j 崭新的lo
  • Java—RPC:远程过程调用(1)

    Java RPC 远程过程调用 1 在我们学习RPC的过程中 首先我们先认识一下项目结构在发展中干的变化 一 项目结构变化 1 单体结构 单体结构又叫单一项目 在我们所认识的传统项目基本上都是单一项目 j可是在互联网逐步发展的过程中 逐渐的
  • 卡通渲染技巧(三)——崩坏3卡通渲染实践

    系列链接 卡通渲染技巧 一 漫反射部分 卡通渲染技巧 二 高光部分 描边 卡通渲染技巧 三 崩坏3卡通渲染实践 耳听为虚眼见为实 不实际看一下你永远不知道技术分享里吹了多少牛 其实是没有实际应用到游戏里 前排赞美 SnapDragon Pr
  • 【手写一个RPC框架】simpleRPC-04

    目录 前言 实现 项目创建 配置依赖 common service server client 文件结构 运行 本项目所有代码可见 https github com weiyu zeng SimpleRPC 前言 之前谈到 网络传输使用BI
  • 音频——WAV 格式详解

    文章目录 WAV 文件格式解析 概述 块解析 RIFF chunk fmt chunk data chunk 示例分析 代码解析 WAV 文件格式解析 概述 wav 文件支持多种不同的比特率 采样率 多声道音频 WAV 文件格式是 Micr