耳机俱乐部论坛

标题: wav文件格式分析详解 [打印本页]

作者: yao_qin    时间: 2009-8-27 20:58
标题: wav文件格式分析详解
一、综述
    WAVE文件作为多媒体中使用的声波文件格式之一,它是以RIFF格式为标准的。
RIFF是英文Resource Interchange File Format的缩写,每个WAVE文件的头四个
字节便是“RIFF”。
    WAVE文件是由若干个Chunk组成的。按照在文件中的出现位置包括:RIFF WAVE
Chunk, Format Chunk, Fact Chunk(可选), Data Chunk。具体见下图:
------------------------------------------------
|             RIFF WAVE Chunk                  |
|             ID  = 'RIFF'                     |
|             RiffType = 'WAVE'                |
------------------------------------------------
|             Format Chunk                     |
|             ID = 'fmt '                      |
------------------------------------------------
|             Fact Chunk(optional)             |
|             ID = 'fact'                      |
------------------------------------------------
|             Data Chunk                       |
|             ID = 'data'                      |
------------------------------------------------
            图1   Wav格式包含Chunk示例
    其中除了Fact Chunk外,其他三个Chunk是必须的。每个Chunk有各自的ID,位
于Chunk最开始位置,作为标示,而且均为4个字节。并且紧跟在ID后面的是Chunk大
小(去除ID和Size所占的字节数后剩下的其他字节数目),4个字节表示,低字节
表示数值低位,高字节表示数值高位。下面具体介绍各个Chunk内容。
PS:
    所有数值表示均为低字节表示低位,高字节表示高位。
二、具体介绍
RIFF WAVE Chunk
    ==================================
    |       |所占字节数|  具体内容   |
    ==================================
    | ID    |  4 Bytes |   'RIFF'    |
    ----------------------------------
    | Size  |  4 Bytes |             |
    ----------------------------------
    | Type  |  4 Bytes |   'WAVE'    |
    ----------------------------------
            图2  RIFF WAVE Chunk
    以'FIFF'作为标示,然后紧跟着为size字段,该size是整个wav文件大小减去ID
和Size所占用的字节数,即FileLen - 8 = Size。然后是Type字段,为'WAVE',表
示是wav文件。
    结构定义如下:
struct RIFF_HEADER
{
  char szRiffID[4];  // 'R','I','F','F'
  DWORD dwRiffSize;
  char szRiffFormat[4]; // 'W','A','V','E'
};
Format Chunk
    ====================================================================
    |               |   字节数  |              具体内容                |
    ====================================================================
    | ID            |  4 Bytes  |   'fmt '                             |
    --------------------------------------------------------------------
    | Size          |  4 Bytes  | 数值为16或18,18则最后又附加信息     |
    --------------------------------------------------------------------  ----
    | FormatTag     |  2 Bytes  | 编码方式,一般为0x0001               |     |
    --------------------------------------------------------------------     |
    | Channels      |  2 Bytes  | 声道数目,1--单声道;2--双声道       |     |
    --------------------------------------------------------------------     |
    | SamplesPerSec |  4 Bytes  | 采样频率                             |     |
    --------------------------------------------------------------------     |
    | AvgBytesPerSec|  4 Bytes  | 每秒所需字节数                       |     |===> WAVE_FORMAT
    --------------------------------------------------------------------     |
    | BlockAlign    |  2 Bytes  | 数据块对齐单位(每个采样需要的字节数) |     |
    --------------------------------------------------------------------     |
    | BitsPerSample |  2 Bytes  | 每个采样需要的bit数                  |     |
    --------------------------------------------------------------------     |
    |               |  2 Bytes  | 附加信息(可选,通过Size来判断有无) |     |
    --------------------------------------------------------------------  ----
                            图3  Format Chunk
    以'fmt '作为标示。一般情况下Size为16,此时最后附加信息没有;如果为18
则最后多了2个字节的附加信息。主要由一些软件制成的wav格式中含有该2个字节的
附加信息。
    结构定义如下:
struct WAVE_FORMAT
{
  WORD wFormatTag;
  WORD wChannels;
  DWORD dwSamplesPerSec;
  DWORD dwAvgBytesPerSec;
  WORD wBlockAlign;
  WORD wBitsPerSample;
};
struct FMT_BLOCK
{
  char  szFmtID[4]; // 'f','m','t',' '
  DWORD  dwFmtSize;
  WAVE_FORMAT wavFormat;
};

Fact Chunk
    ==================================
    |       |所占字节数|  具体内容   |
    ==================================
    | ID    |  4 Bytes |   'fact'    |
    ----------------------------------
    | Size  |  4 Bytes |   数值为4   |
    ----------------------------------
    | data  |  4 Bytes |             |
    ----------------------------------
            图4  Fact Chunk
    Fact Chunk是可选字段,一般当wav文件由某些软件转化而成,则包含该Chunk。
    结构定义如下:
struct FACT_BLOCK
{
  char  szFactID[4]; // 'f','a','c','t'
  DWORD  dwFactSize;
};
Data Chunk
    ==================================
    |       |所占字节数|  具体内容   |
    ==================================
    | ID    |  4 Bytes |   'data'    |
    ----------------------------------
    | Size  |  4 Bytes |             |
    ----------------------------------
    | data  |          |             |
    ----------------------------------
             图5 Data Chunk
    Data Chunk是真正保存wav数据的地方,以'data'作为该Chunk的标示。然后是
数据的大小。紧接着就是wav数据。根据Format Chunk中的声道数以及采样bit数,
wav数据的bit位置可以分成以下几种形式:
    ---------------------------------------------------------------------
    |   单声道  |    取样1    |    取样2    |    取样3    |    取样4    |
    |           |--------------------------------------------------------
    |  8bit量化 |    声道0    |    声道0    |    声道0    |    声道0    |
    ---------------------------------------------------------------------
    |   双声道  |          取样1            |           取样2           |
    |           |--------------------------------------------------------
    |  8bit量化 |  声道0(左)  |  声道1(右)  |  声道0(左)  |  声道1(右)  |
    ---------------------------------------------------------------------
    |           |          取样1            |           取样2           |
    |   单声道  |--------------------------------------------------------
    | 16bit量化 |    声道0    |  声道0      |    声道0    |  声道0      |
    |           | (低位字节)  | (高位字节)  | (低位字节)  | (高位字节)  |
    ---------------------------------------------------------------------
    |           |                         取样1                         |
    |   双声道  |--------------------------------------------------------
    | 16bit量化 |  声道0(左)  |  声道0(左)  |  声道1(右)  |  声道1(右)  |
    |           | (低位字节)  | (高位字节)  | (低位字节)  | (高位字节)  |
    ---------------------------------------------------------------------
                         图6 wav数据bit位置安排方式
    Data Chunk头结构定义如下:
    struct DATA_BLOCK
{
  char szDataID[4]; // 'd','a','t','a'
  DWORD dwDataSize;
};
作者: yao_qin    时间: 2009-8-27 20:58
wav文件里到底有什么?学过软件的应该能看懂个大概。
作者: 小白    时间: 2009-8-27 21:13
这个东西被贴出很多次了,有用吗? 能解释听感不同吗? 如果现在的"理论"能够解释一切现象的话,科学家们都退休了吧,还浪费什么研究资金啊?
作者: yao_qin    时间: 2009-8-27 21:25
原帖由 小白 于 2009-8-27 21:13 发表
这个东西被贴出很多次了,有用吗? 能解释听感不同吗? 如果现在的"理论"能够解释一切现象的话,科学家们都退休了吧,还浪费什么研究资金啊?

你可以不懂,但不懂装懂就不对了。
作者: dark132    时间: 2009-8-27 21:36
我认为WAV的不同也可能和电脑工作状态的稳定性有关

理论上,手工输入相同数据,得到的结果相同

但因为声音和时间排序有关,不排除中断和其他操作系统层面的自工影响到音质的表现

其实我认为讨论这个没什么意思,用PC听音乐本来就是妥协,就这点不同,还要去比个半死,已经失去了听音乐的乐趣

老实用CD放不好,用了电脑就要承认复杂的操作系统对于播放环境的不稳定性
作者: 小白    时间: 2009-8-27 21:38
原帖由 yao_qin 于 2009-8-27 21:25 发表

