using CloudBuilder.AI.Policy; using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; namespace CloudBuilder.AI.Utility { // 音色条目结构 public class VoiceEntry { public SoVITSTtsVoice Voice { get; set; } public string AgeGroup { get; set; } // "老年人", "中年", "青年", "儿童" public string Gender { get; set; } // "男性", "女性" public int Weight { get; set; } // 权重,默认为 50 } /// /// SoVITS 音色随机工具,根据枚举描述中的年龄段、性别和权重进行加权随机 /// public static class SoVITSTtsVoiceRandom { // 所有音色数据(只读,线程安全) private static readonly IReadOnlyList _allEntries; // 预分组缓存,提高效率 private static readonly IReadOnlyDictionary> _byGender; private static readonly IReadOnlyDictionary> _byAge; private static readonly IReadOnlyDictionary<(string Gender, string Age), IReadOnlyList> _byGenderAndAge; private static readonly Random _random = new Random(); static SoVITSTtsVoiceRandom() { // 枚举所有 SoVITSTtsVoice 值 var enumType = typeof(SoVITSTtsVoice); var values = Enum.GetValues(enumType).Cast(); var entries = new List(); foreach (var voice in values) { var field = enumType.GetField(voice.ToString()); var descAttr = field?.GetCustomAttribute(); if (descAttr == null) continue; string desc = descAttr.Description; // 解析格式:年龄段(性别)-数字-数字-权重? 如 "老年人(男性)-2-1-80" // 提取年龄段和性别 int ageEnd = desc.IndexOf('('); int genderEnd = desc.IndexOf(')'); if (ageEnd < 0 || genderEnd < 0 || genderEnd <= ageEnd) continue; string ageGroup = desc.Substring(0, ageEnd).Trim(); string gender = desc.Substring(ageEnd + 1, genderEnd - ageEnd - 1).Trim(); // 提取权重(最后一个 '-' 后面的数字,如果有) int weight = 50; // 默认权重 int lastDash = desc.LastIndexOf('-'); if (lastDash >= 0 && lastDash < desc.Length - 1) { string weightStr = desc.Substring(lastDash + 1); if (int.TryParse(weightStr, out int parsedWeight)) { weight = parsedWeight; } } entries.Add(new VoiceEntry { Voice = voice, AgeGroup = ageGroup, Gender = gender, Weight = weight }); } _allEntries = entries.AsReadOnly(); // 建立分组字典 _byGender = _allEntries .GroupBy(e => e.Gender) .ToDictionary(g => g.Key, g => (IReadOnlyList)g.ToList().AsReadOnly()); _byAge = _allEntries .GroupBy(e => e.AgeGroup) .ToDictionary(g => g.Key, g => (IReadOnlyList)g.ToList().AsReadOnly()); _byGenderAndAge = _allEntries .GroupBy(e => (e.Gender, e.AgeGroup)) .ToDictionary(g => g.Key, g => (IReadOnlyList)g.ToList().AsReadOnly()); } /// /// 从指定列表中按权重随机选择一个音色 /// private static SoVITSTtsVoice PickRandom(IReadOnlyList entries) { if (entries == null || entries.Count == 0) throw new InvalidOperationException("没有可用的音色"); // 计算总权重 int totalWeight = entries.Sum(e => e.Weight); if (totalWeight <= 0) return entries[_random.Next(entries.Count)].Voice; // 所有权重为0时退化为均匀随机 int roll = _random.Next(totalWeight); int cumulative = 0; foreach (var entry in entries) { cumulative += entry.Weight; if (roll < cumulative) return entry.Voice; } // 保险 return entries.Last().Voice; } // -------------------- 公开随机方法 -------------------- /// /// 从所有音色中随机(加权) /// public static SoVITSTtsVoice GetRandom() { return PickRandom(_allEntries); } /// /// 随机男性音色(加权) /// public static SoVITSTtsVoice GetRandomMale() { if (_byGender.TryGetValue("男性", out var list)) return PickRandom(list); throw new InvalidOperationException("没有男性音色"); } /// /// 随机女性音色(加权) /// public static SoVITSTtsVoice GetRandomFemale() { if (_byGender.TryGetValue("女性", out var list)) return PickRandom(list); throw new InvalidOperationException("没有女性音色"); } /// /// 随机指定性别的音色(加权) /// public static SoVITSTtsVoice GetRandomByGender(string gender) { if (_byGender.TryGetValue(gender, out var list)) return PickRandom(list); throw new ArgumentException($"未知性别: {gender}"); } /// /// 随机指定年龄段的音色(加权) /// public static SoVITSTtsVoice GetRandomByAge(string ageGroup) { if (_byAge.TryGetValue(ageGroup, out var list)) return PickRandom(list); throw new ArgumentException($"未知年龄段: {ageGroup}"); } /// /// 随机指定性别和年龄段的音色(加权) /// public static SoVITSTtsVoice GetRandomByGenderAndAge(string gender, string ageGroup) { var key = (gender, ageGroup); if (_byGenderAndAge.TryGetValue(key, out var list)) return PickRandom(list); throw new ArgumentException($"未找到性别 '{gender}' 和年龄段 '{ageGroup}' 的组合"); } // -------------------- 常用组合快捷方法 -------------------- public static SoVITSTtsVoice GetRandomElderlyMale() => GetRandomByGenderAndAge("男性", "老年人"); public static SoVITSTtsVoice GetRandomElderlyFemale() => GetRandomByGenderAndAge("女性", "老年人"); public static SoVITSTtsVoice GetRandomMiddleAgedMale() => GetRandomByGenderAndAge("男性", "男中年"); public static SoVITSTtsVoice GetRandomMiddleAgedFemale() => GetRandomByGenderAndAge("女性", "女中年"); public static SoVITSTtsVoice GetRandomYoungMale() => GetRandomByGenderAndAge("男性", "男青年"); public static SoVITSTtsVoice GetRandomYoungFemale() => GetRandomByGenderAndAge("女性", "女青年"); public static SoVITSTtsVoice GetRandomChildMale() => GetRandomByGenderAndAge("男性", "男儿童"); public static SoVITSTtsVoice GetRandomChildFemale() => GetRandomByGenderAndAge("女性", "女儿童"); // -------------------- 排除指定音色的扩展(可选)-------------------- /// /// 随机男性音色,排除某个指定音色(加权) /// public static SoVITSTtsVoice GetRandomMaleExclude(SoVITSTtsVoice exclude) { if (_byGender.TryGetValue("男性", out var list)) { var filtered = list.Where(e => e.Voice != exclude).ToList(); if (filtered.Count == 0) throw new InvalidOperationException("排除后没有可用的男性音色"); return PickRandom(filtered); } throw new InvalidOperationException("没有男性音色"); } /// /// 随机女性音色,排除某个指定音色(加权) /// public static SoVITSTtsVoice GetRandomFemaleExclude(SoVITSTtsVoice exclude) { if (_byGender.TryGetValue("女性", out var list)) { var filtered = list.Where(e => e.Voice != exclude).ToList(); if (filtered.Count == 0) throw new InvalidOperationException("排除后没有可用的女性音色"); return PickRandom(filtered); } throw new InvalidOperationException("没有女性音色"); } /// /// 从所有音色中随机,排除某个指定音色(加权) /// public static SoVITSTtsVoice GetRandomExclude(SoVITSTtsVoice exclude) { var filtered = _allEntries.Where(e => e.Voice != exclude).ToList(); if (filtered.Count == 0) throw new InvalidOperationException("排除后没有可用的音色"); return PickRandom(filtered); } // -------------------- 获取所有分组信息(便于调试)-------------------- public static IReadOnlyList GetAllVoices() => _allEntries.Select(e => e.Voice).ToList().AsReadOnly(); public static IReadOnlyList GetMaleVoices() => _byGender["男性"].Select(e => e.Voice).ToList().AsReadOnly(); public static IReadOnlyList GetFemaleVoices() => _byGender["女性"].Select(e => e.Voice).ToList().AsReadOnly(); public static IReadOnlyList GetVoicesByAge(string ageGroup) => _byAge[ageGroup].Select(e => e.Voice).ToList().AsReadOnly(); public static IReadOnlyList GetVoicesByGenderAndAge(string gender, string ageGroup) => _byGenderAndAge[(gender, ageGroup)].Select(e => e.Voice).ToList().AsReadOnly(); } }