CD Code Digest Hub /GTA V · v2

GTA V 游戏音频层(game/audio)

75 分钟阅读 16 个章节 1 张图

本页说明 src/dev_ng/game/audio(约 23 万行,GTA V 内部代号 “North Audio”,故类名前缀 na* / audNorth*)如何建立在 RAGE 音频引擎之上:audNorthAudioEngine 的初始化与每帧三线程时序、各类游戏 audEntity 派生及其与 CEntity 的绑定、GameObjects metadata、环境/遮挡、环境声、车辆引擎模型、动态混音、碰撞/脚步/武器音频与脚本接口。底层引擎(sound 层次、命令缓冲、混音器、虚拟化)见 rage-audio-engine.md;音乐、电台与语音对白见 music-radio-speech.md。

背景与相关页面#

相关源码文件(盘点)#

区域主要文件说明
引擎胶水northaudioengine.{h,cpp}、audioentity.{h,cpp}、gtaaudioentity.*、gameobjects.{h,cpp}(生成)、audiodefines.h初始化、线程、帧时序、metadata、调试
车辆vehicleaudioentity、caraudioentity、heli/plane/boat/train/bicycle/traileraudioentity、vehiclecollisionaudio、vehiclegadgetaudio、vehiclefireaudio、vehiclereflectionsaudioentity、vehicleengine/*车辆 LOD、引擎/颗粒合成、碰撞、附件
人物/武器pedaudioentity、pedfootstepaudio、speechaudioentity、speechmanager、weaponaudioentity、gunfireaudioentity、pedscenariomanager近战、呼吸、脚步、语音、枪声
世界物件collisionaudioentity、glassaudioentity、dooraudioentity、objectaudioentity、explosionaudioentity、fireaudioentity、weatheraudioentity、emitteraudioentity物理碰撞/破碎、门、爆炸、天气、静态/实体发声器
环境environment/environment*、environment/occlusion/*、environment/microphones.*、environment/audwarpmanager.*听者、环境组、遮挡、portal 路径
环境声ambience/ambientaudioentity、ambience/audambientzone、ambience/auddirectionalambience、ambience/water/*区域/规则/海岸线
混音与指挥dynamicmixer、dynamicmixmodules、superconductor、gunfightconductor*、vehicleconductor*、conductorintensitymap场景/补丁、战斗与驾驶的自动混音
音乐/电台/语音music/*、radio*、userradiotrackmanager、scannermanager、policescanner、speech*见 music-radio-speech.md
其他scriptaudioentity、frontendaudioentity、cutsceneaudioentity、replayaudioentity、voicechataudioentity、streamslot、waveslotmanager、asyncprobes、audiozones、audiogeometry脚本/前端/过场/回放/语音聊天/流槽

子目录规模:ambience 16 个文件约 1.76 万行,environment 20 个文件约 1.81 万行,music 34 个文件约 4200 行,vehicleengine 12 个文件约 4500 行,根目录 133 个文件约 15.9 万行(本次盘点用 Get-Content | Measure-Object -Line 统计)。最大的单文件依次是 speechaudioentity.cpp、vehicleaudioentity.cpp、scriptaudioentity.cpp、caraudioentity.cpp、ambience/ambientaudioentity.cpp。

1. audNorthAudioEngine:注册点与初始化#

1.1 gameSkeleton 注册#

阶段注册证据
INIT_COREaudNorthAudioEngine::Initsrc/dev_ng/game/Core/game.cpp:1723-1738
INIT_AFTER_MAP_LOADEDaudNorthAudioEngine::Init(当前为空分支,LoadMapData() 已注释)game.cpp:2146-2152、src/dev_ng/game/audio/northaudioengine.cpp:1121-1126
INIT_SESSIONaudNorthAudioEngine::Init(紧随 CStreaming::Init),以及末尾的 audNorthAudioEngine::DLCInitgame.cpp:2279-2288、game.cpp:2428
ShutdownSHUTDOWN_CORE / SHUTDOWN_WITH_MAP_LOADED / SHUTDOWN_SESSION 各一次game.cpp:2032、game.cpp:2242、game.cpp:2451
每帧更新REGISTER_UPDATE_CALL_TIMED(audNorthAudioEngine::StartUpdate),分别位于 Full(预算组 “Audio Update” 2.0 ms)、Simple(0.1 ms)、Commerce 三套主更新game.cpp:3058-3064、game.cpp:3111-3115、game.cpp:3244-3248
帧末同步CApp 主循环在 “AudioWait” 计时条中调用 audNorthAudioEngine::FinishUpdate()(启动自动读档或落地页激活时跳过)src/dev_ng/game/Core/app.cpp:1377-1385
异步扫描CGame 在构建 draw list 前的“异步工作”位置调用 StartEnvironmentLocalObjectListUpdate()game.cpp:3585-3592

另有 InitialiseAudioForInstall()(WinRT 流式安装流程提前初始化音频)直接调用 InitClass northaudioengine.cpp:780-783、src/dev_ng/game/system/StreamingInstall.winrt.cpp:372。

1.2 InitClass()(INIT_CORE)#

Init(INIT_CORE) 调用 InitClass() 并开启环境组更新 northaudioengine.cpp:1111-1120。InitClass 的主要步骤:

  1. 参数预设:-nogameaudio 直接退出;-audiodesigner 会自动设置 -rave=Sounds,GameObjects,Curves,Categories,ModularSynth,DynamicMixer,AudioConfig、跳过若干 chunk、开启控件与调试输出 northaudioengine.cpp:785-845。
  2. 音频物理内存:PC/Durango/Orbis 从 fwConfigManager 的 AudioHeap 池配置(单位 MB)取大小;PC 再加 13 MB 用户音乐与 4 MB 流式环境声补偿并用 sysMemPhysicalAllocate,主机从 MEMTYPE_GAME_VIRTUAL 分配;包装成 sysMemSimpleAllocator northaudioengine.cpp:847-878。
  3. InitializeAudio(sm_AudioController, ...) 初始化 RAGE 引擎与主线程 controller;随后 LoadSPData() 并常驻加载 dlc_gtao_speech.dat northaudioengine.cpp:880-888。
  4. 装上 g_NorthAudioThreadVerifier:当更新线程类型为主更新线程且 North 更新正在运行时创建 sound 即断言 northaudioengine.cpp:756-777、northaudioengine.cpp:905;wave slot 总量超过 MAX_WAVE_DATA(NG 平台 240 MB)报错 northaudioengine.cpp:473-479、northaudioengine.cpp:911-919。
  5. 加载 GameObjects:sm_MetadataMgr.Init("GameObjects", "audio:/config/game.dat", ...);为 FIXED_DLC_PACK_ORDER_COUNT = 20 个 DLC 包在 GameObjects、Sounds、DynamicMixer、Curves、Categories、ModularSynth、Speech 七个 metadata manager 中预留 chunk 顺序,保证无论何时挂载,覆盖关系固定 northaudioengine.cpp:921-941、northaudioengine.cpp:484-485。
  6. 预留一个 sound bucket 给流式 sound(也交给视频回放);g_EmitterAudioEntity.Init() 提前以捕获初始物件创建;controller 绑定 naEnvironmentGroupManager;设置语音类别索引范围(SetSpeechCategoryRange(GLOBAL_SPEECH 的索引, 该索引 + 12))northaudioengine.cpp:950-969。
  7. 两次“提交听者 + controller PreUpdate/Update”以激活 controller;实例化 audCategoryControllerManager 与 audInteractiveMusicManager;StartUserUpdateFunction() 把 UpdateAudioThread 注册为 RAGE 音频线程回调 northaudioengine.cpp:971-989、northaudioengine.cpp:1798-1818。
  8. 创建 “North Audio Update” 线程:栈 256 KB、PRIO_AUDIO_THREAD(= PRIO_HIGHEST)、CPU 5,并建 sm_RunUpdateSema / sm_UpdateFinishedSema northaudioengine.cpp:995-998、northaudioengine.cpp:481、src/dev_ng/game/system/ThreadPriorities.h:7。
  9. 初始化麦克风、枪声尾音、SuperConductor、玻璃/碰撞静态类、动态混音器、前端与回放实体 northaudioengine.cpp:1002-1019;设置默认动画事件处理器并注册 DLC 挂载器 northaudioengine.cpp:1054-1061。

1.3 INIT_SESSION#

会话初始化创建 BASE / GAME_WORLD 类别控制器并启动常驻场景 NORTHAUDIO_ENGINE_MIXING northaudioengine.cpp:1127-1140;仅首次(g_HasInitialisedStaticClasses,注释要求在加载完所有剧情内容之后)执行各实体类的 InitClass:环境组池与管理器、naAudioEntity、audStreamSlot、电台、门、脚本、人物、语音、各车辆类型及引擎组件、环境区、天气、爆炸、海岸线、火焰等 northaudioengine.cpp:1166-1209;然后 sm_OcclusionManager.Init()、sm_Environment.Init()、时间扭曲曲线以及电台、警用扫描、碰撞、过场、语音管理器、脚本、武器、玻璃、环境声、反射、交互音乐等全局实体的 Init() northaudioengine.cpp:1213-1236。DLCInit(INIT_SESSION) 在 DLC 脚本初始化后重建静态发声器、MACS 模型覆盖、环境区、车辆指挥与 DLC 电台 northaudioengine.cpp:1258-1268。SHUTDOWN_SESSION 对应地关闭前端、天气、玻璃、交互音乐、环境组、流槽等 northaudioengine.cpp:1270-1298。

1.4 DLC 与 SP/MP 数据集#

RegisterDataFileMounters() 向 CDataFileMount 注册 7 种数据文件类型:AUDIO_SOUNDDATA、AUDIO_CURVEDATA、AUDIO_GAMEDATA、AUDIO_SPEECHDATA、AUDIO_DYNAMIXDATA、AUDIO_SYNTHDATA、AUDIO_WAVEPACK(都用 eDFMI_UnloadFirst)northaudioengine.cpp:1774-1796。

  • metadata 类挂载器 audMetadataDataFileMounter::LoadDataFile 从文件名推导 chunk 名并 LoadMetadataChunk;若该 chunk 正在延迟卸载则取消卸载;GameObjects chunk 中若含 RADIO_STATIONS_DLC 则合并电台列表;DynamicMixer chunk 加载后初始化 mix group northaudioengine.cpp:1503-1557。
  • 卸载是延迟的:先登记,下一帧 Update() 中对 Sounds 类型先 StartUnloadingChunk(使引用该 metadata 范围的 sound 自行停止),自旋等待 RAGE 引擎至少完成一帧,再真正 UnloadMetadataChunk;合成器 chunk 走 synthSynthesizer::UnloadMetadataChunk northaudioengine.cpp:1559-1653。
  • wave pack 挂载器用 pack 名前 8 个字符注册 audWaveSlot::SetSearchPath,使 .awc 解析到 DLC 设备 northaudioengine.cpp:1671-1700。
  • SP/MP 切换:LoadMPData 卸载 SinglePlayer sounds chunk(保留缓冲),在同一块预分配缓冲中加载 dlc_gtao_sounds.dat,并加载 dlc_gtao_game.dat、dlc_gtao_mix.dat;LoadSPData 反向操作并重建静态发声器与环境区 northaudioengine.cpp:2331-2350、northaudioengine.cpp:2403-2429。是否切换由 UpdateDataSet() 根据 sm_IsMPDataRequested 或脚本标志 LoadMPData 决定 northaudioengine.cpp:2257-2264。

2. 每帧时序:主线程、North Audio Update 线程、RAGE 音频线程#

GTA 在 RAGE 的两个线程(音频引擎线程、混音线程)之外又加了一个游戏侧更新线程。三者都是混音器命令缓冲的客户端:主线程("GameThread")、RAGE 音频线程("AudioThread")、North 线程("NorthAudioUpdateThread",8 KB)northaudioengine.cpp:2622-2630。

证据:StartUpdate 中等待命令缓冲与暂停判定 northaudioengine.cpp:1899-1924、遮挡更新在主线程 northaudioengine.cpp:2002-2005、主线程侧更新与发信号 northaudioengine.cpp:2076-2114;North 线程循环 northaudioengine.cpp:2631-2686;FinishUpdate northaudioengine.cpp:2116-2181;RAGE 音频线程见 rage-audio-engine.md 第 3.2 节。

要点:

  • sm_RunUpdateInSeperateThread 缺省为 true northaudioengine.cpp:356;为 false 时 Update() 直接在主线程的 StartUpdate 中运行 northaudioengine.cpp:2076-2079。
  • 因此 North 线程的 Update() 与主线程在 StartUpdate 之后的工作并行;StartUpdate 在 Full 主更新中注册于 “Render Update” 组之后 game.cpp:3056-3064,FinishUpdate 在 CApp 主循环中靠后的 “AudioWait” 处等待。
  • 遮挡的世界探测必须在主线程上发起(注释:访问 RegdEnt 与 glass frag 数据),探测结果在 North 线程上处理——naOcclusionManager 头文件把方法明确分为 “Game Thread” 与 “Audio Game Thread” 两段 src/dev_ng/game/audio/environment/occlusion/occlusionmanager.h:87-170。
  • 商店模式(COMMERCE_CONTAINER)下 North 线程只跑 MinimalUpdate():处理延迟 sound/批量请求、不更新环境组的 controller 更新、动态混音与类别控制器,然后提交 northaudioengine.cpp:2663-2713。
  • RAGE 音频线程上的 UpdateAudioThread 只做 AUDCATEGORYCONTROLLERMANAGER.Update() northaudioengine.cpp:1810-1818。

2.1 North 线程上的 Update() 顺序#

northaudioengine.cpp:2925-3475(只列主干):

  1. 非暂停时 sm_TimeInMs = fwTimer::GetTimeInMilliseconds();根据 fwTimer::GetTimeWarpActive()、回放编辑器状态计算慢动作 northaudioengine.cpp:2945-2956。
  2. sm_AudioController.PreUpdate(sm_TimeInMs)(所有实体 PreUpdateService)→ audEntity::ProcessBatchedSoundRequests → 车辆/火车激活范围 northaudioengine.cpp:3352-3363。
  3. 写入全局 sound 变量:Game.Clock.*(星期/时/分/秒/日/月/每分钟秒数/小数小时/时间扭曲)、Game.Player.Position.*/Speed/Health/MaxHealth、Game.Microphone.Position.*/FirstPerson、Game.Water.CameraDepth、Game.VideoEditor.*、Game.Player.InteriorRatio、Game.Mode.IsCopsAndCrooks northaudioengine.cpp:3365-3415。主线程 StartUpdate 另写 Game.Lighting.* 曝光/太阳可见度 northaudioengine.cpp:2096-2099。这些变量可被 metadata 中的变量驱动 sound 直接读取。
  4. 门/车辆/汽车/语音的 UpdateClass;火焰、语音管理器、反射;非暂停时发声器、警用扫描、环境声;人物场景管理器;audStreamSlot::UpdateSlots northaudioengine.cpp:3417-3434。
  5. sm_Environment.Update() → 解冻麦克风 → sm_OcclusionManager.UpdateOcclusionMetrics() → sm_DynamicMixer.Update() → sm_AudioController.Update()(环境组更新 + requested settings 提交)→ UpdateCategories() → g_AudioEngine.CommitGameSettings(sm_TimeInMs) → UpdateUserMusicState() northaudioengine.cpp:3441-3466。

