加入星計劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴散
  • 作品版權(quán)保護
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長期合作伙伴
立即加入
  • 正文
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請入駐 產(chǎn)業(yè)圖譜

400行Python代碼實現(xiàn)文語處理助手(6) - 文語合成

2020/02/14
120
閱讀需 28 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

文語合成是 pzh-py-speech 的核心功能,pzh-py-speech 借助的是 pyttsx3 以及 eSpeak 引擎來實現(xiàn)的文語合成功能,今天痞子衡為大家介紹文語合成在 pzh-py-speech 中是如何實現(xiàn)的。

一、pyttsx3 簡介

pyttsx3 是一套基于實現(xiàn) SAPI5 文語合成引擎的 Python 封裝庫,該庫的設(shè)計者為 Natesh M Bhat,該庫其實是 pyTTS 和 pyttsx 項目的延續(xù),pyttsx3 主要是為 Python3 版本設(shè)計的,但同時也兼容 Python2。JaysPySPEECH 使用的是 pyttsx3 2.7。
  

pyttsx3 系統(tǒng)的官方主頁如下:

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 基于的文語合成內(nèi)核是 SAPI5 引擎,這是微軟公司開發(fā)的 TTS 引擎,其官方主頁如下:

SAPI5 官方文檔: https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ms723627(v%3dvs.85)
  

由于 pyttsx3 已經(jīng)將 SAPI5 封裝好,所有我們沒有必要關(guān)注 SAPI5 本身的 TTS 實現(xiàn)原理。

1.2 確認(rèn) PC 支持的語音包

在使用 pyttsx3 進行文語合成時,依賴的是當(dāng)前 PC 的語音環(huán)境,打開控制面板(Control Panel)->語言識別(Speech Recognition),可見到如下頁面:

  

痞子衡使用的 PC 是 Win10 英文版,故默認(rèn)僅有英文語音包(David 是男聲,Zira 是女聲),這點也可以使用如下 pyttsx3 調(diào)用代碼來確認(rèn):

import pyttsx3;

ttsObj = pyttsx3.init()
voices = ttsObj.getProperty('voices')
for voice in voices:
? ? print ('id = {} nname = {} n'.format(voice.id, voice.name))
  

代碼運行結(jié)果如下:

id = HKEY_LOCAL_MACHINESOFTWAREMicrosoftSpeechVoicesTokensTTS_MS_EN-US_DAVID_11.0
name = Microsoft David Desktop - English (United States)

id = HKEY_LOCAL_MACHINESOFTWAREMicrosoftSpeechVoicesTokensTTS_MS_EN-US_ZIRA_11.0
name = Microsoft Zira Desktop - English (United States)

1.3 為 PC 增加語音包支持

要想在使用 pzh-py-speech 時可以實現(xiàn)中英雙語合成,要確保 PC 上既有英文語音包也有中文語音包,痞子衡 PC 上當(dāng)前僅有英文語音包,故需要安裝中文語音包(安裝其他語言語音包的方法類似)。
  

