001/**
002 * Portions Copyright 2001 Sun Microsystems, Inc.
003 * Portions Copyright 1999-2001 Language Technologies Institute, 
004 * Carnegie Mellon University.
005 * All Rights Reserved.  Use is subject to license terms.
006 * 
007 * See the file "license.terms" for information on usage and
008 * redistribution of this file, and for a DISCLAIMER OF ALL 
009 * WARRANTIES.
010 */
011package com.sun.speech.freetts;
012
013import java.io.BufferedReader;
014import java.io.IOException;
015import java.io.InputStreamReader;
016import java.util.Calendar;
017import java.util.GregorianCalendar;
018import java.util.logging.ConsoleHandler;
019import java.util.logging.Handler;
020import java.util.logging.Level;
021import java.util.logging.Logger;
022import java.util.regex.Pattern;
023
024/**
025 * Standalone utility that tells the time.
026 *
027 * Defaults to "alan" voice.
028 * 
029 */
030public class FreeTTSTime extends FreeTTS {
031
032    private final static String VERSION = 
033            "FreeTTSTime Version 1.1, August  1, 2003";
034
035    /**
036     * Class constructor.
037     */
038    public FreeTTSTime() {
039        super(VoiceManager.getInstance().getVoice("alan"));
040    }
041
042    /**
043     * Class constructor.
044     *
045     * @param voice Voice to say time with
046     */
047    public FreeTTSTime(Voice voice) {
048        super(voice);
049    }
050
051
052    /**
053     * Prints the usage message for FreeTTSTime.
054     */
055    public static void usage() {
056        System.out.println(VERSION);
057        System.out.println("Usage:");
058        System.out.println("    -dumpASCII file : dump the final wave to file");
059        System.out.println("    -dumpAudio file : dump audio to file ");
060        System.out.println("    -help           : shows usage information");
061        System.out.println("    -detailedMetrics: turn on detailed metrics");
062        System.out.println("    -dumpRelations  : dump the relations ");
063        System.out.println("    -dumpUtterance  : dump the final utterance");
064        System.out.println("    -metrics        : turn on metrics");
065        System.out.println("    -run  name      : sets the name of the run");
066        System.out.println("    -silent         : don't say anything");
067        System.out.println("    -verbose        : verbose output");
068        System.out.println("    -version        : shows version number");
069        System.out.println("    -timeTest       : runs a lengthy time test");
070        System.out.println("    -iter count     : run for count iterations");
071        System.out.println("    -time XX:XX     : speak the given time");
072        System.out.println("    -time now       : speak the current time");
073        System.out.println("    -period secs    : period of iter");
074        System.out.println("    -clockMode      : tells time every 5 mins");
075        System.out.println("    -voice VOICE    : " +
076                VoiceManager.getInstance().toString());
077        System.exit(0);
078    }
079
080    /**
081     * Starts interactive mode on the given FreeTTSTime. Reads text
082     * from the console and gives it to FreeTTSTime to speak.
083     * terminates on end of file.
084     *
085     * @param freetts the engine that speaks
086     */
087    private static void interactiveMode(FreeTTSTime freetts) {
088        try {
089            while (true) {
090                String time;
091                BufferedReader reader;
092                reader = new BufferedReader(
093                    new InputStreamReader(System.in));
094                System.out.print("Enter time: ");
095                System.out.flush();
096                time = reader.readLine();
097                if ((time == null) || (time.length() == 0)
098                        || time.equals("quit") ) {
099                    freetts.shutdown();
100                    System.exit(0);
101                } else {
102                    freetts.getVoice().startBatch();
103                    freetts.safeTimeToSpeech(time);
104                    freetts.getVoice().endBatch();
105                }
106            }
107        } catch (IOException e) {
108        }
109    }
110
111    /**
112     * Returns a phrase that conveys the exactness
113     * of the time.
114     *
115     * @param hour the hour of the time
116     * @param min the minute of the time
117     *
118     * @return a string phrase
119     */
120    private static String timeApprox(int hour, int min) {
121        int mm;
122
123        mm = min % 5;
124
125        if ((mm == 0) || (mm == 4)) {
126            return "exactly";
127        } else if (mm == 1) {
128            return "just after";
129        } else if (mm == 2) {
130            return "a little after";
131        } else {
132            return "almost";
133        }
134    }
135
136    /**
137     * Returns a phrase that conveys the minutes in relation
138     * to the hour.
139     *
140     * @param hour the hour of the time
141     * @param min the minute of the time
142     *
143     * @return a string phrase.
144     */
145    private static String timeMin(int hour, int min) {
146        int mm;
147
148        mm = min / 5;
149        if ((min % 5) > 2) {
150            mm += 1;
151        }
152        mm = mm * 5;
153        if (mm > 55) {
154            mm = 0;
155        }
156
157        if (mm == 0) {
158            return "";
159        } else if (mm == 5) {
160            return "five past";
161        } else if (mm == 10) {
162            return "ten past";
163        } else if (mm == 15) {
164            return "quarter past";
165        } else if (mm == 20) {
166            return "twenty past";
167        } else if (mm == 25) {
168            return "twenty-five past";
169        } else if (mm == 30) {
170            return "half past";
171        } else if (mm == 35) {
172            return "twenty-five to";
173        } else if (mm == 40) {
174            return "twenty to";
175        } else if (mm == 45) {
176            return "quarter to";
177        } else if (mm == 50) {
178            return "ten to";
179        } else if (mm == 55) {
180            return "five to";
181        } else {
182            return "five to";
183        }
184    }
185
186    /**
187     * Returns a phrase that conveys the hour in relation
188     * to the hour.
189     *
190     * @param hour the hour of the time
191     * @param min the minute of the time
192     *
193     * @return a string phrase.
194     */
195    private static String timeHour(int hour, int min) {
196        int hh;
197
198        hh = hour;
199        if (min > 32)  { // PBL: fixed from flite_time
200            hh += 1;
201        }
202        if (hh == 24) {
203            hh = 0;
204        }
205        if (hh > 12) {
206            hh -= 12;
207        }
208
209        if (hh == 0) {
210            return "midnight";
211        } else if (hh == 1) {
212            return "one";
213        } else if (hh == 2) {
214            return "two";
215        } else if (hh == 3) {
216            return "three";
217        } else if (hh == 4) {
218            return "four";
219        } else if (hh == 5) {
220            return "five";
221        } else if (hh == 6) {
222            return "six";
223        } else if (hh == 7) {
224            return "seven";
225        } else if (hh == 8) {
226            return "eight";
227        } else if (hh == 9) {
228            return "nine";
229        } else if (hh == 10) {
230            return "ten";
231        } else if (hh == 11) {
232            return "eleven";
233        } else if (hh == 12) {
234            return "twelve";
235        } else {
236            return "twelve";
237        }
238    }
239
240    /**
241     * Returns a phrase that conveys the time of day.
242     *
243     * @param hour the hour of the time
244     * @param min the minute of the time
245     *
246     * @return a string phrase
247     */
248    private static String timeOfDay(int hour, int min) {
249        int hh = hour;
250
251        if (min > 58)
252            hh++;
253
254        if (hh == 24) {
255            return "";
256        } else if (hh > 17) {
257            return "in the evening";
258        } else if (hh > 11) {
259            return "in the afternoon";
260        } else {
261            return "in the morning";
262        }
263    }
264
265    /**
266     * Returns a string that corresponds to the given time.
267     *
268     * @param time the time in the form HH:MM
269     *
270     * @return the time in string, null if the given time is not in the
271     *   form HH:MM 
272     */
273    public static String timeToString(String time) {
274        String theTime = null;
275        if (Pattern.matches("[012][0-9]:[0-5][0-9]", time)) {
276            int hour = Integer.parseInt(time.substring(0, 2));
277            int min = Integer.parseInt(time.substring(3));
278
279            theTime = timeToString(hour, min);
280        }
281        return theTime;
282    }
283
284    /**
285     * Returns a string that corresponds to the given time.
286     *
287     * @param hour the hour
288     * @param min the minutes
289     *
290     * @return the time in string, null if the given time out of range
291     */
292    public static String timeToString(int hour, int min) {
293        String theTime = "The time is now, " +
294            timeApprox(hour, min) + " " +
295            timeMin(hour, min) + " " +
296            timeHour(hour, min) + ", " +
297            timeOfDay(hour, min) + "." ;
298        return theTime;
299    }
300
301    
302    /**
303     * Speaks the given time. Time should be in the exact form
304     * HH:MM where HH is the hour 00 to 23, and MM is the minute 00 to
305     * 59.
306     *
307     * @param time the time in the form HH:MM
308     *
309     * @throws IllegalArgumentException if time is not in the form
310     *   HH:MM
311     */
312    public void timeToSpeech(String time) {
313        String theTime = timeToString(time);
314        if (theTime != null) {
315            textToSpeech(theTime);
316        } else {
317            throw new IllegalArgumentException("Bad time format");
318        }
319    }
320    
321    /**
322     * Speaks the time given the hour and minute.
323     *
324     * @param hour the hour of the day (0 to 23)
325     * @param min the minute of the hour (0 to 59)
326     */
327    public void timeToSpeech(int hour, int min) {
328        if (hour < 0 || hour > 23) {
329            throw new IllegalArgumentException("Bad time format: hour");
330        }
331
332        if (min < 0 || min > 59) {
333            throw new IllegalArgumentException("Bad time format: min");
334        }
335        String theTime = timeToString(hour, min);
336        textToSpeech(theTime);
337    }
338
339
340    /**
341     * Speaks the given time.  Prints an error message if the time
342     * is ill-formed.
343     *
344     * @param time the time in the form HH:MM
345     */
346    public void safeTimeToSpeech(String time) {
347        try {
348            if (time.equals("now")) {
349                speakNow();
350            } else {
351                timeToSpeech(time);
352            }
353        } catch (IllegalArgumentException iae) {
354            System.err.println("Bad time format");
355        }
356    }
357
358    /**
359     * Tells the current time.
360     */
361    public void speakNow() {
362        Calendar cal = new GregorianCalendar();
363        int hour = cal.get(Calendar.HOUR_OF_DAY);
364        int min = cal.get(Calendar.MINUTE);
365        timeToSpeech(hour, min);
366    }
367
368    /**
369     * The main entry point for FreeTTSTime.
370     */
371    public static void main(String[] args) {
372
373        String time = null; // default is interactive mode
374        int iterations = 1;
375        int delay = 0;
376
377        boolean setMetrics = false;
378        boolean setDetailedMetrics = false;
379        boolean setVerbose = false;
380        boolean setDumpUtterance = false;
381        boolean setDumpRelations = false;
382        String waveDumpFile = null;
383        String runTitle = null;
384
385        boolean setSilentMode = false;
386        String audioFile = null;
387        boolean setInputMode = false;
388
389        String voiceName = null;
390
391        for (int i = 0; i < args.length; i++) {
392            if (args[i].equals("-metrics")) {
393                setMetrics = true;
394            } else if (args[i].equals("-detailedMetrics")) {
395                setDetailedMetrics = true;
396            } else if (args[i].equals("-silent")) {
397                setSilentMode = true;
398            } else if (args[i].equals("-period")) {
399                if (++i < args.length) {
400                    try {
401                      delay  = Integer.parseInt(args[i]);
402                    } catch (NumberFormatException nfe) {
403                        System.out.println("Bad clock period");
404                        usage();
405                    }
406                }
407            } else if (args[i].equals("-verbose")) {
408                setVerbose = true;
409            } else if (args[i].equals("-dumpUtterance")) {
410                setDumpUtterance = true;
411            } else if (args[i].equals("-dumpRelations")) {
412                setDumpRelations = true;
413            } else if (args[i].equals("-clockMode")) {
414                iterations = Integer.MAX_VALUE;
415                delay = 300;
416            } else if (args[i].equals("-timeTest")) {
417                iterations = 100;
418            } else if (args[i].equals("-dumpAudio")) {
419                if (++i < args.length) {
420                    audioFile = args[i];
421                } else {
422                    usage();
423                }
424            } else if (args[i].equals("-iter")) {
425                if (++i < args.length) {
426                    try {
427                      iterations = Integer.parseInt(args[i]);
428                    } catch (NumberFormatException nfe) {
429                        System.out.println("Bad iteration format");
430                        usage();
431                    }
432                } else {
433                    usage();
434                }
435            } else if (args[i].equals("-dumpASCII")) {
436                if (++i < args.length) {
437                    waveDumpFile = args[i];
438                } else {
439                    usage();
440                }
441            } else if (args[i].equals("-version")) {
442                System.out.println(VERSION);
443            } else if (args[i].equals("-help")) {
444                usage();
445            } else if (args[i].equals("-time")) {
446                setInputMode = true;
447                if (++i < args.length) {
448                    time = args[i];
449                } else {
450                    usage();
451                }
452            } else if (args[i].equals("-run")) {
453                if (++i < args.length) {
454                    runTitle = args[i];
455                } else {
456                    usage();
457                }
458            } else if (args[i].equals("-voice")) {
459                if (++i < args.length) {
460                    voiceName = args[i];
461                } else {
462                    usage();
463                }
464            } else {
465                System.out.println("Unknown option:" + args[i]);
466            }
467        }
468
469        if (voiceName == null) {
470            voiceName = "alan";
471        }
472
473        FreeTTSTime freetts = new
474            FreeTTSTime(VoiceManager.getInstance().getVoice(voiceName));
475        Voice voice = freetts.getVoice();
476
477        if (setMetrics) {
478            voice.setMetrics(true);
479        }
480
481        if (setDetailedMetrics) {
482            voice.setDetailedMetrics(true);
483        }
484
485        if (setVerbose) {
486            Handler handler = new ConsoleHandler();
487            handler.setLevel(Level.ALL);
488            Logger.getLogger("com.sun").addHandler(handler);
489            Logger.getLogger("com.sun").setLevel(Level.ALL);
490        }
491
492        if (setDumpUtterance) {
493            voice.setDumpUtterance(true);
494        }
495
496        if (setDumpRelations) {
497            voice.setDumpRelations(true);
498        }
499
500        if (waveDumpFile != null) {
501            voice.setWaveDumpFile(waveDumpFile);
502        }
503
504        if (runTitle != null) {
505            voice.setRunTitle(runTitle);
506        }
507
508        if (setSilentMode) {
509            freetts.setSilentMode(true);
510        }
511
512        if (audioFile != null) {
513            freetts.setAudioFile(audioFile);
514        }
515
516        if (setInputMode) {
517            freetts.setInputMode(InputMode.TEXT);
518        }
519    
520
521        freetts.startup();
522
523        if (time != null) {
524            freetts.getVoice().startBatch();
525            for (int i = 0; i < iterations; i++) {
526                freetts.safeTimeToSpeech(time);
527                try {
528                    Thread.sleep(delay * 1000L);
529                } catch (InterruptedException ie) {
530                    break;
531                }
532            }
533            freetts.getVoice().endBatch();
534        } else {
535            interactiveMode(freetts);
536        }
537        freetts.shutdown();
538        System.exit(0);
539    }
540}
541
542