2.2 暂停、计时器与慢动作#

Pause() 在前端场景完全生效(或用户/调试暂停)时暂停 RAGE 计时器 0(常规)与 5(慢动作),并通知引擎游戏已暂停(影响 PC 上音频线程帧限速)northaudioengine.cpp:4322-4348。暂停判定在 StartUpdate 中依据 fwTimer::IsUserPaused/IsGamePaused northaudioengine.cpp:1914-1924。慢动作通过 ActivateSlowMoMode/DeactivateSlowMoMode(SlowMoSettings GameObject)与 TIME_WARP_TO_PITCH 等曲线实现 northaudioengine.cpp:2715-2816、northaudioengine.cpp:1216-1220。

2.3 跨线程创建 sound 的规则:延迟 sound#

North 线程运行期间,主线程若要为某 CEntity 创建带 environment group 或 tracker 的 sound,不能直接创建(注释:controller 可能在引用建立前就删掉这些对象)。naAudioEntity::CreateDeferredSound 把请求(metadata 引用、init 参数、RegdConstEnt、是否需要环境组/tracker)放入 sm_DeferredSoundList(每帧最多 sm_MaxNumDeferredSounds = 192,满则丢弃),在 FinishUpdate 的 ProcessDeferredSounds() 中统一创建;若届时音频实体已删除而又需要 CEntity,则丢弃并告警 src/dev_ng/game/audio/audioentity.h:76-112、src/dev_ng/game/audio/audioentity.cpp:207-286。武器开火事件同理先缓存(每帧 ≤20),在 FinishUpdate 中 ProcessCachedWeaponFireEvents() src/dev_ng/game/audio/weaponaudioentity.h:30、northaudioengine.cpp:2152-2171。

