使用Amazon Polly在文字被朗读时突出显示文本
使用Amazon Polly突出显示文本
Amazon Polly是一项将文本转换为逼真语音的服务。它能够为多种语言将文本转换为语音,从而实现一整类应用程序的开发。
这项服务可以与其他AWS人工智能或机器学习(ML)服务一起由聊天机器人、有声读物和其他文本到语音应用程序使用。例如,可以结合使用Amazon Lex和Amazon Polly创建一个与用户进行双向对话并根据用户命令执行特定任务的聊天机器人。可以结合使用Amazon Transcribe、Amazon Translate和Amazon Polly将语音转录为源语言的文本,将其翻译为其他语言,并进行朗读。
在本文中,我们介绍了一种使用Amazon Polly在语音播放过程中突出显示文本的有趣方法。这种解决方案可以在许多文本到语音应用程序中用于以下目的:
- 为书籍、网站和博客的音频增加视觉功能
- 在客户试图在语音播放过程中快速理解文本时提高理解能力
我们的解决方案使客户端(例如浏览器)能够在任意时刻了解由Amazon Polly朗读的文本(单词或句子)。这使得客户端能够动态地突出显示正在朗读的文本。这种能力对于为前面提到的用例提供语音辅助是非常有用的。
我们的解决方案可以扩展到除突出显示文本之外的其他任务。例如,浏览器可以在文本被朗读时显示图像、播放音乐或执行其他动画。这种能力对于创建动态音频书、教育内容和更丰富的文本到语音应用程序非常有用。
解决方案概述
在其核心,该解决方案使用Amazon Polly将一串文本转换为语音。文本可以从浏览器输入,也可以通过调用我们解决方案暴露的端点的API进行输入。由Amazon Polly生成的语音存储为音频文件(MP3格式),存储在Amazon Simple Storage Service(Amazon S3)存储桶中。
然而,仅凭音频文件,浏览器无法确定在任何时刻正在朗读文本的哪些部分,因为我们没有关于每个单词何时被朗读的精细信息。
Amazon Polly提供了一种使用语音标记来获取这个信息的方法。语音标记存储在文本文件中,显示了每个单词或句子何时被朗读的时间(以毫秒为单位)。
Amazon Polly以逐行分隔的JSON流的形式返回语音标记对象。语音标记对象包含以下字段:
- Time – 从相应音频流的开始以毫秒为单位的时间戳
- Type – 语音标记的类型(句子、单词、嘴唇动作或SSML)
- Start – 输入文本(不包括嘴唇动作标记)的对象开始的字节偏移量(而不是字符偏移量)
- End – 输入文本(不包括嘴唇动作标记)的对象结束的字节偏移量(而不是字符偏移量)
- Value – 根据语音标记的类型而异:
- SSML – <mark> SSML标签
- 嘴唇动作 – 嘴唇动作名称
- 单词或句子 – 根据开始和结束字段在输入文本中的子字符串
例如,如果在API调用中使用SpeechMarkTypes
= [“word”, “sentence”],则句子“Mary had a little lamb”可以得到以下语音标记文件:
{"time":0,"type":"sentence","start":0,"end":23,"value":"Mary had a little lamb."}
{"time":6,"type":"word","start":0,"end":4,"value":"Mary"}
{"time":373,"type":"word","start":5,"end":8,"value":"had"}
{"time":604,"type":"word","start":9,"end":10,"value":"a"}
{"time":643,"type":"word","start":11,"end":17,"value":"little"}
{"time":882,"type":"word","start":18, "end":22,"value":"lamb"}
单词“had”(在第3行末尾)在音频流开始后的373毫秒开始,从输入文本的第5字节开始,到第8字节结束。
架构概述
我们解决方案的架构如下图所示。
使用Amazon Polly在语音中突出显示文本
我们解决方案的网站以静态文件(JavaScript、HTML)的形式存储在Amazon S3上,这些文件存储在Amazon CloudFront(1)中并提供给最终用户的浏览器(2)。
当用户通过简单的HTML表单在浏览器中输入文本时,浏览器中的JavaScript对其进行处理。它通过Amazon API Gateway调用API(3)来调用AWS Lambda函数(4)。Lambda函数调用Amazon Polly(5)生成语音(音频)和语音标记(JSON)文件。通过JavaScript异步函数调用了两次Amazon Polly来获取音频和语音标记文件。这些调用的输出是音频和语音标记文件,它们被存储在Amazon S3(6a)中。为了避免多个用户在S3存储桶中覆盖彼此的文件,文件被存储在带有时间戳的文件夹中。这最大程度地减少了两个用户在Amazon S3中覆盖彼此文件的可能性。对于生产发布,我们可以采用更强大的方法,根据用户ID、时间戳和其他唯一特征来分离用户的文件。
Lambda函数为语音和语音标记文件创建预签名URL,并将它们以数组的形式返回给浏览器(7、8、9)。
当浏览器将文本文件发送到API端点(3)时,它会通过同步调用同时获取音频文件和语音标记文件的两个预签名URL(9)。箭头旁边的键符号表示这一点。
浏览器中的JavaScript函数从URL句柄中获取语音标记文件和音频文件(10)。它设置音频播放器以播放音频(为此目的使用了HTML音频标签)。
当用户点击播放按钮时,它解析之前步骤中检索到的语音标记,使用超时创建一系列定时事件。这些事件调用回调函数,这是另一个用于在浏览器中突出显示口语文本的JavaScript函数。同时,JavaScript函数从其URL句柄流式传输音频文件。
结果是,在适当的时间运行这些事件以在音频播放时突出显示文本。使用JavaScript超时函数提供了音频与突出显示文本的同步。
前提条件
要运行此解决方案,您需要一个具有权限使用Amazon CloudFront、Amazon API Gateway、Amazon Polly、Amazon S3、AWS Lambda和AWS Step Functions的AWS账户和AWS身份和访问管理(IAM)用户。
使用Lambda生成语音和语音标记
以下代码两次调用Amazon Polly的synthesize_speech
函数来获取音频和语音标记文件。它们作为异步函数运行,并使用promises协调在同一时间返回结果。
const p1 = new Promise(doSynthesizeSpeechMarks);
const p2 = new Promise(doSynthesizeSpeech);
var result;
await Promise.all([p1, p2])
.then((values) => {
//返回预签名URL数组
console.log('Values:', values);
result = { "output" : values };
})
.catch((err) => {
console.log("Error:" + err);
result = err;
});
在JavaScript端,文本突出显示由highlighter(start, finish, word)函数完成,定时事件由setTimers()
函数设置:
function highlighter(start, finish, word) {
let textarea = document.getElementById("postText");
//console.log(start + "," + finish + "," + word);
textarea.focus();
textarea.setSelectionRange(start, finish);
}
function setTimers() {
let speechMarksStr = sessionStorage.getItem("speechMarks");
//读取语音标记文件并为每个单词设置定时器
console.log(speechMarksStr);
let speechMarks = speechMarksStr.split("\n");
for (let i = 0; i < speechMarks.length; i++) {
//console.log(i + ":" + speechMarks[i]);
if (speechMarks[i].length == 0) {
continue;
}
smjson = JSON.parse(speechMarks[i]);
t = smjson["time"];
s = smjson["start"];
f = smjson["end"];
word = smjson["value"];
setTimeout(highlighter, t, s, f, word);
}
}
替代方案
除了之前的方法,你可以考虑以下几种替代方案:
- 在 Step Functions 状态机中创建语音标记和音频文件。状态机可以通过并行分支条件来调用两个不同的 Lambda 函数:一个用于生成语音,另一个用于生成语音标记。有关此代码的详细信息,请参阅 Github 仓库中的 using-step-functions 子文件夹。
- 异步调用 Amazon Polly 生成音频和语音标记。如果文本内容较大或用户不需要实时响应,可以采用此方法。有关创建长音频文件的详细信息,请参阅创建长音频文件。
- 使用 Boto3 中的 Amazon Polly 客户端上的
generate_presigned_url
调用,直接由 Amazon Polly 生成预签名 URL。如果选择此方法,Amazon Polly 每次都会新生成音频和语音标记。在我们目前的方法中,我们将这些文件存储在 Amazon S3 中。尽管在我们的代码版本中无法从浏览器访问这些存储的文件,但您可以修改代码,通过从 Amazon S3 获取它们来播放以前生成的音频文件(而不是再次使用 Amazon Polly 为文本生成音频)。我们在 AWS 代码库中有更多使用 Python 访问 Amazon Polly 的代码示例。
创建解决方案
整个解决方案可在我们的 Github 仓库中找到。要在您的账户中创建此解决方案,请按照 README.md 文件中的说明进行操作。解决方案包括一个 AWS CloudFormation 模板,用于提供您的资源。
清理
要清理此演示中创建的资源,请执行以下步骤:
- 删除用于存储 CloudFormation 模板(Bucket A)、源代码(Bucket B)和网站(
pth-cf-text-highlighter-website-[Suffix]
)的 S3 存储桶。 - 删除 CloudFormation 堆栈
pth-cf
。 - 删除包含语音文件(
pth-speech-[Suffix]
)的 S3 存储桶。此存储桶是由 CloudFormation 模板创建的,用于存储由 Amazon Polly 生成的音频和语音标记文件。
总结
在本文中,我们展示了一个使用 Amazon Polly 可以在朗读过程中突出显示文本的解决方案示例。该解决方案使用了 Amazon Polly 的语音标记功能,为音频文件中每个单词或句子的起始位置提供了标记。
该解决方案可作为 CloudFormation 模板提供。可以将其部署到任何执行文本转语音转换的 Web 应用程序中。这对于为书籍中的音频添加视觉功能、具有嘴唇同步功能的头像(使用 viseme 语音标记)、网站和博客以及帮助听力障碍人群非常有用。
除了突出显示文本,它还可以扩展到执行其他任务。例如,浏览器可以在朗读文本时显示图像、播放音乐以及执行其他动画效果。这个功能对于创建动态音频书籍、教育内容和更丰富的文本转语音应用程序非常有用。
欢迎您尝试此解决方案,并从以下链接中了解有关相关 AWS 服务的更多信息。您可以根据自己的需求扩展其功能。
- Amazon API Gateway
- Amazon CloudFront
- AWS Lambda
- Amazon Polly
- Amazon S3