C# 语音合成(TTS)示例 - 使用微软Speech库

C#语音合成(TTS)示例,使用微软的System.Speech库实现文本转语音功能。包含基本语音合成、语音设置、事件处理和异步操作。

准备工作

1. 添加必要的引用

在Visual Studio项目中,需要添加对System.Speech的引用:

  • 右键点击项目 -> 添加 -> 引用
  • 在"程序集"中找到并选择System.Speech
  • 或者通过NuGet安装:Install-Package System.Speech

2. 引入命名空间

using System.Speech.Synthesis;
using System.Globalization;

完整示例代码

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Speech.Synthesis;
using System.Globalization;

namespace TextToSpeechExample
{
    public partial class MainForm : Form
    {
        private SpeechSynthesizer synthesizer;
        private bool isSpeaking = false;
        
        public MainForm()
        {
            InitializeComponent();
            InitializeSynthesizer();
            LoadAvailableVoices();
        }
        
        // 初始化语音合成器
        private void InitializeSynthesizer()
        {
            synthesizer = new SpeechSynthesizer();
            
            // 设置事件处理
            synthesizer.SpeakStarted += Synthesizer_SpeakStarted;
            synthesizer.SpeakCompleted += Synthesizer_SpeakCompleted;
            synthesizer.SpeakProgress += Synthesizer_SpeakProgress;
            synthesizer.StateChanged += Synthesizer_StateChanged;
        }
        
        // 加载可用的语音
        private void LoadAvailableVoices()
        {
            cmbVoices.Items.Clear();
            
            // 获取所有已安装的语音
            foreach (InstalledVoice voice in synthesizer.GetInstalledVoices())
            {
                VoiceInfo info = voice.VoiceInfo;
                cmbVoices.Items.Add($"{info.Name} - {info.Culture.Name}");
            }
            
            if (cmbVoices.Items.Count > 0)
            {
                cmbVoices.SelectedIndex = 0;
            }
        }
        
        // 开始说话事件
        private void Synthesizer_SpeakStarted(object sender, SpeakStartedEventArgs e)
        {
            isSpeaking = true;
            UpdateUIState();
        }
        
        // 说话完成事件
        private void Synthesizer_SpeakCompleted(object sender, SpeakCompletedEventArgs e)
        {
            isSpeaking = false;
            UpdateUIState();
        }
        
        // 说话进度事件
        private void Synthesizer_SpeakProgress(object sender, SpeakProgressEventArgs e)
        {
            // 更新进度条(如果需要)
            if (txtContent.Text.Length > 0)
            {
                int progress = (int)((double)e.CharacterPosition / txtContent.Text.Length * 100);
                progressBar.Value = Math.Min(progress, 100);
            }
        }
        
        // 状态改变事件
        private void Synthesizer_StateChanged(object sender, StateChangedEventArgs e)
        {
            lblStatus.Text = $"状态: {e.State}";
        }
        
        // 更新UI状态
        private void UpdateUIState()
        {
            btnSpeak.Enabled = !isSpeaking && !string.IsNullOrEmpty(txtContent.Text);
            btnPause.Enabled = isSpeaking;
            btnResume.Enabled = synthesizer.State == SynthesizerState.Paused;
            btnStop.Enabled = isSpeaking;
        }
        
        // 开始说话按钮点击事件
        private void btnSpeak_Click(object sender, EventArgs e)
        {
            SpeakText();
        }
        
        // 暂停按钮点击事件
        private void btnPause_Click(object sender, EventArgs e)
        {
            if (synthesizer.State == SynthesizerState.Speaking)
            {
                synthesizer.Pause();
                btnPause.Enabled = false;
                btnResume.Enabled = true;
            }
        }
        
        // 恢复按钮点击事件
        private void btnResume_Click(object sender, EventArgs e)
        {
            if (synthesizer.State == SynthesizerState.Paused)
            {
                synthesizer.Resume();
                btnPause.Enabled = true;
                btnResume.Enabled = false;
            }
        }
        
        // 停止按钮点击事件
        private void btnStop_Click(object sender, EventArgs e)
        {
            synthesizer.SpeakAsyncCancelAll();
            isSpeaking = false;
            UpdateUIState();
        }
        
        // 说话方法
        private void SpeakText()
        {
            if (string.IsNullOrEmpty(txtContent.Text))
            {
                MessageBox.Show("请输入要朗读的文本");
                return;
            }
            
            // 配置语音设置
            ConfigureSpeechSettings();
            
            try
            {
                // 异步朗读文本
                synthesizer.SpeakAsync(txtContent.Text);
            }
            catch (Exception ex)
            {
                MessageBox.Show($"朗读出错: {ex.Message}");
            }
        }
        
