zoukankan      html  css  js  c++  java
  • 实现一个简单的语音聊天室(多人语音聊天系统)

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


  • 相关阅读:
    sqlserver2012——.Net
    sqlserver2012——逻辑运算符
    sqlserver2012——变量declare
    sqlserver2012——EXCEPT差查询
    sqlserver2012——INTERSECT交查询
    sqlserver2012——EXISTS关键字
    Vue3.0-beta
    Next
    Nuxt
    小程序相关
  • 原文地址:https://www.cnblogs.com/gcczhongduan/p/4197126.html
Copyright © 2011-2022 走看看