[258991] support TTS engines that do not support multiple instances
diff --git a/plugins/org.eclipse.actf.ai.screenreader.jaws/src/org/eclipse/actf/ai/screenreader/jaws/Jaws.java b/plugins/org.eclipse.actf.ai.screenreader.jaws/src/org/eclipse/actf/ai/screenreader/jaws/Jaws.java
index 25ed274..6740f4f 100644
--- a/plugins/org.eclipse.actf.ai.screenreader.jaws/src/org/eclipse/actf/ai/screenreader/jaws/Jaws.java
+++ b/plugins/org.eclipse.actf.ai.screenreader.jaws/src/org/eclipse/actf/ai/screenreader/jaws/Jaws.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2007 IBM Corporation and Others
+ * Copyright (c) 2007, 2008 IBM Corporation and Others
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -7,6 +7,7 @@
  *
  * Contributors:
  *    Daisuke SATO - initial API and implementation
+ *    Kentarou FUKUDA - initial API and implementation
  *******************************************************************************/
 
 package org.eclipse.actf.ai.screenreader.jaws;
@@ -39,6 +40,11 @@
 	public void dispose() {
 		// not supported
 	}
+	
+	public boolean isDisposed() {
+		return false;
+	}
+
 
 	/*
 	 * (non-Javadoc)
@@ -161,4 +167,5 @@
 			return false;
 		return jaws.isAvailable();
 	}
+	
 }
diff --git a/plugins/org.eclipse.actf.ai.screenreader.windoweyes/src/org/eclipse/actf/ai/screenreader/windoweyes/engine/GWSpeak.java b/plugins/org.eclipse.actf.ai.screenreader.windoweyes/src/org/eclipse/actf/ai/screenreader/windoweyes/engine/GWSpeak.java
index 595da36..8a863d9 100644
--- a/plugins/org.eclipse.actf.ai.screenreader.windoweyes/src/org/eclipse/actf/ai/screenreader/windoweyes/engine/GWSpeak.java
+++ b/plugins/org.eclipse.actf.ai.screenreader.windoweyes/src/org/eclipse/actf/ai/screenreader/windoweyes/engine/GWSpeak.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2007 IBM Corporation and Others
+ * Copyright (c) 2007, 2008 IBM Corporation and Others
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -7,6 +7,7 @@
  *
  * Contributors:
  *    Takashi ITOH - initial API and implementation
+ *    Kentarou FUKUDA - initial API and implementation
  *******************************************************************************/
 
 package org.eclipse.actf.ai.screenreader.windoweyes.engine;
@@ -27,19 +28,21 @@
 	private IGWSpeak dispGWSpeak = null; // Instanceof GWSpeak.Speak ActiveX
 	private IVoiceEventListener eventListener = null;
 	private boolean notifyEndOfSpeech = false; // Invoke indexReceived() if
-												// true
+	// true
 	private long lastNotificationTime = 0; // Last time of indexReceived()
 
 	// Constants
 	private static final int DELAY_FIRST = 500; // Delay on the first
-												// indexReceived()
+	// indexReceived()
 	private static final int DELAY_NEXT = 1000; // Delay on the subsequent
-												// indexReceived()
+	// indexReceived()
 	private static final TCHAR GWM_WINDOW_CLASS = new TCHAR(0,
 			"GWMExternalControl", true); //$NON-NLS-1$
 	private static final TCHAR GWM_WINDOW_NAME = new TCHAR(0,
 			"External Control", true); //$NON-NLS-1$
 
+	private boolean isDisposed = false;
+
 	/**
 	 * Constructor
 	 */
