Java播放本草綱目?你學(xué)廢了嗎?
大家好,我是指北君。
在本篇文章中,我們將學(xué)習(xí)如何用Java播放音樂(lè),當(dāng)下最火的本草綱目,用起來(lái)吧。Java 聲音 API 的設(shè)計(jì)是為了流暢和連續(xù)地播放聲音,甚至是很長(zhǎng)的聲音。我們將使用 Java 提供的 Clip 和 SourceDataLine 聲音API播放一個(gè)音頻文件。
播放聲音的Java APIs
一般來(lái)說(shuō),javax.sound 包中的Java Sound APIs提供了兩種播放音頻的方法。在這兩種方法之間,在如何指定聲音文件數(shù)據(jù)方面有區(qū)別。Java Sound APIs可以以流式、緩沖方式和內(nèi)存、非緩沖方式處理音頻傳輸。Java的兩個(gè)最著名的聲音API是 Clip 和 SourceDataLine。
Clip API
Clip API是Java的一個(gè)非緩沖或內(nèi)存聲音API。Clip類是javax.sound.sampled包的一部分,它在讀取和播放短的聲音文件時(shí)有用。在播放之前,整個(gè)音頻文件被加載到內(nèi)存中,用戶可以完全控制播放。除了循環(huán)播放聲音外,它還允許用戶在一個(gè)隨機(jī)的位置開(kāi)始播放。
讓我們首先創(chuàng)建一個(gè)示例類,SoundPlayerWithClip,它實(shí)現(xiàn)了LineListener接口,以便接收播放的線事件(OPEN、CLOSE、START和STOP)。我們將從LineListener實(shí)現(xiàn)update()方法來(lái)檢查播放狀態(tài)。
public class SoundPlayerUsingClip implements LineListener {
boolean isPlaybackCompleted;
@Override
public void update(LineEvent event) {
if (LineEvent.Type.START == event.getType()) {
System.out.println("Playback started.");
} else if (LineEvent.Type.STOP == event.getType()) {
isPlaybackCompleted = true;
System.out.println("Playback completed.");
}
}
}
其次,讓我們從我們項(xiàng)目的資源文件夾中讀取音頻文件。我們的資源文件夾包含三個(gè)不同格式的音頻文件--即WAV、MP3和MPEG。
nputStream inputStream = getClass().getClassLoader().getResourceAsStream(audioFilePath);
第三,從文件流中,我們將創(chuàng)建一個(gè)AudioInputStream。
AudioInputStream audioStream = AudioSystem.getAudioInputStream(inputStream);
現(xiàn)在,我們將創(chuàng)建一個(gè)DataLine.Info對(duì)象。
AudioFormat audioFormat = audioStream.getFormat();
DataLine.Info info = new DataLine.Info(Clip.class, audioFormat);
讓我們從這個(gè)DataLine.Info創(chuàng)建一個(gè)Clip對(duì)象,并打開(kāi)流,然后調(diào)用start來(lái)開(kāi)始播放音頻。
Clip audioClip = (Clip) AudioSystem.getLine(info);
audioClip.addLineListener(this);
audioClip.open(audioStream);
audioClip.start();
最后,我們需要關(guān)閉任何開(kāi)放的資源。
audioClip.close();
audioStream.close();
一旦代碼運(yùn)行,音頻文件就會(huì)播放。
由于音頻被預(yù)裝在內(nèi)存中,我們有許多其他有用的API,我們可以從中受益。
我們可以使用Clip.loop方法來(lái)連續(xù)循環(huán)播放音頻片段。
例如,我們可以把它設(shè)置為播放五次音頻。
audioClip.loop(4);
或者,我們可以設(shè)置它無(wú)限期地播放音頻(或直到中斷)。
audioClip.loop(Clip.LOOP_CONTINUUSLY);
Clip.setMicrosecondPosition設(shè)置媒體位置。當(dāng)剪輯下次開(kāi)始播放時(shí),它將從這個(gè)位置開(kāi)始。例如,要從第30秒開(kāi)始,我們可以這樣設(shè)置。
audioClip.setMicrosecondPosition(30_000_000);
SourceDataLine API
SourceDataLine API是java的一個(gè)緩沖或流式聲音API。SourceDataLine類是javax.sound.sampled包的一部分,它可以播放無(wú)法預(yù)裝到內(nèi)存中的長(zhǎng)聲音文件。
當(dāng)我們希望優(yōu)化大的音頻文件的內(nèi)存時(shí),或者在流傳實(shí)時(shí)音頻數(shù)據(jù)時(shí),使用SourceDataLine更有效。如果我們事先不知道聲音有多長(zhǎng),何時(shí)結(jié)束,它也很有用。
讓我們首先創(chuàng)建一個(gè)示例類,從我們項(xiàng)目的資源文件夾中讀取音頻文件。我們的資源文件夾包含三個(gè)不同格式的音頻文件--即WAV、MP3和MPEG。
InputStream inputStream = getClass().getClassLoader().getResourceAsStream(audioFilePath);
第二,從文件輸入流中,我們將創(chuàng)建一個(gè)AudioInputStream。
AudioInputStream audioStream = AudioSystem.getAudioInputStream(inputStream);
現(xiàn)在,我們將創(chuàng)建一個(gè)DataLine.Info對(duì)象。
AudioFormat audioFormat = audioStream.getFormat();
DataLine.Info info = new DataLine.Info(Clip.class, audioFormat);
讓我們從這個(gè)DataLine.Info創(chuàng)建一個(gè)SourceDataLine對(duì)象,打開(kāi)流,并調(diào)用start來(lái)開(kāi)始播放音頻。
SourceDataLine sourceDataLine = (SourceDataLine) AudioSystem.getLine(info);
sourceDataLine.open(audioFormat);
sourceDataLine.start();
現(xiàn)在,在SourceDataLine的情況下,的音頻數(shù)據(jù)是分塊加載的,我們需要提供緩沖區(qū)的大小。
private static final int BUFFER_SIZE = 4096;
現(xiàn)在,讓我們從AudioInputStream讀取音頻數(shù)據(jù),并將其發(fā)送到SourceDataLine的播放緩沖區(qū),直到它到達(dá)流的末端。
byte[] bufferBytes = new byte[BUFFER_SIZE];
int readBytes = -1;
while ((readBytes = audioStream.read(bufferBytes)) != -1) {
sourceDataLine.write(bufferBytes, 0, readBytes);
}
最后,讓我們關(guān)閉任何開(kāi)放的資源。
sourceDataLine.drain();
sourceDataLine.close();
audioStream.close();
一旦代碼運(yùn)行,音頻文件就會(huì)播放。在這里,我們不需要實(shí)現(xiàn)任何LineListener接口。
Clip和SourceDataLine之間的比較
讓我們來(lái)討論一下兩者的優(yōu)點(diǎn)和缺點(diǎn)。
Clip | SourceDataLine |
支持從音頻的任何位置播放。參見(jiàn) ? | 不能從聲音中的任意位置開(kāi)始播放。 |
支持在循環(huán)中播放(全部或部分的聲音)。 參見(jiàn) ? | 不能播放(循環(huán))全部或部分聲音。 |
可以在播放前知道聲音的持續(xù)時(shí)間。參見(jiàn) ? | 在播放前不能知道聲音的持續(xù)時(shí)間。 |
可以在當(dāng)前位置停止播放,稍后繼續(xù)播放。請(qǐng)看 ? | 不能在中間停止和恢復(fù)播放。 |
不適合播放大的音頻文件,也沒(méi)有效率,因?yàn)樗窃趦?nèi)存中的。 | 適合播放長(zhǎng)的聲音文件或?qū)崟r(shí)的聲音流。 |
Clip的start()方法確實(shí)在播放聲音,但它不會(huì)阻塞當(dāng)前線程(它立即返回),所以它需要實(shí)現(xiàn)LineListener接口來(lái)了解播放狀態(tài)。 | 與Clip不同,我們不需要實(shí)現(xiàn)LineListener接口來(lái)知道什么時(shí)候播放完成。 |
不可能控制什么聲音數(shù)據(jù)被寫(xiě)入音頻線的播放緩沖區(qū)。 | 可以控制哪些聲音數(shù)據(jù)要被寫(xiě)入音頻線的播放緩沖區(qū)。 |
Java API對(duì)MP3格式的支持
目前,Clip和SourceDataLine都可以播放AIFC、AIFF、AU、SND和WAV格式的音頻文件。
我們可以使用AudioSystem檢查支持的音頻格式。
Type[] list = AudioSystem.getAudioFileTypes();
StringBuilder supportedFormat = new StringBuilder("Supported formats:");
for (Type type : list) {
supportedFormat.append(", " + type.toString());
}
System.out.println(supportedFormat.toString());
然而,我們不能用Java Sound APIs Clip 和 SourceDataLine 播放流行的音頻格式MP3/MPEG。`我們需要尋找一些能播放MP3格式的第三方庫(kù)。
如果我們向 Clip 或 SourceDataLine API提供MP3格式的文件,我們會(huì)得到UnsupportedAudioFileException 。
javax.sound.sampled.UnsupportedAudioFileException: could not get audio input stream from input file
at javax.sound.sampled.AudioSystem.getAudioInputStream(AudioSystem.java:1189)
總結(jié)
在這篇文章中,我們學(xué)習(xí)了如何用Java播放聲音。我們還了解了兩個(gè)不同的Java聲音API - Clip 和 SourceDataLine。我們了解了 Clip 和 SourceDataLine API之間的區(qū)別。