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;
82 OSDMap props = (OSDMap)config["properties"];
83 if (props["voice_speed"] == "")
85 props["voice_speed"] = "medium";
88 #region Buttons on the plugin menu
89 // SpeechButton = new ToolStripMenuItem("Speech", null, OnSpeechMenuButtonClicked);
90 SpeechButton = new ToolStripMenuItem("Speech");
91 instance.MainForm.PluginsMenu.DropDownItems.Add(SpeechButton);
93 SpeechButton.Checked = config["enabled"].AsBoolean();
97 ToolStripMenuItem button = new ToolStripMenuItem("Enabled", null, (sender, e) =>
99 OnSpeechMenuButtonClicked(SpeechButton, EventArgs.Empty);
100 ((ToolStripMenuItem)sender).Checked = SpeechButton.Checked;
103 button.Checked = SpeechButton.Checked;
105 SpeechButton.DropDownItems.Add(button);
108 SpeechButton.DropDownItems.Add(new ToolStripSeparator());
112 ToolStripMenuItem slowButton = new ToolStripMenuItem("Slow");
113 if (props["voice_speed"] == "slow") slowButton.Checked = true;
114 ToolStripMenuItem mediumButton = new ToolStripMenuItem("Medium");
115 if (props["voice_speed"] == "medium") mediumButton.Checked = true;
116 ToolStripMenuItem fastButton = new ToolStripMenuItem("Fast");
117 if (props["voice_speed"] == "fast") fastButton.Checked = true;
119 slowButton.Click += (sender, e) =>
121 slowButton.Checked = !slowButton.Checked;
122 if (slowButton.Checked)
124 props["voice_speed"] = "slow";
125 mediumButton.Checked = false;
126 fastButton.Checked = false;
130 mediumButton.Click += (sender, e) =>
132 mediumButton.Checked = !mediumButton.Checked;
133 if (mediumButton.Checked)
135 props["voice_speed"] = "medium";
136 slowButton.Checked = false;
137 fastButton.Checked = false;
141 fastButton.Click += (sender, e) =>
143 fastButton.Checked = !fastButton.Checked;
144 if (fastButton.Checked)
146 props["voice_speed"] = "fast";
147 slowButton.Checked = false;
148 mediumButton.Checked = false;
152 SpeechButton.DropDownItems.Add(slowButton);
153 SpeechButton.DropDownItems.Add(mediumButton);
154 SpeechButton.DropDownItems.Add(fastButton);
157 SpeechButton.DropDownItems.Add(new ToolStripSeparator());
161 ToolStripMenuItem button = new ToolStripMenuItem("3D Sound", null, (sender, e) =>
163 var me = (ToolStripMenuItem)sender;
164 me.Checked = !me.Checked;
165 config["3d_sound"] = me.Checked;
166 instance.GlobalSettings.Save();
167 Radegast.Media.Speech.Surround = me.Checked;
170 button.Checked = config["3d_sound"].AsBoolean();
171 Radegast.Media.Speech.Surround = button.Checked;
173 SpeechButton.DropDownItems.Add(button);
175 #endregion Buttons on the plugin menu
177 if (SpeechButton.Checked)
182 instance.GlobalSettings.Save();
186 /// Startup code (executed only when needed)
188 private void Initialize()
190 // Never initialize twice
193 // Do the one-time only initializations.
197 env = new Environment.Control(this);
198 talker = new Talk.Control(this);
199 listener = new Listen.Control(this);
200 converse = new Conversation.Control(this);
201 sound = new Sound.FmodSound(this);
203 if (!instance.Netcom.IsLoggedIn)
205 talker.SayMore("Press enter to connect.");
209 // Create conversations and pick active one if we are activating
210 // the speech plugin mid-session
211 foreach (RadegastTab tab in instance.TabConsole.Tabs.Values)
213 converse.CreateConversationFromTab(tab, false);
215 converse.ActivateConversationFromTab(instance.TabConsole.SelectedTab);
222 SpeechButton.Checked = false;
223 config["enabled"] = OSD.FromBoolean(false);
224 SaveSpeechSettings();
225 System.Windows.Forms.MessageBox.Show("Speech failed initialization: " + e.Message);
231 /// Shutdown speech module
233 private void Shutdown()
235 if (!started) return;
246 Logger.Log("Failed to shutdown speech modules: ", Helpers.LogLevel.Warning, e);
255 /// Start all speech subsystems
257 private void StartControls()
259 // Start up each of the area controllers. The order
260 // here is important.
263 sound.Start(); // Sound output
264 talker.Start(); // Synthesis
265 listener.Start(); // Recognition
266 converse.Start(); // Topic-specific conversations
267 env.Start(); // Environmental awareness
271 System.Windows.Forms.MessageBox.Show("Speech can not start. See log.");
272 Logger.Log("Speech can not start.", Helpers.LogLevel.Error, e);
274 System.Console.WriteLine(e.StackTrace);
279 // Register the speech-related actions for context menus.
280 // Editing voice assignments to avatars.
281 instance.ContextActionManager.RegisterContextAction(
282 new GUI.AvatarSpeechAction(instance, this));
283 // Reading the contents of notecards.
284 instance.ContextActionManager.RegisterContextAction(
285 new GUI.NotecardReadAction(instance, this));
287 talker.Say("Rahdegast is ready.");
292 SpeechButton.Checked = false;
297 /// Plugin shut-down entry
299 /// <param name="inst"></param>
300 /// <remarks>Called by Radegast at shut-down, or when Speech is switched off.
301 /// We use this to release system resources.</remarks>
302 public void StopPlugin(RadegastInstance inst)
304 SpeechButton.Dispose();
309 /// Handle toggling of our enable flag
311 /// <param name="sender"></param>
312 /// <param name="e"></param>
313 void OnSpeechMenuButtonClicked(object sender, EventArgs e)
315 SpeechButton.Checked = !SpeechButton.Checked;
317 if (SpeechButton.Checked)
326 // Save this into the INI file.
327 config["enabled"] = OSD.FromBoolean(SpeechButton.Checked);
328 SaveSpeechSettings();
331 public void SaveSpeechSettings()
333 instance.GlobalSettings.Save();
337 /// Find the system-specific DLL for this platform
339 private void LoadOSLayer()
341 string dirName = Application.StartupPath;
343 if (!Directory.Exists(dirName))
344 throw new Exception("No startup directory found " + dirName);
346 // The filename depends on the platform.
347 System.Version version = System.Environment.OSVersion.Version;
348 string loadfilename = null;
349 switch (System.Environment.OSVersion.Platform)
351 case PlatformID.Win32NT:
352 case PlatformID.Win32Windows:
353 // XP and later have Speech Synthesis.
354 // XP=5, Vista=6, W7=7
355 if (version.Major >= 5)
356 loadfilename = "RadSpeechWin";
358 case PlatformID.Unix:
359 loadfilename = "RadSpeechLin";
361 case PlatformID.MacOSX:
362 loadfilename = "RadSpeechMac";
366 // If the name was not set, we do not support this platform
367 if (loadfilename == null)
368 throw new Exception("Platform not supported for Speech");
370 loadfilename = Path.Combine(dirName, loadfilename + ".dll");
373 Assembly assembly = Assembly.LoadFile(loadfilename);
375 // Examine the types exposed by this DLL, looking for ours.
376 foreach (Type type in assembly.GetTypes())
378 if (typeof(IRadSpeech).IsAssignableFrom(type))
380 foreach (var ci in type.GetConstructors())
382 if (ci.GetParameters().Length > 0) continue;
385 // This is the one. Instantiate it.
386 osLayer = (IRadSpeech)ci.Invoke(new object[0]);
391 throw new Exception("ERROR in Speech OS Layer: " + ex.Message);
397 catch (BadImageFormatException)
400 throw new Exception("Speech OS Layer bad format " + loadfilename);
402 catch (ReflectionTypeLoadException ex)
404 // Out of date or dlls missing sub dependencies
405 throw new Exception("ERROR loading Speech OS Layer " + loadfilename + ", " + ex.Message);
409 throw new Exception("Exception loading Speech OS Layer " + loadfilename + ", " + ex.Message);