Chapter 4

Using Java's Audio Classes


CONTENTS


Audio is an area of multimedia that has stirred up a lot of excitement on the Web. It still isn't clear how far audio will permeate the Web world, but there is little doubt that it will play a vital role in the future of Java. As a Web communication tool, audio is extremely engaging and can often grab your attention even when visual cues fall short. Unfortunately, the audio support in Java 1.0 is still in its infancy. Java 1.0 is currently limited to playing audio clips in the Sun AU file format.

In this chapter, you learn the basics about digital audio, along with how the current version of Java provides audio support. You then put together a pretty neat sample applet and finish with a glimpse at where future Java audio extensions might be headed.

Digital Audio Fundamentals

When a microphone converts sound waves to voltage signals, the resulting signal is an analog (or continuous) signal. Because computers are digital machines, it is necessary to convert this analog signal to a digital signal for a computer to process. Analog to digital (A/D) converters handle the task of converting analog signals to digital signals, which is also referred to as sampling. The process of converting an analog signal to a digital signal doesn't always yield exact results. How similarly a digital wave matches its analog counterpart is determined by the frequency at which it is sampled, as well as the amount of information stored at each sample.

To sample a sound, you store the amplitude of the sound wave at regular intervals. Figure 4.1 shows how an analog sound wave is converted to a digital wave by sampling the sound at regular intervals. Notice in Figure 4.1 that the digital representation of the analog sound wave is not a very good one. By taking samples at more frequent intervals, the digital signal will come closer to approximating the analog signal.

Figure 4.1 : An analog sound wave and its digital representation.

When sampling sounds, the rate (frequency) at which the sound is sampled is very important, as well as how much data is stored for each sample. The unit of measurement for frequency is Hertz (Hz), which specifies how many samples are taken per second. In Java 1.0, the only supported sound frequency is 8000 Hz, which means that there are 8000 samples per second. Although it sounds like a lot, this frequency actually results in a fairly low-quality sound. To understand why, consider the fact that the frequency for CD quality audio is 44000 Hz.

Note
The limitations on sound quality imposed by Java are really a reflection of the underlying AU sound format, which is discussed in a moment. When Java widens its support for other sound formats, these limitations will likely disappear.

The amount of data stored per sample determines the number of discrete amplitudes that a digital signal can represent. Obviously, the wider range of amplitudes represented by the digital signal, the closer the original wave is approximated. In Java 1.0, the sample data width is limited to 8 bits. A wave sampled at 8 bits has 256 discrete amplitude levels (2 ^ 8).

Java Audio Support

The current audio support in Java comes in the form of a single class, AudioClip, which is part of the applet package. The AudioClip class models a digital audio sound clip in the AU file format, which provides support for 8000 Hz mono 8-bit µLaw encoded audio clips. This is a fairly low-quality sound format and severely limits Java in providing professional audio capabilities. However, in the current context of the Web, just being able to play AU audio clips in Java is plenty for many applets.

Note
µLaw is the name of a sound data encoding mechanism that provides a 2:1 compression ratio. µLaw has very strong cross-platform support, which is probably one of the reasons Sun chose the AU sound format as Java's first supported format.

The AudioClip class is an abstract class, so you can't directly create instances of it. You create AudioClip objects by calling the getAudioClip method of the Applet class. You'll learn more about that in a moment, but first take a look at the methods in the AudioClip class:

As you can see, these methods are very high-level and quite simplistic. It doesn't get much easier than just calling play to play an audio clip. You may be a little intrigued by the loop method, which plays an audio clip repeatedly in a loop until you explicitly call stop to stop it. The loop method is useful in cases where you have a clip that needs to be repeated, such as a helicopter rotor sound or a music clip.

