void Start() { config = AudioMufflerConfig.loadConfig(); if (!config.EngageMuffler) { return; } GameEvents.onVesselChange.Add(VesselChange); GameEvents.onVesselWasModified.Add(VesselWasModified); AudioSource[] audioSources = FindObjectsOfType(typeof(AudioSource)) as AudioSource[]; audioMixer = AudioMixerFacade .InitializeMixer(KSP.IO.IOUtils.GetFilePathFor(typeof(Muffler), "mixer.bundle") .Replace("/", System.IO.Path.DirectorySeparatorChar.ToString())); StockAudio.PrepareAudioSources(audioMixer, audioSources); audioMixer.setInVesselCutoff(config.WallCutoff); }
void LateUpdate() { if (!config.EngageMuffler) { return; } AudioSource[] audioSources = FindObjectsOfType(typeof(AudioSource)) as AudioSource[]; cacheManager.maintainCaches(audioSources); //Looking for a part containing the Ear: Part earPart = null; //if (vesselGeometry.isPointInVesselBounds(earPosition)) { if (CameraManager.Instance.currentCameraMode == CameraManager.CameraMode.IVA) { earPart = CameraManager.Instance.IVACameraActiveKerbal.InPart; writeDebug("Ear position = IVA"); } else { Vector3 earPosition = CameraManager.GetCurrentCamera().transform.position; writeDebug("Ear position = " + earPosition); for (int i = 0; i < FlightGlobals.ActiveVessel.Parts.Count && earPart == null; i++) { Part part = FlightGlobals.ActiveVessel.Parts[i]; if (cacheManager.vesselGeometry.IsPointInPart(earPosition, part)) { earPart = part; } } } } writeDebug("Ear part = " + (earPart != null ? earPart.name : "null")); //Setting up helmet channel: bool unmanned = FlightGlobals.ActiveVessel.crewableParts == 0; bool muteHelmet = (earPart == null) && !config.HelmetOutsideEVA && FlightGlobals.ActiveVessel.isEVA || !FlightGlobals.ActiveVessel.isEVA && !unmanned && !config.HelmetOutsideIVA && !(CameraManager.Instance.currentCameraMode == CameraManager.CameraMode.IVA) || !config.HelmetForUnmanned && unmanned; // Setting up outside channel: float atmosphericCutoff = Mathf.Lerp(config.MinimalCutoff, 30000, (float)FlightGlobals.ActiveVessel.atmDensity); if (earPart != null) { audioMixer.setOutsideCutoff(Mathf.Min(config.WallCutoff, atmosphericCutoff)); audioMixer.setInVesselVolume(-2f); audioMixer.setOutsideVolume(-12f); } else { audioMixer.setOutsideCutoff(atmosphericCutoff); audioMixer.setInVesselVolume(0f); audioMixer.setOutsideVolume(0f); } // Handling Map view settings: bool isMapView = CameraManager.Instance.currentCameraMode == CameraManager.CameraMode.Map; muteHelmet = muteHelmet || !config.HelmetInMapView && isMapView; audioMixer.muteHelmet(muteHelmet); audioMixer.muteInVessel(!config.VesselInMapView && isMapView); audioMixer.muteOutside(!config.OutsideInMapView && isMapView); // Routing all current audio sources: for (int i = 0; i < audioSources.Length; i++) { AudioSource audioSource = audioSources[i]; /* Hereafter audio sources that are bound to a part's InternalModel are handled differently from the * rest because InternalModel has its own reference system, so these two systems shouldn't be mixed * until the way to convert coordinates between them is found */ if (config.Debug) { writeDebug( "Sound " + i + " clp=" + (audioSource.clip == null ? "null" : audioSource.clip.name) + ": plyng=" + audioSource.isPlaying + " trf.nm=" + audioSource.transform.name + " trf.pos=" + audioSource.transform.position + " amb=" + StockAudio.IsAmbient(audioSource) + " inves=" + StockAudio.IsInVessel(audioSource) ); } // This "if" is here because of strange behaviour of StageManager's audio source which // always has clip = null and !playing when checked if (StockAudio.IsInVessel(audioSource)) { writeDebug("Sound " + i + ":" + audioSource.name + " IN VESSEL"); audioSource.outputAudioMixerGroup = (earPart != null) ? audioMixer.InVesselGroup : audioMixer.OutsideGroup; continue; } if ( //audioSource.bypassEffects || StockAudio.IsPreserved(audioSource) || (audioSource.clip == null) || (!audioSource.isPlaying)) { continue; } if (StockAudio.IsAmbient(audioSource)) { writeDebug("Sound " + i + ":" + audioSource.name + " OUTSIDE"); audioSource.outputAudioMixerGroup = audioMixer.OutsideGroup; continue; } if (isSoundInHelmet(audioSource)) { writeDebug("Sound " + i + ":" + audioSource.name + " IN HELMET"); audioSource.outputAudioMixerGroup = audioMixer.HelmetGroup; continue; } bool isRouted = false; if (earPart != null /*&& vesselGeometry.isPointInVesselBounds(audioSource.transform.position)*/) { Part boundToPartIVA = cacheManager.vesselSounds.GetPartForIVA(audioSource); if (boundToPartIVA != null) { if (earPart.Equals(boundToPartIVA)) { writeDebug("Sound " + i + ":" + audioSource.name + " SAME AS EAR, INTERNAL"); //if audioSource is in the same part with the Ear then skipping filtering audioSource.outputAudioMixerGroup = null; continue; } else { writeDebug("Sound " + i + ":" + audioSource.name + " ANOTHER PART, INTERNAL"); //if audioSource is in another part of the vessel then applying constant muffling audioSource.outputAudioMixerGroup = audioMixer.InVesselGroup; continue; } } // Below the following assumption is used: if an audioSource is bound to a part (by having the same // transform) then it most likely is located inside that part, so in the most cases it will be // enough to test just the part's meshes instead of sequentially testing all of the vessel's // meshes, thus greatly improve performance in case of a high part count: // TODO remove this check when a proper way to transform coordinates between InternalModel // and part's transform is found //if (CameraManager.Instance.currentCameraMode != CameraManager.CameraMode.IVA) { if ( audioSource.transform.IsChildOf(earPart.transform) && cacheManager.vesselGeometry.IsPointInPart(audioSource.transform.position, earPart)) { writeDebug("Sound " + i + ":" + audioSource.name + " SAME AS EAR"); //if audioSource is in the same part with the Ear then skipping filtering audioSource.outputAudioMixerGroup = null; continue; } //} Part boundPart = cacheManager.vesselSounds.GetPartFor(audioSource); if ( boundPart != null && !boundPart.Equals(earPart) && cacheManager.vesselGeometry.IsPointInPart(audioSource.transform.position, boundPart)) { writeDebug("Sound " + i + ":" + audioSource.name + " ANOTHER PART"); //if audioSource is in another part of the vessel then applying constant muffling audioSource.outputAudioMixerGroup = audioMixer.InVesselGroup; continue; } // To this point the audioSource should be already routed as in the game the vast majority of // sounds are either helmet sounds, or bound to parts, or are ambient. So only a few sounds are // expected to pass to the following mesh-by-mesh test: for (int p = 0; p < FlightGlobals.ActiveVessel.Parts.Count && !isRouted; p++) { Part part = FlightGlobals.ActiveVessel.Parts[p]; //if the audioSource is bound to some part, then this part is already checked earlier if (part.Equals(boundPart)) { continue; } if (cacheManager.vesselGeometry.IsPointInPart(audioSource.transform.position, part)) { if (part.Equals(earPart)) { writeDebug("Sound " + i + ":" + audioSource.name + " SAME AS EAR"); //if audioSource is in the same part with the Ear then skipping filtering audioSource.outputAudioMixerGroup = null; } else { writeDebug("Sound " + i + ":" + audioSource.name + " ANOTHER PART"); //if audioSource is in another part of the vessel then applying constant muffling audioSource.outputAudioMixerGroup = audioMixer.InVesselGroup; } isRouted = true; } } } if (isRouted) { continue; } writeDebug("Sound " + i + ":" + audioSource.name + " OUTSIDE"); audioSource.outputAudioMixerGroup = audioMixer.OutsideGroup; } }
private bool isSoundInHelmet(AudioSource audioSource) { return(!StockAudio.IsAmbient(audioSource) && audioSource.transform.position == Vector3.zero); }