How to Play Sound in Java

How can I play sound in Java?

I wrote the following code that works fine. But I think it only works with .wav format.

public static synchronized void playSound(final String url) {
new Thread(new Runnable() {
// The wrapper thread is unnecessary, unless it blocks on the
// Clip finishing; see comments.
public void run() {
try {
Clip clip = AudioSystem.getClip();
AudioInputStream inputStream = AudioSystem.getAudioInputStream(
Main.class.getResourceAsStream("/path/to/sounds/" + url));
clip.open(inputStream);
clip.start();
} catch (Exception e) {
System.err.println(e.getMessage());
}
}
}).start();
}

What is the correct way to play a sound file using java.sound.sampled.Clip

An AudioInputStream is needed as an argument for opening a Clip. Looking at the API, you will see that either AudioSystem.getAudioInputStream(InputStream) or AudioSystem.getAudioInputStream(URL) can be used to get an AudioInputStream.

There are two methods of Class that serve: getResource(String) or getResourceAsStream(String). The getResource method will return a URL. The getResourceAsStream method will return an InputStream.

I prefer using the URL form, as it is more forgiving. When using AudioSystem.getAudioInputStream(InputStream), an IOException will be returned if the file does not support marking and resetting. The method AudioSystem.getAudioInputStream(URL) circumvents this requirement.

Also, you should be aware that a URL, unlike a File, can be used to identify a file that is within jar.

You also have to be careful about setting the relative address. I generally like to have a /res file or /audio file that holds the .wav to be loaded. For example, you can use "../res/myAudio.wav" if /res is a parallel folder or "res/myAudio.wav" if /res is subfolder, relative to the location of the class being used in the getResource method.

Here is a simple working example, where the audio file is in a subfolder, /res:

private static void loadAndPlayClip() throws UnsupportedAudioFileException, 
IOException, LineUnavailableException, InterruptedException
{
String filename = "a3.wav";
URL url = BasicClipExample.class.getResource("res/" + filename);
AudioInputStream ais = AudioSystem.getAudioInputStream(url);
DataLine.Info info = new DataLine.Info(Clip.class, ais.getFormat());
Clip clip = (Clip) AudioSystem.getLine(info);
clip.open(ais);

clip.start();
Thread.sleep(7000); // Duration should match length of audio file.
clip.close();
}

While this example should work, it shouldn't be used as is in production, for two reasons.

  1. The Thread.sleep() command halts the main thread, which is usually neither desirable nor needed. The thread that the system creates for Clip playback is a daemon thread. Daemon threads will not prevent a program from completing and shutting down. In this simple "fire and forget" example the main thread is quickly done, so the program will immediately shut down if we don't stave off completion with a sleep interval.

  2. The initial loading of a Clip and the playing of a Clip are usually done in two separate methods. A Clip was designed for audio that can be conveniently held in memory, allowing it to be replayed without requiring reloading. If you reload the Clip with each play command, you would be both redoing work that is not needed and adding latency, as the clip will not begin playing until it has been entirely loaded. If the audio file is inconvenient to hold in memory (e.g., large file due to long duration), the preferred method is to play it with a SourceDataLine.

Playing .mp3 and .wav in Java?

Java FX has Media and MediaPlayer classes which will play mp3 files.

Example code:

String bip = "bip.mp3";
Media hit = new Media(new File(bip).toURI().toString());
MediaPlayer mediaPlayer = new MediaPlayer(hit);
mediaPlayer.play();

You will need the following import statements:

import java.io.File;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;

Playing Sound in Java

The following program plays a 16-bit wav sound from eclipse if we use javax.sound.

import java.io.*;
import java.net.URL;
import javax.sound.sampled.*;
import javax.swing.*;