        // 配置语音设置
        private void ConfigureSpeechSettings()
        {
            // 设置语音
            if (cmbVoices.SelectedIndex >= 0)
            {
                string voiceName = cmbVoices.SelectedItem.ToString().Split('-')[0].Trim();
                synthesizer.SelectVoice(voiceName);
            }
            
            // 设置语速 (-10 到 10)
            synthesizer.Rate = trackBarRate.Value;
            
            // 设置音量 (0 到 100)
            synthesizer.Volume = trackBarVolume.Value;
        }
        
        // 导出为WAV文件
        private void btnExport_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(txtContent.Text))
            {
                MessageBox.Show("请输入要导出的文本");
                return;
            }
            
            SaveFileDialog saveDialog = new SaveFileDialog();
            saveDialog.Filter = "WAV文件|*.wav";
            saveDialog.Title = "保存语音文件";
            
            if (saveDialog.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    ConfigureSpeechSettings();
                    synthesizer.SetOutputToWaveFile(saveDialog.FileName);
                    synthesizer.Speak(txtContent.Text);
                    synthesizer.SetOutputToDefaultAudioDevice(); // 恢复默认输出设备
                    MessageBox.Show("导出成功!");
                }
                catch (Exception ex)
                {
                    MessageBox.Show($"导出失败: {ex.Message}");
                }
            }
        }
        
        // 窗体关闭事件
        private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (synthesizer != null)
            {
                synthesizer.Dispose();
            }
        }
        
        // 文本变化事件
        private void txtContent_TextChanged(object sender, EventArgs e)
        {
            UpdateUIState();
        }
    }
}

窗体设计代码 (MainForm.Designer.cs)

namespace TextToSpeechExample
{
    partial class MainForm
    {
        private System.ComponentModel.IContainer components = null;

        private TextBox txtContent;
        private Button btnSpeak;
        private Button btnPause;
        private Button btnResume;
        private Button btnStop;
        private ComboBox cmbVoices;
        private Label label1;
        private Label label2;
        private Label label3;
        private TrackBar trackBarRate;
        private TrackBar trackBarVolume;
        private Label label4;
        private Label label5;
        private ProgressBar progressBar;
        private Label lblStatus;
        private Button btnExport;

        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        private void InitializeComponent()
        {
            this.txtContent = new System.Windows.Forms.TextBox();
            this.btnSpeak = new System.Windows.Forms.Button();
            this.btnPause = new System.Windows.Forms.Button();
            this.btnResume = new System.Windows.Forms.Button();
            this.btnStop = new System.Windows.Forms.Button();
            this.cmbVoices = new System.Windows.Forms.ComboBox();
            this.label1 = new System.Windows.Forms.Label();
            this.label2 = new System.Windows.Forms.Label();
            this.label3 = new System.Windows.Forms.Label();
            this.trackBarRate = new System.Windows.Forms.TrackBar();
            this.trackBarVolume = new System.Windows.Forms.TrackBar();
            this.label4 = new System.Windows.Forms.Label();
            this.label5 = new System.Windows.Forms.Label();
            this.progressBar = new System.Windows.Forms.ProgressBar();
            this.lblStatus = new System.Windows.Forms.Label();
            this.btnExport = new System.Windows.Forms.Button();
            ((System.ComponentModel.ISupportInitialize)(this.trackBarRate)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this.trackBarVolume)).BeginInit();
            this.SuspendLayout();
            
            // txtContent
            this.txtContent.Location = new System.Drawing.Point(12, 29);
            this.txtContent.Multiline = true;
            this.txtContent.Name = "txtContent";
            this.txtContent.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
            this.txtContent.Size = new System.Drawing.Size(560, 150);
            this.txtContent.TabIndex = 0;
            this.txtContent.TextChanged += new System.EventHandler(this.txtContent_TextChanged);
            
            // btnSpeak
            this.btnSpeak.Location = new System.Drawing.Point(12, 185);
            this.btnSpeak.Name = "btnSpeak";
            this.btnSpeak.Size = new System.Drawing.Size(75, 23);
            this.btnSpeak.TabIndex = 1;
            this.btnSpeak.Text = "开始朗读";
            this.btnSpeak.UseVisualStyleBackColor = true;
            this.btnSpeak.Click += new System.EventHandler(this.btnSpeak_Click);
            
            // btnPause
            this.btnPause.Location = new System.Drawing.Point(93, 185);
            this.btnPause.Name = "btnPause";
            this.btnPause.Size = new System.Drawing.Size(75, 23);
            this.btnPause.TabIndex = 2;
            this.btnPause.Text = "暂停";
            this.btnPause.UseVisualStyleBackColor = true;
            this.btnPause.Click += new System.EventHandler(this.btnPause_Click);
            
            // btnResume
            this.btnResume.Location = new System.Drawing.Point(174, 185);
            this.btnResume.Name = "btnResume";
            this.btnResume.Size = new System.Drawing.Size(75, 23);
            this.btnResume.TabIndex = 3;
            this.btnResume.Text = "继续";
            this.btnResume.UseVisualStyleBackColor = true;
            this.btnResume.Click += new System.EventHandler(this.btnResume_Click);
            
            // btnStop
            this.btnStop.Location = new System.Drawing.Point(255, 185);
            this.btnStop.Name = "btnStop";
            this.btnStop.Size = new System.Drawing.Size(75, 23);
            this.btnStop.TabIndex = 4;
            this.btnStop.Text = "停止";
            this.btnStop.UseVisualStyleBackColor = true;
            this.btnStop.Click += new System.EventHandler(this.btnStop_Click);
            
            // cmbVoices
            this.cmbVoices.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
            this.cmbVoices.FormattingEnabled = true;
            this.cmbVoices.Location = new System.Drawing.Point(60, 214);
            this.cmbVoices.Name = "cmbVoices";
            this.cmbVoices.Size = new System.Drawing.Size(270, 21);
            this.cmbVoices.TabIndex = 5;
            
            // label1
            this.label1.AutoSize = true;
            this.label1.Location = new System.Drawing.Point(12, 217);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(43, 13);
            this.label1.TabIndex = 6;
            this.label1.Text = "语音:";
            
            // label2
            this.label2.AutoSize = true;
            this.label2.Location = new System.Drawing.Point(12, 13);
            this.label2.Name = "label2";
            this.label2.Size = new System.Drawing.Size(67, 13);
            this.label2.TabIndex = 7;
            this.label2.Text = "输入文本:";
            
            // label3
            this.label3.AutoSize = true;
            this.label3.Location = new System.Drawing.Point(12, 247);
            this.label3.Name = "label3";
            this.label3.Size = new System.Drawing.Size(43, 13);
            this.label3.TabIndex = 8;
            this.label3.Text = "语速:";
            
            // trackBarRate
            this.trackBarRate.Location = new System.Drawing.Point(60, 241);
            this.trackBarRate.Minimum = -10;
            this.trackBarRate.Maximum = 10;
            this.trackBarRate.Name = "trackBarRate";
            this.trackBarRate.Size = new System.Drawing.Size(270, 45);
            this.trackBarRate.TabIndex = 9;
            this.trackBarRate.TickFrequency = 2;
            
            // trackBarVolume
            this.trackBarVolume.Location = new System.Drawing.Point(60, 292);
            this.trackBarVolume.Maximum = 100;
            this.trackBarVolume.Name = "trackBarVolume";
            this.trackBarVolume.Size = new System.Drawing.Size(270, 45);
            this.trackBarVolume.TabIndex = 10;
            this.trackBarVolume.TickFrequency = 10;
            this.trackBarVolume.Value = 100;
            
            // label4
            this.label4.AutoSize = true;
            this.label4.Location = new System.Drawing.Point(12, 298);
            this.label4.Name = "label4";
            this.label4.Size = new System.Drawing.Size(43, 13);
            this.label4.TabIndex = 11;
            this.label4.Text = "音量:";
            
            // label5
            this.label5.AutoSize = true;
            this.label5.Location = new System.Drawing.Point(336, 247);
            this.label5.Name = "label5";
            this.label5.Size = new System.Drawing.Size(31, 13);
            this.label5.TabIndex = 12;
            this.label5.Text = "慢速";
            
            // progressBar
            this.progressBar.Location = new System.Drawing.Point(12, 343);
            this.progressBar.Name = "progressBar";
            this.progressBar.Size = new System.Drawing.Size(560, 23);
            this.progressBar.TabIndex = 14;
            
            // lblStatus
            this.lblStatus.AutoSize = true;
            this.lblStatus.Location = new System.Drawing.Point(12, 369);
            this.lblStatus.Name = "lblStatus";
            this.lblStatus.Size = new System.Drawing.Size(46, 13);
            this.lblStatus.TabIndex = 15;
            this.lblStatus.Text = "状态: ";
            
            // btnExport
            this.btnExport.Location = new System.Drawing.Point(336, 185);
            this.btnExport.Name = "btnExport";
            this.btnExport.Size = new System.Drawing.Size(75, 23);
            this.btnExport.TabIndex = 16;
            this.btnExport.Text = "导出WAV";
            this.btnExport.UseVisualStyleBackColor = true;
            this.btnExport.Click += new System.EventHandler(this.btnExport_Click);
            