@@ -65,6 +68,7 @@
 	 * @see org.eclipse.actf.ai.tts.ITTSEngine#dispose()
 	 */
 	public void dispose() {
+		isDisposed = true;
 		if (null != dispGWSpeak) {
 			eventListener = null;
 			stop();
@@ -195,4 +199,8 @@
 	public void setSpeed(int speed) {
 	}
 
+	public boolean isDisposed() {
+		return isDisposed;
+	}
+
 }
diff --git a/plugins/org.eclipse.actf.ai.tts.protalker/src/org/eclipse/actf/ai/tts/protalker/engine/ProTalker.java b/plugins/org.eclipse.actf.ai.tts.protalker/src/org/eclipse/actf/ai/tts/protalker/engine/ProTalker.java
index 2d23530..843ed25 100644
--- a/plugins/org.eclipse.actf.ai.tts.protalker/src/org/eclipse/actf/ai/tts/protalker/engine/ProTalker.java
+++ b/plugins/org.eclipse.actf.ai.tts.protalker/src/org/eclipse/actf/ai/tts/protalker/engine/ProTalker.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2007 IBM Corporation and Others
+ * Copyright (c) 2007, 2008 IBM Corporation and Others
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -7,6 +7,7 @@
  *
  * Contributors:
  *    Daisuke SATO - initial API and implementation
+ *    Kentarou FUKUDA - initial API and implementation
  *******************************************************************************/
 package org.eclipse.actf.ai.tts.protalker.engine;
 
@@ -27,6 +28,8 @@
 
 	private ProTalkerBridge engine;
 
+	private boolean isDisposed = false;
+
 	public ProTalker() {
 		engine = new ProTalkerBridge(Display.getDefault());
 		setVoice();
@@ -145,6 +148,16 @@
 	 * @see org.eclipse.actf.ai.tts.ITTSEngine#dispose()
 	 */
 	public void dispose() {
+		if (!isDisposed) {
+			isDisposed = true;
+			engine.dispose();
+			if (ProTalkerPlugin.getDefault() != null) {
+				ProTalkerPlugin.getDefault().removePropertyChangeListener(this);
+			}
+		}
+	}
 
+	public boolean isDisposed() {
+		return isDisposed;
 	}
 }
diff --git a/plugins/org.eclipse.actf.ai.tts.protalker/src/org/eclipse/actf/ai/tts/protalker/engine/ProTalkerBridge.java b/plugins/org.eclipse.actf.ai.tts.protalker/src/org/eclipse/actf/ai/tts/protalker/engine/ProTalkerBridge.java
index 55759de..0b0e4f5 100644
--- a/plugins/org.eclipse.actf.ai.tts.protalker/src/org/eclipse/actf/ai/tts/protalker/engine/ProTalkerBridge.java
+++ b/plugins/org.eclipse.actf.ai.tts.protalker/src/org/eclipse/actf/ai/tts/protalker/engine/ProTalkerBridge.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2007 IBM Corporation and Others
+ * Copyright (c) 2007, 2008 IBM Corporation and Others
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -7,6 +7,7 @@
  *
  * Contributors:
  *    Daisuke SATO - initial API and implementation
+ *    Kentarou FUKUDA - initial API and implementation
  *******************************************************************************/
 package org.eclipse.actf.ai.tts.protalker.engine;
 
@@ -77,9 +78,11 @@
 	private OleFrame frame = null;
 
 	private Vector<IVoiceEventListener> indexListener = new Vector<IVoiceEventListener>();
+	
+	private Shell parent;
 
 	ProTalkerBridge(Display display) {
-		Shell parent = new Shell();
+		parent = new Shell();
 		parent.setLayout(new FillLayout());
 		frame = new OleFrame(parent, SWT.NONE);
 		try {
@@ -95,6 +98,11 @@
 		}
 		setVoice(VOICE_MALE);
 	}
+	
+	public void dispose(){
+		auto.dispose();
+		parent.dispose();
+	}
 
 	private Variant getProperty(String name) {
 		int id = auto.getIDsOfNames(new String[] { name })[0];
diff --git a/plugins/org.eclipse.actf.ai.tts.protalker/src/org/eclipse/actf/ai/tts/protalker/preferences/ProTalkerFieldEditor.java b/plugins/org.eclipse.actf.ai.tts.protalker/src/org/eclipse/actf/ai/tts/protalker/preferences/ProTalkerFieldEditor.java
index 29c34c0..dfd3732 100644
--- a/plugins/org.eclipse.actf.ai.tts.protalker/src/org/eclipse/actf/ai/tts/protalker/preferences/ProTalkerFieldEditor.java
+++ b/plugins/org.eclipse.actf.ai.tts.protalker/src/org/eclipse/actf/ai/tts/protalker/preferences/ProTalkerFieldEditor.java
@@ -7,6 +7,7 @@
  *
  * Contributors:
  *    Daisuke SATO - initial API and implementation
+ *    Kentarou FUKUDA - initial API and implementation
  *******************************************************************************/
 package org.eclipse.actf.ai.tts.protalker.preferences;
 
@@ -62,5 +63,11 @@
 			proTalker = null;
 		}
 	}
+	
+	@Override
+	public void dispose() {
+		proTalker.dispose();
+		super.dispose();
+	}
 
 }
diff --git a/plugins/org.eclipse.actf.ai.tts.sapi/src/org/eclipse/actf/ai/tts/sapi/engine/SapiVoice.java b/plugins/org.eclipse.actf.ai.tts.sapi/src/org/eclipse/actf/ai/tts/sapi/engine/SapiVoice.java
index bc48fe5..d3a333f 100644
--- a/plugins/org.eclipse.actf.ai.tts.sapi/src/org/eclipse/actf/ai/tts/sapi/engine/SapiVoice.java
+++ b/plugins/org.eclipse.actf.ai.tts.sapi/src/org/eclipse/actf/ai/tts/sapi/engine/SapiVoice.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2007 IBM Corporation and Others
+ * Copyright (c) 2007, 2008 IBM Corporation and Others
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -7,10 +7,11 @@
  *
  * Contributors:
  *    Takashi ITOH - initial API and implementation
+ *    Kentarou FUKUDA - initial API and implementation
  *******************************************************************************/
 package org.eclipse.actf.ai.tts.sapi.engine;
 
-import org.eclipse.actf.ai.tts.ITTSEngine;
+import org.eclipse.actf.ai.tts.ISAPIEngine;
 import org.eclipse.actf.ai.tts.sapi.SAPIPlugin;
 import org.eclipse.actf.ai.voice.IVoiceEventListener;
 import org.eclipse.actf.util.win32.COMUtil;
@@ -26,16 +27,12 @@
 /**
  * The implementation of ITTSEngine to use Microsoft Speech API.
  */
