免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1157 | 回复: 0
打印 上一主题 下一主题

wave文件格式 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2007-02-25 20:03 |只看该作者 |倒序浏览
Wave文件的格式非常混乱。如果把wave文件的格式比作盆汤,毫无疑问有太多的厨师在完全未经协调的情况下,向这道汤里添加了太多的佐料。Wave文
件的格式规范中,有太多相互独立而且缺乏协调的组织向其中增加内容。结果是wave文件中有很多chunk是在重复别的chunk中的数据,而且通常是用
一种完全不同的方式。下面的讲解中我们尽量把注意力集中于wave文件中那些最经常出现的chunk上。
____________________________
| RIFF WAVE Chunk          |
|   groupID  = 'RIFF'      |
|   riffType = 'WAVE'      |
|    __________________    |
|   | Format Chunk     |   |
|   |    ckID = 'fmt ' |   |
|   |__________________|   |
|    __________________    |
|   | Sound Data Chunk |   |
|   |    ckID = 'data' |   |
|   |__________________|   |
|__________________________|
采样点和采样帧
解释一个Wave文件内容的过程很大部分围绕在采样点和采样帧这两个概念上。一个采样点是代表了给定声音在给定的时间点上的一个样本值。如果我们假设才用
的是PCM格式的wave文件,并且样点精度大于8bit,每一个采样点被保存为一个占用9-32bit的2的补码表示的数值。样点精度值保存在
Format
chunk中的wBitsPerSample属性中。例如一个16bit波形中的一个采样点是一个线性的16bit有符号整数,取值范围-
32768~32767.但是8bit的波形文件中,每一个样本点是一个线性的无符号数,范围0~255。上述带符号和无符号表示方法的差异,又是微软员
工的杰作。
绝大多数cpu的读写指令都是真对8bit的“字节”的,如果一个波形文件的采样点精度不是8的整数倍,它必须被凑到最接近的8的整数倍。1-
8bits/sample的波形文件用8bit保存一个样本,9-16bits/sample的波形文件用16bit保存一个样本……依此类推。
此外,数据比特是左对齐的,多余的填充bit都被设置为0。例如一个12bit的采样点,在wave文件中占用16bits,所以这两个字节的4-15号bit是数据,0-3号bit是填充的0。下图就是一个占据12bit的样点101000010111在内存中的情况:
| 1   0   1   0   0  
0   0   1   0   1  
1   1   0   0   0   0
|
|___|___|___|___|___|___|___|___|___|___|___|___|___|___|___|___|
  
    12 bit sample point is left justified          rightmost
                                                
4 bits are
                                                
zero padded
应该注意,因为wave格式使用intel体系的little endian字节顺序,所以LSB(Least Significant Byte)在内存中出现在先:
___ ___ ___ ___ ___ ___ ___ ___    ___ ___ ___ ___ ___ ___ ___ ___
|   |      |   |  
|   |    |   |  |  
|   |   |   |   |  
|   |   |   |
| 0   1   1   1   0  
0   0   0 |  | 1   0  
1   0   0   0   0   1
|
|___|___|___|___|___|___|___|___|  |___|___|___|___|___|___|___|___|
      
   bits 0 to 3     4 pad
bits               
bits 4 to 11
对多声道声音,(例如一个立体声波形文件),来自每一个声道的采样点数据被交织存放。例如,对于一个立体声波形来说,有两个声道的数据。文件中并不是先把
左声道的全部采样点数据放在文件前半部分,然后把右声道的全部采样点数据集中放在文件后半部分。实际的做法是将两个声道的数据“混合”放置,即:
左声道的第一个样点,右声道的第一个样点,左声道的第二个样点,右声道的第二个样点,左声道的第三个样点,右声道的第三个样点……左右声道的样点交替出现,这就是我们所说的“交织数据”。这样,当播放这些数据的时候,应该被同时播放的数据在文件中的位置总是相邻的。
所有需要被同时“播放”(也就是发送到数-模转换器)的样点,总称为一个“采样帧”。对立体声波形来说,每两个采样点构成一个采样帧。如下图所示:
  sample      
sample            
sample
  frame 0      frame
1            
frame N
_____ _____ _____ _____         _____ _____
| ch1 | ch2 | ch1 | ch2 | . . . | ch1 | ch2 |
|_____|_____|_____|_____|       |_____|_____|
对于一个单声道的波形来说,一个采样帧就由一个采样点构成,无需任何交织。对多声道的波形来说,应该按照下面所示的习惯顺序,来安排一个采样帧中的各个采样点的顺序。下面的每一幅图,展示了一种多声道波形文件中的一个采样帧中,各个采样点出现的习惯顺序。
  channels       1         2
             _________ _________
  stereo    | left    | right   |
            |_________|_________|
               
1        
2         3
             _________ _________ _________
  3 channel | left    | right   | center  |
            |_________|_________|_________|
               
1        
2        
3         4
             _________ _________ _________ _________
            |
front   | front   | rear    |
rear    |
  quad      | left    |
right   | left    | right   |
            |_________|_________|_________|_________|
               
1        
2        
3         4
             _________ _________ _________ _________
  4 channel | left    | center  | right   | surround|
            |_________|_________|_________|_________|
               
1        
2        
3        
4        
5         6
            
_________ _________ _________ _________ _________ _________
            |
left    | left    | center  |
right   | right   |surround |
  6 channel | center
|        
|         | center
|        
|         |
            |_________|_________|_________|_________|_________|_________|
