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

 语音聊天室,或多人语音聊天,是即时通信应用中常见的功能之一,比如,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)如此,多个用户就处于同一个聊天室进行语音聊天了。

  

转载于:https://www.cnblogs.com/zxtceq/p/7278954.html

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本项目是即时通讯 APP,主要功能有群聊、私聊、朋友圈、可以发送文字、语音、视频、图片、表情、红包等。消息可以撤回, 群组可以设置管理员、可以禁言、踢人,能设置能否可查看客户信息,可@客户等,支持扫码入群,能设置好友备注、群名片、消息置顶、消息免打扰等,拥有代理商功能,vip 功能,签到功能,支持消息离线推送(ios 无需上架,Android 需要上应用商店) 后端可以设置客服和网站管理,可以通过后端给客户发消息,可以清除群聊天记录,也可以撤回客户消息,能设置具体的参数,可推荐具体的群等 本系统发送消息采用异步推送以及缓存技术,消息秒推送,即便服务器配置低,客户也感觉不到卡慢,图片浏览之后直接实时预览,无需等待上传。 APP 端历史聊天记录、图片以及前台程序缓存在本地,页面秒开,支持云端同步聊天记录,断网状态页面之间也可以切换,流畅性媲美原生。 技术路线: 后台开发语言:PHP (原生架构) 前台开发语言:uniapp socket 推送:GateWayWorker(支持分布式部署,高并发,抗压能力强,未采用第三方付费推送服务) 数据库:mysql 开发工具:hbuiderX,phpstorm 目前支持 android、ios,pc,h5,不提供 ios 签名、软著申请、上架服务等。 服务器配置 : 服务器操作系统: Linux Centos 7 软件环境: php 5.6 +Apache2.4+MySQL5.6 (推荐使用宝塔) 前台功能详情: 登录注册:客户名密码登录、注册、邀请注册,不记得密码(通过短信验证找回密码) 好友:可发文字、语音(仅限 APP)、图片、视频、表情、红包;图片可预览,支持缓存;内置浏览器可自动提取并打开超链接,单一的超链接可以提取到标题和简介; 群组:显示群聊信息、群聊二维码、管理员权限,可禁言 可撤回消息,可全局禁言也可单独禁言,能设置用能否可以查看客户信息,可@群友,能设置群名片、能设置能否可以发送超链接,能否可以修改昵称、可发群声明 消息:可置顶消息,能设置免打扰 联络人:可备注、可按客户名、昵称查找联络人和群组 消息助手:注册自动增加,无法删除,后端管理员可通过助手发送消息 客服:后端可以设置多个客服,注册之后随机增加或者者一律增加,客服有具体的标识 第三方网站:可嵌入多个第三方网站,后端能设置名称和链接,能设置在哪个端显示。 扫一扫:可扫第三方网站,可以扫码进群,可扫码加好友、扫码登录 朋友圈(仅限手机端):可发送文字、图片,可以点赞评论 个人设置:设置资料(头像、昵称、所在地、性别、个性签名等),修改密码,消息提示设置(响铃或者者震动)、银行卡绑定等。 代理商中心:设置邀请链接、邀请二维码,手动增加客户、管理查看我的团队成员等。 VIP 中心:VIP 分个人 VIP 和团队 VIP,个人 vip 仅限自己使用,团队 vip 可以给下级开通个人 vip 我的钱包:显示余额、充值、提现、账单流水。 APP 端消息离线推送:集成 unipush(个推),ios 无需上应用商店,但需要 push 的签名,Android 端离线推送需要上应用商店(申请软著并且域名备案) 后端功能详情: 系统设置:系统参数设置、APP 参数设置、vip 参数设置、充值充值提现设置、第三方网站设置、汇款账户设置等 客户管理:可查看、修改、删除客户信息、可给客户通过官方账号发私信、能设置具体的客服、可管理投诉信息 群组管理:查看群组的信息、可修改群组信息、可解散群组、可清空指定群组的聊天记录 聊天记录:聊天记录分个人聊天记录和群聊记录,可以查看客户的聊天记录、可删除 登录日志:显示每个客户的登录时间、ip、所在地 资金管理:充值管理、提现管理、账单等 角色管理:可以根据不同功可设置不同的角色 管理员管理:新添加、删除、修改管理员 操作日志:查看管理员操作日志 修改密码:修改当前登录账号的密码

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值