使用微軟TTS語音引擎實現文本朗讀

使用微軟TTS語音引擎實現文本朗讀

 

 版權聲明:本文爲博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/byxdaz/article/details/78443954

TTS(Text-To-Speech)是指文本語音的簡稱,即通過TTS引擎把文本轉化爲語音輸出。TTS語音引擎有微軟TTS語音引擎、科大訊飛語音引擎等。科大訊飛tts sdk參考這個頁面http://www.xfyun.cn/sdk/dispatcher

文本主要介紹如何使用微軟TTS語音引擎實現文本朗讀,以及生成wav格式的聲音文件。

1、語音引擎及語音庫的安裝

微軟TTS語音引擎提供了Windows Speech SDK開發包供編程者使用。Windows Speech SDK包含語音合成SS引擎和語音識別SR引擎兩種,語音合成引擎用於將文字轉換成語音輸出,語音識別引擎用於識別語音命令。

Windows Speech SDK可以在微軟的官網上免費下載,下載地址爲:http://www.microsoft.com/download/en/details.aspx?id=10121

在該下載界面中,選擇下載SpeechSDK51.exe、SpeechSDK51LangPach.exe和sapi.chm 即可。

SpeechSDK51.exe

語音合成引擎

SpeechSDK51LangPach.exe

語音庫,支持日語和簡體中文需要這個支持。

sapi.chm

幫助文檔

speechsdk51MSM.exe

語音引擎集成到你的產品跟產品一起發佈。解壓出來三個文件夾1033、1041和2052。其中,1033下主要是用於英文的TTS和SR的.msm文件,1041下主要是用於日文SR的.msm文件,2052下是用於中文TTS和SR的msm文件。

Sp5TTintXP.exe

XP下Mike和Mary語音。

下載完成後,先安裝語音引擎SpeechSDK51.exe,再安裝中文語音庫SpeechSDK51LangPach.exe。

目前最常用的Windows Speech SDK版本有三種:5.1、5.3和5.4。

       Windows Speech SDK 5.1版本支持xp系統和server 2003系統,需要下載安裝。XP系統默認只帶了個Microsoft Sam英文男聲語音庫,想要中文引擎就需要安裝Windows Speech SDK 5.1。

       Windows Speech SDK 5.3版本支持Vista系統和Server 2008系統,已經集成到系統裏。Vista和Server 2003默認帶Microsoft lili中文女聲語音庫和Microsoft Anna英文女聲語音庫。

       Windows Speech SDK 5.4版本支持Windows7系統,也已經集成到系統裏,不需要下載安裝。Win7系統同樣帶了Microsoft lili中文女聲語音庫和Microsoft Anna英文女聲語音庫。Microsoft lili支持中英文混讀。

2、SAPI接口的使用說明

       1)、基本朗讀過程的實現

在使用語音引擎之前進行初始化:

       ISpVoice *pSpVoice;        // 重要COM接口
       ::CoInitialize(NULL);         // COM初始化
       // 獲取ISpVoice接口
       CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_INPROC_SERVER, IID_ISpVoice, (void**)&pSpVoice);

獲取到ISpVoice接口以後,我們就可以通過pSpVoice指針調用SAPI接口了。

我們可以設置音量:pSpVoice->SetVolume(80);。SetVolume的參數即音量的範圍在0到100之間。

可以這樣朗讀字符串內容:pSpVoice->Speak(string, SPF_DEFAULT, NULL);。這樣string裏的內容就會被朗讀出來了,第二個參數SPF_DEFAULT表示使用默認設置,包括同步朗讀的設置。異步朗讀可以設置成 SPF_ASYNC。同步朗讀表示讀完string中的內容,speak函數纔會返回,而異步朗讀則將字符串送進去就返回,不會阻塞。

使用完語音引擎後應執行:

       pSpVoice->Release();
       ::CoUninitialize();      

這樣資源被釋放,語音朗讀過程結束。

以上就完成了一個簡單的語音合成朗讀的功能。

