In this article we'll discuss a simple way how to play wav
files in Java. We'll start with the complete example and explain each bit in the process. This example only involves one class, the one listed below.
package com.albertattard.pmm;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.UnsupportedAudioFileException;
public class TheMusicPlayer {
public static void play(final InputStream inputStream) {
new Thread() {
@Override
public void run() {
AudioInputStream audioInputStream = null;
try {
audioInputStream = AudioSystem
.getAudioInputStream(inputStream);
} catch (UnsupportedAudioFileException e) {
e.printStackTrace();
return;
} catch (IOException e) {
e.printStackTrace();
return;
}
SourceDataLine sourceDataLine = null;
try {
AudioFormat audioFormat
= audioInputStream.getFormat();
DataLine.Info info = new DataLine.Info(
SourceDataLine.class, audioFormat);
sourceDataLine =
(SourceDataLine) AudioSystem.getLine(info);
sourceDataLine.open(audioFormat);
} catch (LineUnavailableException e) {
e.printStackTrace();
return;
}
sourceDataLine.start();
byte[] data = new byte[524288];// 128Kb
try {
int bytesRead = 0;
while (bytesRead != -1) {
bytesRead =
audioInputStream.read(data, 0, data.length);
if (bytesRead >= 0)
sourceDataLine.write(data, 0, bytesRead);
}
} catch (IOException e) {
e.printStackTrace();
return;
} finally {
sourceDataLine.drain();
sourceDataLine.close();
}
}
}.start();
}
public static void play(final String wavFile)
throws FileNotFoundException {
play(new FileInputStream(wavFile));
}
}
Those of you who are in a hurry, just copy and paste the above code and get on with your life. The others who can spare sometime, just read the following part.
Explained
The class has two static overloaded methods which play the wav
file provides as a parameter. The second method simple invokes the first method passing an instance of input stream (and instance of FileInputStream
, which inherits from InputStream
)
All the logic is found in the first play()
method, and that's what we'll be explaining next.
Let say we have a program and would like to play a sound (saved as a wav
file) when an event/scenario occurs. The code to do so is very straight forward as illustrated below (a copy and paste from the example above).
AudioInputStream audioInputStream = null;
try {
audioInputStream = AudioSystem
.getAudioInputStream(The Input Stream);
} catch (UnsupportedAudioFileException e) {
e.printStackTrace();
return;
} catch (IOException e) {
e.printStackTrace();
return;
}
SourceDataLine sourceDataLine = null;
try {
AudioFormat audioFormat
= audioInputStream.getFormat();
DataLine.Info info = new DataLine.Info(
SourceDataLine.class, audioFormat);
sourceDataLine =
(SourceDataLine) AudioSystem.getLine(info);
sourceDataLine.open(audioFormat);
} catch (LineUnavailableException e) {
e.printStackTrace();
return;
}
sourceDataLine.start();
byte[] data = new byte[524288];// 128Kb
try {
int bytesRead = 0;
while (bytesRead != -1) {
bytesRead =
audioInputStream.read(data, 0, data.length);
if (bytesRead >= 0)
sourceDataLine.write(data, 0, bytesRead);
}
} catch (IOException e) {
e.printStackTrace();
return;
} finally {
sourceDataLine.drain();
sourceDataLine.close();
}
The above code fragment is made from three parts:
- Initialisation of the audio input stream
- Initialisation of the audio output
- Playing the audio data
We're reading bytes from the input stream (the famous wav
) and instead of writing/printing these in the console (as we traditionally do with text files), we're writing to the speaker/sound card of the computer.
We start by initialising the It's good to point out that the AudioInputStream
, which inherits from the InputStream
. This instance behaves the same like any other stream and can be treated/handled in the same manner as other streams.
AudioInputStream audioInputStream = null;
try {
audioInputStream = AudioSystem
.getAudioInputStream(The Input Stream);
} catch (UnsupportedAudioFileException e) {
e.printStackTrace();
return;
} catch (IOException e) {
e.printStackTrace();
return;
}
Then we initialise the audio output (this is NOT an OutputStream
), that is, where the sound will be played. This has to match the audio input initialise in the previous step. Remember that there are many different sound files and not all these can be played with the following code.
SourceDataLine sourceDataLine = null;
try {
AudioFormat audioFormat
= audioInputStream.getFormat();
DataLine.Info info = new DataLine.Info(
SourceDataLine.class, audioFormat);
sourceDataLine =
(SourceDataLine) AudioSystem.getLine(info);
sourceDataLine.open(audioFormat);
} catch (LineUnavailableException e) {
e.printStackTrace();
return;
}
The first two were quite straight forward. In the third step we're actually playing the sound by reading the bytes and play them in a similar fashion we do with other streams. We're reading a predefined amount of data (128k) into the array called data
and saving the amount of bytes read in the other variable bytesRead
.
bytesRead = audioInputStream.read(data, 0, data.length);
When the whole file is played, and there is no more data to be played, the read()
method returns -1
(like it does with any other InputStream
). This will have the while
condition to false
ending the playing process.
It is good to note that the third step will consume the focus of program until the wav
file is played. That is, if you have a two minute wav
file, than the above code will take two minutes to execute. All the other parts of the program (that are executed by this thread) will have to wait for the sound/music to finish. Since this is not what we want, the static method play()
wraps this code into a thread which is started at the end of the method.
public static void play(final InputStream inputStream) {
new Thread() {
@Override
public void run() {
Code removed for brevity
}
}.start();
}
Since the code is started within a thread the control is immediately returned to the calling method which can proceed without having to wait for the file to play.
To play a wav
file, all we need to do is invoke the pay method with the wav
file as a parameter as illustrated below.
package com.albertattard.pmm;
public class TheMain {
public static void main(String[] args) {
// File downloaded from:
// http://freewavesamples.com/bowed-bass-c2
TheMusicPlayer.play(TheMain.class
.getResourceAsStream("Bowed-Bass-C2.wav"));
}
}
It's good to point out that the wav
file Bowed-Bass-C2.wav (available at: http://freewavesamples.com/bowed-bass-c2) is in the same directory/package as the above class.
None of the DOS command line wav players work on Windows 7. I add a psvm method and call it from the command line and it works like a charm! Thank you!!!
ReplyDeleteThank you for your comment. I did not understand it :(. I hope this article was useful at least :)
ReplyDeleteThank you for the article. I have a question:
ReplyDeleteDoes this approach work for real-time "live" playing for a wav file that is being received "buffered" in my PC from an external input "sensor node connected through serial port" ?
I never tested that, so I don't know. Why don't you try it out and post the results here? The method takes an input stream, which suites your needs.
Deletethank you, it's absolutely works!!!
ReplyDeletecheers from indonesia
thanks, works perfectly!!
ReplyDelete