Java/Development Class/Sound

Материал из Java эксперт
Версия от 07:00, 1 июня 2010; Admin (обсуждение | вклад) (1 версия)
(разн.) ← Предыдущая | Текущая версия (разн.) | Следующая → (разн.)
Перейти к: навигация, поиск

Содержание

An example of loading and playing a sound using a Clip

 
       /*
DEVELOPING GAME IN JAVA 
Caracteristiques
Editeur : NEW RIDERS 
Auteur : BRACKEEN 
Parution : 09 2003 
Pages : 972 
Isbn : 1-59273-005-1 
Reliure : Paperback 
Disponibilite : Disponible a la librairie 
*/
        
import java.io.File;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineListener;
/**
 * An example of loading and playing a sound using a Clip. This complete class
 * isn"t in the book ;)
 */
public class ClipTest {
  public static void main(String[] args) throws Exception {
    // specify the sound to play
    // (assuming the sound can be played by the audio system)
    File soundFile = new File("../sounds/voice.wav");
    AudioInputStream sound = AudioSystem.getAudioInputStream(soundFile);
    // load the sound into memory (a Clip)
    DataLine.Info info = new DataLine.Info(Clip.class, sound.getFormat());
    Clip clip = (Clip) AudioSystem.getLine(info);
    clip.open(sound);
    // due to bug in Java Sound, explicitly exit the VM when
    // the sound has stopped.
    clip.addLineListener(new LineListener() {
      public void update(LineEvent event) {
        if (event.getType() == LineEvent.Type.STOP) {
          event.getLine().close();
          System.exit(0);
        }
      }
    });
    // play the sound clip
    clip.start();
  }
}





An example of playing a sound with an echo filter

 
       /*
DEVELOPING GAME IN JAVA 
Caracteristiques
Editeur : NEW RIDERS 
Auteur : BRACKEEN 
Parution : 09 2003 
Pages : 972 
Isbn : 1-59273-005-1 
Reliure : Paperback 
Disponibilite : Disponible a la librairie 
*/
       
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.UnsupportedAudioFileException;
/**
 * An example of playing a sound with an echo filter.
 * 
 * @see EchoFilter
 * @see SimpleSoundPlayer
 */
public class EchoFilterTest {
  public static void main(String[] args) {
    // load the sound
    SimpleSoundPlayer sound = new SimpleSoundPlayer("../sounds/voice.wav");
    // create the sound stream
    InputStream is = new ByteArrayInputStream(sound.getSamples());
    // create an echo with a 11025-sample buffer
    // (1/4 sec for 44100Hz sound) and a 60% decay
    EchoFilter filter = new EchoFilter(11025, .6f);
    // create the filtered sound stream
    is = new FilteredSoundStream(is, filter);
    // play the sound
    sound.play(is);
    // due to bug in Java Sound, explicitly exit the VM.
    System.exit(0);
  }
}
/**
 * The SimpleSoundPlayer encapsulates a sound that can be opened from the file
 * system and later played.
 */
class SimpleSoundPlayer {
  public static void main(String[] args) {
    // load a sound
    SimpleSoundPlayer sound = new SimpleSoundPlayer("../sounds/voice.wav");
    // create the stream to play
    InputStream stream = new ByteArrayInputStream(sound.getSamples());
    // play the sound
    sound.play(stream);
    // exit
    System.exit(0);
  }
  private AudioFormat format;
  private byte[] samples;
  /**
   * Opens a sound from a file.
   */
  public SimpleSoundPlayer(String filename) {
    try {
      // open the audio input stream
      AudioInputStream stream = AudioSystem.getAudioInputStream(new File(
          filename));
      format = stream.getFormat();
      // get the audio samples
      samples = getSamples(stream);
    } catch (UnsupportedAudioFileException ex) {
      ex.printStackTrace();
    } catch (IOException ex) {
      ex.printStackTrace();
    }
  }
  /**
   * Gets the samples of this sound as a byte array.
   */
  public byte[] getSamples() {
    return samples;
  }
  /**
   * Gets the samples from an AudioInputStream as an array of bytes.
   */
  private byte[] getSamples(AudioInputStream audioStream) {
    // get the number of bytes to read
    int length = (int) (audioStream.getFrameLength() * format
        .getFrameSize());
    // read the entire stream
    byte[] samples = new byte[length];
    DataInputStream is = new DataInputStream(audioStream);
    try {
      is.readFully(samples);
    } catch (IOException ex) {
      ex.printStackTrace();
    }
    // return the samples
    return samples;
  }
  /**
   * Plays a stream. This method blocks (doesn"t return) until the sound is
   * finished playing.
   */
  public void play(InputStream source) {
    // use a short, 100ms (1/10th sec) buffer for real-time
    // change to the sound stream
    int bufferSize = format.getFrameSize()
        * Math.round(format.getSampleRate() / 10);
    byte[] buffer = new byte[bufferSize];
    // create a line to play to
    SourceDataLine line;
    try {
      DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
      line = (SourceDataLine) AudioSystem.getLine(info);
      line.open(format, bufferSize);
    } catch (LineUnavailableException ex) {
      ex.printStackTrace();
      return;
    }
    // start the line
    line.start();
    // copy data to the line
    try {
      int numBytesRead = 0;
      while (numBytesRead != -1) {
        numBytesRead = source.read(buffer, 0, buffer.length);
        if (numBytesRead != -1) {
          line.write(buffer, 0, numBytesRead);
        }
      }
    } catch (IOException ex) {
      ex.printStackTrace();
    }
    // wait until all data is played, then close the line
    line.drain();
    line.close();
  }
}
/**
 * The EchoFilter class is a SoundFilter that emulates an echo.
 * 
 * @see FilteredSoundStream
 */
class EchoFilter extends SoundFilter {
  private short[] delayBuffer;
  private int delayBufferPos;
  private float decay;
  /**
   * Creates an EchoFilter with the specified number of delay samples and the
   * specified decay rate.
   * <p>
   * The number of delay samples specifies how long before the echo is
   * initially heard. For a 1 second echo with mono, 44100Hz sound, use 44100
   * delay samples.
   * <p>
   * The decay value is how much the echo has decayed from the source. A decay
   * value of .5 means the echo heard is half as loud as the source.
   */
  public EchoFilter(int numDelaySamples, float decay) {
    delayBuffer = new short[numDelaySamples];
    this.decay = decay;
  }
  /**
   * Gets the remaining size, in bytes, of samples that this filter can echo
   * after the sound is done playing. Ensures that the sound will have decayed
   * to below 1% of maximum volume (amplitude).
   */
  public int getRemainingSize() {
    float finalDecay = 0.01f;
    // derived from Math.pow(decay,x) <= finalDecay
    int numRemainingBuffers = (int) Math.ceil(Math.log(finalDecay)
        / Math.log(decay));
    int bufferSize = delayBuffer.length * 2;
    return bufferSize * numRemainingBuffers;
  }
  /**
   * Clears this EchoFilter"s internal delay buffer.
   */
  public void reset() {
    for (int i = 0; i < delayBuffer.length; i++) {
      delayBuffer[i] = 0;
    }
    delayBufferPos = 0;
  }
  /**
   * Filters the sound samples to add an echo. The samples played are added to
   * the sound in the delay buffer multipied by the decay rate. The result is
   * then stored in the delay buffer, so multiple echoes are heard.
   */
  public void filter(byte[] samples, int offset, int length) {
    for (int i = offset; i < offset + length; i += 2) {
      // update the sample
      short oldSample = getSample(samples, i);
      short newSample = (short) (oldSample + decay
          * delayBuffer[delayBufferPos]);
      setSample(samples, i, newSample);
      // update the delay buffer
      delayBuffer[delayBufferPos] = newSample;
      delayBufferPos++;
      if (delayBufferPos == delayBuffer.length) {
        delayBufferPos = 0;
      }
    }
  }
}
/**
 * The FilteredSoundStream class is a FilterInputStream that applies a
 * SoundFilter to the underlying input stream.
 * 
 * @see SoundFilter
 */
class FilteredSoundStream extends FilterInputStream {
  private static final int REMAINING_SIZE_UNKNOWN = -1;
  private SoundFilter soundFilter;
  private int remainingSize;
  /**
   * Creates a new FilteredSoundStream object with the specified InputStream
   * and SoundFilter.
   */
  public FilteredSoundStream(InputStream in, SoundFilter soundFilter) {
    super(in);
    this.soundFilter = soundFilter;
    remainingSize = REMAINING_SIZE_UNKNOWN;
  }
  /**
   * Overrides the FilterInputStream method to apply this filter whenever
   * bytes are read
   */
  public int read(byte[] samples, int offset, int length) throws IOException {
    // read and filter the sound samples in the stream
    int bytesRead = super.read(samples, offset, length);
    if (bytesRead > 0) {
      soundFilter.filter(samples, offset, bytesRead);
      return bytesRead;
    }
    // if there are no remaining bytes in the sound stream,
    // check if the filter has any remaining bytes ("echoes").
    if (remainingSize == REMAINING_SIZE_UNKNOWN) {
      remainingSize = soundFilter.getRemainingSize();
      // round down to nearest multiple of 4
      // (typical frame size)
      remainingSize = remainingSize / 4 * 4;
    }
    if (remainingSize > 0) {
      length = Math.min(length, remainingSize);
      // clear the buffer
      for (int i = offset; i < offset + length; i++) {
        samples[i] = 0;
      }
      // filter the remaining bytes
      soundFilter.filter(samples, offset, length);
      remainingSize -= length;
      // return
      return length;
    } else {
      // end of stream
      return -1;
    }
  }
}
/**
 * A abstract class designed to filter sound samples. Since SoundFilters may use
 * internal buffering of samples, a new SoundFilter object should be created for
 * every sound played. However, SoundFilters can be reused after they are
 * finished by called the reset() method.
 * <p>
 * Assumes all samples are 16-bit, signed, little-endian format.
 * 
 * @see FilteredSoundStream
 */
abstract class SoundFilter {
  /**
   * Resets this SoundFilter. Does nothing by default.
   */
  public void reset() {
    // do nothing
  }
  /**
   * Gets the remaining size, in bytes, that this filter plays after the sound
   * is finished. An example would be an echo that plays longer than it"s
   * original sound. This method returns 0 by default.
   */
  public int getRemainingSize() {
    return 0;
  }
  /**
   * Filters an array of samples. Samples should be in 16-bit, signed,
   * little-endian format.
   */
  public void filter(byte[] samples) {
    filter(samples, 0, samples.length);
  }
  /**
   * Filters an array of samples. Samples should be in 16-bit, signed,
   * little-endian format. This method should be implemented by subclasses.
   */
  public abstract void filter(byte[] samples, int offset, int length);
  /**
   * Convenience method for getting a 16-bit sample from a byte array. Samples
   * should be in 16-bit, signed, little-endian format.
   */
  public static short getSample(byte[] buffer, int position) {
    return (short) (((buffer[position + 1] & 0xff) << 8) | (buffer[position] & 0xff));
  }
  /**
   * Convenience method for setting a 16-bit sample in a byte array. Samples
   * should be in 16-bit, signed, little-endian format.
   */
  public static void setSample(byte[] buffer, int position, short sample) {
    buffer[position] = (byte) (sample & 0xff);
    buffer[position + 1] = (byte) ((sample >> 8) & 0xff);
  }
}





An example that plays a Midi sequence

 
       /*
DEVELOPING GAME IN JAVA 
Caracteristiques
Editeur : NEW RIDERS 
Auteur : BRACKEEN 
Parution : 09 2003 
Pages : 972 
Isbn : 1-59273-005-1 
Reliure : Paperback 
Disponibilite : Disponible a la librairie 
*/

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MetaEventListener;
import javax.sound.midi.MetaMessage;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Sequence;
import javax.sound.midi.Sequencer;
/**
 * An example that plays a Midi sequence. First, the sequence is played once
 * with track 1 turned off. Then the sequence is played once with track 1 turned
 * on. Track 1 is the drum track in the example midi file.
 */
public class MidiTest implements MetaEventListener {
  // The drum track in the example Midi file
  private static final int DRUM_TRACK = 1;
  public static void main(String[] args) {
    new MidiTest().run();
  }
  private MidiPlayer player;
  public void run() {
    player = new MidiPlayer();
    // load a sequence
    Sequence sequence = player.getSequence("../sounds/music.midi");
    // play the sequence
    player.play(sequence, true);
    // turn off the drums
    System.out.println("Playing (without drums)...");
    Sequencer sequencer = player.getSequencer();
    sequencer.setTrackMute(DRUM_TRACK, true);
    sequencer.addMetaEventListener(this);
  }
  /**
   * This method is called by the sound system when a meta event occurs. In
   * this case, when the end-of-track meta event is received, the drum track
   * is turned on.
   */
  public void meta(MetaMessage event) {
    if (event.getType() == MidiPlayer.END_OF_TRACK_MESSAGE) {
      Sequencer sequencer = player.getSequencer();
      if (sequencer.getTrackMute(DRUM_TRACK)) {
        // turn on the drum track
        System.out.println("Turning on drums...");
        sequencer.setTrackMute(DRUM_TRACK, false);
      } else {
        // close the sequencer and exit
        System.out.println("Exiting...");
        player.close();
        System.exit(0);
      }
    }
  }
}
class MidiPlayer implements MetaEventListener {
  // Midi meta event
  public static final int END_OF_TRACK_MESSAGE = 47;
  private Sequencer sequencer;
  private boolean loop;
  private boolean paused;
  /**
   * Creates a new MidiPlayer object.
   */
  public MidiPlayer() {
    try {
      sequencer = MidiSystem.getSequencer();
      sequencer.open();
      sequencer.addMetaEventListener(this);
    } catch (MidiUnavailableException ex) {
      sequencer = null;
    }
  }
  /**
   * Loads a sequence from the file system. Returns null if an error occurs.
   */
  public Sequence getSequence(String filename) {
    try {
      return getSequence(new FileInputStream(filename));
    } catch (IOException ex) {
      ex.printStackTrace();
      return null;
    }
  }
  /**
   * Loads a sequence from an input stream. Returns null if an error occurs.
   */
  public Sequence getSequence(InputStream is) {
    try {
      if (!is.markSupported()) {
        is = new BufferedInputStream(is);
      }
      Sequence s = MidiSystem.getSequence(is);
      is.close();
      return s;
    } catch (InvalidMidiDataException ex) {
      ex.printStackTrace();
      return null;
    } catch (IOException ex) {
      ex.printStackTrace();
      return null;
    }
  }
  /**
   * Plays a sequence, optionally looping. This method returns immediately.
   * The sequence is not played if it is invalid.
   */
  public void play(Sequence sequence, boolean loop) {
    if (sequencer != null && sequence != null && sequencer.isOpen()) {
      try {
        sequencer.setSequence(sequence);
        sequencer.start();
        this.loop = loop;
      } catch (InvalidMidiDataException ex) {
        ex.printStackTrace();
      }
    }
  }
  /**
   * This method is called by the sound system when a meta event occurs. In
   * this case, when the end-of-track meta event is received, the sequence is
   * restarted if looping is on.
   */
  public void meta(MetaMessage event) {
    if (event.getType() == END_OF_TRACK_MESSAGE) {
      if (sequencer != null && sequencer.isOpen() && loop) {
        sequencer.start();
      }
    }
  }
  /**
   * Stops the sequencer and resets its position to 0.
   */
  public void stop() {
    if (sequencer != null && sequencer.isOpen()) {
      sequencer.stop();
      sequencer.setMicrosecondPosition(0);
    }
  }
  /**
   * Closes the sequencer.
   */
  public void close() {
    if (sequencer != null && sequencer.isOpen()) {
      sequencer.close();
    }
  }
  /**
   * Gets the sequencer.
   */
  public Sequencer getSequencer() {
    return sequencer;
  }
  /**
   * Sets the paused state. Music may not immediately pause.
   */
  public void setPaused(boolean paused) {
    if (this.paused != paused && sequencer != null && sequencer.isOpen()) {
      this.paused = paused;
      if (paused) {
        sequencer.stop();
      } else {
        sequencer.start();
      }
    }
  }
  /**
   * Returns the paused state.
   */
  public boolean isPaused() {
    return paused;
  }
}





A simple player for sampled sound files.

 
/*
 *
 * Copyright (c) 1999 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Sun grants you ("Licensee") a non-exclusive, royalty free,
 * license to use, modify and redistribute this software in 
 * source and binary code form, provided that i) this copyright
 * notice and license appear on all copies of the software; and 
 * ii) Licensee does not utilize the software in a manner
 * which is disparaging to Sun.
 *
 * This software is provided "AS IS," without a warranty
 * of any kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS
 * AND WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE 
 * HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE LIABLE FOR 
 * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
 * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT
 * WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT
 * OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, 
 * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS
 * OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY
 * TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGES.
 This software is not designed or intended for use in on-line
 control of aircraft, air traffic, aircraft navigation or
 aircraft communications; or in the design, construction,
 operation or maintenance of any nuclear facility. Licensee 
 represents and warrants that it will not use or redistribute 
 the Software for such purposes.
 */
/*  The above copyright statement is included because this 
 * program uses several methods from the JavaSoundDemo
 * distributed by SUN. In some cases, the sound processing methods
 * unmodified or only slightly modified.
 * All other methods copyright Steve Potts, 2002
 */
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.util.Vector;
import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MetaEventListener;
import javax.sound.midi.MetaMessage;
import javax.sound.midi.MidiChannel;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.Sequence;
import javax.sound.midi.Sequencer;
import javax.sound.midi.Synthesizer;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineListener;
/**
 * A simple player for sampled sound files.
 * 
 * 
 * @author Steve Potts
 */
