2 * Copyright 1998-2001 Sun Microsystems, Inc.
4 * See the file "license.terms" for information on usage and
5 * redistribution of this file, and for a DISCLAIMER OF ALL
8 package com.sun.speech.engine.synthesis;
10 import java.io.IOException;
12 import java.util.Iterator;
14 import javax.speech.SpeechEvent;
15 import javax.speech.synthesis.JSMLException;
16 import javax.speech.synthesis.Speakable;
17 import javax.speech.synthesis.SpeakableEvent;
18 import javax.speech.synthesis.SpeakableListener;
19 import javax.speech.synthesis.SynthesizerQueueItem;
21 import org.w3c.dom.Document;
23 import com.sun.speech.engine.SpeechEventDispatcher;
24 import com.sun.speech.engine.SpeechEventUtilities;
27 * Extends the JSAPI 1.0 <code>SynthesizerQueueItem</code> with handling
28 * for JSML, generation of engine-specific text, and other features.
30 public class BaseSynthesizerQueueItem extends SynthesizerQueueItem
31 implements SpeechEventDispatcher {
32 private volatile boolean done = false;
33 private volatile boolean cancelled = false;
36 * The object containing the DOM of the parsed JSML.
38 private Document document = null;
41 * Global count of queue items used for debug.
43 protected static int itemNumber = 0;
47 * Count for this item used for debug.
49 protected int thisItemNumber = 0;
53 * <code>Synthesizer</code> that has queued this item.
55 protected BaseSynthesizer synth;
60 public BaseSynthesizerQueueItem() {
61 super(null, null, false, null);
62 thisItemNumber = itemNumber++;
66 * Sets queue item data with a <code>Speakable</code> source.
68 * @param synth the synthesizer
69 * @param source the <code>Speakable</code>
70 * @param listener the <code>SpeakableListener</code> to be
71 * notified as this object is processed
73 * @throws JSMLException if the <code>source</code> contains JSML errors
75 protected void setData(BaseSynthesizer synth,
77 SpeakableListener listener)
78 throws JSMLException {
81 this.text = source.getJSMLText();
82 this.plainText = false;
83 this.listener = listener;
84 document = new JSMLParser(this.text, false).getDocument();
88 * Sets queue item data with a <code>String</code> source that is
89 * either plain text or JSML.
91 * @param synth the synthesizer
92 * @param source the text
93 * @param plainText <code>true</code> only if the
94 * <code>source</code> is plain text
95 * @param listener the <code>SpeakableListener</code> to be
96 * notified as this object is processed
98 * @throws JSMLException if the <code>source</code> contains JSML errors
100 protected void setData(BaseSynthesizer synth,
103 SpeakableListener listener)
104 throws JSMLException {
106 this.source = source;
108 this.plainText = plainText;
109 this.listener = listener;
111 document = new JSMLParser(this.text, false).getDocument();
117 * Sets queue item data with a <code>URL</code> source.
119 * @param synth the synthesizer
120 * @param source the <code>URL</code> containing JSML text
121 * @param listener the <code>SpeakableListener</code> to be
122 * notified as this object is processed
124 * @throws JSMLException if the <code>source</code> contains JSML errors
125 * @throws IOException if there are problems working with the URL.
127 protected void setData(BaseSynthesizer synth,
129 SpeakableListener listener)
130 throws JSMLException, IOException {
132 this.source = source;
134 this.plainText = false;
135 this.listener = listener;
136 document = new JSMLParser(source, false).getDocument();
140 * Gets the DOM document for this object.
142 * @return the DOM document for this object.
144 protected Document getDocument() {
150 * determines if this queue item has been canceled
152 * @return <code> true </code> if this item has been canceled;
153 * otherwise <code> false </code>
155 protected synchronized boolean isCancelled() {
161 * returns true if this queue item has been
163 * @return true if it has been processed
165 public synchronized boolean isCompleted() {
170 * wait for this queue item to be completed
172 * @return true if the item was completed successfully, false if
173 * the item was canceled or an error occurred.
175 public synchronized boolean waitCompleted() {
176 while (!isCompleted()) {
179 } catch (InterruptedException ie) {
181 "FreeTTSSynthesizerQueueItem.Wait interrupted");
185 return !isCancelled();
189 * indicate that this item has been canceled
191 public synchronized void cancelled() {
192 postSpeakableCancelled();
197 * indicate that this item has been completed
199 public synchronized void completed() {
200 postSpeakableEnded();
205 * indicate that this item has been started
207 public void started() {
208 postSpeakableStarted();
212 * Gets the item number for debug purposes only. Each queue item
213 * is given a unique ID.
215 * @return the unique ID for this queue item
217 public int getItemNumber() {
218 return thisItemNumber;
222 * Utility function that generates a
223 * <code>MARKER_REACHED</code> event and posts it
224 * to the event queue. Eventually
225 * <code>fireMarkerReached</code> will be called
226 * by <code>dispatchSpeechEvent</code> as a result
229 * @param text the text of the marker
230 * @param markerType the type of marker
232 * @see SpeakableEvent#getMarkerType
233 * @see #fireMarkerReached
234 * @see #dispatchSpeechEvent
236 public void postMarkerReached(String text, int markerType) {
237 SpeechEventUtilities.postSpeechEvent(
239 new SpeakableEvent(source,
240 SpeakableEvent.MARKER_REACHED,
245 * Utility function that sends a <code>MARKER_REACHED</code> event
246 * to all speakable listeners.
248 * @param event the <code>MARKER_REACHED</code> event
250 * @see #postMarkerReached
252 public void fireMarkerReached(SpeakableEvent event) {
253 if (listener != null) {
254 listener.markerReached(event);
258 if (synth.speakableListeners != null) {
259 Iterator iterator = synth.speakableListeners.iterator();
260 while (iterator.hasNext()) {
261 SpeakableListener sl = (SpeakableListener) iterator.next();
262 sl.markerReached(event);
268 * Utility function that generates a
269 * <code>SPEAKABLE_CANCELLED</code> event and posts it
270 * to the event queue. Eventually
271 * <code>fireSpeakableCancelled</code> will be called
272 * by <code>dispatchSpeechEvent</code> as a result
275 * @see #fireSpeakableCancelled
276 * @see #dispatchSpeechEvent
278 public void postSpeakableCancelled() {
281 // The JSAPI docs say that once a canceled event is sent, no
282 // others will be. This makes sure that a canceled will never be
283 // sent twice. This deals with the race that can occur when an
284 // item that is playing is canceled.
292 SpeechEventUtilities.postSpeechEvent(
294 new SpeakableEvent(source, SpeakableEvent.SPEAKABLE_CANCELLED));
299 * Utility function that sends a <code>SPEAKABLE_CANCELLED</code> event
300 * to all speakable listeners.
302 * @param event the <code>SPEAKABLE_CANCELLED</code> event
304 * @see #postSpeakableCancelled
306 public void fireSpeakableCancelled(SpeakableEvent event) {
307 if (listener != null) {
308 listener.speakableCancelled(event);
312 if (synth.speakableListeners != null) {
313 Iterator iterator = synth.speakableListeners.iterator();
314 while (iterator.hasNext()) {
315 SpeakableListener sl = (SpeakableListener) iterator.next();
316 sl.speakableCancelled(event);
322 * Utility function that generates a
323 * <code>SPEAKABLE_ENDED</code> event and posts it
324 * to the event queue. Eventually
325 * <code>fireSpeakableEnded</code> will be called
326 * by <code>dispatchSpeechEvent</code> as a result
329 * @see #fireSpeakableEnded
330 * @see #dispatchSpeechEvent
332 public void postSpeakableEnded() {
335 // The JSAPI docs say that once a canceled event is sent, no
336 // others will be. This makes sure that a canceled will never be
337 // sent twice. This deals with the race that can occur when an
338 // item that is playing is canceled.
344 SpeechEventUtilities.postSpeechEvent(
346 new SpeakableEvent(source, SpeakableEvent.SPEAKABLE_ENDED));
352 * Utility function that sends a <code>SPEAKABLE_ENDED</code> event
353 * to all speakable listeners.
355 * @param event the <code>SPEAKABLE_ENDED</code> event
357 * @see #postSpeakableEnded
359 public void fireSpeakableEnded(SpeakableEvent event) {
360 if (listener != null) {
361 listener.speakableEnded(event);
365 if (synth.speakableListeners != null) {
366 Iterator iterator = synth.speakableListeners.iterator();
367 while (iterator.hasNext()) {
368 SpeakableListener sl = (SpeakableListener) iterator.next();
369 sl.speakableEnded(event);
375 * Utility function that generates a
376 * <code>SPEAKABLE_PAUSED</code> event and posts it
377 * to the event queue. Eventually
378 * <code>fireSpeakablePaused</code> will be called
379 * by <code>dispatchSpeechEvent</code> as a result
382 * @see #fireSpeakablePaused
383 * @see #dispatchSpeechEvent
385 public void postSpeakablePaused() {
386 SpeechEventUtilities.postSpeechEvent(
388 new SpeakableEvent(source, SpeakableEvent.SPEAKABLE_PAUSED));
392 * Utility function that sends a <code>SPEAKABLE_PAUSED</code> event
393 * to all speakable listeners.
395 * @param event the <code>SPEAKABLE_PAUSED</code> event
397 * @see #postSpeakablePaused
399 public void fireSpeakablePaused(SpeakableEvent event) {
400 if (listener != null) {
401 listener.speakablePaused(event);
405 if (synth.speakableListeners != null) {
406 Iterator iterator = synth.speakableListeners.iterator();
407 while (iterator.hasNext()) {
408 SpeakableListener sl = (SpeakableListener) iterator.next();
409 sl.speakablePaused(event);
415 * Utility function that generates a
416 * <code>SPEAKABLE_RESUMED</code> event and posts it
417 * to the event queue. Eventually
418 * <code>fireSpeakableResumed</code> will be called
419 * by <code>dispatchSpeechEvent</code> as a result
422 * @see #fireSpeakableResumed
423 * @see #dispatchSpeechEvent
425 public void postSpeakableResumed() {
426 SpeechEventUtilities.postSpeechEvent(
428 new SpeakableEvent(source, SpeakableEvent.SPEAKABLE_RESUMED));
432 * Utility function that sends a <code>SPEAKABLE_RESUMED</code> event
433 * to all speakable listeners.
435 * @param event the <code>SPEAKABLE_RESUMED</code> event
437 * @see #postSpeakableResumed
439 public void fireSpeakableResumed(SpeakableEvent event) {
440 if (listener != null) {
441 listener.speakableResumed(event);
445 if (synth.speakableListeners != null) {
446 Iterator iterator = synth.speakableListeners.iterator();
447 while (iterator.hasNext()) {
448 SpeakableListener sl =
449 (SpeakableListener) iterator.next();
450 sl.speakableResumed(event);
456 * Utility function that generates a
457 * <code>SPEAKABLE_STARTED</code> event and posts it
458 * to the event queue. Eventually
459 * <code>fireSpeakableStarted</code> will be called
460 * by <code>dispatchSpeechEvent</code> as a result
463 * @see #fireSpeakableStarted
464 * @see #dispatchSpeechEvent
466 public void postSpeakableStarted() {
467 SpeechEventUtilities.postSpeechEvent(
469 new SpeakableEvent(source, SpeakableEvent.SPEAKABLE_STARTED));
473 * Utility function that sends a <code>SPEAKABLE_STARTED</code> event
474 * to all speakable listeners.
476 * @param event the <code>SPEAKABLE_STARTED</code> event
478 * @see #postSpeakableStarted
480 public void fireSpeakableStarted(SpeakableEvent event) {
481 if (listener != null) {
482 listener.speakableStarted(event);
486 if (synth.speakableListeners != null) {
487 Iterator iterator = synth.speakableListeners.iterator();
488 while (iterator.hasNext()) {
489 SpeakableListener sl = (SpeakableListener) iterator.next();
490 sl.speakableStarted(event);
496 * Utility function that generates a
497 * <code>TOP_OF_QUEUE</code> event and posts it
498 * to the event queue. Eventually
499 * <code>fireTopOfQueue</code> will be called
500 * by <code>dispatchSpeechEvent</code> as a result
503 * @see #fireTopOfQueue
504 * @see #dispatchSpeechEvent
506 public void postTopOfQueue() {
507 SpeechEventUtilities.postSpeechEvent(
509 new SpeakableEvent(source, SpeakableEvent.TOP_OF_QUEUE));
513 * Utility function that sends a <code>TOP_OF_QUEUE</code> event
514 * to all speakable listeners.
516 * @param event the <code>TOP_OF_QUEUE</code> event
518 * @see #postTopOfQueue
520 public void fireTopOfQueue(SpeakableEvent event) {
521 if (listener != null) {
522 listener.topOfQueue(event);
526 if (synth.speakableListeners != null) {
527 Iterator iterator = synth.speakableListeners.iterator();
528 while (iterator.hasNext()) {
529 SpeakableListener sl = (SpeakableListener) iterator.next();
530 sl.topOfQueue(event);
536 * Utility function that generates a
537 * <code>WORD_STARTED</code> event and posts it
538 * to the event queue. Eventually
539 * <code>fireWordStarted</code> will be called
540 * by <code>dispatchSpeechEvent</code> as a result
543 * @see #fireWordStarted
544 * @see #dispatchSpeechEvent
546 public void postWordStarted(String text, int wordStart, int wordEnd) {
547 SpeechEventUtilities.postSpeechEvent(
549 new SpeakableEvent(source, SpeakableEvent.WORD_STARTED,
550 text, wordStart, wordEnd));
554 * Utility function that sends a <code>WORD_STARTED</code> event
555 * to all speakable listeners.
557 * @param event the <code>WORD_STARTED</code> event
559 * @see #postWordStarted
561 public void fireWordStarted(SpeakableEvent event) {
562 if (listener != null) {
563 listener.wordStarted(event);
567 if (synth.speakableListeners != null) {
568 Iterator iterator = synth.speakableListeners.iterator();
569 while (iterator.hasNext()) {
570 SpeakableListener sl = (SpeakableListener) iterator.next();
571 sl.wordStarted(event);
577 * Dispatches a <code>SpeechEvent</code>.
578 * The dispatcher should notify all <code>EngineListeners</code>
579 * from this method. The <code>SpeechEvent</code> was added
580 * via the various post methods of this class.
582 * @param event the <code>SpeechEvent</code> to dispatch
584 * @see #postMarkerReached
585 * @see #postSpeakableCancelled
586 * @see #postSpeakableEnded
587 * @see #postSpeakablePaused
588 * @see #postSpeakableResumed
589 * @see #postSpeakableStarted
590 * @see #postTopOfQueue
591 * @see #postWordStarted
593 public void dispatchSpeechEvent(SpeechEvent event) {
594 switch (event.getId()) {
595 case SpeakableEvent.MARKER_REACHED:
596 fireMarkerReached((SpeakableEvent) event);
598 case SpeakableEvent.SPEAKABLE_CANCELLED:
599 fireSpeakableCancelled((SpeakableEvent) event);
601 case SpeakableEvent.SPEAKABLE_ENDED:
602 fireSpeakableEnded((SpeakableEvent) event);
604 case SpeakableEvent.SPEAKABLE_PAUSED:
605 fireSpeakablePaused((SpeakableEvent) event);
607 case SpeakableEvent.SPEAKABLE_RESUMED:
608 fireSpeakableResumed((SpeakableEvent) event);
610 case SpeakableEvent.SPEAKABLE_STARTED:
611 fireSpeakableStarted((SpeakableEvent) event);
613 case SpeakableEvent.TOP_OF_QUEUE:
614 fireTopOfQueue((SpeakableEvent) event);
616 case SpeakableEvent.WORD_STARTED:
617 fireWordStarted((SpeakableEvent) event);