实现一个简单的语音聊天室(多人语音聊天系统)

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

本文将基于最新版本号的OMCS(V3.5)实现一个简单的语音聊天室,让多个人能够进入同一个房间进行语音沟通。当然,在此之前,您必须对OMCS有所了解,而且已经阅读、理解了OMCS
开发手冊(08) -- 多人语音/视频
这篇文章的内容。先看看Demo执行效果截图:

      
 

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

一. C/S结构

  非常明显,我这个语音聊天室採用的是C/S结构,整个项目结构相对照较简单,例如以下所看到的:

   该项目的服务端不须要编写不论什么代码,直接把OMCS服务端拿过来用;client就比較麻烦些,以下我们就重点讲client的开发。

二. client控件式开发

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

  如今我们分别介绍各个控件:

1. 分贝显示器

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

(1)傅立叶变换

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

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

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

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

2.发言者控件 SpeakerPanel

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

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

    public partial class SpeakerPanel : UserControl ,IDisposable
    {
        private IChatUnit 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(IChatUnit 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)在代码中,IChatUnit就代表当前这个聊天室中的成员。我们使用其MicrophoneConnector连接到目标成员的麦克风。

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

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

3. MultiAudioChatContainer 控件

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

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

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

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

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

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

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

三. 源代码下载

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

  AudioChatRoom.rar  

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

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

(2)改动client配置文件里的ServerIP为刚才server的IP。

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

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

时间: 2024-08-26 07:33:40

实现一个简单的语音聊天室(多人语音聊天系统)的相关文章

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

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

&#8203;下面为大家介绍一个运用自己电脑当成服务器,然后开发一个简单的php聊天室程序的方法:

上一期我们说过B/S技术开发聊天有什么优点,这一期我们就来简单的说说用C/S技术开发又有什么特点? 一.稳定性和灵活性:用C/S技术可以将应用和服务进行分离.二.安全性:C/S对应是的是结构模式,一般只适用于局域网,所以安全性比较好.三.速度快:客户端与服务器端是直接连接的,中间没有经过别的环节,所以响应速度非常快.四.升级维护复杂:如果软件需要升级维护,那么每一台客户的机子都要进行相应的升级维护服务,那么这个过程肯定是比较繁琐的.综上所述,对于不同的聊天室需要采用不同的开发技术,但是国内目前很

[NodeJS]使用Node.js写一个简单的在线聊天室

声明:教程来自<Node即学即用>.源代码案例均出自此书.博文仅为个人学习笔记. 第一步:创建一个聊天server. 首先,我们先来写一个Server: var net = require('net') var chatServer = net.createServer() chatServer.on('connection',function(client){ client.write('connection~~~\n') client.end() }) chatServer.listen(

一个简单的java聊天室

利用java Socket编写的群聊室,可以自己拷过去试试 Server端: package net3; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; import java.net.Socket

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

语音聊天室,或多人语音聊天,是即时通信应用中常见的功能之一,比如,QQ的语音讨论组就是我们用得比较多的. 这篇文章将实现一个简单的语音聊天室,让多个人可以进入同一个房间进行语音沟通.先看运行效果截图:         从左到右的三张图分别是:登录界面.语音聊天室的主界面.标注了各个控件的主界面. (如果觉得界面太丑,没关系,后面下载源码后,你可以自己美化~~) 一. C/S结构 很明显,我这个语音聊天室采用的是C/S结构,整个项目结构相对比较简单,如下所示: 该项目的底层是基于OMCS构建的.这

基于Server-Sent Event的简单聊天室 Web 2.0时代,即时通信已经成为必不可少的网站功能,那实现Web即时通信的机制有哪些呢?在这门项目课中我们将一一介绍。最后我们将会实现一个基于Server-Sent Event和Flask简单的在线聊天室。

基于Server-Sent Event的简单聊天室 Web 2.0时代,即时通信已经成为必不可少的网站功能,那实现Web即时通信的机制有哪些呢?在这门项目课中我们将一一介绍.最后我们将会实现一个基于Server-Sent Event和Flask简单的在线聊天室.

如何实现百万级的语音聊天室

上篇我们介绍了如何从零开始搭建一套语音聊天室后台,设计方案比较基础,本篇我们将介绍语音聊天室的升级版本——在海量用户同时在线的情况下,语音服务器的架构将如何升级改造. 互联网产品后台开发信奉一句话:先扛住再优化.工程师当然是希望把系统设计得尽善尽美,但是业务发展往往是不允许的,因此后台工程师的工作就是在技术和业务之间寻找平衡点.大部分的系统都是逐步迭代演进而来的,没有一蹴而就的完美系统. 前文中,我们介绍了语音服务器分SET部署的概念.其实一直在回避一个问题,分SET的缺点是什么? 分SET限制

用L脚本语言开发一个简单的局域网聊天程序

#scp #这是一个简单的局域网聊天程序的例子 定义:字符串,string1 定义:字符串,string2 #addr1是对方的地址 #addr2是自己的地址 #如果addr1和addr2相同,就是自己和自己聊天 定义:地址,addr1,127.0.0.1,27015 定义:地址,addr2,127.0.0.1,27015 定义:整数,字节数,0 #在自己的UDP端口上监听 定义:网络连接,conn2,UDP 监听:conn2,addr2 #连接对方的UDP端口 定义:网络连接,conn1,UD

语音聊天室可以用.net制作吗?

现在网络上很多有些想要自主制作语音聊天室软件的朋友在问是否可以用.net制作吗?从理论上说是可以的,但实际真正制作的时候肯定会有很多问题,而且关于这方面的资料也很少,杭州雅顾语音聊天室制作公司觉得目前还是应该用JAVA来制作语音聊天室. 因为单纯用.net功能没办法通过DHTML的能力实现,所以无论用什么语言都需要客户端加载一些本地代码,比如相应的组件(控件),ActiveX或 Java控件的运行环境很容易得到,但现在一般的机器上还没有装.NET,所以现在只能使用ActiveX或Java组件来做