bricks/docs/cn.old/vadtext.md
2025-11-19 12:30:39 +08:00

7.7 KiB
Raw Permalink Blame History

VadText 技术文档

# bricks.VadText 模块技术文档

## 概述

`bricks.VadText` 是一个基于 Web Audio 和语音活动检测VAD的交互式语音识别组件用于实时采集用户语音、转换为 WAV 格式并发送至后端 ASR自动语音识别服务最终将识别结果展示在界面上。该组件封装了音频录制、编码、网络请求和 UI 控件逻辑。

该模块依赖于全局对象 `bricks`,并使用了 VADVoice Activity Detection库进行语音检测。

---

## 1. 工具函数:`audioBufferToWav`

### 功能说明
将多通道的 `Float32Array[]` 音频数据(标准 Web Audio API 的 `AudioBuffer` 数据格式)转换为标准的 WAV 容器格式的 `ArrayBuffer`### 函数签名
```js
function audioBufferToWav(channelBuffers, sampleRate)

参数

参数名 类型 描述
channelBuffers Float32Array[] 包含每个声道音频数据的数组,例如 [leftChannel, rightChannel]。单声道时为 [monoData]
sampleRate number 音频采样率Hz如 16000、44100 等。

返回值

  • {ArrayBuffer}:符合 RIFF/WAV 标准的二进制缓冲区,可用于生成 Blob 或 Base64 编码。

实现细节

  • 输出为 16-bit PCM、小端字节序little-endian
  • 支持任意声道数(单声道/立体声等)。
  • 自动填充 WAV 头部信息44 字节标准头):
    • RIFF 标识符与长度
    • fmt 块PCM 格式、声道数、采样率、比特率等
    • data 块:实际音频样本

关键步骤

  1. 计算总样本数:channelBuffers[0].length * channelBuffers.length
  2. 创建大小合适的 ArrayBuffer44 字节头部 + 每样本 2 字节)
  3. 写入 WAV 文件头字段(使用 DataView 和辅助函数 writeString
  4. 将浮点样本(范围 [-1, 1])转换为 16 位整数 PCM
    s < 0 ? s * 0x8000 : s * 0x7FFF
    

⚠️ 注意:此函数假设所有通道具有相同长度。


2. 主类:bricks.VadText

继承自 bricks.VBox,提供垂直布局容器功能,并集成语音输入与文本输出界面。

构造函数

new bricks.VadText(opts)

参数 opts

属性名 类型 必需 默认值 描述
name string 'asr_text' 表单字段名称,用于 getValue() 输出
height string '100%' 组件高度(会被强制设置)
其他属性 any - 传递给父类 VBox

内部初始化内容

  • 按钮 (this.button):点击切换录音状态,图标为 speak.svg
  • 音频播放器 (this.audio):回放已录制的语音片段,样式为 'filler'
  • 水平布局 (hbox):包含按钮和音频播放器
  • 填充分隔符 (this.filler):容纳文本显示区域
  • 文本控件 (this.text_w):显示 ASR 识别结果,支持换行
  • 事件绑定
    • 按钮点击 → toggle_status()
    • 触发 'audio_ready' 事件 → handle_audio()

3. 核心方法

toggle_status()

切换录音启停状态:

  • 若正在录音(this.vad 存在),则调用 stop()
  • 否则调用 start()

start()

启动语音识别流程。

流程

  1. 更新按钮文本为 “stop”
  2. 使用 schedule_once 延迟执行 _start() 以避免同步阻塞

async _start()

异步初始化麦克风 VAD 实例。

步骤

  1. 如果已有全局 bricks.vad 实例,先停止它(保证唯一性)
  2. 创建新的 vad.MicVAD 实例:
    • 配置 onSpeechEnd 回调:当检测到语音结束时触发
    • 回调中派发 'audio_ready' 事件并处理音频
  3. 启动 VAD 监听
  4. 设置全局引用 bricks.vad = this
  5. 清空历史文本 this.text = ''

提示:MicVAD.new() 应返回 Promise确保异步加载完成。


stop()

停止当前录音。

流程

  1. 更新按钮文本为 “start”
  2. 延迟调用 _stop()

async _stop()

真正执行停止操作:

  1. 暂停 VAD 实例
  2. 清理 this.vadbricks.vad
  3. 如果已有识别文本,派发 'changed' 事件通知外部值变更

async handle_audio(audio)

处理由 VAD 捕获的音频数据(Float32Array 单通道音频)。

步骤

  1. 调用 audioBufferToWav([audio], 16000) 生成 WAV 二进制
  2. 使用 arrayBufferToBase64() 转为 Base64 字符串
  3. 设置 this.audio 播放源为 Data URLdata:audio/wav;base64,...
  4. 发送 HTTP 请求到 ASR 接口:
    • 方法POST
    • 参数:
      • model: 使用的模型名(来自 this.model
      • audio: Base64 编码的音频
  5. 成功响应:
    • 追加 rj.contentthis.text
    • 更新文本显示
  6. 失败响应:
    • 弹出错误窗口(bricks.Error

arrayBufferToBase64(buffer)

ArrayBuffer 转换为 Base64 字符串。

实现原理

  1. 使用 Uint8Array 遍历字节
  2. 转换为字符串(String.fromCharCode
  3. 调用 btoa() 进行 Base64 编码

性能提示:大音频文件下可能影响性能,建议改用 FileReaderBuffer.from(buffer).toString('base64')Node.js 环境)


getValue()

获取当前表单值,供外部收集数据使用。

返回格式

{
  [this.name]: "累积识别文本"
}

⚠️ 当前实现有缺陷:未返回对象!应修改为:

getValue(){
  let d = {};
  d[this.name] = this.text;
  return d; // 缺少 return
}

4. 注册组件

bricks.Factory.register('VadText', bricks.VadText);

允许通过工厂模式动态创建该组件,例如:

bricks.Factory.create('VadText', { model: 'whisper-small', url: '/asr' })

5. 依赖与配置要求

外部依赖

  • vad.MicVAD:语音活动检测模块(需提前加载)
  • bricks.Button, bricks.AudioPlayer, bricks.HBox, bricks.VBox, bricks.Filler, bricks.Text, bricks.HttpJson, bricks.ErrorUI 组件库
  • bricks_resource():资源路径解析函数(如 SVG 图标)
  • schedule_once(fn, delay):延迟执行工具函数

配置项(实例属性)

属性 类型 说明
url string ASR 服务接口地址
model string 指定使用的语音识别模型

示例初始化:

new bricks.VadText({
  name: 'user_speech',
  url: '/api/asr',
  model: 'deepseek-asr'
})

6. 事件系统

事件名 触发时机 携带数据
audio_ready VAD 检测到语音结束 Float32Array 音频数据
changed 录音停止且存在识别文本 getValue() 结果对象(需修复返回问题)

7. 已知问题与改进建议

问题 描述 建议修复
getValue() 缺失返回值 当前方法无 return 语句 添加 return d;
Base64 编码效率低 大音频下循环拼接字符串性能差 改用 Blob + FileReader 或现代编码方式
固定采样率 16000 不灵活 可从配置或 VAD 获取真实采样率
错误处理仅弹窗 缺少重试机制 增加重试按钮或自动重连
全局状态污染 bricks.vad 为共享状态 可考虑设计为单例管理器

8. 使用示例

var vadWidget = new bricks.VadText({
  name: 'transcript',
  url: '/api/speech-to-text',
  model: 'base'
});

vadWidget.bind('changed', function(data) {
  console.log("识别结果:", data.transcript);
});

document.body.appendChild(vadWidget.dom);

9. 许可与版权

© 2025 Bricks Framework. All rights reserved.


---

✅ **文档版本**1.0  
📌 **最后更新**2025年4月5日