3. 游戏侧 audEntity 派生族谱#

层次:rage::audEntity → rage::fwAudioEntity(增加动画事件处理,见 music-radio-speech.md 的 fwaudio 一节)→ naAudioEntity(环境组请求、延迟 sound、缓存的内部位置)→ 各具体实体 audioentity.h:46-114。少数类直接派生自 audEntity(audGtaAudioEntity、audSpeechManager)或 fwAudioEntity(audVoiceChatAudioEntity)。

实体基类持有方式 / 职责证据
audPedAudioEntitynaAudioEntityCPed 内嵌成员 m_PedAudioEntity;近战挥击/命中、开火、呼吸/心跳、铃声、溅水、坠落、受击、车辆碰撞;内含 audPedFootStepAudiosrc/dev_ng/game/Peds/ped.h:2740、src/dev_ng/game/audio/pedaudioentity.h:87-225、pedaudioentity.h:474
audSpeechAudioEntitynaAudioEntityCPed 中按需 rage_new 的指针 m_pSpeechAudioEntityped.h:2874、src/dev_ng/game/Peds/Ped.cpp:1870
audVehicleAudioEntity 及 audCarAudioEntity / audHeliAudioEntity / audPlaneAudioEntity / audBoatAudioEntity / audTrainAudioEntity / audBicycleAudioEntity / audTrailerAudioEntitynaAudioEntityCVehicleFactory 按车辆类型 rage_new(直升机/旋翼机/飞艇→Heli,船/潜艇→Boat,汽车/摩托/四轮/两栖→Car,未知类型回退基类并告警)src/dev_ng/game/vehicles/VehicleFactory.cpp:2868-2908、src/dev_ng/game/vehicles/vehicle.h:1790-1791
audObjectAudioEntity / audDoorAudioEntitynaAudioEntityCObject::m_AudioEntity、CDoor::m_DoorAudioEntity 内嵌src/dev_ng/game/objects/object.h:527、src/dev_ng/game/objects/door.h:306
audCollisionAudioEntity、audGlassAudioEntity、audExplosionAudioEntity、audFireAudioEntity、audWeaponAudioEntity、audGunFireAudioEntity、audWeatherAudioEntitynaAudioEntity全局单例(g_CollisionAudioEntity 等),按事件触发例如 collisionaudioentity.h:306、weaponaudioentity.h:339
audAmbientAudioEntity、audRadioAudioEntity、audScriptAudioEntity、audFrontendAudioEntity、audCutsceneAudioEntity、audReplayAudioEntity、audDynamicMixer、audInteractiveMusicManagernaAudioEntity全局单例ambience/ambientaudioentity.h:94、scriptaudioentity.h:506、dynamicmixer.h:291、music/musicplayer.h:48
audVehicleReflectionsEntitynaAudioEntity车辆近墙反射(推断其使用 RefSrc/RefOut submix,未逐行核对)vehiclereflectionsaudioentity.h:28
audVoiceChatAudioEntityfwAudioEntity网络语音聊天voicechataudioentity.h:50

