CD Code Digest Hub /CS:GO

引擎音频管线:从 EmitSound 到声卡

42 分钟阅读 11 个章节 2 张图

本页解释一次“发声”在 CS:GO 源码里如何流动:游戏代码按 game sound 名称发声,soundemittersystem 把名称解析成具体 wav 与参数,服务端经 svc_Sounds 把声音事件发给客户端,客户端引擎(engine/audio/)分配声道、按听者位置空间化、经 DSP 混音后写入声卡;以及专用服务器为何几乎不含这些代码。理解它有助于排查“声音没播/播两遍/方向不对”、调整网络声音预算,以及读懂客户端预测下的音效抑制。

相关页面:引擎运行时总览 · 主循环与 Tick · 网络通道与协议 · 实体复制 · 语音与视频 · 场景与响应规则 · 输入命令与预测 · 专用服务器

相关源码文件#

  • engine/audio/private/snd_dma.cpp:S_Init、S_StartSound、S_StartSoundEntry、S_Update、声道分配与空间化
  • engine/audio/private/snd_mix.cpp:MIX_PaintChannels 混音与 DSP 串联
  • engine/audio/private/snd_channels.h:channel_t 与声道上限
  • engine/audio/private/snd_win.cpp、engine/audio/public/snd_device.h:输出设备选择
  • engine/audio/private/snd_op_sys/sos_system.cpp:声音操作符栈(SOS)
  • engine/EngineSoundServer.cpp、engine/EngineSoundClient.cpp:服务端/客户端 IEngineSound 实现
  • engine/sv_main.cpp、engine/sv_client.cpp、engine/servermsghandler.cpp、engine/cl_main.cpp:svc_Sounds 的发送与接收
  • soundemittersystem/soundemittersystembase.cpp:game sound 脚本解析
  • game/shared/SoundEmitterSystem.cpp:游戏侧 CBaseEntity::EmitSound
  • game/server/recipientfilter.cpp、game/client/c_recipientfilter.cpp:接收者过滤(PAS、距离、预测)
  • game/shared/soundenvelope.cpp、game/client/c_soundscape.cpp:包络声音与环境声景
  • engine/engine_inc.vpc:音频文件的条件编译

1. 模块与职责#