Tip
When using looped sounds, it's important to make sure that the sound begins and ends in a such a way that it isn't noticeable when the looping occurs. For example, using the helicopter rotor sound as an example, the sound itself might just be one "chop" of the rotor blades. To get the desired effect of a continuous rotor sound, you need to loop the sound repeatedly. However, if the end of the sound doesn't blend well with the beginning, there will be a noticeable pop when the looping takes place. Considering the fact that the looping is probably occurring very rapidly, the end result is that it probably doesn't sound like a helicopter. The only real solution to this problem is carefully hand editing the sound and using a trial-and-error approach.
One particularly neat usage of looped audio is playing music. Because Java currently provides no support for music sound formats such as MIDI, you often must play music as looped sounds. The looping aspect comes about because you usually will want to avoid creating long sound files-they take up so much space and therefore take too long to load. Rather, you can create smaller music sounds that can be looped to give the effect of a longer piece of music. Again, you must be very careful to make the loop transition unnoticeable, or the effect will be ruined.

I'd love to give you more juicy details about the AudioClip class, but there just isn't any more to it. By understanding the three methods implemented by the AudioClip class, you are practically already a Java audio guru.

The missing link, however, is how to create AudioClip objects. Recall that you use the Applet class's getAudioClip method to get an AudioClip object. Actually, there are two versions of getAudioClip, which are defined as the following:

The only difference between these two methods is whether or not the URL parameter contains the name of the audio clip. In the first version, it is assumed that the URL contains the complete name; the second version requires a separate name parameter. You typically will use the second version, because you can easily retrieve the URL of the applet or the HTML document in which the applet is embedded. You do this by using either the getCodeBase or getDocumentBase methods of Applet, like this:

AudioClip clip1 = getAudioClip(getCodeBase(), "sound1.au");
AudioClip clip2 = getAudioClip(getDocumentBase(), "sound2.au");

You do not need to use an AudioClip object to play sounds; you are required to create an AudioClip object only if you want to play looped sounds. For normal sounds, you also have the option of using one of the play methods in the Applet class:

These play methods take the same parameters as the getAudioClip methods. In fact, the play methods in Applet simply call getAudioClip to get an AudioClip object, followed by a call to the audio clip's play method. This is evident in Listing 4.1, which shows the Java 1.0 source code for the Applet play methods.


Listing 4.1. The Java 1.0 Applet play() methods.
public void play(URL url) {
  AudioClip clip = getAudioClip(url);
  if (clip != null) {
    clip.play();
  }
}

public void play(URL url, String name) {
  AudioClip clip = getAudioClip(url, name);
  if (clip != null) {
    clip.play();
  }
}

Playing Audio In Java

Even though it looks simple, and you could probably turn out your own Java audio applet at this point, let's look at how Java audio can be used to create an interesting applet. Figure 4.2 shows a screen shot of the OnTheFarm applet, which uses the Java AudioClip class to generate some entertaining, if not rustic, results.

Figure 4.2 : The OnTheFarm sample applet.

The screen shot of OnTheFarm doesn't quite convey the real purpose of the applet, so at this point you should run it for yourself off the CD-ROM to get the real effect. In case you want to cut to the chase and skip the farm experience, OnTheFarm plays a looped music sound clip along with randomly playing various farm animal sounds. Listing 4.2 contains the complete source code for OnTheFarm.


Listing 4.2. The OnTheFarm sample applet.
// OnTheFarm Class
// OnTheFarm.java

// Imports
import java.applet.*;
import java.awt.*;
import java.util.Random;

public class OnTheFarm extends Applet implements Runnable {
  AudioClip clip[] = new AudioClip[8];
  Thread    thread;
  Random    rand = new Random(System.currentTimeMillis());

  public void init() {
    // Load the sounds
    clip[0] = getAudioClip(getDocumentBase(), "Res/Hillbilly.au");
    clip[1] = getAudioClip(getDocumentBase(), "Res/Cow.au");
    clip[2] = getAudioClip(getDocumentBase(), "Res/Duck.au");
    clip[3] = getAudioClip(getDocumentBase(), "Res/Goat.au");
    clip[4] = getAudioClip(getDocumentBase(), "Res/Hen.au");
    clip[5] = getAudioClip(getDocumentBase(), "Res/Horse.au");
    clip[6] = getAudioClip(getDocumentBase(), "Res/Pig.au");
    clip[7] = getAudioClip(getDocumentBase(), "Res/Rooster.au");
  }

  public void start() {
    if (thread == null) {
      thread = new Thread(this);
      thread.start();
    }
  }

  public void stop() {
    if (thread != null) {
      thread.stop();
      thread = null;
    }
  }

