diff --git a/CloudBuilder.Topshelf.sln b/CloudBuilder.Topshelf.sln index 649aae5..0040087 100644 --- a/CloudBuilder.Topshelf.sln +++ b/CloudBuilder.Topshelf.sln @@ -5,6 +5,8 @@ VisualStudioVersion = 17.8.34316.72 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CloudBuilder.Topshelf", "CloudBuilder.Topshelf.csproj", "{63EB47F6-A616-4C64-B850-85ED294E2524}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CloudBuilder.AI", "..\CloudBuilder\CloudBuilder.AI\CloudBuilder.AI.csproj", "{FD89E87E-BDEB-4040-A380-A5218B2BDAB1}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -15,6 +17,10 @@ Global {63EB47F6-A616-4C64-B850-85ED294E2524}.Debug|Any CPU.Build.0 = Debug|Any CPU {63EB47F6-A616-4C64-B850-85ED294E2524}.Release|Any CPU.ActiveCfg = Release|Any CPU {63EB47F6-A616-4C64-B850-85ED294E2524}.Release|Any CPU.Build.0 = Release|Any CPU + {FD89E87E-BDEB-4040-A380-A5218B2BDAB1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FD89E87E-BDEB-4040-A380-A5218B2BDAB1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FD89E87E-BDEB-4040-A380-A5218B2BDAB1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FD89E87E-BDEB-4040-A380-A5218B2BDAB1}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Lib/CloudBuilder.AI.Service.dll b/Lib/CloudBuilder.AI.Service.dll index 8af1dd1..11627aa 100644 Binary files a/Lib/CloudBuilder.AI.Service.dll and b/Lib/CloudBuilder.AI.Service.dll differ diff --git a/Lib/CloudBuilder.AI.dll b/Lib/CloudBuilder.AI.dll index 891f626..63b5b5a 100644 Binary files a/Lib/CloudBuilder.AI.dll and b/Lib/CloudBuilder.AI.dll differ diff --git a/Properties/launchSettings.json b/Properties/launchSettings.json index 08fab86..aeee29a 100644 --- a/Properties/launchSettings.json +++ b/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "CloudBuilder.Topshelf": { "commandName": "Project", - "commandLineArgs": "task:PythonRunTask book_id:B000010 from_chapter_id:181 to_chapter_id:200 speed:1 voice:Sherpa2 actor:韩立 actor_voice:Sherpa1 save_path:D:\\\\Net8\\\\FileServer\\\\Backup\\\\DmsFile\\\\voice" + "commandLineArgs": "task:SoVITSTtsTask book_id:B000011 chapter_id:3 voice:MIDDLE_AGED_FEMALE5 actor:韩立 actor_voice:YOUNG_MALE2 save_path:D:\\\\Net8\\\\FileServer\\\\Backup\\\\DmsFile\\\\voice" } } } \ No newline at end of file diff --git a/Python/SoVITS.cs b/Python/SoVITS.cs index 550edfd..df643e2 100644 --- a/Python/SoVITS.cs +++ b/Python/SoVITS.cs @@ -1,4 +1,5 @@ -using Python.Runtime; +using CloudBuilder.AI.Policy; +using Python.Runtime; using System; using System.Collections.Generic; using System.Linq; @@ -303,101 +304,6 @@ namespace CloudBuilder.Topshelf.Python } - public enum SoVITSTtsVoice - { - [System.ComponentModel.Description("老年人(男性)-1")] - OLD_MAN1, - [System.ComponentModel.Description("老年人(男性)-2")] - OLD_MAN2, - [System.ComponentModel.Description("老年人(男性)-3")] - OLD_MAN3, - [System.ComponentModel.Description("老年人(男性)-4")] - OLD_MAN4, - [System.ComponentModel.Description("老年人(男性)-5")] - OLD_MAN5, - [System.ComponentModel.Description("老年人(男性)-6")] - OLD_MAN6, - [System.ComponentModel.Description("老年人(男性)-7")] - OLD_MAN7, - [System.ComponentModel.Description("男青年(男性)-1")] - MIDDLE_AGED_MAN1, - [System.ComponentModel.Description("男中年(男性)-2")] - MIDDLE_AGED_MAN2, - [System.ComponentModel.Description("男中年(男性)-3")] - MIDDLE_AGED_MAN3, - [System.ComponentModel.Description("男中年(男性)-4")] - MIDDLE_AGED_MAN4, - [System.ComponentModel.Description("男中年(男性)-5")] - MIDDLE_AGED_MAN5, - [System.ComponentModel.Description("男中年(男性)-6")] - MIDDLE_AGED_MAN6, - [System.ComponentModel.Description("男中年(男性)-7")] - MIDDLE_AGED_MAN7, - [System.ComponentModel.Description("男青年(男性)-1")] - YOUNG_MALE1, - [System.ComponentModel.Description("男青年(男性)-2")] - YOUNG_MALE2, - [System.ComponentModel.Description("男青年(男性)-3")] - YOUNG_MALE3, - [System.ComponentModel.Description("男青年(男性)-4")] - YOUNG_MALE4, - [System.ComponentModel.Description("男青年(男性)-5")] - YOUNG_MALE5, - [System.ComponentModel.Description("男青年(男性)-6")] - YOUNG_MALE6, - [System.ComponentModel.Description("男青年(男性)-7")] - YOUNG_MALE7, - [System.ComponentModel.Description("男儿童(男性)-1")] - MALE_CHILD1, - [System.ComponentModel.Description("男儿童(男性)-2")] - MALE_CHILD2, - [System.ComponentModel.Description("男儿童(男性)-3")] - MALE_CHILD3, - [System.ComponentModel.Description("男儿童(男性)-4")] - MALE_CHILD4, - [System.ComponentModel.Description("老年人(女性)-1")] - ELDERLY_FEMALE1, - [System.ComponentModel.Description("老年人(女性)-2")] - ELDERLY_FEMALE2, - [System.ComponentModel.Description("老年人(女性)-3")] - ELDERLY_FEMALE3, - [System.ComponentModel.Description("女中年(女性)-1")] - MIDDLE_AGED_FEMALE1, - [System.ComponentModel.Description("女中年(女性)-2")] - MIDDLE_AGED_FEMALE2, - [System.ComponentModel.Description("女中年(女性)-3")] - MIDDLE_AGED_FEMALE3, - [System.ComponentModel.Description("女中年(女性)-4")] - MIDDLE_AGED_FEMALE4, - [System.ComponentModel.Description("女中年(女性)-5")] - MIDDLE_AGED_FEMALE5, - [System.ComponentModel.Description("女中年(女性)-6")] - MIDDLE_AGED_FEMALE6, - [System.ComponentModel.Description("女中年(女性)-7")] - MIDDLE_AGED_FEMALE7, - [System.ComponentModel.Description("女青年(女性)-1")] - YOUNG_FEMALE1, - [System.ComponentModel.Description("女青年(女性)-2")] - YOUNG_FEMALE2, - [System.ComponentModel.Description("女青年(女性)-3")] - YOUNG_FEMALE3, - [System.ComponentModel.Description("女青年(女性)-4")] - YOUNG_FEMALE4, - [System.ComponentModel.Description("女青年(女性)-5")] - YOUNG_FEMALE5, - [System.ComponentModel.Description("女青年(女性)-6")] - YOUNG_FEMALE6, - [System.ComponentModel.Description("女青年(女性)-7")] - YOUNG_FEMALE7, - [System.ComponentModel.Description("女儿童(女性)-1")] - FEMALE_CHILD1, - [System.ComponentModel.Description("女儿童(女性)-2")] - FEMALE_CHILD2, - [System.ComponentModel.Description("女儿童(女性)-3")] - FEMALE_CHILD3, - [System.ComponentModel.Description("女儿童(女性)-4")] - FEMALE_CHILD4, - } } /* import os diff --git a/Task/AI/NameGenderPredictorTask.cs b/Task/AI/NameGenderPredictorTask.cs index bf4d36d..e2059a0 100644 --- a/Task/AI/NameGenderPredictorTask.cs +++ b/Task/AI/NameGenderPredictorTask.cs @@ -9,10 +9,11 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using static System.Runtime.InteropServices.JavaScript.JSType; namespace CloudBuilder.Topshelf.Task.AI { - //task:NameGenderPredictorTask book_id:B000010 from_chapter_id:177 to_chapter_id:200 + //task:NameGenderPredictorTask book_id:B000011 from_chapter_id:177 to_chapter_id:200 public class NameGenderPredictorTask : IScheduleTask { private readonly IApplicationService service; @@ -59,6 +60,41 @@ namespace CloudBuilder.Topshelf.Task.AI ConsoleOutput.UpdateProgress(processedLines, totalLines); } + string[] personNames = aiSentences.Select(x => x.PersonName).Distinct().ToArray(); + if (personNames != null && personNames.Length > 0) + { + AiBookPersonEntity aiBookPersonEntity; + IRepository repositoryAiBookPersonEntity = service.GetRepository>(); + + AiBookPersonEntity[] bps = repositoryAiBookPersonEntity.DetachedEntities.Where(x => x.BookId == aiSentences[0].BookId).ToArray(); + + if (bps != null && bps.Length > 0) + { + repositoryAiBookPersonEntity.DeleteNow(bps); + } + + List ps = new List(); + //旁白 + aiBookPersonEntity = new AiBookPersonEntity(); + aiBookPersonEntity.BookId = aiSentences[0].BookId; + aiBookPersonEntity.PersonName = null; + aiBookPersonEntity.ActorName = SoVITSTtsVoiceRandom.GetRandomByGender("女性").ToString(); + ps.Add(aiBookPersonEntity); + + foreach (var personName in personNames) + { + if (string.IsNullOrEmpty(personName)) continue; + aiBookPersonEntity = new AiBookPersonEntity(); + aiBookPersonEntity.BookId = aiSentences[0].BookId; + aiBookPersonEntity.PersonName = personName; + aiBookPersonEntity.ActorName = SoVITSTtsVoiceRandom.GetRandomByGender(NameGenderPredictor.IsMale(personName) == true ? "男性" : "女性").ToString(); + + ps.Add(aiBookPersonEntity); + } + + repositoryAiBookPersonEntity.InsertNow(ps); + } + repositoryAiSentenceEntity.UpdateNow(aiSentences); } catch (Exception ex) diff --git a/Task/Python/PythonRunTask.cs b/Task/Python/PythonRunTask.cs index a7d2739..892bc9f 100644 --- a/Task/Python/PythonRunTask.cs +++ b/Task/Python/PythonRunTask.cs @@ -1,4 +1,5 @@ -using CloudBuilder.Core.DatabaseAccessor.Entity; +using CloudBuilder.AI.Policy; +using CloudBuilder.Core.DatabaseAccessor.Entity; using CloudBuilder.Core.DependencyInjection.Task; using CloudBuilder.Topshelf.Python; using CloudBuilder.Topshelf.Utility; @@ -17,18 +18,23 @@ namespace CloudBuilder.Topshelf.Task public void Run(Dictionary bodyDict) { - Python39Command python39Command = new Python39Command(service); + //Python39Command python39Command = new Python39Command(service); try { - SoVITS soVITS = new SoVITS(python39Command); - Array voiceArray = Enum.GetValues(typeof(SoVITSTtsVoice)); + //SoVITS soVITS = new SoVITS(python39Command); + //Array voiceArray = Enum.GetValues(typeof(SoVITSTtsVoice)); - foreach (SoVITSTtsVoice voice in voiceArray) + //foreach (SoVITSTtsVoice voice in voiceArray) + //{ + // soVITS.Generate("三更时分,青龙城毫无征兆地被血色浓雾笼罩,雾气带着诡异的幻术气息,城中低阶修士尽数陷入昏睡.", soVITS.Voices[voice], $"F:\\GPT-SoVITS\\gen\\{(int)voice}.mp3"); + //} + + Array voices = Enum.GetValues(typeof(ChineseTtsVoice)); + + foreach (ChineseTtsVoice voice in voices) { - soVITS.Generate("三更时分,青龙城毫无征兆地被血色浓雾笼罩,雾气带着诡异的幻术气息,城中低阶修士尽数陷入昏睡.", soVITS.Voices[voice], $"F:\\GPT-SoVITS\\gen{(int)voice}.mp3"); + SaveAudioToFileHelper.AudioToFile(voice, 1.0f, "三更时分,青龙城毫无征兆地被血色浓雾笼罩,雾气带着诡异的幻术气息,城中低阶修士尽数陷入昏睡.", $"F:\\GPT-SoVITS\\gen\\{(int)voice}.mp3"); } - - } catch (Exception ex) { @@ -36,7 +42,7 @@ namespace CloudBuilder.Topshelf.Task } finally { - python39Command.Dispose(); + //python39Command.Dispose(); } return; diff --git a/Task/TTS/SoVITSTtsTask.cs b/Task/TTS/SoVITSTtsTask.cs new file mode 100644 index 0000000..1dbc017 --- /dev/null +++ b/Task/TTS/SoVITSTtsTask.cs @@ -0,0 +1,277 @@ +using CloudBuilder.AI.Entity; +using CloudBuilder.AI.Policy; +using CloudBuilder.AI.Utility; +using CloudBuilder.Core.DatabaseAccessor.Entity; +using CloudBuilder.Core.DependencyInjection.Task; +using CloudBuilder.Topshelf.Python; +using CloudBuilder.Topshelf.Utility; +using Microsoft.EntityFrameworkCore.Metadata.Internal; +using Microsoft.IdentityModel.Tokens; +using NAudio.Lame; +using NAudio.Wave; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Threading.Tasks; +using static System.Net.Mime.MediaTypeNames; +using static System.Runtime.InteropServices.JavaScript.JSType; + +namespace CloudBuilder.Topshelf.Task.TTS +{ + //task:SoVITSTtsTask book_id:B000011 chapter_id:3 voice:MIDDLE_AGED_FEMALE5 actor:韩立 actor_voice:YOUNG_MALE2 save_path:D:\\Net8\\FileServer\\Backup\\DmsFile\\voice + public class SoVITSTtsTask : IScheduleTask + { + private readonly IApplicationService service; + + public SoVITSTtsTask(IApplicationService service) + { + this.service = service; + } + + public void Run(Dictionary bodyDict) + { + ConsoleOutput.HideStdError(); + + IRepository repository = service.GetRepository>(); + + string bookId = string.Empty; + if (bodyDict.ContainsKey("book_id")) + bookId = bodyDict["book_id"]; + + string chapterId = string.Empty; + if (bodyDict.ContainsKey("chapter_id")) + chapterId = bodyDict["chapter_id"]; + + string from_chapter_id = string.Empty; + if (bodyDict.ContainsKey("from_chapter_id")) + from_chapter_id = bodyDict["from_chapter_id"]; + + string to_chapter_id = string.Empty; + if (bodyDict.ContainsKey("to_chapter_id")) + to_chapter_id = bodyDict["to_chapter_id"]; + + AiSentenceViewEntity[] ents = repository.DetachedEntities.Where(x => x.BookId == bookId + && (string.IsNullOrEmpty(chapterId) || x.ChapterId == Convert.ToInt32(chapterId)) + && (string.IsNullOrEmpty(from_chapter_id) || x.ChapterId >= Convert.ToInt32(from_chapter_id)) + && (string.IsNullOrEmpty(to_chapter_id) || x.ChapterId <= Convert.ToInt32(to_chapter_id) + )).OrderBy(x => x.ChapterId).ThenBy(x => x.ParagraphId).ThenBy(x => x.SentenceIndex).ToArray(); + + if (ents == null || ents.Length == 0) return; + + string save_path = string.Empty, text = string.Empty, voice = SoVITSTtsVoiceRandom.GetRandomYoungFemale().ToString(), actor = string.Empty, actor_voice = SoVITSTtsVoiceRandom.GetRandomYoungMale().ToString(); + if (bodyDict.ContainsKey("actor")) + actor = bodyDict["actor"]; + + if (bodyDict.ContainsKey("voice")) + voice = bodyDict["voice"]; + + if (bodyDict.ContainsKey("actor_voice")) + actor_voice = bodyDict["actor_voice"]; + + if (bodyDict.ContainsKey("save_path")) + save_path = bodyDict["save_path"]; + + float speed = 1.0f; + + if (bodyDict.ContainsKey("speed")) + speed = Convert.ToSingle(bodyDict["speed"]); + + GenerateAndMergeChapterAudio(ents, speed, actor, actor_voice, voice, save_path); + } + + public void GenerateAndMergeChapterAudio(AiSentenceViewEntity[] ents, float speed, string actor, string actor_voice, string mainVoice, string save_path) + { + IRepository repositoryAiBookPersonEntity = service.GetRepository>(); + AiBookPersonEntity[] bps = repositoryAiBookPersonEntity.DetachedEntities.Where(x => x.BookId == ents[0].BookId).ToArray(); + + + Python39Command python39Command = new Python39Command(service); + SoVITS soVITS = new SoVITS(python39Command); + // 1. 按 BookId + ChapterId 分组(关键!) + var chapterGroups = ents + .Where(ent => ent.Content.Any(c => c >= 0x4E00 && c <= 0x9FFF)) // 只保留含中文的句子 + .GroupBy(ent => new { ent.BookId, ent.ChapterId }) + .ToList(); + + int totalLines = ents.Count(); + int processedLines = 0; // 已处理行数计数器 + UpdateProgress(processedLines, totalLines); + Dictionary personMapping = new Dictionary(); + foreach (var chapter in chapterGroups) + { + string bookId = chapter.Key.BookId; + int chapterId = chapter.Key.ChapterId; + List tempMp3List = new List(); // 本章所有碎片音频 + + // 2. 生成本章所有句子音频 + foreach (AiSentenceViewEntity ent in chapter) + { + try + { + processedLines++; + + string voice = SoVITSTtsVoiceRandom.GetRandomYoungFemale().ToString(); + if (ent.DialogueIndc == YesNoPolicy.YES) + { + string key = string.IsNullOrEmpty(ent.PersonName) ? "NULL" : ent.PersonName; + if (key == actor) + { + if (personMapping.ContainsKey(key)) + voice = personMapping[key]; + else + { + voice = actor_voice; + personMapping.Add(key, voice); + } + + } + else + { + if (!string.IsNullOrEmpty(key) && personMapping.ContainsKey(key)) + { + voice = personMapping[key]; + } + else if (!string.IsNullOrEmpty(key) && bps != null && bps.Length > 0 && bps.Where(x => x.PersonName == key).Any()) + { + voice = bps.Where(x => x.PersonName == key).FirstOrDefault().ActorName; + personMapping.Add(key, voice); + } + else + { + if (ent.Gender == YesNoPolicy.YES) + { + voice = SoVITSTtsVoiceRandom.GetRandomFemaleExclude(EnumHelper.ToEnum(actor_voice)).ToString(); + personMapping.Add(key, voice); + } + else if (ent.Gender == YesNoPolicy.NO) + { + voice = SoVITSTtsVoiceRandom.GetRandomFemale().ToString(); + personMapping.Add(key, voice); + } + else + { + voice = SoVITSTtsVoiceRandom.GetRandom().ToString(); + personMapping.Add(key, voice); + } + } + + } + } + else + { + voice = mainVoice; + if (personMapping.ContainsKey("mainVoice")) + voice = personMapping["mainVoice"]; + else if (bps != null && bps.Length > 0 && bps.Where(x => string.IsNullOrEmpty(x.PersonName)).Any()) + { + voice = bps.Where(x => string.IsNullOrEmpty(x.PersonName)).FirstOrDefault().ActorName; + personMapping.Add("mainVoice", voice); + } + } + + if (string.IsNullOrEmpty(voice) || string.IsNullOrEmpty(ent.Content)) + continue; + + // 碎片文件名:BookId_ChapterId_ParagraphId_SentenceId.mp3 + string fileName = string.Format("{0}_{1}_{2}_{3}.mp3", ent.BookId, ent.ChapterId, ent.ParagraphId, ent.SentenceId); + string filePath = Path.Combine(save_path, fileName); + + // 生成音频 + soVITS.Generate(ent.Content, soVITS.Voices[EnumHelper.ToEnum(voice)], filePath); + + tempMp3List.Add(filePath); // 加入合并列表 + } + catch (Exception ex) + { + Console.WriteLine($"❌ 失败:{ex.Message}"); + } + + UpdateProgress(processedLines, totalLines); + } + + Thread.Sleep(100); + + // 3. 合并为:BookId_ChapterId.mp3 + string outputFileName = string.Format("{0}_{1}.mp3", bookId, chapterId); + string outputFilePath = Path.Combine(save_path, outputFileName); + + MergeChapterMp3Files(tempMp3List, outputFilePath); + + // 4. 可选:合并后删除碎片文件(节省空间) + foreach (var chunk in tempMp3List) + { + try { File.Delete(chunk); } catch { } + } + + UpdateProgress(processedLines, totalLines); + } + } + /// + /// 最终修复:完全解决 No fmt chunk / 采样率不兼容 / 只有一截声音 + /// + public void MergeChapterMp3Files(List chunkFiles, string outputFilePath) + { + var validFiles = chunkFiles.Where(File.Exists).ToList(); + if (validFiles.Count == 0) return; + + try + { + // 目标格式:32000Hz 单声道 16bit + WaveFormat targetFormat = new WaveFormat(32000, 16, 1); + + // 直接生成 MP3,不经过 WAV 文件,彻底避免 fmt 错误 + using (var mp3Writer = new LameMP3FileWriter(outputFilePath, targetFormat, 128)) + { + foreach (var file in validFiles) + { + try + { + // 释放文件占用 + GC.Collect(); + GC.WaitForPendingFinalizers(); + + // 用Windows系统解码器读取任何MP3 + using (var reader = new MediaFoundationReader(file)) + using (var resampler = new MediaFoundationResampler(reader, targetFormat)) + { + byte[] buffer = new byte[8192]; + int read; + while ((read = resampler.Read(buffer, 0, buffer.Length)) > 0) + { + mp3Writer.Write(buffer, 0, read); + } + } + } + catch (Exception ex) + { + Console.WriteLine($"跳过文件:{file},错误:{ex.Message}"); + } + } + } + + //Console.WriteLine($"✅ 合并成功:{outputFilePath},大小:{new FileInfo(outputFilePath).Length} 字节"); + } + catch (Exception ex) + { + //Console.WriteLine($"❌ 合并失败:{ex.Message}"); + } + } + + private static void UpdateProgress(int processed, int total) + { + double progressPercent = (double)processed / total * 100; + int progressBarLength = 50; // 进度条总长度 + int filledLength = (int)(progressPercent / 100 * progressBarLength); + + // 构建进度条(如:[██████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░]) + string progressBar = "[" + new string('█', filledLength) + new string('░', progressBarLength - filledLength) + "]"; + + string progressInfo = $"{progressBar} {progressPercent:F2}% | 已处理:{processed}/{total} "; + Console.Write($"\r{progressInfo}"); + Console.Out.Flush(); + } + } +} diff --git a/Task/TTS/TtsTask.cs b/Task/TTS/TtsTask.cs index a8bfea2..10820fa 100644 --- a/Task/TTS/TtsTask.cs +++ b/Task/TTS/TtsTask.cs @@ -1,4 +1,5 @@ using CloudBuilder.AI.Entity; +using CloudBuilder.AI.Policy; using CloudBuilder.Core.DatabaseAccessor.Entity; using CloudBuilder.Core.DependencyInjection.Task; using CloudBuilder.Topshelf.Utility; diff --git a/Utility/SaveAudioToFileHelper.cs b/Utility/SaveAudioToFileHelper.cs index 71af4ec..6137a41 100644 --- a/Utility/SaveAudioToFileHelper.cs +++ b/Utility/SaveAudioToFileHelper.cs @@ -1,4 +1,5 @@ -using SherpaOnnx; +using CloudBuilder.AI.Policy; +using SherpaOnnx; using System; using System.Collections.Generic; using System.ComponentModel; @@ -156,132 +157,6 @@ namespace CloudBuilder.Topshelf.Utility audio.SaveToWaveFile(saveFilePath); } } - /// - /// 微软Edge TTS中文语音音色枚举 - /// 对应官方Neural语音标识,覆盖港澳台及大陆各地区音色 - /// - public enum ChineseTtsVoice - { - /// - /// 普通话(中国大陆) 娇柔(女性) - /// - [System.ComponentModel.Description("普通话(中国大陆) 喜佳(女性)-0")] - Sherpa0, - /// - /// 普通话(中国大陆)- 柔美(女性) - /// - [System.ComponentModel.Description("普通话(中国大陆) 柔美(女性)-2")] - Sherpa2, - /// - /// 普通话(中国大陆)- 知性(男性) - /// - [System.ComponentModel.Description("普通话(中国大陆) 知性(男性)-4")] - Sherpa4, - /// - /// 普通话(中国大陆)- 知青(男性) - /// - [System.ComponentModel.Description("普通话(中国大陆) 知青(男性)-3")] - Sherpa3, - /// - /// 普通话(中国大陆)- 中青(男性) - /// - [System.ComponentModel.Description("普通话(中国大陆) 中青(男性)-1")] - Sherpa1, - - - /// - /// 普通话(中国大陆)- 爱莎(女性)- 文件小速度快 - /// - [System.ComponentModel.Description("普通话(中国大陆) 爱莎(女性)-6")] - Aaishe6, - - /// - /// 普通话(中国大陆)- 爱莎(女性)- 文件小速度快 - /// - [System.ComponentModel.Description("普通话(中国大陆) 爱莎(女性)-14")] - Aaishe14, - - /// - /// 普通话(中国大陆)- 爱莎(女性)- 文件小速度快 - /// - [System.ComponentModel.Description("普通话(中国大陆) 爱莎(女性)-18")] - Aaishe18, - - /// - /// 普通话(中国大陆)- 云健(男性)- 文件小速度快 - /// - [System.ComponentModel.Description("普通话(中国大陆) 云健(男性)-10")] - Aaishe10, - - /// - /// 普通话(中国大陆)- 爱莎(女性)- 文件小速度快 - /// - [System.ComponentModel.Description("普通话(中国大陆) 爱莎(女性)-1")] - Aaishe1, - - /// - /// 普通话(中国大陆)- 云健(男性)- 文件小速度快 - /// - [System.ComponentModel.Description("普通话(中国大陆) 云健(男性)-15")] - Aaishe15, - - /// 普通话(中国大陆) 云阳(男性) - /// - [System.ComponentModel.Description("普通话(中国大陆) 云阳(男性)-10")] - Fanchen10, - /// - /// 普通话(中国大陆) 云阳(男性) - /// - [System.ComponentModel.Description("普通话(中国大陆) 云阳(男性)-110")] - Fanchen110, - /// 普通话(中国大陆) 喜佳(女性) - /// - [System.ComponentModel.Description("普通话(中国大陆) 云夏(女性)-6")] - Fanchen6, - /// - /// 普通话(中国大陆) 云阳(男性) - /// - [System.ComponentModel.Description("普通话(中国大陆) 云阳(男性)-15")] - Fanchen15, - /// - /// 普通话(中国大陆) 喜佳(女性) - /// - [System.ComponentModel.Description("普通话(中国大陆) 喜佳(女性)-2")] - Fanchen2, - /// - /// 普通话(中国大陆) 喜佳(女性) - /// - [System.ComponentModel.Description("普通话(中国大陆) 喜佳(女性)-14")] - Fanchen14, - /// - /// 普通话(中国大陆) 喜佳(女性) - /// - [System.ComponentModel.Description("普通话(中国大陆) 喜佳(女性)-20")] - Fanchen20, - /// - /// 普通话(中国大陆) 喜佳(女性) - /// - [System.ComponentModel.Description("普通话(中国大陆) 喜佳(女性)-36")] - Fanchen36, - - /// - /// 粤语(香港)- 女性(男性)- 文件大 - /// - [System.ComponentModel.Description("粤语(香港) 梅隆 (女性)-0")] - Melo, - - /// - /// 普通话(中国大陆)- 神精女(女性) - /// - [System.ComponentModel.Description("普通话(中国大陆) 神精女(女性)-0")] - Eula, - - /// - /// 普通话(中国大陆)- 小神精女(女性) - /// - [System.ComponentModel.Description("普通话(中国大陆) 小神精女(女性)-27")] - Theresa, - } public static class ChineseTtsVoiceExtensions {