The Filter3dTest class demonstrates the Filter3d functionality : Sound « Development Class « Java






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.Component;
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.comp = 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;
  }
}         
           
         
  








Related examples in the same category

1.Play sound
2.Duke Speaks
3.Duke Speaks Test
4.Sound Applet
5.Sound player
6.Simple program to try out the new Sound stuff in JDK1.2
7.Sound Application Sound Application
8.An example of loading and playing a sound using a ClipAn example of loading and playing a sound using a Clip
9.An example of playing a sound with an echo filter
10.Sound Manager TestSound Manager Test
11.An example that plays a Midi sequence
12.Determining When a Sampled Audio Player Has Finished Playing
13.Setting the Volume of a Sampled Audio Player
14.Determining the Position of a Sampled Audio Player
15.Load audio file From URL
16.Playing Streaming Sampled Audio
17.Determining the File Format of a Sampled Audio File
18.Loading and Playing Sampled Audio
19.Determining the Position of a Midi Sequencer
20.Continuously Playing a Sampled Audio File
21.Load image and sound from Jar file
22.Determine the duration of a Midi audio file
23.Playing Streaming Midi Audio
24.Capturing Audio with Java Sound API
25.A simple player for sampled sound files.
26.Load and play Midi audio
27.Play a streaming Midi audio
28.Determining When a Midi Audio Player Has Finished Playing
29.Float Control Component
30.This is a simple program to record sounds and play them back
31.Determining the Duration of a Midi Audio File
32.Loading and Playing Midi Audio
33.Determining the File Format of a Midi Audio File
34.Play an audio file from a JAR file
35.Setting the Volume of Playing Midi Audio
36.Make your own Java Media Player to play media files
37.Determining the Encoding of a Sampled Audio File
38.Determining the Duration of a Sampled Audio File
39.audio sound: implements java.applet.AudioClip