930 lines
26 KiB
C#
930 lines
26 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using Cysharp.Threading.Tasks;
|
||
using Framework;
|
||
using PhxhSDK;
|
||
using UnityEngine;
|
||
using UnityEngine.Audio;
|
||
using Constants = Framework.Constants;
|
||
using Object = UnityEngine.Object;
|
||
|
||
/// <summary>
|
||
/// 音频管理器 - 统一的音频系统管理
|
||
/// - 对象池管理
|
||
/// - 播放控制
|
||
/// - 资源加载
|
||
/// - 音量管理
|
||
/// - 业务状态管理
|
||
/// </summary>
|
||
public class AudioManager: Singlenton<AudioManager>, IStart, IInitable, IUpdatable
|
||
{
|
||
#region Fields & Properties
|
||
|
||
// Unity GameObject
|
||
public GameObject AudioRoot;
|
||
public AudioListener GlobalListener;
|
||
|
||
// 业务状态
|
||
public static bool DisableAudio = false;
|
||
public bool IsPlayingAlbum { get; set; }
|
||
public bool IsPlayingBGM { get; set; }
|
||
public bool GlobalIsOpenSpeak;
|
||
|
||
// 对象池限制
|
||
public int MusicPoolLimit = 1;
|
||
public int SoundPoolLimit = 100;
|
||
public int SpeakPoolLimit = 1;
|
||
|
||
// 音频池
|
||
private Dictionary<int, AudioPlayer> _allAudioDic;
|
||
private Dictionary<string, long> _playSoundTimeStampDic;
|
||
|
||
// 播放列表(用于限制播放数量)
|
||
private List<int> _playingMusicList;
|
||
private List<int> _playingSoundList;
|
||
private List<int> _playingSpeakList;
|
||
|
||
// Listener 跟随状态
|
||
private bool _isFollowCamera = false;
|
||
|
||
#endregion
|
||
|
||
#region Volume Properties (便捷访问 AudioVolumeSettings)
|
||
|
||
/// <summary>
|
||
/// 全局总音量
|
||
/// </summary>
|
||
public float GlobalVolume
|
||
{
|
||
get => AudioVolumeSettings.Instance.GlobalVolume;
|
||
set => AudioVolumeSettings.Instance.GlobalVolume = value;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 音乐音量
|
||
/// </summary>
|
||
public float GlobalMusicVolume
|
||
{
|
||
get => AudioVolumeSettings.Instance.MusicVolume;
|
||
set => AudioVolumeSettings.Instance.MusicVolume = value;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 音效音量
|
||
/// </summary>
|
||
public float GlobalSoundsVolume
|
||
{
|
||
get => AudioVolumeSettings.Instance.SoundVolume;
|
||
set => AudioVolumeSettings.Instance.SoundVolume = value;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 语音音量
|
||
/// </summary>
|
||
public float GlobalSpeakVolume
|
||
{
|
||
get => AudioVolumeSettings.Instance.SpeakVolume;
|
||
set => AudioVolumeSettings.Instance.SpeakVolume = value;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置音乐开关
|
||
/// </summary>
|
||
public void SetMusicEnable(bool isEnable)
|
||
{
|
||
GlobalMusicVolume = isEnable ? 1 : 0;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置音效开关
|
||
/// </summary>
|
||
public void SetSoundEnable(bool isEnable)
|
||
{
|
||
GlobalSoundsVolume = isEnable ? 1 : 0;
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Lifecycle (Framework)
|
||
|
||
private void AfterSwitchCamera(bool isRegister = true)
|
||
{
|
||
if (isRegister)
|
||
{
|
||
CameraManager.Instance.AfterSwitchMainCamera += AfterSwitchMainCamera;
|
||
}
|
||
else
|
||
{
|
||
CameraManager.Instance.AfterSwitchMainCamera -= AfterSwitchMainCamera;
|
||
}
|
||
}
|
||
|
||
public void Init()
|
||
{
|
||
_allAudioDic = new Dictionary<int, AudioPlayer>();
|
||
|
||
_playingMusicList = new List<int>();
|
||
_playingSoundList = new List<int>();
|
||
_playingSpeakList = new List<int>();
|
||
|
||
_playSoundTimeStampDic = new();
|
||
|
||
EventManager.Instance.Register(EventManager.EventName.Audio_ErrorAudioPlay, _OnErrorAudioPlay);
|
||
|
||
AfterSwitchCamera();
|
||
}
|
||
|
||
public void Release()
|
||
{
|
||
EventManager.Instance.Unregister(EventManager.EventName.Audio_ErrorAudioPlay, _OnErrorAudioPlay);
|
||
AfterSwitchCamera(false);
|
||
}
|
||
|
||
public void Start()
|
||
{
|
||
if (!AudioRoot)
|
||
{
|
||
AudioRoot = new GameObject();
|
||
AudioRoot.name = "Audio(Singleton)";
|
||
Object.DontDestroyOnLoad(AudioRoot);
|
||
GlobalListener = AudioRoot.AddComponent<AudioListener>();
|
||
_InitListenerPos();
|
||
}
|
||
}
|
||
|
||
public void Update(float dt)
|
||
{
|
||
_SetListenerPos();
|
||
|
||
// 遍历所有音频
|
||
foreach (var kv in _allAudioDic)
|
||
{
|
||
var audio = kv.Value;
|
||
if (audio.State != PlayState.Stopped)
|
||
{
|
||
audio.Update(dt);
|
||
}
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Asset Loading
|
||
|
||
private string GetFullPath(string key, bool isI18N = false)
|
||
{
|
||
var cfg = TableManager.Instance.Tables.AudioSourceConfig.GetOrDefault(key);
|
||
if (cfg == null)
|
||
{
|
||
DebugUtil.LogError($"音频Key:{key}找不到!");
|
||
return "";
|
||
}
|
||
|
||
var path = cfg.Path;
|
||
if (isI18N)
|
||
{
|
||
if (path.Contains(LanguageManager.I18N))
|
||
{
|
||
var eLanguage = LanguageManager.Instance.GetCurrentLanguage();
|
||
var replacePath = LanguageManager.PathDic[eLanguage];
|
||
path = path.Replace(LanguageManager.I18N, replacePath);
|
||
}
|
||
return path;
|
||
}
|
||
return cfg.Path;
|
||
}
|
||
|
||
private float _GetNormalizeVolume(string key)
|
||
{
|
||
var cfg = TableManager.Instance.Tables.AudioSourceConfig.GetOrDefault(key);
|
||
if (cfg == null)
|
||
{
|
||
DebugUtil.LogError($"音频Key:{key}找不到!");
|
||
return 0f;
|
||
}
|
||
|
||
var volume = cfg.VolumeScale;
|
||
return volume;
|
||
}
|
||
|
||
private bool _CheckPath(string key)
|
||
{
|
||
var cfg = TableManager.Instance.Tables.AudioSourceConfig;
|
||
if (cfg.GetOrDefault(key) != null)
|
||
{
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
public async UniTask<float> GetClipLength(string key)
|
||
{
|
||
var path = GetFullPath(key);
|
||
var clip = await AssetManager.Instance.LoadAssetAsync<AudioClip>(path);
|
||
if (clip)
|
||
{
|
||
return clip.length;
|
||
}
|
||
|
||
return -1;
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Audio Pool Management
|
||
|
||
/// <summary>
|
||
/// 获取可用的Audio
|
||
/// </summary>
|
||
private AudioPlayer _GetUsableAudio(PhxhSDK.EAudioType eAudioType, bool isLoop)
|
||
{
|
||
try
|
||
{
|
||
var limit = _CheckIsAudioLimited(eAudioType);
|
||
if (limit)
|
||
{
|
||
return _GetOldAudio(eAudioType);
|
||
}
|
||
|
||
var freeAudio = _GetFreeAudio(eAudioType);
|
||
return freeAudio ?? _CreateNewAudio(eAudioType, isLoop);
|
||
}
|
||
|
||
catch (Exception e)
|
||
{
|
||
DebugUtil.LogError($"AudioManger, GetUsableAudio Fail.{e}");
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
private AudioPlayer _GetOldAudio(PhxhSDK.EAudioType eAudioType)
|
||
{
|
||
var playingList = _GetPlayingList(eAudioType);
|
||
if (playingList.Count > 0)
|
||
{
|
||
var audio = GetAudioByID(playingList[0]);
|
||
return audio;
|
||
}
|
||
// 如果没有正在播放的音频,创建新的(使用默认不循环)
|
||
return _CreateNewAudio(eAudioType, isLoop: false);
|
||
}
|
||
|
||
private int _GetLimit(PhxhSDK.EAudioType eAudioType)
|
||
{
|
||
switch (eAudioType)
|
||
{
|
||
case PhxhSDK.EAudioType.Music:
|
||
return MusicPoolLimit;
|
||
case PhxhSDK.EAudioType.Sound:
|
||
return SoundPoolLimit;
|
||
case PhxhSDK.EAudioType.Speak:
|
||
return SpeakPoolLimit;
|
||
default:
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
private List<int> _GetPlayingList(PhxhSDK.EAudioType eAudioType)
|
||
{
|
||
switch (eAudioType)
|
||
{
|
||
case PhxhSDK.EAudioType.Music:
|
||
return _playingMusicList;
|
||
case PhxhSDK.EAudioType.Sound:
|
||
return _playingSoundList;
|
||
case PhxhSDK.EAudioType.Speak:
|
||
return _playingSpeakList;
|
||
default:
|
||
return _playingSoundList;
|
||
}
|
||
}
|
||
|
||
private bool _CheckIsAudioLimited(PhxhSDK.EAudioType eAudioType)
|
||
{
|
||
var limit = _GetLimit(eAudioType);
|
||
var playingList = _GetPlayingList(eAudioType);
|
||
return playingList.Count >= limit;
|
||
}
|
||
|
||
private AudioPlayer _GetFreeAudio(PhxhSDK.EAudioType eAudioType)
|
||
{
|
||
foreach (var kv in _allAudioDic)
|
||
{
|
||
var audio = kv.Value;
|
||
if (audio.AudioType == eAudioType && audio.State == PlayState.Stopped)
|
||
{
|
||
return audio;
|
||
}
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
private AudioPlayer _CreateNewAudio(PhxhSDK.EAudioType eAudioType, bool isLoop)
|
||
{
|
||
var rootGo = new GameObject()
|
||
{
|
||
name = "Audio"
|
||
};
|
||
rootGo.transform.SetParent(AudioRoot.transform);
|
||
var audio = new AudioPlayer(eAudioType, rootGo);
|
||
|
||
// 设置 GameObject 名称
|
||
string typeName = eAudioType switch
|
||
{
|
||
PhxhSDK.EAudioType.Music => "Music",
|
||
PhxhSDK.EAudioType.Sound => "Sound",
|
||
PhxhSDK.EAudioType.Speak => "Speak",
|
||
_ => "Audio"
|
||
};
|
||
rootGo.name = $"{typeName}{audio.AudioId}";
|
||
if (isLoop)
|
||
{
|
||
rootGo.name += "Loop";
|
||
}
|
||
|
||
_allAudioDic.Add(audio.AudioId, audio);
|
||
|
||
return audio;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 添加到播放列表
|
||
/// </summary>
|
||
public void Add2Playing(int id)
|
||
{
|
||
var audio = GetAudioByID(id);
|
||
if (audio == null)
|
||
return;
|
||
|
||
var list = _GetPlayingList(audio.AudioType);
|
||
if (!list.Contains(id))
|
||
{
|
||
list.Add(id);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 从播放列表移除
|
||
/// </summary>
|
||
public void RemovePlaying(int id)
|
||
{
|
||
_playingMusicList.Remove(id);
|
||
_playingSoundList.Remove(id);
|
||
_playingSpeakList.Remove(id);
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Play API (Internal)
|
||
|
||
/// <summary>
|
||
/// 内部播放接口 - GameObject
|
||
/// </summary>
|
||
private int _PlayAudio(string key, PhxhSDK.EAudioType type, GameObject posObj = null, bool isLoop = false,
|
||
int priority = AudioConstant.DefaultPriority, bool isFade = false, float fadeTime = 0f)
|
||
{
|
||
var settings = AudioVolumeSettings.Instance;
|
||
switch (type)
|
||
{
|
||
case PhxhSDK.EAudioType.Sound:
|
||
if (settings.GlobalVolume <= 0 || settings.SoundVolume <= 0)
|
||
return -1;
|
||
break;
|
||
case PhxhSDK.EAudioType.Speak:
|
||
if (settings.GlobalVolume <= 0 || settings.SpeakVolume <= 0)
|
||
return -1;
|
||
break;
|
||
}
|
||
|
||
if (string.IsNullOrEmpty(key))
|
||
return -1;
|
||
|
||
switch (type)
|
||
{
|
||
case PhxhSDK.EAudioType.Music:
|
||
return _PlayMusic(key, posObj, isLoop, isFade, fadeTime);
|
||
case PhxhSDK.EAudioType.Sound:
|
||
return _PlaySound(key, posObj, isLoop, priority, isFade, fadeTime);
|
||
case PhxhSDK.EAudioType.Speak:
|
||
return _PlaySpeakSound(key, posObj, isFade, fadeTime);
|
||
default:
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 内部播放接口 - Vector3
|
||
/// </summary>
|
||
private int _PlayAudio(string key, PhxhSDK.EAudioType type, Vector3 pos, bool loop = false,
|
||
int priority = AudioConstant.DefaultPriority, bool isFade = false, float fadeTime = 0f)
|
||
{
|
||
var settings = AudioVolumeSettings.Instance;
|
||
switch (type)
|
||
{
|
||
case PhxhSDK.EAudioType.Sound:
|
||
if (settings.GlobalVolume <= 0 || settings.SoundVolume <= 0)
|
||
return -1;
|
||
break;
|
||
case PhxhSDK.EAudioType.Speak:
|
||
if (settings.GlobalVolume <= 0 || settings.SpeakVolume <= 0)
|
||
return -1;
|
||
break;
|
||
}
|
||
|
||
switch (type)
|
||
{
|
||
case PhxhSDK.EAudioType.Music:
|
||
return _PlayMusic(key, pos, loop, isFade, fadeTime);
|
||
case PhxhSDK.EAudioType.Sound:
|
||
return _PlaySound(key, pos, loop, priority, isFade, fadeTime);
|
||
case PhxhSDK.EAudioType.Speak:
|
||
return _PlaySpeakSound(key, pos, isFade, fadeTime);
|
||
default:
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
private int _PlayMusic(string key, GameObject posObj = null, bool isLoop = true, bool isFade = false, float fadeTime = 0f, float volume = 1f)
|
||
{
|
||
return _PlayMusicCore(key, isLoop, isFade, fadeTime, volume, posObj, null);
|
||
}
|
||
|
||
private int _PlayMusic(string key, Vector3 pos, bool isLoop, bool isFade = false, float fadeTime = 0f, float volume = 1f)
|
||
{
|
||
return _PlayMusicCore(key, isLoop, isFade, fadeTime, volume, null, pos);
|
||
}
|
||
|
||
private int _PlayMusicCore(string key, bool isLoop, bool isFade, float fadeTime, float volume,
|
||
GameObject followTarget, Vector3? staticPosition)
|
||
{
|
||
var path = GetFullPath(key);
|
||
var audio = _GetUsableAudio(PhxhSDK.EAudioType.Music, isLoop);
|
||
|
||
if (audio == null)
|
||
{
|
||
DebugUtil.LogError($"AudioManager, PlayMusic, Cant GetUsableAudio key:{key}");
|
||
return -1;
|
||
}
|
||
|
||
return ConfigureAndPlay(audio, path, key, isLoop, isFade, fadeTime, volume,
|
||
followTarget, staticPosition);
|
||
}
|
||
|
||
private int _PlaySound(string key, GameObject posObj = null, bool isLoop = false,
|
||
int priority = AudioConstant.DefaultPriority, bool isFade = false, float fadeTime = 0f, float volume = 1f)
|
||
{
|
||
return _PlaySoundCore(key, isLoop, priority, isFade, fadeTime, volume, posObj, null);
|
||
}
|
||
|
||
private int _PlaySound(string key, Vector3 pos, bool isLoop = false, int priority = AudioConstant.DefaultPriority,
|
||
bool isFade = false, float fadeTime = 0f, float volume = 1f)
|
||
{
|
||
return _PlaySoundCore(key, isLoop, priority, isFade, fadeTime, volume, null, pos);
|
||
}
|
||
|
||
private int _PlaySoundCore(string key, bool isLoop, int priority, bool isFade, float fadeTime, float volume,
|
||
GameObject followTarget, Vector3? staticPosition)
|
||
{
|
||
if (AudioVolumeSettings.Instance.SoundVolume < 0.01f)
|
||
{
|
||
return -1;
|
||
}
|
||
|
||
// 防重播检查(低优先级音效100ms内防重)
|
||
if (priority >= AudioConstant.LowPriority)
|
||
{
|
||
var curTimeStamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||
if (_playSoundTimeStampDic.TryGetValue(key, out var timeStamp))
|
||
{
|
||
if (curTimeStamp - timeStamp < 100)
|
||
{
|
||
return -1;
|
||
}
|
||
_playSoundTimeStampDic[key] = curTimeStamp;
|
||
}
|
||
else
|
||
{
|
||
_playSoundTimeStampDic.TryAdd(key, curTimeStamp);
|
||
}
|
||
}
|
||
|
||
var audio = _GetUsableAudio(PhxhSDK.EAudioType.Sound, isLoop);
|
||
|
||
if (audio == null)
|
||
{
|
||
DebugUtil.LogError($"AudioManager, PlaySound, Cant GetUsableAudio key:{key}");
|
||
return -1;
|
||
}
|
||
|
||
var path = GetFullPath(key);
|
||
return ConfigureAndPlay(audio, path, key, isLoop, isFade, fadeTime, volume,
|
||
followTarget, staticPosition);
|
||
}
|
||
|
||
private int _PlaySpeakSound(string key, GameObject posObj = null, bool isFade = false, float fadeTime = 0f, float volume = 1f)
|
||
{
|
||
return _PlaySpeakSoundCore(key, isFade, fadeTime, volume, posObj, null);
|
||
}
|
||
|
||
private int _PlaySpeakSound(string key, Vector3 pos, bool isFade = false, float fadeTime = 0f, float volume = 1f)
|
||
{
|
||
return _PlaySpeakSoundCore(key, isFade, fadeTime, volume, null, pos);
|
||
}
|
||
|
||
private int _PlaySpeakSoundCore(string key, bool isFade, float fadeTime, float volume,
|
||
GameObject followTarget, Vector3? staticPosition)
|
||
{
|
||
if (AudioVolumeSettings.Instance.SpeakVolume < 0.01f)
|
||
{
|
||
return -1;
|
||
}
|
||
|
||
var audio = _GetUsableAudio(PhxhSDK.EAudioType.Speak, isLoop: false);
|
||
|
||
if (audio == null)
|
||
{
|
||
DebugUtil.LogError($"AudioManager, PlaySpeak, Cant GetUsableAudio key:{key}");
|
||
return -1;
|
||
}
|
||
|
||
var path = GetFullPath(key, isI18N: true);
|
||
return ConfigureAndPlay(audio, path, key, isLoop: false, isFade, fadeTime, volume,
|
||
followTarget, staticPosition);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 统一的配置和播放核心方法
|
||
/// </summary>
|
||
private int ConfigureAndPlay(AudioPlayer audio, string path, string key, bool isLoop, bool isFade,
|
||
float fadeTime, float volume, GameObject followTarget, Vector3? staticPosition)
|
||
{
|
||
// 1. 设置音量缩放系数(来自配置表)
|
||
var volumeScale = _GetNormalizeVolume(key);
|
||
audio.SetVolumeScale(volumeScale);
|
||
|
||
// 2. 设置循环
|
||
audio.Loop = isLoop;
|
||
|
||
// 3. 设置位置(3D音效)
|
||
if (followTarget != null)
|
||
{
|
||
audio.SetFollowTarget(followTarget);
|
||
audio.SetIs3D(true);
|
||
}
|
||
else if (staticPosition.HasValue)
|
||
{
|
||
audio.SetPosition(staticPosition.Value);
|
||
audio.SetIs3D(true);
|
||
}
|
||
else
|
||
{
|
||
audio.SetIs3D(false);
|
||
}
|
||
|
||
// 4. 播放音频
|
||
if (isFade)
|
||
{
|
||
audio.Volume = 0;
|
||
audio.LoadAndPlayWithFade(path, fadeTime, fadeTime);
|
||
}
|
||
else
|
||
{
|
||
audio.Volume = volume * volumeScale;
|
||
audio.LoadAndPlay(path);
|
||
}
|
||
|
||
return audio.AudioId;
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Play API (Public)
|
||
|
||
/// <summary>
|
||
/// 播放音频 - GameObject位置
|
||
/// </summary>
|
||
public int PlayAudio(string key, PhxhSDK.EAudioType type = PhxhSDK.EAudioType.Sound,
|
||
GameObject posObj = null, bool loop = false, int priority = AudioConstant.DefaultPriority, bool isFade = false)
|
||
{
|
||
if (DisableAudio)
|
||
return -1;
|
||
|
||
if (!_CheckPath(key))
|
||
{
|
||
if (DebugUtil.LogEnable)
|
||
{
|
||
DebugUtil.LogG($"无法找到对应音频文件,key:{key}");
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
if (key == Constants.Sound.ERROR_UI || key == Constants.Sound.ERROR_UI_FIGHT)
|
||
{
|
||
EventManager.Instance.Send(EventManager.EventName.Audio_ErrorAudioPlay);
|
||
}
|
||
|
||
if (type == PhxhSDK.EAudioType.Music)
|
||
{
|
||
IsPlayingAlbum = false;
|
||
IsPlayingBGM = false;
|
||
}
|
||
|
||
return _PlayAudio(key, type, posObj, loop, priority, isFade);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 播放音频 - Vector3位置
|
||
/// </summary>
|
||
public int PlayAudio(string key, Vector3 pos, PhxhSDK.EAudioType type = PhxhSDK.EAudioType.Sound, bool loop = false,
|
||
int priority = AudioConstant.DefaultPriority, bool isFade = false)
|
||
{
|
||
if (DisableAudio)
|
||
return -1;
|
||
|
||
if (!_CheckPath(key))
|
||
{
|
||
DebugUtil.LogG($"无法找到对应音频文件,key:{key}");
|
||
return -1;
|
||
}
|
||
|
||
if (key == Constants.Sound.ERROR_UI || key == Constants.Sound.ERROR_UI_FIGHT)
|
||
{
|
||
EventManager.Instance.Send(EventManager.EventName.Audio_ErrorAudioPlay);
|
||
}
|
||
|
||
if (type == PhxhSDK.EAudioType.Music)
|
||
{
|
||
IsPlayingAlbum = false;
|
||
IsPlayingBGM = false;
|
||
}
|
||
|
||
return _PlayAudio(key, type, pos, loop, priority, isFade);
|
||
}
|
||
|
||
public void PlayByKey(int audioID, string key, bool isFade = false, float fadeOutTime = 4f)
|
||
{
|
||
if (_allAudioDic.TryGetValue(audioID, out var audio))
|
||
{
|
||
var fullPath = GetFullPath(key);
|
||
if (isFade)
|
||
{
|
||
audio.LoadAndPlayWithFade(fullPath, fadeOutTime);
|
||
}
|
||
else
|
||
{
|
||
audio.LoadAndPlay(fullPath);
|
||
}
|
||
}
|
||
}
|
||
|
||
public void PlayByKey(int audioID, string key, float fadeInTime, float targetVolume)
|
||
{
|
||
if (_allAudioDic.TryGetValue(audioID, out var audio))
|
||
{
|
||
var fullPath = GetFullPath(key);
|
||
audio.Volume = targetVolume;
|
||
audio.LoadAndPlayWithFade(fullPath, fadeInTime);
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Control API
|
||
|
||
public void InitLimit(int count0, int count1, int count2)
|
||
{
|
||
MusicPoolLimit = count0;
|
||
SoundPoolLimit = count1;
|
||
SpeakPoolLimit = count2;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据音频ID获取AudioPlayer实例
|
||
/// 注意:即使DisableAudio=true,也会返回实例(因为可能需要查询状态或停止播放)
|
||
/// </summary>
|
||
public AudioPlayer GetAudioByID(int id)
|
||
{
|
||
return _allAudioDic.TryGetValue(id, out var audio) ? audio : null;
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 停止指定ID的音频
|
||
/// 注意:即使DisableAudio=true,也应该能停止音频
|
||
/// </summary>
|
||
public void StopByID(int audioID)
|
||
{
|
||
if (audioID >= 0 && _allAudioDic.TryGetValue(audioID, out var audio))
|
||
{
|
||
audio.Stop();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 淡出停止指定ID的音频
|
||
/// 注意:即使DisableAudio=true,也应该能停止音频
|
||
/// </summary>
|
||
public void StopWithFadeByID(int id, float fadeOutTime = 1f)
|
||
{
|
||
if (id >= 0 && _allAudioDic.TryGetValue(id, out var audio))
|
||
{
|
||
audio.StopWithFadeOut(fadeOutTime);
|
||
}
|
||
}
|
||
|
||
public void PauseAllSound()
|
||
{
|
||
foreach (var kv in _allAudioDic)
|
||
{
|
||
if (kv.Value.AudioType == PhxhSDK.EAudioType.Sound)
|
||
{
|
||
kv.Value.Pause();
|
||
}
|
||
}
|
||
}
|
||
|
||
public void ResumeAllSound()
|
||
{
|
||
foreach (var kv in _allAudioDic)
|
||
{
|
||
if (kv.Value.AudioType == PhxhSDK.EAudioType.Sound)
|
||
{
|
||
kv.Value.Resume();
|
||
}
|
||
}
|
||
}
|
||
|
||
public void StopAllMusic()
|
||
{
|
||
IsPlayingAlbum = false;
|
||
IsPlayingBGM = false;
|
||
|
||
DebugUtil.LogG("StopAllMusic");
|
||
foreach (var kv in _allAudioDic)
|
||
{
|
||
if (kv.Value.AudioType == PhxhSDK.EAudioType.Music)
|
||
{
|
||
kv.Value.Stop();
|
||
}
|
||
}
|
||
}
|
||
|
||
public void StopAllSounds()
|
||
{
|
||
DebugUtil.LogG("StopAllSounds");
|
||
foreach (var kv in _allAudioDic)
|
||
{
|
||
if (kv.Value.AudioType == PhxhSDK.EAudioType.Sound)
|
||
{
|
||
kv.Value.Stop();
|
||
}
|
||
}
|
||
}
|
||
|
||
public void StopAllSpeaks()
|
||
{
|
||
DebugUtil.LogG("StopAllSpeaks");
|
||
foreach (var kv in _allAudioDic)
|
||
{
|
||
if (kv.Value.AudioType == PhxhSDK.EAudioType.Speak)
|
||
{
|
||
kv.Value.Stop();
|
||
}
|
||
}
|
||
}
|
||
|
||
public void StopAll()
|
||
{
|
||
IsPlayingAlbum = false;
|
||
IsPlayingBGM = false;
|
||
|
||
DebugUtil.LogG("StopAll");
|
||
foreach (var kv in _allAudioDic)
|
||
{
|
||
var audio = kv.Value;
|
||
audio.Stop();
|
||
}
|
||
}
|
||
|
||
public void ReleaseAllAudios()
|
||
{
|
||
foreach (var kv in _allAudioDic)
|
||
{
|
||
kv.Value.Dispose();
|
||
}
|
||
_allAudioDic.Clear();
|
||
_playingMusicList.Clear();
|
||
_playingSoundList.Clear();
|
||
_playingSpeakList.Clear();
|
||
AudioPlayer.ReleaseLoadedAssets();
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Special API
|
||
|
||
/// <summary>
|
||
/// 获取音频频谱数据(处理并归一化)
|
||
/// </summary>
|
||
public void GetSpectrumData(int id, float[] spectrumsShow, float[] spectrumsData)
|
||
{
|
||
AudioPlayer audioInst = GetAudioByID(id);
|
||
if (null == audioInst)
|
||
{
|
||
for (int i = 0; i < spectrumsShow.Length; ++i)
|
||
{
|
||
spectrumsShow[i] = 0f;
|
||
}
|
||
return;
|
||
}
|
||
|
||
audioInst.GetSpectrumData(spectrumsData);
|
||
int n = spectrumsData.Length / spectrumsShow.Length;
|
||
for (int i = 0; i < spectrumsShow.Length; ++i)
|
||
{
|
||
float value = 0f;
|
||
for (int j = 0; j < n; ++j)
|
||
{
|
||
value += spectrumsData[i * n + j];
|
||
}
|
||
|
||
value = Mathf.Log10(Mathf.Log(i + 2f, 2f) * value * (i * i + 1) * 9 + 1);
|
||
spectrumsShow[i] = value;
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Listener Management
|
||
|
||
private void _SetListenerPos()
|
||
{
|
||
if (AudioRoot && _isFollowCamera)
|
||
{
|
||
var camera = CameraManager.Instance.MainCamera;
|
||
if (camera)
|
||
{
|
||
AudioRoot.transform.position = camera.transform.position;
|
||
}
|
||
}
|
||
}
|
||
|
||
private void _InitListenerPos()
|
||
{
|
||
AudioRoot.transform.position = new Vector3(0, 0, 0);
|
||
}
|
||
|
||
public void EnterFollowState()
|
||
{
|
||
_isFollowCamera = true;
|
||
}
|
||
|
||
public void ExitFollowState()
|
||
{
|
||
_isFollowCamera = false;
|
||
_InitListenerPos();
|
||
}
|
||
|
||
public void SetGlobalListenerFollow(bool isActive)
|
||
{
|
||
if(isActive)
|
||
EnterFollowState();
|
||
else
|
||
ExitFollowState();
|
||
}
|
||
|
||
public void AfterSwitchMainCamera(Camera camera)
|
||
{
|
||
var listener = camera.GetComponent<AudioListener>();
|
||
if (listener)
|
||
{
|
||
Object.DestroyImmediate(listener);
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Events
|
||
|
||
private void _OnErrorAudioPlay()
|
||
{
|
||
foreach (var kv in _allAudioDic)
|
||
{
|
||
var sound = kv.Value;
|
||
if ((sound.AudioType == PhxhSDK.EAudioType.Sound || sound.AudioType == PhxhSDK.EAudioType.Speak) && sound.IsPlaying)
|
||
{
|
||
if (sound.Clip.name == Constants.Sound.COMMON_CLAIM || sound.Clip.name == Constants.Sound.COMMON_CLICK)
|
||
{
|
||
sound.Stop();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
}
|