久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

用Java實(shí)現(xiàn)音頻播放 技術(shù)文摘 技術(shù)文摘 飛越夢(mèng)想,成就明天

 元傲一 2006-09-14

用Java實(shí)現(xiàn)音頻播放

分類:技術(shù)文摘

 一,、JavaSound的體系結(jié)構(gòu)

  二,、JavaSound混頻原理

  三、音頻數(shù)據(jù)與存儲(chǔ)格式

  四,、設(shè)計(jì)音樂(lè)播放器

  五,、播放音樂(lè)

  六、支持更多的音頻格式

  ━━━━━━━━━━━━━

  桌面PC的性能日益提高,,Java虛擬機(jī)的優(yōu)化技術(shù)也不斷獲得突破,,這一切使得用Java處理實(shí)時(shí)信號(hào)成為可能。本文將通過(guò)設(shè)計(jì)和構(gòu)造一個(gè)支持實(shí)時(shí)MP3,、WAV和Ogg音頻格式解碼/回放的Java音樂(lè)播放器,,闡述用JavaSound API編寫音頻處理程序的思路和一般過(guò)程。

  JavaSound是一個(gè)小巧的低層API,,支持?jǐn)?shù)字音頻和MIDI數(shù)據(jù)的記錄/回放,。在JDK 1.3.0之前,JavaSound是一個(gè)標(biāo)準(zhǔn)的Java擴(kuò)展API,,但從Java 2的1.3.0版開始,JavaSound就被包含到JDK之中,。由于Java有著跨平臺(tái)(操作系統(tǒng),、硬件平臺(tái))的特點(diǎn),基于JavaSound的音頻處理程序(包括本文的程序)能夠在任何實(shí)現(xiàn)了Java 1.3+的系統(tǒng)上運(yùn)行,,無(wú)需加裝任何支持軟件,。

  一、JavaSound的體系結(jié)構(gòu)

  當(dāng)前JDK的JavaSound API隨同Java媒體框架(JMF,,Java Media Framework)一起發(fā)布,,主頁(yè)在java./products/java-media/jmf/,,適合JDK 1.1以及更高的版本。除了JDK實(shí)現(xiàn)的JavaSound API之外,,還有一個(gè)源代碼開放的JavaSound實(shí)現(xiàn)是Tritonus,,主頁(yè)在http://www./。

  圖一描述了JavaSound API的體系結(jié)構(gòu),,虛線表示Sun的JavaSound標(biāo)準(zhǔn)定義的API調(diào)用,。上面一根虛線表示我們編寫音頻處理程序要調(diào)用的API,JavaSound API包含在javax.sound.sampled和javax.sound.midi包中,。兩根虛線之間的部分就是JavaSound API的具體實(shí)現(xiàn),。



  圖一:JavaSound體系結(jié)構(gòu)

  就象上面一根虛線表示的API具有統(tǒng)一標(biāo)準(zhǔn)一樣,在所有的JavaSound實(shí)現(xiàn)中,,圖一下面一根虛線表示的SPI(服務(wù)提供者接口,, Service Provider Interface)也是統(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ì)于來(lái)自各種音頻輸入端口的信號(hào),,例如麥克風(fēng),、CD播放器、磁帶播放器,,等等,,我們可以在它們到達(dá)TargetDataLine之前,利用混頻器控制輸入混頻,,最后在程序中通過(guò)TargetDataLine獲得數(shù)字化的音頻輸入流,。



  圖二:JavaSound混頻器

  類似地,在處理輸出音頻的應(yīng)用中,,混頻器用來(lái)對(duì)一系列來(lái)自SourceDataLine的數(shù)據(jù)進(jìn)行混頻處理,,經(jīng)處理后的信號(hào)可輸出到各種輸出端口,例如揚(yáng)聲器、耳機(jī)等,。SourceDataLine是一個(gè)可寫入音頻信號(hào)數(shù)字流的設(shè)備,,例如,我們可以從一個(gè)WAV文件讀取內(nèi)容寫入到SourceDataLine,,然后再通過(guò)揚(yáng)聲器輸出,。

  輸入到混頻器的信號(hào)可以來(lái)源于剪輯。剪輯(Clip)是一個(gè)包含一段完整音頻數(shù)據(jù)流的設(shè)備,,或者說(shuō),,剪輯就是一個(gè)緩沖在內(nèi)存中的完整音頻數(shù)據(jù)流。在一些要求反復(fù)播放音樂(lè)片段的場(chǎng)合,,例如游戲的背景音樂(lè),,剪輯是很有用的。

  圖三描述了JavaSound API中一些常用的類,、接口及其關(guān)系,,所有圖三顯示的類、接口都通過(guò)Line這個(gè)基本接口統(tǒng)一起來(lái),。Line接口用來(lái)關(guān)閉/打開設(shè)備,、注冊(cè)事件監(jiān)聽器,以及提供一些用來(lái)調(diào)整聲音效果的對(duì)象,,例如調(diào)整音量大小的對(duì)象,。AudioSystem在JavaSound體系中起著一個(gè)工廠(Factory)類的作用,提供了一系列的靜態(tài)方法,,我們通過(guò)這些靜態(tài)方法來(lái)獲取JavaSound系統(tǒng)默認(rèn)配置的資源(所謂靜態(tài)方法,,就是可以在不創(chuàng)建AudioSystem實(shí)例的情況下直接調(diào)用的方法)。



  圖三:常用的JavaSound類

  順便說(shuō)明一下,,在當(dāng)前(JDK 1.4)實(shí)現(xiàn)的JavaSound的默認(rèn)配置中,,輸入聲音來(lái)自本地聲卡的麥克風(fēng),輸出聲音到本地聲卡的揚(yáng)聲器,。應(yīng)當(dāng)說(shuō)當(dāng)前實(shí)現(xiàn)的JavaSound對(duì)端口和混頻器的支持還不完善,,但對(duì)于包括本文音樂(lè)播放器在內(nèi)的許多應(yīng)用來(lái)說(shuō),默認(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(Pulse Code Modulation,,脈沖編碼調(diào)制)、MP3等,;通道數(shù)量,;取樣率,;幀速率;等等,。

  音頻數(shù)據(jù)可以用多種格式保存到磁盤上,。在JavaSound參考實(shí)現(xiàn)中,直接支持的文件格式包括WAV(Windows),、AIFF(主要用于Apple的Macintosh)以及AU(主要用于UNIX),,音頻文件的格式由AudioFileFormat類指定。

  并非所有音頻數(shù)據(jù)格式都可以保存到任意音頻文件格式(或從音頻文件回放),,具體由平臺(tái)和操作系統(tǒng)的類型決定,。為簡(jiǎn)單計(jì),本文的播放器只考慮包含PCM Mono或Stereo數(shù)據(jù)的WAV文件,,這是當(dāng)前流行的音頻數(shù)據(jù)/文件格式組合,,常用于CD音質(zhì)的音頻數(shù)據(jù)。壓縮的音頻數(shù)據(jù)(例如MP3和Ogg Vorbis)通常有各自特殊的存儲(chǔ)格式(如.MP3和.OGG),,通常不以WAV/AIFF/AU格式存儲(chǔ),。

  四、設(shè)計(jì)音樂(lè)播放器

  我們要編寫的音樂(lè)播放器(圖四)由表一所示的幾個(gè)類構(gòu)成,。鑒于構(gòu)造用戶界面往往需要大量的代碼,,且這些代碼通常可以用IDE自動(dòng)生成,,所以下文只對(duì)一些關(guān)鍵的GUI元素略作介紹,,不再給出完整的代碼。



  圖四:播放器的用戶界面

  播放器的用戶界面主要由一個(gè)帶菜單的JFrame框架,、一個(gè)名稱為filenamesList的JList和幾個(gè)JButton構(gòu)成,。框架有一個(gè)私有的TestBase成員,,其實(shí)例在GUIInit()方法的末尾通過(guò)pBase = new TestBase()語(yǔ)句初始化,。

  表一



  用戶界面中的按鈕用類似下面的代碼創(chuàng)建,其中addBttnIconText()是一個(gè)私有方法,,它把一個(gè)圖標(biāo)放到按鈕的文字標(biāo)簽之上,。Java程序的用戶界面和Windows界面風(fēng)格迥異,建議讀者使用Java開發(fā)工具自帶的圖標(biāo),,或者從Java圖標(biāo)庫(kù)下載(例如http://developer.java./developer/techDocs/hi/repository/),。

JButton playBttn = new JButton();
            ...
            addBttnIconText(playBttn, "播放", "Play24.gif");
            playBttn.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(ActionEvent e) {
            playClick(e);
            }
            });


  當(dāng)用戶點(diǎn)擊一個(gè)按鈕,與該按鈕對(duì)應(yīng)的xxxClick()事件句柄函數(shù)開始執(zhí)行,。播放器共有5個(gè)按鈕,,相應(yīng)的事件句柄也有5個(gè):playClick(“播放”按鈕),stopClick(“停止”按鈕),pauseClick(“暫停”按鈕),,prevClick(“后退”按鈕),,nextClick(“前進(jìn)”按鈕)。

  例如,,點(diǎn)擊“播放”按鈕時(shí),playClick()句柄首先獲得JList中選中的文件,,然后調(diào)用TestBase實(shí)例中的playFile()輔助方法播放文件,。playClick()句柄的代碼如下所示,注意它把音樂(lè)文件及其所在目錄連接起來(lái)的方法是操作系統(tǒng)中立的,。

void playClick(ActionEvent e) {
            String fileToPlay = (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()播放新選中的音樂(lè)文件,,如下所示,。

void prevClick(ActionEvent e) {
            pBase.stop();
            filenamesList.setSelectedIndex(   filenamesList.getSelectedIndex() - 1);
            playClick(e);
            }
            void nextClick(ActionEvent e) {
            pBase.stop();
            filenamesList.setSelectedIndex((filenamesList.getSelectedIndex()+1)
            % curPlayListLength);
            playClick(e);
            }


  五、播放音樂(lè)

  TestBase類包含主要的播放邏輯,。例如,,當(dāng)用戶點(diǎn)擊“播放”按鈕,TestBase類中的play()方法開始執(zhí)行,。

public void play() {
            if ((!stopped) || (paused)) return;
            if (playerThread == null)  {
            playerThread = new Thread(this);
            playerThread.start();
            try { Thread.sleep(500);
            } catch (Exception ex) {}
            }
            synchronized(synch) {
            stopped = false;
            synch.notifyAll();
            }
            }


  play()方法首先確認(rèn)播放器當(dāng)前已被終止播放,,而不是暫停播放。然后它檢查這是不是第一次調(diào)用play():如果是,,則創(chuàng)建一個(gè)playerThread線程,。我們用一個(gè)獨(dú)立的線程負(fù)責(zé)音樂(lè)播放,這樣,,無(wú)論播放器正在讀取文件,、解碼,還是正在把音頻數(shù)據(jù)輸出到揚(yáng)聲器,,用戶界面總是可操作的,。

  啟動(dòng)線程之后,play()方法鎖定靜態(tài)synch同步對(duì)象,,將stopped標(biāo)記設(shè)置為false,,然后通知正在等待的線程(playerThread線程在開始播放音樂(lè)文件之前,會(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í)),然后播放音樂(lè),。表二列出了描述播放器狀態(tài)的各個(gè)標(biāo)記及其含義。

public void run() {
            while (! threadExit)  {
            waitforSignal();
            if (! stopped)
            playMusic();
            }
            }


  表二



  playMusic()方法利用JavaSound API播放當(dāng)前選中的文件,。首先要通過(guò)AudioSystem類獲得一個(gè)AudioInputStream,。然后,利用AudioInputStream的getFormat()獲知音頻數(shù)據(jù)的格式,。在此基礎(chǔ)上,,我們?cè)噲D通過(guò)getLine()方法獲得一個(gè)支持該種格式的SourceDataLine。如果要播放的是WAV文件,,現(xiàn)在我們已經(jīng)有了非壓縮的PCM格式的音頻數(shù)據(jù),,可以用line對(duì)象開始播放音頻。

ais= AudioSystem.getAudioInputStream(new File(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è)AudioInputStream把原來(lái)的AudioInputStream轉(zhuǎn)換成新的AudioFormat格式。

  3,、獲得一個(gè)處理解碼后格式的SourceDataLine,。

  如下所示:

AudioFormat  decodedFormat = new AudioFormat(
            AudioFormat.Encoding.PCM_SIGNED,
            baseFormat.getSampleRate(),
            16,
            baseFormat.getChannels(),
            baseFormat.getChannels() * 2,
            baseFormat.getSampleRate(),
            false);
            ais = AudioSystem.getAudioInputStream(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類工廠。

private SourceDataLine getLine(AudioFormat audioFormat)  {
            SourceDataLine res = null;
            DataLine.Info info = new DataLine.Info(SourceDataLine.class,
            audioFormat);
            try  {
            res = (SourceDataLine) AudioSystem.getLine(info);
            res.open(audioFormat);
            }
            catch (Exception e) {
            }
            return res;
            }


  準(zhǔn)備好AudioInputStream和SourceDataLine之后,,playMusic()剩余的任務(wù)已經(jīng)很簡(jiǎn)單:用一個(gè)循環(huán)從AudioInputStream讀取數(shù)據(jù),,然后寫入到SourceDataLine。

int inBytes = 0;
            while ((inBytes != -1) && (!stopped) && (!threadExit))  {
            try  {
            inBytes = ais.read(audioData, 0, BUFFER_SIZE);
            }
            catch (IOException e)  { e.printStackTrace(); 	}
            if (inBytes >= 0) {
            int	outBytes = line.write(audioData, 0, inBytes);
            }
            if (paused) waitforSignal();
            }


  六,、支持更多的音頻格式

  假設(shè)已經(jīng)在test目錄下準(zhǔn)備好了所有的.java文件,,執(zhí)行javac *.java即可順利編譯,,執(zhí)行java test.TestPlayer就可以啟動(dòng)圖一的播放器。但現(xiàn)在播放器只能播放有限的文件,,因?yàn)镴DK實(shí)現(xiàn)的JavaSound只支持WAV,、AIFF和AU。但是,,我們可以用JavaSound SPI為播放器增加對(duì)MP3和Ogg Vorbis的支持,,只要下載和安裝相應(yīng)的插件Jar文件即可。

  Java版的Vorbis解碼器可以從JavaCraft(http://www./jorbis/)下載,,最新版本是0.0.12,。另外還要有一個(gè)JOrbis解碼器的SPI封裝器,這是使解碼器在JavaSound下透明地運(yùn)行所必需的,,可以從http://www./vorbisspi/vorbisspi.html下載,。VorbisSPI的最新版本是0.7。

  對(duì)于MP3支持,,JavaZoom也提供了一個(gè)兼容JavaSound的純Java解碼器,,稱為JavaLayer(http://www./javalayer/javalayer.html),最新的版本是0.2.0,。注意要下載的是JavaLayer的J2SE版,,不要下載J2ME版。

  解開下載得到的文件,,把所有Jar文件放到播放器所在目錄,。用下面的命令啟動(dòng)播放器:java -classpath .;.\jogg-0.0.5.jar;.\jorbis-0.0.12.jar;.\jl020.jar;.\mp3sp.jar;.\vorbisspi0.6.jar test.TestPlayer。如果你下載的解碼器版本不同,,啟動(dòng)命令也要作相應(yīng)地改動(dòng),。把SPI擴(kuò)展插件加入到了播放器的classpath之后,JavaSound就會(huì)在運(yùn)行時(shí)自動(dòng)使用它們,。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn),。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多