001/**
002 * Copyright 2001 Sun Microsystems, Inc.
003 * 
004 * See the file "license.terms" for information on usage and
005 * redistribution of this file, and for a DISCLAIMER OF ALL 
006 * WARRANTIES.
007 */
008package com.sun.speech.freetts.audio;
009
010import java.io.ByteArrayInputStream;
011import java.io.File;
012import java.io.IOException;
013import java.io.InputStream;
014import java.io.SequenceInputStream;
015import java.util.Vector;
016import java.util.logging.Level;
017import java.util.logging.Logger;
018
019import javax.sound.sampled.AudioFileFormat;
020import javax.sound.sampled.AudioFormat;
021import javax.sound.sampled.AudioInputStream;
022import javax.sound.sampled.AudioSystem;
023
024import com.sun.speech.freetts.util.Utilities;
025
026/**
027 * Streams audio to a file. 
028 *
029 *
030 */
031public class SingleFileAudioPlayer implements AudioPlayer {
032    /** Logger instance. */
033    private static final Logger LOGGER =
034        Logger.getLogger(SingleFileAudioPlayer.class.getName());
035
036    private AudioFormat currentFormat = null;
037    private String baseName;
038    private byte[] outputData;
039    private int curIndex = 0;
040    private int totBytes = 0;
041    private AudioFileFormat.Type outputType;
042    private Vector<InputStream> outputList;
043
044
045    /**
046     * Constructs a FileAudioPlayer 
047     *
048     * @param baseName the base name of the audio file
049     * @param type the type of audio output
050     *
051     */
052    public SingleFileAudioPlayer(String baseName, AudioFileFormat.Type type) {
053        this.baseName = baseName + "." + type.getExtension();
054        this.outputType = type;
055
056        outputList = new Vector<InputStream>();
057    }
058
059    /**
060     * Creates a default audio player for an AudioFileFormat of type
061     * WAVE.  Reads the "com.sun.speech.freetts.AudioPlayer.baseName"
062     * property for the base filename to use, and will produce a file
063     * of the form &lt;baseName>.wav.  The default value for the
064     * base name is "freetts".
065     */
066    public SingleFileAudioPlayer() {
067        this(Utilities.getProperty(
068                 "com.sun.speech.freetts.AudioPlayer.baseName", "freetts"),
069             AudioFileFormat.Type.WAVE);
070    }
071
072    /**
073     * Sets the audio format for this player
074     *
075     * @param format the audio format
076     *
077     * @throws UnsupportedOperationException if the line cannot be opened with
078     *     the given format
079     */
080    public synchronized void setAudioFormat(AudioFormat format) {
081        currentFormat = format;
082    }
083
084
085    /**
086     * Gets the audio format for this player
087     *
088     * @return format the audio format
089     */
090    public AudioFormat getAudioFormat() {
091        return currentFormat;
092    }
093
094
095    /**
096     * Pauses audio output
097     */
098    public void pause() {
099    }
100
101    /**
102     * Resumes audio output
103     */
104    public synchronized void resume() {
105    }
106        
107
108
109    /**
110     * Cancels currently playing audio
111     */
112    public synchronized void cancel() {
113    }
114
115    /**
116     * Prepares for another batch of output. Larger groups of output
117     * (such as all output associated with a single FreeTTSSpeakable)
118     * should be grouped between a reset/drain pair.
119     */
120    public synchronized void reset() {
121    }
122
123
124    /**
125     * Starts the first sample timer
126     */
127    public void startFirstSampleTimer() {
128    }
129
130    /**
131     * Closes this audio player
132     */
133    public synchronized void close() throws IOException {
134        try {
135            File file = new File(baseName);
136            InputStream is = new SequenceInputStream(outputList.elements());
137            AudioInputStream ais = new AudioInputStream(is,
138                    currentFormat, totBytes / currentFormat.getFrameSize());
139            if (LOGGER.isLoggable(Level.FINE)) {
140                LOGGER.fine("Avail " + ais.available());
141                LOGGER.fine("totBytes " + totBytes);
142                LOGGER.fine("FS " + currentFormat.getFrameSize());
143            }
144            LOGGER.info("Wrote synthesized speech to " + baseName);
145            AudioSystem.write(ais, outputType, file);
146        } catch (IllegalArgumentException iae) {
147            throw new IOException("Can't write audio type " + outputType, iae);
148        }
149    }
150
151
152    /**
153     * Returns the current volume.
154     *
155     * @return the current volume (between 0 and 1)
156     */
157    public float getVolume() {
158        return 1.0f;
159    }         
160
161    /**
162     * Sets the current volume.
163     *
164     * @param volume  the current volume (between 0 and 1)
165     */
166    public void setVolume(float volume) {
167    }         
168
169
170
171
172    /**
173     *  Starts the output of a set of data. Audio data for a single
174     *  utterance should be grouped between begin/end pairs.
175     *
176     * @param size the size of data between now and the end
177     */
178    public void begin(int size) {
179        outputData = new byte[size];
180        curIndex = 0;
181    }
182
183    /**
184     *  Marks the end of a set of data. Audio data for a single 
185     *  utterance should be groupd between begin/end pairs.
186     *
187     *  @return true if the audio was output properly, false if the
188     *      output was cancelled or interrupted.
189     *
190     */
191    public boolean  end()  {
192        outputList.add(new ByteArrayInputStream(outputData));
193        totBytes += outputData.length;
194        return true;
195    }
196
197
198    /**
199     * Waits for all queued audio to be played
200     *
201     * @return true if the audio played to completion, false if
202     *   the audio was stopped
203     */
204    public boolean drain()  {
205        return true;
206    }
207
208    /**
209     * Gets the amount of played since the last mark
210     *
211     * @return the amount of audio in milliseconds
212     */
213    public synchronized long getTime()  {
214        return -1L;
215    }
216
217
218    /**
219     * Resets the audio clock
220     */
221    public synchronized void resetTime() {
222    }
223    
224
225    
226    /**
227     * Writes the given bytes to the audio stream
228     *
229     * @param audioData audio data to write to the device
230     *
231     * @return <code>true</code> of the write completed successfully, 
232     *          <code> false </code>if the write was cancelled.
233     */
234    public boolean write(byte[] audioData) {
235        return write(audioData, 0, audioData.length);
236    }
237    
238    /**
239     * Writes the given bytes to the audio stream
240     *
241     * @param bytes audio data to write to the device
242     * @param offset the offset into the buffer
243     * @param size the size into the buffer
244     *
245     * @return <code>true</code> of the write completed successfully, 
246     *          <code> false </code>if the write was cancelled.
247     */
248    public boolean write(byte[] bytes, int offset, int size) {
249        System.arraycopy(bytes, offset, outputData, curIndex, size);
250        curIndex += size;
251        return true;
252    }
253
254    /**
255     * Returns the name of this audioplayer
256     *
257     * @return the name of the audio player
258     */
259    public String toString() {
260        return "FileAudioPlayer";
261    }
262
263    /**
264     * Shows metrics for this audio player
265     */
266    public void showMetrics() {
267    }
268}