配音功能实现过程中的语音识别服务接入探索

AI 驱动的语言教练:语音识别——基于火山引擎 (ByteDance OpenSpeech) 的实践

在构建一个交互式语言学习平台时,将用户的口语输入转换为文本是实现智能反馈的第一步,也是至关重要的一步。这一过程由自动语音识别 (ASR) 技术支撑。本文将深入探讨我们如何利用火山引擎旗下的 ByteDance OpenSpeech API 来实现这一功能,并详细阐述后端在处理用户录音时的技术细节和所付出的工作量。

初始尝试:百度智能云语音识别

项目初期我选择了百度智能云作为语音识别服务提供方。百度的 RESTful API 支持 PCM 音频上传识别,我在本地将用户录制的 WebM 音频通过 ffmpeg 转码为 16KHz、16位、单声道的 PCM 格式,再通过 HTTP 请求发送至百度云。

尽管测试过程中识别效果尚可,但百度返回的是一段纯文本,不具备分句、分词等结构信息,这对后续评分造成了困难。此外,部分情况下识别延迟较高,影响用户体验。

中途切换:科大讯飞语音评测方案

为了获得更结构化的识别结果,我转向了科大讯飞的语音评测 API。讯飞接口支持朗读评分,返回了详细的音素级别的打分信息。但很快我发现,讯飞接口存在较强的“业务绑定”限制,如:

  • 必须基于他们定义的题库内容传参;
  • 要求客户端使用 JavaScript SDK 与 WebSocket 交互,服务端封装较复杂;
  • 一旦识别失败,错误日志难以追踪。

由于项目部署在 Next.js + API Routes 的后端上,并未启用 WebSocket 长连接,讯飞方案最终被放弃

最终方案:火山引擎语音识别大模型

最终我选择了火山引擎的语音识别大模型(Doubao)。该模型识别效果较好,且支持语句、词、拼音级别的结构化返回,非常契合我们的需求。

主要接入流程如下:

  1. 用户录音上传

    • 前端通过 MediaRecorder 获取 .webm 音频;
    • 将录音发送至后端 /api/upload 接口;
    • 后端保存至本地 uploads/recordings/ 目录,并记录数据库。
  2. 转码与上传

    • 使用 ffmpeg.webm 音频转为 wav
    • 为了使火山引擎能访问音频文件,项目采用了 Ngrok 映射本地路径到公网地址
    • 将本地文件上传至对象存储 OSS 或提供公网 URL(用于模型调用);
  3. 识别接口调用

    • 构造识别请求体,包含公网可访问的音频地址;
    • 设置模型版本、语言类型、识别级别("sentence", "word", "pinyin")等参数;
    • 接收结构化 JSON 返回结果,解析为语音对照内容。
  4. 不断调试和请求优化

    • 接入过程中反复测试返回内容结构;
    • 修正了多次参数不一致的问题(如请求体嵌套格式);
    • 为了适配豆包(DeepSeek)模型后续评分,增加了统一字段处理步骤。

主要实现过程如下所示

语音输入的旅程:从前端到云端

用户的语音从前端浏览器录制完成之后,需要经过一系列的处理才能送达语音识别服务:

  1. 后端接收前端 WebM 音频
    前端通过 FormData 将用户的 WebM 格式录音和相关元数据(如 userId, videoId, subtitleId)发送到我们的后端 /api/dubbing API 路由。后端服务接收到这些数据后,将音频流暂存到本地文件系统(通常是 uploads/recordings 目录)。

  2. WebM 到 WAV 的格式转换
    尽管 WebM 是一种高效的Web友好音频格式,但许多语音识别服务(包括 ByteDance OpenSpeech)通常更倾向于处理标准的 WAV 格式,因为它通常提供未压缩的音频数据,有利于提高识别准确性。
    为此,我们集成了强大的 FFmpeg 工具。在后端 Node.js 环境中,我们通过 child_process.spawn 调用系统上的 FFmpeg 命令,将接收到的 WebM 文件转换为 16kHz 采样率、16位深、单声道的 WAV 格式。这个转换逻辑封装在 lib/audio.ts 文件中的 convertWebmToWav 函数里,确保了音频数据在进入 AI 服务前的标准化处理。

  3. 音频文件上传至阿里云 OSS
    为了确保 ByteDance OpenSpeech 服务能够稳定、高效地访问用户录制的音频文件,我们选择将转换后的 WAV 文件上传到 阿里云对象存储服务 (OSS)

    • app/api/dubbing/route.ts 中,我们初始化了 ali-oss 客户端,通过环境变量安全地配置了 OSS 的 regionaccessKeyIdaccessKeySecretbucketendpoint
    • 音频文件被上传到 OSS 的特定路径(例如 recordings/ 目录下,并以用户ID、视频ID、字幕ID组合的唯一文件名存储),上传成功后会返回一个公共可访问的 URL。这个 URL 是后续调用语音识别服务的基础。
