实现一个简单的语音聊天室(源码)

  语音聊天室,或多人语音聊天,是即时通信应用中常见的功能之一,比如,QQ的语音讨论组就是我们用得比较多的。

       这篇文章将实现一个简单的语音聊天室,让多个人可以进入同一个房间进行语音沟通。先看运行效果截图:

         

    从左到右的三张图分别是:登录界面、语音聊天室的主界面、标注了各个控件的主界面。

  (如果觉得界面太丑,没关系,后面下载源码后,你可以自己美化~~)

一. C/S结构

  很明显,我这个语音聊天室采用的是C/S结构,整个项目结构相对比较简单,如下所示:

      

   该项目的底层是基于OMCS构建的。这样,服务端就基本没写代码,直接把OMCS服务端拿过来用;客户端就比较麻烦些,下面我就重点讲客户端的开发。

二. 客户端控件式开发

  客户端开发了多个自定义控件,然后将它们组装到一起,以完成语音聊天室的功能。为了便于讲解,我主界面的图做了标注,以指示出各个自定义控件。  

  现在我们分别介绍各个控件:

1. 分贝显示器 

  分贝显示器用于显示声音的大小,比如麦克风采集到的声音的大小,或扬声器播放的声音的大小。如上图中3标注的。

(1)傅立叶变换

  将声音数据转换成分贝强度使用的是傅立叶变换。其对应的是客户端项目中的FourierTransformer静态类。源码比较简单,就不贴出来了,大家自己去看。

(2)声音强度显示控件 DecibelDisplayer

  DecibelDisplayer 使用的是PrograssBar来显示声音强度的大小。

  每当有声音数据交给DecibelDisplayer显示时,首先,DecibelDisplayer会调用上面的傅立叶变换将其转换为分贝,然后,将其映射为PrograssBar的对应的Value。

2.发言者控件 SpeakerPanel

  SpeakerPanel 用于表示聊天室中的一个成员,如上图中1所示。它显示了成员的ID,成员的声音的强度(使用DecibelDisplayer控件),以及其麦克风的状态(启用、引用)。

  这个控件很重要,我将其源码贴出来:

    public partial class SpeakerPanel : UserControl ,IDisposable
    {
        private ChatUnit chatUnit;     

        public SpeakerPanel()
        {
            InitializeComponent();
            this.SetStyle(ControlStyles.ResizeRedraw, true);//调整大小时重绘
            this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);// 双缓冲
            this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);// 禁止擦除背景.
            this.SetStyle(ControlStyles.UserPaint, true);//自行绘制
            this.UpdateStyles();
        }

        public string MemberID
        {
            get
            {
                if (this.chatUnit == null)
                {
                    return null;
                }

                return this.chatUnit.MemberID;
            }
        }

        public void Initialize(ChatUnit unit)
        {
            this.chatUnit = unit;
            this.skinLabel_name.Text = unit.MemberID;

            this.chatUnit.MicrophoneConnector.ConnectEnded += new CbGeneric<OMCS.Passive.ConnectResult>(MicrophoneConnector_ConnectEnded);
            this.chatUnit.MicrophoneConnector.OwnerOutputChanged += new CbGeneric(MicrophoneConnector_OwnerOutputChanged);
            this.chatUnit.MicrophoneConnector.AudioDataReceived += new CbGeneric<byte[]>(MicrophoneConnector_AudioDataReceived);
            this.chatUnit.MicrophoneConnector.BeginConnect(unit.MemberID);
        }

        public void Initialize(string curUserID)
        {
            this.skinLabel_name.Text = curUserID;
            this.skinLabel_name.ForeColor = Color.Red;
            this.pictureBox_Mic.Visible = false;
            this.decibelDisplayer1.Visible = false;
        }

        void MicrophoneConnector_AudioDataReceived(byte[] data)
        {
            this.decibelDisplayer1.DisplayAudioData(data);
        }

        void MicrophoneConnector_OwnerOutputChanged()
        {
            if (this.InvokeRequired)
            {
                this.BeginInvoke(new CbGeneric(this.MicrophoneConnector_OwnerOutputChanged));
            }
            else
            {
                this.ShowMicState();
            }
        }

        private ConnectResult connectResult;
        void MicrophoneConnector_ConnectEnded(ConnectResult res)
        {
            if (this.InvokeRequired)
            {
                this.BeginInvoke(new CbGeneric<ConnectResult>(this.MicrophoneConnector_ConnectEnded), res);
            }
            else
            {
                this.connectResult = res;
                this.ShowMicState();
            }
        }

        public void Dispose()
        {
            this.chatUnit.Close();
        }

        private void ShowMicState()
        {
            if (this.connectResult != OMCS.Passive.ConnectResult.Succeed)
            {
                this.pictureBox_Mic.BackgroundImage = this.imageList1.Images[2];
                this.toolTip1.SetToolTip(this.pictureBox_Mic, this.connectResult.ToString());
            }
            else
            {
                this.decibelDisplayer1.Working = false;
                if (!this.chatUnit.MicrophoneConnector.OwnerOutput)
                {
                    this.pictureBox_Mic.BackgroundImage = this.imageList1.Images[1];
                    this.toolTip1.SetToolTip(this.pictureBox_Mic, "好友禁用了麦克风");
                    return;
                }

                if (this.chatUnit.MicrophoneConnector.Mute)
                {
                    this.pictureBox_Mic.BackgroundImage = this.imageList1.Images[1];
                    this.toolTip1.SetToolTip(this.pictureBox_Mic, "静音");
                }
                else
                {
                    this.pictureBox_Mic.BackgroundImage = this.imageList1.Images[0];
                    this.toolTip1.SetToolTip(this.pictureBox_Mic, "正常");
                    this.decibelDisplayer1.Working = true;
                }
            }

        }

        private void pictureBox_Mic_Click(object sender, EventArgs e)
        {
            if (!this.chatUnit.MicrophoneConnector.OwnerOutput)
            {
                return;
            }

            this.chatUnit.MicrophoneConnector.Mute = !this.chatUnit.MicrophoneConnector.Mute;
            this.ShowMicState();
        }
    }

