简介
WAV 为微软开发的一种声音文件格式,它符合 RIFF(Resource Interchange File Format)文件规范,用于保存 Windows 平台的音频信息资源。
第一节 文件头部格式
文件头(Header)是位于文件开头的一段承担一定任务的数据,通常是对主体数据的描述。
WAV 文件头由哪几部分构成
RIFF块(RIFF-Chunk)
格式化块(Format-Chunk)
附加块(Fact-Chunk)块偏移地址需要根据实际数据变更
数据块(Data-Chunk)块偏移地址需要根据实际数据变更
偏移地址 | 字节数 | 数据类型 | 内容 |
&H00 | 4 | String | 'RIFF'文件标志 |
&H04 | 4 | UInteger | 文件总长 |
&H08 | 4 | String | 'WAVE'文件标志 |
表1-1 RIFF块(RIFF-Chunk)
偏移地址 | 字节数 | 数据类型 | 内容 |
&H0C | 4 | String | 'fmt'标志 |
&H10 | 4 | UInteger | 块长度 |
&H12 | 2 | UShort | PCM格式类别 |
&H14 | 2 | UShort | 声道数目 |
&H18 | 4 | UInteger | 采样率 |
&H1C | 4 | UInteger | 传输速率 |
&H1E | 2 | UShort | 数据块对齐 |
&H20 | 2 | UShort | 每样本bit数 |
&H22 | 2 | UShort | 可选 |
表1-2 格式化块(Format-Chunk)
偏移地址 | 字节数 | 数据类型 | 内容 |
&H26 | 4 | String | 'fact'标志 |
&H2A | 4 | UInteger | 块长度 |
&H2E | 4 | UInteger | 附加信息 |
表1-3 附加块(Fact-Chunk)
偏移地址 | 字节数 | 数据类型 | 内容 |
&H32 | 4 | String | 'data'文件标志 |
&H36 | 4 | UInteger | 数据块总长 |
表1-4 数据块(Data-Chunk)
第二节 代码实现
1. 构建一个 WaveHeader 结构体
''' <summary> ''' wav音频头部信息 ''' </summary> Public Structure WaveHeader #Region "RiffChunk" ''' <summary> ''' RIFF标志 ''' </summary> Dim RIFF As String ''' <summary> ''' 文件长度 ''' </summary> Dim FileSize As UInteger ''' <summary> ''' WAVE标志 ''' </summary> Dim WAVE As String #End Region #Region "FormatChunk" ''' <summary> ''' FORMAT标志 ''' </summary> Dim FORMAT As String ''' <summary> ''' Format长度 ''' </summary> Dim FormatSize As UInteger ''' <summary> ''' 编码方式 ''' </summary> Dim FilePadding As UShort ''' <summary> ''' 声道数目 ''' </summary> Dim FormatChannels As UShort ''' <summary> ''' 采样频率 ''' </summary> Dim SamplesPerSecond As UInteger ''' <summary> ''' 每秒所需字节数 ''' </summary> Dim AverageBytesPerSecond As UInteger ''' <summary> ''' 数据块对齐单位 ''' </summary> Dim BytesPerSample As UShort ''' <summary> ''' 单个采样所需Bit数 ''' </summary> Dim BitsPerSample As UShort ''' <summary> ''' 附加信息 ''' </summary> Dim FormatExtra As UShort #End Region #Region "FactChunk" ''' <summary> ''' FACT标志 ''' </summary> Dim FACT As String ''' <summary> ''' Fact长度 ''' </summary> Dim FactSize As UInteger ''' <summary> ''' Fact信息 ''' </summary> Dim FactInf As UInteger #End Region #Region "DataChunk" ''' <summary> ''' DATA标志 ''' </summary> Dim DATA As String ''' <summary> ''' Data长度 ''' </summary> Dim DataSize As UInteger #End Region End Structure
/// <summary> /// wav音频头部信息 /// </summary> public struct WaveHeader { #region "RiffChunk" /// <summary> /// RIFF标志 /// </summary> public string RIFF; /// <summary> /// 文件长度 /// </summary> public uint FileSize; /// <summary> /// WAVE标志 /// </summary> #endregion public string WAVE; #region "FormatChunk" /// <summary> /// FORMAT标志 /// </summary> public string FORMAT; /// <summary> /// Format长度 /// </summary> public uint FormatSize; /// <summary> /// 编码方式 /// </summary> public ushort FilePadding; /// <summary> /// 声道数目 /// </summary> public ushort FormatChannels; /// <summary> /// 采样频率 /// </summary> public uint SamplesPerSecond; /// <summary> /// 每秒所需字节数 /// </summary> public uint AverageBytesPerSecond; /// <summary> /// 数据块对齐单位 /// </summary> public ushort BytesPerSample; /// <summary> /// 单个采样所需Bit数 /// </summary> public ushort BitsPerSample; /// <summary> /// 附加信息 /// </summary> #endregion public ushort FormatExtra; #region "FactChunk" /// <summary> /// FACT标志 /// </summary> public string FACT; /// <summary> /// Fact长度 /// </summary> public uint FactSize; /// <summary> /// Fact信息 /// </summary> #endregion public uint FactInf; #region "DataChunk" /// <summary> /// DATA标志 /// </summary> public string DATA; /// <summary> /// Data长度 /// </summary> #endregion public uint DataSize; }
2. 打开 *.wav 二进制文件
Dim data() As Byte = My.Computer.FileSystem.ReadAllBytes(wavFileName)
byte[] data = System.IO.File.ReadAllBytes(wavFileName);
3. 文件头部信息解析
''' <summary> ''' 返回指定字节数组包含的Wave头部信息 ''' </summary> Public Function GetWaveHeaderFromBytes(data As Byte()) As WaveHeader Dim header As New WaveHeader Dim tempIndex As UShort = 0 header.RIFF = CType(System.Text.Encoding.ASCII.GetChars(data, 0, 4), String) header.FileSize = System.BitConverter.ToUInt32(data, 4) header.WAVE = CType(System.Text.Encoding.ASCII.GetChars(data, 8, 4), String) 'FormatChunk header.FORMAT = CType(System.Text.Encoding.ASCII.GetChars(data, 12, 4), String) header.FormatSize = System.BitConverter.ToUInt32(data, 16) header.FilePadding = System.BitConverter.ToUInt16(data, 20) header.FormatChannels = System.BitConverter.ToUInt16(data, 22) header.SamplesPerSecond = System.BitConverter.ToUInt32(data, 24) header.AverageBytesPerSecond = System.BitConverter.ToUInt32(data, 28) header.BytesPerSample = System.BitConverter.ToUInt16(data, 32) header.BitsPerSample = System.BitConverter.ToUInt16(data, 34) If header.FormatSize = 18 Then header.FormatExtra = System.BitConverter.ToUInt16(data, 36) Else header.FormatExtra = 0 End If tempIndex = 20 + header.FormatSize 'FactChunk header.FACT = CType(System.Text.Encoding.ASCII.GetChars(data, tempIndex, 4), String) If header.FACT = "fact" Then header.FactSize = System.BitConverter.ToUInt32(data, tempIndex + 4) header.FactInf = IIf(header.FactSize = 2, System.BitConverter.ToUInt16(data, tempIndex + 8), System.BitConverter.ToUInt32(data, tempIndex + 8)) tempIndex = tempIndex + header.FactSize + 8 Else header.FACT = "NULL" header.FactSize = 0 header.FactInf = 0 End If 'DataChunk header.DATA = CType(System.Text.Encoding.ASCII.GetChars(data, tempIndex, 4), String) header.DataSize = System.BitConverter.ToUInt32(data, tempIndex + 4) Return header End Function
/// <summary> /// 返回指定字节数组包含的Wave头部信息 /// </summary> public WaveHeader GetWaveHeaderFromBytes(byte[] data) { WaveHeader header = new WaveHeader(); ushort tempIndex = 0; header.RIFF = Convert.ToString(System.Text.Encoding.ASCII.GetChars(data, 0, 4)); header.FileSize = System.BitConverter.ToUInt32(data, 4); header.WAVE = Convert.ToString(System.Text.Encoding.ASCII.GetChars(data, 8, 4)); //FormatChunk header.FORMAT = Convert.ToString(System.Text.Encoding.ASCII.GetChars(data, 12, 4)); header.FormatSize = System.BitConverter.ToUInt32(data, 16); header.FilePadding = System.BitConverter.ToUInt16(data, 20); header.FormatChannels = System.BitConverter.ToUInt16(data, 22); header.SamplesPerSecond = System.BitConverter.ToUInt32(data, 24); header.AverageBytesPerSecond = System.BitConverter.ToUInt32(data, 28); header.BytesPerSample = System.BitConverter.ToUInt16(data, 32); header.BitsPerSample = System.BitConverter.ToUInt16(data, 34); if (header.FormatSize == 18) { header.FormatExtra = System.BitConverter.ToUInt16(data, 36); } else { header.FormatExtra = 0; } tempIndex = 20 + header.FormatSize; //FactChunk header.FACT = Convert.ToString(System.Text.Encoding.ASCII.GetChars(data, tempIndex, 4)); if (header.FACT == "fact") { header.FactSize = System.BitConverter.ToUInt32(data, tempIndex + 4); header.FactInf = (header.FactSize == 2 ? System.BitConverter.ToUInt16(data, tempIndex + 8) : System.BitConverter.ToUInt32(data, tempIndex + 8)); tempIndex = tempIndex + header.FactSize + 8; } else { header.FACT = "NULL"; header.FactSize = 0; header.FactInf = 0; } //DataChunk header.DATA = Convert.ToString(System.Text.Encoding.ASCII.GetChars(data, tempIndex, 4)); header.DataSize = System.BitConverter.ToUInt32(data, tempIndex + 4); return header; }
附录
SoundPlayer 类 - MSDN