写点什么

墙裂安利!用腾讯云 AI 语音合成打造自己的第一本有声书

  • 2022 年 8 月 19 日
    广东
  • 本文字数:6246 字

    阅读完需:约 20 分钟

墙裂安利!用腾讯云AI语音合成打造自己的第一本有声书

现代生活中,我们不可避免会遇到很多碎片时间,等公交、倒地铁、排核酸、买早点等等。这些时间累积起来,无疑是一笔很大的个人资源,而想利用这部分时间,听显然是最好的方式。


国内云计算语音合成服务已经非常成熟,基于开源工具整合 TTS PaaS 服务,可以非常方便地打造一款个人定制的有声书制作工具。


好了,需求有了,可行性也没问题,开始搞起!Get hands dirty!


一、分析调研

有声书需求,一句话来讲就是,把电子书制作成有声音音频,并提供下载链接。


先细化下需求,拆解成不同过程,以及看看都需要哪些能力来支持:


1、我们根据个人喜好,下载好电子书(商用务必确保已获授权)。由于我之前用 Kindle,手上有大量 mobi 的电子书。

2、打开制作工具,上传指定的 mobi 电子书。(调研 Web 交互库)

3、制作工具需要先解析 mobi,获取其中的文本内容。(调研 mobi 解析库)

4、基于文本内容,调用语音合成服务,获取有声书音频内容。(调研云计算语音合成 PaaS 服务)

5、提供有声书音频下载。


经过一番调研,准备使用工具栈如下


二、代码开发

工具栈到位,开始编码。


第一步:电子书文件解析

解析模块,先引入外部库 mobi,通过 mobi.extract 函数读取电子书文件,解析为 html 格式的文件 tmp_html。


mobi 库使用可以参见文档 mobi - library for unpacking mobi files

import mobidef load_file(self, file_name):        logging.info('begin to parse file')        start_t = time.time()        tmp_dir, tmp_html = mobi.extract(file_name) # 解析 mobi 文件        end_t = time.time()        logging.info('extract {} to {}. cost {}ms'.format(file_name, tmp_html, int((end_t-start_t)*1000)))
with open(tmp_html, 'r') as fp: lines = fp.readlines() self.html_content = ''.join(lines) # 读取 html logging.info('load file total {} chars'.format(len(self.html_content)))
shutil.rmtree(tmp_dir) logging.info('clean temp dir {}'.format(tmp_dir))
复制代码


得到 html 文件后,通过 lxml.etree 将其解析为一棵 DOM 树,然后就可以通过 xpath 这个大杀器,可以得到其中想要的任意内容了。


比如特定属性的元素,特定位置的段落、标题等等,不了解的同学可以看下 XPath教程


from lxml import etreedef parse_html(self):        logging.info('parse html')        # pre process        self.html_content = self.pre_process(self.html_content)                # parse dom        dom = etree.fromstring(self.html_content)        plist = dom.xpath('//p/text()')        audio_texts = []
# 示例,比如从 1010 段开始,获取后面 10 个段落 idx_start = 1010 for p in plist[idx_start:idx_start+10]: #logging.info('{}'.format(p)) audio_texts.append(p) self.text = ''.join(audio_texts) logging.info('content length {}'.format(len(self.text)))
复制代码


以上就是电子书解析模块,封装在 AudioBookGenerator 类,详见 src/audio_book_generator.py


第二步:有声语音合成

有声语音合成,需要基于第三方的语音合成 TTS 服务。调研了市面上常见云计算厂商 PaaS 服务后,决定采用腾讯云 TTS 服务。


我觉得比较好的点有三个:


1、开发文档是基于开发者视角的,看起来非常顺畅。

2、长文本合成接口最长支持 10 万字,可以完整合成一个章节,适合合成有声书的场景,不用频繁拆分文本。

3、其中的智逍遥音色,以及旁对白支持的能力,适合小说场景;尤其是其中包含人物对话,旁白和对白分开后层次分明。


关于服务注册开通,官方文档写的很详细,就不赘述了,大家可以参见 腾讯云TTS


服务开通后,在控制台打开 API 密钥管理页面,拷贝如下的访问密钥,配置到 config 文件中即可。


配置文件 src/config.py

class Config(object):

SECRET_ID = 'XXXX' # 对应上面的 SecretId

SECRET_KEY = 'XXXX' # 对应上面的 SecretKey


下面看看怎么使用官网提供的 SDK ,调用语音合成服务。


打开 长文本合成官方开发文档,滚动下下面,找到对应的 sdk,这里我们用 python sdk


集成 SDK 到我们的工程。


长文本合成是个异步服务,提供两个接口用于服务调用。


1、创建合成任务接口:CreateTtsTask

2、查询任务状态及结果接口:DescribeTtsTaskStatus

下面的 create_task 函数和 query_task 函数,分别针对这两个函数进行了封装。


需要注意的是,查询任务状态时,任务可能并未执行完成,所以需要间隔一段时间后循环查询,直到任务完成(成功或失败)。


创建任务:CreateTtsTask


调用时,要注意两个参数


1、VoiceType:音色 id,用于选择不同的发音人,这里使用之前调研时确定的智逍遥(100510000),感觉非常适合武侠或玄幻小说的场景

2、VoiceoverDialogueSplit:旁对白支持选项,需要设置为 True,可以将文本中的对话和旁白分割,并分别用对应的音色进行合成


请求成功后,返回该任务的唯一 ID:TaskId

def create_task(self) -> str:        task_id = ''
req = models.CreateTtsTaskRequest() req.Text = self.text # 合成文本 req.VoiceType = self.voice_type # 设置音色id,此处选用 智逍遥100510000 req.VoiceoverDialogueSplit = self.voiceover_dialogue_split # 打开旁对白支持 req.Codec = self.codec req.SampleRate = self.sample_rate req.ModelType = self.model_type try: resp = self.client.CreateTtsTask(req) task_id = resp.Data.TaskId req_id = resp.RequestId print('call CreateTtsTask succeed, task_id: {} request_id: {}'.format(task_id, req_id)) except TencentCloudSDKException as err: print('call CreateTtsTask failed, err: {}'.format(str(err))) return task_id
复制代码


查询任务状态及结果:DescribeTtsTaskStatus


调用是,将上面得到的 TaskId  作为参数传进去,请求会实时返回任务的相关信息,主要包含:


Status:任务状态

ErrorMsg:任务错误信息(任务失败时)

ResultUrl:合成音频地址

def query_task(self, task_id):        req = models.DescribeTtsTaskStatusRequest()        req.TaskId = task_id        try:            resp = self.client.DescribeTtsTaskStatus(req)            data = resp.Data            req_id = resp.RequestId            print('call DescribeTtsTaskStatus succeed, data: {} request_id: {}'.format(str(data), req_id))        except TencentCloudSDKException as err:            print('call DescribeTtsTaskStatus failed, err: {}'.format(str(err)))
if data: return data.Status, data.ErrorMsg, data.ResultUrl # 任务状态、错误信息、音频文件地址 else: return 3, 'internal error', ''
复制代码

以上就是有声书语音合成模块,封装在 TencentSDK 类,详见 src/tencent_sdk.py


第三步:完成有声书制作脚本

通过 main 脚本,将上两步的电子书解析模块、语音合成模块集成到一起,再增加文件下载功能,即可完成有声书制作脚本。


腾讯云 TTS 服务返回的合成音频 url,新增 HttpAgent 类,将音频二进制文件下载到本地。

from audio_book_generator import AudioBookGeneratorfrom http_agent import HttpAgent
def main(): file_name = sys.argv[1] logging.info('upload file: {}'.format(file_name))
# gen audio generator = AudioBookGenerator() generator.process(file_name) audio_url = generator.get_audio_url() logging.info('get audo url: {}'.format(audio_url)) # download audio session_path = os.environ.get('SESSION_PATH', './') audio_name = os.path.join(session_path, 'result.mp3') agent = HttpAgent() agent.download(audio_url, audio_name) logging.info('download audio to: {}'.format(audio_name))
复制代码


HttpAgent 详见文件 src/http_agent.py


本地工具已完成,可以通过下列命令调用看下效果。

(venv) justin@VM_centos:[~/audio_book/src]: python main.py ../dou.mobi 2022-06-21 10:36:44,959 - main.py[line:13] - INFO: upload file: ../dou.mobi2022-06-21 10:36:44,959 - /home/justin/audio_book/src/audio_book_generator.py[line:26] - INFO: begin to parse file2022-06-21 10:36:47,253 - /home/justin/audio_book/src/audio_book_generator.py[line:30] - INFO: extract ../dou.mobi to /tmp/mobiexk287bwzw/mobi7/book.html. cost 2294ms2022-06-21 10:36:47,293 - /home/justin/audio_book/src/audio_book_generator.py[line:35] - INFO: load file total 4988080 chars2022-06-21 10:36:47,295 - /home/justin/audio_book/src/audio_book_generator.py[line:38] - INFO: clean temp dir /tmp/mobiexk287bwzw2022-06-21 10:36:47,295 - /home/justin/audio_book/src/audio_book_generator.py[line:45] - INFO: parse html2022-06-21 10:36:47,506 - /home/justin/audio_book/src/audio_book_generator.py[line:60] - INFO: content length 6252022-06-21 10:36:47,549 - /home/justin/audio_book/venv/lib64/python3.6/site-packages/urllib3/connectionpool.py[line:1005] - DEBUG: Starting new HTTPS connection (1): tts.tencentcloudapi.com:4432022-06-21 10:36:47,699 - /home/justin/audio_book/venv/lib64/python3.6/site-packages/urllib3/connectionpool.py[line:465] - DEBUG: https://tts.tencentcloudapi.com:443 "POST / HTTP/1.1" 200 1252022-06-21 10:36:47,701 - /home/justin/audio_book/venv/lib64/python3.6/site-packages/tencentcloud/common/http/request.py[line:112] - DEBUG: GetResponse Status: 200Header: Server: nginxDate: Tue, 21 Jun 2022 02:36:41 GMTContent-Type: application/jsonContent-Length: 125Connection: keep-aliveData: {"Response":{"RequestId":"ffb6f632-bd56-427d-ae21-xxxx","Data":{"TaskId":"gz-27ac44ab-c21e-4e58-b0b3-xxxx"}}}
call CreateTtsTask succeed, task_id: gz-27ac44ab-c21e-4e58-b0b3-xxxx request_id: ffb6f632-bd56-427d-ae21-xxxx
2022-06-21 10:37:27,964 - /home/justin/audio_book/venv/lib64/python3.6/site-packages/urllib3/connectionpool.py[line:1005] - DEBUG: Starting new HTTPS connection (1): tts.tencentcloudapi.com:4432022-06-21 10:37:28,016 - /home/justin/audio_book/venv/lib64/python3.6/site-packages/urllib3/connectionpool.py[line:465] - DEBUG: https://tts.tencentcloudapi.com:443 "POST / HTTP/1.1" 200 5762022-06-21 10:37:28,017 - /home/justin/audio_book/venv/lib64/python3.6/site-packages/tencentcloud/common/http/request.py[line:112] - DEBUG: GetResponse Status: 200Header: Server: nginxDate: Tue, 21 Jun 2022 02:37:21 GMTContent-Type: application/jsonContent-Length: 576Connection: keep-aliveData: {"Response":{"RequestId":"7c4c20d3-ad79-47ea-86a8-xxxx","Data":{"TaskId":"gz-27ac44ab-c21e-4e58-b0b3-xxxx","Status":2,"StatusStr":"success","ResultUrl":"https://xxxx","ErrorMsg":""}}}
call DescribeTtsTaskStatus succeed, data: {"TaskId": "gz-27ac44ab-c21e-4e58-b0b3-xxxx", "Status": 2, "StatusStr": "success", "ResultUrl": "https://xxxx", "ErrorMsg": ""} request_id: 7c4c20d3-ad79-47ea-86a8-xxxx2022-06-21 10:37:28,580 - /home/justin/audio_book/venv/lib64/python3.6/site-packages/urllib3/connectionpool.py[line:465] - DEBUG: https://xxxx:443 "GET /xxxx HTTP/1.1" 200 535248http download succ: https://xxxx -> ./result.mp32022-06-21 10:37:29,001 - main.py[line:26] - INFO: download audio to: ./result.mp3
复制代码

