第八篇 WAV文件格式

news2024/11/14 16:23:59

WAVE PCM soundfile format        

        WAV即WAVE,WAVE文件是计算机领域最常用的数字化声音文件格式之一,它是微软专门为Windows系统定义的波形文件格式(Waveform Audio),其扩展名为"*.wav"。

        最基本的WAVE文件是PCM(脉冲编码调制)格式的,这种文件直接存储采样的声音数据没有经过任何的压缩,是声卡直接支持的数据格式,要让声卡正确播放其它被压缩的声音数据,就应该先把压缩的数据解压缩成PCM格式,然后再让声卡来播放。

        WAVE文件还有很多种有损压缩格式,比如IMA/DVI ADPCM,Microsoft ADPCM,AAC编码等。被压缩的声音数据,需要先解压成PCM格式,才能用声卡来播放

        如果是PCM,则为无损格式,文件会比较大,并且大小相对固定,可以使用以下公式计算文件大小。

FileSize = HeadSize + TimeInSecond * SampleRate * Channels * BitsPerSample / 8

其中:

  • HeadSize为WAV文件头部长度;
  • SampleRate,即采样率,可选8000、16000、32000、44100或48000;
  • Channels表示声道数量,通常为1或2;
  • BitsPerSample代表单个Sample的位深,可选8、16以及32,其中32位时可以是float类型。

关于RIFF

        RIFF,全称Resource Interchange File Format,是一种按照标记区块存储数据的通用文件存储格式,多用于存储音频、视频等多媒体数据。Microsoft在Windows下的WAV、AVI等都是基于RIFF实现的。一个标准的RIFF规范规范文件,最小存储单位为“块”(Chunk),每个块(Chunk)包含以下三个信息:

         只有ID为"RIFF"或者"LIST"的块允许拥有子块(SubChunk)。RIFF文件的第一个块的ID必须是"RIFF",也就是说ID为"LIST"的块只能是子块(SubChunk),他们和各个子块形成了复杂的RIFF文件结构。

  RIFF数据域的的起始位置四个字节为类型码(Form Type),用于说明数据域的格式,比如WAV文件的类型码为"WAVE"。

  "LIST"块的数据域的起始位置也有一个四字节类型码(List Type),用于说明LIST数据域的数据内容。比如,类型码为"INFO"时,其数据域可能包括"ICOP"、"ICRD"块,用于记录文件版权和创建时间信息。

WAV文件格式

        WAV格式遵循RIFF规范(Resource Interchange File Format 资源交互文件格式) 。RIFF文件结构可以看作是树状结构,其基本构成是称为“块”(Chunk)的单元,最顶端是一个“RIFF”块,下面的每个块有“类型块标识(可选)”,"标志符“,”数据大小“及”数据“等项所组成,块的结构如表1所示:

        上面说到的 “类型块标识”只在部分bhunk中用到,如”WAVE"chunk中,这时表示下面嵌套有别的chunk,当使用了 “类型块标识”时,该chunk就没有别的项(如"标志符“,”数据大小“等),它只作为文件读取时的一个标识,先找到这个 “类型块标识”,再以它为起读取它下面嵌套的其他chunk。

非PCM格式的文件会至少多加入一个“fact”块,它用来记录数据解压缩后的大小。(注意是数据而不是文件)这个“fact”块一般加在“data”块的前面。

每个WAV文件由文件头和数据体两大部分组成,数据体的记录方式是小端(little-endian), 以最简单的无损WAV格式文件为例,此时文件的音频数据体为PCM,比较简单,重点在于WAV的文件头。

1  WAV文件头结构 

        WAV文件是非常简单的一种RIFF文件,它的文件头包含三部分,RIFF,fmt,fact(fact是非必需有的) 


typedef __packed struct
{
        ChunkRIFF riff;        //riff块
        ChunkFMT fmt;          //fmt块
        ChunkFACT fact;        //fact块 在线性PCM,没有这个结构体         
        ChunkDATA data;        //data块                 
}__WaveHeader;
 1.1. “RIFF”Chunk的内部组织

        RIFF块的格式类型(Format)为“WAVE ”。RIFF块包含两个子块(Subchunk),这两个子块的ID分别是“fmt ”和“data ” 。其中“fmt ”子块由结构PCMWAVEFORMAT所组成,其子块的大小就是sizeof(PCMWAVEFORMAT),数据组成就是PCMWAVEFORMAT结构中的数据。      

