离线音频配音 您所在的位置:网站首页 微软配音不能用了 离线音频配音

离线音频配音

2023-10-14 15:16| 来源: 网络整理| 查看: 265

你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。

离线音频配音 Azure AI 服务 Azure AI 语音

本指南介绍 Azure 认知服务中的 AI 工具如何帮助你自动化离线配音流程,确保原始输入获得优质配音版本。

配音是用替换音轨替换掉原始音频/视频源的过程。 包括将源语音流转换为目标语音流。 配音分为两种类型。 实时配音会修改原始音频/视频轨上的内容。 离线配音是指后期制作的过程。 与实时配音相比,离线配音支持人工协助以提高整体配音效果。 可以在每个阶段更正错误并添加足够多的元数据,让配音更忠实于原声。 可以使用同一管道为视频轨提供字幕。

FFmpeg 是 Fabrice Bellard 的商标。

体系结构

此体系结构显示了在离线模式下执行人工协助语音转语音配音的管道。 每个模块成功完成后触发下一个模块。 语音转语音管道包含生成离线配音版音频流所需的元素。 它使用口述文本的脚本生成字幕。 还可以选择增强字幕来生成隐藏式字幕。

下载此体系结构的 PowerPoint 文件。

工作流 视频引入:FFmpeg 开源软件可以将输入视频内容拆分为音频流和视频流。 管道将音频流保存为 WAV 文件,位于 WAV 文件容器(Blob 存储)中。 FFmpeg 稍后在此过程中合并视频流。 语音转文本模块:语音转文本模块使用 WAV 文件容器中的音频文件作为输入。 它使用 Azure 语音服务来确定源音频所讲的一种或多种语言,并检测每一个说话的人。 它将音频转录并保存为文本格式。 语音转文本模块将文本文件、字幕文件和字词级别时间戳文件存储在 STT 脚本容器中。 字幕文件生成和过滤:语音转文本模块生成附带注释的字幕文件,协助人工审核内容并更正由语音转文本模块造成的错误。 字幕文件中包含文本、时间戳以及其他元数据(例如说话人 ID 和语言 ID),这些元数据让管道的其余部分能够正常工作。 管道将字幕文件作为 WebVTT 文件存储在 STT 脚本容器中。 其中包含注释,指出可能需要人工验证的点。 这些注释不会干扰将文件作为字幕文件添加到最终输出视频的功能。 语音转文本模块生成字幕文件后,人工编辑可以更正错误,并选择性地添加情绪标记来反映输入音频。 翻译器模块:文本的翻译不包含时间戳。 输入字幕文本是根据说话人进行编译的。 Azure 翻译器服务只需要具有上下文的文本即可执行映射到目标语言。 音频的时间点和位置将转发给管道的文本转语音模块,以便在输出中恰当表现源音频。 此输入还包括不同段落的语言标识。 它使翻译服务能够跳过不需要翻译的段落。 此外,翻译模块还能执行内容过滤。 生成目标字幕文件:此模块负责用目标语言再现字幕文件。 它通过替换经转录的输入文件中的文本来实现此功能。 会保留与文本关联的时间戳和元数据。 元数据中还可能包括留给人工审阅者的提示,更正内容以提高翻译文本的质量。 与语音转文本输出一样,该文件是存储在 Azure 存储中的 WebVTT 文件。 其中包括指出可能需要人工编辑的点的注释。 文本转语音模块:Azure 语音服务将从源音频中转录的文本转换为目标文本轨。该服务使用“语音合成标记语言”(SSML) 来表示目标文本的转换参数。 它会在文本转文本的过程中,微调声调、发音、情绪、语速、停顿以及其他参数。 该服务可实现多种自定义,包括支持使用神经网络定制声音。 它会生成输出音频文件(WAV 文件),该文件保存在 Blob 存储中留待之后使用。 时间点调整和 SSML 文件生成:源语言和目标语言中的音频段落的时长可能不同,每种语言的细节可能有所不同。 管道会调整目标输出流中语音的时间点和位置,既确保忠实于源音频,又让目标语言听上去依然自然。 它将输出语音转换为 SSML 文件。 它还将音频段落与正确的说话人匹配,并提供正确的语气和情绪。 管道生成 SSML 文件作为输入音频的输出。 SSML 文件存储在 Azure 存储中。 其中会标出经过人工修订在位置和语速上可能有所改进的段落。 合并:FFmpeg 将生成的 WAF 文件添加到视频流,以生成最终输出。 可以将此管道中生成的字幕添加到视频中。 Azure 存储:在生成和处理内容时,管道使用 Azure 存储来存储和检索内容。 如果需要在任何阶段更正错误,则可以编辑中间文件。 可以从不同模块重启管道,通过人工验证提高最终输出质量。 平台:平台组件完成管道,从而实现更高的安全性、访问权限和日志记录。 Azure Active Directory (Azure AD) 和 Azure 密钥保管库负责控制访问和存储密钥。 可以启用 Application Insights 来执行用于调试的日志记录。

