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