[323932] aiBrowser enhancements (extended audio description, etc.)
diff --git a/plugins/org.eclipse.actf.ai.audio.description/META-INF/MANIFEST.MF b/plugins/org.eclipse.actf.ai.audio.description/META-INF/MANIFEST.MF
index ce568fb..cb258a8 100644
--- a/plugins/org.eclipse.actf.ai.audio.description/META-INF/MANIFEST.MF
+++ b/plugins/org.eclipse.actf.ai.audio.description/META-INF/MANIFEST.MF
@@ -14,7 +14,10 @@
  org.eclipse.actf.ai.xmlstore,
  org.eclipse.actf.core,
  org.eclipse.actf.ui,
- org.eclipse.actf.model.ui
+ org.eclipse.actf.model.ui,
+ org.eclipse.actf.model.dom.dombycom,
+ org.eclipse.actf.util.vocab,
+ org.eclipse.actf.ai.audio.io
 Eclipse-LazyStart: true
 Export-Package: org.eclipse.actf.ai.audio.description
 Bundle-RequiredExecutionEnvironment: J2SE-1.5
diff --git a/plugins/org.eclipse.actf.ai.audio.description/plugin.xml b/plugins/org.eclipse.actf.ai.audio.description/plugin.xml
index 721d33c..9d23b51 100644
--- a/plugins/org.eclipse.actf.ai.audio.description/plugin.xml
+++ b/plugins/org.eclipse.actf.ai.audio.description/plugin.xml
@@ -57,5 +57,23 @@
          point="org.eclipse.core.runtime.preferences">
       <initializer class="org.eclipse.actf.ai.audio.description.preferences.ADPreferenceInitializer"/>
    </extension>
-
+   <!-- experimental
+   <extension
+         point="org.eclipse.ui.actionSets">
+      <actionSet
+            id="org.eclipse.actf.ai.audio.description.actionSet1"
+            label="Audio Description"
+            visible="true">
+         <action
+               accelerator="F11"
+               class="org.eclipse.actf.ai.audio.description.actions.ReadAdditionalDescriptionAction"
+               id="org.eclipse.actf.ai.audio.description.action1"
+               label="Read &amp;Additional Description"
+               menubarPath="navigate/"
+               state="false"
+               style="push">
+         </action>
+      </actionSet>
+   </extension>
+   -->
 </plugin>
diff --git a/plugins/org.eclipse.actf.ai.audio.description/src/org/eclipse/actf/ai/audio/description/IMetadata.java b/plugins/org.eclipse.actf.ai.audio.description/src/org/eclipse/actf/ai/audio/description/IMetadata.java
index f39eee9..a0cdc58 100644
--- a/plugins/org.eclipse.actf.ai.audio.description/src/org/eclipse/actf/ai/audio/description/IMetadata.java
+++ b/plugins/org.eclipse.actf.ai.audio.description/src/org/eclipse/actf/ai/audio/description/IMetadata.java
@@ -94,4 +94,43 @@
 	 * @see #IMPORTANCE_HIGH
 	 */
 	int getImportance();
+
+	int getSpeed();
+
+	String getGender();
+
+	/*
+	 * public String getAddition();
+	 * 
+	 * public void setAddition(String addition);
+	 * 
+	 * public boolean hasAddition();
+	 * 
+	 * public void setAdditionSpeed(String speed);
+	 * 
+	 * public int getAdditionSpeed();
+	 * 
+	 * public void setAdditionGender(String gender);
+	 * 
+	 * public String getAdditionGender();
+	 */
+
+	public boolean hasValidWav();
+
+	public String getWavUri();
+
+	public String getWavLocal();
+
+	public int getWavSpeed();
+
+	public boolean getWavEnabled();
+
+	public void setWavUri(String wavUri);
+
+	public void setWavLocal(String wavLocal);
+
+	public void setWavSpeed(String wavSpeed);
+
+	public void setWavEnabled(String wavEnabled);
+
 }