public class SimpleSoundPlayer implements Runnable, LineListener, MetaEventListener {
  final int bufSize = 16384;
  Vector sounds = new Vector();
  Thread thread;
  Sequencer sequencer;
  boolean midiEOM, audioEOM;
  Synthesizer synthesizer;
  MidiChannel channels[];
  Object currentSound;
  String currentName;
  double duration;
  int num;
  boolean bump;
  boolean paused = false;
  String errStr;
  public void open() {
    try {
      sequencer = MidiSystem.getSequencer();
      if (sequencer instanceof Synthesizer) {
        synthesizer = (Synthesizer) sequencer;
        channels = synthesizer.getChannels();
      }
    } catch (Exception ex) {
      ex.printStackTrace();
      return;
    }
    sequencer.addMetaEventListener(this);
  }
  public void close() {
    if (sequencer != null) {
      sequencer.close();
    }
  }
  private void addSound(File file) {
    sounds.add(file);
  }
  public boolean loadSound(Object object) {
    duration = 0.0;
    currentName = ((File) object).getName();
    try {
      currentSound = AudioSystem.getAudioInputStream((File) object);
    } catch (Exception e1) {
      try {
        FileInputStream is = new FileInputStream((File) object);
        currentSound = new BufferedInputStream(is, 1024);
      } catch (Exception e3) {
        e3.printStackTrace();
        currentSound = null;
        return false;
      }
      // }
    }
    // user pressed stop or changed tabs while loading
    if (sequencer == null) {
      currentSound = null;
      return false;
    }
    if (currentSound instanceof AudioInputStream) {
      try {
        AudioInputStream stream = (AudioInputStream) currentSound;
        AudioFormat format = stream.getFormat();
        /**
         * we can"t yet open the device for ALAW/ULAW playback, convert
         * ALAW/ULAW to PCM
         */
        if ((format.getEncoding() == AudioFormat.Encoding.ULAW)
            || (format.getEncoding() == AudioFormat.Encoding.ALAW)) {
          AudioFormat tmp = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
              format.getSampleRate(), format.getSampleSizeInBits() * 2, format.getChannels(),
              format.getFrameSize() * 2, format.getFrameRate(), true);
          stream = AudioSystem.getAudioInputStream(tmp, stream);
          format = tmp;
        }
        DataLine.Info info = new DataLine.Info(Clip.class, stream.getFormat(), ((int) stream
            .getFrameLength() * format.getFrameSize()));
        Clip clip = (Clip) AudioSystem.getLine(info);
        clip.addLineListener(this);
        clip.open(stream);
        currentSound = clip;
        // seekSlider.setMaximum((int) stream.getFrameLength());
      } catch (Exception ex) {
        ex.printStackTrace();
        currentSound = null;
        return false;
      }
    } else if (currentSound instanceof Sequence || currentSound instanceof BufferedInputStream) {
      try {
        sequencer.open();
        if (currentSound instanceof Sequence) {
          sequencer.setSequence((Sequence) currentSound);
        } else {
          sequencer.setSequence((BufferedInputStream) currentSound);
        }
      } catch (InvalidMidiDataException imde) {
        System.out.println("Unsupported audio file.");
        currentSound = null;
        return false;
      } catch (Exception ex) {
        ex.printStackTrace();
        currentSound = null;
        return false;
      }
    }
    duration = getDuration();
    return true;
  }
  public void playSound() {
    midiEOM = audioEOM = bump = false;
    if (currentSound instanceof Sequence || currentSound instanceof BufferedInputStream
        && thread != null) {
      sequencer.start();
      while (!midiEOM && thread != null && !bump) {
        try {
          thread.sleep(99);
        } catch (Exception e) {
          break;
        }
      }
      sequencer.stop();
      sequencer.close();
    } else if (currentSound instanceof Clip) {
      Clip clip = (Clip) currentSound;
      clip.start();
      try {
        thread.sleep(99);
      } catch (Exception e) {
      }
      while ((paused || clip.isActive()) && thread != null && !bump) {
        try {
          thread.sleep(99);
        } catch (Exception e) {
          break;
        }
      }
      clip.stop();
      clip.close();
    }
    currentSound = null;
  }
  public double getDuration() {
    double duration = 0.0;
    if (currentSound instanceof Sequence) {
      duration = ((Sequence) currentSound).getMicrosecondLength() / 1000000.0;
    } else if (currentSound instanceof BufferedInputStream) {
      duration = sequencer.getMicrosecondLength() / 1000000.0;
    } else if (currentSound instanceof Clip) {
      Clip clip = (Clip) currentSound;
      duration = clip.getBufferSize()
          / (clip.getFormat().getFrameSize() * clip.getFormat().getFrameRate());
    }
    return duration;
  }
  public void update(LineEvent event) {
    if (event.getType() == LineEvent.Type.STOP && !paused) {
      audioEOM = true;
    }
  }
  public void meta(MetaMessage message) {
    if (message.getType() == 47) { // 47 is end of track
      midiEOM = true;
    }
  }
  private void reportStatus(String msg) {
    if ((errStr = msg) != null) {
      System.out.println(errStr);
    }
  }
  public Thread getThread() {
    return thread;
  }
  public void start() {
    thread = new Thread(this);
    thread.setName("SimpleSamplePlayer");
    thread.start();
  }
  public void stop() {
    if (thread != null) {
      thread.interrupt();
    }
    thread = null;
  }
  public void run() {
    for (; num < sounds.size() && thread != null; num++) {
      if (loadSound(sounds.get(num)) == true) {
        playSound();
      }
      // take a little break between sounds
      try {
        thread.sleep(222);
      } catch (Exception e) {
        break;
      }
    }
    num = 0;
    thread = null;
    currentName = null;
    currentSound = null;
    System.out.println("Press <ctrl-c> to exit");
  }
  public void loadSounds(String name) {
    try {
      File file = new File(name);
      if (file != null && file.isDirectory()) {
        String files[] = file.list();
        for (int i = 0; i < files.length; i++) {
          File leafFile = new File(file.getAbsolutePath(), files[i]);
          addSound(leafFile);
        }
      }
    } catch (Exception e) {
      System.out.println("Exception " + e);
    }
  }
  public static void main(String args[]) {
    // every file in this directory will be played
    String media = "c:/unleashed/ch18/sounds";
    final SimpleSoundPlayer ssp = new SimpleSoundPlayer();
    ssp.open();
    // we first load the sound file names in a vector
    ssp.loadSounds(media);
    // Then we start a thread to play the sounds
    ssp.start();
    // We have to wait for a while so that the process doesn"t
    // terminate, killing the playing thread
    try {
      Thread.sleep(500000);
    } catch (Exception e) {
      System.out.println("Interrupted");
    }
    // close and exit
    ssp.close();
    System.exit(0);
  }
}





audio sound: implements java.applet.AudioClip

 
// This example is from the book _Java AWT Reference_ by John Zukowski.
// Written by John Zukowski.  Copyright (c) 1997 O"Reilly & Associates.
// You may study, use, modify, and distribute this example for any purpose.
// This example is provided WITHOUT WARRANTY either expressed or
import java.io.FileInputStream;
import java.net.URL;
import sun.audio.AudioData;
import sun.audio.AudioDataStream;
import sun.audio.AudioPlayer;
import sun.audio.AudioStream;
import sun.audio.ContinuousAudioDataStream;
public class SunAudioClip implements java.applet.AudioClip {
  private AudioData audiodata;
  private AudioDataStream audiostream;
  private ContinuousAudioDataStream continuousaudiostream;
  static int length;
  public SunAudioClip(URL url) throws java.io.IOException {
    audiodata = new AudioStream(url.openStream()).getData();
    audiostream = null;
    continuousaudiostream = null;
  }
  public SunAudioClip(String filename) throws java.io.IOException {
    FileInputStream fis = new FileInputStream(filename);
    AudioStream audioStream = new AudioStream(fis);
    audiodata = audioStream.getData();
    audiostream = null;
    continuousaudiostream = null;
  }
  public void play() {
    audiostream = new AudioDataStream(audiodata);
    AudioPlayer.player.start(audiostream);
  }
  public void loop() {
    continuousaudiostream = new ContinuousAudioDataStream(audiodata);
    AudioPlayer.player.start(continuousaudiostream);
  }
  public void stop() {
    if (audiostream != null)
      AudioPlayer.player.stop(audiostream);
    if (continuousaudiostream != null)
      AudioPlayer.player.stop(continuousaudiostream);
  }
  public static void main(String args[]) throws Exception {
    URL url1 = new URL("http://localhost:8080/audio/1.au");
    URL url2 = new URL("http://localhost:8080/audio/2.au");
    SunAudioClip sac1 = new SunAudioClip(url1);
    SunAudioClip sac2 = new SunAudioClip(url2);
    SunAudioClip sac3 = new SunAudioClip("1.au");
    sac1.play();
    sac2.loop();
    sac3.play();
    try { // Delay for loop
      Thread.sleep(2000);
    } catch (InterruptedException ie) {
    }
    sac2.stop();
  }
}





Capturing Audio with Java Sound API

 
 
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.TargetDataLine;
public class Main {
  public static void main(String args[]) throws Exception {
    final ByteArrayOutputStream out = new ByteArrayOutputStream();
    float sampleRate = 8000;
    int sampleSizeInBits = 8;
    int channels = 1;
    boolean signed = true;
    boolean bigEndian = true;
    final AudioFormat format = new AudioFormat(sampleRate, sampleSizeInBits, channels, signed,
        bigEndian);
    DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
    final TargetDataLine line = (TargetDataLine) AudioSystem.getLine(info);
    line.open(format);
    line.start();
    Runnable runner = new Runnable() {
      int bufferSize = (int) format.getSampleRate() * format.getFrameSize();
      byte buffer[] = new byte[bufferSize];
      public void run() {
        try {
          int count = line.read(buffer, 0, buffer.length);
          if (count > 0) {
            out.write(buffer, 0, count);
          }
          out.close();
        } catch (IOException e) {
          System.err.println("I/O problems: " + e);
          System.exit(-1);
        }
      }
    };
    Thread captureThread = new Thread(runner);
    captureThread.start();
    byte audio[] = out.toByteArray();
    InputStream input = new ByteArrayInputStream(audio);
    final SourceDataLine line1 = (SourceDataLine) AudioSystem.getLine(info);
    final AudioInputStream ais = new AudioInputStream(input, format, audio.length
        / format.getFrameSize());
    line1.open(format);
    line1.start();
    runner = new Runnable() {
      int bufferSize = (int) format.getSampleRate() * format.getFrameSize();
      byte buffer[] = new byte[bufferSize];
      public void run() {
        try {
          int count;
          while ((count = ais.read(buffer, 0, buffer.length)) != -1) {
            if (count > 0) {
              line1.write(buffer, 0, count);
            }
          }
          line1.drain();
          line1.close();
        } catch (IOException e) {
          System.err.println("I/O problems: " + e);
          System.exit(-3);
        }
      }
    };
    Thread playThread = new Thread(runner);
    playThread.start();
  }
}





Continuously Playing a Sampled Audio File

 
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;
public class Main {
  public static void main(String[] argv) throws Exception {
    DataLine.Info info = null;
    Clip clip = (Clip) AudioSystem.getLine(info);
    clip.start();
    clip.loop(Clip.LOOP_CONTINUOUSLY);
    int numberOfPlays = 3;
    clip.loop(numberOfPlays - 1);
  }
}





Determine the duration of a Midi audio file

 
import java.io.File;
import java.net.URL;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.Sequence;
import javax.sound.midi.Sequencer;
public class Main {
  public static void main(String[] argv) throws Exception {
    Sequence sequence = MidiSystem.getSequence(new File("midiaudiofile"));
    sequence = MidiSystem.getSequence(new URL("http://hostname/midiaudiofile"));
    // Create a sequencer for the sequence
    Sequencer sequencer = MidiSystem.getSequencer();
    sequencer.open();
    sequencer.setSequence(sequence);
    double durationInSecs = sequencer.getMicrosecondLength() / 1000000.0;
  }
}





Determining the Duration of a Midi Audio File

 
import javax.sound.midi.MidiSystem;
import javax.sound.midi.Sequencer;
public class Main {
  public static void main(String[] argv) throws Exception {
    Sequencer sequencer = MidiSystem.getSequencer();
    sequencer.open();
    double durationInSecs = sequencer.getMicrosecondLength() / 1000000.0;
  }
}





Determining the Duration of a Sampled Audio File

 
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;
public class Main {
  public static void main(String[] argv) throws Exception {
    DataLine.Info info = null;
    Clip clip = (Clip) AudioSystem.getLine(info);
    double durationInSecs = clip.getBufferSize()
        / (clip.getFormat().getFrameSize() * clip.getFormat().getFrameRate());
  }
}





Determining the Encoding of a Sampled Audio File

 
import java.io.File;
import java.net.URL;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
public class Main {
  public static void main(String[] argv) throws Exception {
    AudioInputStream stream = AudioSystem.getAudioInputStream(new File(
        "audiofile"));
  }
}





Determining the File Format of a Midi Audio File

 
import java.io.File;
import java.net.URL;
import javax.sound.midi.MidiFileFormat;
import javax.sound.midi.MidiSystem;
public class Main {
  public static void main(String[] argv) throws Exception {
    // From file
    MidiFileFormat fformat = MidiSystem.getMidiFileFormat(new File("midifile"));
    // From URL
 //   fformat = MidiSystem.getMidiFileFormat(new URL("http://hostname/midifile"));
    // Get file format
    switch (fformat.getType()) {
    case 0:
      // mid
      break;
    case 1:
      // rmf
      break;
    }
  }
}





Determining the File Format of a Sampled Audio File

 
import java.io.File;
import java.net.URL;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioSystem;
public class Main {
  public static void main(String[] argv) throws Exception {
    AudioFileFormat fformat = AudioSystem.getAudioFileFormat(new File(
        "audiofile"));
    fformat = AudioSystem.getAudioFileFormat(new URL(
        "http://hostname/audiofile"));
    if (fformat.getType() == AudioFileFormat.Type.AIFC) {
    } else if (fformat.getType() == AudioFileFormat.Type.AIFF) {
    } else if (fformat.getType() == AudioFileFormat.Type.AU) {
    } else if (fformat.getType() == AudioFileFormat.Type.WAVE) {
    }
  }
}





Determining the Position of a Midi Sequencer

 
import javax.sound.midi.MidiSystem;
import javax.sound.midi.Sequencer;
public class Main {
  public static void main(String[] argv) throws Exception {
    Sequencer sequencer = MidiSystem.getSequencer();
    sequencer.open();
    double seconds = sequencer.getMicrosecondPosition() / 1000000.0;
  }
}





Determining the Position of a Sampled Audio Player

 
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;
public class Main {
  public static void main(String[] argv) throws Exception {
    DataLine.Info info = null;
    Clip clip = (Clip) AudioSystem.getLine(info);
    double timeInSeconds = clip.getMicrosecondPosition() / 1000000.0d;
  }
}





Determining When a Midi Audio Player Has Finished Playing

 
import javax.sound.midi.MetaEventListener;
import javax.sound.midi.MetaMessage;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.Sequencer;
public class Main {
  public static void main(String[] argv) throws Exception {
    Sequencer sequencer = MidiSystem.getSequencer();
    sequencer.open();
    sequencer.addMetaEventListener(new MetaEventListener() {
      public void meta(MetaMessage event) {
        if (event.getType() == 47) {
          // Sequencer is done playing
        }
      }
    });
  }
}





Determining When a Sampled Audio Player Has Finished Playing

 

import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineListener;
public class Main {
  public static void main(String[] argv) throws Exception {
    DataLine.Info info = null;
    Clip clip = (Clip) AudioSystem.getLine(info);
    clip.addLineListener(new LineListener() {
      public void update(LineEvent evt) {
        if (evt.getType() == LineEvent.Type.STOP) {
        }
      }
    });
  }
}





Duke Speaks

Duke Speaks Test

Float Control Component

 
 
import javax.sound.sampled.FloatControl;
public class Main {
  FloatControl control;
  public Main(FloatControl c) {
    control = c;
    control.setValue(3);
  }
}





Load and play Midi audio

 

import java.io.File;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.Sequence;
import javax.sound.midi.Sequencer;
public class Main {
  public static void main(String[] argv) throws Exception {
    // From file
    Sequence sequence = MidiSystem.getSequence(new File("midiaudiofile"));
    // From URL
    // sequence = MidiSystem.getSequence(new
    // URL("http://hostname/midiaudiofile"));
    // Create a sequencer for the sequence
    Sequencer sequencer = MidiSystem.getSequencer();
    sequencer.open();
    sequencer.setSequence(sequence);
    // Start playing
    sequencer.start();
  }
}





Load audio file From URL

 
import java.io.File;
import java.net.URL;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
    AudioSystem stream = AudioSystem.getAudioInputStream(new URL(
        "http://hostname/audiofile"));
    AudioFormat format = stream.getFormat();
    if (format.getEncoding() == AudioFormat.Encoding.ULAW) {
    } else if (format.getEncoding() == AudioFormat.Encoding.ULAW) {
    }
  }
}





Load image and sound from Jar file

 
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.URL;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.Line;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
public class Main extends JFrame {
  JButton button;
  ImageIcon buttonIcon;
  Clip buhClip;
  public Main() throws Exception {
    URL imageURL = getClass().getClassLoader().getResource("images/k.jpeg");
    buttonIcon = new ImageIcon(imageURL);
    button = new JButton("Click to Buh!", buttonIcon);
    button.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        if (buhClip != null) {
          buhClip.setFramePosition(0);
          buhClip.start();
        } else
          System.out.println("Couldn"t load sound");
      }
    });
    getContentPane().add(button);
    URL soundURL = getClass().getClassLoader().getResource("sounds/b.aiff");
    Line.Info linfo = new Line.Info(Clip.class);
    Line line = AudioSystem.getLine(linfo);
    buhClip = (Clip) line;
    AudioInputStream ais = AudioSystem.getAudioInputStream(soundURL);
    buhClip.open(ais);
  }
  public static final void main(String[] args) throws Exception {
    JFrame frame = new Main();
    frame.pack();
    frame.setVisible(true);
  }
}





Loading and Playing Midi Audio

 
import java.io.File;
import java.net.URL;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.Sequence;
import javax.sound.midi.Sequencer;
public class Main {
  public static void main(String[] argv) throws Exception {
    Sequence sequence = MidiSystem.getSequence(new File("midifile"));
    // From URL
    sequence = MidiSystem.getSequence(new URL("http://hostname/midifile"));
    // Create a sequencer for the sequence
    Sequencer sequencer = MidiSystem.getSequencer();
    sequencer.open();
    sequencer.setSequence(sequence);
    // Start playing
    sequencer.start();
  }
}





Loading and Playing Sampled Audio

 
import java.io.File;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;
public class Main {
  public static void main(String[] argv) throws Exception {
    AudioInputStream stream = AudioSystem.getAudioInputStream(new File(
        "audiofile"));
    // From URL
    // stream = AudioSystem.getAudioInputStream(new URL(
    // "http://hostname/audiofile"));
    AudioFormat format = stream.getFormat();
    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); // big endian
      stream = AudioSystem.getAudioInputStream(format, stream);
    }
    DataLine.Info info = new DataLine.Info(Clip.class, stream.getFormat(),
        ((int) stream.getFrameLength() * format.getFrameSize()));
    Clip clip = (Clip) AudioSystem.getLine(info);
    clip.open(stream);
    clip.start();
  }
}





Make your own Java Media Player to play media files

 
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import javax.swing.*;
import javax.media.*;
public class MediaPlayerDemo extends JFrame {
  public static void main(String args[]) {
    Player player;
    File file = new File("yourFile");
    player = Manager.createPlayer(file.toURI().toURL());
//    player.addControllerListener(new EventHandler());
    player.start(); // start player
    
    player.close();
    Component visual = player.getVisualComponent();
    Component control = player.getControlPanelComponent();
  }
}





Play an audio file from a JAR file

 

import java.io.InputStream;
import sun.audio.AudioPlayer;
import sun.audio.AudioStream;
public class Main {
  public static void main(String args[]) throws Throwable {
    InputStream in = Main.class.getResourceAsStream(args[0]);
    AudioStream as = new AudioStream(in);
    AudioPlayer.player.start(as);
    Thread.sleep(5000);
  }
}





Play a streaming Midi audio

 
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URL;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.Sequencer;
public class Main {
  public static void main(String[] argv) throws Exception {
    Sequencer sequencer = MidiSystem.getSequencer();
    sequencer.open();
    // From file
    InputStream input = new BufferedInputStream(new FileInputStream(new File("midiaudiofile")));
    // From URL
    input = new BufferedInputStream(new URL("http://hostname/rmffile").openStream());
    sequencer.setSequence(input);
    // Start playing
    sequencer.start();
  }
}





Playing Streaming Midi Audio

 
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URL;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.Sequencer;
public class Main {
  public static void main(String[] argv) throws Exception {
    Sequencer sequencer = MidiSystem.getSequencer();
    sequencer.open();
    // From file
    InputStream is = new BufferedInputStream(new FileInputStream(new File(
        "midifile")));
    // From URL
//    is = new BufferedInputStream(new URL("http://hostname/rmffile")
  //      .openStream());
    sequencer.setSequence(is);
    // Start playing
    sequencer.start();
  }
}





Playing Streaming Sampled Audio

 
import java.io.File;
import java.net.URL;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.SourceDataLine;
public class Main {
  public static void main(String[] argv) throws Exception {
    AudioInputStream stream = AudioSystem.getAudioInputStream(new File(
        "audiofile"));
//    stream = AudioSystem.getAudioInputStream(new URL(
  //      "http://hostname/audiofile"));
    AudioFormat format = stream.getFormat();
    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); // big endian
      stream = AudioSystem.getAudioInputStream(format, stream);
    }
    SourceDataLine.Info info = new DataLine.Info(SourceDataLine.class, stream
        .getFormat(), ((int) stream.getFrameLength() * format.getFrameSize()));
    SourceDataLine line = (SourceDataLine) AudioSystem.getLine(info);
    line.open(stream.getFormat());
    line.start();
    int numRead = 0;
    byte[] buf = new byte[line.getBufferSize()];
    while ((numRead = stream.read(buf, 0, buf.length)) >= 0) {
      int offset = 0;
      while (offset < numRead) {
        offset += line.write(buf, offset, numRead - offset);
      }
    }
    line.drain();
    line.stop();
  }
}





Play sound

 
import java.applet.Applet;
import java.applet.AudioClip;
public class NoisyButton {
  public static void main(String[] args) throws Exception {
    java.io.File file = new java.io.File("bark.aiff");
    AudioClip sound = Applet.newAudioClip(file.toURL());
    sound.play();
  }
}





Setting the Volume of a Sampled Audio Player

 
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.BooleanControl;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.FloatControl;
public class Main {
  public static void main(String[] argv) throws Exception {
    DataLine.Info info = null;
    Clip clip = (Clip) AudioSystem.getLine(info);
    FloatControl gainControl = (FloatControl) clip
        .getControl(FloatControl.Type.MASTER_GAIN);
    double gain = .5D; // number between 0 and 1 (loudest)
    float dB = (float) (Math.log(gain) / Math.log(10.0) * 20.0);
    gainControl.setValue(dB);
    BooleanControl muteControl = (BooleanControl) clip
        .getControl(BooleanControl.Type.MUTE);
    muteControl.setValue(true);
    muteControl.setValue(false);
  }
}





Setting the Volume of Playing Midi Audio

 
import javax.sound.midi.MidiChannel;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.Sequencer;
import javax.sound.midi.Synthesizer;
public class Main {
  public static void main(String[] argv) throws Exception {
    Sequencer sequencer = MidiSystem.getSequencer();
    sequencer.open();
    if (sequencer instanceof Synthesizer) {
      Synthesizer synthesizer = (Synthesizer) sequencer;
      MidiChannel[] channels = synthesizer.getChannels();
      // gain is a value between 0 and 1 (loudest)
      double gain = 0.9D;
      for (int i = 0; i < channels.length; i++) {
        channels[i].controlChange(7, (int) (gain * 127.0));
      }
    }
  }
}





Simple program to try out the new Sound stuff in JDK1.2

 
/*
 * Copyright (c) Ian F. Darwin, http://www.darwinsys.ru/, 1996-2002.
 * All rights reserved. Software written by Ian F. Darwin and others.
 * $Id: LICENSE,v 1.8 2004/02/09 03:33:38 ian Exp $
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS""
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 * 
 * Java, the Duke mascot, and all variants of Sun"s Java "steaming coffee
 * cup" logo are trademarks of Sun Microsystems. Sun"s, and James Gosling"s,
 * pioneering role in inventing and promulgating (and standardizing) the Java 
 * language and environment is gratefully acknowledged.
 * 
 * The pioneering role of Dennis Ritchie and Bjarne Stroustrup, of AT&T, for
 * inventing predecessor languages C and C++ is also gratefully acknowledged.
 */
import java.applet.Applet;
import java.net.URL;
/**
 * Simple program to try out the "new Sound" stuff in JDK1.2 -- allows
 * Applications, not just Applets, to play Sound.
 */
public class SoundPlay {
  static String defSounds[] = { "file:///javasrc/graphics/test.wav",
      "file:///music/midi/Beet5th.mid", };
  public static void main(String[] av) {
    if (av.length == 0)
      main(defSounds);
    else
      for (int i = 0; i < av.length; i++) {
        System.out.println("Starting " + av[i]);
        try {
          URL snd = new URL(av[i]);
          // open to see if works or throws exception, close to free
          // fd"s
          // snd.openConnection().getInputStream().close();
          Applet.newAudioClip(snd).play();
        } catch (Exception e) {
          System.err.println(e);
        }
      }
    // With this call, program exits before/during play.
    // Without it, on some versions, program hangs forever after play.
    // System.exit(0);
  }
}





Sound Applet

 
/* From http://java.sun.ru/docs/books/tutorial/index.html */
/*
 * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * -Redistribution of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *
 * -Redistribution in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation
 *  and/or other materials provided with the distribution.
 *
 * Neither the name of Sun Microsystems, Inc. or the names of contributors may
 * be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * This software is provided "AS IS," without a warranty of any kind. ALL
 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
 * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN")
 * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
 * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
 * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
 * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
 * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
 * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 *
 * You acknowledge that this software is not designed, licensed or intended
 * for use in the design, construction, operation or maintenance of any
 * nuclear facility.
 */
import java.applet.AudioClip;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.net.URL;
import javax.swing.JApplet;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JPanel;
public class SoundApplet extends JApplet implements ActionListener,
    ItemListener {
  AppletSoundList soundList;
  String auFile = "spacemusic.au";
  String aiffFile = "flute+hrn+mrmba.aif";
  String midiFile = "trippygaia1.mid";
  String rmfFile = "jungle.rmf";
  String wavFile = "bottle-open.wav";
  String chosenFile;
  AudioClip onceClip, loopClip;
  JComboBox formats;
  JButton playButton, loopButton, stopButton;
  boolean looping = false;
  public void init() {
    String[] fileTypes = { auFile, aiffFile, midiFile, rmfFile, wavFile };
    formats = new JComboBox(fileTypes);
    formats.setSelectedIndex(0);
    chosenFile = (String) formats.getSelectedItem();
    formats.addItemListener(this);
    playButton = new JButton("Play");
    playButton.addActionListener(this);
    loopButton = new JButton("Loop");
    loopButton.addActionListener(this);
    stopButton = new JButton("Stop");
    stopButton.addActionListener(this);
    stopButton.setEnabled(false);
    JPanel controlPanel = new JPanel();
    controlPanel.add(formats);
    controlPanel.add(playButton);
    controlPanel.add(loopButton);
    controlPanel.add(stopButton);
    getContentPane().add(controlPanel);
    startLoadingSounds();
  }
  public void itemStateChanged(ItemEvent e) {
    chosenFile = (String) formats.getSelectedItem();
    soundList.startLoading(chosenFile);
  }
  void startLoadingSounds() {
    //Start asynchronous sound loading.
    soundList = new AppletSoundList(this, getCodeBase());
    soundList.startLoading(auFile);
    soundList.startLoading(aiffFile);
    soundList.startLoading(midiFile);
    soundList.startLoading(rmfFile);
    soundList.startLoading(wavFile);
  }
  public void stop() {
    onceClip.stop(); //Cut short the one-time sound.
    if (looping) {
      loopClip.stop(); //Stop the sound loop.
    }
  }
  public void start() {
    if (looping) {
      loopClip.loop(); //Restart the sound loop.
    }
  }
  public void actionPerformed(ActionEvent event) {
    //PLAY BUTTON
    Object source = event.getSource();
    if (source == playButton) {
      //Try to get the AudioClip.
      onceClip = soundList.getClip(chosenFile);
      onceClip.play(); //Play it once.
      stopButton.setEnabled(true);
      showStatus("Playing sound " + chosenFile + ".");
      if (onceClip == null) {
        showStatus("Sound " + chosenFile + " not loaded yet.");
      }
      return;
    }
    //START LOOP BUTTON
    if (source == loopButton) {
      loopClip = soundList.getClip(chosenFile);
      looping = true;
      loopClip.loop(); //Start the sound loop.
      loopButton.setEnabled(false); //Disable loop button.
      stopButton.setEnabled(true);
      showStatus("Playing sound " + chosenFile + " continuously.");
      if (loopClip == null) {
        showStatus("Sound " + chosenFile + " not loaded yet.");
      }
      return;
    }
    //STOP LOOP BUTTON
    if (source == stopButton) {
      if (looping) {
        looping = false;
        loopClip.stop(); //Stop the sound loop.
        loopButton.setEnabled(true); //Enable start button.
      } else if (onceClip != null) {
        onceClip.stop();
      }
      stopButton.setEnabled(false);
      showStatus("Stopped playing " + chosenFile + ".");
      return;
    }
  }
}
//Loads and holds a bunch of audio files whose locations are specified
//relative to a fixed base URL.
class AppletSoundList extends java.util.Hashtable {
  JApplet applet;
  URL baseURL;
  public AppletSoundList(JApplet applet, URL baseURL) {
    super(5); //Initialize Hashtable with capacity of 5 entries.
    this.applet = applet;
    this.baseURL = baseURL;
  }
  public void startLoading(String relativeURL) {
    new AppletSoundLoader(applet, this, baseURL, relativeURL);
  }
  public AudioClip getClip(String relativeURL) {
    return (AudioClip) get(relativeURL);
  }
  public void putClip(AudioClip clip, String relativeURL) {
    put(relativeURL, clip);
  }
}
class AppletSoundLoader extends Thread {
  JApplet applet;
  AppletSoundList soundList;
  URL baseURL;
  String relativeURL;
  public AppletSoundLoader(JApplet applet, AppletSoundList soundList,
      URL baseURL, String relativeURL) {
    this.applet = applet;
    this.soundList = soundList;
    this.baseURL = baseURL;
    this.relativeURL = relativeURL;
    setPriority(MIN_PRIORITY);
    start();
  }
  public void run() {
    AudioClip audioClip = applet.getAudioClip(baseURL, relativeURL);
    soundList.putClip(audioClip, relativeURL);
  }
}





Sound Application

 
/* From http://java.sun.ru/docs/books/tutorial/index.html */
/*
 * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * -Redistribution of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *
 * -Redistribution in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation
 *  and/or other materials provided with the distribution.
 *
 * Neither the name of Sun Microsystems, Inc. or the names of contributors may
 * be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * This software is provided "AS IS," without a warranty of any kind. ALL
 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
 * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN")
 * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
 * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
 * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
 * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
 * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
 * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 *
 * You acknowledge that this software is not designed, licensed or intended
 * for use in the design, construction, operation or maintenance of any
 * nuclear facility.
 */
import java.applet.Applet;
import java.applet.AudioClip;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.net.MalformedURLException;
import java.net.URL;
import javax.swing.JApplet;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class SoundApplication extends JPanel implements ActionListener,
    ItemListener {
  SoundList soundList;
  String auFile = "spacemusic.au";
  String aiffFile = "flute+hrn+mrmba.aif";
  String midiFile = "trippygaia1.mid";
  String rmfFile = "jungle.rmf";
  String wavFile = "bottle-open.wav";
  String chosenFile;
  AudioClip onceClip, loopClip;
  URL codeBase;
  JComboBox formats;
  JButton playButton, loopButton, stopButton;
  JLabel status;
  boolean looping = false;
  public SoundApplication() {
    String[] fileTypes = { auFile, aiffFile, midiFile, rmfFile, wavFile };
    formats = new JComboBox(fileTypes);
    formats.setSelectedIndex(0);
    chosenFile = (String) formats.getSelectedItem();
    formats.addItemListener(this);
    playButton = new JButton("Play");
    playButton.addActionListener(this);
    loopButton = new JButton("Loop");
    loopButton.addActionListener(this);
    stopButton = new JButton("Stop");
    stopButton.addActionListener(this);
    stopButton.setEnabled(false);
    status = new JLabel(
        "Click Play or Loop to play the selected sound file.");
    JPanel controlPanel = new JPanel();
    controlPanel.add(formats);
    controlPanel.add(playButton);
    controlPanel.add(loopButton);
    controlPanel.add(stopButton);
    JPanel statusPanel = new JPanel();
    statusPanel.add(status);
    add(controlPanel);
    add(statusPanel);
    startLoadingSounds();
  }
  public void itemStateChanged(ItemEvent e) {
    chosenFile = (String) formats.getSelectedItem();
    soundList.startLoading(chosenFile);
  }
  void startLoadingSounds() {
    //Start asynchronous sound loading.
    try {
      codeBase = new URL("file:" + System.getProperty("user.dir") + "/");
    } catch (MalformedURLException e) {
      System.err.println(e.getMessage());
    }
    soundList = new SoundList(codeBase);
    soundList.startLoading(auFile);
    soundList.startLoading(aiffFile);
    soundList.startLoading(midiFile);
    soundList.startLoading(rmfFile);
    soundList.startLoading(wavFile);
  }
  public void stop() {
    onceClip.stop(); //Cut short the one-time sound.
    if (looping) {
      loopClip.stop(); //Stop the sound loop.
    }
  }
  public void start() {
    if (looping) {
      loopClip.loop(); //Restart the sound loop.
    }
  }
  public void actionPerformed(ActionEvent event) {
    //PLAY BUTTON
    Object source = event.getSource();
    if (source == playButton) {
      //Try to get the AudioClip.
      onceClip = soundList.getClip(chosenFile);
      stopButton.setEnabled(true);
      onceClip.play(); //Play it once.
      status.setText("Playing sound " + chosenFile + ".");
      if (onceClip == null) {
        status.setText("Sound " + chosenFile + " not loaded yet.");
      }
      return;
    }
    //START LOOP BUTTON
    if (source == loopButton) {
      loopClip = soundList.getClip(chosenFile);
      looping = true;
      loopClip.loop(); //Start the sound loop.
      loopButton.setEnabled(false); //Disable start button.
      stopButton.setEnabled(true);
      status.setText("Playing sound " + chosenFile + " continuously.");
      if (loopClip == null) {
        status.setText("Sound " + chosenFile + " not loaded yet.");
      }
      return;
    }
    //STOP LOOP BUTTON
    if (source == stopButton) {
      if (looping) {
        looping = false;
        loopClip.stop(); //Stop the sound loop.
        loopButton.setEnabled(true); //Enable start button.
      } else if (onceClip != null) {
        onceClip.stop();
      }
      stopButton.setEnabled(false);
      status.setText("Stopped playing " + chosenFile + ".");
      return;
    }
  }
  public static void main(String s[]) {
    WindowListener l = new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
        System.exit(0);
      }
    };
    JFrame f = new JFrame("SoundApplication");
    f.addWindowListener(l);
    f.getContentPane().add(new SoundApplication());
    f.setSize(new Dimension(400, 100));
    f.show();
  }
}
/**
 * Loads and holds a bunch of audio files whose locations are specified relative
 * to a fixed base URL.
 */
class SoundList extends java.util.Hashtable {
  JApplet applet;
  URL baseURL;
  public SoundList(URL baseURL) {
    super(5); //Initialize Hashtable with capacity of 5 entries.
    this.baseURL = baseURL;
  }
  public void startLoading(String relativeURL) {
    new SoundLoader(this, baseURL, relativeURL);
  }
  public AudioClip getClip(String relativeURL) {
    return (AudioClip) get(relativeURL);
  }
  public void putClip(AudioClip clip, String relativeURL) {
    put(relativeURL, clip);
  }
}
class SoundLoader extends Thread {
  SoundList soundList;
  URL completeURL;
  String relativeURL;
  public SoundLoader(SoundList soundList, URL baseURL, String relativeURL) {
    this.soundList = soundList;
    try {
      completeURL = new URL(baseURL, relativeURL);
    } catch (MalformedURLException e) {
      System.err.println(e.getMessage());
    }
    this.relativeURL = relativeURL;
    setPriority(MIN_PRIORITY);
    start();
  }
  public void run() {
    AudioClip audioClip = Applet.newAudioClip(completeURL);
    soundList.putClip(audioClip, relativeURL);
  }
}





Sound Manager Test

 
       /*
DEVELOPING GAME IN JAVA 
Caracteristiques
Editeur : NEW RIDERS 
Auteur : BRACKEEN 
Parution : 09 2003 
Pages : 972 
Isbn : 1-59273-005-1 
Reliure : Paperback 
Disponibilite : Disponible a la librairie 
*/

import java.awt.Color;
import java.awt.Container;
import java.awt.DisplayMode;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.LinkedList;
import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MetaEventListener;
import javax.sound.midi.MetaMessage;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Sequence;
import javax.sound.midi.Sequencer;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.Mixer;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.UnsupportedAudioFileException;
import javax.swing.AbstractButton;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JToggleButton;
import javax.swing.RepaintManager;
/**
 * The SoundManagerTest demonstrates the functionality of the SoundManager
 * class. It provides the following demos:
 * <ul>
 * <li>Playing a Midi sequence.
 * <li>Toggle a track of a playing Midi sequence.
 * <li>Playing a sound.
 * <li>Playing a Sound with an Echo filter.
 * <li>Looping a sound.
 * <li>Playing the maximum number of sounds at once.
 * <li>Pausing all sounds.
 * </ul>
 * <p>
 * This class wasn"t listed in the book ;)
 * 
 * @see SoundManager
 * @see Sound
 * @see SoundFilter
 */
public class SoundManagerTest extends GameCore implements ActionListener {
  public static void main(String[] args) {
    new SoundManagerTest().run();
  }
  // uncompressed, 44100Hz, 16-bit, mono, signed, little-endian
  private static final AudioFormat PLAYBACK_FORMAT = new AudioFormat(44100,
      16, 1, true, false);
  private static final int MANY_SOUNDS_COUNT = SoundManager
      .getMaxSimultaneousSounds(PLAYBACK_FORMAT);
  private static final int DRUM_TRACK = 1;
  private static final String EXIT = "Exit";
  private static final String PAUSE = "Pause";
  private static final String PLAY_MUSIC = "Play Music";
  private static final String MUSIC_DRUMS = "Toggle Drums";
  private static final String PLAY_SOUND = "Play Sound";
  private static final String PLAY_ECHO_SOUND = "Play Echoed Sound";
  private static final String PLAY_LOOPING_SOUND = "Play Looping Sound";
  private static final String PLAY_MANY_SOUNDS = "Play " + MANY_SOUNDS_COUNT
      + " Sounds";
  private SoundManager soundManager;
  private MidiPlayer midiPlayer;
  private Sequence music;
  private Sound boop;
  private Sound bzz;
  private InputStream lastloopingSound;
  public void init() {
    super.init();
    initSounds();
    initUI();
  }
  /**
   * Loads sounds and music.
   */
  public void initSounds() {
    midiPlayer = new MidiPlayer();
    soundManager = new SoundManager(PLAYBACK_FORMAT);
    music = midiPlayer.getSequence("../sounds/music.midi");
    boop = soundManager.getSound("../sounds/boop.wav");
    bzz = soundManager.getSound("../sounds/fly-bzz.wav");
  }
  /**
   * Creates the UI, which is a row of buttons.
   */
  public void initUI() {
    // make sure Swing components don"t paint themselves
    NullRepaintManager.install();
    JFrame frame = super.screen.getFullScreenWindow();
    Container contentPane = frame.getContentPane();
    contentPane.setLayout(new FlowLayout());
    contentPane.add(createButton(PAUSE, true));
    contentPane.add(createButton(PLAY_MUSIC, true));
    contentPane.add(createButton(MUSIC_DRUMS, false));
    contentPane.add(createButton(PLAY_SOUND, false));
    contentPane.add(createButton(PLAY_ECHO_SOUND, false));
    contentPane.add(createButton(PLAY_LOOPING_SOUND, true));
    contentPane.add(createButton(PLAY_MANY_SOUNDS, false));
    contentPane.add(createButton(EXIT, false));
    // explicitly layout components (needed on some systems)
    frame.validate();
  }
  /**
   * Draws all Swing components
   */
  public void draw(Graphics2D g) {
    JFrame frame = super.screen.getFullScreenWindow();
    frame.getLayeredPane().paintComponents(g);
  }
  /**
   * Creates a button (either JButton or JToggleButton).
   */
  public AbstractButton createButton(String name, boolean canToggle) {
    AbstractButton button;
    if (canToggle) {
      button = new JToggleButton(name);
    } else {
      button = new JButton(name);
    }
    button.addActionListener(this);
    button.setIgnoreRepaint(true);
    button.setFocusable(false);
    return button;
  }
  /**
   * Performs actions when a button is pressed.
   */
  public void actionPerformed(ActionEvent e) {
    String command = e.getActionCommand();
    AbstractButton button = (AbstractButton) e.getSource();
    if (command == EXIT) {
      midiPlayer.close();
      soundManager.close();
      stop();
    } else if (command == PAUSE) {
      // pause the sound
      soundManager.setPaused(button.isSelected());
      midiPlayer.setPaused(button.isSelected());
    } else if (command == PLAY_MUSIC) {
      // toggle music on or off
      if (button.isSelected()) {
        midiPlayer.play(music, true);
      } else {
        midiPlayer.stop();
      }
    } else if (command == MUSIC_DRUMS) {
      // toggle drums on or off
      Sequencer sequencer = midiPlayer.getSequencer();
      if (sequencer != null) {
        boolean mute = sequencer.getTrackMute(DRUM_TRACK);
        sequencer.setTrackMute(DRUM_TRACK, !mute);
      }
    } else if (command == PLAY_SOUND) {
      // play a normal sound
      soundManager.play(boop);
    } else if (command == PLAY_ECHO_SOUND) {
      // play a sound with an echo
      EchoFilter filter = new EchoFilter(11025, .6f);
      soundManager.play(boop, filter, false);
    } else if (command == PLAY_LOOPING_SOUND) {
      // play or stop the looping sound
      if (button.isSelected()) {
        lastloopingSound = soundManager.play(bzz, null, true);
      } else if (lastloopingSound != null) {
        try {
          lastloopingSound.close();
        } catch (IOException ex) {
        }
        lastloopingSound = null;
      }
    } else if (command == PLAY_MANY_SOUNDS) {
      // play several sounds at once, to test the system
      for (int i = 0; i < MANY_SOUNDS_COUNT; i++) {
        soundManager.play(boop);
      }
    }
  }
}
/**
 * The SoundManager class manages sound playback. The SoundManager is a
 * ThreadPool, with each thread playing back one sound at a time. This allows
 * the SoundManager to easily limit the number of simultaneous sounds being
 * played.
 * <p>
 * Possible ideas to extend this class:
 * <ul>
 * <li>add a setMasterVolume() method, which uses Controls to set the volume
 * for each line.
 * <li>don"t play a sound if more than, say, 500ms has passed since the request
 * to play
 * </ul>
 */
class SoundManager extends ThreadPool {
  private AudioFormat playbackFormat;
  private ThreadLocal localLine;
  private ThreadLocal localBuffer;
  private Object pausedLock;
  private boolean paused;
  /**
   * Creates a new SoundManager using the maximum number of simultaneous
   * sounds.
   */
  public SoundManager(AudioFormat playbackFormat) {
    this(playbackFormat, getMaxSimultaneousSounds(playbackFormat));
  }
  /**
   * Creates a new SoundManager with the specified maximum number of
   * simultaneous sounds.
   */
  public SoundManager(AudioFormat playbackFormat, int maxSimultaneousSounds) {
    super(Math.min(maxSimultaneousSounds,
        getMaxSimultaneousSounds(playbackFormat)));
    this.playbackFormat = playbackFormat;
    localLine = new ThreadLocal();
    localBuffer = new ThreadLocal();
    pausedLock = new Object();
    // notify threads in pool it"s ok to start
    synchronized (this) {
      notifyAll();
    }
  }
  /**
   * Gets the maximum number of simultaneous sounds with the specified
   * AudioFormat that the default mixer can play.
   */
  public static int getMaxSimultaneousSounds(AudioFormat playbackFormat) {
    DataLine.Info lineInfo = new DataLine.Info(SourceDataLine.class,
        playbackFormat);
    Mixer mixer = AudioSystem.getMixer(null);
    return mixer.getMaxLines(lineInfo);
  }
  /**
   * Does any clean up before closing.
   */
  protected void cleanUp() {
    // signal to unpause
    setPaused(false);
    // close the mixer (stops any running sounds)
    Mixer mixer = AudioSystem.getMixer(null);
    if (mixer.isOpen()) {
      mixer.close();
    }
  }
  public void close() {
    cleanUp();
    super.close();
  }
  public void join() {
    cleanUp();
    super.join();
  }
  /**
   * Sets the paused state. Sounds may not pause immediately.
   */
  public void setPaused(boolean paused) {
    if (this.paused != paused) {
      synchronized (pausedLock) {
        this.paused = paused;
        if (!paused) {
          // restart sounds
          pausedLock.notifyAll();
        }
      }
    }
  }
  /**
   * Returns the paused state.
   */
  public boolean isPaused() {
    return paused;
  }
  /**
   * Loads a Sound from the file system. Returns null if an error occurs.
   */
  public Sound getSound(String filename) {
    return getSound(getAudioInputStream(filename));
  }
  /**
   * Loads a Sound from an input stream. Returns null if an error occurs.
   */
  public Sound getSound(InputStream is) {
    return getSound(getAudioInputStream(is));
  }
  /**
   * Loads a Sound from an AudioInputStream.
   */
  public Sound getSound(AudioInputStream audioStream) {
    if (audioStream == null) {
      return null;
    }
    // get the number of bytes to read
    int length = (int) (audioStream.getFrameLength() * audioStream
        .getFormat().getFrameSize());
    // read the entire stream
    byte[] samples = new byte[length];
    DataInputStream is = new DataInputStream(audioStream);
    try {
      is.readFully(samples);
      is.close();
    } catch (IOException ex) {
      ex.printStackTrace();
    }
    // return the samples
    return new Sound(samples);
  }
  /**
   * Creates an AudioInputStream from a sound from the file system.
   */
  public AudioInputStream getAudioInputStream(String filename) {
    try {
      return getAudioInputStream(new FileInputStream(filename));
    } catch (IOException ex) {
      ex.printStackTrace();
      return null;
    }
  }
  /**
   * Creates an AudioInputStream from a sound from an input stream
   */
  public AudioInputStream getAudioInputStream(InputStream is) {
    try {
      if (!is.markSupported()) {
        is = new BufferedInputStream(is);
      }
      // open the source stream
      AudioInputStream source = AudioSystem.getAudioInputStream(is);
      // convert to playback format
      return AudioSystem.getAudioInputStream(playbackFormat, source);
    } catch (UnsupportedAudioFileException ex) {
      ex.printStackTrace();
    } catch (IOException ex) {
      ex.printStackTrace();
    } catch (IllegalArgumentException ex) {
      ex.printStackTrace();
    }
    return null;
  }
  /**
   * Plays a sound. This method returns immediately.
   */
  public InputStream play(Sound sound) {
    return play(sound, null, false);
  }
  /**
   * Plays a sound with an optional SoundFilter, and optionally looping. This
   * method returns immediately.
   */
  public InputStream play(Sound sound, SoundFilter filter, boolean loop) {
    InputStream is;
    if (sound != null) {
      if (loop) {
        is = new LoopingByteInputStream(sound.getSamples());
      } else {
        is = new ByteArrayInputStream(sound.getSamples());
      }
      return play(is, filter);
    }
    return null;
  }
  /**
   * Plays a sound from an InputStream. This method returns immediately.
   */
  public InputStream play(InputStream is) {
    return play(is, null);
  }
  /**
   * Plays a sound from an InputStream with an optional sound filter. This
   * method returns immediately.
   */
  public InputStream play(InputStream is, SoundFilter filter) {
    if (is != null) {
      if (filter != null) {
        is = new FilteredSoundStream(is, filter);
      }
      runTask(new SoundPlayer(is));
    }
    return is;
  }
  /**
   * Signals that a PooledThread has started. Creates the Thread"s line and
   * buffer.
   */
  protected void threadStarted() {
    // wait for the SoundManager constructor to finish
    synchronized (this) {
      try {
        wait();
      } catch (InterruptedException ex) {
      }
    }
    // use a short, 100ms (1/10th sec) buffer for filters that
    // change in real-time
    int bufferSize = playbackFormat.getFrameSize()
        * Math.round(playbackFormat.getSampleRate() / 10);
    // create, open, and start the line
    SourceDataLine line;
    DataLine.Info lineInfo = new DataLine.Info(SourceDataLine.class,
        playbackFormat);
    try {
      line = (SourceDataLine) AudioSystem.getLine(lineInfo);
      line.open(playbackFormat, bufferSize);
    } catch (LineUnavailableException ex) {
      // the line is unavailable - signal to end this thread
      Thread.currentThread().interrupt();
      return;
    }
    line.start();
    // create the buffer
    byte[] buffer = new byte[bufferSize];
    // set this thread"s locals
    localLine.set(line);
    localBuffer.set(buffer);
  }
  /**
   * Signals that a PooledThread has stopped. Drains and closes the Thread"s
   * Line.
   */
  protected void threadStopped() {
    SourceDataLine line = (SourceDataLine) localLine.get();
    if (line != null) {
      line.drain();
      line.close();
    }
  }
  /**
   * The SoundPlayer class is a task for the PooledThreads to run. It receives
   * the threads"s Line and byte buffer from the ThreadLocal variables and
   * plays a sound from an InputStream.
   * <p>
   * This class only works when called from a PooledThread.
   */
  protected class SoundPlayer implements Runnable {
    private InputStream source;
    public SoundPlayer(InputStream source) {
      this.source = source;
    }
    public void run() {
      // get line and buffer from ThreadLocals
      SourceDataLine line = (SourceDataLine) localLine.get();
      byte[] buffer = (byte[]) localBuffer.get();
      if (line == null || buffer == null) {
        // the line is unavailable
        return;
      }
      // copy data to the line
      try {
        int numBytesRead = 0;
        while (numBytesRead != -1) {
          // if paused, wait until unpaused
          synchronized (pausedLock) {
            if (paused) {
              try {
                pausedLock.wait();
              } catch (InterruptedException ex) {
                return;
              }
            }
          }
          // copy data
          numBytesRead = source.read(buffer, 0, buffer.length);
          if (numBytesRead != -1) {
            line.write(buffer, 0, numBytesRead);
          }
        }
      } catch (IOException ex) {
        ex.printStackTrace();
      }
    }
  }
}
class MidiPlayer implements MetaEventListener {
  // Midi meta event
  public static final int END_OF_TRACK_MESSAGE = 47;
  private Sequencer sequencer;
  private boolean loop;
  private boolean paused;
  /**
   * Creates a new MidiPlayer object.
   */
  public MidiPlayer() {
    try {
      sequencer = MidiSystem.getSequencer();
      sequencer.open();
      sequencer.addMetaEventListener(this);
    } catch (MidiUnavailableException ex) {
      sequencer = null;
    }
  }
  /**
   * Loads a sequence from the file system. Returns null if an error occurs.
   */
  public Sequence getSequence(String filename) {
    try {
      return getSequence(new FileInputStream(filename));
    } catch (IOException ex) {
      ex.printStackTrace();
      return null;
    }
  }
  /**
   * Loads a sequence from an input stream. Returns null if an error occurs.
   */
  public Sequence getSequence(InputStream is) {
    try {
      if (!is.markSupported()) {
        is = new BufferedInputStream(is);
      }
      Sequence s = MidiSystem.getSequence(is);
      is.close();
      return s;
    } catch (InvalidMidiDataException ex) {
      ex.printStackTrace();
      return null;
    } catch (IOException ex) {
      ex.printStackTrace();
      return null;
    }
  }
  /**
   * Plays a sequence, optionally looping. This method returns immediately.
   * The sequence is not played if it is invalid.
   */
  public void play(Sequence sequence, boolean loop) {
    if (sequencer != null && sequence != null && sequencer.isOpen()) {
      try {
        sequencer.setSequence(sequence);
        sequencer.start();
        this.loop = loop;
      } catch (InvalidMidiDataException ex) {
        ex.printStackTrace();
      }
    }
  }
  /**
   * This method is called by the sound system when a meta event occurs. In
   * this case, when the end-of-track meta event is received, the sequence is
   * restarted if looping is on.
   */
  public void meta(MetaMessage event) {
    if (event.getType() == END_OF_TRACK_MESSAGE) {
      if (sequencer != null && sequencer.isOpen() && loop) {
        sequencer.start();
      }
    }
  }
  /**
   * Stops the sequencer and resets its position to 0.
   */
  public void stop() {
    if (sequencer != null && sequencer.isOpen()) {
      sequencer.stop();
      sequencer.setMicrosecondPosition(0);
    }
  }
  /**
   * Closes the sequencer.
   */
  public void close() {
    if (sequencer != null && sequencer.isOpen()) {
      sequencer.close();
    }
  }
  /**
   * Gets the sequencer.
   */
  public Sequencer getSequencer() {
    return sequencer;
  }
  /**
   * Sets the paused state. Music may not immediately pause.
   */
  public void setPaused(boolean paused) {
    if (this.paused != paused && sequencer != null && sequencer.isOpen()) {
      this.paused = paused;
      if (paused) {
        sequencer.stop();
      } else {
        sequencer.start();
      }
    }
  }
  /**
   * Returns the paused state.
   */
  public boolean isPaused() {
    return paused;
  }
}
/**
 * Simple abstract class used for testing. Subclasses should implement the
 * draw() method.
 */
