当前位置: 首页 > news >正文

关于亚马逊TTS的笔记

文章目录

  • 一、前言
  • 二、本地环境配置操作
  • 三、基础内容
    • 1、查看支持的语言及语音(不要盲目相信官方文档描述)
    • 2、引入依赖
    • 3、統一註冊事件
      • 1、利用本地环境模式
      • 2、引入本地文件的模式
      • 3、直接赋值的模式
  • 四、TTS 方式
    • 1、标准模式(standard)
    • 2、神经语言模式(neural)
      • 2.1、注意事项
      • 2.2、组合示例
        • 2.2.1、添加停顿(break)
        • 2.2.1、强调词语(emphasis)
        • 2.2.2、指定另一种语言(lang)
        • 2.2.3、在文字中放置自訂標籤(mark)
        • 2.2.4、段落暂停(p)
        • 2.2.5、语速、音调、音量(prosody)
  • 五、内容补充
    • 注意点:

一、前言

本文根据AWS的TTS 功能构建试验文档,包含java实践

引入文档 - Amazon Polly

基于官方提供文档和合作伙伴提供的讯息,主要有两种模式搭建

1、利用环境安装

2、利用环境的文件

二、本地环境配置操作

列出配置

aws configure list

设置配置

然后填入已经准备好的讯息

  • AWS Access Key ID: 您的访问密钥 ID。
  • AWS Secret Access Key: 您的密钥。
  • 默认区域(如:us-west-1)。
  • 输出格式(如:json)。
aws configure

三、基础内容

1、查看支持的语言及语音(不要盲目相信官方文档描述)

言语列表参考地址: https://docs.aws.amazon.com/polly/latest/dg/available-voices.html

# 这里是获取标准的语言
DescribeVoicesRequest describeVoiceRequest = DescribeVoicesRequest.builder()
        .engine("standard")
        .build();

DescribeVoicesResponse describeVoicesResult = polly.describeVoices(describeVoiceRequest);

2、引入依赖

<dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>software.amazon.awssdk</groupId>
                <artifactId>bom</artifactId>
                <version>2.29.45</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.11.4</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>2.0.5</version>
        </dependency>
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>polly</artifactId>
        </dependency>
        <dependency>
            <groupId>com.googlecode.soundlibs</groupId>
            <artifactId>jlayer</artifactId>
            <version>1.0.1.4</version>
        </dependency>
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>sso</artifactId>
        </dependency>
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>ssooidc</artifactId>
        </dependency>
    </dependencies>

3、統一註冊事件

大陆及港澳台 REGION = "ap-southeast-1"

构建 PollyClient

1、利用本地环境模式

正如【二】中所示,在运行环境中配置 aws configure 即可。

执行sdk过程中不设置 profileFilecredentialsProvider 即可

PollyClient polly = PollyClient.builder()
        .region(Region.of(REGION))
        .build();

2、引入本地文件的模式

资源目录 中设置配置文件,示例配置文件 credentials

[default]
aws_access_key_id = EXAMPLEACCESSKEY1234
aws_secret_access_key = EXAMPLESECRETKEY1234567890abcdefg

[my-profile]
aws_access_key_id = EXAMPLEACCESSKEY12345
aws_secret_access_key = EXAMPLESECRETKEY1234567890abcdefgh
// 从 resources 中获取凭证文件的输入流,假设文件路径为 "/aws/credentials"
InputStream credentialsStream = PollyDemo.class
        .getResourceAsStream("/aws/credentials");
if (credentialsStream == null) {
    throw new RuntimeException("未能在 resources 中找到 '/aws/credentials' 文件");
}

// 将资源文件复制到临时文件中
Path tempCredentialsFile = Files.createTempFile("aws-credentials", ".tmp");
Files.copy(credentialsStream, tempCredentialsFile, StandardCopyOption.REPLACE_EXISTING);
credentialsStream.close();
// 使用临时文件构造 ProfileFile 对象
ProfileFile profileFile = ProfileFile.builder()
        .content(tempCredentialsFile)
        .type(ProfileFile.Type.CREDENTIALS)
        .build();

DefaultCredentialsProvider build = DefaultCredentialsProvider.builder().profileFile(profileFile)
        .profileName("my-profile").build();

PollyClient polly = PollyClient.builder()
        .credentialsProvider(build)
        .region(Region.AP_SOUTHEAST_1)
        .build();

3、直接赋值的模式

直接明文配置内容

// 创建 PollyClient
PollyClient polly = PollyClient.builder()
        .credentialsProvider(StaticCredentialsProvider.create(
                AwsBasicCredentials.create(ACCESS_KEY, SECRET_KEY)
        ))
        .region(Region.of(REGION))
        .build();

四、TTS 方式

这里采用 mode 作为参数值 ,可参考 software.amazon.awssdk.services.polly.model.Engine

其实靠谱实际用的就是 standardneural

  • standard (标准)
  • neural (神经)
  • long-form (长格式语音)
  • generative(生成式语音)
DescribeVoicesRequest describeVoiceRequest = DescribeVoicesRequest.builder()
        .engine(mode)
        .build();

获取语音实体,这里示例获取Joanna

  DescribeVoicesResponse describeVoicesResult = polly.describeVoices(describeVoiceRequest);
        Voice voice = describeVoicesResult.voices().stream()
                .filter(
                        v -> {
                            System.out.println(v.name());
                            return v.name().equals("Joanna");
                        }
                )
                .findFirst()
                .orElseThrow(() -> new RuntimeException("Voice not found"));

利用存在的语音实体来获取所需转录的文本语音文件流

这里 text 为所需转录的文本

format 通常为 OutputFormat.MP3

SynthesizeSpeechRequest synthReq = SynthesizeSpeechRequest.builder()
        .text(text)
        .voiceId(voice.id())
        .outputFormat(format)
        .build();

ResponseInputStream<SynthesizeSpeechResponse> synthRes = polly.synthesizeSpeech(synthReq);

1、标准模式(standard)

可以权当为类似微软的 【纯文本模式】,主要用于文本转录

2、神经语言模式(neural)

这里主要不同的是可以对 text 内容设置

可以参阅网站 : https://docs.aws.amazon.com/zh_cn/polly/latest/dg/ssml.html

主要围绕着官网摘抄

2.1、注意事项

针对内容中含有的【标点符号】,应该尽量替换

名称字符转义代码
引号(双引号)""
表示和的符号&&
撇号或单引号'
小于号<<
大于号>>

2.2、组合示例

所有标签都需要存在根节点 speak

根节点内可以任意组合标签,这里举例是通用举例

<speak>{{content}}</speak>
2.2.1、添加停顿(break)

两种属性来进行设置停顿

  • strength

    • 如果 break 标签旁边没有其他标点,则将创建 <break strength="medium"/>(逗号时长停顿)。
    • 如果标签位于逗号旁边,则将标签升级到 <break strength="strong"/>(句子时长停顿)。
    • 如果标签位于句号旁边,则将标签升级到 <break strength="x-strong"/>(段落时长停顿)。
  • time

    • [number]s:停顿的持续时长,以秒为单位。持续时长上限为 10s

    • [number]ms:停顿的持续时长,以毫秒为单位。持续时长上限为 10000ms

<speak>
     Mary had a little lamb <break time="3s"/>Whose fleece was white as snow.
</speak>
<speak>
     Mary had a little lamb <break strength="medium"/>Whose fleece was white as snow.
</speak>
2.2.1、强调词语(emphasis)
  • level
    • Strong:提高音量並減慢說話的速度,讓語音更大聲、更緩慢。
    • Moderate:提高音量並減慢說話的速度,但幅度比 strong 還小。Moderate 是預設值。
    • Reduced:降低音量並加快說話的速度。語音更輕柔、更快速。
<speak>I already told you I <emphasis level="strong">really like</emphasis> that person.</speak>
2.2.2、指定另一种语言(lang)

支持的语言可参考: https://docs.aws.amazon.com/zh_cn/polly/latest/dg/supported-languages.html

参考内容,若是本段内容使用 en-US ,则加入的内容为额外使用粤语来阅读

<speak>
     Hi <lang xml:lang="yue-CN">请问你吃饭了没</lang>.
</speak>
2.2.3、在文字中放置自訂標籤(mark)

高频调用时,可用于 特殊标记

<speak>
     Mary had a little <mark name="animal"/>lamb.
</speak>

调用接口后,返回标记的偏移值和标记值

{"time":767,"type":"ssml","start":25,"end":46,"value":"animal"}
2.2.4、段落暂停(p)

其实说白了,兼容 p 标签 ,可以短暂暂停。

2.2.5、语速、音调、音量(prosody)

基于神经语言模式,volumerate 屬性即可

  • volume
    • default:重設音量為目前語音的預設音量。
    • silentx-softsoftmediumloudx-loud:將音量設定為目前語音的預先定義值。
    • +ndB-ndB:根據目前音量來變更音量。+0dB 的值代表無變動,+6dB 表示大約目前音量的兩倍,-6dB 則是指大約目前音量的一半。
  • rate
    • x-slowslowmediumfastx-fast。 將音調設定為所選語音的預先定義值。
    • n%:說話速度的非負值百分比變更。例如,100% 表示說話速度無變化,200% 表示說話速度是預設速度的兩倍,以及 50% 表示說話速度率是預設速度的一半。這個值的範圍為 20 到 200%。
<speak>
     Sometimes it can be useful to <prosody volume="loud" rate="105%">increase the volume 
     for a specific speech.</prosody>                     
</speak>

五、内容补充

注意点:

这里需要增加languageCodeengine ,否则会莫名400错误,找不到engine

   SynthesizeSpeechRequest synthReq = SynthesizeSpeechRequest.builder()
                 .text(voiceReq.getContent())
                 .voiceId(voice.id())
                 .outputFormat(OutputFormat.MP3)
                 .languageCode(voice.languageCode())
                 .engine(Engine.NEURAL)
                 .build();
@PostMapping("/tts")
public String tts(VoiceReq voiceReq) {
    if (hasWork && StringUtil.isNotEmpty(voiceReq.getContent())) {
        String basePath = TtsUtils.buildFilePath("tc");
        String fileName = DigestUtil.sha256Hex(voiceReq.getContent());
        StringBuffer absolvePath = new StringBuffer(basePath).append("/").append(fileName).append(".mp3");
        try {
            boolean fileExist = TtsUtils.checkFile(absolvePath.toString());
            if (fileExist) {
                return fileName;
            }
            AwsTrans.synthesize(aws_access_key_id, aws_secret_access_key, voiceReq, absolvePath.toString());
            return fileName;
        } catch (IOException e) {
            e.printStackTrace();
        } catch (JavaLayerException e) {
            e.printStackTrace();
        }
        return null;
    }
    return null;
}
public class AwsTrans {

    /**
     * @param ACCESS_KEY
     * @param SECRET_KEY
     * @param voiceReq
     * @param pathName
     * @return
     * @throws IOException
     * @throws JavaLayerException
     * @desc build the new voice file from aws tts function
     */
    public static String synthesize(String ACCESS_KEY, String SECRET_KEY, VoiceReq voiceReq, String pathName) throws IOException, JavaLayerException {
        PollyClient polly = PollyClient.builder()
                .credentialsProvider(StaticCredentialsProvider.create(
                        AwsBasicCredentials.create(ACCESS_KEY, SECRET_KEY)
                ))
                .region(Region.AP_SOUTHEAST_1)
                .build();

        String fileName = talkPolly(polly, voiceReq, pathName);
        polly.close();
        return fileName;
    }

    private static String talkPolly(PollyClient polly, VoiceReq voiceReq, String pathName) {
        try {
            DescribeVoicesRequest describeVoiceRequest = DescribeVoicesRequest.builder()
                    .engine(Engine.NEURAL)
                    .build();
            LanguageCode languageCode = LanguageCode.YUE_CN;
            String voiceName = "Hiujin";
            if (voiceReq.getLanguage().equalsIgnoreCase("en")) {
                languageCode = LanguageCode.EN_US;
                voiceName = "Joanna";
            } else if (voiceReq.getLanguage().equalsIgnoreCase("sc")) {
                languageCode = LanguageCode.CMN_CN;
                voiceName = "Zhiyu";
            }
            DescribeVoicesResponse describeVoicesResult = polly.describeVoices(describeVoiceRequest);
            LanguageCode finalLanguageCode = languageCode;
            String finalVoiceName = voiceName;
            Voice voice = describeVoicesResult.voices().stream()
                    .filter(v -> v.name().equals(finalVoiceName))
                    .findFirst()
                    .orElseThrow(() -> new RuntimeException("Voice not found"));

            SynthesizeSpeechRequest synthReq = SynthesizeSpeechRequest.builder()
                    .text(voiceReq.getContent())
                    .voiceId(voice.id())
                    .outputFormat(OutputFormat.MP3)
                    .languageCode(voice.languageCode())
                    .engine(Engine.NEURAL)
                    .build();

            ResponseInputStream<SynthesizeSpeechResponse> synthRes = polly.synthesizeSpeech(synthReq);

            saveToFile(synthRes, pathName);
            System.out.println("Synthesis complete.");
            return pathName;
        } catch (PollyException | IOException e) {
            System.err.println("Polly Exception: " + e.getMessage());
            return null;
        }
    }

    private static void saveToFile(InputStream stream, String pathName) throws IOException {
        StringBuffer pathString = new StringBuffer(pathName);
        try (FileOutputStream fos = new FileOutputStream(pathString.toString())) {
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = stream.read(buffer)) != -1) {
                fos.write(buffer, 0, bytesRead);
            }
            System.out.println("Audio saved to " + pathString.toString());
        }
    }
}

相关文章:

  • 银行回单识别技术应用与API服务解析
  • 1 分钟掌握 PlantUML,快速绘制 UML 类图!
  • Docker学习--本地镜像管理相关命令--docker history 命令
  • 在Windows下使用Docker部署Nacos注册中心(基于MySQL容器)
  • 初识C++(入门)
  • kubernetes》》k8s》》Deployment》》ClusterIP、LoadBalancer、Ingress 内部访问、外边访问
  • 31天Python入门——第20天:魔法方法详解
  • TruPlasma RF 1002-G2/13 软件 TruPlasma RF 1003-G2/13软件 TRUMPF 调试监控软件
  • SQL Server:用户权限
  • 系统设计:高并发策略与缓存设计
  • 003-JMeter发起请求详解
  • LVS高可用负载均衡
  • 图漾相机——C#语言属性设置
  • 薛定谔的指针
  • Spring Cloud Gateway中Route Predicate Factories(路由断言工厂)的详细介绍
  • 华为OD机试 - 寻找连续区间 - 滑动窗口(Java 2024 E卷 100分)
  • Python入门(7):Python序列结构-字典
  • Docker容器网络相关设置
  • 【系统移植】 (二)交叉开发环境搭建
  • 【蓝桥杯真题精讲】第 15 届 Python A 组(省赛)
  • 首映|《星际宝贝史迪奇》真人电影,不变的“欧哈纳”
  • 俄乌代表团抵达谈判会场
  • 贝壳一季度收入增长42%:二手房市场活跃度维持在高位
  • 上海市税务局:收到对刘某某存在涉税问题的举报,正依法依规办理
  • 上海“城市文明开放麦”全城总动员,樊振东担任首位上海城市文明大使
  • 为何选择上海?两家外企提到营商环境、人才资源……