| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 1288 人关注过本帖
标题:求教,局域网聊天添加语音功能但是运行不了
只看楼主 加入收藏
恒欣
Rank: 1
等 级:新手上路
帖 子:5
专家分:0
注 册:2012-12-17
收藏
 问题点数:0 回复次数:4 
求教,局域网聊天添加语音功能但是运行不了
大家好,我想在局域网聊天中添加语音功能,参考的是别人的代码,叫maturevoice
程序代码:
 private void tool_Voice_Click(object sender, EventArgs e)
        {
            netchat1.SetRemoteIPEnd(((this.Tag as TreeNode).Tag as ClassUserInfo).UserIP, 8000);
            //先初始化语音模块,启动监听方法,再启动语音采集方法
            if (tool_Voice.Text == "语音")
            {
                tool_Voice.Text = "关闭";
                try
                {
                    netchat1.Intptr = this.Handle;
                    netchat1.InitVoice();
                }
                catch
                {
                    MessageBox.Show("声音模块初始化失败");
                }
                try
                {
                    netchat1.Listen();
                }
                catch
                {
                    MessageBox.Show("监听模块异常");
                }
                try
                {
                    netchat1.StartSendVoice();
                }
                catch
                {
                    if (MessageBox.Show("应用程序出现问题,需要重新启动应该程序嘛?", "系统提示", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question) == DialogResult.Yes)
                    {
                        Application.Exit();//先关闭应用程序
                        System.Diagnostics.Process.Start(System.Reflection.Assembly.GetExecutingAssembly().Location);
                    }
                }
            }
            else
            {
                netchat1.Stop();
                tool_Voice.Text = "语音";
            }
        }

 

其中“语音”类似QQ,使用的是toolstripbutton



[ 本帖最后由 恒欣 于 2012-12-17 17:16 编辑 ]
搜索更多相关主题的帖子: 聊天 局域网 语音 
2012-12-17 17:08
恒欣
Rank: 1
等 级:新手上路
帖 子:5
专家分:0
注 册:2012-12-17
收藏
得分:0 
聊天窗口的代码里除了上面的代码我还添加了一小段
程序代码:
 private NetChat netchat1;
        private void Voice_Load(object sender, EventArgs e)
        {
            netchat1 = new NetChat(8000);
                    }
