.NET Framework 早期版本內建 System.Speech 僅支援英文。若需中文語音合成,需安裝 Microsoft Speech SDK 5.1 及中文語言包 (Tracy/Simplified Chinese),並透過 COM Interop 呼叫 SAPI 物件模型。本文示範完整封裝流程:tlbimp 產生互通組件、單例語音類別、中英文自動切換邏輯、音量/語速控制。
C:\Windows\Speech\ 存在 vtxtauto.tlb (SAPI 類型庫)
# Visual Studio 開發人員命令提示字元
tlbimp.exe "C:\Windows\Speech\vtxtauto.tlb" /namespace:MSTTS /out:MSTTS.dll
產生 MSTTS.dll,加入專案參考即可使用 MSTTS.VTxtAuto 類別。
using System;
using MSTTS; // 互通組件命名空間
namespace Bedlang
{
///
/// SAPI 5.1 語音合成包裝,支援中英文混合自動切換語音引擎
///
public sealed class Speech : IDisposable
{
private static readonly Lazy _instance = new(() => new Speech());
private readonly VTxtAuto _voice;
private bool _disposed;
private Speech()
{
_voice = new VTxtAuto();
_voice.Register("", ""); // 註冊 COM 元件 (若需授權碼請填入)
}
public static Speech Instance => _instance.Value;
/// 設定中文語音引擎 (需安裝中文語言包)
private void SetChineseVoice()
{
// GetVoices 索引 0 通常為系統預設中文語音
_voice.Voice = _voice.GetVoices("", "").Item(0);
}
/// 設定英文語音引擎
private void SetEnglishVoice()
{
// 索引 1 通常為英文語音 (Microsoft Sam/Mike/Mary)
_voice.Voice = _voice.GetVoices("", "").Item(1);
}
/// 單一語言朗讀
private void SpeakInternal(string text)
{
try
{
// SVSFlagsAsync = 1: 非同步朗讀
_voice.Speak(text, SpeechVoiceSpeakFlags.SVSFlagsAsync);
}
catch (Exception ex)
{
throw new InvalidOperationException($"語音合成失敗: {ex.Message}", ex);
}
}
/// 智慧中英文混合朗讀
public void AnalyseSpeak(string text)
{
if (string.IsNullOrEmpty(text)) return;
int chineseStart = 0, englishStart = 0;
bool isChinese = true; // 預設第一段為中文
for (int i = 0; i < text.Length; i++)
{
char c = text[i];
bool charIsEnglish = c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z';
if (isChinese)
{
if (charIsEnglish)
{
// 中文段結束,朗讀中文段
string segment = text.Substring(chineseStart, i - chineseStart);
if (!string.IsNullOrWhiteSpace(segment))
{
SetChineseVoice();
SpeakInternal(segment);
}
englishStart = i;
isChinese = false;
}
}
else // 目前為英文段
{
if (!charIsEnglish)
{
// 英文段結束,朗讀英文段
string segment = text.Substring(englishStart, i - englishStart);
if (!string.IsNullOrWhiteSpace(segment))
{
SetEnglishVoice();
SpeakInternal(segment);
}
chineseStart = i;
isChinese = true;
}
}
}
// 處理最後一段
if (isChinese)
{
string segment = text.Substring(chineseStart);
if (!string.IsNullOrWhiteSpace(segment))
{
SetChineseVoice();
SpeakInternal(segment);
}
}
else
{
string segment = text.Substring(englishStart);
if (!string.IsNullOrWhiteSpace(segment))
{
SetEnglishVoice();
SpeakInternal(segment);
}
}
}
/// 音量 (0-100)
public int Volume
{
get => _voice.Volume;
set => _voice.Volume = Math.Clamp(value, 0, 100);
}
/// 語速 (-10 到 10)
public int Rate
{
get => _voice.Rate;
set => _voice.Rate = Math.Clamp(value, -10, 10);
}
/// 立即停止朗讀
public void Stop()
{
// SVSFPurgeBeforeSpeak = 1: 清除隊列並停止
_voice.Speak("", SpeechVoiceSpeakFlags.SVSFPurgeBeforeSpeak);
}
/// 暫停朗讀
public void Pause() => _voice.Pause();
/// 繼續朗讀
public void Resume() => _voice.Resume();
public void Dispose()
{
if (!_disposed)
{
Stop();
_disposed = true;
}
}
}
}
using System;
using System.Windows.Forms;
using Bedlang;
public class TtsDemoForm : Form
{
private TextBox _txtInput;
private Button _btnSpeak, _btnStop, _btnPause, _btnResume;
private TrackBar _trkVolume, _trkRate;
public TtsDemoForm()
{
InitializeComponent();
}
private void InitializeComponent()
{
_txtInput = new TextBox
{
Location = new Point(10, 10),
Size = new Size(400, 100),
Multiline = true,
ScrollBars = ScrollBars.Vertical,
Text = "你好 Hello 世界 World!歡迎使用 C# 語音合成。"
};
_btnSpeak = new Button { Text = "朗讀", Location = new Point(10, 120), Width = 80 };
_btnStop = new Button { Text = "停止", Location = new Point(100, 120), Width = 80 };
_btnPause = new Button { Text = "暫停", Location = new Point(190, 120), Width = 80 };
_btnResume = new Button { Text = "繼續", Location = new Point(280, 120), Width = 80 };
_trkVolume = new TrackBar { Location = new Point(10, 160), Width = 200, Minimum = 0, Maximum = 100, Value = 80, TickFrequency = 10 };
_trkRate = new TrackBar { Location = new Point(10, 200), Width = 200, Minimum = -10, Maximum = 10, Value = 0, TickFrequency = 2 };
_btnSpeak.Click += (s, e) => Speech.Instance.AnalyseSpeak(_txtInput.Text);
_btnStop.Click += (s, e) => Speech.Instance.Stop();
_btnPause.Click += (s, e) => Speech.Instance.Pause();
_btnResume.Click += (s, e) => Speech.Instance.Resume();
_trkVolume.ValueChanged += (s, e) => Speech.Instance.Volume = _trkVolume.Value;
_trkRate.ValueChanged += (s, e) => Speech.Instance.Rate = _trkRate.Value;
this.Controls.AddRange(new Control[]
{
_txtInput, _btnSpeak, _btnStop, _btnPause, _btnResume,
new Label { Text = "音量", Location = new Point(220, 160), AutoSize = true }, _trkVolume,
new Label { Text = "語速", Location = new Point(220, 200), AutoSize = true }, _trkRate
});
this.Text = "SAPI 5.1 中英文語音合成示範";
this.ClientSize = new Size(450, 250);
this.FormClosed += (s, e) => Speech.Instance.Dispose();
}
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new TtsDemoForm());
}
}
# 命令列編譯
csc /target:library /out:Bedlang.dll Speech.cs /r:MSTTS.dll
csc /out:TtsDemo.exe TtsDemoForm.cs /r:Bedlang.dll /r:System.Windows.Forms.dll /r:System.Drawing.dll
或直接在 Visual Studio 建立專案,加入 MSTTS.dll 參考即可。
簡易 ASCII 範圍判斷 (A-Z, a-z) 作為英文識別,其餘視為中文。實際專案建議使用 CharUnicodeInfo.GetUnicodeCategory 精確判斷 UnicodeCategory.OtherLetter (含中日韓統一表意文字)。
SpeechVoiceSpeakFlags.SVSFlagsAsync (值 1) 讓 Speak 立即返回,不阻塞 UI 執行緒。停止時使用 SVSFPurgeBeforeSpeak (值 1) 清除待朗讀隊列。
SAPI 語音引擎初始化成本高,採用 Lazy<T> 確保執行緒安全單例,應用程式生命週期內只建立一次。
| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| SAPI 5.1 (本文) | 離線、免費、Windows 內建 | 語音較機械、需安裝 SDK、僅限 Windows | 舊系統維護、離線嵌入式 |
| System.Speech | .NET 內建、API 現代 | 中文需額外安裝語言包、語音品質同 SAPI | .NET Framework/Core 桌面應用 |
| Windows.Media.SpeechSynthesis | 現代語音、支援神經網路、SSML 完整 | 需 UWP/WinUI 3/Windows App SDK | 現代 Windows 應用開發 |
| Azure Speech SDK | 最佳語音品質、多語言混合、自訂語音 | 需網路、依用量付費 | 企業級、雲端、跨平台 |
本文提供完整的 SAPI 5.1 中文語音合成 C# 封裝:tlbimp 互通組件、單例管理、中英文自動切換、音量語速控制、暫停/繼續/停止。雖技術屬前世代,但在無法使用現代語音平台的舊系統維護、離線環境部署中仍具實用價值。新專案強烈建議遷移至 Windows.Media.SpeechSynthesis 或 Azure Speech SDK。