React:智能前端---语音合成大模型

228 阅读6分钟

想象一下,当你在输入框中输入一段文本,你的网页就能用男女老少,喜怒哀乐等各种稀奇古怪的声音朗读。

今天,我将基于React和火山引擎的TTS(文本转语音)接口,带着大家实现一个简单的智能语音前端功能。


一、语音合成大模型简介与使用

1. 语音合成大模型简介

火山引擎语音合成(TTS)服务基于先进的语音生成技术,提供高质量、高灵活性的文本到语音转换能力,开发者可通过简单调用 API,将文字实时转化为自然流畅的语音。

核心功能

  1. 多语言覆盖 支持中文、英文等主流语言,适配全球化应用需求。

  2. 情感化语音 通过 emotion 参数(如 "happy"、"sad")动态调节语音情感,赋予语音更丰富的表达力。

  3. 个性化语音风格 提供多种预设角色(如孙悟空男声 zh_male_sunwukong_mars_bigtts、女性语音等),可灵活选择语音类型与音色。

  4. 参数精细化控制 可调节语速(speed_ratio)、音量(volume_ratio)、音高(pitch_ratio)等细节,定制专属语音效果。

2. 准备工作

在正式开始之前,你需要先做好以下准备:

1.登录火山引擎官网www.volcengine.com/ 进行登录。

2.搜索豆包语音

屏幕截图 2025-07-05 192812.png

3.进入豆包语音

屏幕截图 2025-07-05 192824.png

4.创建应用,在左侧找到语音合成大模型。

屏幕截图 2025-07-05 193929.png
屏幕截图 2025-07-05 193539.png

5.环境变量配置中需要用到的内容

屏幕截图 2025-07-05 193620.png
屏幕截图 2025-07-05 204054.png

二、环境变量配置

环境变量是存储在操作系统或项目配置文件中的键值对,它用于动态配置应用程序的行为。

在开发中,它们常用于存储敏感信息(如 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. 效果展示

屏幕截图 2025-07-05 211003.png

用户可以修改文本框中的文本内容,然后点击上方按钮,就能听到语音了。

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_TOKENVITE_APP_IDVITE_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);
}
  • 关键步骤解析

    1. Base64 解码atob 将 API 返回的 Base64 字符串解码为原始字节。
    2. 字节数组转换:将字节字符转换为 Uint8Array
    3. Blob 对象创建:生成 audio/mp3 类型的 Blob。
    4. URL 生成URL.createObjectURL 返回临时 URL,供 <audio> 播放。
  • 注意事项

    • 若音频格式为 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:应用信息,包含 appidtoken 和 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")。

四、 语音合成的应用场景

  • 虚拟助手:为智能家居、客服机器人提供语音交互能力。
  • 教育领域:将教材文字转换为语音,辅助听障学生学习。
  • 游戏开发:为游戏角色生成动态语音,增强沉浸感。
  • 无障碍服务:为视障用户提供语音导航或内容朗读。