[P0][CR-F1] 每次播放音效都新建 AudioContext 且从不关闭,达到上限后静默无声 #528
Loading…
Reference in New Issue
No description provided.
Delete Branch "%!s(<nil>)"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
问题描述
"背包已满"和"打开仓库"这两个提示音,每播放一次就
new AudioContext()一次,而且从来不close()。外层又用catch {}把所有错误吞掉了。AudioContext 是重量级资源,每个 context 通常都有自己的音频渲染线程。部分浏览器(例如 iOS Safari)对同时存在的 AudioContext 数量有硬上限,超过上限后构造会失败,或者得到一个无法出声的 context。由于错误被吞掉,玩家看到的现象只是"声音没了",控制台里什么都没有。
证据
playInventoryFullFeedback:overhead-labels.ts:84-125new AudioCtx();close(),错误被catch {}吞掉。playStashOpenSound:mouse-controller.ts:544-564,写法相同,L548 同样每次new AudioCtx()。AudioManager:manager.ts:1-25。它延迟创建唯一一个 AudioContext,并建好了 master / sfx / music 三路增益。act-scene 在 act-scene.ts:204 也实例化了它,但这两个提示音没有使用。audioContext ??= new AudioContext()。playInventoryFullFeedback的注释写的是 "Play authentic Diablo II audio rejection feedback"(overhead-labels.ts:80-83),实际上是代码合成的锯齿波和三角波,并不是原版资源。AGENTS.md§4.3 允许暂缓接入音效,所以合成音本身不算违规,但注释会误导读者。根因
AudioManager。catch {}掩盖了失败,问题只有在长时间游玩后才会出现,很难复现。修复指南
AudioManager:AudioManager上加playTone(spec),用于播放合成音。振荡器和增益节点在共享的 context 上创建,连接到sfxGain,并用ctx.currentTime安排时间:ensureContext()需要改为返回 context;用delayS取代setTimeout。suspended状态,调用resume()。AudioManager通过构造参数传给 mouse-controller 和 pack-loader;globalThis.__d2PlayInventoryFullFeedback;sfx事件,由前端播放(见 #526)。console.warn一次(记一个标志,避免刷屏),不要用catch {}。AudioManager加dispose(),在其中ctx.close(),并在场景销毁时调用(与 #533 配合)。AGENTS.md§4.3,后续接入 Sounds.txt 和 MPQ 中的原版音效时,也走同一个AudioManager。在那之前,把 "authentic" 从注释中删掉,改为注明"临时合成音"。验收标准
src/中只有AudioManager(以及独立的main.ts资源浏览页)会创建 AudioContext;scene/**、ui/**、game/**中没有new AudioContext/webkitAudioContext。AudioContext构造函数,连续触发 100 次"背包已满"和"打开仓库",构造函数只被调用 1 次。catch {},失败时有一次性的 warn。npm run typecheck0 error;npx vitest run全部通过。相关
dispose与资源生命周期。已在提交
58cbc88(fix(audio): reuse shared singleton AudioContext in SoundManager (#528))中完成修复,并在5de27f8中补充端到端与 136 关全量验证套件。修复与验证摘要
AudioManager/SoundManager中复用单一延迟初始化的AudioContext单例,通过ctx.currentTime + delayS调度多段音效并在onended断开释放音频节点,提供dispose()关闭上下文。tests/p0-528-audio-singleton.test.tsnpm run build0 错误、14 个 P0/E2E 测试套件(190/190 用例)100% 通过、全仓 Vitest 6,460/6,460 通过、136/136 关无头浏览器巡检通过。