(1)在代码中,ChatUnit就代表当前这个聊天室中的成员。我们使用其MicrophoneConnector连接到目标成员的麦克风。

(2)预定MicrophoneConnector的AudioDataReceived事件,当收到语音数据时,将其交给DecibelDisplayer去显示声音的大小。

(3)预定MicrophoneConnector的ConnectEnded和OwnerOutputChanged事件,根据其结果来显示SpeakerPanel空间上麦克风图标的状态(对应ShowMicState方法)。

3. MultiAudioChatContainer 控件

  MultiAudioChatContainer对应上图中2标注的控件,它主要做了以下几件事情:

(1)在初始化时,加入聊天室:通过调用IMultimediaManager的ChatGroupEntrance属性的Join方法。

(2)使用FlowLayoutPanel将聊天室中每个成员对应的SpeakerPanel罗列出来。

(3)当有成员加入或退出聊天室时(对应ChatGroup的SomeoneJoin和SomeoneExit事件),动态添加或移除对应的SpeakerPanel实例。

(4)通过CheckBox将自己设备(麦克风和扬声器)的控制权暴露出来。我们可以启用或禁用我们自己的麦克风或扬声器。

(5)注意,其提供了Close方法,这意味着,在关闭包含了该控件的宿主窗体时,要调用其Close方法以释放其内部持有的麦克风连接器等资源。

  在完成MultiAudioChatContainer后,我们这个聊天室的核心就差不多了。接下来就是弄个主窗体,然后把MultiAudioChatContainer拖上去,初始化IMultimediaManager,并传递给MultiAudioChatContainer就大功告成了。

三. 源码下载

  上面只是讲了实现多人语音聊天室中的几个重点,并不全面,大家下载下面的源码可以更深入的研究。

  AudioChatRoom.rar  

  最后,跟大家说说部署的步骤:

(1)将服务端部署在一台机器上,启动服务端。

(2)修改客户端配置文件中的ServerIP为刚才服务器的IP。

(3)在多台机器上运行客户端,以不同的帐号登录到同一个房间(如默认的R1000)。

(4)如此,多个用户就处于同一个聊天室进行语音聊天了。

  

时间: 2024-08-02 13:07:22

实现一个简单的语音聊天室(源码)的相关文章

asp.net利用socket实现简单多对多聊天室源码

