Converting Text to Speech in a C# WPF Application

Adding an option for converting text to speech in an application can come in handy in a variety of different situations. So today in this short but useful tutorial, I’ll teach you how to build a text-to-speech sample using Microsoft Speech API (SAPI) with Windows Presentation Foundation (WPF). This application will allow you to open a text file in a RichTextBox control and then read out the text present in the text file.

Lets first start with a brief intro on Microsoft Speech API (SAPI) version 5.3. It is basically a managed API that allows developers to write speech enabled applications in .NET Framework 3.0 and above and is already integrated with Windows Vista. The API resides in System.Speech.Dll assembly.

To begin, create a new WPF Application project in Visual Studio 2008. You must add System.Speech.Dll assembly reference to your project. To do this, go to Solution Explorer, right click on your project name, select Add Reference and then look into .NET tab where you will find System.Speech, select it and press OK.

Now, you can import all namespaces of System.Speech in your application. For now, just import System.Speech.Synthesis, System.Windows.Form and System.IO.

Speech Synthesis is a process of converting text to speech. For this, we will use SpeechSynthesizer() class, its methods and properties in our application to accomplish our goal.

The speech synthesizer class has got four properties: Rate, State, Volume and Voice that are used to get and set rate, state, volume and voice of the speech. The value of rate is between -10 to 10 where as Volume varies from 0 to 100.

The Speak() and SpeakAsync() methods are used to speak synchronously and asynchronously respectively.

SpeechSyntehsizer foo = new SpeechSynthesizer();
foo.Speak("Hello WPF."); 

So, on the basis of above class methods and properties I will start building this app. First of all, we will write the XAML code for the design elements and the controls.

<Window x:Class="TextToSpeech.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Text to Speech" Height="380" Width="580" 
AllowsTransparency="False" WindowStyle="SingleBorderWindow">
    <Grid >
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="286*" />
            <ColumnDefinition Width="272*" />
        </Grid.ColumnDefinitions>
        <Button Height="23" HorizontalAlignment="Right" Margin="0,0,12,8" 

Name="SpeechButton" VerticalAlignment="Bottom" Width="101"

Click="SpeechButton_Click" Grid.Column="1">

            Speak
        </Button>
        <RichTextBox Margin="0,45,0,67" Name="richTBoxContent" 
Background="WhiteSmoke" 
                     Foreground="Black" Grid.ColumnSpan="2" />
        <Button Height="23" Margin="0,10,12,0" 

Name="OpenTextFileButton" VerticalAlignment="Top"

Click="OpenTextFileButton_Click" Grid.Column="1" HorizontalAlignment="Right"

Width="110">

            Open a Text File
        </Button>
        <TextBox Height="23" Margin="9,10,130,0" Name="FileNameTextBox" 
VerticalAlignment="Top" Grid.ColumnSpan="2" />
        <ComboBox Height="23" Margin="90,0,76,30" 
                  Name="comboBoxVolume" VerticalAlignment="Bottom" 
SelectedIndex="4" >
            <ComboBoxItem>10</ComboBoxItem>
            <ComboBoxItem>20</ComboBoxItem>
            <ComboBoxItem>30</ComboBoxItem>
            <ComboBoxItem>40</ComboBoxItem>
            <ComboBoxItem>50</ComboBoxItem>
            <ComboBoxItem>60</ComboBoxItem>
            <ComboBoxItem>70</ComboBoxItem>
            <ComboBoxItem>80</ComboBoxItem>
            <ComboBoxItem>90</ComboBoxItem>
            <ComboBoxItem>100</ComboBoxItem>
        </ComboBox>
        <Label Height="28" HorizontalAlignment="Left" Margin="32,0,0,25" 

Name="labelVolume" VerticalAlignment="Bottom" Width="51">

Volume:</Label>

        <ComboBox Height="23" Margin="22,0,130,30" 

Name="comboBoxRate" VerticalAlignment="Bottom"

SelectedIndex="2" Grid.Column="1">

            <ComboBoxItem>-10</ComboBoxItem>
            <ComboBoxItem>-5</ComboBoxItem>
            <ComboBoxItem>0</ComboBoxItem>
            <ComboBoxItem>5</ComboBoxItem>
            <ComboBoxItem>10</ComboBoxItem>
 
        </ComboBox>

<Label Height="28" Margin="268,0,249,25" Name="labelRate"

VerticalAlignment="Bottom" Grid.ColumnSpan="2">

            Rate:</Label>
    </Grid>
</Window>
 

The above XAML code will generate a windows which will look something like this:

Now, we will create a event handler of SpeechButton and a method to convert the contents of rich text box to a string.

 // Setting up the properties to speak.
        private void SpeechButton_Click(object sender, RoutedEventArgs e)
        {
            int Volume;
            int Rate;
            ComboBoxItem volumeItem = (ComboBoxItem)comboBoxVolume.
Items[comboBoxVolume.SelectedIndex];
            Volume = Convert.ToInt32(volumeItem.Content.ToString());
            ComboBoxItem rateItem = (ComboBoxItem)comboBoxRate.
Items[comboBoxRate.SelectedIndex];
            Rate = Convert.ToInt32(rateItem.Content.ToString());
            foo.Volume = Volume;
            foo.Rate = Rate;
            foo.Speak(ConvertRichTextToString());
        }
 
 
 // Convert the contents of rich text box to string.
        private string ConvertRichTextToString()
        {
            TextRange textRange = new TextRange(richTBoxContent.
Document.ContentStart, richTBoxContent.Document.ContentEnd);
            return textRange.Text;
        }

And now, we write the following code to open up a text file and load its contents into the rich text box.

// Open any text file.
        private void OpenTextFileButton_Click(object sender, RoutedEventArgs e)
        {
            OpenFileDialog fileDialog = new OpenFileDialog();
            fileDialog.InitialDirectory = @"C:\";
            fileDialog.Filter = "Text Files (*.txt)|*.txt";
            fileDialog.RestoreDirectory = true;
            if (fileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                LoadFile(fileDialog.FileName);
                FileNameTextBox.Text = fileDialog.FileName;
            }
        }
 
        // Load text file to the rich text box.
        private void LoadFile(string File)
        {
            TextRange range;
            FileStream fileStream;
            if (System.IO.File.Exists(File))
            {
                range = new TextRange(richTBoxContent.Document.ContentStart,
 richTBoxContent.Document.ContentEnd);
                fileStream = new FileStream(File, FileMode.OpenOrCreate);
                range.Load(fileStream, System.Windows.DataFormats.Text);
                fileStream.Close();
            }
        }

Thats it! We are done. Feel free to post any queries you have related to the code posted above.