管道能感知到错误。 当语言或说话人发生变化时,这种感知能力非常重要。 获取到正确的语音转文本和翻译输出的关键在于了解语音的上下文。 可能需要在完成每个步骤之后检查并更正输出文本。 在某些用例中,管道可以执行自动配音,但并没有为实时语音转语音配音进行过优化。 在离线模式下,会先处理全部音频,然后管道再继续进行操作。 这使每个模块都能够运行完整时长的音频,并与模块设计保持一致。

组件 Azure 认知服务是基于云的 AI 服务套件,可帮助开发人员在不具备直接的 AI 或数据科学技能或知识的情况下将认知智能内置于应用程序中。 可采用常用开发语言通过 REST API 和客户端库 SDK 使用这些服务。 Azure 语音服务将语音转文本、文本转语音、语音翻译、语音助手和说话人辨识功能统一到基于一项服务的订阅产品中。 Azure 翻译器服务是一种基于云的神经网络机器翻译服务,是 REST API 的 Azure 认知服务系列的一部分。 可以配合任何操作系统一起使用。 Azure 翻译器服务为全球数千家企业使用的许多 Microsoft 产品和服务提供支持,以执行语言翻译和其他与语言相关的操作。 用例

音频配音是媒体公司和 OTT 平台最有用且使用最广泛的工具之一。 它通过提高内容与本地受众的相关性,帮助在全球范围内触达更多受众。 虽然离线处理最适合配音,但某些场景下也可以使用实时配音。

离线配音

以下是离线音频配音最适合的一些用例:

使用另一种语言再现电影和其他媒体内容。 替换因为录制设备不佳、环境噪音或声音主体表现不到位而无法使用的现场原声。 实施内容过滤,删除猥亵语言或俚语。 在适用段落更正发音,调整面向目标受众的内容。 生成字幕。 作为该过程的副产品,配音管道会从音频/视频生成脚本。 可以使用此脚本生成原始语言或配音语言的字幕。 可以增强转录的文本,为媒体内容生成隐藏式字幕。

过去,上述场景在遇到困难时需要经过训练的专业人员手动干预。 语音和语言建模领域近几年不断发展。现在,你可以将 AI 模块注入这些流程中,提高效率和经济效益。

实时配音

虽然可以使用此管道进行实时配音,但不允许对更正进行人工干预。 此外,更大的音频文件需要的处理性能和时间也更多,会增加转录的成本。 应对此限制的一种方法是将输入音频拆分成多段更小的音频,再作为单独的元素通过管道推送。 不过,这种技术手段可能会带来一些问题:

将输入音频拆分为更小的段落会扰乱上下文,并降低转录质量。 默认的说话人 ID 标记可能会产生不一致的结果,这一问题在说话人标识不使用自定义模型时尤其明显。 当音频拆分为多段时,基于出场顺序的默认标记很难准确进行说话人映射。 如果源音频为特定说话人分配了单独的通道,则可以解决这一问题。 时间点对于获取自然且同步的输出音频至关重要。 需要最大限度减少生产管道中的延迟,并精确进行衡量。 可以通过识别管道造成的延迟并使用足够大的缓冲区,来确保输出与输入音频的节奏保持一致。 此外,还需要考虑到可能出现的管道故障,并相应地保持输出一致性。

考虑到上述几点后,就可以使用此管道在以下场景处理实时音频:

音频中只包含一个说话人,明显能听出在使用一种语言。 没有多个说话人的音频重叠在一起的情况,并且当说话的人或使用的语言发生变化时有明显的停顿。 每个说话人生成不同的音频通道,并且可以独立处理。 设计注意事项

本部分介绍音频配音管道中提供的服务和功能,重点介绍生成高质量输出音频需要考虑哪些因素。 请注意,本指南的实现所使用的基础或通用模型适合于所有认知模型。 自定义模型可以增强每一项服务以及整体解决方案的输出效果。

语音转文本

每个模块的性能会显著影响整体输出的质量。 当输入质量因为语音问题或背景噪音变差时,后续模块中就会出现错误,让生成没有错误的高质量输出变得越来越困难和耗时。 不过,可以利用 Azure 语音服务中的多项功能最大限度减少此类问题,确保生成高质量的输出。

将音频适当分成多段。 适当将源音频分成多段对于改进整体解决方案而言至关重要。 语音转文本模块根据明显的停顿执行分段,即对源音频进行粗略分段。 此外,语音转文本模块还将更长的音频段落拆分为保留在语音转文本服务中的段落。 不过,这样分段会带来上下文错误、说话人映射错误等问题。

