195 lines
8.7 KiB
C#
195 lines
8.7 KiB
C#
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
|
|
{
|
|
/// <summary>
|
|
/// 获取枚举值对应的Description特性值
|
|
/// </summary>
|
|
/// <param name="voice">语音枚举值</param>
|
|
/// <returns>描述字符串(无特性时返回枚举名称)</returns>
|
|
public static int GetSidId(this ChineseTtsVoice voice)
|
|
{
|
|
// 获取枚举成员的特性
|
|
var field = voice.GetType().GetField(voice.ToString());
|
|
var attribute = field?.GetCustomAttribute<DescriptionAttribute>();
|
|
|
|
// 有特性返回描述,无则返回枚举名
|
|
return Convert.ToInt32(attribute?.Description.Split('-')[1] ?? "0");
|
|
}
|
|
}
|
|
|
|
public static class EnumHelper
|
|
{
|
|
// 枚举转字符串
|
|
public static string ToEnumString<T>(T value) where T : Enum
|
|
{
|
|
return value.ToString();
|
|
}
|
|
|
|
// 字符串转枚举
|
|
public static T ToEnum<T>(string str) where T : struct, Enum
|
|
{
|
|
return Enum.TryParse<T>(str, out var result) ? result : default;
|
|
}
|
|
}
|
|
}
|
|
|