2 // Radegast Metaverse Client Speech Interface
3 // Copyright (c) 2009-2014, Radegast Development Team
4 // All rights reserved.
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions are met:
9 // * Redistributions of source code must retain the above copyright notice,
10 // this list of conditions and the following disclaimer.
11 // * Redistributions in binary form must reproduce the above copyright
12 // notice, this list of conditions and the following disclaimer in the
13 // documentation and/or other materials provided with the distribution.
14 // * Neither the name of the application "Radegast", nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 // $Id: PluginControl.cs 203 2009-09-07 19:26:02Z mojitotech@gmail.com $
32 using System.Collections.Generic;
36 using System.Reflection;
38 using System.Windows.Forms;
40 using OpenMetaverse.StructuredData;
42 namespace RadegastSpeech
44 [Radegast.Plugin(Name = "Speech", Description = "Adds TTS and STT accesibility capabilities to Radegast", Version = "0.3")]
45 public class PluginControl : IRadegastPlugin
47 private const string VERSION = "0.3";
48 public RadegastInstance instance;
49 internal Talk.Control talker;
50 internal Listen.Control listener;
51 internal Conversation.Control converse;
52 internal Environment.Control env;
53 internal Sound.Control sound;
54 internal ToolStripDropDownButton ToolsMenu;
55 private ToolStripMenuItem SpeechButton;
56 internal IRadSpeech osLayer;
61 /// Plugin start-up entry.
63 /// <param name="inst"></param>
64 /// <remarks>Called by Radegast at start-up</remarks>
65 public void StartPlugin(RadegastInstance inst)
69 // Get configuration settings, and initialize if not found.
70 config = instance.GlobalSettings["plugin.speech"] as OSDMap;
74 config = new OSDMap();
75 config["enabled"] = new OSDBoolean(false);
76 config["voices"] = new OSDMap();
77 config["properties"] = new OSDMap();
78 config["substitutions"] = new OSDMap();
79 instance.GlobalSettings["plugin.speech"] = config;
80 instance.GlobalSettings.Save();
83 // Add our enable/disable item to the Plugin Menu.
84 ToolsMenu = instance.MainForm.PluginsMenu;
86 SpeechButton = new ToolStripMenuItem("Speech", null, OnSpeechMenuButtonClicked);
87 ToolsMenu.DropDownItems.Add(SpeechButton);
89 SpeechButton.Checked = config["enabled"].AsBoolean();
91 ToolsMenu.Visible = true;
93 if (SpeechButton.Checked)
100 /// Startup code (executed only when needed)
102 private void Initialize()
104 // Never initialize twice
107 // Do the one-time only initializations.
111 env = new Environment.Control(this);
112 talker = new Talk.Control(this);
113 listener = new Listen.Control(this);
114 converse = new Conversation.Control(this);
115 sound = new Sound.FmodSound(this);
117 if (!instance.Netcom.IsLoggedIn)
119 talker.SayMore("Press enter to connect.");
123 // Create conversations and pick active one if we are activating
124 // the speech plugin mid-session
125 foreach (RadegastTab tab in instance.TabConsole.Tabs.Values)
127 converse.CreateConversationFromTab(tab, false);
129 converse.ActivateConversationFromTab(instance.TabConsole.SelectedTab);
136 SpeechButton.Checked = false;
137 config["enabled"] = OSD.FromBoolean(false);
138 SaveSpeechSettings();
139 System.Windows.Forms.MessageBox.Show("Speech failed initialization: " + e.Message);
145 /// Shutdown speech module
147 private void Shutdown()
149 if (!started) return;
160 Logger.Log("Failed to shutdown speech modules: ", Helpers.LogLevel.Warning, e);
169 /// Start all speech subsystems
171 private void StartControls()
173 // Start up each of the area controllers. The order
174 // here is important.
177 sound.Start(); // Sound output
178 talker.Start(); // Synthesis
179 listener.Start(); // Recognition
180 converse.Start(); // Topic-specific conversations
181 env.Start(); // Environmental awareness
185 System.Windows.Forms.MessageBox.Show("Speech can not start. See log.");
186 Logger.Log("Speech can not start.", Helpers.LogLevel.Error, e);
188 System.Console.WriteLine(e.StackTrace);
193 // Register the speech-related actions for context menus.
194 // Editing voice assignments to avatars.
195 instance.ContextActionManager.RegisterContextAction(
196 new GUI.AvatarSpeechAction(instance, this));
197 // Reading the contents of notecards.
198 instance.ContextActionManager.RegisterContextAction(
199 new GUI.NotecardReadAction(instance, this));
201 talker.Say("Rahdegast is ready.");
206 SpeechButton.Checked = false;
211 /// Plugin shut-down entry
213 /// <param name="inst"></param>
214 /// <remarks>Called by Radegast at shut-down, or when Speech is switched off.
215 /// We use this to release system resources.</remarks>
216 public void StopPlugin(RadegastInstance inst)
218 SpeechButton.Dispose();
223 /// Handle toggling of our enable flag
225 /// <param name="sender"></param>
226 /// <param name="e"></param>
227 void OnSpeechMenuButtonClicked(object sender, EventArgs e)
229 SpeechButton.Checked = !SpeechButton.Checked;
231 if (SpeechButton.Checked)
240 // Save this into the INI file.
241 config["enabled"] = OSD.FromBoolean(SpeechButton.Checked);
242 SaveSpeechSettings();
245 public void SaveSpeechSettings()
247 instance.GlobalSettings.Save();
251 /// Find the system-specific DLL for this platform
253 private void LoadOSLayer()
255 string dirName = Application.StartupPath;
257 if (!Directory.Exists(dirName))
258 throw new Exception("No startup directory found " + dirName);
260 // The filename depends on the platform.
261 System.Version version = System.Environment.OSVersion.Version;
262 string loadfilename = null;
263 switch (System.Environment.OSVersion.Platform)
265 case PlatformID.Win32NT:
266 case PlatformID.Win32Windows:
267 // XP and later have Speech Synthesis.
268 // XP=5, Vista=6, W7=7
269 if (version.Major >= 5)
270 loadfilename = "RadSpeechWin";
272 case PlatformID.Unix:
273 loadfilename = "RadSpeechLin";
275 case PlatformID.MacOSX:
276 loadfilename = "RadSpeechMac";
280 // If the name was not set, we do not support this platform
281 if (loadfilename == null)
282 throw new Exception("Platform not supported for Speech");
284 loadfilename = Path.Combine(dirName, loadfilename + ".dll");
287 Assembly assembly = Assembly.LoadFile(loadfilename);
289 // Examine the types exposed by this DLL, looking for ours.
290 foreach (Type type in assembly.GetTypes())
292 if (typeof(IRadSpeech).IsAssignableFrom(type))
294 foreach (var ci in type.GetConstructors())
296 if (ci.GetParameters().Length > 0) continue;
299 // This is the one. Instantiate it.
300 osLayer = (IRadSpeech)ci.Invoke(new object[0]);
305 throw new Exception("ERROR in Speech OS Layer: " + ex.Message);
311 catch (BadImageFormatException)
314 throw new Exception("Speech OS Layer bad format " + loadfilename);
316 catch (ReflectionTypeLoadException ex)
318 // Out of date or dlls missing sub dependencies
319 throw new Exception("ERROR loading Speech OS Layer " + loadfilename + ", " + ex.Message);
323 throw new Exception("Exception loading Speech OS Layer " + loadfilename + ", " + ex.Message);