提取时间点信息。 配音过程涉及时间点不对齐的问题,这一问题源于不同语言对同一段内容的表达方式不同。 语音转文本会从源音频生成时间点信息,这些信息是在目标语言中以正确的时间点重现语音的基础。 尽管在文本的翻译中并不需要,但需要将时间点信息传递给生成 SSML 文件的模块。 时间点数据的粒度保持在段落级别。 由于翻译不提供对齐信息,因此字词级别的时间点无法传递给目标语言。 因此,修改文本输入从而在翻译中容纳上下文需要是可逆的。

将音频源作为一个流呈现。 能够区分源音频中的各个说话人是生成正确配音目标的另一个重要考虑因素。 根据每个说话人的标识将音频流分成多个同类段落的过程称为“分割聚类”。 如果不同说话人段落不能作为单独的音频流,则执行一致的分割聚类最简单的方法是将音频作为一个流来呈现,而不是将其拆分为多个段落。 如果这样呈现音频,则会根据出场顺序为不同的说话人编制索引,这样可以减少使用自定义说话人-标识模型来准确识别说话人的需求。

请考虑将多种模型组合在一起使用。 自定义模型能够更好地识别说话人,并适当标记任何特定说话人所说的文本内容。 某些情况下,最好将两种方法结合在一起使用,为主要说话人构建说话人识别模型,同时使用该服务以其出场顺序为剩下的说话人进行分割聚类。 可以使用 Azure 说话人识别来采用此方法,训练说话人识别模型识别主要说话人。 需要为每个主要说话人收集足够多的音频数据。 使用 Azure 语音转文本生成的文本脚本来识别说话人的切换,并将音频数据分成不同说话人的段落。 然后再根据需要进行后期处理并优化结果。 此过程可能涉及合并或拆分段落、调整段落边界,或更正说话人识别错误。

根据具体需求以及音频数据的特征,实现细节可能有所不同。 此外,分割聚类系统属于计算密集型,因此需要考虑语音转文本模型的处理速度。 可能需要使用速度经过优化的模型或是并行处理技术来加快分割聚类过程。

使用词汇文本可减少 ITN 可能造成的错误。 语音转文本提供一系列格式设置功能,以确保转录的文本准确且易读。 语音转文本可生成多种输入语音的表示形式,每种都有不同的格式设置。 经过删除不流利部分、逆文本规范化 (ITN)、发音、大写、亵渎语音过滤等处理后获得的识别文本称为“显示文本”。 显示文本的可读性更强,并使用预构建的模型来定义如何显示不同组件。 例如,显示格式可能包括专有名词使用大写字母,或插入逗号来分隔短语。 应在管道中继续使用这些格式,因为有助于在翻译中提供上下文,并最大限度减少重新设置文本格式的需求。

服务识别的字词以“词汇格式”显示。 词汇格式包含字词拼写和使用同音异义词等因素。 该体系结构使用此格式来评估更正语音转文本输出在多大程度上需要人工干预。 此格式表示语音转文本服务的实际识别,并且格式设置不过滤不匹配的识别。 例如,如果根据上下文可以判断出想表达这样的含义,则词汇格式会将字词“那里”呈现为“他们的”。

稍后在管道中过滤猥亵语言。 语音转文本提供过滤猥亵语言的选项。 此处介绍的体系结构不使用猥亵语言过滤。 可以通过显示格式设置启用猥亵语言过滤。 最好避免在管道早期使用此类过滤,因为这样做可能会导致一些上下文丢失,并在管道进行后续步骤的过程中造成不匹配。 不过,过滤影响的是显示文本和 MaskedITN,而不是语音转文本输出中的词汇文本。

使用自定义模型时,请考虑创建联合语言模型。 Azure 语音服务提供语言识别功能。 需要事先定义与输入音频相关的区域设置才能使用此功能。 语言识别需要一些上下文,有时在标记语言更改时会存在延迟。 如果使用的是自定义模型,则请考虑创建联合语言模型,这种模型经过多种语言的语句训练。 在处理通常将两种语言混在一起使用的区域的语音时,这种方法很有用。例如,杂糅了印地语和英语的印度英语。

确保不同说话人之间切换清晰,停顿自然。 和语言切换类似,当不同说话人之间切换清晰,停顿自然时,用户切换的效果也最好。 如果不同说话人之间的对话重叠在一起或者停顿很短,并且音频捆绑到一个通道中,则语音转文本输出可能包含需要人工干预的错误。 可能并非内容重点,但却影响输出质量的背景语音也会导致出现类似的问题。 更正此类错误需要字词级别时间点来识别不同说话人合适的偏移。

翻译器服务

Azure 翻译器服务提供可用于生成高质量输出的功能。

保留足够多的上下文。 使用 Azure 翻译器服务时主要应考虑要呈现多少上下文才能生成准确的翻译。 为了让翻译准确无误,请考虑在有意义的时间段内捆绑来自同一说话人 ID 的文本(如果上下文看起来没问题)。 请注意,虽然语音转文本会根据有意义的停顿来拆分输入音频,但对于更长的独白,会将音频拆分成多个大约 30 秒时长的段落。 这样拆分可能会造成丢失一些上下文。 此外,不同说话人的语音重叠在一起可能会扰乱上下文,导致翻译输出质量较差。 因此,在此步骤中,人工干预通常是不可或缺的。

