智能前端语音交互:开启 AI 时代新体验 🚀

298 阅读4分钟

引言

在当今的人工智能时代,语音交互成为了一种极具潜力的交互方式。本项目致力于构建一个前端语音交互系统,实现文字到语音的转换。以下将结合代码片段,详细阐述项目中的各个关键部分。

项目准备

敏感信息保护与环境变量配置

在实际开发中,我们通过 .gitignore 文件来控制哪些文件不提交到远程仓库,像 node_modules/ 目录,由于其体积大且可通过 npm i 重新安装,所以无需提交。

而本地环境变量存储在 .env.local 文件中,该文件也被添加到 .gitignore 里,防止敏感信息泄露。在代码中,我们使用 import.meta.env 来获取这些环境变量:

const { VITE_TOKEN, VITE_APP_ID, VITE_CLUSTER_ID } = import.meta.env;

这里的 VITE_TOKENVITE_APP_ID 和 VITE_CLUSTER_ID 是调用火山接口所需的关键信息,通过环境变量的方式存储,提高了代码的安全性。

核心功能实现

状态管理

在代码中,我们使用 React 的 useState 钩子来管理组件的状态。

const [prompt, setPrompt] = useState('大家好,我是蔡徐坤');
const [status, setStatus] = useState('ready');
  • prompt 状态用于存储用户输入的文字,初始值设置为 “大家好,我是蔡徐坤”。当用户在输入框中输入内容时,通过 onChange 事件更新 prompt 的值:

<textarea 
  className="input" 
  value={prompt} 
  onChange={(e) => setPrompt(e.target.value)}
/>
  • status 状态用于表示当前的处理状态,初始值为 'ready',表示系统就绪。处理过程中,状态会变为 'loading'(加载中),处理完成后变为 'done'。界面会根据这个状态进行相应的显示,体现了单向数据流的思想,即数据状态驱动界面更新。

生成语音功能

当用户点击 “Generate & Play” 按钮时,会触发 generateAudio 函数。以下是该函数的详细解释:

const generateAudio = () => {
  const voiceName = "zh_female_tianmeixiaoyuan_moon_bigtts"; // 语音角色
  const endpoint = "/tts/api/v1/tts"; // API地址

  const headers = {
    'Content-Type': 'application/json',
    Authorization: `Bearer;${VITE_TOKEN}`
  };

  // POST请求体 
  const payload = {
    app: {
      appid: VITE_APP_ID,
      token: VITE_TOKEN,
      cluster: VITE_CLUSTER_ID
    },
    user: {
      uid: 'bearbobo'
    },
    audio: {
      voice_type: voiceName,
      encoding: 'ogg_opus', // 编码
      compression_rate: 1, // 压缩比例
      rate: 24000,
      speed_ratio: 1.0,
      volume_ratio: 1.0,
      pitch_ratio: 1.0,
      emotion: 'happy' // 情绪
    },
    request: {
      reqid: Math.random().toString(36).substring(7),
      text: prompt,
      text_type: 'plain',
      operation: 'query', 
      silence_duration: '125', 
      with_frontend: '1', 
      frontend_type: 'unitTson', 
      pure_english_opt: '1',
    }
  };

  setStatus('loading'); // 将状态设置为加载中

  fetch(
    endpoint,
    {
      method: 'POST',
      headers: headers,
      body: JSON.stringify(payload)
    }
  )
  .then(res => res.json())
  .then(data => {
    const url = createBlobURL(data.data); // 返回一个可以播放声音的URL 
    audioRef.current.src = url;
    audioRef.current.play();
    setStatus('done'); // 将状态设置为完成
  });
};
  • 语音角色和 API 地址voiceName 指定了语音的角色,endpoint 是调用的 API 地址。
  • 请求头:设置了请求的内容类型为 application/json,并在 Authorization 字段中使用之前获取的 VITE_TOKEN 进行身份验证。
  • 请求体:包含了应用信息、用户信息、音频配置和请求信息等。其中,text 字段的值为 prompt,即用户输入的文字。
  • 状态更新:在发送请求前,将 status 状态设置为 'loading',表示系统正在处理请求。当请求成功返回并完成语音播放后,将状态设置为 'done'

Base64 编码处理

接口返回的音频数据是 Base64 编码的,我们需要将其转换为可播放的 URL。通过 createBlobURL 函数实现了这一转换:

function createBlobURL(base64AudioData) {
  var byteArrays = []; 
  var byteCharacters = atob(base64AudioData); 
  for (var offset = 0; offset < byteCharacters.length; offset++) { 
    var byteArray = byteCharacters.charCodeAt(offset); 
    byteArrays.push(byteArray); 
  } 
  var blob = new Blob([new Uint8Array(byteArrays)], { type: 'audio/mp3' }); 
  return URL.createObjectURL(blob);
}
  • atob 函数用于解码 Base64 字符串,将其转换为二进制数据。
  • 遍历解码后的字符,将每个字符的 Unicode 编码存储在 byteArrays 中。
  • 使用 Blob 对象将二进制数据封装为音频文件,指定文件类型为 'audio/mp3'
  • 最后,使用 URL.createObjectURL 方法创建一个可以播放声音的 URL。

界面展示

在最后的返回部分,构建了一个简单的界面:

return (
  <div className="container">
    <div>
      <label>Prompt</label>
      <button onClick={generateAudio}>Generate & Play</button>
      <textarea 
        className="input" 
        value={prompt} 
        onChange={(e) => setPrompt(e.target.value)}
      />
    </div>
    <div className="out">
      <div>{status}</div>
      <audio ref={audioRef}/> 
    </div>
  </div>
);
  • 输入框:用户可以在输入框中输入要转换为语音的文字,输入框的值与 prompt 状态绑定,通过 onChange 事件更新 prompt
  • 按钮:点击 “Generate & Play” 按钮会触发 generateAudio 函数,开始调用火山接口生成语音并播放。
  • 状态显示区域:显示当前的处理状态,如 readyloading 或 done
  • 隐藏的 audio 元素:通过 audioRef 引用该元素,将生成的音频 URL 赋值给其 src 属性,然后调用 play 方法播放语音。

总结

通过这个项目,我们成功实现了一个基于前端的语音交互系统,将文字转换为语音。在开发过程中,我们深入了解了敏感信息保护、环境变量配置、状态管理、API 调用和 Base64 编码处理等关键知识点。这些知识不仅适用于本项目,还可以应用到其他前端开发项目中。希望这篇文章能对你有所帮助,让你在 AI 时代的前端开发中迈出坚实的一步👏!