所有实体类型名还登记在 AudioConfig.xml 的 EntityTypes 中,供 Visualizer 工具显示 src/dev_ng/game/audio/AudioConfig.xml:1-29。动画事件:fwAudioEntity::SetDefaultAnimHandler 被设为 naAnimHandler,后者在 HandleEvent 中处理动画音频标签 northaudioengine.cpp:1056、northaudioengine.cpp:5694。

4. GameObjects metadata(game.dat)#

gameobjects.h 与 sounddefs.h 一样由 metadata 编译器生成:GAMEOBJECTS_SCHEMA_VERSION 151、AUD_NUM_GAMEOBJECTS 121(不含抽象类型)src/dev_ng/game/audio/gameobjects.h:1-38。编译工具位于 rage/base/tools/audio/audmetadatacompiler*(及 tools_ng/bin/audio/metadatacompiler),细节见工具链页。类型覆盖游戏音频的全部可调数据,例如:

类别代表类型证据
车辆CarAudioSettings、VehicleEngineAudioSettings、GranularEngineAudioSettings、ElectricEngineAudioSettings、HeliAudioSettings、PlaneAudioSettings、BoatAudioSettings、TrainAudioSettings、VehicleCollisionSettingsgameobjects.h:1406、gameobjects.h:1590、gameobjects.h:6270
材质/碰撞/脚步CollisionMaterialSettings、ModelAudioCollisionSettings、ShoeAudioSettings、FootstepSurfaceSounds、ModelFootStepTuninggameobjects.h:1743、gameobjects.h:4490
环境/内部AmbientZone、AmbientRule、AmbientZoneList、AmbientBankMap、DirectionalAmbience、InteriorSettings、InteriorRoom、ShoreLine*AudioSettings、MicrophoneSettings、ReflectionsSettings、PortalSettingsgameobjects.h:3768、gameobjects.h:4167、gameobjects.h:5872
音乐/电台/语音InteractiveMusicMood、MusicEvent、*Action、*Constraint、RadioStationSettings、RadioStationTrackList、SpeechContext、PedVoiceGroups、Scanner*gameobjects.h:5187、gameobjects.h:3311
其他WeaponSettings、ExplosionAudioSettings、DoorAudioSettings、StaticEmitter、EntityEmitter、SlowMoSettings、AlarmSettings、MacsModelOverrideListgameobjects.h:2838、gameobjects.h:1976

查询接口 audNorthAudioEngine::GetObject<T>(name|hash|metadataRef) 直接转发给 GameObjects 的 audMetadataManager src/dev_ng/game/audio/northaudioengine.h:326-344;非模板版 GetObject(hash, TYPE_ID) 会比对对象首字节的类型 ID,不符返回 NULL 并报错 northaudioengine.cpp:4427-4439。audGameObjectManager 实现 audObjectModifiedInterface,在 __BANK 下接收 RAVE 的查看/覆盖/编辑/试听回调 northaudioengine.cpp:5571-5694。

5. 环境:naEnvironment、环境组、遮挡与麦克风#