你可以不懂,但不懂装懂就不对了。




我装什么懂了呢? 我只知道听感的差异仍是客观存在的,我仍可以盲听出来.  你帖的这大段东西要是能解答这个问题就好了. 可惜不能.
作者: yao_qin    时间: 2009-8-27 21:40
原帖由 小白 于 2009-8-27 21:38 发表




我装什么懂了呢? 我只知道听感的差异仍是客观存在的,我仍可以盲听出来.  你帖的这大段东西要是能解答这个问题就好了. 可惜不能.

我贴出来给大家包括我自己学习有问题吗?
作者: ricepig    时间: 2009-8-27 21:40
支持两段完全相同的wav进行比较

不要比到后来,还是两段“二进制不同”的wav
作者: 小白    时间: 2009-8-27 21:43
原帖由 ricepig 于 2009-8-27 21:40 发表
支持两段完全相同的wav进行比较

不要比到后来,还是两段“二进制不同”的wav




头部索引还是不同的. 只要是来源不同的WAV文件,头部总是不同的. 尾部可能也有差异. 当然你也许可以用编辑软件硬把他们改成相同,但这整个是篡改文件了.
作者: ricepig    时间: 2009-8-27 21:44
原帖由 小白 于 2009-8-27 21:43 发表




头部索引还是不同的. 只要是来源不同的WAV文件,头部总是不同的. 尾部可能也有差异. 当然你也许可以用编辑软件硬把他们改成相同,但这整个是篡改文件了.

那就不要说是“数据完全相同”的wav文件
头部的与声音无关的部分可以被忽略,至于哪些是无关的部分,lz的转帖是很直接的参考资料
作者: 小白    时间: 2009-8-27 21:48
如果你看过WAV文件的结构,我是说用二进制软件实际地看过一个现实中的WAV文件,你就会知道,它开头是一串索引编码,后面就是一大串的0. 这些0是音乐开始前的静默段. 然后就开始出现数据. 这些数据就是音乐的内容.

我所指的"数据完全相同",就是指除去开头索引码和那些0之外的"音乐正文"完全相同.
作者: jahal    时间: 2009-8-27 21:48
水区组团围观
作者: ricepig    时间: 2009-8-27 21:53
原帖由 小白 于 2009-8-27 21:48 发表
如果你看过WAV文件的结构,我是说用二进制软件实际地看过一个现实中的WAV文件,你就会知道,它开头是一串索引编码,后面就是一大串的0. 这些0是音乐开始前的静默段. 然后就开始出现数据. 这些数据就是音乐的内容.

我 ...

如果你觉得0没有意义,为什么不把0截去再进行比较呢?
作者: 小白    时间: 2009-8-27 21:57
我可没有说"0没有意义". 音乐的开头总要留一点空白的(零点几秒到一秒多),你一按PLAY键立刻蹦出音乐,多突兀啊.  把这些0都截掉比较当然也可以. 不影响音乐部分的音质.
作者: 9UP升    时间: 2009-8-27 22:10
提示: 作者被禁止或删除 内容自动屏蔽
作者: nadesicozhao    时间: 2009-8-27 22:23
原帖由 9UP升 于 2009-8-27 22:10 发表


你這樣說話的語氣,會被警告,然後和諧掉,你知嗎


小白指出这转贴被帖过多次而已
就弄来一个不懂装懂的头衔
真不知道最近新来论坛的人都是什么素质
作者: 9UP升    时间: 2009-8-27 22:25
提示: 作者被禁止或删除 内容自动屏蔽
作者: nadesicozhao    时间: 2009-8-27 22:28
原帖由 9UP升 于 2009-8-27 22:25 发表


趙版,先處理一下今天很多不適當的文字,你是對事不對人吧
http://www.headphoneclub.com/bbs/thread-127893-8-2.html

118樓叫人去死


开学了
我看到就会处理的 你要是觉得不合适的 请你用举报功能

另外刚看了下 此人说马甲去死 没有针对任何正常网友
虽然不合适 但并非必须处理 提醒一次