diff --git a/plugins/org.eclipse.actf.ai.audio.description/src/org/eclipse/actf/ai/audio/description/actions/ReadAdditionalDescriptionAction.java b/plugins/org.eclipse.actf.ai.audio.description/src/org/eclipse/actf/ai/audio/description/actions/ReadAdditionalDescriptionAction.java
new file mode 100644
index 0000000..401537d
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.audio.description/src/org/eclipse/actf/ai/audio/description/actions/ReadAdditionalDescriptionAction.java
@@ -0,0 +1,29 @@
+package org.eclipse.actf.ai.audio.description.actions;
+
+import org.eclipse.actf.ai.audio.description.impl.MetadataManager;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.IWorkbenchWindowActionDelegate;
+
+public class ReadAdditionalDescriptionAction implements
+		IWorkbenchWindowActionDelegate {
+
+	public void dispose() {
+		// TODO Auto-generated method stub
+
+	}
+
+	public void init(IWorkbenchWindow window) {
+		// TODO Auto-generated method stub
+
+	}
+
+	public void run(IAction action) {
+		MetadataManager.requestAdditions();
+	}
+
+	public void selectionChanged(IAction action, ISelection selection) {
+	}
+
+}
diff --git a/plugins/org.eclipse.actf.ai.audio.description/src/org/eclipse/actf/ai/audio/description/impl/MetadataImpl.java b/plugins/org.eclipse.actf.ai.audio.description/src/org/eclipse/actf/ai/audio/description/impl/MetadataImpl.java
index 43b8135..cd3c377 100644
--- a/plugins/org.eclipse.actf.ai.audio.description/src/org/eclipse/actf/ai/audio/description/impl/MetadataImpl.java
+++ b/plugins/org.eclipse.actf.ai.audio.description/src/org/eclipse/actf/ai/audio/description/impl/MetadataImpl.java
@@ -13,6 +13,8 @@
 import java.util.Locale;
 
 import org.eclipse.actf.ai.audio.description.IMetadata;
