There are two simple ways to play audio in JavaFX, depending on both the size of the file, and the control you need to have over how it’s played.
Short sound files can be played in JavaFX by creating an AudioClip
object from the sound file and invoking play()
. Longer files can be opened more efficiently by loading the audio file in as a Media
component and loading it into JavaFX’s MediaPlayer
. Media objects are more memory-efficient, because they only decompress sections of audio as the file is played.
The MediaPlayer also has significantly more control over how the audio is played – from multi-track playback to jumping to particular time stamps in a track.
AudioClip | MediaPlayer | |
---|---|---|
Play | ✅ | ✅ |
Pause | ✅ | ✅ |
Stop | ✅ | ✅ |
Rate | ✅ | ✅ |
Right/Left Balance | ✅ | ✅ |
Equalizer | ✅ | |
Mute | ✅ | |
Jump to timepoint | ✅ | |
Playback Events | ✅ | |
Internal Error Property | ✅ | |
Internal volume | ✅ | |
Memory buffering | ✅ |
Depending on how much control you need, you can get hugely different results from both objects. This article goes through how to create and play sounds using both.
What you’ll get from this tutorial
Because the two methods give you such different levels of control over a sound file’s playback, I’ve separated the first part of the article into two sections.
That covers how to play music in the background. To play a sound based on something the user’s done? For that, I’ll go through how to start a sound playing from an ActionEvent
whenever your user clicks a button.
I mention this one because a lot of JavaFX UI development is event-driven. In fact, the vast majority of UI updates can be driven by events and property binding. Check out the links for my comprehensive guides on both.
Playing short sounds with an AudioClip
For short audio clips that won’t take up much memory, I’d always use the AudioClip
object, which completely decompresses the sound file and holds it in memory. That’s great for firing off sounds with low latency, because the clip is immediately ready to play.
AudioClip buzzer = new AudioClip(getClass().getResource("/audio/buzzer.mp3").toExternalForm());
The AudioClip
constructor accepts a String
, but it’s really expecting the String form of a URL
. The best way to do that in JavaFX is through the class’s URL-resolver which we access using getResource()
. To convert the URL
to a String, we call toExternalForm()
.
If you’ve not done that before, check out the drop-down for more details.
If you haven’t done it before, the getClass().getResource().toExternalForm()
might seem a bit long-winded, but it’s an incredibly efficient way to load resources into the your app.
If you have any questions about how to work out what that path should look like, check out this article on how to load resources in JavaFX. It’s a full guide that takes you through how to do it in Java, FXML and CSS.
Once you’ve mastered loading files in using Java’s and JavaFX’s in-build mechanisms, you’ll never use File().toURI().toString()
again.
The AudioClip
object is designed as a fire-and-forget. So, once you’ve instantiated it, just invoke play()
and JavaFX will handle the rest.
buzzer.play();
You can stop the sound with stop()
, but that’s about as much control as you’ll get with the AudioClip
class. If you need more control, you might want to consider creating a Media
object and loading that into a
. That’s also true if you sound file is large enough that loading it all into memory might cause problems.MediaPlayer
Playing longer audio files with the JavaFX MediaPlayer
The downside of the AudioClip
is that along with its limited media control options, it loads the entire clip – uncompressed – into memory. The Media
object instead only loads a portion of the sound into memory during playback.
To load a sound file into a Media
component, you instantiate it in exactly the same way as with the AudioClip
, by getting its location as a URL. If you’ve skipped down and want to check out more information about the getResource()
method, check out the green dropdown above on resources.
Media buzzer = new Media(getClass().getResource("/audio/buzzer.mp3").toExternalForm());
Once created, we can load the Media
into a
object, which gives us control over the playback.MediaPlayer
MediaPlayer mediaPlayer = new MediaPlayer(buzzer);
Now we have our MediaPlayer
we can do significantly more with our audio file. In this case, however, we just want to take advantage of its memory buffering. To start the sound playing, you just need to invoke play()
.
mediaPlayer.play();
That covers starting music running, but what happens when you want to start it from something your user’s done? For that, we’ll need to hook into the Scene’s underlying event management.
Playing a sound on click with JavaFX
Almost all user interface interactions in JavaFX are driven by either events, or properties and binding. In this case, the user’s interaction with our button is going to set off an ActionEvent
. We’ll use that to start our sound playing.
If you’re adding sound effects to user interactions, I’d probably use the AudioClip
; for longer tracks, I’d use the MediaPlayer
.
To make use of the ActionEvent
the button creates, I’ll create an EventHandler
that will make our sound play each time the button is clicked. Because event handlers are a Functional Interface
, we can define them as a lambda for brevity.
AudioClip buzzer = new AudioClip(getClass().getResource("/audio/buzzer.mp3").toExternalForm()); Button myButton = new Button("Press me for sound!"); myButton.setOnAction(event -> { buzzer.play(); });
Obviously don’t forget to include your button in the Scene...
If you have a lot of sounds, and you don’t want to hold them all in memory, you could create an AudioClip
anonymously every time your button gets clicked.
Button myButton = new Button("Press me for sound!"); myButton.setOnAction(event -> { new AudioClip( getClass() .getResource("/audio/buzzer.mp3") .toExternalForm()) .play(); });
Once the reference to the AudioClip
gets lost, the garbage collector will come and fetch it, freeing up the memory for you to use elsewhere.
Finally, if you need to keep track of a sound clip’s status once it’s started playing, you’ll need to use the MediaPlayer
object. This tends to be more useful for longer tracks like music tracks, but you can use it for either really.
To do this, we can use the Media
object inside a MediaPlayer
, which will track the current status of the media through its status
property.
Media buzzer = new Media(getClass().getResource("/audio/longTrack.mp3").toExternalForm()); MediaPlayer mediaPlayer = new MediaPlayer(buzzer); Button myButton = new Button("Press me for sound!"); myButton.setOnAction(event -> { if(mediaPlayer.getStatus() != MediaPlayer.Status.PLAYING){ mediaPlayer.play(); } });
Conclusions
Playing sounds in JavaFX can be as simple as loading the resource into an AudioClip
. The AudioClip
is designed for short sounds – completely loaded into memory – in cases where you don’t need to control playback.
For longer sounds, or in cases where you need more control over the sound’s playback, the Media component can be wrapped in a MediaPlayer
to give you significantly more control over how the sound file is played.
In both cases, the sound file’s playback can be started by invoking play()
on the AudioClip
or MediaPlayer
object you’ve created.