版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
用Java實(shí)現(xiàn)音頻播放一、JavaSound的體系結(jié)構(gòu)二、 JavaSound混頻原理三、 音頻數(shù)據(jù)與存儲(chǔ)格式四、設(shè)計(jì)音樂播放器五、播放音樂六、支持更多的音頻格式桌面PC的性能日益提高,Java虛擬機(jī)的優(yōu)化技術(shù)也不斷獲得突破,這一切使得用Java處理實(shí)時(shí)信號(hào)成為可能。本文將通過設(shè)計(jì)和構(gòu)造一個(gè)支持實(shí)時(shí)MP3、WAV和Ogg音頻格式解碼/回放的Java音樂播放器,闡述用JavaSoundAPI編寫音頻處理程序的思路和一般過程。JavaSound是一個(gè)小巧的低層API,支持?jǐn)?shù)字音頻和MIDI數(shù)據(jù)的記錄/回放。在JDK1.3.0之前,JavaSound是一個(gè)標(biāo)準(zhǔn)的Java擴(kuò)展API,但從Java2的1.3.0版開始,JavaSound就被包含到JDK之中。由于Java有著跨平臺(tái)(操作系統(tǒng)、硬件平臺(tái))的特點(diǎn),基于JavaSound的音頻處理程序(包括本文的程序)能夠在任何實(shí)現(xiàn)了Java1.3+的系統(tǒng)上運(yùn)行,無需加裝任何支持軟件。一、JavaSound的體系結(jié)構(gòu)當(dāng)前JDK的JavaSoundAPI隨同Java媒體框架(JMF,JavaMediaFramework)一起發(fā)布,主頁在/products/java-media/jmf/,適合JDK1.1以及更高的版本。除了JDK實(shí)現(xiàn)的JavaSoundAPI之外,還有一個(gè)源代碼開放的JavaSound實(shí)現(xiàn)是Tritonus,主頁在http://www.triton/。圖一描述了JavaSoundAPI的體系結(jié)構(gòu),虛線表示Sun的JavaSound標(biāo)準(zhǔn)定義的API調(diào)用。上面一根虛線表示我們編寫音頻處理程序要調(diào)用的API,JavaSoundAPI包含在javax.sound.sampled和javax.sound.midi包中。兩根虛線之間的部分就是JavaSoundAPI的具體實(shí)現(xiàn)。S~FJavaSound
的應(yīng)用軟件JavaSoundSPISPIS~FJavaSound
的應(yīng)用軟件JavaSoundSPISPI插件「檢啓字||l電礙卅裁論||遏輯^―^―^―^―—Java.SoundA.PI ^―■JavaSound的實(shí)現(xiàn)(SunfttlJDK1.3+/JVM,Tntonus等)圖一:JavaSound體系結(jié)構(gòu)就象上面一根虛線表示的API具有統(tǒng)一標(biāo)準(zhǔn)一樣,在所有的JavaSound實(shí)現(xiàn)中,圖一下面一根虛線表示的SPI(服務(wù)提供者接口,ServiceProviderInterface)也是統(tǒng)一的。SPI的作用是以插件(Plug-In)的形式提供自定義的擴(kuò)展模塊,我們只要提供與SPI兼容的插件擴(kuò)展模塊,就可以在不改變API的情況下擴(kuò)展音頻處理程序的能力。SPI包含在java.sound.sampled.spi和javax.sound.midi.spi包中。例如,假設(shè)有一個(gè)只能播放WAV文件的程序,我們只要增加一個(gè)支持MP3文件解碼的插件模塊,就可以在不改動(dòng)播放程序的任何一行代碼的前提下,為這個(gè)播放程序添加播放MP3的能力。二、JavaSound混頻原理圖二闡述了JavaSound的混頻器原理。在處理輸入音頻的應(yīng)用中,對(duì)于來自各種音頻輸入端口的信號(hào),例如麥克風(fēng)、CD播放器、磁帶播放器,等等,我們可以在它們到達(dá)TargetDataLine之前,利用混頻器控制輸入混頻,最后在程序中通過TargetDataLine獲得數(shù)字化的音頻輸入流。圖二:JavaSound混頻器類似地,在處理輸出音頻的應(yīng)用中,混頻器用來對(duì)一系列來自SourceDataLine的數(shù)據(jù)進(jìn)行混頻處理,經(jīng)處理后的信號(hào)可輸出到各種輸出端口,例如揚(yáng)聲器、耳機(jī)等。SourceDataLine是一個(gè)可寫入音頻信號(hào)數(shù)字流的設(shè)備,例如,我們可以從一個(gè)WAV文件讀取內(nèi)容寫入到SourceDataLine,然后再通過揚(yáng)聲器輸出。輸入到混頻器的信號(hào)可以來源于剪輯。剪輯(Clip)是一個(gè)包含一段完整音頻數(shù)據(jù)流的設(shè)備,或者說,剪輯就是一個(gè)緩沖在內(nèi)存中的完整音頻數(shù)據(jù)流。在一些要求反復(fù)播放音樂片段的場合,例如游戲的背景音樂,剪輯是很有用的。圖三描述了JavaSoundAPI中一些常用的類、接口及其關(guān)系,所有圖三顯示的類、接口都通過Line這個(gè)基本接口統(tǒng)一起來。Line接口用來關(guān)閉/打開設(shè)備、注冊(cè)事件監(jiān)聽器,以及提供一些用來調(diào)整聲音效果的對(duì)象,例如調(diào)整音量大小的對(duì)象。AudioSystem在JavaSound體系中起著一個(gè)工廠(Factory)類的作用,提供了一系列的靜態(tài)方法,我們通過這些靜態(tài)方法來獲取JavaSound系統(tǒng)默認(rèn)配置的資源(所謂靜態(tài)方法,就是可以在不創(chuàng)建AudioSystem實(shí)例的情況下直接調(diào)用的方法)。圖三:常用的JavaSound類順便說明一下,在當(dāng)前(JDK1.4)實(shí)現(xiàn)的JavaSound的默認(rèn)配置中,輸入聲音來自本地聲卡的麥克風(fēng),輸出聲音到本地聲卡的揚(yáng)聲器。應(yīng)當(dāng)說當(dāng)前實(shí)現(xiàn)的JavaSound對(duì)端口和混頻器的支持還不完善,但對(duì)于包括本文音樂播放器在內(nèi)的許多應(yīng)用來說,默認(rèn)實(shí)現(xiàn)的JavaSound配置已經(jīng)足夠了。三、音頻數(shù)據(jù)與存儲(chǔ)格式取樣得到的音頻數(shù)據(jù)——也就是從TargetDataLine輸入或從SourceDataLine輸出的數(shù)據(jù),必須符合音頻格式的標(biāo)準(zhǔn)。音頻數(shù)據(jù)的格式選項(xiàng)由AudioFormat類封裝,主要選項(xiàng)包括:編碼方式,可以是PCM(PulseCodeModulation,脈沖編碼調(diào)制)、MP3等;通道數(shù)量;取樣率;幀速率;等等。音頻數(shù)據(jù)可以用多種格式保存到磁盤上。在JavaSound參考實(shí)現(xiàn)中,直接支持的文件格式包括WAV(Windows)、AIFF(主要用于Apple的Macintosh)以及AU(主要用于UNIX),音頻文件的格式由AudioFileFormat類指定。并非所有音頻數(shù)據(jù)格式都可以保存到任意音頻文件格式(或從音頻文件回放),具體由平臺(tái)和操作系統(tǒng)的類型決定。為簡單計(jì),本文的播放器只考慮包含PCMMono或Stereo數(shù)據(jù)的WAV文件,這是當(dāng)前流行的音頻數(shù)據(jù)/文件格式組合,常用于CD音質(zhì)的音頻數(shù)據(jù)。壓縮的音頻數(shù)據(jù)(例如MP3和OggVorbis)通常有各自特殊的存儲(chǔ)格式(如.MP3和.OGG),通常不以WAV/AIFF/AU格式存儲(chǔ)。四、設(shè)計(jì)音樂播放器我們要編寫的音樂播放器(圖四)由表一所示的幾個(gè)類構(gòu)成。鑒于構(gòu)造用戶界面往往需要大量的代碼,且這些代碼通??梢杂肐DE自動(dòng)生成,所以下文只對(duì)一些關(guān)鍵的GUI元素略作介紹,不再給出完整的代碼。
圖四:播放器的用戶界面播放器的用戶界面主要由一個(gè)帶菜單的JFrame框架、一個(gè)名稱為filenamesList的JList和幾個(gè)JButton構(gòu)成??蚣苡幸粋€(gè)私有的TestBase成員,其實(shí)例在GUIInit()方法的末尾通過pBase=newTestBase()語句初始化。表衛(wèi)類的名稱4說明4應(yīng)用程序創(chuàng)建并顯示GUIoP播皴器的GUL讀取一個(gè)配苴文件,掃描特定的目錄查找可播飲的文件,在GUI的列表中顯示出這吐文件-借助恣類的幫助管理GUIor包含與聲音播抜有關(guān)的所有代碼邏輯-它用一亍專用的線程來播啟聲音,因此在播頑聲音的同時(shí),用戶仍可使用GUI例如點(diǎn)擊各種控制按鈕-衛(wèi)顯示“關(guān)于"對(duì)話框-卩用戶界面中的按鈕用類似下面的代碼創(chuàng)建,其中addBttnlconText()是一個(gè)私有方法,它把一個(gè)圖標(biāo)放到按鈕的文字標(biāo)簽之上。Java程序的用戶界面和Windows界面風(fēng)格迥異,建議讀者使用Java開發(fā)工具自帶的圖標(biāo),或者從Java圖標(biāo)庫下載(例如/developer/techDocs/hi/repository/)JButtonplayBttn二newJButton();addBttnlconText(playBttn,"播放","Play24.gif");playBttn.addActionListener(newjava.awt.event.ActionListener(){publicvoidactionPerformed(ActionEvente){playClick(e);}});當(dāng)用戶點(diǎn)擊一個(gè)按鈕,與該按鈕對(duì)應(yīng)的xxxClick()事件句柄函數(shù)開始執(zhí)行。播放器共有5個(gè)按鈕,相應(yīng)的事件句柄也有5個(gè):playClick(“播放”按鈕),stopClick(“停止”按鈕),pauseClick(“暫?!卑粹o),prevClick(“后退”按鈕),nextClick(“前進(jìn)”按鈕)。例如,點(diǎn)擊“播放”按鈕時(shí),playClick()句柄首先獲得JList中選中的文件,然后調(diào)用TestBase實(shí)例中的playFile()輔助方法播放文件。playClick()句柄的代碼如下所示,注意它把音樂文件及其所在目錄連接起來的方法是操作系統(tǒng)中立的。voidplayClick(ActionEvente){StringfileToPlay=(String)filenamesList.getSelectedValue();if(fileToPlay!=null){pBase.playFile(searchDir+System.getProperty(“file.separator")+fileToPlay);}}stopClick()和pauseClick()方法分別調(diào)用TestBase中的stop()和pause()方法。prevClick()和nextClick()句柄的任務(wù)稍微復(fù)雜一點(diǎn)。首先,它們要調(diào)用TestBase中的stop()方法中止當(dāng)前的播放動(dòng)作,然后選中JList中當(dāng)前項(xiàng)目的前一項(xiàng)或后一項(xiàng),最后調(diào)用playClick()播放新選中的音樂文件,如下所示。voidprevClick(ActionEvente){pBase.stop();filenamesList.setSelectedlndex( filenamesList.getSelectedlndex()-1);playClick(e);}voidnextClick(ActionEvente){pBase.stop();filenamesList.setSelectedIndex((filenamesList.getSelectedIndex()+1)
%curPlayListLength);playClick(e);}五、播放音樂TestBase類包含主要的播放邏輯。例如,當(dāng)用戶點(diǎn)擊“播放”按鈕TestBase類中的play()方法開始執(zhí)行。publicvoidplay(){if((!stopped)||(paused))return;if(playerThread==null){playerThread=newThread(this);playerThread.start();try{Thread.sleep(500);}catch(Exceptionex){}}synchronized(synch){stopped=false;synch.notifyAll();}}play()方法首先確認(rèn)播放器當(dāng)前已被終止播放,而不是暫停播放。然后它檢查這是不是第一次調(diào)用play():如果是,則創(chuàng)建一個(gè)playerThread線程。我們用一個(gè)獨(dú)立的線程負(fù)責(zé)音樂播放,這樣,無論播放器正在讀取文件、解碼,還是正在把音頻數(shù)據(jù)輸出到揚(yáng)聲器,用戶界面總是可操作的。啟動(dòng)線程之后,play()方法鎖定靜態(tài)synch同步對(duì)象,將stopped標(biāo)記設(shè)置為false,然后通知正在等待的線程(playerThread線程在開始播放音樂文件之前,會(huì)等待靜態(tài)synch對(duì)象上的提醒通知)。
playerThread線程啟動(dòng)后,它的run()方法開始運(yùn)行。這個(gè)線程一直執(zhí)行while循環(huán),直到threadExit標(biāo)記變成true為止。在while循環(huán)中,線程首先等待“開始播放”的信號(hào)(當(dāng)用戶點(diǎn)擊“播放”按鈕時(shí)),然后播放音樂。表二列出了描述播放器狀態(tài)的各個(gè)標(biāo)記及其含義。表二表二d標(biāo)記名稱說明衛(wèi)程序運(yùn)行結(jié)束,線程應(yīng)當(dāng)盡快終止-心播蹶器處于停止?fàn)顟B(tài);沒有正在播儉的文件-心播蹶器處于暫停狀態(tài);弱礙瀆烹按鈕從當(dāng)前位苴開始繼續(xù)播旅。心正在播議煤體文件-心playMusic()方法利用JavaSoundAPI播放當(dāng)前選中的文件。首先要通過AudioSystem類獲得一個(gè)AudioInputStream。然后,利用AudioInputStream的getFormat()獲知音頻數(shù)據(jù)的格式。在此基礎(chǔ)上,我們?cè)噲D通過getLine()方法獲得一個(gè)支持該種格式的SourceDataLine。如果要播放的是WAV文件,現(xiàn)在我們已經(jīng)有了非壓縮的PCM格式的音頻數(shù)據(jù),可以用line對(duì)象開始播放音頻。ais=AudioSystem.getAudioInputStream(newFile(fileToPlay));???if(ais!=null){baseFormat二ais.getFormat();line=getLine(baseFormat);}如果音頻數(shù)據(jù)是壓縮格式的,如MP3或Ogg,必須先進(jìn)行一次轉(zhuǎn)換——把MP3/Ogg解碼成PCM。解碼主要包括三個(gè)步驟:1、 創(chuàng)建一個(gè)解壓縮結(jié)果的定制AudioFormat(PCM編碼),但保留和原壓縮流一樣的取樣率、通道信息等。2、 創(chuàng)建一個(gè)AudiolnputStream把原來的AudiolnputStream轉(zhuǎn)換成新的AudioFormat格式。3、 獲得一個(gè)處理解碼后格式的SourceDataLine。如下所示:AudioFormatdecodedFormat二newAudioFormat(AudioFormat.Encoding.PCM_SIGNED,baseFormat.getSampleRate(),16,baseFormat.getChannels(),baseFormat.getChannels()*2,baseFormat.getSampleRate(),false);ais=AudioSystem.getAudiolnputStream(decodedFormat,ais);line=getLine(decodedFormat);getLine()方法的返回值是一個(gè)與參數(shù)中指定的AudioFormat兼容的SourceDataLine。如果不能獲得兼容的SourceDataLine,getLine()返回null。在getLine()方法中,我們首先創(chuàng)建和填充一個(gè)DataLine.Info結(jié)構(gòu),調(diào)用AudioSystem.getLine()方法,將info結(jié)構(gòu)傳遞給AudioSystem類工廠。privateSourceDataLinegetLine(AudioFormataudioFormat){SourceDataLineres=null;DataLine.Infoinfo=newDataLine.Info(SourceDataLine.class,audioFormat);try{res=(SourceDataLine)AudioSystem.getLine(info);res.open(audioFormat);}catch(Exceptione){returnres準(zhǔn)備好AudiolnputStream和SourceDataLine之后,playMusic()剩余的任務(wù)已經(jīng)很簡單:用一個(gè)循環(huán)從AudioInputStream讀取數(shù)據(jù),然后寫入到SourceDataLinBytes=0;while((inBytes!=T)&&(!stopped)&&(!threadExit)){try{inBytes=ais.read(audioData,0,BUFFER_SIZE);}—catch(IOExceptione){e.printStackTrace(); }if(inBytes>=0){intoutBytes=line.write(audioData,0,inBytes)
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 二零二五年度軟件測試工程師保密合同及質(zhì)量保證協(xié)議3篇
- 2025年度店鋪股份質(zhì)押與融資服務(wù)合同范本2篇
- 二零二五年度智慧城市基礎(chǔ)設(shè)施建設(shè)項(xiàng)目承包合同樣本3篇
- 科學(xué)思維在學(xué)科復(fù)習(xí)中的運(yùn)用
- 網(wǎng)絡(luò)安全教育在學(xué)校中的創(chuàng)新實(shí)踐與策略研究
- Unit 6【單元測試·基礎(chǔ)卷】-2023-2024學(xué)年九年級(jí)英語上冊(cè)(牛津上海版)(解析版)
- 二零二五年度車輛抵押借款合同爭議解決機(jī)制4篇
- 2025年度土地租賃合同參考范本3篇
- 2025年度個(gè)人水電安裝與維護(hù)承包服務(wù)合同4篇
- 2025版?zhèn)€人二手房交易合同附房屋維修責(zé)任說明2篇
- 彩票市場銷售計(jì)劃書
- 骨科抗菌藥物應(yīng)用分析報(bào)告
- 支付行業(yè)反洗錢與反恐怖融資
- 百詞斬托福詞匯excel版本
- 基礎(chǔ)設(shè)施綠色施工技術(shù)研究
- 寶鋼BQB 481-2023全工藝?yán)滠堉蓄l無取向電工鋼帶文件
- 車輛定損情況確認(rèn)書范本
- 高中英語新課標(biāo)詞匯表(附詞組)
- 證券公司信用風(fēng)險(xiǎn)和操作風(fēng)險(xiǎn)管理理論和實(shí)踐中金公司
- 2022年高考湖南卷生物試題(含答案解析)
- GB/T 20909-2007鋼門窗
評(píng)論
0/150
提交評(píng)論