Safari Books Online is a digital library providing on-demand subscription access to thousands of learning resources.
To get started, you'll create a SimpleSoundPlayer to play sound. This class loads samples from an AudioInputStream into a byte array. It also plays sound from any InputStream by copying data from it to a Line.
In the SimpleSoundPlayer example in Listing 4.1, the samples loaded are converted to an InputStream by using a ByteArrayInputStream. This enables you to read samples from memory instead of from disk. You could just write the byte array directly to the Line, but you'll need to read from InputStreams to add some more advanced functionality later.
Because you're using a ByteArrayInputStream wrapped around a byte array, you can create as many ByteArrayInputStreams for the same sound as you want, so you can play multiple copies of the same sound simultaneously.
|
Code View:
Scroll
/
Show All
import java.io.*;
import javax.sound.sampled.*;
/**
The SimpleSoundPlayer encapsulates a sound that can be opened
from the file system and later played.
*/
public class SimpleSoundPlayer {
public static void main(String[] args) {
// load a sound
SimpleSoundPlayer sound =
new SimpleSoundPlayer("../sounds/voice.wav");
// create the stream to play
InputStream stream =
new ByteArrayInputStream(sound.getSamples());
// play the sound
sound.play(stream);
// exit
System.exit(0);
}
private AudioFormat format;
private byte[] samples;
/**
Opens a sound from a file.
*/
public SimpleSoundPlayer(String filename) {
try {
// open the audio input stream
AudioInputStream stream =
AudioSystem.getAudioInputStream(
new File(filename));
format = stream.getFormat();
// get the audio samples
samples = getSamples(stream);
}
catch (UnsupportedAudioFileException ex) {
ex.printStackTrace();
}
catch (IOException ex) {
ex.printStackTrace();
}
}
/**
Gets the samples of this sound as a byte array.
*/
public byte[] getSamples() {
return samples;
}
/**
Gets the samples from an AudioInputStream as an array
of bytes.
*/
private byte[] getSamples(AudioInputStream audioStream) {
// get the number of bytes to read
int length = (int)(audioStream.getFrameLength() *
format.getFrameSize());
// read the entire stream
byte[] samples = new byte[length];
DataInputStream is = new DataInputStream(audioStream);
try {
is.readFully(samples);
}
catch (IOException ex) {
ex.printStackTrace();
}
// return the samples
return samples;
}
/**
Plays a stream. This method blocks (doesn't return) until
the sound is finished playing.
*/
public void play(InputStream source) {
// use a short, 100ms (1/10th sec) buffer for real-time
// change to the sound stream
int bufferSize = format.getFrameSize() *
Math.round(format.getSampleRate() / 10);
byte[] buffer = new byte[bufferSize];
// create a line to play to
SourceDataLine line;
try {
DataLine.Info info =
new DataLine.Info(SourceDataLine.class, format);
line = (SourceDataLine)AudioSystem.getLine(info);
line.open(format, bufferSize);
}
catch (LineUnavailableException ex) {
ex.printStackTrace();
return;
}
// start the line
line.start();
// copy data to the line
try {
int numBytesRead = 0;
while (numBytesRead != -1) {
numBytesRead =
source.read(buffer, 0, buffer.length);
if (numBytesRead != -1) {
line.write(buffer, 0, numBytesRead);
}
}
}
catch (IOException ex) {
ex.printStackTrace();
}
// wait until all data is played
line.drain();
// close the line
line.close();
}
}
|
In SimpleSoundPlayer, the getSamples(AudioInputStream) method reads from an AudioInputStream and stores the data in the samples byte array. The play() method reads data from an InputStream to a buffer and then writes the buffer to a SourceDataLine, which plays the sound. Also, the main() method in SimpleSoundPlayer tests the class by playing the voice.wav sound.
Note that because of a bug in Java Sound, Java programs won't exit by themselves. Usually, the Java VM exits when there are only daemon threads running, but when you use Java Sound, a nondaemon thread always runs in the background. So, to exit your Java programs that use Java Sound, be sure to call System.exit(0).
Well, you can play sounds yourself, but what if you want to play a sound repeatedly in a loop? This could be really useful for background ambient sounds or, say, for a buzzing fly.
To loop sound, you don't even need to make any changes to the SimpleSoundPlayer. Instead of using a ByteArrayInputStream, you'll create a LoopingByteInputStream in Listing 4.2, which works similarly to ByteArrayInputStream. The only difference is that LoopingByteInputStream indefinitely reads the byte array in a loop until its close() method is called.
|
Code View:
Scroll
/
Show All
package com.brackeen.javagamebook.util;
import java.io.ByteArrayInputStream;
import java.io.IOException;
/**
The LoopingByteInputStream is a ByteArrayInputStream that
loops indefinitely. The looping stops when the close() method
is called.
<p>Possible ideas to extend this class:<ul>
<li>Add an option to only loop a certain number of times.
</ul>
*/
public class LoopingByteInputStream extends ByteArrayInputStream {
private boolean closed;
/**
Creates a new LoopingByteInputStream with the specified
byte array. The array is not copied.
*/
public LoopingByteInputStream(byte[] buffer) {
super(buffer);
closed = false;
}
/**
Reads <code>length</code> bytes from the array. If the
end of the array is reached, the reading starts over from
the beginning of the array. Returns -1 if the array has
been closed.
*/
public int read(byte[] buffer, int offset, int length) {
if (closed) {
return -1;
}
int totalBytesRead = 0;
while (totalBytesRead < length) {
int numBytesRead = super.read(buffer,
offset + totalBytesRead,
length - totalBytesRead);
if (numBytesRead > 0) {
totalBytesRead += numBytesRead;
}
else {
reset();
}
}
return totalBytesRead;
}
/**
Closes the stream. Future calls to the read() methods
will return 1.
*/
public void close() throws IOException {
super.close();
closed = true;
}
}
|
There's nothing special about LoopingByteInputStream. It extends ByteArrayInputStream, and whenever the end of the stream is reached, it calls the reset() method to start reading from the beginning of the array again.
Now you can easily play and loop sound stored in a byte array. Also, because you have access to all the sound samples, you can manipulate the samples to create different effects, or filters.