GTA V 游戏音频层(game/audio)
本页说明 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。
背景与相关页面#
gameSkeleton注册机制:../01-runtime/game-skeleton-and-main-loop.md;主循环与CApp:../01-runtime/startup-and-app-fsm.md。- 实体侧:../06-peds/ped-architecture.md、../08-vehicles/vehicle-architecture.md、../05-world/world-and-entities.md。
- 内部空间/portal(遮挡路径依赖):../05-world/interiors-and-portals.md。
- 碰撞来源:../09-physics/game-physics-and-ragdoll.md;武器:../21-weapons-camera/weapons.md。
- 脚本原生命令总表:../14-script-runtime/natives-and-commands.md。
- 回放录制音频包:../19-persistence-replay/replay-and-video.md;DLC 数据文件挂载:../05-world/game-streaming-and-scene-streamer.md。
- 音频工具链(RAVE、metadata 编译器、AMP 编译器、遮挡构建器等)位于
src/dev_ng/rage/base/tools/audio/与tools_ng/bin/audio/,见 ../24-toolchain/rage-tools-and-rag.md 与 ../24-toolchain/asset-pipeline.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_CORE | audNorthAudioEngine::Init | src/dev_ng/game/Core/game.cpp:1723-1738 |
INIT_AFTER_MAP_LOADED | audNorthAudioEngine::Init(当前为空分支,LoadMapData() 已注释) | game.cpp:2146-2152、src/dev_ng/game/audio/northaudioengine.cpp:1121-1126 |
INIT_SESSION | audNorthAudioEngine::Init(紧随 CStreaming::Init),以及末尾的 audNorthAudioEngine::DLCInit | game.cpp:2279-2288、game.cpp:2428 |
| Shutdown | SHUTDOWN_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 的主要步骤:
- 参数预设:
-nogameaudio直接退出;-audiodesigner会自动设置-rave=Sounds,GameObjects,Curves,Categories,ModularSynth,DynamicMixer,AudioConfig、跳过若干 chunk、开启控件与调试输出northaudioengine.cpp:785-845。 - 音频物理内存:PC/Durango/Orbis 从
fwConfigManager的AudioHeap池配置(单位 MB)取大小;PC 再加 13 MB 用户音乐与 4 MB 流式环境声补偿并用sysMemPhysicalAllocate,主机从MEMTYPE_GAME_VIRTUAL分配;包装成sysMemSimpleAllocatornorthaudioengine.cpp:847-878。 InitializeAudio(sm_AudioController, ...)初始化 RAGE 引擎与主线程 controller;随后LoadSPData()并常驻加载dlc_gtao_speech.datnorthaudioengine.cpp:880-888。- 装上
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。 - 加载 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。 - 预留一个 sound bucket 给流式 sound(也交给视频回放);
g_EmitterAudioEntity.Init()提前以捕获初始物件创建;controller 绑定naEnvironmentGroupManager;设置语音类别索引范围(SetSpeechCategoryRange(GLOBAL_SPEECH 的索引, 该索引 + 12))northaudioengine.cpp:950-969。 - 两次“提交听者 + controller PreUpdate/Update”以激活 controller;实例化
audCategoryControllerManager与audInteractiveMusicManager;StartUserUpdateFunction()把UpdateAudioThread注册为 RAGE 音频线程回调northaudioengine.cpp:971-989、northaudioengine.cpp:1798-1818。 - 创建 “North Audio Update” 线程:栈 256 KB、
PRIO_AUDIO_THREAD(=PRIO_HIGHEST)、CPU 5,并建sm_RunUpdateSema/sm_UpdateFinishedSemanorthaudioengine.cpp:995-998、northaudioengine.cpp:481、src/dev_ng/game/system/ThreadPriorities.h:7。 - 初始化麦克风、枪声尾音、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 groupnorthaudioengine.cpp:1503-1557。 - 卸载是延迟的:先登记,下一帧
Update()中对 Sounds 类型先StartUnloadingChunk(使引用该 metadata 范围的 sound 自行停止),自旋等待 RAGE 引擎至少完成一帧,再真正UnloadMetadataChunk;合成器 chunk 走synthSynthesizer::UnloadMetadataChunknorthaudioengine.cpp:1559-1653。 - wave pack 挂载器用 pack 名前 8 个字符注册
audWaveSlot::SetSearchPath,使.awc解析到 DLC 设备northaudioengine.cpp:1671-1700。 - SP/MP 切换:
LoadMPData卸载SinglePlayersounds 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缺省为 truenorthaudioengine.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(只列主干):
- 非暂停时
sm_TimeInMs = fwTimer::GetTimeInMilliseconds();根据fwTimer::GetTimeWarpActive()、回放编辑器状态计算慢动作northaudioengine.cpp:2945-2956。 sm_AudioController.PreUpdate(sm_TimeInMs)(所有实体PreUpdateService)→audEntity::ProcessBatchedSoundRequests→ 车辆/火车激活范围northaudioengine.cpp:3352-3363。- 写入全局 sound 变量:
Game.Clock.*(星期/时/分/秒/日/月/每分钟秒数/小数小时/时间扭曲)、Game.Player.Position.*/Speed/Health/MaxHealth、Game.Microphone.Position.*/FirstPerson、Game.Water.CameraDepth、Game.VideoEditor.*、Game.Player.InteriorRatio、Game.Mode.IsCopsAndCrooksnorthaudioengine.cpp:3365-3415。主线程StartUpdate另写Game.Lighting.*曝光/太阳可见度northaudioengine.cpp:2096-2099。这些变量可被 metadata 中的变量驱动 sound 直接读取。 - 门/车辆/汽车/语音的
UpdateClass;火焰、语音管理器、反射;非暂停时发声器、警用扫描、环境声;人物场景管理器;audStreamSlot::UpdateSlotsnorthaudioengine.cpp:3417-3434。 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)。
| 实体 | 基类 | 持有方式 / 职责 | 证据 |
|---|---|---|---|
audPedAudioEntity | naAudioEntity | CPed 内嵌成员 m_PedAudioEntity;近战挥击/命中、开火、呼吸/心跳、铃声、溅水、坠落、受击、车辆碰撞;内含 audPedFootStepAudio | src/dev_ng/game/Peds/ped.h:2740、src/dev_ng/game/audio/pedaudioentity.h:87-225、pedaudioentity.h:474 |
audSpeechAudioEntity | naAudioEntity | CPed 中按需 rage_new 的指针 m_pSpeechAudioEntity | ped.h:2874、src/dev_ng/game/Peds/Ped.cpp:1870 |
audVehicleAudioEntity 及 audCarAudioEntity / audHeliAudioEntity / audPlaneAudioEntity / audBoatAudioEntity / audTrainAudioEntity / audBicycleAudioEntity / audTrailerAudioEntity | naAudioEntity | CVehicleFactory 按车辆类型 rage_new(直升机/旋翼机/飞艇→Heli,船/潜艇→Boat,汽车/摩托/四轮/两栖→Car,未知类型回退基类并告警) | src/dev_ng/game/vehicles/VehicleFactory.cpp:2868-2908、src/dev_ng/game/vehicles/vehicle.h:1790-1791 |
audObjectAudioEntity / audDoorAudioEntity | naAudioEntity | CObject::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、audWeatherAudioEntity | naAudioEntity | 全局单例(g_CollisionAudioEntity 等),按事件触发 | 例如 collisionaudioentity.h:306、weaponaudioentity.h:339 |
audAmbientAudioEntity、audRadioAudioEntity、audScriptAudioEntity、audFrontendAudioEntity、audCutsceneAudioEntity、audReplayAudioEntity、audDynamicMixer、audInteractiveMusicManager | naAudioEntity | 全局单例 | ambience/ambientaudioentity.h:94、scriptaudioentity.h:506、dynamicmixer.h:291、music/musicplayer.h:48 |
audVehicleReflectionsEntity | naAudioEntity | 车辆近墙反射(推断其使用 RefSrc/RefOut submix,未逐行核对) | vehiclereflectionsaudioentity.h:28 |
audVoiceChatAudioEntity | fwAudioEntity | 网络语音聊天 | 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、VehicleCollisionSettings | gameobjects.h:1406、gameobjects.h:1590、gameobjects.h:6270 |
| 材质/碰撞/脚步 | CollisionMaterialSettings、ModelAudioCollisionSettings、ShoeAudioSettings、FootstepSurfaceSounds、ModelFootStepTuning | gameobjects.h:1743、gameobjects.h:4490 |
| 环境/内部 | AmbientZone、AmbientRule、AmbientZoneList、AmbientBankMap、DirectionalAmbience、InteriorSettings、InteriorRoom、ShoreLine*AudioSettings、MicrophoneSettings、ReflectionsSettings、PortalSettings | gameobjects.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、MacsModelOverrideList | gameobjects.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 schemanaOcclusionInteriorMetadata { 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、-audDisablePortalOcclusionocclusionmanager.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:由AmbientZoneGameObject 初始化,区分“激活区”(何时启用)与“定位区”(声源放在哪),可为球或盒并可旋转;每帧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::Updatesrc/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 = 5src/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-Exsubmix(见 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/offApplynorthaudioengine.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 scenenorthaudioengine.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().TriggerEvent | commands_audio.cpp:3109-3127 |
START_AUDIO_SCENE / STOP_AUDIO_SCENE | g_ScriptAudioEntity.StartScene/StopScene | commands_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 1src/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_DATA26/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等辅助模块未逐一核对。