2)、ISpVoice的成員函數

雞啄米再簡單說明幾個ISpVoice接口的成員函數:

       HRESULT Speak(LPCWSTR *pwcs, DWORD dwFlags, ULONG *pulStreamNumber);

用於讀取字符串pwcs裏的內容。參數pwcs爲要朗讀的字符串。dwFlags是用於控制朗讀方式的標誌,具體意義可以查看文檔中的枚舉 SPEAKFLAGS。pulStreamNumber爲輸出參數,它指向本次朗讀請求對應的當前輸入流編號,每次朗讀一個字符串時都會有一個流編號返 回,異步朗讀時使用。

       HRESULT SetRate( long   RateAdjust);         // 設置朗讀速度,取值範圍:-1010
       HRESULT GetRate(long *pRateAdjust);        // 獲取朗讀速度   
       HRESULT SetVoice(ISpObjectToken   *pToken);    // 設置使用的語音庫
       HRESULT GetVoice(ISpObjectToken** ppToken);  // 獲取語音庫
       HRESULT Pause ( void );                              // 暫停朗讀
       HRESULT Resume ( void );                          // 恢復朗讀 
       //  在當前朗讀文本中根據lNumItems的符號向前或者向後跳過指定數量(lNumItems的絕對值)的句子。
       HRESULT Skip(LPCWSTR  *pItemType, long  lNumItems, ULONG *pulNumSkipped);
       //  播放WAV文件
       HRESULT SpeakStream(IStream   *pStream, DWORD      dwFlags, ULONG     *pulStreamNumber);
       // 將聲音輸出到WAV文件
       HRESULT SetOutput(IUnknown *pUnkOutput,BOOL fAllowFormatChanges); 
       HRESULT SetVolume(USHORT usVolume);      // 設置音量,範圍:0100
       HRESULT GetVolume(USHORT *pusVolume);  // 獲取音量
       HRESULT SetSyncSpeakTimeout(ULONG msTimeout);      // 設置同步朗讀超時時間,單位爲毫秒
       HRESULT GetSyncSpeakTimeout(ULONG *pmsTimeout);  // 獲取同步朗讀超時時間

因爲在同步朗讀時,speak函數是阻塞的,如果語音輸出設備被其他程序佔用,則speak則會一直等待,所以最好設置好超時時間,超時後speak函數自行返回。

3)、使用XML朗讀

在進行TTS開發時可以使用XML,SAPI可以分析XML標籤,通過XML能夠實現一些ISpVoice的成員函數的功能。比如設置語音庫、音量、語速等。此時speak函數的dwFlags參數要設置爲包含SPF_IS_XML。如:

       // 選擇語音庫Microsoft Sam
       pSpVoice->speak(L"<VOICE REQUIRED='NAME=Microsoft Sam'/>雞啄米", SPF_DEFAULT | SPF_IS_XML, NULL);
       // 設置音量
       <VOLUME LEVEL='90'>雞啄米</VOLUME>
       // 設置語言
       <lang langid='804'>雞啄米</lang>

804代表中文,409代表英文。如果用函數SpGetLanguageFromToken獲取語言時,0x804表示中文,0x409表示英文。

4)、設置SAPI通知消息。

      SAPI在朗讀的過程中,會給指定窗口發送消息,窗口收到消息後,可以主動獲取SAPI的事件,根據事件的不同,用戶可以得到當前SAPI的一些信息,比如正在朗讀的單詞的位置,當前的朗讀口型值(用於顯示動畫口型,中文語音的情況下並不提供這個事件)等等。要獲取SAPI的通知,首先要註冊一個消息:
  m_cpVoice->SetNotifyWindowMessage( hWnd,WM_TTSAPPCUSTOMEVENT, 0, 0 );
  這個代碼一般是在主窗口初始化的時候調用,hWnd是主窗口(或者接收消息的窗口)句柄。WM_TTSAPPCUSTOMEVENT是用戶自定義消息。在窗口響應WM_TTSAPPCUSTOMEVENT消息的函數中,通過如下代碼獲取sapi的通知事件:

   CSpEvent       event;  // 使用這個類,比用 SPEVENT結構更方便

    while(event.GetFrom(m_cpVoice) == S_OK )
    {
       switch( event.eEventId )
       {
         ...
       }
    }

  eEventID有很多種,比如SPEI_START_INPUT_STREAM表示開始朗讀,SPEI_END_INPUT_STREAM表示朗讀結束等。
   可以根據需要進行判斷使用。