5.1 naEnvironment#

naEnvironment(sm_Environment)是 GTA 的世界声学模型,提供:回声估计(GetBestEchoForSourcePosition、CalculateEchoDelayAndAttenuation)、本地环境物体扫描(异步 LocalObjectsList_StartAsyncUpdate/EndAsyncUpdate,最多 64 个“有趣物体”和 64 棵树)、震耳(deafening)效果、内部空间判断与 InteriorSettings/InteriorRoom 查询、雨声方向混合,以及一个 100×100、每格 150 m 的“音频世界扇区”网格(从 −7500 起),每格记录高速公路节点数、最高建筑、建筑/树数量、是否水域,用于建成度(built-up factor)与方向性环境声 src/dev_ng/game/audio/environment/environment.h:40-146、environment.h:237-364。-calculateAudioWorldSectors / -inGameAudioWorldSectors 用于生成/在游戏中计算扇区数据 northaudioengine.cpp:1023-1033。

5.2 环境组(environment group)#

naEnvironmentGroup : audEnvironmentGroupInterface 是挂在实体/sound 上的“声源环境度量”容器:内部位置(可从 CEntity、fwInteriorLocation、CInteriorInst+房间、或 GameObject 哈希设置)、遮挡值、是否使用遮挡路径(路径位置与距离)、portal 遮挡开关等;池类为 FW_REGISTER_LOCKING_CLASS_POOL src/dev_ng/game/audio/environment/environmentgroup.h:53-138。RAGE controller 在 Update 中调用 naEnvironmentGroupManager::UpdateEnvironmentGroups(list, g_MaxEnvironmentGroupsToUpdate = 10, t):先强制更新新建组、玩家(或玩家车辆)组与内部位置变化的组(即使超出预算),再按优先级(近、快者优先)更新,余量用于普通更新,链接组“几乎免费”地共享结果 src/dev_ng/game/audio/environment/environmentgroupmanager.cpp:48-92、src/dev_ng/rage/base/src/audioengine/controller.cpp:40。

5.3 遮挡:探测 + portal 路径#

naOcclusionManager 组合两种方法:听者与声源都在室外时用基于探测的包围盒/区域遮挡;处于不同房间时用 portal 路径遮挡 occlusionmanager.h:83-84。

  • 探测(主线程):围绕听者按 9 个仰角发射异步 LOS 探测——水平 24 个方位(每帧 3 条,8 帧覆盖一周)、中间 4 个仰角×8 方位(每帧 4 条)、近垂直 4 个仰角×4 方位(每帧 2 条),记录最近的“遮挡性材质”交点(例如忽略铁丝网);遮挡区为 8 方位×4 仰角 occlusionmanager.h:24-47、occlusionmanager.h:103-117。
  • 处理(North 线程):UpdateOcclusionMetrics() 汇总探测数据,环境组按声源相对方位取区域平均;ComputeOcclusionPathMetrics() 根据 portal 阻挡和路径夹角插值出距离、最终声像位置与遮挡值 occlusionmanager.h:166-199。
  • portal 路径数据是离线预构建的:naOcclusionPathNode 是路径树节点,子节点记录经由哪个 portal 到达;注释说明路径在 __BANK 构建中通过 RAG 的 “ComputePortalPaths” 按钮生成并写文件 src/dev_ng/game/audio/environment/occlusion/occlusionpathnode.h:95-98。数据格式由 parser schema naOcclusionInteriorMetadata { PortalInfoList, PathNodeList } 定义,portal 信息含目标内部/房间与 portal 上的实体(门/玻璃、最大遮挡)src/dev_ng/game/audio/environment/occlusion/occlusioninteriormetadata.psc:1-46。运行时按内部空间从 g_fwMetaDataStore(索引来自 CInteriorProxy::GetAudioOcclusionMetadataFileIndex())取出并装入路径节点池;池空间不足(例如角色切换时旧内部未卸载)则推迟加载 src/dev_ng/game/audio/environment/occlusion/occlusionmanager.cpp:2201-2251。构建相关命令行 -audOcclusionBuildEnabled/Start/Continue/List/TempDir/AssetsDir、-audDisablePortalOcclusion occlusionmanager.cpp:50-58;离线工具为 rage/base/tools/audio/audOcclusionBuilder。

5.4 麦克风与听者#

naMicrophones 根据当前相机与 MicrophoneSettings GameObject 计算两个矩阵:音量麦克风(决定衰减)和声像麦克风(决定 panning),并设置到 RAGE audEnvironment 的听者;例如默认把音量麦克风沿相机前方移动 micLength,车内相机会投影到车辆前方;脚本可通过 ScriptForceMicPosition 标志强制位置(并周期性告警)src/dev_ng/game/audio/environment/microphones.cpp:600-646、microphones.cpp:184-187。环境更新依赖麦克风冻结状态,因此 Update() 在环境更新后才解冻麦克风 northaudioengine.cpp:3441-3445。

environment/audwarpmanager 是 __BANK 调试传送工具(等待场景加载、最长 15 s),在 FinishUpdate 的 __BANK 段更新 src/dev_ng/game/audio/environment/audwarpmanager.h:12-45、northaudioengine.cpp:2149。

