解决重复切换State的时候会导致重复播放对应Segment的问题。
修复FadeIn读取了FadeOut参数的问题。 增加Initial Delay功能。 重构AudioScheduler.ConfigureSource() -> SetupSource(), RegisterActiveSound(), StartPlayBack()。 移动长音频相关功能至LongAudio文件夹。
This commit is contained in:
@@ -11,6 +11,8 @@ namespace OCES.Audio
|
||||
/// </summary>
|
||||
public class AudioScheduler : MonoBehaviour
|
||||
{
|
||||
public UnityEngine.UI.Text audioSystemTextBox;
|
||||
|
||||
const int k_maxGlobalConcurrent = 128;
|
||||
|
||||
//记录某个 AudioObject 最近一次触发播放的时刻,用于 MinInterval 节流判断,是"上次什么时候播过"。
|
||||
@@ -18,7 +20,7 @@ namespace OCES.Audio
|
||||
readonly Dictionary<uint, int> m_clipConcurrentCount = new();
|
||||
readonly List<ActiveSound> m_activeSounds = new();
|
||||
|
||||
// 复用列表,避免 TryPlay 每帧分配临时 List(原 LINQ .Where().ToList())
|
||||
// 复用列表,避免 TryPlay 每帧分配临时 List
|
||||
readonly List<ActiveSound> m_tempSameObject = new();
|
||||
readonly List<ActiveSound> m_tempSameGroup = new();
|
||||
readonly List<ActiveSound> m_tempLowerPriority = new();
|
||||
@@ -31,6 +33,14 @@ namespace OCES.Audio
|
||||
AudioContainerSelector m_containerSelector;
|
||||
PitchStepManager m_pitchStepManager;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
void Update()
|
||||
{
|
||||
Editor.DebugInfoCollector.Instance.ActiveSounds = this.m_activeSounds;
|
||||
Editor.DebugInfoCollector.Instance.ClipConcurrentCount = this.m_clipConcurrentCount;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
int GetClipCount(uint id)
|
||||
{
|
||||
@@ -40,7 +50,7 @@ namespace OCES.Audio
|
||||
void IncrementClipCount(uint id)
|
||||
{
|
||||
this.m_clipConcurrentCount[id] = GetClipCount(id) + 1;
|
||||
//Debug.Log($"{id} count added to {GetClipCount(id)}");
|
||||
Debug.Log($"{id} count added to {GetClipCount(id)}");
|
||||
}
|
||||
|
||||
void DecrementClipCount(uint id)
|
||||
@@ -167,45 +177,24 @@ namespace OCES.Audio
|
||||
|
||||
void PlayNewSound(AudioObject audioObject, float pitch)
|
||||
{
|
||||
// Blend:同时播放所有音轨,无需走后续逻辑
|
||||
if (audioObject.ContainerType == ContainerType.Blend)
|
||||
ActiveSound active = new()
|
||||
{
|
||||
for (int i = 0; i < audioObject.Name.Count; i++)
|
||||
ConfigureSource(this.m_pool.AcquireAudioSource(), audioObject, pitch, clipIndex: i, registerRemove: true);
|
||||
return;
|
||||
|
||||
// TODO BlendRanges, BlendCrossFadeType, BlendReactParam 支持
|
||||
}
|
||||
|
||||
AudioSource source = this.m_pool.AcquireAudioSource();
|
||||
|
||||
// 持续播放模式(Continuous)
|
||||
if (audioObject.Name.Count > 1 && audioObject.ContainerPlayMode)
|
||||
{
|
||||
ActiveSound chainActive = new()
|
||||
{
|
||||
Source = source,
|
||||
AudioObject = audioObject,
|
||||
StartTime = Time.realtimeSinceStartupAsDouble,
|
||||
Pitch = pitch,
|
||||
};
|
||||
this.m_activeSounds.Add(chainActive);
|
||||
IncrementClipCount(audioObject.Id);
|
||||
int continuousStart = audioObject.ContainerType == ContainerType.Random ? -1 : 0;
|
||||
chainActive.Coroutine =
|
||||
StartCoroutine(PlayContainerContinuous(source, audioObject, chainActive, continuousStart, pitch));
|
||||
return;
|
||||
}
|
||||
|
||||
// 单次播放(步进模式)
|
||||
int startIndex = audioObject.ContainerType switch
|
||||
{
|
||||
ContainerType.Random => this.m_containerSelector.PickShuffleIndex(audioObject),
|
||||
ContainerType.Sequence => this.m_containerSelector.GetNextSequenceIndex(audioObject),
|
||||
_ => 0,
|
||||
AudioObject = audioObject,
|
||||
Pitch = pitch,
|
||||
State = ActiveSoundState.Pending,
|
||||
StartTime = Time.realtimeSinceStartupAsDouble,
|
||||
};
|
||||
|
||||
ConfigureSource(source, audioObject, pitch, startIndex, registerRemove: true);
|
||||
|
||||
if (audioObject.InitialDelay > 0)
|
||||
{
|
||||
this.m_activeSounds.Add(active);
|
||||
IncrementClipCount(audioObject.Id);
|
||||
|
||||
active.Coroutine = StartCoroutine(PlayAfterDelay(active, audioObject, pitch));
|
||||
return;
|
||||
}
|
||||
|
||||
ExecutePlay(active);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -233,11 +222,13 @@ namespace OCES.Audio
|
||||
limitRepetition);
|
||||
|
||||
// 配置并播放
|
||||
if (!ConfigureSource(source, audioObject, pitch, index, registerRemove: false))
|
||||
if (!SetupSource(source, audioObject, pitch, index))
|
||||
{
|
||||
Debug.LogError($"音频文件未找到:{audioObject.Name[index]}");
|
||||
yield break;
|
||||
}
|
||||
|
||||
StartPlayback(source);
|
||||
|
||||
yield return new WaitWhile(() => source.isPlaying);
|
||||
|
||||
@@ -261,6 +252,20 @@ namespace OCES.Audio
|
||||
}
|
||||
}
|
||||
}
|
||||
static void StartPlayback(AudioSource source)
|
||||
{
|
||||
source.Play();
|
||||
}
|
||||
|
||||
IEnumerator PlayAfterDelay(ActiveSound active, AudioObject audioObject, float pitch)
|
||||
{
|
||||
Debug.Log($"Delaying for {audioObject.InitialDelay} second(s).");
|
||||
yield return new WaitForSeconds(audioObject.InitialDelay);
|
||||
|
||||
if (!this.m_activeSounds.Contains(active)) yield break;
|
||||
|
||||
ExecutePlay(active, isRegistered: true);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// 工具方法
|
||||
@@ -312,42 +317,121 @@ namespace OCES.Audio
|
||||
_ => this.m_sfxGroup,
|
||||
};
|
||||
|
||||
|
||||
bool ConfigureSource(AudioSource source, AudioObject audioObject, float pitch, int clipIndex = 0, bool registerRemove = true)
|
||||
bool SetupSource(AudioSource source, AudioObject audioObject, float pitch, int clipIndex = 0)
|
||||
{
|
||||
// TODO 现用现找可能会导致主线程卡死,尤其是低端机。需要配合Decompress配置优化性能。
|
||||
AudioClip clip = Resources.Load<AudioClip>($"Audios/{audioObject.Name[clipIndex]}");
|
||||
AudioClip clip = Resources.Load<AudioClip>($"Audios/{audioObject.Name[clipIndex]}"); // TODO 抽象同一资源加载接口
|
||||
if (!clip)
|
||||
{
|
||||
Debug.LogError($"音频文件未找到:{audioObject.Name[clipIndex]}");
|
||||
return false;
|
||||
}
|
||||
|
||||
source.clip = clip;
|
||||
source.loop = audioObject.LoopCount < 0;
|
||||
source.clip = clip;
|
||||
source.loop = audioObject.LoopCount < 0;
|
||||
source.priority = audioObject.Priority;
|
||||
source.outputAudioMixerGroup = GetMixerGroup(audioObject.MixingType);
|
||||
source.pitch = pitch;
|
||||
source.Play();
|
||||
|
||||
if (registerRemove)
|
||||
{
|
||||
IncrementClipCount(audioObject.Id);
|
||||
|
||||
ActiveSound active = new()
|
||||
{
|
||||
Source = source,
|
||||
AudioObject = audioObject,
|
||||
StartTime = Time.realtimeSinceStartupAsDouble,
|
||||
Pitch = pitch,
|
||||
};
|
||||
this.m_activeSounds.Add(active);
|
||||
active.Coroutine = StartCoroutine(RemoveWhenFinished(active));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RegisterActiveSound(ActiveSound activeSound, AudioSource audioSource, bool isRegistered = false)
|
||||
{
|
||||
activeSound.Source = audioSource;
|
||||
activeSound.State = ActiveSoundState.Playing;
|
||||
activeSound.StartTime = Time.realtimeSinceStartupAsDouble;
|
||||
|
||||
if (!isRegistered)
|
||||
{
|
||||
this.m_activeSounds.Add(activeSound);
|
||||
IncrementClipCount(activeSound.AudioObject.Id);
|
||||
}
|
||||
|
||||
activeSound.Coroutine = StartCoroutine(RemoveWhenFinished(activeSound));
|
||||
}
|
||||
|
||||
void ExecutePlay(ActiveSound active, bool isRegistered = false)
|
||||
{
|
||||
AudioObject audioObject = active.AudioObject;
|
||||
float pitch = active.Pitch;
|
||||
|
||||
// =======================
|
||||
// Blend(每个clip一个ActiveSound)
|
||||
// =======================
|
||||
if (audioObject.ContainerType == ContainerType.Blend)
|
||||
{
|
||||
for (int i = 0; i < audioObject.Name.Count; i++)
|
||||
{
|
||||
AudioSource source = this.m_pool.AcquireAudioSource();
|
||||
|
||||
ActiveSound child = new()
|
||||
{
|
||||
AudioObject = audioObject,
|
||||
Pitch = pitch,
|
||||
State = ActiveSoundState.Playing
|
||||
};
|
||||
|
||||
if (!SetupSource(source, audioObject, pitch, i))
|
||||
{
|
||||
this.m_pool.ReturnToPool(source.gameObject);
|
||||
continue;
|
||||
}
|
||||
|
||||
StartPlayback(source);
|
||||
RegisterActiveSound(child, source);
|
||||
}
|
||||
|
||||
// 删除 pending 占位
|
||||
if (this.m_activeSounds.Contains(active))
|
||||
{
|
||||
DecrementClipCount(audioObject.Id);
|
||||
this.m_activeSounds.Remove(active);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
AudioSource sourceSingle = this.m_pool.AcquireAudioSource();
|
||||
|
||||
// =======================
|
||||
// Continuous
|
||||
// =======================
|
||||
if (audioObject.Name.Count > 1 && audioObject.ContainerPlayMode)
|
||||
{
|
||||
active.Source = sourceSingle;
|
||||
active.State = ActiveSoundState.Playing;
|
||||
|
||||
this.m_activeSounds.Add(active);
|
||||
IncrementClipCount(audioObject.Id);
|
||||
|
||||
int start = audioObject.ContainerType == ContainerType.Random ? -1 : 0;
|
||||
|
||||
active.Coroutine = StartCoroutine(
|
||||
PlayContainerContinuous(sourceSingle, audioObject, active, start, pitch)
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// =======================
|
||||
// 单次播放
|
||||
// =======================
|
||||
int index = audioObject.ContainerType switch
|
||||
{
|
||||
ContainerType.Random => m_containerSelector.PickShuffleIndex(audioObject),
|
||||
ContainerType.Sequence => m_containerSelector.GetNextSequenceIndex(audioObject),
|
||||
_ => 0
|
||||
};
|
||||
|
||||
if (!SetupSource(sourceSingle, audioObject, pitch, index))
|
||||
{
|
||||
m_pool.ReturnToPool(sourceSingle.gameObject);
|
||||
return;
|
||||
}
|
||||
|
||||
StartPlayback(sourceSingle);
|
||||
RegisterActiveSound(active, sourceSingle, isRegistered);
|
||||
}
|
||||
IEnumerator RemoveWhenFinished(ActiveSound active)
|
||||
{
|
||||
if (active.AudioObject.LoopCount < 0)
|
||||
@@ -372,7 +456,7 @@ namespace OCES.Audio
|
||||
|
||||
bool PlayAgain(ActiveSound active)
|
||||
{
|
||||
active.Source.Play();
|
||||
StartPlayback(active.Source);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -384,7 +468,7 @@ namespace OCES.Audio
|
||||
//Debug.Log($"[StopSound] 协程已终止: {active.AudioObject.Name[0]}");
|
||||
}
|
||||
|
||||
active.Source.Stop();
|
||||
if(active.Source) active.Source.Stop();
|
||||
DecrementClipCount(active.AudioObject.Id);
|
||||
this.m_activeSounds.Remove(active);
|
||||
this.m_pool.ReturnToPool(active.Source.gameObject);
|
||||
@@ -405,5 +489,6 @@ namespace OCES.Audio
|
||||
public int CurrentLoopCount;
|
||||
public Coroutine Coroutine;
|
||||
public float Pitch;
|
||||
public ActiveSoundState State;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user