可以正常生成音频文件 result.mp3。附录中有一个 demo 音频,大家可以听下,效果蛮不错。


第四步:脚本可视化

有声书制作脚本已完成,但脚本用起来还是不方便,而且没办法给他人使用。


这里需要对脚本进行可视化,将其部署为一个 Web 工具。


这里采用 Wooey 开源库,有如下优点:


1、通过编译一个适配类,将脚本工具非常方便地转化为 Web 交互页面

2、支持常见 UI 交互组件,如下拉框、文件上传等,通过代码配置的方式展示到页面上,无需任何前端知识

3、支持任务启动、回显执行过程,结果文件下载等功能


适配类如下,通过  parseer 增加了文件上传组件。

import osimport sysimport argparse
parser = argparse.ArgumentParser(description="convert mobi file to audio")parser.add_argument('--audio', help='the mobi file to make audio', type=argparse.FileType('r'), required=True) # 文件上传组件
def audio_book(mobi_file): _format = mobi_file.split('.')[-1].lower() if _format != 'mobi': print('only mobi is supported') return # TODO: 此处填写业务逻辑
if __name__ == '__main__':
args = parser.parse_args() audio_book(args.audio.name)
复制代码

调用电子书制作脚本工具,通过 python venv 方式,隔离 wooey 与 工具脚本的环境变量,方便 wooey 平台集成其他任意脚本。

SCRIPT_PATH = '/root/audio_book'
def audio_book(mobi_file): # ... # TODO: 此处填写业务逻辑 cmd = [] cmd.append('export SESSION_PATH={}'.format(os.getcwd())) # 传输本次执行 session 路径到脚本 cmd.append('cd {}'.format(SCRIPT_PATH)) cmd.append('source {}/venv/bin/activate'.format(SCRIPT_PATH)) cmd.append('cd src') cmd.append('python main.py {}'.format(mobi_file)) cmd.append('cd ') cmd = '&&'.join(cmd)
print(cmd) os.system(cmd)
复制代码

添加脚本到可视化平台


[root@VM-centos ~/TOOLS]# python manage.py addscript ../audio_book/audio_book_adaptor.py --group 小工具 Converting ../audio_book/audio_book_adaptor.pyConverted 0 scripts


所有工作已完成,让我们 enjoy 下效果。


三、产品体验

合成自己的第一本有声书

打开工具平台,选择有声书制作工具


点击【选择文件】按钮,上传想要转换的电子书文件


启动任务,从页面可以看到脚本执行日志


任务执行结束后,状态显示成功,可以从页面底部的文件列表中,点击 result.mp3 进行下载。


到此,整个有声书制作工具已经完成,试听音频以及工程代码放在附录中。


关于有声书的效果,感兴趣的同学可以从附录下载听下,个人觉得还蛮不错。


工程代码部分,基本上是开箱可用的,感兴趣的可以下载下来跑一下,也可以基于此增加自己需要的一些 feature。


好了,那就到这里 ~~


附录

有声书声音效果试听:result.mp3

有声书制作工程代码:https://github.com/jizhouli/audio_book_generator


了解更多腾讯云 AI 语音合成产品信息:https://cloud.tencent.com/product/tts 

发布于: 刚刚阅读数: 4
用户头像

还未添加个人签名 2020.11.02 加入

还未添加个人简介

评论

发布
暂无评论
墙裂安利!用腾讯云AI语音合成打造自己的第一本有声书_腾讯云_牵着蜗牛去散步_InfoQ写作社区