using System; using System.Collections; using System.Collections.Generic; using AibisDream.FixSystem; using AibisDream.Framework; using FMOD; using FMOD.Studio; using FMODUnity; using UnityEngine; using STOP_MODE = FMOD.Studio.STOP_MODE; namespace AibisDream.Kit { public class AudioManager : Singleton, IAudioManager { private const string AmbStateName = "scene"; public EventReference ambEvent; #region Event实例 private EventInstance _ambInstance; private EventInstance _voiceInstance; private readonly Dictionary _musicDict = new(); private readonly Dictionary _sfxDict = new(); #endregion #region 事件索引 private List _removeTrigger; private readonly AudioData _audioData = new(); #endregion private void Start() { // 注册事件 RegisterEvent(); // 启动AMB InitAmb(); // 注册数据 RegisterData(); } private void RegisterEvent() { // 语音相关 EnumEventSystem.Global.Register(DialogEventEnum.LineStart, OnLineStart); EnumEventSystem.Global.Register(DialogEventEnum.LineShown, OnLineShown); // 场景切换相关 EnumEventSystem.Global.Register(EventEnum.SceneLoad, OnSceneLoad); EnumEventSystem.Global.Register(EventEnum.FixStateSwitch, OnFixStateChange); EnumEventSystem.Global.Register(GameLoopEnum.GameQuit, OnGameQuit); } private void RegisterData() { // 注册数据 StorageSystem.Instance.RegisterData(_audioData); _audioData.LoadEvent += LoadData; } private void LoadData(AudioData data) { foreach (var musicEvent in _musicDict.Values) { musicEvent.release(); } foreach (var sfxEvent in _sfxDict.Values) { sfxEvent.release(); } foreach (var music in data.musicNames) { var instance = RuntimeManager.CreateInstance(music.Value); instance.start(); _musicDict[music.Key] = instance; } foreach (var sfx in data.sfxNames) { var instance = RuntimeManager.CreateInstance(sfx.Value); instance.start(); _sfxDict[sfx.Key] = instance; } } public void PauseAll() { foreach (var music in _musicDict.Values) { music.setPaused(true); } foreach (var sfx in _sfxDict.Values) { sfx.setPaused(true); } _ambInstance.setPaused(true); _voiceInstance.setPaused(true); } public void UnPauseAll() { foreach (var music in _musicDict.Values) { music.setPaused(false); } foreach (var sfx in _sfxDict.Values) { sfx.setPaused(false); } _ambInstance.setPaused(false); _voiceInstance.setPaused(false); } #region AMB相关 private void InitAmb() { _ambInstance = RuntimeManager.CreateInstance(ambEvent); _ambInstance.start(); } public void ChangeAmb(AmbState state) { RuntimeManager.StudioSystem.setParameterByName(AmbStateName, (float)state); } public void SetGlobalParam(string paramName, float value) { RuntimeManager.StudioSystem.setParameterByName(paramName, value); } private void OnSceneLoad(string loadingScene) { // 场景加载时切换AMB var state = MatchAmbState(loadingScene); ChangeAmb(state); } private void OnGameQuit() { ChangeAmb(AmbState.Main); // 清除当前播放的所有东西 foreach (var music in _musicDict.Values) { music.stop(STOP_MODE.IMMEDIATE); music.release(); } foreach (var sfx in _sfxDict.Values) { sfx.stop(STOP_MODE.IMMEDIATE); sfx.release(); } if (_voiceInstance.isValid()) { _voiceInstance.stop(STOP_MODE.IMMEDIATE); _voiceInstance.release(); } _musicDict.Clear(); _sfxDict.Clear(); _audioData.musicNames.Clear(); _audioData.sfxNames.Clear(); } private void OnFixStateChange(FixState nextState) { // 状态切换时加载 var state = MatchAmbState(nextState); ChangeAmb(state); } #endregion #region 音乐相关 public void PlayMusic(string eventName, string audioKey = null) { var eventPath = FormatEventPath(eventName); var key = string.IsNullOrEmpty(audioKey) ? eventName : audioKey; if (_musicDict.TryGetValue(key, out var oldMusic)) { oldMusic.stop(STOP_MODE.IMMEDIATE); oldMusic.release(); RemoveMusic(key); } var newMusic = RuntimeManager.CreateInstance(eventPath); AddMusic(key, newMusic); newMusic.start(); } public void StopMusic(string key, STOP_MODE stopMode) { if (_musicDict.TryGetValue(key, out var oldMusic)) { oldMusic.stop(stopMode); oldMusic.release(); RemoveMusic(key); } } public void SetMusicParam(string key, string param, float paramValue) { if (_musicDict.TryGetValue(key, out var music)) { music.setParameterByName(param, paramValue); } } private void RemoveMusic(string key) { _musicDict.Remove(key); _audioData.musicNames.Remove(key); } private void AddMusic(string key, EventInstance eventInstance) { _musicDict[key] = eventInstance; eventInstance.GetDescription().getPath(out var path); _audioData.musicNames[key] = path; } #endregion public void OnLineStart(DialogText dialogText) { if (_voiceInstance.IsPlaying()) { _voiceInstance.stop(STOP_MODE.IMMEDIATE); _voiceInstance.release(); } if (string.IsNullOrEmpty(dialogText.actor.voicePath) || !FModEx.IsEventExist(dialogText.actor.VoicePath)) { return; } _voiceInstance = RuntimeManager.CreateInstance(dialogText.actor.VoicePath); _voiceInstance.start(); } public void OnLineShown() { if (_voiceInstance.isValid()) { _voiceInstance.stop(STOP_MODE.IMMEDIATE); _voiceInstance.release(); } } public void PlaySfx(string eventName, string audioKey = null) { var eventPath = FormatEventPath(eventName); var key = string.IsNullOrEmpty(audioKey) ? eventName : audioKey; if (_sfxDict.TryGetValue(key, out var oldSfx)) { oldSfx.stop(STOP_MODE.IMMEDIATE); oldSfx.release(); RemoveSfx(key); } var newSfx = RuntimeManager.CreateInstance(eventPath); if (newSfx.GetDescription().isOneshot(out var isOneshot) != RESULT.OK || !isOneshot) { AddSfx(key, newSfx); } newSfx.start(); } public void StopSfx(string key, STOP_MODE stopMode=STOP_MODE.IMMEDIATE) { if (_sfxDict.TryGetValue(key, out var oldSfx)) { oldSfx.stop(stopMode); oldSfx.release(); RemoveSfx(key); } } public void SetSfxParam(string key, string paramName, float paramValue) { if (_sfxDict.TryGetValue(key, out var sfx)) { sfx.setParameterByName(paramName, paramValue); } } private void RemoveSfx(string key) { _sfxDict.Remove(key); _audioData.sfxNames.Remove(key); } private void AddSfx(string key, EventInstance eventInstance) { _sfxDict[key] = eventInstance; eventInstance.GetDescription().getPath(out var path); _audioData.sfxNames[key] = path; } #region 调整音量 public void SetVolume(float volumeValue) { var masterVca = RuntimeManager.GetBus("bus:/"); masterVca.setVolume(volumeValue); } #endregion #region 一些静态方法 private static string FormatEventPath(string eventName) { return eventName.StartsWith("event:/") ? eventName : $"event:/{eventName}"; } private static AmbState MatchAmbState(string key) { return key switch { "ExpressFixScene" => AmbState.Clinic, "ShitouHeadiFixScene" => AmbState.Clinic, "PeipeiFixScene" => AmbState.Clinic, "Bridge" => AmbState.Footbridge, "SkyBridge" => AmbState.Footbridge, "SubWay" => AmbState.Subway, "ClinicOut" => AmbState.Outside, "OutSideSceneTemp" => AmbState.Outside, _ => AmbState.Main }; } private static AmbState MatchAmbState(FixState fixState) { return fixState switch { FixState.Clinic => AmbState.Clinic, FixState.Engine => AmbState.Engine, FixState.BodyModule => AmbState.Body, FixState.Gear => AmbState.GearBox, FixState.UF => AmbState.UF, FixState.Eye => AmbState.Eye, FixState.Memory => AmbState.Memory, _ => AmbState.Main }; } #endregion public void StopSfxWithDelay(string key, string paramName, float paramValue, float delay, STOP_MODE stopMode = STOP_MODE.ALLOWFADEOUT) { if (_sfxDict.TryGetValue(key, out var sfx)) { StartCoroutine(DelayedStop(sfx, key, paramName, paramValue, delay, stopMode)); } } private IEnumerator DelayedStop(EventInstance instance, string key, string paramName, float paramValue, float delay, STOP_MODE stopMode) { instance.setParameterByName(paramName, paramValue); yield return new WaitForSeconds(delay); instance.stop(stopMode); instance.release(); RemoveSfx(key); } public void StopMusicWithDelay(string key, string paramName, float paramValue, float delay, STOP_MODE stopMode = STOP_MODE.ALLOWFADEOUT) { if (_musicDict.TryGetValue(key, out var music)) { StartCoroutine(DelayedStopMusic(music, key, paramName, paramValue, delay, stopMode)); } } private IEnumerator DelayedStopMusic(EventInstance instance, string key, string paramName, float paramValue, float delay, STOP_MODE stopMode) { instance.setParameterByName(paramName, paramValue); yield return new WaitForSeconds(delay); instance.stop(stopMode); instance.release(); RemoveMusic(key); } } public interface IAudioManager { // Yarn音频接口 public void PlayMusic(string eventName, string audioKey); public void StopMusic(string eventName, STOP_MODE stopMode); public void SetMusicParam(string eventName, string paramName, float paramValue); // AMB参数变化 public void ChangeAmb(AmbState state); public void SetGlobalParam(string paramName, float value); // 语音相关 public void OnLineStart(DialogText dialogText); public void OnLineShown(); // 交互相关 public void PlaySfx(string eventName, string audioKey); public void StopSfx(string eventName, STOP_MODE stopMode); public void SetSfxParam(string eventName, string paramName, float paramValue); // 暂停 public void PauseAll(); public void UnPauseAll(); } public class AudioData : IData { public readonly Dictionary sfxNames = new(); public readonly Dictionary musicNames = new(); public event Action LoadEvent; public IEnumerator Load() { LoadEvent?.Invoke(this); yield break; } } }