主题:[原创]WAV文件的数据结构以及绘制静态波形图的代码
WAV文件的数据结构以及绘制静态波形图的代码
一、结构分析
WAV文件格式是一种由微软和IBM联合开发的用于音频数字存储的标准,它采用RIFF文件格式结构。
1.WAVE文件由"块"组成。块的基本结构如下:
表1 块的基本结构
----------------------------------------------
偏移量 名称 字节数 类型 说明
----------------------------------------------
00 块标志 4 字符 由ASCII字符组成
04 块长度 4 长整型
08 块内容 数据
----------------------------------------------
说明:
①块内容中所包含的数据是以字(WORD)为单位排列的,其长度必须为偶数,如果是奇数,则在最后添加一个空字节(NULL)。
②块长度不包括标志符4字节以及块长度本身所占用的4字节。
2.WAVE文件一般有四种块,它们是:RIFF块、格式块、附加块(可选),数据块。按照在文件中的出现位置,WAV文件的结构顺序如下:
表2 块在文件中的顺序
--------------------------------
名称 字节数 说明
--------------------------------
RIFF块 12 RIFF WAVE Chunk
格式块 24或26 Format Chunk
附加块 12 Fact Chunk
数据块 不定 Data Chunk
--------------------------------
说明:
①其中,RIFF块和格式块又合称为文件头,所以,如果没有附加块的话,WAV文件就是由文件头和数据体两大部分组成。
3.RIFF块的数据结构如下:
表3 RIFF块的数据结构
---------------------------------------------------
偏移量 名称 字节数 数据类型 内容
---------------------------------------------------
00 标志符 4 字符 “RIFF”的Ascii码
04 文件长度 4 长整形 文件的总字节数
08 WAV标志 4 字符 “WAVE”的Ascii码
---------------------------------------------------
说明:
①RIFF是英文Resource Interchange File Format的缩写,它是一种含有嵌套数据结构的二进制文件格式。
②文件长度不包括标志符4字节以及文件长度本身所占用的4字节。
③为什么还要有一个“WAVE”标志呢?这是因为除了波形格式(.WAV)以外,能以RIFF文件格式存储的数据还包括:音频视频交错格式(.AVI)、位图格式(.RDI)、MIDI格式(.RMI)、调色板格式(.PAL)、多媒体电影(.RMN)、动画光标(.ANI)、其它RIFF文件(.BND)。
4.格式块的数据结构如下:
表4 格式块的数据结构
----------------------------------------------------------------------------
偏移地址 字节数 数据类型 内容
----------------------------------------------------------------------------
0C 4 字符 波形格式标志“fmt ”
10 4 长整形 格式块长度(一般=16,若=18表示最后有2字节附加信息)
14 2 整形 格式类别(值=1表示编码方式为PCMμ律编码)
16 2 整形 声道数(单声道=1,双声音=2)
18 4 长整形 采样频率(每秒样本数,表示每个通道的播放速度)
1C 4 长整形 数据传送速率(每秒字节=采样频率×每个样本字节数)
20 2 整形 每个样本字节数(又称基准块=每个样本位数×声道数÷8)
22 2 整形 每个样本位数(又称量化位数)
24 2 整形 附加信息(可选,通过块长度来判断有无)
----------------------------------------------------------------------------
说明:
①格式块长度不包括格式标志4字节以及块长度本身所占用的4个字节
②附加信息主要由一些软件制成的wav格式中含有该2个字节的
③名词解释:
采样频率:将模拟声音波形进行数字化时,每秒钟抽取声波幅度样本的次数。采样频率一般有11025Hz(11kHz)、22050Hz(22kHz)和44100Hz(44kHz)三种。
量化位数:采样结果的数据长度,它决定了模拟信号数字化以后的动态范围。常用的有8位、12位和16位。
声道数:有单声道和双声道之分,双声道中的左声道称为声道0,右声道称为声道1。
5.附加块是可选的,一般当 wav文件是由某些软件转化而成时,则可能包含附加块。附加块的数据结构如下:
表5 附加块的数据结构
---------------------------------------------------
偏移地址 字节数 数据类型 内容
---------------------------------------------------
24 4 字符 标志符“fact”
28 4 长整形 块长度(=4)
2C 4
---------------------------------------------------
说明:
①如果格式块中有附加信息,则偏移地址均后推2字节。
6.数据块的数据结构如下:
表6 数据块的数据结构
----------------------------------------
偏移地址 字节数 类型 内容
----------------------------------------
24 4 字符 数据标志符“data”
28 4 长整型 样本数据长度
2C... ... 样本数据
----------------------------------------
说明:
①如果格式块中有附加住信息,则偏移地址均后推2字节。
②如果文件中有附加块,则偏移地址均后推12字节。
③样本数据长度不包括标志符4字节以及长度数据本身所占用的4字节。
④WAV文件的播放时间=样本数据长度÷数据传送速率
⑤8位单声道:每个样本数据由8位表示;
⑥16位单声道:每个样本数据由16位表示,其中低字节存放高位,高字节存放低位
⑦8位立体声:每个样本数据由16位表示,每8位表示一个声道,高8位和低8位分别代表左右两个声道。
⑧16位立体声:每个样本数据由32位表示,每16位表示一个声道,高16位和低16位分别代表左右两个声道。
二、实例
1.让我们来做个实验,以加深理性认识。
启动系统自带的录音机,点击“录音”键,同时喊叫,过一秒钟停止。保存为8KHZ/8位/单声道的WAV文件(文件名:8_1.wav),这个文件的前58个数据如下(也许你的数据会有差异):
①8KHZ,8位,单声道
---------------------------------------------------
00: 52 49 46 46 9E 1F 00 00 57 41 56 45 66 6D 74 20
10: 12 00 00 00 01 00 01 00 40 1F 00 00 40 1F 00 00
20: 01 00 08 00 00 00 66 61 63 74 04 00 00 00 6B 1F
30: 00 00 64 61 74 61 6B 1F 00 00
---------------------------------------------------
2.以 8_1.wav 为例,分析如下:
00-0B:RIFF块,其中:
00-03:“RIFF”的Ascii字符
04-07:文件长度=&H1F9E字节,当然,实际长度还要+8
08-0B:“WAVE”的Ascii字符
0C-25:格式块,其中:
0C-0F:“fmt ”的Ascii字符
10-13:块长度=18(10进制),说明该块的最后有2字节的附加信息
14-15:格式类别=1,表示编码方式为PCMμ律编码
16-17:声道数=1,表示为单声道
18-1B:采样频率=&H1F40(8000HZ)
1C-1F:数据传输速率=&H1F40,这是每秒钟传输的字节数
20-21:每个样本字节数=1字节
22-23:每个样本位数=8位
24-25:附加信息2个字节 00 00
26-32:附加块,其中:
26-29:“fact”的Ascii码
2A-2D:块长度=4
2E-31:块内容,&H1F6B
32-39:数据块之一部分,其中:
32-35:“data”的Ascii字符
36-39:数据块长度=&H1F6B(8043),这个长度是波型音频压缩数据的实际长度
注意:数据块长度&H1F6B是个奇数,根据前面的说明,数据块的最后会加上一个空字节00。我们来计算一下:文件长度=8094(&H1F9E)+8=58+8043+1=8102,看看你的 Hex编辑器,是不是这个数字?
最后计算一下这个文件的播放时间:数据块长度÷数据传输速率=&H1F6B÷&H1F40=1.0054(秒)
三、绘制静态波形图
下面让我们来绘制 8_1.wav 的静态波形图。
新建一个窗体,添加一个按纽,代码如下:
Option Explicit
Dim DAT() As Byte '样本数据
Private Sub Command1_Click()
On Error GoTo 100
Dim filename As String
Dim tagType As String * 4 '标记字符
Dim dataSum As Long '样本数据个数
Dim Temp1 As Long
filename = "D:\8_1.wav"
Open filename For Binary As #1
Get #1, , tagType
If tagType <> "RIFF" Then GoTo 100 '如果不是RIFF退出
Get #1, 9, tagType
If tagType <> "WAVE" Then GoTo 100 '如果不是WAVE退出
Get #1, , tagType
If tagType <> "fmt " Then GoTo 100 '如果不是fmt 退出
Get #1, , dataSum '获取格式块长度
Get #1, 21 + dataSum, tagType
If tagType = "fact" Then '如果是附加块
Get #1, , Temp1 '废弃
Get #1, , Temp1 '废弃
Get #1, , Temp1 '废弃
Get #1, , dataSum '获取数据块长度
ElseIf tagType = "data" Then
Get #1, , dataSum '获取数据块长度
Else
GoTo 100
End If
ReDim DAT(dataSum)
Get #1, , DAT
Close #1
DrawWaves '绘制波形
Exit Sub
100
Close #1
End Sub
Private Sub DrawWaves()
Dim i As Long
Dim exten As Long '波幅
Dim lastX As Long '水平坐标
Dim curY1 As Long '垂直坐标
Dim curY2 As Long '垂直坐标
ScaleWidth = UBound(DAT) '窗体宽度=样本个数
exten = ScaleHeight / 2 '波幅=窗体高度的二分之一
Cls
For i = 0 To ScaleWidth
curY1 = (DAT(i) - 128) / 128 * exten '垂直坐标=样本数据*最大波幅
Line (lastX, exten + curY2)-(i, exten + curY1)
curY2 = curY1: lastX = i
Next
End Sub
一、结构分析
WAV文件格式是一种由微软和IBM联合开发的用于音频数字存储的标准,它采用RIFF文件格式结构。
1.WAVE文件由"块"组成。块的基本结构如下:
表1 块的基本结构
----------------------------------------------
偏移量 名称 字节数 类型 说明
----------------------------------------------
00 块标志 4 字符 由ASCII字符组成
04 块长度 4 长整型
08 块内容 数据
----------------------------------------------
说明:
①块内容中所包含的数据是以字(WORD)为单位排列的,其长度必须为偶数,如果是奇数,则在最后添加一个空字节(NULL)。
②块长度不包括标志符4字节以及块长度本身所占用的4字节。
2.WAVE文件一般有四种块,它们是:RIFF块、格式块、附加块(可选),数据块。按照在文件中的出现位置,WAV文件的结构顺序如下:
表2 块在文件中的顺序
--------------------------------
名称 字节数 说明
--------------------------------
RIFF块 12 RIFF WAVE Chunk
格式块 24或26 Format Chunk
附加块 12 Fact Chunk
数据块 不定 Data Chunk
--------------------------------
说明:
①其中,RIFF块和格式块又合称为文件头,所以,如果没有附加块的话,WAV文件就是由文件头和数据体两大部分组成。
3.RIFF块的数据结构如下:
表3 RIFF块的数据结构
---------------------------------------------------
偏移量 名称 字节数 数据类型 内容
---------------------------------------------------
00 标志符 4 字符 “RIFF”的Ascii码
04 文件长度 4 长整形 文件的总字节数
08 WAV标志 4 字符 “WAVE”的Ascii码
---------------------------------------------------
说明:
①RIFF是英文Resource Interchange File Format的缩写,它是一种含有嵌套数据结构的二进制文件格式。
②文件长度不包括标志符4字节以及文件长度本身所占用的4字节。
③为什么还要有一个“WAVE”标志呢?这是因为除了波形格式(.WAV)以外,能以RIFF文件格式存储的数据还包括:音频视频交错格式(.AVI)、位图格式(.RDI)、MIDI格式(.RMI)、调色板格式(.PAL)、多媒体电影(.RMN)、动画光标(.ANI)、其它RIFF文件(.BND)。
4.格式块的数据结构如下:
表4 格式块的数据结构
----------------------------------------------------------------------------
偏移地址 字节数 数据类型 内容
----------------------------------------------------------------------------
0C 4 字符 波形格式标志“fmt ”
10 4 长整形 格式块长度(一般=16,若=18表示最后有2字节附加信息)
14 2 整形 格式类别(值=1表示编码方式为PCMμ律编码)
16 2 整形 声道数(单声道=1,双声音=2)
18 4 长整形 采样频率(每秒样本数,表示每个通道的播放速度)
1C 4 长整形 数据传送速率(每秒字节=采样频率×每个样本字节数)
20 2 整形 每个样本字节数(又称基准块=每个样本位数×声道数÷8)
22 2 整形 每个样本位数(又称量化位数)
24 2 整形 附加信息(可选,通过块长度来判断有无)
----------------------------------------------------------------------------
说明:
①格式块长度不包括格式标志4字节以及块长度本身所占用的4个字节
②附加信息主要由一些软件制成的wav格式中含有该2个字节的
③名词解释:
采样频率:将模拟声音波形进行数字化时,每秒钟抽取声波幅度样本的次数。采样频率一般有11025Hz(11kHz)、22050Hz(22kHz)和44100Hz(44kHz)三种。
量化位数:采样结果的数据长度,它决定了模拟信号数字化以后的动态范围。常用的有8位、12位和16位。
声道数:有单声道和双声道之分,双声道中的左声道称为声道0,右声道称为声道1。
5.附加块是可选的,一般当 wav文件是由某些软件转化而成时,则可能包含附加块。附加块的数据结构如下:
表5 附加块的数据结构
---------------------------------------------------
偏移地址 字节数 数据类型 内容
---------------------------------------------------
24 4 字符 标志符“fact”
28 4 长整形 块长度(=4)
2C 4
---------------------------------------------------
说明:
①如果格式块中有附加信息,则偏移地址均后推2字节。
6.数据块的数据结构如下:
表6 数据块的数据结构
----------------------------------------
偏移地址 字节数 类型 内容
----------------------------------------
24 4 字符 数据标志符“data”
28 4 长整型 样本数据长度
2C... ... 样本数据
----------------------------------------
说明:
①如果格式块中有附加住信息,则偏移地址均后推2字节。
②如果文件中有附加块,则偏移地址均后推12字节。
③样本数据长度不包括标志符4字节以及长度数据本身所占用的4字节。
④WAV文件的播放时间=样本数据长度÷数据传送速率
⑤8位单声道:每个样本数据由8位表示;
⑥16位单声道:每个样本数据由16位表示,其中低字节存放高位,高字节存放低位
⑦8位立体声:每个样本数据由16位表示,每8位表示一个声道,高8位和低8位分别代表左右两个声道。
⑧16位立体声:每个样本数据由32位表示,每16位表示一个声道,高16位和低16位分别代表左右两个声道。
二、实例
1.让我们来做个实验,以加深理性认识。
启动系统自带的录音机,点击“录音”键,同时喊叫,过一秒钟停止。保存为8KHZ/8位/单声道的WAV文件(文件名:8_1.wav),这个文件的前58个数据如下(也许你的数据会有差异):
①8KHZ,8位,单声道
---------------------------------------------------
00: 52 49 46 46 9E 1F 00 00 57 41 56 45 66 6D 74 20
10: 12 00 00 00 01 00 01 00 40 1F 00 00 40 1F 00 00
20: 01 00 08 00 00 00 66 61 63 74 04 00 00 00 6B 1F
30: 00 00 64 61 74 61 6B 1F 00 00
---------------------------------------------------
2.以 8_1.wav 为例,分析如下:
00-0B:RIFF块,其中:
00-03:“RIFF”的Ascii字符
04-07:文件长度=&H1F9E字节,当然,实际长度还要+8
08-0B:“WAVE”的Ascii字符
0C-25:格式块,其中:
0C-0F:“fmt ”的Ascii字符
10-13:块长度=18(10进制),说明该块的最后有2字节的附加信息
14-15:格式类别=1,表示编码方式为PCMμ律编码
16-17:声道数=1,表示为单声道
18-1B:采样频率=&H1F40(8000HZ)
1C-1F:数据传输速率=&H1F40,这是每秒钟传输的字节数
20-21:每个样本字节数=1字节
22-23:每个样本位数=8位
24-25:附加信息2个字节 00 00
26-32:附加块,其中:
26-29:“fact”的Ascii码
2A-2D:块长度=4
2E-31:块内容,&H1F6B
32-39:数据块之一部分,其中:
32-35:“data”的Ascii字符
36-39:数据块长度=&H1F6B(8043),这个长度是波型音频压缩数据的实际长度
注意:数据块长度&H1F6B是个奇数,根据前面的说明,数据块的最后会加上一个空字节00。我们来计算一下:文件长度=8094(&H1F9E)+8=58+8043+1=8102,看看你的 Hex编辑器,是不是这个数字?
最后计算一下这个文件的播放时间:数据块长度÷数据传输速率=&H1F6B÷&H1F40=1.0054(秒)
三、绘制静态波形图
下面让我们来绘制 8_1.wav 的静态波形图。
新建一个窗体,添加一个按纽,代码如下:
Option Explicit
Dim DAT() As Byte '样本数据
Private Sub Command1_Click()
On Error GoTo 100
Dim filename As String
Dim tagType As String * 4 '标记字符
Dim dataSum As Long '样本数据个数
Dim Temp1 As Long
filename = "D:\8_1.wav"
Open filename For Binary As #1
Get #1, , tagType
If tagType <> "RIFF" Then GoTo 100 '如果不是RIFF退出
Get #1, 9, tagType
If tagType <> "WAVE" Then GoTo 100 '如果不是WAVE退出
Get #1, , tagType
If tagType <> "fmt " Then GoTo 100 '如果不是fmt 退出
Get #1, , dataSum '获取格式块长度
Get #1, 21 + dataSum, tagType
If tagType = "fact" Then '如果是附加块
Get #1, , Temp1 '废弃
Get #1, , Temp1 '废弃
Get #1, , Temp1 '废弃
Get #1, , dataSum '获取数据块长度
ElseIf tagType = "data" Then
Get #1, , dataSum '获取数据块长度
Else
GoTo 100
End If
ReDim DAT(dataSum)
Get #1, , DAT
Close #1
DrawWaves '绘制波形
Exit Sub
100
Close #1
End Sub
Private Sub DrawWaves()
Dim i As Long
Dim exten As Long '波幅
Dim lastX As Long '水平坐标
Dim curY1 As Long '垂直坐标
Dim curY2 As Long '垂直坐标
ScaleWidth = UBound(DAT) '窗体宽度=样本个数
exten = ScaleHeight / 2 '波幅=窗体高度的二分之一
Cls
For i = 0 To ScaleWidth
curY1 = (DAT(i) - 128) / 128 * exten '垂直坐标=样本数据*最大波幅
Line (lastX, exten + curY2)-(i, exten + curY1)
curY2 = curY1: lastX = i
Next
End Sub