package lettergame; import java.io.*; import javax.sound.sampled.*; /** * A Player takes input from a monaural wave file and plays it out to the speaker. * @author Kenneth J. Goldman, adapted in part from code by Ming Chow. * @version 1.0 */ class Player implements Runnable { private final int bufferSize = 16384; private AudioFormat format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 44100, 16, 1, 2, 44100, true); private AudioInputStream audioInputStream; private SourceDataLine line; private Thread thread; private boolean playingOut; private File soundFile; /** * Starts playing the given audio file, and returns immediately. * That is, the given file continues to play even though control is * returned to the caller. * @param file a monaural .wav file to play out */ public void start(File file) { this.soundFile = file; (thread = new Thread(this)).start(); } /** * Plays the given audio file to completion, and then returns. * @param file a monaural .wav file to play out */ public void play(File file) { start(file); try { thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } } /** * Reports whether or not playback is in progress. * @return true if an audio file is currently being played */ public synchronized boolean isPlaying() { return playingOut; } private synchronized void start() { playingOut = true; } /** * Terminates playback of the current audio file. */ public synchronized void stop() { playingOut = false; } /** * Prints an error message and terminates playback. * @param message the error message to report. */ private void fail(String message) { if (isPlaying()) { stop(); System.err.println(message); } } /** * This method should not be called directly. It embodies the playback * functionality that is started as a separate thread by the * start() and play() methods. */ public void run() { start(); // Make sure a file exists of the expected format if (soundFile.isFile()) { try { audioInputStream = AudioSystem.getAudioInputStream(soundFile); } catch (UnsupportedAudioFileException e1) { fail(e1.getMessage()); return; } catch (IOException e1) { fail(e1.getMessage()); return; } AudioInputStream playbackInputStream = AudioSystem.getAudioInputStream(format, audioInputStream); try { if (playbackInputStream == null) { fail("Can't convert audio format " + audioInputStream + " to format " + format); return; } // Check that a lin supports the required attributes. DataLine.Info info = new DataLine.Info( SourceDataLine.class, format); if (!AudioSystem.isLineSupported(info)) { fail("Line matching " + info + " not supported."); return; } // Get and open the source data line for playback. try { line = (SourceDataLine) AudioSystem.getLine(info); line.open(format, bufferSize); } catch (LineUnavailableException ex) { fail("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]; // Start the source data line line.start(); while (isPlaying()) { try { int numBytesRemaining = playbackInputStream.read(data); if (numBytesRemaining <= 0) // read something? break; while (numBytesRemaining > 0) numBytesRemaining -= line.write(data, 0, numBytesRemaining); } catch (Exception e) { fail("Error during playback: " + e); } } // After reading all the data, let it play out and // then stop and close the line. if (isPlaying()) line.drain(); line.stop(); line.close(); line = null; stop(); } catch (Exception e) { fail("Unable to create playback stream: " + e); } } } }