关于Speech sdk 在.net中tts的应用 学习

关于Microsoft Speech SDK 中TTS的研究

 

 

【摘要】本文介绍了利用Microsoft Speech SDK 5.1中的text-to-speech(TTS),采用C#作为开发语言,Visual Studio 2005作为开发工具,实现了普通中英文混合文本的朗读,和带XML标记的文本的朗读,并且可将朗读出来的内容保存为文件。

 

【关键字】Speech SDK,TTS,text-to-speech,朗读

 

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   用于设置文本朗读的音量;

l   分别用于设置文本朗读的绝对速度和相对速度;

l   分别用于设置文本朗读的绝对语调和相对语调;

l   在他们之间的句子被视为强调;

l   可以将单词逐个字母的拼写出来;

l   表示停止发声,并保持500微秒;

l   02/03/07 可以按要求朗读出日期

l   用于设置朗读所用的语言,其中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 对象来实例化,这个接口是实现文本朗读的核心。接着提供用于朗读文本的方法,例如:

   ///

        /// 读出Xml文件内容

        ///

        /// Xml文件内容

        public void SpeakXml(string xml)

        {

            voice.Speak(xml, SpeechVoiceSpeakFlags.SVSFIsXML | SpeechVoiceSpeakFlags.SVSFlagsAsync);

        }

并且使用Pause()、Resume()、Stop()等方法来控制朗读暂停、继续和停止。至于保存音频文件,我们可以使用以下方法,将音频输出流指向一个文件流,来完成保存工作。

        ///

        /// 保存音频到文件

        ///

        /// 要读的Xml格式的内容

        /// 要保存的文件名

        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标记,即,将属于中文的句子加上中文语音XML标记,即。这样调用SpeakXml方法就可以实现中英文混合朗读。

在这里我选择第二种方法,在类中增加静态方法:AddXmlLangTag,返回添加过标记得文本内容。

///

        /// 设置中文语音

        ///

        public void SetChinaVoice()

        {

            voice.Voice = voice.GetVoices(string.Empty, string.Empty).Item(0);

        }

 

        ///

        /// 设置英文语音

        ///

        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 { ///

/// TTS语音朗读类 /// public class Talker { private static Talker talker = null; //Talker对象 private SpVoice voice = null; //SpVoice对象,用于实现TTS /// /// 默认的英文语音ID /// public const string DefaultEnglishLangID = "409"; /// /// 默认的中文语音ID /// public const string DefaultChineseLangID = "804"; /// /// 构造函数 /// private Talker() { voice = new SpeechLib.SpVoiceClass(); } /// /// 获取或设置音量 /// public int Volume { get { return voice.Volume; } set { voice.Volume = value; } } /// /// 获取或设置语速 /// public int Rate { get { return voice.Rate; } set { voice.Rate = value; } } /// /// 获取或设置语音 /// public SpObjectToken Voice { get { return voice.Voice; } set { voice.Voice = value; } } /// /// 获得实例 /// /// Talker对象 public static Talker Instance() { if (talker == null) { talker = new Talker(); } return talker; } /// /// 给文本内容添加语言标记 /// /// 文本内容 /// 经过语言标记的文本内容 public static string AddXmlLangTag(string src) { return AddXmlLangTag(src, Talker.DefaultEnglishLangID, Talker.DefaultChineseLangID); } /// /// 给文本内容添加语言标记 /// /// 文本内容 /// 英文语音ID /// 中文语音ID /// 经过语言标记的文本内容 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,如果是则加上 * 如果不是就加上" + sub + ""); startPos = endPos; } isAscii = false; endPos++; } else { if (!isAscii) { string sub = src.Substring(startPos, endPos - startPos); dest.Append("" + sub + ""); startPos = endPos; } isAscii = true; endPos++; } } string r = src.Substring(startPos, endPos - startPos); string langID = isAscii == true ? englishLangID : chineseLangID; dest.Append("" + r + ""); return dest.ToString(); } /// /// 给文本内容添加语调标记 /// /// 文本内容 /// 语调 /// 经过语调标记的文本内容 public static string AddXmlPitchTag(string src, int pitch) { string pitchTag = ""; return pitchTag + src; } /// /// 设置中文语音 /// public void SetChinaVoice() { voice.Voice = voice.GetVoices(string.Empty, string.Empty).Item(0); } /// /// 设置英文语音 /// public void SetEnglishVoice() { voice.Voice = voice.GetVoices(string.Empty, string.Empty).Item(1); } /// /// 读出文件内容 /// /// 文件内容 public void SpeakText(string text) { voice.Speak(text, SpeechVoiceSpeakFlags.SVSFDefault | SpeechVoiceSpeakFlags.SVSFlagsAsync); } /// /// 读出Xml文件内容 /// /// Xml文件内容 public void SpeakXml(string xml) { voice.Speak(xml, SpeechVoiceSpeakFlags.SVSFIsXML | SpeechVoiceSpeakFlags.SVSFlagsAsync); } /// /// 读出文件内容 /// /// 文件名 public void SpeakFile(string fileName) { voice.Speak(fileName, SpeechVoiceSpeakFlags.SVSFIsFilename | SpeechVoiceSpeakFlags.SVSFlagsAsync); } /// /// 暂停 /// public void Pause() { voice.Pause(); } /// /// 继续 /// public void Resume() { voice.Resume(); } /// /// 停止说话 /// public void Stop() { voice.Speak(string.Empty, SpeechVoiceSpeakFlags.SVSFPurgeBeforeSpeak); } /// /// 保存音频到文件 /// /// 要读的Xml格式的内容 /// 要保存的文件名 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(); } } } 本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/virlene/archive/2007/03/15/1529844.aspx

你可能感兴趣的