与 ByteDance OpenSpeech 的深度交互

音频文件在 OSS 上可用后,后端开始与 ByteDance OpenSpeech API 进行通信以执行语音识别。

  1. 异步提交识别任务 (/api/v3/auc/bigmodel/submit)
    ByteDance OpenSpeech API 通常采用异步处理模式,这意味着我们不会立即获得识别结果。

    • 我们通过 axios.post 向提交 API 发送请求,包含用户的唯一 ID (uid)、音频的 OSS 公共 URL 及其格式信息 (format: 'wav', codec: 'raw', rate: 16000, bits: 16, channel: 1),以及识别模型的请求配置 (model_name: 'bigmodel', show_utterances: true)。
    • 请求头中包含必要的认证信息:X-Api-App-KeyX-Api-Access-KeyX-Api-Resource-IdX-Api-Request-Id (一个我们生成的唯一请求ID) 和 X-Api-Sequence
    • 提交请求成功后,API 会返回一个 request-id,这个 ID 将用于后续查询识别结果。
    • 关键代码点app/api/dubbing/route.ts 中的 axios.post('https://openspeech.bytedance.com/api/v3/auc/bigmodel/submit', ...) 调用。
  2. 轮询查询识别结果 (/api/v3/auc/bigmodel/query)
    由于识别任务是异步的,我们需要持续查询其状态。

    • app/api/dubbing/route.ts 中,我们实现了一个轮询机制,在一个循环中定时(例如每秒)向查询 API 发送请求。每次查询请求都带上之前获取到的 request-id
    • 我们设置了最大轮询次数(例如 10 次),以避免无限等待。
    • 当查询 API 返回包含文本识别结果的数据时,表示识别成功,我们可以中断轮询并获取识别文本。
    • 开发过程中遇到的问题:Ngrok 地址过期
      在开发初期,我们曾遇到语音识别无法正常工作的问题,经排查发现是 .env 文件中硬编码的 Ngrok 公共 URL 过期,导致 ByteDance 服务无法访问 OSS 上的音频文件。解决办法是及时更新 Ngrok URL,并在切换到 OSS 后,彻底移除了硬编码的 Ngrok 地址,转而依赖 OSS 的公共 URL,确保了音频文件的高效可达性。
    • 关键代码点app/api/dubbing/route.ts 中轮询 axios.post('https://openspeech.bytedance.com/api/v3/auc/bigmodel/query', ...)for 循环和条件判断。
工作量体现

构建这一语音识别模块,我们投入了大量工作,主要体现在:

  • API 接口的深入理解与集成:细致阅读 ByteDance OpenSpeech API 的文档,理解其复杂的异步请求-查询流程、认证机制以及各种请求参数的含义。
  • 多媒体格式转换的实现与优化:研究 FFmpeg 的使用,确保 WebM 到 WAV 转换的稳定性和兼容性,满足 ASR 服务的技术要求。
  • 云存储服务集成:安全配置阿里云 OSS,实现文件的上传和公共 URL 获取,并处理潜在的网络或权限问题。
  • 异步流程与轮询机制的健壮性设计:实现可靠的轮询逻辑,处理 API 响应的等待、超时和错误情况,确保即使在网络不稳定时也能尽可能获取结果。
  • 调试与问题排查:在开发过程中,针对如 Ngrok 地址过期导致的访问问题等,需要深入分析网络请求和日志,定位并解决问题。

接入过程中的挑战与思考

在整个语音识别服务接入过程中,我不仅要考虑准确性,还要兼顾接口兼容性、请求稳定性、部署便捷性等多方面因素。不断试错、调整、重构接口调用过程,是这段开发经历最真实的写照。

最终,火山引擎的识别结构清晰、部署方式灵活,成功融入了我们整个配音评分链路中。也让我更加深入地理解了语音识别技术的工程落地细节

Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