typedef __packed struct
{
    u32 ChunkID;                  //chunk id;这里固定为"RIFF",即0X46464952
    u32 ChunkSize ;               //集合大小;文件总大小-8
    u32 Format;                   //格式;WAVE,即0X45564157
}ChunkRIFF ;
 1.2. “FMT”Chunk的内部组织
typedef __packed struct
{
    u32 ChunkID;            //chunk id;这里固定为"fmt ",即0X20746D66
    u32 ChunkSize ;         //子集合大小(不包括ID和Size);这里为:20.
    u16 AudioFormat;        //音频格式;0X01,表示线性PCM;0X11表示IMA ADPCM
    u16 NumOfChannels;  //通道数量;1,表示单声道;2,表示双声道;
    u32 SampleRate;     //采样率;0X1F40,表示8Khz
    u32 ByteRate;       //字节速率;
    u16 BlockAlign;     //块对齐(字节);
    u16 BitsPerSample;  //单个采样数据大小;4位ADPCM,设置为4
    u16 ByteExtraData;   //附加的数据字节;2个; 线性PCM,没有这个参数
    u16 sampleperblock;  //一般是一个数据块中的采样数量 如:0x01F9
}ChunkFMT;  

ByteExtraData这个数据重点说一下,在原始线性PCM编码格式,不需要这个参数,只有压缩的PCM编码格式,一般都是按块存储的,需要知道一个块内的采样数量。 

在IMA-ADPCM编码格式下的ByteExtraData和sampleperblock, 在data Chunk前面4个字节。

sampleperblock = 0x1F9,表示一个block中有 505个采样点。

1.3. “Fact”Chunk的内部组织
typedef __packed struct
{
    u32 ChunkID;                 //chunk id;这里固定为"fact",即0X74636166;
    u32 ChunkSize ;              //子集合大小(不包括ID和Size);这里为:4.
    u32 NumOfSamples;            //采样的数量;
}ChunkFACT;

”All (compressed) non-PCM formats must have a Fact chunk (Rev. 3documentation). The chunk contains at least one value, the number of samples in the file.”
虽然标准协议要求”所有非PCM编码的WAV文件要求有fact块,它用来记录数据解压缩后的大小。(注意是数据而不是文件)“

NumOfSamples是这个chunk中最重要的数据,如果这是某种压缩格式的声音文件,那么长这里可以知道它解压缩后的大小,对于解压时的计算会有很大的好处!

这个“fact”块一般加在“data”块的前面。但实际分析了两个wav文件的文件头,一种PCM编码,一种IMA-ADPCM编码,这两个文件都没有fact块,但是window可以正常播放;说明fact块对这两种类型的wav文件也不是必须的 

        例如:一个典型的WAV文件头部长度是44字节,包含了采样率,通道数,位深等信息。

请添加图片描述

  

channel_1.wav的WAV文件头部

实际数据长度:0x43D4  

数据开始地址是0x2c 

数据结束地址是0x43FF

 43FF-2C +1 = 43D4

As an example, here are the opening 72 bytes of a WAVE file with bytes shown as hexadecimal numbers:

52 49 46 46 24 08 00 00 57 41 56 45 66 6d 74 20 10 00 00 00 01 00 02 00 
22 56 00 00 88 58 01 00 04 00 10 00 64 61 74 61 00 08 00 00 00 00 00 00 
24 17 1e f3 3c 13 3c 14 16 f9 18 f9 34 e7 23 a6 3c f2 24 f2 11 ce 1a 0d 

//波形文件的文件头,占用44Byte,文件头后为波形数据区 