6. 环境声(ambience)#

  • audAmbientZone:由 AmbientZone GameObject 初始化,区分“激活区”(何时启用)与“定位区”(声源放在哪),可为球或盒并可旋转;每帧 Update(t, listenerPos, isRaining, loudSoundExclusionRadius, hours) 评估其 AmbientRule(每区同时播放 ≤ g_MaxPlayingRules = 16 条规则),并根据时间/天气动态申请或释放 wave slot(UpdateDynamicWaveSlots、CalculateDynamicBankUsage);也可链接到内部空间、计算海岸线 src/dev_ng/game/audio/ambience/audambientzone.h:18、audambientzone.h:68-127。bank 选择通过 AmbientBankMap(含 DLC bank map)以及 DynamicBankListBuilderFn(遍历 sound 层次统计所需 bank)完成 audambientzone.h:174-175、audambientzone.h:369。区域用 fwQuadTreeNode 管理(ZonesQuadTreeUpdateFunction)audambientzone.h:294。
  • audAmbientAudioEntity::Update:处理粒子特效注册的音频请求(每帧 ≤ g_MaxRegisteredAudioEffects = 128,按读写索引双缓冲,主线程每帧 IncrementAudioEffectWriteIndex);更新区域、人群喧哗(walla)与 walla 语音、警报(≤6 个)、世界高度、流式环境声;听者跳变超过 20 m(距离平方 400)时强制刷新四叉树 src/dev_ng/game/audio/ambience/ambientaudioentity.h:30-32、ambientaudioentity.h:200、src/dev_ng/game/audio/ambience/ambientaudioentity.cpp:2068-2100、northaudioengine.cpp:2081-2085。
  • 方向性环境声 / 海岸线:audDirectionalAmbience(DirectionalAmbience 对象,结合 naEnvironment 的方向性建成度、车辆/行人密度);ambience/water/ 下 audShoreLine 派生出 Pool/River/Ocean/Lake 四种水岸,__BANK 下带编辑器(创建/编辑/序列化)ambientaudioentity.h:132-266。
  • 发声器:audEmitterAudioEntity 管理静态发声器(StaticEmitterList,≤700)与实体发声器(≤160),静态发声器在会话/SP 数据加载时重建 src/dev_ng/game/audio/emitteraudioentity.h:25-28。
  • 遗留:audiozones.{h,cpp} 中的 CAudioZones(注释称 2004 年创建的盒/球音区)仍被 gamelogic.cpp 调用 CAudioZones::Update src/dev_ng/game/audio/audiozones.h:6-37、src/dev_ng/game/control/gamelogic.cpp:701。audiogeometry.{h,cpp}(基于 fwaudio/audmesh 的音频网格)在 northaudioengine.cpp 中的 include 已被注释,未发现其他引用,判定为未启用 northaudioengine.cpp:10。

7. 车辆音频与 vehicleengine#

7.1 车辆音频 LOD#

audVehicleType 区分 TRAIN/PLANE/HELI/CAR/BOAT/BICYCLE/TRAILER;每辆车有 LOD:REAL(完整引擎与路噪)、DUMMY(无引擎,用 NPC 路噪)、SUPER_DUMMY(基本不播放但不阻塞一次性效果)、DISABLED src/dev_ng/game/audio/vehicleaudioentity.h:43-64。

  • REAL 车辆数受限于车辆 submix 数:g_MaxVehicleSubmixes = 8(引擎路由数),注释 “Can't have more real vehicles than submixes”;DUMMY 上限 8(每象限 2);颗粒引擎上限 g_MaxGranularEngines = 5 src/dev_ng/game/audio/vehicleaudioentity.cpp:217-226、src/dev_ng/game/audio/caraudioentity.cpp:231。
  • 这些上限通过自适应激活半径实现:UpdateActivationRanges() 每帧统计范围内车辆数,多于上限则按时间步缩小 sm_ActivationRangeScale / sm_GranularActivationRangeScale / 各象限 sm_DummyRangeScale,少于则放大,并分别夹在 [0.1, 3]、[0.02, 6]、[0.1, 30] vehicleaudioentity.cpp:6934-6985。
  • LOD 切换带迟滞:需在目标 LOD 停留超过 g_MinTimeAtDesiredLOD,且在当前 LOD 已停留 g_MinTimeAtCurrentLOD(升级、优先车辆或玩家车辆“饥饿”时例外);REAL 车辆需先获取引擎 wave slot(失败则玩家车标记为 starving)vehicleaudioentity.cpp:4450-4500。REAL 车辆占用 V-Eng/V-Ex submix(见 rage-audio-engine.md 第 8.2 节)。

7.2 引擎模型#

audVehicleEngine 状态机 STARTING / STOPPING / ON / OFF,另有自动熄火(AUTOSHUTOFF_INACTIVE/ACTIVE/RESTARTING)与 KERS(BOOSTING/RECHARGING/OFF)src/dev_ng/game/audio/vehicleengine/vehicleengine.h:28-96。组成部件:audVehicleTransmission(换挡声)、audVehicleTurbo(涡轮啸叫 + 泄压阀;RevsToTurboBoost、TurboBoostToVol/Pitch 三条曲线)、audVehicleElectricEngine、传统循环式 audVehicleEngineComponent ×2(引擎/排气)与 audGranularEngineComponent ×2(引擎/排气)vehicleengine.h:180-235、src/dev_ng/game/audio/vehicleengine/turbo.h:21-52。

颗粒引擎(GranularEngineAudioSettings):每个组件按质量档(audGranularMix::GrainPlayerQualityMax)持有 audGranularSound,通道分 ACCEL/DECEL/IDLE;另有两路同步合成器 sound(GENERAL、DAMAGE)src/dev_ng/game/audio/vehicleengine/granularenginecomponent.h:26-100。Update 把转速映射为 revRatio = (revs - 0.2) * 1.25,按 metadata 的 PreSubmix 音量与转速/油门修正计算增益,throttle ≤ 0.1 且低转速时淡入怠速通道;对每个正在播放的质量档调用 granularSound->SetX(revRatio * multiplier);限转器通过 SetGrainSkippingEnabled(按 RevLimiterGrainsToPlay/Skip/VolumeCut 跳 grain)并锁定音高实现 src/dev_ng/game/audio/vehicleengine/granularenginecomponent.cpp:499-618。底层 grain 播放器见 rage-audio-engine.md 第 9 节。

7.3 车辆声部与每帧更新#