一个采样帧中的各个采样点之间没有填充字节,各个采样帧之间也没有填充字节。注意上面的讨论中我们所说的都是非压缩的data
chunk中的数据格式。还有一些data chunk中可能存储压缩过的数据,显然这种情况下需要解压缩,解压缩之后的数据还是严格遵守上述格式的。
Format chunk
Format (fmt) chunk描述了波形文件的基本参数,比如抽样频率,bit精度,声道数量。
#define FormatID 'fmt '   /* chunkID for Format Chunk. 注意这个ID是四个字符,最后的t后面有一个空格! */
typedef struct {
  ID             chunkID;
  long           chunkSize;
  short          wFormatTag;
  unsigned short wChannels;
  unsigned long  dwSamplesPerSec;
  unsigned long  dwAvgBytesPerSec;
  unsigned short wBlockAlign;
  unsigned short wBitsPerSample;
/* 根据wFormatTag的不同取值,这里还可能出现更多的fields */
} FormatChunk;
ID总是"fmt
"。chunkSize是chunk的长度,单位是字节,这个长度不包括ID和Size两个域所占用的8字节。针对不同的wFormatTag的取值,
chunkSize也可能取不同的值。Wave数据可以以非压缩的格式存放,也可以用很多不同的压缩格式存放在data
chunk内。压缩格式存放时,每一个采样点占用的字节数可能是各不相同的。wFormatTag指明数据存储时是否经过压缩。如果经过压缩
(wFormatTag的取值不是1),FormatChunk末尾会被追加更多的域,用以说明压缩格式,以便解压缩。这些关于压缩格式的信息,首先是一
个unsigned short类型数据,指明这个unsigned short之后还有多少字节的附加数据。压缩格式的数据还必须包括一个Fact
chunk,这个chunk包括了一个unsigned
long数据指明数据被解压缩之后包含多少个样点。压缩格式太多了,详细信息参考微软站点。当wFormatTag=1,用的就是非压缩格式。
wChannels:该声音文件中的声道个数。1-单声道,2-立体声,4-四声道声音,等等。多声道情况下的采样点,采样帧及其格式参见上面的叙述。
dwSamplesPerSec:采样帧频率,即每秒播放的采样帧个数。标准的采样帧频率有11025, 22050,  44100 Hz。但是也有其它非标准的采样帧频率被使用。
dwAvgBytesPerSec:每秒钟有多少帧被播放。dwAvgBytesPerSec被音乐播放工具用于估计顺畅播放声音所需的RAM缓冲区大小。这个值应该用下面的公式计算并四舍五入到最接近的整数上:
dwSamplesPerSec * wBlockAlign
wBlockAlign该用下面的公式计算并四舍五入到最接近的整数上:
wChannels * (wBitsPerSample / 8)
这个值是一个采样帧的大小,单位是字节。例如一个16bit立体声声音文件中的一个采样帧大小是4字节。
The wBitsPerSample指定一个采样点的数据精度是多少个bit。
每个wave文件中有且只有一个format chunk。
举个例子说明format chunk。下面是windows标准的开机音乐的format chunk内容:
00000000h: 52 49 46 46 24 6B 07 00 57 41 56 45 66 6D 74 20 ; RIFF$k..WAVEfmt
00000010h: 10 00 00 00 01 00 02 00 22 56 00 00 88 58 01 00 ; ........"V..ˆX..
00000020h: 04 00 10
00                                    
; ....
从0ch开始是format chunk。
0ch-0fh是四个字节的chunkID: “fmt “。
10h-13h是chunkSize: 16。这个值是刨去chunkID和chunkSize之后format chunk的长度:16字节。
14h-15h是wFormatTag: 0x0001, 非压缩wave音频。
16h-17h是wChannels: 0x0002, 立体声音乐。
18h-1bh是dwSamplesPerSec: 0x5622=22050,即采样帧频率22050Hz.
1ch-1fh是dwAvgBytesPerSec:
0x015888=88200,采样帧频率22050,双声道,采样精度16bits=2bytes(见下面wBitsPerSample的取值),所以
每秒播放的字节数是22050*2*2=88200字节。有dwAvgBytesPerSec = dwSamplesPerSec *
wBlockAlign。
20h-21h是wBlockAlign,0x0004,有wBlockAligh = wChannels * (wBitsPerSample / 8)。
22h-23h是wBitsPerSample,0x0010,16bits每样点。
继续说明data chunk的格式。它包含了真正的声音样点数据:
#define DataID 'data'  /* chunk ID for data Chunk */
typedef struct {
  ID             chunkID;
  long           chunkSize;
  unsigned char  waveformData[];
} DataChunk;
Data chunk的chunkID总是data。chunkSize是chunk占据的字节数,不计ID和size两个域占用的8个字节,不计为了使chunk占用的字节数为偶数而添加的填充字节。下面的讨论都基于非压缩格式的wave数据。
waveformData数组中就是真正的波形数据。这些数据被划分为成为“采样帧”的单元,前面已经介绍过。从chunkSize属性可以知道波形数据
的长度,以字节为单位。Data chunk中保存的采样帧的个数,可以由data chunk的chunkSize除以format
chunk中的wBlockAligh得到。Data chunk是必须有的,每个wave文件中必须有且只有一个data chunk。
波形文件的存储,远远不止这一种形式。且不论那些数不胜数的压缩格式,此外还有一些脑子进水了的人,把数据存储在wave文件中内嵌的IFF
List中,这个list会包含多个data chunk和slnt chunk,此时Type
ID是wavl。不要在你的程序里支持这种wave文件格式,除非你脑子里也进水了。
               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/13667/showart_249755.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP