音视频入门基础:WAV专题(2)——WAV格式简介

注:本文有部分内容引用了维基百科:https://zh.wikipedia.org/wiki/WAV

一、引言

Waveform Audio File Format(缩写WAVE或WAV)是微软与IBM公司所开发在个人电脑存储音频流的编码格式,在Windows平台的应用软件受到广泛的支持。此格式属于资源交换文件格式(RIFF)的应用之一(关于RIFF格式可以上微软的官网了解:https://learn.microsoft.com/zh-cn/windows/win32/xaudio2/resource-interchange-file-format--riff-)。WAV音频文件通常会将采用脉冲编码调制(音频压缩编码格式为PCM)的音频存储在区块中。由于此音频格式未经过压缩,所以在音质方面不会出现失真的情况,但文件的体积较大。

WAV格式的官方文档可以从https://www.mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html 下载:

二、WAV文件的Header

WAV文件遵守RIFF格式的规则,文件最前面的数据为文件头(header),使音频播放器能够简单掌握文件的基本信息。注意:文件头不一定为固定的44字节,由是否存在扩展块和是否存在可选区块决定。文件头中的内容以区块(chunk)为最小单位(注意:是子区块包含区块),每一区块长度为4字节,而区块之上则由子区块包裹,每一子区块长度不受限制,但须在前头先声明标签及长度(单位为字节)。文件头的前3个区块记录文件格式及长度;接着第一个子区块(标签为"fmt",被称为“Format chunk”)包含多个区块,记录声道数量、采样率等信息;另外一个子区块(标签为"data",被称为“Data chunk”)才包含真正的音频资料(或者说音频数据),长度则视音频长度而定。内容如下表所示。须注意的是,每个区块的字节序不尽相同(也就是说有些区块按照大端字节序存贮,有些按照小端字节序存贮),而音频数据本身则是采用小端字节序:

注:

1.上图描述有误,“位元(组)率”实际应为“=声道数 * 采样频率 * 量化位数 / 8”(图片中漏乘了声道数量),它代表每秒钟数据的字节数。

2.上图仅仅列出了WAV文件Header中必须包含的区块。Format chunk和Data chunk是必须的。但实际Header中还可能存在可选区块,Format chunk中也可能包含扩展块。

3.上图中“总区块大小”的描述有误,其区块内容不一定为“N+36”。

三、WAV文件实例分析

用notepad++打开《音视频入门基础:WAV专题(1)——使用FFmpeg命令生成WAV音频文件》中生成的WAV文件,下图红框中的数据为文件头(header):

(一)WAV Header中的前3个区块

第0到3字节为WAV Header中的第一个区块:“区块编号”,内容为“RIFF”或“RIFX”。“RIFF”指定该文件中真正的音频数据按照小端字节序存贮,“RIFX”指定该文件中真正的音频数据按照大端字节序存贮。下图中的值为“RIFF”,表示该文件遵守RIFF格式的规则,音频数据按照小端字节序存贮:

第4到7字节为WAV Header中的第二个区块:“总区块大小”,值等于:该WAV文件所有区块减去“”区块编号和“总区块大小”所占的字节数,即“该WAV文件所有区块所占空间 - 8”,单位为字节。由于该区块是小端字节序,所以下图中的总区块大小为0x03184846,也就是十进制的51923014,表示总区块大小为51923014字节:

第8到11字节为WAV Header中的第三个区块:“档案格式”,内容固定为“WAVE”,表示该文件为WAVE / WAV格式的文件:

(二)WAV Header中的子区块:Format chunk

第12到15字节为“子区块1标签”,内容固定为“fmt ”,表示这是第一个子区块的开头,该子区块被称为Format chunk,记录声道数量、采样率等信息:

第16到19字节为“子区块1大小”,单位为字节。如果子区块1中无扩展块,值为16,子区块1中有扩展块,值为“16 + 2字节扩展块长度 + 扩展块内容”。当WAV文件中的音频数据使用的不是PCM压缩编码格式时,Format chunk中就会有扩展块。下图中的值为0x10(小端字节序),也就是十进制的16(字节),所以该wav文件的Format chunk中无扩展块:

“子区块1大小”就是下图红框中的区块加起来的总大小。也就是子区块1中,去掉“子区块1标签”和“子区块1大小”这两个区块后的总大小:

第20到21字节为“音讯格式”,也就是所谓的音频压缩编码格式,值一般为0x01(小端字节序),表示音频压缩编码格式为PCM:

第22到23字节为声道数量。值为1是单声道,为2则是双声道(立体声)。下图中的值为0x02(小端字节序),表示音频为双声道:

第24到27字节为“取样频率”,也就是音频的采样频率,单位为Hz。下图中的值为0xAC44(小端字节序),换算成10进制就是44100,表示音频的采样频率为44100Hz:

第28到31字节为“位元(组)率”,即音频的码率(单位为byte per second,音频每秒播放的字节数)。其值为:声道数 * 采样频率 * 采样位数 / 8。下图中的值为0x02B110(小端字节序),也就是10进制的176400,表示音频的码率为176400Byte/s。可以计算出来:176400 = 2 * 44100 * 16 / 8,符合上述计算公式:

第32到33字节为“区块对齐”,即每个采样点所需的字节数,其值为:声道数 * 采样位数 / 8。下图中的值为0x04(小端字节序),换算成10进制就是4。 4 = 2 * 16 / 8,符合上述计算公式:

第34到35字节为“位元深度”,即采样位数。下图中的值为0x10(小端字节序),换算成10进制就是16,表示音频的采样位数是16位:

由于我们上述读取到的“子区块1大小”为16字节。所以至此“子区块1”读取结束。后面的内容为另外一个子区块的。

(三)WAV Header中的可选区块

WAV文件的Header中还可能包含一些可选的区块,如:Fact chunk、Cue points chunk、Playlist chunk、Associated data list chunk等。这些区块在WAV文件中不是必须的,WAV文件的Header中可能存在、也可能不存在这些区块。list chunk是其中一种可选区块,包含关于版权、作者、文件工程师和其他类似文本的信息。由于我们演示的这个WAV文件是通过FFmpeg生成的,FFmpeg在它的Header中加入了list chunk。

从该WAV文件的第36到39字节中读取到了“子区块1”后面区块的标签:“LIST”,表示这是list chunk的开头:

WAV文件遵守RIFF格式的规则,根据微软官方对RIFF格式的描述:https://learn.microsoft.com/zh-cn/windows/win32/xaudio2/resource-interchange-file-format--riff-

我们可以知道chunkSize(区块大小)是一个4字节值。所以下图红框中,也就是该WAV文件的第40到43字节为list chunk这个子区块的大小,下图中的值为0x1A(小端字节序),换算成10进制就是26,表示子区块list chunk中,去掉“list chunk标签”和“list chunk大小”这两个区块后的总大小是26字节:

所以据此,我们可以推断出list chunk中去掉“list chunk标签”和“list chunk大小”后的有效数据的范围是从该WAV文件的第44字节到第69字节:

至此该子区块(list chunk)读取结束。后面的内容为另外一个子区块的。

(四)WAV Header中的子区块:Data chunk

从该WAV文件的第70到73字节中读取到了“子区块list chunk”后面区块的标签:“data”,表示这是Data chunk的开头,该子区块存放真正的音频数据:

第74到77字节为“子区块Data chunk的大小”,单位为字节。下图中的值为0x03184800(小端字节序),换算成十进制是51922944,表示该WAV文件中存放的真正的音频数据的大小为51922944字节。由上述第20到21字节中的“音讯格式”,我们可以知道该WAV文件中音频的压缩编码格式为PCM。所以该WAV文件中PCM音频数据的大小为51922944字节。该值 = (采样频率*采样位数*声道)*时间 / 8(单位:字节数)。我们把实际数据带入该公式:51922944  ≈ (44100 * 16 * 2) * 294 / 8,可以看到是符合该公式的。“约等于”是因为该WAV文件的总时长不是正好的294秒:

“子区块Data chunk的大小”占用的空间为4字节,所以WAV存放真正的音频数据的极限是2 ^ 32 = 4294967296字节,也就是说它最多只能容纳4GB的音频流信息,这是Wave作为32bit时代产物的局限性。要想存贮超过4G的音频数据,可以使用WAVE 64位扩展格式Wave64。

从第78字节开始存放的就是真正的音频数据了。从上面我们可以知道该WAV文件的Header为从第0到第77字节,也就是说它的Header占78字节。我们在Windows系统中通过“属性”查看该WAV文件的大小,可以看到是51923022 字节。51923022 =  51922944(PCM音频数据的大小) + 78(Header的大小)。可以看出来,数值是完全符合的:

四、删除WAV文件头测试

由上面我们可以知道该WAV文件的Header为从第0到第77字节,所以删掉该文件头以后WAV文件能不能播放呢?我们做一个测试:

备份WAV文件。在notepad++中删掉该WAV文件的Header:


 

然后发现用vlc打开没反应。也就是说无法打开了:

从上面我们可以知道该WAV文件的第20到21字节值为0x01,表示音频压缩编码格式为PCM。所以去掉WAV Header后,剩下的数据就是PCM音频数据。我们可以按照《音视频入门基础:PCM专题(3)——使用Audacity工具分析PCM音频文件》,通过导入PCM音频文件的方法,来打开去掉Header后的WAV文件:

果然可以成功打开播放:

五、Wave64格式

想存贮超过4G的音频数据,可以使用WAVE 64位扩展格式Wave64,具体可以参考:《什么是Wave64和RF64?》、《F64 | MBWF | WAV 64-bit | Learn, Get Free Samples Now》

六、总结

1.WAV的文件头不一定为固定的44字节,由是否存在扩展块和是否存在可选区块决定。所以如果不通过第三方库,自己写解析WAV文件的程序时要注意这一点。

2.WAV文件头包含了音频采样频率、采样位数、声道数信息。而WAV文件遵守RIFF格式的规则,RIFF规定了符号和字节序。所以播放器可以根据这些信息播放WAV文件。同样的,即使去掉了WAV Header,只要我们播放时指定了这五个参数(这五个参数可以参考:《音视频入门基础:PCM专题(1)——使用FFmpeg命令生成PCM音频文件并播放》),而我们又同时知道了它的音频压缩编码方式,我们就能播放该音频文件。

3.WAV是文件格式。PCM是音频压缩编码格式。WAV文件中的音频压缩编码格式一般是PCM,但也可以是其它。

4.WAV文件只能容纳4GB的音频流信息,要想存贮超过4G的音频数据,可以使用WAVE 64位扩展格式Wave64。

七、参考

维基百科:http://​https://zh.wikipedia.org/wiki/WAV​

https://docs.fileformat.com/audio/wav/

《WAV文件格式详解》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/50678.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AI/机器学习(计算机视觉/NLP)方向面试复习3

1. Pooling 有哪些方式?pytorch的实现? Pooling可以分成:最大池化,平均池化,全局平均池化,随机池化,空间金字塔池化。 1. 最大池化(Max Pooling) 最大池化是最常用的池…

union的特性和大小端

一、union在c和c语言中的特性 1.共享内存空间:union的所有成员共享同一块内存空间。意味着在同一时刻,union 只能存储其成员 中的一个值。当你修改了union中的一个成员,那么其它成员的值也会被改变,因为它们实际上都是指向同一块…

JS逆向高级爬虫

JS逆向高级爬虫 JS逆向的目的是通过运行本地JS的文件或者代码,以实现脱离他的网站和浏览器,并且还能拿到和浏览器加密一样的效果。 10.1、编码算法 【1】摘要算法:一切从MD5开始 MD5是一个非常常见的摘要(hash)逻辑. 其特点就是小巧. 速度快. 极难被破解. 所以,…

skywalking docker部署

skywalking-oap # 拉取skywalking-oap镜像 docker pull apache/skywalking-oap-server:9.7.0# 启动容器 docker run --name oap \ -d \ -p 11800:11800 \ -p 12800:12800 \ apache/skywalking-oap-server:9.7.0skywalking-ui # 摘取skywalking-ui镜像 docker pull apache/sky…

大屏使用技巧——如何实现数据分发

当多个组件需共用同一数据源时,为了减少重复请求,需要进行数据分发。那如何实现接一次数据就能让多个组件映射同一数据源中的不同数据字段呢? 实现思路 目标组件的静态数据中添加标记字段,数据过滤器内通过 data 参数获取到对应…

加密micropython写的程序为.mpy的方法

2024年7月26日 用虚拟机安装一个Linux,本例为CentOS7的Linux系统。 1.保证Linux能够连接网络。 2.进入root用户,使用下面的命令行安装gcc编译器: yum install gcc 3.安装完成后,查看gcc是否安装成功,用下面的命令…

家政项目小程序的设计

管理员账户功能包括:系统首页,个人中心,用户管理,家政人员管理,家政服务管理,咨询信息管理,咨询服务管理,家政预约管理,留言板管理,系统管理 微信端账号功能…

前端开发知识-vue

大括号里边放键值对,即是一个对象。 一、vue可以简化前端javascript的操作。 主要特点是可以实现视图、数据的双向绑定。 使用vue主要分为三个步骤: 1.javascript中引入vue.js 可以src中可以是vue的网址,也可以是本地下载。 2.在javasc…

FastAPI(七十九)实战开发《在线课程学习系统》接口开发-- 加入课程和退出课程

源码见:"fastapi_study_road-learning_system_online_courses: fastapi框架实战之--在线课程学习系统" 加入课程 我们先看下加入课程 1.是否登录 2.课程是否存在 3.是否已经存在 4.添加 首先实现逻辑 def get_student_course(db: Session, course: int…

C++——QT:保姆级教程,从下载到安装到用QT写出第一个程序

登录官网,在官网选择合适的qt版本进行下载 这里选择5.12.9版本 点击exe文件下载,因为服务器在国外,国内不支持,所以可以从我的网盘下载 链接: https://pan.baidu.com/s/1XMILFS1uHTenH3mH_VlPLw 提取码: 1567 --来自百度网盘超级…

本地部署Graphhopper路径规划服务(graphhopper.sh启动版)

文章目录 文章参考源码获取一、配置Java环境变量二、配置Maven环境变量三、构建graphhopper步骤1. 下载数据2. 配置graphhopper配置文件config-example.yml3. 在项目中启动命令行执行./graphhopper.sh build3.1|、遇到的问题3.1.1、pom.xml中front-maven-plugin-无法下载npm6.1…

土耳其云手机提升TikTok电商效率

在数字化飞速发展的今天,TikTok不仅是一个社交平台,更是一个巨大的电商市场。随着TikTok电商功能在全球范围内的扩展,土耳其的商家和内容创作者正面临着前所未有的机遇。本文将详细介绍土耳其云手机怎样帮助商家抓住机遇,实现业务…

项目都做完了,领导要求国际化????--JAVA后端篇

springboot项目国际化相信各位小伙伴都会,很简单,但是怎么项目都做完了,领导却要求国际化文件就很头疼了 国际化的SpringBoot代码: 第一步:创建工具类 /*** 获取i18n资源文件** author bims*/ public class Message…

MATLAB怎么实现多条曲线共用一个图例

y1 sin(x); y2 3*sin(x); y3 cos(x); y4 3*cos(x); 例子:如果我们有4条曲线,分布是cos类和sin类,我们的图例就想区分是cos类还是sin类。 第一步(关键步骤) 我们要把我们所需要的类别曲线先画一遍, …

《Programming from the Ground Up》阅读笔记:p75-p87

《Programming from the Ground Up》学习第4天,p75-p87总结,总计13页。 一、技术总结 1.persistent data p75, Data which is stored in files is called persistent data, because it persists in files that remain on disk even when the program …

用 apifox cli 命令行运行本地接口出现TypeError:Invalid IP address: undefined

用 apifox cli 命令行运行本地接口出现TypeError:Invalid IP address: undefined,客户端运行是通过的但命令行运行会报错 修改端口也是一样报错,地址修改为127.0.0.1会报错connect ECONNREFUSED 127.0.0.1:8080 解决方法:不用localhost&…

智能合约在能源行业中的应用:促进可再生能源的发展与利用

随着全球能源需求的增长和环境保护意识的提升,可再生能源作为替代传统能源的重要选择,正逐步成为能源供应的主流。本文将探讨智能合约在能源行业中的应用,特别是如何通过智能合约促进可再生能源的发展与利用。 可再生能源的重要性与挑战 可再…

react中使用forwardRef获取子组件中的节点以及子组件的方法(useImperativeHandle的使用)

1.forwardRef的使用 import { forwardRef, useRef } from "react"const Son forwardRef((props, ref)> {return (<input type"text" ref{ref} id"kannoId"/>) })function ForwardRef() {const sonRef useRef(null)const showRef ()…

Javascript前端面试基础5【每日更10】

let与var的区别 let命令不存在变量提升&#xff0c;如果在let前使用&#xff0c;会导致报错&#xff08;var存在变量提升&#xff09;如果块区中存在let和const命令&#xff0c;就会形成封闭作用域不允许重复声明&#xff0c;因此&#xff0c;不能在函数内部重新声明参数 m…

基于Qt的视频剪辑

在Qt中进行视频剪辑可以通过多种方式实现&#xff0c;但通常需要使用一些额外的库来处理视频数据。以下是一些常见的方法和步骤&#xff1a; 使用FFmpeg FFmpeg是一个非常强大的多媒体框架&#xff0c;可以用来处理视频和音频数据。你可以使用FFmpeg的命令行工具或者其库来实现…