# audio.bgm_ambience_3d —— BGM、区域环境音、角色音效、3D 监听器和前后台恢复 状态:`PARTIAL` 本合同检查 40250 的 2D/3D/Stream 三类声音实例、BGM 音乐状态机、区域环境声、角色动作音效、监听器坐标/方向、音量曲线、限频和前后台生命周期。判断标准是播放实例的分配、状态转换、坐标/音量公式、重复播放限制、地图卸载和恢复副作用一致;“测试能播放”不等于 SoundManager 生命周期等价。 ## 40250 参考调用链 - `MilesLib/SoundManager.cpp` / `SoundManager2D.cpp` / `SoundManager3D.cpp` / `SoundManagerStream.cpp` - `Create` 按 2D、Stream、3D 顺序初始化驱动;`Destroy` 先停止音乐和 3D 实例,再关闭 listener/provider/driver。 - 2D/3D 音效使用固定实例池,实例完成后才可复用;3D 位置按 `(world-listener)/soundScale`,环境声使用 `ambienceSoundScale`。 - 角色音效可按 5000cm 半径过滤,并按文件名限制 0.3 秒内重复播放;`StopAllSound3D` 同时清空历史。 - `MilesLib/SoundManager.cpp` - BGM 维护多个 `TMusicInstance`,状态为 OFF、FADE_IN、FADE_LIMIT_OUT、FADE_OUT、PLAY;`Update` 每帧递增/递减音量,达到边界后切换状态或停止实例。 - `FadeInMusic` 先淡出已有音乐;`FadeOutAllMusic` 为每个活动实例设置淡出;`FadeLimitOutMusic` 保留目标音量。 - `SaveVolume` 同时把音乐和音效音量置零并保存备份,`RestoreVolume` 恢复两者;不是只停止或只静音 BGM。 - `MilesLib/SoundManager3D.cpp` / `PythonSoundManagerModule.cpp` - 暴露 `PlaySound`、`PlaySound3D`、`PlayMusic`、FadeIn/FadeOut、StopAllSound、音乐/音效音量、SoundScale 和 AmbienceSoundScale。 - 3D listener 维护位置、方向、up 和速度;环境声由 `CArea::UpdateAroundAmbience` 每次更新。 - `GameLib/Area.cpp` / `Area.h` - `TAmbienceInstance` 从 AreaAmbienceData/Property 读取位置、范围、最大音量半径、LOOP/ONCE/STEP 和步进随机间隔。 - LOOP 在范围内创建循环实例并按距离线性衰减;离开范围停止并清理索引。ONCE 只在进入范围时播放,STEP 按时间到期播放,离开时重置下一次时间。 ## 当前端实现映射 - `project/audio.gd` - 已有 BGM 双播放器、UI 音效池、AudioStreamPlayer3D、listener 重定位、200/1000 缩放、音量比例曲线、`.mss` 60FPS 事件、角色音效距离/0.3 秒限频,以及 Area LOOP/ONCE/STEP。 - 3D 播放器按需创建,没有 40250 固定实例池/Provider 生命周期;Godot `unit_size/max_distance` 衰减也不是 Miles provider 的同一声学模型。 - BGM 使用 Tween 交叉淡入淡出,不是 `TMusicInstance::Update` 的逐帧状态机;加载失败时 `_bgm_name` 先写入,存在“资源失败后同名请求被跳过”的状态风险。 - `project/bgm_director.gd` / `project/game_scene.gd` - 已按 `bgm_changed` 和 `world_reset` 选曲,服务器音量优先,缺省回落 `musicInfo.fieldMusic`,并保存最近战场曲目。 - 生产链已创建 Audio、BgmDirector 并在每帧更新 listener/区域环境声;地图切换时会停止 BGM,但没有完整证明参考端多音乐实例的淡出状态被逐个清理。 - `project/app_lifecycle.gd` - 已在后台保存并恢复音量、暂停树、降低 FPS,保持 BGM 名称。 - 后台入口调用 `save_volume` 后同步执行 `set_music_volume(0.0)` 和 `set_sound_volume(0.0)`;恢复仍由 `restore_volume` 一次恢复两类音量。 - `project/game_scene.gd` - `_exit_tree` 在卸载 native world 前调用 `Audio.shutdown()`,停止 BGM/3D 音效、解绑播放器 stream 并清空声音缓存,覆盖退出时的资源释放边界。 - `formats/area_data.*` / `extension/src/metin2_world.cpp` - 已读取 AreaAmbienceData、`.pra` 属性和 `playtype/range/maxvolume/playinterval`,按流式 tile 暴露 ambience source;world unload 会移除来源。 - 来源数据和播放状态由不同节点维护,地图卸载/快速切图时音频实例和来源索引的生产级清理尚未有组合测试。 ## 分支等价矩阵 | 40250 判据/副作用 | 当前实现 | 结论 | |---|---|---| | 2D/UI/3D/Stream 三类初始化、实例复用和销毁 | Audio 创建 BGM/UI/3D 节点,使用 Godot 动态播放器 | 部分等价;缺少固定实例池、Miles provider/driver 生命周期和完整销毁证据 | | BGM 多实例 OFF/FADE_IN/FADE_LIMIT_OUT/FADE_OUT/PLAY 状态机 | 双播放器 Tween 交叉淡入淡出 | 部分等价;目标可听结果近似,但状态/边界/多个音乐实例和每帧速度公式不一致 | | FadeIn 先处理旧曲、FadeLimitOut、FadeOutAll | 有对应方法和淡出 Tween | 部分等价;没有逐实例状态快照和停止时序证明,加载失败状态回滚也不完整 | | 音量比例/等级曲线、音乐/音效独立音量 | `ratio_to_apply_volume`、grade 和 UI 设置测试通过 | 基础公式等价;后台 BGM/SFX 同时静音与恢复已有回归,真实 3D 播放实例仍需独立证据 | | SaveVolume/RestoreVolume 同时静音和恢复 BGM/SFX | 保存两类值,后台同时调用 `set_music_volume(0)`/`set_sound_volume(0)`,前台调用 `restore_volume` | 基础行为等价;真实 Miles provider 实例和 3D 播放器清理仍需独立证据 | | listener 位置/方向、200/1000 缩放和 3D 播放 | `GameScene._process` 更新 listener,Audio 重定位实例 | 部分等价;单位与主要公式存在,Godot attenuation/provider 行为不是 Miles 1:1 | | 角色动作 `.mss` 事件、5000cm 距离和 0.3s 限频 | PlayerView/Audio 已实现,相关测试通过 | 基础路径通过;远端角色、全部动作事件和实例池耗尽/失败回退未覆盖 | | Area LOOP/ONCE/STEP、距离衰减和离开范围清理 | world source + `update_ambience_sources` 已实现 | 部分等价;A3 真实来源测试通过,但快速换图、音频实例释放和多 source 乱序缺少证据 | | 地图 BGM/区域声/音效在切图、失焦、暂停、重连后恢复 | BGM 停止、listener/ambient 每帧更新、后台 BGM/SFX 恢复存在 | 部分等价;跨图活动实例清理和真实 3D 播放器释放尚未形成生产级闭环 | ## 已执行测试 - `project/audio_miles_test.gd`:通过;覆盖 200/1000 缩放、音量曲线、`.mss` 解析、角色音效距离/限频和环境声实例基本路径。 - `project/ambience_test.gd`:通过;A3 真实地图来源解析出 1 个 LOOP 环境声,范围、类型和声音路径保持。 - `project/bgm_test.gd`:通过;覆盖地图 BGM 选曲、服务端音量、默认回落、换图信号,以及前后台 BGM/SFX 音量保存、同时静音和恢复。 - `project/rendering_scenario_test.gd`:通过;Debug 原生库退出无 `ObjectDB` 音频资源泄漏,验证 `GameScene._exit_tree -> Audio.shutdown` 的退出边界。 - `project/test_bgm_jukebox_parity.gd`:18 passed、0 failed;覆盖点唱机曲目注册、停止、音量钳制、地图默认和覆写恢复,不覆盖真实 Audio 实例状态机。 - `project/app_flow_lifecycle_test.gd`:通过;覆盖 AppLifecycle 单一属主和断线重连流程,不覆盖真实音频实例释放。 - `project/system_option_ui_test.gd`:通过;覆盖音量设置控件绑定,不覆盖后台和播放失败边界。 ## Deep audit round 2026-09-21 ### Fresh regression results - `audio_miles_test.gd`、`ambience_test.gd`、`bgm_test.gd`、`test_bgm_jukebox_parity.gd` 18/18、`app_flow_lifecycle_test.gd`、`system_option_ui_test.gd` 均退出码 0。 - 回归覆盖了 3D 缩放/限频、A3 环境声、地图 BGM 选择、点唱机覆盖/恢复、生命周期属主和设置绑定;FakeAudio 仍不能证明真实播放实例状态机。 ### Static parity conclusion - BGM/区域环境声/角色 `.mss` 音效的主要入口存在,但 Godot 动态播放器/Tween 不等价于 40250 固定实例池、Provider 和 `TMusicInstance` 状态机。 - `app_lifecycle.gd` 后台路径已同步静音并恢复 BGM/SFX;加载失败后 `_bgm_name` 可能阻止同名重试,跨图快速切换时来源索引和播放器释放仍缺少生产级证据。 - listener、缩放和 0.3s/5000cm 规则有局部验证,但远端 Actor 动作音效、实例池耗尽、失焦/暂停/重连/退出清理和 Godot attenuation 与 Miles provider 的边界未闭合。 ### Active source group review - 对 Visual Studio `MilesLib` 活跃编译单元逐组核对后,参考链补充确认:`CSoundManager2D/3D/Stream::Initialize/Destroy` 创建并释放固定实例池;`CSoundData` 维护 pack-backed 数据缓存和 access/play time;`CSoundInstance2D/3D/Stream` 分别处理 SetSound、Play/Stop、位置/方向/速度与音量;`CSoundManager` 负责 listener、距离缩放、0.3 秒限频、环境声、音乐实例索引和多状态淡入淡出。 - 当前端 Audio 的动态播放器和 Tween 只覆盖可听结果的部分路径,尚未证明固定池、sound data cache、播放失败/空槽、逐实例停止和 MusicIndex 状态机等副作用一致;本组静态核对完成,合同仍为 `PARTIAL`。 ## Implementation fix round 2026-09-22T08:50Z - 修复前 `bgm_test.gd` 新增的 40250 `SaveVolume` 回归失败:进入后台后 `master_bgm=0`,但 `master_sfx=0.9`。 - 按 `CSoundManager::SaveVolume` 的顺序,`AppLifecycle._enter_background` 在保存备份后同时调用 `set_music_volume(0.0)` 与 `set_sound_volume(0.0)`;`_exit_background` 继续由 `restore_volume` 同时恢复两者。 - 修复后 `bgm_test.gd`、`audio_miles_test.gd`、`app_flow_lifecycle_test.gd`、`disconnect_world_reset_test.gd`、`netbridge_test.gd` 均通过;渲染退出回归无 `ObjectDB` 泄漏。 - 该轮只闭合后台音量副作用和退出音频缓存清理,BGM 多实例状态机、Miles 固定实例池、真实 3D 衰减/Provider、快速换图来源清理仍保持 `PARTIAL`。 本轮仍为 `PARTIAL`,完成后台音量和退出清理的实现及第二轮证据登记。 ## 已确认差异和未验证项 1. 验证正在播放的 3D 实例不会绕过 master SFX,并补真实设备/实例路径的后台静音回归。 2. 建立 BGM 多实例状态快照测试,覆盖 FadeIn、FadeLimitOut、FadeOut、FadeOutAll、相同曲目重入、加载失败回滚和切图清理。 3. 明确 Godot 3D 衰减与 Miles provider 的适配边界,至少固定 `unit_size/max_distance`、监听器方向/速度和实例池耗尽行为。 4. 增加真实流式地图快速切换、来源删除、重复 key、LOOP/ONCE/STEP 乱序和退出时无悬挂播放器的测试。 5. 补齐远端 Actor 的动作音效触发、重连/死亡/换图清理和资源缺失回退证据。 ## 下一轮验证入口 - 先审计 `lifecycle.platform_release`,核对 Windows 窗口、导出、自包含资源、崩溃处理和发布门禁。 - 回到实现阶段时,优先建立 BGM 多实例状态机和真实 3D 播放器清理测试,再处理固定实例池/Provider 的适配边界。 ### 2026-09-22 死代码删除 以下文件只被自身测试引用、运行时从不加载,已删除;本文中以它们为实现或证据的结论作废,相关行为需按 40250 源码单元重新移植:`test_bgm_jukebox_parity.gd`。