abstract class GameCore {
  protected static final int FONT_SIZE = 24;
  private static final DisplayMode POSSIBLE_MODES[] = {
      new DisplayMode(800, 600, 32, 0), new DisplayMode(800, 600, 24, 0),
      new DisplayMode(800, 600, 16, 0), new DisplayMode(640, 480, 32, 0),
      new DisplayMode(640, 480, 24, 0), new DisplayMode(640, 480, 16, 0) };
  private boolean isRunning;
  protected ScreenManager screen;
  /**
   * Signals the game loop that it"s time to quit
   */
  public void stop() {
    isRunning = false;
  }
  /**
   * Calls init() and gameLoop()
   */
  public void run() {
    try {
      init();
      gameLoop();
    } finally {
      screen.restoreScreen();
    }
  }
  /**
   * Sets full screen mode and initiates and objects.
   */
  public void init() {
    screen = new ScreenManager();
    DisplayMode displayMode = screen
        .findFirstCompatibleMode(POSSIBLE_MODES);
    screen.setFullScreen(displayMode);
    Window window = screen.getFullScreenWindow();
    window.setFont(new Font("Dialog", Font.PLAIN, FONT_SIZE));
    window.setBackground(Color.blue);
    window.setForeground(Color.white);
    isRunning = true;
  }
  public Image loadImage(String fileName) {
    return new ImageIcon(fileName).getImage();
  }
  /**
   * Runs through the game loop until stop() is called.
   */
  public void gameLoop() {
    long startTime = System.currentTimeMillis();
    long currTime = startTime;
    while (isRunning) {
      long elapsedTime = System.currentTimeMillis() - currTime;
      currTime += elapsedTime;
      // update
      update(elapsedTime);
      // draw the screen
      Graphics2D g = screen.getGraphics();
      draw(g);
      g.dispose();
      screen.update();
      // take a nap
      try {
        Thread.sleep(20);
      } catch (InterruptedException ex) {
      }
    }
  }
  /**
   * Updates the state of the game/animation based on the amount of elapsed
   * time that has passed.
   */
  public void update(long elapsedTime) {
    // do nothing
  }
  /**
   * Draws to the screen. Subclasses must override this method.
   */
  public abstract void draw(Graphics2D g);
}
/**
 * The NullRepaintManager is a RepaintManager that doesn"t do any repainting.
 * Useful when all the rendering is done manually by the application.
 */
class NullRepaintManager extends RepaintManager {
  /**
   * Installs the NullRepaintManager.
   */
  public static void install() {
    RepaintManager repaintManager = new NullRepaintManager();
    repaintManager.setDoubleBufferingEnabled(false);
    RepaintManager.setCurrentManager(repaintManager);
  }
  public void addInvalidComponent(JComponent c) {
    // do nothing
  }
  public void addDirtyRegion(JComponent c, int x, int y, int w, int h) {
    // do nothing
  }
  public void markCompletelyDirty(JComponent c) {
    // do nothing
  }
  public void paintDirtyRegions() {
    // do nothing
  }
}
/**
 * The ScreenManager class manages initializing and displaying full screen
 * graphics modes.
 */
class ScreenManager {
  private GraphicsDevice device;
  /**
   * Creates a new ScreenManager object.
   */
  public ScreenManager() {
    GraphicsEnvironment environment = GraphicsEnvironment
        .getLocalGraphicsEnvironment();
    device = environment.getDefaultScreenDevice();
  }
  /**
   * Returns a list of compatible display modes for the default device on the
   * system.
   */
  public DisplayMode[] getCompatibleDisplayModes() {
    return device.getDisplayModes();
  }
  /**
   * Returns the first compatible mode in a list of modes. Returns null if no
   * modes are compatible.
   */
  public DisplayMode findFirstCompatibleMode(DisplayMode modes[]) {
    DisplayMode goodModes[] = device.getDisplayModes();
    for (int i = 0; i < modes.length; i++) {
      for (int j = 0; j < goodModes.length; j++) {
        if (displayModesMatch(modes[i], goodModes[j])) {
          return modes[i];
        }
      }
    }
    return null;
  }
  /**
   * Returns the current display mode.
   */
  public DisplayMode getCurrentDisplayMode() {
    return device.getDisplayMode();
  }
  /**
   * Determines if two display modes "match". Two display modes match if they
   * have the same resolution, bit depth, and refresh rate. The bit depth is
   * ignored if one of the modes has a bit depth of
   * DisplayMode.BIT_DEPTH_MULTI. Likewise, the refresh rate is ignored if one
   * of the modes has a refresh rate of DisplayMode.REFRESH_RATE_UNKNOWN.
   */
  public boolean displayModesMatch(DisplayMode mode1, DisplayMode mode2)
  {
    if (mode1.getWidth() != mode2.getWidth()
        || mode1.getHeight() != mode2.getHeight()) {
      return false;
    }
    if (mode1.getBitDepth() != DisplayMode.BIT_DEPTH_MULTI
        && mode2.getBitDepth() != DisplayMode.BIT_DEPTH_MULTI
        && mode1.getBitDepth() != mode2.getBitDepth()) {
      return false;
    }
    if (mode1.getRefreshRate() != DisplayMode.REFRESH_RATE_UNKNOWN
        && mode2.getRefreshRate() != DisplayMode.REFRESH_RATE_UNKNOWN
        && mode1.getRefreshRate() != mode2.getRefreshRate()) {
      return false;
    }
    return true;
  }
  /**
   * Enters full screen mode and changes the display mode. If the specified
   * display mode is null or not compatible with this device, or if the
   * display mode cannot be changed on this system, the current display mode
   * is used.
   * <p>
   * The display uses a BufferStrategy with 2 buffers.
   */
  public void setFullScreen(DisplayMode displayMode) {
    final JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setUndecorated(true);
    frame.setIgnoreRepaint(true);
    frame.setResizable(false);
    device.setFullScreenWindow(frame);
    if (displayMode != null && device.isDisplayChangeSupported()) {
      try {
        device.setDisplayMode(displayMode);
      } catch (IllegalArgumentException ex) {
      }
      // fix for mac os x
      frame.setSize(displayMode.getWidth(), displayMode.getHeight());
    }
    // avoid potential deadlock in 1.4.1_02
    try {
      EventQueue.invokeAndWait(new Runnable() {
        public void run() {
          frame.createBufferStrategy(2);
        }
      });
    } catch (InterruptedException ex) {
      // ignore
    } catch (InvocationTargetException ex) {
      // ignore
    }
  }
  /**
   * Gets the graphics context for the display. The ScreenManager uses double
   * buffering, so applications must call update() to show any graphics drawn.
   * <p>
   * The application must dispose of the graphics object.
   */
  public Graphics2D getGraphics() {
    Window window = device.getFullScreenWindow();
    if (window != null) {
      BufferStrategy strategy = window.getBufferStrategy();
      return (Graphics2D) strategy.getDrawGraphics();
    } else {
      return null;
    }
  }
  /**
   * Updates the display.
   */
  public void update() {
    Window window = device.getFullScreenWindow();
    if (window != null) {
      BufferStrategy strategy = window.getBufferStrategy();
      if (!strategy.contentsLost()) {
        strategy.show();
      }
    }
    // Sync the display on some systems.
    // (on Linux, this fixes event queue problems)
    Toolkit.getDefaultToolkit().sync();
  }
  /**
   * Returns the window currently used in full screen mode. Returns null if
   * the device is not in full screen mode.
   */
  public JFrame getFullScreenWindow() {
    return (JFrame) device.getFullScreenWindow();
  }
  /**
   * Returns the width of the window currently used in full screen mode.
   * Returns 0 if the device is not in full screen mode.
   */
  public int getWidth() {
    Window window = device.getFullScreenWindow();
    if (window != null) {
      return window.getWidth();
    } else {
      return 0;
    }
  }
  /**
   * Returns the height of the window currently used in full screen mode.
   * Returns 0 if the device is not in full screen mode.
   */
  public int getHeight() {
    Window window = device.getFullScreenWindow();
    if (window != null) {
      return window.getHeight();
    } else {
      return 0;
    }
  }
  /**
   * Restores the screen"s display mode.
   */
  public void restoreScreen() {
    Window window = device.getFullScreenWindow();
    if (window != null) {
      window.dispose();
    }
    device.setFullScreenWindow(null);
  }
  /**
   * Creates an image compatible with the current display.
   */
  public BufferedImage createCompatibleImage(int w, int h, int transparancy) {
    Window window = device.getFullScreenWindow();
    if (window != null) {
      GraphicsConfiguration gc = window.getGraphicsConfiguration();
      return gc.createCompatibleImage(w, h, transparancy);
    }
    return null;
  }
}
/**
 * The EchoFilter class is a SoundFilter that emulates an echo.
 * 
 * @see FilteredSoundStream
 */
class EchoFilter extends SoundFilter {
  private short[] delayBuffer;
  private int delayBufferPos;
  private float decay;
  /**
   * Creates an EchoFilter with the specified number of delay samples and the
   * specified decay rate.
   * <p>
   * The number of delay samples specifies how long before the echo is
   * initially heard. For a 1 second echo with mono, 44100Hz sound, use 44100
   * delay samples.
   * <p>
   * The decay value is how much the echo has decayed from the source. A decay
   * value of .5 means the echo heard is half as loud as the source.
   */
  public EchoFilter(int numDelaySamples, float decay) {
    delayBuffer = new short[numDelaySamples];
    this.decay = decay;
  }
  /**
   * Gets the remaining size, in bytes, of samples that this filter can echo
   * after the sound is done playing. Ensures that the sound will have decayed
   * to below 1% of maximum volume (amplitude).
   */
  public int getRemainingSize() {
    float finalDecay = 0.01f;
    // derived from Math.pow(decay,x) <= finalDecay
    int numRemainingBuffers = (int) Math.ceil(Math.log(finalDecay)
        / Math.log(decay));
    int bufferSize = delayBuffer.length * 2;
    return bufferSize * numRemainingBuffers;
  }
  /**
   * Clears this EchoFilter"s internal delay buffer.
   */
  public void reset() {
    for (int i = 0; i < delayBuffer.length; i++) {
      delayBuffer[i] = 0;
    }
    delayBufferPos = 0;
  }
  /**
   * Filters the sound samples to add an echo. The samples played are added to
   * the sound in the delay buffer multipied by the decay rate. The result is
   * then stored in the delay buffer, so multiple echoes are heard.
   */
  public void filter(byte[] samples, int offset, int length) {
    for (int i = offset; i < offset + length; i += 2) {
      // update the sample
      short oldSample = getSample(samples, i);
      short newSample = (short) (oldSample + decay
          * delayBuffer[delayBufferPos]);
      setSample(samples, i, newSample);
      // update the delay buffer
      delayBuffer[delayBufferPos] = newSample;
      delayBufferPos++;
      if (delayBufferPos == delayBuffer.length) {
        delayBufferPos = 0;
      }
    }
  }
}
/**
 * A abstract class designed to filter sound samples. Since SoundFilters may use
 * internal buffering of samples, a new SoundFilter object should be created for
 * every sound played. However, SoundFilters can be reused after they are
 * finished by called the reset() method.
 * <p>
 * Assumes all samples are 16-bit, signed, little-endian format.
 * 
 * @see FilteredSoundStream
 */
abstract class SoundFilter {
  /**
   * Resets this SoundFilter. Does nothing by default.
   */
  public void reset() {
    // do nothing
  }
  /**
   * Gets the remaining size, in bytes, that this filter plays after the sound
   * is finished. An example would be an echo that plays longer than it"s
   * original sound. This method returns 0 by default.
   */
  public int getRemainingSize() {
    return 0;
  }
  /**
   * Filters an array of samples. Samples should be in 16-bit, signed,
   * little-endian format.
   */
  public void filter(byte[] samples) {
    filter(samples, 0, samples.length);
  }
  /**
   * Filters an array of samples. Samples should be in 16-bit, signed,
   * little-endian format. This method should be implemented by subclasses.
   */
  public abstract void filter(byte[] samples, int offset, int length);
  /**
   * Convenience method for getting a 16-bit sample from a byte array. Samples
   * should be in 16-bit, signed, little-endian format.
   */
  public static short getSample(byte[] buffer, int position) {
    return (short) (((buffer[position + 1] & 0xff) << 8) | (buffer[position] & 0xff));
  }
  /**
   * Convenience method for setting a 16-bit sample in a byte array. Samples
   * should be in 16-bit, signed, little-endian format.
   */
  public static void setSample(byte[] buffer, int position, short sample) {
    buffer[position] = (byte) (sample & 0xff);
    buffer[position + 1] = (byte) ((sample >> 8) & 0xff);
  }
}
/**
 * A thread pool is a group of a limited number of threads that are used to
 * execute tasks.
 */
class ThreadPool extends ThreadGroup {
  private boolean isAlive;
  private LinkedList taskQueue;
  private int threadID;
  private static int threadPoolID;
  /**
   * Creates a new ThreadPool.
   * 
   * @param numThreads
   *            The number of threads in the pool.
   */
  public ThreadPool(int numThreads) {
    super("ThreadPool-" + (threadPoolID++));
    setDaemon(true);
    isAlive = true;
    taskQueue = new LinkedList();
    for (int i = 0; i < numThreads; i++) {
      new PooledThread().start();
    }
  }
  /**
   * Requests a new task to run. This method returns immediately, and the task
   * executes on the next available idle thread in this ThreadPool.
   * <p>
   * Tasks start execution in the order they are received.
   * 
   * @param task
   *            The task to run. If null, no action is taken.
   * @throws IllegalStateException
   *             if this ThreadPool is already closed.
   */
  public synchronized void runTask(Runnable task) {
    if (!isAlive) {
      throw new IllegalStateException();
    }
    if (task != null) {
      taskQueue.add(task);
      notify();
    }
  }
  protected synchronized Runnable getTask() throws InterruptedException {
    while (taskQueue.size() == 0) {
      if (!isAlive) {
        return null;
      }
      wait();
    }
    return (Runnable) taskQueue.removeFirst();
  }
  /**
   * Closes this ThreadPool and returns immediately. All threads are stopped,
   * and any waiting tasks are not executed. Once a ThreadPool is closed, no
   * more tasks can be run on this ThreadPool.
   */
  public synchronized void close() {
    if (isAlive) {
      isAlive = false;
      taskQueue.clear();
      interrupt();
    }
  }
  /**
   * Closes this ThreadPool and waits for all running threads to finish. Any
   * waiting tasks are executed.
   */
  public void join() {
    // notify all waiting threads that this ThreadPool is no
    // longer alive
    synchronized (this) {
      isAlive = false;
      notifyAll();
    }
    // wait for all threads to finish
    Thread[] threads = new Thread[activeCount()];
    int count = enumerate(threads);
    for (int i = 0; i < count; i++) {
      try {
        threads[i].join();
      } catch (InterruptedException ex) {
      }
    }
  }
  /**
   * Signals that a PooledThread has started. This method does nothing by
   * default; subclasses should override to do any thread-specific startup
   * tasks.
   */
  protected void threadStarted() {
    // do nothing
  }
  /**
   * Signals that a PooledThread has stopped. This method does nothing by
   * default; subclasses should override to do any thread-specific cleanup
   * tasks.
   */
  protected void threadStopped() {
    // do nothing
  }
  /**
   * A PooledThread is a Thread in a ThreadPool group, designed to run tasks
   * (Runnables).
   */
  private class PooledThread extends Thread {
    public PooledThread() {
      super(ThreadPool.this, "PooledThread-" + (threadID++));
    }
    public void run() {
      // signal that this thread has started
      threadStarted();
      while (!isInterrupted()) {
        // get a task to run
        Runnable task = null;
        try {
          task = getTask();
        } catch (InterruptedException ex) {
        }
        // if getTask() returned null or was interrupted,
        // close this thread.
        if (task == null) {
          break;
        }
        // run the task, and eat any exceptions it throws
        try {
          task.run();
        } catch (Throwable t) {
          uncaughtException(this, t);
        }
      }
      // signal that this thread has stopped
      threadStopped();
    }
  }
}
/**
 * The Sound class is a container for sound samples. The sound samples are
 * format-agnostic and are stored as a byte array.
 */
class Sound {
  private byte[] samples;
  /**
   * Create a new Sound object with the specified byte array. The array is not
   * copied.
   */
  public Sound(byte[] samples) {
    this.samples = samples;
  }
  /**
   * Returns this Sound"s objects samples as a byte array.
   */
  public byte[] getSamples() {
    return samples;
  }
}
/**
 * The LoopingByteInputStream is a ByteArrayInputStream that loops indefinitly.
 * The looping stops when the close() method is called.
 * <p>
 * Possible ideas to extend this class:
 * <ul>
 * <li>Add an option to only loop a certain number of times.
 * </ul>
 */
class LoopingByteInputStream extends ByteArrayInputStream {
  private boolean closed;
  /**
   * Creates a new LoopingByteInputStream with the specified byte array. The
   * array is not copied.
   */
  public LoopingByteInputStream(byte[] buffer) {
    super(buffer);
    closed = false;
  }
  /**
   * Reads <code>length</code> bytes from the array. If the end of the array
   * is reached, the reading starts over from the beginning of the array.
   * Returns -1 if the array has been closed.
   */
  public int read(byte[] buffer, int offset, int length) {
    if (closed) {
      return -1;
    }
    int totalBytesRead = 0;
    while (totalBytesRead < length) {
      int numBytesRead = super.read(buffer, offset + totalBytesRead,
          length - totalBytesRead);
      if (numBytesRead > 0) {
        totalBytesRead += numBytesRead;
      } else {
        reset();
      }
    }
    return totalBytesRead;
  }
  /**
   * Closes the stream. Future calls to the read() methods will return 1.
   */
  public void close() throws IOException {
    super.close();
    closed = true;
  }
}
/**
 * The FilteredSoundStream class is a FilterInputStream that applies a
 * SoundFilter to the underlying input stream.
 * 
 * @see SoundFilter
 */
class FilteredSoundStream extends FilterInputStream {
  private static final int REMAINING_SIZE_UNKNOWN = -1;
  private SoundFilter soundFilter;
  private int remainingSize;
  /**
   * Creates a new FilteredSoundStream object with the specified InputStream
   * and SoundFilter.
   */
  public FilteredSoundStream(InputStream in, SoundFilter soundFilter) {
    super(in);
    this.soundFilter = soundFilter;
    remainingSize = REMAINING_SIZE_UNKNOWN;
  }
  /**
   * Overrides the FilterInputStream method to apply this filter whenever
   * bytes are read
   */
  public int read(byte[] samples, int offset, int length) throws IOException {
    // read and filter the sound samples in the stream
    int bytesRead = super.read(samples, offset, length);
    if (bytesRead > 0) {
      soundFilter.filter(samples, offset, bytesRead);
      return bytesRead;
    }
    // if there are no remaining bytes in the sound stream,
    // check if the filter has any remaining bytes ("echoes").
    if (remainingSize == REMAINING_SIZE_UNKNOWN) {
      remainingSize = soundFilter.getRemainingSize();
      // round down to nearest multiple of 4
      // (typical frame size)
      remainingSize = remainingSize / 4 * 4;
    }
    if (remainingSize > 0) {
      length = Math.min(length, remainingSize);
      // clear the buffer
      for (int i = offset; i < offset + length; i++) {
        samples[i] = 0;
      }
      // filter the remaining bytes
      soundFilter.filter(samples, offset, length);
      remainingSize -= length;
      // return
      return length;
    } else {
      // end of stream
      return -1;
    }
  }
}





Sound player

 
import java.applet.Applet;
import java.applet.AudioClip;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
public class Play {
  public static void main(String args[]) {
    try {
      // Loop
      URL url = new URL(
          "http://java.sun.ru/applets/other/Hangman/audio/whoopy.au");
      AudioClip clip = Applet.newAudioClip(url);
      clip.loop();
      Thread.sleep(5000);
      //Play
      File file = new File("bark.wav");
      clip = Applet.newAudioClip(file.toURL());
      clip.play();
      Thread.sleep(500);
      System.exit(0);
    } catch (InterruptedException e) {
    } catch (MalformedURLException e) {
    }
  }
}





The Filter3dTest class demonstrates the Filter3d functionality

 
       /*
DEVELOPING GAME IN JAVA 
Caracteristiques
Editeur : NEW RIDERS 
Auteur : BRACKEEN 
Parution : 09 2003 
Pages : 972 
Isbn : 1-59273-005-1 
Reliure : Paperback 
Disponibilite : Disponible a la librairie 
*/
         
import java.awt.AWTException;
import java.awt.Color;
import java.awt.ruponent;
import java.awt.Cursor;
import java.awt.DisplayMode;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.Point;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.UnsupportedAudioFileException;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
/**
 * The Filter3dTest class demonstrates the Filter3d functionality. A fly buzzes
 * around the listener, and the closer the fly is, the louder it"s heard.
 * 
 * @see Filter3d
 * @see SimpleSoundPlayer
 */
public class Filter3dTest extends GameCore {
  public static void main(String[] args) {
    new Filter3dTest().run();
  }
  private Sprite fly;
  private Sprite listener;
  private InputManager inputManager;
  private GameAction exit;
  private SimpleSoundPlayer bzzSound;
  private InputStream bzzSoundStream;
  public void init() {
    super.init();
    // set up input manager
    exit = new GameAction("exit", GameAction.DETECT_INITAL_PRESS_ONLY);
    inputManager = new InputManager(screen.getFullScreenWindow());
    inputManager.mapToKey(exit, KeyEvent.VK_ESCAPE);
    inputManager.setCursor(InputManager.INVISIBLE_CURSOR);
    createSprites();
    // load the sound
    bzzSound = new SimpleSoundPlayer("../sounds/fly-bzz.wav");
    // create the 3d filter
    Filter3d filter = new Filter3d(fly, listener, screen.getHeight());
    // create the filtered sound stream
    bzzSoundStream = new FilteredSoundStream(new LoopingByteInputStream(
        bzzSound.getSamples()), filter);
    // play the sound in a separate thread
    new Thread() {
      public void run() {
        bzzSound.play(bzzSoundStream);
      }
    }.start();
  }
  /**
   * Loads images and creates sprites.
   */
  private void createSprites() {
    // load images
    Image fly1 = loadImage("../images/fly1.png");
    Image fly2 = loadImage("../images/fly2.png");
    Image fly3 = loadImage("../images/fly3.png");
    Image ear = loadImage("../images/ear.png");
    // create "fly" sprite
    Animation anim = new Animation();
    anim.addFrame(fly1, 50);
    anim.addFrame(fly2, 50);
    anim.addFrame(fly3, 50);
    anim.addFrame(fly2, 50);
    fly = new Sprite(anim);
    // create the listener sprite
    anim = new Animation();
    anim.addFrame(ear, 0);
    listener = new Sprite(anim);
    listener.setX((screen.getWidth() - listener.getWidth()) / 2);
    listener.setY((screen.getHeight() - listener.getHeight()) / 2);
  }
  public void update(long elapsedTime) {
    if (exit.isPressed()) {
      stop();
    } else {
      listener.update(elapsedTime);
      fly.update(elapsedTime);
      fly.setX(inputManager.getMouseX());
      fly.setY(inputManager.getMouseY());
    }
  }
  public void stop() {
    super.stop();
    // stop the bzz sound
    try {
      bzzSoundStream.close();
    } catch (IOException ex) {
    }
  }
  public void draw(Graphics2D g) {
    // draw background
    g.setColor(new Color(0x33cc33));
    g.fillRect(0, 0, screen.getWidth(), screen.getHeight());
    // draw listener
    g.drawImage(listener.getImage(), Math.round(listener.getX()), Math
        .round(listener.getY()), null);
    // draw fly
    g.drawImage(fly.getImage(), Math.round(fly.getX()), Math.round(fly
        .getY()), null);
  }
}
/**
 * The SimpleSoundPlayer encapsulates a sound that can be opened from the file
 * system and later played.
 */
class SimpleSoundPlayer {
  public static void main(String[] args) {
    // load a sound
    SimpleSoundPlayer sound = new SimpleSoundPlayer("../sounds/voice.wav");
    // create the stream to play
    InputStream stream = new ByteArrayInputStream(sound.getSamples());
    // play the sound
    sound.play(stream);
    // exit
    System.exit(0);
  }
  private AudioFormat format;
  private byte[] samples;
  /**
   * Opens a sound from a file.
   */
  public SimpleSoundPlayer(String filename) {
    try {
      // open the audio input stream
      AudioInputStream stream = AudioSystem.getAudioInputStream(new File(
          filename));
      format = stream.getFormat();
      // get the audio samples
      samples = getSamples(stream);
    } catch (UnsupportedAudioFileException ex) {
      ex.printStackTrace();
    } catch (IOException ex) {
      ex.printStackTrace();
    }
  }
  /**
   * Gets the samples of this sound as a byte array.
   */
  public byte[] getSamples() {
    return samples;
  }
  /**
   * Gets the samples from an AudioInputStream as an array of bytes.
   */
  private byte[] getSamples(AudioInputStream audioStream) {
    // get the number of bytes to read
    int length = (int) (audioStream.getFrameLength() * format
        .getFrameSize());
    // read the entire stream
    byte[] samples = new byte[length];
    DataInputStream is = new DataInputStream(audioStream);
    try {
      is.readFully(samples);
    } catch (IOException ex) {
      ex.printStackTrace();
    }
    // return the samples
    return samples;
  }
  /**
   * Plays a stream. This method blocks (doesn"t return) until the sound is
   * finished playing.
   */
  public void play(InputStream source) {
    // use a short, 100ms (1/10th sec) buffer for real-time
    // change to the sound stream
    int bufferSize = format.getFrameSize()
        * Math.round(format.getSampleRate() / 10);
    byte[] buffer = new byte[bufferSize];
    // create a line to play to
    SourceDataLine line;
    try {
      DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
      line = (SourceDataLine) AudioSystem.getLine(info);
      line.open(format, bufferSize);
    } catch (LineUnavailableException ex) {
      ex.printStackTrace();
      return;
    }
    // start the line
    line.start();
    // copy data to the line
    try {
      int numBytesRead = 0;
      while (numBytesRead != -1) {
        numBytesRead = source.read(buffer, 0, buffer.length);
        if (numBytesRead != -1) {
          line.write(buffer, 0, numBytesRead);
        }
      }
    } catch (IOException ex) {
      ex.printStackTrace();
    }
    // wait until all data is played, then close the line
    line.drain();
    line.close();
  }
}
/**
 * The InputManager manages input of key and mouse events. Events are mapped to
 * GameActions.
 */
class InputManager implements KeyListener, MouseListener, MouseMotionListener,
    MouseWheelListener {
  /**
   * An invisible cursor.
   */
  public static final Cursor INVISIBLE_CURSOR = Toolkit.getDefaultToolkit()
      .createCustomCursor(Toolkit.getDefaultToolkit().getImage(""),
          new Point(0, 0), "invisible");
  // mouse codes
  public static final int MOUSE_MOVE_LEFT = 0;
  public static final int MOUSE_MOVE_RIGHT = 1;
  public static final int MOUSE_MOVE_UP = 2;
  public static final int MOUSE_MOVE_DOWN = 3;
  public static final int MOUSE_WHEEL_UP = 4;
  public static final int MOUSE_WHEEL_DOWN = 5;
  public static final int MOUSE_BUTTON_1 = 6;
  public static final int MOUSE_BUTTON_2 = 7;
  public static final int MOUSE_BUTTON_3 = 8;
  private static final int NUM_MOUSE_CODES = 9;
  // key codes are defined in java.awt.KeyEvent.
  // most of the codes (except for some rare ones like
  // "alt graph") are less than 600.
  private static final int NUM_KEY_CODES = 600;
  private GameAction[] keyActions = new GameAction[NUM_KEY_CODES];
  private GameAction[] mouseActions = new GameAction[NUM_MOUSE_CODES];
  private Point mouseLocation;
  private Point centerLocation;
  private Component comp;
  private Robot robot;
  private boolean isRecentering;
  /**
   * Creates a new InputManager that listens to input from the specified
   * component.
   */
  public InputManager(Component comp) {
    this.rup = comp;
    mouseLocation = new Point();
    centerLocation = new Point();
    // register key and mouse listeners
    comp.addKeyListener(this);
    comp.addMouseListener(this);
    comp.addMouseMotionListener(this);
    comp.addMouseWheelListener(this);
    // allow input of the TAB key and other keys normally
    // used for focus traversal
    comp.setFocusTraversalKeysEnabled(false);
  }
  /**
   * Sets the cursor on this InputManager"s input component.
   */
  public void setCursor(Cursor cursor) {
    comp.setCursor(cursor);
  }
  /**
   * Sets whether realtive mouse mode is on or not. For relative mouse mode,
   * the mouse is "locked" in the center of the screen, and only the changed
   * in mouse movement is measured. In normal mode, the mouse is free to move
   * about the screen.
   */
  public void setRelativeMouseMode(boolean mode) {
    if (mode == isRelativeMouseMode()) {
      return;
    }
    if (mode) {
      try {
        robot = new Robot();
        recenterMouse();
      } catch (AWTException ex) {
        // couldn"t create robot!
        robot = null;
      }
    } else {
      robot = null;
    }
  }
  /**
   * Returns whether or not relative mouse mode is on.
   */
  public boolean isRelativeMouseMode() {
    return (robot != null);
  }
  /**
   * Maps a GameAction to a specific key. The key codes are defined in
   * java.awt.KeyEvent. If the key already has a GameAction mapped to it, the
   * new GameAction overwrites it.
   */
  public void mapToKey(GameAction gameAction, int keyCode) {
    keyActions[keyCode] = gameAction;
  }
  /**
   * Maps a GameAction to a specific mouse action. The mouse codes are defined
   * herer in InputManager (MOUSE_MOVE_LEFT, MOUSE_BUTTON_1, etc). If the
   * mouse action already has a GameAction mapped to it, the new GameAction
   * overwrites it.
   */
  public void mapToMouse(GameAction gameAction, int mouseCode) {
    mouseActions[mouseCode] = gameAction;
  }
  /**
   * Clears all mapped keys and mouse actions to this GameAction.
   */
  public void clearMap(GameAction gameAction) {
    for (int i = 0; i < keyActions.length; i++) {
      if (keyActions[i] == gameAction) {
        keyActions[i] = null;
      }
    }
    for (int i = 0; i < mouseActions.length; i++) {
      if (mouseActions[i] == gameAction) {
        mouseActions[i] = null;
      }
    }
    gameAction.reset();
  }
  /**
   * Gets a List of names of the keys and mouse actions mapped to this
   * GameAction. Each entry in the List is a String.
   */
  public List getMaps(GameAction gameCode) {
    ArrayList list = new ArrayList();
    for (int i = 0; i < keyActions.length; i++) {
      if (keyActions[i] == gameCode) {
        list.add(getKeyName(i));
      }
    }
    for (int i = 0; i < mouseActions.length; i++) {
      if (mouseActions[i] == gameCode) {
        list.add(getMouseName(i));
      }
    }
    return list;
  }
  /**
   * Resets all GameActions so they appear like they haven"t been pressed.
   */
  public void resetAllGameActions() {
    for (int i = 0; i < keyActions.length; i++) {
      if (keyActions[i] != null) {
        keyActions[i].reset();
      }
    }
    for (int i = 0; i < mouseActions.length; i++) {
      if (mouseActions[i] != null) {
        mouseActions[i].reset();
      }
    }
  }
  /**
   * Gets the name of a key code.
   */
  public static String getKeyName(int keyCode) {
    return KeyEvent.getKeyText(keyCode);
  }
  /**
   * Gets the name of a mouse code.
   */
  public static String getMouseName(int mouseCode) {
    switch (mouseCode) {
    case MOUSE_MOVE_LEFT:
      return "Mouse Left";
    case MOUSE_MOVE_RIGHT:
      return "Mouse Right";
    case MOUSE_MOVE_UP:
      return "Mouse Up";
    case MOUSE_MOVE_DOWN:
      return "Mouse Down";
    case MOUSE_WHEEL_UP:
      return "Mouse Wheel Up";
    case MOUSE_WHEEL_DOWN:
      return "Mouse Wheel Down";
    case MOUSE_BUTTON_1:
      return "Mouse Button 1";
    case MOUSE_BUTTON_2:
      return "Mouse Button 2";
    case MOUSE_BUTTON_3:
      return "Mouse Button 3";
    default:
      return "Unknown mouse code " + mouseCode;
    }
  }
  /**
   * Gets the x position of the mouse.
   */
  public int getMouseX() {
    return mouseLocation.x;
  }
  /**
   * Gets the y position of the mouse.
   */
  public int getMouseY() {
    return mouseLocation.y;
  }
  /**
   * Uses the Robot class to try to postion the mouse in the center of the
   * screen.
   * <p>
   * Note that use of the Robot class may not be available on all platforms.
   */
  private synchronized void recenterMouse() {
    if (robot != null && comp.isShowing()) {
      centerLocation.x = comp.getWidth() / 2;
      centerLocation.y = comp.getHeight() / 2;
      SwingUtilities.convertPointToScreen(centerLocation, comp);
      isRecentering = true;
      robot.mouseMove(centerLocation.x, centerLocation.y);
    }
  }
  private GameAction getKeyAction(KeyEvent e) {
    int keyCode = e.getKeyCode();
    if (keyCode < keyActions.length) {
      return keyActions[keyCode];
    } else {
      return null;
    }
  }
  /**
   * Gets the mouse code for the button specified in this MouseEvent.
   */
  public static int getMouseButtonCode(MouseEvent e) {
    switch (e.getButton()) {
    case MouseEvent.BUTTON1:
      return MOUSE_BUTTON_1;
    case MouseEvent.BUTTON2:
      return MOUSE_BUTTON_2;
    case MouseEvent.BUTTON3:
      return MOUSE_BUTTON_3;
    default:
      return -1;
    }
  }
  private GameAction getMouseButtonAction(MouseEvent e) {
    int mouseCode = getMouseButtonCode(e);
    if (mouseCode != -1) {
      return mouseActions[mouseCode];
    } else {
      return null;
    }
  }
  // from the KeyListener interface
  public void keyPressed(KeyEvent e) {
    GameAction gameAction = getKeyAction(e);
    if (gameAction != null) {
      gameAction.press();
    }
    // make sure the key isn"t processed for anything else
    e.consume();
  }
  // from the KeyListener interface
  public void keyReleased(KeyEvent e) {
    GameAction gameAction = getKeyAction(e);
    if (gameAction != null) {
      gameAction.release();
    }
    // make sure the key isn"t processed for anything else
    e.consume();
  }
  // from the KeyListener interface
  public void keyTyped(KeyEvent e) {
    // make sure the key isn"t processed for anything else
    e.consume();
  }
  // from the MouseListener interface
  public void mousePressed(MouseEvent e) {
    GameAction gameAction = getMouseButtonAction(e);
    if (gameAction != null) {
      gameAction.press();
    }
  }
  // from the MouseListener interface
  public void mouseReleased(MouseEvent e) {
    GameAction gameAction = getMouseButtonAction(e);
    if (gameAction != null) {
      gameAction.release();
    }
  }
  // from the MouseListener interface
  public void mouseClicked(MouseEvent e) {
    // do nothing
  }
  // from the MouseListener interface
  public void mouseEntered(MouseEvent e) {
    mouseMoved(e);
  }
  // from the MouseListener interface
  public void mouseExited(MouseEvent e) {
    mouseMoved(e);
  }
  // from the MouseMotionListener interface
  public void mouseDragged(MouseEvent e) {
    mouseMoved(e);
  }
  // from the MouseMotionListener interface
  public synchronized void mouseMoved(MouseEvent e) {
    // this event is from re-centering the mouse - ignore it
    if (isRecentering && centerLocation.x == e.getX()
        && centerLocation.y == e.getY()) {
      isRecentering = false;
    } else {
      int dx = e.getX() - mouseLocation.x;
      int dy = e.getY() - mouseLocation.y;
      mouseHelper(MOUSE_MOVE_LEFT, MOUSE_MOVE_RIGHT, dx);
      mouseHelper(MOUSE_MOVE_UP, MOUSE_MOVE_DOWN, dy);
      if (isRelativeMouseMode()) {
        recenterMouse();
      }
    }
    mouseLocation.x = e.getX();
    mouseLocation.y = e.getY();
  }
  // from the MouseWheelListener interface
  public void mouseWheelMoved(MouseWheelEvent e) {
    mouseHelper(MOUSE_WHEEL_UP, MOUSE_WHEEL_DOWN, e.getWheelRotation());
  }
  private void mouseHelper(int codeNeg, int codePos, int amount) {
    GameAction gameAction;
    if (amount < 0) {
      gameAction = mouseActions[codeNeg];
    } else {
      gameAction = mouseActions[codePos];
    }
    if (gameAction != null) {
      gameAction.press(Math.abs(amount));
      gameAction.release();
    }
  }
}
/**
 * Simple abstract class used for testing. Subclasses should implement the
 * draw() method.
 */
