語音聊天室
{
\"code\": 200,
\"title\": \"\",
\"content\": \"1雙方之間的網絡連接要進行頻數據的傳輸,首先就是要建立數據連結。常用的通訊協議中,TCP較可靠,所以用在不允許數據丟失的應用上。而UDP則較多應用於處理速度要求較快、數據傳輸可靠性要求不是很高的應用上,如數據廣播。通訊協議的選擇取決於我們所要做的應用的類型。怎樣建立網絡連接,穩定的接收和發送音頻信號的數據流是關鍵。\\n\\n在網絡上傳輸音頻的方麵存在的問題主要可以歸納為以下幾點:\\n\\n音頻信號的采集以及回放\\n\\n在進行音頻信號的采集中我們必須考慮到采樣率的問題,聲音信號的采樣率有8Khz、16Khz、32Khz、44Khz等,每種數據采樣慮產生的數據量都不一樣,越高的采樣率產生的數據量越大,所以我們要選擇合適的采樣率以適應網絡的帶寬。\\n\\n音頻數字信號的編碼與解碼。\\n\\n語音聊天室\\n\\n如果把直接采集到的音頻信號數據流在網絡上進行傳輸,它所占有的帶寬也\\n\\n語音聊天室是十分大的,以8Khz的采樣率采\\n\\n集14位的音頻數據那麼就有以下這樣的一個式子:\\n\\n從中我們可以看出以這樣的方式傳輸音頻數據,每秒需要向網絡中發送112kb的數據。所以。從節省帶寬的角度考慮,我們很有必要對這樣的數據進行壓縮。對多媒體信號的壓縮我們有許多可以選擇的格式,如mp2、mp3、G**等等。同樣,我們這裡也存在一個對壓縮格式進行選擇的問題,考慮到音頻數據傳輸的及時性,對傳輸的音頻數據質量的要求,以及各種壓縮格式的壓縮比率以及進行壓縮和解壓縮所要耗費的係統資源等方麵問題,選擇合適的壓縮格式就顯得尤為重要。\\n\\n解決方法\\n\\n下麵就針對前麵提出的問題討論一下解決的辦法\\n\\n雙方之間的網絡連接\\n\\n在這方麵有其獨特的優勢,Java提供了豐富的網絡類庫的支援,可以輕鬆編寫多種類型的網絡通訊程式。在我下麵的例子中我就使用了TCP\\/IP協議,通過Java的Socket類進行編程2音頻信號的采集和回放以及音頻數字信號的編碼與解碼 在解決這兩個問題的時候,在網上很幸運地通過一些文章的介紹,找到了AnswerMachine演示程式的源代碼(由ofj的FlorianBomers和MatthiasPfisterer編寫,網址在這個程式代碼中,有幾個解決我們問題所需要的類,而且作者將這些類封裝的很好,我們基本不需要做什麼改動,隻需要遮蔽其中的調試資訊的輸出就行了,更可貴的是它還封裝了幾種常見的音頻格式。其中的G**格式(GlobalSystemforMobileTelecommunications)就是我們下麵例子中采用的壓縮格式,G**格式可以將128kbps的音頻數據流(16bit通過8kHz的音頻采樣)壓縮為13kbps的音頻數據流,非常適合語音信號的傳送,所以可謂是一石二鳥。\\n\\n語音聊天室\\n\\n1、考米網\\n\\n“考米網”是深圳市盈華訊方通訊技術有限公司領先推出的能夠電話聊天交友的網站,在網絡交友的基礎上增添電話聊天,提供一對一通話、多人通話、二人電話約會、自建會議聊天室、電話中發送的語音短訊,設置個性化個人資料等十幾種功能。在不方便使用電腦的時候,也能通過手機、電話撥打400熱線,隨時和網上的好友保持聯絡。\\n\\n2、YY聊天\\n\\n是一種YY團隊語音工具,是多玩遊戲網針對中文用戶設計的多人語音群聊工具。它是一款免費語音軟件,穩定清晰的語音工具,用於遊戲玩家交流等。即時通話,是一款不錯的通訊軟件。\\n\\n3、ISpeak\\n\\nISpeak娛樂互動平台(簡稱IS),是國內專業遊戲語音服務商,擁有上千萬註冊用戶,每天活躍用戶上百萬,公會頻道近10萬個的規模,已經成為國內一流的語音互動平台。\\n\\nIS以網絡遊戲用戶為基礎,提供專業的語音、聊天、社區論壇等。為用戶提供免費的語音頻道和無償服務;為公會提供一個更好的實現自我平台,促進和扶持公會的成長;為廠商提供一個遊戲推廣,服務玩家的超大型互動娛樂社區。\\n\\n4、QQ語音\\n\\n騰訊公司一直為使用QQ的用戶提供語音聊天服務。其語音聊天的缺點是通話質量差,隻能電腦對電腦傳播,所以一直冇有大範圍傳播開。\\n\\n源代碼分析\\n\\n我分析過這幾個類的源代碼,不得不佩服它的作者,每個類的源代碼都很精煉,大家可以自己分析一下。好了下麵就給大家講講這幾個類,並且將它們用到的JavaSoundAPI中的類和函數等一併做個簡單介紹,讓大家對JavaSoundAPI中常用的類也有個大致的瞭解。由於JavaSoundAPI中的類比較多。限於篇幅無法對所有用到的類做詳儘的解釋,以下內容隻是簡單提及了各個類的用途和使用規範,有關JavaSoundAPI中類的具體介紹請大家訪問這裡[url=http:\\/\\/java.\\/j2se\\/1.4.2\\/docs\\/api\\/]查詢javax.sound.sampled的相關內容。\\n\\n以下的提到幾個檔案是從AnswerMachine演示程式的源代碼中提取出來的,由於是開放源代碼的程式,大家在使用的時候請注意相關的\\n\\n公共協議\\n\\n①AMAudioFormat類\\n\\nAMAudioFormat類(封裝在AMAudioFormat.java檔案中)\\n\\nAMAudioFormat類封裝了CD、FM、TELEPHONE、G**這四種質量的音頻格式的參數,使用起來也非常簡單,這樣我們在使用JavaSoundAPI時就不用自己去寫那些複雜的代碼了,但為了明白JavaSoundAPI的原理,我們需要對它的代碼做一下分析。它使用了JavaSoundAPI中的AudioFormat這個類,這個類非常重要,在Java中對任何音頻數據的使用都要實現通過它指定所需要使用的音頻格式,AudioFormat類有一個巢狀的類AudioFormat.Encoding,實際上大部分對AudioFormat類的使用都是使用的這個巢狀的類。\\n\\nAMAudioFormat類的重要方法:\\n\\n名稱:getLineAudioFormat\\n\\n調用格式:getLineAudioFormat(整型音頻格式代號)\\n\\n返回值:根據傳遞音頻格式代號生成的AudioFormat對象。\\n\\n說道這裡大家可能要問了,那麼通過JavaSoundAPI可以直接使用G**格式嗎?答案是比較複雜,但同樣有解決的辦法,作者在這裡使用了另外的開源程式的類庫-tritonus的G**編碼解碼庫。大家需要在這裡下載tritonous_share.jar和tritonus_gsm.jar兩個檔案,並在AMAudioFormat類中引用,這樣就完成了G**格式的設置。需要告訴大家的是在對AMAudioFormat.java這個類進行編譯後,我們的程式運行的時候就可以不需要tritonous_share.jar和tritonus_gsm.jar這兩個檔案的支援了。\\n\\n②AudioCapture類\\n\\n(封裝在AudioCapture.java檔案中)2\\n\\nAudioCapture類封裝了從音頻硬體捕獲音頻數據並自動編碼為G**音頻壓縮數據的過程,並且通過它的getAudioInputStream()方法提供給我們一個音頻數據輸入流,我們就可以直接將這個流發送到網絡中。\\n\\nAudioCapture類的重要方法:\\n\\n名稱:getAudioInputStream\\n\\n調用格式:getAudioInputStream()\\n\\n返回值:AudioInputStream對象\\n\\nAudioCapture類使用了JavaSoundAPI中的AudioInputStream、AudioFormat、AudioSystem這幾個類和TargetDataLine、LineListener介麵。除了AudioFormat類我再簡單介紹一下其他的類:\\n\\nAudioInputStream類是帶有特殊音頻格式和長度的InputStream類,它有兩個構造方法,分彆是AudioInputStream(InputStreamstream,AudioFormatformat,longlength)和AudioInputStream(TargetData-Lineline)。\\n\\n視頻語音聊天室\\n\\nTargetDataLine介麵是DataLine介麵的一種,通過它就可以直接從音\\n\\n視頻語音聊天室頻\\n\\n硬體獲取數據了,它有幾個常用的方法,分彆是:open(AudioFormatformat)、voidopen(AudioFormatformat,intbufferSize)、intread(byte[]b,intoff,intlen)。\\n\\nAudioSystem類是Java標準音頻係統的入口點,在AudioSystem類中使用他的getLine()方法創建TargetDataLine對象。\\n\\nLineListener介麵用來對線路狀態改變的時間進行監聽,他的重要的方法是update(LineEventevent)方法。\\n\\n③AudioPlayStream類\\n\\n(封裝在AudioPlayStream.java檔案中)\\n\\nAudioPlayStream類與AudioCapture類剛好相反,它封裝了G**壓縮音頻數據的解碼和音頻信號的回放過程,提供給我們一個音頻信號輸出流。AudioCapture類用到的JavaSoundAPI中的類它也基本都用到了,隻是它使用了SourceDataLine介麵而不是TargetDataLine介麵\\n\\n④Debug類\\n\\n(封裝在Debug.java檔案中)\\n\\nDebug類主要用來在調試時輸出訊息,代碼很少,後來我把其中輸出資訊的語句都遮蔽了,對程式運行冇有影響。\\n\\n為了方便使用以上的幾個類,我們需要對它們進行編譯和打包,編譯時需要設置相關的編譯環境,以下是我們需要用到的命令列\\n\\n說明一下,我將以上提到的Java源碼檔案放在了am目錄下,編譯之後可以得到一個8k的am.jar檔案,我們下一步所需要做的就是在我們的程式中引用這個包。\\n\\n實例介紹\\n\\n有了以上的基本的介紹,我就可以通過對我寫的一個極為簡單的語音對講軟件代碼的解釋讓大家更清楚地瞭解一下這幾個模塊的具體使用方法,大家可以從中獲得開發具有諸如網絡電話、自動應答等功能的軟件的類似方法,用於語音數據的傳輸。\\n\\n程式的結構\\n\\n整個程式分三層,作用分彆如下:\\n\\n.頂層:用戶介麵\\n\\n.中間層:控製層\\n\\n.底層:傳輸層\\n\\n程式有兩個主要的類:(表)\\n\\n類名描述\\n\\nCallLink網絡傳輸層,用於接收或發送音頻數據。\\n\\nVoiceSender作為第二個啟動的線程提供從音頻硬體捕獲並編碼好的數據給網絡傳輸層。\\n\\n程式的主類jphone使用了Runnable和ActionListener介麵,主類除了基本的幾個方法之外,還具有方法initAudioHardware()、ShowMSG、startPhone分彆用於初始化AudioCapture類與AudioPlayStream類、顯示程式狀態和開始程式。主類jphone具有兩個子類VoiceSender和CallLink。\\n\\n子類VoiceSender同樣使用了Runnable介麵,它在程式中作為第二個啟動的線程負責發送捕獲到的音頻數據。CallLink子類就是負責建立scoket連接,並且負責接收或發送網絡數據、監聽網絡連接等功能的實現。它具有主要的方法是getInputStream()、getOutputStream()、listen()、open()、close()等。\\n\\n為了讓大家更清楚的瞭解程式的結構請大家看下麵的類圖。\\n\\n程式工作流程\\n\\n當程式啟動時首先執行建立當前主類的實例,當按下呼叫按鈕的時候執行startPhone()方法,startPhone()方法通過調用initAudioHardware()方法建立AudioCapture對象和AudioPlayStream對象的實例PhoneMIC和PhoneSPK,緊接著在建立CallLink子類的實例curCallLink來與具有目標IP地址的計算機進行scoket連接後,startPhone()方法又將子類VoiceSender作為secondThread線程啟動,然後又調用run()方法。run()方法通過已經建立的CallLink子類的實例curCallLink監聽網絡上的數據(也就是等待彆人的呼叫),一旦有音頻數據到來curCallLink實例就為AudioPlayStream對象PhoneSPK提供網絡傳來的音頻數據,而PhoneSPK在一個循環中不斷的將音頻數據轉換為音頻信號,完成類似電話聽筒的功能。\\n\\n子類VoiceSender就作為第二線程啟動的時候,startPhone()方法傳遞給它的參數是實例化的CallLink子類curCallLink,子類VoiceSender通過實例化的AudioCapture對象PhoneMIC將音頻信號壓縮成G**數據,並通過curCallLink將音頻數據發送到具有目標IP地址的計算機上,完成類似電話受話器的功能。\\n\\n在這裡實例化的CallLink子類curCallLink就相當於兩個電話之間的電話線,這樣通過我以上的解釋大家對程式的原理就有一個大概的瞭解了吧。\\n\\n其中的音頻數據發送線程和音頻數據接收線程是同步的,不過考慮到網絡的因素,可能在聲音的傳輸上有一些延遲,不過由於延遲比較小對及時聽到對方的話語影響不大。\\n\\n編寫代碼摘要\\n\\n在使用AudioCapture類和AudioPlayStream類的方法之前需要知道怎樣初始化這兩個類。在聲明AudioCapture對象的時候需要傳遞給它一個靜態的整型值用於表達將音頻信號壓縮的方式,這個靜態的整型常量可以是AMAudioFormat類的以下四個值之一:FORMAT_CODE_CD、FORMAT_CODE_FM、FORMAT\\n\\n所以聲明AudioCapture對象就要用一下的形式:\\n\\nprivateAudioCapturePhoneMICnull;\\n\\nPhoneMICnewAudioCapture\\n\\nFORMAT_CODE_G**);\\n\\n而聲明AudioPlayStream對象則不同,我們在初始化它的時候需要傳遞給它一個AudioFormat對象,用於通知它我們所要播放音頻的格式,這個AudioFormat對象可以通過AMAudioFormat類的getLineAudioFormat(格式參數值)方法獲得,其中格式參數的取值和上麵提到過的AMAudioFormat的四個值相同,所以聲明AudioPlayStream對象就要用以下的形式:\\n\\nprivateAudioPlayStreamPhoneSPKnull;\\n\\n在這之後就可以使用AudioCapture和AudioPlayStream對象的open()方法打開音頻捕獲和音頻回放通道完成它們的初始化了。如以下的形式:\\n\\nPhoneMIC.open();\\n\\nPhoneSPK.open();\\n\\n初始化完成之後要使AudioPlayStream對象播放聲音還需要以下過程,首先建立一個緩衝區(字節數組)用於存放從網絡傳來的音頻數據流,然後執行AudioPlayStream對象的start()方法使AudioPlayStream\\n\\n對象開始聲音的回放,這時執行一個while循環,在循環中將音頻流數據寫入緩衝區,再使用\\n\\n語音聊天\\n\\nAudioPlayStream對象的write()方法將緩衝區的數據還原成語音信號然後\\n\\n語音聊天\\n\\n播放出來。如下麵的例子:\\n\\n其中complete的值用於標誌終止聲音播放的異常原因。\\n\\n類似的,初始化完成之後要使AudioCapture對象捕獲和壓縮聲音數據還需要其他的操作,首先聲明一個InputStream對象,賦其值為AudioCapture對象的getAudioInputStream()方法的返回值,執行\\n\\n對象的start()方法,然後在建立一個循環,將通過InputStream的read()方法得到的數據發送到網絡上。例如以下代碼:\\n\\nsendStream.write(compressedVoice,0,b);\\n\\n......\\n\\n通過使用CallLink的幾個方法,我們可以方便的傳輸和接收音頻數據流。以下是它的代碼:\\n\\n\\/\\/使用套接字進行連接\\n\\nvoidopen()throwsIOException,UnknownHostException\\n\\n\\/\\/打開網路連接\\n\\n\\/\\/監聽,等候呼叫\\n\\ninServSocknewServerSocket(TALK_PORT);\\n\\ninSockinServSock.accept();\\n\\npublicInputStreamgetInputStream()throwsIOException\\n\\n\\/\\/返迴音頻數據輸入流\\n\\nHif(inSock!=null)\\n\\nreturninSock.getInputStream();\\n\\nelse\\n\\nreturnnull;\\n\\npublicOutputStreamgetOutputStream()throwsIOException\\n\\n\\/\\/返迴音頻數據輸出流\\n\\nif(outSock!=null)\\n\\nreturnoutSock.getOutputStream();\\n\\nelse\\n\\nreturnnull;\\n\\nvoidclose()throwsIOException\\n\\n\\/\\/關閉網絡連接;\\n\\ninSock.close();\\n\\noutSock.close();\\n\\n程式的代碼總體有366行,限於篇幅,這裡就不一一列舉了。\\n\\n編譯以及程式的使用方法:\\n\\n運行和編譯本程式需要加上額外的環境參數,為了方便使用可以建立以下內容的批處理檔案:(假設程式所需要的包均在程式所在目錄下的lib檔案夾中)\\n\\n用於編譯的批處理程式c.bat的內容\\n\\njavac-classpath.;lib\\\\am.jarjphone.java\\n\\n用於運行的批處理程式r.bat的內容\\n\\njava-classpath.;lib\\\\am.jarjphone\\n\\n啟動時在A計算機的IP地址框內輸入要進行連接的計算機B的IP地址,在計算機B的IP地址框內輸入要進行連接的計算機A的IP地址,讓後分彆點擊“撥出電話”按鈕就可以進行連接了。當然彆忘了接上麥克風和打開音箱電源,嗬嗬。\\n\\n提醒大家,這裡的IP地址欄裡預先存在的地址是127.0.0.1,也就是說,程式也可以和自己進行連接,點擊“撥出電話”按鈕,等8秒左右敲敲你的麥克風,聽到冇有,是不是也有“嘣、嘣、嘣”的聲音??\\n\\n\"
}