using System;
using System.Collections.Generic;
using System.Linq;
using Framework;
using PhxhSDK;
///
/// 音频停止池 - 使用 LRU 算法管理停止的 AudioPlayer
/// - 按 key 索引,提高复用率
/// - 分类型限制,防止内存泄漏
/// - LRU 淘汰,自动清理长期不用的 AudioPlayer
///
public class AudioStoppedPool
{
#region Fields
// 按 key 索引(用于快速查找匹配的 AudioPlayer)
// key 格式: "音频类型_配置key",如 "Music_bgm_01"
private Dictionary> _poolByKey;
// 按 ID 索引(用于快速查找和移除)
private Dictionary _poolById;
// 当前各类型的数量
private Dictionary _currentCount;
// 各类型的上限
private Dictionary _maxLimit;
#endregion
#region Init
public AudioStoppedPool()
{
_poolByKey = new Dictionary>();
_poolById = new Dictionary();
_currentCount = new Dictionary
{
{ EAudioType.Music, 0 },
{ EAudioType.Sound, 0 },
{ EAudioType.Speak, 0 }
};
_maxLimit = new Dictionary
{
{ EAudioType.Music, 5 }, // Music 停止池上限 5
{ EAudioType.Sound, 50 }, // Sound 停止池上限 50
{ EAudioType.Speak, 3 } // Speak 停止池上限 3
};
}
#endregion
#region Public Methods
///
/// 设置停止池上限
///
public void SetLimit(EAudioType type, int limit)
{
_maxLimit[type] = limit;
}
///
/// 添加到停止池(带 LRU 淘汰)
///
/// 被淘汰的 AudioPlayer 列表
public List AddStopped(EAudioType type, string key, AudioPlayer audio)
{
if (audio == null || string.IsNullOrEmpty(key))
return new List();
var poolKey = MakePoolKey(type, key);
// 1. 添加到 key 索引
if (!_poolByKey.TryGetValue(poolKey, out var list))
{
list = new LinkedList();
_poolByKey[poolKey] = list;
}
list.AddFirst(audio); // 添加到链表头部(最近使用)
// 2. 添加到 ID 索引
_poolById[audio.AudioId] = audio;
_currentCount[type]++;
// 3. 检查是否超过上限,执行 LRU 淘汰
var toDestroy = new List();
if (_currentCount[type] > _maxLimit[type])
{
int excessCount = _currentCount[type] - _maxLimit[type];
toDestroy = EvictOldest(type, excessCount);
}
return toDestroy;
}
///
/// 获取空闲 AudioPlayer(优先匹配 key)
///
public AudioPlayer GetFree(EAudioType type, string key)
{
if (string.IsNullOrEmpty(key))
return null;
var poolKey = MakePoolKey(type, key);
// 1. 精确匹配(同类型 + 同 key)
if (_poolByKey.TryGetValue(poolKey, out var list) && list.Count > 0)
{
var audio = list.First.Value;
RemoveFromPool(audio);
return audio;
}
// 2. Fallback: 同类型,任意 key(从最近使用的开始)
var typePrefix = $"{type}_";
foreach (var kv in _poolByKey)
{
if (kv.Key.StartsWith(typePrefix) && kv.Value.Count > 0)
{
var audio = kv.Value.First.Value;
RemoveFromPool(audio);
return audio;
}
}
return null;
}
///
/// 通过 ID 查询停止池中的 AudioPlayer
///
public AudioPlayer GetById(int id)
{
return _poolById.TryGetValue(id, out var audio) ? audio : null;
}
///
/// 通过 ID 从停止池移除
///
public AudioPlayer RemoveById(int id)
{
if (!_poolById.TryGetValue(id, out var audio))
return null;
RemoveFromPool(audio);
return audio;
}
///
/// 获取当前统计信息
///
public (int music, int sound, int speak) GetStats()
{
return (_currentCount[EAudioType.Music],
_currentCount[EAudioType.Sound],
_currentCount[EAudioType.Speak]);
}
///
/// 清空停止池
///
public void Clear()
{
_poolByKey.Clear();
_poolById.Clear();
_currentCount[EAudioType.Music] = 0;
_currentCount[EAudioType.Sound] = 0;
_currentCount[EAudioType.Speak] = 0;
}
///
/// 释放所有 AudioPlayer 资源并清空停止池
/// 适用于场景卸载、关卡退出等需要彻底清理的场景
///
/// 释放的 AudioPlayer 数量
public int DisposeAll()
{
int count = 0;
// 遍历所有 key 的音频列表
foreach (var list in _poolByKey.Values)
{
// 释放每个 AudioPlayer
foreach (var audio in list)
{
audio.Dispose();
count++;
}
}
// 清空池子
_poolByKey.Clear();
_poolById.Clear();
_currentCount[EAudioType.Music] = 0;
_currentCount[EAudioType.Sound] = 0;
_currentCount[EAudioType.Speak] = 0;
return count;
}
#endregion
#region Private Methods
///
/// 从停止池移除 AudioPlayer
///
private void RemoveFromPool(AudioPlayer audio)
{
if (audio == null)
return;
// 从 ID 索引移除
_poolById.Remove(audio.AudioId);
// 从 key 索引移除
var poolKey = MakePoolKey(audio.AudioType, audio.CurrentPlayingKey);
if (_poolByKey.TryGetValue(poolKey, out var list))
{
list.Remove(audio);
if (list.Count == 0)
{
_poolByKey.Remove(poolKey);
}
}
// 更新计数
_currentCount[audio.AudioType]--;
}
///
/// LRU 淘汰:移除最久未使用的 N 个 AudioPlayer
///
private List EvictOldest(EAudioType type, int count)
{
var toDestroy = new List();
var typePrefix = $"{type}_";
// 收集所有该类型的 AudioPlayer(从链表尾部开始,即最久未使用的)
var candidates = new List();
foreach (var kv in _poolByKey)
{
if (kv.Key.StartsWith(typePrefix))
{
// 从链表尾部开始收集(最久未使用)
var node = kv.Value.Last;
while (node != null && candidates.Count < count)
{
candidates.Add(node.Value);
node = node.Previous;
}
if (candidates.Count >= count)
break;
}
}
// 移除并返回
foreach (var audio in candidates)
{
RemoveFromPool(audio);
toDestroy.Add(audio);
}
return toDestroy;
}
///
/// 生成池 key
///
private string MakePoolKey(EAudioType type, string key)
{
return $"{type}_{key}";
}
#endregion
}