            // MainForm
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(584, 391);
            this.Controls.Add(this.btnExport);
            this.Controls.Add(this.lblStatus);
            this.Controls.Add(this.progressBar);
            this.Controls.Add(this.label5);
            this.Controls.Add(this.label4);
            this.Controls.Add(this.trackBarVolume);
            this.Controls.Add(this.trackBarRate);
            this.Controls.Add(this.label3);
            this.Controls.Add(this.label2);
            this.Controls.Add(this.label1);
            this.Controls.Add(this.cmbVoices);
            this.Controls.Add(this.btnStop);
            this.Controls.Add(this.btnResume);
            this.Controls.Add(this.btnPause);
            this.Controls.Add(this.btnSpeak);
            this.Controls.Add(this.txtContent);
            this.Name = "MainForm";
            this.Text = "文本转语音(TTS)示例";
            this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainForm_FormClosing);
            ((System.ComponentModel.ISupportInitialize)(this.trackBarRate)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this.trackBarVolume)).EndInit();
            this.ResumeLayout(false);
            this.PerformLayout();
        }
    }
}

高级功能扩展

1. 使用SSML(语音合成标记语言)

SSML允许更精细地控制语音输出:

private void SpeakWithSSML()
{
    string ssml = @"
        <speak version='1.0' xmlns='http://www.w3.org/2001/10/synthesis' xml:lang='zh-CN'>
            <voice name='Microsoft Huihui Desktop'>
                <prosody rate='fast' volume='loud'>
                    这是一段使用SSML控制的语音。
                </prosody>
                <break time='500ms'/>
                <prosody pitch='high' rate='slow'>
                    现在语速变慢,音调变高。
                </prosody>
            </voice>
        </speak>";
    
    synthesizer.SpeakSsml(ssml);
}

2. 使用Windows.Media.SpeechSynthesis (UWP/WPF)

对于UWP或WPF应用程序,可以使用更现代的API:

using Windows.Media.SpeechSynthesis;
using Windows.Storage.Streams;

// 在UWP/WPF中的异步语音合成
private async Task SpeakTextAsync(string text)
{
    using (var speechSynthesizer = new SpeechSynthesizer())
    {
        // 选择语音
        var voices = SpeechSynthesizer.AllVoices;
        var selectedVoice = voices.FirstOrDefault(v => v.Gender == VoiceGender.Female && v.Language == "zh-CN");
        if (selectedVoice != null)
        {
            speechSynthesizer.Voice = selectedVoice;
        }
        
        // 生成语音流
        SpeechSynthesisStream stream = await speechSynthesizer.SynthesizeTextToStreamAsync(text);
        
        // 播放语音
        MediaElement mediaElement = new MediaElement();
        mediaElement.SetSource(stream, stream.ContentType);
        mediaElement.Play();
    }
}

3. 语音提示和通知

// 简单的语音提示
public static void SpeakNotification(string message)
{
    using (var synth = new SpeechSynthesizer())
    {
        synth.Volume = 80;
        synth.Rate = 1;
        synth.SpeakAsync(message);
    }
}

// 使用不同的语音和语调
public static void SpeakWithEmotion(string message, string emotion = "normal")
{
    using (var synth = new SpeechSynthesizer())
    {
        // 根据情绪调整语音设置
        switch (emotion.ToLower())
        {
            case "excited":
                synth.Rate = 2;
                synth.Volume = 100;
                break;
            case "calm":
                synth.Rate = -1;
                synth.Volume = 70;
                break;
            case "emphasis":
                synth.Rate = 0;
                synth.Volume = 100;
                break;
            default:
                synth.Rate = 0;
                synth.Volume = 80;
                break;
        }
        
        synth.SpeakAsync(message);
    }
}

参考项目 C# 语音合成例子源码(TTS微软库) www.3dddown.com/cna/57531.html

注意

  1. 语音可用性:不同的Windows版本和语言包可能安装不同的语音。中文语音可能需要额外安装语言包。

  2. 异步处理SpeakAsync方法是非阻塞的,适合在UI应用程序中使用。如果需要等待语音完成,可以使用Speak方法(会阻塞当前线程)。

  3. 资源管理:确保正确释放SpeechSynthesizer对象,特别是在长时间运行的应用程序中。

  4. 异常处理:语音合成可能因为各种原因失败(如没有可用的语音),应该添加适当的异常处理。

  5. 性能考虑:对于大量文本,考虑使用流式输出或分块处理,以避免内存问题。

posted @ 2025-12-15 11:54  yes_go  阅读(4)  评论(0)    收藏  举报