001
002package ca.bc.webarts.tools;
003//import com.sun.speech.freetts.util.Utilities;
004
005import java.io.BufferedReader;
006import java.io.File;
007import java.io.IOException;
008import java.io.InputStreamReader;
009
010import java.util.Calendar;
011import java.util.GregorianCalendar;
012import java.util.Locale;
013
014import javax.speech.Central;
015import javax.speech.Engine;
016import javax.speech.EngineList;
017import javax.speech.EngineException;
018import javax.speech.synthesis.Synthesizer;
019import javax.speech.synthesis.SynthesizerModeDesc;
020import javax.speech.synthesis.SynthesizerProperties;
021import javax.speech.synthesis.Voice;
022//import com.sun.speech.freetts.util.Utilities;
023
024import ca.bc.webarts.widgets.Util;
025
026
027/**
028 *  Simple program showing how to use the Limited Domain (time) FreeTTS
029 *  synthesizer.
030 *
031 * @author    TGutwin
032 */
033public class JVoice implements Runnable
034{
035
036  private Synthesizer synthesizer;
037  private boolean initialized_ = false;
038  private static  boolean activeThread_ = false;
039  private static JVoice singletonInstance= null;
040  private static String [] textToRead_ = {""};
041  private static boolean verboseOutput_ = true;
042
043
044  /**
045   *  Construct a default JVoice object. It creates the Limited Domain
046   *  synthesizer.
047   */
048  private JVoice()
049  {
050    try
051    {
052      // Create a new SynthesizerModeDesc that will match the FreeTTS
053      // Synthesizer.
054      /*SynthesizerModeDesc desc = new SynthesizerModeDesc
055          ("Limited domain FreeTTS Speech Synthesizer from Sun Labs",
056           null,
057           Locale.US,
058           Boolean.FALSE,
059           null);*/
060      SynthesizerModeDesc desc = new SynthesizerModeDesc(
061                null,          // engine name
062                "general",     // mode name
063                Locale.US,     // locale
064                null,          // running
065                null);         // voice
066      synthesizer = Central.createSynthesizer(desc);
067
068      if (synthesizer == null)
069      {
070        String message = "Can't find synthesizer.\n" +
071            "Make sure that there is a \"speech.properties\" file " +
072            "at either of these locations: \n";
073        message += "user.home    : " +
074            System.getProperty("user.home") + "\n";
075        message += "java.home/lib: " + System.getProperty("java.home")
076             + File.separator + "lib\n";
077
078        System.err.println(message);
079        initialized_ = false;
080      }
081      else
082      {
083
084        // create the voice
085        String systemVoiceName = System.getProperty("voiceName", "kevin16");
086
087        Voice systemVoice = new Voice(systemVoiceName, Voice.GENDER_DONT_CARE, Voice.AGE_DONT_CARE, null);
088
089        // here are some other ones....
090        Voice voiceLQ = new Voice("kevin", Voice.GENDER_DONT_CARE, Voice.AGE_DONT_CARE, null);
091        Voice voiceHQ = new Voice("kevin16", Voice.GENDER_DONT_CARE, Voice.AGE_DONT_CARE, null);
092        Voice voiceAlan = new Voice("alan", Voice.GENDER_DONT_CARE, Voice.AGE_DONT_CARE, null);
093
094        // get it ready to speak
095        synthesizer.allocate();
096        synthesizer.waitEngineState(Synthesizer.ALLOCATED);
097        synthesizer.resume();
098        synthesizer.getSynthesizerProperties().setVoice(systemVoice);
099        initialized_ = true;
100      }
101    }
102    catch (Exception e)
103    {
104      e.printStackTrace();
105    }
106  }
107
108
109  /** Gets the singleton for this class **/
110  public static JVoice getInstance()
111  {
112    JVoice retVal = null;
113    if (singletonInstance != null && singletonInstance.isInitialized())
114      retVal = singletonInstance;
115    else
116    {
117      // create a new one
118      retVal = new JVoice();
119    }
120    return retVal;
121  }
122
123
124  /* Implements the Runnable by override run() method in interface */
125  public void run()
126  {
127    activeThread_ = true;
128    //System.out.println("JVoice Thread: run()");
129    readText();
130    activeThread_ = false;
131  }
132
133
134  /* waits until the read thread is done before returning */
135  public void blockUntilDoneVoicing()
136  {
137    while (activeThread_)
138      Util.sleep(500);
139  }
140
141
142  public void setTextToRead(String textToRead)
143  {
144    String [] txt = {textToRead};
145    setTextToRead(txt);
146  }
147
148
149  public void setTextToRead(String[] textToRead)
150  {
151    textToRead_ = textToRead;
152  }
153
154
155  /* a simple helper to inform if this class singleton has been init. */
156  public boolean isInitialized(){return initialized_;}
157
158
159    /**
160     * Example of how to list all the known voices for a specific
161     * mode using just JSAPI.  FreeTTS maps the domain name to the
162     * JSAPI mode name.  The currently supported domains are
163     * "general," which means general purpose synthesis for tasks
164     * such as reading e-mail, and "time" which means a domain that's
165     * only good for speaking the time of day.
166     */
167    public static void listAllVoices(String modeName)
168    {
169        System.out.println();
170        System.out.print(
171            "All " + modeName + " Mode JSAPI Synthesizers and Voices ");
172
173        /* Create a template that tells JSAPI what kind of speech
174         * synthesizer we are interested in.  In this case, we're
175         * just looking for a general domain synthesizer for US
176         * English.
177         */
178        SynthesizerModeDesc required = new SynthesizerModeDesc(
179            null,      // engine name
180            modeName,  // mode name
181            Locale.US, // locale
182            null,      // running
183            null);     // voices
184
185        /* Contact the primary entry point for JSAPI, which is
186         * the Central class, to discover what synthesizers are
187         * available that match the template we defined above.
188         */
189        EngineList engineList = Central.availableSynthesizers(required);
190        if(engineList!=null) System.out.println(" [" + engineList.size()  + "]");
191        for (int i = 0; i < engineList.size(); i++) {
192
193            SynthesizerModeDesc desc = (SynthesizerModeDesc) engineList.get(i);
194            System.out.println("    " + desc.getEngineName()
195                               + " (mode=" + desc.getModeName()
196                               + ", locale=" + desc.getLocale() + "):");
197            Voice[] voices = desc.getVoices();
198            for (int j = 0; j < voices.length; j++) {
199                System.out.println("        " + voices[j].getName());
200            }
201        }
202    }
203
204
205  /** some simple test routines. **/
206  private static void runTests(String[] argv)
207  {
208    listAllVoices("general");
209    if (argv!= null && argv.length >1)
210    {
211      try
212      {
213        String sentence = "";
214        JVoice jVoice = JVoice.getInstance();
215
216        for (int i=1; i <argv.length; i++)
217          sentence += argv[i]+" ";
218        if (jVoice.isInitialized())
219        {
220          jVoice.readText("J-Voice Test 1:");
221          jVoice.readText(sentence);
222          //jVoice.close();
223        }
224        jVoice = JVoice.getInstance();
225        if (jVoice.isInitialized())
226        {
227          jVoice.readText("J-Voice Test 2:");
228          jVoice.readText(sentence);
229          //jVoice.close();
230        }
231        jVoice.close();
232      }
233      catch (Exception e)
234      {
235        e.printStackTrace();
236      }
237    }
238  }
239
240  /**
241   *  The main program for the JVoice class
242   *
243   * @param  argv  The command line arguments
244   */
245  public static void main(String[] argv)
246  {
247    if (argv!= null && argv.length >0 && argv[0].equals("test"))
248    {
249      // Run Some tests
250      System.out.println("JVoice: Test Mode.");
251      JVoice jVoice = JVoice.getInstance();
252      jVoice.runTests(argv);
253    }
254    else
255    {
256      try
257      {
258        if (verboseOutput_)
259          System.out.println("JVoice: Voicing a Sentence.");
260        String sentence = "";
261        for (int i=0; i <argv.length; i++)
262          sentence += argv[i]+" ";
263        JVoice jVoice = JVoice.getInstance();
264        if (jVoice.isInitialized())
265        {
266          try
267          {
268            if (verboseOutput_)
269              System.out.println("JVoice: Start " + sentence);
270            jVoice.setTextToRead(argv);
271            (new Thread(jVoice)).start();
272            jVoice.activeThread_ = true;
273            while (jVoice.activeThread_)
274              Util.sleep(500);
275            //jVoice.readText(sentence);
276            jVoice.close();
277          }
278          catch (Exception ex)
279          {
280            if (jVoice != null)
281              jVoice.close();
282            ex.printStackTrace();
283          }
284        }
285        else
286          System.out.println("JVoice: Can't Initialize Speech Engine");
287        jVoice = null;
288      }
289      catch (Exception e)
290      {
291        e.printStackTrace();
292      }
293    }
294
295    System.exit(0);
296  }
297
298
299  /**
300   *  Reads the text in this class's textToRead_ field.
301   *
302   */
303  synchronized public void readText()
304  {
305    if (textToRead_ != null && textToRead_.length > 0)
306    {
307      String sentence = "";
308      for (int i=0; i <textToRead_.length; i++)
309        sentence += textToRead_[i]+" ";
310      readText(sentence);
311    }
312  }
313
314
315  /**
316   *  Reads an array of Strings.
317   *
318   * @param  theTextArray  An Array of Strings to read.
319   */
320  synchronized public void readText(String [] theTextArray)
321  {
322    if (theTextArray != null && theTextArray.length > 0)
323    {
324       textToRead_ = theTextArray;
325       readText();
326    }
327  }
328
329
330  /**
331   *  Reads text from the console and gives it to the synthesizer to speak.
332   *
333   * @param  theText  Description of the Parameter
334   */
335  synchronized public void readText(String theText)
336  {
337    if (synthesizer != null && initialized_)
338    {
339      if (verboseOutput_)
340        System.out.println("JVoice: Attempting to read the following sentence:");
341
342      if (theText != null && !(theText.equals("")))
343      {
344        if (verboseOutput_)
345          System.out.print("   "+ theText);
346        synthesizer.speakPlainText(theText, null);
347        if (verboseOutput_)
348          System.out.println("   !");
349      }
350
351      try
352      {
353        // wait 'till speaking is done
354        //System.out.println("JVoice: Waiting for the QUEUE to empty before proceeding");
355        synthesizer.waitEngineState(Synthesizer.QUEUE_EMPTY);
356        //System.out.println("! Done");
357      }
358      catch (java.lang.InterruptedException ie)
359      {
360        System.out.println("JVoice: readText(String): Interupted: " + ie);
361      }
362    }
363    else
364    {
365      System.out.println("JVoice: NOT Initialized");
366    }
367  }
368
369
370  /**  Closes things down  */
371  public void close()
372  {
373    //System.out.println("JVoice: close(): synthesizer==null ->"+ (synthesizer==null));
374    if (synthesizer != null)
375    {
376      try
377      {
378        try
379        {
380          // wait till speaking is done
381          synthesizer.waitEngineState(Synthesizer.QUEUE_EMPTY);
382        }
383        catch (java.lang.InterruptedException ie)
384        {
385          System.out.println("JVoice: close(): Interupted: " + ie);
386        }
387        synthesizer.deallocate();
388        synthesizer = null;
389        this.initialized_ = false;
390      }
391      catch (EngineException ee)
392      {
393        System.out.println("JVoice: close(): Trouble deallocating synthesizer: " + ee);
394      }
395      catch (Exception e)
396      {
397        e.printStackTrace();
398      }
399    }
400  }
401}
402
403