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
<source lang="java"> /*
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(); }
}
</source>
An example of playing a sound with an echo filter
<source lang="java"> /*
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.*
* 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); } } </source>
An example that plays a Midi sequence
<source lang="java"> /*
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; }
}
</source>
A simple player for sampled sound files.
<source lang="java">
/*
* * 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); }
}
</source>
audio sound: implements java.applet.AudioClip
<source lang="java">
// 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(); }
}
</source>
Capturing Audio with Java Sound API
<source lang="java">
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(); }
}
</source>
Continuously Playing a Sampled Audio File
<source lang="java">
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); }
}
</source>
Determine the duration of a Midi audio file
<source lang="java">
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; }
}
</source>
Determining the Duration of a Midi Audio File
<source lang="java">
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; }
}
</source>
Determining the Duration of a Sampled Audio File
<source lang="java">
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()); }
}
</source>
Determining the Encoding of a Sampled Audio File
<source lang="java">
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")); }
}
</source>
Determining the File Format of a Midi Audio File
<source lang="java">
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; } }
}
</source>
Determining the File Format of a Sampled Audio File
<source lang="java">
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) { } }
}
</source>
Determining the Position of a Midi Sequencer
<source lang="java">
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; }
}
</source>
Determining the Position of a Sampled Audio Player
<source lang="java">
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; }
}
</source>
Determining When a Midi Audio Player Has Finished Playing
<source lang="java">
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 } } }); }
}
</source>
Determining When a Sampled Audio Player Has Finished Playing
<source lang="java">
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) { } } }); }
}
</source>
Duke Speaks
Duke Speaks Test
Float Control Component
<source lang="java">
import javax.sound.sampled.FloatControl; public class Main {
FloatControl control; public Main(FloatControl c) { control = c; control.setValue(3); }
}
</source>
Load and play Midi audio
<source lang="java">
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(); }
}
</source>
Load audio file From URL
<source lang="java">
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) { } }
}
</source>
Load image and sound from Jar file
<source lang="java">
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); }
}
</source>
Loading and Playing Midi Audio
<source lang="java">
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(); }
}
</source>
Loading and Playing Sampled Audio
<source lang="java">
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(); }
}
</source>
Make your own Java Media Player to play media files
<source lang="java">
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(); }
}
</source>
Play an audio file from a JAR file
<source lang="java">
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); }
}
</source>
Play a streaming Midi audio
<source lang="java">
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(); }
}
</source>
Playing Streaming Midi Audio
<source lang="java">
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(); }
}
</source>
Playing Streaming Sampled Audio
<source lang="java">
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(); }
}
</source>
Play sound
<source lang="java">
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(); }
}
</source>
Setting the Volume of a Sampled Audio Player
<source lang="java">
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); }
}
</source>
Setting the Volume of Playing Midi Audio
<source lang="java">
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)); } } }
}
</source>
Simple program to try out the new Sound stuff in JDK1.2
<source lang="java">
/*
* 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); }
}
</source>
Sound Applet
<source lang="java">
/* 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); }
}
</source>
Sound Application
<source lang="java">
/* 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); }
}
</source>
Sound Manager Test
<source lang="java"> /*
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:*
-
*
- Playing a Midi sequence. *
- Toggle a track of a playing Midi sequence. *
- Playing a sound. *
- Playing a Sound with an Echo filter. *
- Looping a sound. *
- Playing the maximum number of sounds at once. *
- Pausing all sounds. *
* <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:*
-
*
- add a setMasterVolume() method, which uses Controls to set the volume * for each line. *
- don"t play a sound if more than, say, 500ms has passed since the request * to play *
*/
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:*
-
*
- Add an option to only loop a certain number of times. *
*/
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 length
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; } }
}
</source>
Sound player
<source lang="java">
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) { } }
}
</source>
The Filter3dTest class demonstrates the Filter3d functionality
<source lang="java"> /*
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:*
-
*
- pan the sound to the left and right speakers *
* * @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:*
-
*
- Add an option to only loop a certain number of times. *
*/
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 length
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; }
}
</source>
This is a simple program to record sounds and play them back
<source lang="java">
/*
* * 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(); }
}
</source>