// To play sound using Clip, the process need to be alive.
// Hence, we use a Swing application.
public class SoundClipTest extends JFrame {

// Constructor
public SoundClipTest() {
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setTitle("Test Sound Clip");
this.setSize(300, 200);
this.setVisible(true);
// You could also get the sound file with a URL
File soundFile = new File("C:/Users/niklas/workspace/assets/Sound/sound.wav");
try ( // Open an audio input stream.
AudioInputStream audioIn = AudioSystem.getAudioInputStream(soundFile);
// Get a sound clip resource.
Clip clip = AudioSystem.getClip()) {
// Open audio clip and load samples from the audio input stream.
clip.open(audioIn);
clip.start();
} catch (UnsupportedAudioFileException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (LineUnavailableException e) {
e.printStackTrace();
}
}

public static void main(String[] args) {
new SoundClipTest();
}
}

How to play sound in java?

This is actually a multi-threading issue.

The problem is that you start the clip, but then terminate your program without giving it a chance to play to it's end. The Clip.start() method is not a blocking operation, which means it does not wait, but rather starts a new daemon thread to play the sound, a daemon thread which is killed once the program exits the main method.

Here is a code example from my other answer, for playing an audio file using a Clip. Notice the way I calculate the sound duration and then sleep() to let it play.

import java.io.File;
import java.io.IOException;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;

public class PlaySound {
private static boolean tryToInterruptSound = false;
private static long mainTimeOut = 3000;
private static long startTime = System.currentTimeMillis();

public static synchronized Thread playSound(final File file) {

Thread soundThread = new Thread() {
@Override
public void run() {
try{
Clip clip = null;
AudioInputStream inputStream = null;
clip = AudioSystem.getClip();
inputStream = AudioSystem.getAudioInputStream(file);
AudioFormat format = inputStream.getFormat();
long audioFileLength = file.length();
int frameSize = format.getFrameSize();
float frameRate = format.getFrameRate();
long durationInMiliSeconds =
(long) (((float)audioFileLength / (frameSize * frameRate)) * 1000);

clip.open(inputStream);
clip.start();
System.out.println("" + (System.currentTimeMillis() - startTime) + ": sound started playing!");
Thread.sleep(durationInMiliSeconds);
while (true) {
if (!clip.isActive()) {
System.out.println("" + (System.currentTimeMillis() - startTime) + ": sound got to it's end!");
break;
}
long fPos = (long)(clip.getMicrosecondPosition() / 1000);
long left = durationInMiliSeconds - fPos;
System.out.println("" + (System.currentTimeMillis() - startTime) + ": time left: " + left);
if (left > 0) Thread.sleep(left);
}
clip.stop();
System.out.println("" + (System.currentTimeMillis() - startTime) + ": sound stoped");
clip.close();
inputStream.close();
} catch (LineUnavailableException e) {
e.printStackTrace();
} catch (UnsupportedAudioFileException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
System.out.println("" + (System.currentTimeMillis() - startTime) + ": sound interrupted while playing.");
}
}
};
soundThread.setDaemon(true);
soundThread.start();
return soundThread;
}

public static void main(String[] args) {
Thread soundThread = playSound(new File("C:\\Booboo.wav"));
System.out.println("" + (System.currentTimeMillis() - startTime) + ": playSound returned, keep running the code");
try {
Thread.sleep(mainTimeOut );
} catch (InterruptedException e) {
e.printStackTrace();
}
if (tryToInterruptSound) {
try {
soundThread.interrupt();
Thread.sleep(1);
// Sleep in order to let the interruption handling end before
// exiting the program (else the interruption could be handled
// after the main thread ends!).
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("" + (System.currentTimeMillis() - startTime) + ": End of main thread; exiting program " +
(soundThread.isAlive() ? "killing the sound deamon thread" : ""));
}
}

Why doesn't my Java application play the sound at each loop?

How about this single-threaded solution which is a cleaner version of your own, but re-using already opened clips from buffers? To me the typing sounds pretty natural even though there are no two sounds playing at the same time. You can adjust the typing speed by changing the corresponding static constants in the Application class.

package de.scrum_master.stackoverflow.q61159885;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine.Info;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import static javax.sound.sampled.AudioSystem.getAudioInputStream;
import static javax.sound.sampled.AudioSystem.getLine;

public class AudioPlayer implements Closeable {
private final Map<String, Clip> bufferedClips = new HashMap<>();

public void play(String audioFilePath) throws IOException, UnsupportedAudioFileException, LineUnavailableException {
Clip clip = bufferedClips.get(audioFilePath);
if (clip == null) {
AudioFormat audioFormat = getAudioInputStream(new File(audioFilePath)).getFormat();
Info lineInfo = new Info(Clip.class, audioFormat);
clip = (Clip) getLine(lineInfo);
bufferedClips.put(audioFilePath, clip);
clip.open(getAudioInputStream(new File(audioFilePath)));
}
clip.setMicrosecondPosition(0);
clip.start();
}

@Override
public void close() {
bufferedClips.values().forEach(Clip::close);
}
}
package de.scrum_master.stackoverflow.q61159885;

import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
import java.io.IOException;
import java.util.Random;

public class Application {
private static final Random RANDOM = new Random();

private static final int ITERATIONS = 10000;
private static final int MINIMUM_WAIT = 75;
private static final int MAX_RANDOM_WAIT = 200;

public static void main(String[] args) throws UnsupportedAudioFileException, IOException, LineUnavailableException {
try (AudioPlayer audioPlayer = new AudioPlayer()) {
for (int i = 0; i < ITERATIONS; i++) {
sleep(MINIMUM_WAIT + RANDOM.nextInt(MAX_RANDOM_WAIT));
audioPlayer.play(randomAudioFile());
}
}
}

private static void sleep(int delay) {
try {
Thread.sleep(delay);
} catch (InterruptedException ignored) {}
}

private static String randomAudioFile() {
return "resources/keystroke-0" + (RANDOM.nextInt(3) + 1) + ".wav";
}
}

You might have noticed that the AudioPlayer is Closeable, i.e. you can use "try with resouces" in the calling application. That way it makes sure that at the end of the program all clips are closed automatically.

The key to replaying the same clip is of course clip.setMicrosecondPosition(0) before you start.


Update: If you want to simulate multiple persons, just modify the main class like this. BTW, I don't know anything about audio programming and whether there is a way to better deal with mixers and overlapping sounds. It is just a proof of concept in order to give you an idea. There is one thread per person, but each person types in a serial fashion, not two keys at the same time. But multiple persons can overlap because there is one AudioPlayer per person with its own set of buffered clips.

package de.scrum_master.stackoverflow.q61159885;

import java.util.Random;

public class Application {
private static final Random RANDOM = new Random();

private static final int PERSONS = 2;
private static final int ITERATIONS = 10000;
private static final int MINIMUM_WAIT = 150;
private static final int MAX_RANDOM_WAIT = 200;

public static void main(String[] args) {
for (int p = 0; p < PERSONS; p++)
new Thread(() -> {
try (AudioPlayer audioPlayer = new AudioPlayer()) {
for (int i = 0; i < ITERATIONS; i++) {
sleep(MINIMUM_WAIT + RANDOM.nextInt(MAX_RANDOM_WAIT));
audioPlayer.play(randomAudioFile());
}
} catch (Exception ignored) {}
}).start();
}

private static void sleep(int delay) {
try {
Thread.sleep(delay);
} catch (InterruptedException ignored) {}
}

private static String randomAudioFile() {
return "resources/keystroke-0" + (RANDOM.nextInt(3) + 1) + ".wav";
}
}

Can't play sound in java using clip

You have to wait for the clip to play and end. You can also create some Listener but that's more complicated. When the clip finishes playing (isActive() is false) you end.

public class P {

public static void main(String[] args) {

new Thread(new Runnable() {

@Override
public void run() {

try {

System.out.println("started");

Clip clip = AudioSystem.getClip();

File file = new File(".......................wav");

AudioInputStream inputStream = AudioSystem.getAudioInputStream(file);

clip.open(inputStream);

clip.start();

while(clip.isOpen()) {
try { Thread.sleep(2000); } catch(InterruptedException ie) {}
if(!clip.isActive()) break;
}

} catch (Exception e) {
e.printStackTrace();
}

}
}).start();
}

}


Related Topics



Leave a reply



Submit