簡介
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
