Search code examples
c#listboxwin-universal-apptext-to-speech

UWP text to speech from listbox (c#')


I am trying to implement text to speech to read items from a listbox in order. Initially the program would read out all items at the same time overlapping each other. After researching I added a MediaEnded handler but I can only manage to read out the first item only with this implemented. I would appreciate any help.

    private async void ToSpeechButton_Click(object sender, RoutedEventArgs e)
    {
            MediaElement mediaElement = new MediaElement();
            var tsc = new TaskCompletionSource<bool>();
            mediaElement.MediaEnded += (o, f) => { tsc.TrySetResult(true); };
            mediaElement.Play();

        foreach (var item in ListBox.Items)
        {   
            readText(item.ToString());
            await tsc.Task;
        }                

    }


    private async void readText(string mytext)
    {
        MediaElement mediaplayer = new MediaElement();
        using (var speech = new SpeechSynthesizer())
        {
            speech.Voice = SpeechSynthesizer.AllVoices.First(gender => gender.Gender == VoiceGender.Female);
            SpeechSynthesisStream stream = await speech.SynthesizeTextToStreamAsync(mytext);
            mediaplayer.SetSource(stream, stream.ContentType);
            mediaplayer.Play();
        }
    } 

Solution

  • If you read the document of SpeechSynthesizer class, you will find there are two method to convert text to speech. One is what you have used, SynthesizeTextToStreamAsync method, this method asynchronously generate speech output from a string.

    The other one is SynthesizeSsmlToStreamAsync method, and this method asynchronously generate speech output from a string containing Speech Synthesis Markup Language (SSML).

    In this case, we can use SynthesizeSsmlToStreamAsync method to pause the speech between each item for example like this:

    private string allitem;
    
    private void ToSpeechButton_Click(object sender, RoutedEventArgs e)
    {
        foreach (var item in listBox.Items)
        {
            var txt = item as ListBoxItem;
            allitem += txt.Content.ToString() + "<break time='500ms'/>";
        }
        readText(allitem);
    }
    
    private async void readText(string mytext)
    {
        MediaElement mediaplayer = new MediaElement();
        using (var speech = new SpeechSynthesizer())
        {
            speech.Voice = SpeechSynthesizer.AllVoices.First(gender => gender.Gender == VoiceGender.Female);
            string ssml = @"<speak version='1.0' " + "xmlns='http://www.w3.org/2001/10/synthesis' xml:lang='en-US'>" + allitem + "</speak>";
            SpeechSynthesisStream stream = await speech.SynthesizeSsmlToStreamAsync(ssml);
            mediaplayer.SetSource(stream, stream.ContentType);
            mediaplayer.Play();
        }
    }
    

    Combine this <break time='500ms'/> string after each item, the speech will pause for 500ms after each item.