You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
CrowdControl/Assets/Feel/MMTools/Tools/MMAudio/MMSoundManager/MMSoundManager.cs

1218 lines
39 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.SceneManagement;
namespace MoreMountains.Tools
{
/// <summary>
/// A simple yet powerful sound manager, that will let you play sounds with an event based approach and performance in mind.
///
/// Features :
///
/// - Play/stop/pause/resume/free sounds
/// - Full control : loop, volume, pitch, pan, spatial blend, bypasses, priority, reverb, doppler level, spread, rolloff mode, distance
/// - 2D & 3D spatial support
/// - Built-in pooling, automatically recycle a set of audio sources for maximum performance
/// - Built in audio mixer and groups, with ready-made tracks (Master, Music, SFX, UI), and options to play on more groups if needed
/// - Stop/pause/resume/free entire tracks
/// - Stop/pause/resume/free all sounds at once
/// - Mute / set volume entire tracks
/// - Save and load settings, with auto save / auto load mechanics built-in
/// - Fade in/out sounds
/// - Fade in/out tracks
/// - Solo mode : play a sound with one or all tracks muted, then unmute them automatically afterwards
/// - PlayOptions struct
/// - Option to have sounds persist across scene loads and from scene to scene
/// - Inspector controls for tracks (volume, mute, unmute, play, pause, stop, resume, free, number of sounds)
/// - MMSfxEvents
/// - MMSoundManagerEvents : mute track, control track, save, load, reset, stop persistent sounds
/// </summary>
[AddComponentMenu("More Mountains/Tools/Audio/MMSoundManager")]
public class MMSoundManager : MMPersistentSingleton<MMSoundManager>,
MMEventListener<MMSoundManagerTrackEvent>,
MMEventListener<MMSoundManagerEvent>,
MMEventListener<MMSoundManagerSoundControlEvent>,
MMEventListener<MMSoundManagerSoundFadeEvent>,
MMEventListener<MMSoundManagerAllSoundsControlEvent>,
MMEventListener<MMSoundManagerTrackFadeEvent>
{
/// the possible ways to manage a track
public enum MMSoundManagerTracks { Sfx, Music, UI, Master, Other}
[Header("Settings")]
/// the current sound settings
[Tooltip("the current sound settings ")]
public MMSoundManagerSettingsSO settingsSo;
[Header("Pool")]
/// the size of the AudioSource pool, a reserve of ready-to-use sources that will get recycled. Should be approximately equal to the maximum amount of sounds that you expect to be playing at once
[Tooltip("the size of the AudioSource pool, a reserve of ready-to-use sources that will get recycled. Should be approximately equal to the maximum amount of sounds that you expect to be playing at once")]
public int AudioSourcePoolSize = 10;
/// whether or not the pool can expand (create new audiosources on demand). In a perfect world you'd want to avoid this, and have a sufficiently big pool, to avoid costly runtime creations.
[Tooltip("whether or not the pool can expand (create new audiosources on demand). In a perfect world you'd want to avoid this, and have a sufficiently big pool, to avoid costly runtime creations.")]
public bool PoolCanExpand = true;
protected MMSoundManagerAudioPool _pool;
protected GameObject _tempAudioSourceGameObject;
protected MMSoundManagerSound _sound;
protected List<MMSoundManagerSound> _sounds;
protected AudioSource _tempAudioSource;
protected Dictionary<AudioSource, Coroutine> _fadeSoundCoroutines;
protected Dictionary<MMSoundManagerTracks, Coroutine> _fadeTrackCoroutines;
#region Initialization
/// <summary>
/// On Awake we initialize our manager
/// </summary>
protected override void Awake()
{
base.Awake();
InitializeSoundManager();
}
/// <summary>
/// On Start we load and apply our saved settings if needed.
/// This is done on Start and not Awake because of a bug in Unity's AudioMixer API
/// </summary>
protected virtual void Start()
{
if ((settingsSo != null) && (settingsSo.Settings.AutoLoad))
{
settingsSo.LoadSoundSettings();
}
}
/// <summary>
/// Initializes the pool, fills it, registers to the scene loaded event
/// </summary>
protected virtual void InitializeSoundManager()
{
if (_pool == null)
{
_pool = new MMSoundManagerAudioPool();
}
_sounds = new List<MMSoundManagerSound>();
_pool.FillAudioSourcePool(AudioSourcePoolSize, this.transform);
_fadeSoundCoroutines = new Dictionary<AudioSource, Coroutine>();
_fadeTrackCoroutines = new Dictionary<MMSoundManagerTracks, Coroutine>();
}
#endregion
#region PlaySound
/// <summary>
/// Plays a sound, separate options object signature
/// </summary>
/// <param name="audioClip"></param>
/// <param name="options"></param>
/// <returns></returns>
public virtual AudioSource PlaySound(AudioClip audioClip, MMSoundManagerPlayOptions options)
{
return PlaySound(audioClip, options.MmSoundManagerTrack, options.Location,
options.Loop, options.Volume, options.ID,
options.Fade, options.FadeInitialVolume, options.FadeDuration, options.FadeTween,
options.Persistent,
options.RecycleAudioSource, options.AudioGroup,
options.Pitch, options.PanStereo, options.SpatialBlend,
options.SoloSingleTrack, options.SoloAllTracks, options.AutoUnSoloOnEnd,
options.BypassEffects, options.BypassListenerEffects, options.BypassReverbZones, options.Priority,
options.ReverbZoneMix,
options.DopplerLevel, options.Spread, options.RolloffMode, options.MinDistance, options.MaxDistance,
options.DoNotAutoRecycleIfNotDonePlaying, options.PlaybackTime, options.PlaybackDuration, options.AttachToTransform,
options.UseSpreadCurve, options.SpreadCurve, options.UseCustomRolloffCurve, options.CustomRolloffCurve,
options.UseSpatialBlendCurve, options.SpatialBlendCurve, options.UseReverbZoneMixCurve, options.ReverbZoneMixCurve
);
}
/// <summary>
/// Plays a sound, signature with all options
/// </summary>
/// <param name="audioClip"></param>
/// <param name="mmSoundManagerTrack"></param>
/// <param name="location"></param>
/// <param name="loop"></param>
/// <param name="volume"></param>
/// <param name="ID"></param>
/// <param name="fade"></param>
/// <param name="fadeInitialVolume"></param>
/// <param name="fadeDuration"></param>
/// <param name="fadeTween"></param>
/// <param name="persistent"></param>
/// <param name="recycleAudioSource"></param>
/// <param name="audioGroup"></param>
/// <param name="pitch"></param>
/// <param name="panStereo"></param>
/// <param name="spatialBlend"></param>
/// <param name="soloSingleTrack"></param>
/// <param name="soloAllTracks"></param>
/// <param name="autoUnSoloOnEnd"></param>
/// <param name="bypassEffects"></param>
/// <param name="bypassListenerEffects"></param>
/// <param name="bypassReverbZones"></param>
/// <param name="priority"></param>
/// <param name="reverbZoneMix"></param>
/// <param name="dopplerLevel"></param>
/// <param name="spread"></param>
/// <param name="rolloffMode"></param>
/// <param name="minDistance"></param>
/// <param name="maxDistance"></param>
/// <returns></returns>
public virtual AudioSource PlaySound(AudioClip audioClip, MMSoundManagerTracks mmSoundManagerTrack, Vector3 location,
bool loop = false, float volume = 1.0f, int ID = 0,
bool fade = false, float fadeInitialVolume = 0f, float fadeDuration = 1f, MMTweenType fadeTween = null,
bool persistent = false,
AudioSource recycleAudioSource = null, AudioMixerGroup audioGroup = null,
float pitch = 1f, float panStereo = 0f, float spatialBlend = 0.0f,
bool soloSingleTrack = false, bool soloAllTracks = false, bool autoUnSoloOnEnd = false,
bool bypassEffects = false, bool bypassListenerEffects = false, bool bypassReverbZones = false, int priority = 128, float reverbZoneMix = 1f,
float dopplerLevel = 1f, int spread = 0, AudioRolloffMode rolloffMode = AudioRolloffMode.Logarithmic, float minDistance = 1f, float maxDistance = 500f,
bool doNotAutoRecycleIfNotDonePlaying = false, float playbackTime = 0f, float playbackDuration = 0f, Transform attachToTransform = null,
bool useSpreadCurve = false, AnimationCurve spreadCurve = null, bool useCustomRolloffCurve = false, AnimationCurve customRolloffCurve = null,
bool useSpatialBlendCurve = false, AnimationCurve spatialBlendCurve = null, bool useReverbZoneMixCurve = false, AnimationCurve reverbZoneMixCurve = null
)
{
if (this == null) { return null; }
if (!audioClip) { return null; }
// audio source setup ---------------------------------------------------------------------------------
// we reuse an audiosource if one is passed in parameters
AudioSource audioSource = recycleAudioSource;
if (audioSource == null)
{
// we pick an idle audio source from the pool if possible
audioSource = _pool.GetAvailableAudioSource(PoolCanExpand, this.transform);
if ((audioSource != null) && (!loop))
{
recycleAudioSource = audioSource;
// we destroy the host after the clip has played (if it not tag for reusability.
StartCoroutine(_pool.AutoDisableAudioSource(audioClip.length / Mathf.Abs(pitch), audioSource, audioClip, doNotAutoRecycleIfNotDonePlaying, playbackTime, playbackDuration));
}
}
// we create an audio source if needed
if (audioSource == null)
{
_tempAudioSourceGameObject = new GameObject("MMAudio_"+audioClip.name);
SceneManager.MoveGameObjectToScene(_tempAudioSourceGameObject, this.gameObject.scene);
audioSource = _tempAudioSourceGameObject.AddComponent<AudioSource>();
}
// audio source settings ---------------------------------------------------------------------------------
audioSource.transform.position = location;
audioSource.clip = audioClip;
audioSource.pitch = pitch;
audioSource.spatialBlend = spatialBlend;
audioSource.panStereo = panStereo;
audioSource.loop = loop;
audioSource.bypassEffects = bypassEffects;
audioSource.bypassListenerEffects = bypassListenerEffects;
audioSource.bypassReverbZones = bypassReverbZones;
audioSource.priority = priority;
audioSource.reverbZoneMix = reverbZoneMix;
audioSource.dopplerLevel = dopplerLevel;
audioSource.spread = spread;
audioSource.rolloffMode = rolloffMode;
audioSource.minDistance = minDistance;
audioSource.maxDistance = maxDistance;
audioSource.time = playbackTime;
// curves
if (useSpreadCurve) { audioSource.SetCustomCurve(AudioSourceCurveType.Spread, spreadCurve); }
if (useCustomRolloffCurve) { audioSource.SetCustomCurve(AudioSourceCurveType.CustomRolloff, customRolloffCurve); }
if (useSpatialBlendCurve) { audioSource.SetCustomCurve(AudioSourceCurveType.SpatialBlend, spatialBlendCurve); }
if (useReverbZoneMixCurve) { audioSource.SetCustomCurve(AudioSourceCurveType.ReverbZoneMix, reverbZoneMixCurve); }
// attaching to target
if (attachToTransform != null)
{
MMFollowTarget followTarget = audioSource.gameObject.MMGetComponentNoAlloc<MMFollowTarget>();
if (followTarget == null)
{
followTarget = audioSource.gameObject.AddComponent<MMFollowTarget>();
}
followTarget.Target = attachToTransform;
followTarget.InterpolatePosition = false;
followTarget.InterpolateRotation = false;
followTarget.InterpolateScale = false;
followTarget.FollowRotation = false;
followTarget.FollowScale = false;
followTarget.enabled = true;
}
// track and volume ---------------------------------------------------------------------------------
if (settingsSo != null)
{
audioSource.outputAudioMixerGroup = settingsSo.MasterAudioMixerGroup;
switch (mmSoundManagerTrack)
{
case MMSoundManagerTracks.Master:
audioSource.outputAudioMixerGroup = settingsSo.MasterAudioMixerGroup;
break;
case MMSoundManagerTracks.Music:
audioSource.outputAudioMixerGroup = settingsSo.MusicAudioMixerGroup;
break;
case MMSoundManagerTracks.Sfx:
audioSource.outputAudioMixerGroup = settingsSo.SfxAudioMixerGroup;
break;
case MMSoundManagerTracks.UI:
audioSource.outputAudioMixerGroup = settingsSo.UIAudioMixerGroup;
break;
}
}
if (audioGroup) { audioSource.outputAudioMixerGroup = audioGroup; }
audioSource.volume = volume;
// we start playing the sound
audioSource.Play();
// we destroy the host after the clip has played if it was a one time AS.
if (!loop && !recycleAudioSource)
{
float destroyDelay = (playbackDuration > 0) ? playbackDuration : audioClip.length - playbackTime;
Destroy(_tempAudioSourceGameObject, destroyDelay);
}
// we fade the sound in if needed
if (fade)
{
FadeSound(audioSource, fadeDuration, fadeInitialVolume, volume, fadeTween);
}
// we handle soloing
if (soloSingleTrack)
{
MuteSoundsOnTrack(mmSoundManagerTrack, true, 0f);
audioSource.mute = false;
if (autoUnSoloOnEnd)
{
MuteSoundsOnTrack(mmSoundManagerTrack, false, audioClip.length);
}
}
else if (soloAllTracks)
{
MuteAllSounds();
audioSource.mute = false;
if (autoUnSoloOnEnd)
{
StartCoroutine(MuteAllSoundsCoroutine(audioClip.length - playbackTime, false));
}
}
// we prepare for storage
_sound.ID = ID;
_sound.Track = mmSoundManagerTrack;
_sound.Source = audioSource;
_sound.Persistent = persistent;
_sound.PlaybackTime = playbackTime;
_sound.PlaybackDuration = playbackDuration;
// we check if that audiosource is already being tracked in _sounds
bool alreadyIn = false;
for (int i = 0; i < _sounds.Count; i++)
{
if (_sounds[i].Source == audioSource)
{
_sounds[i] = _sound;
alreadyIn = true;
}
}
if (!alreadyIn)
{
_sounds.Add(_sound);
}
// we return the audiosource reference
return audioSource;
}
#endregion
#region SoundControls
/// <summary>
/// Pauses the specified audiosource
/// </summary>
/// <param name="source"></param>
public virtual void PauseSound(AudioSource source)
{
source.Pause();
}
/// <summary>
/// resumes play on the specified audio source
/// </summary>
/// <param name="source"></param>
public virtual void ResumeSound(AudioSource source)
{
source.Play();
}
/// <summary>
/// Stops the specified audio source
/// </summary>
/// <param name="source"></param>
public virtual void StopSound(AudioSource source)
{
source.Stop();
}
/// <summary>
/// Frees a specific sound, stopping it and returning it to the pool
/// </summary>
/// <param name="source"></param>
public virtual void FreeSound(AudioSource source)
{
source.Stop();
if (!_pool.FreeSound(source))
{
Destroy(source.gameObject);
}
}
#endregion
#region TrackControls
/// <summary>
/// Mutes an entire track
/// </summary>
/// <param name="track"></param>
public virtual void MuteTrack(MMSoundManagerTracks track)
{
ControlTrack(track, ControlTrackModes.Mute, 0f);
}
/// <summary>
/// Unmutes an entire track
/// </summary>
/// <param name="track"></param>
public virtual void UnmuteTrack(MMSoundManagerTracks track)
{
ControlTrack(track, ControlTrackModes.Unmute, 0f);
}
/// <summary>
/// Sets the volume of an entire track
/// </summary>
/// <param name="track"></param>
/// <param name="volume"></param>
public virtual void SetTrackVolume(MMSoundManagerTracks track, float volume)
{
ControlTrack(track, ControlTrackModes.SetVolume, volume);
}
/// <summary>
/// Returns the current volume of a track
/// </summary>
/// <param name="track"></param>
/// <param name="volume"></param>
public virtual float GetTrackVolume(MMSoundManagerTracks track, bool mutedVolume)
{
switch (track)
{
case MMSoundManagerTracks.Master:
if (mutedVolume)
{
return settingsSo.Settings.MutedMasterVolume;
}
else
{
return settingsSo.Settings.MasterVolume;
}
case MMSoundManagerTracks.Music:
if (mutedVolume)
{
return settingsSo.Settings.MutedMusicVolume;
}
else
{
return settingsSo.Settings.MusicVolume;
}
case MMSoundManagerTracks.Sfx:
if (mutedVolume)
{
return settingsSo.Settings.MutedSfxVolume;
}
else
{
return settingsSo.Settings.SfxVolume;
}
case MMSoundManagerTracks.UI:
if (mutedVolume)
{
return settingsSo.Settings.MutedUIVolume;
}
else
{
return settingsSo.Settings.UIVolume;
}
}
return 1f;
}
/// <summary>
/// Pauses all sounds on a track
/// </summary>
/// <param name="track"></param>
public virtual void PauseTrack(MMSoundManagerTracks track)
{
foreach (MMSoundManagerSound sound in _sounds)
{
if (sound.Track == track)
{
sound.Source.Pause();
}
}
}
/// <summary>
/// Plays or resumes all sounds on a track
/// </summary>
/// <param name="track"></param>
public virtual void PlayTrack(MMSoundManagerTracks track)
{
foreach (MMSoundManagerSound sound in _sounds)
{
if (sound.Track == track)
{
sound.Source.Play();
}
}
}
/// <summary>
/// Stops all sounds on a track
/// </summary>
/// <param name="track"></param>
public virtual void StopTrack(MMSoundManagerTracks track)
{
foreach (MMSoundManagerSound sound in _sounds)
{
if (sound.Track == track)
{
sound.Source.Stop();
}
}
}
/// <summary>
/// Returns true if sounds are currently playing on that track
/// </summary>
/// <param name="track"></param>
public virtual bool HasSoundsPlaying(MMSoundManagerTracks track)
{
foreach (MMSoundManagerSound sound in _sounds)
{
if ((sound.Track == track) && (sound.Source.isPlaying))
{
return true;
}
}
return false;
}
/// <summary>
/// Returns a list of MMSoundManagerSounds for the specified track
/// </summary>
/// <param name="track">the track on which to grab the playing sounds</param>
/// <returns></returns>
public virtual List<MMSoundManagerSound> GetSoundsPlaying(MMSoundManagerTracks track)
{
List<MMSoundManagerSound> soundsPlaying = new List<MMSoundManagerSound>();
foreach (MMSoundManagerSound sound in _sounds)
{
if ((sound.Track == track) && (sound.Source.isPlaying))
{
soundsPlaying.Add(sound);
}
}
return soundsPlaying;
}
/// <summary>
/// Stops all sounds on a track, and returns them to the pool
/// </summary>
/// <param name="track"></param>
public virtual void FreeTrack(MMSoundManagerTracks track)
{
foreach (MMSoundManagerSound sound in _sounds)
{
if (sound.Track == track)
{
sound.Source.Stop();
sound.Source.gameObject.SetActive(false);
}
}
}
/// <summary>
/// Mutes the music track, QoL method ready to bind to a UnityEvent
/// </summary>
public virtual void MuteMusic() { MuteTrack(MMSoundManagerTracks.Music); }
/// <summary>
/// Unmutes the music track, QoL method ready to bind to a UnityEvent
/// </summary>
public virtual void UnmuteMusic() { UnmuteTrack(MMSoundManagerTracks.Music); }
/// <summary>
/// Mutes the sfx track, QoL method ready to bind to a UnityEvent
/// </summary>
public virtual void MuteSfx() { MuteTrack(MMSoundManagerTracks.Sfx); }
/// <summary>
/// Unmutes the sfx track, QoL method ready to bind to a UnityEvent
/// </summary>
public virtual void UnmuteSfx() { UnmuteTrack(MMSoundManagerTracks.Sfx); }
/// <summary>
/// Mutes the UI track, QoL method ready to bind to a UnityEvent
/// </summary>
public virtual void MuteUI() { MuteTrack(MMSoundManagerTracks.UI); }
/// <summary>
/// Unmutes the UI track, QoL method ready to bind to a UnityEvent
/// </summary>
public virtual void UnmuteUI() { UnmuteTrack(MMSoundManagerTracks.UI); }
/// <summary>
/// Mutes the master track, QoL method ready to bind to a UnityEvent
/// </summary>
public virtual void MuteMaster() { MuteTrack(MMSoundManagerTracks.Master); }
/// <summary>
/// Unmutes the master track, QoL method ready to bind to a UnityEvent
/// </summary>
public virtual void UnmuteMaster() { UnmuteTrack(MMSoundManagerTracks.Master); }
/// <summary>
/// Sets the volume of the Music track to the specified value, QoL method, ready to bind to a UnityEvent
/// </summary>
public virtual void SetVolumeMusic(float newVolume) { SetTrackVolume(MMSoundManagerTracks.Music, newVolume);}
/// <summary>
/// Sets the volume of the SFX track to the specified value, QoL method, ready to bind to a UnityEvent
/// </summary>
public virtual void SetVolumeSfx(float newVolume) { SetTrackVolume(MMSoundManagerTracks.Sfx, newVolume);}
/// <summary>
/// Sets the volume of the UI track to the specified value, QoL method, ready to bind to a UnityEvent
/// </summary>
public virtual void SetVolumeUI(float newVolume) { SetTrackVolume(MMSoundManagerTracks.UI, newVolume);}
/// <summary>
/// Sets the volume of the Master track to the specified value, QoL method, ready to bind to a UnityEvent
/// </summary>
public virtual void SetVolumeMaster(float newVolume) { SetTrackVolume(MMSoundManagerTracks.Master, newVolume);}
/// <summary>
/// Returns true if the specified track is muted, false otherwise
/// </summary>
/// <param name="track"></param>
/// <returns></returns>
public virtual bool IsMuted(MMSoundManagerTracks track)
{
switch (track)
{
case MMSoundManagerTracks.Master:
return !settingsSo.Settings.MasterOn;
case MMSoundManagerTracks.Music:
return !settingsSo.Settings.MusicOn;
case MMSoundManagerTracks.Sfx:
return !settingsSo.Settings.SfxOn;
case MMSoundManagerTracks.UI:
return !settingsSo.Settings.UIOn;
}
return false;
}
/// <summary>
/// A method that will let you mute/unmute a track, or set it to a specified volume
/// </summary>
public enum ControlTrackModes { Mute, Unmute, SetVolume }
protected virtual void ControlTrack(MMSoundManagerTracks track, ControlTrackModes trackMode, float volume = 0.5f)
{
string target = "";
float savedVolume = 0f;
switch (track)
{
case MMSoundManagerTracks.Master:
target = settingsSo.Settings.MasterVolumeParameter;
if (trackMode == ControlTrackModes.Mute) { settingsSo.TargetAudioMixer.GetFloat(target, out settingsSo.Settings.MutedMasterVolume); settingsSo.Settings.MasterOn = false; }
else if (trackMode == ControlTrackModes.Unmute) { savedVolume = settingsSo.Settings.MutedMasterVolume; settingsSo.Settings.MasterOn = true; }
break;
case MMSoundManagerTracks.Music:
target = settingsSo.Settings.MusicVolumeParameter;
if (trackMode == ControlTrackModes.Mute) { settingsSo.TargetAudioMixer.GetFloat(target, out settingsSo.Settings.MutedMusicVolume); settingsSo.Settings.MusicOn = false; }
else if (trackMode == ControlTrackModes.Unmute) { savedVolume = settingsSo.Settings.MutedMusicVolume; settingsSo.Settings.MusicOn = true; }
break;
case MMSoundManagerTracks.Sfx:
target = settingsSo.Settings.SfxVolumeParameter;
if (trackMode == ControlTrackModes.Mute) { settingsSo.TargetAudioMixer.GetFloat(target, out settingsSo.Settings.MutedSfxVolume); settingsSo.Settings.SfxOn = false; }
else if (trackMode == ControlTrackModes.Unmute) { savedVolume = settingsSo.Settings.MutedSfxVolume; settingsSo.Settings.SfxOn = true; }
break;
case MMSoundManagerTracks.UI:
target = settingsSo.Settings.UIVolumeParameter;
if (trackMode == ControlTrackModes.Mute) { settingsSo.TargetAudioMixer.GetFloat(target, out settingsSo.Settings.MutedUIVolume); settingsSo.Settings.UIOn = false; }
else if (trackMode == ControlTrackModes.Unmute) { savedVolume = settingsSo.Settings.MutedUIVolume; settingsSo.Settings.UIOn = true; }
break;
}
switch (trackMode)
{
case ControlTrackModes.Mute:
settingsSo.SetTrackVolume(track, 0f);
break;
case ControlTrackModes.Unmute:
settingsSo.SetTrackVolume(track, settingsSo.MixerVolumeToNormalized(savedVolume));
break;
case ControlTrackModes.SetVolume:
settingsSo.SetTrackVolume(track, volume);
break;
}
settingsSo.GetTrackVolumes();
if (settingsSo.Settings.AutoSave)
{
settingsSo.SaveSoundSettings();
}
}
#endregion
#region Fades
/// <summary>
/// Fades an entire track over the specified duration towards the desired finalVolume
/// </summary>
/// <param name="track"></param>
/// <param name="duration"></param>
/// <param name="initialVolume"></param>
/// <param name="finalVolume"></param>
/// <param name="tweenType"></param>
public virtual void FadeTrack(MMSoundManagerTracks track, float duration, float initialVolume = 0f, float finalVolume = 1f, MMTweenType tweenType = null)
{
Coroutine coroutine = StartCoroutine(FadeTrackCoroutine(track, duration, initialVolume, finalVolume, tweenType));
_fadeTrackCoroutines[track] = coroutine;
}
/// <summary>
/// Fades a target sound towards a final volume over time
/// </summary>
/// <param name="source"></param>
/// <param name="duration"></param>
/// <param name="initialVolume"></param>
/// <param name="finalVolume"></param>
/// <param name="tweenType"></param>
public virtual void FadeSound(AudioSource source, float duration, float initialVolume, float finalVolume, MMTweenType tweenType)
{
Coroutine coroutine = StartCoroutine(FadeCoroutine(source, duration, initialVolume, finalVolume, tweenType));
_fadeSoundCoroutines[source] = coroutine;
}
/// <summary>
/// Stops any fade currently happening on the specified track
/// </summary>
/// <param name="track"></param>
public virtual void StopFadeTrack(MMSoundManagerTracks track)
{
Coroutine outCoroutine;
if (_fadeTrackCoroutines.TryGetValue(track, out outCoroutine))
{
StopCoroutine(outCoroutine);
_fadeTrackCoroutines.Remove(track);
}
}
/// <summary>
/// Stops any fade currently happening on the specified sound
/// </summary>
/// <param name="source"></param>
public virtual void StopFadeSound(AudioSource source)
{
Coroutine outCoroutine;
if (_fadeSoundCoroutines.TryGetValue(source, out outCoroutine))
{
StopCoroutine(outCoroutine);
_fadeSoundCoroutines.Remove(source);
}
}
/// <summary>
/// Fades an entire track over time
/// </summary>
/// <param name="track"></param>
/// <param name="duration"></param>
/// <param name="initialVolume"></param>
/// <param name="finalVolume"></param>
/// <param name="tweenType"></param>
/// <returns></returns>
protected virtual IEnumerator FadeTrackCoroutine(MMSoundManagerTracks track, float duration, float initialVolume, float finalVolume, MMTweenType tweenType)
{
float startedAt = Time.unscaledTime;
if (tweenType == null)
{
tweenType = new MMTweenType(MMTween.MMTweenCurve.EaseInOutQuartic);
}
while (Time.unscaledTime - startedAt <= duration)
{
float elapsedTime = Time.unscaledTime - startedAt;
float newVolume = MMTween.Tween(elapsedTime, 0f, duration, initialVolume, finalVolume, tweenType);
settingsSo.SetTrackVolume(track, newVolume);
yield return null;
}
settingsSo.SetTrackVolume(track, finalVolume);
}
/// <summary>
/// Fades an audiosource's volume over time
/// </summary>
/// <param name="source"></param>
/// <param name="duration"></param>
/// <param name="initialVolume"></param>
/// <param name="finalVolume"></param>
/// <param name="tweenType"></param>
/// <returns></returns>
protected virtual IEnumerator FadeCoroutine(AudioSource source, float duration, float initialVolume, float finalVolume, MMTweenType tweenType)
{
float startedAt = Time.unscaledTime;
if (tweenType == null)
{
tweenType = new MMTweenType(MMTween.MMTweenCurve.EaseInOutQuartic);
}
while (Time.unscaledTime - startedAt <= duration)
{
float elapsedTime = Time.unscaledTime - startedAt;
float newVolume = MMTween.Tween(elapsedTime, 0f, duration, initialVolume, finalVolume, tweenType);
source.volume = newVolume;
yield return null;
}
source.volume = finalVolume;
}
#endregion
#region Solo
/// <summary>
/// Mutes all sounds playing on a specific track
/// </summary>
/// <param name="track"></param>
/// <param name="mute"></param>
/// <param name="delay"></param>
public virtual void MuteSoundsOnTrack(MMSoundManagerTracks track, bool mute, float delay = 0f)
{
StartCoroutine(MuteSoundsOnTrackCoroutine(track, mute, delay));
}
/// <summary>
/// Mutes all sounds playing on the MMSoundManager
/// </summary>
/// <param name="mute"></param>
public virtual void MuteAllSounds(bool mute = true)
{
StartCoroutine(MuteAllSoundsCoroutine(0f, mute));
}
/// <summary>
/// Mutes all sounds on the specified track after an optional delay
/// </summary>
/// <param name="track"></param>
/// <param name="mute"></param>
/// <param name="delay"></param>
/// <returns></returns>
protected virtual IEnumerator MuteSoundsOnTrackCoroutine(MMSoundManagerTracks track, bool mute, float delay)
{
if (delay > 0)
{
yield return MMCoroutine.WaitForUnscaled(delay);
}
foreach (MMSoundManagerSound sound in _sounds)
{
if (sound.Track == track)
{
sound.Source.mute = mute;
}
}
}
/// <summary>
/// Mutes all sounds after an optional delay
/// </summary>
/// <param name="delay"></param>
/// <param name="mute"></param>
/// <returns></returns>
protected virtual IEnumerator MuteAllSoundsCoroutine(float delay, bool mute = true)
{
if (delay > 0)
{
yield return MMCoroutine.WaitForUnscaled(delay);
}
foreach (MMSoundManagerSound sound in _sounds)
{
sound.Source.mute = mute;
}
}
#endregion
#region Find
/// <summary>
/// Returns an audio source played with the specified ID, if one is found
/// </summary>
/// <param name="ID"></param>
/// <returns></returns>
public virtual AudioSource FindByID(int ID)
{
foreach (MMSoundManagerSound sound in _sounds)
{
if (sound.ID == ID)
{
return sound.Source;
}
}
return null;
}
/// <summary>
/// Returns an audio source played with the specified ID, if one is found
/// </summary>
/// <param name="ID"></param>
/// <returns></returns>
public virtual AudioSource FindByClip(AudioClip clip)
{
foreach (MMSoundManagerSound sound in _sounds)
{
if (sound.Source.clip == clip)
{
return sound.Source;
}
}
return null;
}
#endregion
#region AllSoundsControls
/// <summary>
/// Pauses all sounds playing on the MMSoundManager
/// </summary>
public virtual void PauseAllSounds()
{
foreach (MMSoundManagerSound sound in _sounds)
{
sound.Source.Pause();
}
}
/// <summary>
/// Plays all sounds playing on the MMSoundManager
/// </summary>
public virtual void PlayAllSounds()
{
foreach (MMSoundManagerSound sound in _sounds)
{
sound.Source.Play();
}
}
/// <summary>
/// Stops all sounds playing on the MMSoundManager
/// </summary>
public virtual void StopAllSounds()
{
foreach (MMSoundManagerSound sound in _sounds)
{
sound.Source.Stop();
}
}
/// <summary>
/// Stops all sounds and returns them to the pool
/// </summary>
public virtual void FreeAllSounds()
{
foreach (MMSoundManagerSound sound in _sounds)
{
if (sound.Source != null)
{
FreeSound(sound.Source);
}
}
}
/// <summary>
/// Stops all sounds except the persistent ones, and returns them to the pool
/// </summary>
public virtual void FreeAllSoundsButPersistent()
{
foreach (MMSoundManagerSound sound in _sounds)
{
if ((!sound.Persistent) && (sound.Source != null))
{
FreeSound(sound.Source);
}
}
}
/// <summary>
/// Stops all looping sounds and returns them to the pool
/// </summary>
public virtual void FreeAllLoopingSounds()
{
foreach (MMSoundManagerSound sound in _sounds)
{
if ((sound.Source.loop) && (sound.Source != null))
{
FreeSound(sound.Source);
}
}
}
#endregion
#region Events
/// <summary>
/// Registered on enable, triggers every time a new scene is loaded
/// At which point we free all sounds except the persistent ones
/// </summary>
protected virtual void OnSceneLoaded(Scene arg0, LoadSceneMode loadSceneMode)
{
FreeAllSoundsButPersistent();
}
public virtual void OnMMEvent(MMSoundManagerTrackEvent soundManagerTrackEvent)
{
switch (soundManagerTrackEvent.TrackEventType)
{
case MMSoundManagerTrackEventTypes.MuteTrack:
MuteTrack(soundManagerTrackEvent.Track);
break;
case MMSoundManagerTrackEventTypes.UnmuteTrack:
UnmuteTrack(soundManagerTrackEvent.Track);
break;
case MMSoundManagerTrackEventTypes.SetVolumeTrack:
SetTrackVolume(soundManagerTrackEvent.Track, soundManagerTrackEvent.Volume);
break;
case MMSoundManagerTrackEventTypes.PlayTrack:
PlayTrack(soundManagerTrackEvent.Track);
break;
case MMSoundManagerTrackEventTypes.PauseTrack:
PauseTrack(soundManagerTrackEvent.Track);
break;
case MMSoundManagerTrackEventTypes.StopTrack:
StopTrack(soundManagerTrackEvent.Track);
break;
case MMSoundManagerTrackEventTypes.FreeTrack:
FreeTrack(soundManagerTrackEvent.Track);
break;
}
}
public virtual void OnMMEvent(MMSoundManagerEvent soundManagerEvent)
{
switch (soundManagerEvent.EventType)
{
case MMSoundManagerEventTypes.SaveSettings:
SaveSettings();
break;
case MMSoundManagerEventTypes.LoadSettings:
settingsSo.LoadSoundSettings();
break;
case MMSoundManagerEventTypes.ResetSettings:
settingsSo.ResetSoundSettings();
break;
}
}
/// <summary>
/// Save sound settings to file
/// </summary>
public virtual void SaveSettings()
{
settingsSo.SaveSoundSettings();
}
/// <summary>
/// Loads sound settings from file
/// </summary>
public virtual void LoadSettings()
{
settingsSo.LoadSoundSettings();
}
/// <summary>
/// Deletes any saved sound settings
/// </summary>
public virtual void ResetSettings()
{
settingsSo.ResetSoundSettings();
}
public virtual void OnMMEvent(MMSoundManagerSoundControlEvent soundControlEvent)
{
if (soundControlEvent.TargetSource == null)
{
_tempAudioSource = FindByID(soundControlEvent.SoundID);
}
else
{
_tempAudioSource = soundControlEvent.TargetSource;
}
if (_tempAudioSource != null)
{
switch (soundControlEvent.MMSoundManagerSoundControlEventType)
{
case MMSoundManagerSoundControlEventTypes.Pause:
PauseSound(_tempAudioSource);
break;
case MMSoundManagerSoundControlEventTypes.Resume:
ResumeSound(_tempAudioSource);
break;
case MMSoundManagerSoundControlEventTypes.Stop:
StopSound(_tempAudioSource);
break;
case MMSoundManagerSoundControlEventTypes.Free:
FreeSound(_tempAudioSource);
break;
}
}
}
public virtual void OnMMEvent(MMSoundManagerTrackFadeEvent trackFadeEvent)
{
switch (trackFadeEvent.Mode)
{
case MMSoundManagerTrackFadeEvent.Modes.PlayFade:
FadeTrack(trackFadeEvent.Track, trackFadeEvent.FadeDuration, settingsSo.GetTrackVolume(trackFadeEvent.Track), trackFadeEvent.FinalVolume, trackFadeEvent.FadeTween);
break;
case MMSoundManagerTrackFadeEvent.Modes.StopFade:
StopFadeTrack(trackFadeEvent.Track);
break;
}
}
public virtual void OnMMEvent(MMSoundManagerSoundFadeEvent soundFadeEvent)
{
_tempAudioSource = FindByID(soundFadeEvent.SoundID);
switch (soundFadeEvent.Mode)
{
case MMSoundManagerSoundFadeEvent.Modes.PlayFade:
if (_tempAudioSource != null)
{
FadeSound(_tempAudioSource, soundFadeEvent.FadeDuration, _tempAudioSource.volume, soundFadeEvent.FinalVolume,
soundFadeEvent.FadeTween);
}
break;
case MMSoundManagerSoundFadeEvent.Modes.StopFade:
StopFadeSound(_tempAudioSource);
break;
}
}
public virtual void OnMMEvent(MMSoundManagerAllSoundsControlEvent allSoundsControlEvent)
{
switch (allSoundsControlEvent.EventType)
{
case MMSoundManagerAllSoundsControlEventTypes.Pause:
PauseAllSounds();
break;
case MMSoundManagerAllSoundsControlEventTypes.Play:
PlayAllSounds();
break;
case MMSoundManagerAllSoundsControlEventTypes.Stop:
StopAllSounds();
break;
case MMSoundManagerAllSoundsControlEventTypes.Free:
FreeAllSounds();
break;
case MMSoundManagerAllSoundsControlEventTypes.FreeAllButPersistent:
FreeAllSoundsButPersistent();
break;
case MMSoundManagerAllSoundsControlEventTypes.FreeAllLooping:
FreeAllLoopingSounds();
break;
}
}
public virtual void OnMMSfxEvent(AudioClip clipToPlay, AudioMixerGroup audioGroup = null, float volume = 1f, float pitch = 1f, int priority = 128)
{
MMSoundManagerPlayOptions options = MMSoundManagerPlayOptions.Default;
options.Location = this.transform.position;
options.AudioGroup = audioGroup;
options.Volume = volume;
options.Pitch = pitch;
if (priority >= 0)
{
options.Priority = Mathf.Min(priority, 256);
}
options.MmSoundManagerTrack = MMSoundManagerTracks.Sfx;
options.Loop = false;
PlaySound(clipToPlay, options);
}
public virtual AudioSource OnMMSoundManagerSoundPlayEvent(AudioClip clip, MMSoundManagerPlayOptions options)
{
return PlaySound(clip, options);
}
/// <summary>
/// On enable we start listening for events
/// </summary>
protected virtual void OnEnable()
{
MMSfxEvent.Register(OnMMSfxEvent);
MMSoundManagerSoundPlayEvent.Register(OnMMSoundManagerSoundPlayEvent);
this.MMEventStartListening<MMSoundManagerEvent>();
this.MMEventStartListening<MMSoundManagerTrackEvent>();
this.MMEventStartListening<MMSoundManagerSoundControlEvent>();
this.MMEventStartListening<MMSoundManagerTrackFadeEvent>();
this.MMEventStartListening<MMSoundManagerSoundFadeEvent>();
this.MMEventStartListening<MMSoundManagerAllSoundsControlEvent>();
SceneManager.sceneLoaded += OnSceneLoaded;
}
/// <summary>
/// On disable we stop listening for events
/// </summary>
protected virtual void OnDisable()
{
if (_enabled)
{
MMSfxEvent.Unregister(OnMMSfxEvent);
MMSoundManagerSoundPlayEvent.Unregister(OnMMSoundManagerSoundPlayEvent);
this.MMEventStopListening<MMSoundManagerEvent>();
this.MMEventStopListening<MMSoundManagerTrackEvent>();
this.MMEventStopListening<MMSoundManagerSoundControlEvent>();
this.MMEventStopListening<MMSoundManagerTrackFadeEvent>();
this.MMEventStopListening<MMSoundManagerSoundFadeEvent>();
this.MMEventStopListening<MMSoundManagerAllSoundsControlEvent>();
SceneManager.sceneLoaded -= OnSceneLoaded;
}
}
#endregion
}
}