+import org.eclipse.actf.ai.tts.ITTSEngine;
+import org.eclipse.actf.ai.voice.IVoice;
 
 public class MetadataImpl implements IMetadata {
 	private double scale = 1;
@@ -33,14 +35,69 @@
 
 	private String lang;
 
+	private int speed = IVoice.SPEED_NORMAL;
+
+	private String gender = ITTSEngine.GENDER_MALE;
+
+	/*
+	 * private String addition = "";
+	 * 
+	 * private boolean hasAddition = false;
+	 * 
+	 * private String additionGender = ITTSEngine.GENDER_MALE;
+	 * 
+	 * private int additionSpeed = IVoice.SPEED_NORMAL;
+	 */
+
+	private boolean hasValidWav = false;
+
+	private int wavSpeed = 100;
+
+	private String wavUri = "";
+
+	private String wavLocal = "";
+
+	private boolean wavEnabled = true;
+
 	public MetadataImpl(String start, String duration, String desc,
-			String lang, String importance) {
+			String lang, String importance, String speed, String gender,
+			String extended) {
 		setStart(start);
 		setDuration(duration);
 		setLang(lang);
 		setDescription(desc);
 		setImportance(importance);
+		setSpeed(speed);
+		setGender(gender);
+		setExtended(extended);
+	}
 
+	private void setExtended(String extended) {
+		if ("true".equalsIgnoreCase(extended)) {
+			setType(MASK_PAUSE_DURING_SPEAK | MASK_SPEAK);
+		}
+	}
+
+	private void setSpeed(String speed) {
+		try {
+			int speedVal = Integer.parseInt(speed);
+			if (speedVal >= IVoice.SPEED_MIN && speedVal <= IVoice.SPEED_MAX) {
+				this.speed = speedVal;
+			}
+		} catch (Exception e) {
+		}
+	}
+
+	private void setGender(String gender) {
+		if (ITTSEngine.GENDER_MALE.equalsIgnoreCase(gender)) {
+			this.gender = ITTSEngine.GENDER_MALE;
+		} else if (ITTSEngine.GENDER_FEMALE.equalsIgnoreCase(gender)) {
+			this.gender = ITTSEngine.GENDER_FEMALE;
+		}
+	}
+
+	private void setType(int type) {
+		this.type = type;
 	}
 
 	private void setStart(String start) {
@@ -163,4 +220,78 @@
 	public int getImportance() {
 		return importance;
 	}
+
+	public int getSpeed() {
+		return speed;
+	}
+
+	public String getGender() {
+		return gender;
+	}
+
+	/*
+	 * public String getAddition() { return addition; }
+	 * 
+	 * public void setAddition(String addition) { if (addition != null) {
+	 * this.addition = addition; hasAddition = true; } }
+	 * 
+	 * public boolean hasAddition() { return hasAddition; }
+	 * 
+	 * public String getAdditionGender() { return additionGender; }
+	 * 
+	 * public int getAdditionSpeed() { return additionSpeed; }
+	 * 
+	 * public void setAdditionGender(String gender) { if
+	 * (ITTSEngine.GENDER_MALE.equalsIgnoreCase(gender)) { this.additionGender =
+	 * ITTSEngine.GENDER_MALE; } else if
+	 * (ITTSEngine.GENDER_FEMALE.equalsIgnoreCase(gender)) { this.additionGender
+	 * = ITTSEngine.GENDER_FEMALE; } }
+	 * 
+	 * public void setAdditionSpeed(String speed) { try { int speedVal =
+	 * Integer.parseInt(speed); if (speedVal >= IVoice.SPEED_MIN && speedVal <=
+	 * IVoice.SPEED_MAX) { this.additionSpeed = speedVal; } } catch (Exception
+	 * e) { } }
+	 */
+
+	public boolean getWavEnabled() {
+		return wavEnabled;
+	}
+
+	public String getWavLocal() {
+		return wavLocal;
+	}
+
+	public int getWavSpeed() {
+		return wavSpeed;
+	}
+
+	public String getWavUri() {
+		return wavUri;
+	}
+
+	public void setWavEnabled(String wavEnabled) {
+		// TODO Auto-generated method stub
+
+	}
+
+	public void setWavLocal(String wavLocal) {
+		this.wavLocal = wavLocal;
+		// TODO validation
+		hasValidWav = true;
+	}
+
+	public void setWavSpeed(String wavSpeed) {
+		// TODO Auto-generated method stub
+
+	}
+
+	public void setWavUri(String wavUri) {
+		// TODO Auto-generated method stub
+
+	}
+
+	public boolean hasValidWav() {
+		return hasValidWav;
+	}
+
 }
diff --git a/plugins/org.eclipse.actf.ai.audio.description/src/org/eclipse/actf/ai/audio/description/impl/MetadataManager.java b/plugins/org.eclipse.actf.ai.audio.description/src/org/eclipse/actf/ai/audio/description/impl/MetadataManager.java
index a6f5753..69fdc21 100644
--- a/plugins/org.eclipse.actf.ai.audio.description/src/org/eclipse/actf/ai/audio/description/impl/MetadataManager.java
+++ b/plugins/org.eclipse.actf.ai.audio.description/src/org/eclipse/actf/ai/audio/description/impl/MetadataManager.java
@@ -11,17 +11,37 @@
 
 package org.eclipse.actf.ai.audio.description.impl;
 
+import java.net.MalformedURLException;
+import java.net.URL;
+
 import org.eclipse.actf.ai.audio.description.IMetadata;
 import org.eclipse.actf.ai.audio.description.IMetadataProvider;
+import org.eclipse.actf.ai.audio.io.AudioFactory;
+import org.eclipse.actf.ai.audio.io.AudioPipeListener;
+import org.eclipse.actf.ai.audio.io.IAudioPipe;
+import org.eclipse.actf.ai.audio.io.IAudioReader;
+import org.eclipse.actf.ai.audio.io.IAudioWriter;
 import org.eclipse.actf.ai.fennec.treemanager.ISoundControl;
 import org.eclipse.actf.ai.fennec.treemanager.IVideoControl;
 import org.eclipse.actf.ai.internal.audio.description.DescriptionPlugin;
 import org.eclipse.actf.ai.navigator.IMediaControl.IHandle;
+import org.eclipse.actf.ai.voice.IVoiceEventListener;
+import org.eclipse.actf.model.dom.dombycom.AnalyzedResult;
+import org.eclipse.actf.model.dom.dombycom.INodeEx;
+import org.eclipse.actf.model.dom.dombycom.INodeExVideo;
+import org.eclipse.actf.model.ui.util.ModelServiceUtils;
+import org.eclipse.swt.widgets.Display;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
 
-public class MetadataManager {
+public class MetadataManager implements IVoiceEventListener, AudioPipeListener {
 
 	private IMetadataProvider metadataProvider;
 
+	private IAudioPipe audio;
+
+	private IAudioWriter writer = AudioFactory.createDefaultWriter();
+
 	private int index;
 
 	private int oldIndex;
@@ -37,10 +57,18 @@
 
 	private boolean stopFlag = false;
 
+	private int voiceIndex = -1;
+
+	private static boolean requestAdditions = false;
+
+	private boolean restart = false;
+
 	public void setForceFlag(int flag) {
 		forceFlag = flag;
 	}
 
+	private AnalyzedResult analyzedResult;
+
 	public MetadataManager(IHandle handle, IMetadataProvider metadataProvider) {
 		this.handle = handle;
 		this.video = handle.getVideoControl();
@@ -48,6 +76,31 @@
 
 		this.metadataProvider = metadataProvider;
 		index = oldIndex = -1;
+
+		final IHandle handle2 = handle;
+		Thread thread = new Thread() {
+			public void run() {
+				while (true) {
+					try {
+						Thread.sleep(100);
+					} catch (InterruptedException e) {
+						e.printStackTrace();
+					}
+					if (restart) {
+						Display.getDefault().syncExec(new Runnable() {
+							public void run() {
+								System.out.println("play");
+								handle2.getVideoControl().playMedia();
+							}
+						});
+						restart = false;
+					}
+				}
+			};
+		};
+		thread.start();
+
+		DescriptionPlugin.getDefault().setVoiceEventListener(this);
 	}
 
 	private boolean pauseBeforeFlag = false;
@@ -56,6 +109,14 @@
 
 	private boolean pauseAfterFlag = false;
 
+	private boolean pauseDualingSpeakFlag = false;
+
+	//experimental
+	private String addition = "";
+	private int additionSpeed;
+	private String additionGender;
+	private boolean additionFlag;
+
 	public void process(double time) {
 		if (stopFlag)
 			return;
@@ -68,26 +129,47 @@
 		// System.out.println(time+",
 		// "+metadataProvider.getItem(index).getTime());
 
+		IMetadata curMetadata = null;
+
 		if (oldIndex != index && oldIndex < index) {
-			int type = metadataProvider.getItem(index).getType();
+			curMetadata = metadataProvider.getItem(index);
+			int type = curMetadata.getType();
 
 			pauseBeforeFlag = isIt(type | forceFlag,
 					IMetadata.MASK_PAUSE_BEFORE);
 			speakFlag = isIt(type | forceFlag, IMetadata.MASK_SPEAK);
 			pauseAfterFlag = isIt(type | forceFlag, IMetadata.MASK_PAUSE_AFTER);
+			pauseDualingSpeakFlag = isIt(type | forceFlag,
+					IMetadata.MASK_PAUSE_DURING_SPEAK);
 		}
 
-		if (pauseBeforeFlag) {
+		if (pauseBeforeFlag || pauseDualingSpeakFlag) {
 			pauseBeforeFlag = false;
+			voiceIndex = 100;
 			processPause();
 		}
 		if (speakFlag) {
 			speakFlag = false;
+
+			requestAdditions = false;
+
+			/*
+			 * if (null != curMetadata && curMetadata.hasAddition()) {
+			 * additionFlag = true; voiceIndex = 200; addition =
+			 * curMetadata.getAddition(); additionSpeed =
+			 * curMetadata.getAdditionSpeed(); additionGender =
+			 * curMetadata.getAdditionGender(); }
+			 */
+
 			processSpeak();
 		}
 		if (pauseAfterFlag) {
 			pauseAfterFlag = false;
-			processPlay();
+			processPause();
+		}
+		if (pauseDualingSpeakFlag) {
+			pauseDualingSpeakFlag = false;
+			// processPlay();
 		}
 
 		oldIndex = index;
@@ -97,14 +179,29 @@
 		return (type & pause) == pause;
 	}
 
+	private void analyze() {
+		// TODO tentative code
+		analyzedResult = new AnalyzedResult();
+		Document doc = ModelServiceUtils.getActiveModelService()
+				.getLiveDocument();
+		Node root = null;
+		if (doc != null)
+			root = doc.getFirstChild();
+		if (root instanceof INodeEx) {
+			analyzedResult = ((INodeEx) root).analyze(analyzedResult);
+		}
+	}
+
 	private void processPause() {
 		if (DescriptionPlugin.getDefault().getEnable())
-			video.pauseMedia();
+			handle.getVideoControl().pauseMedia();
+		// pauseMedia();
 	}
 
 	private void processPlay() {
 		if (DescriptionPlugin.getDefault().getEnable())
-			video.playMedia();
+			handle.getVideoControl().playMedia();
+		// playMedia();
 	}
 
 	private void processSpeak() {
@@ -112,21 +209,56 @@
 
 			/*
 			 * int[] volumes = null; volumes = sound.getVolumes(); int[]
-			 * volumes2 = new int[volumes.length]; for(int i=0; i<volumes2.length;
-			 * i++) volumes2[i] = 200; sound.setVolumes(volumes2);
+			 * volumes2 = new int[volumes.length]; for(int i=0;
+			 * i<volumes2.length; i++) volumes2[i] = 200;
+			 * sound.setVolumes(volumes2);
 			 */
+			IMetadata metadata = metadataProvider.getItem(index);
 
-			String desc = metadataProvider.getItem(index).getDescription();
-			say(desc);
+			if (metadata.hasValidWav()) {
+				if (audio != null && audio.isActive()) {
+					// TODO
+					audio.stop();
+				}
+
+				// TODO check
+				IAudioReader reader;
+				try {
+					reader = AudioFactory.createAudioReader(new URL(metadata
+							.getWavLocal()));
+					audio = AudioFactory.createAudioPipe(reader, writer);
+					audio.setBufferSize(100);
+					audio.setInterval(1);
+					audio.prepare();
+					audio.addAudioPipeListener(this);
+					audio.start();
+				} catch (MalformedURLException e) {
+					// TODO Auto-generated catch block
+					e.printStackTrace();
+				}
+
+			} else {
+				say(metadata.getDescription(), metadata.getSpeed(), metadata
+						.getGender());
+
+			}
 		}
 	}
 
-	public void say(String str) {
+	private void processAdditionSpeak() {
+		if (DescriptionPlugin.getDefault().getEnable()) {
+			say(addition, additionSpeed, additionGender);
+		}
+	}
+
+	public void say(String str, int speed, String gender) {
 		// System.out.println(str);
 		DescriptionPlugin plugin = DescriptionPlugin.getDefault();
-
 		if (plugin.canSpeak()) {
-			plugin.speak(str);
+			plugin.speak(str, speed, gender);
+			if (voiceIndex != -1) {
+				plugin.addSpeakIndex(voiceIndex);
+			}
 		} else {
 			handle.getVoice().speak(str, true);
 		}
@@ -151,4 +283,63 @@
 	public Object getMetadataProvider() {
 		return metadataProvider;
 	}
+
+	// TODO tentative from here
+	// TODO cache
+	public boolean pauseMedia() {
+		analyze();
+		INodeExVideo[] videos = analyzedResult.getVideoNodes();
+		boolean r = true;
+		for (int i = 0; i < videos.length; i++) {
+			r &= videos[i].pauseMedia();
+		}
+		return r;
+	}
+
+	public boolean playMedia() {
+		analyze();
+		INodeExVideo[] videos = analyzedResult.getVideoNodes();
+		boolean r = true;
+		for (int i = 0; i < videos.length; i++) {
+			r &= videos[i].playMedia();
+		}
+		return r;
+	}
+
+	// tentative end here
+
+	public void indexReceived(int index) {
+		if (100 == index) {
+			// playMedia();
+			handle.getVideoControl().playMedia();
+			voiceIndex = -1;
+		} else if (200 == index && additionFlag) {
+			additionFlag = false;
+			if (requestAdditions) {
+				voiceIndex = 100;
+				handle.getVideoControl().pauseMedia();
+				// pauseMedia();
+				processAdditionSpeak();
+				requestAdditions = false;
+			} else {
+				voiceIndex = -1;
+			}
+		}
+	}
+
+	// TODO
+	public static void requestAdditions() {
+		requestAdditions = true;
+	}
+
+	public void finished(IAudioPipe pipe) {
+		if (voiceIndex == 100) {
+			restart = true;
+		}
+	}
+
+	public void stopped(IAudioPipe pipe) {
+		System.out.println("stop");
+	}
+
 }
diff --git a/plugins/org.eclipse.actf.ai.audio.description/src/org/eclipse/actf/ai/audio/description/impl/MetadataProviderImpl.java b/plugins/org.eclipse.actf.ai.audio.description/src/org/eclipse/actf/ai/audio/description/impl/MetadataProviderImpl.java
index 27f70fd..adcaf66 100644
--- a/plugins/org.eclipse.actf.ai.audio.description/src/org/eclipse/actf/ai/audio/description/impl/MetadataProviderImpl.java
+++ b/plugins/org.eclipse.actf.ai.audio.description/src/org/eclipse/actf/ai/audio/description/impl/MetadataProviderImpl.java
@@ -143,12 +143,18 @@
 						altFlag = true;
 					}
 				}
+				if ("volumeLevel".equals(localName)) {
+					// do nothing
+				}
 			}
 		}
 
 		@Override
 		public void endElement(String uri, String localName, String qName)
 				throws SAXException {
+			if ("alternative".equals(localName)) {
+				altFlag = false;
+			}
 			// super.endElement(uri, localName, qName);
 		}
 	}
