痞子衡嵌入式:語音處理工具Jays-PySPEECH誕生記(6)- 文語合成實現(pyttsx3, eSpeak1.48.04)

痞子衡發表於2017-06-24

  大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家介紹的是語音處理工具Jays-PySPEECH誕生之文語合成實現

  文語合成是Jays-PySPEECH的核心功能,Jays-PySPEECH藉助的是pyttsx3以及eSpeak引擎來實現的文語合成功能,今天痞子衡為大家介紹文語合成在Jays-PySPEECH中是如何實現的。

一、pyttsx3簡介

  pyttsx3是一套基於實現SAPI5文語合成引擎的Python封裝庫,該庫的設計者為Natesh M Bhat,該庫其實是 pyTTSpyttsx 專案的延續,pyttsx3主要是為Python3版本設計的,但同時也相容Python2。JaysPySPEECH使用的是pyttsx3 2.7。
  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),可見到如下頁面:

痞子衡嵌入式:語音處理工具Jays-PySPEECH誕生記(6)- 文語合成實現(pyttsx3, eSpeak1.48.04)

  痞子衡使用的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竟然也無法開啟):

痞子衡嵌入式:語音處理工具Jays-PySPEECH誕生記(6)- 文語合成實現(pyttsx3, eSpeak1.48.04)

  先安裝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):

痞子衡嵌入式:語音處理工具Jays-PySPEECH誕生記(6)- 文語合成實現(pyttsx3, eSpeak1.48.04)

  右鍵HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech Server\v11.0\Voices,將其匯出成.reg檔案,使用文字編輯器開啟這個.reg檔案將其中"\Speech Server\v11.0"全部替換成"\Speech"並儲存,然後將這個修改後的.reg檔案再匯入登錄檔。

痞子衡嵌入式:語音處理工具Jays-PySPEECH誕生記(6)- 文語合成實現(pyttsx3, eSpeak1.48.04)

  匯入成功後,便可在登錄檔和語音識別選項裡看到Huihui身影:

痞子衡嵌入式:語音處理工具Jays-PySPEECH誕生記(6)- 文語合成實現(pyttsx3, eSpeak1.48.04)

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從標準輸入或者輸入檔案中讀取文字,雖然語音輸出與真人聲音相去甚遠,但是在專案需要的時候,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誕生記(6)- 文語合成實現(pyttsx3, eSpeak1.48.04)

三、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誕生之文語合成實現痞子衡便介紹完畢了,掌聲在哪裡~~~

相關文章