using UnityEngine; using System.Collections; using System.Collections.Generic; [RequireComponent(typeof(AudioSource))] public class HajiyevMusicManager : MonoBehaviour { #region Public Inspector Attributes [Tooltip("Clips to play")] public List playList; [Tooltip("Keep playing this clip till clip is changed")] public bool repeat = false; [Tooltip("When clip is done, go to next clip")] public bool autoAdvance = true; [Tooltip("When end of playlist is reached, go back to start on advance")] public bool recycle = true; [Tooltip("Begin playing first clip as soon as the MusicManager instantiates")] public bool playOnAwake = false; [Tooltip("If nonzero, fade in on resume from pause or change track from middle of song for that many seconds")] [Range(0f, 10f)] public float fadeInTime = 0.5f; [Tooltip("If nonzero, fade out on pause or change track from middle of song for that many seconds")] [Range(0f, 10f)] public float fadeOutTime = 1f; [Tooltip("Volume control")] [Range(0f, 1f)] public float volume = 0.5f; [Tooltip("If true, on awake, load audioclips from resources directories into playlist")] public bool loadFromResources = true; [Tooltip("If true, use as a Singleton class and don't destroy when scene is changed")] public bool dontDestroyOnLoad = true; #endregion #region Public Scripted Setup /// /// Remove all clips from playlist /// public void ClearPlayList() { playList.Clear(); } /// /// Add a clip to the end of the playlist /// /// public void AddToPlayList(AudioClip clip) { playList.Add(clip); } /// /// Remove selected track from playlist (0 is first track) /// /// public void RemoveFromPlayList(int index) { playList.RemoveAt(index); } /// /// Load given clip from resources folder(s) into playlist /// /// /// False if clip could not be loaded public bool LoadClipFromResources(string name) { AudioClip clip = Resources.Load(name); if (clip) { playList.Add(clip); return true; } return false; } /// /// Load all clips in resources folders into playlist, but without duplication /// /// optional subfolder of resources folder /// Number of unique clips added to playlist public int LoadClipsFromResources(string path="") { AudioClip[] clips = Resources.LoadAll(path); int count = 0; foreach(AudioClip clip in clips) { if (!playList.Contains(clip)) { count++; playList.Add(clip); } } return count; } /// /// Get the audioclip at specified track in playlist (0 is first track) /// /// /// public AudioClip GetPlayListClip(int index) { if (index >= 0 && index < GetPlayListLength()) { return playList[index]; } else { return null; } } /// /// Get the number of tracks in the playlist /// /// public int GetPlayListLength() { return playList.Count; } /// /// Set the fade-in time when resuming from pause /// /// public void SetFadeIn(float time) { if (time < 0 || time > 10) { throw new UnityException("Fade in time out of range: " + time); } fadeInTime = time; } /// /// Set the fade-out time when pausing or stopping in the middle of a clip /// /// public void SetFadeOut(float time) { if (time < 0 || time > 10) { throw new UnityException("Fade out time out of range: " + time); } fadeOutTime = time; } /// /// Adjust the volume control, from 0 to 1 (use 1 for "turn it up to eleven") /// /// public void SetVolume(float amount) { volume = Mathf.Clamp(amount, 0, 1); } /// /// Set autoadvance (like a CD player, next track when this track finishes), /// only works if not repeating current track. /// public void AutoAdvance() { autoAdvance = true; } /// /// Unset autoadvance, stop playing when track ends /// public void NoAutoAdvance() { autoAdvance = false; } /// /// Repeat current track until manually changed /// public void Repeat() { repeat = true; } /// /// Don't repeat track--advance or stop when track is done /// public void NoRepeat() { repeat = false; } /// /// Repeat playlist from beginning when end is reached /// public void Recycle() { recycle = true; } /// /// Stop playing when end of playlist is reached /// public void NoRecycle() { recycle = false; } #endregion #region Public Controls /// /// Toggle between playing and pausing of current track /// public void PlayPauseToggle() { if (!IsPlaying()) { Play(); } else { Pause(); } } /// /// Toggle the Repeat (loop this track) flag /// public void ToggleRepeat() { if (repeat) { NoRepeat(); } else { Repeat(); } } /// /// Toggle the Recycle (loop the whole playlist) flag /// public void ToggleRecycle() { if (recycle) { NoRecycle(); } else { Recycle(); } } /// /// Increment the volume /// public void VolumeUp(float amount = 0.05f) { SetVolume(volume + amount); } /// /// Decrement the volume /// public void VolumeDown(float amount = 0.05f) { SetVolume(volume - amount); } /// /// Toggle between muting and playing at last-set volume /// public void ToggleMute() { if (saveVolume < 0) { SendControlMessage(Message.MusicManager_Mute); saveVolume = volume; SetVolume(0); } else { SendControlMessage(Message.MusicManager_UnMute); SetVolume(saveVolume); saveVolume = -1; } } /// /// Start playing, or resume from pause /// public void Play() { SendControlMessage(Message.MusicManager_Play); if (!IsPlaying()) { if (IsPaused()) { ResumeFromPause(); audioSource.time = saveTime; } else { saveTime = 0; Rewind(); ResumeFromPause(); audioSource.time = 0; } } } /// /// Stop playing, but don't lose place in clip /// public void Pause() { SendControlMessage(Message.MusicManager_Pause); saveTime = 0; if (IsPlaying()) { isPlaying = false; Fade(volume, 0, fadeOutTime); saveTime = audioSource.time; } } /// /// Go back to beginning of playlist /// public void Rewind() { if (IsPlaying()) { Pause(); SetTrack(0); Play(); } else { SetTrack(0); Pause(); } } /// /// Go back to beginning of clip /// public void RewindClip() { if (IsPlaying()) { Pause(); audioSource.time = 0; Play(); } else { audioSource.time = 0; Pause(); } } /// /// Stop playing and rewind to beginning of playlist /// public void Stop() { Pause(); Rewind(); } /// /// Stop playing and rewind to beginning of clip /// public void StopClip() { Pause(); Invoke("RewindClip", fadeOutTime); } /// /// Advance to next song on playlist. /// public void Next() { if (IsPlaying()) { Pause(); SetTrack(NextTrack()); Play(); } else { SetTrack(NextTrack()); Pause(); } } /// /// Go back to previous song on playlist /// public void Previous() { if (IsPlaying()) { Pause(); SetTrack(PreviousTrack()); Play(); } else { SetTrack(PreviousTrack()); Pause(); } } /// /// Advance a bit in the song /// /// public void MoveForward(float seconds = 10f) { if (IsPlaying()) { Pause(); if (audioSource.clip) { audioSource.time = Mathf.Clamp(audioSource.time + seconds, 0, audioSource.clip.length); } Play(); } else { if (audioSource.clip) { audioSource.time = Mathf.Clamp(audioSource.time + seconds, 0, audioSource.clip.length); } } } /// /// Go back a bit in the song /// /// public void MoveBackward(float seconds = 10f) { if (IsPlaying()) { Pause(); if (audioSource.clip) { audioSource.time = Mathf.Clamp(audioSource.time - seconds, 0, audioSource.clip.length); } Play(); } else { if (audioSource.clip) { audioSource.time = Mathf.Clamp(audioSource.time - seconds, 0, audioSource.clip.length); } } } /// /// Reorder the playlist randomly /// public void Shuffle() { if (IsPlaying()) { Stop(); //reorder playlist Randomize(playList); Play(); } else if (IsPaused()) { Stop(); //reorder playlist Randomize(playList); } else { Randomize(playList); } } #endregion #region Public Queries /// /// Track number playing (from 0 to number of tracks - 1) /// /// public int CurrentTrackNumber() { return trackNumber; } /// /// Audio clip now playing (even if paused) /// /// public AudioClip NowPlaying() { return playList[trackNumber]; } /// /// True if there is a current clip and it is not paused /// /// public bool IsPlaying() { return isPlaying; } /// /// True if there is a current clip and it is paused /// /// public bool IsPaused() { return !IsPlaying() && CurrentTrackNumber() >= 0; } /// /// Time into the current clip /// /// public float TimeInSeconds() { return audioSource.time; } /// /// Length of the current clip, or 0 if no clip /// /// public float LengthInSeconds() { if (!audioSource.clip) { return 0; } return audioSource.clip.length; } #endregion #region Internal Manager State private AudioSource audioSource; int trackNumber = -1; //negative means no active track bool isPlaying = false; float fadeStartVolume; float fadeEndVolume; float fadeTime = 0; float fadeStartTime; float saveVolume = -1; float saveTime = 0; #endregion #region Internal Singleton private static HajiyevMusicManager _instance; public static HajiyevMusicManager instance { get { if (_instance == null) {//in case not awake yet _instance = FindObjectOfType(); } return _instance; } } void Awake() { // if the singleton hasn't been initialized yet if (dontDestroyOnLoad) { if (_instance != null && _instance != this) { Debug.LogError("Duplicate singleton " + this.gameObject + " created; destroying it now"); Destroy(this.gameObject); } if (_instance != this) { _instance = this; DontDestroyOnLoad(this.gameObject); } } else { _instance = this; } } #endregion #region Internal Methods // Use this for initialization void Start() { audioSource = GetComponent(); audioSource.loop = repeat && !autoAdvance; audioSource.playOnAwake = false; audioSource.volume = volume; if (loadFromResources) { LoadClipsFromResources(); } Rewind(); if (playOnAwake) { Play(); } } // Update is called once per frame void Update() { if (fadeTime > 0) { SetFadeVolume(); } else { audioSource.volume = volume; } if (isPlaying && !audioSource.isPlaying && audioSource.time == 0) { ClipFinished(); } audioSource.loop = repeat; } void SetFadeVolume() { fadeEndVolume = Mathf.Clamp(fadeEndVolume, 0, volume);//in case volume control adjusted //during fade float t = (Time.time - fadeStartTime) / fadeTime; if (t >= 1) { fadeTime = 0; if (fadeEndVolume == 0) { audioSource.Stop(); audioSource.time = saveTime; } audioSource.volume = volume; } else { audioSource.volume = (1 - t) * fadeStartVolume + t * fadeEndVolume; } } void ClipFinished() { if (autoAdvance) { Next(); } else { Pause(); } } void ResumeFromPause() { if (CurrentTrackNumber() >= 0) { isPlaying = true; Fade(0, volume, fadeInTime); } } void SetTrack(int trackNum) { if (CurrentTrackNumber() != trackNum) { SendControlMessage(Message.MusicManager_ChangeTrack); } saveTime = 0; if (GetPlayListLength() == 0) { trackNumber = -1; } else if (trackNum >= 0 && trackNum < GetPlayListLength()) { trackNumber = trackNum; } else if (recycle) { trackNumber = (trackNum % GetPlayListLength() + GetPlayListLength()) % GetPlayListLength(); } else { trackNumber = -1; } } int NextTrack() { int trackNum = trackNumber + 1; if (trackNum >= 0 && trackNum < GetPlayListLength()) { return trackNum; } else if (recycle) { return (trackNum % GetPlayListLength() + GetPlayListLength()) % GetPlayListLength(); } else { Stop(); return -1; } } int PreviousTrack() { int trackNum = trackNumber - 1; if (trackNum >= 0 && trackNum < GetPlayListLength()) { return trackNum; } else if (recycle) { return (trackNum % GetPlayListLength() + GetPlayListLength()) % GetPlayListLength(); } else { return -1; } } void Fade(float startVolume, float endVolume, float time) { if (audioSource.isPlaying) { fadeStartTime = Time.time; fadeStartVolume = startVolume; fadeEndVolume = endVolume; fadeTime = time; audioSource.volume = startVolume; if (startVolume == 0) { audioSource.Stop(); audioSource.clip = NowPlaying(); audioSource.Play(); } } else { if (startVolume == 0) { audioSource.clip = NowPlaying(); audioSource.volume = endVolume; audioSource.Play(); } } } void Randomize(List list) { for (int i = 0; i < list.Count - 1 /*because random(a,a) is inclusive*/; i++) { int j = Random.Range(i + 1, list.Count); AudioClip tmp = list[i]; list[i] = list[j]; list[j] = tmp; } } #endregion #region Messages [Tooltip("Send control messages like MusicManager_Play, etc., to scripts in user-supplied parent game objects (advanced)")] public bool sendControlMessagesUpward = false; [Tooltip("Send control messages like MusicManager_Play, etc., to scripts in user-supplied child game objects (advanced)")] public bool sendControlMessagesDownward = false; [Tooltip("Send control messages like MusicManager_Play, etc., to user-supplied scripts in this game object (advanced)")] public bool sendControlMessagesToThisGameObject = true; [Tooltip("Log control messages like MusicManager_Play, etc., to the Unity console or the executable's log file")] public bool logControlMessages = false; void SendControlMessage(Message message) { string message_string = message.ToString(); if (sendControlMessagesUpward) { SendMessageUpwards(message_string, SendMessageOptions.DontRequireReceiver); } if (sendControlMessagesDownward) { BroadcastMessage(message_string, SendMessageOptions.DontRequireReceiver); } if (sendControlMessagesToThisGameObject) { SendMessage(message_string, SendMessageOptions.DontRequireReceiver); } if (logControlMessages) { Debug.Log("Control Message: \"" + message_string + "\""); } } private enum Message { MusicManager_Play, MusicManager_Pause, MusicManager_Mute, MusicManager_UnMute, MusicManager_ChangeTrack } #endregion }