abstract class GameCore {
  protected static final int FONT_SIZE = 24;
  private static final DisplayMode POSSIBLE_MODES[] = {
      new DisplayMode(800, 600, 32, 0), new DisplayMode(800, 600, 24, 0),
      new DisplayMode(800, 600, 16, 0), new DisplayMode(640, 480, 32, 0),
      new DisplayMode(640, 480, 24, 0), new DisplayMode(640, 480, 16, 0) };
  private boolean isRunning;
  protected ScreenManager screen;
  /**
   * Signals the game loop that it"s time to quit
   */
  public void stop() {
    isRunning = false;
  }
  /**
   * Calls init() and gameLoop()
   */
  public void run() {
    try {
      init();
      gameLoop();
    } finally {
      screen.restoreScreen();
      lazilyExit();
    }
  }
  /**
   * Exits the VM from a daemon thread. The daemon thread waits 2 seconds then
   * calls System.exit(0). Since the VM should exit when only daemon threads
   * are running, this makes sure System.exit(0) is only called if neccesary.
   * It"s neccesary if the Java Sound system is running.
   */
  public void lazilyExit() {
    Thread thread = new Thread() {
      public void run() {
        // first, wait for the VM exit on its own.
        try {
          Thread.sleep(2000);
        } catch (InterruptedException ex) {
        }
        // system is still running, so force an exit
        System.exit(0);
      }
    };
    thread.setDaemon(true);
    thread.start();
  }
  /**
   * Sets full screen mode and initiates and objects.
   */
  public void init() {
    screen = new ScreenManager();
    DisplayMode displayMode = screen
        .findFirstCompatibleMode(POSSIBLE_MODES);
    screen.setFullScreen(displayMode);
    Window window = screen.getFullScreenWindow();
    window.setFont(new Font("Dialog", Font.PLAIN, FONT_SIZE));
    window.setBackground(Color.blue);
    window.setForeground(Color.white);
    isRunning = true;
  }
  public Image loadImage(String fileName) {
    return new ImageIcon(fileName).getImage();
  }
  /**
   * Runs through the game loop until stop() is called.
   */
  public void gameLoop() {
    long startTime = System.currentTimeMillis();
    long currTime = startTime;
    while (isRunning) {
      long elapsedTime = System.currentTimeMillis() - currTime;
      currTime += elapsedTime;
      // update
      update(elapsedTime);
      // draw the screen
      Graphics2D g = screen.getGraphics();
      draw(g);
      g.dispose();
      screen.update();
      // take a nap
      try {
        Thread.sleep(20);
      } catch (InterruptedException ex) {
      }
    }
  }
  /**
   * Updates the state of the game/animation based on the amount of elapsed
   * time that has passed.
   */
  public void update(long elapsedTime) {
    // do nothing
  }
  /**
   * Draws to the screen. Subclasses must override this method.
   */
  public abstract void draw(Graphics2D g);
}
/**
 * The GameAction class is an abstract to a user-initiated action, like jumping
 * or moving. GameActions can be mapped to keys or the mouse with the
 * InputManager.
 */
class GameAction {
  /**
   * Normal behavior. The isPressed() method returns true as long as the key
   * is held down.
   */
  public static final int NORMAL = 0;
  /**
   * Initial press behavior. The isPressed() method returns true only after
   * the key is first pressed, and not again until the key is released and
   * pressed again.
   */
  public static final int DETECT_INITAL_PRESS_ONLY = 1;
  private static final int STATE_RELEASED = 0;
  private static final int STATE_PRESSED = 1;
  private static final int STATE_WAITING_FOR_RELEASE = 2;
  private String name;
  private int behavior;
  private int amount;
  private int state;
  /**
   * Create a new GameAction with the NORMAL behavior.
   */
  public GameAction(String name) {
    this(name, NORMAL);
  }
  /**
   * Create a new GameAction with the specified behavior.
   */
  public GameAction(String name, int behavior) {
    this.name = name;
    this.behavior = behavior;
    reset();
  }
  /**
   * Gets the name of this GameAction.
   */
  public String getName() {
    return name;
  }
  /**
   * Resets this GameAction so that it appears like it hasn"t been pressed.
   */
  public void reset() {
    state = STATE_RELEASED;
    amount = 0;
  }
  /**
   * Taps this GameAction. Same as calling press() followed by release().
   */
  public synchronized void tap() {
    press();
    release();
  }
  /**
   * Signals that the key was pressed.
   */
  public synchronized void press() {
    press(1);
  }
  /**
   * Signals that the key was pressed a specified number of times, or that the
   * mouse move a spcified distance.
   */
  public synchronized void press(int amount) {
    if (state != STATE_WAITING_FOR_RELEASE) {
      this.amount += amount;
      state = STATE_PRESSED;
    }
  }
  /**
   * Signals that the key was released
   */
  public synchronized void release() {
    state = STATE_RELEASED;
  }
  /**
   * Returns whether the key was pressed or not since last checked.
   */
  public synchronized boolean isPressed() {
    return (getAmount() != 0);
  }
  /**
   * For keys, this is the number of times the key was pressed since it was
   * last checked. For mouse movement, this is the distance moved.
   */
  public synchronized int getAmount() {
    int retVal = amount;
    if (retVal != 0) {
      if (state == STATE_RELEASED) {
        amount = 0;
      } else if (behavior == DETECT_INITAL_PRESS_ONLY) {
        state = STATE_WAITING_FOR_RELEASE;
        amount = 0;
      }
    }
    return retVal;
  }
}
class Sprite {
  private Animation anim;
  // position (pixels)
  private float x;
  private float y;
  // velocity (pixels per millisecond)
  private float dx;
  private float dy;
  /**
   * Creates a new Sprite object with the specified Animation.
   */
  public Sprite(Animation anim) {
    this.anim = anim;
  }
  /**
   * Updates this Sprite"s Animation and its position based on the velocity.
   */
  public void update(long elapsedTime) {
    x += dx * elapsedTime;
    y += dy * elapsedTime;
    anim.update(elapsedTime);
  }
  /**
   * Gets this Sprite"s current x position.
   */
  public float getX() {
    return x;
  }
  /**
   * Gets this Sprite"s current y position.
   */
  public float getY() {
    return y;
  }
  /**
   * Sets this Sprite"s current x position.
   */
  public void setX(float x) {
    this.x = x;
  }
  /**
   * Sets this Sprite"s current y position.
   */
  public void setY(float y) {
    this.y = y;
  }
  /**
   * Gets this Sprite"s width, based on the size of the current image.
   */
  public int getWidth() {
    return anim.getImage().getWidth(null);
  }
  /**
   * Gets this Sprite"s height, based on the size of the current image.
   */
  public int getHeight() {
    return anim.getImage().getHeight(null);
  }
  /**
   * Gets the horizontal velocity of this Sprite in pixels per millisecond.
   */
  public float getVelocityX() {
    return dx;
  }
  /**
   * Gets the vertical velocity of this Sprite in pixels per millisecond.
   */
  public float getVelocityY() {
    return dy;
  }
  /**
   * Sets the horizontal velocity of this Sprite in pixels per millisecond.
   */
  public void setVelocityX(float dx) {
    this.dx = dx;
  }
  /**
   * Sets the vertical velocity of this Sprite in pixels per millisecond.
   */
  public void setVelocityY(float dy) {
    this.dy = dy;
  }
  /**
   * Gets this Sprite"s current image.
   */
  public Image getImage() {
    return anim.getImage();
  }
}
/**
 * The Filter3d class is a SoundFilter that creates a 3d sound effect. The sound
 * is filtered so that it is quiter the farther away the sound source is from
 * the listener.
 * <p>
 * Possible ideas to extend this class:
 * <ul>
 * <li>pan the sound to the left and right speakers
 * </ul>
 * 
 * @see FilteredSoundStream
 */
class Filter3d extends SoundFilter {
  // number of samples to shift when changing the volume.
  private static final int NUM_SHIFTING_SAMPLES = 500;
  private Sprite source;
  private Sprite listener;
  private int maxDistance;
  private float lastVolume;
  /**
   * Creates a new Filter3d object with the specified source and listener
   * Sprites. The Sprite"s position can be changed while this filter is
   * running.
   * <p>
   * The maxDistance parameter is the maximum distance that the sound can be
   * heard.
   */
  public Filter3d(Sprite source, Sprite listener, int maxDistance) {
    this.source = source;
    this.listener = listener;
    this.maxDistance = maxDistance;
    this.lastVolume = 0.0f;
  }
  /**
   * Filters the sound so that it gets more quiet with distance.
   */
  public void filter(byte[] samples, int offset, int length) {
    if (source == null || listener == null) {
      // nothing to filter - return
      return;
    }
    // calculate the listener"s distance from the sound source
    float dx = (source.getX() - listener.getX());
    float dy = (source.getY() - listener.getY());
    float distance = (float) Math.sqrt(dx * dx + dy * dy);
    // set volume from 0 (no sound) to 1
    float newVolume = (maxDistance - distance) / maxDistance;
    if (newVolume <= 0) {
      newVolume = 0;
    }
    // set the volume of the sample
    int shift = 0;
    for (int i = offset; i < offset + length; i += 2) {
      float volume = newVolume;
      // shift from the last volume to the new volume
      if (shift < NUM_SHIFTING_SAMPLES) {
        volume = lastVolume + (newVolume - lastVolume) * shift
            / NUM_SHIFTING_SAMPLES;
        shift++;
      }
      // change the volume of the sample
      short oldSample = getSample(samples, i);
      short newSample = (short) (oldSample * volume);
      setSample(samples, i, newSample);
    }
    lastVolume = newVolume;
  }
}
/**
 * The FilteredSoundStream class is a FilterInputStream that applies a
 * SoundFilter to the underlying input stream.
 * 
 * @see SoundFilter
 */
class FilteredSoundStream extends FilterInputStream {
  private static final int REMAINING_SIZE_UNKNOWN = -1;
  private SoundFilter soundFilter;
  private int remainingSize;
  /**
   * Creates a new FilteredSoundStream object with the specified InputStream
   * and SoundFilter.
   */
  public FilteredSoundStream(InputStream in, SoundFilter soundFilter) {
    super(in);
    this.soundFilter = soundFilter;
    remainingSize = REMAINING_SIZE_UNKNOWN;
  }
  /**
   * Overrides the FilterInputStream method to apply this filter whenever
   * bytes are read
   */
  public int read(byte[] samples, int offset, int length) throws IOException {
    // read and filter the sound samples in the stream
    int bytesRead = super.read(samples, offset, length);
    if (bytesRead > 0) {
      soundFilter.filter(samples, offset, bytesRead);
      return bytesRead;
    }
    // if there are no remaining bytes in the sound stream,
    // check if the filter has any remaining bytes ("echoes").
    if (remainingSize == REMAINING_SIZE_UNKNOWN) {
      remainingSize = soundFilter.getRemainingSize();
      // round down to nearest multiple of 4
      // (typical frame size)
      remainingSize = remainingSize / 4 * 4;
    }
    if (remainingSize > 0) {
      length = Math.min(length, remainingSize);
      // clear the buffer
      for (int i = offset; i < offset + length; i++) {
        samples[i] = 0;
      }
      // filter the remaining bytes
      soundFilter.filter(samples, offset, length);
      remainingSize -= length;
      // return
      return length;
    } else {
      // end of stream
      return -1;
    }
  }
}
/**
 * The Animation class manages a series of images (frames) and the amount of
 * time to display each frame.
 */
class Animation {
  private ArrayList frames;
  private int currFrameIndex;
  private long animTime;
  private long totalDuration;
  /**
   * Creates a new, empty Animation.
   */
  public Animation() {
    frames = new ArrayList();
    totalDuration = 0;
    start();
  }
  /**
   * Adds an image to the animation with the specified duration (time to
   * display the image).
   */
  public synchronized void addFrame(Image image, long duration) {
    totalDuration += duration;
    frames.add(new AnimFrame(image, totalDuration));
  }
  /**
   * Starts this animation over from the beginning.
   */
  public synchronized void start() {
    animTime = 0;
    currFrameIndex = 0;
  }
  /**
   * Updates this animation"s current image (frame), if neccesary.
   */
  public synchronized void update(long elapsedTime) {
    if (frames.size() > 1) {
      animTime += elapsedTime;
      if (animTime >= totalDuration) {
        animTime = animTime % totalDuration;
        currFrameIndex = 0;
      }
      while (animTime > getFrame(currFrameIndex).endTime) {
        currFrameIndex++;
      }
    }
  }
  /**
   * Gets this Animation"s current image. Returns null if this animation has
   * no images.
   */
  public synchronized Image getImage() {
    if (frames.size() == 0) {
      return null;
    } else {
      return getFrame(currFrameIndex).image;
    }
  }
  private AnimFrame getFrame(int i) {
    return (AnimFrame) frames.get(i);
  }
  private class AnimFrame {
    Image image;
    long endTime;
    public AnimFrame(Image image, long endTime) {
      this.image = image;
      this.endTime = endTime;
    }
  }
}
/**
 * The LoopingByteInputStream is a ByteArrayInputStream that loops indefinitly.
 * The looping stops when the close() method is called.
 * <p>
 * Possible ideas to extend this class:
 * <ul>
 * <li>Add an option to only loop a certain number of times.
 * </ul>
 */
class LoopingByteInputStream extends ByteArrayInputStream {
  private boolean closed;
  /**
   * Creates a new LoopingByteInputStream with the specified byte array. The
   * array is not copied.
   */
  public LoopingByteInputStream(byte[] buffer) {
    super(buffer);
    closed = false;
  }
  /**
   * Reads <code>length</code> bytes from the array. If the end of the array
   * is reached, the reading starts over from the beginning of the array.
   * Returns -1 if the array has been closed.
   */
  public int read(byte[] buffer, int offset, int length) {
    if (closed) {
      return -1;
    }
    int totalBytesRead = 0;
    while (totalBytesRead < length) {
      int numBytesRead = super.read(buffer, offset + totalBytesRead,
          length - totalBytesRead);
      if (numBytesRead > 0) {
        totalBytesRead += numBytesRead;
      } else {
        reset();
      }
    }
    return totalBytesRead;
  }
  /**
   * Closes the stream. Future calls to the read() methods will return 1.
   */
  public void close() throws IOException {
    super.close();
    closed = true;
  }
}
/**
 * A abstract class designed to filter sound samples. Since SoundFilters may use
 * internal buffering of samples, a new SoundFilter object should be created for
 * every sound played. However, SoundFilters can be reused after they are
 * finished by called the reset() method.
 * <p>
 * Assumes all samples are 16-bit, signed, little-endian format.
 * 
 * @see FilteredSoundStream
 */
