大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家介紹的是語音處理工具Jays-PySPEECH誕生之文語合成實現。
文語合成是Jays-PySPEECH的核心功能,Jays-PySPEECH藉助的是pyttsx3以及eSpeak引擎來實現的文語合成功能,今天痞子衡為大家介紹文語合成在Jays-PySPEECH中是如何實現的。
一、pyttsx3簡介
pyttsx3是一套基於實現SAPI5文語合成引擎的Python封裝庫,該庫的設計者為Natesh M Bhat,該庫其實是 pyTTS 和 pyttsx 專案的延續,pyttsx3主要是為Python3版本設計的,但同時也相容Python2。JaysPySPEECH使用的是pyttsx3 2.7。
pyttsx3系統的官方主頁如下:
- pyttsx3官方主頁: https://github.com/nateshmbhat/pyttsx3
- pyttsx3安裝方法: https://pypi.org/project/pyttsx3/
pyttsx3的使用足夠簡單,其官方文件 https://pyttsx3.readthedocs.io/en/latest/engine.html 半小時即可讀完,下面是最簡單的一個示例程式碼:
import pyttsx3;
engine = pyttsx3.init();
engine.say("I will speak this text");
engine.runAndWait() ;
1.1 Microsoft Speech API (SAPI5)引擎
前面痞子衡講了pyttsx3基於的文語合成核心是SAPI5引擎,這是微軟公司開發的TTS引擎,其官方主頁如下:
由於pyttsx3已經將SAPI5封裝好,所有我們沒有必要關注SAPI5本身的TTS實現原理。
1.2 確認PC支援的語音包
在使用pyttsx3進行文語合成時,依賴的是當前PC的語音環境,開啟控制皮膚(Control Panel)->語言識別(Speech Recognition),可見到如下頁面:
痞子衡使用的PC是Win10英文版,故預設僅有英文語音包(David是男聲,Zira是女聲),這點也可以使用如下pyttsx3呼叫程式碼來確認:
import pyttsx3;
ttsObj = pyttsx3.init()
voices = ttsObj.getProperty('voices')
for voice in voices:
print ('id = {} \nname = {} \n'.format(voice.id, voice.name))
程式碼執行結果如下:
id = HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices\Tokens\TTS_MS_EN-US_DAVID_11.0 name = Microsoft David Desktop - English (United States) id = HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices\Tokens\TTS_MS_EN-US_ZIRA_11.0 name = Microsoft Zira Desktop - English (United States)
1.3 為PC增加語音包支援
要想在使用Jays-PySPEECH時可以實現中英雙語合成,要確保PC上既有英文語音包也有中文語音包,痞子衡PC上當前僅有英文語音包,故需要安裝中文語音包(安裝其他語言語音包的方法類似)。
Windows系統下中文語音包有很多,可以使用第三方公司提供的語音包(比如 NeoSpeech公司 ),也可以使用微軟提供的語音包,痞子衡選用的是經典的慧慧語音包(zh-CN_HuiHui)。
進入 Microsoft Speech Platform - Runtime (Version 11) 和 Microsoft Speech Platform - Runtime Languages (Version 11) 下載頁面將選中檔案下載(親測僅能用Google Chrome瀏覽器才能正常訪問,IE竟然也無法開啟):
先安裝SpeechPlatformRuntime.msi(雙擊安裝即可),安裝完成之後重啟電腦,再安裝MSSpeech_TTS_zh-CN_HuiHui.msi,安裝結束之後需要修改登錄檔,開啟Run(Win鍵+R鍵)輸入"regedit"即可看到如下registry編輯介面,HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices路徑下可以看到預設語音包(DAVID, ZIRA),HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech Server\v11.0\Voices路徑下可看到新安裝的語音包(HuiHui):
右鍵HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech Server\v11.0\Voices,將其匯出成.reg檔案,使用文字編輯器開啟這個.reg檔案將其中"\Speech Server\v11.0"全部替換成"\Speech"並儲存,然後將這個修改後的.reg檔案再匯入登錄檔。
匯入成功後,便可在登錄檔和語音識別選項裡看到Huihui身影:
Note: 上述修改僅針對32bit作業系統,如果是64bit系統,需要同時將HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Speech Server\v11.0\Voices路徑的登錄檔按同樣方法也操作一遍。
二、eSpeak簡介
由於pyttsx3僅能線上發聲,無法將合成後的語音儲存為wav檔案,因此痞子衡需要為JaysPySPEECH再尋一款可以儲存為wav的TTS引擎。痞子衡選中的是eSpeak,eSpeak是一個簡潔的開源語音合成軟體,用C語言寫成,支援英語和其他很多語言,同時也支援SAPI5介面,合成的語音可以匯出為wav檔案。
eSpeak的官方主頁如下:
- eSpeak官方主頁: http://espeak.sourceforge.net/
- eSpeak下載安裝: http://espeak.sourceforge.net/download.html
- eSpeak補充語言包: http://espeak.sourceforge.net/data/index.html
eSpeak從標準輸入或者輸入檔案中讀取文字,雖然語音輸出與真人聲音相去甚遠,但是在專案需要的時候,eSpeak仍不失為一個簡便快捷的工具。
痞子衡將eSpeak 1.48.04安裝在了C:\tools_mcu\eSpeak路徑下,進入這個路徑可以找到\eSpeak\command_line\espeak.exe,這便是我們需要呼叫的工具,為了方便呼叫,你需要將"C:\tools_mcu\eSpeak\command_line"路徑加入系統環境變數Path中。
關於中文支援,在\eSpeak\espeak-data\zh_dict檔案裡已經包含了基本的中文字元,但是如要想要完整的中文支援,還需要下載zh_listx.zip中文語音包,解壓後將裡面的zh_listx檔案放到\eSpeak\dictsource目錄下,並且在\eSpeak\dictsource路徑下執行命令"espeak --compile=zh",執行成功後可以看到\eSpeak\espeak-data\zh_dict檔案明顯變大了。
eSpeak對於python來說是個外部程式,我們需要藉助subprocess來呼叫espeak.exe,下面是示例程式碼:
import subprocess
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
enText = "Hello world"
zhText = u"你好世界"
txtFile = "C:/test.txt" #檔案內為中文
wavFile = "C:/test.wav"
# 線上發音(-v是設定voice,en是英文,m3男聲,zh是中文,f3是女聲)
subprocess.call(["espeak", "-ven+m3", enText])
subprocess.call(["espeak", "-vzh+f3", zhText])
# 儲存為wav檔案(第一種方法僅能儲存英文wav,如果想儲存其他語言wav需要使用第二種方法)
subprocess.call(["espeak","-w"+wavFile, enText])
subprocess.call(["espeak","-vzh+f3", "-f"+txtFile, "-w"+wavFile])
如果想直接體驗eSpeak的發音質量,可以直接開啟\eSpeak\TTSApp.exe應用程式,軟體使用非常簡單:
三、Jays-PySPEECH文語合成實現
文語合成實現主要分為兩部分:TTS, TTW。實現TTS需要import pyttsx3,實現TTW需要藉助subprocess呼叫eSpeak,下面 痞子衡分別介紹這兩部分的實現:
3.1 Text-to-Speech實現
TTS程式碼實現其實很簡單,目前僅實現了pyttsx3引擎,並且僅支援中英雙語識別。具體到Jays-PySPEECH上主要是實現GUI介面上"TTS"按鈕的回撥函式,即textToSpeech(),如果使用者選定了配置引數(語言型別、發音人型別、TTS引擎型別),並點選了"TTS"按鈕,此時便會觸發textToSpeech()的執行。程式碼如下:
reload(sys)
sys.setdefaultencoding('utf-8')
import pyttsx3
class mainWin(win.speech_win):
def __init__(self, parent):
# ...
self.ttsObj = None
def refreshVoice( self, event ):
languageType, languageName = self.getLanguageSelection()
engineType = self.m_choice_ttsEngine.GetString(self.m_choice_ttsEngine.GetSelection())
if engineType == 'pyttsx3 - SAPI5':
if self.ttsObj == None:
self.ttsObj = pyttsx3.init()
voices = self.ttsObj.getProperty('voices')
voiceItems = [None] * len(voices)
itemIndex = 0
for voice in voices:
voiceId = voice.id.lower()
voiceName = voice.name.lower()
if (voiceId.find(languageType.lower()) != -1) or (voiceName.find(languageName.lower()) != -1):
voiceItems[itemIndex] = voice.name
itemIndex += 1
voiceItems = voiceItems[0:itemIndex]
self.m_choice_voice.Clear()
self.m_choice_voice.SetItems(voiceItems)
else:
voiceItem = ['N/A']
self.m_choice_voice.Clear()
self.m_choice_voice.SetItems(voiceItem)
def textToSpeech( self, event ):
# 獲取語音語言型別(English/Chinese)
languageType, languageName = self.getLanguageSelection()
# 從asrttsText文字框獲取要轉換的文字
lines = self.m_textCtrl_asrttsText.GetNumberOfLines()
if lines != 0:
data = ''
for i in range(0, lines):
data += self.m_textCtrl_asrttsText.GetLineText(i)
else:
return
ttsEngineType = self.m_choice_ttsEngine.GetString(self.m_choice_ttsEngine.GetSelection())
if ttsEngineType == 'pyttsx3 - SAPI5':
# 嘗試建立pyttsx3文語合成物件ttsObj
if self.ttsObj == None:
self.ttsObj = pyttsx3.init()
# 搜尋當前PC是否存在指定語言型別的發聲人
hasVoice = False
voices = self.ttsObj.getProperty('voices')
voiceSel = self.m_choice_voice.GetString(self.m_choice_voice.GetSelection())
for voice in voices:
#print ('id = {} \nname = {} \nlanguages = {} \n'.format(voice.id, voice.name, voice.languages))
voiceId = voice.id.lower()
voiceName = voice.name.lower()
if (voiceId.find(languageType.lower()) != -1) or (voiceName.find(languageName.lower()) != -1):
if (voiceSel == '') or (voiceSel == voice.name):
hasVoice = True
break
if hasVoice:
# 呼叫pyttsx3裡的say()和runAndWait()完成文語合成,直接線上發音
self.ttsObj.setProperty('voice', voice.id)
self.ttsObj.say(data)
self.statusBar.SetStatusText("TTS Conversation Info: Run and Wait")
self.ttsObj.runAndWait()
self.statusBar.SetStatusText("TTS Conversation Info: Successfully")
else:
self.statusBar.SetStatusText("TTS Conversation Info: Language is not supported by current PC")
self.textToWav(data, languageType)
else:
self.statusBar.SetStatusText("TTS Conversation Info: Unavailable TTS Engine")
3.2 Text-to-Wav實現
TTW程式碼實現也很簡單,目前僅實現了eSpeak引擎,並且僅支援中英雙語識別。具體到Jays-PySPEECH上主要是實現GUI介面上"TTW"按鈕的回撥函式,即textToWav(),如果使用者選定了配置引數(發音人性別型別、TTW引擎型別),並點選了"TTW"按鈕,此時便會觸發textToWav()的執行。程式碼如下:
import subprocess
class mainWin(win.speech_win):
def textToWav(self, text, language):
fileName = self.m_textCtrl_ttsFileName.GetLineText(0)
if fileName == '':
fileName = 'tts_untitled1.wav'
ttsFilePath = os.path.join(os.path.dirname(os.path.abspath(os.path.dirname(__file__))), 'conv', 'tts', fileName)
ttwEngineType = self.m_choice_ttwEngine.GetString(self.m_choice_ttwEngine.GetSelection())
if ttwEngineType == 'eSpeak TTS':
ttsTextFile = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'ttsTextTemp.txt')
ttsTextFileObj = open(ttsTextFile, 'wb')
ttsTextFileObj.write(text)
ttsTextFileObj.close()
try:
#espeak_path = "C:/tools_mcu/eSpeak/command_line/espeak.exe"
#subprocess.call([espeak_path, "-v"+languageType[0:2], text])
gender = self.m_choice_gender.GetString(self.m_choice_gender.GetSelection())
gender = gender.lower()[0] + '3'
# 呼叫espeak.exe完成文字到wav檔案的轉換
subprocess.call(["espeak", "-v"+language[0:2]+'+'+gender, "-f"+ttsTextFile, "-w"+ttsFilePath])
except:
self.statusBar.SetStatusText("TTW Conversation Info: eSpeak is not installed or its path is not added into system environment")
os.remove(ttsTextFile)
else:
self.statusBar.SetStatusText("TTW Conversation Info: Unavailable TTW Engine")
至此,語音處理工具Jays-PySPEECH誕生之文語合成實現痞子衡便介紹完畢了,掌聲在哪裡~~~