How to Play Sound With Java – 如何用Java播放声音

最后修改: 2022年 5月 28日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial, we’ll learn how to play sound with Java. The Java Sound APIs are designed to play sounds smoothly and continuously, even very long sounds.

在本教程中,我们将学习如何用Java播放声音。Java声音API的设计是为了流畅和连续地播放声音,甚至是很长的声音。

As part of this tutorial, we’ll play an audio file using Clip and SourceDataLine Sound APIs provided by Java. We’ll also play different audio format files.

作为本教程的一部分,我们将使用Java提供的ClipSourceDataLine声音API播放一个音频文件。我们还将播放不同的音频格式文件。

In addition, we’ll discuss the pros and cons of each API. Further, we’ll see a couple of third-party Java libraries that can also play sound.

此外,我们将讨论每个API的优点和缺点。此外,我们将看到几个也能播放声音的第三方Java库。

2. Java APIs to Play Sound

2.播放声音的Java APIs</b

In general, the Java Sound APIs present in the javax.sound package provide two ways to playback audio. Between the two approaches, there’s a difference in how the sound file data is specified. The Java Sound APIs can handle audio transport in both a streaming, buffered fashion, and an in-memory, unbuffered fashion. Java’s two most well-known sound APIs are Clip and SourceDataLine.

一般来说,存在于 javax.sound包中的Java Sound APIs提供了两种播放音频的方法。在这两种方法中,声音文件数据的指定方式是不同的。Java声音API可以以流式、缓冲方式和内存、非缓冲方式处理音频传输。Java的两个最著名的声音API是ClipSourceDataLine.

2.1. Clip API

2.1. Clip API

Clip API is an unbuffered or in-memory sound API for Java. The Clip class is part of the javax.sound.sampled package, and it is useful when reading and playing a short sound file. Before playing back, the entire audio file is loaded into memory, and the user has total control over the playback.

Clip API是一个用于Java的无缓冲或内存中的声音API。Clip类是javax.sound.sampled包的一部分,它在读取和播放简短的声音文件时很有用。在播放之前,整个音频文件被加载到内存中,用户可以完全控制播放。

In addition to looping sounds, it also allows users to start playback at a random location.

除了循环播放声音外,它还允许用户在随机位置开始播放。

Let’s first create a sample class, SoundPlayerWithClip, which implements the LineListener interface in order to receive line events (OPEN, CLOSE, START, and STOP) for the playback. We’ll implement the update() method from LineListener to check the playback status:

让我们首先创建一个示例类,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.");
        }
    }
}

Secondly, let’s read the audio file from our project’s resource folder. Our resource folder contains three audio files of different formats — namely, WAV, MP3, and MPEG:

其次,让我们从我们项目的资源文件夹中读取音频文件。我们的资源文件夹包含三个不同格式的音频文件–即WAV、MP3和MPEG。

InputStream inputStream = getClass().getClassLoader().getResourceAsStream(audioFilePath);

Thirdly, From the file stream, we’ll create an AudioInputStream:

第三,从文件流中,我们将创建一个AudioInputStream

AudioInputStream audioStream = AudioSystem.getAudioInputStream(inputStream);

Now, we’ll create a DataLine.Info object:

现在,我们将创建一个DataLine.Info对象。

AudioFormat audioFormat = audioStream.getFormat();
DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);

Let’s create a Clip object from this DataLine.Info and open the stream, and then call start to start playing the audio:

让我们从这个DataLine.Info创建一个Clip对象并打开流,然后调用start开始播放音频。

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

Finally, we’ll need to close any open resource:

最后,我们需要关闭任何开放的资源。

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

Once the code runs, the audio file will play.

一旦代码运行,音频文件就会播放。

As the audio is preloaded in memory, we have many other useful APIs from which we can benefit.

由于音频被预装在内存中,我们有许多其他有用的API,我们可以从中受益。

We can use the Clip.loop method to continuously play the audio clip in a loop.

我们可以使用Clip.loop方法来连续循环播放音频片段。

