# AudioSourceComponent **命名空间**: `XCEngine::Components` **类型**: `class` **头文件**: `XCEngine/Components/AudioSourceComponent.h` **描述**: 表示一个可挂载到 `GameObject` 上的音频源,负责音频片段引用、基础播放状态、样本解码和简化混音输出。 ## 角色概述 `AudioSourceComponent` 名字看起来像传统商业引擎里的完整 3D 音频源,但按当前实现,它更准确的定位是: - 一个挂在对象上的播放状态容器 - 一个对 `AudioClip` 做基础解码和样本拷贝的运行时节点 - 一个向 [AudioSystem](../../Audio/AudioSystem/AudioSystem.md) 注册/反注册自身的音频源 它已经不只是纯数据组件,因为确实参与了解码和混音;但它距离完整的 mixer routing、3D spatialization、reverb、doppler 和高质量变调系统仍有明显距离。 ## 当前实现行为 ### 1. `Play()` 的前提是 clip 有效 `Play()` 会先检查: - `m_clip != nullptr` - `m_clip->IsValid() == true` 不满足时直接返回,不报错、不抛异常。 ### 2. 设置音频片段时会立即尝试解码 `SetClip()` 会: - 保存 `m_clip` - 把 `m_isDecoded` 置为 false - 如果 clip 有效,立即执行 `DecodeAudioData()` 所以当前实现更接近“设置资源时做一次预解码缓存”,而不是延迟到首帧播放时才解码。 ### 3. 组件真的参与样本混音 `ProcessAudio()` 当前会: - 检查播放状态、clip 和已解码缓存 - 按输出采样块遍历样本 - 从 `m_decodedData` 中拷贝/映射到输出 buffer - 乘以音量 - 可选执行简化距离衰减 - 在结尾推进 `m_samplePosition` 这说明它不是“只存参数,实际交给别处”的假组件;它确实承担了当前版本的播放核心路径。 ## 和 AudioSystem 的关系 按当前实现: - `Play()` 会把源注册到 `AudioSystem` - `Pause()` 和 `Stop()` 会从 `AudioSystem` 反注册 - `OnEnable()` / `OnDisable()` 也会在播放态下做注册或反注册 - `OnDestroy()` 会调用 `Stop()` 这套设计的好处是对象激活状态和音频系统中的活跃源列表能保持基本一致。 ## 当前参数里哪些真的生效 这是当前文档必须最明确说明的一部分。 ### 已确认有直接效果的参数 - `clip` - `volume` - `pitch` 说明: 当前只体现在 `Update()` 中对时间和样本位置推进的影响,不是高质量重采样。 - `looping` - `spatialize` 说明: 决定是否执行当前的简化距离衰减。 - `Audio3DParams::maxDistance` 说明: 这是当前 `Apply3DAttenuation()` 里真正使用到的 3D 参数之一。 ### 当前主要是存值或接线不完整的参数 - `pan` 说明: 在当前 `ProcessAudio()` 路径中没有看到实际声像处理。 - `outputMixer` 说明: 当前没有看到混音路由消费它。 - `dopplerLevel` - `spread` - `reverbZoneMix` - `Audio3DParams::speedOfSound` - `Audio3DParams::minDistance` - `Audio3DParams::panLevel` 这些字段的 API 形状已经存在,但根据当前取证到的代码,它们还没有完整接入实际播放路径。 ## 序列化语义 `AudioSourceComponent` 当前 **没有覆写** `Serialize()` / `Deserialize()`。 这意味着: - 场景保存/加载时,不会持久化 clip、音量、循环、3D 参数等音频源状态 - 即使 `ComponentFactoryRegistry` 能按类型名重新创建 `AudioSourceComponent`,它恢复出来的也只是默认构造状态 这是当前模块最重要的限制之一。 ## 线程与更新路径 需要特别注意,当前实现的时间推进有两条路径: - `Update(float deltaTime)` 会推进 `m_lastingTime` 和 `m_samplePosition` - `ProcessAudio()` 在混音结束后也会推进 `m_samplePosition` 这意味着当前样本位置推进逻辑比较粗糙,不能把它理解成已经完成了严格的音频时钟统一。 ## 当前实现限制 - `Stop(Audio::StopMode mode)` 当前忽略 `mode`,无论传什么都立即停止。 - `ProcessAudio(..., listenerRotation)` 形参当前未使用。 - `Apply3DAttenuation()` 会直接修改成员 `m_volume`,导致距离衰减会污染后续音量状态,而不是只作用于当次混音。 - `Pause()` 会反注册音频源,但 `Play()` 在“从暂停恢复”路径下只改状态,不重新注册到 `AudioSystem`。 - 如果对象已经在播放中再次调用 `Play()`,当前实现会再次 `RegisterSource(this)`,没有去重保护。 - `pan`、`outputMixer`、多数 3D 参数目前没有完整接入实际音频处理。 - 没有在当前测试目录中看到该组件的直接单元测试覆盖。 这些限制都来自当前源码,而不是推测。 ## 推荐理解方式 现阶段更适合把 `AudioSourceComponent` 理解成“已经能跑起来的基础播放节点”,而不是完整商业级音频系统的最终形态。它已经足够支撑基础功能验证和引擎接线,但在参数生效范围和播放状态一致性上还需要继续打磨。 ## 相关方法 - [Constructor](Constructor.md) - [Play](Play.md) - [Pause](Pause.md) - [Stop](Stop.md) - [SetClip](SetClip.md) - [GetClip](GetClip.md) - [SetVolume](SetVolume.md) - [GetVolume](GetVolume.md) - [SetPitch](SetPitch.md) - [GetPitch](GetPitch.md) - [SetPan](SetPan.md) - [GetPan](GetPan.md) - [SetLooping](SetLooping.md) - [IsLooping](IsLooping.md) - [SetSpatialize](SetSpatialize.md) - [IsSpatialize](IsSpatialize.md) - [Set3DParams](Set3DParams.md) - [Get3DParams](Get3DParams.md) - [SetDopplerLevel](SetDopplerLevel.md) - [GetDopplerLevel](GetDopplerLevel.md) - [SetSpread](SetSpread.md) - [GetSpread](GetSpread.md) - [SetReverbZoneMix](SetReverbZoneMix.md) - [GetReverbZoneMix](GetReverbZoneMix.md) - [SetOutputMixer](SetOutputMixer.md) - [GetOutputMixer](GetOutputMixer.md) - [SetTime](SetTime.md) - [GetTime](GetTime.md) - [GetDuration](GetDuration.md) - [StartEnergyDetect](StartEnergyDetect.md) - [StopEnergyDetect](StopEnergyDetect.md) - [GetEnergy](GetEnergy.md) - [Update](Update.md) - [OnEnable](OnEnable.md) - [OnDisable](OnDisable.md) - [OnDestroy](OnDestroy.md) - [ProcessAudio](ProcessAudio.md) ## 相关文档 - [当前模块](../Components.md) - [AudioListenerComponent](../AudioListenerComponent/AudioListenerComponent.md) - [AudioSystem](../../Audio/AudioSystem/AudioSystem.md) - [GameObject](../GameObject/GameObject.md) - [API 总索引](../../../main.md)