NLDClient-yudde/ProjectNLD/Assets/Code/Scripts/Framework/Audio/AudioManager.cs

1036 lines
29 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using System;
using Framework;
using System.Collections.Generic;
using CriWare;
using CriWare.Assets;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.SceneManagement;
using Object = UnityEngine.Object;
public enum AudioType
{
MUSIC,
SOUND
}
namespace PhxhSDK
{
public class AudioManager : Singlenton<AudioManager>, IStart, IInitable, IUpdatable
{
public GameObject initGameObject;
public enum ClipType
{
AudioClip,
CriwareClip
}
public ClipType curClipType;
protected float vol = 1f;
protected float musicVol = 1f;
protected float soundsVol = 1f;
protected Dictionary<int, Audio> musicAudio;
protected Dictionary<int, Audio> soundsAudio;
protected Dictionary<string, int> loadingAcb;
protected static int SOUNDS_POOL_MAX_NUM = 50;
protected static int MUSIC_POOL_MAX_NUM = 10;
protected bool banMusic = false;
protected bool banSound = false;
protected bool initialized = false;
protected List<int> _toBeRemoved = new List<int>();
private GameObject _criError;
private GameObject _criAtomGo;
private static Queue<Audio> _freeMusicAudio;
private static Queue<Audio> _freeSoundsAudio;
public void Start()
{
if (initGameObject == null)
{
// Need to create a new GameObject to attach the singleton to.
curClipType = ClipType.CriwareClip;
initGameObject = new GameObject();
initGameObject.name = "Audio(Singleton)";
//initGameObject.hideFlags = HideFlags.HideInHierarchy;
// Make instance persistent.
Object.DontDestroyOnLoad(initGameObject);
Object.DontDestroyOnLoad(GameObject.Find("CriWareLibraryInitializer"));
//#if UNITY_EDITOR
if (curClipType == ClipType.CriwareClip)
{
_criError = new GameObject();
_criError.name = "CriwareError";
_criError.AddComponent<CriWareErrorHandler>();
Object.DontDestroyOnLoad(_criError);
}
//#endif
}
}
/// <summary>
/// When set to true, new Audios that have the same audio clip as any other Audio, will be ignored
/// </summary>
public bool IgnoreDuplicateMusic { get; set; }
/// <summary>
/// When set to true, new Audios that have the same audio clip as any other Audio, will be ignored
/// </summary>
public bool IgnoreDuplicateSounds { get; set; }
public float GlobalVolume
{
get { return vol; }
set { vol = value; }
}
public float GlobalMusicVolume
{
get { return musicVol; }
set { musicVol = value; }
}
public float GlobalSoundsVolume
{
get { return soundsVol; }
set { soundsVol = value; }
}
public void Init()
{
if (!initialized)
{
musicAudio = new Dictionary<int, Audio>();
soundsAudio = new Dictionary<int, Audio>();
loadingAcb = new Dictionary<string, int>();
_freeMusicAudio = new Queue<Audio>();
_freeSoundsAudio = new Queue<Audio>();
IgnoreDuplicateMusic = false;
IgnoreDuplicateSounds = false;
initialized = true;
}
SceneManager.sceneLoaded += OnSceneLoaded;
}
public void Release()
{
SceneManager.sceneLoaded -= OnSceneLoaded;
}
public void RemoveAudio(GameObject go)
{
var source = go.GetComponent<CriAtomSourceForAsset>();
if (source)
{
using var e = soundsAudio.GetEnumerator();
while (e.MoveNext())
{
var audio = e.Current.Value;
if (audio.audioSource == source)
{
audio.Stop();
_toBeRemoved.Add(e.Current.Key);
}
}
}
}
public void PauseAll()
{
PauseAllMusic();
PauseAllSounds();
}
public void PauseAllMusic()
{
List<int> keys = new List<int>(musicAudio.Keys);
foreach (int key in keys)
{
Audio audio = musicAudio[key];
audio.Pause();
}
}
public void PauseAllSounds()
{
List<int> keys = new List<int>(soundsAudio.Keys);
foreach (int key in keys)
{
Audio audio = soundsAudio[key];
audio.Pause();
}
}
public void ResumeAll()
{
ResumeAllMusic();
ResumeAllSounds();
}
public void ResumeAllMusic()
{
if (banMusic)
return;
List<int> keys = new List<int>(musicAudio.Keys);
foreach (int key in keys)
{
Audio audio = musicAudio[key];
audio.Resume();
}
}
public void ResumeAllSounds()
{
List<int> keys = new List<int>(soundsAudio.Keys);
foreach (int key in keys)
{
Audio audio = soundsAudio[key];
audio.Resume();
}
}
public void StopAll(float fadeOutSeconds = -1.0f)
{
StopAllMusic(fadeOutSeconds);
StopAllSounds();
}
public void StopAllMusic(float fadeOutSeconds = -1.0f)
{
List<int> keys = new List<int>(musicAudio.Keys);
foreach (int key in keys)
{
Audio audio = musicAudio[key];
if (fadeOutSeconds > 0)
{
audio.fadeOutSeconds = fadeOutSeconds;
}
audio.Stop();
}
}
public void StopAllSounds()
{
List<int> keys = new List<int>(soundsAudio.Keys);
foreach (int key in keys)
{
Audio audio = soundsAudio[key];
audio.Stop();
}
}
public void StopSoundById(int soundId)
{
if (soundId >= 0)
{
List<int> keys = new List<int>(soundsAudio.Keys);
foreach (int key in keys)
{
Audio audio = soundsAudio[key];
if (audio.audioID == soundId)
{
audio.Stop();
}
}
}
}
public void StopMusicById(int soundId)
{
if (soundId >= 0)
{
List<int> keys = new List<int>(musicAudio.Keys);
foreach (int key in keys)
{
Audio audio = musicAudio[key];
if (audio.audioID == soundId)
{
audio.Stop();
}
}
}
}
public async UniTask<int> PlayMusicCommon(string musicName, string cueName = "", bool loop = true)
{
CriAtomAcbAsset au;
await CheckIsLoading(musicName);
if (AssetManager.Instance.IsAssetPreLoaded(musicName + ".acb"))
{
au = LoadAudio(AudioType.MUSIC, musicName);
}
else
{
au = await LoadAudioAsync(AudioType.MUSIC, musicName);
}
if (au != null)
{
Audio.AudioInfo audioInfo = new Audio.AudioInfo()
{
persist = true,
volume = 1,
fadeInValue = 0.3f,
fadeOutValue = 0.3f,
};
return PlayMusic(au, loop, null, false, cueName, audioInfo);
}
return -1;
}
public void Update(float deltaTime)
{
using (var e = musicAudio.GetEnumerator())
{
while (e.MoveNext())
{
var music = e.Current.Value;
music.Update();
if (!music.audioSource)
{
_toBeRemoved.Add(e.Current.Key);
}
if (!music.IsPlaying && !music.IsPaused && !music.IsLoading)
{
_toBeRemoved.Add(e.Current.Key);
}
}
if (_toBeRemoved.Count > 0)
{
for (int i = 0; i < _toBeRemoved.Count; i++)
{
var audio = musicAudio[_toBeRemoved[i]];
if (!GetIsArrivePoolLimit(AudioType.MUSIC))
{
_freeMusicAudio.Enqueue(audio);
}
else
{
switch (curClipType)
{
case ClipType.AudioClip:
{
Object.Destroy(audio.audioSource);
break;
}
case ClipType.CriwareClip:
{
if (audio.RootGo && audio.RootGo != initGameObject)
{
Object.Destroy(audio.RootGo);
}
Object.Destroy(audio.audioSource);
break;
}
default:
break;
}
}
musicAudio.Remove(_toBeRemoved[i]);
}
_toBeRemoved.Clear();
}
}
using (var e1 = soundsAudio.GetEnumerator())
{
// Update sound fx
while (e1.MoveNext())
{
var sound = e1.Current.Value;
sound.Update();
if (!sound.audioSource)
{
_toBeRemoved.Add(e1.Current.Key);
}
else if (!sound.IsPlaying && !sound.IsPaused && !sound.IsLoading)
{
_toBeRemoved.Add(e1.Current.Key);
}
}
if (_toBeRemoved.Count > 0)
{
for (int i = 0; i < _toBeRemoved.Count; i++)
{
var audio = soundsAudio[_toBeRemoved[i]];
if (!GetIsArrivePoolLimit(AudioType.SOUND))
{
_freeSoundsAudio.Enqueue(audio);
}
else
{
switch (curClipType)
{
case ClipType.AudioClip:
{
Object.Destroy(audio.audioSource);
break;
}
case ClipType.CriwareClip:
{
Object.Destroy(audio.audioSource);
if (audio.RootGo && audio.RootGo != initGameObject)
{
Object.Destroy(audio.RootGo);
}
break;
}
default:
break;
}
}
soundsAudio.Remove(_toBeRemoved[i]);
}
_toBeRemoved.Clear();
}
}
}
private bool GetIsArrivePoolLimit(AudioType audioType)
{
if (audioType == AudioType.MUSIC)
{
return musicAudio.Count + _freeMusicAudio.Count >= MUSIC_POOL_MAX_NUM;
}
return soundsAudio.Count + _freeSoundsAudio.Count >= SOUNDS_POOL_MAX_NUM;
}
public async UniTask<int> PlaySoundCommon(string soundName, string cueName = "")
{
await CheckIsLoading(soundName);
try
{
switch (curClipType)
{
case ClipType.AudioClip:
{
break;
}
case ClipType.CriwareClip:
{
CriAtomAcbAsset acb;
if (AssetManager.Instance.IsAssetPreLoaded(soundName + ".acb"))
{
acb = LoadAudio(AudioType.SOUND, soundName);
}
else
{
acb = await LoadAudioAsync(AudioType.SOUND, soundName);
}
if (acb != null)
{
return PlaySound(acb, GlobalVolume, false, null, false, cueName);
}
break;
}
default:
break;
}
DebugUtil.LogError("==== audio null!!! ====");
}
catch (Exception e)
{
DebugUtil.LogError("{0}.PlaySound:{1} error:{2}", GetType(), soundName, e.Message);
}
return -1;
}
public void SetMusicEnable(bool isEnable)
{
GlobalMusicVolume = isEnable ? 1 : 0;
}
public void SetSoundEnable(bool isEnable)
{
GlobalSoundsVolume = isEnable ? 1 : 0;
}
public async UniTask<int> PlayMusicCommon(string musicName, Transform trans =null, string cueName = "", bool loop = true)
{
CriAtomAcbAsset au;
await CheckIsLoading(musicName);
if (AssetManager.Instance.IsAssetPreLoaded(musicName + ".acb"))
{
au = await LoadAudioAsync(AudioType.MUSIC, musicName);
}
else
{
au = LoadAudio(AudioType.MUSIC, musicName);
}
if (au != null)
{
Audio.AudioInfo audioInfo = new Audio.AudioInfo()
{
persist = true,
volume = 1,
fadeInValue = 0.3f,
fadeOutValue = 0.3f,
};
return PlayMusic(au, loop, trans, true, cueName, audioInfo);
}
return -1;
}
public async UniTask<int> PlaySoundCommon(string soundName, Transform trans, string cueName, bool is3D = false, Vector3 pos = new Vector3())
{
await CheckIsLoading(soundName);
try
{
switch (curClipType)
{
case ClipType.AudioClip:
{
break;
}
case ClipType.CriwareClip:
{
CriAtomAcbAsset acb;
if (AssetManager.Instance.IsAssetPreLoaded(soundName + ".acb"))
{
acb = LoadAudio(AudioType.SOUND, soundName);
}
else
{
acb = await LoadAudioAsync(AudioType.SOUND, soundName);
}
if (acb != null)
{
return PlaySound(acb, GlobalVolume, false, trans, is3D, cueName, pos);
}
break;
}
default:
break;
}
DebugUtil.LogError("==== audio null!!! ====");
}
catch (Exception e)
{
DebugUtil.LogError("{0}.PlaySound:{1} error:{2}", GetType(), soundName, e.Message);
}
return -1;
}
private async UniTask CheckIsLoading(string audioName)
{
while (loadingAcb.ContainsKey(audioName))
{
await UniTask.NextFrame();
}
}
/// <summary>
/// 播放音乐
/// </summary>
/// <param name="acbAsset"></param> acbAsset
/// <param name="loop"></param> 是否循环
/// <param name="sourceTransform"></param> 挂载的trans
/// <param name="is3D"></param> 是否3D音效
/// <param name="cueName"></param> 播放的Cue里面哪个音源
/// <param name="audioInfo"></param> 结构体,包含淡入淡出的时间
/// <returns></returns>
private int PlayMusic(CriAtomAcbAsset acbAsset, bool loop, Transform sourceTransform,
bool is3D, string cueName, Audio.AudioInfo audioInfo)
{
if (acbAsset == null)
{
DebugUtil.LogError("Sound Manager: Audio clip is null, cannot play music", acbAsset);
}
if (IgnoreDuplicateMusic)
{
List<int> keys = new List<int>(musicAudio.Keys);
foreach (int key in keys)
{
if (musicAudio[key].audioSource.Cue.AcbAsset.name == acbAsset.name)
{
return musicAudio[key].audioID;
}
}
}
// Stop all current music playing
//StopAllMusic(audioInfo.fadeOutValue);
// Create the audioSource
Audio audio;
if (_freeMusicAudio.Count > 0)
{
audio = _freeMusicAudio.Dequeue();
audio.Play(cueName, true);
}
else
{
audio = new Audio(Audio.AudioType.Music, acbAsset, loop, sourceTransform, is3D, cueName, audioInfo);
}
// Add it to music list
musicAudio.Add(audio.audioID, audio);
if (banMusic)
{
audio.Pause();
}
return audio.audioID;
}
private int PlaySound(CriAtomAcbAsset acbAsset, float volume, bool loop, Transform sourceTransform,
bool is3D = false, string cueName = "", Vector3 pos = new Vector3(), float pitch = 1.0f)
{
if (acbAsset == null)
{
DebugUtil.LogError("Sound Manager: Audio clip is null, cannot play music");
}
if (banSound)
{
return -1;
}
if (IgnoreDuplicateSounds)
{
List<int> keys = new List<int>(soundsAudio.Keys);
foreach (var key in keys)
{
if (soundsAudio.ContainsKey(key) && soundsAudio[key].audioSource.Cue.AcbAsset.name.Equals(acbAsset.name) )
return soundsAudio[key].audioID;
}
}
#if UNITY_EDITOR || UNITY_IOS || UNITY_ANDROID
// Create the audioSource
Audio audio;
Audio.AudioInfo audioInfo = new Audio.AudioInfo()
{
persist = false,
volume = volume,
fadeInValue = 0f,
fadeOutValue = 0f,
};
Vector3 newPos;
if (!is3D)
{
newPos = initGameObject.transform.position;
}
else if (!sourceTransform)
{
newPos = pos;
}
else
{
newPos = sourceTransform.transform.position;
}
GameObject go;
if (_freeSoundsAudio.Count > 0)
{
audio = _freeSoundsAudio.Dequeue();
go = audio.RootGo;
if (!go)
{
DebugUtil.LogError("==== _freeSoundsAudio null!!! ====");
}
audio.SetIs3D(is3D);
audio.Play(cueName);
}
else
{
go = new GameObject();
go.name = "Audio";
audio = new Audio(AudioBase.AudioType.Sound, acbAsset, loop, go.transform, is3D, cueName, audioInfo);
#if UNITY_EDITOR
go.name += "_"+ audio.audioID;
#endif
go.transform.SetParent(initGameObject.transform);
}
go.transform.position = newPos;
audio.SetPitch(pitch);
// Add it to music list
soundsAudio.Add(audio.audioID, audio);
return audio.audioID;
#else
return -1;
#endif
}
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
List<int> keys;
// Stop and remove all non-persistent music audio
keys = new List<int>(musicAudio.Keys);
switch (curClipType)
{
case ClipType.AudioClip:
{
foreach (var key in keys)
{
if (musicAudio.ContainsKey(key))
{
var music = musicAudio[key];
if (!music.persist && music.activated)
{
Object.Destroy(music.audioSource);
musicAudio.Remove(key);
}
}
}
// Stop sound fx
keys = new List<int>(soundsAudio.Keys);
foreach (var key in keys)
{
if (soundsAudio.ContainsKey(key))
{
var sound = soundsAudio[key];
Object.Destroy(sound.audioSource);
soundsAudio.Remove(key);
}
}
break;
}
case ClipType.CriwareClip:
{
foreach (var key in keys)
{
if (musicAudio.ContainsKey(key))
{
var music = musicAudio[key];
if (!music.persist && music.activated)
{
Object.Destroy(music.audioSource);
musicAudio.Remove(key);
}
}
}
// Stop sound fx
keys = new List<int>(soundsAudio.Keys);
foreach (var key in keys)
{
if (soundsAudio.ContainsKey(key))
{
var sound = soundsAudio[key];
Object.Destroy(sound.audioSource);
soundsAudio.Remove(key);
}
}
break;
}
default:
break;
}
}
//异步加载音频
private async UniTask<CriAtomAcbAsset> LoadAudioAsync(AudioType type, string audioName, bool is3D = false)
{
string prefix = "BGM";
switch (type)
{
case AudioType.MUSIC:
prefix = "BGM";
break;
case AudioType.SOUND:
prefix = "Sound";
break;
}
loadingAcb.Add(audioName, 1);
string acfName = audioName + ".acf";
await LoadAcfAsync(type, acfName);
string acbName = audioName + ".acb";
CriAtomAcbAsset acbAsset = await AssetManager.Instance.LoadAssetAsync<CriAtomAcbAsset>(acbName);
acbAsset.LoadImmediate();
loadingAcb.Remove(audioName);
return acbAsset;
}
//同步加载音频,需要已预加载资源
private CriAtomAcbAsset LoadAudio(AudioType type, string audioName, bool is3D = false)
{
string prefix = "BGM";
switch (type)
{
case AudioType.MUSIC:
prefix = "BGM";
break;
case AudioType.SOUND:
prefix = "Sound";
break;
}
// string acfName = audioName + ".acf";
// LoadAcf(type, acfName);
string acbName = audioName + ".acb";
CriAtomAcbAsset acbAsset = AssetManager.Instance.LoadAsset<CriAtomAcbAsset>(acbName);
return acbAsset;
}
private async UniTask<CriAtomAcfAsset> LoadAcfAsync(AudioType type, string audioName, bool is3D = false)
{
string prefix = "BGM";
switch (type)
{
case AudioType.MUSIC:
prefix = "BGM";
break;
case AudioType.SOUND:
prefix = "Sound";
break;
}
CriAtomAcfAsset acfAsset = await AssetManager.Instance.LoadAssetAsync<CriAtomAcfAsset>(audioName);
acfAsset.Register();
return acfAsset;
}
private CriAtomAcfAsset LoadAcf(AudioType type, string audioName, bool is3D = false)
{
CriAtomAcfAsset acfAsset = AssetManager.Instance.LoadAsset<CriAtomAcfAsset>(audioName);
acfAsset.Register();
return acfAsset;
}
//外部接口
public async UniTask PlayAudio(string key, Transform trans = null)
{
//全局播放挂到Audio物体上
var audioData = TableManager.Instance.Tables.Audios.GetByKey(key);
if (audioData == null)
{
DebugUtil.LogError("==== 播放的音频不存在音频Key: {0}", key);
return;
}
var audioName = Constants.AUDIO_MUSIC_PATH + audioData.Path;
if (!trans)
{
int audioID;
switch (audioData.Type)
{
case (cfg.AudioCfg.AudioType.Sound):
{
audioID = await PlaySoundCommon(audioName, audioData.CueName);
break;
}
case (cfg.AudioCfg.AudioType.Music):
{
audioID = await PlayMusicCommon(audioName, audioData.CueName);
break;
}
default:
break;
}
return;
}
//定点播放
switch (audioData.Type)
{
case (cfg.AudioCfg.AudioType.Sound):
{
int audioID = await PlaySoundCommon(audioName, trans, audioData.CueName);
break;
}
case (cfg.AudioCfg.AudioType.Music):
{
int audioID = await PlayMusicCommon(audioName, trans, audioData.CueName);
break;
}
default:
break;
}
}
public async UniTask PlayAudio(string key, Vector3 pos)
{
//全局播放挂到Audio物体上
var audioData = TableManager.Instance.Tables.Audios.GetByKey(key);
if (audioData == null)
{
DebugUtil.LogError("==== 播放的音频不存在音频Key: {0}", key);
return;
}
var audioName = Constants.AUDIO_MUSIC_PATH + audioData.Path;
//定点播放
// var go = new GameObject();
// go.transform.position = pos;
switch (audioData.Type)
{
case (cfg.AudioCfg.AudioType.Sound):
{
int audioID = await PlaySoundCommon(audioName, null, audioData.CueName, true, pos);
break;
}
case (cfg.AudioCfg.AudioType.Music):
{
int audioID = await PlayMusicCommon(audioName, null, audioData.CueName, true);
break;
}
default:
break;
}
}
public async UniTask PlayAudio(int ID, Vector3 pos)
{
//全局播放挂到Audio物体上
var audioData = TableManager.Instance.Tables.Audios.GetByID(ID);
if (audioData == null)
{
DebugUtil.LogError("==== 播放的音频不存在音频ID: {0}", ID);
return;
}
var audioName = Constants.AUDIO_MUSIC_PATH + audioData.Path;
//定点播放
switch (audioData.Type)
{
case (cfg.AudioCfg.AudioType.Sound):
{
int audioID = await PlaySoundCommon(audioName, null, audioData.CueName, true, pos);
break;
}
case (cfg.AudioCfg.AudioType.Music):
{
int audioID = await PlayMusicCommon(audioName, null, audioData.CueName, true);
break;
}
default:
break;
}
}
public async UniTask PlayAudio(int ID, Transform trans = null)
{
//全局播放挂到Audio物体上
var audioData = TableManager.Instance.Tables.Audios.GetByID(ID);
if (audioData == null)
{
DebugUtil.LogError("==== 播放的音频不存在音频Key: {0}", ID);
return;
}
var audioName = Constants.AUDIO_MUSIC_PATH + audioData.Path;
if (!trans)
{
int audioID;
switch (audioData.Type)
{
case (cfg.AudioCfg.AudioType.Sound):
{
audioID = await PlaySoundCommon(audioName, audioData.CueName);
break;
}
case (cfg.AudioCfg.AudioType.Music):
{
audioID = await PlayMusicCommon(audioName, audioData.CueName);
break;
}
default:
break;
}
return;
}
//定点播放
var go = new GameObject();
go.transform.position = trans.position;
switch (audioData.Type)
{
case (cfg.AudioCfg.AudioType.Sound):
{
int audioID = await PlaySoundCommon(audioName, trans, audioData.CueName);
go.name = "Audio_" + audioID;
break;
}
case (cfg.AudioCfg.AudioType.Music):
{
int audioID = await PlayMusicCommon(audioName, trans, audioData.CueName);
go.name = "Audio_" + audioID;
break;
}
}
go.transform.SetParent(initGameObject.transform);
}
}
}
public class Audio : AudioCriware
{
private static int _audioID;
public static void ReleaseAudioIDs()
{
_audioID = 0;
}
public struct AudioInfo
{
public bool persist;
public float volume;
public float fadeInValue;
public float fadeOutValue;
}
public Audio(AudioType audioType, CriAtomAcbAsset acb, bool loop, Transform sourceTransform, bool is3D, string cueName, AudioInfo audioInfo):
base(acb, loop, sourceTransform, is3D, cueName, audioInfo)
{
audioID = ++_audioID;
}
}