@@ -160,16 +166,39 @@
 
 		private boolean durationFlag = false;
 
+		private boolean additionFlag = false;
+
+		private boolean waveFlag = false;
+
 		private StringBuffer buf = new StringBuffer();
 
 		private String start;
 
 		private String duration;
 
+		private ArrayList<String> speed = new ArrayList<String>();
+
+		private ArrayList<String> gender = new ArrayList<String>();
+
 		private ArrayList<String> desc = new ArrayList<String>();
 
 		private ArrayList<String> lang = new ArrayList<String>();
 
+		private ArrayList<String> extended = new ArrayList<String>();
+
+		/*
+		 * experimental private ArrayList<String> additions = new
+		 * ArrayList<String>();
+		 * 
+		 * private ArrayList<String> additionsLang = new ArrayList<String>();
+		 * 
+		 * private ArrayList<String> additionsSpeed = new ArrayList<String>();
+		 * 
+		 * private ArrayList<String> additionsGender = new ArrayList<String>();
+		 */
+
+		private ArrayList<String> waveLocal = new ArrayList<String>();
+
 		private String importance;
 
 		public ItemHandler(BaseHandler back, String importance) {
@@ -190,6 +219,17 @@
 				durationFlag = true;
 			} else if ("description".equals(localName)) {
 				descFlag = true;
+				speed.add(attributes.getValue("speed"));
+				gender.add(attributes.getValue("gender"));
+				extended.add(attributes.getValue("extended"));
+				/*
+				 * } else if ("addition".equals(localName)) { additionFlag =
+				 * true; additionsSpeed.add(attributes.getValue("speed"));
+				 * additionsGender.add(attributes.getValue("gender"));
+				 */
+			} else if ("wave".equals(localName)) {
+				waveFlag = true;
+				waveLocal.add(attributes.getValue("local"));
 			}
 
 		}