struct FORMAT_WAV
{
    long ChunkID;               //“RIFF”
    long ChunkSize;           //chunk(大块)的数量,wav文件的总大小,单位字节
    long Format;                  //“WAVE”
    long Subchunk1ID;      //“fmt”  第一个chunk的ID
    long Subchunk1Size;   //第一个chunk的Size
    short AudioFormat;       //音频格式
    short NumChannels;   //声道的数量
    long SampleRate;         //采样率
    long ByteRate;               //比特率
    short BlockAlign;           //块对齐
    short BitsPerSample;  //每个采样点的位宽
    long Subchunk2ID;      //"data" 第二个chunk的ID
    long Subchunk2Size;   //第二个chunk的Size,波形数据的大小,单位为字节
}

2. WAV音频数据体(data chuck)结构
2.1.  "data"Chunk的内部组织
typedef __packed struct
{
    u32 ChunkID;                   //chunk id;这里固定为"data",即0X64617461
    u32 ChunkSize ;                // 音频数据块大小。(除去WAV头的所有数据)
}ChunkDATA;

        “data”chunk的前8个字节存储的是标志符“data”和后接数据大小size(DWORD)

        从“data”chunk的第9个字节开始,存储的就是声音信息的数据了,这些数据可能是压缩的,也可能是没有压缩的。 

        PCM的音频数据是原始数据没有被压缩,因此PCM格式的音频数据是以原始的音频流数据顺序存储下去的。如图所示:(它的基本组织单位是BYTE(8bit)或WORD(16bit)

2.2.   数据块BLOCK结构

        在IMA-ADPCM中,“data”chuck中的数据是以block形式来组织的,我把它叫做“段”,也就是说在进行压缩时,并不是依次把所有的数据进行压缩保存,而是分段进行的,这样有一个十分重要的好处:那就是在只需要文件中的某一段信息时,可以在解压缩时可以只解所需数据所在的段就行了,没有必要再从文件开始起一个一个地解压缩。这对于处理大文件将有相当的优势。同时,这样也可以保证声音效果。

        Block一般是由block header (block头) 和 data 两者组成的。Block在单声道下的定义如下:

//ADPCM压缩的数据块结构
typedef __packed struct
{
    u16 presample;               //block中第一个采样值(未压缩)
    u8 index ;                   //上一个数据块的最后一个 index,第一个block的index=0
    u8 rsv;                      //保留
    u8 dat[sampleperblock-1];    //数据
}DATA_BLOCK;

        为了数据存储对齐,方便处理,一般一个音频BLOCK的大小是16的整数倍;如果设置BLOCK大小为256Byte,减去数据块头长度4字节,还剩252字节,4bit表示一个采样的话,可存储共252x2+1=505个采样点(加上数据头里的一个采样值)。
        对于PCM编码的WAV文件,只需要按照顺序存储原始采样值即可,不需要分块。 

WAV扩展 

        有一些WAV的头部并不仅仅只有44个字节,比如通过FFmpge编码而来的WAV文件头部信息通常大于44个字节。这是因为根据WAV规范,其头部还支持携带附加信息,所以只按照44个字节的长度去解析WAV头部信息是不一定正确的,还需要考虑附加信息。那么如何知道一个WAV文件头部是否包含附加信息呢?

  根据"fmt "子块长度来判断即可。

如果fmt SubChunk Size等于0x10(16),表示头部不包含附加信息,即WAV头部信息长度为44;如果等于0x12(18),则包含附加信息,此时头部信息长度大于44。

  当WAV头部包含附加信息时,fmt SubChunk Size长度为18,并且紧随是另一个子块,这个包含了一些自定义的附加信息,接着往下才是"data"子块,格式如下:

        如果一个无损WAV文件头部包含了附加信息,那么PCM音频所在的位置就不确定了,但由于附加信息也是一个子块(SubChunk),根据RIFF规范,该子块也必然记录着其长度信息,所以我们还是有办法能够动态计算出其位置,下面是计算步骤:

  1. 判断fmt块长度是否为18。
  2. 如果fmt长度为18,那么必然从0x26位置开始为附加信息块,0x30-0x33位置记录着该子块长度。
  3. 根据步骤2获取的子块长度,假定为N(16进制),那么PCM音频信息开始位置为:0x34 + N + 8。 

读取WAV文件的方法

        在知道了WAV文件的内部数据组织后,可以直接通过FILE或HFILE来实现文件的读取,但由于WAV文件是以RIFF格式来组织的,所以用多媒体输入输出流来操作将更加方便,可以直接在文件中查找chunk并定位数据。

PCM和IMA-ADPCM编码的WAV实际例子 

 用GoldWave.exe 从pcm格式导出ima adpcm格式的wav头比较

wav文件头的大小: 

        PCM格式的wav,它文件头是44byte

        ADPCM格式的wav,它的文件头是48byte, 多了4byte的附加数据

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

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

相关文章

【Linux】进程概念的铺垫

进程概念的铺垫 先谈硬件——冯诺依曼体系结构冯诺依曼体系结构的5大部件输入输出设备存储器中央处理器(CPU) 冯诺依曼体系结构示意图 再谈软件——操作系统操作系统的 目的 和 定位如何理解 "管理"总结系统调用 - system call 先谈硬件——冯…

LINUX原始机安装JDK

文章目录 下载 JDK压缩包创建jdk文件夹sftp 远程上传解压缩 tar -zxvf 包名配置环境变量刷新 环境变量验证是否安装成功安装JAVA 依赖yum更新及替换镜像curl 命令下载更新更新yum依赖判断repo文件是否存在生成缓存、启用阿里云镜像 重新下载java依赖再次验证hello world 下载 J…

Java | Leetcode Java题解之第341题扁平化嵌套列表迭代器

题目&#xff1a; 题解&#xff1a; public class NestedIterator implements Iterator<Integer> {private List<Integer> vals;private Iterator<Integer> cur;public NestedIterator(List<NestedInteger> nestedList) {vals new ArrayList<Inte…

用数据分析找到神奇公式-《股市稳赚》读后感

格林布拉特的这本书介绍了一种简单的方法&#xff0c;核心是用神奇公式寻找到高资本收益率和高股票收益率的最佳投资组合。作者对其背后原理的普遍性充满自信&#xff0c;神奇公式寻找经营良好且价格低于平均水平的公司&#xff0c;买进质优价低的公司&#xff0c;这个公式&…

C++ 设计模式——简单工厂模式

简单工厂模式 简单工厂模式主要组成部分代码实现简单工厂模式模式的 UML 图UML图解析类与方法优点和缺点适用场景 简单工厂模式 简单工厂模式是一种创建型设计模式&#xff0c;通过一个工厂类来负责对象的实例。这种模式将对象创建的细节封装在工厂类中&#xff0c;客户端无需…

掌握JavaScript中的Call和Apply,让你的代码更强大、更灵活

在学习JavaScript时&#xff0c;你可能会遇到call和apply这两个方法。它们的作用其实很相似&#xff0c;都是用来调用函数并设置函数内部的this值&#xff0c;但它们的使用方式稍有不同。 想象一下&#xff0c;你和朋友们一起拍照。call就像是你一一叫朋友们的名字&#xff0c;…

基于WEB的旅游推荐系统设计与实现

TOC springboot280基于WEB的旅游推荐系统设计与实现 第1章 绪论 1.1选题动因 当前的网络技术&#xff0c;软件技术等都具备成熟的理论基础&#xff0c;市场上也出现各种技术开发的软件&#xff0c;这些软件都被用于各个领域&#xff0c;包括生活和工作的领域。随着电脑和笔…

Linux tail -f 报错 No space left on device

问题&#xff1a; 执行tail -f my_file 时报错&#xff1a;No space left on device df -h 检查磁盘剩余空间&#xff0c;剩余空间都很充足&#xff1b; df -i 检测iNode使用情况&#xff0c;剩余iNode也很充足&#xff1b; 参考这篇文章解决了问题 tail: cannot watch /v…

黑马前端——days05_css

页面框架文件 <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><meta http-equiv"X-UA-Compatible"…

书籍推荐:凤凰架构

书籍推荐 本文的视频版&#xff1a;https://www.bilibili.com/video/BV1XQeueZEp4 周志明写的一本关于 Java 架构的书籍&#xff0c;内容都是关于 Java 架构中相关的技术&#xff0c;讲的知识比较广&#xff0c;大多都是概念相关的东西&#xff0c;适合有一定 Java 开发经验的…

从零开始搭建 LVS 高性能集群 (DR模式)

从零开始搭建 LVS 高性能集群 &#xff08;DR模式&#xff09; 架构 本设计方案采用三台服务器构建集群&#xff0c;使用Linux Virtual Server (LVS) 作为负载均衡器&#xff0c;运行在直接路由 (DR) 模式下。集群中的每一台服务器都将运行相同的服务&#xff0c;以实现 高可用…

搭建网站与企业 ERP 融合,微信开放新机遇

#HTTP虚拟专线# VHTTP 用户都将分配一个专属的固定公网 IP 地址&#xff0c;不再受到动态 IP &#xff0c;您的网站、企业 ERP 系统等关键业务能够始终保持在线。 每位VHTTP用户分配一个专属的固定公网IP地址&#xff0c;支持 80/443访问。用户只需自行简单设置自己 域名 DNS 解…

【leetcode】删除链表的倒数第 N 个结点-25-5

方法&#xff1a;递归 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* ListNode(int x) : val(x), next(nullptr) {}* ListNode(int x, ListNode *next) : val(x), ne…

企业级无线局域网(WLAN)架构:高效部署策略与技术指南

前言&#xff1a;无线网络直接影响整体网络性能&#xff0c;在当今企业网环境中&#xff0c;已有超过一半的数据流量通过无线信道传输&#xff0c;随着物联网技术的普及&#xff0c;无线网将承载更多的关键业务流量。企业/园区场景的无线网络值得考虑的关键因素有很多&#xff…

从商务到旅行,2024年不可或缺的翻译工具全攻略

随着全球化的不断深入&#xff0c;语言障碍已不再是横亘在人们交流之间的不可逾越之墙。随着科技的发展现在有不少类似百度翻译在线翻译这样的工具在造福我们。这次我们就来探讨一下大家都在用的翻译工具有哪些吧。 1.福晰在线翻译 链接直达>>https://fanyi.pdf365.cn/…

abc 290 d Marking (一个环上每隔d个给未标记的打标记,问第几个标记是什么)

题目&#xff1a; https://atcoder.jp/contests/abc290/tasks/abc290_d 题解&#xff1a; 代码&#xff1a; // Problem: D - Marking // Contest: AtCoder - Toyota Programming Contest 2023 Spring Qual B&#xff08;AtCoder Beginner Contest 290&#xff09; // URL: ht…

C++速览之智能指针

1、存在的问题 c 把内存的控制权对程序员开放&#xff0c;让程序显式的控制内存&#xff0c;这样能够快速的定位到占用的内存&#xff0c;完成释放的工作。但是此举经常会引发一些问题&#xff0c;比如忘记释放内存。由于内存没有得到及时的回收、重复利用&#xff0c;所以在一…

数字孪生智慧医院建设方案

2. 智慧医院背景与挑战 公立医院面临医疗保障能力、服务需求和DIP付费制度改革等变化&#xff0c;同时存在智慧医院建设中的“建多用不多”和系统间数据不互通等问题。 3. 解决方案与标准 通过“找方案”和“找标准”微信公众号&#xff0c;分享智慧城市、智慧医院等领域的解…

Flink程序部署与提交

前言 我们看门见山&#xff0c;生产环境一般用的是在YARN上面采用应用模式进行部署flink程序。实际生产中一般需要和资源管理平台&#xff08;如YARN&#xff09;结合起来&#xff0c;选择特定的模式来分配资源、部署应用。 部署模式 在一些应用场景中&#xff0c;对于集群资…

AIGC:clip-interrogator

文字生成图片是近年来多模态和大模型研究的热门方向&#xff0c;openai提出的CLIP提供了一个方法建立起了图片和文字的联系&#xff0c;但是只能做到给定一张图片选择给定文本语义最相近的那一个&#xff0c;实际项目开发中我们总是需要从一张图片获取描述&#xff0c;clip-int…