Title: Sound Software
Course: CPS 261: Advanced Java Concepts ~ DL & MM MASTER

Sound Software

There is an additional library of software that doesn't come with the normal Java Distributions known as javax.media that comes in a downloadable jar file called jmf.jar. I don't know a lot about it, but my guess is that it has a lot of functionality for dealing with audio and other media. If you are doing a lot of serious work, you should probably investigate this more.

However, the normal java software does have some basic sound functionality that does not require installing another library. I want to demonstrate this because it can provide you a simple way to do sound in your Java projects.

The software I am showing in this set of notes works with .wav sound files. I'm not sure what other file formats also work, but I know that it doesn't work with .mp3 files.

However, you should be able to take most sound formats and use an audio application to convert your sound to a .wav file. I have done some work on a Mac and found the application called "Audacity". It allowed me to edit an mp3 file and then export it as a ".wav" file. You can also find a download of this program for a PC. You should find the downloads for Audacity at SourceForge.net.

Zip of the software and sound files used:

SoundSoftware.zip

MySimpleSoundPlayer.java


import java.io.File;
import java.util.Scanner;

import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;

public class MySimpleSoundPlayer {
    AudioInputStream audioInputStream;

    public void playSound(String fileName)
    {
        try {
            System.out.println("Playing: "+ fileName);
            audioInputStream = AudioSystem.getAudioInputStream(
                    new File(fileName));

            Clip clip = AudioSystem.getClip();


            clip.open(audioInputStream);
            clip.start();
        } catch(Exception ex) {
            System.out.println("Error with playing sound."+ex);
            ex.printStackTrace();
        }
    }
    public static void main(String[] args) {


        MySimpleSoundPlayer msp = new MySimpleSoundPlayer();

    // I have a all of my sound files in a folder called "sounds"
    //    msp.playSound("sounds/winner.wav");
        msp.playSound("sounds/loser.wav");

// If you terminate main too quickly, you won't hear the sounds.
// You can do anything you want to keep main alive.  The following is the
// simplest to just wait for the user to enter something in.

        System.out.println("Enter anything into the keyboard when you want to terminate the program");
        Scanner keyboard = new Scanner(System.in);
        keyboard.nextLine();

        System.out.println("Done");




    }

}



The above approach is simple, but it does have some limitations.

- It's hard to know when one sound file finishes.
- Because you don't know when a sound file finishes, if you start another sound file you can get 2 sounds at the same time which is probably not what you want. You can try this out on the previous example, by uncommenting the line that plays "winner.wav".
- Note that if I exit main, then my sound stops. This is because main is the only thread and when the system sees that it goes away, then all resources get cleaned up which includes the sound playing.
- There is no way to loop sounds and play them sequentially until you decide to quit. We often loop sounds in games.

Improved Sound Player.

In order to fix some of of the above limitations, I have added some software to provide better control of our basic sound player.

- In order to know when a sound file is done we Implement the LineListener interface.
- We get called in a routine called "update" when state changes happen. When are interesting in knowing when the Sound file has "Stopped".
- To make this work, we have to call the addLineListener method on the Clip class.

Using the LineListener feature, we now have the following features in MySoundPlayer.

1. We can create an ArrayList of sounds that get played sequentially. When one sound is done, we play the next sound in the list.
2. I have added a thread that manages the sequencing of sounds being played. It can continue even if main exits (if this is what we want).
3. I have added a looping feature so we can restart our list of sounds after they have completely played.
4. I have provided methods "next" and "stop" so you can go to the next sound or just stop completely.

MySoundPlayer.java

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Scanner;

import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineListener;

public class MySoundPlayer implements Runnable, LineListener{
	ArrayList sounds;
	AudioInputStream audioInputStream;
	Clip clip;
	boolean looping = false;
	boolean stopThread = false;
	Thread thread=null;
	
	public void run()
	{
		int index = 0;
		while (index < sounds.size())
		{
			playSound(sounds.get(index));
			index +=1;
			// We now need to wait for completion or user requesting next
			waitForSoundCompletion();
			if (stopThread)
				break;
			// Finished last sound
			if (looping && index == sounds.size())
				index = 0;
		}
		System.out.println("Thread done doing sounds");
	}
	public void next()
	{
		clip.stop();
// The following line will interrup the "wait" call in waitForSoundCompletion.
// We will actually get an "InterruptedException" in this method
		thread.interrupt();
	}
	public void stop()
	{
		stopThread=true;
		next();
	}
	private synchronized void waitForSoundCompletion()
	{
		try {
			wait();
		} catch (InterruptedException e) {
			// Must be wanting to stop this Process
			System.out.println("Thread interrupted");
		}
	}
	private synchronized void wakeup()
	{
// The following will cause "wait" to finish in the waitForSoundCompletion routine
		notify();
	}
	@Override
	public void update(LineEvent event) {
		System.out.println("update event="+event.getType());
		if (event.getType() == LineEvent.Type.STOP)
		{
			try {
				// Not sure this is necessary?
				audioInputStream.close();
				System.out.println("Closing audioInputStream");
			} catch (IOException e) {
				e.printStackTrace();
			}
			wakeup(); // Wakeup thread so it can play the next sound.
		}
		
	}
	private void playSound(String fileName)
	{
		try {
			System.out.println("Playing: "+ fileName);
	        audioInputStream = AudioSystem.getAudioInputStream(
    				new File(fileName));
	
	        clip = AudioSystem.getClip();
	
	        // This will call update when a Clip is done.  update can call wakeup.
	        clip.addLineListener(this);
	
	        clip.open(audioInputStream);
	        clip.start();
	    } catch(Exception ex) {
	        System.out.println("Error with playing sound."+ex);
	        ex.printStackTrace();
	    }
	}
	public MySoundPlayer(ArrayList sounds, boolean looping)
	{
		this.looping = looping;
		this.sounds = sounds;
		thread = new Thread(this);
		thread.start();
	}

	public static void main(String[] args) {
		ArrayList mySounds= new ArrayList();
		mySounds.add("sounds/loser.wav");
		mySounds.add("sounds/Lady_Godiva.wav");
		mySounds.add("sounds/winner.wav");
		mySounds.add("sounds/score.wav");
		
		MySoundPlayer msp = new MySoundPlayer(mySounds, true);
		
		Scanner keyboard = new Scanner(System.in);
		
		boolean continueRunning=true;
		while(continueRunning)
		{
			System.out.println("Enter s)top  n)ext");
			String response = keyboard.nextLine();
			switch (response.charAt(0))
			{
			case 'n': // next
				msp.next();
				break;
			case 's':  // stop
				msp.stop();
				continueRunning = false;
				break;
			default:
				System.out.println("Dont recognize command: "+response);
				break;
			}
		}

		

	}

}



Last Updated: July 20, 2015 8:58 AM