This week, I'm taking on the basics of programming a music application in Java. In this tutorial, we're going to build a basic sampler in java and learn about the Java sound and timing objects.
The Plan
As in the sampler we built in .Net, we will need three basic elements in this program.
1. A control for toggling samples on and off
2. A timing mechanism
3. Sample playback routines
1. A control for toggling samples on and off
For this control, we're going to use a control that I built for another project called toggleButton. This class works like a light switch. It is either true or false.
Custom controls in java are pretty similar to custom controls in any other language. Like any GUI component, toggleButton has three basic sections, properties, event handling and drawing code. The control's properties allow programmers to get and set necessary information. The event handling code tells the control how to respond when it is clicked. The drawing code is required for drawing the component on the screen.
The only two routines that we need to know about for this project are getValue() and setValue(). These get and set the state of the control. Is it on or off? It is very similar to a checkbox.
Take time to browse the toggleButton code if you think you need a better understanding of what is going on.
2. A timing mechanism
The two classes we will use for timing are Timer and TimerTask.
Timer objects are used to execute a piece of code on a regular interval. In our case, we will want to play the appropriate samples on each tick of the sequencer. Our timer will have to run at even intervals that we will calculate in milliseconds according to this formula: (60000 / tempo) / ticksPerBeat
TimerTask objects are containers for the code that is executed by the timer. We will extend the TimerTask class to create a new class that will be executed by the Timer. Here is the pseudo-code for our TimerTask.
1. Increment the tick counter. If we have passed ticksPerBeat * beatsInAMeasure then reset to 0.
2. For each track in our sampler, if the toggle for the current tick has a value of true, then play the appropriate sample.
That's pretty straightforward. See line 249 of javaSampler.java for the actual code.
3. Sample playback routines
Playing audio in java is incredibly easy. In fact this incredibly short example shows that it can be done in just 3 lines of code. We will be using a slightly more complicated approach that uses the AudioInputStream, DataLine and Clip classes.
First, we use the AudioInputStream and DataLine objects to open the clip.
// load the clip
public Clip loadClip(String filename)
{
Clip clip = null;
try
{
// open a link to the wav file
AudioInputStream stream = AudioSystem.getAudioInputStream(new File(filename));
AudioFormat format = stream.getFormat(); // read the file details
if (format.getEncoding() != AudioFormat.Encoding.PCM_SIGNED)
{
format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, format.getSampleRate(), format.getSampleSizeInBits()*2, format.getChannels(), format.getFrameSize()*2, format.getFrameRate(), true);
stream = AudioSystem.getAudioInputStream(format, stream);
}
// Create the clip
DataLine.Info info = new DataLine.Info(Clip.class, stream.getFormat(), ((int)stream.getFrameLength()*format.getFrameSize()));
clip = (Clip) AudioSystem.getLine(info);
// finally, load the data into the Clip object
clip.open(stream);
} catch (IOException e) {
} catch (LineUnavailableException e) {
} catch (UnsupportedAudioFileException e) {
}
// return the new clip
return clip;
}
Then, playing the clip is quite simple.
// play a clip through the speakers
public void playClip(Clip clip)
{
if( clip != null )
{
// first, reset the clip to play from the beginning
clip.stop();
clip.setFramePosition(0); // this routine can be used to start a wav file from an arbitrary position
// play the wav file
clip.start();
}
}
Putting it All Together
Download the complete source code for this example, then unzip all the files to the same directory. Assuming that you already have the latest Java SDK, you should then be able to open a command prompt and compile the program. Do so by typing "javac javaSampler.java", then run the program with "java javaSampler".
The GUI is certainly not the prettiest thing I have ever created, but the code for creating it is in the constructor labeled "javaSampler()".
If you have any questions, or you catch any bugs, post them in the comments!