火山引擎TTS使用體驗

文章目錄

  • 前言
  • 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.…