For example, we can set it to play the audio five times:

例如,我们可以将其设置为播放五次音频。

audioClip.loop(4);

Or, we can set it to play the audio for an infinite time (or until interrupted):

或者,我们可以把它设置为无限期地播放音频(或直到中断)。

audioClip.loop(Clip.LOOP_CONTINUOUSLY);

The Clip.setMicrosecondPosition sets the media position. When the clip begins playing the next time, it will start at this position. For example, to start from the 30th second, we can set it as:

Clip.setMicrosecondPosition设置媒体位置。当剪辑下次开始播放时,它将从这个位置开始。例如,要从第30秒开始,我们可以设置为。

audioClip.setMicrosecondPosition(30_000_000);

2.2. SourceDataLine API

2.2.SourceDataLine API

SourceDataLine API is a Buffered or Streaming sound API for java. The SourceDataLine class is part of the javax.sound.sampled package, and it can play long sound files that cannot be preloaded into memory.

SourceDataLine API是java的一个缓冲或流式声音API。SourceDataLine类是javax.sound.sampled包的一部分,它可以播放无法预装到内存的长声音文件。

Using SourceDataLine is more effective when we wish to optimize the memory for large audio files or when streaming real-time audio data. It’s also useful if we don’t know in advance how long the sound is and when it will end.

当我们希望优化大型音频文件的内存或流式传输实时音频数据时,使用SourceDataLine更为有效。如果我们事先不知道声音有多长,何时结束,它也很有用。

Let’s first create a sample class and read the audio file from our project’s resource folder. Our resource folder contains three audio files of different formats — namely, WAV, MP3, and MPEG:

让我们首先创建一个样本类,从我们项目的资源文件夹中读取音频文件。我们的资源文件夹包含三个不同格式的音频文件–即WAV、MP3和MPEG。

InputStream inputStream = getClass().getClassLoader().getResourceAsStream(audioFilePath);

Secondly, from the file input stream, we’ll create an AudioInputStream:

其次,从文件输入流,我们将创建一个AudioInputStream

AudioInputStream audioStream = AudioSystem.getAudioInputStream(inputStream);

Now, we’ll create a DataLine.Info object:

现在,我们将创建一个DataLine.Info对象。

AudioFormat audioFormat = audioStream.getFormat();
DataLine.Info info = new DataLine.Info(Clip.class, audioFormat);

Let’s create a SourceDataLine object from this DataLine.Info, open the stream, and call start to start playing the audio:

让我们从这个DataLine.Info创建一个SourceDataLine对象,打开流,并调用start来开始播放音频。

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

Now, in the case of SourceDataLine, the audio data is loaded in chunks, and we need to provide the buffer size:

现在,在SourceDataLine的情况下,音频数据是分块加载的,我们需要提供缓冲区大小

private static final int BUFFER_SIZE = 4096;

Now, let’s read audio data from the AudioInputStream and send it to the SourceDataLine’s playback buffer until its reaches the end of the stream:

现在,让我们从AudioInputStream中读取音频数据,并将其发送到SourceDataLine的播放缓冲区,直到它到达流的末端。

byte[] bufferBytes = new byte[BUFFER_SIZE];
int readBytes = -1;
while ((readBytes = audioStream.read(bufferBytes)) != -1) {
    sourceDataLine.write(bufferBytes, 0, readBytes);
}

Finally, let’s close any open resource:

最后,让我们关闭任何开放的资源。

sourceDataLine.drain();
sourceDataLine.close();
audioStream.close();

Once the code runs, the audio file will play.

一旦代码运行,音频文件就会播放。

Here, we don’t need to implement any LineListener interface.

在这里,我们不需要实现任何LineListener接口。

2.3. Comparison Between Clip and SourceDataLine

2.3.ClipSourceDataLine之间的比较

Let’s discuss the pros and cons of both:

让我们讨论一下两者的利弊。