  public void run() {
    while (Thread.currentThread() == thread) {
      // Loop the music sound
      clip[0].loop();

      while (true) {
        // Wait three seconds
        try
          Thread.sleep(3000);
        catch (InterruptedException e)
          break;

        // Play an animal sound
        clip[(rand.nextInt() % 3) + 4].play();
      }
    }
  }

  public void paint(Graphics g) {
    Font        font = new Font("TimesRoman", Font.PLAIN, 20);
    FontMetrics fm = g.getFontMetrics(font);
    String      str = new String("On the farm...");
    g.setFont(font);
    g.drawString(str, (size().width - fm.stringWidth(str)) / 2,
      ((size().height - fm.getHeight()) / 2) + fm.getAscent());
  }
}

OnTheFarm has three member variables: an array of AudioClip objects, a Thread object, and a Random object. The AudioClip objects are used to hold each different sound. The Thread object is used to manage the main applet thread, which handles looping the music sound and playing the random animal sounds. Finally, the Random object is used to generate random numbers that determine which animal sounds are played.

Note
A thread is necessary here because you want the music sound to be looped continuously. The only way to guarantee that the looping is getting enough attention is to give it its own thread. This is a standard technique for looping sounds in Java.

The init method handles loading the audio clips by way of the getAudioClip method. The start and stop methods are standard thread management methods. All the action takes place in the run method, where the music sound is first looped. The random animal sounds are then played inside an infinite while loop. Notice that the thread is put to sleep for three seconds between sounds, which keeps the sounds from overlapping too much and makes them easier to hear. The infinite while loop is automatically terminated when the thread is destroyed, which occurs when the applet terminates.

Note
There's something taking place in the OnTheFarm sample applet that you may be taking for granted: sound mixing. The sound support in Java 1.0 has built-in sound mixing, which enables you to play multiple sounds at once. Java handles all the details of overlaying and playing multiple sounds, which is no small feat. Although you have learned about the downside of the current audio support in regard to its simplicity and limitations, built-in sound mixing is a big benefit.

The only other method in OnTheFarm is paint, which simply draws the message On the farm... centered in the applet window. That's all there is to creating a virtual audio farm in Java.

The Future of Java Audio

There is no arguing the limitations of audio in the current release of Java. It is very clear that the Java architects focused on more critical aspects of the language and class libraries in this first release, which is just as well. As nice as it would be to have fancy audio support right now, I would certainly rather opt for stronger security and portability at this stage, which is apparently the same logic used by Sun.

Knowing the current limitations of Java audio, what might the future hold? Sun has promised more complete audio features in a future release of Java that will include support for MPEG and CD quality audio. There will also no doubt be additional sound formats supported, such as Windows WAV files.

As far as future Java audio classes go, it is likely that Sun will introduce a stream-based set of audio classes. In this scenario, an audio clip would correspond to a stream of audio data. To play an audio stream, you would simply write the stream of data to an audio channel. An audio device, which corresponds to your audio hardware, would contain multiple audio channels.

Sun already uses this same approach in the low-level classes that support the AudioClip class. These low-level classes are currently undocumented and unsupported, but it is likely that they will form the basis of future Java audio classes. If you want to check these classes out for yourself, they can be found in the Classes\Sun\audio directory under your main Java directory. You may have to expand the compressed Classes file to install these classes. You can then use the javap tool to look at the data and methods for these classes. For more information on how to use the javap tool to examine Java classes, check out Chapter 25, "Class Organization and Documentation Tools."

Summary

In this chapter, you learned all about audio in Java, including the fundamentals of digital audio and the means by which Java enables you to play it. You learned about the AudioClip class and how to use it to play audio files in the AU format. You also learned that although it is limited in its current state, audio in Java is nevertheless available and quite usable. Even more importantly, you saw how adding audio to Java applets is simple, consisting of only a couple of method calls.

This chapter concluded with a brief discussion of what the future might hold for Java audio. It isn't clear yet how Sun will supplement Java audio in the future, but it has shown a definite intent. A more powerful Java audio is on the horizon. Until then, you should try to make the most of AU sounds and the AudioClip class. You saw in the OnTheFarm sample applet how you can still have fun with Java audio as it is-so go have some!