audVehicleAudioEntity 每帧先 PopulateVehicleVariables / PopulateVehicleLODVariables 收集一份 audVehicleVariables(转速、油门、速度、轮胎状态等),再由各声部函数消费:路噪(UpdateRoadNoiseSounds、UpdateFastRoadNoise、UpdateNPCRoadNoise、高速路接缝 UpdateFreewayBumps、搓板路 UpdateRidgedSurfaces)、轮胎(CalculateWheelMaterials、UpdateWheelDetail、UpdateSkids、UpdateWheelSpin、UpdateWheelWater、UpdateWheelDirtyness、UpdateSurfaceSettleSounds)、车身(UpdateClatter、UpdateCabinTone、UpdateClothSounds、UpdateCaterpillarTracks)、水(UpdateWater、UpdateWaveImpactSounds、UpdateDrowning、UpdateSubmersible)、天气(UpdateRain)、警笛与告警(UpdateSiren、UpdateWarningSounds),以及子类钩子 UpdateVehicleSpecific 与车载 DSP(UpdateDSPEffects)src/dev_ng/game/audio/vehicleaudioentity.h:916-991、vehicleaudioentity.h:596。附件(炮塔、叉车、拖车臂、抓钩、挖掘臂、敞篷顶、电磁铁、动态尾翼)以 audVehicleGadget 列表挂在实体上,火焰声部为 audVehicleFireAudio vehicleaudioentity.h:1121-1123、src/dev_ng/game/audio/vehiclegadgetaudio.h:56-340。汽车子类 audCarAudioEntity 内嵌 audVehicleEngine,另有氮气、起步轰油、补油(rev blip)、高速风噪、空中旋转、液压悬挂等循环声 src/dev_ng/game/audio/caraudioentity.h:276-308。

8. 动态混音与“指挥家”#

  • audDynamicMixer(游戏侧):StartScene(name|hash|MixerScene*|metadataRef, audScene** ref, script, startOffset) 启动场景;场景(audScene,同时 ≤32 个)含若干 audDynMixPatch(≤64 个,每个最多控制 32 个类别控制器),场景有可被脚本设置的变量(SetVariableValue)、淡入淡出与“完全应用”判定;ApplyPatch 可把补丁作用于某个 mix group;AddEntityToMixGroup/RemoveEntityFromMixGroup/MoveEntityToMixGroup 把实体(及其 sound)放入 mix group(底层即 RAGE 的 16 个动态类别层次)src/dev_ng/game/audio/dynamicmixer.h:72-372。naAudioEntity 的环境组请求类型含 MIXGROUP/PLAYER_MIXGROUP,这表明 mix group 归属挂在环境组上 audioentity.h:59-65(推断:未逐行核对 naEnvironmentGroup 中 mix group 的存储)。
  • 引擎常驻场景 NORTHAUDIO_ENGINE_MIXING 用变量 radioVol、radioFrontendVol、djFrontendVol、posRadioVol 等控制电台混音 northaudioengine.cpp:1135-1139;慢动作场景用 onApply/offApply northaudioengine.cpp:3340-3350。
  • SuperConductor(USE_CONDUCTORS 1):audSuperConductor 汇总玩家状态(开火、在车内、潜行、在室内、死亡、水下、切换角色)并向 audGunFightConductor、audVehicleConductor 发送消息/命令;这两个指挥家是 fwFsm 状态机,各自带“动态混音”和“假场景”子状态机,用于根据战斗/驾驶强度自动调整混音与补充音效 src/dev_ng/game/audio/superconductor.h:24-73、gunfightconductor.h:19、vehicleconductor.h:32。ProcessUpdate() 在 FinishUpdate 中(非暂停时)执行 northaudioengine.cpp:2152-2154。
  • 代码控制的全局场景(UpdateCategories,North 线程):游戏挂起时启动 GAME_SUSPENDED_SCENE;窗口失焦且菜单选项 “失焦静音” 打开时启动 MUTE_ALL_SCENE;把遮挡管理器的全局遮挡阻尼写到 BASE 类别;暂停/淡出时通过 FRONTEND_SCENE 的 fade、musicApply、posRadioVol 变量只压低游戏世界而保留前端与电台;联机大厅 MP_LOBBY_SCENE、回放编辑器 REPLAY_EDITOR_PLAYBACK_SCENE、第一人称 FIRST_PERSON_MODE_SCENE;并按暂停菜单的音乐/音效音量设置(SP 与 MP 分别读取)换算类别音量 northaudioengine.cpp:3590-3831。

9. 碰撞、脚步、武器与物理音频#

  • 碰撞:物理模块处理接触流形时,先做 CPhysics::ProcessCollisionVfxandSfx,再调用 g_CollisionAudioEntity.UpdateManifold(vfxColnInfo, manifold, breakableA, breakableB) src/dev_ng/game/physics/physics.cpp:3284-3286。UpdateManifold 先把玻璃事件交给 audGlassAudioEntity,再过滤无效/附着/同一 ped 不同部位等情况 src/dev_ng/game/audio/collisionaudioentity.cpp:5599-5656;材质来自 CollisionMaterialSettings。其他入口:ReportFragmentBreak、ReportUproot、ReportMeleeCollision、ReportHeliBladeCollision、ReportBulletHit,以及 MACS 模型覆盖 src/dev_ng/game/audio/collisionaudioentity.h:325-335。延迟的冲击在 FinishUpdate 中 TriggerDeferedImpacts() northaudioengine.cpp:2163。车辆专用碰撞在 vehiclecollisionaudio。
  • 脚步:audPedFootStepAudio::AddFootstepEvent 由 CPedFootstepHelper(scuff/lift 等事件)和动画回调驱动,回放系统也会重放 src/dev_ng/game/Peds/PedFootstepHelper.cpp:1236、PedFootstepHelper.cpp:1303、src/dev_ng/game/control/replay/Audio/FootStepPacket.cpp:36;鞋/地表数据来自 ShoeAudioSettings、FootstepSurfaceSounds 等 GameObject。
  • 武器:audWeaponAudioEntity 每帧最多缓存 20 个开火事件、64 个抛射物事件、32 个旋转/引信事件 weaponaudioentity.h:30-57;枪声尾音由 audGunFireAudioEntity 处理(USE_GUN_TAILS);audNorthAudioEngine::RegisterLoudSound 在响声距音量听者足够近时记录时间、通知发声器实体,并(非玩家开枪且非联机时)让 SuperConductor 停止 quiet scene northaudioengine.cpp:4350-4375。

