com.alicizax.unity.framework/Runtime/Audio/AudioCategory.cs

335 lines
9.4 KiB
C#
Raw Normal View History

2025-09-05 19:46:30 +08:00
using UnityEngine;
using UnityEngine.Audio;
namespace AlicizaX.Audio.Runtime
{
internal sealed class AudioCategory
2025-09-05 19:46:30 +08:00
{
private readonly AudioService _service;
private readonly AudioAgent[] _agents;
private readonly AudioAgent[] _activeAgents;
private readonly AudioAgent[] _playHeap;
private readonly int[] _freeStack;
private int _activeCount;
private int _heapCount;
private int _freeCount;
private bool _enabled;
internal AudioType Type { get; }
internal int TypeIndex { get; }
internal Transform InstanceRoot { get; private set; }
internal AudioMixerGroup MixerGroup { get; }
internal AudioGroupConfig Config { get; }
internal int Capacity => _agents.Length;
internal int ActiveCount => _activeCount;
internal int FreeCount => _freeCount;
internal int HeapCount => _heapCount;
internal bool Enabled
{
get => _enabled;
2025-09-05 19:46:30 +08:00
set
{
if (_enabled == value)
{
return;
}
_enabled = value;
if (!_enabled)
2025-09-05 19:46:30 +08:00
{
Stop(false);
2025-09-05 19:46:30 +08:00
}
}
}
internal AudioCategory(AudioService service, AudioMixer audioMixer, AudioGroupConfig config, int globalIndexOffset)
2025-09-05 19:46:30 +08:00
{
_service = service;
Config = config;
Type = config.AudioType;
TypeIndex = (int)config.AudioType;
_enabled = !config.Mute;
MixerGroup = ResolveMixerGroup(audioMixer, config);
InstanceRoot = new GameObject("Audio Category - " + Type).transform;
InstanceRoot.SetParent(service.InstanceRoot, false);
2025-09-05 19:46:30 +08:00
int capacity = config.AgentHelperCount;
_agents = new AudioAgent[capacity];
_activeAgents = new AudioAgent[capacity];
_playHeap = new AudioAgent[capacity];
_freeStack = new int[capacity];
_freeCount = capacity;
for (int i = 0; i < capacity; i++)
2025-09-05 19:46:30 +08:00
{
AudioSourceObject sourceObject = service.AcquireSourceObject(this, i);
AudioAgent agent = new AudioAgent();
agent.Initialize(service, this, i, globalIndexOffset + i, sourceObject);
_agents[i] = agent;
_freeStack[i] = capacity - 1 - i;
2025-09-05 19:46:30 +08:00
}
}
internal ulong Play(AudioPlayRequest request)
{
if (!_enabled)
2025-09-05 19:46:30 +08:00
{
return 0UL;
2025-09-05 19:46:30 +08:00
}
AudioAgent agent = AcquireAgent();
return agent != null ? agent.Play(request) : 0UL;
}
internal void Stop(bool fadeout)
{
for (int i = _activeCount - 1; i >= 0; i--)
2025-09-05 19:46:30 +08:00
{
AudioAgent agent = _activeAgents[i];
if (agent != null)
{
agent.Stop(fadeout);
}
2025-09-05 19:46:30 +08:00
}
}
internal void Update(float deltaTime)
2025-09-05 19:46:30 +08:00
{
int i = 0;
while (i < _activeCount)
2025-09-05 19:46:30 +08:00
{
AudioAgent agent = _activeAgents[i];
agent.Update(deltaTime);
if (i < _activeCount && _activeAgents[i] == agent)
{
i++;
}
2025-09-05 19:46:30 +08:00
}
}
internal bool TryGetAgent(int index, out AudioAgent agent)
2025-09-05 19:46:30 +08:00
{
if ((uint)index >= (uint)_agents.Length)
2025-09-05 19:46:30 +08:00
{
agent = null;
return false;
2025-09-05 19:46:30 +08:00
}
agent = _agents[index];
return agent != null;
}
2025-09-05 19:46:30 +08:00
internal void FillDebugInfo(float volume, AudioCategoryDebugInfo info)
{
if (info == null)
2025-09-05 19:46:30 +08:00
{
return;
2025-09-05 19:46:30 +08:00
}
info.Type = Type;
info.Enabled = _enabled;
info.Volume = volume;
info.Capacity = _agents.Length;
info.ActiveCount = _activeCount;
info.FreeCount = _freeCount;
info.HeapCount = _heapCount;
}
internal void MarkOccupied(AudioAgent agent)
{
if (agent.ActiveIndex < 0)
2025-09-05 19:46:30 +08:00
{
agent.ActiveIndex = _activeCount;
_activeAgents[_activeCount++] = agent;
}
if (agent.HeapIndex < 0)
{
int heapIndex = _heapCount++;
_playHeap[heapIndex] = agent;
agent.HeapIndex = heapIndex;
SiftHeapUp(heapIndex);
}
}
internal void MarkFree(AudioAgent agent)
{
RemoveActive(agent);
RemoveHeap(agent);
if (_freeCount < _freeStack.Length)
{
_freeStack[_freeCount++] = agent.Index;
}
}
internal void Shutdown()
{
Stop(false);
for (int i = 0; i < _agents.Length; i++)
{
AudioAgent agent = _agents[i];
if (agent != null)
2025-09-05 19:46:30 +08:00
{
agent.Shutdown();
_service.ReleaseSourceObject(TypeIndex, i);
_agents[i] = null;
2025-09-05 19:46:30 +08:00
}
}
_activeCount = 0;
_heapCount = 0;
_freeCount = 0;
2025-09-05 19:46:30 +08:00
if (InstanceRoot != null)
{
Object.Destroy(InstanceRoot.gameObject);
InstanceRoot = null;
2025-09-05 19:46:30 +08:00
}
}
private AudioAgent AcquireAgent()
{
if (_freeCount > 0)
{
int index = _freeStack[--_freeCount];
return _agents[index];
}
if (_heapCount <= 0)
2025-09-05 19:46:30 +08:00
{
return null;
}
AudioAgent oldest = _playHeap[0];
RemoveActive(oldest);
RemoveHeapAt(0);
return oldest;
2025-09-05 19:46:30 +08:00
}
private void RemoveActive(AudioAgent agent)
2025-09-05 19:46:30 +08:00
{
int index = agent.ActiveIndex;
if (index < 0)
2025-09-05 19:46:30 +08:00
{
return;
}
int lastIndex = --_activeCount;
AudioAgent last = _activeAgents[lastIndex];
_activeAgents[lastIndex] = null;
if (index != lastIndex)
{
_activeAgents[index] = last;
last.ActiveIndex = index;
2025-09-05 19:46:30 +08:00
}
agent.ActiveIndex = -1;
}
2025-09-05 19:46:30 +08:00
private void RemoveHeap(AudioAgent agent)
{
int index = agent.HeapIndex;
if (index >= 0)
2025-09-05 19:46:30 +08:00
{
RemoveHeapAt(index);
2025-09-05 19:46:30 +08:00
}
}
2025-09-05 19:46:30 +08:00
private void RemoveHeapAt(int index)
{
int lastIndex = --_heapCount;
AudioAgent removed = _playHeap[index];
AudioAgent last = _playHeap[lastIndex];
_playHeap[lastIndex] = null;
removed.HeapIndex = -1;
if (index == lastIndex)
2025-09-05 19:46:30 +08:00
{
return;
}
2025-09-05 19:46:30 +08:00
_playHeap[index] = last;
last.HeapIndex = index;
int parent = (index - 1) >> 1;
if (index > 0 && IsOlder(last, _playHeap[parent]))
{
SiftHeapUp(index);
2025-09-05 19:46:30 +08:00
}
else
{
SiftHeapDown(index);
2025-09-05 19:46:30 +08:00
}
}
private void SiftHeapUp(int index)
2025-09-05 19:46:30 +08:00
{
AudioAgent item = _playHeap[index];
while (index > 0)
2025-09-05 19:46:30 +08:00
{
int parent = (index - 1) >> 1;
AudioAgent parentAgent = _playHeap[parent];
if (!IsOlder(item, parentAgent))
2025-09-05 19:46:30 +08:00
{
break;
2025-09-05 19:46:30 +08:00
}
_playHeap[index] = parentAgent;
parentAgent.HeapIndex = index;
index = parent;
2025-09-05 19:46:30 +08:00
}
_playHeap[index] = item;
item.HeapIndex = index;
2025-09-05 19:46:30 +08:00
}
private void SiftHeapDown(int index)
2025-09-05 19:46:30 +08:00
{
AudioAgent item = _playHeap[index];
int half = _heapCount >> 1;
while (index < half)
2025-09-05 19:46:30 +08:00
{
int child = (index << 1) + 1;
int right = child + 1;
AudioAgent childAgent = _playHeap[child];
if (right < _heapCount && IsOlder(_playHeap[right], childAgent))
{
child = right;
childAgent = _playHeap[child];
}
if (!IsOlder(childAgent, item))
2025-09-05 19:46:30 +08:00
{
break;
2025-09-05 19:46:30 +08:00
}
_playHeap[index] = childAgent;
childAgent.HeapIndex = index;
index = child;
}
_playHeap[index] = item;
item.HeapIndex = index;
}
private static bool IsOlder(AudioAgent left, AudioAgent right)
{
return left.StartedAt < right.StartedAt;
}
private static AudioMixerGroup ResolveMixerGroup(AudioMixer audioMixer, AudioGroupConfig config)
{
AudioMixerGroup[] groups = audioMixer.FindMatchingGroups("Master/" + config.AudioType);
if (groups != null && groups.Length > 0)
{
return groups[0];
2025-09-05 19:46:30 +08:00
}
groups = audioMixer.FindMatchingGroups("Master");
return groups != null && groups.Length > 0 ? groups[0] : null;
2025-09-05 19:46:30 +08:00
}
}
}