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;
10 import java.util.Collection;
11 import java.util.Iterator;
13 import javax.speech.AudioException;
14 import javax.speech.AudioManager;
15 import javax.speech.Engine;
16 import javax.speech.EngineEvent;
17 import javax.speech.EngineException;
18 import javax.speech.EngineListener;
19 import javax.speech.EngineModeDesc;
20 import javax.speech.EngineProperties;
21 import javax.speech.EngineStateError;
22 import javax.speech.SpeechEvent;
23 import javax.speech.VocabManager;
26 * Supports the JSAPI 1.0 <code>Engine</code> interface.
27 * Actual JSAPI implementations might want to extend or modify this
30 abstract public class BaseEngine implements Engine, SpeechEventDispatcher {
32 * A bitmask holding the current state of this <code>Engine</code>.
34 protected long engineState;
37 * An <code>Object</code> used for synchronizing access to
38 * <code>engineState</code>.
41 protected Object engineStateLock;
44 * List of <code>EngineListeners</code> registered for
45 * <code>EngineEvents</code> on this <code>Engine</code>.
47 protected Collection engineListeners;
50 * The <code>AudioManager</code> for this <code>Engine</code>.
52 protected AudioManager audioManager = null;
55 * The <code>EngineModeDesc</code> for this <code>Engine</code>.
57 protected EngineModeDesc engineModeDesc = null;
60 * The <code>EngineProperties</code> for this <code>Engine</code>.
62 protected EngineProperties engineProperties = null;
65 * Utility state for clearing the <code>engineState</code>.
67 protected final static long CLEAR_ALL_STATE = ~(0L);
70 * Creates a new <code>Engine</code> in the
71 * <code>DEALLOCATED</code> state.
78 * Creates a new <code>Engine</code> in the
79 * <code>DEALLOCATED</code> state.
81 * @param desc the operating mode of this <code>Engine</code>
83 public BaseEngine(EngineModeDesc desc) {
84 engineModeDesc = desc;
85 engineListeners = new java.util.ArrayList();
86 engineState = DEALLOCATED;
87 engineStateLock = new Object();
88 engineProperties = createEngineProperties();
92 * Returns a or'ed set of flags indicating the current state of
93 * this <code>Engine</code>.
95 * <p>An <code>EngineEvent</code> is issued each time this
96 * <code>Engine</code> changes state.
98 * <p>The <code>getEngineState</code> method can be called successfully
99 * in any <code>Engine</code> state.
101 * @return the current state of this <code>Engine</code>
103 * @see #getEngineState
104 * @see #waitEngineState
106 public long getEngineState() {
111 * Blocks the calling thread until this <code>Engine</code>
112 * is in a specified state.
114 * <p>All state bits specified in the <code>state</code> parameter
115 * must be set in order for the method to return, as defined
116 * for the <code>testEngineState</code> method. If the <code>state</code>
117 * parameter defines an unreachable state
118 * (e.g. <code>PAUSED | RESUMED</code>) an exception is thrown.
120 * <p>The <code>waitEngineState</code> method can be called successfully
121 * in any <code>Engine</code> state.
123 * @param state a bitmask of the state to wait for
125 * @see #testEngineState
126 * @see #getEngineState
128 * @throws InterruptedException
129 * if another thread has interrupted this thread.
130 * @throws IllegalArgumentException
131 * if the specified state is unreachable
133 public void waitEngineState(long state)
134 throws InterruptedException, IllegalArgumentException {
135 synchronized (engineStateLock) {
136 while (!testEngineState(state))
137 engineStateLock.wait();
142 * Returns <code>true</code> if this state of this
143 * <code>Engine</code> matches the specified state.
145 * <p>The test performed is not an exact match to the current
146 * state. Only the specified states are tested. For
147 * example the following returns true only if the
148 * <code>Synthesizer</code> queue is empty, irrespective
149 * of the pause/resume and allocation states.
152 * if (synth.testEngineState(Synthesizer.QUEUE_EMPTY)) ...
155 * <p>The <code>testEngineState</code> method is equivalent to:
158 * if ((engine.getEngineState() & state) == state)
161 * <p>The <code>testEngineState</code> method can be called
162 * successfully in any <code>Engine</code> state.
164 * @param state a bitmask of the states to test for
166 * @return <code>true</code> if this <code>Engine</code> matches
167 * <code>state</code>; otherwise <code>false</code>
168 * @throws IllegalArgumentException
169 * if the specified state is unreachable
171 public boolean testEngineState(long state)
172 throws IllegalArgumentException {
173 return ((getEngineState() & state) == state);
177 * Updates this <code>Engine</code> state by clearing defined bits,
178 * then setting other specified bits.
180 * @return a length-2 array with old and new state values.
182 protected long[] setEngineState(long clear, long set) {
183 long states[] = new long[2];
184 synchronized (engineStateLock) {
185 states[0] = engineState;
186 engineState = engineState & (~clear);
187 engineState = engineState | set;
188 states[1] = engineState;
189 engineStateLock.notifyAll();
195 * Allocates the resources required for this <code>Engine</code> and
196 * puts it into the <code>ALLOCATED</code> state. When this method
197 * returns successfully the <code>ALLOCATED</code> bit of this
198 * <code>Engine</code> state is set, and the
199 * <code>testEngineState(Engine.ALLOCATED)</code> method returns
202 * <p>During the processing of the method, this <code>Engine</code> is
203 * temporarily in the <code>ALLOCATING_RESOURCES</code> state.
207 * @throws EngineException if this <code>Engine</code> cannot be allocated
208 * @throws EngineStateError if this <code>Engine</code> is in the
209 * <code>DEALLOCATING_RESOURCES</code> state
211 public void allocate() throws EngineException, EngineStateError {
212 if (testEngineState(ALLOCATED)) {
216 long[] states = setEngineState(CLEAR_ALL_STATE, ALLOCATING_RESOURCES);
217 postEngineAllocatingResources(states[0], states[1]);
223 * Called from the <code>allocate</code> method. Override this in
228 * @throws EngineException if problems are encountered
230 abstract protected void handleAllocate() throws EngineException;
233 * Frees the resources of this <code>Engine</code> that were
234 * acquired during allocation and during operation and return this
235 * <code>Engine</code> to the <code>DEALLOCATED</code>. When this
236 * method returns the <code>DEALLOCATED</code> bit of this
237 * <code>Engine</code> state is set so the
238 * <code>testEngineState(Engine.DEALLOCATED)</code> method returns
241 * <p>During the processing of the method, this
242 * <code>Engine</code> is temporarily in the
243 * <code>DEALLOCATING_RESOURCES</code> state.
245 * <p>A deallocated engine can be re-started with a subsequent
246 * call to <code>allocate</code>.
250 * @throws EngineException if this <code>Engine</code> cannot be
252 * @throws EngineStateError if this <code>Engine</code> is in the
253 * <code>ALLOCATING_RESOURCES</code> state
255 public void deallocate() throws EngineException, EngineStateError {
256 if (testEngineState(DEALLOCATED)) {
260 long[] states = setEngineState(CLEAR_ALL_STATE,
261 DEALLOCATING_RESOURCES);
262 postEngineDeallocatingResources(states[0], states[1]);
268 * Called from the <code>deallocate</code> method. Override this in
271 * @throws EngineException if this <code>Engine</code> cannot be
274 abstract protected void handleDeallocate() throws EngineException;
277 * Pauses the audio stream for this <code>Engine</code> and put
278 * this <code>Engine</code> into the <code>PAUSED</code> state.
280 * @throws EngineStateError if this <code>Engine</code> is in the
281 * <code>DEALLOCATING_RESOURCES</code> or
282 * <code>DEALLOCATED</code> state.
284 public void pause() throws EngineStateError {
285 synchronized (engineStateLock) {
286 checkEngineState(DEALLOCATED | DEALLOCATING_RESOURCES);
288 if (testEngineState(PAUSED)) {
294 long[] states = setEngineState(RESUMED, PAUSED);
295 postEnginePaused(states[0], states[1]);
300 * Called from the <code>pause</code> method. Override this in subclasses.
302 abstract protected void handlePause();
305 * Resumes the audio stream for this <code>Engine</code> and put
306 * this <code>Engine</code> into the <code>RESUMED</code> state.
308 * @throws AudioException if unable to gain access to the audio channel
309 * @throws EngineStateError if this <code>Engine</code> is in the
310 * <code>DEALLOCATING_RESOURCES</code> or
311 * <code>DEALLOCATED</code> state
313 public void resume() throws AudioException, EngineStateError {
314 synchronized (engineStateLock) {
315 checkEngineState(DEALLOCATED | DEALLOCATING_RESOURCES);
317 if (testEngineState(RESUMED))
322 long[] states = setEngineState(PAUSED, RESUMED);
323 postEngineResumed(states[0], states[1]);
328 * Called from the <code>resume</code> method. Override in subclasses.
330 abstract protected void handleResume();
333 * Returns an object that provides management of the audio input
334 * or output of this <code>Engine</code>.
336 * @return the audio manader for this <code>Engine</code>
338 public AudioManager getAudioManager() {
339 if (audioManager == null) {
340 audioManager = new BaseAudioManager();
346 * Returns an object that provides management of the vocabulary for
347 * this <code>Engine</code>. Returns <code>null</code> if this
348 * <code>Engine</code> does not support vocabulary management.
350 * @return the vocabulary manager of this <code>Engine</code>
352 * @throws EngineStateError if this <code>Engine</code> in the
353 * <code>DEALLOCATING_RESOURCES</code> or
354 * <code>DEALLOCATED</code> state
356 public VocabManager getVocabManager() throws EngineStateError {
361 * Gets the <code>EngineProperties</code> of this <code>Engine</code>.
362 * Must be set in subclasses.
364 * @return the <code>EngineProperties</code> of this <code>Engine</code>.
366 public EngineProperties getEngineProperties() {
367 return engineProperties;
371 * Gets the current operating properties and mode of
372 * this <code>Engine</code>.
374 * @return the operating mode of this <code>Engine</code>
376 * @throws SecurityException
378 public EngineModeDesc getEngineModeDesc() throws SecurityException {
379 return engineModeDesc;
383 * Sets the current operating properties and mode of
384 * this <code>Engine</code>.
386 * @param desc the new operating mode of this <code>Engine</code>
388 protected void setEngineModeDesc(EngineModeDesc desc) {
389 engineModeDesc = desc;
393 * Requests notification of <code>EngineEvents</code> from this
394 * <code>Engine</code>.
396 * @param listener the listener to add.
398 public void addEngineListener(EngineListener listener) {
399 synchronized (engineListeners) {
400 if (!engineListeners.contains(listener)) {
401 engineListeners.add(listener);
407 * Removes an <code>EngineListener</code> from the list of
408 * <code>EngineListeners</code>.
410 * @param listener the listener to remove.
412 public void removeEngineListener(EngineListener listener) {
413 synchronized (engineListeners) {
414 engineListeners.remove(listener);
419 * Utility function that generates an
420 * <code>ENGINE_ALLOCATED</code> event and posts it
421 * to the event queue. Eventually
422 * <code>fireEngineAllocated</code> will be called
423 * by the <code>dispatchSpeechEvent</code> as a result of this
426 * @param oldState the old state of this <code>Engine</code>
427 * @param newState the new state of this <code>Engine</code>
429 * @see #fireEngineAllocated
430 * @see #dispatchSpeechEvent
432 protected void postEngineAllocated(long oldState, long newState) {
433 SpeechEventUtilities.postSpeechEvent(
437 EngineEvent.ENGINE_ALLOCATED,
438 oldState, newState));
442 * Utility function that sends an <code>ENGINE_ALLOCATED</code>
443 * event to all <code>EngineListeners</code> registered with this
444 * <code>Engine</code>. Called by <code>dispatchSpeechEvent</code>.
446 * @param event the <code>ENGINE_ALLOCATED</code> event
448 * @see #postEngineAllocated
449 * @see #dispatchSpeechEvent
451 public void fireEngineAllocated(EngineEvent event) {
452 if (engineListeners == null) {
455 Iterator iterator = engineListeners.iterator();
456 while (iterator.hasNext()) {
457 EngineListener el = (EngineListener) iterator.next();
458 el.engineAllocated(event);
463 * Utility function that generates an
464 * <code>ENGINE_ALLOCATING_RESOURCES</code> event and
465 * posts it to the event queue. Eventually
466 * <code>fireEngineAllocatingResources</code>
467 * will be called by <code>dispatchSpeechEvent</code> as a
468 * result of this action.
470 * @param oldState the old state of this <code>Engine</code>
471 * @param newState the new state of this <code>Engine</code>
473 * @see #fireEngineAllocatingResources
474 * @see #dispatchSpeechEvent
476 protected void postEngineAllocatingResources(long oldState,
478 SpeechEventUtilities.postSpeechEvent(
482 EngineEvent.ENGINE_ALLOCATING_RESOURCES,
483 oldState, newState));
487 * Utility function that sends an
488 * <code>ENGINE_ALLOCATING_RESOURCES</code> event to all
489 * <code>EngineListeners</code> registered with this
490 * <code>Engine</code>. Called by <code>dispatchSpeechEvent</code>.
492 * @param event the <code>ENGINE_ALLOCATING_RESOURCES</code> event
494 * @see #postEngineAllocatingResources
495 * @see #dispatchSpeechEvent
497 public void fireEngineAllocatingResources(EngineEvent event) {
498 if (engineListeners == null) {
501 Iterator iterator = engineListeners.iterator();
502 while (iterator.hasNext()) {
503 EngineListener el = (EngineListener) iterator.next();
504 el.engineAllocatingResources(event);
509 * Utility function that generates an
510 * <code>ENGINE_DEALLOCATED</code> event and posts it
511 * to the event queue. Eventually
512 * <code>fireEngineDeallocated</code> will be called
513 * by <code>dispatchSpeechEvent</code> as a result of this action.
515 * @param oldState the old state of this <code>Engine</code>
516 * @param newState the new state of this <code>Engine</code>
518 * @see #fireEngineDeallocated
519 * @see #dispatchSpeechEvent
521 protected void postEngineDeallocated(long oldState, long newState) {
522 SpeechEventUtilities.postSpeechEvent(
526 EngineEvent.ENGINE_DEALLOCATED,
527 oldState, newState));
531 * Utility function that sends an
532 * <code>ENGINE_DEALLOCATED</code> event to all
533 * <code>EngineListeners</code> registered with this
534 * <code>Engine</code>. Called by <code>dispatchSpeechEvent</code>.
536 * @param event the <code>ENGINE_DEALLOCATED</code> event
538 * @see #postEngineDeallocated
539 * @see #dispatchSpeechEvent
541 public void fireEngineDeallocated(EngineEvent event) {
542 if (engineListeners == null) {
545 Iterator iterator = engineListeners.iterator();
546 while (iterator.hasNext()) {
547 EngineListener el = (EngineListener) iterator.next();
548 el.engineDeallocated(event);
553 * Utility function that generates
554 * <code>ENGINE_DEALLOCATING_RESOURCES</code> event and
555 * posts it to the event queue. Eventually
556 * <code>fireEngineAllocatingResources</code> will be called
557 * by <code>dispatchSpeechEvent</code> as a result of this action.
559 * @param oldState the old state of this <code>Engine</code>
560 * @param newState the new state of this <code>Engine</code>
562 * @see #fireEngineDeallocatingResources
563 * @see #dispatchSpeechEvent
565 protected void postEngineDeallocatingResources(long oldState,
567 SpeechEventUtilities.postSpeechEvent(
571 EngineEvent.ENGINE_DEALLOCATING_RESOURCES,
572 oldState, newState));
576 * Utility function that sends a
577 * <code>ENGINE_DEALLOCATING_RESOURCES</code> event to all
578 * <code>EngineListeners</code> registered with this
579 * <code>Engine</code>. Called by <code>dispatchSpeechEvent</code>.
581 * @param event the <code>ENGINE_DEALLOCATING_RESOURCES</code> event
583 * @see #postEngineDeallocatingResources
584 * @see #dispatchSpeechEvent
586 public void fireEngineDeallocatingResources(EngineEvent event) {
587 if (engineListeners == null) {
590 Iterator iterator = engineListeners.iterator();
591 while (iterator.hasNext()) {
592 EngineListener el = (EngineListener) iterator.next();
593 el.engineDeallocatingResources(event);
598 * Utility function that generates an
599 * <code>ENGINE_PAUSED</code> event and posts it
600 * to the event queue. Eventually
601 * <code>fireEnginePaused</code> will be called
602 * by <code>dispatchSpeechEvent</code> as a result of this action.
604 * @param oldState the old state of this <code>Engine</code>
605 * @param newState the new state of this <code>Engine</code>
607 * @see #fireEnginePaused
608 * @see #dispatchSpeechEvent
610 protected void postEnginePaused(long oldState, long newState) {
611 SpeechEventUtilities.postSpeechEvent(
615 EngineEvent.ENGINE_PAUSED,
616 oldState, newState));
620 * Utility function that sends an <code>ENGINE_PAUSED</code> event
622 * <code>EngineListeners</code> registered with this
623 * <code>Engine</code>. Called by <code>dispatchSpeechEvent</code>.
625 * @param event the <code>ENGINE_PAUSED</code> event
627 * @see #postEnginePaused
628 * @see #dispatchSpeechEvent
630 public void fireEnginePaused(EngineEvent event) {
631 if (engineListeners == null) {
634 Iterator iterator = engineListeners.iterator();
635 while (iterator.hasNext()) {
636 EngineListener el = (EngineListener) iterator.next();
637 el.enginePaused(event);
642 * Utility function that generates an <code>ENGINE_RESUMED</code>
643 * event and posts it to the event queue. Eventually
644 * <code>fireEngineResumed</code> will be called
645 * by <code>dispatchSpeechEvent</code> as a result of this action.
647 * @param oldState the old state of this <code>Engine</code>
648 * @param newState the new state of this <code>Engine</code>
650 * @see #fireEngineResumed
651 * @see #dispatchSpeechEvent
653 protected void postEngineResumed(long oldState, long newState) {
654 SpeechEventUtilities.postSpeechEvent(
658 EngineEvent.ENGINE_RESUMED,
659 oldState, newState));
663 * Utility function that sends an <code>ENGINE_RESUMED</code> event
665 * <code>EngineListeners</code> registered with this
666 * <code>Engine</code>. Called by <code>dispatchSpeechEvent</code>.
668 * @param event the <code>ENGINE_RESUMED</code> event
670 * @see #postEngineResumed
671 * @see #dispatchSpeechEvent
673 public void fireEngineResumed(EngineEvent event) {
674 if (engineListeners == null) {
677 Iterator iterator = engineListeners.iterator();
678 while (iterator.hasNext()) {
679 EngineListener el = (EngineListener) iterator.next();
680 el.engineResumed(event);
685 * Factory constructor for EngineProperties object.
687 * @return a <code>BaseEngineProperties</code> object specific to
690 abstract protected BaseEngineProperties createEngineProperties();
693 * Convenience method that throws an <code>EngineStateError</code>
694 * if any of the bits in the passed state are set in the
695 * <code>state</code>.
697 * @param state the <code>Engine</code> state to check
699 * @throws EngineStateError if any of the bits in the passed state
700 * are set in the <code>state</code>
702 protected void checkEngineState(long state) throws EngineStateError {
703 long currentState = getEngineState();
704 if ((currentState & state) != 0) {
705 throw new EngineStateError
706 ("Invalid EngineState: expected=("
707 + stateToString(state) + ") current state=("
708 + stateToString(currentState) + ")");
713 * Returns a <code>String</code> of the names of all the
714 * <code>Engine</code> states in the given <code>Engine</code>
717 * @param state the bitmask of states
719 * @return a <code>String</code> containing the names of all the
720 * states set in <code>state</code>
722 protected String stateToString(long state) {
723 StringBuffer buf = new StringBuffer();
724 if ((state & Engine.DEALLOCATED) != 0)
725 buf.append(" DEALLOCATED ");
726 if ((state & Engine.ALLOCATING_RESOURCES) != 0)
727 buf.append(" ALLOCATING_RESOURCES ");
728 if ((state & Engine.ALLOCATED) != 0)
729 buf.append(" ALLOCATED ");
730 if ((state & Engine.DEALLOCATING_RESOURCES) != 0)
731 buf.append(" DEALLOCATING_RESOURCES ");
732 if ((state & Engine.PAUSED) != 0)
733 buf.append(" PAUSED ");
734 if ((state & Engine.RESUMED) != 0)
735 buf.append(" RESUMED ");
736 return buf.toString();
740 * Dispatches a <code>SpeechEvent</code>.
741 * The dispatcher should notify all <code>EngineListeners</code>
742 * from this method. The <code>SpeechEvent</code> was added
743 * via the various post methods of this class.
745 * @param event the <code>SpeechEvent</code> to dispatch
747 * @see #postEngineAllocatingResources
748 * @see #postEngineAllocated
749 * @see #postEngineDeallocatingResources
750 * @see #postEngineDeallocated
751 * @see #postEnginePaused
752 * @see #postEngineResumed
754 public void dispatchSpeechEvent(SpeechEvent event) {
755 switch (event.getId()) {
756 case EngineEvent.ENGINE_ALLOCATED:
757 fireEngineAllocated((EngineEvent) event);
759 case EngineEvent.ENGINE_ALLOCATING_RESOURCES:
760 fireEngineAllocatingResources((EngineEvent) event);
762 case EngineEvent.ENGINE_DEALLOCATED:
763 fireEngineDeallocated((EngineEvent) event);
765 case EngineEvent.ENGINE_DEALLOCATING_RESOURCES:
766 fireEngineDeallocatingResources((EngineEvent) event);
768 //case EngineEvent.ENGINE_ERROR:
769 //fireEngineError((EngineErrorEvent) event);
771 case EngineEvent.ENGINE_PAUSED:
772 fireEnginePaused((EngineEvent) event);
774 case EngineEvent.ENGINE_RESUMED:
775 fireEngineResumed((EngineEvent) event);
781 * Returns the engine name and mode for debug purposes.
783 * @return the engine name and mode.
785 public String toString() {
786 return getEngineModeDesc().getEngineName() +
787 ":" + getEngineModeDesc().getModeName();