Windows Phone 8 中的 TTS 文本朗读

openkk 12年前

自从 Windows Phone 8 SDK 泄漏后,经过一些研究以及官方文档和示例,我的注意力放到了新的 SpeechSynthesizer 和 InstalledVoices.All 类上。

于是我做了一个示例来试用 WP8 的 TTS 文本朗读,先开始 XAML:

<phone:PhoneApplicationPage      x:Class="TextToSpeechDemo.MainPage"      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"      xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"      xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"      mc:Ignorable="d"      FontFamily="{StaticResource PhoneFontFamilyNormal}"      FontSize="{StaticResource PhoneFontSizeNormal}"      Foreground="{StaticResource PhoneForegroundBrush}"      SupportedOrientations="Portrait" Orientation="Portrait"      shell:SystemTray.IsVisible="True">        <!--LayoutRoot is the root grid where all page content is placed-->      <Grid x:Name="LayoutRoot" Background="Transparent">          <Grid.RowDefinitions>              <RowDefinition Height="Auto"/>              <RowDefinition Height="*"/>          </Grid.RowDefinitions>            <!--TitlePanel contains the name of the application and page title-->          <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">              <TextBlock Text="{Binding Path=LocalizedResources.ApplicationTitle, Source={StaticResource LocalizedStrings}}" Style="{StaticResource PhoneTextNormalStyle}"/>              <TextBlock Text="{Binding Path=LocalizedResources.PageTitle, Source={StaticResource LocalizedStrings}}" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>          </StackPanel>            <!--ContentPanel - place additional content here-->          <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">              <StackPanel>                  <ScrollViewer Height="200">                      <ComboBox HorizontalAlignment="Left" Width="456" Name="voicesComboBox" DisplayMemberPath="Name" />                  </ScrollViewer>                  <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">                      <RadioButton Content="Male" IsChecked="true" Name="MaleRadioButton"/>                      <RadioButton Content="Female"/>                  </StackPanel>                  <TextBox HorizontalAlignment="Left" Height="230" TextWrapping="Wrap" Width="456" Text="I may be a sorry case, but I don't write jokes in base 13." Name="inputTextBox"/>                  <Button Content="Speak to me" HorizontalAlignment="Left" Width="456" Click="SpeakToMe_Click"/>              </StackPanel>          </Grid>      </Grid>  </phone:PhoneApplicationPage>

接下来是 MainPage.xaml 对应的代码,其内容基本上都来自于 WP8 SDK 文档,我增加了一些错误检查和小改进:

using System;  using System.Linq;  using System.Windows;  using Microsoft.Phone.Controls;  using Windows.Phone.Speech.Synthesis;     namespace TextToSpeechDemo  {      public partial class MainPage : PhoneApplicationPage      {          SpeechSynthesizer synth;          // Constructor          public MainPage()          {              InitializeComponent();              voicesComboBox.ItemsSource = new MyLocals().Items();          }             private async void SpeakToMe_Click(object sender, RoutedEventArgs e)          {              if (voicesComboBox.SelectedIndex == -1)              {                  MessageBox.Show("Please select a language.");              }              else              {                  if (string.IsNullOrEmpty(inputTextBox.Text))                  {                      MessageBox.Show("Please enter some text.");                  }                  else                  {                      try                      {                          // Initialize the SpeechSynthesizer object.                          synth = new SpeechSynthesizer();                             var myLocal = (MyLocale)voicesComboBox.SelectedItem;                             // Query for a voice. Results rdered by Gender to ensure the order always goes Female then Male.                          var voices = (from voice in InstalledVoices.All                                        where voice.Language == myLocal.Lcid                                        select voice).OrderByDescending(v => v.Gender);                             // gender: 0 = Female, 1 = Male. Corresponds to the index of the above results.                          int gender = 0;                          if (MaleRadioButton.IsChecked == true) gender = 1; else gender = 0;                             // Set the voice as identified by the query.                          synth.SetVoice(voices.ElementAt(gender));                             // Speak                          await synth.SpeakTextAsync(inputTextBox.Text);                      }                      catch (Exception ex)                      {                          MessageBox.Show(ex.Message);                      }                  }              }          }      }  }

然后是一个帮忙填充 ComboBox 列表语言的类,检查 InstalledVoices.All 显示出 30 条数据。我不知道为什么我无法直接获取,好像是一个 COM 对象跟 LINQ 查询的关系吧。这里我能监测出 12 种语言,我没有深入研究去找另外 3 个。

using System;  using System.Collections.Generic;  using System.Linq;  using System.Text;  using System.Threading.Tasks;     namespace TextToSpeechDemo  {      class MyLocale      {          public MyLocale(string name, string lcid)          {              _name = name;              _lcid = lcid;          }             private string _name;          public string Name          {              get { return _name; }              set { _name = value; }          }             private string _lcid;          public string Lcid          {              get { return _lcid; }              set { _lcid = value; }          }      }         class MyLocals      {          private IList<MyLocale> _myLocals;             public MyLocals()          {              _myLocals = new List<MyLocale>();                 _myLocals.Add(new MyLocale("Chinese Simplified (PRC)", "zh-CN"));              _myLocals.Add(new MyLocale("Chinese Traditional (Taiwan)", "zh-TW"));              _myLocals.Add(new MyLocale("English (United States)", "en-US"));              _myLocals.Add(new MyLocale("English (United Kingdom)", "en-GB"));              _myLocals.Add(new MyLocale("French (France)", "fr-FR"));              _myLocals.Add(new MyLocale("German (Germany)", "de-DE"));              _myLocals.Add(new MyLocale("Italian (Italy)", "it-IT"));              _myLocals.Add(new MyLocale("Japanese (Japan)", "ja-JP"));              _myLocals.Add(new MyLocale("Polish (Poland)", "pl-PL"));              _myLocals.Add(new MyLocale("Portuguese (Brazil)", "pt-BR"));              _myLocals.Add(new MyLocale("Russian (Russia)", "ru-RU"));              _myLocals.Add(new MyLocale("Spanish (Spain)", "es-ES"));          }             public IEnumerable<MyLocale> Items()          {              return (IEnumerable<MyLocale>)_myLocals;          }      }  }

接下来是设置 ID_CAP_SPEECH_RECOGNITION 功能,否则将会出异常:

Windows Phone 8 中的 TTS 文本朗读

下图是程序运行结果,选择其中一个语言并点击按钮将会开始阅读。

Windows Phone 8 中的 TTS 文本朗读

我注意到另外一件事:如果我选择一种非英语的语言让它朗读英语文本,朗读出来的效果是带口音的,例如我选择法语的1,2,3,4,然后用西班牙语来朗读,它发出的声音是西班牙语的法语4,很有趣。

英文原文