5)、speech sdk語音識別,識別語音生成英文/中文等字符串。

具體參考這篇文章:http://blog.csdn.net/artemisrj/article/details/8723095

3、編程實例

1)、首先將需要將Windows Speech SDK開發包的頭文件和庫文件所在路徑添加到編譯器中。

 

2)、封裝tts操作類。

//TextToSpeech.h文件

 


 
  1. //tts

  2.  
  3. #pragma once

  4.  
  5. #include <sapi.h> //包含TTS語音引擎頭文件和庫文件

  6. #include <sphelper.h>

  7. #include <string.h>

  8. #pragma comment(lib, "sapi.lib")

  9.  
  10. class TextToSpeech

  11. {

  12. public:

  13. TextToSpeech(void);

  14. virtual ~TextToSpeech(void);

  15.  
  16. int Init();

  17. int UnInit();

  18.  
  19. //枚舉所有語音Token

  20. int EnumAudioToken(CString arrayVoicePackageName[],int nVoicePackageNameCount);

  21.  
  22. //創建SpVoice

  23. int CreateSpVoice();

  24. //釋放SpVoice

  25. int DeleteSpVoice();

  26. //重置SpVoice(用於臨時清除朗讀數據)

  27. int ResetSpVoice();

  28.  
  29. //設置朗讀速度(取值範圍:-10到10)

  30. int SetRate( long RateAdjust);

  31. //獲取朗讀速度

  32. int GetRate(long *pRateAdjust);

  33.  
  34. //設置使用的語音庫

  35. int SetVoice(ISpObjectToken *pToken);

  36. //獲取語音庫

  37. int GetVoice(unsigned int nIndex,ISpObjectToken** ppToken);

  38.  
  39. //設置音量(取值範圍:0到100)

  40. int SetVolume(USHORT usVolume);

  41. //獲取音量

  42. int GetVolume(USHORT *pusVolume);

  43.  
  44. //朗讀

  45. int Speak(CString strContent,DWORD dwFlags=SPF_DEFAULT);

  46. //朗讀生成文件

  47. int SpeakToWaveFile(CString strContent,char *pFilePathName,DWORD dwFlags=SPF_DEFAULT);

  48. //暫停朗讀

  49. int Pause();

  50. //繼續朗讀

  51. int Resume(); 

  52. //跳過部分朗讀

  53.  int Skip(CString strItemType="Sentence",long lNumItems=65535, ULONG *pulNumSkipped=NULL);

  54.  
  55. protected:

  56. IEnumSpObjectTokens * m_pIEnumSpObjectTokens;

  57. ISpObjectToken * m_pISpObjectToken;

  58. ISpVoice * m_pISpVoice;

  59. BOOL m_bComInit;

  60. };

 

 

 

