記一次講故事機械人的開發-我有故事,讓機械人來讀

記一次講故事機械人的開發-我有故事,讓機械人來讀

最近工作較忙,回家閑下來只想閉目休息,一分鐘屏幕都不想再看,然而我又想追更之前看的小說,於是,需求來了——我需要一個給我講故事的機械人!

瀏覽器或者閱讀器App里其實也有朗讀功能,但是比較僵硬,總是將引人入勝的情節念成流水賬,分分鐘讓人棄坑,所以我考慮自己使用爬蟲定時下載更新的章節,而後將文字合成存儲到音頻文件,這樣不僅可以選擇一個靠譜的語音合成工具來處理文字,而且保存下來的音頻還能反覆收聽,一舉兩得。

文本整合容易,但是如何將其快速轉換成音頻呢?難道要自己訓練模型「煉丹」解決?no no ,費力不討好,畢竟自己手頭這點算法知識非常淺顯,而且硬件條件也不允許,本着「能用就行」的原則,我決定先使用市面上開放平台的產品來解決。經過對比,發現有道智雲的語音合成還不錯(此處可體驗),決定使用有道智雲的語音合成API進行開發。

效果先睹為快:

我拿來朱自清先生的《荷塘月色》的其中兩段作為實驗素材,開發了簡單的demo,走通了從加載文本到生成音頻文件的邏輯,下面我來詳細介紹開發過程。

需要語音合成的文本:

待翻譯的文本

合成結果(第一段):


對應音頻鏈接為:

//music.xf1433.com/up/view.php/14f5c51944701491ebeb73317fb29e16.mp3

合成結果(第二段):


對應音頻鏈接為:

//music.xf1433.com/up/view.php/3f10780ede6ca4abcc376b6e316ab55c.mp3

調用API接口的準備工作

首先,是需要在有道智雲的個人頁面上創建實例、創建應用、綁定應用和實例,獲取到應用的id和密鑰。具體個人註冊的過程和應用創建過程詳見文章分享一次批量文件翻譯的開發過程

創建實例

開發過程詳細介紹

下面介紹具體的代碼開發過程。

首先根據文檔分析有道智雲的API輸入輸出規範。語音合成API調用十分簡單,該API採用https方式通信,所需參數如下表:

字段名 類型 含義 必填 備註
q text 待合成音頻文件的文本字符串 True 比如:您好
langType text 合成文本的語言類型 True 支持語言
appKey text 應用 ID True 可在 應用管理 查看
salt text UUID True UUID
sign text True MD5(應用ID+q+salt+應用密鑰)
voice text 翻譯結果發音選擇,0為女聲,1為男聲,默認為女聲 false 0
format text 目標音頻格式,支持mp3 false mp3
speed text 合成音頻的語速 false 比如:”1″為正常速度
volume text 合成音頻的音量 false 正常為”1.00″,最大為”5.00″,最小為”0.50″

簡單概括,組織好自己的語言(utf-8編碼文本),輔以簽名等必要參數,並告訴API所需要的音頻特徵,即可得到一份令人滿意的合成音頻。

接口輸出中,如果合成成功,正常返回為二進制語音文件,具體header信息 Content-type: audio/mp3,如果合成出現錯誤,則會返回json結果,具體header信息為:Content-type: application/json,可據此判斷運行情況。

Demo開發:

這個demo使用python3開發,包括maindow.py,synthesis.py,synthesistool.py三個文件,分別為demo的界面、界面邏輯處理和語音合成接口調用工具封裝。

  1. 界面部分:

    界面部分代碼如下,比較簡單。

    root=tk.Tk()
    root.title("youdao speech synthesis test")
    frm = tk.Frame(root)
    frm.grid(padx='50', pady='50')
    # 文件選取按鈕
    btn_get_file = tk.Button(frm, text='選擇待合成文件', command=get_files)
    btn_get_file.grid(row=0, column=0, ipadx='3', ipady='3', padx='10', pady='20')
    # 所選文件列表展示框
    text1 = tk.Text(frm, width='40', height='10')
    text1.grid(row=0, column=1)
    # 啟動按鈕
    btn_sure=tk.Button(frm,text="合成",command=synthesis_files)
    btn_sure.grid(row=1,column=1)
    

    其中啟動按鈕btn_sure的綁定事件synthesis_files()來收集帶所有的文本文件,啟動合成,並打印運行結果:

    def synthesis_files():
        if syn_m.file_paths:
            message=syn_m.get_synthesis_result()
            tk.messagebox.showinfo("提示", message)
            os.system('start' + '.\\result')
        else :
            tk.messagebox.showinfo("提示","無文件")
    
  2. synthesis.py

    這裡主要是配合界面實現一些文本讀取和請求接口處理返回值的邏輯。首先定義一個Synthesis_model

    class Synthesis_model():
        def __init__(self,file_paths,result_root_path,syn_type):
            self.file_paths=file_paths				# 待合成文件路徑
            self.result_root_path=result_root_path  # 結果路徑
            self.syn_type=syn_type                  # 合成類型
    

    get_synthesis_result()方法實現了批量讀取文件並調用合成方法、處理返回信息的邏輯:

        def get_synthesis_result(self):
            syn_result=""
            for file_path in self.file_paths:
                # 讀取文件
                file_name=os.path.basename(file_path).split('.')[0]
                file_content=open(file_path,encoding='utf-8').read()
                # 調用合成方法
                result=self.synthesis_use_netease(file_name,file_content)
                # 處理返回消息
                if result=="1":
                    syn_result=syn_result+file_path+" ok !\n"
                else:
                    syn_result=syn_result+file_path+result
            return syn_result
    

    單獨定義了方法synthesis_use_netease()具體實現調用API的方法,這樣增加了demo的擴展性,實現了一種合成模塊可插拔的松耦合形式:

    def synthesis_use_netease(self,file_name,text):
        result=connect(text,'zh-CHS')
        print(result)
        if result.headers['Content-Type']=="audio/mp3":
            millis = int(round(time.time() * 1000))
            filePath = "./result/" + file_name+"-"+str(millis) + ".mp3"
            fo = open(filePath, 'wb')
            fo.write(result.content)
            fo.close()
            return "1"
        else:
            return "error:"+result.content
    
  3. synthesistool.py
    1. synthesistool.py中是和請求有道智雲API直接相關的一些方法,最核心的是connect()方法,整合了API所要求的各個參數,並調用執行請求的方法do_request(),並返回API處理結果。

      def connect(text,lang_type):
          q = text
      
          data = {}
          data['langType'] = lang_type
          salt = str(uuid.uuid1())
          signStr = APP_KEY + q + salt + APP_SECRET
          sign = encrypt(signStr)
          data['appKey'] = APP_KEY
          data['q'] = q
          data['salt'] = salt
          data['sign'] = sign
      
          response = do_request(data)
          return response
      

    需要體驗一下的小夥伴,請下載我的代碼自行嘗試或去官網體驗 : P。項目地址://github.com/LemonQH/SpeechSynthesis

    特別提示:
    1、運行demo時,需要替換synthesistool.py模塊中的 APP_KEY 、 APP_SECRET為你自己生成的 APP_KEY、APP_SECRET哦
    2、該工程默認存放結果存在./result 文件夾下,你需要手動在項目路徑下創建該目錄。或者修改為任意你想存放的位置

總結

以上就是我的開發過程,有道智雲的語音合成API文檔清晰,調用過程全程無坑,開發體驗和合成效果都令人感到舒適。

我有故事,我把它交給機械人來講,閉目養神不枯燥,真是一件美事!