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 的静态波形图(注意本代码只能绘制单声道的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 scaleFactor As Double '比例因数
Dim xStep As Double '水平步进量
Dim maxAmplitude As Long '最大波幅
Dim number As Long '样本数据编号
Dim lastX As Long '水平坐标
Dim curY1 As Long '垂直坐标
Dim curY2 As Long '垂直坐标
scaleFactor = UBound(DAT) / Picture1.Width '比例因数=样本个数/图片框宽度
xStep = IIf(scaleFactor < 1, 1 / scaleFactor, 1) '如果比例因数<1,水平步进量=1/比例因数,否则=1
maxAmplitude = Picture1.Height / 2 '最大波幅为图片框高度的二分之一
Picture1.Cls
For i = 0 To Picture1.Width Step xStep
number = scaleFactor * i '因为样本数据量太大,所以只能选用
curY1 = (DAT(number) - 128) / 128 * maxAmplitude '垂直坐标=样本数据*最大波幅
Picture1.Line (lastX, maxAmplitude + curY2)-(i, maxAmplitude + curY1)
curY2 = curY1
lastX = i
Next
End Sub
附:
①绘制8位立体声波形的代码:
Private Sub DrawWaves()
Dim i As Long
Dim exten As Long '波幅
Dim lastX As Long '水平坐标
Dim curLY1 As Long '左声道垂直坐标
Dim curLY2 As Long '左声道垂直坐标
Dim curRY1 As Long '右声道垂直坐标
Dim curRY2 As Long '右声道垂直坐标
Dim LYOffset As Long '左声道垂直偏移量
Dim RYOffset As Long '右声道垂直偏移量
Picture1.ScaleWidth = UBound(DAT) / 2 '图片框宽度=单声道样本个数
exten = Picture1.ScaleHeight / 4 '最大波幅为图片框高度的四分之一
LYOffset = exten
RYOffset = exten * 3
Cls
For i = 0 To Picture1.ScaleWidth
curLY1 = (DAT(i * 2) - 128) / 128 * exten
curRY1 = (DAT(i * 2 + 1) - 128) / 128 * exten
Picture1.Line (lastX, LYOffset + curLY2)-(i, LYOffset + curLY1) '画左声道波形
Picture1.Line (lastX, RYOffset + curRY2)-(i, RYOffset + curRY1) '画右声道波形
curLY2 = curLY1: curRY2 = curRY1: lastX = i
Next
End Sub
②绘制16位立体声波形的代码:
Private Sub DrawWaves()
Dim i As Long, j As Long, dL As String, dR As String
Dim exten As Long '最大波幅
Dim lastX As Long '水平坐标
Dim curLY1 As Long '左声道垂直坐标
Dim curLY2 As Long '左声道垂直坐标
Dim curRY1 As Long '右声道垂直坐标
Dim curRY2 As Long '右声道垂直坐标
Dim LYOffset As Long '左声道垂直偏移量
Dim RYOffset As Long '右声道垂直偏移量
Picture1.ScaleWidth = UBound(DAT) / 4 '图片框宽度=立体声样本个数/4
exten = Picture1.ScaleHeight / 4 '最大波幅为图片框高度的四分之一
LYOffset = exten
RYOffset = exten * 3
Cls
For i = 0 To Picture1.ScaleWidth
j = i * 4
dL = "&H" & Hex(DAT(j + 1)) & Hex(DAT(j))
dR = "&H" & Hex(DAT(j + 3)) & Hex(DAT(j + 2))
curLY1 = Val(dL) / 32768 * exten
curRY1 = Val(dR) / 32768 * exten
Picture1.Line (lastX, LYOffset + curLY2)-(i, LYOffset + curLY1) '画左声道波形
Picture1.Line (lastX, RYOffset + curRY2)-(i, RYOffset + curRY1) '画右声道波形
curLY2 = curLY1: curRY2 = curRY1: lastX = i
Next
End Sub
转发至微博
转发至微博
评论