模块形态/装配职责证据
engine/audio/private/*编进 engine.dll,几乎全部标 [!$DEDICATED]声道、混音、DSP、SOS、设备输出、语音播放engine/engine_inc.vpc:456-500、engine/engine_inc.vpc:513-521
EngineSoundServer.cpp总是编译服务端 IEngineSound:预缓存、SV_StartSoundengine/engine_inc.vpc:419-422、engine/EngineSoundServer.cpp:171-173
EngineSoundClient.cpp、engsoundservice.cpp[!$DEDICATED]客户端 IEngineSound;引擎回调客户端 DLL 取实体位置(ISoundServices)engine/engine_inc.vpc:423-424、engine/EngineSoundClient.cpp:111-113、engine/engsoundservice.cpp:572
soundemittersystem.dllAppSystem;客户端启动器、srcds、server.dll、client.dll、engine 都要它game sound 名称 → wav/音量/音高/声道/操作符栈launcher/launcher.cpp:745、dedicated/sys_windows.cpp:269、game/server/gameinterface.cpp:773、game/client/cdll_client_int.cpp:1500-1501、engine/sys_dll2.cpp:559
soundsystem.dll仅 Win32 启动器加载独立的轻量播放器:CS:GO 选项页枚举音频设备、game_controls UI 音效、hlmv 等工具;不是引擎 S_* 的后端launcher/launcher.cpp:747-749、game/client/cstrike15/Scaleform/options_audio_scaleform.cpp:52-55、vgui2/game_controls/gameuisystemmgr.cpp:93-100、utils/hlmv/mdlviewer.cpp:1764
soundsystem_lowlevel静态库,engine(非 DEDICATED)与 soundsystem.dll 都链接CAudioDeviceList/IAudioDevice2:XAudio2/DirectSound(Windows)、SDL(POSIX)、null 设备engine/engine_inc.vpc:1253、soundsystem/soundsystem.vpc:59、soundsystem/lowlevel/soundsystem_lowlevel.vpc:24-30、public/soundsystem/lowlevel.h:36-39
vaudio_milesVAudioInit 动态加载Miles MP3 解码(IVAudio)engine/audio/private/snd_dma.cpp:941-957

soundsystem.dll 的设备枚举同样来自 soundsystem_lowlevel 的 CAudioDeviceList:soundsystem/soundsystem.cpp:277-279。vaudio_miles 不在 csgo_partner 构建组里(vpc_scripts/groups.vgc:11-103),其工程引用的 common/miles/mss.h 在本快照中未被 git 跟踪(engine/voice_codecs/miles/vaudio_miles.vpc:40),因此 MP3 解码依赖预编译二进制(推断)。

2. 端到端数据流#

证据:游戏侧入口与分派 game/shared/SoundEmitterSystem.cpp:984-1004、调用引擎 game/shared/SoundEmitterSystem.cpp:783-800;参数解析 soundemittersystem/soundemittersystembase.cpp:1653-1720;CEngineSoundServer → SV_StartSound engine/EngineSoundServer.cpp:293-295;SV_StartSound → BroadcastSound engine/sv_main.cpp:1440;BroadcastSound → SendSound engine/sv_main.cpp:1777;可靠/不可靠两条路 engine/sv_client.cpp:1006-1054;客户端接收与排队 engine/servermsghandler.cpp:575-606、engine/servermsghandler.cpp:165-173;分派到 S_StartSoundEntry/S_StartSound engine/cl_main.cpp:751-799;客户端本地发声 engine/EngineSoundClient.cpp:322-347;S_StartSoundEntry 最终调用 S_StartSound engine/audio/private/snd_dma.cpp:7532-7539;选声道 engine/audio/private/snd_dma.cpp:6711-6723;每帧空间化 engine/audio/private/snd_dma.cpp:8318-8330;混音与输出 engine/audio/private/snd_mix.cpp:2841-2966。

3. game sound 脚本:soundemittersystem#

  • 加载:CSoundEmitterSystemBase::Init 带引用计数,首次初始化时读取 scripts/game_sounds_manifest.txt;清单项 precache_file、autocache_file、preload_file 分别以不同的预载/自动缓存参数调用 AddSoundsFromFile:soundemittersystem/soundemittersystembase.cpp:31、soundemittersystem/soundemittersystembase.cpp:182-194、soundemittersystem/soundemittersystembase.cpp:211-267。
  • 条目字段:除 channel/volume/pitch/soundlevel/delay_msec 外,CS:GO 版本支持 soundentry_version 与 operator_stacks(交给引擎 SOS 执行)以及 HRTF 选项:soundemittersystem/soundemittersystembase.cpp:781-800、soundemittersystem/soundemittersystembase.cpp:908-929。
  • 查找:名称经 MurmurHash2LowerCase(种子为字符 'DOTA')得到 HSOUNDSCRIPTHASH,再查 m_HashToSoundEntry:soundemittersystem/soundemittersystembase.cpp:1859-1873。GetParametersForSoundEx 找不到条目时退回名为 Error 的条目;随机化音量与音高,并按 m_nRandomSeed 与性别挑选具体 wav:soundemittersystem/soundemittersystembase.cpp:1661-1720。
  • 接口以 SOUNDEMITTERSYSTEM_INTERFACE_VERSION 导出:soundemittersystem/soundemittersystembase.cpp:1969-1971。

4. 游戏代码如何发声#

4.1 CSoundEmitterSystem(server 与 client 共用源码)#

  • CBaseEntity::EmitSound( name ) 默认使用 CBroadcastRecipientFilter:game/shared/SoundEmitterSystem.cpp:1868-1882。
  • EmitSound 先看名字:含 .wav/.mp3 或以 !(句子)开头时走 EmitSoundBySoundFile,否则计算哈希走 EmitSoundByHandle:game/shared/SoundEmitterSystem.cpp:984-1004。
  • EmitSoundByHandle 用 GetSoundEntryParameters 解析参数(服务端还会让 env_microphone 有机会“吞掉”声音):game/shared/SoundEmitterSystem.cpp:615-670。当 sv_soundemitter_version(默认 2,复制到客户端)> 1 且条目版本 > 1 时置 SND_IS_SCRIPTHANDLE,表示网络上传的是 game sound 哈希 而非 wav 名:game/shared/SoundEmitterSystem.cpp:44、game/shared/SoundEmitterSystem.cpp:756-764。随后复制过滤器并调用 enginesound->EmitSound,同时传入条目名、哈希、选中的 wav 与随机种子:game/shared/SoundEmitterSystem.cpp:770-800。
  • enginesound 在 server.dll 中是 CEngineSoundServer,在 client.dll 中是 CEngineSoundClient(见第 5、6 节)。

4.2 接收者过滤:PAS、距离与预测#

引擎的 BroadcastSound 不做可听性裁剪,裁剪全部由游戏传入的 IRecipientFilter 决定(见第 5 节)。服务端常用过滤器:

过滤器/规则行为证据
AddRecipientsByPAS单人时加所有玩家;多人时调用引擎 SV_DetermineMulticastRecipients(usepas=true),按声源所在簇的 PAS 与各客户端“耳朵位置”所在簇求交,GOTV 客户端总被加入game/server/recipientfilter.cpp:264-276、engine/vengineserver_impl.cpp:1074-1077、engine/sv_main.cpp:1449-1496
CPASAttenuationFilter::Filter在 PAS 基础上按衰减剔除:最大可听距离 = 2 * SOUND_NORMAL_CLIP_DIST / attenuation(1000 单位基准);衰减 ≤ 0 时加入所有玩家;HLTV/Replay 不剔除game/server/recipientfilter.cpp:444-485、public/const.h:421
UsePredictionRules(服务端)玩家开启武器预测(IsPredictingWeapons())时,执行其命令期间 SuppressHostEvents(this);使用预测规则的过滤器会移除该玩家,避免其听到自己已在本地预测播放过的声音game/server/player.cpp:3602-3624、game/server/recipientfilter.cpp:288-303
UsePredictionRules(客户端)只在“可预测”(首次预测)时保留本地玩家,重放预测命令时移除game/client/c_recipientfilter.cpp:173-204

预测与“首次预测”标志的机制见 输入命令与预测。

4.3 包络声音与声景#

  • CSoundEnvelopeController:CSoundPatch::StartSound 仍通过 CBaseEntity::EmitSound 发声,附带 SND_CHANGE_VOL 与 SND_GENERATE_GUID(音乐类除外不加改音量标志),后续音量/音高包络通过对同一声音发“改变”命令实现:game/shared/soundenvelope.cpp:649-700。控制器在客户端每帧 Update、服务端在 PreClientUpdate 中处理按时间排序的命令队列:game/shared/soundenvelope.cpp:892-903、game/shared/soundenvelope.cpp:1024-1054。
  • 声景(soundscape):服务端 env_soundscape 把声景索引写入玩家的 audioparams_t:game/server/soundscape.cpp:215;客户端 C_SoundscapeSystem 读取 scripts/soundscapes_manifest.txt,在索引变化时启动新声景,并用 EmitAmbientSound 在本地播放环境声(不经网络):game/client/c_soundscape.cpp:395、game/client/c_soundscape.cpp:743-765、game/client/c_soundscape.cpp:1556。

5. 服务器 → 客户端:svc_Sounds#

5.1 服务端#

  1. CEngineSoundServer::PrecacheSound 通过 SV_FindOrAddSound 把文件加入声音预缓存表,溢出时 Host_Error:engine/EngineSoundServer.cpp:197-222、engine/sv_precache.cpp:72-82。预缓存表本身是引擎字符串表之一(见 网络通道与协议)。
  2. EmitSoundInternal 校验音量 0–1、声级范围、音高 0–255,越界只打印警告并丢弃;以 ! 开头的句子经 VOX 查表转为句子索引:engine/EngineSoundServer.cpp:268-296、engine/EngineSoundServer.cpp:329-354。
  3. SV_StartSound 构造 SoundInfo_t:位置取显式 origin,否则取实体碰撞体世界中心;指定了 soundtime 时加一个 tick 并置 SND_DELAY;SND_IS_SCRIPTHANDLE 时 nSoundNum 直接存哈希,否则存预缓存索引,未预缓存则打印 not precached 并放弃:engine/sv_main.cpp:1344-1441。
  4. CGameServer::BroadcastSound:暂停时(非 developer)丢弃;对过滤器里每个合法且 active 的客户端调用 SendSound:engine/sv_main.cpp:1747-1779。
  5. CGameClient::SendSound:Bot(非 GOTV、非分屏)与正在看击杀回放的客户端不发;可靠声音立即作为单条 CSVCMsg_Sounds 进可靠流;不可靠声音打上序号进入 m_Sounds 队列,等下一个快照:engine/sv_client.cpp:988-1055。
  6. 快照发送时 WriteGameSounds → FillSoundsMessage 对队列做逐条差量编码,多人模式上限取 ConVar sv_multiplayer_sounds(C++ 变量名 sv_multiplayer_maxsounds,默认 20),单人 255;超出部分在 sv_sound_discardextraunreliable=1(默认)时直接丢弃:engine/baseclient.cpp:1406、engine/baseclient.cpp:1511-1512、engine/sv_client.cpp:1057-1126。GOTV 客户端把同样的队列打成一条消息交给 HLTV 服务器:engine/sv_client.cpp:1619-1631(见 Demo 与 GOTV)。
方面可靠声音不可靠声音
触发过滤器 IsReliable() 为真(SND_STOP 断言必须可靠)默认
发送时机立即 SendNetMsg(..., true)跟随下一个快照
序号不传序号,客户端自增服务端 m_nSoundSequence
丢失由可靠流重传超预算丢弃;丢包即丢失

证据:engine/sv_main.cpp:1365-1368、engine/sv_client.cpp:1006-1054、engine/servermsghandler.cpp:591-597、engine/sv_client.cpp:1102-1109。

消息格式:CSVCMsg_Sounds 含 reliable_sound 与重复的 sounddata_t(坐标、音量、延迟、序号、实体、声道、音高、标志、sound_num 或 sound_num_handle、random_seed、声级、是否句子等):common/netmessages.proto:593-618;消息类型号 svc_Sounds = 17,组别 SOUNDS:common/netmessages.proto:343、common/netmessages.h:489。可用标志位见 public/soundflags.h:118-139。

5.2 客户端#

  • CClientState::SVCMsg_Sounds 逐条 ReadDelta,可靠声音在本地自增序号,然后全部插入按序号排序的红黑树:engine/servermsghandler.cpp:575-606、engine/cl_main.cpp:656-673。
  • 整个数据包解析完后,PacketEnd 调 CL_DispatchSounds 按序号依次播放并清空:engine/servermsghandler.cpp:165-173、engine/cl_main.cpp:805-824。
  • CL_DispatchSound:SND_IS_SCRIPTHANDLE 时用哈希与服务端传来的 random_seed 调 S_StartSoundEntry;句子构造占位 CSfxTable;普通声音按预缓存索引取 CSfxTable;demo 快进时除停止命令外都不播放:engine/cl_main.cpp:702-800。由于客户端用同一种子再次调用 GetParametersForSoundEx,客户端选中的 wav 变体应与服务端一致(推断):engine/audio/private/snd_dma.cpp:7401-7408、soundemittersystem/soundemittersystembase.cpp:1708。
  • 观察:CL_DispatchSound 先依据 SND_DELAY 用服务器时钟算出 params.delay(engine/cl_main.cpp:707-734),随后又被 params.delay = sound.fDelay 覆盖(engine/cl_main.cpp:747)。是否有意无法从代码确认。

6. 客户端引擎:从 EmitSound 到声道#

  • 本地发声:CEngineSoundClient::EmitSoundInternal 做与服务端相同的参数校验;实体索引为负时视为“眼中”声音,改用客户端 DLL 提供的 in-eye 实体或视角实体;本地玩家不在过滤器内则不播;无 origin 时取实体渲染位置;SND_IS_SCRIPTHANDLE 走 S_StartSoundEntry,否则 S_PrecacheSound 后 S_StartSound:engine/EngineSoundClient.cpp:196-262、engine/EngineSoundClient.cpp:300-348。
  • S_StartSoundEntry:-nosound 或哈希无效时返回;按哈希取回条目名与参数;先执行 SOS 的 cue 栈(可阻止播放或排队延后),再执行 start 栈(可改延迟或阻止),最后预缓存 wav 并 S_StartSound:engine/audio/private/snd_dma.cpp:7349-7408、engine/audio/private/snd_dma.cpp:7470-7540。
  • S_StartSound / 排队:host_threaded_sound 打开时,开始命令被推入 g_QueuedSounds 并立即返回 GUID,由下一次 S_Update 统一启动;否则直接 S_StartSound_Immediate:engine/audio/private/snd_dma.cpp:7134-7161、engine/audio/private/snd_dma.cpp:8224-8228。
  • S_StartSound_Immediate:流式文件强制 CHAN_STREAM;带 SND_STOP/CHANGE_VOL/CHANGE_PITCH 的命令只修改已有声道;音高为 0 拒绝;静态声音(以及默认情况下的 script handle 声音)分配静态声道,其余分配动态声道:engine/audio/private/snd_dma.cpp:6646-6728。非静态声音若开始时就被空间化到 0 音量,且不是循环、句子或长音频,会被立即释放——之后走近也听不到:engine/audio/private/snd_dma.cpp:6965-6985。
  • 声道:channel_t 保存 CSfxTable、CAudioMixer、SOS 栈、各扬声器当前/目标音量、soundsource/entchannel、位置、DSP 混合比例等:engine/audio/private/snd_channels.h:61-110。总上限 MAX_CHANNELS = 128,前 32 个为动态声道,其后为静态声道:engine/audio/private/snd_channels.h:160-168。SND_PickDynamicChannel 通过“偷取”获得声道,同一实体同一声道上的同一循环声音不会重启:engine/audio/private/snd_dma.cpp:1890-1922。

7. 每帧更新、空间化与混音#

证据:听者状态 game/client/view.cpp:792-804、engine/host.cpp:888-893;声音阶段 engine/host.cpp:3583-3603、engine/host.cpp:2877-2893;S_Update 主体 engine/audio/private/snd_dma.cpp:8203-8330 与结尾 engine/audio/private/snd_dma.cpp:8645-8658;混音线程分支 engine/audio/private/snd_dma.cpp:9075-9099;混音循环 engine/audio/private/snd_mix.cpp:2841-2966。

  • 线程:host_threaded_sound(PC 默认 0,X360/PS3 默认 1)把整次 Host_UpdateSounds 作为线程池任务运行:engine/host.cpp:636、engine/host.cpp:3605-3645。snd_mix_async(默认 0)另起名为 SndMix 的线程,以约 33 ms 周期调用 S_Update_Guts:engine/audio/private/snd_dma.cpp:609、engine/audio/private/snd_dma.cpp:9019-9060。
  • 空间化:SND_Spatialize 对 HRTF 声道单独计算;若声道带 SOS update 栈,则完全由操作符计算音量与位置;否则通过 g_pSoundServices->GetSoundSpatialization 向客户端实体查询当前位置/朝向,再算增益与 DSP 混合比:engine/audio/private/snd_dma.cpp:5299-5303、engine/audio/private/snd_dma.cpp:5379-5384、engine/audio/private/snd_dma.cpp:5509-5512、engine/engsoundservice.cpp:63-79。
  • 混音(MIX_PaintChannels):每轮最多一个 paintbuffer,先把所有声道上采样混入 room/facing/facing-away/dry/speaker 几个缓冲,再依次施加 facing-away、speaker、room、空间延迟、水下、玩家 DSP,合并 dry 缓冲,压限到 16 位后 TransferSamples:engine/audio/private/snd_mix.cpp:2856-2966。
  • 两代设备接口:S_Update_Guts 在 Windows(USE_AUDIO_DEVICE_V1)上按 DMA 位置 PaintBegin/PaintEnd;其他平台按已排队缓冲数补足 mixAheadTime,以 MIX_BUFFER_SIZE 为单位混音:engine/audio/private/snd_dma.cpp:8923-9015。
  • HRTF:snd_use_hrtf 默认开启,使用 Steam Audio(phonon3d)的双耳效果,可在独立 SndMixPhonon 线程运行;创建效果失败时自动关闭 HRTF:engine/audio/private/snd_dma.cpp:86、engine/audio/private/snd_wave_data.cpp:3540-3553、engine/audio/private/snd_wave_data.cpp:3779-3796;链接库见 engine/engine_inc.vpc:1294-1298。
  • SOS 操作符栈:启动时从 scripts/sound_operator_stacks.txt 读取 update_stacks/start_stacks/stop_stacks 等分组,每帧 Update 处理阻塞条目:engine/audio/private/snd_op_sys/sos_system.cpp:1355-1400、engine/audio/private/snd_op_sys/sos_system.cpp:1809-1812。

8. 初始化与输出设备#

  • Host_Init 在读取配置后调用 S_Init:engine/host.cpp:5709-5713。Snd_Restart_f 会保存正在播放的音乐、S_Shutdown、清空客户端声音表再 S_Init:engine/host.cpp:348-366。
  • S_Init 顺序:VOX 句子表 → VAudioInit → -nosound 时换成 null 设备并返回 → S_Startup 建设备 → paintbuffer、音量表、混音器配置、SOS → 停止全部声音 → 音频源缓存 → DSP:engine/audio/private/snd_dma.cpp:974-1063。
  • 设备:USE_AUDIO_DEVICE_V1 只在 Windows 定义:engine/audio/public/snd_device.h:41-43。V1 的 AutoDetectInit 在 Windows(非 SDL)创建 DirectSound 设备,失败返回 null 设备:engine/audio/private/snd_win.cpp:78-157。非 Windows 走 V2:S_Startup 用 CAudioDeviceList 建设备列表并按 sound_device_override 创建 IAudioDevice2,缓冲按 150 ms 计算:engine/audio/private/snd_dma.cpp:869-924;POSIX 上 soundsystem_lowlevel 只编译 SDL 与 null 设备(soundsystem/lowlevel/soundsystem_lowlevel.vpc:24-27),而默认首选子系统写的是 XAudio(engine/audio/private/snd_dma.cpp:830-840),实际应回落到 SDL(推断)。
  • 混音输出采样率固定为 44100:engine/audio/public/snd_device.h:16-21。

9. 专用服务器:音频如何被剔除#

两层机制叠加:

  1. 编译期(DEDICATED 宏,engine_ds 工程):VPC 在 $DEDICATED 下定义 DEDICATED;SWDS;NO_BINK:engine/engine_inc.vpc:30。混音、DSP、SOS、wave 解码、客户端 IEngineSound、语音录放等文件都标 [!$DEDICATED],soundsystem_lowlevel 也不链接:engine/engine_inc.vpc:423-424、engine/engine_inc.vpc:456-500、engine/engine_inc.vpc:513-521、engine/engine_inc.vpc:1253。调用侧用 #ifndef DEDICATED 包住 S_Init、Host_UpdateSounds、_Host_RunFrame_Sound:engine/host.cpp:5709-5713、engine/host.cpp:2881-2892、engine/host.cpp:3585。客户端消息处理器在 cl_null.cpp 中是空实现:engine/cl_null.cpp:177-186。SV_GetSoundDuration 在该构建下恒返回 0:engine/EngineSoundServer.cpp:452-459。
  2. 运行期(普通 engine 模块以专用服务器身份运行):Windows 与 Linux 的 srcds 都按名字加载 engine 模块:dedicated/sys_windows.cpp:268、dedicated/sys_linux.cpp:47。在非 DEDICATED 构建中,S_Init 检测到 sv.IsDedicated() 后只初始化音频源缓存就返回,不建设备、不混音:engine/audio/private/snd_dma.cpp:991-995。保留音频源缓存使 Host_GetSoundDuration 仍能给出真实时长(推断):engine/host.cpp:3676-3700。

无论哪种方式,服务端发声路径(EngineSoundServer.cpp、预缓存、SV_StartSound、svc_Sounds)都保留:engine/engine_inc.vpc:422;soundemittersystem.dll 也必须加载,因为 server.dll 在 DLLInit 中强制获取它:dedicated/sys_windows.cpp:269、game/server/gameinterface.cpp:773。

约束与边界#

  • 不变量:服务端只负责“谁该收到哪个声音事件”,可听性的距离/PAS 裁剪由游戏过滤器完成;引擎 BroadcastSound 只检查 active 与暂停(engine/sv_main.cpp:1747-1779)。客户端 SOS 栈、HRTF、DSP 都只在客户端执行,服务端不知道最终音量。
  • 失败路径:未预缓存的 wav 在服务端被丢弃(engine/sv_main.cpp:1430-1435);无效的 game sound 哈希在客户端被丢弃(engine/audio/private/snd_dma.cpp:7362-7366);不可靠声音超出每快照预算即丢弃(engine/sv_client.cpp:1102-1109);开始时不可听的短声音永不播放(engine/audio/private/snd_dma.cpp:6965-6985)。
  • 条件编译:DEDICATED、USE_AUDIO_DEVICE_V1(仅 Windows)、USE_SDL、_X360/_PS3 分支(XAudio、PS3 MP3 解码)均存在;本页以 Windows PC 客户端与专用服务器为主线,主机平台只做标记。
  • 证据不足:engine_ds 产物与 srcds 实际加载的 engine 模块之间的关系本快照无法闭环;vaudio_miles 与 Miles SDK 头文件缺失;仓库不含 game_sounds_manifest.txt、sound_operator_stacks.txt 等内容文件,具体 CS:GO 声音条目和操作符栈无法核对。
  • 本页未展开:DSP 预设算法(snd_dsp.cpp)、混音组/ducking(snd_mixgroups.cpp)、各 SOS 操作符实现、wave/ADPCM/MP3 解码器、VOX 句子语法、CEnvMicrophone、遮挡追踪、音乐播放与工具(soundedit、makesoundcache)。语音播放见 语音与视频;无线电与台词见 场景与响应规则。