@@ -203,6 +243,10 @@
 				buf.append(ch, start, length);
 			} else if (descFlag) {
 				buf.append(ch, start, length);
+			} else if (additionFlag) {
+				buf.append(ch, start, length);
+			} else if (waveFlag) {
+				buf.append(ch, start, length);
 			}
 		}
 
@@ -222,11 +266,36 @@
 				desc.add(buf.toString());
 				lang.add(langStack.peek());
 				buf.delete(0, buf.length());
+				/*
+				 * } else if ("addition".equals(localName)) { additionFlag =
+				 * false; additions.add(buf.toString());
+				 * additionsLang.add(langStack.peek()); buf.delete(0,
+				 * buf.length());
+				 */
+			} else if ("wave".equals(localName)) {
+				waveFlag = false;
+				// waveLang.add(langStack.peek()); //TODO
+				buf.delete(0, buf.length());
 			} else if ("item".equals(localName)) {
 
 				for (int i = 0; i < desc.size(); i++) {
 					MetadataImpl mi = new MetadataImpl(start, duration, desc
-							.get(i), lang.get(i), importance);
+							.get(i), lang.get(i), importance, speed.get(i),
+							gender.get(i), extended.get(i));
+
+					/*
+					 * for (int j = 0; j < additions.size(); j++) { if
+					 * (additionsLang.get(j).equals(lang.get(i))) {
+					 * mi.setAddition(additions.get(j));
+					 * mi.setAdditionGender(additionsGender.get(j));
+					 * mi.setAdditionSpeed(additionsSpeed.get(j)); } }
+					 */
+
+					for (int j = 0; j < waveLocal.size(); j++) {
+						// TODO lang
+						mi.setWavLocal(waveLocal.get(j));
+					}
+
 					// System.out.println(start + ", " + duration + ", " +
 					// desc.get(i) + ", " + lang.get(i));
 
@@ -320,6 +389,8 @@
 	}
 
 	public boolean hasMetadata() {
+		if (metadata == null)
+			return false;
 		return metadata.size() > 0;
 	}
 }
