杀手3契约闪退:C#音频采集1

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