-public class SapiVoice implements ITTSEngine, IPropertyChangeListener {
+public class SapiVoice implements ISAPIEngine, IPropertyChangeListener {
 
 	public static final String ID = "org.eclipse.actf.ai.tts.sapi.engine.SapiVoice"; //$NON-NLS-1$
 	public static final String AUDIO_OUTPUT = "org.eclipse.actf.ai.tts.SapiVoice.audioOutput"; //$NON-NLS-1$
 
-	public static final int SVSFDefault = 0, SVSFlagsAsync = 1,
-			SVSFPurgeBeforeSpeak = 2, SVSFIsFilename = 4, SVSFIsXML = 8,
-			SVSFIsNotXML = 16, SVSFPersistXML = 32;
-
-	private ISpVoice dispSpVoice;
+	public ISpVoice dispSpVoice;
 	private Variant varSapiVoice;
 	private OleAutomation automation;
 	private int idGetVoices;
@@ -43,6 +40,7 @@
 	private ISpNotifySource spNotifySource = null;
 	private static IPreferenceStore preferenceStore = SAPIPlugin.getDefault()
 			.getPreferenceStore();
+	private boolean isDisposed = false;
 
 	public SapiVoice() {
 		int pv = COMUtil.createDispatch(ISpVoice.IID);
@@ -55,8 +53,16 @@
 		idGetVoices = getIDsOfNames("GetVoices"); //$NON-NLS-1$
 		idGetAudioOutputs = getIDsOfNames("GetAudioOutputs"); //$NON-NLS-1$
 
-		setVoiceName();
+		// init by using default engine to avoid init error of some TTS engines
+		String orgID = preferenceStore.getString(ID);
+		preferenceStore.setValue(ID, preferenceStore.getDefaultString(ID));
 		setAudioOutputName();
+		// switch to actual engine
+		preferenceStore.setValue(ID, orgID);
+		// setVoiceName();
+
+		// to avoid access violation error at application shutdown
+		stop();
 	}
 
 	/*
@@ -103,7 +109,7 @@
 		}
 	}
 
-	private void speak(String text, int sapiFlags) {
+	public void speak(String text, int sapiFlags) {
 		char[] data = (text + "\0").toCharArray(); //$NON-NLS-1$
 		int bstrText = MemoryUtil.SysAllocString(data);
 		try {
@@ -192,6 +198,30 @@
 			}
 			varVoices.dispose();
 		}
+		if (!success) {
+			int index = voiceName.indexOf("name=");
+			varVoices = getVoices(null, null);
+			if (null != varVoices && index > -1) {
+				String name = voiceName.substring(index + 5);
+				SpeechObjectTokens voiceTokens = SpeechObjectTokens
+						.getTokens(varVoices);
+				if (null != voiceTokens) {
+					int count = voiceTokens.getCount();
+					for (int i = 0; i < count; i++) {
+						Variant varVoice = voiceTokens.getItem(i);
+						if (null != varVoice) {
+							SpObjectToken token = SpObjectToken
+									.getToken(varVoice);
+							if (null != token
+									&& name.equals(token.getDescription(0))) {
+								success = setVoice(varVoice);
+							}
+						}
+					}
+				}
+			}
+			varVoices.dispose();
+		}
 		return success;
 	}
 
@@ -282,7 +312,15 @@
 	 * @see org.eclipse.actf.ai.tts.ITTSEngine#dispose()
 	 */
 	public void dispose() {
-		varSapiVoice.dispose();
+		if (!isDisposed) {
+			isDisposed = true;
+
+			varSapiVoice.dispose();
+
+			if (SAPIPlugin.getDefault() != null) {
+				SAPIPlugin.getDefault().removePropertyChangeListener(this);
+			}
+		}
 	}
 
 	/*
@@ -338,4 +376,8 @@
 	public boolean isAvailable() {
 		return automation != null;
 	}
+
+	public boolean isDisposed() {
+		return isDisposed;
+	}
 }
diff --git a/plugins/org.eclipse.actf.ai.tts.sapi/src/org/eclipse/actf/ai/tts/sapi/preferences/SapiAudioOutputFieldEditor.java b/plugins/org.eclipse.actf.ai.tts.sapi/src/org/eclipse/actf/ai/tts/sapi/preferences/SapiAudioOutputFieldEditor.java
index e1574bd..7c0a260 100644
--- a/plugins/org.eclipse.actf.ai.tts.sapi/src/org/eclipse/actf/ai/tts/sapi/preferences/SapiAudioOutputFieldEditor.java
+++ b/plugins/org.eclipse.actf.ai.tts.sapi/src/org/eclipse/actf/ai/tts/sapi/preferences/SapiAudioOutputFieldEditor.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2007 IBM Corporation and Others
+ * Copyright (c) 2007, 2008 IBM Corporation and Others
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -7,23 +7,37 @@
  *
  * Contributors:
  *    Takashi ITOH - initial API and implementation
+ *    Kentarou FUKUDA - initial API and implementation
  *******************************************************************************/
 
 package org.eclipse.actf.ai.tts.sapi.preferences;
 
+import org.eclipse.actf.ai.tts.sapi.SAPIPlugin;
 import org.eclipse.actf.ai.tts.sapi.engine.SapiVoice;
 import org.eclipse.actf.ai.voice.preferences.util.ComboFieldEditor;
+import org.eclipse.jface.preference.IPreferenceStore;
 import org.eclipse.swt.widgets.Composite;
 
-
-
 public class SapiAudioOutputFieldEditor extends ComboFieldEditor {
 
-    public SapiAudioOutputFieldEditor(String labelText, Composite parent) {
-        super(SapiVoice.AUDIO_OUTPUT, labelText, null, parent);
-    }
-    
-    protected void initLabelsAndValues(String[][] labelsAndValues) {
-        super.initLabelsAndValues(SapiTestManager.getInstance().getAudioOutputNames());
-    }
+	private static IPreferenceStore preferenceStore = SAPIPlugin.getDefault()
+			.getPreferenceStore();
+
+	public SapiAudioOutputFieldEditor(String labelText, Composite parent) {
+		super(SapiVoice.AUDIO_OUTPUT, labelText, null, parent);
+	}
+
+	protected void initLabelsAndValues(String[][] labelsAndValues) {
+		super.initLabelsAndValues(SapiTestManager.getInstance()
+				.getAudioOutputNames());
+	}
+	
+	@Override
+	protected void fireValueChanged(String property, Object oldValue,
+			Object newValue) {
+
+		preferenceStore.setValue(SapiVoice.AUDIO_OUTPUT, newValue.toString());
+
+		super.fireValueChanged(property, oldValue, newValue);
+	}
 }
diff --git a/plugins/org.eclipse.actf.ai.tts.sapi/src/org/eclipse/actf/ai/tts/sapi/preferences/SapiPreferencePage.java b/plugins/org.eclipse.actf.ai.tts.sapi/src/org/eclipse/actf/ai/tts/sapi/preferences/SapiPreferencePage.java
index 635d5af..148d5b8 100644
--- a/plugins/org.eclipse.actf.ai.tts.sapi/src/org/eclipse/actf/ai/tts/sapi/preferences/SapiPreferencePage.java
+++ b/plugins/org.eclipse.actf.ai.tts.sapi/src/org/eclipse/actf/ai/tts/sapi/preferences/SapiPreferencePage.java
@@ -7,6 +7,7 @@
  *
  * Contributors:
  *    Takashi ITOH - initial API and implementation
+ *    Kentarou FUKUDA - initial API and implementation
  *******************************************************************************/
 package org.eclipse.actf.ai.tts.sapi.preferences;
 
@@ -26,10 +27,12 @@
 import org.eclipse.ui.IWorkbench;
 import org.eclipse.ui.IWorkbenchPreferencePage;
 
-
 public class SapiPreferencePage extends GroupFieldEditorVoicePreferencePage
 		implements IWorkbenchPreferencePage {
 
+	private String orgVoice;
+	private String orgAudio;
+
 	public SapiPreferencePage() {
 		super();
 		setDescription(Messages.getString("tts.sapi.description")); //$NON-NLS-1$
@@ -37,38 +40,53 @@
 	}
 
 	public void createFieldEditors() {
-        if(!TTSRegistry.isAvailable(SapiVoice.ID)){
-            setMessage(Messages.getString("tts.sapi.notAvailable"));
-            return;
-        }
-        final ComboFieldEditor voiceEditor, audioEditor;
-        addField(voiceEditor = new SapiVoiceFieldEditor(Messages.getString("tts.sapi.voicename"), getFieldEditorParent())); //$NON-NLS-1$
-        addField(audioEditor = new SapiAudioOutputFieldEditor(Messages.getString("tts.sapi.audiooutput"), getFieldEditorParent())); //$NON-NLS-1$
-        
-        Composite comp = new Composite(getFieldEditorParent(),SWT.NONE);
-        GridLayout layout = new GridLayout();
-        layout.marginHeight = layout.marginWidth = 0;
-        comp.setLayout(layout);
-        GridData gd = new GridData(GridData.HORIZONTAL_ALIGN_END);
-        gd.horizontalSpan = voiceEditor.getNumberOfControls();
-        comp.setLayoutData(gd);
-        
-        Button testButton = new Button(comp,SWT.NONE);
-        testButton.setText(Messages.getString("tts.sapi.test"));
-        testButton.addSelectionListener(new SelectionAdapter(){
-            public void widgetSelected(SelectionEvent e) {
-                int voiceIndex = voiceEditor.getComboControl().getSelectionIndex();
-                int audioIndex = audioEditor.getComboControl().getSelectionIndex();
-                SapiTestManager.getInstance().speakTest(voiceIndex,audioIndex);
-            }
-        });
+		if (!TTSRegistry.isAvailable(SapiVoice.ID)) {
+			setMessage(Messages.getString("tts.sapi.notAvailable"));
+			return;
+		}
+
+		orgVoice = getPreferenceStore().getString(SapiVoice.ID);
+		orgAudio = getPreferenceStore().getString(SapiVoice.AUDIO_OUTPUT);
+
+		final ComboFieldEditor voiceEditor, audioEditor;
+		addField(voiceEditor = new SapiVoiceFieldEditor(Messages
+				.getString("tts.sapi.voicename"), getFieldEditorParent())); //$NON-NLS-1$
+		addField(audioEditor = new SapiAudioOutputFieldEditor(Messages
+				.getString("tts.sapi.audiooutput"), getFieldEditorParent())); //$NON-NLS-1$
+
+		Composite comp = new Composite(getFieldEditorParent(), SWT.NONE);
+		GridLayout layout = new GridLayout();
+		layout.marginHeight = layout.marginWidth = 0;
+		comp.setLayout(layout);
+		GridData gd = new GridData(GridData.HORIZONTAL_ALIGN_END);
+		gd.horizontalSpan = voiceEditor.getNumberOfControls();
+		comp.setLayoutData(gd);
+
+		Button testButton = new Button(comp, SWT.NONE);
+		testButton.setText(Messages.getString("tts.sapi.test"));
+		testButton.addSelectionListener(new SelectionAdapter() {
+			public void widgetSelected(SelectionEvent e) {
+				SapiTestManager.getInstance().speakTest();
+			}
+		});
 	}
 
-    public void init(IWorkbench workbench) {
+	public void init(IWorkbench workbench) {
 	}
 
-    public void dispose() {
-        super.dispose();
-        SapiTestManager.freeInstance();
-    }
+	@Override
+	public boolean performCancel() {
+		getPreferenceStore().setValue(SapiVoice.ID, orgVoice);
+		getPreferenceStore().setValue(SapiVoice.AUDIO_OUTPUT, orgAudio);
+		return super.performCancel();
+	}
+	
+	@Override
+	protected void performApply() {
+		super.performApply();
+		
+		orgVoice = getPreferenceStore().getString(SapiVoice.ID);
+		orgAudio = getPreferenceStore().getString(SapiVoice.AUDIO_OUTPUT);
+	}
+
 }
diff --git a/plugins/org.eclipse.actf.ai.tts.sapi/src/org/eclipse/actf/ai/tts/sapi/preferences/SapiTestManager.java b/plugins/org.eclipse.actf.ai.tts.sapi/src/org/eclipse/actf/ai/tts/sapi/preferences/SapiTestManager.java
index 73150be..a535b40 100644
--- a/plugins/org.eclipse.actf.ai.tts.sapi/src/org/eclipse/actf/ai/tts/sapi/preferences/SapiTestManager.java
+++ b/plugins/org.eclipse.actf.ai.tts.sapi/src/org/eclipse/actf/ai/tts/sapi/preferences/SapiTestManager.java
@@ -7,6 +7,7 @@
  *
  * Contributors:
  *    Takashi ITOH - initial API and implementation
+ *    Kentarou FUKUDA - initial API and implementation
  *******************************************************************************/
 
 package org.eclipse.actf.ai.tts.sapi.preferences;
@@ -14,136 +15,109 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import org.eclipse.actf.ai.tts.ITTSEngine;
+import org.eclipse.actf.ai.tts.ISAPIEngine;
+import org.eclipse.actf.ai.tts.TTSRegistry;
 import org.eclipse.actf.ai.tts.sapi.SAPIPlugin;
 import org.eclipse.actf.ai.tts.sapi.engine.SapiVoice;
 import org.eclipse.actf.ai.tts.sapi.engine.SpObjectToken;
 import org.eclipse.actf.ai.tts.sapi.engine.SpeechObjectTokens;
-import org.eclipse.actf.ai.voice.VoiceUtil;
 import org.eclipse.core.runtime.Platform;
 import org.eclipse.swt.ole.win32.Variant;
 
-
-
-
 public class SapiTestManager {
 
-    private static SapiTestManager instance;
+	private static SapiTestManager instance;
 
-    private SapiVoice sapiVoice;
-    private String[][] voiceNames;
-    private String[][] audioOutputNames;
+	private SapiVoice sapiVoice;
+	private String[][] voiceNames;
+	private String[][] audioOutputNames;
 
-    private static final String SAMPLE_TEXT = "Hello. This is test."; //$NON-NLS-1$
-    
-    public SapiTestManager() {
-        sapiVoice = new SapiVoice();
-        Variant varVoices = sapiVoice.getVoices(null, null);
-        if (null != varVoices) {
-            SpeechObjectTokens voiceTokens = SpeechObjectTokens.getTokens(varVoices);
-            if( null != voiceTokens ) {
-                String exclude = Platform.getResourceString(SAPIPlugin.getDefault().getBundle(), "%voice.exclude"); //$NON-NLS-1$
-                List<String[]> voiceList = new ArrayList<String[]>();
-                int count = voiceTokens.getCount();
-                for (int i = 0; i < count; i++) {
-                    Variant varVoice = voiceTokens.getItem(i);
-                    if (null != varVoice) {
-                        SpObjectToken token = SpObjectToken.getToken(varVoice);
-                        if (null != token) {
-                            String voiceName = token.getDescription(0);
-                            if( null==exclude || !exclude.equals(voiceName) ) {
-                                voiceList.add(new String[]{voiceName,voiceName});
-                            }
-                        }
-                    }
-                }
-                voiceNames = voiceList.toArray(new String[voiceList.size()][]);
-            }
-            varVoices.dispose();
-        }
-        Variant varAudioOutputs = sapiVoice.getAudioOutputs(null, null);
-        if (null != varAudioOutputs) {
-            SpeechObjectTokens audioOutputTokens = SpeechObjectTokens.getTokens(varAudioOutputs);
-            if (null != audioOutputTokens) {
-                List<String[]> audioOutputList = new ArrayList<String[]>();
-                int count = audioOutputTokens.getCount();
-                for (int i = 0; i < count; i++) {
-                    Variant varAudioOutput = audioOutputTokens.getItem(i);
-                    if (null != varAudioOutput) {
-                        SpObjectToken token = SpObjectToken.getToken(varAudioOutput);
-                        if (null != token) {
-                            String audioOutputName = token.getDescription(0);
-                            audioOutputList.add(new String[]{audioOutputName,audioOutputName});
-                        }
-                    }
-                }
-                audioOutputNames= audioOutputList.toArray(new String[audioOutputList.size()][]);
-            }
-            varAudioOutputs.dispose();
-        }
-    }
-    
-    public static SapiTestManager getInstance() {
-        if( null == instance ) {
-            instance = new SapiTestManager();
-        }
-       return instance;
-    }
-    
-    public static void freeInstance() {
-        if( null != instance ) {
-            instance.dispose();
-            instance = null;
-        }
-    }
-    
-    public void dispose() {
-        if( null != sapiVoice ) {
-            sapiVoice.dispose();
-            sapiVoice = null;
-        }
-    }
-    
-    public SapiVoice getVoice() {
-        return sapiVoice;
-    }
-    
-    public String[][] getVoiceNames() {
-        return voiceNames;
-    }
-    
-    public String getVoiceName(int index) {
-        if (null != voiceNames && index<voiceNames.length ) {
-            return voiceNames[index][1];
-        }
-        return null;
-    }
-    
-    public String[][] getAudioOutputNames() {
-        return audioOutputNames;
-    }
-    
-    public String getAudioOutputName(int index) {
-        if (null != audioOutputNames && index<audioOutputNames.length) {
-            return audioOutputNames[index][1];
-        }
-        return null;
-    }
-    
-    public void speakTest(int voiceIndex, int audioIndex) {
-        if( voiceIndex>=0 ) {
-            String voiceName = getVoiceName(voiceIndex);
-            if( null != voiceName ) {
-                sapiVoice.setVoiceName("name="+voiceName);
-            }
-        }
-        if( audioIndex>=0 ) {
-            String audioOutputName = getAudioOutputName(audioIndex);
-            if( null != audioOutputName ) {
-                sapiVoice.setAudioOutputName(audioOutputName);
-            }
-        }
-        sapiVoice.setSpeed(VoiceUtil.getDefaultSpeed());
-        sapiVoice.speak(SAMPLE_TEXT, ITTSEngine.TTSFLAG_FLUSH, -1);
-    }
+	private static final String SAMPLE_TEXT = "Hello. This is test."; //$NON-NLS-1$
+
+	public SapiTestManager() {
+		sapiVoice = (SapiVoice) TTSRegistry.createTTSEngine(SapiVoice.ID);
+		Variant varVoices = sapiVoice.getVoices(null, null);
+		if (null != varVoices) {
+			SpeechObjectTokens voiceTokens = SpeechObjectTokens
+					.getTokens(varVoices);
+			if (null != voiceTokens) {
+				String exclude = Platform.getResourceString(SAPIPlugin
+						.getDefault().getBundle(), "%voice.exclude"); //$NON-NLS-1$
+				List<String[]> voiceList = new ArrayList<String[]>();
+				int count = voiceTokens.getCount();
+				for (int i = 0; i < count; i++) {
+					Variant varVoice = voiceTokens.getItem(i);
+					if (null != varVoice) {
+						SpObjectToken token = SpObjectToken.getToken(varVoice);
+						if (null != token) {
+							String voiceName = token.getDescription(0);
+							if (null == exclude || !exclude.equals(voiceName)) {
+								voiceList.add(new String[] { voiceName,
+										voiceName });
+							}
+						}
+					}
+				}
+				voiceNames = voiceList.toArray(new String[voiceList.size()][]);
+			}
+			varVoices.dispose();
+		}
+		Variant varAudioOutputs = sapiVoice.getAudioOutputs(null, null);
+		if (null != varAudioOutputs) {
+			SpeechObjectTokens audioOutputTokens = SpeechObjectTokens
+					.getTokens(varAudioOutputs);
+			if (null != audioOutputTokens) {
+				List<String[]> audioOutputList = new ArrayList<String[]>();
+				int count = audioOutputTokens.getCount();
+				for (int i = 0; i < count; i++) {
+					Variant varAudioOutput = audioOutputTokens.getItem(i);
+					if (null != varAudioOutput) {
+						SpObjectToken token = SpObjectToken
+								.getToken(varAudioOutput);
+						if (null != token) {
+							String audioOutputName = token.getDescription(0);
+							audioOutputList.add(new String[] { audioOutputName,
+									audioOutputName });
+						}
+					}
+				}
+				audioOutputNames = audioOutputList
+						.toArray(new String[audioOutputList.size()][]);
+			}
+			varAudioOutputs.dispose();
+		}
+	}
+
+	public static SapiTestManager getInstance() {
+		if (null == instance) {
+			instance = new SapiTestManager();
+		}
+		return instance;
+	}
+
+	public String[][] getVoiceNames() {
+		return voiceNames;
+	}
+
+	public String getVoiceName(int index) {
+		if (null != voiceNames && index < voiceNames.length) {
+			return voiceNames[index][1];
+		}
+		return null;
+	}
+
+	public String[][] getAudioOutputNames() {
+		return audioOutputNames;
+	}
+
+	public String getAudioOutputName(int index) {
+		if (null != audioOutputNames && index < audioOutputNames.length) {
+			return audioOutputNames[index][1];
+		}
+		return null;
+	}
+
+	public void speakTest() {
+		sapiVoice.speak(SAMPLE_TEXT, ISAPIEngine.SVSFDefault);
+	}
 }
diff --git a/plugins/org.eclipse.actf.ai.tts.sapi/src/org/eclipse/actf/ai/tts/sapi/preferences/SapiVoiceFieldEditor.java b/plugins/org.eclipse.actf.ai.tts.sapi/src/org/eclipse/actf/ai/tts/sapi/preferences/SapiVoiceFieldEditor.java
index c61c4be..70b7d91 100644
--- a/plugins/org.eclipse.actf.ai.tts.sapi/src/org/eclipse/actf/ai/tts/sapi/preferences/SapiVoiceFieldEditor.java
+++ b/plugins/org.eclipse.actf.ai.tts.sapi/src/org/eclipse/actf/ai/tts/sapi/preferences/SapiVoiceFieldEditor.java
@@ -7,23 +7,39 @@
  *
  * Contributors:
  *    Takashi ITOH - initial API and implementation
+ *    Kentarou FUKUDA - initial API and implementation
  *******************************************************************************/
 
 package org.eclipse.actf.ai.tts.sapi.preferences;
 
+import org.eclipse.actf.ai.tts.sapi.SAPIPlugin;
 import org.eclipse.actf.ai.tts.sapi.engine.SapiVoice;
 import org.eclipse.actf.ai.voice.preferences.util.ComboFieldEditor;
+import org.eclipse.jface.preference.IPreferenceStore;
 import org.eclipse.swt.widgets.Composite;
 
-
-
 public class SapiVoiceFieldEditor extends ComboFieldEditor {
 
+	private static IPreferenceStore preferenceStore = SAPIPlugin.getDefault()
+			.getPreferenceStore();
+
 	public SapiVoiceFieldEditor(String labelText, Composite parent) {
 		super(SapiVoice.ID, labelText, null, parent);
 	}
 
 	protected void initLabelsAndValues(String[][] labelsAndValues) {
-		super.initLabelsAndValues(SapiTestManager.getInstance().getVoiceNames());
+		super
+				.initLabelsAndValues(SapiTestManager.getInstance()
+						.getVoiceNames());
 	}
+
+	@Override
+	protected void fireValueChanged(String property, Object oldValue,
+			Object newValue) {
+
+		preferenceStore.setValue(SapiVoice.ID, newValue.toString());
+
+		super.fireValueChanged(property, oldValue, newValue);
+	}
+
 }
diff --git a/plugins/org.eclipse.actf.ai.voice/src/org/eclipse/actf/ai/tts/ISAPIEngine.java b/plugins/org.eclipse.actf.ai.voice/src/org/eclipse/actf/ai/tts/ISAPIEngine.java
new file mode 100644
index 0000000..73ba5d4
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.voice/src/org/eclipse/actf/ai/tts/ISAPIEngine.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2008 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    kentarou - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.tts;
+
+
+public interface ISAPIEngine extends ITTSEngine {
+
+	public static final int SVSFDefault = 0, SVSFlagsAsync = 1,
+			SVSFPurgeBeforeSpeak = 2, SVSFIsFilename = 4, SVSFIsXML = 8,
+			SVSFIsNotXML = 16, SVSFPersistXML = 32;
+
+	/**
+	 * @param rate
+	 *            The rate property to be set.
+	 * @return The invocation is succeeded then it returns true.
+	 */
+	public boolean setRate(int rate);
+
+	/**
+	 * @return The rate property of the voice engine.
+	 */
+	public int getRate();
+	
+	public void speak(String text, int sapiFlags);
+	
+
+}
diff --git a/plugins/org.eclipse.actf.ai.voice/src/org/eclipse/actf/ai/tts/ITTSEngine.java b/plugins/org.eclipse.actf.ai.voice/src/org/eclipse/actf/ai/tts/ITTSEngine.java
index a0a1667..2bf6688 100644
--- a/plugins/org.eclipse.actf.ai.voice/src/org/eclipse/actf/ai/tts/ITTSEngine.java
+++ b/plugins/org.eclipse.actf.ai.voice/src/org/eclipse/actf/ai/tts/ITTSEngine.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2007 IBM Corporation and Others
+ * Copyright (c) 2007, 2008 IBM Corporation and Others
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -7,6 +7,7 @@
  *
  * Contributors:
  *    Takashi ITOH - initial API and implementation
+ *    Kentarou FUKUDA - initial API and implementation
  *******************************************************************************/
 package org.eclipse.actf.ai.tts;
 
@@ -74,6 +75,15 @@
 	public void dispose();
 
 	/**
+	 * Returns <code>true</code> if the TTSEngine has been disposed, and
+	 * <code>false</code> otherwise.
+	 * 
+	 * @return <code>true</code> when the TTSEngine is disposed and
+	 *         <code>false</code> otherwise
+	 */
+	public boolean isDisposed();
+
+	/**
 	 * Get current speaking speed
 	 * 
 	 * @see IVoice#getSpeed()
diff --git a/plugins/org.eclipse.actf.ai.voice/src/org/eclipse/actf/ai/tts/TTSRegistry.java b/plugins/org.eclipse.actf.ai.voice/src/org/eclipse/actf/ai/tts/TTSRegistry.java
index 8efb6c1..3c143b6 100644
--- a/plugins/org.eclipse.actf.ai.voice/src/org/eclipse/actf/ai/tts/TTSRegistry.java
+++ b/plugins/org.eclipse.actf.ai.voice/src/org/eclipse/actf/ai/tts/TTSRegistry.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2007 IBM Corporation and Others
+ * Copyright (c) 2007, 2008 IBM Corporation and Others
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -7,6 +7,7 @@
  *
  * Contributors:
  *    Takashi ITOH - initial API and implementation
+ *    Kentarou FUKUDA - initial API and implementation
  *******************************************************************************/
 package org.eclipse.actf.ai.tts;
 
@@ -29,6 +30,8 @@
 
 	private static boolean[] availables;
 
+	private static ITTSEngine[] INSTANCES;
+
 	static {
 		initialize();
 	}
@@ -55,14 +58,18 @@
 			}
 		});
 		availables = new boolean[ttsElements.length];
+		INSTANCES = new ITTSEngine[ttsElements.length];
 		for (int i = 0; i < ttsElements.length; i++) {
 			try {
-				if (((ITTSEngine) ttsElements[i]
-						.createExecutableExtension("class")).isAvailable()) {
+				ITTSEngine test = (ITTSEngine) ttsElements[i]
+						.createExecutableExtension("class");
+				if (test.isAvailable()) {
 					availables[i] = true;
+					INSTANCES[i] = test;
 				}
 			} catch (Exception e) {
 				availables[i] = false;
+				INSTANCES[i] = null;
 			}
 		}
 	}
@@ -126,8 +133,11 @@
 		try {
 			for (int i = 0; i < ttsElements.length; i++) {
 				if (id.equals(ttsElements[i].getAttribute("id"))) { //$NON-NLS-1$
-					return (ITTSEngine) ttsElements[i]
-							.createExecutableExtension("class"); //$NON-NLS-1$
+				 if (INSTANCES[i] == null || INSTANCES[i].isDisposed()) {
+						INSTANCES[i] = (ITTSEngine) ttsElements[i]
+								.createExecutableExtension("class"); //$NON-NLS-1$
+					}
+					return INSTANCES[i];
 				}
 			}
 		} catch (Exception e) {
diff --git a/plugins/org.eclipse.actf.ai.voice/src/org/eclipse/actf/ai/voice/internal/Voice.java b/plugins/org.eclipse.actf.ai.voice/src/org/eclipse/actf/ai/voice/internal/Voice.java
index 0f284fc..0218ea0 100644
--- a/plugins/org.eclipse.actf.ai.voice/src/org/eclipse/actf/ai/voice/internal/Voice.java
+++ b/plugins/org.eclipse.actf.ai.voice/src/org/eclipse/actf/ai/voice/internal/Voice.java
@@ -134,6 +134,7 @@
 	 */
 	public void dispose() {
 		if( null != ttsEngine ) {
+			stop();
 			ttsEngine.dispose();
 			ttsEngine = null;
 		}
diff --git a/plugins/org.eclipse.actf.ai.voice/src/org/eclipse/actf/ai/voice/preferences/VoicePreferencePage.java b/plugins/org.eclipse.actf.ai.voice/src/org/eclipse/actf/ai/voice/preferences/VoicePreferencePage.java
index 8570ad8..644a5cb 100644
--- a/plugins/org.eclipse.actf.ai.voice/src/org/eclipse/actf/ai/voice/preferences/VoicePreferencePage.java
+++ b/plugins/org.eclipse.actf.ai.voice/src/org/eclipse/actf/ai/voice/preferences/VoicePreferencePage.java
@@ -7,6 +7,7 @@
  *
  * Contributors:
  *    Takashi ITOH - initial API and implementation
+ *    Kentarou FUKUDA - initial API and implementation
  *******************************************************************************/
 package org.eclipse.actf.ai.voice.preferences;
 
@@ -29,14 +30,13 @@
 import org.eclipse.ui.IWorkbench;
 import org.eclipse.ui.IWorkbenchPreferencePage;
 
-
 public class VoicePreferencePage extends GroupFieldEditorVoicePreferencePage
 		implements IWorkbenchPreferencePage {
 
-    private static final String SAMPLE_TEXT = "Hello. This is test."; //$NON-NLS-1$
+	private static final String SAMPLE_TEXT = "Hello. This is test."; //$NON-NLS-1$
 
-    private static IVoice voice = VoiceUtil.getVoice();
-	
+	private static IVoice voice = VoiceUtil.getVoice();
+
 	public VoicePreferencePage() {
 		super();
 		setPreferenceStore(VoicePlugin.getDefault().getPreferenceStore());
@@ -44,46 +44,54 @@
 	}
 
 	public void createFieldEditors() {
-        RadioGroupFieldEditor rgfe;
-        String[][] labelAndIds = TTSRegistry.getLabelAndIds();
-		addField(rgfe = new RadioGroupFieldEditor(IVoice.PREF_ENGINE,
-				Messages.getString("voice.engine"), 1, labelAndIds, //$NON-NLS-1$
+		
+		final RadioGroupFieldEditor rgfe;
+		String[][] labelAndIds = TTSRegistry.getLabelAndIds();
+		addField(rgfe = new RadioGroupFieldEditor(IVoice.PREF_ENGINE, Messages
+				.getString("voice.engine"), 1, labelAndIds, //$NON-NLS-1$
 				getFieldEditorParent()));
-        Composite c = rgfe.getRadioBoxControl(getFieldEditorParent());
-        for(int i=0; i<labelAndIds.length; i++){
-            if(labelAndIds[i][1].length() == 0){
-                c.getChildren()[i].setEnabled(false);
-            }
-        }
-        
-        final ScaleFieldEditor speedEditor;
-		addField(speedEditor = new ScaleFieldEditor(IVoice.PREF_SPEED, Messages.getString("voice.speed"), //$NON-NLS-1$
+		Composite c = rgfe.getRadioBoxControl(getFieldEditorParent());
+		for (int i = 0; i < labelAndIds.length; i++) {
+			if (labelAndIds[i][1].length() == 0) {
+				c.getChildren()[i].setEnabled(false);
+			}
+		}
+
+		final ScaleFieldEditor speedEditor;
+		addField(speedEditor = new ScaleFieldEditor(IVoice.PREF_SPEED,
+				Messages.getString("voice.speed"), //$NON-NLS-1$
 				getFieldEditorParent(), IVoice.SPEED_MIN, IVoice.SPEED_MAX, 5,
 				25));
-		
-        Composite comp = new Composite(getFieldEditorParent(),SWT.NONE);
-        GridLayout layout = new GridLayout();
-        layout.marginHeight = layout.marginWidth = 0;
-        comp.setLayout(layout);
-        GridData gd = new GridData(GridData.HORIZONTAL_ALIGN_END);
-        gd.horizontalSpan = speedEditor.getNumberOfControls();
-        comp.setLayoutData(gd);
 
-        Button testButton = new Button(comp,SWT.NONE);
-        testButton.setText(Messages.getString("voice.test"));
-        testButton.addSelectionListener(new SelectionAdapter(){
-            public void widgetSelected(SelectionEvent e) {
-            	voice.setSpeed(speedEditor.getScaleControl().getSelection());
-            	voice.speak(SAMPLE_TEXT,true);
-            }
-        });
+		Composite comp = new Composite(getFieldEditorParent(), SWT.NONE);
+		GridLayout layout = new GridLayout();
+		layout.marginHeight = layout.marginWidth = 0;
+		comp.setLayout(layout);
+		GridData gd = new GridData(GridData.HORIZONTAL_ALIGN_END);
+		gd.horizontalSpan = speedEditor.getNumberOfControls();
+		comp.setLayoutData(gd);
+
+		Button testButton = new Button(comp, SWT.NONE);
+		testButton.setText(Messages.getString("voice.test"));
+		testButton.addSelectionListener(new SelectionAdapter() {
+			public void widgetSelected(SelectionEvent e) {
+
+				voice.setSpeed(speedEditor.getScaleControl().getSelection());
+				voice.speak(SAMPLE_TEXT, false);
+			}
+		});
 	}
 
+	@Override
+	public boolean performCancel() {
+		return super.performCancel();
+	}
+	
 	public void init(IWorkbench workbench) {
 	}
 
 	public void dispose() {
 		super.dispose();
-		((Voice)voice).setSpeed();
+		((Voice) voice).setSpeed();
 	}
 }