diff --git a/plugins/org.eclipse.actf.ai.audio.description/src/org/eclipse/actf/ai/internal/audio/description/DescriptionPlugin.java b/plugins/org.eclipse.actf.ai.audio.description/src/org/eclipse/actf/ai/internal/audio/description/DescriptionPlugin.java
index 937d4c2..4a03356 100644
--- a/plugins/org.eclipse.actf.ai.audio.description/src/org/eclipse/actf/ai/internal/audio/description/DescriptionPlugin.java
+++ b/plugins/org.eclipse.actf.ai.audio.description/src/org/eclipse/actf/ai/internal/audio/description/DescriptionPlugin.java
@@ -22,6 +22,7 @@
 import org.eclipse.actf.ai.tts.AbstractUIPluginForTTS;
 import org.eclipse.actf.ai.tts.ITTSEngine;
 import org.eclipse.actf.ai.tts.TTSRegistry;
+import org.eclipse.actf.ai.voice.IVoiceEventListener;
 import org.eclipse.actf.ai.xmlstore.IXMLInfo;
 import org.eclipse.actf.ai.xmlstore.IXMLSelector;
 import org.eclipse.actf.ai.xmlstore.IXMLStore;
@@ -37,7 +38,7 @@
  * information of audio description and TTS function to speak the description.
  */
 public class DescriptionPlugin extends AbstractUIPluginForTTS implements
-		IPropertyChangeListener {
+		IPropertyChangeListener, IVoiceEventListener {
 
 	/**
 	 * The plug-in ID.
@@ -57,7 +58,7 @@
 	/*
 	 * The switch weather enable or disable audio description speaking.
 	 */
-	private boolean enable = false;
+	private boolean enable = true;
 
 	/*
 	 * The current active audio description manager. If the target page has no
@@ -77,6 +78,8 @@
 		plugin = this;
 	}
 
+	private IVoiceEventListener listener;
+	
 	/**
 	 * @param url
 	 *            The URL string which determines audio description files in the
@@ -90,6 +93,7 @@
 		IXMLStore store = service.getRootStore();
 		IXMLSelector selector1 = service.getSelectorWithDocElem("puits",
 				"urn:puits");
+		System.out.println(url);
 		IXMLSelector selector2 = service.getSelectorWithURI(url);
 		store = store.specify(selector1);
 		store = store.specify(selector2);
@@ -141,8 +145,10 @@
 		if (e.equals("org.eclipse.actf.ai.screenreader.jaws"))
 			return null;
 		engine = TTSRegistry.createTTSEngine(e);
-		if (engine != null)
+		if (engine != null){
 			engine.setSpeed(50);
+			engine.setEventListener(this);
+		}
 		return engine;
 	}
 
@@ -254,11 +260,25 @@
 	 * @param str
 	 *            The string to be spoken.
 	 */
-	public void speak(String str) {
-		if (engine != null)
+	public void speak(String str, int speed, String gender) {
+		if (engine != null){
+			engine.setEventListener(this);
+			engine.setGender(gender);
+			engine.setSpeed(speed);
 			engine.speak(str, ITTSEngine.TTSFLAG_FLUSH, -1);
+		}
 	}
 
+	public void addSpeakIndex(int index){
+		if(engine != null){
+			engine.speak("",ITTSEngine.TTSFLAG_DEFAULT,index);
+		}
+	}
+	
+	public void setVoiceEventListener(IVoiceEventListener listener){
+		this.listener = listener;
+	}
+	
 	/**
 	 * Toggle the enable and disable of the plug-in.
 	 * 
@@ -292,4 +312,10 @@
 	public static String getString(String key) {
 		return Platform.getResourceString(getDefault().getBundle(), key);
 	}
+
+	public void indexReceived(int index) {
+		if(listener!=null){
+			listener.indexReceived(index);
+		}
+	}
 }