因为在传输的时候服务器与客户端只以字节流的方式来完成数据的传输,那么我们就可以在不同的数据数里加一个标志.                   图为httpwatch捕获的http报文   这种思想来源自HTTP请求与响应报文,模拟HTTP请求与响应,我自己规定字节流里第一个字节{0:文字,1:文件,2:震动}.   但是这个时候有一个坏处,如果标识后期又要加一个功能,比如要求用户可能控制不同的文字大小,不同的文字与颜色.那样标志就越来越多了,传输的报文也越来越难封装和解析了(中间多了定义标识,

实现一个简单的视频聊天室(源码)

       在 <实现一个简单的语音聊天室>一文发布后,很多朋友建议我也实现一个视频聊天室给他们参考一下,其实,视频聊天室与语音聊天室的原理是差不多的,由于加入了摄像头.视频的处理,逻辑会繁杂一些,本文就实现一个简单的多人视频聊天系统,让多个人可以进入同一个房间进行语音视频沟通.先看看3个人进行视频聊天的运行效果截图:         上面两张截图分别是:登录界面.标注了各个控件的视频聊天室的主界面. 一. C/S结构 很明显,我这个语音聊天室采用的是C/S结构,整个项目结构相对比较简单,如

c++-请进!!如何用VC实现一个简单的语音聊天程序(C++)

问题描述 请进!!如何用VC实现一个简单的语音聊天程序(C++) 请问各位大神 如何实现一个简单的 语音聊天程序,需要用到哪些库,YY插件太多了,感觉开着YY太占内存和CPU了: 解决方案 http://download.csdn.net/detail/liuhuanxia84/2189833 解决方案二: http://download.csdn.net/detail/chen345/2834856 解决方案三: 一个简单的时钟程序<VC实现>

一个node+express+socket.io开发的聊天室源码

首先是下载包: npm install express npm install socket.io 建立文件:   开发的聊天室源码-express socket聊天室">   服务器端代码:server.js    代码如下 复制代码 var http=require("http"); var express=require("express"); var sio=require("socket.io"); var app=ex

Node.js+Socket.IO实现的WebSocket群聊天室源码

首先上图上实例 聊天室地址:http://chat.52itstyle.com WebSocket简介 谈到Web实时推送,就不得不说WebSocket.在WebSocket出现之前,很多网站为了实现实时推送技术,通常采用的方案是轮询 (Polling)和Comet技术,Comet又可细分为两种实现方式,一种是长轮询机制,一种称为流技术,这两种方式实际上是对轮询技术的改进,这些 方案带来很明显的缺点,需要由浏览器对服务器发出HTTP request,大量消耗服务器带宽和资源.面对这种状况,HTM

200分求:一个简单的聊天室源码(VS2005/C#),最好没有页面刷新的

问题描述 谢谢! 解决方案 解决方案二:http://search.download.csdn.net/search/%E8%81%8A%E5%A4%A9%E5%AE%A4%E3%80%80c%23

用三五天时间边学边用rails做了一个简单的在线聊天室。

问题描述 刚开始需要一个熟悉的过程,不过之后确实能体会到rails令人清爽的一面.地址是http://219.234.86.88:3001/源代码在googlecode.

一个简单的PHP投票程序源码_php实例

分析:  我们利用一个文件(data.dat)来存储投票栏目.每个栏目占据一行.  这样一来,便可随意加入和减去想要投票的栏目.  我们再利用一个文件(votes.dat)来存储我们的投票结果.  并纪录最近一位投票者的IP地址,简单的防止一人多投.  所以,您应该在该程序目录下自行建立两个文件data.dat和votes.dat  程序运行过程部分  程序运行时应该先有一个投票的HTML表单,等待投票.  当有人在HTML表单上投票时,判断它的IP是否为最近一位投票者的IP,  如果此人刚刚投

分享一个简单的 VS 插件及源码

今天做了一个 VS 的插件,给大家分享下.暂时有以下三个功能:   *添加文件头 在任意文本页面,点击该按钮,可添加文件头:  格式可以在插件对应目录下的 hxyAddIn_FileHeaderTemplate.xml 中修改: ,内容如下: <?xml version="1.0" encoding="utf-8" ?>  <FileHeaderTemplate>      <NowFormat>yyyyMMdd</NowF