缘起

《论语》有声版?难道还不够多吗?

同样的问题,它的父项目《一瓶论语》“前言”“使用说明”里作了回应。有声版呢?还是用软件读?开玩笑吧?

真人有声版《论语》的确不少,有些还提供免费下载。与它们相比,这个TTS有声版的主要优点,一是中立,二是扎实,三是自动化。至于开源项目的天然优势,比如容易及时更新,已经在《一瓶论语》里说得很详细。

“TTS”是“Text to Speech”的缩写,它通过软件自动把文字内容“合成”为语音信号,直接“读”出声音或者保存成音频文件。2个多月前,我偶然被自己安卓手机上“Google文字转语音引擎”朗读中文网站文章的质量吸引,自然就想到,用它读文言文会怎样呢?有可能流畅自然地朗读《论语》吗?这种人文和一点点技术相结合的题目,总让我兴趣盎然,《论语》TTS有声版就是后来不断试验的一个小结。

让人开心的是,只要不太多的辅助,Google TTS就能把2500年前的文言文读得像模像样。请试听一下:

这里的辅助工作,主要是帮助TTS引擎断句,还有多音字和难字的辨析。然后,就可以把整个的“台词”交给TTS引擎自动处理了。更棒的是,安卓系统开放了TextToSpeech开发接口,尽管控制的范围不如我希望的丰富,但也足以按章朗读,统一合并,然后自动生成播放列表和同步字幕了。限于时间,我只用Google TTS做了试验。其它优秀的TTS引擎,比如科大讯飞,相信也会有出色的表现。

显然,与真人相比,Google TTS的“播音腔”没太有感情,也缺乏变化。换一个角度想,对于《论语》来说,这正是它的独特优点。《论语》是中国传统文化的精华,每个人心中都应该为它留出方寸之地,不断咀嚼玩味,形成独立的理解。一个值得反复聆听的“标准”有声版,就应该字正腔圆,不带主观色彩,对原文一视同仁,没有分心的背景音乐。对于《论语》,这种去掉阅读者“理解”和其它修饰的朴素版,似乎更容易激发听者自身的共鸣,缩短从声音到心灵的距离,而不会一遍惊艳,两遍三遍不过尔尔,然后束之高阁,甚至有所厌弃。这就是软件朗读“中立”的意义。

诚然,目前的阅读效果还有不少不太自然的地方(见后面的已知问题)。如果你把TTS引擎当成一个刚认字的孩子,虽然还不太理解《论语》的含义,仍在努力把它读顺(而且会越来越顺!),感受就会好得多。我在这两个月里听过不下100遍,仍然兴味盎然,似乎可以作为一个佐证。

这个《论语》TTS有声版,就是希望成为一个反复可听、持续改进的“标准版”。而且听得多了,总会有自己朗读一个“完美版”的创作冲动。我当然希望看到更多严谨到位的真人版不断问世。

文本和音频

本项目的主要内容是两个文本文件:

  • ly.txt:从《一瓶论语》自动提取的《论语》原文,作为TTS引擎“台词”文本的基础,并用来制作字幕。
  • ly-tts.txt:TTS引擎的“台词”文本,所有的手工修改都是在它上面进行的,最后送入TTS引擎,生成前面的音频文件。

如果让TTS引擎直接朗读ly.txt,最明显的问题就是很多多音字、难字都读错了,所以要找读音确定的同音字来替换。为了不影响TTS分词的效果,应该尽量考虑所用字的词性和含义。同等条件下,选择原文没用过的字也许好一些。这个列表比较长,放在了附录的字词替换表中。

现在每个字都能正确发音了,连起来一听还是怪怪的,原因就在于断句。《论语》毕竟是2500年前的古汉语,我们现代人如果只看白文版,也很难一次读通顺,何况是对古文所知寥寥的软件呢?所以,需要选择性地插入或者删除一些标点符号,给TTS引擎足够的提示。这里占用的时间最多,需要反复修改比对。传统的“句读”(dòu)是从段落中分隔出句子(添加。?!等),从句子中分隔出“子句”(添加,;、等),这里的粒度更细,要在连读错误的“子句”内部分隔词组。古汉语的一个显著特点是单字成词,所以TTS引擎的很多先验知识就不成立了。断句的处理包括:

  • 删除:“ ” ‘ ’ 《 》
  • 替换:: → ,
  • 选择性替换:、 → ,
  • 选择性添加:,’(短间隔,用于长难句的句读)

举例来说,1.6章的“弟子入则孝,出则悌”,如果不在“子”、“入”之间加一个短间隔,就会把“弟子入”连读,“入”的发音既轻且短。短间隔’的停顿比顿号更短一些,是我经过试验发现的,其它的TTS引擎未必表现相同。如果TTS引擎允许在文本中直接插入描述性标记,比如间隔时间、汉语拼音、轻重,甚至语气、场景等,用起来可以更灵活。

至此,用阅读软件打开ly-tts.txt,就可以调用Google TTS引擎,随点随读或者导出音频了。目前Google TTS的普通话语音数据包还只有女声一种。需要特别表扬的是,2017年12月更新的程序及其数据包比之前的效果进步明显,发音和断句更自然,读错的字也少了很多。可以预期,随着技术的演进,这个TTS版的质量也会持续提高,越来越接近“完美中立”的目标。

考虑到现代文和文言文的差异,朗读前会把所有的逗号、分号替换成句号。本项目发布的ly-tts.mp3,使用了0.9倍的阅读速度,总长度是108分钟出头,或者说6496秒。一些地方听起来会显得稍赶,但我觉得,《论语》这样的经典文本,就应该听到熟极而流的程度,所以没有再作调整。大家当然可以根据自己的喜好选择阅读速度,或者进一步修改,比如整体调高音调,变成童声的效果。

为了便于生成播放列表和字幕,我从每一章分别导出wav文件(synthesizeToFile),用sox在各文件末尾增加0.25秒静音后,合并成单个wav,最后用ffmpeg转换成mp3。对于有声书来说,32 kbps和64 kbps都是常见的选择,考虑到频繁的静音间隔,32 kbps VBR的表现我觉得已经挺好了。最后的文件体积约为25 MB。

播放列表和字幕

出于对目录的偏爱,我还制作了详细的播放列表(cue)、歌词(lrc)、字幕(srt),放在本项目附带的subtitles.zip里。它们都是从ly.txt和sox处理后的多个wav文件自动生成的,没什么稀奇之处,这里只展示一下使用效果。

Windows上,我喜欢用foobar2000播放音频。可以用它打开播放列表ly-tts.cue,并在旁边同步滚动歌词ly-tts.lrc(需要Lyric Show Panel 3插件):

foobar2000

foobar2000目前严格遵循“最多99首歌”的播放列表规则,所以没法打开精确到章、总共“532首歌”的ly-tts-detailed.cue。【2018-2-4更新:从v1.4 beta2起,foobar2000将cue播放列表的曲数上限提高到999。】如果用PotPlayer,就没有这个限制,而且它既可以用ly-tts.lrc,也可以用ly-tts.srt当字幕,全屏观看的效果是很好的:

foobar2000

安卓手机上,AIMP支持cue播放列表,可惜还不支持同步歌词:

foobar2000

MX Player则支持同步字幕ly-tts.srt:

foobar2000

其它平台上,想必也有比较合理的解决方案。

已知问题

平心而论,目前这个版本的阅读效果不乏惊艳之处,但总有一些明显不够流畅自然的地方,记录在这里,希望可以在后续的程序和数据包中得到改进。

发音偏轻,长度偏短

  • 1.14,“就有道”的“就”,应该是没有被理解为动词approach,而是副词just。相比之下,12.19的“如杀无道以就有道”,16.1的“陈力就列”,因为是连在一句内,就稍好一些。
  • 5.7,“由也耗勇过我”的“过”,没有被理解为动词exceed,而是当成副词ever,读得偏轻偏短。另外一些“过”也有这个问题,尤其是句子结尾的“有过”,包括7.31“苟有过”,20.1“百姓有过”。

发音偏重

  • 8.21,“悱饮食而致孝乎鬼神,恶衣服而致美乎黻冕,卑宫室而尽力乎沟洫”的3个“乎”。

字间隔偏长

这是“子句”内的字之间,间隔偏长的情况:

  • 1.13,“言可复也”,句末的“也”,没有很好地作为助词处理,在前面多加了一个小间隔。其它的例子包括4.5“是人之所欲也……是人之所悟也”,6.15“非敢后也”,7.28“智之次也”。
  • 2.18,“干禄”,也许是“干”没有被当成动词?
  • 3.21,“夏后氏”,没有理解“夏后”是独立的姓氏,导致“氏”之前的间隔较长。16.1“季孙之忧”的“季孙”也是这样。
  • 7.7,“束脩”,似乎没有被当成独立的名词,两个字的间隔略长。
  • 19.23,“室家”,“而入”。

句间隔偏长

这是“子句”之间间隔偏长的情况,就像我们自己读文章,“一口气没接上来”:

  • 1.10,“夫子’温、良、恭、俭、让’以德之。夫子之求之也……”,两句的间隔比正常情况偏长。
  • 14.18,“与文子同升诸公。子闻之”。
  • 16.1,“而必谓之辞。丘也闻”。

通过调整标点,一定程度上解决了其它若干处问题:

  • 9.10,“子见资摧者、冕衣常者’与瞽者,见之”,“见之”之前。改成“资摧者,冕衣常者,与瞽者”,则好一些。这应该是添加’的副作用。
  • 11.26,“子路、增皙、冉有、公西华侍坐。子曰”,“子曰”之前。把人名间的顿号改为逗号,虽然拖沓了一些,但消除了这个长间隔。
  • 12.11,“子不子,虽有粟”。把原来的顿号都改成逗号,就消除了这个长间隔。

