使用nodejs完成TTS有聲書的制作

現(xiàn)今網絡媒體中已經出現(xiàn)了多種基于TTS的亞文化二創(chuàng),比如電棍活字印刷,綾地寧寧朗誦等等。
這些作品大都是作者通過特定的可執(zhí)行文件手動編輯輸入文字并剪輯的,效率并不高。
本文將以綾地寧寧朗誦為例子,介紹一種通過nodejs批量化操作,制造有聲書視頻的方法。
本文將使用的工具包括:
nodejs
MoeGoe
puppeteer
ffmpeg
工具介紹
nodejs
Node.js 是能夠在服務器端運行 JavaScript 的開放源代碼、跨平臺執(zhí)行環(huán)境。
Node.js 大部分基本模塊都用 JavaScript 語言編寫。在 Node.js 出現(xiàn)之前,JavaScript 通常作為客戶端程序設計語言使用,以JavaScript 寫出的程序常在用戶的瀏覽器上執(zhí)行。Node.js 的出現(xiàn)使 JavaScript 也能用于服務端編程。Node.js 含有一系列內置模塊,使得程序可以脫離 Apache HTTP Server 或 IIS,作為獨立服務器執(zhí)行。
簡要的講,nodejs允許我們通過JavaScript編程操作計算機系統(tǒng)中的文件,進程和網絡等等對象,正如同CMD,PowerShell這類技術一樣。
參考:https://nodejs.dev/en/
Moegoe
Moegoe是b站ID名為@CjangCjengh 的大佬開發(fā)的基于VITS的AI語音合成引擎,主要提供TTS(Text to Sound,文字轉語音)的功能,目前已經預先計算好的音色模型多種多樣,通過在Moegoe中應用這些模型文件,我們得以用該音色朗誦文字,Moegoe最常用的音色是綾地寧寧(綾地 寧々),在bilibili可見各種的二次創(chuàng)作。
目前Moegoe僅有Windows版打包文件,直接使用Python源文件進行部署會面臨一系列問題,本文使用作者預先打包好的Windows可執(zhí)行文件進行操作。
參考:https://github.com/CjangCjengh/MoeGoe
puppeteer
puppeteer是一種流行的無頭瀏覽器(headless browser)框架,一般意義上的瀏覽器通過其內核解析HTML,CSS,JavaScript等等網頁組成文件,將其渲染為一個瀏覽器頁面進程,用戶通過圖形化的瀏覽器進行交互,無頭瀏覽器不提供可視化界面,而是通過API提供瀏覽器應當有的全部功能,包括對DOM(document object model)進行操作(這也就包括了我們在瀏覽器中常用的功能,比如點擊超鏈接,保存圖片等等),截圖,跳轉網頁,提供COOKIE等等。
puppeteer是封裝為npm包的無頭瀏覽器,這意味著我們可以在nodejs的編程環(huán)境下直接通過JavaScript腳本對其進行操作
參考:https://www.npmjs.com/package/puppeteer
FFmpeg
FFmpeg 是一個開放源代碼的自由軟件,可以執(zhí)行音頻和視頻多種格式的錄影、轉換、串流功能,包含了libavcodec——這是一個用于多個項目中音頻和視頻的解碼器庫,以及l(fā)ibavformat——一個音頻與視頻格式轉換庫。
FFmpeg提供一個命令行操作界面,用戶得以以此對音視頻進行操作,應當指出市面上大部分的音視頻剪輯軟件都是對FFmpeg的再封裝。
本文基于nodejs完成大部分操作,因此選用了fluent-ffmpeg作為FFmpeg在nodejs環(huán)境下的封裝作為工具。
參考:
https://ffmpeg.org
https://www.npmjs.com/package/fluent-ffmpeg
操作思路
使用上述技術完成小仲馬《茶花女》有聲書的生成腳本的思路如圖:

首先通過人工操作,對《茶花女》以txt格式記錄的原文本進行編輯,主要通過正則表達式作為工具,對原文文本進行段落的劃分,并去除多余的注釋的換行符,制表符等等。
在原文文本具有一致明確的格式后,通過JavaScript的String對象的實例方法對原文中的每個句子重新標記好其在文章中的位置信息,包括每個句子所在的章節(jié),位于整篇文檔中的第幾行,是其所在章節(jié)中的第幾句話,記錄為一個Javascript數組中的一個json元素。
在以視頻作為表現(xiàn)形式的有聲書中,原文中每一句話將表現(xiàn)為一段視覺可見的文字和與之對應的語音,兩者結合形成一段表現(xiàn)該文字的視頻,多段視頻前后連接即形成整篇有聲書。
視覺可見的文字將以一張電子圖片的形式生成,在這里使用puppeteer作為生成工具,預先制作好一個展示文字的網頁模板,模板中預留出文字和文字相關的元信息的位置,在通過訪問DOM的方法進行賦值并截圖,由無頭瀏覽器生成圖片備用。

聲音將通過Moegoe的Windows進程生成。Moegoe的操作基于Python打包,nodejs環(huán)境不能直接操作,因此使用node:child_process模塊進行操作,以生成一個子進程的方法,對子進程的標準輸入和輸出流進行操作,達到批量生成文字的目的。
在每一小段圖片和音頻生成后,使用FFmpeg將兩者結合生成一段視頻,將各個小段視頻連接,加上片頭片尾即可得到成品。
踩坑記錄
通過nodejs操作Moegoe是本項目中最具挑戰(zhàn)性的環(huán)節(jié),Moegoe提供的是命令行界面,用戶通過輸入文字和進程進行交流,使用child_process模塊對Moegoe進行封裝,實際上是在重寫整個命令行界面,對命令行進行封裝,使之成為易于使用的JavaScript API,因此首先要充分窮舉命令行界面的所有行為,明確其內部的循環(huán)語句的結構,再再JavaScript中對應的進行循環(huán)語句的條件判斷。
最最最致命的是,Windows命令行界面默認使用GB2312作為文字輸入輸出的編碼,而Python的輸入輸出流是cp936,兩者互相讀取將導致亂碼,亂碼輸入的中文文字對于Moegoe而言生成的音頻也是亂碼。因此選用了iconv-lite作為解決編碼問題的方案,在寫入時使用GB2312,在讀取輸出時使用cp936。
在nodejs的實際編程中,采用了分別編寫圖片生成腳本,音頻生成腳本,視頻生成腳本,再通過一個總的腳本引入三者,讀取標記好的原文文本信息進行批量化生成。
在批量化生成的過程中使用了JavaScript的異步編程語法,await和async關鍵字,應當注意,await結合async的語法生成的Promise鏈有時會有不自動結束pending狀態(tài)的問題,因此需要對promise鏈中的每一步進行仔細考慮。
源代碼
我的github登錄不上了,放評論區(qū)