请考虑在多语言源中跳过分段。 某些情况下,输入会包含好几种语言,并且一些段落不需要翻译。 例如,源和目标区域设置可能匹配,或者术语或行话保留不译。 若要跳过不想翻译的语言,每一段都需要定义 Azure 翻译器服务的源和目标区域设置。 该服务需要配置文件来定义不应翻译的语言。 为了降低成本,请考虑过滤掉不需要翻译的段落,这样就不用将这些输入到 Azure 翻译器服务中。

单独过滤字幕中的亵渎语言。 与语音转文本类似,Azure 翻译器服务让你能够过滤亵渎语言。 过滤猥亵内容或将内容标记为猥亵可以让字幕更适合目标受众观看。 然而,通过过滤猥亵语言删除内容可能会混淆服务的语音输出的时间点分析和位置。 还可能导致误解上下文。 应考虑单独生成过滤掉猥亵语言之后的字幕文件。

确保字幕的时间点准确对齐。 尽管语音转文本可以生成字词级别的时间戳,但 Azure 翻译器服务并不会将时间戳与对齐数据进行匹配。 因此,采用 WebVTT 格式的字幕文件中的时间戳与源文件中的时间戳保持不变。 这可能导致字幕在显示时出现轻微对不齐的情况,因为翻译到另一种语言可能会缩短或增加内容的长度。

文本到语音转换

选择适当的声音。 根据内容类型和希望触达的受众,所选择的说话人的声音会极大地影响受众对内容的接受程度。 该服务提供多种预生成的声音,覆盖多种语言的男声和女声。 此外,还提供允许注入情绪的预构建模型。 或者,也可以使用神经网络定制声音来呈现目标语音语言,提供精简版和专业版两个版本。 如果使用的是专业版,则可以为声音添加情绪,为源语音输入建立模型。 所有情况下,都需要为每个说话人和每种语言定义要使用的声音。 除非检查语音转文本输出,以确保没有身份不明的说话人,否则需要定义一个默认声音。

执行语音放置。 目标语音的放置以及将其集成到 SSML 文件中是文本转文本模块的关键元素。 以下部分介绍这些元素。

准确集成 SSML 文件。 SSML 让你能够自定义文本转文本输出,并识别有关目标音频的音频构成、说话人、停顿和时间点放置的详细信息。 可以使用 SSML 设置声调、发音、语速和情绪等文本转文本输出属性的格式。 有关详细信息,请参阅语音合成标记。

正确构成 SSML 文件需要给定段落的时间点信息并选择声音。 因此,所有内容都需要返回到源音频中的正确段落,并且沿管道进行的任何更改都应与这些片段对齐。 由于翻译服务不生成时间点信息,因此语音转文本输出是时间点详细信息的唯一来源。 此外,唯一可传递的元素是应该用于确定目标音频时长的片段时间点和偏移,以及中间的停顿。 所有这些信息都应该传递给管道中剩余的组件,并且如前所述,应该在目标音频的时间点进行调整。

使用 voice 标记拆分输出会导致尾随和前导处静音。 可以在 voice 标记中为静音定义一个值。 在下面的示例中,两段语音之间的间隔为 450 毫秒(其中 300 毫秒为尾随,150 毫秒为前导)。 在计算输出的最终放置时,需要考虑到此类间隔。 下面是定义其中一些详细信息的示例 SSML 元素:

If we're home schooling, the best we can do is roll with what each day brings and try to have fun along the way. A good place to start is by trying out the slew of educational apps that help children stay happy and smash their schooling at the same time.

为每个说话人生成单独的 SSML 文件。 SSML 文件的格式设置让你可以为不同声音段落定义相对时间。 该值必须为正值。 例如,此 XML 定义生成口述文本后有一个停顿:

Welcome to Microsoft Cognitive Services Text-to-Speech API.

当好几个人同时说话时,此方法不起作用。 一种选项是为每个说话人生成单独的 SSML 文件(或将不重叠的部分整合到一起),并在生成音频后覆盖这些文件。

用单独的通道来包含背景音效。 源音频中的背景噪音不会通过此管道传播,因此 SSML 文件的构成中不会将其包含在最终输出中。 需要在单独的通道中呈现任何适用的背景音效。 然后,使用外部工具将其添加到最终的文本转文本输出中。

手动添加情绪和样式,增强语音合成效果。 源音频中未检测到情绪和语气,因此它们不会通过管道传播,也不会出现在 SSML 文件中。 需要手动添加这些详细信息。 最好将它们添加到源 WebVTT 输出,并以 WebVTT 注释的形式将其作为信息进行传递。 与元素中的所有其他信息一样,情绪和样式详细信息以注释的方式添加,并在生成 SSML 时进行解析。 可以通过为不同的情绪呈现不同模型,或使用专业版神经网络定制声音功能来提升语音的合成效果。

时间点放置与调整

语音在被翻译成不同语言后,内容长度上通常会有所差异。 需要考虑输出语音的放置和调整来适应长度上的差异。 这里给出了执行此类调整建议采用的一些方法。

根据源和目标语言语速动态调整语速。 直接的方法是将目标语音与源语音的时长匹配。 这种方法需要运行一次文本转文本,以确定当转换为语音后,目标文本占用的时长。 最终语速用于生成实际的目标音频,该音频反映同一段音频相对于原始时间的目标文本时长的语速。

这种方法很简单,可以为每段语音生成不同的语速。 输出听上去不自然。 为了达到理想的听感,可以更改源音频中说话人实际的语速。 可以以目标语言的标准语速为参考,衡量生成的语速的快慢来提升输出效果,然后再调整语速以反映源音频中的语速。 不要尝试让其适应相同的时间跨度。 若要进行此类调整,请以源语言平时说话时每分钟的平均字数作为参考。 这些字数与语音转文本的输出进行比较,用于识别每段音频中源说话人的语速。 然后,在目标语言中使用此语速。 使用目标语言的说话人的正常语速应纳入到文本转文本模块中。 此外,还可以为语速设置限制,以确保生成的音频输出在受控限制范围内。 以下这段代码可实施此过程:

internal static double GetRelativeTargetRate(PreProcessTTSInput input, IOrchestratorLogger logger) { string sourceLanguage = input.IdentifiedLocale.Split('-')[0]; string targetLanguage = input.TargetLocale.Split('-')[0]; double maxTargetRate = input.PreProcessingStepConfig.MaxSpeechRate; double minTargetRate = input.PreProcessingStepConfig.MinSpeechRate; if (SpeechRateLookup.Rate.ContainsKey(sourceLanguage) && SpeechRateLookup.Rate.ContainsKey(targetLanguage)) { try { // Look up values from static table double sourceWordRate = SpeechRateLookup.Rate[sourceLanguage].WordRate; double targetWordRate = SpeechRateLookup.Rate[targetLanguage].WordRate; double sourceCharRate = SpeechRateLookup.Rate[sourceLanguage].CharRate; double targetCharRate = SpeechRateLookup.Rate[targetLanguage].CharRate; int sourceWordCount = input.LexicalText.Split(" ").Length; int sourceCharCount = input.LexicalText.Length; // Calculate the rate of the source segment relative to the nominal language rate double sourceRelativeWordRate = ((sourceWordCount / input.Duration.TotalMinutes) / sourceWordRate); double sourceRelativeCharRate = ((sourceCharCount / input.Duration.TotalMinutes) / sourceCharRate); double sourceAverageRelativeRate = (sourceRelativeWordRate + sourceRelativeCharRate) / 2.0; // Calculate the ratio between the target and source nominal rates double averagedLanguageRateRatio = (((targetWordRate / sourceWordRate) + (targetCharRate / sourceCharRate)) / 2.0); // Scale the target rate based on the source/target ratio and the relative rate of input with respect to the nominal source rate double relativeTargetRate = sourceAverageRelativeRate / averagedLanguageRateRatio; relativeTargetRate = Math.Min(relativeTargetRate, maxTargetRate); relativeTargetRate = Math.Max(relativeTargetRate, minTargetRate); return relativeTargetRate; } catch (DivideByZeroException exception) { logger.LogError(exception.Message); return 1.0; } } else { return 1.0; } }

调整停顿和时间点,让文本转语音输出听上去自然。 如果目标语言的语音时长更长,请使用原始音频中各段语音之间的停顿来增加时长。 这样做可能会稍微减少句子之间的停顿,但可以确保语速听起来更加自然。

将生成的音频与原始音频的时间对齐。 可以在开头、中间或结尾处执行此操作。 在此示例中,假设使用原始音频跨度的中间来对齐目标输出的音频段落。

可以从目标语言中的标记或字符数量来估计语音信号的时长。 或者,利用文本转语音服务通读一遍文本以确定确切的时长。 由于音频是居中的,因此将目标和源音频时长之间的差异均匀地分配到原始音频之前和之后的两处停顿。

如果目标音频与目标中的任何其他音频都不重叠,则直接放置就可以。 目标音频中的新偏移的计算公式如下所示,用原始偏移减去目标和源音频时长之间差值的一半:

offsetti = offsetsi - (tti – tsi)/2

不过,目标音频中的多段音频可能会重叠。 此过程旨在更正这一问题,只考虑前一段音频。 以下是可能发生重叠的场景:

offsetti> offsett(i-1) + tt(i-1)。 在此场景中,原始音频中两段之间分配的停顿都可以容纳得下这两段音频,没有问题。 offsetti = offsett(i-1) + tt(i-1)。 在此场景中,目标语言中两段连续的音频之间没有停顿。 向 offsetti (Δtt) 添加一点偏移(足够用目标语言说出一个字的时间)。 (Δtt) 用于避免在目标音频中出现原始音频中没有的连续音频。 offsetti< offsett(i-1) + tt(i-1)。 这种情况下,两段音频重叠在一起。 存在两种可能的方案: offsets(i-1) + ts(i-1)< median {offsets(i-1) + ts(i-1)}∀i。 这种情况下,停顿时间比视频中标称的停顿要短。 移动翻译文本可以解决此问题。 增加停顿时间应该就可以避免出现重叠。 因此,offsetti = offsett(i-1) + tt(i-1) + Δtt。 offsets(i-1) + ts(i-1)> median {offsets(i-1) + ts(i-1)}∀i。 这种情况下,停顿虽然比视频中标称的停顿时间长,但没办法与翻译之后的音频对齐。 可以使用语速来调整此段落,让时间点对齐得更好。 可以使用此公式来计算新的语速:rateti = rateti * (((offsett(i-1) + tt(i-1)) - offsetti) + tti + Δtt )/( tti)。

实现此过程可能导致最后一段的运行时间比原始音频的时间长,如果视频在原始音频结束时结束,但目标音频更长的话,这种情况更加明显。 这种情况下,有两种选择:

让音频运行更长时间。 将目标音频的全部偏移添加到预偏移,让最后的 offsettn = offsetsn - (ttn – tsn)。 如果此偏移与前一段音频重叠,则使用前文所述的语速修改方法进行调整。

如果翻译之后,目标文本比原始文本更长,则适合在中间对齐音频段落。 如果目标语言语速总是比原始的更快,则可能最好将时间点对齐到原始音频段落的开头。

private List CompensatePausesAnchorMiddle(List inputs) { logger.LogInformation($"Performing Text to Speech Preprocessing on {inputs.Count} segments."); foreach (var input in inputs) { ValidateInput(input); double rate = PreprocessTTSHelper.GetRelativeTargetRate(input, logger); input.Rate = rate; } TimeSpan medianPause = PreprocessTTSHelper.CalculateMedianPause(inputs); // Setting nominal pause as average spoken duration of an English word TimeSpan nominalPause = new TimeSpan(0, 0, 0, 0, (int)(60.0 / 228.0 * 1000.0)); string targetLanguage = inputs[0].TargetLocale.Split('-')[0]; if (SpeechRateLookup.Rate.ContainsKey(targetLanguage)) { nominalPause = new TimeSpan(0, 0, 0, 0, (int)(60.0 / SpeechRateLookup.Rate[targetLanguage].WordRate * 1000)); } TimeSpan previous_target_offset = new TimeSpan(0); TimeSpan previous_target_duration = new TimeSpan(0); TimeSpan previous_source_offset = new TimeSpan(0); TimeSpan previous_source_duration = new TimeSpan(0); foreach (PreProcessTTSInput source_segment in inputs) { var source_duration = source_segment.Duration; var source_offset = source_segment.Offset; var target_duration = PreprocessTTSHelper.GetTargetDuration(source_segment); var target_offset = source_offset - (target_duration - source_duration) / 2; if (target_offset == previous_target_offset + previous_target_duration) { target_offset += nominalPause; source_segment.HumanInterventionRequired = true; source_segment.HumanInterventionReason = "After translation this segment just fit in the space available, a small pause was added before it to space it out."; } else if (target_offset < previous_target_offset + previous_target_duration) { source_segment.HumanInterventionRequired = true; var previous_pause = source_segment.Offset - previous_source_offset + previous_source_duration; if (previous_pause < medianPause) { target_offset = previous_target_offset + previous_target_duration + nominalPause; source_segment.HumanInterventionReason = "After translation this segment overlapped with previous segment, but since the pause before this segment in the source was relatively small, this segment was NOT sped up and was placed right after the previous segment with a small pause"; } else { var target_rate = source_segment.Rate * ((previous_target_offset + previous_target_duration - target_offset) + target_duration + nominalPause) / target_duration; source_segment.Rate = target_rate; target_duration = PreprocessTTSHelper.GetTargetDuration(source_segment); target_offset = previous_target_offset + previous_target_duration + nominalPause; source_segment.HumanInterventionReason = "After translation this segment overlapped with previous segment, also the pause this segment in the source was relatively large (and yet the translated segment did not fit), hence this segment was sped up and placed after the previous segment with a small pause"; } } source_segment.Offset = target_offset; previous_target_offset = target_offset; previous_target_duration = target_duration; previous_source_offset = source_offset; previous_source_duration = source_duration; } return inputs; } 创建用于字幕和人工编辑的 WebVTT 文件。

Web 视频文本轨格式 (WebVTT) 是一种用于显示字幕等记时文本轨的格式。 用于向视频添加文本覆盖。 WebVTT 文件显示管道输出受众接收信息。 此外,字幕文件也使用 WebVTT 文件。 本部分介绍在此管道中使用 WebVTT 时的一些注意事项。

在 WebVTT 文件的注释中包含消息。 WebVTT 文件应包含处理各种模块所需的所有信息。 除了时间点和文本信息外,还包括语言 ID、用户 ID,以及提醒进行人工干预的标记。 若要在 WebVTT 文件中包含上述详细信息,并保持其兼容性能够用作字幕文件,请使用 WebVTT 文件中每一段中的 NOTE 部分。 以下代码显示了建议的格式:

NOTE { "Locale": "en-US", "Speaker": null, "HumanIntervention": true, "HumanInterventionReasons": [ { "Word": "It's", "WordIndex": 3, "ContentionType": "WrongWord" } ] } 1 00:00:00.0600000-->00:00:04.6000000 - Hello there! - It's a beautiful day

确保字段在各种尺寸的屏幕上都能正确显示。 生成字幕文件时,需要考虑要在屏幕上显示多少字和多少行。 在生成 WebVTT 文件过程中,需要为每一个用例专门考虑这一点。 了解情况的人员会查看这些文件,并利用提示解决问题。 此类文件中还包含管道中后续其他模块正常工作所需的元数据。

需要将较长的段落(转录或翻译结果)拆分成多个 VTTCues,以确保字幕正确显示。 需要保留注释和其他详细信息,使其仍然指向正确的上下文,并确保管道没会截断文本的翻译或文本转文本的放置。

将原始文本段落拆分成多个 VTTCues 时,还需要考虑每一段的时间点。 可以使用转录期间生成的字词级别时间戳来达到此目的。 由于 WebVTT 文件支持修改,因此可以手动更新这些时间戳,但过程中会产生相当大的维护开销。

识别可能需要人工干预的点

配音管道包含三个重要模块,分别是:语音转文本、翻译,以及文本转语音。 每个模块中都有可能发生错误,错误可能会传播,并在管道中稍后变得更加明显。 强烈建议包含人工干预来审查和纠正结果,以确保质量达到可接受的水平。 可以通过识别需要干预的具体区域来节省大量时间。 本部分介绍每个模块中需要手动干预的潜在错误。

语音转文本

语音转文本的输出会受到音频本身、背景噪音和语言模型的影响。 可以通过自定义语言模型更好地适应输入来提升音频内容。 根据所处场景下可以进行人工干预的点,可以考虑以下选项:

识别转录错误。 语音转文本系统有时会产生转录错误。 它会提供多个转录选项,每个选项都拥有置信度分数。 这些分数可以帮助你确定最准确的转录。 n-最佳列表包含给定语音输入最可能的假设。 此列表中排名最高的假设分数最高,表示转录内容是正确的可能性。 例如,分数为 0.95 表示排名最高的假设的准确度置信度为 95%。 通过比较不同输出之间的分数和差异,可以识别需要人工干预的差异之处。 查看 n-最佳列表时,需要考虑阈值和多数投票:

设置更高的阈值可以提高配音管道的质量。 该阈值在识别 n-最佳结果中可能存在的错误方面起着关键作用。 它确定在此过程中比较的 n-最佳结果的数量。 阈值越高,就会返回更多 n-最佳结果进行比较,从而获得更高的干预质量。 将该阈值设置为零会限制候选项的选择,但与语音转文本系统生成的转录相比,其提供的置信度更高。

平衡召回率和精准率。 使用多数投票时,为在阈值范围内的每个候选项选择词汇形式。 接下来,确定与输出阶段相关的插入、删除和替换。 需要考虑这些要注明的争用点,但注明所有争用点会优先召回率,而代价则是精准率更低。 若要提高精准率,请根据标准平均分为插入、删除和替换进行排序。 此类错误的数量(相对于候选项数减去 1)充当权重。 可以设置阈值来校准希望达到的精度级别。 权重为 0 会优先召回率,表示至少有一个候选项中存在错误。 相反,权重为 1 时会优先精准率,这意味着所有候选项中都有错误。 在此管道中,需要在召回率和精准率之间合适的平衡点。

public string EvaluateCorrectness(SpeechCorrectnessInput input) { var speechOutputSegments = input.Input; var ConfidenceThreshold = input.Configuration.ConfidenceThreshold; var errorOccurrenceThreshold = input.Configuration.OccurrenceThreshold; var speechCorrectnessOutputSegments = new List(); foreach (var speechOutputSegment in speechOutputSegments) { // Filter the candidates within the lower confidence candidate threshold var filteredCandidates = GetFilteredCandidates(speechOutputSegment,ConfidenceThreshold); ICollection interventions = null; if (filteredCandidates.Count > 0) { // Calculate the possible reasons for needed intervention based on the selected text, the filtered candidates, and the error threshold interventions = CalculateInterventionReasons(speechOutputSegment.LexicalText, filteredCandidates, errorOccurrenceThreshold); } // Build the speech correctness segment SpeechCorrectnessOutputSegment correctionSegment = new SpeechCorrectnessOutputSegment() { SegmentID = speechOutputSegment.SegmentID }; if (interventions != null && interventions.Count > 0) { correctionSegment.InterventionNeeded = true; correctionSegment.InterventionReasons = interventions; } else { correctionSegment.InterventionNeeded = false; correctionSegment.InterventionReasons = null; } speechCorrectnessOutputSegments.Add(correctionSegment); } return JsonConvert.SerializeObject(speechCorrectnessOutputSegments); } private ICollection CalculateInterventionReasons(string selectedText, ICollection candidates, double errorOccurrenceThreshold) { ICollection allPossiblePointsOfContention = new List(); foreach (var candidate in candidates) { // Get points of contention for all speech candidates in this segment, compared to the text selected var candidateSentence = candidate.LexicalText.Split(' '); var segmentSentence = selectedText.Split(' '); var editDistanceResults = CalculateEditDistance(candidateSentence, segmentSentence); var allPossibleCandidatePointsOfContention = GetAllPossiblePointsOfContention( candidateSentence, segmentSentence, new Stack(), editDistanceResults); allPossiblePointsOfContention.Add(allPossibleCandidatePointsOfContention); } // Calculate which combination of all possible points of contention will lead to the highest count for the selected points of contention across all candidates var bestCandidatePointsOfContention = GetBestContentionCombination(allPossiblePointsOfContention); // Keep count of occurrences of all points of contention across the candidates var candidatePOCCounts = new Dictionary(); foreach (var pointsOfContention in bestCandidatePointsOfContention) { CountPointsOfContention(pointsOfContention, candidatePOCCounts); } var filteredPointsOfContention = new List(); foreach (var pocToCount in candidatePOCCounts) { // For each point of contention, calculate its rate of occurrence relative to the number of candidates var occurrenceRate = (double)pocToCount.Value / candidates.Count; // If the rate of occurrence is within the threshold, count the point of contention as an error that requires intervention if (occurrenceRate >= errorOccurrenceThreshold) { filteredPointsOfContention.Add(pocToCount.Key); } } return filteredPointsOfContention; }

实现间距足够大的语言转换检测。 在语言识别中,检测从一种语言转换到另一种语言涉及识别多个字词。 当转换发生在一定数量的标记内时,会进行标记。 此假设基于这样一个假设,即间隔足够大的段落更有可能被准确检测到。 目的是避免误报,即当语言发生变化时,语音转文本系统在切换回之前的语言之前无法检测到第二种语言。

标记身份不明的说话人 ID。 使用语音转文本进行分割聚类时,系统可能会将一些说话人 ID 标记为“Unidentified”。 这种情况通常发生在某个人说话的时间很短时。 建议标记这些标签提醒进行人工编辑。

翻译

BLEU 分数通常用作评估翻译输出质量的指标。 但 BLEU 分数存在限制。 它依赖于将机器翻译的输出与人工生成的参考翻译进行比较来衡量相似度。 该指标不考虑流利度、语法以及意义的保留等重要元素。

可以使用替换方法来评估翻译质量。 其中涉及执行双向翻译和比较结果。 首先,将源语言的输入段落翻译成目标语言。 然后,倒转过程,将目标语言翻译回源语言。 此过程会生成两段字符串:源段落 (Si) 以及源的预期值 (E(Si))。 可以通过比较这段字符串确定插入和删除的次数。

如果插入和删除的次数超过预定义的阈值,则会标记相应语句提醒需要进一步检查。 如果次数低于阈值,则认为段落 Si 是翻译质量出色的匹配项。 如果发现差异,最好是在源语言中将其标出。 这样做有助于让其他人员注意需要更正的具体项,而不是显示需要人工验证的整个段落。

文本到语音转换

与文本转语音标注关联度最高的是更改的语速,以及超出源音频中间隔的位置的变化。 文本转文本部分中会突出显示语速或位置发生过更改的位置。

作者

本文由 Microsoft 维护, 它最初是由以下贡献者撰写的。

首席作者:

Nayer Wanas | 首席软件工程师

其他参与者:

Mick Alberts | 技术文档撰写人 Vivek Chettiar | 软件工程师 II Bernardo Chuecos Rincon | 软件工程师 II Pratyush Mishra | 首席工程经理

若要查看非公开领英个人资料,请登录领英。

后续步骤 离线音频配音的实现示例 什么是 Azure 认知服务? 语音服务的语言和声音支持 相关资源 选择 Azure 认知服务技术 实现自定义语音转文本


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有