audioService.py (37236B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\carbon\client\script\environment\audioService.py 2 import logging 3 import sys 4 import audio2 5 import trinity 6 import uthread2 7 import dogma.const as dogmaconst 8 import eveaudio 9 import eveaudio.const as audConst 10 import eveaudio.immersiveoverlay 11 import evecamera 12 from carbon.common.script.sys.service import Service 13 from eve.client.script.ui.view.viewStateConst import ViewState 14 from eve.common.script.sys.eveCfg import InShipInSpace 15 from eve.common.script.sys.idCheckers import IsTriglavianSystem 16 from eveaudio.eveaudiomanager import CreateAudioManager 17 from eveaudio.fsdUtils import GetEventFromSoundID 18 from eveaudio.helpers import CreateAudioObserver 19 from eveaudio.shiphealthnotification import ShipHealthNotifier 20 from eveaudio.ui import UIAudioManager 21 CUSTOM_SOUND_LEVELS_SETTINGS = {'custom_atmosphere': 'advanced_settings_atmosphere', 22 'custom_jumpactivation': 'advanced_settings_jump_activations', 23 'custom_secondaryinterfaces': 'advanced_settings_secondary_interfaces', 24 'custom_shipeffects': 'advanced_settings_ship_effects', 25 'custom_shipsounds': 'advanced_settings_ship_sounds', 26 'custom_turrets': 'advanced_settings_turrets', 27 'custom_warningsfx': 'advanced_settings_warning_sfx', 28 'custom_uiclick': 'advanced_settings_uiclick'} 29 DAMPENING_SETTINGS = {'inactiveSounds_master': 'custom_damp_master', 30 'inactiveSounds_music': 'custom_damp_music', 31 'inactiveSounds_turrets': 'custom_damp_turrets', 32 'inactiveSounds_shield': 'custom_damp_shield', 33 'inactiveSounds_armor': 'custom_damp_armor', 34 'inactiveSounds_hull': 'custom_damp_hull', 35 'inactiveSounds_shipsound': 'custom_damp_shipsound', 36 'inactiveSounds_jumpgates': 'custom_damp_jumpgates', 37 'inactiveSounds_wormholes': 'custom_damp_wormholes', 38 'inactiveSounds_jumping': 'custom_damp_jumping', 39 'inactiveSounds_aura': 'custom_damp_aura', 40 'inactiveSounds_modules': 'custom_damp_modules', 41 'inactiveSounds_explosions': 'custom_damp_explosions', 42 'inactiveSounds_warping': 'custom_damp_warping', 43 'inactiveSounds_locking': 'custom_damp_locking', 44 'inactiveSounds_planets': 'custom_damp_planets', 45 'inactiveSounds_impacts': 'custom_damp_impacts', 46 'inactiveSounds_deployables': 'custom_damp_deployables', 47 'inactiveSounds_boosters': 'custom_damp_boosters', 48 'inactiveSounds_stationint': 'custom_damp_stationint', 49 'inactiveSounds_stationext': 'custom_damp_stationext', 50 'inactiveSounds_structures': 'custom_damp_structures'} 51 INDUSTRY_LEVELS = {i:u'set_industry_level_%s_state' % (i,) for i in xrange(6)} 52 RESEARCH_LEVELS = {i:u'set_research_level_%s_state' % (i,) for i in xrange(6)} 53 logger = logging.getLogger('audioSvc') 54 55 def GetAudioService(): 56 return sm.GetService('audio') 57 58 59 class AudioService(Service): 60 __guid__ = 'svc.audio' 61 __exportedcalls__ = {'Activate': [], 62 'Deactivate': [], 63 'GetMasterVolume': [], 64 'SetMasterVolume': [], 65 'GetUIVolume': [], 66 'SetUIVolume': [], 67 'GetWorldVolume': [], 68 'SetWorldVolume': [], 69 'GetVoiceVolume': [], 70 'SetVoiceVolume': [], 71 'GetCombatMusicUsage': [], 72 'MuteSounds': [], 73 'UnmuteSounds': [], 74 'IsActivated': [], 75 'AudioMessage': [], 76 'GetDialoguePlayPosition': [], 77 'LoadBanksForView': [], 78 'PostDialogueEvent': [], 79 'SendUIEvent': [], 80 'SendUIEventWithCallback': [], 81 'SetUIRTPC': [], 82 'StartSoundLoop': [], 83 'StopSoundLoop': [], 84 'SubscribeToEventCallback': [], 85 'UnsubscribeFromEventCallback': [], 86 'BallSupportsAudio': [], 87 'ItemSupportsAudio': [], 88 'GetAudioEmittersOnItem': [], 89 'GetOrCreateAudioEmitterForItem': [], 90 'PlayEventOnItem': [], 91 'PlaySoundOnItem': [], 92 'StopEventOnItem': [], 93 'StopSoundOnItem': [], 94 'GetTurretSuppression': [], 95 'SetTurretSuppression': [], 96 'SetShipState': [], 97 'SetupPlanet3DAudio': [], 98 'WarpPreparation': [], 99 'EnableDebugShowAllEmitters': [], 100 'DisableDebugShowAllEmitters': [], 101 'GetDebugDisplayAllEmitters': [], 102 'SeekOnEventPercent': [], 103 'SeekOnEventMs': [], 104 'SetGameParameterOnAudioEmitter': [], 105 'DisableSpatialAudio': [], 106 'EnableSpatialAudio': [], 107 'SpatialAudioEnabled': [], 108 'SpatialAudioIsSupported': []} 109 __startupdependencies__ = ['settings', 'sceneManager', 'dynamicMusic'] 110 __dependencies__ = ['map', 'michelle'] 111 __notifyevents__ = ['DoBallsAdded', 112 'DoBallsRemove', 113 'OnActiveCameraChanged', 114 'OnBallparkCall', 115 'OnBallparkSetState', 116 'OnCapacitorChange', 117 'OnChannelsJoined', 118 'OnClientEvent_ActivateModule', 119 'OnClientEvent_DeactivateModule', 120 'OnClientEvent_StopShip', 121 'OnDamageStateChange', 122 'OnDogmaItemChange', 123 'OnDungeonTriggerAmbientSound', 124 'OnDungeonEntered', 125 'OnExitingDungeon', 126 'OnIntroCompleted', 127 'OnIntroStarted', 128 'OnPlanetViewChanged', 129 'OnProvingGroundsMatchFound', 130 'OnViewStateChanged', 131 'OnSessionChanged', 132 'OnSessionReset', 133 'OnSpecialFX', 134 'OnWarpArrived', 135 'OnWarpDecelerate', 136 'OnClientEvent_WarpFinished'] 137 __componentTypes__ = ['audioEmitter'] 138 139 def __init__(self): 140 Service.__init__(self) 141 languageID = 'en' if self.AppGetSetting('forceEnglishVoice', False) else session.languageID 142 self.manager = CreateAudioManager('res:/Audio/', languageID, 'Eve Client') 143 self.soundLoops = {} 144 self.lastLookedAt = None 145 self.shipHealthNotifier = ShipHealthNotifier(self.SendWiseEvent, self.SetGlobalRTPC) 146 self.lastSystemID = None 147 self.lastCameraID = None 148 self.shipState = None 149 self.uiAudioManager = None 150 self.immersiveOverlayManager = None 151 self.activeAmbientDungeonSounds = [] 152 self.numEntitiesInPark = 0 153 self.engineLimit = 0 154 155 def Run(self, ms = None): 156 self.active = False 157 self.uiAudioManager = UIAudioManager() 158 self.jukeboxPlayer = None 159 self.immersiveOverlayManager = eveaudio.immersiveoverlay.Manager(send_audio_event=self.SendUIEvent) 160 uthread2.StartTasklet(self._WatchActiveWindow) 161 if self.AppGetSetting('audioEnabled', 1): 162 self.Activate() 163 164 def Stop(self, stream): 165 self.uiAudioManager = None 166 self.jukeboxPlayer = None 167 168 def SetGlobalRTPC(self, rtpcName, value): 169 if not self.IsActivated(): 170 return 171 self.manager.SetGlobalRTPC(rtpcName, value) 172 173 def SetGlobalState(self, stateGroup, stateName): 174 if not self.IsActivated(): 175 return 176 self.manager.SetState(unicode(stateGroup), unicode(stateName)) 177 178 def Activate(self): 179 self.manager.Enable() 180 self.active = True 181 self.ApplySettings() 182 sm.ScatterEvent('OnAudioActivated') 183 self.manager.RegisterAudioDeviceChangeCallback(self.UserAudioDeviceChangeCallback) 184 185 def Deactivate(self): 186 self.manager.Disable() 187 self.activeAmbientDungeonSounds = [] 188 self.active = False 189 sm.ScatterEvent('OnAudioDeactivated') 190 191 def ApplySettings(self): 192 self.SetMasterVolume(self.GetMasterVolume()) 193 self.SetUIVolume(self.GetUIVolume()) 194 self.SetWorldVolume(self.GetWorldVolume()) 195 self.SetVoiceVolume(self.GetVoiceVolume()) 196 self._SetAmpVolume(self.GetMusicVolume()) 197 self.SetTurretSuppression(self.GetTurretSuppression()) 198 self.SetOldJukeboxOverride(self.GetOldJukeboxOverride()) 199 self.SetCombatMusicUsage(self.GetCombatMusicUsage()) 200 if settings.user.audio.Get('soundLevel_advancedSettings', False): 201 self.LoadUpSavedAdvancedSettings() 202 203 def SetMasterVolume(self, vol = 1.0, persist = True): 204 if vol < 0.0 or vol > 1.0: 205 raise RuntimeError('Erroneous value received for volume') 206 self.SetGlobalRTPC(unicode('volume_master'), vol) 207 if persist: 208 self.AppSetSetting('masterVolume', vol) 209 210 def GetMasterVolume(self): 211 return self.AppGetSetting('masterVolume', 0.7) 212 213 def SetUIVolume(self, vol = 1.0, persist = True): 214 if vol < 0.0 or vol > 1.0: 215 raise RuntimeError('Erroneous value received for volume') 216 self.SetGlobalRTPC(unicode('volume_ui'), vol) 217 if persist: 218 self.AppSetSetting('uiGain', vol) 219 220 def GetUIVolume(self): 221 return self.AppGetSetting('uiGain', 0.5) 222 223 def SetWorldVolume(self, vol = 1.0, persist = True): 224 if vol < 0.0 or vol > 1.0: 225 raise RuntimeError('Erroneous value received for volume') 226 self.SetGlobalRTPC(unicode('volume_world'), vol) 227 if persist: 228 self.AppSetSetting('worldVolume', vol) 229 230 def SetCustomValue(self, vol, settingName, persist = True): 231 rtpcConfigName = CUSTOM_SOUND_LEVELS_SETTINGS.get(settingName) 232 if not rtpcConfigName: 233 return 234 self.SetSoundVolumeBetween0and1(volume=vol, configNameRTPC=rtpcConfigName, configNameAppSetting=settingName, persist=persist) 235 236 def SetSoundVolumeBetween0and1(self, volume, configNameRTPC, configNameAppSetting, persist): 237 if volume is None: 238 volume = settings.user.audio.Get(configNameAppSetting, 0.5) 239 if volume < 0.0 or volume > 1.0: 240 raise RuntimeError('Erroneous value received for volume, configName=', configNameAppSetting) 241 self.SetGlobalRTPC(unicode(configNameRTPC), volume) 242 if persist: 243 settings.user.audio.Set(configNameAppSetting, volume) 244 245 def EnableAdvancedSettings(self): 246 for eachSettingName in CUSTOM_SOUND_LEVELS_SETTINGS.iterkeys(): 247 self.SetCustomValue(vol=None, settingName=eachSettingName, persist=False) 248 249 def DisableAdvancedSettings(self): 250 for eachSettingName in CUSTOM_SOUND_LEVELS_SETTINGS.iterkeys(): 251 self.SetCustomValue(vol=0.5, settingName=eachSettingName, persist=False) 252 253 def LoadUpSavedAdvancedSettings(self): 254 for eachSettingName in CUSTOM_SOUND_LEVELS_SETTINGS.iterkeys(): 255 volume = settings.user.audio.Get(eachSettingName, 0.5) 256 self.SetCustomValue(vol=volume, settingName=eachSettingName, persist=False) 257 258 def SetDampeningValue(self, settingName, setOn = True): 259 audioEvent = DAMPENING_SETTINGS.get(settingName) 260 if not audioEvent: 261 return 262 if setOn: 263 audioEvent += '_on' 264 else: 265 audioEvent += '_off' 266 self.uiAudioManager.SendUIEvent(audioEvent) 267 268 def SetDampeningValueSetting(self, settingName, setOn = True): 269 settings.user.audio.Set(settingName, setOn) 270 271 def DisableDampeningValues(self): 272 for eachSettingName in DAMPENING_SETTINGS.iterkeys(): 273 self.SetDampeningValue(settingName=eachSettingName, setOn=False) 274 275 def LoadUpSavedDampeningValues(self): 276 for eachSettingName in DAMPENING_SETTINGS.iterkeys(): 277 setOn = settings.user.audio.Get(eachSettingName, False) 278 self.SetDampeningValue(settingName=eachSettingName, setOn=setOn) 279 280 def GetWorldVolume(self): 281 return self.AppGetSetting('worldVolume', 0.7) 282 283 def SetVoiceVolume(self, vol = 1.0, persist = True): 284 if vol < 0.0 or vol > 1.0: 285 raise RuntimeError('Erroneous value received for volume') 286 if not self.IsActivated(): 287 return 288 self.SetGlobalRTPC('volume_voice', vol) 289 if persist: 290 self.AppSetSetting('evevoiceGain', vol) 291 292 def GetVoiceVolume(self): 293 return self.AppGetSetting('evevoiceGain', 0.9) 294 295 def _SetAmpVolume(self, volume = 0.5, persist = True): 296 if volume < 0.0 or volume > 1.0: 297 raise RuntimeError('Erroneous value received for volume') 298 if not self.IsActivated(): 299 return 300 self.SetGlobalRTPC('volume_music', volume) 301 if persist: 302 self.AppSetSetting('eveampGain', volume) 303 304 def UserSetAmpVolume(self, volume = 0.5, persist = True): 305 self._SetAmpVolume(volume, persist) 306 sm.GetService('dynamicMusic').MusicVolumeChangedByUser(volume) 307 308 def GetMusicVolume(self): 309 return self.AppGetSetting('eveampGain', 0.5) 310 311 def IsActivated(self): 312 return self.active 313 314 def HandleFsdSoundID(self, soundID, emitter = None): 315 if emitter: 316 event = GetEventFromSoundID(soundID) 317 if event: 318 return emitter.SendEvent(event) 319 else: 320 self.LogError('FSD sound ID {} was passed in without a corresponding emitter.'.format(soundID)) 321 return 0 322 323 def SendUIEvent(self, event): 324 if not self.IsActivated(): 325 return 326 return self.uiAudioManager.SendUIEvent(event) 327 328 def StopUIEvent(self, playingID): 329 self.uiAudioManager.StopUIEvent(playingID) 330 331 def SendUIEventByTypeID(self, typeID): 332 self.uiAudioManager.SendUIEventByTypeID(typeID) 333 334 def PostDialogueEvent(self, event): 335 self.uiAudioManager.PostDialogueEvent(event) 336 337 def AppGetSetting(self, setting, default): 338 try: 339 return settings.public.audio.Get(setting, default) 340 except (AttributeError, NameError): 341 return default 342 343 def AppSetSetting(self, setting, value): 344 try: 345 settings.public.audio.Set(setting, value) 346 except (AttributeError, NameError): 347 pass 348 349 def AudioMessage(self, msg, msgKey = None): 350 if not self.IsActivated(): 351 return 352 self.uiAudioManager.SendAudioMessage(msg, userErrorKey=msgKey) 353 354 def StartSoundLoop(self, rootLoopMsg): 355 if not self.IsActivated(): 356 return 357 try: 358 if rootLoopMsg not in self.soundLoops: 359 self.LogInfo('StartSoundLoop starting loop with root %s' % rootLoopMsg) 360 self.soundLoops[rootLoopMsg] = 1 361 self.uiAudioManager.SendUIEvent('msg_%s_play' % rootLoopMsg) 362 else: 363 self.soundLoops[rootLoopMsg] += 1 364 self.LogInfo('StartSoundLoop incrementing %s loop to %d' % (rootLoopMsg, self.soundLoops[rootLoopMsg])) 365 except: 366 self.LogWarn('StartSoundLoop failed - halting loop with root', rootLoopMsg) 367 self.uiAudioManager.SendUIEvent('msg_%s_stop' % rootLoopMsg) 368 sys.exc_clear() 369 370 def StopSoundLoop(self, rootLoopMsg, eventMsg = None): 371 if rootLoopMsg not in self.soundLoops: 372 self.LogInfo('StopSoundLoop told to halt', rootLoopMsg, 'but that message is not playing!') 373 return 374 try: 375 self.soundLoops[rootLoopMsg] -= 1 376 if self.soundLoops[rootLoopMsg] <= 0: 377 self.LogInfo('StopSoundLoop halting message with root', rootLoopMsg) 378 del self.soundLoops[rootLoopMsg] 379 self.uiAudioManager.SendUIEvent('msg_%s_stop' % rootLoopMsg) 380 else: 381 self.LogInfo('StopSoundLoop decremented count of loop with root %s to %d' % (rootLoopMsg, self.soundLoops[rootLoopMsg])) 382 except: 383 self.LogWarn('StopSoundLoop failed due to an exception - forcibly halting', rootLoopMsg) 384 self.uiAudioManager.SendUIEvent('msg_%s_stop' % rootLoopMsg) 385 sys.exc_clear() 386 387 if eventMsg is not None: 388 self.uiAudioManager.SendUIEvent(eventMsg) 389 390 def SetupPlanet3DAudio(self, planet, typeID): 391 if typeID in audConst.PLANET_3D_AUDIO_TRIGGERS.keys(): 392 events = [audConst.PLANET_3D_AUDIO_TRIGGERS[typeID]] 393 if IsTriglavianSystem(session.solarsystemid2): 394 events.append(audConst.TRIG_PLANET_3D_TRIGGER) 395 observer = CreateAudioObserver('planet_atmosphere') 396 observer.observer.SetAttenuationScalingFactor(planet.radius) 397 [ observer.observer.SendEvent(event) for event in events ] 398 planet.observers.append(observer) 399 400 def GetTurretSuppression(self): 401 return self.AppGetSetting('suppressTurret', 0) 402 403 def SetTurretSuppression(self, suppress, persist = True): 404 if not self.IsActivated(): 405 return 406 if suppress: 407 self.SetGlobalRTPC('turret_muffler', 0.0) 408 suppress = 1 409 else: 410 self.SetGlobalRTPC('turret_muffler', 1.0) 411 suppress = 0 412 if persist: 413 self.AppSetSetting('suppressTurret', suppress) 414 415 def GetOldJukeboxOverride(self): 416 return self.AppGetSetting('useOldJukeboxOverride', 0) 417 418 def SetOldJukeboxOverride(self, useClassicMusic): 419 self.AppSetSetting('useOldJukeboxOverride', useClassicMusic) 420 421 def GetCombatMusicUsage(self): 422 return self.AppGetSetting('useCombatMusic', 1) 423 424 def SetCombatMusicUsage(self, useCombatMusic): 425 self.AppSetSetting('useCombatMusic', useCombatMusic) 426 427 def MuteSounds(self): 428 self.SetMasterVolume(0.0, False) 429 430 def UnmuteSounds(self): 431 self.SetMasterVolume(self.GetMasterVolume(), False) 432 433 def SendWiseEvent(self, event): 434 if event: 435 self.uiAudioManager.SendUIEvent(event) 436 437 def OnChannelsJoined(self, channelIDs): 438 if not session.stationid: 439 return 440 if (('solarsystemid2', session.solarsystemid2),) in channelIDs: 441 pilotsInChannel = eveaudio.GetPilotsInSystem() 442 self.SetHangarPopulationSwitch(pilotsInChannel) 443 444 def OnClientEvent_ActivateModule(self, effectID): 445 if effectID == dogmaconst.effectModuleBonusMicrowarpdrive: 446 self._SetControllerVariableOnShip(audConst.ModuleType.__name__, audConst.ModuleType.microwarpdrive.value) 447 elif effectID == dogmaconst.effectModuleBonusAfterburner: 448 self._SetControllerVariableOnShip(audConst.ModuleType.__name__, audConst.ModuleType.afterburner.value) 449 self._SetControllerVariableOnShip(audConst.ModuleState.__name__, audConst.ModuleState.activated.value) 450 451 def OnClientEvent_DeactivateModule(self, effectID): 452 if effectID == dogmaconst.effectModuleBonusMicrowarpdrive: 453 self._SetControllerVariableOnShip(audConst.ModuleType.__name__, audConst.ModuleType.microwarpdrive.value) 454 elif effectID == dogmaconst.effectModuleBonusAfterburner: 455 self._SetControllerVariableOnShip(audConst.ModuleType.__name__, audConst.ModuleType.afterburner.value) 456 self._SetControllerVariableOnShip(audConst.ModuleState.__name__, audConst.ModuleState.powerdown.value) 457 458 def OnClientEvent_StopShip(self): 459 self._SetControllerVariableOnShip(audConst.STOP_SHIP_CTRL_VARIABLE, 1) 460 461 def SetHangarPopulationSwitch(self, pilotsInChannel): 462 self.uiAudioManager.SendUIEvent(eveaudio.GetHangarPopulationSwitch(pilotsInChannel)) 463 464 def SwapBanks(self, banks): 465 self.manager.SwapSoundBanks(banks) 466 467 def ReloadBanks(self): 468 soundBankStatus = self.manager.ReloadSoundBanks() 469 self.ApplySettings() 470 return soundBankStatus 471 472 def OnDamageStateChange(self, *args, **kwargs): 473 return self.shipHealthNotifier.OnDamageStateChange(*args, **kwargs) 474 475 def OnCapacitorChange(self, *args, **kwargs): 476 return self.shipHealthNotifier.OnCapacitorChange(*args, **kwargs) 477 478 def OnSessionChanged(self, isRemote, session, change): 479 if 'userid' in change and session.userid: 480 self.ApplySettings() 481 if not session.stationid: 482 if 'solarsystemid' in change: 483 oldSolarSystem, newSolarSystem = change['solarsystemid'] 484 if oldSolarSystem and newSolarSystem: 485 eveaudio.PlaySystemSpecificEntrySound(self.lastSystemID, session.solarsystemid2, self.uiAudioManager.GetUIEmitter()) 486 self.lastSystemID = session.solarsystemid2 487 if 'shipid' in change: 488 oldShipID, newShipID = change['shipid'] 489 self._SetControllerVariableOnShip(audConst.ActingParty.__name__, audConst.ActingParty.firstParty.value, shipID=newShipID) 490 self._SetControllerVariableOnShip(audConst.ActingParty.__name__, audConst.ActingParty.thirdParty.value, shipID=oldShipID) 491 492 def OnSessionReset(self): 493 self.StopAllAudio() 494 self.numEntitiesInPark = 0 495 self.activeAmbientDungeonSounds = [] 496 497 def StopAllAudio(self): 498 self.manager.manager.StopAll() 499 500 def OnSpecialFX(self, shipID, moduleID, moduleTypeID, targetID, otherTypeID, guid, isOffensive, start, active, *args, **kwargs): 501 if guid == audConst.MICROWARPDRIVE_GUID or guid == audConst.AFTERBURNER_GUID: 502 if guid == audConst.MICROWARPDRIVE_GUID: 503 self._SetControllerVariableOnShip(audConst.ModuleType.__name__, audConst.ModuleType.microwarpdrive.value, shipID=shipID) 504 else: 505 self._SetControllerVariableOnShip(audConst.ModuleType.__name__, audConst.ModuleType.afterburner.value, shipID=shipID) 506 if not start: 507 self._SetControllerVariableOnShip(audConst.ModuleState.__name__, audConst.ModuleState.deactivated.value, shipID=shipID) 508 else: 509 self._SetControllerVariableOnShip(audConst.ModuleState.__name__, audConst.ModuleState.activated.value, shipID=shipID) 510 elif guid == audConst.WARPING_GUID: 511 if shipID != session.shipid: 512 self._SetControllerVariableOnShip(audConst.WarpState.__name__, audConst.WarpState.prep.value, shipID=shipID) 513 514 def LoadBanksForView(self, newView): 515 if newView == ViewState.Hangar: 516 self.SetGlobalState('in_hangar', 'yes') 517 self.SwapBanks(eveaudio.EVE_HANGAR_BANKS) 518 eveaudio.SetTheraSystemHangarSwitch(session.solarsystemid2, self.uiAudioManager.GetUIEmitter()) 519 elif newView == ViewState.Space: 520 self.SwapBanks(eveaudio.EVE_SPACE_BANKS) 521 if newView != ViewState.Hangar: 522 self.SetGlobalState('in_hangar', 'no') 523 524 def _WatchActiveWindow(self): 525 isActive = None 526 while True: 527 if settings.user.audio.Get('inactiveSounds_advancedSettings', False): 528 if trinity.app.IsActive() != isActive: 529 isActive = trinity.app.IsActive() 530 if isActive: 531 self.DisableDampeningValues() 532 else: 533 self.LoadUpSavedDampeningValues() 534 uthread2.Sleep(0.3) 535 536 def OnBallparkSetState(self): 537 self.SetResearchAndIndustryLevel() 538 539 def SetResearchAndIndustryLevel(self): 540 ballpark = sm.GetService('michelle').GetBallpark() 541 if ballpark is None: 542 return 543 industryLevel = ballpark.industryLevel 544 researchLevel = ballpark.researchLevel 545 if industryLevel in INDUSTRY_LEVELS.keys(): 546 self.uiAudioManager.SendUIEvent(INDUSTRY_LEVELS.get(industryLevel)) 547 if researchLevel in RESEARCH_LEVELS.keys(): 548 self.uiAudioManager.SendUIEvent(RESEARCH_LEVELS.get(researchLevel)) 549 550 def OnDogmaItemChange(self, *args, **kwargs): 551 if not InShipInSpace(): 552 return 553 shipid = session.shipid 554 ship = sm.StartService('godma').GetItem(shipid) 555 if shipid and ship: 556 cargoHoldState = ship.GetCapacity() 557 return self.shipHealthNotifier.OnCargoHoldChange(shipid, cargoHoldState, *args, **kwargs) 558 559 def OnDungeonTriggerAmbientSound(self, dungeonID, soundID, entityItemID): 560 logger.info('Ambient sound {} was requested to play in dungeon {} on item {}'.format(soundID, dungeonID, entityItemID)) 561 emitterID = self.PlaySoundOnItem(soundID, entityItemID) 562 if emitterID > 0: 563 self.activeAmbientDungeonSounds.append((emitterID, soundID)) 564 565 def OnDungeonEntered(self, dungeonID, instanceID): 566 self.activeAmbientDungeonSounds = [] 567 568 def OnExitingDungeon(self, dungeonID): 569 if self.activeAmbientDungeonSounds: 570 for emitterID, soundID in self.activeAmbientDungeonSounds: 571 stopped = self.StopSoundOnEmitter(soundID, emitterID) 572 if stopped: 573 logger.info('Ambient sound {} was stopped for dungeon {}'.format(soundID, dungeonID)) 574 575 self.activeAmbientDungeonSounds = [] 576 577 def OnIntroCompleted(self): 578 self.SetGlobalState(*audConst.VIDEO_OVERLAY_STATE_OFF) 579 580 def OnIntroStarted(self): 581 self.SetGlobalState(*audConst.VIDEO_OVERLAY_STATE_ON) 582 583 def OnPlanetViewChanged(self, planetID, oldPlanetID): 584 events = [] 585 if planetID and not oldPlanetID: 586 planetData = self.map.GetPlanetInfo(planetID) 587 events.append(audConst.PI_AUDIO_TRIGGERS.get(planetData.typeID)) 588 if IsTriglavianSystem(session.solarsystemid2): 589 events.append(audConst.TRIG_PLANET_PI_TRIGGER) 590 elif oldPlanetID and not planetID: 591 planetData = self.map.GetPlanetInfo(oldPlanetID) 592 events.append(audConst.PI_AUDIO_TRIGGERS.get(planetData.typeID).replace('play', 'stop')) 593 if IsTriglavianSystem(session.solarsystemid2): 594 events.append(audConst.TRIG_PLANET_PI_TRIGGER.replace('play', 'stop')) 595 [ self.uiAudioManager.SendUIEvent(event) for event in events ] 596 597 def OnProvingGroundsMatchFound(self): 598 self.uiAudioManager.SendUIEvent(audConst.UI_AUDIO_TRIGGERS['arena_opponent_matched'] + '_play') 599 600 def OnViewStateChanged(self, fromView, toView): 601 self.numEntitiesInPark = 0 602 603 def DoBallsAdded(self, ballsToAdd): 604 numEntities = 0 605 for ball, slimItem in ballsToAdd: 606 if getattr(slimItem, 'categoryID', None) in audConst.ENGINE_LIMITING_CATEGORIES: 607 numEntities += 1 608 609 self.numEntitiesInPark += numEntities 610 self._LimitEnginesIfNeeded() 611 612 def DoBallsRemove(self, pythonBalls, isRelease): 613 numEntities = 0 614 for ball, slimItem, terminal in pythonBalls: 615 if getattr(slimItem, 'categoryID', None) in audConst.ENGINE_LIMITING_CATEGORIES: 616 numEntities += 1 617 618 self.numEntitiesInPark -= numEntities 619 self._LimitEnginesIfNeeded() 620 621 def OnActiveCameraChanged(self, cameraID): 622 if session is None or self.lastCameraID == cameraID: 623 return 624 self.uiAudioManager.SendUIEvent('ship_camera_sounds_stop') 625 if cameraID == evecamera.CAM_TACTICAL: 626 self.uiAudioManager.SendUIEvent('state_camera_set_overview') 627 elif cameraID == evecamera.CAM_SHIPPOV: 628 if hasattr(session, 'shipid') and session.shipid is not None: 629 ship = sm.GetService('michelle').GetItem(session.shipid) 630 if ship: 631 self.uiAudioManager.SendUIEvent('state_camera_set_cockpit') 632 else: 633 self.uiAudioManager.SendUIEvent('state_camera_set_normal') 634 635 def SetUIRTPC(self, rtpcName, value): 636 self.uiAudioManager.SetUIRTPC(rtpcName, value) 637 638 def WarpPreparation(self, action, shipID): 639 ball = sm.GetService('michelle').GetBall(shipID) 640 if action == 'play': 641 if not self.GetShipState() == audConst.ShipState.preparing_warp: 642 self._SetControllerVariableOnShip(audConst.WarpState.__name__, audConst.WarpState.prep.value, ball=ball, shipID=shipID) 643 self.uiAudioManager.SendUIEvent(audConst.UI_AUDIO_TRIGGERS['warp_prep'] + '_play') 644 self.AudioMessage('', msgKey='WarpDriveActive') 645 self.SetShipState(audConst.ShipState.preparing_warp) 646 if action == 'stop': 647 if self.GetShipState() == audConst.ShipState.preparing_warp: 648 self._SetControllerVariableOnShip(audConst.WarpState.__name__, audConst.WarpState.start.value, ball=ball, shipID=shipID) 649 self.uiAudioManager.SendUIEvent(audConst.UI_AUDIO_TRIGGERS['warp_prep'] + '_stop') 650 self.SetShipState(audConst.ShipState.warping) 651 if action == 'cancel': 652 if self.GetShipState() == audConst.ShipState.preparing_warp: 653 self._SetControllerVariableOnShip(audConst.WarpState.__name__, audConst.WarpState.stop.value, ball=ball, shipID=shipID) 654 self.uiAudioManager.SendUIEvent(audConst.UI_AUDIO_TRIGGERS['warp_prep_abort'] + '_play') 655 self.SetShipState(audConst.ShipState.idle) 656 657 def SetShipState(self, value): 658 self.shipState = value 659 660 def GetShipState(self): 661 return self.shipState 662 663 def SendUIEventWithCallback(self, event): 664 return self.uiAudioManager.SendUIEventWithCallback(unicode(event)) 665 666 def SubscribeToEventCallback(self, callbackFunc): 667 self.uiAudioManager.SubscribeToEventCallback(callbackFunc) 668 669 def UnsubscribeFromEventCallback(self, callbackFunc): 670 self.uiAudioManager.UnsubscribeFromEventCallback(callbackFunc) 671 672 def OnBallparkCall(self, funcname, args): 673 if args[0] == session.shipid: 674 if funcname == 'WarpTo': 675 self.WarpPreparation('play', args[0]) 676 if funcname == 'Stop': 677 self.WarpPreparation('cancel', args[0]) 678 679 def OnWarpArrived(self, *args): 680 self._SetControllerVariableOnShip(audConst.WarpState.__name__, audConst.WarpState.decelerate.value) 681 682 def OnWarpDecelerate(self, warpToItemID, warpToTypeID): 683 self.uiAudioManager.SendUIEvent('ship_warp_wind_end') 684 self._SetControllerVariableOnShip(audConst.WarpState.__name__, audConst.WarpState.stop.value) 685 686 def OnClientEvent_WarpFinished(self, warpToItemID, warpToTypeID): 687 self._SetControllerVariableOnShip(audConst.WarpState.__name__, audConst.WarpState.finished.value) 688 689 def PlayEventOnItem(self, event, itemID, emitterName = '3d_sfx', maxTries = 10, sleepTime = 0.5): 690 ball = self._GetAndWaitForBall(itemID, maxTries=maxTries, sleepTime=sleepTime) 691 if ball: 692 audioEmitter = self.GetOrCreateAudioEmitterForItem(itemID, emitterName=emitterName, ball=ball) 693 if audioEmitter: 694 playingID = audioEmitter.SendEvent(event) 695 if playingID > 0: 696 return audioEmitter.ID 697 return 0 698 699 def StopEventOnItem(self, event, itemID): 700 audioEmitters = self.GetAudioEmittersOnItem(itemID) 701 stopped = False 702 for emitter in audioEmitters: 703 stopped = emitter.StopEvent(event) 704 if stopped: 705 break 706 707 return stopped 708 709 def PlaySoundOnItem(self, soundID, itemID, emitterName = '3d_sfx', maxTries = 10, sleepTime = 0.5): 710 event = GetEventFromSoundID(soundID) 711 if event: 712 return self.PlayEventOnItem(event, itemID, emitterName=emitterName, maxTries=maxTries, sleepTime=sleepTime) 713 return 0 714 715 def StopSoundOnItem(self, soundID, itemID): 716 event = GetEventFromSoundID(soundID) 717 if event: 718 return self.StopEventOnItem(event, itemID) 719 return False 720 721 def StopSoundOnEmitter(self, soundID, emitterID, fadeOutDuration = 1000): 722 event = GetEventFromSoundID(soundID) 723 if event: 724 emitter = self.manager.GetAudioEmitter(emitterID) 725 if emitter: 726 return emitter.StopEvent(event, fadeOutDuration) 727 return False 728 729 def ItemSupportsAudio(self, itemID, ball = None): 730 if not ball: 731 ball = self.michelle.GetBallAndWaitForModel(itemID) 732 if ball: 733 if self.BallSupportsAudio(ball): 734 return True 735 return False 736 737 def BallSupportsAudio(self, ball): 738 model = ball.GetModel() 739 if model: 740 if hasattr(model, 'observers'): 741 return True 742 return False 743 744 def GetAudioEmittersOnItem(self, itemID): 745 audioEmitters = [] 746 ball = self.michelle.GetBallAndWaitForModel(itemID) 747 if ball: 748 model = ball.GetModel() 749 if model: 750 audioEmitters = model.Find('audio2.AudEmitter') 751 return audioEmitters 752 753 def SetGameParameterOnAudioEmitter(self, emitterID, parameterName, parameterValue): 754 emitter = self.manager.GetAudioEmitter(emitterID) 755 if emitter: 756 emitter.SetRTPC(parameterName, parameterValue) 757 return True 758 return False 759 760 def SetDebugLogCallback(self, callback): 761 self.manager.SetDebugLogCallback(callback) 762 763 def StopDebugLogging(self): 764 self.manager.StopDebugLogging() 765 766 def EnableDebugShowAllEmitters(self): 767 self.manager.manager.EnableDebugDisplayAllEmitters() 768 769 def DisableDebugShowAllEmitters(self): 770 self.manager.manager.DisableDebugDisplayAllEmitters() 771 772 def GetDebugDisplayAllEmitters(self): 773 try: 774 return self.manager.manager.GetDebugDisplayAllEmitters() 775 except AttributeError: 776 return False 777 778 def _SetControllerVariableOnShip(self, variableName, variableValue, ball = None, shipID = 0): 779 if not shipID: 780 shipID = session.shipid 781 if shipID: 782 if not ball: 783 ball = self.michelle.GetBallAndWaitForModel(shipID) 784 if ball and hasattr(ball, 'SetControllerVariable'): 785 ball.SetControllerVariable(variableName, variableValue) 786 787 def _LimitEnginesIfNeeded(self): 788 if self.numEntitiesInPark > audConst.ENGINE_LIMIT_MIN: 789 if not self.engineLimit > 0: 790 logger.debug('Engine audio voice limiting activated.'.format(audConst.ENGINE_LIMIT_MIN)) 791 oneFifth = audConst.ENGINE_LIMIT_MIN / 5 % 100 792 newLimit = 0 793 if self.numEntitiesInPark in range(audConst.ENGINE_LIMIT_MIN, int(audConst.ENGINE_LIMIT_MAX * 0.5)): 794 newLimit = oneFifth * 4 % 100 795 elif self.numEntitiesInPark in range(int(audConst.ENGINE_LIMIT_MAX * 0.5), int(audConst.ENGINE_LIMIT_MAX * 0.75)): 796 newLimit = oneFifth * 3 % 100 797 elif self.numEntitiesInPark in range(int(audConst.ENGINE_LIMIT_MAX * 0.75), audConst.ENGINE_LIMIT_MAX): 798 newLimit = oneFifth * 2 % 100 799 elif self.numEntitiesInPark > audConst.ENGINE_LIMIT_MAX: 800 newLimit = oneFifth 801 if self.engineLimit != newLimit: 802 self.engineLimit = newLimit 803 self.SetGlobalRTPC(audConst.ENGINE_LIMIT_RTPC, newLimit) 804 logger.debug('Engine audio voice limit is {} ships at one time because there are {} entities in the park'.format(newLimit, self.numEntitiesInPark)) 805 elif self.numEntitiesInPark < audConst.ENGINE_LIMIT_MIN and self.engineLimit > 0: 806 logger.debug('Engine audio voice limiting deactivated. There are less than {} entities in the park'.format(audConst.ENGINE_LIMIT_MIN)) 807 self.engineLimit = 0 808 self.SetGlobalRTPC(audConst.ENGINE_LIMIT_RTPC, audConst.ENGINE_LIMIT_MIN) 809 810 def _GetAndWaitForBall(self, itemID, maxTries = 10, sleepTime = 0.5): 811 ball = None 812 sleepCount = 0 813 while not ball: 814 if sleepCount > maxTries: 815 break 816 ball = self.michelle.GetBall(itemID) 817 if not ball: 818 sleepCount += 1 819 uthread2.Sleep(sleepTime) 820 821 return ball 822 823 def _GetOrCreateAudioEmitterForBall(self, ball, emitterName = 'entity_sfx'): 824 audioEmitter = None 825 if ball: 826 if self.BallSupportsAudio(ball): 827 model = ball.GetModel() 828 emitter = model.Find('audio2.AudEmitter') 829 if emitter: 830 audioEmitter = emitter[0] 831 logger.info('Found existing audio emitter {} for ball {}.'.format(audioEmitter.name, ball.id)) 832 else: 833 newObserver = CreateAudioObserver(emitterName) 834 model.observers.append(newObserver) 835 audioEmitter = newObserver.observer 836 logger.info('Created new audio emitter {} for ball {}'.format(audioEmitter.name, ball.id)) 837 return audioEmitter 838 839 def GetOrCreateAudioEmitterForItem(self, itemID, emitterName = 'entity_sfx', ball = None): 840 if not ball: 841 ball = self.michelle.GetBallAndWaitForModel(itemID) 842 audioEmitter = None 843 if self.ItemSupportsAudio(itemID, ball=ball): 844 audioEmitter = self._GetOrCreateAudioEmitterForBall(ball, emitterName=emitterName) 845 return audioEmitter 846 847 def SeekOnEventPercent(self, playingID, percentToSeek): 848 self.uiAudioManager.SeekOnEventPercent(playingID, percentToSeek) 849 850 def SeekOnEventMs(self, playingID, msToSeek): 851 self.uiAudioManager.SeekOnEventMs(playingID, msToSeek) 852 853 def GetDialoguePlayPosition(self, playingID): 854 return self.uiAudioManager.GetEventPlayPosition(playingID) 855 856 def CreateImmersiveOverlay(self, name): 857 return self.immersiveOverlayManager.create(name) 858 859 def DisableSpatialAudio(self): 860 self.manager.DisableSpatialAudio() 861 862 def EnableSpatialAudio(self): 863 self.manager.EnableSpatialAudio() 864 865 def SpatialAudioEnabled(self): 866 return self.manager.GetSpatialAudioEnabled() 867 868 def SpatialAudioIsSupported(self): 869 if self.manager.SpatialAudioIsSupported(): 870 if self.manager.audioEndpointActive: 871 return True 872 return False 873 874 def UserAudioDeviceChangeCallback(self, spatialAudioSupported): 875 self.manager.audioEndpointActive = spatialAudioSupported 876 sm.ScatterEvent('OnUserAudioDeviceChanged')