?? animatedaudioplayer.java
字號:
/** * Copyright 2001 Sun Microsystems, Inc. * * See the file "license.terms" for information on usage and * redistribution of this file, and for a DISCLAIMER OF ALL * WARRANTIES. */package com.sun.speech.freetts.audio;import javax.sound.sampled.*;import com.sun.speech.freetts.util.BulkTimer;/** * Provides an implementation of <code>AudioPlayer</code> that creates * javax.sound.sampled audio clips and outputs them via the * javax.sound API. The interface provides a highly reliable audio * output package. Since audio is batched and not sent to the audio * layer until an entire utterance has been processed, this player has * higher latency (50 msecs for a typical 4 second utterance). * * The system property: * <code> com.sun.speech.freetts.audio.AudioPlayer.debug; * </code> if set to <code>true</code> will cause this * class to emit debugging information (useful to a developer). */public class AnimatedAudioPlayer implements AudioPlayer { private volatile boolean paused; private volatile boolean done = false; private volatile boolean cancelled = false; private volatile Clip currentClip; private float volume = 1.0f; // the current volume private boolean debug = false; private BulkTimer timer = new BulkTimer(); private AudioFormat defaultFormat = // default format is 8khz new AudioFormat(8000f, 16, 1, true, true); private AudioFormat currentFormat = defaultFormat; private boolean firstSample = true; private int curIndex = 0; private byte[] outputData; private LineListener lineListener = new JavaClipLineListener(); private long closeDelay; // workaround for linux sound bug private LineListener externalLineListener; // -- added by bd /** * Constructs a default AnimatedAudioPlayer * -- modified by bd */ public AnimatedAudioPlayer(LineListener externalLineListener) { this.externalLineListener = externalLineListener; debug = Boolean.getBoolean ("com.sun.speech.freetts.audio.AudioPlayer.debug"); closeDelay = Long.getLong ("com.sun.speech.freetts.audio.AudioPlayer.closeDelay", 150L).longValue(); setPaused(false); } /** * Sets the audio format for this player * * @param format the audio format * * @throws UnsupportedOperationException if the line cannot be opened with * the given format */ public synchronized void setAudioFormat(AudioFormat format) { currentFormat = format; } /** * Retrieves the audio format for this player * * @return format the audio format */ public AudioFormat getAudioFormat() { return currentFormat; } /** * Pauses audio output. All audio output is * stopped. Output can be resumed at the * current point by calling <code>resume</code>. Output can be * aborted by calling <code> cancel </code> */ public synchronized void pause() { if (!isPaused()) { setPaused(true); Clip clip = currentClip; if (clip != null) { clip.stop(); } } } /** * Resumes playing audio after a pause. * */ public synchronized void resume() { if (isPaused()) { setPaused(false); Clip clip = currentClip; if (clip != null) { clip.start(); } notify(); } } /** * Cancels all queued audio. Any 'write' in process will return * immediately false. */ public synchronized void cancel() { cancelled = true; Clip clip = currentClip; if (clip != null) { clip.close(); } } /** * Prepares for another batch of output. Larger groups of output * (such as all output associated with a single FreeTTSSpeakable) * should be grouped between a reset/drain pair. */ public synchronized void reset() { timer.start("speakableOut"); } /** * Waits for all queued audio to be played * * @return <code>true</code> if the write completed successfully, * <code> false </code>if the write was cancelled. */ public boolean drain() { timer.stop("speakableOut"); return true; } /** * Closes this audio player */ public synchronized void close() { done = true; notify(); } /** * Returns the current volume. * @return the current volume (between 0 and 1) */ public float getVolume() { return volume; } /** * Sets the current volume. * @param volume the current volume (between 0 and 1) */ public void setVolume(float volume) { if (volume > 1.0f) { volume = 1.0f; } if (volume < 0.0f) { volume = 0.0f; } this.volume = volume; Clip clip = currentClip; if (clip != null) { setVolume(clip, volume); } } /** * Sets pause mode * @param state true if we are paused */ private void setPaused(boolean state) { paused = state; } /** * Returns true if we are in pause mode * * @return <code> true </code>if paused; * otherwise returns <code>false</code> */ private boolean isPaused() { return paused; } /** * Sets the volume on the given clip * * @param line the line to set the volume on * @param vol the volume (range 0 to 1) */ private void setVolume(Clip clip, float vol) { if (clip.isControlSupported (FloatControl.Type.MASTER_GAIN)) { FloatControl volumeControl = (FloatControl) clip.getControl (FloatControl.Type.MASTER_GAIN); float range = volumeControl.getMaximum() - volumeControl.getMinimum(); volumeControl.setValue(vol * range + volumeControl.getMinimum()); } } /** * Returns the current position in the output stream since the * last <code>resetTime</code> * * Currently not supported. * * @return the position in the audio stream in milliseconds * */ public synchronized long getTime() { return -1L; } /** * Resets the time for this audio stream to zero */ public synchronized void resetTime() { } /** * Starts the output of a set of data. Audio data for a single * utterance should be grouped between begin/end pairs. * * @param size the size of data between now and the end * */ public void begin(int size) { cancelled = false; timer.start("utteranceOutput"); curIndex = 0; outputData = new byte[size]; } /** * Marks the end a set of data. Audio data for a single utterance should * be grouped between begin/end pairs. * * @return <code>true</code> if the audio was output properly, * <code>false </code> if the output was cancelled * or interrupted. */ public synchronized boolean end() { boolean ok = true; while (!done && !cancelled && isPaused()) { try { wait(); } catch (InterruptedException ie) { return false; } } if (done || cancelled) { cancelled = false; return false; } timer.start("clipGeneration"); try { DataLine.Info info = new DataLine.Info(Clip.class, currentFormat); Clip clip = (Clip) AudioSystem.getLine(info); clip.addLineListener(lineListener); clip.addLineListener(externalLineListener); // -- added by bd clip.open(currentFormat, outputData, 0, outputData.length); setVolume(clip, volume); if (currentClip != null) { throw new IllegalStateException("clip already set"); } currentClip = clip; clip.start(); try { while (currentClip != null) { wait(); // drain does not work } } catch (InterruptedException ie) { ok = false; } } catch (LineUnavailableException lue) { System.err.println("Line unavailable"); System.err.println("Format is " + currentFormat); ok = false; } timer.stop("clipGeneration"); timer.stop("utteranceOutput"); ok = !cancelled; cancelled = false; return ok; } /** * Writes the given bytes to the audio stream * * @param audioData audio data to write to the device * * @return <code>true</code> if the write completed successfully, * <code> false </code>if the write was cancelled. */ public boolean write(byte[] audioData) { return write(audioData, 0, audioData.length); } /** * Writes the given bytes to the audio stream * * @param bytes audio data to write to the device * @param offset the offset into the buffer * @param size the size into the buffer * * @return <code>true</code> if the write completed successfully, * <code> false </code>if the write was cancelled. */ public boolean write(byte[] bytes, int offset, int size) { if (firstSample) { firstSample = false; timer.stop("firstAudio"); } System.arraycopy(bytes, offset, outputData, curIndex, size); curIndex += size; return true; } /** * Returns the name of this audio player * * @return the name of the audio player */ public String toString() { return "JavaClipAudioPlayer"; } /** * Outputs the given msg if debugging is enabled for this * audio player. */ private void debugPrint(String msg) { if (debug) { System.out.println(toString() + ": " + msg); } } /** * Shows metrics for this audio player */ public void showMetrics() { timer.show(toString()); } /** * Starts the first sample timer */ public void startFirstSampleTimer() { timer.start("firstAudio"); firstSample = true; } /** * Provides a LineListener for this clas. */ private class JavaClipLineListener implements LineListener { /** * Implements update() method of LineListener interface. Responds * to the line events as appropriate. * * @param event the LineEvent to handle */ public void update(LineEvent event) { if (event.getType().equals(LineEvent.Type.START)) { debugPrint("Event START"); } else if (event.getType().equals(LineEvent.Type.STOP)) { debugPrint("Event STOP"); // A line may be stopped for three reasons: // the clip has finished playing // the clip has been paused // the clip has been cancelled (i.e. closed) // if the clip has stopped because it has finished // playing, then we should close the clip, otherwise leave // it alone synchronized(AnimatedAudioPlayer.this) { if (!cancelled && !isPaused()) { // BUG: // There is a javax.sound bug that causes a crash on linux // it is described here: // http://developer.java.sun.com/developer/bugParade/bugs/4498848.html // The bug seems to be related to synchronization of the close // call on the clip. If we delay before the close by // a small amount, the crash is averted. This is a WORKAROUND // only and should be removed once the javax.sound bug is // fixed. We get the delay from the property: // // com.sun.speech.freetts.audio.AudioPlayer.closeDelay // // try { if (closeDelay > 0L) { Thread.sleep(closeDelay); } } catch (InterruptedException ie) { } currentClip.close(); } } } else if (event.getType().equals(LineEvent.Type.OPEN)) { debugPrint("Event OPEN"); } else if (event.getType().equals(LineEvent.Type.CLOSE)) { // When a clip is closed we no longer need it, so // set currentClip to null and notify anyone who may // be waiting on it. debugPrint("EVNT CLOSE"); synchronized (AnimatedAudioPlayer.this) { currentClip = null; AnimatedAudioPlayer.this.notify(); } } } }}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -