文章目录

  • 前言
  • 1. 简介
    • 1.1 能力体验
    • 1.2 功能特性
    • 1.3 音色列表
    • 1.4 收费情况
  • 2. 开启服务
    • 2.1 创建应用
    • 2.3 使用服务介绍
  • 3.Websocket接入演示
    • 3.1 编写demo
    • 3.2 代码解释
    • 3.4运行demo
  • 4. 参考链接


前言

语音合成TTS(text to Speech)是我觉得后续开发产品所不可或缺的一个功能,因为相比较于过去的GUI 图形+文字展示,动态形象+语音会更利用人与设备之间的交互。

另外语音沟通更加灵活,因为过去的GUI图形界面都是预先设计好的,就像APP内的界面。这种更加标准但是不够灵活,不能满足所有人的需求和爱好。所以我觉得现在了解下TTS也是非常有必要的。

之前看到小智AI中有提到支持了cosyVoice(阿里的TTS模型)与火山引擎的TTS。然后搜索了两者的区别发现火山引擎的TTS在高拟真克隆这块做的比较好。这样的话就能够使用该TTS生成各种符合产品形象的声音。所以暂时先选择了火山引擎的TTS去研究一下。

1. 简介

火山引擎的TTS也叫做豆包语音合成大模型,它是依托新一代大模型能力,豆包语音合成模型能够根据上下文智能预测文本的情绪、语调等信息,并生成超自然、高保真、个性化的语音,以满足不同用户的个性化需求。

简介中很多的内容都是来自于火山引擎的文档中心,我这里就简单的介绍下,大家需要详细了解的可以到以下地址去看看

https://www.volcengine.com/docs/6561/1257544

1.1 能力体验

在官方的网站中有个能力体验的页面,这个体验很简单 输入想要描述的文字,然后选择配音和一些声音相关的配置就能生成语音了。

我们最终要实现的功能也是类似的,只是这个是人家已经实现好的功能比较固化,我们想要更灵活一些,所以需要通过代码去使用这个模型来做一些定制化的开发。
在这里插入图片描述

1.2 功能特性

下面这个表里面说了很多,说实话有一些对于我们这些刚接触的人来说并没有什么概念,例如这里我也只是对部署方案比较感兴趣。这里后续有需要的时候大家可以去官方介绍文档中去查看。

在这里插入图片描述

1.3 音色列表

使用克隆语音需要用到另外一个模型,不是我们本次所使用的“语音合成大模型”,所以我们并不是说想用什么声音就用什么声音,而是要使用官方给出的声音列表。不过好在可选择性还是很多的。
这里截图不全,更详细的内容可以查看官方文档
在这里插入图片描述

1.4 收费情况

我最初以为TTS里面包含了语音复制,什么短文本语音合成啥的呢,结果一看乖乖嘞被分成了4个而且是收费的。

但是我们第一次用的话是免费的,会赠送一定的使用额度,所以大家不要太过于担心。
在这里插入图片描述

2. 开启服务

我们需要申请appid、token、secret_key等用来开启和使用TTS的服务。

这个就类似于从豆包那里申请个账号,这个账号里面包含了我们的身份信息,以及能够使用哪些模型还有我们的剩余额度,有了这些信息后我们才能真正的去使用大模型语音合成功能。

2.1 创建应用

先根据下方的快速入门创建账号:

https://www.volcengine.com/docs/6561/163043

点击“创建应用”来新增应用,填入应用名称、简介和所需接入的能力服务
在这里插入图片描述
我们第一次使用会有个免费额度,所以大家不用太担心。
在这里插入图片描述
创建成功后,能够在应用管理界面看到我们所创建的应用
在这里插入图片描述

获取token和Secret_key信息
在控制台界面,我们能够找到属于我们的Access Token 和 Secret Key,有了这些信息我们才能去使用该服务。
在这里插入图片描述

2.3 使用服务介绍

使用的话有两种方式,分别是API和SDK接入。
核心区别对比
SDK的话目前它只能运行在安卓和IOS操作系统上,应该是集成到APP中,这不方便我们去进行体验。而且像小智AI这种也是采用的API方式接入的,所以这里我们也使用API的方式

API接入又分为WebSocket还有Http,基本上工作原理大差不差,都是发送请求,然后接收响应处理。这里我们就以WebSocket为主。

3.Websocket接入演示

Websocket接入演示的功能,需要使用账号申请部分申请到的 appid和access_token进行调用文本一次性送入,后端边合成边返回音频数据。所以大家一定要先按照上面的步骤获取对应的token和appid等信息。

接口说明地址为下方的链接,详细的使用方法大概可以进入该链接查看:

wss://openspeech.bytedance.com/api/v1/tts/ws_binary

3.1 编写demo

文档中心有个demo,我们拿下来直接运行即可,本次演示的代码来源是tts_websocket_demo.py
在这里插入图片描述
源码如下:

#coding=utf-8'''
requires Python 3.6 or laterpip install asyncio
pip install websockets'''import asyncio
import websockets
import uuid
import json
import gzip
import copyMESSAGE_TYPES = {11: "audio-only server response", 12: "frontend server response", 15: "error message from server"}
MESSAGE_TYPE_SPECIFIC_FLAGS = {0: "no sequence number", 1: "sequence number > 0",2: "last message from server (seq < 0)", 3: "sequence number < 0"}
MESSAGE_SERIALIZATION_METHODS = {0: "no serialization", 1: "JSON", 15: "custom type"}
MESSAGE_COMPRESSIONS = {0: "no compression", 1: "gzip", 15: "custom compression method"}appid = "xxx"
token = "xxx"
cluster = "xxx"
voice_type = "xxx"
host = "openspeech.bytedance.com"
api_url = f"wss://{host}/api/v1/tts/ws_binary"# version: b0001 (4 bits)
# header size: b0001 (4 bits)
# message type: b0001 (Full client request) (4bits)
# message type specific flags: b0000 (none) (4bits)
# message serialization method: b0001 (JSON) (4 bits)
# message compression: b0001 (gzip) (4bits)
# reserved data: 0x00 (1 byte)
default_header = bytearray(b'\x11\x10\x11\x00')request_json = {"app": {"appid": appid,"token": "access_token","cluster": cluster},"user": {"uid": "388808087185088"},"audio": {"voice_type": "xxx","encoding": "mp3","speed_ratio": 1.0,"volume_ratio": 1.0,"pitch_ratio": 1.0,},"request": {"reqid": "xxx","text": "字节跳动语音合成。","text_type": "plain","operation": "xxx"}
}async def test_submit():submit_request_json = copy.deepcopy(request_json)submit_request_json["audio"]["voice_type"] = voice_typesubmit_request_json["request"]["reqid"] = str(uuid.uuid4())submit_request_json["request"]["operation"] = "submit"payload_bytes = str.encode(json.dumps(submit_request_json))payload_bytes = gzip.compress(payload_bytes)  # if no compression, comment this linefull_client_request = bytearray(default_header)full_client_request.extend((len(payload_bytes)).to_bytes(4, 'big'))  # payload size(4 bytes)full_client_request.extend(payload_bytes)  # payloadprint("\n------------------------ test 'submit' -------------------------")print("request json: ", submit_request_json)print("\nrequest bytes: ", full_client_request)file_to_save = open("test_submit.mp3", "wb")header = {"Authorization": f"Bearer; {token}"}async with websockets.connect(api_url, extra_headers=header, ping_interval=None) as ws:await ws.send(full_client_request)while True:res = await ws.recv()done = parse_response(res, file_to_save)if done:file_to_save.close()breakprint("\nclosing the connection...")async def test_query():query_request_json = copy.deepcopy(request_json)query_request_json["audio"]["voice_type"] = voice_typequery_request_json["request"]["reqid"] = str(uuid.uuid4())query_request_json["request"]["operation"] = "query"payload_bytes = str.encode(json.dumps(query_request_json))payload_bytes = gzip.compress(payload_bytes)  # if no compression, comment this linefull_client_request = bytearray(default_header)full_client_request.extend((len(payload_bytes)).to_bytes(4, 'big'))  # payload size(4 bytes)full_client_request.extend(payload_bytes)  # payloadprint("\n------------------------ test 'query' -------------------------")print("request json: ", query_request_json)print("\nrequest bytes: ", full_client_request)file_to_save = open("test_query.mp3", "wb")header = {"Authorization": f"Bearer; {token}"}async with websockets.connect(api_url, extra_headers=header, ping_interval=None) as ws:await ws.send(full_client_request)res = await ws.recv()parse_response(res, file_to_save)file_to_save.close()print("\nclosing the connection...")def parse_response(res, file):print("--------------------------- response ---------------------------")# print(f"response raw bytes: {res}")protocol_version = res[0] >> 4header_size = res[0] & 0x0fmessage_type = res[1] >> 4message_type_specific_flags = res[1] & 0x0fserialization_method = res[2] >> 4message_compression = res[2] & 0x0freserved = res[3]header_extensions = res[4:header_size*4]payload = res[header_size*4:]print(f"            Protocol version: {protocol_version:#x} - version {protocol_version}")print(f"                 Header size: {header_size:#x} - {header_size * 4} bytes ")print(f"                Message type: {message_type:#x} - {MESSAGE_TYPES[message_type]}")print(f" Message type specific flags: {message_type_specific_flags:#x} - {MESSAGE_TYPE_SPECIFIC_FLAGS[message_type_specific_flags]}")print(f"Message serialization method: {serialization_method:#x} - {MESSAGE_SERIALIZATION_METHODS[serialization_method]}")print(f"         Message compression: {message_compression:#x} - {MESSAGE_COMPRESSIONS[message_compression]}")print(f"                    Reserved: {reserved:#04x}")if header_size != 1:print(f"           Header extensions: {header_extensions}")if message_type == 0xb:  # audio-only server responseif message_type_specific_flags == 0:  # no sequence number as ACKprint("                Payload size: 0")return Falseelse:sequence_number = int.from_bytes(payload[:4], "big", signed=True)payload_size = int.from_bytes(payload[4:8], "big", signed=False)payload = payload[8:]print(f"             Sequence number: {sequence_number}")print(f"                Payload size: {payload_size} bytes")file.write(payload)if sequence_number < 0:return Trueelse:return Falseelif message_type == 0xf:code = int.from_bytes(payload[:4], "big", signed=False)msg_size = int.from_bytes(payload[4:8], "big", signed=False)error_msg = payload[8:]if message_compression == 1:error_msg = gzip.decompress(error_msg)error_msg = str(error_msg, "utf-8")print(f"          Error message code: {code}")print(f"          Error message size: {msg_size} bytes")print(f"               Error message: {error_msg}")return Trueelif message_type == 0xc:msg_size = int.from_bytes(payload[:4], "big", signed=False)payload = payload[4:]if message_compression == 1:payload = gzip.decompress(payload)print(f"            Frontend message: {payload}")else:print("undefined message type!")return Trueif __name__ == '__main__':loop = asyncio.get_event_loop()loop.run_until_complete(test_submit())loop.run_until_complete(test_query())

填写token等信息
在运行demo之前,我们需要在下方的这里填写上之前在火山引擎处申请到的信息,这里大家就理解为自己的账号密码就行了
在这里插入图片描述
appid还有token还有cluster在我们的应用空间那里就能看到,然后有个比较特殊的voice_type需要找到文档中的音色列表去那里找自己想要合成的声音类型。
在这里插入图片描述

3.2 代码解释

导入各基本模块

import asyncio
import websockets
import uuid
import json
import gzip
import copy

这里的asyncio 是python中的用于实现并发的模块,里面提供了例如协程、协程锁等各种用于异步通信的功能。其它的就不用说了看名字就知道干啥的了。

基础配置填写

appid = "xxx"
token = "xxx"
cluster = "xxx"
voice_type = "xxx"
host = "openspeech.bytedance.com"
api_url = f"wss://{host}/api/v1/tts/ws_binary"

这个就是前面我们提到的把应用空间了申请到的信息填写上去
在这里插入图片描述

请求体

# version: b0001 (4 bits)
# header size: b0001 (4 bits)
# message type: b0001 (Full client request) (4bits)
# message type specific flags: b0000 (none) (4bits)
# message serialization method: b0001 (JSON) (4 bits)
# message compression: b0001 (gzip) (4bits)
# reserved data: 0x00 (1 byte)
default_header = bytearray(b'\x11\x10\x11\x00')request_json = {"app": {"appid": appid,"token": "access_token","cluster": cluster},"user": {"uid": "388808087185088"},"audio": {"voice_type": "xxx","encoding": "mp3","speed_ratio": 1.0,"volume_ratio": 1.0,"pitch_ratio": 1.0,},"request": {"reqid": "xxx","text": "字节跳动语音合成。","text_type": "plain","operation": "xxx"}
}

header是发送请求时的消息头,tts的通讯协议要求二进制的方式进行传输,所以头这里也是采用的二进制。上面的注释代表的是其二进制代表的内容。

request_json是我们的请求体,里面需要填充我们要发送的具体信息,后续发送时也会将其转换为二进制发送,请求体中的参数主要就是这几个
在这里插入图片描述
可以通过下方的链接去查询

https://www.volcengine.com/docs/6561/1257584

提交转换请求

请求
async def test_submit():

功能

  • 向字节跳动的语音合成WebSocket API提交一个文本合成请求

主要操作:

  • 准备提交请求的JSON数据,包括appid、token、cluster等认证信息
  • 设置操作类型为"submit"(提交)
  • 生成唯一的请求ID
  • 使用gzip压缩请求数据
  • 建立WebSocket连接并发送请求
  • 持续接收服务器返回的音频数据流,保存到test_submit.mp3文件
  • 处理完所有音频数据后关闭连接
查询
async def test_query():

功能

  • 向字节跳动的语音合成WebSocket API发送查询请求

主要操作:

  • 准备查询请求的JSON数据,结构与submit类似
  • 设置操作类型为"query"(查询)
  • 生成唯一的请求ID
  • 使用gzip压缩请求数据
  • 建立WebSocket连接并发送请求
  • 接收服务器响应(通常是一次性返回)
  • 将响应数据保存到test_query.mp3文件
  • 关闭连接

查询与请求的主要区别:

  • test_submit()用于提交语音合成任务并持续接收音频流
  • test_query()用于查询状态或结果,通常只接收一次响应
  • test_submit()会处理多个响应消息直到完成
  • test_query()通常只处理单个响应消息

处理接收到的响应

处理响应
def parse_response(res, file):
  1. 解析响应头部信息
  • 协议版本(protocol_version)

  • 头部大小(header_size)

  • 消息类型(message_type)

  • 消息特定标志(message_type_specific_flags)

  • 序列化方法(serialization_method)

  • 压缩方法(message_compression)

  • 保留字段(reserved)

  1. 处理不同类型的服务器响应:

错误消息响应(message_type=0xf):

  • 解析错误代码(code)
  • 解析错误消息大小(msg_size)
  • 解压缩并显示错误内容

前端消息响应(message_type=0xc):

  • 解析消息大小(msg_size)
  • 解压缩并显示前端消息

3.4运行demo

注意下载下来的demo好像名称中有个空格,大家注意修改下名称。

执行指令

python tts_websocket_demo.py

执行结果
通过打印日志能够,模型服务返回了对应的响应数据(音频的原始数据)
在这里插入图片描述
然后我们就能看到我们的文件夹多了两个mp3的文件,分别是通过请求得到的和通过查询得到的。
听了下是熊二说的“字节跳动语音合成”,这里我设置的语音类型也是熊二的。
在这里插入图片描述
此时再去查看我们的模型使用情况,会发现少了一定的额度。
在这里插入图片描述

4. 参考链接

豆包语音合成大模型官网

语音技术开发参考 - 豆包官方的

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/85480.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/85480.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/85480.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Django中使用流式响应,自己也能实现ChatGPT的效果

最近在研究ChatGPT的时候&#xff0c;想通过openai提供的接口使国内用户也可以无限制访问&#xff0c;于是打算基于django开发一款应用。页面的渲染也得想ChatGPT一样采用流式响应&#xff0c;django中StreamingHttpResponse是支持流式响应的一种方式。 django 代码 class Ch…

Python Redis 简介

Redis 是一个高性能的内存键值数据库&#xff0c;支持多种数据结构&#xff08;字符串、列表、哈希、集合等&#xff09;&#xff0c;常用于缓存、消息队列和实时数据处理。Python 通过 redis-py 库与 Redis 交互。 核心功能 内存存储&#xff1a;数据存储在内存中&#xff0c…

mac安装whistle代理抓包工具(支持mock)

工具地址&#xff1a;https://wproxy.org/whistle/ 1、 安装nodejs环境 参考方法&#xff1a;https://github.com/nvm-sh/nvm 1&#xff09;安装 curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash如图&#xff0c;安装成功 2&#xff09;…

基于 mydumper 实现 MySQL 定期全量备份、恢复方案

一、Mydumper 工具介绍 mydumper 是一款社区开源的逻辑备份工具,由 C 语言编写,与 MySQL 官方提供的 mysqldump 相比,它具有更高的性能和更多的功能,例如: • 支持多线程导出数据,速度更快; • 支持一致性备份; • 支持将导出文件压缩,节约空间; • 支持多线程恢复;…

C++中,std::async 一个用于异步编程的工具

在C中&#xff0c;std::async 是一个用于异步编程的工具&#xff0c;它允许你在一个单独的线程中执行任务&#xff0c;并返回一个 std::future 对象&#xff0c;通过这个对象可以获取任务的结果或者检查任务的状态。 基本用法1 lambda 表达式 #include <iostream> #incl…

【Linux驱动开发 ---- 4_驱动开发框架和 API】

Linux驱动开发 ---- 4_驱动开发框架和 API 目录 Linux驱动开发 ---- 4_驱动开发框架和 API&#x1f3af; 目标&#xff1a;&#x1f4cc; 1. Linux 设备模型&#xff08;Linux Device Model&#xff09;**设备模型的核心概念**&#xff1a; &#x1f4cc; 2. 设备树&#xff08…

实景VR展厅建设流程

实景VR展厅&#xff1a;建设流程、用途、案例与未来发展 随着虚拟现实&#xff08;VR&#xff09;技术的发展&#xff0c;实景VR展厅作为一种创新的展示方式&#xff0c;正在各个领域得到广泛应用。实景VR展厅提供沉浸式的体验&#xff0c;丰富展示内容和形式&#xff0c;为观…

android下拉栏添加媒体音量调节

参考下拉栏的屏幕亮度调节&#xff0c;添加媒体音量调节。 平台信息&#xff1a; MSM8909.LA.3.1.2_AOSP PLATFORM_VERSION9 BUILD_IDPKQ1.190903.001 效果图 布局文件与音量图标 Index: frameworks/base/packages/SystemUI/res/layout/quick_settings_media_volume_dialog.x…

VS Code 配置ROS2开发环境常见问题记录

1、ctrlshiftp后找不到C/C: Edit configurations 安装或重装C插件。 参考链接&#xff1a; 搜索C/C: Edit configurations显示no matching command问题https://www.cnblogs.com/hunghau/p/17195622.html 2、ROS2 API无法转到定义 &#xff08;1&#xff09;在c_cpp_proper…

Docker Desktop搭建RocketMQ的完整教程

Docker Desktop搭建RocketMQ图文教程 1. 准备工作 已安装Docker Desktop&#xff08;本地安装方法参考上一节教程&#xff09;。需部署三个组件&#xff1a;NameServer、Broker、Console&#xff08;管理界面&#xff09;。 2. 创建目录和文件 在任意盘&#xff08;如D盘&am…

算法35天|1049. 最后一块石头的重量 II、 494. 目标和、 474.一和零

1049. 最后一块石头的重量 II 题目 思路与解法 class Solution { public:int lastStoneWeightII(vector<int>& stones) {int sum 0;for(int i0;i<stones.size();i){sum stones[i];}// 问题转换为&#xff1a;// 先求得&#xff0c;背包容量为target时&#x…

AWS S3拒绝非https的请求访问

问题 aws s3桶&#xff0c;安全要求必须强制使用ssl加密访问&#xff0c;即https。需要添加一个策略拒绝所有不是https的访问s3桶请求。 解决 在对于桶添加相关拒绝策略即可。如下&#xff1a; {"Version": "2012-10-17","Statement": [{&qu…

GitLab CVE-2025-4278 安全漏洞解决方案

本分分享极狐GitLab 补丁版本 18.0.2, 17.11.4, 17.10.8 的详细内容。这几个版本包含重要的缺陷和安全修复代码&#xff0c;我们强烈建议所有私有化部署用户应该立即升级到上述的某一个版本。对于极狐GitLab SaaS&#xff0c;技术团队已经进行了升级&#xff0c;无需用户采取任…

Async、await是什么?跟promise有什么区别?使用的好处是什么

Async/Await 与 Promise 的深度解析 一、基本概念 1. Promise Promise 是 ES6 引入的异步编程解决方案&#xff0c;表示一个异步操作的最终完成&#xff08;或失败&#xff09;及其结果值。 function fetchData() {return new Promise((resolve, reject) > {setTimeout(…

MySQL数据库中所有表的空间占用与行数统计

查看某个数据库中所有表的空间与行数统计 SELECT TABLE_NAME AS 表名,TABLE_ROWS AS 行数,ROUND(DATA_LENGTH / 1024 / 1024, 2) AS 数据大小(MB),ROUND(INDEX_LENGTH / 1024 / 1024, 2) AS 索引大小(MB),ROUND((DATA_LENGTH INDEX_LENGTH) / 1024 / 1024, 2) AS 总占用空间(…

el-upload 点击上传按钮前先判断条件满足再弹选择文件框

解决思路&#xff1a; 先写一个上传按钮&#xff0c;点击上传按钮后判断条件是否满足&#xff0c;满足则显示上传组件并使用ref来控制点击事件&#xff0c;隐藏自身。 注&#xff1a;上传成功或者上传失败时或者上传前判断条件添加不满足return将this.isShow true 代码部分…

django ReturnDict 如何修改内容

在Django中&#xff0c;QuerySet 对象通常用于从数据库中检索数据&#xff0c;并且可以被转换为各种格式&#xff0c;例如字典。如果你想修改QuerySet返回的结果&#xff08;例如&#xff0c;将其转换为dict&#xff09;&#xff0c;你可以在查询执行后进行操作。这里有几种常见…

密室出逃消消乐小游戏微信流量主小程序开源

这个密室出逃消消乐小游戏采用了微信小程序的标准目录结构&#xff0c;包含以下核心功能&#xff1a; 游戏界面&#xff1a;6x6 的网格布局&#xff0c;随机生成不同类型的物品 游戏逻辑&#xff1a;交换相邻物品&#xff0c;消除三个或以上相同类型的物品 计分系统&#xff1a…

SmartMediaKit实战经验总结之高稳定、低延迟、强兼容

在万物互联与数字化加速融合的今天&#xff0c;音视频实时通信技术正成为各行业发展的核心驱动力。从教育到工业、从安防到远程医疗&#xff0c;毫秒级低延迟的音视频交互体验已成为新一代实时系统的“生命线”。而在这个领域&#xff0c;视沃科技旗下的大牛直播SDK&#xff08…

前端性能调优工具与指标

性能指标解析 核心Web指标 核心Web指标(Core Web Vitals)是Google定义的一组关键性能指标&#xff0c;直接影响用户体验和SEO排名&#xff1a; FCP (First Contentful Paint): 首次内容绘制&#xff0c;记录页面首次渲染任何文本、图像、非白色画布或SVG的时间点 优: < 1.…