2026-03-27 19:18:53 +08:00
|
|
|
|
# WASAPIBackend
|
2026-03-26 16:45:24 +08:00
|
|
|
|
|
|
|
|
|
|
**命名空间**: `XCEngine::Audio::WASAPI`
|
|
|
|
|
|
|
|
|
|
|
|
**类型**: `class`
|
|
|
|
|
|
|
|
|
|
|
|
**头文件**: `XCEngine/Audio/WindowsAudioBackend.h`
|
|
|
|
|
|
|
2026-03-27 19:18:53 +08:00
|
|
|
|
**描述**: 当前内建的 Windows 音频输出后端,实现 `IAudioBackend` 接口并负责设备打开、输出线程和样本提交。
|
|
|
|
|
|
|
|
|
|
|
|
## 命名说明
|
|
|
|
|
|
|
|
|
|
|
|
文档目录名和头文件名是 `WindowsAudioBackend`,但头文件里声明的主要类型名是 `WASAPIBackend`。
|
|
|
|
|
|
|
|
|
|
|
|
更重要的是,虽然类名带 `WASAPI`,当前实现实际上使用的是:
|
|
|
|
|
|
|
|
|
|
|
|
- `<mmsystem.h>`
|
|
|
|
|
|
- `waveOutOpen`
|
|
|
|
|
|
- `waveOutWrite`
|
|
|
|
|
|
- `waveOutPrepareHeader`
|
|
|
|
|
|
|
|
|
|
|
|
也就是更接近 WinMM `waveOut` 路径,而不是 Core Audio WASAPI COM 接口。文档必须把这一点明确说出来,避免名称误导。
|
|
|
|
|
|
|
|
|
|
|
|
## 角色概述
|
|
|
|
|
|
|
|
|
|
|
|
`WASAPIBackend` 是当前唯一已取证到的 `IAudioBackend` 实现,也是 [AudioSystem](../AudioSystem/AudioSystem.md) 默认创建的后端。
|
|
|
|
|
|
|
|
|
|
|
|
它负责:
|
|
|
|
|
|
|
|
|
|
|
|
- 用 `AudioConfig` 初始化 Windows 输出格式
|
|
|
|
|
|
- 枚举 waveOut 设备
|
|
|
|
|
|
- 维护一个后台线程
|
|
|
|
|
|
- 把浮点混音结果转换成 `int16` 输出缓冲
|
|
|
|
|
|
|
|
|
|
|
|
## 当前实现行为
|
|
|
|
|
|
|
|
|
|
|
|
### 1. 初始化阶段
|
|
|
|
|
|
|
|
|
|
|
|
`Initialize()` 会:
|
|
|
|
|
|
|
|
|
|
|
|
- 保存配置
|
|
|
|
|
|
- 填充 `WAVEFORMATEX`
|
|
|
|
|
|
- 调用 `InitDevice()`
|
|
|
|
|
|
- 调用 `InitBuffer()`
|
|
|
|
|
|
|
|
|
|
|
|
`InitDevice()` 当前通过 `waveOutOpen(..., WAVE_MAPPER, ...)` 打开默认设备,并把 `m_deviceName` 设成 `"Default Device"`。
|
|
|
|
|
|
|
|
|
|
|
|
### 2. 设备切换并不完整
|
|
|
|
|
|
|
|
|
|
|
|
`SetDevice(const std::string&)` 现在只是在枚举到设备名匹配时保存 `m_deviceName` 并返回 true,但没有重新打开对应设备。
|
|
|
|
|
|
|
|
|
|
|
|
这意味着它更像“记录想使用哪个设备”,而不是已经完成真正的设备热切换。
|
|
|
|
|
|
|
|
|
|
|
|
### 3. 启停与线程
|
|
|
|
|
|
|
|
|
|
|
|
`Start()` 会创建后台线程,`Stop()` 会关闭运行标记并 `join()` 线程。线程主函数会等待 `m_dataReady`,然后切换前后缓冲并把当前 front buffer 写给系统。
|
|
|
|
|
|
|
|
|
|
|
|
### 4. 样本提交
|
|
|
|
|
|
|
|
|
|
|
|
`ProcessAudio()` 当前会:
|
|
|
|
|
|
|
|
|
|
|
|
- 如果静音或 buffer 为空则直接返回
|
|
|
|
|
|
- 读取 `m_masterVolume`
|
|
|
|
|
|
- 把浮点样本 clamp 到 `[-1, 1]`
|
|
|
|
|
|
- 转成 `int16`
|
|
|
|
|
|
- 写入 back buffer
|
|
|
|
|
|
- 标记 `m_dataReady`
|
|
|
|
|
|
|
|
|
|
|
|
## 当前实现限制
|
|
|
|
|
|
|
|
|
|
|
|
- 类名虽然叫 `WASAPIBackend`,实际不是 WASAPI COM 后端。
|
|
|
|
|
|
- `SetDevice()` 不会真正重建设备句柄。
|
|
|
|
|
|
- `PrepareBackData()` 当前是空实现。
|
|
|
|
|
|
- `ProcessAudio()` 把传入的 `bufferSize` 按字节数解释,而当前 `AudioSystem` 调用方传的是样本数量,这会造成语义不一致。
|
|
|
|
|
|
- 内部使用的双缓冲大小固定基于 `BufferSize = 8192` 和 `int16` 缓冲数组,和 `AudioConfig` 的关系还不完全统一。
|
|
|
|
|
|
- 这是 Windows 专用实现,不适用于跨平台文档语义。
|
|
|
|
|
|
- 没有看到该类型的直接单元测试。
|
|
|
|
|
|
|
|
|
|
|
|
## 线程语义
|
|
|
|
|
|
|
|
|
|
|
|
- 后端内部确实使用线程、原子变量、互斥量和条件变量。
|
|
|
|
|
|
- 但这不意味着上层可以任意并发调用所有 public 方法;当前文档仍不提供完整并发安全承诺。
|
|
|
|
|
|
|
|
|
|
|
|
## 相关方法
|
|
|
|
|
|
|
|
|
|
|
|
- [Constructor](Constructor.md)
|
|
|
|
|
|
- [Destructor](Destructor.md)
|
|
|
|
|
|
- [Initialize](Initialize.md)
|
|
|
|
|
|
- [Shutdown](Shutdown.md)
|
|
|
|
|
|
- [GetDeviceName](GetDeviceName.md)
|
|
|
|
|
|
- [GetAvailableDevices](GetAvailableDevices.md)
|
|
|
|
|
|
- [SetDevice](SetDevice.md)
|
|
|
|
|
|
- [GetMasterVolume](GetMasterVolume.md)
|
|
|
|
|
|
- [SetMasterVolume](SetMasterVolume.md)
|
|
|
|
|
|
- [IsMuted](IsMuted.md)
|
|
|
|
|
|
- [SetMuted](SetMuted.md)
|
|
|
|
|
|
- [Start](Start.md)
|
|
|
|
|
|
- [Stop](Stop.md)
|
|
|
|
|
|
- [Suspend](Suspend.md)
|
|
|
|
|
|
- [Resume](Resume.md)
|
|
|
|
|
|
- [ProcessAudio](ProcessAudio.md)
|
|
|
|
|
|
- [IsRunning](IsRunning.md)
|
|
|
|
|
|
- [GetConfig](GetConfig.md)
|
2026-03-26 16:45:24 +08:00
|
|
|
|
|
|
|
|
|
|
## 相关文档
|
|
|
|
|
|
|
2026-03-27 19:18:53 +08:00
|
|
|
|
- [当前模块](../Audio.md)
|
|
|
|
|
|
- [IAudioBackend](../IAudioBackend/IAudioBackend.md)
|
|
|
|
|
|
- [AudioSystem](../AudioSystem/AudioSystem.md)
|
|
|
|
|
|
- [API 总索引](../../../main.md)
|