C#音频采集代码
1.using System;
2.using System.Collections.Generic;
3.using System.Text;
4.
5.using System.IO;
6.using System.Threading;
7.using Microsoft.DirectX;
8.using Microsoft.DirectX.DirectSound;
9.using System.Net.Sockets;
10.using System.Net;
11.
12.namespace MatureVoice
13.{
14. class VoiceCapture
15. {
16. private MemoryStream memstream;//内存流
17. private SecondaryBuffer secBuffer;//辅助缓冲区
18. private int iNotifySize = 0;//通知大小
19. private int iBufferSize = 0;//捕捉缓冲区大小
20. private CaptureBuffer capturebuffer;//捕捉缓冲区对象
21. private AutoResetEvent notifyEvent;//通知事件
22. private Thread notifyThread;//通知线程
23. private int iNotifyNum=0;//通知个数
24. private Notify myNotify;//通知对象
25. private Capture capture;//捕捉设备对象
26. private Device PlayDev;//播放设备对象
27. private BufferDescription buffDiscript;
28. private Socket Client;
29. private EndPoint epServer;
30. private int iBufferOffset=0;//捕捉缓冲区位移
31. private IntPtr intptr;//窗口句柄
32.
33. public IntPtr Intptr
34. {
35. set
36. {
37. intptr = value;
38. }
39. }
40.
41. public int NotifySize
42. {
43. set
44. {
45. iNotifySize = value;
46. }
47.
48. }
49.
50. public int NotifyNum
51. {
52. set
53. {
54. iNotifyNum = value;
55. }
56. }
57.
58. public Socket LocalSocket
59. {
60. set
61. {
62. Client = value;
63. }
64. }
65.
66. public EndPoint RemoteEndPoint
67. {
68. set
69. {
70. epServer = value;
71. }
72. }
73.
74.
75. /// <summary>
76. /// 初始化相关操作
77. /// </summary>
78. public void InitVoice()
79. {//初始化声音相关设置:(1)捕捉缓冲区(2)播放缓冲区
80. if (!CreateCaputerDevice())
81. {
82. throw new Exception();
83. }//建立设备对象
84. CreateCaptureBuffer();//建立缓冲区对象
85. CreateNotification();//设置通知及事件
86. //======(2)==============
87. if (!CreatePlayDevice())
88. {
89. throw new Exception();
90. }
91. CreateSecondaryBuffer();
92. }
93.
94. /// <summary>
95. /// 启动声音采集
96. /// </summary>
97. public void StartVoiceCapture()
98. {
99. capturebuffer.Start(true);//true表示设置缓冲区为循环方式,开始捕捉
100. }
101.
102. /// <summary>
103. /// 创建用于播放的音频设备对象
104. /// </summary>
105. /// <returns>创建成功返回true</returns>
106. private bool CreatePlayDevice()
107. {
108. DevicesCollection dc = new DevicesCollection();
109. Guid g;
110. if (dc.Count > 0)
111. {
112. g = dc[0].DriverGuid;
113. }
114. else
115. { return false; }
116. PlayDev = new Device(g);
117. PlayDev.SetCooperativeLevel(intptr, CooperativeLevel.Normal);
118. return true;
119. }
120.
121. /// <summary>
122. /// 创建辅助缓冲区
123. /// </summary>
124. private void CreateSecondaryBuffer()
125. {
126. buffDiscript = new BufferDescription();
127. WaveFormat mWavFormat = SetWaveFormat();
128. buffDiscript.Format = mWavFormat;
129. iNotifySize = mWavFormat.AverageBytesPerSecond / iNotifyNum;//设置通知大小
130. iBufferSize = iNotifyNum * iNotifySize;
131. buffDiscript.BufferBytes = iBufferSize;
132. buffDiscript.ControlPan = true;
133. buffDiscript.ControlFrequency = true;
134. buffDiscript.ControlVolume = true;
135. buffDiscript.GlobalFocus = true;
136. secBuffer = new SecondaryBuffer(buffDiscript, PlayDev);
137. byte[] bytMemory = new byte[100000];
138. memstream = new MemoryStream(bytMemory, 0, 100000, true, true);
139. //g729 = new G729();
140. //g729.InitalizeEncode();
141. //g729.InitalizeDecode();
142. }
143.
144. /// <summary>
145. /// 创建捕捉设备对象
146. /// </summary>
147. /// <returns>如果创建成功返回true</returns>
148. private bool CreateCaputerDevice()
149. {
150. //首先要玫举可用的捕捉设备
151. CaptureDevicesCollection capturedev = new CaptureDevicesCollection();
152. Guid devguid;
153. if (capturedev.Count > 0)
154. {
155. devguid = capturedev[0].DriverGuid;
156. }
157. else
158. {
159. System.Windows.Forms.MessageBox.Show("当前没有可用于音频捕捉的设备", "系统提示");
160. return false;
161. }
162. //利用设备GUID来建立一个捕捉设备对象
163. capture = new Capture(devguid);
164. return true;
165. }
166.
167. /// <summary>
168. /// 创建捕捉缓冲区对象
169. /// </summary>
170. private void CreateCaptureBuffer()
171. {
172. //想要创建一个捕捉缓冲区必须要两个参数:缓冲区信息(描述这个缓冲区中的格式等),缓冲设备。
173. WaveFormat mWavFormat = SetWaveFormat();
174. CaptureBufferDescription bufferdescription = new CaptureBufferDescription();
175. bufferdescription.Format = mWavFormat;//设置缓冲区要捕捉的数据格式
176. iNotifySize = mWavFormat.AverageBytesPerSecond / iNotifyNum;//1秒的数据量/设置的通知数得到的每个通知大小小于0.2s的数据量,话音延迟小于200ms为优质话音
177. iBufferSize = iNotifyNum * iNotifySize;
178. bufferdescription.BufferBytes = iBufferSize;
179. bufferdescription.ControlEffects = true;
180. bufferdescription.WaveMapped = true;
181. capturebuffer = new CaptureBuffer(bufferdescription, capture);//建立设备缓冲区对象
182.
183. }
184.
185. //设置通知
186. private void CreateNotification()
187. {
188. BufferPositionNotify[] bpn = new BufferPositionNotify[iNotifyNum];//设置缓冲区通知个数
189. //设置通知事件
190. notifyEvent = new AutoResetEvent(false);
191. notifyThread = new Thread(RecoData);//通知触发事件
192. notifyThread.IsBackground = true;
193. notifyThread.Start();
194. for (int i = 0; i < iNotifyNum; i++)
195. {
196. bpn[i].Offset = iNotifySize + i * iNotifySize - 1;//设置具体每个的位置
197. bpn[i].EventNotifyHandle = notifyEvent.Handle;
198. }
199. myNotify = new Notify(capturebuffer);
200. myNotify.SetNotificationPositions(bpn);
201.
202. }
203.
204. //线程中的事件
205. private void RecoData()
206. {
207. while (true)
208. {
209. // 等待缓冲区的通知消息
210. notifyEvent.WaitOne(Timeout.Infinite, true);
211. // 录制数据
212. RecordCapturedData(Client,epServer);
213. }
214. }
215.
216. //真正转移数据的事件,其实就是把数据传送到网络上去。
217. private void RecordCapturedData(Socket Client,EndPoint epServer )
218. {
219. byte[] capturedata = null;
220. int readpos = 0, capturepos = 0, locksize = 0;
221. capturebuffer.GetCurrentPosition(out capturepos, out readpos);
222. locksize = readpos - iBufferOffset;//这个大小就是我们可以安全读取的大小
223. if (locksize == 0)
224. {
225. return;
226. }
227. if (locksize < 0)
228. {//因为我们是循环的使用缓冲区,所以有一种情况下为负:当文以载读指针回到第一个通知点,而Ibuffeoffset还在最后一个通知处
229. locksize += iBufferSize;
230. }
231. capturedata = (byte[])capturebuffer.Read(iBufferOffset, typeof(byte), LockFlag.FromWriteCursor, locksize);
232. //capturedata = g729.Encode(capturedata);//语音编码
233. try
234. {
235. Client.SendTo(capturedata, epServer);//传送语音
236. }
237. catch
238. {
239. throw new Exception();
240. }
241. iBufferOffset += capturedata.Length;
242. iBufferOffset %= iBufferSize;//取模是因为缓冲区是循环的。
243. }
244.
245.
246. private int intPosWrite = 0;//内存流中写指针位移
247. private int intPosPlay = 0;//内存流中播放指针位移
248. private int intNotifySize = 5000;//设置通知大小
249.
250. /// <summary>
251. /// 从字节数组中获取音频数据,并进行播放
252. /// </summary>
253. /// <param name="intRecv">字节数组长度</param>
254. /// <param name="bytRecv">包含音频数据的字节数组</param>
255. public void GetVoiceData(int intRecv, byte[] bytRecv)
256. {
257. //intPosWrite指示最新的数据写好后的末尾。intPosPlay指示本次播放开始的位置。
258. if (intPosWrite + intRecv <= memstream.Capacity)
259. {//如果当前写指针所在的位移+将要写入到缓冲区的长度小于缓冲区总大小
260. if ((intPosWrite - intPosPlay >= 0 && intPosWrite - intPosPlay < intNotifySize) || (intPosWrite - intPosPlay < 0 && intPosWrite - intPosPlay + memstream.Capacity < intNotifySize))
261. {
262. memstream.Write(bytRecv, 0, intRecv);
263. intPosWrite += intRecv;
264.
265. }
266. else if (intPosWrite - intPosPlay >= 0)
267. {//先存储一定量的数据,当达到一定数据量时就播放声音。
268. buffDiscript.BufferBytes = intPosWrite - intPosPlay;//缓冲区大小为播放指针到写指针之间的距离。
269. SecondaryBuffer sec = new SecondaryBuffer(buffDiscript, PlayDev);//建立一个合适的缓冲区用于播放这段数据。
270. memstream.Position = intPosPlay;//先将memstream的指针定位到这一次播放开始的位置
271. sec.Write(0, memstream, intPosWrite - intPosPlay, LockFlag.FromWriteCursor);
272. sec.Play(0, BufferPlayFlags.Default);
273. memstream.Position = intPosWrite;//写完后重新将memstream的指针定位到将要写下去的位置。
274. intPosPlay = intPosWrite;
275. }
276. else if (intPosWrite - intPosPlay < 0)
277. {
278. buffDiscript.BufferBytes = intPosWrite - intPosPlay + memstream.Capacity;//缓冲区大小为播放指针到写指针之间的距离。
279. SecondaryBuffer sec = new SecondaryBuffer(buffDiscript, PlayDev);//建立一个合适的缓冲区用于播放这段数据。
280. memstream.Position = intPosPlay;
281. sec.Write(0, memstream, memstream.Capacity - intPosPlay, LockFlag.FromWriteCursor);
282. memstream.Position = 0;
283. sec.Write(memstream.Capacity - intPosPlay, memstream, intPosWrite, LockFlag.FromWriteCursor);
284. sec.Play(0, BufferPlayFlags.Default);
285. memstream.Position = intPosWrite;
286. intPosPlay = intPosWrite;
287. }
288. }
289. else
290. {//当数据将要大于memstream可容纳的大小时
291. int irest = memstream.Capacity - intPosWrite;//memstream中剩下的可容纳的字节数。
292. memstream.Write(bytRecv, 0, irest);//先写完这个内存流。
293. memstream.Position = 0;//然后让新的数据从memstream的0位置开始记录
294. memstream.Write(bytRecv, irest, intRecv - irest);//覆盖旧的数据
295. intPosWrite = intRecv - irest;//更新写指针位置。写指针指示下一个开始写入的位置而不是上一次结束的位置,因此不用减一
296. }
297. }
298.
299. /// <summary>
300. /// 设置音频格式,如采样率等
301. /// </summary>
302. /// <returns>设置完成后的格式</returns>
303. private WaveFormat SetWaveFormat()
304. {
305. WaveFormat format = new WaveFormat();
306. format.FormatTag = WaveFormatTag.Pcm;//设置音频类型
307. format.SamplesPerSecond = 11025;//采样率(单位:赫兹)典型值:11025、22050、44100Hz
308. format.BitsPerSample = 16;//采样位数
309. format.Channels = 1;//声道
310. format.BlockAlign = (short)(format.Channels * (format.BitsPerSample / 8));//单位采样点的字节数
311. format.AverageBytesPerSecond = format.BlockAlign * format.SamplesPerSecond;
312.
313. return format;
314. //按照以上采样规格,可知采样1秒钟的字节数为22050*2=44100B 约为 43K
315. }
316.
317. /// <summary>
318. /// 停止语音采集
319. /// </summary>
320. public void Stop()
321. {
322. capturebuffer.Stop();
323. if (notifyEvent != null)
324. {
325. notifyEvent.Set();
326. }
327. if (notifyThread != null && notifyThread.IsAlive == true)
328. {
329. notifyThread.Abort();
330. }
331. }
332. }
333.}