using System; using System.Collections; using System.Collections.Generic; using System.Text; using UnityEngine; public class AudioManager : MonoBehaviour { private const int MaxVirtualChannels = 1024; // Should match MAX_CHANNELS in q_sound.h private const int MaxRealChannels = 128; // Should match MAX_DYNAMIC_CHANNELS in q_sound.h private FMOD.System fmodSystem; public FMOD.System FmodSystem => fmodSystem; private static AudioManager instance; public static AudioManager Instance { get { if (instance == null) { instance = FindObjectOfType(); if (instance == null) { var gameObject = new GameObject("FMOD Audio Manager"); instance = gameObject.AddComponent(); } } return instance; } } [SerializeField, Range(0, 1)] private float masterVolume = 1f; private float prevMasterVolume; private FMOD.ChannelGroup masterChannelGroup; void Awake() { // Ensure FMOD is initialized the moment Instance is accessed InitFmod(); } void OnEnable() { // Ensure FMOD gets re-initialized after hot reload InitFmod(); } void Update() { if (!fmodSystem.hasHandle() || !masterChannelGroup.hasHandle()) return; if (!Mathf.Approximately(masterVolume, prevMasterVolume)) { masterChannelGroup.setVolume(masterVolume); prevMasterVolume = masterVolume; } masterChannelGroup.getVolume(out masterVolume); } void OnDisable() { // Ensure FMOD gets closed before hot reload CloseFmod(); } void OnDestroy() { CloseFmod(); instance = null; } private void InitFmod() { if (fmodSystem.hasHandle()) return; FMOD.RESULT result = FMOD.Factory.System_Create(out fmodSystem); CheckFmodResult(result, "FMOD.Factory.System_Create"); result = fmodSystem.getVersion(out uint version); CheckFmodResult(result, "FMOD.System.getVersion"); Debug.LogFormat("[FMOD] Initialized FMOD System version {0:X1}.{1:X2}.{2:X2}", (version >> 16) & 0xFF, (version >> 8) & 0xFF, version & 0xFF); result = fmodSystem.getNumDrivers(out int numDrivers); CheckFmodResult(result, "FMOD.System.getNumDrivers"); result = fmodSystem.setOutput(numDrivers > 0 ? FMOD.OUTPUTTYPE.AUTODETECT : FMOD.OUTPUTTYPE.NOSOUND); CheckFmodResult(result, "FMOD.System.setOutput"); result = fmodSystem.getOutput(out FMOD.OUTPUTTYPE output); CheckFmodResult(result, "FMOD.System.getOutput"); Debug.Log($"[FMOD] Using output type: {output}"); result = fmodSystem.setSoftwareChannels(MaxRealChannels); CheckFmodResult(result, "FMOD.System.setSoftwareChannels"); result = fmodSystem.init(MaxVirtualChannels, FMOD.INITFLAGS.VOL0_BECOMES_VIRTUAL, IntPtr.Zero); CheckFmodResult(result, "FMOD.System.init"); result = fmodSystem.getMasterChannelGroup(out masterChannelGroup); CheckFmodResult(result, "FMOD.System.getMasterChannelGroup"); masterChannelGroup.setVolume(masterVolume); prevMasterVolume = masterVolume; } private void CheckFmodResult(FMOD.RESULT result, string cause) { if (result != FMOD.RESULT.OK) { CloseFmod(); throw new Exception($"[FMOD] Initialization failed : {cause} : {result.ToString()} : {FMOD.Error.String(result)}"); } } private void CloseFmod() { if (fmodSystem.hasHandle()) { Debug.Log("[FMOD] Closing FMOD System"); fmodSystem.close(); fmodSystem.release(); fmodSystem.clearHandle(); } } #if UNITY_EDITOR || DEVELOPMENT_BUILD void OnGUI() { var debug = new StringBuilder("FMOD System\n"); FMOD.Memory.GetStats(out int currentAlloc, out int maxAlloc); debug.AppendFormat("MEMORY: cur = {0}MB, max = {1}MB\n", currentAlloc >> 20, maxAlloc >> 20); fmodSystem.getChannelsPlaying(out int channels, out int realChannels); debug.AppendFormat("CHANNELS: real = {0}, total = {1}\n", realChannels, channels); GUI.Box(new Rect(0, 300, 250, 60), debug.ToString()); } #endif }