Wednesday, December 30, 2009

The Sound Player









The Sound Player


SoundPlayer.java (located in SoundExamps/SoundPlayer/) shows off the capabilities of the AudioClip class (see Figure 7-1) in a longer example.



Figure 7-1. The SoundPlayer application



A selection of sound files in different formats (all located in the Sounds/ subdirectory below SoundPlayer/) are offered up. They can be played once, looped, or stopped. It's possible to have multiple clips playing and looping simultaneously, and the stop button terminates all the currently playing clips. This example is somewhat similar to the Java Sound tutorial example, SoundApplication.


Figure 7-2 gives the class diagram for SoundPlayer, showing all the public and private methods and variables in the class.


Two important data structures are in play here:



private HashMap soundsMap;
private ArrayList playingClips;



soundsMap holds the loaded AudioClips, indexed by their filenames. playingClips maintains a list of currently playing AudioClips (or, to be more precise, what I think is playing).



Figure 7-2. Class diagram for SoundPlayer



loadSounds( ) loads the AudioClips and stores them in soundsMap for later use:



private void loadSounds( )
{
soundsMap = new HashMap( );
for (int i=0; i < soundFNms.length; i++) {
AudioClip clip = Applet.newAudioClip(
getClass( ).getResource(SOUND_DIR + soundFNms[i]) );
if (clip == null)
System.out.println("Problem loading "+SOUND_DIR+soundFNms[i]);
else
soundsMap.put(soundFNms[i], clip);
}
}



newAudioClip( ) is employed since SoundPlayer is an application, and the URL is specified using the assumption that the files are locally stored in the SOUND_DIR subdirectory (Sounds/). The final version of SoundPlayer is a JAR file, created in this way:



jar cvmf mainClass.txt SoundPlayer.jar SoundPlayer.class Sounds



All the class files and everything in the Sounds/ subdirectory are packed together. mainClass.txt contains a single line:



Main-Class: SoundPlayer



The JAR can be started by double-clicking its icon or from the command line:



java -jar SoundPlayer.jar



playMusic( ) in SoundPlayer retrieves the relevant AudioClip, and plays it once or repeatedly. It stores a reference to the clip in the playingClips ArrayList to register that the clip is playing:



private void playMusic(boolean toLoop)
{
String chosenFile = (String) playListJcb.getSelectedItem( );

// try to get the AudioClip.
AudioClip audioClip = (AudioClip) soundsMap.get(chosenFile);
if (audioClip == null) {
statusLabel.setText("Sound " + chosenFile + " not loaded");
return;
}

if (toLoop)
audioClip.loop( );
else
audioClip.play( ); // play it once

playingClips.add(audioClip); // store a ref to the playing clip
String times = (toLoop) ? " repeatedly" : " once";
statusLabel.setText("Playing sound " + chosenFile + times);
} // end of playMusic( )



playMusic( ) is called from actionPerformed( ) when the user presses the Play or Loop button and is passed a toLoop argument to distinguish between the two.


stopMusic( ) stops all the playing music by calling AudioClip.stop( ) on all the references in playingClips. An issue is that some of the clips may have finished but there's no way to detect them. This isn't really a problem since calling stop( ) on a stopped AudioClip has no effect:



private void stopMusic( )
{
if (playingClips.isEmpty( ))
statusLabel.setText("Nothing to stop");
else {
AudioClip audioClip;
for(int i=0; i < playingClips.size( ); i++) {
audioClip = (AudioClip) playingClips.get(i);
audioClip.stop( ); // may already have stopped, but calling
// stop( ) again does no harm
}
playingClips.clear( );
statusLabel.setText("Stopped all music");
}
}










    No comments: