[375638] Add support for Microsoft Speech Platform
diff --git a/plugins/org.eclipse.actf.ai.tts.msp/.classpath b/plugins/org.eclipse.actf.ai.tts.msp/.classpath
new file mode 100644
index 0000000..304e861
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.tts.msp/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/plugins/org.eclipse.actf.ai.tts.msp/.cvsignore b/plugins/org.eclipse.actf.ai.tts.msp/.cvsignore
new file mode 100644
index 0000000..ddb0a2d
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.tts.msp/.cvsignore
@@ -0,0 +1,2 @@
+bin
+.settings
diff --git a/plugins/org.eclipse.actf.ai.tts.msp/.project b/plugins/org.eclipse.actf.ai.tts.msp/.project
new file mode 100644
index 0000000..7eaa664
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.tts.msp/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.eclipse.actf.ai.tts.msp</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.PluginNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/plugins/org.eclipse.actf.ai.tts.msp/META-INF/MANIFEST.MF b/plugins/org.eclipse.actf.ai.tts.msp/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..244dcfc
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.tts.msp/META-INF/MANIFEST.MF
@@ -0,0 +1,14 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: ACTF MSP Plug-in (Incubation)
+Bundle-SymbolicName: org.eclipse.actf.ai.tts.msp;singleton:=true
+Bundle-Version: 0.9.2.qualifier
+Bundle-Activator: org.eclipse.actf.ai.tts.msp.MspPlugin
+Bundle-Vendor: Eclipse.org
+Bundle-Localization: plugin
+Require-Bundle: org.eclipse.ui,
+ org.eclipse.core.runtime,
+ org.eclipse.actf.util.win32,
+ org.eclipse.actf.ai.voice
+Bundle-ActivationPolicy: lazy
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
diff --git a/plugins/org.eclipse.actf.ai.tts.msp/about.html b/plugins/org.eclipse.actf.ai.tts.msp/about.html
new file mode 100644
index 0000000..481dbcf
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.tts.msp/about.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
+<title>About</title>
+</head>
+<body lang="EN-US">
+<h2>About This Content</h2>
+
+<p>June 5, 2006</p>	
+<h3>License</h3>
+<p>The Eclipse Foundation makes available all content in this plug-in (&quot;Content&quot;).  
+Unless otherwise indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 (&quot;EPL&quot;).  A copy of the EPL is available 
+at <a href="http://www.eclipse.org/org/documents/epl-v10.php">http://www.eclipse.org/legal/epl-v10.html</a>.
+For purposes of the EPL, &quot;Program&quot; will mean the Content.</p>
+<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is 
+being redistributed by another party (&quot;Redistributor&quot;) and different terms and conditions may
+apply to your use of any object code in the Content.  Check the Redistributor&rsquo;s license 
+that was provided with the Content.  If no such license exists, contact the Redistributor.  Unless otherwise
+indicated below, the terms and conditions of the EPL still apply to any source code in the Content
+and such source code may be obtained at <a href="http://www.eclipse.org/">http://www.eclipse.org</a>.</p>
+</body>
+</html>
\ No newline at end of file
diff --git a/plugins/org.eclipse.actf.ai.tts.msp/build.properties b/plugins/org.eclipse.actf.ai.tts.msp/build.properties
new file mode 100644
index 0000000..661c95d
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.tts.msp/build.properties
@@ -0,0 +1,19 @@
+###############################################################################
+# Copyright (c) 2007 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:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+			   about.html,\
+               .,\
+               plugin.xml,\
+               plugin.properties,\
+               plugin_zh.properties,\
+               plugin_ja.properties
diff --git a/plugins/org.eclipse.actf.ai.tts.msp/plugin.properties b/plugins/org.eclipse.actf.ai.tts.msp/plugin.properties
new file mode 100644
index 0000000..12bcbe8
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.tts.msp/plugin.properties
@@ -0,0 +1,12 @@
+###############################################################################
+# Copyright (c) 2007, 2012 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:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+PreferencePage.name=MSP Settings
+voice.exclude=AcTF On-Screen TTS
\ No newline at end of file
diff --git a/plugins/org.eclipse.actf.ai.tts.msp/plugin.xml b/plugins/org.eclipse.actf.ai.tts.msp/plugin.xml
new file mode 100644
index 0000000..535465d
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.tts.msp/plugin.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.0"?>
+<plugin>
+   <extension
+         point="org.eclipse.actf.ai.voice.TTSEngine">
+         <engine
+               class="org.eclipse.actf.ai.tts.msp.engine.MspVoice"
+               id="org.eclipse.actf.ai.tts.msp.engine.MspVoice"
+               name="MSP TTS"
+               priority="95"/>
+   </extension>
+
+   <extension
+         point="org.eclipse.ui.preferencePages">
+      <page
+            category="org.eclipse.actf.ai.voice.preferences.VoicePreferencePage"
+            class="org.eclipse.actf.ai.tts.msp.preferences.MspPreferencePage"
+            id="org.eclipse.actf.ai.tts.sapi.preferences.SapiPreferencePage"
+            name="%PreferencePage.name"/>
+   </extension>
+   
+   <extension
+         point="org.eclipse.core.runtime.preferences">
+      <initializer class="org.eclipse.actf.ai.tts.msp.preferences.MspPreferenceInitializer"/>
+   </extension>
+   
+</plugin>
diff --git a/plugins/org.eclipse.actf.ai.tts.msp/plugin_ja.properties b/plugins/org.eclipse.actf.ai.tts.msp/plugin_ja.properties
new file mode 100644
index 0000000..2e0f0e1
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.tts.msp/plugin_ja.properties
@@ -0,0 +1,11 @@
+###############################################################################
+# Copyright (c) 2007, 2012 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:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+PreferencePage.name=MSP\u8a2d\u5b9a
\ No newline at end of file
diff --git a/plugins/org.eclipse.actf.ai.tts.msp/plugin_zh.properties b/plugins/org.eclipse.actf.ai.tts.msp/plugin_zh.properties
new file mode 100644
index 0000000..cf8c8a5
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.tts.msp/plugin_zh.properties
@@ -0,0 +1,12 @@
+###############################################################################
+# Copyright (c) 2010, 2012 Shaojian Zhu, 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:
+#     Shaojian Zhu - initial API and implementation
+#     IBM Corporation - initial API and implementation
+###############################################################################
+PreferencePage.name=MSP\u8bbe\u7f6e
diff --git a/plugins/org.eclipse.actf.ai.tts.msp/src/messages.properties b/plugins/org.eclipse.actf.ai.tts.msp/src/messages.properties
new file mode 100644
index 0000000..1999543
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.tts.msp/src/messages.properties
@@ -0,0 +1,15 @@
+###############################################################################
+# 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
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+tts_msp_description=Microsoft Speech Platform TTS Settings
+tts_msp_voicename=&Voice name:
+tts_msp_audiooutput=&Audio Output Device:
+tts_msp_notAvailable=Microsoft Speech Platform TTS is not Available
+tts_msp_test=&Test
diff --git a/plugins/org.eclipse.actf.ai.tts.msp/src/messages_ja.properties b/plugins/org.eclipse.actf.ai.tts.msp/src/messages_ja.properties
new file mode 100644
index 0000000..d63039c
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.tts.msp/src/messages_ja.properties
@@ -0,0 +1,15 @@
+###############################################################################
+# Copyright (c) 2007, 2012 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:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+tts_msp_description=Microsoft Speech Platform TTS\u8a2d\u5b9a
+tts_msp_voicename=\u97f3\u58f0(&V):
+tts_msp_audiooutput=\u30aa\u30fc\u30c7\u30a3\u30aa\u518d\u751f\u30c7\u30d0\u30a4\u30b9(&A):
+tts_msp_notAvailable=Microsoft Speech Platform TTS \u306f\u4f7f\u7528\u3067\u304d\u307e\u305b\u3093
+tts_msp_test=\u30c6\u30b9\u30c8(&T)
\ No newline at end of file
diff --git a/plugins/org.eclipse.actf.ai.tts.msp/src/messages_zh.properties b/plugins/org.eclipse.actf.ai.tts.msp/src/messages_zh.properties
new file mode 100644
index 0000000..b51a92e
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.tts.msp/src/messages_zh.properties
@@ -0,0 +1,16 @@
+###############################################################################
+# Copyright (c) 2010, 2012 Shaojian Zhu, 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:
+#     Shaojian Zhu - initial API and implementation
+#     IBM Corporation - initial API and implementation
+###############################################################################
+tts_msp_description=Microsoft Speech Platform TTS \u8bbe\u7f6e
+tts_msp_voicename=\u4eba\u58f0(&V):
+tts_msp_audiooutput=\u97f3\u9891\u8f93\u51fa\u8bbe\u5907(&A):
+tts_msp_notAvailable=Microsoft Speech Platform TTS \u4e0d\u53ef\u7528
+tts_msp_test=\u6d4b\u8bd5(&T):
diff --git a/plugins/org.eclipse.actf.ai.tts.msp/src/org/eclipse/actf/ai/tts/msp/Messages.java b/plugins/org.eclipse.actf.ai.tts.msp/src/org/eclipse/actf/ai/tts/msp/Messages.java
new file mode 100644
index 0000000..6cdad62
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.tts.msp/src/org/eclipse/actf/ai/tts/msp/Messages.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2007 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:
+ *    Takashi ITOH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.tts.msp;
+
+import org.eclipse.osgi.util.NLS;
+
+public final class Messages extends NLS {
+
+	private static final String BUNDLE_NAME = "messages";//$NON-NLS-1$
+
+	private Messages() {
+		// Do not instantiate
+	}
+
+	public static String tts_msp_description;
+	public static String tts_msp_voicename;
+	public static String tts_msp_audiooutput;
+	public static String tts_msp_notAvailable;
+	public static String tts_msp_test;
+
+	static {
+		NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+	}
+}
\ No newline at end of file
diff --git a/plugins/org.eclipse.actf.ai.tts.msp/src/org/eclipse/actf/ai/tts/msp/MspPlugin.java b/plugins/org.eclipse.actf.ai.tts.msp/src/org/eclipse/actf/ai/tts/msp/MspPlugin.java
new file mode 100644
index 0000000..8c9c817
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.tts.msp/src/org/eclipse/actf/ai/tts/msp/MspPlugin.java
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2012 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:
+ *    Takashi ITOH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.tts.msp;
+
+import org.eclipse.actf.ai.tts.AbstractUIPluginForTTS;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The main plugin class to be used in the desktop.
+ */
+public class MspPlugin extends AbstractUIPluginForTTS {
+
+	// The shared instance.
+	private static MspPlugin plugin;
+
+	/**
+	 * The constructor.
+	 */
+	public MspPlugin() {
+		plugin = this;
+	}
+
+	/**
+	 * This method is called upon plug-in activation
+	 */
+	public void start(BundleContext context) throws Exception {
+		super.start(context);
+	}
+
+	/**
+	 * This method is called when the plug-in is stopped
+	 */
+	public void stop(BundleContext context) throws Exception {
+		super.stop(context);
+		plugin = null;
+	}
+
+	/**
+	 * Returns the shared instance.
+	 */
+	public static MspPlugin getDefault() {
+		return plugin;
+	}
+
+	/**
+	 * Returns an image descriptor for the image file at the given plug-in
+	 * relative path.
+	 * 
+	 * @param path
+	 *            the path
+	 * @return the image descriptor
+	 */
+	public static ImageDescriptor getImageDescriptor(String path) {
+		return AbstractUIPlugin.imageDescriptorFromPlugin(
+				"org.eclipse.actf.ai.tts.msp", path); //$NON-NLS-1$
+	}
+}
diff --git a/plugins/org.eclipse.actf.ai.tts.msp/src/org/eclipse/actf/ai/tts/msp/engine/ISpNotifySource.java b/plugins/org.eclipse.actf.ai.tts.msp/src/org/eclipse/actf/ai/tts/msp/engine/ISpNotifySource.java
new file mode 100644
index 0000000..66de1a6
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.tts.msp/src/org/eclipse/actf/ai/tts/msp/engine/ISpNotifySource.java
@@ -0,0 +1,130 @@
+/*******************************************************************************
+ * Copyright (c) 2007 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:
+ *    Takashi ITOH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.tts.msp.engine;
+
+import org.eclipse.actf.ai.voice.IVoiceEventListener;
+import org.eclipse.actf.util.win32.COMUtil;
+import org.eclipse.swt.internal.Callback;
+import org.eclipse.swt.internal.ole.win32.GUID;
+import org.eclipse.swt.internal.ole.win32.IDispatch;
+import org.eclipse.swt.ole.win32.OLE;
+
+/**
+ * COM wrapper of ISpNotifySource interface.
+ * 
+ * @see "Microsoft Speech API ISpNotifySource"
+ */
+public class ISpNotifySource extends IDispatch {
+
+	public static final GUID IID = COMUtil
+			.IIDFromString("{5EFF4AEF-8487-11D2-961C-00C04F8EE628}"); //$NON-NLS-1$
+
+	public static final int SPEI_START_INPUT_STREAM = 1;
+	public static final int SPEI_END_INPUT_STREAM = 2;
+	public static final int SPEI_VOICE_CHANGE = 3;
+	public static final int SPEI_TTS_BOOKMARK = 4;
+	public static final int SPEI_WORD_BOUNDARY = 5;
+	public static final int SPEI_PHONEME = 6;
+	public static final int SPEI_SENTENCE_BOUNDARY = 7;
+	public static final int SPEI_VISEME = 8;
+	public static final int SPEI_TTS_AUDIO_LEVEL = 9;
+
+	private int address;
+	private Callback callback = null;
+	private IVoiceEventListener eventListener = null;
+
+	public ISpNotifySource(int address) {
+		super(address);
+		this.address = address;
+		callback = new Callback(this, "SAPINotifyCallback", 2); //$NON-NLS-1$
+		SetNotifyCallbackFunction(callback.getAddress(), 0, 0);
+		SetInterest(1 << SPEI_TTS_BOOKMARK, 1 << SPEI_TTS_BOOKMARK);
+	}
+
+	public static ISpNotifySource getNotifySource(ISpVoice dispatch) {
+		int[] ppv = new int[1];
+		if (OLE.S_OK == dispatch.QueryInterface(IID, ppv)) {
+			return new ISpNotifySource(ppv[0]);
+		}
+		return null;
+	}
+
+	public void setEventListener(IVoiceEventListener eventListener) {
+		this.eventListener = eventListener;
+	}
+
+	int SAPINotifyCallback(int wParam, int lParam) {
+		SpEvent se = new SpEvent();
+		try {
+			while (OLE.S_OK == GetEvent(se.getAddress())) {
+				if (null != eventListener) {
+					if (SPEI_TTS_BOOKMARK == se.getEventId()) {
+						eventListener.indexReceived(se.getWParam());
+					}
+					continue;
+				}
+				String eventName;
+				switch (se.getEventId()) {
+				case SPEI_START_INPUT_STREAM:
+					eventName = "SPEI_START_INPUT_STREAM"; //$NON-NLS-1$
+					break;
+				case SPEI_END_INPUT_STREAM:
+					eventName = "SPEI_END_INPUT_STREAM"; //$NON-NLS-1$
+					break;
+				case SPEI_VOICE_CHANGE:
+					eventName = "SPEI_VOICE_CHANGE"; //$NON-NLS-1$
+					break;
+				case SPEI_TTS_BOOKMARK:
+					eventName = "SPEI_TTS_BOOKMARK"; //$NON-NLS-1$
+					break;
+				case SPEI_WORD_BOUNDARY:
+					eventName = "SPEI_WORD_BOUNDARY"; //$NON-NLS-1$
+					break;
+				case SPEI_PHONEME:
+					eventName = "SPEI_PHONEME"; //$NON-NLS-1$
+					break;
+				case SPEI_SENTENCE_BOUNDARY:
+					eventName = "SPEI_SENTENCE_BOUNDARY"; //$NON-NLS-1$
+					break;
+				case SPEI_VISEME:
+					eventName = "SPEI_VISEME"; //$NON-NLS-1$
+					break;
+				case SPEI_TTS_AUDIO_LEVEL:
+					eventName = "SPEI_TTS_AUDIO_LEVEL"; //$NON-NLS-1$
+					break;
+				default:
+					eventName = "Unknown+(" + se.getEventId() + ")"; //$NON-NLS-1$ //$NON-NLS-2$
+					break;
+				}
+				System.out
+						.println(eventName
+								+ ": " + se.getLParamType() + "/" + se.getStreamNum() + "/" + se.getAudioStreamOffset() + "/" + se.getLParam() + "/" + se.getWParam()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+			}
+		} finally {
+			se.dispose();
+		}
+		return 0;
+	}
+
+	public int SetNotifyCallbackFunction(int pCallbackAddress, int wParam,
+			int lParam) {
+		return COMUtil.VtblCall(5, address, pCallbackAddress, wParam, lParam);
+	}
+
+	public int SetInterest(int eventInterest, int queuedInterest) {
+		return COMUtil.VtblCall(10, address, 0x40000000 | eventInterest, 2,
+				0x40000000 | queuedInterest, 2);
+	}
+
+	public int GetEvent(int pSPEVENTAddress) {
+		return COMUtil.VtblCall(11, address, 1, pSPEVENTAddress, 0);
+	}
+}
diff --git a/plugins/org.eclipse.actf.ai.tts.msp/src/org/eclipse/actf/ai/tts/msp/engine/ISpVoice.java b/plugins/org.eclipse.actf.ai.tts.msp/src/org/eclipse/actf/ai/tts/msp/engine/ISpVoice.java
new file mode 100644
index 0000000..898ac3c
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.tts.msp/src/org/eclipse/actf/ai/tts/msp/engine/ISpVoice.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2012 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:
+ *    Takashi ITOH - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.tts.msp.engine;
+
+import org.eclipse.actf.util.win32.COMUtil;
+import org.eclipse.swt.internal.ole.win32.GUID;
+import org.eclipse.swt.internal.ole.win32.IDispatch;
+
+/**
+ * COM wrapper of ISpVoice interface.
+ * 
+ * @see "Microsoft Speech API ISpVoice"
+ */
+public class ISpVoice extends IDispatch {
+
+	public static final GUID IID = COMUtil
+			.IIDFromString("{d941651c-44e6-4c17-badf-c36826fc3424}"); //$NON-NLS-1$
+
+	private int address;
+
+	public ISpVoice(int address) {
+		super(address);
+		this.address = address;
+	}
+
+	public int put_Voice(int pVoiceAddress) {
+		return COMUtil.VtblCall(9, address, pVoiceAddress);
+	}
+
+	public int put_AudioOutput(int pAudioOutputAddress) {
+		return COMUtil.VtblCall(11, address, pAudioOutputAddress);
+	}
+
+	public int get_Rate(int pRateAddress) {
+		return COMUtil.VtblCall(14, address, pRateAddress);
+	}
+
+	public int put_Rate(int rate) {
+		return COMUtil.VtblCall(15, address, rate);
+	}
+
+	public int Speak(int pTextAddress, int flags) {
+		return COMUtil.VtblCall(28, address, pTextAddress, flags, 0);
+	}
+
+}
diff --git a/plugins/org.eclipse.actf.ai.tts.msp/src/org/eclipse/actf/ai/tts/msp/engine/MspVoice.java b/plugins/org.eclipse.actf.ai.tts.msp/src/org/eclipse/actf/ai/tts/msp/engine/MspVoice.java
new file mode 100644
index 0000000..eba32ea
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.tts.msp/src/org/eclipse/actf/ai/tts/msp/engine/MspVoice.java
@@ -0,0 +1,389 @@
+/*******************************************************************************
+ * 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Takashi ITOH - initial API and implementation
+ *    Kentarou FUKUDA - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.tts.msp.engine;
+
+import org.eclipse.actf.ai.tts.ISAPIEngine;
+import org.eclipse.actf.ai.tts.msp.MspPlugin;
+import org.eclipse.actf.ai.voice.IVoiceEventListener;
+import org.eclipse.actf.util.win32.COMUtil;
+import org.eclipse.actf.util.win32.MemoryUtil;
+import org.eclipse.actf.util.win32.NativeIntAccess;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.swt.ole.win32.OLE;
+import org.eclipse.swt.ole.win32.OleAutomation;
+import org.eclipse.swt.ole.win32.Variant;
+
+/**
+ * The implementation of ITTSEngine to use Microsoft Speech API.
+ */
+public class MspVoice implements ISAPIEngine, IPropertyChangeListener {
+
+	public static final String ID = "org.eclipse.actf.ai.tts.msp.engine.MspVoice"; //$NON-NLS-1$
+	public static final String AUDIO_OUTPUT = "org.eclipse.actf.ai.tts.MspVoice.audioOutput"; //$NON-NLS-1$
+
+	public ISpVoice dispSpVoice;
+	private Variant varSapiVoice;
+	private OleAutomation automation;
+	private int idGetVoices;
+	private int idGetAudioOutputs;
+	private ISpNotifySource spNotifySource = null;
+	private static IPreferenceStore preferenceStore = MspPlugin.getDefault()
+			.getPreferenceStore();
+	private boolean isDisposed = false;
+
+	public MspVoice() {
+		int pv = COMUtil.createDispatch(ISpVoice.IID);
+		dispSpVoice = new ISpVoice(pv);
+		varSapiVoice = new Variant(dispSpVoice);
+		automation = varSapiVoice.getAutomation();
+		spNotifySource = ISpNotifySource.getNotifySource(dispSpVoice);
+		MspPlugin.getDefault().addPropertyChangeListener(this);
+
+		idGetVoices = getIDsOfNames("GetVoices"); //$NON-NLS-1$
+		idGetAudioOutputs = getIDsOfNames("GetAudioOutputs"); //$NON-NLS-1$
+
+		// 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();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
+	 */
+	public void propertyChange(PropertyChangeEvent event) {
+		if (ID.equals(event.getProperty())) {
+			stop();
+			setVoiceName();
+		} else if (AUDIO_OUTPUT.equals(event.getProperty())) {
+			stop();
+			setAudioOutputName();
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.actf.ai.tts.ITTSEngine#setEventListener(org.eclipse.actf.ai.voice.IVoiceEventListener)
+	 */
+	public void setEventListener(IVoiceEventListener eventListener) {
+		spNotifySource.setEventListener(eventListener);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.actf.ai.tts.ITTSEngine#speak(java.lang.String, int, int)
+	 */
+	public void speak(String text, int flags, int index) {
+		int firstFlag = SVSFlagsAsync;
+		if (0 != (TTSFLAG_FLUSH & flags)) {
+			firstFlag |= SVSFPurgeBeforeSpeak;
+		}
+		if (index >= 0) {
+			speak(
+					"<BOOKMARK mark=\"" + index + "\"/>", firstFlag | SVSFPersistXML); //$NON-NLS-1$ //$NON-NLS-2$
+			speak(text, SVSFlagsAsync);
+			speak("<BOOKMARK mark=\"-1\"/>", SVSFlagsAsync | SVSFPersistXML); //$NON-NLS-1$
+		} else {
+			speak(text, firstFlag);
+		}
+	}
+
+	public void speak(String text, int sapiFlags) {
+		char[] data = (text + "\0").toCharArray(); //$NON-NLS-1$
+		int bstrText = MemoryUtil.SysAllocString(data);
+		try {
+			dispSpVoice.Speak(bstrText, sapiFlags);
+		} finally {
+			MemoryUtil.SysFreeString(bstrText);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.actf.ai.tts.ITTSEngine#stop()
+	 */
+	public void stop() {
+		speak("", TTSFLAG_FLUSH, -1); //$NON-NLS-1$
+	}
+
+	/**
+	 * @param rate
+	 *            The rate property to be set.
+	 * @return The invocation is succeeded then it returns true.
+	 */
+	public boolean setRate(int rate) {
+		return OLE.S_OK == dispSpVoice.put_Rate(rate);
+	}
+
+	/**
+	 * @return The rate property of the voice engine.
+	 */
+	public int getRate() {
+		NativeIntAccess nia = new NativeIntAccess();
+		try {
+			if (OLE.S_OK == dispSpVoice.get_Rate(nia.getAddress())) {
+				return nia.getInt();
+			}
+		} finally {
+			nia.dispose();
+		}
+		return -1;
+	}
+
+	/**
+	 * @param varVoice
+	 *            The voice object to be set.
+	 * @return The invocation is succeeded then it returns true.
+	 */
+	public boolean setVoice(Variant varVoice) {
+		return OLE.S_OK == dispSpVoice.put_Voice(varVoice.getDispatch()
+				.getAddress());
+	}
+
+	/**
+	 * @param varAudioOutput
+	 *            The audio output object to be set.
+	 * @return The invocation is succeeded then it returns true.
+	 */
+	public boolean setAudioOutput(Variant varAudioOutput) {
+		return OLE.S_OK == dispSpVoice
+				.put_AudioOutput(null != varAudioOutput ? varAudioOutput
+						.getDispatch().getAddress() : 0);
+	}
+
+	private void setVoiceName() {
+		String voiceName = preferenceStore.getString(ID);
+		if (voiceName.length() > 0) {
+			setVoiceName("name=" + voiceName); //$NON-NLS-1$
+		}
+	}
+
+	/**
+	 * @param voiceName
+	 *            The voice name to be set.
+	 * @return The invocation is succeeded then it returns true.
+	 */
+	public boolean setVoiceName(String voiceName) {
+		boolean success = false;
+		Variant varVoices = getVoices(voiceName, null);
+		if (null != varVoices) {
+			SpeechObjectTokens tokens = SpeechObjectTokens.getTokens(varVoices);
+			if (null != tokens && 0 < tokens.getCount()) {
+				Variant varVoice = tokens.getItem(0);
+				if (null != varVoice) {
+					success = setVoice(varVoice);
+				}
+			}
+			varVoices.dispose();
+		}
+		if (!success) {
+			int index = voiceName.indexOf("name="); //$NON-NLS-1$
+			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;
+	}
+
+	private void setAudioOutputName() {
+		String audioOutput = preferenceStore.getString(AUDIO_OUTPUT);
+		if (audioOutput.length() > 0) {
+			setAudioOutputName(audioOutput);
+		} else {
+			setAudioOutput(null);
+		}
+	}
+
+	/**
+	 * @param audioOutput
+	 *            The audio output name to be set.
+	 * @return The invocation is succeeded then it returns true.
+	 */
+	public boolean setAudioOutputName(String audioOutput) {
+		boolean success = false;
+		Variant varAudioOutputs = getAudioOutputs(null, null);
+		if (null != varAudioOutputs) {
+			SpeechObjectTokens tokens = SpeechObjectTokens
+					.getTokens(varAudioOutputs);
+			if (null != tokens) {
+				for (int i = 0; i < tokens.getCount(); i++) {
+					Variant varAudioOutput = tokens.getItem(i);
+					if (null != varAudioOutput) {
+						SpObjectToken token = SpObjectToken
+								.getToken(varAudioOutput);
+						if (null != token
+								&& audioOutput.equals(token.getDescription(0))) {
+							success = setAudioOutput(varAudioOutput);
+							break;
+						}
+					}
+				}
+			}
+			varAudioOutputs.dispose();
+		}
+		return success;
+	}
+
+	/**
+	 * @param requiredAttributes
+	 * @param optionalAttributes
+	 * @return The tokens of voices.
+	 */
+	public Variant getVoices(String requiredAttributes,
+			String optionalAttributes) {
+		return getTokens(idGetVoices, requiredAttributes, optionalAttributes);
+	}
+
+	/**
+	 * @param requiredAttributes
+	 * @param optionalAttributes
+	 * @return The tokens of audio outputs.
+	 */
+	public Variant getAudioOutputs(String requiredAttributes,
+			String optionalAttributes) {
+		return getTokens(idGetAudioOutputs, requiredAttributes,
+				optionalAttributes);
+	}
+
+	private Variant getTokens(int id, String requiredAttributes,
+			String optionalAttributes) {
+		if (null == requiredAttributes) {
+			return automation.invoke(id);
+		} else if (null == optionalAttributes) {
+			return automation.invoke(id, new Variant[] { new Variant(
+					requiredAttributes) });
+		}
+		return automation.invoke(id, new Variant[] {
+				new Variant(requiredAttributes),
+				new Variant(optionalAttributes) });
+	}
+
+	private int getIDsOfNames(String name) {
+		int dispid[] = automation.getIDsOfNames(new String[] { name });
+		if (null != dispid) {
+			return dispid[0];
+		}
+		return 0;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.actf.ai.tts.ITTSEngine#dispose()
+	 */
+	public void dispose() {
+		if (!isDisposed) {
+			isDisposed = true;
+
+			varSapiVoice.dispose();
+
+			if (MspPlugin.getDefault() != null) {
+				MspPlugin.getDefault().removePropertyChangeListener(this);
+			}
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.actf.ai.tts.ITTSEngine#getSpeed()
+	 */
+	public int getSpeed() {
+		int rate = getRate(); // -10 <= rate <= 10
+		return (rate + 10) * 5; // 0 <= speed <= 100
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.actf.ai.tts.ITTSEngine#setSpeed(int)
+	 */
+	public void setSpeed(int speed) {
+		int rate = speed / 5 - 10;
+		setRate(rate);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.actf.ai.tts.ITTSEngine#setLanguage(java.lang.String)
+	 */
+	public void setLanguage(String language) {
+		String token;
+		if (LANG_JAPANESE.equals(language)) {
+			token = "language=411"; //$NON-NLS-1$
+		} else if (LANG_ENGLISH.equals(language)) {
+			token = "language=409;9"; //$NON-NLS-1$
+		} else {
+			return;
+		}
+		setVoiceName(token);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.actf.ai.tts.ITTSEngine#setGender(java.lang.String)
+	 */
+	public void setGender(String gender) {
+		//TODO
+		if(GENDER_MALE.equalsIgnoreCase(gender)){
+//			setVoiceName("name=Microsoft Mike");
+		}else if(GENDER_FEMALE.equalsIgnoreCase(gender)){
+//			setVoiceName("name=Microsoft Mary");			
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.actf.ai.tts.ITTSEngine#isAvailable()
+	 */
+	public boolean isAvailable() {
+		return automation != null;
+	}
+
+	public boolean isDisposed() {
+		return isDisposed;
+	}
+}
diff --git a/plugins/org.eclipse.actf.ai.tts.msp/src/org/eclipse/actf/ai/tts/msp/engine/SpEvent.java b/plugins/org.eclipse.actf.ai.tts.msp/src/org/eclipse/actf/ai/tts/msp/engine/SpEvent.java
new file mode 100644
index 0000000..0c3e84a
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.tts.msp/src/org/eclipse/actf/ai/tts/msp/engine/SpEvent.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * Copyright (c) 2007 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:
+ *    Takashi ITOH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.tts.msp.engine;
+
+import org.eclipse.actf.util.win32.MemoryUtil;
+
+/**
+ * COM wrapper of SPEVENT structure.
+ * 
+ * @see "Microsoft Speech API SPEVENT"
+ */
+public class SpEvent {
+
+	private int address = MemoryUtil.GlobalAlloc(24);
+
+	public int getAddress() {
+		return address;
+	}
+
+	public short getEventId() {
+		short[] eventId = new short[1];
+		MemoryUtil.MoveMemory(eventId, address, 2);
+		return eventId[0];
+	}
+
+	public short getLParamType() {
+		short[] lParamType = new short[1];
+		MemoryUtil.MoveMemory(lParamType, address + 2, 2);
+		return lParamType[0];
+	}
+
+	public int getStreamNum() {
+		int[] streamNum = new int[1];
+		MemoryUtil.MoveMemory(streamNum, address + 4, 4);
+		return streamNum[0];
+	}
+
+	public long getAudioStreamOffset() {
+		int[] audioStreamOffset = new int[2];
+		MemoryUtil.MoveMemory(audioStreamOffset, address + 8, 4 * 2);
+		return audioStreamOffset[0]
+				+ ((long) audioStreamOffset[1] << 32);
+	}
+
+	public int getWParam() {
+		int[] wParam = new int[1];
+		MemoryUtil.MoveMemory(wParam, address + 16, 4);
+		return wParam[0];
+	}
+
+	public int getLParam() {
+		int[] lParam = new int[1];
+		MemoryUtil.MoveMemory(lParam, address + 20, 4);
+		return lParam[0];
+	}
+
+	public void dispose() {
+		MemoryUtil.GlobalFree(address);
+	}
+}
diff --git a/plugins/org.eclipse.actf.ai.tts.msp/src/org/eclipse/actf/ai/tts/msp/engine/SpObjectToken.java b/plugins/org.eclipse.actf.ai.tts.msp/src/org/eclipse/actf/ai/tts/msp/engine/SpObjectToken.java
new file mode 100644
index 0000000..5402f12
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.tts.msp/src/org/eclipse/actf/ai/tts/msp/engine/SpObjectToken.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2007 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:
+ *    Takashi ITOH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.tts.msp.engine;
+
+import org.eclipse.swt.ole.win32.OLE;
+import org.eclipse.swt.ole.win32.OleAutomation;
+import org.eclipse.swt.ole.win32.Variant;
+
+/*
+ * COM Wrapper of SpObjectToken interface
+ * 
+ * @see "Microsoft Speech API SpObjectToken"
+ */
+public class SpObjectToken {
+
+	private OleAutomation automation;
+	private int idGetDescription;
+
+	public SpObjectToken(Variant varToken) {
+		automation = varToken.getAutomation();
+		idGetDescription = getIDsOfNames("GetDescription"); //$NON-NLS-1$
+	}
+
+	public static SpObjectToken getToken(Variant varToken) {
+		if (null == varToken || OLE.VT_DISPATCH != varToken.getType()) {
+			return null;
+		}
+		return new SpObjectToken(varToken);
+	}
+
+	public String getDescription(int locale) {
+		try {
+			return automation.invoke(idGetDescription,
+					new Variant[] { new Variant(locale) }).getString().trim();
+		} catch (Exception e) {
+		}
+		return null;
+	}
+
+	private int getIDsOfNames(String name) {
+		int dispid[] = automation.getIDsOfNames(new String[] { name });
+		if (null != dispid) {
+			return dispid[0];
+		}
+		return 0;
+	}
+}
diff --git a/plugins/org.eclipse.actf.ai.tts.msp/src/org/eclipse/actf/ai/tts/msp/engine/SpeechObjectTokens.java b/plugins/org.eclipse.actf.ai.tts.msp/src/org/eclipse/actf/ai/tts/msp/engine/SpeechObjectTokens.java
new file mode 100644
index 0000000..d5f834f
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.tts.msp/src/org/eclipse/actf/ai/tts/msp/engine/SpeechObjectTokens.java
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ * Copyright (c) 2007 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:
+ *    Takashi ITOH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.tts.msp.engine;
+
+import org.eclipse.swt.ole.win32.OLE;
+import org.eclipse.swt.ole.win32.OleAutomation;
+import org.eclipse.swt.ole.win32.Variant;
+
+/*
+ * COM Wrapper of ISpeechObjectTokens interface
+ * 
+ * @see "Microsoft Speech API ISpeechObjectTokens"
+ */
+public class SpeechObjectTokens {
+
+	private OleAutomation automation;
+	private int idCount;
+	private int idItem;
+
+	public SpeechObjectTokens(Variant varTokens) {
+		automation = varTokens.getAutomation();
+		idCount = getIDsOfNames("Count"); //$NON-NLS-1$
+		idItem = getIDsOfNames("Item"); //$NON-NLS-1$
+	}
+
+	public static SpeechObjectTokens getTokens(Variant varTokens) {
+		if (null == varTokens || OLE.VT_DISPATCH != varTokens.getType()) {
+			return null;
+		}
+		return new SpeechObjectTokens(varTokens);
+	}
+
+	public int getCount() {
+		try {
+			return automation.getProperty(idCount).getInt();
+		} catch (Exception e) {
+		}
+		return 0;
+	}
+
+	public Variant getItem(int index) {
+		try {
+			return automation.invoke(idItem,
+					new Variant[] { new Variant(index) });
+		} catch (Exception e) {
+		}
+		return null;
+	}
+
+	private int getIDsOfNames(String name) {
+		int dispid[] = automation.getIDsOfNames(new String[] { name });
+		if (null != dispid) {
+			return dispid[0];
+		}
+		return 0;
+	}
+}
diff --git a/plugins/org.eclipse.actf.ai.tts.msp/src/org/eclipse/actf/ai/tts/msp/preferences/MspAudioOutputFieldEditor.java b/plugins/org.eclipse.actf.ai.tts.msp/src/org/eclipse/actf/ai/tts/msp/preferences/MspAudioOutputFieldEditor.java
new file mode 100644
index 0000000..9a19e2d
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.tts.msp/src/org/eclipse/actf/ai/tts/msp/preferences/MspAudioOutputFieldEditor.java
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2012 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:
+ *    Takashi ITOH - initial API and implementation
+ *    Kentarou FUKUDA - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.tts.msp.preferences;
+
+import org.eclipse.actf.ai.tts.msp.MspPlugin;
+import org.eclipse.actf.ai.tts.msp.engine.MspVoice;
+import org.eclipse.actf.ai.voice.preferences.util.ComboFieldEditor;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.swt.widgets.Composite;
+
+public class MspAudioOutputFieldEditor extends ComboFieldEditor {
+
+	private static IPreferenceStore preferenceStore = MspPlugin.getDefault()
+			.getPreferenceStore();
+
+	public MspAudioOutputFieldEditor(String labelText, Composite parent) {
+		super(MspVoice.AUDIO_OUTPUT, labelText, null, parent);
+	}
+
+	protected void initLabelsAndValues(String[][] labelsAndValues) {
+		super.initLabelsAndValues(MspTestManager.getInstance()
+				.getAudioOutputNames());
+	}
+	
+	@Override
+	protected void fireValueChanged(String property, Object oldValue,
+			Object newValue) {
+
+		preferenceStore.setValue(MspVoice.AUDIO_OUTPUT, newValue.toString());
+
+		super.fireValueChanged(property, oldValue, newValue);
+	}
+}
diff --git a/plugins/org.eclipse.actf.ai.tts.msp/src/org/eclipse/actf/ai/tts/msp/preferences/MspPreferenceInitializer.java b/plugins/org.eclipse.actf.ai.tts.msp/src/org/eclipse/actf/ai/tts/msp/preferences/MspPreferenceInitializer.java
new file mode 100644
index 0000000..33c90a7
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.tts.msp/src/org/eclipse/actf/ai/tts/msp/preferences/MspPreferenceInitializer.java
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2012 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:
+ *    Takashi ITOH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.tts.msp.preferences;
+
+import org.eclipse.actf.ai.tts.msp.MspPlugin;
+import org.eclipse.actf.ai.tts.msp.engine.MspVoice;
+import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer;
+import org.eclipse.jface.preference.IPreferenceStore;
+
+
+public class MspPreferenceInitializer extends AbstractPreferenceInitializer {
+
+	public void initializeDefaultPreferences() {
+		IPreferenceStore store = MspPlugin.getDefault().getPreferenceStore();
+		store.setDefault(MspVoice.ID, "Microsoft Sam"); //$NON-NLS-1$
+	}
+
+}
diff --git a/plugins/org.eclipse.actf.ai.tts.msp/src/org/eclipse/actf/ai/tts/msp/preferences/MspPreferencePage.java b/plugins/org.eclipse.actf.ai.tts.msp/src/org/eclipse/actf/ai/tts/msp/preferences/MspPreferencePage.java
new file mode 100644
index 0000000..35697c6
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.tts.msp/src/org/eclipse/actf/ai/tts/msp/preferences/MspPreferencePage.java
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2012 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:
+ *    Takashi ITOH - initial API and implementation
+ *    Kentarou FUKUDA - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.tts.msp.preferences;
+
+import org.eclipse.actf.ai.tts.TTSRegistry;
+import org.eclipse.actf.ai.tts.msp.Messages;
+import org.eclipse.actf.ai.tts.msp.MspPlugin;
+import org.eclipse.actf.ai.tts.msp.engine.MspVoice;
+import org.eclipse.actf.ai.voice.preferences.util.ComboFieldEditor;
+import org.eclipse.actf.ai.voice.preferences.util.GroupFieldEditorVoicePreferencePage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+
+public class MspPreferencePage extends GroupFieldEditorVoicePreferencePage
+		implements IWorkbenchPreferencePage {
+
+	private String orgVoice;
+	private String orgAudio;
+
+	public MspPreferencePage() {
+		super();
+		setDescription(Messages.tts_msp_description);
+		setPreferenceStore(MspPlugin.getDefault().getPreferenceStore());
+	}
+
+	public void createFieldEditors() {
+		if (!TTSRegistry.isAvailable(MspVoice.ID)) {
+			setMessage(Messages.tts_msp_notAvailable);
+			return;
+		}
+
+		orgVoice = getPreferenceStore().getString(MspVoice.ID);
+		orgAudio = getPreferenceStore().getString(MspVoice.AUDIO_OUTPUT);
+
+		final ComboFieldEditor voiceEditor;
+		addField(voiceEditor = new MspVoiceFieldEditor(
+				Messages.tts_msp_voicename, getFieldEditorParent()));
+		addField(new MspAudioOutputFieldEditor(Messages.tts_msp_audiooutput,
+				getFieldEditorParent()));
+
+		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.tts_msp_test);
+		testButton.addSelectionListener(new SelectionAdapter() {
+			public void widgetSelected(SelectionEvent e) {
+				MspTestManager.getInstance().speakTest();
+			}
+		});
+	}
+
+	public void init(IWorkbench workbench) {
+	}
+
+	@Override
+	public boolean performCancel() {
+		getPreferenceStore().setValue(MspVoice.ID, orgVoice);
+		getPreferenceStore().setValue(MspVoice.AUDIO_OUTPUT, orgAudio);
+		return super.performCancel();
+	}
+
+	@Override
+	protected void performApply() {
+		super.performApply();
+
+		orgVoice = getPreferenceStore().getString(MspVoice.ID);
+		orgAudio = getPreferenceStore().getString(MspVoice.AUDIO_OUTPUT);
+	}
+
+}
diff --git a/plugins/org.eclipse.actf.ai.tts.msp/src/org/eclipse/actf/ai/tts/msp/preferences/MspTestManager.java b/plugins/org.eclipse.actf.ai.tts.msp/src/org/eclipse/actf/ai/tts/msp/preferences/MspTestManager.java
new file mode 100644
index 0000000..7cf26f8
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.tts.msp/src/org/eclipse/actf/ai/tts/msp/preferences/MspTestManager.java
@@ -0,0 +1,123 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2012 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:
+ *    Takashi ITOH - initial API and implementation
+ *    Kentarou FUKUDA - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.tts.msp.preferences;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.actf.ai.tts.ISAPIEngine;
+import org.eclipse.actf.ai.tts.TTSRegistry;
+import org.eclipse.actf.ai.tts.msp.MspPlugin;
+import org.eclipse.actf.ai.tts.msp.engine.MspVoice;
+import org.eclipse.actf.ai.tts.msp.engine.SpObjectToken;
+import org.eclipse.actf.ai.tts.msp.engine.SpeechObjectTokens;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.swt.ole.win32.Variant;
+
+public class MspTestManager {
+
+	private static MspTestManager instance;
+
+	private MspVoice sapiVoice;
+	private String[][] voiceNames;
+	private String[][] audioOutputNames;
+
+	private static final String SAMPLE_TEXT = "Hello. This is test."; //$NON-NLS-1$
+
+	public MspTestManager() {
+		sapiVoice = (MspVoice) TTSRegistry.createTTSEngine(MspVoice.ID);
+		Variant varVoices = sapiVoice.getVoices(null, null);
+		if (null != varVoices) {
+			SpeechObjectTokens voiceTokens = SpeechObjectTokens
+					.getTokens(varVoices);
+			if (null != voiceTokens) {
+				String exclude = Platform.getResourceString(MspPlugin
+						.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 MspTestManager getInstance() {
+		if (null == instance) {
+			instance = new MspTestManager();
+		}
+		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.msp/src/org/eclipse/actf/ai/tts/msp/preferences/MspVoiceFieldEditor.java b/plugins/org.eclipse.actf.ai.tts.msp/src/org/eclipse/actf/ai/tts/msp/preferences/MspVoiceFieldEditor.java
new file mode 100644
index 0000000..3b321b9
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.tts.msp/src/org/eclipse/actf/ai/tts/msp/preferences/MspVoiceFieldEditor.java
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2012 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:
+ *    Takashi ITOH - initial API and implementation
+ *    Kentarou FUKUDA - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.tts.msp.preferences;
+
+import org.eclipse.actf.ai.tts.msp.MspPlugin;
+import org.eclipse.actf.ai.tts.msp.engine.MspVoice;
+import org.eclipse.actf.ai.voice.preferences.util.ComboFieldEditor;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.swt.widgets.Composite;
+
+public class MspVoiceFieldEditor extends ComboFieldEditor {
+
+	private static IPreferenceStore preferenceStore = MspPlugin.getDefault()
+			.getPreferenceStore();
+
+	public MspVoiceFieldEditor(String labelText, Composite parent) {
+		super(MspVoice.ID, labelText, null, parent);
+	}
+
+	protected void initLabelsAndValues(String[][] labelsAndValues) {
+		super
+				.initLabelsAndValues(MspTestManager.getInstance()
+						.getVoiceNames());
+	}
+
+	@Override
+	protected void fireValueChanged(String property, Object oldValue,
+			Object newValue) {
+
+		preferenceStore.setValue(MspVoice.ID, newValue.toString());
+
+		super.fireValueChanged(property, oldValue, newValue);
+	}
+
+}