Windows 系統(tǒng)下中文語音包有很多,可以使用第三方公司提供的語音包(比如 NeoSpeech 公司 ),也可以使用微軟提供的語音包,痞子衡選用的是經(jīng)典的慧慧語音包(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,安裝結(jié)束之后需要修改注冊表,打開 Run(Win 鍵+R 鍵)輸入"regedit"即可看到如下 registry 編輯界面,HKEY_LOCAL_MACHINESOFTWAREMicrosoftSpeechVoices 路徑下可以看到默認(rèn)語音包(DAVID, ZIRA),HKEY_LOCAL_MACHINESOFTWAREMicrosoftSpeech Serverv11.0Voices 路徑下可看到新安裝的語音包(HuiHui):

  

右鍵 HKEY_LOCAL_MACHINESOFTWAREMicrosoftSpeech Serverv11.0Voices,將其導(dǎo)出成 .reg 文件,使用文本編輯器打開這個 .reg 文件將其中"Speech Serverv11.0"全部替換成"Speech"并保存,然后將這個修改后的 .reg 文件再導(dǎo)入注冊表。

 

 

導(dǎo)入成功后,便可在注冊表和語音識別選項里看到 Huihui 身影:

Note: 上述修改僅針對 32bit 操作系統(tǒng),如果是 64bit 系統(tǒng),需要同時將

HKEY_LOCAL_MACHINESOFTWAREWOW6432NodeMicrosoftSpeech Serverv11.0Voices 路徑的注冊表按同樣方法也操作一遍。

二、eSpeak 簡介

由于 pyttsx3 僅能在線發(fā)聲,無法將合成后的語音保存為 wav 文件,因此痞子衡需要為 JaysPySPEECH 再尋一款可以保存為 wav 的 TTS 引擎。痞子衡選中的是 eSpeak,eSpeak 是一個簡潔的開源語音合成軟件,用 C 語言寫成,支持英語和其他很多語言,同時也支持 SAPI5 接口,合成的語音可以導(dǎo)出為 wav 文件。
  

eSpeak 的官方主頁如下:

eSpeak 官方主頁: http://espeak.sourceforge.net/

eSpeak 下載安裝: http://espeak.sourceforge.net/download.html

eSpeak 補充語言包: http://espeak.sourceforge.net/data/index.html
  

eSpeak 從標(biāo)準(zhǔn)輸入或者輸入文件中讀取文本,雖然語音輸出與真人聲音相去甚遠,但是在項目需要的時候,eSpeak 仍不失為一個簡便快捷的工具。
  

痞子衡將 eSpeak 1.48.04 安裝在了 C:tools_mcueSpeak 路徑下,進入這個路徑可以找到 eSpeakcommand_lineespeak.exe,這便是我們需要調(diào)用的工具,為了方便調(diào)用,你需要將"C:tools_mcueSpeakcommand_line"路徑加入系統(tǒng)環(huán)境變量 Path 中。
  

關(guān)于中文支持,在 eSpeakespeak-datazh_dict 文件里已經(jīng)包含了基本的中文字符,但是如要想要完整的中文支持,還需要下載 zh_listx.zip 中文語音包,解壓后將里面的 zh_listx 文件放到 eSpeakdictsource 目錄下,并且在 eSpeakdictsource 路徑下執(zhí)行命令"espeak --compile=zh",執(zhí)行成功后可以看到 eSpeakespeak-datazh_dict 文件明顯變大了。
  

eSpeak 對于 python 來說是個外部程序,我們需要借助 subprocess 來調(diào)用 espeak.exe,下面是示例代碼:

import subprocess
import sys
reload(sys)
sys.setdefaultencoding('utf-8')

enText = "Hello world"

zhText = u"你好世界"

txtFile = "C:/test.txt" ?#文件內(nèi)為中文

wavFile = "C:/test.wav"

# 在線發(fā)音(-v 是設(shè)置 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 的發(fā)音質(zhì)量,可以直接打開 eSpeakTTSApp.exe 應(yīng)用程序,軟件使用非常簡單:

三、pzh-py-speech 文語合成實現(xiàn)

文語合成實現(xiàn)主要分為兩部分:TTS, TTW。實現(xiàn) TTS 需要 import pyttsx3,實現(xiàn) TTW 需要借助 subprocess 調(diào)用 eSpeak,下面 痞子衡分別介紹這兩部分的實現(xiàn):

3.1 Text-to-Speech 實現(xiàn)

TTS 代碼實現(xiàn)其實很簡單,目前僅實現(xiàn)了 pyttsx3 引擎,并且僅支持中英雙語識別。具體到 pzh-py-speech 上主要是實現(xiàn) GUI 界面上"TTS"按鈕的回調(diào)函數(shù),即 textToSpeech(),如果用戶選定了配置參數(shù)(語言類型、發(fā)音人類型、TTS 引擎類型),并點擊了"TTS"按鈕,此時便會觸發(fā) textToSpeech()的執(zhí)行。代碼如下:

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 文本框獲取要轉(zhuǎn)換的文本
? ? ? ? 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':
? ? ? ? ? ?

# 嘗試創(chuàng)建 pyttsx3 文語合成對象 ttsObj
? ? ? ? ? ?

if self.ttsObj == None:
? ? ? ? ? ? ? ? ?self.ttsObj = pyttsx3.init()
? ? ? ? ? ?

# 搜索當(dāng)前 PC 是否存在指定語言類型的發(fā)聲人
? ? ? ? ? ?

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:
? ? ? ? ? ? ? ?

# 調(diào)用 pyttsx3 里的 say()和 runAndWait()完成文語合成,直接在線發(fā)音
? ? ? ? ? ? ? ?

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 實現(xiàn)

TTW 代碼實現(xiàn)也很簡單,目前僅實現(xiàn)了 eSpeak 引擎,并且僅支持中英雙語識別。具體到 pzh-py-speech 上主要是實現(xiàn) GUI 界面上"TTW"按鈕的回調(diào)函數(shù),即 textToWav(),如果用戶選定了配置參數(shù)(發(fā)音人性別類型、TTW 引擎類型),并點擊了"TTW"按鈕,此時便會觸發(fā) textToWav()的執(zhí)行。代碼如下:

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'
? ? ? ? ? ? ? ?

# 調(diào)用 espeak.exe 完成文字到 wav 文件的轉(zhuǎn)換

? ? ? ? ? ? ? ? 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")
  

至此,語音處理工具 pzh-py-speech 誕生之文語合成實現(xiàn)痞子衡便介紹完畢了,掌聲在哪里~~~

相關(guān)推薦

電子產(chǎn)業(yè)圖譜

碩士畢業(yè)于蘇州大學(xué)電子信息學(xué)院,目前就職于恩智浦(NXP)半導(dǎo)體MCU系統(tǒng)部門,擔(dān)任嵌入式系統(tǒng)應(yīng)用工程師。痞子衡會定期分享嵌入式相關(guān)文章