//TextToSpeech.cpp文件

 


 
  1. #include "StdAfx.h"

  2. #include "TextToSpeech.h"

  3.  
  4. TextToSpeech::TextToSpeech(void)

  5. {

  6. m_pIEnumSpObjectTokens = NULL;

  7. m_pISpObjectToken = NULL;

  8. m_pISpVoice = NULL;

  9. m_bComInit = FALSE;

  10. }

  11.  
  12. TextToSpeech::~TextToSpeech(void)

  13. {

  14. }

  15.  
  16. int TextToSpeech::Init()

  17. {

  18. //初始化COM組件

  19. if(FAILED(::CoInitializeEx(NULL,0)))

  20. {

  21. //MessageBox("初始化COM組件失敗!", "提示", MB_OK|MB_ICONWARNING);

  22. return -1;

  23. }

  24.  
  25. m_bComInit = TRUE;

  26. return 0;

  27. }

  28.  
  29. int TextToSpeech::UnInit()

  30. {

  31. if(m_bComInit)

  32. {

  33. ::CoUninitialize();

  34. }

  35.  
  36. return 0;

  37. }

  38.  
  39. int TextToSpeech::EnumAudioToken(CString arrayVoicePackageName[],int nVoicePackageNameCount)

  40. {

  41. //枚舉所有語音Token

  42. if(SUCCEEDED(SpEnumTokens(SPCAT_VOICES, NULL, NULL, &m_pIEnumSpObjectTokens)))

  43. {

  44. //得到所有語音Token的個數

  45. ULONG ulTokensNumber = 0;

  46. m_pIEnumSpObjectTokens->GetCount(&ulTokensNumber);

  47.  
  48. //檢測該機器是否安裝有語音包

  49. if(ulTokensNumber == 0)

  50. {

  51. //MessageBox("該機器沒有安裝語音包!", "提示", MB_OK|MB_ICONWARNING);

  52. return -1;

  53. }

  54. if(ulTokensNumber > nVoicePackageNameCount)

  55. {

  56. //緩衝區過小

  57. return 0;

  58. }

  59.  
  60. //將語音包的名字加入數組中

  61. CString strVoicePackageName = _T("");

  62. CString strTokenPrefixText = _T("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Speech\\Voices\\Tokens\\");

  63. for(ULONG i=0; i<ulTokensNumber; i++)

  64. {

  65. m_pIEnumSpObjectTokens->Item(i, &m_pISpObjectToken);

  66. WCHAR* pChar;

  67. m_pISpObjectToken->GetId(&pChar);

  68. strVoicePackageName = pChar;

  69. strVoicePackageName.Delete(0, strTokenPrefixText.GetLength());

  70. arrayVoicePackageName[i] = strVoicePackageName;

  71. }

  72.  
  73. return ulTokensNumber;

  74. }

  75.  
  76. return -1;

  77. }

  78.  
  79. //創建SpVoice

  80. int TextToSpeech::CreateSpVoice()

  81. {

  82. //獲取ISpVoice接口

  83. if(FAILED(CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_INPROC_SERVER, IID_ISpVoice, (void**)&m_pISpVoice)))

  84. {

  85. //MessageBox("獲取ISpVoice接口失敗!", "提示", MB_OK|MB_ICONWARNING);

  86. return -1;

  87. }

  88.  
  89. return 0;

  90. }

  91. //釋放SpVoice

  92. int TextToSpeech::DeleteSpVoice()

  93. {

  94. if(m_pISpVoice != NULL)

  95. {

  96. m_pISpVoice->Release();

  97. }

  98. m_pISpVoice = NULL;

  99.  
  100. return 0;

  101. }

  102. //重置SpVoice

  103. int TextToSpeech::ResetSpVoice()

  104. {

  105. DeleteSpVoice();

  106. return CreateSpVoice();

  107. }

  108.  
  109. //設置朗讀速度(取值範圍:-10到10)

  110. int TextToSpeech::SetRate( long RateAdjust)

  111. {

  112. if(m_pISpVoice == NULL)

  113. return -1;

  114.  
  115. //設置播放速度

  116. m_pISpVoice->SetRate(RateAdjust);

  117. return 0;

  118. }

  119. //獲取朗讀速度

  120. int TextToSpeech::GetRate(long *pRateAdjust)

  121. {

  122. if(m_pISpVoice == NULL)

  123. return -1;

  124.  
  125. m_pISpVoice->GetRate(pRateAdjust);

  126. return 0;

  127. }

  128.  
  129. //設置使用的語音庫

  130. int TextToSpeech::SetVoice(ISpObjectToken *pToken)

  131. {

  132. if(m_pISpVoice == NULL)

  133. return -1;

  134.  
  135. m_pISpVoice->SetVoice(pToken);

  136. return 0;

  137. }

  138. //獲取語音庫

  139. int TextToSpeech::GetVoice(unsigned int nIndex,ISpObjectToken** ppToken)

  140. {

  141. if(m_pIEnumSpObjectTokens == NULL)

  142. return -1;

  143.  
  144. //設置語言

  145. m_pIEnumSpObjectTokens->Item(nIndex, ppToken);

  146. m_pISpObjectToken = *ppToken;

  147. return 0;

  148. }

  149.  
  150. //設置音量(取值範圍:0到100)

  151. int TextToSpeech::SetVolume(USHORT usVolume)

  152. {

  153. if(m_pISpVoice == NULL)

  154. return -1;

  155.  
  156. //設置音量大小

  157. m_pISpVoice->SetVolume(usVolume);

  158. return 0;

  159. }

  160. //獲取音量

  161. int TextToSpeech::GetVolume(USHORT *pusVolume)

  162. {

  163. if(m_pISpVoice == NULL)

  164. return -1;

  165.  
  166. //設置音量大小

  167. m_pISpVoice->GetVolume(pusVolume);

  168. return 0;

  169. }

  170.  
  171. //開始朗讀

  172. int TextToSpeech::Speak(CString strContent, DWORD dwFlags)

  173. {

  174. if(m_pISpVoice == NULL)

  175. return -1;

  176.  
  177. //開始進行朗讀

  178. HRESULT hSucess = m_pISpVoice->Speak(strContent.AllocSysString(), dwFlags, NULL);

  179.  
  180. return 0;

  181. }

  182. //朗讀生成文件

  183. int TextToSpeech::SpeakToWaveFile(CString strContent,char *pFilePathName,DWORD dwFlags)

  184. {

  185. if(m_pISpVoice == NULL || pFilePathName == NULL)

  186. return -1;

  187.  
  188. //生成WAV文件

  189. CComPtr<ISpStream> cpISpStream;

  190. CComPtr<ISpStreamFormat> cpISpStreamFormat;

  191. CSpStreamFormat spStreamFormat;

  192. m_pISpVoice->GetOutputStream(&cpISpStreamFormat);

  193. spStreamFormat.AssignFormat(cpISpStreamFormat);

  194. HRESULT hResult = SPBindToFile(pFilePathName, SPFM_CREATE_ALWAYS,

  195. &cpISpStream, &spStreamFormat.FormatId(), spStreamFormat.WaveFormatExPtr());

  196. if(SUCCEEDED(hResult))

  197. {

  198. m_pISpVoice->SetOutput(cpISpStream, TRUE);

  199. m_pISpVoice->Speak(strContent.AllocSysString(), dwFlags, NULL);

  200. return 0;

  201. //MessageBox("生成WAV文件成功!", "提示", MB_OK);

  202. }

  203. else

  204. {

  205. //MessageBox("生成WAV文件失敗!", "提示", MB_OK|MB_ICONWARNING);

  206. return 1;

  207. }

  208. }

  209.  
  210. //暫停朗讀

  211. int TextToSpeech::Pause()

  212. {

  213. if(m_pISpVoice != NULL)

  214. {

  215. m_pISpVoice->Pause();

  216. }

  217.  
  218. return 0;

  219. }

  220. //繼續朗讀

  221. int TextToSpeech::Resume()

  222. {

  223. if(m_pISpVoice != NULL)

  224. {

  225. m_pISpVoice->Resume();

  226. }

  227.  
  228. return 0;

  229. }