2012-12-17 17:11
恒欣
Rank: 1
等 级:新手上路
帖 子:5
专家分:0
注 册:2012-12-17
收藏
得分:0 
这是VoiceCapture类
程序代码:
using System;
using System.Collections.Generic;
using System.Text;
using using System.Threading;
using Microsoft.DirectX;
using Microsoft.DirectX.DirectSound;
using using namespace BuaaflyClass
{
    class VoiceCapture
    {
        private MemoryStream memstream;//内存流
        private SecondaryBuffer secBuffer;//辅助缓冲区
        private int iNotifySize = 0;//通知大小
        private int iBufferSize = 0;//捕捉缓冲区大小
        private CaptureBuffer capturebuffer;//捕捉缓冲区对象
        private AutoResetEvent notifyEvent;//通知事件
        private Thread notifyThread;//通知线程
        private int iNotifyNum = 0;//通知个数
        private Notify myNotify;//通知对象
        private Capture capture;//捕捉设备对象
        private Device PlayDev;//播放设备对象
        private BufferDescription buffDiscript;
        private Socket Client;
        private EndPoint epServer;
        private int iBufferOffset = 0;//捕捉缓冲区位移
        private IntPtr intptr;//窗口句柄
        public IntPtr Intptr
        {
            set
            {
                intptr = value;
            }
        }
        public int NotifySize
        {
            set
            {
                iNotifySize = value;
            }
        }
        public int NotifyNum
        {
            set
            {
                iNotifyNum = value;
            }
        }
        public Socket LocalSocket
        {
            set
            {
                Client = value;
            }
        }
        public EndPoint RemoteEndPoint
        {
            set
            {
                epServer = value;
            }
        }

        /// <summary>
        /// 初始化相关操作
        /// </summary>
        public void InitVoice()
        {
            //初始化声音相关设置:(1)捕捉缓冲区(2)播放缓冲区
            if (!CreateCaputerDevice())
            {
                throw new Exception();
            }//建立设备对象
            CreateCaptureBuffer();//建立缓冲区对象
            CreateNotification();//设置通知及事件
            //======(2)==============
            if (!CreatePlayDevice())
            {
                throw new Exception();
            }
            CreateSecondaryBuffer();
        }
        /// <summary>
        /// 启动声音采集
        /// </summary>
        public void StartVoiceCapture()
        {
            capturebuffer.Start(true);//true表示设置缓冲区为循环方式,开始捕捉
        }
        /// <summary>
        /// 创建用于播放的音频设备对象
        /// </summary>
        /// <returns>创建成功返回true</returns>
        private bool CreatePlayDevice()
        {
            DevicesCollection dc = new DevicesCollection();
            Guid g;
            if (dc.Count > 0)
            {
                g = dc[0].DriverGuid;
            }
            else
            { return false; }
            PlayDev = new Device(g);
            PlayDev.SetCooperativeLevel(intptr, CooperativeLevel.Normal);
            return true;
        }
        /// <summary>
        /// 创建辅助缓冲区
        /// </summary>
        private void CreateSecondaryBuffer()
        {
            buffDiscript = new BufferDescription();
            WaveFormat mWavFormat = SetWaveFormat();
            buffDiscript.Format = mWavFormat;
            iNotifySize = mWavFormat.AverageBytesPerSecond / iNotifyNum;//设置通知大小
            iBufferSize = iNotifyNum * iNotifySize;
            buffDiscript.BufferBytes = iBufferSize;
            buffDiscript.ControlPan = true;
            buffDiscript.ControlFrequency = true;
            buffDiscript.ControlVolume = true;
            buffDiscript.GlobalFocus = true;
            secBuffer = new SecondaryBuffer(buffDiscript, PlayDev);
            byte[] bytMemory = new byte[100000];
            memstream = new MemoryStream(bytMemory, 0, 100000, true, true);
            //g729 = new G729();
            //g729.InitalizeEncode();
            //g729.InitalizeDecode();
        }
        /// <summary>
        /// 创建捕捉设备对象
        /// </summary>
        /// <returns>如果创建成功返回true</returns>
        private bool CreateCaputerDevice()
        {
            //首先要玫举可用的捕捉设备
            CaptureDevicesCollection capturedev = new CaptureDevicesCollection();
            Guid devguid;
            if (capturedev.Count > 0)
            {
                devguid = capturedev[0].DriverGuid;
            }
            else
            {
                System.Windows.Forms.MessageBox.Show("当前没有可用于音频捕捉的设备", "系统提示");
                return false;
            }
            //利用设备GUID来建立一个捕捉设备对象
            capture = new Capture(devguid);
            return true;
        }
        /// <summary>
        /// 创建捕捉缓冲区对象
        /// </summary>
        private void CreateCaptureBuffer()
        {
            //想要创建一个捕捉缓冲区必须要两个参数:缓冲区信息(描述这个缓冲区中的格式等),缓冲设备。
            WaveFormat mWavFormat = SetWaveFormat();
            CaptureBufferDescription bufferdescription = new CaptureBufferDescription();
            bufferdescription.Format = mWavFormat;//设置缓冲区要捕捉的数据格式
            iNotifySize = mWavFormat.AverageBytesPerSecond / iNotifyNum;//1秒的数据量/设置的通知数得到的每个通知大小小于0.2s的数据量,话音延迟小于200ms为优质话音
            iBufferSize = iNotifyNum * iNotifySize;
            bufferdescription.BufferBytes = iBufferSize;
            bufferdescription.ControlEffects = true;
            bufferdescription.WaveMapped = true;
            capturebuffer = new CaptureBuffer(bufferdescription, capture);//建立设备缓冲区对象
        }
        //设置通知
        private void CreateNotification()
        {
            BufferPositionNotify[] bpn = new BufferPositionNotify[iNotifyNum];//设置缓冲区通知个数
            //设置通知事件
            notifyEvent = new AutoResetEvent(false);
            notifyThread = new Thread(RecoData);//通知触发事件
            notifyThread.IsBackground = true;
            notifyThread.Start();
            for (int i = 0; i < iNotifyNum; i++)
            {
                bpn[i].Offset = iNotifySize + i * iNotifySize - 1;//设置具体每个的位置
            }
            myNotify = new Notify(capturebuffer);
            myNotify.SetNotificationPositions(bpn);
        }
        //线程中的事件
        private void RecoData()
        {
            while (true)
            {
                // 等待缓冲区的通知消息
                notifyEvent.WaitOne(Timeout.Infinite, true);
                // 录制数据
                RecordCapturedData(Client, epServer);
            }
        }
        //真正转移数据的事件,其实就是把数据传送到网络上去。
        private void RecordCapturedData(Socket Client, EndPoint epServer)
        {
            byte[] capturedata = null;
            int readpos = 0, capturepos = 0, locksize = 0;
            capturebuffer.GetCurrentPosition(out capturepos, out readpos);
            locksize = readpos - iBufferOffset;//这个大小就是我们可以安全读取的大小
            if (locksize == 0)
            {
                return;
            }
            if (locksize < 0)
            {//因为我们是循环的使用缓冲区,所以有一种情况下为负:当文以载读指针回到第一个通知点,而Ibuffeoffset还在最后一个通知处
                locksize += iBufferSize;
            }
            capturedata = (byte[])capturebuffer.Read(iBufferOffset, typeof(byte), LockFlag.FromWriteCursor, locksize);
            //capturedata = g729.Encode(capturedata);//语音编码
            try
            {
                Client.SendTo(capturedata, epServer);//传送语音
            }
            catch
            {
                throw new Exception();
            }
            iBufferOffset += capturedata.Length;
            iBufferOffset %= iBufferSize;//取模是因为缓冲区是循环的。
        }

        private int intPosWrite = 0;//内存流中写指针位移
        private int intPosPlay = 0;//内存流中播放指针位移
        private int intNotifySize = 5000;//设置通知大小
        /// <summary>
        /// 从字节数组中获取音频数据,并进行播放
        /// </summary>
        /// <param name="intRecv">字节数组长度</param>
        /// <param name="bytRecv">包含音频数据的字节数组</param>
        public void GetVoiceData(int intRecv, byte[] bytRecv)
        {
            //intPosWrite指示最新的数据写好后的末尾。intPosPlay指示本次播放开始的位置。
            if (intPosWrite + intRecv <= memstream.Capacity)
            {//如果当前写指针所在的位移+将要写入到缓冲区的长度小于缓冲区总大小
                if ((intPosWrite - intPosPlay >= 0 && intPosWrite - intPosPlay < intNotifySize) || (intPosWrite - intPosPlay < 0 && intPosWrite - intPosPlay + memstream.Capacity < intNotifySize))
                {
                    memstream.Write(bytRecv, 0, intRecv);
                    intPosWrite += intRecv;
                }
                else if (intPosWrite - intPosPlay >= 0)
                {//先存储一定量的数据,当达到一定数据量时就播放声音。
                    buffDiscript.BufferBytes = intPosWrite - intPosPlay;//缓冲区大小为播放指针到写指针之间的距离。
                    SecondaryBuffer sec = new SecondaryBuffer(buffDiscript, PlayDev);//建立一个合适的缓冲区用于播放这段数据。
                    memstream.Position = intPosPlay;//先将memstream的指针定位到这一次播放开始的位置
                    sec.Write(0, memstream, intPosWrite - intPosPlay, LockFlag.FromWriteCursor);
                    sec.Play(0, BufferPlayFlags.Default);
                    memstream.Position = intPosWrite;//写完后重新将memstream的指针定位到将要写下去的位置。
                    intPosPlay = intPosWrite;
                }
                else if (intPosWrite - intPosPlay < 0)
                {
                    buffDiscript.BufferBytes = intPosWrite - intPosPlay + memstream.Capacity;//缓冲区大小为播放指针到写指针之间的距离。
                    SecondaryBuffer sec = new SecondaryBuffer(buffDiscript, PlayDev);//建立一个合适的缓冲区用于播放这段数据。
                    memstream.Position = intPosPlay;
                    sec.Write(0, memstream, memstream.Capacity - intPosPlay, LockFlag.FromWriteCursor);
                    memstream.Position = 0;
                    sec.Write(memstream.Capacity - intPosPlay, memstream, intPosWrite, LockFlag.FromWriteCursor);
                    sec.Play(0, BufferPlayFlags.Default);
                    memstream.Position = intPosWrite;
                    intPosPlay = intPosWrite;
                }
            }
            else
            {//当数据将要大于memstream可容纳的大小时
                int irest = memstream.Capacity - intPosWrite;//memstream中剩下的可容纳的字节数。
                memstream.Write(bytRecv, 0, irest);//先写完这个内存流。
                memstream.Position = 0;//然后让新的数据从memstream的0位置开始记录
                memstream.Write(bytRecv, irest, intRecv - irest);//覆盖旧的数据
                intPosWrite = intRecv - irest;//更新写指针位置。写指针指示下一个开始写入的位置而不是上一次结束的位置,因此不用减一
            }
        }
        /// <summary>
        /// 设置音频格式,如采样率等
        /// </summary>
        /// <returns>设置完成后的格式</returns>
        private WaveFormat SetWaveFormat()
        {
            WaveFormat format = new WaveFormat();
            format.FormatTag = WaveFormatTag.Pcm;//设置音频类型
            format.SamplesPerSecond = 11025;//采样率(单位:赫兹)典型值:11025、22050、44100Hz
            format.BitsPerSample = 16;//采样位数
            format.Channels = 1;//声道
            format.BlockAlign = (short)(format.Channels * (format.BitsPerSample / 8));//单位采样点的字节数
            format.AverageBytesPerSecond = format.BlockAlign * format.SamplesPerSecond;
            return format;
            //按照以上采样规格,可知采样1秒钟的字节数为22050*2=44100B 约为 43K
        }
        /// <summary>
        /// 停止语音采集
        /// </summary>
        public void Stop()
        {
            capturebuffer.Stop();
            if (notifyEvent != null)
            {
                notifyEvent.Set();
            }
            if (notifyThread != null && notifyThread.IsAlive == true)
            {
                notifyThread.Abort();
            }
        }
    }
}


