Java and Sound, Part 1

Author's note: On systems that support it, sound can be an important part of many applications. Sound can be used to notify the user that her attention is required, to add the extra dimension of aural feedback to visual GUIs, or for entertainment purposes. Other applications -- such as media players, recorders, and editors -- are sound-centric and therefore require advanced sound capabilities.

In this first of a two-part series of excerpts from Chapter 17 of Java Examples in a Nutshell, 3rd Edition, David Flanagan illustrates some of Java's sound capabilities. In the first section, he covers how to play simple audio clips with the java.applet.AudioClip class. In the second section, he shows how to use the javax.sound.sampled and javax.sound.midi packages to load and play sound clips, how to monitor and change the playback position within a clip, and how to set audio parameters such as volume, balance, and tempo.

Introduction

The Java Sound API is a low-level interface to sound, and the default implementation does not support compressed formats such as MP3. In these excerpts from the chapter we'll be working with simple audio files, with types such as .wav, .aiff, and .au. Note that, although Java Sound supports the recording of sounds, there is no recording example in this chapter: setup and configuration problems on different platforms make sound recording a topic that is beyond the scope of this chapter.

Playing Sounds with AudioClip

The sound capabilities of modern computer hardware are usually much more advanced than the dumb terminals of yesterday, and typical users expect their computers to make sounds that are prettier than the coarse console bell. Java programs can do this by loading and playing a file of audio data with the java.applet.AudioClip interface. As the package name implies, the AudioClip interface was originally intended only for applets. Since Java 1.0, applets have been able to call their getAudioClip( ) instance method to read an audio file over the network. In Java 1.2, however, the static java.applet.Applet.newAudioClip( ) method was added to allow any application to read audio data from any URL (including local file: URLs). This method and the AudioClip interface make it very easy to play arbitrary sounds from your programs, as demonstrated by Example 17-2.

Invoke PlaySound with the URL of a sound file as its sole argument. If you are using a local file, be sure to prefix the filename with the file: protocol. The types of sound files supported depend on the Java implementation. Sun's default implementation supports .wav, .aiff, and .au files for sampled sound, .mid files for MIDI, and even .rmf files for the MIDI-related, proprietary "Rich Music Format" defined by Beatnik. [1]

When you run the PlaySound class of Example 17-2, you may notice that the program never exits. Like AWT applications, programs that use Java's sound capabilities start a background thread and do not automatically exit when the main( ) method returns. [2] To make PlaySound better behaved, we need to explicitly call System.exit( ) when the AudioClip has finished playing. But this highlights one of the shortcomings of the AudioClip interface: it allows you to play( ), stop( ), or loop( ) a sound, but it provides no way to track the progress of the sound or find out when it has finished playing. To achieve that level of control over the playback of sound, we need to use the JavaSound API, which we'll consider in the next section.

Finding Music Files

Before you can test PlaySound, you'll need some sound files to play. You probably have lots of sampled audio files sitting around on your computer: they are often bundled with operating systems and applications. Look for files with .wav, .au, or .aif extensions. You may not have MIDI files on your computer, but many MIDI hobbyists make files available for download on the Internet; a quick Internet search will locate many samples you can use.

The JavaSound web page is also worth a visit (http://java.sun.com/products/java-media/sound/). This page includes links to an interesting JavaSound demo application that includes source code and its own set of sampled audio and MIDI files. Also of interest here are free downloadable MIDI soundbank files, which may give you richer MIDI sound.

Playing Sounds with javax.sound

To achieve more detailed control over the playback of sounds, including the ability to skip to any specified spot in the sound and control things such as volume and balance, we must use the Java Sound API, which is a much lower-level API than the AudioClip interface. JavaSound consists of the javax.sound.sampled package for sampled audio and the javax.sound.midi package for MIDI-based audio. Example 17-3, SoundPlayer.java demonstrates the basic capabilities of these two packages. It loads a sound file (sampled audio or MIDI) completely into memory and then displays a GUI that allows you to play it. The GUI makes extensive use of the Swing JSlider component, which allows you both to select the playback position of the sound and to set things such as the volume, balance, and tempo of the sound. The program displays different controls for sampled audio files than it does for MIDI files. Both GUIs are shown in Figure 17-1. You'll notice that the code is substantially different for sampled audio and MIDI files as well.

Figure 17-1. SoundPlayer playing sampled audio and MIDI files

A shortcoming of the SoundPlayer class is that it can only play sampled audio files that use PCM encoding. ALAW and ULAW encoded files are not supported, nor are more complex compressed encodings, such as MP3. The JavaSound API attempts to directly mirror the capabilities of sound hardware, and can therefore only play PCM sounds. It does provide a transcoding technique, however, to convert sounds to PCM encoding. We'll see this later in the chapter, in Example 17-4.

Streaming Sounds with javax.sound

Sound files, especially sampled audio files in the uncompressed PCM format, are quite large, and, except for the shortest audio clips, it is not generally a good idea to read the entire file into memory. Instead, these files are typically played in streaming mode so that only the portion of the sound that is currently playing must reside in memory. In exchange for reduced memory consumption, we lose the ability to easily jump to any position within the sound, of course. Example 17-4, PlaySoundStream.java is a listing of PlaySoundStream, a program that plays streaming audio data read from the specified URL. It demonstrates streaming techniques for both sampled audio and MIDI data, and also demonstrates a transcoding technique for converting unsupported encodings (such as ALAW and ULAW) into PCM encoding so they can be played. PlaySoundStream is a console-based application with no GUI. Note the use of the wait( ) and notify( ) methods to make streamMidiSequence( ) into a modal method that does not return until the sound has finished playing. Note also that you must use a -m command-line
argument in order to play MIDI data.

Check back here next week for two more excerpts from the "Sound" chapter of Java Examples in a Nutshell, 3rd Edition. We'll cover how to play streaming sounds in both sampled audio and MIDI formats, and how to read a simple musical score and converts it into a MIDI Sequence.

David Flanagan
is the author of a number of O'Reilly books, including
Java in a Nutshell, Java Examples in a Nutshell, Java Foundation Classes in a
Nutshell, JavaScript: The
Definitive Guide, and JavaScript Pocket Reference.

Footnotes

[1] There is no guarantee that the RMF format will continue to be supported.