Export (0) Print
Expand All

WPF Text To Speech UI

Microsoft Robotics

Glossary Item Box

Microsoft Robotics Developer StudioSend feedback on this topic

WPF Text To Speech UI

The WPF Text-to-Speech UI sample demonstrates how to use a Windows Presentation Foundation (WPF) window as an interface to the Text-to-Speech (TTS) service.

This sample is provided in the C# language. You can find the project files for this sample at the following location under the Microsoft Robotics Developer Studio installation folder:

Sample location
Samples\UX\WpfTextToSpeechUI

Contents:

Prerequisites

Hardware

This sample requires no special hardware other than a sound card in the PC.

Software

This sample is designed for use with Microsoft Visual C#. You can use:

  • Microsoft Visual C# Express Edition
  • Microsoft Visual Studio Standard, Professional, or Team Edition.

You will also need the Microsoft Text-to-Speech (TTS) engine. This is included with Vista and Windows XP.

Setting Up the Sample

Before you run this sample, you should confirm that the Text-to-Speech system is working on your PC. To verify that it is installed and working, open the Control Panel. On Vista you should find a Text to Speech applet. On Windows XP it is simply called Speech. Open this applet and click on the Preview Voice button. You should hear the text that is in the preview textbox spoken through your speakers or headset.

Running the Sample

To start the WPFTextToSpeechUI service, start a node by using following command from the Microsoft Robotics Developer Studio Command Prompt:

Console
dsshost /p:50000 /t:50001 /m:"samples\config\WPFTextToSpeechUI.manifest.xml"

A WPF window should be displayed that looks like Figure 1. Initially the textbox will be empty, but Figure 1 shows a long text message being used to test the service.

WPF UI

WPF UI - Speaking some text

The large number (19) is the viseme that corresponds to part of the text. A viseme is similar to a phoneme, but it is an index to the facial expression that corresponds to a person making a particular sound. Visemes are intended to assist with displaying facial expressions if the application displays a character visualization. Visemes do not correspond one-to-one to phonemes, and they are not completely standardized. In this example, the visemes are displayed from notification messages received from the TTS service and the purpose is to show that data in the window can be updated dynamically.

How it works

Note that this is an advanced sample and it is assumed that you are already familiar with writing RDS services and also developing WPF applications.

Open WpfTextToSpeechUI.sln in Visual Studio. Expand the references in the Solution Explorer. Notice that there is a reference to Microsoft.Ccr.Adapter.Wpf. This adapter is necessary for a WPF window to run correctly under the CCR in a Robotics Developer Studio environment. At the top of the WpfTextToSpeechUI.cs there is a using statement:

C#
using ccrwpf = Microsoft.Ccr.Adapters.Wpf;

Also in the references you should see several other DLLs that are required by WPF, such as PresentationCore.

Listed as source files in the solution are TextToSpeechUI.xaml and TextToSpeechUI.xaml.cs. The XAML file contains the definition of the window, and the C# file contains the code that executes in the context of the window. This sample does not explain how to create WPF window code. It is assumed that you are already familiar with WPF. These files can be created in Visual Studio and added to a RDS service project.

The service must subscribe to the TTS service for viseme notifications:

C#
// subscribe to TTS service to receive viseme notifications
var subscribe = _ttsPort.Subscribe(_ttsNotifications, typeof(texttospeech.VisemeNotify));

yield return (Choice)subscribe;

var fault = (Fault)subscribe;
if (fault != null)
{
    LogError(fault);
    StartFailed();
    yield break;
}

The service also needs to set up a handler for the TTS notifications:

C#
// activate a handler for viseme notifications
MainPortInterleave.CombineWith(
    Arbiter.Interleave(
        new TeardownReceiverGroup(),
        new ExclusiveReceiverGroup(
            Arbiter.Receive<texttospeech.VisemeNotify>(true, _ttsNotifications, VisemeNotifyHandler)
        ),
        new ConcurrentReceiverGroup()
    )
);

Subscribing to another service is a standard part of service initialization so it is not discussed here.

In addition, the service must create a WPF adapter:

C#
// create WPF adapter
_wpfServicePort = ccrwpf.WpfAdapter.Create(TaskQueue);

var runWindow = _wpfServicePort.RunWindow(() => new TextToSpeechUI(this));
yield return (Choice)runWindow;

var exception = (Exception)runWindow;
if (exception != null)
{
    LogError(exception);
    StartFailed();
    yield break;
}

// need double cast because WPF adapter doesn't know about derived window types
_userInterface = (Window)runWindow as TextToSpeechUI;

The code above creates a WPF service port, and then creates a WPF window using the RunWindow method with the TextToSpeechUI class which is declared in TextToSpeechUI.xaml.cs. Notice that a pointer to the service is passed to the new WPF window with this. The WPF window code can then call back into the service when necessary.

Now you can enter text into the textbox in the WPF window and click on the "Say it!" button.

When the "Say it!" button is pressed, the WPF window code calls the service to send the message from the textbox to the TTS service. The following snippet shows the code in the WPF window that handles button clicks, but the SayTextFromUi method in the service is omitted for brevity.

C#
/// <summary>
/// This method is called when the "Say it!" button is pressed.
/// </summary>
private void Button_Click(object sender, RoutedEventArgs e)
{
    // let the service do it
    if (_service != null)
    {
        _service.SayTextFromUi(_text.Text);
    }
}

Saying some text causes notification messages to arrive from the TTS service with viseme information. The VisemeNotifyHandler in the service passes the viseme messages to the WPF window by setting the Viseme property via the Invoke method of the WPF service port:

C#
/// <summary>
/// Handle viseme notifications from the TTS service
/// </summary>
/// <param name="viseme">notification</param>
void VisemeNotifyHandler(texttospeech.VisemeNotify viseme)
{
    // The notification handler executes outside context of the WPF dispatcher.
    // Because we want to change a property that causes changes to the UI we need
    // to set it in the WPF dispatcher.
    _wpfServicePort.Invoke(() =>
    {
        _userInterface.Viseme = viseme.Body;   
    });
}

The WPF window code processes the message in the set method on the Viseme property and it appears on the screen:

C#
/// <summary>
/// Gets or sets the most recent viseme that was received from the TTS service
/// </summary>
public VisemeNotification Viseme
{
    get { return (VisemeNotification)GetValue(VisemeProperty); }
    set { SetValue(VisemeProperty, value); }
}

// Using a DependencyProperty as the backing store for Viseme.  This enables animation, styling, binding, etc...
/// <summary>
/// VisemeProperty
/// </summary>
public static readonly DependencyProperty VisemeProperty =
    DependencyProperty.Register("Viseme", typeof(VisemeNotification), typeof(TextToSpeechUI));

This is a brief introduction to using WPF with RDS. You might need to spend some time examining the sample and running in the debugger it to understand how it works.

Related Services

Please review the Service Tutorials if you have difficulty in understanding the service code in this sample. WPF is not covered in this documentation set because this is a separate product.

Summary

 

 

© 2012 Microsoft Corporation. All Rights Reserved.

Show:
© 2014 Microsoft