Back in February, I wrote a blog post showing you how to, using Silverlight 4 OOB (out of browser) with elevated trust, access system devices; including how to use the SAPI.SpVoice API through the new Silverlight 4 COM Interop feature to implement text to speech. The biggest problem to that approach is that it will only work on the Windows platform.

So, I started thinking to myself, that the real purpose behind Text-To-Speech isn’t just the cool factor, but it is accessibility for impaired users.  I have been doing web development for nearly a decade, and I have always been conscious about web users with impairments that may make viewing or navigating my websites more difficult.  Text-To-Speech isn’t very helpful if it will only work in a fully trusted out of browser Silverlight 4 application on a Windows machine. So instead, lets create a Text-To-Speech solution that will work inside the browser, on any browser, on any machine.  And heck, I want this to be in Silverlight 3.

Lets get started by creating a new Silverlight 3 application, and be sure to include a web project as well.

create new silverlight project

Right click the web project and add a reference to the System.Speech.dll.

add reference to System.Speech

Next, lets right click the web project and click Properties –> Web and set the specific port number to your liking, I set mine to 1914. This will come in handy when we create our WCF service.

set port number

Now we need to create the WCF service that will take our text and send it back as a WAV stream, so go ahead and right click the web project and select “Add New Item”.  From this list add a new WCF service and name is SpeechService.  Now this is important; when you have created your WCF service an endpoint is created for you in the Web.config file.  You need to change the binding of the endpoint to basicHttpBinding.  Silverlight only works using basicHttpBinding, and will not create the ServiceReferences.ClientConfig file properly if you do not do this.

change endpoint binding  to basicHttpBinding

Now, lets add an OperationCOntract to your WCF services interface called Speak that returns a byte[], and takes a string parameter.

[ServiceContract]
public interface ISpeechService
{
    [OperationContract]
    byte[] Speak(string textToSay);
}

The implementation of this method will look like the following:

public byte[] Speak(string textToSay)
{
    SpeechSynthesizer ss = new SpeechSynthesizer();
    MemoryStream ms = new MemoryStream();
    ss.SetOutputToWaveStream(ms);
    ss.Speak(textToSay);
    return ms.ToArray();
}

The next thing we need to do is create our UI in Silverlight.  Here is what mine looks like.

<Grid x:Name="LayoutRoot">
<StackPanel>
    <TextBox x:Name="_txtTextToSay" />
    <Button Content="Speak To Me" Click="Button_Click" />
    <MediaElement x:Name="_audioPlayer"/>
</StackPanel>
</Grid>

Create an event handler for your button.  Next add a service reference to your SpeechService in your web project.

add service reference to your speechservice

The next part is somewhat complicated and time consuming.  You have to write your own WAVV decoding class that takes the byte array that is return from the service and converts it to a System.Windows.Media.MediaStreamSource. Luckily for you, I already did this for you with the help of some resources on MSDN.  In the button’s event handler add this code:

private void Button_Click(object sender, RoutedEventArgs e)
{
    SpeechServiceClient client = new SpeechServiceClient("BasicHttpBinding_ISpeechService");
    client.SpeakCompleted += (o, ea) =>
        {
            WavMediaStreamSource audioStream = new WavMediaStreamSource(new MemoryStream(ea.Result));
            _audioPlayer.SetSource(audioStream);
        };
    client.SpeakAsync(_txtTextToSay.Text);
}

Basically what this does is uses the WavMediaStreamSource class I created that inherits from MediaStreamSource, takes the byte[] returned from the SpeechService and converts it back to a stream, then is passes it off to my WAV decoding classes, which is used as the source for the MediaElement responsible for playing the audio.

All that is next is to build your solution and start making your Silverlight applications more accessible.

Download the Source

Brian Lagunas

View all posts