//跳過部分朗讀 int TextToSpeech::Skip(CString strItemType,long lNumItems, ULONG *pulNumSkipped) {  if(m_pISpVoice == NULL || strItemType.GetLength() == 0)   return -1;

 m_pISpVoice->Skip(strItemType.AllocSysString(), lNumItems,pulNumSkipped);  return 0; }

 

 

 

 

3)調用實例代碼。


 
  1. TextToSpeech ttsSpeech;

  2. ttsSpeech.Init();

  3. CString arrayVoicePackageName[50] = {0};

  4. int nVoicePackageNameCount = 50;

  5. int nCount = ttsSpeech.EnumAudioToken(arrayVoicePackageName,nVoicePackageNameCount);

  6. ttsSpeech.CreateSpVoice();

  7. ISpObjectToken* ppToken = NULL;

  8. ttsSpeech.GetVoice(0,&ppToken);

  9. ttsSpeech.SetVoice(ppToken);

  10. ttsSpeech.SetRate(0);

  11. ttsSpeech.SetVolume(100);

  12. ttsSpeech.Speak("我是中國人");

  13. //ttsSpeech.SpeakToWaveFile("我是中國人","d:\\11.wav");

  14. ttsSpeech.DeleteSpVoice();

  15. ttsSpeech.UnInit();

 

