2. Java音频播放API

Java在javax.sound包中提供了两种音频播放方式,主要区别在于音频数据的处理机制:流式缓冲播放和内存非缓冲播放。最常用的两个API是ClipSourceDataLine

2.1. Clip API

Clip是Java的非缓冲/内存音频API,位于javax.sound.sampled包中。特别适合播放短音频文件,播放前会将整个音频加载到内存,提供完整的播放控制能力。

除了循环播放,还支持从任意位置开始播放。

首先创建实现LineListener接口的SoundPlayerWithClip类,用于监听播放事件(OPENCLOSESTARTSTOP):

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(SourceDataLine.class, audioFormat);

创建Clip对象,打开流并开始播放:

Clip audioClip = (Clip) AudioSystem.getLine(info);
audioClip.addLineListener(this);
audioClip.open(audioStream);
audioClip.start();

最后关闭资源:

audioClip.close();
audioStream.close();

由于音频已预加载到内存,我们可以利用更多实用API:

使用Clip.loop方法循环播放:

audioClip.loop(4); // 播放5次

或无限循环播放:

audioClip.loop(Clip.LOOP_CONTINUOUSLY);

使用setMicrosecondPosition设置播放起始位置(例如从30秒开始):

audioClip.setMicrosecondPosition(30_000_000);

2.2. SourceDataLine API

SourceDataLine是Java的缓冲/流式音频API,同样位于javax.sound.sampled包中。适合播放无法预加载到内存的长音频文件,或实时流式音频数据。

当需要优化大文件内存占用,或无法预知音频时长时特别有用。

首先创建类并读取音频文件:

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);

创建SourceDataLine对象并开始播放:

SourceDataLine sourceDataLine = (SourceDataLine) AudioSystem.getLine(info);
sourceDataLine.open(audioFormat);
sourceDataLine.start();

音频数据分块加载,需指定缓冲区大小:

private static final int BUFFER_SIZE = 4096;

AudioInputStream读取数据并写入播放缓冲区:

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();

2.3. Clip与SourceDataLine对比

特性 Clip SourceDataLine
任意位置播放 支持(setMicrosecondPosition 不支持
循环播放 支持(setLoopPoints/loop 不支持
获取音频时长 支持(getFrameLength 不支持
暂停/恢复 支持(stop/start 不支持
大文件处理 低效(内存加载) 高效(流式处理)
线程阻塞 非阻塞(需LineListener 阻塞(无需监听器)
缓冲区控制 不可控 可控

2.4. MP3格式支持

目前ClipSourceDataLine仅支持AIFC、AIFF、AU、SND、WAV格式。检查支持格式:

Type[] list = AudioSystem.getAudioFileTypes();
StringBuilder supportedFormat = new StringBuilder("Supported formats:");
for (Type type : list) {
    supportedFormat.append(", " + type.toString());
}
System.out.println(supportedFormat.toString());

Java原生API不支持MP3/MPEG格式,尝试播放会抛出异常:

javax.sound.sampled.UnsupportedAudioFileException: could not get audio input stream from input file
    at javax.sound.sampled.AudioSystem.getAudioInputStream(AudioSystem.java:1189)

3. 第三方音频播放库

3.1. JavaFX库

JavaFX的MediaMediaPlayer类支持MP3播放,也兼容WAV等格式:

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());
}

优势:支持WAV、MP3、MPEG多种格式。

3.2. JLayer库

JLayer 支持MP3等MPEG格式,但不支持WAV:

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音频播放技术:

  • 掌握了ClipSourceDataLine两种核心API的使用场景
  • 明确了二者在控制能力、内存占用、格式支持上的差异
  • 通过对比表格快速选择适合的API
  • 当需要播放MP3时,推荐使用JavaFX或JLayer等第三方库

实际开发中,短音频用Clip更灵活,长音频或流式场景选SourceDataLine,MP3需求则直接上第三方库。示例代码已上传至GitHub


原始标题:How to Play Sound With Java