10. 脚本音频接口(概要)#

脚本命令在 src/dev_ng/game/script/commands_audio.cpp 中注册(共 388 处 SCR_REGISTER_*),命令表与注册机制见 ../14-script-runtime/natives-and-commands.md。主要映射:

命令族实现证据
REQUEST_SCRIPT_AUDIO_BANK / HINT_* / RELEASE_*g_ScriptAudioEntity.RequestScriptBank/HintScriptBank/ScriptBankNoLongerNeeded(可选网络同步与玩家位掩码)commands_audio.cpp:713-760
GET_SOUND_ID / PLAY_SOUND_FROM_ENTITY 等脚本 sound 槽(AUD_MAX_SCRIPT_SOUNDS = 100),可网络同步commands_audio.cpp:772-800
TRIGGER_MUSIC_EVENT 等g_InteractiveMusicManager.GetEventManager().TriggerEventcommands_audio.cpp:3109-3127
START_AUDIO_SCENE / STOP_AUDIO_SCENEg_ScriptAudioEntity.StartScene/StopScenecommands_audio.cpp:3137-3148
对话、电台、环境语音、SET_AUDIO_FLAG、流见 music-radio-speech.md注册表 commands_audio.cpp:3927-4299

audScriptAudioEntity 容量:脚本 bank 槽 11(可用 10,网络 2)、脚本 sound 100、同时登记脚本 20、对话 60 行/36 个说话人、预载语音槽 4 src/dev_ng/game/audio/scriptaudioentity.h:22-38。

11. 流槽与 wave slot 的游戏侧管理#

audStreamSlot 管理少量流式 wave slot:5 个物理槽 RADIO1_A/B、RADIO2_A/B、RADIO3_A,加 2 个虚拟槽;客户端按优先级(IDLE < 实体发声器远/近 < 静态环境发声器 < 网络发声器 < 脚本车辆 < 前端菜单 < 脚本 < 脚本环境发声器 < 音乐 < 玩家电台 < 回放音乐 < 过场)抢占,被抢占者通过 stopCallback/hasStoppedCallback 让出 src/dev_ng/game/audio/streamslot.h:12-70、src/dev_ng/game/audio/streamslot.cpp:19-30。audStreamSlot::UpdateSlots 在 North Update() 中执行 northaudioengine.cpp:3434。audWaveSlotManager(waveslotmanager.*)为实体提供成组 wave slot 的分配辅助(本页未展开)。

12. 构建与平台差异#

  • audiodefines.h:NA_RADIO_ENABLED 1、NA_POLICESCANNER_ENABLED 1 src/dev_ng/game/audio/audiodefines.h:4-19;USE_CONDUCTORS 1。
  • 音频堆与 wave 数据预算:NG 平台从 AudioHeap 池配置读取;PC 额外 +17 MB(见 1.2)。
  • __BANK:RAVE 远程编辑、试听(AuditionSound)、GameObject 覆盖回调、调试绘制、海岸线编辑器、遮挡路径构建、音频世界扇区生成。
  • GTA_REPLAY:StartUpdate 与 North 线程在回放捕获时手动驱动 RAGE 音频帧与混音器(TriggerUpdate + WaitForAudioFrame/WaitOnMixBuffers)northaudioengine.cpp:1899-1908、northaudioengine.cpp:2650-2656。
  • PS3/Xenon 分支(耳机检测、MAX_WAVE_DATA 26/24 MB 等)在 dev_ng 中仍保留但非目标平台。

13. 约束与边界行为#

  • 主线程不得在 StartUpdate 与 FinishUpdate 之间直接创建 sound(__ASSERT 下由 thread verifier 断言);需要环境组/tracker 的 sound 走延迟列表,满 192 则丢弃。
  • North 线程的 Update() 必须在 FinishUpdate() 前完成;FinishUpdate 阻塞等待 sm_UpdateFinishedSema,因此音频更新超时会直接拖长主线程帧(计时条 “AudioWait”)。
  • DLC metadata 卸载要求先让引用该 chunk 的 sound 停止并等待引擎跑过一帧;卸载过程中如果再次挂载同名 chunk 会取消卸载。
  • REAL 车辆数被 submix 数硬性限制为 8;超出时通过缩小激活半径和 LOD 迟滞收敛,而非排队。
  • 内部遮挡数据依赖 g_fwMetaDataStore 中对应内部的 .pso 已流入;未加载时 portal 路径不可用(推断:退化为探测/区域遮挡,未逐行确认回退分支)。

14. 未决问题 / 证据不足#

  • UpdateCategories() 仅概述了场景与滑条部分;UpdateVehicleCategories() 未分析。
  • naEnvironment::Update 内部的混响/回声计算、audEnvironmentSound 如何消费 naEnvironmentGroup 的度量只做了概述。
  • 直升机/飞机/船/火车子类的专有声部(旋翼、喷气、船体拍水、轨道)以及各声部内部的曲线与参数映射未展开;vehiclegadgetaudio 的各附件仅列出类名。
  • 遮挡探测的材质判断、CalculateExteriorBlockageForPortal 公式未展开。
  • audWaveSlotManager、asyncprobes、entitytracker、hashactiontable、pedscenariomanager 等辅助模块未逐一核对。