4、注意事項

1)、sphelper.h編譯錯誤解決方案

SAPI 包含sphelper.h編譯錯誤解決方案 在使用Microsoft Speech SDK 5.1開發語音識別程序時,包含了頭文件“sphelper.h”和庫文件“sapi.lib”。編譯時出錯: 1>c:\program files\microsoft speech sdk 5.1\include\sphelper.h(769): error C4430: missing type specifier - int assumed. Note: C++ does not supportdefault-int 1>c:\program files\microsoft speech sdk5.1\include\sphelper.h(1419) : error C4430: missing type specifier - intassumed. Note: C++ does not support default-int 1>c:\program files\microsoftspeech sdk 5.1\include\sphelper.h(2373) : error C2065: 'psz' : undeclaredidentifier 1>c:\program files\microsoft speech sdk5.1\include\sphelper.h(2559) : error C2440: 'initializing' : cannot convert from'CSpDynamicString' to 'SPPHONEID *' 1> No user-defined-conversion operatoravailable that can perform this conversion, or the operator cannot be called1>c:\program files\microsoft speech sdk 5.1\include\sphelper.h(2633) : errorC2664: 'wcslen' : cannot convert parameter 1 from 'SPPHONEID *' to 'constwchar_t *' 1> Types pointed to are unrelated; conversion requiresreinterpret_cast, C-style cast or function-style cast 搜索了一圈,根據大家的經驗彙總,應該是Speech代碼編寫時間太早,語法不嚴密。而VS2008對於語法檢查非常嚴格,導致編譯無法通過。修改頭文件中的以下行即可正常編譯:

 Ln769 const ulLenVendorPreferred = wcslen(pszVendorPreferred);

             const unsigned long ulLenVendorPreferred = wcslen(pszVendorPreferred);

Ln 1418static CoMemCopyWFEX(const WAVEFORMATEX * pSrc, WAVEFORMATEX ** ppCoMemWFEX)

              static HRESULT CoMemCopyWFEX(const WAVEFORMATEX * pSrc, WAVEFORMATEX ** ppCoMemWFEX)

Ln 2372for (const WCHAR * psz = (const WCHAR *)lParam; *psz; psz++) {}

             const WCHAR * psz; for (psz = (const WCHAR *)lParam; *psz; psz++) {}

Ln 2559SPPHONEID* pphoneId = dsPhoneId;

              SPPHONEID* pphoneId = (SPPHONEID*)((WCHAR *)dsPhoneId);

Ln 2633pphoneId += wcslen(pphoneId) + 1;

              pphoneId+= wcslen((const wchar_t *)pphoneId) + 1; 

 

2)、Speak指定爲SPF_ASYNC(異步)時,不要過早的釋放ISpVoice對象,否則就沒有聲音,因爲ISpVoice生命週期結束了,就不會播放。一般將ISpVoice對象放到類的成員變量中,類析構時才釋放ISpVoice對象。

3)、Speak第一次朗讀時很慢,因爲加載引擎需要一段時間,可以使用線程預先Speak("",SPF_ASYNC)而加載引擎,但需要注意的是在初始化COM的時候使用CoInitializeEx,而不要使用CoInitialize。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章