[ 本帖最后由 nadesicozhao 于 2009-8-27 22:31 编辑 ]
作者: 9UP升    时间: 2009-8-27 22:29
提示: 作者被禁止或删除 内容自动屏蔽
作者: ricepig    时间: 2009-8-27 23:13
原帖由 小白 于 2009-8-27 21:57 发表
我可没有说"0没有意义". 音乐的开头总要留一点空白的(零点几秒到一秒多),你一按PLAY键立刻蹦出音乐,多突兀啊.  把这些0都截掉比较当然也可以. 不影响音乐部分的音质.

0有意义的话,两段wav前面的0不同,造成差异,是很自然的吧?

说“完全相同”是不严谨的吧?
作者: yao_qin    时间: 2009-8-27 23:37
原帖由 9UP升 于 2009-8-27 22:10 发表


你這樣說話的語氣,會被警告,然後和諧掉,你知嗎

没用的。都是成年人,会用代理会用马甲,不过不同的是我会让他知道我的马甲还是我的发言。
作者: 9UP升    时间: 2009-8-27 23:46
提示: 作者被禁止或删除 内容自动屏蔽
作者: 小白    时间: 2009-8-28 09:48
原帖由 ricepig 于 2009-8-27 23:13 发表

0有意义的话,两段wav前面的0不同,造成差异,是很自然的吧?

说“完全相同”是不严谨的吧?





0的意义就是"不出声音",这就是它的意义. 你说0段的长短不同,也就是音乐开始的时间不同,这个音乐在文件的0.56秒处开始,那个音乐在文件0.91秒处开始,会影响音乐本身的音质吗?
作者: yao_qin    时间: 2009-8-28 10:03
0的定义可能是不出声音,但也可能是头文件标示信息的一部分,举个例子,极端的不确切的假设:如果100表示双声道,10标示单声道,那个切掉最后一个0就完全不对了。
作者: DreamYA    时间: 2009-8-28 10:03
原帖由 小白 于 2009-8-28 09:48 发表





0的意义就是"不出声音",这就是它的意义. 你说0段的长短不同,也就是音乐开始的时间不同,这个音乐在文件的0.56秒处开始,那个音乐在文件0.91秒处开始,会影响音乐本身的音质吗?



根据WAV的定义和数据结构,WAV文件中的每一个数值所在的数据位置都是严格匹配并有意义的,因为音频数据在WAV是严格顺序依次存储的,在二进制上任何一个字符的移位都会造成整个声音波形的奇变。所说的两者“非零的有意义的数据”是不是就是对其在每一个采样点上呢(即对于16bit音频文件,“值为零的无意义数据”长度恰好为32bit的整数倍),如果不是那么这就不能叫数据一样。

有关wav的资料请参考http://ccrma.stanford.edu/courses/422/projects/WaveFormat/

WAV数据结构:

作者: 小白    时间: 2009-8-28 10:08
不会错位的. 在一长串代表"空白"的000000000000000之后,音乐开始的地方,两个文件必定是匹配的,不会这个从奇数位开始,那个从偶数位开始. 这个我观察过太多次了.  所以如果把两个文件开始的00000000000000串都完全删除的话,文件就能完全匹配上,绝不会错位. (当然开始部位的索引码仍不同.)
作者: DreamYA    时间: 2009-8-28 10:49
原帖由 小白 于 2009-8-28 10:08 发表
不会错位的. 在一长串代表"空白"的000000000000000之后,音乐开始的地方,两个文件必定是匹配的,不会这个从奇数位开始,那个从偶数位开始. 这个我观察过太多次了.  所以如果把两个文件开始的00000000000000串都完全删除的话,文件就能完全匹配上,绝不会错位.


请教白版是如何观察的,要知道即使是1秒的空白就会有44100×32个0,请问是否通过工具统计过“有意义的数据”之前的0的个数呢?如果没有统计,就简单的通过“观察”就认为“有意义的数据”对齐,这本身就会有很大的漏洞。因为就像之前我所说的WAV的数据结构是严格按顺序存储的,每个字符的移位都会造成最后波形的不同,即使相对位置上两者一一对应。

[ 本帖最后由 DreamYA 于 2009-8-28 11:15 编辑 ]
作者: lcp12345    时间: 2009-8-28 11:14
这个技术贴不错,原来wave的每个bit也有定义!

