using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Audio; using QFSW.MOP2; using UnityEngine.SceneManagement; using Sirenix.OdinInspector; namespace KairoEngine.SFX { // Todo: Remove playing SFX on scene change public class SoundEmitter : MonoBehaviour { public SphereCollider trigger; public AudioSource audioSource; [ReadOnly] public AudioClip audioClip; public SFXClip sfx; public string soundEmitterPrefabPool = ""; private float fadeInTime; private float fadeOutTime; private Coroutine fadeOutCoroutine; private bool done = false; /// /// play a sound at a location in the world using an Audio Clip. /// /// The audio clip to be played /// The Vector3 position to play the sound in the world /// Audio clip volume /// Radius for SoundSensors /// Audio Mixer Channel /// Trigger SoundSensors in world? public void Initialize(AudioClip audioClip, Vector3 location, float volume, float radius, AudioMixerGroup audioMixerGroup, bool world = true) { this.audioClip = audioClip; this.trigger.radius = radius; transform.position = location; this.audioSource.volume = volume; this.audioSource.outputAudioMixerGroup = audioMixerGroup; this.audioSource.clip = this.audioClip; this.done = false; if(world == false) { trigger.enabled = false; } this.audioSource.Play(); } /// /// Play a sound at a location in the world using a SFX Clip. /// /// SFX Clip with audio clip and parameters. /// Vector3 representing the location to play the sound. public void Initialize(SFXClip sfx, Vector3 location) { this.sfx = sfx; this.done = false; // Audio Clip if(sfx.sfxType == SFXClipType.Single) { // Single this.audioClip = sfx.clip; } else if(sfx.sfxType == SFXClipType.RandomList) { // Random from list this.audioClip = sfx.clips[Random.Range(0, sfx.clips.Count)]; } this.audioSource.clip = this.audioClip; // Sound Location transform.position = location; // Audio Mixer Channel this.audioSource.outputAudioMixerGroup = sfx.audioMixerChannel; // Loop if(sfx.sfxType == SFXClipType.Single) this.audioSource.loop = sfx.loop; // Volume this.audioSource.volume = sfx.volume + Random.Range(-sfx.volumeVariation, sfx.volumeVariation); // Pitch this.audioSource.pitch = sfx.pitch + Random.Range(-sfx.pitchVariation, sfx.pitchVariation); // Spatial or stereo audio if(sfx.soundSpace != SFXSpace.Stereo) { this.audioSource.spatialize = true; } else { this.audioSource.spatialize = false; this.audioSource.spatialBlend = 0; } // Pan if(sfx.soundSpace != SFXSpace.GameWorld) { // Stereo audio Pan this.audioSource.panStereo = sfx.pan + Random.Range(-sfx.panVariation, sfx.panVariation); } else { // 3D audio has no Pan this.audioSource.panStereo = 0; } // Spatial Blend if(sfx.soundSpace == SFXSpace.Mix) { this.audioSource.spatialBlend = sfx.spatialBlend + Random.Range(-sfx.spatialBlendVariation, sfx.spatialBlendVariation); } // Set Distance if(sfx.soundSpace == SFXSpace.GameWorld || sfx.soundSpace == SFXSpace.Mix) { this.audioSource.minDistance = sfx.minDistance; this.audioSource.maxDistance = sfx.maxDistance; } // Use world sensor if(sfx.sensorDetection == true) { trigger.enabled = true; this.trigger.radius = sfx.soundRadius; } else trigger.enabled = true; // Set Fade In if(sfx.fadeIn) { this.audioSource.volume = 0f; this.fadeInTime = 0f; StartCoroutine(FadeIn()); } // Set Fade out if(sfx.fadeOut) { fadeOutTime = 0f; fadeOutCoroutine = StartCoroutine(FadeOut()); } // Set Remove on scene change // ! Turn this on when dependencies are solved //if(sfx.removeOnSceneChange) TransitionEvents.OnChangeScene += OnSceneChanged; // Trim and Play the audio clip if(sfx.trim) this.audioSource.time = sfx.trimStartDuration; this.audioSource.Play(); if(sfx.trim) this.audioSource.SetScheduledEndTime(AudioSettings.dspTime +(sfx.trimEndDuration - sfx.trimStartDuration)); } void Update() { if(done == true) return; // Destroy this Gameobject when the sound is done playing if(audioSource.isPlaying == false) { if(sfx == null) return; if(sfx.loop && sfx.sfxType == SFXClipType.RandomList) { Initialize(sfx, transform.position); } else { Remove(); } } } private void Remove() { SoundController.soundEmitters.Remove(this); // ! Enable when dependencies are resolved //if(sfx.removeOnSceneChange) TransitionEvents.OnChangeScene -= OnSceneChanged; sfx = null; if(MasterObjectPooler.Instance != null && soundEmitterPrefabPool != "") MasterObjectPooler.Instance.GetPool(soundEmitterPrefabPool).Release(this.gameObject); else Destroy(this.gameObject); } IEnumerator FadeIn() { float start = Time.realtimeSinceStartup; while(Time.realtimeSinceStartup <= (start + (sfx.fadeInDuration))) { fadeInTime = (Time.realtimeSinceStartup - start); this.audioSource.volume = Mathf.Lerp(0, 1, fadeInTime/sfx.fadeInDuration); yield return null; } this.audioSource.volume = 1; } IEnumerator FadeOut(bool wait = true, bool remove = false, float time = 0) { fadeOutTime = 0f; if(time == 0) time = sfx.fadeOutDuration; if(wait) yield return new WaitForSecondsRealtime((this.audioSource.clip.length - this.audioSource.time) - time); if(sfx == null) yield return 0; float start = Time.realtimeSinceStartup; while(Time.realtimeSinceStartup < (start + (time))) { fadeOutTime = (Time.realtimeSinceStartup - start); this.audioSource.volume = Mathf.Lerp(1, 0, fadeOutTime/time); yield return null; } this.audioSource.volume = 0; if(remove) Remove(); } // ! Enable when dependencies are solved /* private void OnSceneChanged(SceneChangeData sceneData, float time) { StopCoroutine(fadeOutCoroutine); done = true; if(sfx == null) return; if(sfx.fadeOut) StartCoroutine(FadeOut(false, true, time)); else Remove(); } */ } }