连读颤音

这种情况,大多数与w、u、o、y音有关。

  • 4.7,“人之过也”的“过”。其它的例子包括14.13“以告者过也”,16.1“且尔言过矣”,19.8“小人之过”,19.12“言游过矣”,19.21两处“过也”。
  • 5.12,“加诸我也”的“我”。其它的例子包括6.9“复我”,7.11“惟我”,7.16“于我如浮云”,9.11“博我以文,约我以礼”,11.4“回也非助我者也”,11.11“非我也”,13.10“苟有用我者”,17.1“岁不我与”,17.5“召我”、“用我”。
  • 8.6,“不可夺也”的“夺”。
  • 11.26,“唯国以礼”的“国”。
  • 12.10,“是惑也”的“惑”。
  • 14.11,“则优”的“优”。
  • 14.34,“或曰”的“或”。
  • 16.5,两处“三乐”的“乐”,过渡不够自然。
  • 17.20,“辞以疾”的“以”。
  • 19.1,“丧思哀”的“哀”。

句末短音

  • 2.4,“七十而从心所欲不逾矩”的“矩”,听起来有点像读成了jù,但换成“举”、“沮”等同音字也一样,所以并不是读错了。真人朗读经常也会这样:因为“矩”处于整章结尾,如果这个字并不特别重要,往往会结束过快,切掉靠后的音节长度,效果接近于轻声,听起来像是第4声。如果有选项控制一个字发音饱满就好了。另一个例子是3.22“邦君为两君之好”,“好”也有点像第4声。

错读无替换

  • 1.15,“如切如磋”的“切”读为qiè,而且没有读qiē的字可以替换。这个问题已经向Google提交了反馈,目前只得手工修改1.15导出的wav文件(唯一需要手工修改的地方)。方法是,用同样的配置朗读“正切如磋”(此时读为qiē),截取后三个字的波形,替换原来的三个字。

附录:字词替换表

  • 说:悦
  • 弟:悌
  • 好:耗
  • 鲜:显
  • 与:欤,预
  • 女:汝
  • 省:醒
  • 道:导
  • 乘:剩
  • 没:殁
  • 共:拱
  • 曾:增
  • 知:智
  • 阙:缺
  • 相:象
  • 乐:跃
  • 宁:泞
  • 亡:无
  • 监:鉴
  • 大:太
  • 处:楚
  • 朝:昭
  • 放:仿
  • 参:身
  • 逮:待
  • 妻:器
  • 几:机
  • 给:挤
  • 斐:匪
  • 少:邵
  • 恶:悟,乌
  • 孙:逊
  • 衣:翼
  • 澹:谭
  • 语:遇
  • 识:制
  • 和:贺
  • 校:较
  • 间:鉴
  • 钻:躜
  • 予:余
  • 贾:古
  • 缊:愠
  • 便:骈
  • 躩:攫
  • 蹜:宿
  • 绤:细
  • 冠:关
  • 衰:摧
  • 讱:韧
  • 鞟:廓
  • 陶:姚
  • 乡:向
  • 中:重
  • 长:掌
  • 齐:斋,资
  • 见:现
  • 期:基
  • 丧:桑
  • 夫:扶
  • 率:帅
  • 莫:暮
  • 数:硕,树
  • 柏:摆
  • 塞:腮
  • 得:德
  • 小子:小紫
  • 陈成子:陈成紫
  • 韫:蕴
  • 屏:禀
  • 貉:河
  • 裳:常
  • 硁:坑
  • 斗:抖
  • 行:型,沆
  • 占:沾
  • 偲:思
  • 南宫适:南宫括
  • 裨:皮
  • 要:邀
  • 召:劭
  • 被:披
  • 发:珐
  • 辟:避
  • 蒉:篑
  • 荷:赫
  • 难:婻
  • 陈:阵
  • 遂:碎
  • 卷:呟
  • 薄:伯(Google TTS读为báo,共2处:8.3“如履薄冰”、15.15“薄责于人”。按商务版《现代汉语词典》、《现代汉语学习词典》,读bó为宜。)
  • 归:馈
  • 时:伺
  • 亟:气
  • 佛:必
  • 沮:巨
  • 蓧:钓
  • 食:饲
  • 泥:腻
  • 更:耕
  • 量:梁
  • 磷:赁
  • 曲:屈
  • 菲:悱
  • 侗:僮
  • 华:花
  • 栖:西
  • 揭:弃
  • 徼:焦
  • 一:衣,宜,意
  • 为:唯(do, be, fake, by, end-of-sentence aux.),谓(for, because, help)
  • 有:又
  • 杀:晒