• <转载>关于Microsoft Speech SDK 中TTS的研究


    1.   TTS概述
    随着语音技术的发展,微软也推出了相应的语音开发工具,即Microsoft Speech SDK,这个SDK中包含了语音应用设计接口(SAPI)、微软的连续语音识别引擎(MCSR)以及微软的语音合成(TTS)引擎等等。它其中的TTS(text-to-speech)引擎可以用于实现语音合成,我们通过TTS引擎可以分析文本内容并且将其朗读出。实现TTS技术的方法有很多种,现在主要采用三种:连词技术、语音合成技术、子字连接技术。目前的5.1版本的SDK一共可以支持3种语言的识别 (英语,汉语和日语)以及2种语言的合成(英语和汉语)。其中还包括对于低层控制和高度适应性的直接语音管理、训练向导、事件、语法编译、资源、语音识别(SR)管理以及TTS管理等强大的设计接口。


    2.   实现原理
     
         以下是SpeechAPI的总体结构:

           从图中我们可以看出语音引擎则通过DDI层(设备驱动接口)和SAPI(SpeechAPI)进行交互,应用程序通过API层和SAPI通信。通过使用这些API,用户可以快速开发在语音识别或语音合成方面应用程序。
     
         应用程序使用ISpVoice接口来控制TTS,通过调用其中的Speak方法可以朗读出文本内容,通过调用SetVoice / GetVoice方法(在.NET中已经转变成Voice属性)来获取或设置朗读的语音,而通过调用GetVolume / SetVolume、GetRate / SetRate等方法(在.NET中已经转变成Volume和Rate属性)来获取或设置朗读的音量和语速。
     
    功能强大之处在于TTS能识别XML标记,通过给文本加上XML标记,我们让TTS朗读出更加符合语言阅读习惯的句子。例如:
     
    l   <volume level=”60”></volume> 用于设置文本朗读的音量;
     
    l   <rate absspeed=”1”/>、<rate speed=”5”/> 分别用于设置文本朗读的绝对速度和相对速度;
     
    l   <pitch absmiddle=”2”/>、<pitch middle=”5”/> 分别用于设置文本朗读的绝对语调和相对语调;
     
    l   <emph></emph> 在他们之间的句子被视为强调;
     
    l   <spell></spell> 可以将单词逐个字母的拼写出来;
     
    l   <silence msec=”500”/> 表示停止发声,并保持500微秒;
     
    l   <context id=”date_mdy”>02/03/07</context> 可以按要求朗读出日期
     
    l   <voice required="Language=409"></voice> 用于设置朗读所用的语言,其中409表示使用英语,804表示使用汉语,而411表示日语。
     
     
     
     
     
    3.   软件的开发
     
     
     
    3.1.  开发环境的搭建
     
     
     
    由于Microsoft Speech SDK是以COM组件的形式提供给我们的,因此在使用.NET开发时必须引入Interop.SpeechLib.dll文件,如图:


    在引入DLL文件后,我们就可以通过添加“using SpeechLib;”引入命名空间,或直接使用SpeechLib前缀来使用SpeechLib空间下的所有类。
     
     
     
    3.2.    二次封装TTS类
     
     
     
    我们将使用Singleton设计模式来对TTS进行封装,首先声明一个SpVoice接口,并用SpVoiceClass 对象来实例化,这个接口是实现文本朗读的核心。接着提供用于朗读文本的方法,例如:
     
       /// <summary>
     
            /// 读出Xml文件内容
     
            /// </summary>
     
            /// <param name="xml">Xml文件内容</param>
     
            public void SpeakXml(string xml)
     
            {
     
                voice.Speak(xml, SpeechVoiceSpeakFlags.SVSFIsXML | SpeechVoiceSpeakFlags.SVSFlagsAsync);
     
            }
     
    并且使用Pause()、Resume()、Stop()等方法来控制朗读暂停、继续和停止。至于保存音频文件,我们可以使用以下方法,将音频输出流指向一个文件流,来完成保存工作。
     
            /// <summary>
     
            /// 保存音频到文件
     
            /// </summary>
     
            /// <param name="xml">要读的Xml格式的内容</param>
     
            /// <param name="fileName">要保存的文件名</param>
     
            public void Save(string xml, string fileName)
     
            {
     
                SpFileStream stream = new SpFileStream();
     
                stream.Open(fileName, SpeechStreamFileMode.SSFMCreateForWrite, false);
     
                voice.AudioOutputStream = stream;
     
                voice.Speak(xml, SpeechVoiceSpeakFlags.SVSFlagsAsync | SpeechVoiceSpeakFlags.SVSFIsXML);
     
                voice.WaitUntilDone(Timeout.Infinite);
     
                stream.Close();
     
            }
     
     
     
     
     
    3.3.  实现中英文的混合朗读
     
     
     
    如果我们直接调用SpVoice接口中Speak方法来朗读文本,那么在朗读过程中,要么使用英文朗读引擎,要么使用中文朗读引擎,这样就只能朗读纯英文文本或纯中文文本。要怎样才能朗读混合的文本呢?第一种方法,我们可以在朗读过程中,根据文本的内容来切换朗读所用的引擎,即调用SetChinaVoice()和SetEnglishVoice()方法。第二种方法,我们在朗读文本之前,先分析文本,将属于英文的句子加上英文语音XML标记,即<voice required=”Language=409”></voice>,将属于中文的句子加上中文语音XML标记,即<voice required=”Language=804”></voice>。这样调用SpeakXml方法就可以实现中英文混合朗读。
     
    在这里我选择第二种方法,在类中增加静态方法:AddXmlLangTag,返回添加过标记得文本内容。
     
    /// <summary>
     
            /// 设置中文语音
     
            /// </summary>
     
            public void SetChinaVoice()
     
            {
     
                voice.Voice = voice.GetVoices(string.Empty, string.Empty).Item(0);
     
            }
     
     
     
            /// <summary>
     
            /// 设置英文语音
     
            /// </summary>
     
            public void SetEnglishVoice()
     
            {
     
                voice.Voice = voice.GetVoices(string.Empty, string.Empty).Item(1);
     
            }
     
     
     
     
     
    3.4.  界面的实现
     
     
     


    在打开文件时,可以选择打开文本文件(*.Txt)和XML文件(*.Xml),如果打开的是XML文件,将不对内容作任何修改,并且也不允许调节音量、语速、语调,因为这些都应该在XML文件中写好;如果打开的是文本文件,则在朗读前,会调用AddXmlLangTag方法给文本加上语言标记,调用AddXmlPitchTag方法给文本加上语调标记,同时也允许调节音量、语速、语调。
     
       

     
     
    4.   总结
     
         通过为普通文本内容设置语音XML标记,并调用SpVoice接口的Speak方法,可以实现中英文文本的混合朗读。如果要使朗读的效果更佳,就必须手工为每一个句子设置相应的XML标记,这样可使朗读更接近人性化。
     
     
     
     
     
     
     
     
     
     
     
    【参考文献】
     
    1.Microsoft Speech SDK 帮助 (sapi.chm)
     
    2.http://www.codeproject.com/vb/net/TTSinVBpackage.asp
     
    3.http://www.c-sharpcorner.com/SpeechNet.asp
     
    4.http://www.supinfo-projects.com/cn/2006/xing_jin_inter_2006/
     
    5.http://www.microsoft.com/china/community/program/originalarticles/TechDoc/Cnspeech.mspx
     
    6.http://www.microsoft.com/speech/default.mspx
     
     
     
     
     
     
     
     
     
    【源码】
     
     
     
    Talker.cs
     
     
     
    using System;
     
    using System.Collections.Generic;
     
    using System.Text;
     
    using System.Threading;
     
    using SpeechLib;
     
     
     
    namespace VoiceTalker
     
    {
     
        /// <summary>
     
        /// TTS语音朗读类
     
        /// </summary>
     
        public class Talker
     
        {
     
            private static Talker talker = null;        //Talker对象
     
            private SpVoice voice = null;               //SpVoice对象,用于实现TTS
     
     
     
            /// <summary>
     
            /// 默认的英文语音ID
     
            /// </summary>
     
            public const string DefaultEnglishLangID = "409";
     
     
     
            /// <summary>
     
            /// 默认的中文语音ID
     
            /// </summary>
     
            public const string DefaultChineseLangID = "804";
     
     
     
            /// <summary>
     
            /// 构造函数
     
            /// </summary>
     
            private Talker()
     
            {
     
                voice = new SpeechLib.SpVoiceClass();
     
            }
     
     
     
            /// <summary>
     
            /// 获取或设置音量
     
            /// </summary>
     
            public int Volume
     
            {
     
                get
     
                {
     
                    return voice.Volume;
     
                }
     
                set
     
                {
     
                    voice.Volume = value;
     
                }
     
            }
     
     
     
            /// <summary>
     
            /// 获取或设置语速
     
            /// </summary>
     
            public int Rate
     
            {
     
                get
     
                {
     
                    return voice.Rate;
     
                }
     
                set
     
                {
     
                    voice.Rate = value;
     
                }
     
            }
     
     
     
            /// <summary>
     
            /// 获取或设置语音
     
            /// </summary>
     
            public SpObjectToken Voice
     
            {
     
                get
     
                {
     
                    return voice.Voice;
     
                }
     
                set
     
                {
     
                    voice.Voice = value;
     
                }
     
            }
     
     
     
     
     
            /// <summary>
     
            /// 获得实例
     
            /// </summary>
     
            /// <returns>Talker对象</returns>
     
            public static Talker Instance()
     
            {
     
                if (talker == null)
     
                {
     
                    talker = new Talker();
     
                }
     
                return talker;
     
            }
     
     
     
            /// <summary>
     
            /// 给文本内容添加语言标记
     
            /// </summary>
     
            /// <param name="src">文本内容</param>
     
            /// <returns>经过语言标记的文本内容</returns>
     
            public static string AddXmlLangTag(string src)
     
            {
     
                return AddXmlLangTag(src, Talker.DefaultEnglishLangID, Talker.DefaultChineseLangID);
     
            }
     
     
     
            /// <summary>
     
            /// 给文本内容添加语言标记
     
            /// </summary>
     
            /// <param name="src">文本内容</param>
     
            /// <param name="englishLangID">英文语音ID</param>
     
            /// <param name="chineseLangID">中文语音ID</param>
     
            /// <returns>经过语言标记的文本内容</returns>
     
            public static string AddXmlLangTag(string src, string englishLangID, string chineseLangID)
     
            {
     
                if (src.Length < 1)
     
                {
     
                    return "";
     
                }
     
     
     
                StringBuilder dest = new StringBuilder();
     
                int startPos = 0, endPos = 0;
     
                bool isAscii = !(src[0] > 128);
     
                for (int i = 0; i < src.Length; i++)
     
                {
     
                    /* 判断每个字符是否为ASCII,如果是则加上<voice required="Language=englishLangID"></voice>
     
                     * 如果不是就加上<voice required="Language=chineseLangID></voice>
     
                     */
     
                    if (src[i] > 128)
     
                    {
     
                        if (isAscii)
     
                        {
     
                            string sub = src.Substring(startPos, endPos - startPos);
     
                            dest.Append("<voice required=\"Language=" + englishLangID + "\">" + sub + "</voice>");
     
                            startPos = endPos;
     
                        }
     
                        isAscii = false;
     
                        endPos++;
     
                    }
     
                    else
     
                    {
     
                        if (!isAscii)
     
                        {
     
                            string sub = src.Substring(startPos, endPos - startPos);
     
                            dest.Append("<voice required=\"Language=" + chineseLangID + "\">" + sub + "</voice>");
     
                            startPos = endPos;
     
                        }
     
                        isAscii = true;
     
                        endPos++;
     
                    }
     
                }
     
                string r = src.Substring(startPos, endPos - startPos);
     
                string langID = isAscii == true ? englishLangID : chineseLangID;
     
                dest.Append("<voice required=\"Language=" + langID + "\">" + r + "</voice>");
     
                return dest.ToString();
     
            }
     
     
     
            /// <summary>
     
            /// 给文本内容添加语调标记
     
            /// </summary>
     
            /// <param name="src">文本内容</param>
     
            /// <param name="pitch">语调</param>
     
            /// <returns>经过语调标记的文本内容</returns>
     
            public static string AddXmlPitchTag(string src, int pitch)
     
            {
     
                string pitchTag = "<pitch absmiddle=\"" + pitch.ToString() + "\"/>";
     
                return pitchTag + src;
     
            }
     
     
     
            /// <summary>
     
            /// 设置中文语音
     
            /// </summary>
     
            public void SetChinaVoice()
     
            {
     
                voice.Voice = voice.GetVoices(string.Empty, string.Empty).Item(0);
     
            }
     
     
     
            /// <summary>
     
            /// 设置英文语音
     
            /// </summary>
     
            public void SetEnglishVoice()
     
            {
     
                voice.Voice = voice.GetVoices(string.Empty, string.Empty).Item(1);
     
            }
     
     
     
            /// <summary>
     
            /// 读出文件内容
     
            /// </summary>
     
            /// <param name="text">文件内容</param>
     
            public void SpeakText(string text)
     
            {
     
                voice.Speak(text, SpeechVoiceSpeakFlags.SVSFDefault | SpeechVoiceSpeakFlags.SVSFlagsAsync);
     
            }
     
     
     
            /// <summary>
     
            /// 读出Xml文件内容
     
            /// </summary>
     
            /// <param name="xml">Xml文件内容</param>
     
            public void SpeakXml(string xml)
     
            {
     
                voice.Speak(xml, SpeechVoiceSpeakFlags.SVSFIsXML | SpeechVoiceSpeakFlags.SVSFlagsAsync);
     
            }
     
     
     
            /// <summary>
     
            /// 读出文件内容
     
            /// </summary>
     
            /// <param name="fileName">文件名</param>
     
            public void SpeakFile(string fileName)
     
            {
     
                voice.Speak(fileName, SpeechVoiceSpeakFlags.SVSFIsFilename | SpeechVoiceSpeakFlags.SVSFlagsAsync);
     
            }
     
     
     
            /// <summary>
     
            /// 暂停
     
            /// </summary>
     
            public void Pause()
     
            {
     
                voice.Pause();
     
            }
     
     
     
            /// <summary>
     
            /// 继续
     
            /// </summary>
     
            public void Resume()
     
            {
     
                voice.Resume();
     
            }
     
     
     
            /// <summary>
     
            ///   停止说话
     
            /// </summary>
     
            public void Stop()
     
            {
     
                voice.Speak(string.Empty, SpeechVoiceSpeakFlags.SVSFPurgeBeforeSpeak);
     
            }
     
     
     
            /// <summary>
     
            /// 保存音频到文件
     
            /// </summary>
     
            /// <param name="xml">要读的Xml格式的内容</param>
     
            /// <param name="fileName">要保存的文件名</param>
     
            public void Save(string xml, string fileName)
     
            {
     
                SpFileStream stream = new SpFileStream();
     
                stream.Open(fileName, SpeechStreamFileMode.SSFMCreateForWrite, false);
     
                voice.AudioOutputStream = stream;
     
                voice.Speak(xml, SpeechVoiceSpeakFlags.SVSFlagsAsync | SpeechVoiceSpeakFlags.SVSFIsXML);
     
                voice.WaitUntilDone(Timeout.Infinite);
     
                stream.Close();
     
            }
     
        }
     
    }
     
     
     
     
     
     
     
    VoiceForm.cs
     
     
     
    using System;
     
    using System.Collections.Generic;
     
    using System.ComponentModel;
     
    using System.Data;
     
    using System.Drawing;
     
    using System.Text;
     
    using System.IO;
     
    using System.Windows.Forms;
     
     
     
    namespace VoiceTalker
     
    {
     
        public partial class VoiceForm : Form
     
        {
     
            private bool isXml = false;
     
            private Talker talker = Talker.Instance();
     
     
     
            public VoiceForm()
     
            {
     
                InitializeComponent();
     
            }
     
     
     
            private void openButton_Click(object sender, EventArgs e)
     
            {
     
                DialogResult dr = openFileDialog.ShowDialog();
     
                if (dr == DialogResult.OK)
     
                {
     
                    FileInfo fi = new FileInfo(openFileDialog.FileName);
     
                    //判断是否为Xml格式的文件
     
                    if (fi.Extension.ToLower() == ".xml")
     
                    {
     
                        controlBox.Enabled = false;
     
                        isXml = true;
     
                    }
     
                    else
     
                    {
     
                        controlBox.Enabled = true;
     
                    }
     
                    fileNameText.Text = fi.FullName;
     
                    contentText.Text = "";
     
     
     
                    //读取文件内容
     
                    StreamReader sr = new StreamReader(openFileDialog.OpenFile(), Encoding.Default);
     
                    contentText.Text = sr.ReadToEnd();
     
                }
     
            }
     
     
     
            private void speakButton_Click(object sender, EventArgs e)
     
            {
     
                try
     
                {
     
                    if (isXml)
     
                    {
     
                        talker.SpeakXml(contentText.Text);
     
                    }
     
                    else
     
                    {
     
                        talker.Volume = volumeBar.Value;
     
                        talker.Rate = rateBar.Value;
     
                        String readText = Talker.AddXmlLangTag(contentText.Text);
     
                        readText = Talker.AddXmlPitchTag(readText, pitchBar.Value);
     
                        talker.SpeakXml(readText);
     
                    }
     
                }
     
                catch (Exception ex)
     
                {
     
                    MessageBox.Show(ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
     
                }
     
            }
     
     
     
            private void StopButton_Click(object sender, EventArgs e)
     
            {
     
                try
     
                {
     
                    talker.Stop();
     
                }
     
                catch (Exception ex)
     
                {
     
                    MessageBox.Show(ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
     
                }
     
            }
     
     
     
            private void pauseButton_Click(object sender, EventArgs e)
     
            {
     
                try
     
                {
     
                    if (pauseButton.Text == "暂停")
     
                    {
     
                        talker.Pause();
     
                        pauseButton.Text = "继续";
     
                    }
     
                    else
     
                    {
     
                        talker.Resume();
     
                        pauseButton.Text = "暂停";
     
                    }
     
                }
     
                catch (Exception ex)
     
                {
     
                    MessageBox.Show(ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
     
                }
     
            }
     
     
     
            private void saveButton_Click(object sender, EventArgs e)
     
            {
     
                try
     
                {
     
                    DialogResult dr = saveFileDialog.ShowDialog();
     
                    if (dr == DialogResult.OK)
     
                    {
     
                        string fileName = saveFileDialog.FileName;
     
                        string content = contentText.Text;
     
                        if (!isXml)
     
                        {
     
                            talker.Volume = volumeBar.Value;
     
                            talker.Rate = rateBar.Value;
     
                            content = Talker.AddXmlLangTag(content);
     
                            content = Talker.AddXmlPitchTag(content, pitchBar.Value);
     
                        }
     
                        talker.Save(content, fileName);
     
                    }
     
                }
     
                catch (Exception ex)
     
                {
     
                    MessageBox.Show(ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
     
                }
     
            }
     
     
     
            private void volumeBar_Scroll(object sender, EventArgs e)
     
            {
     
                try
     
                {
     
                    talker.Volume = volumeBar.Value;
     
                }
     
                catch (Exception ex)
     
                {
     
                    MessageBox.Show(ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
     
                }
     
            }
     
     
     
            private void rateBar_Scroll(object sender, EventArgs e)
     
            {
     
                try
     
                {
     
                    talker.Rate = rateBar.Value;
     
                }
     
                catch (Exception ex)
     
                {
     
                    MessageBox.Show(ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
     
                }
     
            }
     
     
     
            private void aboutButton_Click(object sender, EventArgs e)
     
            {
     
                new AboutForm().ShowDialog();
     
            }
     
     
     
            private void exitButton_Click(object sender, EventArgs e)
     
            {
     
                Application.Exit();
     
            }
     
        }
     
    }


    本文出处:http://blog.csdn.net/virlene/archive/2007/03/15/1529844.aspx

  • 相关阅读:
    软件测试基础
    Python
    Python
    C# 打开帮助文档,打开电脑中其他应用或者文件
    C# 设置窗口大小为不可调、取消最大化、最小化窗口按键
    C# 控件置于最顶层、最底层、隐藏、显示
    C# 在窗口绘制图形(打点、画圆、画线)
    C# 不同窗口传递参数
    C# 禁止在textBox输入框输入非法字符
    C# 设定弹出窗体位置
  • 原文地址:https://www.cnblogs.com/ChangTan/p/2065324.html
Copyright © 2020-2023  润新知