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