Last updated on May 3, 2022
Ever since seeing the first “Iron Man” movie, I wanted a Jarvis program.
This is the start of a program I named “Valet”. Right now, it greets me (using a greeting based on the time of day), and tells me the current temperature – along with today’s low and high temperatures.
Because I’m focusing on finishing my Fluent Interface Creator program, I’m not going to work on this program for a while. However, I wanted to put it out on GitHub, in case anyone wants to see how to do text-to-speech, in a C# program.
How to configure, and use, Valet
Set up connection to weather information
If you want to use this program to tell you the weather, you will need an API key for the Open Weather Map service. It’s a free service.
Inside the Valet solution, in the Engine project, place that key into the Application.settings file, for the OWMAPIKey.
Personalize the program
Inside the Valet project, edit the Settings.settings file.
I use PostalCode and CountryCode, as parameters for the weather information. You can retrieve the data by other methods: city name, city ID, or latitude and longitude. More information is available here: https://openweathermap.org/current#one
If you use different parameters, you will need to modify the Engine\WeatherReader.cs class. You will need to change the calling URL, the parameters passed to the functions, and the code that parses the XML response inside the RetrieveCurrentWeatherForecast function.
You can also change the API request and response to use JSON, if you don’t want to use XML.
Other settings
“TemperatureUnit” accepts “Fahrenheit” or “Celsius”.
“VoiceName” accepts any installed voice. To see what voices are available, you can set a break point after line 28, of \Valet\MainWindow.xaml.cs, and see the values in the “voices” variable.
If the VoiceName does not match an installed voice, the program will try to find a voice that matches the gender stored in “VoiceGender”, in Settings.settings.
“VoiceRate” of 1 is standard. A larger number will make the voice talk faster.
I set “VoiceVolume” to 100. That works on my computer.
How to use the Speech library in C#
Step 1: In your UI project, add a reference to the System.Speech assembly.
In the project, right-click on “References”, select “Add Reference…”, select “Assemblies”, select “Framework”, and check the box next to “System.Speech”.
Step 2: In the class you want to “speak”, add “using System.Speech.Synthesis;”, to use the classes in the Speech assembly.
In this program,I have a class-level variable “_speechSynthesizer”. Its datatype is SpeechSynthesizer. This is the object that will do the “speaking” in the program.
Next, I set up the voice settings inside the InitializeVoice() function. The configuration values are stored in a Settings file, to make them easier to change.
The Speak() function calls the SpeakAsync() function on the _speechSynthesizer object. If you use Speak(), it will lock up interaction with the program, until it is done speaking the text passed to Speak(). SpeakAsync() will let the user continue interacting with the program.
MainWindow.xaml.cs
using System; using System.Collections.ObjectModel; using System.Linq; using System.Speech.Synthesis; using System.Windows; using Engine; using Engine.Services; using Engine.Utilities; namespace Valet { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { private readonly SpeechSynthesizer _speechSynthesizer = new SpeechSynthesizer(); public MainWindow() { InitializeComponent(); InitializeVoice(); } private void InitializeVoice() { ReadOnlyCollection<InstalledVoice> voices = _speechSynthesizer.GetInstalledVoices(); if(!string.IsNullOrWhiteSpace(Settings.Default.VoiceName) && voices.Any(v => v.VoiceInfo.Name == Settings.Default.VoiceName)) { _speechSynthesizer.SelectVoice(Settings.Default.VoiceName); } else { if(Settings.Default.VoiceGender.Equals("Male", StringComparison.CurrentCultureIgnoreCase)) { _speechSynthesizer.SelectVoiceByHints(VoiceGender.Male); } else if(Settings.Default.VoiceGender.Equals("Female", StringComparison.CurrentCultureIgnoreCase)) { _speechSynthesizer.SelectVoiceByHints(VoiceGender.Female); } else { _speechSynthesizer.SelectVoiceByHints(VoiceGender.Neutral); } } _speechSynthesizer.Rate = Settings.Default.VoiceRate; _speechSynthesizer.Volume = Settings.Default.VoiceVolume; } private void Greeting_OnClick(object sender, RoutedEventArgs e) { SpeakGreeting(); } private void CurrentTemperature_OnClick(object sender, RoutedEventArgs e) { SpeakCurrentTemperature(); } private void TodaysLowAndHighTemperatures_OnClick(object sender, RoutedEventArgs e) { SpeakTodaysLowAndHighTemperatures(); } private void CompleteStatus_OnClick(object sender, RoutedEventArgs e) { SpeakGreeting(); SpeakCurrentTemperature(); SpeakTodaysLowAndHighTemperatures(); } private void SpeakGreeting() { Speak(GreetingBuilder.GetCurrentGreetingFor(Settings.Default.UserName)); } private void SpeakCurrentTemperature() { Speak(WeatherReader.GetCurrentForecast(Settings.Default.PostalCode, Settings.Default.CountryCode, (TemperatureUnit) Enum.Parse(typeof(TemperatureUnit), Settings.Default.TemperatureUnit))); } private void SpeakTodaysLowAndHighTemperatures() { Speak(WeatherReader.GetTodaysLowAndHighTemperatures(Settings.Default.PostalCode, Settings.Default.CountryCode, (TemperatureUnit) Enum.Parse(typeof(TemperatureUnit), Settings.Default.TemperatureUnit))); } private void Speak(string message) { _speechSynthesizer.SpeakAsync(message); } } }
Future ideas
Once I finish some other projects, I want to look for other APIs I can use. I found one for the local highway system. It can warn me of accidents and let me know expected travel times.
It would be a good idea to create an interface for API connectors, and have WeatherForecast (and all the future code) implement it. That will make it easier to plug in future features.
Another idea is to create a more-sophisticated caching routine for API calls – one that is configurable and allows caching multiple calls to an API, but with different parameters.
Licensing
The source code for this project is under the MIT license. Do what you want with it.
If you expand it, and want to share your changes, please submit a pull request on GitHub. I haven’t worked with pull requests there, but I am definitely open to enhancements and improvements.
Source code: TO BE UPDATED (creating new .NET 6 version)
Open Weather Map API information: https://openweathermap.org/api
This is very nice!
Thank you.
Spanish, Catalan voices ?
Install the Microsoft Catalan/Spanish language pack (https://support.microsoft.com/en-us/help/22797/windows-10-narrator-tts-voices) or buy the Catalan/Spanish voice (Nuria/Sara) from https://www.cereproc.com/en/storesapi.
The voice will be available in the “voices” variable in the InitializeVoice() function (see the “Helena” voice in the screenshot below).
Huh… Why I am thinking not about JARVIS but CABAL (C&C series) ? 😀 OK anyhow thanks for this, it looks and sounds awesome. (good training too)
Thanks. I never played Command & Conquer. I just wanted to make sure it wasn’t Holly, from Red Dwarf. 🙂