Audio(音频)
| 项 | 内容 |
|---|---|
| 契约版本 | audio@1 |
| 当前状态 | 测试中 |
| 源码目录 | src/core/audio/ |
| 页面定位 | 音频播放参考:通道如何路由、音量如何混合、语音如何打断与测量 |
Audio 只负责「让声音按语义响起来」。解码由 Asset 的 stream 形态与变体生成器承担,音频文件本身不进入 Audio 的存储。
职责
负责:
- 四个逻辑通道:
bgm、se、voice、ambient。 - 通道级播放、停止、循环、淡入淡出与音量。
- 总音量与各通道音量的混合,以及
audio:resolve-mix机制插槽。 - 语音播放、打断与实时音量测量(供立绘口型)。
- 浏览器自动播放限制下的解锁与补播。
- 向
script:presentation@1登记 BGM 与音效执行者。
不负责:
- 不识别音频格式、不做 HCA 解码或 PCM 转换;这些经 Asset 的
stream形态完成。 - 不持久化音量设置;由外壳启动时按本地设置调用
setVolume。 - 不管理字幕、台词推进或语音与台词的对应关系。
- 不提供音频编辑、总线可视化或离线渲染。
对外接口
服务
| 名称 | policy | 说明 |
|---|---|---|
audio:channel | trusted | play(channel, vpath, { loop?, volume?, fadeMs? })、stop(channel, { fadeMs? })、playVoice(vpath)、voiceLevel() |
audio:mixer | trusted | volumes()、setVolume(channel|'master', 0–1)、unlocked() |
机制插槽
| 名称 | policy | 说明 |
|---|---|---|
audio:resolve-mix | trusted | 输入 { channel, vpath, volume, master },输出最终增益;默认二者相乘 |
通知与实现位
| 名称 | 类别 | policy | 说明 |
|---|---|---|---|
audio:on-voice | 通知 | — | { vpath, state: 'start'|'end' } |
audio:on-track-end | 通知 | — | { channel, vpath },只在不循环的曲目自然播完时发出 |
audio:output@1 | 实现位(single) | public | 默认 webaudio;形状 { context() → AudioContext, unlocked() → boolean } |
audio:bus@1 | 实现位(single) | public | 默认 builtin;形状 { build(context, destination) → { input(channel), dispose() } } |
通道取 bgm/se/voice/ambient。两个实现位都是 single、public,卸载回落默认条目。
关键语义
四通道
| 通道 | 并发 | 循环 | 换曲行为 |
|---|---|---|---|
bgm | 同时只有一首 | 缺省循环 | 旧曲按 fadeMs 淡出、新曲淡入;同一 VPath 不重播,只调音量 |
ambient | 同时只有一首 | 缺省循环 | 同 bgm |
se | 可叠加 | 缺省不循环 | 新音效不打断旧音效;stop('se') 停掉全部 |
voice | 同时只有一条 | 不循环 | playVoice 先停当前语音再播 |
PCM 带 loop 即按循环点循环。loop 缺省为 true(bgm/ambient)或 false(se/voice);fadeMs 不写时由内置实现决定过渡时长,需要精确控制时显式传入。
语音
playVoice(vpath)与play('voice', vpath)相同。- 开始与结束(含播完、被打断、被停)各发一次
audio:on-voice,state分别为start/end。 voiceLevel()返回当前语音的实时音量(RMS,0–1),由语音源另接的分析节点在通道音量与总音量调节之前测得。- 玩家调小或静音语音也照常有值;没有语音在播或环境没有分析节点时为 0。
- 该值供立绘渲染器做口型,不参与混音或存档。
解锁与补播
浏览器要求首次用户操作后才能出声。内置 webaudio 在文档捕获阶段监听首次 pointerdown/keydown/touchend,在该手势内 resume()。
| 通道 | 解锁前的行为 |
|---|---|
bgm | 记下最近一次指令,解锁后补播 |
ambient | 记下最近一次指令,解锁后补播 |
se | 直接丢弃 |
voice | 直接丢弃 |
外壳在每次用户操作后重新查询 unlocked(),决定是否收起提示。
播放返回与音量
play在开始出声或因未解锁而记下时兑现;取用或解码失败时拒绝。- 取用期间被同通道更新的指令取代或被
stop,本次不出声。 - 音量缺省全为 1。Audio 不存盘,由外壳启动时按本地设置调用
setVolume。 - 最终增益按
audio:resolve-mix的结果落在每个曲目自己的增益节点上;曲目开始与任一音量变化时各算一次。 - 结果不是 0–1 的有限数时按默认结果处理,并在控制台留告警。
stop(channel, { fadeMs? })对se是停掉全部;对bgm/ambient是停当前曲;对voice是打断当前语音并发end。
与 Asset 的衔接
- 以
as: 'stream'取用整段 PCM;建好AudioBuffer后即释放句柄,不另设缓存。 asset:on-invalidate命中正在播放的曲目时不打断;下次play重新取用。stream的loop是采样位置{ start, end },end不含;Audio 将它映射为循环区间。
演出派发
- 向
script:presentation@1登记core:bgm(loop缺省true)与core:se的执行者。 - 快进中 BGM 照常播放,音效省略。
core:say的语音由内置对话框经audio:channel播放,推进即停。
当前实现范围
已完成:
- 两个服务、机制插槽、两个通知、两个实现位与内置条目。
- 引擎单元测试用假上下文覆盖播放语义;契约用例在 Node 与系统 Edge 中通过。
- Edge 中另有一例真实点击解锁、补播与音效播完通知。
- 四通道、语音打断、
voiceLevel()、音量混合与演出派发均已接入。
未完成/限制:
- Phase 2 已完成;真实章节在 Windows WebView2 与 Android 各通过 409 项检查,语音读取与非零 PCM 播放调度已核验。
- 实际听感不在本轮验证范围。
- 没有分析节点或没有语音时,
voiceLevel()恒为 0;调用方需自行处理该降级。 - 解锁前的
se/voice会被丢弃,不排队补播。