作者:Joseph Pan (转载请注明出处http://www.cnblogs.com/weizhoupan/archive/2011/03/20/1989718.html )
喜欢买碟或者卡拉ok的朋友一定不会对声道这个术语陌生。通常我们在音像店买回来的VCD或者DVD都是双声道的形式,唱片商在录制唱片时往往提供了两个或多个声道,以保存不同的音频内容,以形成立体声效。左声道保存的大多为一些背景声效,如卡拉OK的消音伴唱。而右声道保存的往往是较为主要的声音,比如主唱的歌声。利用多声道技术,听众可以清晰地分辨出各种乐器来自的方向,从而使音乐更富想象力,更加接近于临场感受。
有时候我们只需要音频里的单声道内容,比如喜欢用电脑录制卡拉ok的朋友就经常为了找歌曲的伴唱而流连于各大伴奏网站。现在的网络翻唱非常流行,很多网络歌手就是先搜索喜欢的歌曲的伴奏,然后利用Adobe Audition(前身就是大名鼎鼎的CoolEdit)录制自己的演唱,然后加一些简单的降噪和压限处理,最后mix到伴奏的音轨里面。尽管利用Audition也可以完成单声道的提取工作,但是操作起来比较复杂。
其实对WAV的单声道提取并不困难。关键在于对WAV文件格式的理解。
一、WAV的文件头
WAV为微软公司(Microsoft)开发的一种声音文件格式,它符合RIFF(Resource Interchange File Format)文件规范。所有的WAV都有一个文件头,这个文件头包含了音频流的编码参数。
偏移地址 |
字节数 |
类型 |
内容 |
00H~03H |
4 |
字符 |
资源交换文件标志(RIFF) |
04H~07H |
4 |
长整数 |
从下个地址开始到文件尾的总字节数 |
08H~0BH |
4 |
字符 |
WAV文件标志(WAVE) |
0CH~0FH |
4 |
字符 |
波形格式标志(FMT) |
10H~13H |
4 |
整数 |
过滤字节(一般为00000010H) |
14H~15H |
2 |
整数 |
格式种类(值为1,表示数据PCMμ律编码的数据) |
16H~17H |
2 |
整数 |
通道数,单声道为1,双声道为2 |
18H~1BH |
4 |
长整数 |
采样频率 |
1CH~1FH |
4 |
长整数 |
波形数据传输速率(每秒平均字节数) |
20H~21H |
2 |
整数 |
数据的调整数(按字节计算) |
22H~23H |
2 |
整数 |
样本数据位数 |
表1 WAV的文件头
由表1我们可以得到以下几个重要的信息:
- 16H~17H处记录通道数,当值为1时,表示文件为单声道;当值为2时,表示文件为双声道。
- 18H~1BH处记录采样频率。它的取值与声卡的支持情况有关。常见的有8000、11025、22050、44100、48000、96000等。其中,44100是大多数歌曲文件采用的标准采样频率。
- 22H~23H处记录样本数据位数。即每一个采样的长度。常见的有8位和16位。这里还包含了另外一个信息:若样本的数据位数为n,对于双声道文件,则低n/2位用于存放左声道;高n/2位用于存放右声道。
根据这三点信息,我们可以自己编程实现单声道的提取。下面我们就来一步步动手实现。由于程序涉及的只是简单的二进制文件读写操作,因此这里只举C#作简单示例,其他语言的处理与之大同小异。
二、文件读取类的编写
为了方便以后对WAV文件的研究,我们可以先单独写一个WAV文件读取类,专门获取文件头的每一块信息:
WaveAccess
1 /*WaveAccess 2 * 提供wav文件头的访问 3 * 和文件写入相关操作 4 */ 5 6 class WaveAccess 7 { 8 9 private byte[] riff; //4 10 private byte[] riffSize; //4 11 private byte[] waveID; //4 12 private byte[] fmtID; //4 13 private byte[] notDefinition; //4 14 private byte[] waveType; //2 15 private byte[] channel; //2 16 private byte[] sample; //4 17 private byte[] send; //4 18 private byte[] blockAjust; //2 19 private byte[] bitNum; //2 20 private byte[] unknown; //2 21 private byte[] dataID; //4 22 private byte[] dataLength; //4 23 24 short[] data; 25 private string longFileName; 26 27 public string LongFileName 28 { 29 get { return longFileName; } 30 } 31 32 public string ShortFileName 33 { 34 get 35 { 36 int pos = LongFileName.LastIndexOf("\\"); 37 return LongFileName.Substring(pos + 1); 38 } 39 } 40 41 public short[] Data 42 { 43 get { return data; } 44 set { data = value; } 45 } 46 47 public string Riff 48 { 49 get { return Encoding.Default.GetString(riff); } 50 set { riff = Encoding.Default.GetBytes(value); } 51 } 52 53 public uint RiffSize 54 { 55 get { return BitConverter.ToUInt32(riffSize,0); } 56 set { riffSize = BitConverter.GetBytes(value); } 57 } 58 59 60 public string WaveID 61 { 62 get { return Encoding.Default.GetString(waveID); } 63 set { waveID = Encoding.Default.GetBytes(value); } 64 } 65 66 67 public string FmtID 68 { 69 get { return Encoding.Default.GetString(fmtID); } 70 set { fmtID = Encoding.Default.GetBytes(value); } 71 } 72 73 74 public int NotDefinition 75 { 76 get { return BitConverter.ToInt32(notDefinition,0); } 77 set { notDefinition = BitConverter.GetBytes(value); } 78 } 79 80 81 public short WaveType 82 { 83 get { return BitConverter.ToInt16(waveType, 0); } 84 set { waveType = BitConverter.GetBytes(value); } 85 } 86 87 88 public ushort Channel 89 { 90 get { return BitConverter.ToUInt16(channel,0); } 91 set { channel = BitConverter.GetBytes(value); } 92 } 93 94 95 public uint Sample 96 { 97 get { return BitConverter.ToUInt32(sample,0); } 98 set { sample = BitConverter.GetBytes(value); } 99 } 100 101 102 public uint Send 103 { 104 get { return BitConverter.ToUInt32(send, 0); } 105 set { send = BitConverter.GetBytes(value); } 106 } 107 108 109 public ushort BlockAjust 110 { 111 get { return BitConverter.ToUInt16(blockAjust, 0); ; } 112 set { blockAjust = BitConverter.GetBytes(value); } 113 } 114 115 116 public ushort BitNum 117 { 118 get { return BitConverter.ToUInt16(bitNum, 0);} 119 set { bitNum = BitConverter.GetBytes(value); } 120 } 121 122 123 public ushort Unknown 124 { 125 get 126 { 127 if (unknown == null) 128 { 129 return 1; 130 } 131 else 132 return BitConverter.ToUInt16(unknown, 0); 133 } 134 135 set { unknown = BitConverter.GetBytes(value); } 136 } 137 138 139 public string DataID 140 { 141 get { return Encoding.Default.GetString(dataID); } 142 set { dataID = Encoding.Default.GetBytes(value); } 143 } 144 145 public uint DataLength 146 { 147 get { return BitConverter.ToUInt32(dataLength, 0); } 148 set { dataLength = BitConverter.GetBytes(value); } 149 } 150 151 152 public WaveAccess() { } 153 154 public WaveAccess(string filepath) 155 { 156 try 157 { 158 riff = new byte[4]; 159 riffSize = new byte[4]; 160 waveID = new byte[4]; 161 fmtID = new byte[4]; 162 notDefinition = new byte[4]; 163 waveType = new byte[2]; 164 channel = new byte[2]; 165 sample = new byte[4]; 166 send = new byte[4]; 167 blockAjust = new byte[2]; 168 bitNum = new byte[2]; 169 unknown = new byte[2]; 170 dataID = new byte[4]; //52 171 dataLength = new byte[4]; //56 个字节 172 173 longFileName = filepath; 174 175 176 FileStream fs = new FileStream(filepath,FileMode.Open); 177 BinaryReader bread = new BinaryReader(fs); 178 riff = bread.ReadBytes(4); 179 riffSize = bread.ReadBytes(4); 180 waveID = bread.ReadBytes(4); 181 fmtID = bread.ReadBytes(4); 182 notDefinition = bread.ReadBytes(4); 183 waveType = bread.ReadBytes(2); 184 channel = bread.ReadBytes(2); 185 sample = bread.ReadBytes(4); 186 send = bread.ReadBytes(4); 187 blockAjust = bread.ReadBytes(2); 188 bitNum = bread.ReadBytes(2); 189 if (BitConverter.ToUInt32(notDefinition,0)== 18 ) 190 { 191 unknown = bread.ReadBytes(2); 192 } 193 dataID = bread.ReadBytes(4); 194 dataLength = bread.ReadBytes(4); 195 uint length = DataLength/2; 196 data = new short[length]; 197 for (int i = 0; i < length; i++) 198 { 199 data[i] = bread.ReadInt16();//读入2字节有符号整数 200 } 201 fs.Close(); 202 bread.Close(); 203 } 204 catch (System.Exception ex) 205 { 206 Console.Write(ex.Message); 207 } 208 } 209 210 public short[] GetData(uint begin,uint end ) 211 { 212 if ((end - begin) >= Data.Length) 213 return Data; 214 else 215 { 216 uint temp = end - begin+1; 217 short[] dataTemp = new short[temp]; 218 uint j = begin; 219 for (int i = 0; i<temp; i++) 220 { 221 dataTemp[i] = Data[j]; 222 j++; 223 } 224 return dataTemp; 225 } 226 227 } 228 229 /// <summary> 230 /// 生成wav文件到系统 231 /// </summary> 232 /// <param name="fileName">要保存的文件名</param> 233 /// <returns></returns>
|
请发表评论