C#音频采集代码

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.}  


 

 

 

 

  • 0
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值