想象一下,当你在输入框中输入一段文本,你的网页就能用男女老少,喜怒哀乐等各种稀奇古怪的声音朗读。
今天,我将基于React和火山引擎的TTS(文本转语音)接口,带着大家实现一个简单的智能语音前端功能。
一、语音合成大模型简介与使用
1. 语音合成大模型简介
火山引擎语音合成(TTS)服务基于先进的语音生成技术,提供高质量、高灵活性的文本到语音转换能力,开发者可通过简单调用 API,将文字实时转化为自然流畅的语音。
核心功能
-
多语言覆盖 支持中文、英文等主流语言,适配全球化应用需求。
-
情感化语音 通过
emotion参数(如 "happy"、"sad")动态调节语音情感,赋予语音更丰富的表达力。 -
个性化语音风格 提供多种预设角色(如孙悟空男声
zh_male_sunwukong_mars_bigtts、女性语音等),可灵活选择语音类型与音色。 -
参数精细化控制 可调节语速(
speed_ratio)、音量(volume_ratio)、音高(pitch_ratio)等细节,定制专属语音效果。
2. 准备工作
在正式开始之前,你需要先做好以下准备:
1.登录火山引擎官网www.volcengine.com/ 进行登录。
2.搜索豆包语音
3.进入豆包语音
4.创建应用,在左侧找到语音合成大模型。
5.环境变量配置中需要用到的内容
二、环境变量配置
环境变量是存储在操作系统或项目配置文件中的键值对,它用于动态配置应用程序的行为。
在开发中,它们常用于存储敏感信息(如 API Token、数据库密码)或区分不同环境(开发/生产)的配置。
1. 配置火山引擎 TTS 的环境变量
首先,在项目目录下创建.env.local文件,然后在 .env.local 文件中定义以下变量:
//环境变量,获取方式看上文 “准备工作” 中的最后两张图片
VITE_TOKEN= XXX
VITE_APP_ID= XXX
VITE_CLUSTER_ID= XXX
代码分析:
-
命名规则:
以VITE_开头的变量会被 Vite 自动注入到import.meta.env中,供前端代码访问。 -
变量作用:
VITE_TOKEN:火山引擎 API 的认证 Token。VITE_APP_ID:应用 ID,标识调用方身份。VITE_CLUSTER_ID:指定服务集群(如volcano_tts)。
三:语音合成大模型的调用实现与分析
为了更清晰地了解,我会先展示实现后的页面效果和完整代码:
1. 效果展示
用户可以修改文本框中的文本内容,然后点击上方按钮,就能听到语音了。
2. 完整代码
import { useState, useRef } from 'react'; // 引入 React 的 Hook 函数
import './App.css'; // 引入样式文件
function App() {
// 从环境变量中读取认证信息
const { VITE_TOKEN, VITE_APP_ID, VITE_CLUSTER_ID } = import.meta.env;
// 状态管理:用户输入文本和界面状态
const [prompt, setPrompt] = useState('hi~,想我了吗?');
const [status, setStatus] = useState("准备");
// DOM 引用:操作 <audio> 元素
const audioRef = useRef(null);
// 将 Base64 音频数据转换为 Blob URL
function createBlobURL(base64AudioData) {
const byteCharacters = atob(base64AudioData);
const byteArrays = [];
for (let offset = 0; offset < byteCharacters.length; offset++) {
byteArrays.push(byteCharacters.charCodeAt(offset));
}
const blob = new Blob([new Uint8Array(byteArrays)], { type: "audio/mp3" });
return URL.createObjectURL(blob);
}
// 调用火山引擎 TTS 接口生成语音
const generateAudio = () => {
const voiceName = "zh_male_sunwukong_mars_bigtts"; // 语音风格
const endpoint = "/tts/api/v1/tts"; // API 地址
// 请求头:认证信息
const headers = {
'Content-Type': 'application/json',
Authorization: `Bearer;${VITE_TOKEN}`,
};
// 请求体:应用信息、用户信息、音频参数、请求参数
const payload = {
app: {
appid: VITE_APP_ID,
token: VITE_TOKEN,
cluster: VITE_CLUSTER_ID,
},
user: {
uid: 'bearbobo',
},
audio: {
voice_type: voiceName,
encoding: 'ogg_opus',
rate: 24000,
emotion: 'happy',
},
request: {
text: prompt,
text_type: 'plain',
operation: 'query',
},
};
setStatus('loading'); // 更新状态为 loading
// 发起 HTTP POST 请求
fetch(endpoint, {
method: 'POST',
headers: headers,
body: JSON.stringify(payload),
})
.then(res => res.json())
.then(data => {
const url = createBlobURL(data.data); // 转换 Base64 为 URL
audioRef.current.src = url;
audioRef.current.play(); // 播放音频
setStatus('done'); // 更新状态为 done
});
};
return (
<div className='container'>
<div>
<label>Prompt</label><br />
<button onClick={generateAudio}>点我啊~</button><br />
<textarea
className="input"
value={prompt}
onChange={(e) => setPrompt(e.target.value)}
/>
</div>
<div className='out'>
<div>{status}</div>
<audio ref={audioRef} />
</div>
</div>
);
}
export default App;
3. 代码解析
3.1 组件定义与环境变量读取
function App() {
const { VITE_TOKEN, VITE_APP_ID, VITE_CLUSTER_ID } = import.meta.env;
-
作用:
-
从
.env.local文件中读取火山引擎 TTS 服务的认证信息(VITE_TOKEN、VITE_APP_ID、VITE_CLUSTER_ID)。 -
使用
VITE_前缀确保环境变量仅暴露给前端代码(Vite 自动过滤非VITE_变量)。
-
3.2 状态管理
const [prompt, setPrompt] = useState('妖精!吃俺老孙一棒!');
const [status, setStatus] = useState("准备");
-
prompt状态:- 存储用户输入的文本内容,初始值为默认问候语。
- 通过
<textarea>的onChange事件更新:onChange={(e) => setPrompt(e.target.value)}。
-
status状态:- 控制界面状态("准备"、"loading"、"done"),用于反馈生成进度。
3.3 DOM 引用
const audioRef = useRef(null);
-
作用:
- 创建对
<audio>元素的引用,以便在代码中直接操作音频播放。
- 创建对
-
关键操作:
audioRef.current.src = url;:将生成的音频 URL 赋值给<audio>元素。audioRef.current.play();:触发音频自动播放。
3.4 Base64 到音频的转换
function createBlobURL(base64AudioData) {
const byteCharacters = atob(base64AudioData);
const byteArrays = [];
for (let offset = 0; offset < byteCharacters.length; offset++) {
byteArrays.push(byteCharacters.charCodeAt(offset));
}
const blob = new Blob([new Uint8Array(byteArrays)], { type: "audio/mp3" });
return URL.createObjectURL(blob);
}
-
关键步骤解析:
- Base64 解码:
atob将 API 返回的 Base64 字符串解码为原始字节。 - 字节数组转换:将字节字符转换为
Uint8Array。 - Blob 对象创建:生成
audio/mp3类型的 Blob。 - URL 生成:
URL.createObjectURL返回临时 URL,供<audio>播放。
- Base64 解码:
-
注意事项:
- 若音频格式为
ogg_opus,需调整type为audio/ogg,但此处统一处理为audio/mp3。
- 若音频格式为
3.5 参数配置
const generateAudio = () => {
const voiceName = "zh_male_sunwukong_mars_bigtts"; // 语音风格
const endpoint = "/tts/api/v1/tts"; // API 地址
const headers = {
'Content-Type': 'application/json',
Authorization: `Bearer;${VITE_TOKEN}`,
};
const payload = {
app: {
appid: VITE_APP_ID,
token: VITE_TOKEN,
cluster: VITE_CLUSTER_ID,
},
user: {
uid: 'bearbobo'
},
audio: {
voice_type: voiceName,
encoding: 'ogg_opus',
rate: 24000,
emotion: 'happy',
},
request: {
text: prompt,
text_type: 'plain',
operation: 'query',
},
};
-
参数详解:
-
app:应用信息,包含appid、token和cluster。 -
user:用户标识(uid),用于区分不同用户请求。 -
audio:音频参数:voice_type:指定语音风格(如孙悟空男声)。encoding:音频编码格式(ogg_opus为高效压缩格式)。rate:采样率(24kHz/48kHz)。emotion:语音情感调节(如 "happy")。
-
request:请求参数:text:用户输入的文本。text_type:文本类型(plain表示纯文本)。operation:操作类型(query表示生成语音)。
-
3.6 调用火山引擎 TTS 接口
setStatus('loading'); // 更新状态为 loading
fetch(endpoint, {
method: 'POST',
headers: headers,
body: JSON.stringify(payload),
})
.then(res => res.json())
.then(data => {
const url = createBlobURL(data.data); // 转换 Base64 为 URL
audioRef.current.src = url;
audioRef.current.play(); // 播放音频
setStatus('done'); // 更新状态为 done
});
-
状态更新(
setStatus('loading'))- 将组件状态
status设置为"loading",驱动 UI 显示加载提示(如“加载中”)。 - React 的
useState是异步的,状态更新会触发组件重新渲染,从而更新界面。
- 将组件状态
-
HTTP 请求(
fetch)向火山引擎 TTS 接口发送文本和参数,请求生成语音。
method: 'POST':指定请求方法为 POST。headers:包含认证信息(Authorization)和内容类型(application/json)。body: JSON.stringify(payload):将请求体payload序列化为 JSON 字符串。
-
处理响应(
.then(res => res.json()))- 解析 API 返回的 JSON 数据。
-
Base64 转 Blob URL(
createBlobURL)将 API 返回的音频数据转换为浏览器可播放的格式。
- 使用
atob将 Base64 解码为字节字符串。 - 将字节字符串转换为
Uint8Array。 - 创建
Blob对象并生成临时 URL(URL.createObjectURL)。
- 使用
-
播放音频
通过
useRef获取的 DOM 引用直接操作<audio>元素。audioRef.current.src = url:将生成的 URL 设置为<audio>标签的src。audioRef.current.play():调用.play()方法播放音频。
-
完成状态更新(
setStatus('done'))- 将状态设为
"done",通知用户操作已完成。
- 将状态设为
3.7 UI 交互与状态绑定
return (
<div className='container'>
<div>
<label>Prompt</label><br />
<button onClick={generateAudio}>点我啊~</button><br />
<textarea
className="input"
value={prompt}
onChange={(e) => setPrompt(e.target.value)}
/>
</div>
<div className='out'>
<div>{status}</div>
<audio ref={audioRef} />
</div>
</div>
);
-
UI 元素解析:
<textarea>:双向绑定prompt状态,实时更新用户输入。<button>:点击触发generateAudio函数。<audio>:通过audioRef动态赋值src并自动播放。- 状态显示:
{status}实时展示当前状态("准备"、"loading"、"done")。
四、 语音合成的应用场景
- 虚拟助手:为智能家居、客服机器人提供语音交互能力。
- 教育领域:将教材文字转换为语音,辅助听障学生学习。
- 游戏开发:为游戏角色生成动态语音,增强沉浸感。
- 无障碍服务:为视障用户提供语音导航或内容朗读。