using CloudBuilder.AI.Policy; using SherpaOnnx; using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Reflection; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; using static System.Net.Mime.MediaTypeNames; namespace CloudBuilder.Topshelf.Utility { public static class SaveAudioToFileHelper { private static OfflineTts fanchenTTs = null; private static OfflineTts sherpaTTs = null; private static OfflineTts theresaTTs = null; private static OfflineTts meloTTs = null; private static OfflineTts icefallTTs = null; private static OfflineTts matchaTTs = null; private static OfflineTts eulaTTs = null; public static void InitConfig() { OfflineTtsConfig config; OfflineTtsGeneratedAudio audio; int numThreads = 4; if (fanchenTTs == null) { config = new OfflineTtsConfig(); config.Model.Vits.Model = "tts-models/vits-zh-hf-fanchen-C/vits-zh-hf-fanchen-C.onnx"; config.Model.Vits.Tokens = "tts-models/vits-zh-hf-fanchen-C/tokens.txt"; config.Model.Vits.Lexicon = "tts-models/vits-zh-hf-fanchen-C/lexicon.txt"; config.RuleFars = "tts-models/vits-zh-hf-fanchen-C/number.fst,tts-models/vits-zh-hf-fanchen-C/phone.fst"; config.Model.NumThreads = numThreads; //187 fanchenTTs = new OfflineTts(config); config = new OfflineTtsConfig(); config.Model.Vits.Model = "tts-models/sherpa-onnx-vits-zh-ll/model.onnx"; config.Model.Vits.Tokens = "tts-models/sherpa-onnx-vits-zh-ll/tokens.txt"; config.Model.Vits.Lexicon = "tts-models/sherpa-onnx-vits-zh-ll/lexicon.txt"; config.RuleFars = "tts-models/sherpa-onnx-vits-zh-ll/number.fst,tts-models/sherpa-onnx-vits-zh-ll/phone.fst"; config.Model.NumThreads = numThreads; //多人音质最好(0 - 4) sherpaTTs = new OfflineTts(config); config = new OfflineTtsConfig(); config.Model.Vits.Model = "tts-models/vits-zh-hf-theresa/theresa.onnx"; config.Model.Vits.Tokens = "tts-models/vits-zh-hf-theresa/tokens.txt"; config.Model.Vits.Lexicon = "tts-models/vits-zh-hf-theresa/lexicon.txt"; config.RuleFars = "tts-models/vits-zh-hf-theresa/number.fst,tts-models/vits-zh-hf-theresa/phone.fst"; config.Model.NumThreads = numThreads; //804 theresaTTs = new OfflineTts(config); config = new OfflineTtsConfig(); config.Model.Vits.Model = "tts-models/vits-melo-tts-zh_en/model.onnx"; config.Model.Vits.Tokens = "tts-models/vits-melo-tts-zh_en/tokens.txt"; config.Model.Vits.Lexicon = "tts-models/vits-melo-tts-zh_en/lexicon.txt"; config.RuleFars = "tts-models/vits-melo-tts-zh_en/number.fst,tts-models/vits-melo-tts-zh_en/phone.fst"; config.Model.NumThreads = numThreads; //1 meloTTs = new OfflineTts(config); config = new OfflineTtsConfig(); config.Model.Vits.Model = "tts-models/vits-icefall-zh-aishell3/model.onnx"; config.Model.Vits.Tokens = "tts-models/vits-icefall-zh-aishell3/tokens.txt"; config.Model.Vits.Lexicon = "tts-models/vits-icefall-zh-aishell3/lexicon.txt"; config.RuleFars = "tts-models/vits-icefall-zh-aishell3/number.fst,tts-models/vits-icefall-zh-aishell3/phone.fst"; config.Model.NumThreads = numThreads; //中文、32 个不同说话人,最快 icefallTTs = new OfflineTts(config); config = new OfflineTtsConfig(); config.Model.Vits.Model = "tts-models/vits-zh-hf-eula/eula.onnx"; config.Model.Vits.Tokens = "tts-models/vits-zh-hf-eula/tokens.txt"; config.Model.Vits.Lexicon = "tts-models/vits-zh-hf-eula/lexicon.txt"; config.RuleFars = "tts-models/vits-zh-hf-eula/number.fst,tts-models/vits-zh-hf-eula/phone.fst"; config.Model.NumThreads = numThreads; eulaTTs = new OfflineTts(config); config = new OfflineTtsConfig(); config.Model.Matcha.AcousticModel = "tts-models/matcha-icefall-zh-baker/model-steps-3.onnx"; config.Model.Matcha.Vocoder = "tts-models/matcha-icefall-zh-baker/vocos-22khz-univ.onnx"; config.Model.Matcha.Tokens = "tts-models/matcha-icefall-zh-baker/tokens.txt"; config.Model.Matcha.Lexicon = "tts-models/matcha-icefall-zh-baker/lexicon.txt"; config.RuleFars = "tts-models/matcha-icefall-zh-baker/number.fst,tts-models/matcha-icefall-zh-baker/phone.fst"; config.Model.NumThreads = numThreads; //1 matchaTTs = new OfflineTts(config); } } public static void AudioToFile(ChineseTtsVoice actorName, float speed, string text, string saveFilePath) { InitConfig(); int sid = 0; OfflineTtsGeneratedAudio audio; switch (actorName) { case ChineseTtsVoice.Fanchen10: case ChineseTtsVoice.Fanchen110: case ChineseTtsVoice.Fanchen6: case ChineseTtsVoice.Fanchen15: case ChineseTtsVoice.Fanchen2: case ChineseTtsVoice.Fanchen20: case ChineseTtsVoice.Fanchen14: case ChineseTtsVoice.Fanchen36: sid = actorName.GetSidId(); audio = fanchenTTs.Generate(text, speed, sid); break; case ChineseTtsVoice.Sherpa0: case ChineseTtsVoice.Sherpa1: case ChineseTtsVoice.Sherpa2: case ChineseTtsVoice.Sherpa3: case ChineseTtsVoice.Sherpa4: sid = actorName.GetSidId(); audio = sherpaTTs.Generate(text, speed, sid); break; case ChineseTtsVoice.Melo: sid = actorName.GetSidId(); audio = meloTTs.Generate(text, speed, sid); break; case ChineseTtsVoice.Eula: sid = actorName.GetSidId(); audio = eulaTTs.Generate(text, speed, sid); break; case ChineseTtsVoice.Theresa: sid = actorName.GetSidId(); audio = theresaTTs.Generate(text, speed, sid); break; case ChineseTtsVoice.Aaishe1: case ChineseTtsVoice.Aaishe6: case ChineseTtsVoice.Aaishe14: case ChineseTtsVoice.Aaishe15: case ChineseTtsVoice.Aaishe18: case ChineseTtsVoice.Aaishe10: sid = actorName.GetSidId(); audio = icefallTTs.Generate(text, speed, sid); break; default: sid = actorName.GetSidId(); audio = matchaTTs.Generate(text, speed, sid); break; } string directoryName = Path.GetDirectoryName(saveFilePath); if (!Directory.Exists(directoryName)) Directory.CreateDirectory(directoryName); // --- 4. 保存音频 --- audio.SaveToWaveFile(saveFilePath); } } public static class ChineseTtsVoiceExtensions { /// /// 获取枚举值对应的Description特性值 /// /// 语音枚举值 /// 描述字符串(无特性时返回枚举名称) public static int GetSidId(this ChineseTtsVoice voice) { // 获取枚举成员的特性 var field = voice.GetType().GetField(voice.ToString()); var attribute = field?.GetCustomAttribute(); // 有特性返回描述,无则返回枚举名 return Convert.ToInt32(attribute?.Description.Split('-')[1] ?? "0"); } } public static class EnumHelper { // 枚举转字符串 public static string ToEnumString(T value) where T : Enum { return value.ToString(); } // 字符串转枚举 public static T ToEnum(string str) where T : struct, Enum { return Enum.TryParse(str, out var result) ? result : default; } } }