1. SpeechSynthesizer概述
核心功能
System.Speech.Synthesis命名空间中的SpeechSynthesizer类提供了文本转语音(TTS)功能,主要特点包括:
- 支持多种语音和语言
- 可调节语速、音量和音调
- 支持同步和异步语音合成
- 提供SSML(语音合成标记语言)支持
技术规格
系统要求
- Windows操作系统
- 已安装语音合成引擎
- .NET Framework 4.8+ 或 .NET Core 3.1+
2. 基础使用示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| using System; using System.Speech.Synthesis;
namespace SampleSynthesis { class Program { static void Main(string[] args) { using (var synth = new SpeechSynthesizer()) { synth.SetOutputToDefaultAudioDevice(); synth.Rate = -2; synth.Volume = 80; synth.Speak("欢迎使用WPF语音合成功能"); synth.SpeakAsync("这段文本将异步朗读"); } } } }
|
关键方法说明
SetOutputToDefaultAudioDevice()
: 设置输出到默认音频设备
Speak()
: 同步朗读文本
SpeakAsync()
: 异步朗读文本
SelectVoice()
: 选择特定语音
GetInstalledVoices()
: 获取已安装语音列表
注解
创建新 SpeechSynthesizer 对象时,它使用默认的系统语音。 若要将 配置为 SpeechSynthesizer 使用已安装的语音合成 (文本转语音) 语音之一,请使用 SelectVoice 或 SelectVoiceByHints 方法。 若要获取有关已安装哪些语音的信息,请使用 GetInstalledVoices 方法和 VoiceInfo 类。
此类还提供对语音合成的以下方面的控制:
- 若要配置 SpeechSynthesizer 对象的输出,请使用 SetOutputToAudioStream、 SetOutputToDefaultAudioDevice、 SetOutputToNull和 SetOutputToWaveFile 方法。
- 若要生成语音,请使用 Speak、 SpeakAsync、 SpeakSsml或 SpeakSsmlAsync 方法。 SpeechSynthesizer可以从文本、Prompt或 PromptBuilder 对象或语音合成标记语言 (SSML) 版本 1.0 生成语音。
- 若要暂停和恢复语音合成,请使用 Pause 和 Resume 方法。
- 若要添加或删除词典,请使用 AddLexicon 和 RemoveLexicon 方法。 SpeechSynthesizer可以使用一个或多个词典来指导其字词的发音。
- 若要修改语音输出的传递,请使用 Rate 和 Volume 属性。
遇到提示中的某些功能时,会 SpeechSynthesizer 引发事件:(BookmarkReached、 PhonemeReached、 VisemeReached和 SpeakProgress) 。
它还引发事件,报告说话操作的开始 (SpeakStarted) 和结束 (SpeakCompleted) 以及语音 (VoiceChange ) 的变化。
3. 完整工具类实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
| using System; using System.Speech.Synthesis; using System.Collections.ObjectModel;
namespace WPF.Utilities { public sealed class SpeakerHelper : IDisposable { private static readonly Lazy<SpeakerHelper> _instance = new Lazy<SpeakerHelper>(() => new SpeakerHelper()); private readonly SpeechSynthesizer _synth = new SpeechSynthesizer(); private readonly object _lock = new object(); private bool _disposed;
public static SpeakerHelper Instance => _instance.Value;
private SpeakerHelper() { _synth.SetOutputToDefaultAudioDevice(); _synth.Rate = -2; _synth.Volume = 80; foreach (var voice in _synth.GetInstalledVoices()) { if (voice.VoiceInfo.Culture.Name.StartsWith("zh")) { _synth.SelectVoice(voice.VoiceInfo.Name); break; } } }
public ReadOnlyCollection<InstalledVoice> GetInstalledVoices() { lock (_lock) { return _disposed ? throw new ObjectDisposedException(nameof(SpeakerHelper)) : _synth.GetInstalledVoices(); } }
public void Speak(string text, int rate = -2) { if (string.IsNullOrWhiteSpace(text)) return;
lock (_lock) { if (_disposed) throw new ObjectDisposedException(nameof(SpeakerHelper));
try { _synth.Rate = Math.Clamp(rate, -10, 10); _synth.Speak(text); } catch (Exception ex) { Console.WriteLine($"语音合成错误: {ex.Message}"); } } }
public void SpeakAsync(string text, int rate = -2) { if (string.IsNullOrWhiteSpace(text)) return;
lock (_lock) { if (_disposed) throw new ObjectDisposedException(nameof(SpeakerHelper));
try { _synth.Rate = Math.Clamp(rate, -10, 10); _synth.SpeakAsyncCancelAll(); _synth.SpeakAsync(text); } catch (Exception ex) { Console.WriteLine($"异步语音合成错误: {ex.Message}"); } } }
public void SetVolume(int volume) { lock (_lock) { if (_disposed) throw new ObjectDisposedException(nameof(SpeakerHelper)); _synth.Volume = Math.Clamp(volume, 0, 100); } }
public void SaveToWav(string text, string filePath, int rate = -2) { if (string.IsNullOrWhiteSpace(text)) return;
lock (_lock) { if (_disposed) throw new ObjectDisposedException(nameof(SpeakerHelper));
try { _synth.Rate = Math.Clamp(rate, -10, 10); _synth.SetOutputToWaveFile(filePath); _synth.Speak(text); _synth.SetOutputToDefaultAudioDevice(); } catch (Exception ex) { Console.WriteLine($"保存语音文件失败: {ex.Message}"); throw; } } }
public void Dispose() { lock (_lock) { if (!_disposed) { _synth?.Dispose(); _disposed = true; } } } } }
|
工具类使用示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); }
private void SpeakButton_Click(object sender, RoutedEventArgs e) { SpeakerHelper.Instance.Speak("欢迎使用语音合成功能"); SpeakerHelper.Instance.SpeakAsync(txtContent.Text, rate: 0); }
private void SaveButton_Click(object sender, RoutedEventArgs e) { var dialog = new SaveFileDialog { Filter = "WAV文件|*.wav", Title = "保存语音文件" }; if (dialog.ShowDialog() == true) { SpeakerHelper.Instance.SaveToWav( txtContent.Text, dialog.FileName, rate: 1); } } }
|
最佳实践建议
-
资源管理:
- 在应用程序退出时调用
SpeakerHelper.Instance.Dispose()
- 使用
using
语句处理临时实例
-
线程安全:
-
异常处理:
- 方法内部已处理基本异常
- 关键操作仍建议添加try-catch
-
性能考虑:
- 避免频繁创建/销毁实例
- 对于长时间运行的应用,定期检查语音引擎状态
4. 高级功能配置
SSML标记语言支持
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| string ssmlText = @"<?xml version=""1.0""?> <speak version=""1.0"" xmlns=""http://www.w3.org/2001/10/synthesis"" xml:lang=""zh-CN""> <voice name=""Microsoft Huihui Desktop""> <prosody rate=""-2"" volume=""80""> 这是使用SSML标记的文本。 </prosody> <break time=""500ms""/> <prosody pitch=""high"" rate=""-4""> 我可以改变音调和语速。 </prosody> </voice> </speak>";
SpeakerHelper.Instance.SpeakSsml(ssmlText);
|
自定义语音选择
1 2 3 4 5 6 7 8 9 10 11 12
| var voices = SpeakerHelper.Instance.GetInstalledVoices();
foreach (var voice in voices) { if (voice.VoiceInfo.Culture.Name == "zh-CN") { _synth.SelectVoice(voice.VoiceInfo.Name); break; } }
|
音频格式配置
1 2 3
| _synth.SetOutputToWaveFile("output.wav", new SpeechAudioFormatInfo(32000, AudioBitsPerSample.Sixteen, AudioChannel.Mono));
|
5. 性能优化建议
内存管理
- 避免频繁创建/销毁SpeechSynthesizer实例
- 对长时间运行的应用,定期检查内存使用
- 使用
using
语句或显式调用Dispose()
并发处理
- 异步方法(
SpeakAsync
)适合UI应用
- 同步方法(
Speak
)适合后台服务
- 避免同时播放多个语音流
资源释放
1 2 3 4 5
| using (var synth = new SpeechSynthesizer()) { synth.Speak("这段语音播放后会自动释放资源"); }
|
6. 常见问题解答
Q1: 为什么没有中文语音?
- 检查是否安装了中文语音包
- 在Windows设置中添加中文TTS语音
- 运行
SpeakerHelper.Instance.GetInstalledVoices()
确认
Q2: 语音输出不清晰?
- 尝试调整语速(
Rate
属性)
- 检查音频设备设置
- 使用SSML优化发音
Q3: 如何跨平台使用?
- Windows: 使用System.Speech.Synthesis
- 跨平台: 考虑Azure Cognitive Services语音SDK
- 移动端: 使用各平台原生TTS API
7. 参考文献