Search code examples
c#sapitext-to-speech

Get text content from SAPI SpeakStarted event


This is a Microsoft speech api related questions:

The idea it's that when SpeechSynthesizer triggers SpeakStarted event, I would like to know what the SpeechSynthesizer is going to say within this event. So I can synchronize the speech and GUI display.

Code:

SpeechSynthesizer _reader = new SpeechSynthesizer();


_reader.SpeakStarted += new EventHandler<SpeakStartedEventArgs>(_reader_SpeakStarted);


  void _reader_SpeakStarted(object sender, SpeakStartedEventArgs e)
        {
            // now _speak started!
            Console.WriteLine("_reader_SpeakStarted\t" + e.ToString());
           // string textContent=????
          //  updateGUI(textContent);

        }


_reader.SpeakAsync("Hello world A B C"); //asynchronize method!!!
_reader.SpeakAsync("Hello world B C D");
_reader.SpeakAsync("Hello world C D E");

Problem:

Within SpeakStarted event one couldn't tell the text content to be spoken.

It's weird that the "Hello world x x x" does appear in the arguments, if you put a break point there, but there is no way to access it.

If someone could kindly help me, I would very much appreciate it.

.Net 4, windows 7 64 bits, VS2010


Solution

  • The prompt object doesn't always contain text which is why it is not exposed as a public property. The same object can also be constructed using marker-up or an audio file. The best way do execute this would be to store the values you are passing into the SpeakAsync method as global arguments which can be passed into the UI when you want to display them.

    private static string _spokenWords;

        static void Main(string[] args)
        {
    
            SpeechSynthesizer _reader = new SpeechSynthesizer();
    
            _reader.SpeakStarted += ReaderSpeakStarted;
    
            _spokenWords = "Hello world A B C";
            _reader.SpeakAsync(_spokenWords); //asynchronize method!!! 
    
    
        }
    
    
        static void ReaderSpeakStarted(object sender, SpeakStartedEventArgs e)
        {
            // now _speak started! 
            Console.WriteLine("_reader_SpeakStarted\t" + _spokenWords);
    
            // string textContent=???? 
            //  updateGUI(textContent); 
    

    You can also look at using reflection to break out the private text field in the prompt object to access the value. Although this is going to be more resource intensive and not necessarily as stable:

    static void Main(string[] args) {

            SpeechSynthesizer _reader = new SpeechSynthesizer();
    
            _reader.SpeakStarted += ReaderSpeakStarted;
    
            _reader.SpeakAsync("Hello world A B C"); //asynchronize method!!! 
            _reader.SpeakAsync("Hello world B C D"); 
            _reader.SpeakAsync("Hello world C D E"); 
    
        }
    
    
        static void ReaderSpeakStarted(object sender, SpeakStartedEventArgs e)
        {
            // now _speak started! 
            var spokenWord = GetTextFieldValue(e.Prompt);
            Console.WriteLine("_reader_SpeakStarted\t" + spokenWord);
    
            // string textContent=???? 
            //  updateGUI(textContent); 
    
        }
    
        private static string GetTextFieldValue(Prompt p)
        {
            var text = typeof(Prompt).GetField("_text", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(p);
            return (string)(text.GetType() == typeof(String) ? text : string.Empty);
        }