13 comments

  • Im thinking of using the core of this for a free timer im putting out in the wild.

    do i need to adjust the localhost to something public therefore? and punch open a port for the service?

    Many thanks.

    • Yes, you must change to service endpoints in both the web.config and the ServiceReferences.ClientConfig in the Silverlight project to match your environment. I don’t know how you have your solution setup, but to avoid having to worry about cross-domain policies, place your service in the hosting web project (the project that hosts your Silverlight client).

  • Dear Brian,
    I am working with Silverlight 3 and Visual Studio 2008 and I am using the same stuff that you are presented here.
    “Speech to Text” is working perfectly, when I started my project inside Visual Studio.
    BUt when I published web site on a host. Security error appears.
    The reason is System.Speech has to be called only from full trust assembly.
    I made strong name( sn.exe)to my assembly and put it in GAC (gacutil.exe).
    But still I have error.
    I was greatly appreciated ,if you can comment my situation.
    Thank you,
    Nina.

      • Dear Drian,
        Thank you for your response.
        You can see may website at : http://www.mtklicensing.com:3536
        I made a separate page to check only sound as : http://www.mtklicensing.com:3537
        If you type some text and than make a text selection and than click button “Start Sound”
        web service will work.
        All texts, please see here:

        C L I E N T __________________________________________
        private void requestToTheServer(string text)
        {
        try{
        uri = new Uri(Application.Current.Host.Source, “../SpeechServiceTrust.svc”);
        clientSpeech = new OnlySpeech.SpeechServiceTrustClient(“CustomBinding_SpeechServiceTrust”, uri.AbsoluteUri);

        clientSpeech.CreateWavStreamWCFCompleted+=new EventHandler(clientSpeech_CreateWavStreamWCFCompleted);
        clientSpeech.CreateWavStreamWCFAsync(text);

        }
        catch (Exception ee1)
        {
        MessageBox.Show(“exception=” + ee1.Message);
        if(ee1.InnerException!=null) MessageBox.Show(“inner=” + ee1.InnerException.Message);
        }
        }

        void clientSpeech_CreateWavStreamWCFCompleted(object sender, SpeechService.OnlySpeech.CreateWavStreamWCFCompletedEventArgs e)
        {
        if (e.Result == null) MessageBox.Show(“null”);

        byte[] bbs=e.Result;
        char[] ccs=new char[bbs.Length];
        for(int i=0;i<bbs.Length;i++)
        {
        ccs[i]=(char)bbs[i];
        }
        string resh=new string(ccs);
        MessageBox.Show(resh);
        // SpeechBytes = e.Result;
        clientSpeech.CreateWavStreamWCFCompleted -= new EventHandler(clientSpeech_CreateWavStreamWCFCompleted);

        }

        S E R V E R ________________________ service method

        public byte[] getSpeechStream(string text)
        {
        try
        {
        SpeechSynthesizer ss = null;
        try
        {
        ss = new SpeechSynthesizer();
        }
        catch (Exception e1)
        {
        string mes = “”;
        if (e1.InnerException != null) mes = e1.InnerException.Message;
        mes += “____1______” + e1.Message;

        byte[] a = System.Text.Encoding.GetEncoding(“iso-8859-1”).GetBytes(mes);
        return a;
        }
        MemoryStream ms = new MemoryStream();
        try
        {
        ss.SetOutputToWaveStream(ms);
        }
        catch (Exception e2)
        {
        string mes = “”;
        if (e2.InnerException != null) mes = e2.InnerException.Message;
        mes += “_____2_____” + e2.Message;

        byte[] a = System.Text.Encoding.GetEncoding(“iso-8859-1”).GetBytes(mes);
        return a;
        }
        ss.Speak(text);
        return ms.ToArray();
        }
        catch (Exception ee)
        {
        string mes = “”;
        if (ee.InnerException != null) mes = ee.InnerException.Message;
        mes += “_____3_____” + ee.Message;

        byte[] a = System.Text.Encoding.GetEncoding(“iso-8859-1”).GetBytes(mes);
        return a;
        }
        }

        —————————–
        I really appreciate your attantion.
        Thank you,
        Nina

  • Dear Brian,
    I am really Sorry I did spelling mistake in your name and other words in my previous message. Sorry.
    If you will be so kind and launch reference with my speech page as
    http://www.mtklicensing.com:3537/
    Enter some text and click button “start”.
    You will see message :
    ——–3——– No voice installed on the system or none available with the current security settings.

    This message was catched by try with number 3 ( please see a code in previous message)
    It means exception happend in this line :
    SpeechSynthesizer ss = null;

    My server is Windows Server 2003
    In internet nobody discuss problem with security and speech.
    Please tell me , do you know web site, where speech as you described in article works.
    If you do , could you please sent me information how properly to make publishing on a host
    such speech service application.
    Thank you,
    Nina
    P.S. Please remember, this application perfectly work inside Visual Studio.

    • SpeechSynthesizer.SetOutputToWaveFile Method requires full trust for the immediate caller. This member cannot be used by partially trusted code. For more information, see http://msdn.microsoft.com/en-us/library/8skskf63.aspx

      You can also try to set the IIS Application Pools->Set Application Pools Defaults configuration for all Application Pools->Load User Profile from false to true.

      Good luck.

      • Dear Brian,
        It is so nice that you are responding to me.
        I saw this suggestion in some forum. And I did this already.
        Unfortunetely, I am getting the same message.
        Thank you so much any way.
        I appreciate.
        N.

  • If you have problems deploying this on a server it is likely that there is a permissions problem. I fixed this by making the application pool run under a local account that had administrative permissions.

    Cheers – Graham

  • i’m using visual studio 2010, but it can’t work since it needs to convert into a newer version… how can i solve this? there’s a lot of files which disables the function of this file.

Follow Me

Follow me on Twitter, subscribe to my YouTube channel, and watch me stream live on Twitch.