2012-12-17 17:18
恒欣
Rank: 1
等 级:新手上路
帖 子:5
专家分:0
注 册:2012-12-17
收藏
得分:0 
这是NetChat类
程序代码:
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using using namespace BuaaflyClass
{
    class NetChat
    {
        private int intPointVariable = 1;//端口增长幅度
        private IPEndPoint ipeLocal;
        private Socket LocalSocket;
        private IPEndPoint ipeRemote;
        private int intMaxDataSize = 10000;//接收缓冲区长度
        private VoiceCapture voicecapture1 = new VoiceCapture();
        private IntPtr intptr;
        public IntPtr Intptr
        {
            set
            {
                intptr = value;
            }
        }
        public IPEndPoint LocalIPEnd
        {
            get { return ipeLocal; }
        }
        public NetChat(int intPort)
        {
            ipeLocal = new IPEndPoint(IPAddress.Any, intPort);//配置本地IP 和 端口
            LocalSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
        }
        public NetChat(string strIP, int intPort)
        {
            ipeLocal = new IPEndPoint(IPAddress.Parse(strIP), intPort);//配置本地IP 和 端口
            LocalSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
        }
        /// <summary>
        /// 绑定自己的IP和端口
        /// </summary>
        /// <param name="ipe">IP端口对</param>
        /// <returns>绑定成功返回true</returns>
        public string BindSelf(IPEndPoint ipe)
        {
            while (true)
            {
                try
                {
                    LocalSocket.Bind((EndPoint)ipeLocal);
                    return ipeLocal.Address.ToString() + " : " + ipeLocal.Port;
                }
                catch
                {
                    ipeLocal.Port = 8000 + intPointVariable;
                    intPointVariable++;
                }
            }
        }
        /// <summary>
        /// 设置远程IP端口节点
        /// </summary>
        /// <param name="strRemote">远程IP</param>
        /// <param name="intPort">远程端口</param>
        public void SetRemoteIPEnd(string strRemote, int intPort)
        {
            ipeRemote = new IPEndPoint(IPAddress.Parse(strRemote), intPort);
        }
        private Thread ListenThread;
        private byte[] bytData;
        /// <summary>
        /// 监听方法,用于监听远程发送到本机的信息
        /// </summary>
        public void Listen()
        {
            ListenThread = new Thread(new ThreadStart(DoListen));
            ListenThread.IsBackground = true;//设置为后台线程,这样当主线程结束后,该线程自动结束
            ListenThread.Start();
        }
        private EndPoint epRemote;
        /// <summary>
        /// 监听线程
        /// </summary>
        private void DoListen()
        {
            bytData = new byte[intMaxDataSize];
            epRemote = (EndPoint)(new IPEndPoint(IPAddress.Any, 0));
            //可能会抛出一个异常
            while (true)
            {
                if (LocalSocket.Poll(5000, SelectMode.SelectRead))
                {//每5ms查询一下网络,如果有可读数据就接收
                    LocalSocket.BeginReceiveFrom(bytData, 0, bytData.Length, SocketFlags.None, ref epRemote, new AsyncCallback(ReceiveData), null);
                }
            }
        }
        /// <summary>
        /// 接收数据
        /// </summary>
        /// <param name="iar"></param>
        private void ReceiveData(IAsyncResult iar)
        {
            int intRecv = 0;
            try
            {
                intRecv = LocalSocket.EndReceiveFrom(iar, ref epRemote);
            }
            catch
            {
                throw new Exception();
            }
            if (intRecv > 0)
            {
                byte[] bytReceivedData = new byte[intRecv];
                Buffer.BlockCopy(bytData, 0, bytReceivedData, 0, intRecv);
                voicecapture1.GetVoiceData(intRecv, bytReceivedData);//调用声音模块中的GetVoiceData来从字节数组中获取声音并播放
            }
        }
        public void InitVoice()
        {
            voicecapture1 = new VoiceCapture();
            voicecapture1.NotifyNum = 10;
            voicecapture1.NotifySize = 4410;
            voicecapture1.LocalSocket = LocalSocket;
            voicecapture1.RemoteEndPoint = (EndPoint)ipeRemote;
            voicecapture1.Intptr = intptr;
            voicecapture1.InitVoice();
        }
        public void StartSendVoice()
        {
            voicecapture1.StartVoiceCapture();
        }
        public void Stop()
        {
            if (ListenThread != null && ListenThread.IsAlive == true)
            {
                ListenThread.Abort();
            }
            voicecapture1.Stop();
        }
    }
}

2012-12-17 17:18
恒欣
Rank: 1
等 级:新手上路
帖 子:5
专家分:0
注 册:2012-12-17
收藏
得分:0 
只要一点语音就报错为“未将对象引用到设置对象的实例”有关调用实时(JIT)调试而不是此对话框的详细信息,
请参见此消息的结尾。

************** 异常文本 **************
System.NullReferenceException: 未将对象引用设置到对象的实例。
   在 BuaaflyClient.F_Chat.tool_Voice_Click(Object sender, EventArgs e) 位置 e:\Buaafly - 方案1\BuaaflyClient\F_Chat.cs:行号 416
   在 System.Windows.Forms.ToolStripItem.RaiseEvent(Object key, EventArgs e)
   在 System.Windows.Forms.ToolStripButton.OnClick(EventArgs e)
   在 System.Windows.Forms.ToolStripItem.HandleClick(EventArgs e)
   在 System.Windows.Forms.ToolStripItem.HandleMouseUp(MouseEventArgs e)
   在 System.Windows.Forms.ToolStripItem.FireEventInteractive(EventArgs e, ToolStripItemEventType met)
   在 System.Windows.Forms.ToolStripItem.FireEvent(EventArgs e, ToolStripItemEventType met)
   在 System.Windows.Forms.ToolStrip.OnMouseUp(MouseEventArgs mea)
   在 System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
   在 System.Windows.Forms.Control.WndProc(Message& m)
   在 System.Windows.Forms.ScrollableControl.WndProc(Message& m)
   在 System.Windows.Forms.ToolStrip.WndProc(Message& m)
   在 System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
   在 System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
   在 System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
2012-12-17 17:20
快速回复:求教,局域网聊天添加语音功能但是运行不了
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.079005 second(s), 8 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved