最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C#基于UDP實(shí)現(xiàn)的P2P語(yǔ)音聊天工具

 更新時(shí)間:2022年03月27日 07:21:04   投稿:lijiao  
這篇文章主要是一個(gè)應(yīng)用,使用udp傳送語(yǔ)音和文本等信息。在這個(gè)系統(tǒng)中沒(méi)有服務(wù)端和客戶端,相互通訊都是直接相互聯(lián)系的,能夠很好的實(shí)現(xiàn)效果

語(yǔ)音獲取

要想發(fā)送語(yǔ)音信息,首先得獲取語(yǔ)音,這里有幾種方法,一種是使用DirectX的DirectXsound來(lái)錄音,我為了簡(jiǎn)便使用一個(gè)開源的插件NAudio來(lái)實(shí)現(xiàn)語(yǔ)音錄取。 在項(xiàng)目中引用NAudio.dll

//------------------錄音相關(guān)----------------------------- 
    private IWaveIn waveIn; 
    private WaveFileWriter writer; 
 
    private void LoadWasapiDevicesCombo() 
    { 
      var deviceEnum = new MMDeviceEnumerator(); 
      var devices = deviceEnum.EnumerateAudioEndPoints(DataFlow.Capture, DeviceState.Active).ToList(); 
      comboBox1.DataSource = devices; 
      comboBox1.DisplayMember = "FriendlyName"; 
    } 
    private void CreateWaveInDevice() 
    { 
 
      waveIn = new WaveIn(); 
      waveIn.WaveFormat = new WaveFormat(8000, 1); 
      waveIn.DataAvailable += OnDataAvailable; 
      waveIn.RecordingStopped += OnRecordingStopped; 
    } 
    void OnDataAvailable(object sender, WaveInEventArgs e) 
    { 
      if (this.InvokeRequired) 
      { 
        this.BeginInvoke(new EventHandler<WaveInEventArgs>(OnDataAvailable), sender, e); 
      } 
      else 
      { 
        writer.Write(e.Buffer, 0, e.BytesRecorded); 
        int secondsRecorded = (int)(writer.Length / writer.WaveFormat.AverageBytesPerSecond); 
        if (secondsRecorded >= 10)//最大10s 
        { 
          StopRecord(); 
        } 
        else 
        { 
          l_sound.Text = secondsRecorded + " s"; 
        } 
      } 
    } 
    void OnRecordingStopped(object sender, StoppedEventArgs e) 
    { 
      if (InvokeRequired) 
      { 
        BeginInvoke(new EventHandler<StoppedEventArgs>(OnRecordingStopped), sender, e); 
      } 
      else 
      { 
        FinalizeWaveFile(); 
      } 
    } 
    void StopRecord() 
    { 
      AllChangeBtn(btn_luyin, true); 
      AllChangeBtn(btn_stop, false); 
      AllChangeBtn(btn_sendsound, true); 
      AllChangeBtn(btn_play, true); 
 
      //btn_luyin.Enabled = true; 
      //btn_stop.Enabled = false; 
      //btn_sendsound.Enabled = true; 
      //btn_play.Enabled = true; 
      if (waveIn != null) 
        waveIn.StopRecording(); 
      //Cleanup(); 
    } 
 private void Cleanup() 
    { 
      if (waveIn != null) 
      { 
        waveIn.Dispose(); 
        waveIn = null; 
      } 
      FinalizeWaveFile(); 
    } 
    private void FinalizeWaveFile() 
    { 
      if (writer != null) 
      { 
        writer.Dispose(); 
        writer = null; 
      } 
    } 
  //開始錄音 
    private void btn_luyin_Click(object sender, EventArgs e) 
    { 
      btn_stop.Enabled = true; 
      btn_luyin.Enabled = false; 
      if (waveIn == null) 
      { 
        CreateWaveInDevice(); 
      } 
      if (File.Exists(soundfile)) 
      { 
        File.Delete(soundfile); 
      } 
 
      writer = new WaveFileWriter(soundfile, waveIn.WaveFormat); 
      waveIn.StartRecording(); 
    } 

上面的代碼實(shí)現(xiàn)了錄音,并且寫入文件p2psound_A.wav

語(yǔ)音發(fā)送

獲取到語(yǔ)音后我們要把語(yǔ)音發(fā)送出去

當(dāng)我們錄好音后點(diǎn)擊發(fā)送,這部分相關(guān)代碼是

MsgTranslator tran = null; 
ublic Form1() 
    { 
      InitializeComponent(); 
      LoadWasapiDevicesCombo();//顯示音頻設(shè)備 
 
      Config cfg = SeiClient.GetDefaultConfig(); 
      cfg.Port = 7777; 
      UDPThread udp = new UDPThread(cfg); 
      tran = new MsgTranslator(udp, cfg); 
      tran.MessageReceived += tran_MessageReceived; 
      tran.Debuged += new EventHandler<DebugEventArgs>(tran_Debuged); 
    } 
 private void btn_sendsound_Click(object sender, EventArgs e) 
    { 
      if (t_ip.Text == "") 
      { 
        MessageBox.Show("請(qǐng)輸入ip"); 
        return; 
      } 
      if (t_port.Text == "") 
      { 
        MessageBox.Show("請(qǐng)輸入端口號(hào)"); 
        return; 
      } 
      string ip = t_ip.Text; 
      int port = int.Parse(t_port.Text); 
      string nick = t_nick.Text; 
      string msg = "語(yǔ)音消息"; 
 
      IPEndPoint remote = new IPEndPoint(IPAddress.Parse(ip), port); 
      Msg m = new Msg(remote, "zz", nick, Commands.SendMsg, msg, "Come From A"); 
      m.IsRequireReceive = true; 
      m.ExtendMessageBytes = FileContent(soundfile); 
      m.PackageNo = Msg.GetRandomNumber(); 
      m.Type = Consts.MESSAGE_BINARY; 
      tran.Send(m); 
    } 
 private byte[] FileContent(string fileName) 
    { 
      FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read); 
      try 
      { 
        byte[] buffur = new byte[fs.Length]; 
        fs.Read(buffur, 0, (int)fs.Length); 
 
        return buffur; 
      } 
      catch (Exception ex) 
      { 
        return null; 
      } 
      finally 
      { 
        if (fs != null) 
        { 
 
          //關(guān)閉資源 
          fs.Close(); 
        } 
      } 
    } 

如此一來(lái)我們就把產(chǎn)生的語(yǔ)音文件發(fā)送出去了

語(yǔ)音的接收與播放

其實(shí)語(yǔ)音的接收和文本消息的接收沒(méi)有什么不同,只不過(guò)語(yǔ)音發(fā)送的時(shí)候是以二進(jìn)制發(fā)送的,因此我們?cè)谑盏秸Z(yǔ)音后 就應(yīng)該寫入到一個(gè)文件里面去,接收完成后,播放這段語(yǔ)音就行了。

下面這段代碼主要是把收到的數(shù)據(jù)保存到文件中去,這個(gè)函數(shù)式我的NetFrame里收到消息時(shí)所觸發(fā)的事件,在文章前面提過(guò)的那篇文章里

void tran_MessageReceived(object sender, MessageEventArgs e) 
    { 
      Msg msg = e.msg; 
 
      if (msg.Type == Consts.MESSAGE_BINARY) 
      { 
        string m = msg.Type + "->" + msg.UserName + "發(fā)來(lái)二進(jìn)制消息!"; 
        AddServerMessage(m); 
        if (File.Exists(recive_soundfile)) 
        { 
          File.Delete(recive_soundfile); 
        } 
        FileStream fs = new FileStream(recive_soundfile, FileMode.Create, FileAccess.Write); 
        fs.Write(msg.ExtendMessageBytes, 0, msg.ExtendMessageBytes.Length); 
        fs.Close(); 
        //play_sound(recive_soundfile); 
        ChangeBtn(true); 
 
      } 
      else 
      { 
        string m = msg.Type + "->" + msg.UserName + "說(shuō):" + msg.NormalMsg; 
        AddServerMessage(m); 
      } 
    } 

收到語(yǔ)音消息后,我們要進(jìn)行播放,播放時(shí)仍然用剛才那個(gè)插件播放

//--------播放部分---------- 
    private IWavePlayer wavePlayer; 
    private WaveStream reader; 
 
    public void play_sound(string filename) 
    { 
      if (wavePlayer != null) 
      { 
        wavePlayer.Dispose(); 
        wavePlayer = null; 
      } 
      if (reader != null) 
      { 
        reader.Dispose(); 
      } 
      reader = new MediaFoundationReader(filename, new MediaFoundationReader.MediaFoundationReaderSettings() { SingleReaderObject = true }); 
 
      if (wavePlayer == null) 
      { 
 
        wavePlayer = new WaveOut(); 
        wavePlayer.PlaybackStopped += WavePlayerOnPlaybackStopped; 
        wavePlayer.Init(reader); 
      } 
      wavePlayer.Play(); 
    } 
    private void WavePlayerOnPlaybackStopped(object sender, StoppedEventArgs stoppedEventArgs) 
    { 
      if (stoppedEventArgs.Exception != null) 
      { 
        MessageBox.Show(stoppedEventArgs.Exception.Message); 
      } 
      if (wavePlayer != null) 
      { 
        wavePlayer.Stop(); 
      } 
      btn_luyin.Enabled = true; 
    }private void btn_play_Click(object sender, EventArgs e) 
    { 
      btn_luyin.Enabled = false; 
      play_sound(soundfile); 
    } 

在上面演示了接收和發(fā)送一段語(yǔ)音消息的界面

技術(shù)總結(jié)

主要用到的技術(shù)就是UDP和NAudio的錄音和播放功能

希望這篇文章能夠給大家提供一個(gè)思路,幫助大家實(shí)現(xiàn)P2P語(yǔ)音聊天工具。

相關(guān)文章

  • Unity實(shí)現(xiàn)坦克模型

    Unity實(shí)現(xiàn)坦克模型

    這篇文章主要為大家詳細(xì)介紹了Unity實(shí)現(xiàn)坦克模型,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • C#自定義控件VS用戶控件

    C#自定義控件VS用戶控件

    這篇文章主要為大家詳細(xì)介紹了C#中自定義控件VS用戶控件的區(qū)別,介紹了開發(fā)自己的控件的幾種方法、自定義控件與用戶控件區(qū)別,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-12-12
  • C#函數(shù)out多個(gè)返回值問(wèn)題

    C#函數(shù)out多個(gè)返回值問(wèn)題

    這篇文章主要介紹了C#函數(shù)out多個(gè)返回值問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • C#實(shí)現(xiàn)接口base調(diào)用示例詳解

    C#實(shí)現(xiàn)接口base調(diào)用示例詳解

    這篇文章主要為大家介紹了C#實(shí)現(xiàn)接口base調(diào)用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • C#使用IronPython調(diào)用python代碼的實(shí)現(xiàn)示例

    C#使用IronPython調(diào)用python代碼的實(shí)現(xiàn)示例

    本文主要介紹了在C#中使用IronPython調(diào)用Python代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2025-01-01
  • c#模擬平拋運(yùn)動(dòng)動(dòng)畫的方法詳解

    c#模擬平拋運(yùn)動(dòng)動(dòng)畫的方法詳解

    本篇文章是對(duì)使用c#模擬平拋運(yùn)動(dòng)動(dòng)畫的方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-06-06
  • C#連接MySQL操作詳細(xì)教程

    C#連接MySQL操作詳細(xì)教程

    這篇文章主要為大家詳細(xì)介紹了C#連接MySQL操作詳細(xì)教程,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-03-03
  • 基于WPF實(shí)現(xiàn)彈幕效果的示例代碼

    基于WPF實(shí)現(xiàn)彈幕效果的示例代碼

    這篇文章主要為大家詳細(xì)介紹了如何利用WPF實(shí)現(xiàn)彈幕效果,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)或工作有一定幫助,感興趣的小伙伴可以了解一下
    2022-09-09
  • 詳解Unity日志打印工具功能

    詳解Unity日志打印工具功能

    Unity是一個(gè)非?;鸬挠螒蛞妫覀?cè)匍_發(fā)的時(shí)候有的時(shí)候要進(jìn)行程序的調(diào)試,下面給大家介紹下Unity日志打印工具功能,感興趣的朋友一起看看吧
    2022-01-01
  • C#數(shù)據(jù)庫(kù)操作的示例詳解

    C#數(shù)據(jù)庫(kù)操作的示例詳解

    這篇文章主要通過(guò)一些示例為大家詳細(xì)介紹了C#中數(shù)據(jù)庫(kù)操作用法,文中的示例代碼講解詳細(xì),具有有一定的借鑒價(jià)值,需要的可以參考一下
    2022-07-07

最新評(píng)論

外汇| 泌阳县| 南宁市| 库尔勒市| 花莲市| 定远县| 双辽市| 禄劝| 延边| 左权县| 宁晋县| 宁晋县| 河池市| 武城县| 开原市| 彭山县| 长乐市| 双城市| 江孜县| 集安市| 额敏县| 松原市| 江山市| 克拉玛依市| 阿合奇县| 石家庄市| 淮安市| 封丘县| 湄潭县| 新源县| 珠海市| 汉中市| 武山县| 扎兰屯市| 从江县| 汉沽区| 曲麻莱县| 明星| 闽侯县| 敦煌市| 内丘县|