using FMOD.Studio; using FMODUnity; using System; using System.Collections.Generic; using System.Runtime.InteropServices; using UnityEditor; using UnityEngine; using UnityEngine.SceneManagement; using STOP_MODE = FMOD.Studio.STOP_MODE; public class AudioManager : MonoBehaviour { //VCAs (VCA buses) are mainly used for volume control //Regular routing buses routing are for the remaining logic, for example, within the FMOD itself to put FMOD events inside the Mixer (Mixer Routing). //Regular routing buses are used when we make some attenuation based on parameters and write the logic in code for Unity. private VCA masterVCA; private VCA musicVCA; private VCA sfxVCA; private VCA uiVCA; private VCA ambienceVCA; [SerializeField] [Header("Volume")] [Range(0, 1)] public float MasterVolume = 0.5f; [Range(0, 1)] public float MusicVolume = 0.5f; [Range(0, 1)] public float AmbienceVolume = 0.5f; [Range(0, 1)] public float SFXVolume = 0.5f; [Range(0, 1)] public float UIVolume = 0.5f; [Range(0, 1)] private Bus masterBus; private Bus ambientBus; private Bus musicBus; private Bus sfxBus; private Bus uiBus; const string sid = "00000000-0000-0000-0000-000000000000"; static readonly Guid nullGuid = new Guid(sid); private List eventInstances = new(); class TimelineInfo { public FMOD.StringWrapper LastMarker = new(); } TimelineInfo timelineInfo; GCHandle timelineHandle; EVENT_CALLBACK beatCallback; private static AudioManager _instance { get; set; } public static event Action OnNewBGMMarker; // public static AudioManager instance; private static EventInstance musicEventInstance; // public access for the Singleton // and lazy instantiation if not exists public static AudioManager Instance { get { // if exists directly return if (_instance) return _instance; // otherwise search it in the scene _instance = FindObjectOfType(); // found it? if (_instance) return _instance; // otherwise create and initialize it CreateInstance(); return _instance; } } [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] private static void CreateInstance() { // skip if already exists if (_instance) return; if (SceneManager.GetActiveScene().name == "BankLoader") return; InitializeInstance(new GameObject(nameof(AudioManager)).AddComponent()); } private static void InitializeInstance(AudioManager instance) { _instance = instance; DontDestroyOnLoad(_instance.gameObject); _instance.eventInstances = new List(); _instance.masterBus = RuntimeManager.GetBus("bus:/"); _instance.musicBus = RuntimeManager.GetBus("bus:/Music"); _instance.ambientBus = RuntimeManager.GetBus("bus:/Ambiences"); _instance.sfxBus = RuntimeManager.GetBus("bus:/SFX"); _instance.uiBus = RuntimeManager.GetBus("bus:/UI"); _instance.masterVCA = RuntimeManager.GetVCA("vca:/Master"); _instance.musicVCA = RuntimeManager.GetVCA("vca:/Music"); _instance.ambienceVCA = RuntimeManager.GetVCA("vca:/Ambiences"); _instance.sfxVCA = RuntimeManager.GetVCA("vca:/SFX"); _instance.uiVCA = RuntimeManager.GetVCA("vca:/UI"); } public void SetMasterVCA(float value) { masterVCA.setVolume(value); } public void SetMusicVCA(float value) { musicVCA.setVolume(value); } public void SetAmbientVCA(float value) { ambienceVCA.setVolume(value); } public void SetSFXVCA(float value) { sfxVCA.setVolume(value); } public void SetUIVCA(float value) { uiVCA.setVolume(value); } private void Awake() { if (_instance && _instance != this) { Destroy(gameObject); return; } InitializeInstance(this); } public EventInstance CreateInstance(EventReference eventReference) { EventInstance eventInstance = RuntimeManager.CreateInstance(eventReference); eventInstances.Add(eventInstance); return eventInstance; } private void Update() { //musicBus.setVolume(MusicVolume); //ambientBus.setVolume(AmbienceVolume); //sfxBus.setVolume(SFXVolume); //uiBus.setVolume(UIVolume); //masterBus.setVolume(MasterVolume); //VCA volumes masterVCA.setVolume(MasterVolume); ambienceVCA.setVolume(AmbienceVolume); musicVCA.setVolume(MusicVolume); sfxVCA.setVolume(SFXVolume); uiVCA.setVolume(UIVolume); } public static bool IsEventReferenceValid(EventReference eventReference) { return eventReference.Guid != nullGuid; } public static void PlayOneShot(EventReference eventReference) { if (eventReference.Guid != nullGuid) RuntimeManager.PlayOneShot(eventReference); else { Debug.LogWarning("EventReference is null, ignoring..."); } } public void PlayOneShot3D(EventReference sound, Vector3 position) { if (!IsEventReferenceValid(sound)) { Debug.LogWarning("Tried to play invalid FMOD event (3D)"); return; } RuntimeManager.PlayOneShot(sound, position); } public EventInstance PlayAttachedInstance(EventReference sound, GameObject go) { if (!IsEventReferenceValid(sound)) { Debug.LogWarning("Tried to play invalid FMOD event (attached)"); return default; } EventInstance instance = RuntimeManager.CreateInstance(sound); RuntimeManager.AttachInstanceToGameObject(instance, go); instance.start(); // instance.release(); eventInstances.Add(instance); return instance; } public void StopInstance(EventInstance instance, STOP_MODE mode = STOP_MODE.ALLOWFADEOUT) { if (!instance.isValid()) return; instance.stop(mode); instance.release(); } public void SetParameter(EventInstance instance, string parameterName, float value) { if (!instance.isValid()) return; instance.setParameterByName(parameterName, value); } public void SetGlobalParameter(string parameterName, float value) { RuntimeManager.StudioSystem.setParameterByName(parameterName, value); } public void InitializeMusic(EventReference musicEventReference) { if (musicEventReference.Guid == nullGuid) { Debug.LogWarning("EventReference is null, ignoring."); return; } musicEventInstance = CreateInstance(musicEventReference); } public void SetMusicParameter(string parameter, float value) { musicEventInstance.setParameterByName(parameter, value); } public void StartMusic() { if (!musicEventInstance.isValid()) { Debug.LogWarning("Music is not initialized yet, ignoring."); return; } musicEventInstance.start(); } public int GetMusicPosition() { musicEventInstance.getTimelinePosition(out int position); return position; } public void StopSFX() { sfxBus.stopAllEvents(FMOD.Studio.STOP_MODE.IMMEDIATE); } public void FadeOutMusic() { musicEventInstance.stop(FMOD.Studio.STOP_MODE.ALLOWFADEOUT); musicEventInstance.release(); } public static bool IsPlaying() { musicEventInstance.getPlaybackState(out PLAYBACK_STATE state); return state != PLAYBACK_STATE.STOPPED; } public static bool IsPlaying(EventInstance instance) { instance.getPlaybackState(out PLAYBACK_STATE state); return state != PLAYBACK_STATE.STOPPED; } public static void Unpause() { musicEventInstance.setPaused(false); } public static void Unpause(EventInstance instance) { instance.setPaused(false); } public static void Pause() { musicEventInstance.setPaused(true); } public static void Pause(EventInstance instance) { instance.setPaused(true); } private void CleanUp() { if (eventInstances != null) { foreach (EventInstance eventInstance in eventInstances) { eventInstance.stop(FMOD.Studio.STOP_MODE.IMMEDIATE); eventInstance.release(); } } } private void OnDestroy() { CleanUp(); } }