如何用 Java 播放聲音
- java
一、概述
在本教程中,我們將學習如何使用 Java 播放聲音。 Java Sound API 旨在流暢地連續播放聲音,甚至是很長的聲音。
作為本教程的一部分,我們將使用 Java 提供的Clip
和SourceDataLine
Sound API 播放音頻文件。我們還將播放不同的音頻格式文件。
此外,我們將討論每個 API 的優缺點。此外,我們將看到一些也可以播放聲音的第三方 Java 庫。
2. 播放聲音的 Java API
通常, javax.sound
包中的 Java Sound API 提供了兩種播放音頻的方法。在這兩種方法之間,聲音文件數據的指定方式有所不同。 Java Sound API 可以以流式緩衝方式和內存中無緩衝方式處理音頻傳輸。 Java 最著名的兩個聲音 API 是Clip
和SourceDataLine.
2.1 Clip
API
Clip
API 是用於 Java 的無緩衝或內存中的聲音 API。 Clip
類是javax.sound.sampled
包的一部分,它**在讀取和播放短聲音文件時很有用**。在播放之前,整個音頻文件被加載到內存中,用戶可以完全控製播放。
除了循環聲音外,它還允許用戶在隨機位置開始播放。
讓我們首先創建一個示例類SoundPlayerWithClip
,它實現了LineListener
接口,以便接收播放的線路事件( OPEN
、 CLOSE
、 START
和STOP
)。我們將實現LineListener
的update()
方法來檢查播放狀態:
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.");
}
}
}
其次,讓我們從項目的資源文件夾中讀取音頻文件。我們的資源文件夾包含三種不同格式的音頻文件,即 WAV、MP3 和 MPEG:
InputStream inputStream = getClass().getClassLoader().getResourceAsStream(audioFilePath);
第三,從文件流中,我們將創建一個AudioInputStream
:
AudioInputStream audioStream = AudioSystem.getAudioInputStream(inputStream);
現在,我們將創建一個DataLine.Info
對象:
AudioFormat audioFormat = audioStream.getFormat();
DataLine.Info info = new DataLine.Info(Clip.class, audioFormat);
讓我們從這個DataLine.Info
創建一個Clip
對象並打開流,然後調用start
開始播放音頻:
Clip audioClip = (Clip) AudioSystem.getLine(info);
audioClip.addLineListener(this);
audioClip.open(audioStream);
audioClip.start();
最後,我們需要關閉所有打開的資源:
audioClip.close();
audioStream.close();
代碼運行後,將播放音頻文件。
由於音頻是預加載在內存中的,我們還有許多其他有用的 API 可以從中受益。
我們可以使用Clip.loop
方法循環播放音頻片段。
例如,我們可以將其設置為播放五次音頻:
audioClip.loop(4);
或者,我們可以將其設置為無限播放音頻(或直到被中斷):
audioClip.loop(Clip.LOOP_CONTINUOUSLY);
Clip.setMicrosecondPosition
設置媒體位置。當剪輯下次開始播放時,它將從這個位置開始。例如,從第 30 秒開始,我們可以設置為:
audioClip.setMicrosecondPosition(30_000_000);
2.2. SourceDataLine
API
SourceDataLine
API 是用於 java 的緩衝或流式聲音 API。 SourceDataLine
類是javax.sound.sampled
包的一部分,它可以播放無法預加載到內存中的長聲音文件。
當我們希望優化大型音頻文件的內存或流式傳輸實時音頻數據時,使用SourceDataLine
更有效。如果我們事先不知道聲音有多長以及何時結束,它也很有用。
讓我們首先創建一個示例類並從我們項目的資源文件夾中讀取音頻文件。我們的資源文件夾包含三種不同格式的音頻文件,即 WAV、MP3 和 MPEG:
InputStream inputStream = getClass().getClassLoader().getResourceAsStream(audioFilePath);
其次,從文件輸入流中,我們將創建一個AudioInputStream
:
AudioInputStream audioStream = AudioSystem.getAudioInputStream(inputStream);
現在,我們將創建一個DataLine.Info
對象:
AudioFormat audioFormat = audioStream.getFormat();
DataLine.Info info = new DataLine.Info(Clip.class, audioFormat);
讓我們從這個DataLine.Info
創建一個SourceDataLine
對象,打開流,然後調用start
開始播放音頻:
SourceDataLine sourceDataLine = (SourceDataLine) AudioSystem.getLine(info);
sourceDataLine.open(audioFormat);
sourceDataLine.start();
現在,在SourceDataLine
的情況下,音頻數據以塊的形式加載,我們需要提供緩衝區大小:
private static final int BUFFER_SIZE = 4096;
現在,讓我們從AudioInputStream
讀取音頻數據並將其發送到SourceDataLine's
播放緩衝區,直到它到達流的末尾:
byte[] bufferBytes = new byte[BUFFER_SIZE];
int readBytes = -1;
while ((readBytes = audioStream.read(bufferBytes)) != -1) {
sourceDataLine.write(bufferBytes, 0, readBytes);
}
最後,讓我們關閉所有打開的資源:
sourceDataLine.drain();
sourceDataLine.close();
audioStream.close();
代碼運行後,將播放音頻文件。
在這裡,我們不需要實現任何LineListener
接口。
2.3. Clip
和SourceDataLine
比較
讓我們討論一下兩者的優缺點:
夾子 | 源數據線 |
支持從音頻中的任何位置播放。 請參見 setMicrosecondPosition(long) 或setFramePosition(int). | 無法從聲音中的任意位置開始播放。 |
支持循環播放(全部或部分聲音)。 參見 setLoopPoints(int, int) 和loop(int). | 無法播放(循環)全部或部分聲音。 |
可以在播放前知道聲音的持續時間。 請參閱 getFrameLength() 或getMicrosecondLength(). | 在播放之前無法知道聲音的持續時間。 |
可以在當前位置停止播放並稍後繼續播放。參見stop() 和start() | 無法停止並在中間繼續播放。 |
由於它在內存中,因此不適合播放大型音頻文件且效率低下。 | 它適用於播放長聲音文件或實時流式傳輸聲音。 |
Clip's start ()方法確實會播放聲音,但不會阻塞當前線程(立即返回),所以需要實現LineListener 接口才能知道播放狀態。 | 與Clip 不同,我們不必實現LineListener 接口即可知道播放何時完成。 |
無法控制將哪些聲音數據寫入音頻線的播放緩衝區。 | 可以控制將哪些聲音數據寫入音頻線的播放緩衝區。 |
2.4. MP3 格式的 Java API 支持
目前, 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 API Clip
和SourceDataLine.
**我們需要尋找一些可以播放 MP3 格式的第三方庫。
如果我們將 MP3 格式文件提供給Clip
或SourceDataLine
API,我們將得到UnsupportedAudioFileException
:
javax.sound.sampled.UnsupportedAudioFileException: could not get audio input stream from input file
at javax.sound.sampled.AudioSystem.getAudioInputStream(AudioSystem.java:1189)
3. 第三方 Java API 播放聲音
下面我們來看看一對同樣可以播放不同音頻格式文件的第三方庫。
3.1 JavaFX 庫
JavaFX 具有將播放 MP3 文件的Media
和MediaPlayer
類。它還可以播放其他音頻格式,如 WAV。
讓我們創建一個示例類並使用Media
和MediaPlayer
類來播放我們的 MP3 文件:
String audioFilePath = "AudioFileWithMp3Format.mp3";
SoundPlayerUsingJavaFx soundPlayerWithJavaFx = new SoundPlayerUsingJavaFx();
try {
com.sun.javafx.application.PlatformImpl.startup(() -> {});
Media media = new Media(
soundPlayerWithJavaFx.getClass().getClassLoader().getResource(audioFilePath).toExternalForm());
MediaPlayer mp3Player = new MediaPlayer(media);
mp3Player.play();
} catch (Exception ex) {
System.out.println("Error occured during playback process:" + ex.getMessage());
}
此 API 的一個優點是它可以播放 WAV、MP3 和 MPEG 音頻格式。
3.2. JLayer 庫
JLayer庫可以播放 MPEG 格式等音頻格式,包括 MP3。**但是,它不能播放 WAV 等其他格式。**
讓我們使用 Javazoom Player
類創建一個示例類:
String audioFilePath = "AudioFileWithMp3Format.mp3";
SoundPlayerUsingJavaZoom player = new SoundPlayerUsingJavaZoom();
try {
BufferedInputStream buffer = new BufferedInputStream(
player.getClass().getClassLoader().getResourceAsStream(audioFilePath));
Player mp3Player = new Player(buffer);
mp3Player.play();
} catch (Exception ex) {
System.out.println("Error occured during playback process:" + ex.getMessage());
}
4 結論
在本文中,我們學習瞭如何使用 Java 播放聲音。我們還了解了兩種不同的 Java Sound API, Clip
和SourceDataLine
。後來,我們看到了Clip
和SourceDataLine
API 之間的區別,這將幫助我們為任何用例選擇合適的 API。
最後,我們看到一些第三方庫也可以播放音頻並支持其他格式,例如 MP3。