using System.Collections; using System.Linq; using System.Collections.Generic; using UnityEngine; using UnityEngine.Audio; public class AudioManager : MonoBehaviour { [Header("SoundEmitters pool")] [SerializeField] private SoundEmitterFactorySO _factory = default; [SerializeField] private SoundEmitterPoolSO _pool = default; [SerializeField] private int _initialSize = 10; [Header("Listening on channels")] [Tooltip("The SoundManager listens to this event, fired by objects in any scene, to play SFXs")] [SerializeField] private AudioCueEventChannelSO _SFXEventChannel = default; [Tooltip("The SoundManager listens to this event, fired by objects in any scene, to play Music")] [SerializeField] private AudioCueEventChannelSO _musicEventChannel = default; [Header("Audio control")] [SerializeField] private AudioMixer audioMixer = default; [Range(0f, 1f)] [SerializeField] private float _masterVolume = 1f; [Range(0f, 1f)] [SerializeField] private float _musicVolume = 1f; [Range(0f, 1f)] [SerializeField] private float _sfxVolume = 1f; private SoundEmitterList _soundEmitterList; private void Awake() { //TODO: Get the initial volume levels from the settings _soundEmitterList = new SoundEmitterList(); RegisterChannel(_SFXEventChannel); RegisterChannel(_musicEventChannel); //TODO: Treat music requests differently? _pool.Prewarm(_initialSize); _pool.SetParent(this.transform); } private void OnDestroy() { UnregisterChannel(_SFXEventChannel); UnregisterChannel(_musicEventChannel); } /// /// This is only used in the Editor, to debug volumes. /// It is called when any of the variables is changed, and will directly change the value of the volumes on the AudioMixer. /// void OnValidate() { if (Application.isPlaying) { SetGroupVolume("MasterVolume", _masterVolume); SetGroupVolume("MusicVolume", _musicVolume); SetGroupVolume("SFXVolume", _sfxVolume); } } public void SetGroupVolume(string parameterName, float normalizedVolume) { bool volumeSet = audioMixer.SetFloat(parameterName, NormalizedToMixerValue(normalizedVolume)); if (!volumeSet) Debug.LogError("The AudioMixer parameter was not found"); } public float GetGroupVolume(string parameterName) { if (audioMixer.GetFloat(parameterName, out float rawVolume)) { return MixerValueToNormalized(rawVolume); } else { Debug.LogError("The AudioMixer parameter was not found"); return 0f; } } private void RegisterChannel(AudioCueEventChannelSO audioCueEventChannel) { audioCueEventChannel.OnAudioCuePlayRequested += PlayAudioCue; audioCueEventChannel.OnAudioCueStopRequested += StopAudioCue; audioCueEventChannel.OnAudioCueFinishRequested += FinishAudioCue; } private void UnregisterChannel(AudioCueEventChannelSO audioCueEventChannel) { audioCueEventChannel.OnAudioCuePlayRequested += PlayAudioCue; audioCueEventChannel.OnAudioCueStopRequested += StopAudioCue; audioCueEventChannel.OnAudioCueFinishRequested += FinishAudioCue; } // Both MixerValueNormalized and NormalizedToMixerValue functions are used for easier transformations /// when using UI sliders normalized format private float MixerValueToNormalized(float mixerValue) { // We're assuming the range [-80dB to 0dB] becomes [0 to 1] return 1f + (mixerValue / 80f); } private float NormalizedToMixerValue(float normalizedValue) { // We're assuming the range [0 to 1] becomes [-80dB to 0dB] // This doesn't allow values over 0dB return (normalizedValue - 1f) * 80f; } /// /// Plays an AudioCue by requesting the appropriate number of SoundEmitters from the pool. /// public AudioCueKey PlayAudioCue(AudioCueSO audioCue, AudioConfigurationSO settings, Vector3 position = default) { AudioClip[] clipsToPlay = audioCue.GetClips(); SoundEmitter[] soundEmitterArray = new SoundEmitter[clipsToPlay.Length]; int nOfClips = clipsToPlay.Length; for (int i = 0; i < nOfClips; i++) { soundEmitterArray[i] = _pool.Request(); if (soundEmitterArray[i] != null) { soundEmitterArray[i].PlayAudioClip(clipsToPlay[i], settings, audioCue.looping, position); if (!audioCue.looping) soundEmitterArray[i].OnSoundFinishedPlaying += OnSoundEmitterFinishedPlaying; } } return _soundEmitterList.Add(audioCue, soundEmitterArray); } public bool FinishAudioCue(AudioCueKey emitterKey) { bool isFound = _soundEmitterList.Get(emitterKey, out SoundEmitter[] soundEmitters); if (isFound) { for (int i = 0; i < soundEmitters.Length; i++) { soundEmitters[i].Finish(); soundEmitters[i].OnSoundFinishedPlaying += OnSoundEmitterFinishedPlaying; } } return isFound; } public bool StopAudioCue(AudioCueKey emitterKey) { bool isFound = _soundEmitterList.Get(emitterKey, out SoundEmitter[] soundEmitters); if (isFound) { for (int i = 0; i < soundEmitters.Length; i++) { StopAndCleanEmitter(soundEmitters[i]); } _soundEmitterList.Remove(emitterKey); } return isFound; } private void OnSoundEmitterFinishedPlaying(SoundEmitter soundEmitter) { StopAndCleanEmitter(soundEmitter); } private void StopAndCleanEmitter(SoundEmitter soundEmitter) { if (soundEmitter.IsFinishing()) soundEmitter.OnSoundFinishedPlaying -= OnSoundEmitterFinishedPlaying; soundEmitter.Stop(); _pool.Return(soundEmitter); } //TODO: Add methods to play and cross-fade music, or to play individual sounds? }