abstract class SoundFilter {
  /**
   * Resets this SoundFilter. Does nothing by default.
   */
  public void reset() {
    // do nothing
  }
  /**
   * Gets the remaining size, in bytes, that this filter plays after the sound
   * is finished. An example would be an echo that plays longer than it"s
   * original sound. This method returns 0 by default.
   */
  public int getRemainingSize() {
    return 0;
  }
  /**
   * Filters an array of samples. Samples should be in 16-bit, signed,
   * little-endian format.
   */
  public void filter(byte[] samples) {
    filter(samples, 0, samples.length);
  }
  /**
   * Filters an array of samples. Samples should be in 16-bit, signed,
   * little-endian format. This method should be implemented by subclasses.
   */
  public abstract void filter(byte[] samples, int offset, int length);
  /**
   * Convenience method for getting a 16-bit sample from a byte array. Samples
   * should be in 16-bit, signed, little-endian format.
   */
  public static short getSample(byte[] buffer, int position) {
    return (short) (((buffer[position + 1] & 0xff) << 8) | (buffer[position] & 0xff));
  }
  /**
   * Convenience method for setting a 16-bit sample in a byte array. Samples
   * should be in 16-bit, signed, little-endian format.
   */
  public static void setSample(byte[] buffer, int position, short sample) {
    buffer[position] = (byte) (sample & 0xff);
    buffer[position + 1] = (byte) ((sample >> 8) & 0xff);
  }
}
/**
 * The ScreenManager class manages initializing and displaying full screen
 * graphics modes.
 */
class ScreenManager {
  private GraphicsDevice device;
  /**
   * Creates a new ScreenManager object.
   */
  public ScreenManager() {
    GraphicsEnvironment environment = GraphicsEnvironment
        .getLocalGraphicsEnvironment();
    device = environment.getDefaultScreenDevice();
  }
  /**
   * Returns a list of compatible display modes for the default device on the
   * system.
   */
  public DisplayMode[] getCompatibleDisplayModes() {
    return device.getDisplayModes();
  }
  /**
   * Returns the first compatible mode in a list of modes. Returns null if no
   * modes are compatible.
   */
  public DisplayMode findFirstCompatibleMode(DisplayMode modes[]) {
    DisplayMode goodModes[] = device.getDisplayModes();
    for (int i = 0; i < modes.length; i++) {
      for (int j = 0; j < goodModes.length; j++) {
        if (displayModesMatch(modes[i], goodModes[j])) {
          return modes[i];
        }
      }
    }
    return null;
  }
  /**
   * Returns the current display mode.
   */
  public DisplayMode getCurrentDisplayMode() {
    return device.getDisplayMode();
  }
  /**
   * Determines if two display modes "match". Two display modes match if they
   * have the same resolution, bit depth, and refresh rate. The bit depth is
   * ignored if one of the modes has a bit depth of
   * DisplayMode.BIT_DEPTH_MULTI. Likewise, the refresh rate is ignored if one
   * of the modes has a refresh rate of DisplayMode.REFRESH_RATE_UNKNOWN.
   */
  public boolean displayModesMatch(DisplayMode mode1, DisplayMode mode2)
  {
    if (mode1.getWidth() != mode2.getWidth()
        || mode1.getHeight() != mode2.getHeight()) {
      return false;
    }
    if (mode1.getBitDepth() != DisplayMode.BIT_DEPTH_MULTI
        && mode2.getBitDepth() != DisplayMode.BIT_DEPTH_MULTI
        && mode1.getBitDepth() != mode2.getBitDepth()) {
      return false;
    }
    if (mode1.getRefreshRate() != DisplayMode.REFRESH_RATE_UNKNOWN
        && mode2.getRefreshRate() != DisplayMode.REFRESH_RATE_UNKNOWN
        && mode1.getRefreshRate() != mode2.getRefreshRate()) {
      return false;
    }
    return true;
  }
  /**
   * Enters full screen mode and changes the display mode. If the specified
   * display mode is null or not compatible with this device, or if the
   * display mode cannot be changed on this system, the current display mode
   * is used.
   * <p>
   * The display uses a BufferStrategy with 2 buffers.
   */
  public void setFullScreen(DisplayMode displayMode) {
    final JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setUndecorated(true);
    frame.setIgnoreRepaint(true);
    frame.setResizable(false);
    device.setFullScreenWindow(frame);
    if (displayMode != null && device.isDisplayChangeSupported()) {
      try {
        device.setDisplayMode(displayMode);
      } catch (IllegalArgumentException ex) {
      }
      // fix for mac os x
      frame.setSize(displayMode.getWidth(), displayMode.getHeight());
    }
    // avoid potential deadlock in 1.4.1_02
    try {
      EventQueue.invokeAndWait(new Runnable() {
        public void run() {
          frame.createBufferStrategy(2);
        }
      });
    } catch (InterruptedException ex) {
      // ignore
    } catch (InvocationTargetException ex) {
      // ignore
    }
  }
  /**
   * Gets the graphics context for the display. The ScreenManager uses double
   * buffering, so applications must call update() to show any graphics drawn.
   * <p>
   * The application must dispose of the graphics object.
   */
  public Graphics2D getGraphics() {
    Window window = device.getFullScreenWindow();
    if (window != null) {
      BufferStrategy strategy = window.getBufferStrategy();
      return (Graphics2D) strategy.getDrawGraphics();
    } else {
      return null;
    }
  }
  /**
   * Updates the display.
   */
  public void update() {
    Window window = device.getFullScreenWindow();
    if (window != null) {
      BufferStrategy strategy = window.getBufferStrategy();
      if (!strategy.contentsLost()) {
        strategy.show();
      }
    }
    // Sync the display on some systems.
    // (on Linux, this fixes event queue problems)
    Toolkit.getDefaultToolkit().sync();
  }
  /**
   * Returns the window currently used in full screen mode. Returns null if
   * the device is not in full screen mode.
   */
  public JFrame getFullScreenWindow() {
    return (JFrame) device.getFullScreenWindow();
  }
  /**
   * Returns the width of the window currently used in full screen mode.
   * Returns 0 if the device is not in full screen mode.
   */
  public int getWidth() {
    Window window = device.getFullScreenWindow();
    if (window != null) {
      return window.getWidth();
    } else {
      return 0;
    }
  }
  /**
   * Returns the height of the window currently used in full screen mode.
   * Returns 0 if the device is not in full screen mode.
   */
  public int getHeight() {
    Window window = device.getFullScreenWindow();
    if (window != null) {
      return window.getHeight();
    } else {
      return 0;
    }
  }
  /**
   * Restores the screen"s display mode.
   */
  public void restoreScreen() {
    Window window = device.getFullScreenWindow();
    if (window != null) {
      window.dispose();
    }
    device.setFullScreenWindow(null);
  }
  /**
   * Creates an image compatible with the current display.
   */
  public BufferedImage createCompatibleImage(int w, int h, int transparancy) {
    Window window = device.getFullScreenWindow();
    if (window != null) {
      GraphicsConfiguration gc = window.getGraphicsConfiguration();
      return gc.createCompatibleImage(w, h, transparancy);
    }
    return null;
  }
}





This is a simple program to record sounds and play them back

 
 
/*
 *
 * Copyright (c) 1999 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Sun grants you ("Licensee") a non-exclusive, royalty free,
 * license to use, modify and redistribute this software in 
 * source and binary code form, provided that i) this copyright
 * notice and license appear on all copies of the software; and 
 * ii) Licensee does not utilize the software in a manner
 * which is disparaging to Sun.
 *
 * This software is provided "AS IS," without a warranty
 * of any kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS
 * AND WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE 
 * HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE LIABLE FOR 
 * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
 * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT
 * WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT
 * OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, 
 * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS
 * OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY
 * TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGES.
 This software is not designed or intended for use in on-line
 control of aircraft, air traffic, aircraft navigation or
 aircraft communications; or in the design, construction,
 operation or maintenance of any nuclear facility. Licensee 
 represents and warrants that it will not use or redistribute 
 the Software for such purposes.
 */
/*  The above copyright statement is included because this 
 * program uses several methods from the JavaSoundDemo
 * distributed by SUN. In some cases, the sound processing methods
 * unmodified or only slightly modified.
 * All other methods copyright Steve Potts, 2002
 */
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.TargetDataLine;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.border.EmptyBorder;
import javax.swing.border.SoftBevelBorder;
/**
 * SimpleSoundCapture Example. This is a simple program to record sounds and
 * play them back. It uses some methods from the CapturePlayback program in the
 * JavaSoundDemo. For licensizing reasons the disclaimer above is included.
 * 
 * @author Steve Potts
 */
public class SimpleSoundCapture extends JPanel implements ActionListener {
  final int bufSize = 16384;
  Capture capture = new Capture();
  Playback playback = new Playback();
  AudioInputStream audioInputStream;
  JButton playB, captB;
  JTextField textField;
  String errStr;
  double duration, seconds;
  File file;
  public SimpleSoundCapture() {
    setLayout(new BorderLayout());
    EmptyBorder eb = new EmptyBorder(5, 5, 5, 5);
    SoftBevelBorder sbb = new SoftBevelBorder(SoftBevelBorder.LOWERED);
    setBorder(new EmptyBorder(5, 5, 5, 5));
    JPanel p1 = new JPanel();
    p1.setLayout(new BoxLayout(p1, BoxLayout.X_AXIS));
    JPanel p2 = new JPanel();
    p2.setBorder(sbb);
    p2.setLayout(new BoxLayout(p2, BoxLayout.Y_AXIS));
    JPanel buttonsPanel = new JPanel();
    buttonsPanel.setBorder(new EmptyBorder(10, 0, 5, 0));
    playB = addButton("Play", buttonsPanel, false);
    captB = addButton("Record", buttonsPanel, true);
    p2.add(buttonsPanel);
    p1.add(p2);
    add(p1);
  }
  public void open() {
  }
  public void close() {
    if (playback.thread != null) {
      playB.doClick(0);
    }
    if (capture.thread != null) {
      captB.doClick(0);
    }
  }
  private JButton addButton(String name, JPanel p, boolean state) {
    JButton b = new JButton(name);
    b.addActionListener(this);
    b.setEnabled(state);
    p.add(b);
    return b;
  }
  public void actionPerformed(ActionEvent e) {
    Object obj = e.getSource();
    if (obj.equals(playB)) {
      if (playB.getText().startsWith("Play")) {
        playback.start();
        captB.setEnabled(false);
        playB.setText("Stop");
      } else {
        playback.stop();
        captB.setEnabled(true);
        playB.setText("Play");
      }
    } else if (obj.equals(captB)) {
      if (captB.getText().startsWith("Record")) {
        capture.start();
        playB.setEnabled(false);
        captB.setText("Stop");
      } else {
        capture.stop();
        playB.setEnabled(true);
      }
    }
  }
  /**
   * Write data to the OutputChannel.
   */
  public class Playback implements Runnable {
    SourceDataLine line;
    Thread thread;
    public void start() {
      errStr = null;
      thread = new Thread(this);
      thread.setName("Playback");
      thread.start();
    }
    public void stop() {
      thread = null;
    }
    private void shutDown(String message) {
      if ((errStr = message) != null) {
        System.err.println(errStr);
      }
      if (thread != null) {
        thread = null;
        captB.setEnabled(true);
        playB.setText("Play");
      }
    }
    public void run() {
      // make sure we have something to play
      if (audioInputStream == null) {
        shutDown("No loaded audio to play back");
        return;
      }
      // reset to the beginnning of the stream
      try {
        audioInputStream.reset();
      } catch (Exception e) {
        shutDown("Unable to reset the stream\n" + e);
        return;
      }
      // get an AudioInputStream of the desired format for playback
      AudioFormat.Encoding encoding = AudioFormat.Encoding.PCM_SIGNED;
      float rate = 44100.0f;
      int channels = 2;
      int frameSize = 4;
      int sampleSize = 16;
      boolean bigEndian = true;
      AudioFormat format = new AudioFormat(encoding, rate, sampleSize, channels, (sampleSize / 8)
          * channels, rate, bigEndian);
      AudioInputStream playbackInputStream = AudioSystem.getAudioInputStream(format,
          audioInputStream);
      if (playbackInputStream == null) {
        shutDown("Unable to convert stream of format " + audioInputStream + " to format " + format);
        return;
      }
      // define the required attributes for our line,
      // and make sure a compatible line is supported.
      DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
      if (!AudioSystem.isLineSupported(info)) {
        shutDown("Line matching " + info + " not supported.");
        return;
      }
      // get and open the source data line for playback.
      try {
        line = (SourceDataLine) AudioSystem.getLine(info);
        line.open(format, bufSize);
      } catch (LineUnavailableException ex) {
        shutDown("Unable to open the line: " + ex);
        return;
      }
      // play back the captured audio data
      int frameSizeInBytes = format.getFrameSize();
      int bufferLengthInFrames = line.getBufferSize() / 8;
      int bufferLengthInBytes = bufferLengthInFrames * frameSizeInBytes;
      byte[] data = new byte[bufferLengthInBytes];
      int numBytesRead = 0;
      // start the source data line
      line.start();
      while (thread != null) {
        try {
          if ((numBytesRead = playbackInputStream.read(data)) == -1) {
            break;
          }
          int numBytesRemaining = numBytesRead;
          while (numBytesRemaining > 0) {
            numBytesRemaining -= line.write(data, 0, numBytesRemaining);
          }
        } catch (Exception e) {
          shutDown("Error during playback: " + e);
          break;
        }
      }
      // we reached the end of the stream.
      // let the data play out, then
      // stop and close the line.
      if (thread != null) {
        line.drain();
      }
      line.stop();
      line.close();
      line = null;
      shutDown(null);
    }
  } // End class Playback
  /**
   * Reads data from the input channel and writes to the output stream
   */
  class Capture implements Runnable {
    TargetDataLine line;
    Thread thread;
    public void start() {
      errStr = null;
      thread = new Thread(this);
      thread.setName("Capture");
      thread.start();
    }
    public void stop() {
      thread = null;
    }
    private void shutDown(String message) {
      if ((errStr = message) != null && thread != null) {
        thread = null;
        playB.setEnabled(true);
        captB.setText("Record");
        System.err.println(errStr);
      }
    }
    public void run() {
      duration = 0;
      audioInputStream = null;
      // define the required attributes for our line,
      // and make sure a compatible line is supported.
      AudioFormat.Encoding encoding = AudioFormat.Encoding.PCM_SIGNED;
      float rate = 44100.0f;
      int channels = 2;
      int frameSize = 4;
      int sampleSize = 16;
      boolean bigEndian = true;
      AudioFormat format = new AudioFormat(encoding, rate, sampleSize, channels, (sampleSize / 8)
          * channels, rate, bigEndian);
      DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
      if (!AudioSystem.isLineSupported(info)) {
        shutDown("Line matching " + info + " not supported.");
        return;
      }
      // get and open the target data line for capture.
      try {
        line = (TargetDataLine) AudioSystem.getLine(info);
        line.open(format, line.getBufferSize());
      } catch (LineUnavailableException ex) {
        shutDown("Unable to open the line: " + ex);
        return;
      } catch (SecurityException ex) {
        shutDown(ex.toString());
        //JavaSound.showInfoDialog();
        return;
      } catch (Exception ex) {
        shutDown(ex.toString());
        return;
      }
      // play back the captured audio data
      ByteArrayOutputStream out = new ByteArrayOutputStream();
      int frameSizeInBytes = format.getFrameSize();
      int bufferLengthInFrames = line.getBufferSize() / 8;
      int bufferLengthInBytes = bufferLengthInFrames * frameSizeInBytes;
      byte[] data = new byte[bufferLengthInBytes];
      int numBytesRead;
      line.start();
      while (thread != null) {
        if ((numBytesRead = line.read(data, 0, bufferLengthInBytes)) == -1) {
          break;
        }
        out.write(data, 0, numBytesRead);
      }
      // we reached the end of the stream.
      // stop and close the line.
      line.stop();
      line.close();
      line = null;
      // stop and close the output stream
      try {
        out.flush();
        out.close();
      } catch (IOException ex) {
        ex.printStackTrace();
      }
      // load bytes into the audio input stream for playback
      byte audioBytes[] = out.toByteArray();
      ByteArrayInputStream bais = new ByteArrayInputStream(audioBytes);
      audioInputStream = new AudioInputStream(bais, format, audioBytes.length / frameSizeInBytes);
      long milliseconds = (long) ((audioInputStream.getFrameLength() * 1000) / format
          .getFrameRate());
      duration = milliseconds / 1000.0;
      try {
        audioInputStream.reset();
      } catch (Exception ex) {
        ex.printStackTrace();
        return;
      }
    }
  } // End class Capture
  public static void main(String s[]) {
    SimpleSoundCapture ssc = new SimpleSoundCapture();
    ssc.open();
    JFrame f = new JFrame("Capture/Playback");
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    f.getContentPane().add("Center", ssc);
    f.pack();
    Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
    int w = 360;
    int h = 170;
    f.setLocation(screenSize.width / 2 - w / 2, screenSize.height / 2 - h / 2);
    f.setSize(w, h);
    f.show();
  }
}