Clip SourceDataLine
Supports playing from any position in the audio.
See setMicrosecondPosition(long) or setFramePosition(int).
Can’t start playing from an arbitrary position in the sound.
Supports playing in the loop (all or part of the sound).
see setLoopPoints(int, int) and loop(int).
Can’t play (loop) all or a part of the sound.
Can know duration of the sound before playing.
See getFrameLength() or getMicrosecondLength().
Can’t know the duration of the sound before playing.
It’s possible to stop playing back at the current position and resume playing later. see stop() and start() Can’t stop and resume playing in the middle.
Not suitable and inefficient to playback big audio files as it’s in-memory. It’s suitable and efficient for playback of long sound files or to stream the sound in real-time.
The Clip’s start() method does play the sound, but it does not block the current thread (it returns immediately), so it requires implementing the LineListener interface to know the playback status. Unlike the Clip, we don’t have to implement the LineListener interface to know when the playback completes.
It’s not possible to control what sound data is to be written to the audio line’s playback buffer. It’s possible to control what sound data is to be written to the audio line’s playback buffer.

2.4. Java API Support for MP3 Format

2.4.Java API对MP3格式的支持</b

Currently, both Clip and SourceDataLine can play audio files in AIFC, AIFF, AU, SND, and WAV formats.

目前,ClipSourceDataLine都可以播放AIFC、AIFF、AU、SND和WAV格式的音频文件。

We can check the supported audio format using AudioSystem:

我们可以使用AudioSystem检查支持的音频格式。

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

However, we cannot play the popular audio format MP3/MPEG with Java Sound APIs Clip and SourceDataLine. We need to look for some third-party libraries that can play MP3 format.

然而,我们不能用Java Sound APIs ClipSourceDataLine来播放流行的音频格式MP3/MPEG。我们需要寻找一些能够播放MP3格式的第三方库。

If we provide the MP3 format file to either Clip or SourceDataLine API, we’ll get the UnsupportedAudioFileException:

如果我们向ClipSourceDataLine API提供MP3格式文件,我们会得到UnsupportedAudioFileException

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

3. Third-Party Java APIs to Play Sound

3.播放声音的第三方Java APIs</b

Let’s have a look at a pair of third-party libraries that can also play the different audio format files.

让我们来看看一对第三方库,它们也可以播放不同的音频格式文件。

3.1. JavaFX Library

3.1. JavaFX库

JavaFX has Media and MediaPlayer classes that will play MP3 files. It can also play other audio formats like WAV.

JavaFX具有MediaMediaPlayer类,可以播放MP3文件。它还可以播放其他音频格式,如WAV。

Let’s create a sample class and use the Media and MediaPlayer class to play our MP3 file:

让我们创建一个示例类并使用MediaMediaPlayer类来播放我们的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());
}

One advantage of this API is that it can play WAV, MP3, and MPEG audio formats.

这个API的一个优点是,它可以播放WAV、MP3和MPEG音频格式。

3.2. JLayer Library

3.2.JLayer图书馆

The JLayer library can play audio formats like MPEG formats, including MP3. However, it cannot play other formats like WAV.

JLayer可以播放MPEG格式等音频格式,包括MP3。然而,它不能播放其他格式,如WAV。

Let’s create a sample class using the Javazoom Player class:

让我们使用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. Conclusion

4.结论

In this article, we learned how to play sound with Java. We also learned about two different Java Sound APIs, Clip and SourceDataLine. Later, we saw the differences between Clip and SourceDataLine APIs, which will help us choose the appropriate one for any use case.

在这篇文章中,我们学习了如何用Java播放声音。我们还了解了两个不同的Java声音API,ClipSourceDataLine。后来,我们看到了ClipSourceDataLine API之间的区别,这将有助于我们为任何使用情况选择合适的API。

Lastly, we saw some third-party libraries that could also play audio and support other formats, such as MP3.

最后,我们看到一些第三方库也可以播放音频并支持其他格式,如MP3。

As always, the example code used in this article is available over on GitHub.

一如既往,本文中使用的示例代码可在GitHub上找到。