还真的要去学习一下,再来理解下
作者: 小白    时间: 2009-8-28 11:54
原帖由 DreamYA 于 2009-8-28 10:49 发表


请教白版是如何观察的,要知道即使是1秒的空白就会有44100×32个0,请问是否通过工具统计过“有意义的数据”之前的0的个数呢?如果没有统计,就简单的通过“观察”就认为“有意义的数据”对齐,这本身就会有很大 ...




这里面牵涉到一个认识: 我一直认为那些0,就是代表"无声",代表着音轨中音乐开始之前的那短暂空白. 我当然不会去数具体有多少个0,因为数不清的,太多了. 我关注的是0结束之后正式音乐数据开始. 从这里开始,那些数据,不管是抓轨的,还是用什么CD机,DVD机录出来的,数据开始变得完全一致了.

如果你认为那一长串0原来不代表"音乐开始前的空白段",而是有其他的意义,而且还会影响到音乐开始后的波形,当然这并非不可能,但我觉得依据何在呢?
作者: ricepig    时间: 2009-8-28 12:32
原帖由 小白 于 2009-8-28 11:54 发表




这里面牵涉到一个认识: 我一直认为那些0,就是代表"无声",代表着音轨中音乐开始之前的那短暂空白. 我当然不会去数具体有多少个0,因为数不清的,太多了. 我关注的是0结束之后正式音乐数据开始. 从这里开始,那些 ...

这个问题ls的兄弟们解释得很清楚了,小白还是虚心一点好好看看吧,不要惯性思维了。
留白和对齐是计算机里很多地方都会使用的小技巧,不要再认为只是代表”无声“了
作者: 小白    时间: 2009-8-28 14:01
原帖由 ricepig 于 2009-8-28 12:32 发表

这个问题ls的兄弟们解释得很清楚了,小白还是虚心一点好好看看吧,不要惯性思维了。
留白和对齐是计算机里很多地方都会使用的小技巧,不要再认为只是代表”无声“了




我可没有不虚心,大家一起讨论问题而已.

我实践下来知道,音乐数据开始之前的这些0的多少很容易控制: 音乐开始前的空白段留得长一些,0就多一些,空白段留得短一些,0就少一些. 所以我认为这些0只是代表音乐开始之前的"空白段"的长短而已. 从理论上讲可以精确控制出现多少0 (严格控制空白段的时间长短就可以). 举个例子我拿SONY D50去转录一个音轨(CD机播放),我按下Record键晚一点点,录出来的wav文件前面就多出现很多0. 这些0的多少,是完全可控制的!

所以为什么这些0会影响后面的音乐数据? 又是如何影响的? 我还是无法理解.
作者: 小白    时间: 2009-8-28 14:20
如果还没明白我的逻辑,我再多写几句:

我用SONY D50数码录音机,连接一台CD机,进行录制WAV文件,在这个过程中,录出来的WAV文件开头处的00000000000000字节串的长短,我是完全可以控制它的. RECORD键按得早一些,少留些空白,那么0串就短一些,我RECORD键按得晚一些,多留些空白,那么0串就可以很长. 要多长有多长.

所以这个000000000000串的长短,是个可控的因素. 它如何去影响音乐开始后的音质? 难道说,我一次录的时候早一点按下RECORD键,后一次录的时候晚些按下RECORD键,其他条件全部相同,结果录出来的2个WAV文件,就因为前面000000000000串的长短不同,而有了不同音质??

有些事情,谁实践过,谁知道! 光是看书本理论,看图表,看公式,没有亲手实践过的体验,有时真是沟通起来蛮难的.
作者: ricepig    时间: 2009-8-28 14:39
小白,我的意思是,你应该好好看看计算机的对齐和留白是为什么。这不光光是为了表示空数据。

这种方式不但在音频里,在图像存储的时候也很普遍。

你所说的实践,是在两个”二进制不同“的文件上的比较结果,这个结果,完全支持不了你的”两个完全相同的wav文件回放,声音不同“的结论

我们现在讨论的是这个问题。
作者: ricepig    时间: 2009-8-28 14:47
如果你还看不明白,我可以简化一点解释给你听:

一个Byte是8bit。而目前计算机都是32位(或者是64位),换句话说,计算机以32bit作为最小的运算单元,这需要4个byte的输入数据拼接(这个所谓拼接是简化的说法,大家知道意思就行)。数据前面0留白的数量,会决定后面的数据这么4-4-4划分的不同,这可能造成处理结果的不同。

当然,上面是个简化解释结果,只是大概说明了一下这个留白对解码的影响。

除此之外,两段wave文件之前的留白不同。在没有证明”空白音频长度对人没有心理暗示“这个之前,你的结论也是不成立的。因为我们可以将听感归结为”可能是留白造成了不同的心理暗示“

这么解释清楚了嘛?
作者: 小白    时间: 2009-8-28 15:55
谢谢费时间解释. 我知道空白段的0000000000000000串的0,肯定是偶数个(说不定还是4的倍数,但我数不清),因为二进制浏览软件里都是2位一隔给你看的. 我看到的一直是 00 00 00 00,等等. 没有出现过 00 00 00 01这样的.  

姑且就把正式音乐数据开始之前的000000000000000字串的长度,作为"可能影响音质"的因素之一. 但我最感兴趣的是,我相信大家最感兴趣的,不是这些00000000000,而是最关键的一点,000000000000之后的正式音乐数据,用二进制浏览软件查看,是完全一致的.  而听感不同.
作者: ricepig    时间: 2009-8-28 21:36
“可能影响音质”的因素没有排除之前,你的“听感不同”没有意义。因为不知道是这个因素影响了听感还是别的因素。

另外,你可能还没有看明白我的解释,前面的0的不同,会影响到后面非零数据被打包成32位数据的。

二进制编辑软件里的每个零,表示的是一个十六进制位(也就是4个二进制位),是半个字节,里面的两位“00”这样才是一个字节。从你说的01这种,就可以看出你没有看懂我的解释。

建立在上述基础上,你就不能说明“后面的数据是完全一致的”,你明白了没有?

[ 本帖最后由 ricepig 于 2009-8-28 21:40 编辑 ]
作者: 小白    时间: 2009-8-28 22:13
简单说,你认为象这张截图所显示的,貌似相同的音乐数据,事实上可能背后是不同的?

你要回答"是",也是一句话,我就明白你的意思了.


作者: ricepig    时间: 2009-8-28 22:26
没有明白你说的“背后是不同的”什么意思

我要说明的是,同样的数字,其位置(偏移量)的不同,会对回放产生影响。

位置(偏移量)其实也是一种信息,是wav规定的,并明确存储于wav文件中的。

wave文件不存储任何它所未规定的信息。

over

[ 本帖最后由 ricepig 于 2009-8-28 22:27 编辑 ]
作者: pcear    时间: 2009-8-28 22:30
原帖由 ricepig 于 2009-8-28 21:36 发表
“可能影响音质”的因素没有排除之前,你的“听感不同”没有意义。因为不知道是这个因素影响了听感还是别的因素。

另外,你可能还没有看明白我的解释,前面的0的不同,会影响到后面非零数据被打包成32位数据的。
...

支持小白,前面的0的确代表空白,也许用静音这个词更贴切一些。
但你说的这个情况也合理,所以切wav应该用音频编辑软件,用二进制编辑软件容易造成问题。因为wav里面的实际数据结构有文件头的采样率、bit深度之类规定,用二进制编辑只修改data chunk会打破wav的文件结构。
作者: 小白    时间: 2009-8-28 22:31
ricepig: 不管怎么说,你这么一讲我明白了. 所以EAC抓轨要对光驱做"偏移校正"(offset),是否也是这个意思?
作者: ricepig    时间: 2009-8-28 22:33
是的,估计是有这方面考虑的。
作者: whisky_qz    时间: 2009-8-28 23:17
EAC的偏移矫恰恰是为了正确控制抓轨的文件开头部分的空白长度。不然不同的光驱抓轨出来的WAV数据就会不一样。
WAV文件前的0就是代表空白,跟音质没有关系的,不要再往这个方向钻了。




欢迎光临 耳机俱乐部论坛 (https://www.headphoneclub.com/) Powered by Discuz! X3.4