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, 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, 1.0f, sid);
break;
case ChineseTtsVoice.Sherpa0:
case ChineseTtsVoice.Sherpa1:
case ChineseTtsVoice.Sherpa2:
case ChineseTtsVoice.Sherpa3:
case ChineseTtsVoice.Sherpa4:
sid = actorName.GetSidId();
audio = sherpaTTs.Generate(text, 1.0f, sid);
break;
case ChineseTtsVoice.Melo:
sid = actorName.GetSidId();
audio = meloTTs.Generate(text, 1.0f, sid);
break;
case ChineseTtsVoice.Eula:
sid = actorName.GetSidId();
audio = eulaTTs.Generate(text, 1.0f, sid);
break;
case ChineseTtsVoice.Theresa:
sid = actorName.GetSidId();
audio = theresaTTs.Generate(text, 1.0f, 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, 1.0f, sid);
break;
default:
sid = actorName.GetSidId();
audio = matchaTTs.Generate(text, 1.0f, sid);
break;
}
// --- 4. 保存音频 ---
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
{
///
/// 获取枚举值对应的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;
}
}
}