📄 javastreamingaudioplayer.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 com.sun.speech.freetts.util.BulkTimer;import com.sun.speech.freetts.util.Utilities;import javax.sound.sampled.AudioFormat;import javax.sound.sampled.AudioSystem;import javax.sound.sampled.SourceDataLine;import javax.sound.sampled.DataLine;import javax.sound.sampled.FloatControl;import javax.sound.sampled.Line;import javax.sound.sampled.LineEvent;import javax.sound.sampled.LineListener;import javax.sound.sampled.LineUnavailableException;/** * Streams audio to java audio. This class provides a low latency * method of sending audio output through the javax.sound audio API. * Audio data is sent in small sets to the audio system allowing it to * be played soon after it is generated. * * Unfortunately, the current release of the JDK (JDK 1.4 beta 2) has * a bug or two in * the implementation of 'SourceDataLine.drain'. A workaround solution that * sleep/waits on SourceDataLine.isActive is used here instead. To * disable the work around (i.e use the real 'drain') set the * property: * <p> * <code> * com.sun.speech.freetts.audio.AudioPlayer.drainWorksProperly; * </code> * to <code>true</code>. * * If the workaround is enabled, the line.isActive method will be * performed periodically. The period of the test can be controlled * with: * * <p> * <code> * com.sun.speech.freetts.audio.AudioPlayer.drainDelay" * </code> * * <p> * The default if 5ms. * * <p> * The property * <code> * com.sun.speech.freetts.audio.AudioPlayer.bufferSize" * </code> * * <p> * Controls the audio buffer size, it defaults to 8192 * * <p> * Even with this drain work around, there are some issues with this * class. The workaround drain is not completely reliable. * A <code>resume</code> following a <code>pause</code> does not * always continue at the proper position in the audio. On a rare * occasion, sound output will be repeated a number of times. This may * be related to bug 4421330 in the Bug Parade database. * * */public class JavaStreamingAudioPlayer implements AudioPlayer { private volatile boolean paused; private volatile boolean done = false; private volatile boolean cancelled = false; private SourceDataLine line; private float volume = 1.0f; // the current volume private long timeOffset = 0L; private BulkTimer timer = new BulkTimer(); // default format is 8khz private AudioFormat defaultFormat = new AudioFormat(8000f, 16, 1, true, true); private AudioFormat currentFormat = defaultFormat; private boolean debug = false; private boolean firstSample = true; private long cancelDelay; private long drainDelay; private long openFailDelayMs; private long totalOpenFailDelayMs; private Object openLock = new Object(); private Object lineLock = new Object(); /** * controls the buffering to java audio */ private final static int AUDIO_BUFFER_SIZE = Utilities.getInteger( "com.sun.speech.freetts.audio.AudioPlayer.bufferSize", 8192).intValue(); /** * controls the number of bytes of audio to write to the buffer * for each call to write() */ private final static int BYTES_PER_WRITE = Utilities.getInteger ("com.sun.speech.freetts.audio.AudioPlayer.bytesPerWrite", 160).intValue(); /** * Constructs a default JavaStreamingAudioPlayer */ public JavaStreamingAudioPlayer() { debug = Utilities.getBoolean ("com.sun.speech.freetts.audio.AudioPlayer.debug"); cancelDelay = Utilities.getLong ("com.sun.speech.freetts.audio.AudioPlayer.cancelDelay", 0L).longValue(); drainDelay = Utilities.getLong ("com.sun.speech.freetts.audio.AudioPlayer.drainDelay", 150L).longValue(); openFailDelayMs = Utilities.getLong ("com.sun.speech.freetts.audio.AudioPlayer.openFailDelayMs", 0L).longValue(); totalOpenFailDelayMs = Utilities.getLong ("com.sun.speech.freetts.audio.AudioPlayer.totalOpenFailDelayMs", 0L).longValue(); line = null; 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; debugPrint("AF changed to " + format); } /** * Gets the audio format for this player * * @return format the audio format */ public AudioFormat getAudioFormat() { return currentFormat; } /** * Starts the first sample timer */ public void startFirstSampleTimer() { timer.start("firstAudio"); firstSample = true; } /** * Opens the audio * * @param format the format for the audio * * @throws UnsupportedOperationException if the line cannot be opened with * the given format */ private synchronized void openLine(AudioFormat format) { synchronized (lineLock) { if (line != null) { line.close(); line = null; } } DataLine.Info info = new DataLine.Info(SourceDataLine.class, format); boolean opened = false; long totalDelayMs = 0; do { try { line = (SourceDataLine) AudioSystem.getLine(info); line.addLineListener(new JavaStreamLineListener()); synchronized (openLock) { line.open(format, AUDIO_BUFFER_SIZE); try { openLock.wait(); } catch (InterruptedException ie) { ie.printStackTrace(); } opened = true; } } catch (LineUnavailableException lue) { System.err.println("LINE UNAVAILABLE: " + "Format is " + currentFormat); try { Thread.sleep(openFailDelayMs); totalDelayMs += openFailDelayMs; } catch (InterruptedException ie) { ie.printStackTrace(); } } } while (!opened && totalDelayMs < totalOpenFailDelayMs); if (opened) { setVolume(line, volume); resetTime(); if (isPaused() && line.isRunning()) { line.stop(); } else { line.start(); } } else { if (line != null) { line.close(); } line = null; } } /** * Pauses audio output */ public synchronized void pause() { if (!isPaused()) { setPaused(true); if (line != null) { line.stop(); } } } /** * Resumes audio output */ public synchronized void resume() { if (isPaused()) { setPaused(false); if (!isCancelled() && line != null) { line.start(); notify(); } } } /** * Cancels currently playing audio. */ // [[[ WORKAROUND TODO // The "Thread.sleep(cancelDelay)" is added to fix a problem in the // FreeTTSEmacspeak demo. The problem was that the engine would // stutter after using it for a while. Adding this sleep() fixed the // problem. If we later find out that this problem no longer exists, // we should remove the thread.sleep(). ]]] public void cancel() { debugPrint("cancelling..."); if (cancelDelay > 0) { try { Thread.sleep(cancelDelay); } catch (InterruptedException ie) { ie.printStackTrace(); } } synchronized (lineLock) { if (line != null && line.isRunning()) { line.stop(); line.flush(); } } /* sets 'cancelled' to false, which breaks the write while loop */ synchronized (this) { cancelled = true; notify(); } debugPrint("...cancelled"); } /** * 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("audioOut"); if (line != null) { waitResume(); if (isCancelled() && !isDone()) { cancelled = false; line.start(); } } } /** * Closes this audio player */ public synchronized void close() { done = true; if (line != null && line.isOpen()) { line.close(); line = null; notify();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -