C# 调用 VITS,推理模型 将文字转wav音频net8.0 跨平台

news2025/3/31 18:06:42

一、系统环境

操作系统:win10,win11

运行环境:dotnet8

工具:命令行,powershell

开源库:sherpa-onnx

二、工具和源码下载

开源库:https://k2-fsa.github.io/sherpa/onnx/index.html

 

运行环境下载

 https://dotnet.microsoft.com/zh-cn/download/visual-studio-sdks?cid=getdotnetsdk

 

三、目录结构

 

四、开始编译

 4.1.使用ps进入目录

 

 cd D:\MyWork\aiwlzc\gigc

4.2. 还原工程和依赖

dotnet nuget locals all --list

4.3 编译文件生成exe

 

dotnet build

 

五、运行测试

 

 

offline-tts.exe --matcha-acoustic-model=./matcha-icefall-zh-baker/model-steps-3.onnx \ --matcha-vocoder=./vocos-22khz-univ.onnx \ --lexicon=./matcha-icefall-zh-baker/lexicon.txt \ --tokens=./matcha-icefall-zh-baker/tokens.txt \ --dict-dir=./matcha-icefall-zh-baker/dict \ --tts-rule-fsts=./matcha-icefall-zh-baker/phone.fst,./matcha-icefall-zh-baker/date.fst,./matcha-icefall-zh-baker/number.fst \ --debug=1 \ --output-filename=./未来之窗vits.wav \ --text='在未来的繁华都市,“未来之窗” 科技公司宛如神明般掌控世界走向。他们率先攻克可控核聚变难题,清洁、无尽的能源如电流般涌向全球,瞬间改写能源格局,让污染工厂成为历史。其研发的量子脑机接口更是神奇,戴上设备,人类能与计算机意识相连。学生眨眼间掌握海量知识,科研人员思维突破禁锢,灵感如泉涌。凭借超算与 AI 算法,精准预测地震、海啸,提前疏散民众,化险为夷。“未来之窗” 用科技锻造坚实护盾,引领人类迈向璀璨新纪元,在科技的王座上熠熠生辉 '

六、模型库下载

https://objects.githubusercontent.com/github-production-release-asset-2e65be/531380835/23b01fb6-e209-441c-b835-84e906b213e2?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=releaseassetproduction%2F20250322%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20250322T185542Z&X-Amz-Expires=300&X-Amz-Signature=2526dbd564fa852810b627cd969b6c539ab247c59ff36d0fd93c8166ce20f813&X-Amz-SignedHeaders=host&response-content-disposition=attachment%3B%20filename%3Dvocos-22khz-univ.onnx&response-content-type=application%2Foctet-stream

https://github.com/k2-fsa/sherpa-onnx/releases/download/tts-models/vits-piper-en_US-amy-low.tar.bz2 https://github.com/k2-fsa/sherpa-onnx/releases/download/vocoder-models/vocos-22khz-univ.onnx

 七、官方命令


dotnet run \
  --matcha-acoustic-model=./matcha-icefall-en_US-ljspeech/model-steps-3.onnx \
  --matcha-vocoder=./vocos-22khz-univ.onnx \
  --tokens=./matcha-icefall-zh-baker/tokens.txt \
  --data-dir=./matcha-icefall-en_US-ljspeech/espeak-ng-data \
  --debug=1 \
  --output-filename=./matcha-zh.wav \
  --text='Today as always, men fall into two groups: slaves and free men. Whoever does not have two-thirds of his day for himself, is a slave, whatever he may be: a statesman, a businessman, an official, or a scholar.'

# vits-aishell3

curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/tts-models/vits-icefall-zh-aishell3.tar.bz2
tar xvf vits-icefall-zh-aishell3.tar.bz2

dotnet run \
  --vits-model=./vits-icefall-zh-aishell3/model.onnx \
  --tokens=./vits-icefall-zh-aishell3/tokens.txt \
  --lexicon=./vits-icefall-zh-aishell3/lexicon.txt \
  --tts-rule-fsts=./vits-icefall-zh-aishell3/phone.fst,./vits-icefall-zh-aishell3/date.fst,./vits-icefall-zh-aishell3/number.fst \
  --tts-rule-fars=./vits-icefall-zh-aishell3/rule.far \
  --sid=66 \
  --debug=1 \
  --output-filename=./aishell3-66.wav \
  --text=这是一个语音合成测试

# Piper models

wget -qq https://github.com/k2-fsa/sherpa-onnx/releases/download/tts-models/vits-piper-en_US-amy-low.tar.bz2
tar xf vits-piper-en_US-amy-low.tar.bz2

dotnet run \
  --vits-model=./vits-piper-en_US-amy-low/en_US-amy-low.onnx \
  --tokens=./vits-piper-en_US-amy-low/tokens.txt \
  --data-dir=./vits-piper-en_US-amy-low/espeak-ng-data \
  --debug=1 \
  --output-filename=./amy.wav \
  --text='This is a text to speech application in dotnet with Next Generation Kaldi'

Please refer to
https://k2-fsa.github.io/sherpa/onnx/tts/pretrained_models/index.html
to download more models.
";

    var helpText = HelpText.AutoBuild(result, h =>
    {
      h.AdditionalNewLineAfterOption = false;
      h.Heading = usage;
      h.Copyright = "Copyright (c) 2024 Xiaomi Corporation";
      return HelpText.DefaultParsingErrorsHandler(result, h);
    }, e => e);
    Console.WriteLine(helpText);
  }


dotnet new console -n offline-tts-play
dotnet new sln  -n sherpa-onnx.sln
dotnet sln ./sherpa-onnx.sln add ./offline-tts-play
dotnet nuget locals all --list
dotnet nuget locals all --clear

八、源码

  private void btn_启动_Click(object sender, EventArgs e)
        {
            // 模型和文件路径
            string vitsModelPath = this.txt_agi_path.Text + "model.onnx";// @"./vits-melo-tts-zh_en/model.onnx";
            string vitsLexiconPath = this.txt_agi_path.Text + "lexicon.txt";// @"./vits-melo-tts-zh_en/lexicon.txt";
            string vitsTokensPath = this.txt_agi_path.Text + "tokens.txt";//= @"./vits-melo-tts-zh_en/tokens.txt";
            string vitsDictDir = this.txt_agi_path.Text + "dict";// = @"./vits-melo-tts-zh_en/dict";
            string outputFilename = Application.StartupPath + "/agivits/" + DateTime.Now.ToLongTimeString().Replace(':', '_') + ".wav";// @"./zh-en-3.wav";
            string text = "它也支持繁体字. 我相信你們一定聽過愛迪生說過的這句話Genius is one percent inspiration and ninety-nine percent perspiration. ";


            Options.Model= vitsModelPath;
            Options.Lexicon= vitsLexiconPath;
            Options.Tokens=vitsTokensPath;
            //  public static string OutputFilename { get; set; } = "./generated.wav";
            Options.OutputFilename = outputFilename;
            //
            Options.DictDir= vitsDictDir;
            Options.DataDir = vitsDictDir;
            
            var config = new OfflineTtsConfig();

          
            config.Model.Vits.Model = Options.Model;
            config.Model.Vits.Lexicon = Options.Lexicon;
            config.Model.Vits.Tokens = Options.Tokens;

            config.Model.Vits.DataDir = Options.DataDir;
            config.Model.Vits.DictDir = Options.DictDir;
            config.Model.Vits.NoiseScale = Options.NoiseScale;
            config.Model.Vits.NoiseScaleW = Options.NoiseScaleW;
            config.Model.Vits.LengthScale = Options.LengthScale;

            config.Model.Matcha.AcousticModel = Options.AcousticModel;
           config.Model.Matcha.Vocoder = Options.Vocoder;
    

            config.Model.NumThreads = 1;
            config.Model.Debug = Options.Debug;
            config.Model.Provider = "cpu";
           config.RuleFsts = Options.RuleFsts;
           config.MaxNumSentences = Options.MaxNumSentences;

            var tts = new OfflineTts(config);
            var speed = 1.0f / Options.LengthScale;
            var sid = Options.SpeakerId;

            Console.WriteLine(PortAudio.VersionInfo.versionText);
            PortAudio.Initialize();
            Console.WriteLine($"Number of devices: {PortAudio.DeviceCount}");

            for (int i = 0; i != PortAudio.DeviceCount; ++i)
            {
                Console.WriteLine($" Device {i}");
                DeviceInfo deviceInfo = PortAudio.GetDeviceInfo(i);
                Console.WriteLine($"   Name: {deviceInfo.name}");
                Console.WriteLine($"   Max output channels: {deviceInfo.maxOutputChannels}");
                Console.WriteLine($"   Default sample rate: {deviceInfo.defaultSampleRate}");
            }
            int deviceIndex = PortAudio.DefaultOutputDevice;
            if (deviceIndex == PortAudio.NoDevice)
            {
                Console.WriteLine("No default output device found. Please use ../offline-tts instead");
                textBox1.Text = "No default output device found. Please use ../offline-tts instead";
               // Environment.Exit(1);

            }

            var info = PortAudio.GetDeviceInfo(deviceIndex);
            Console.WriteLine();
            Console.WriteLine($"Use output default device {deviceIndex} ({info.name})");
            textBox1.Text = $"Use output default device {deviceIndex} ({info.name})";
            var param = new StreamParameters();
            param.device = deviceIndex;
            param.channelCount = 1;
            param.sampleFormat = SampleFormat.Float32;
            param.suggestedLatency = info.defaultLowOutputLatency;
            param.hostApiSpecificStreamInfo = IntPtr.Zero;

            // https://learn.microsoft.com/en-us/dotnet/standard/collections/thread-safe/blockingcollection-overview
         //   var dataItems = new BlockingCollection<float[]>();
            /*
            var MyCallback = (IntPtr samples, int n) =>
            {
                float[] data = new float[n];

                Marshal.Copy(samples, data, 0, n);

                dataItems.Add(data);

                // 1 means to keep generating
                // 0 means to stop generating
                return 1;
            };
            */
            

            var playFinished = false;

            float[] lastSampleArray = null;
            int lastIndex = 0; // not played

            PortAudioSharp.Stream.Callback playCallback = (IntPtr input, IntPtr output,
                UInt32 frameCount,
                ref StreamCallbackTimeInfo timeInfo,
                StreamCallbackFlags statusFlags,
                IntPtr userData
                ) =>
            {
                if (dataItems.IsCompleted && lastSampleArray == null && lastIndex == 0)
                {
                    Console.WriteLine($"Finished playing");
                   // textBox1.Text = $"Failed to write {Options.OutputFilename}";
                    playFinished = true;
                    return StreamCallbackResult.Complete;
                }

                int expected = Convert.ToInt32(frameCount);
                int i = 0;

                while ((lastSampleArray != null || dataItems.Count != 0) && (i < expected))
                {
                    int needed = expected - i;

                    if (lastSampleArray != null)
                    {
                        int remaining = lastSampleArray.Length - lastIndex;
                        if (remaining >= needed)
                        {
                            float[] this_block = lastSampleArray.Skip(lastIndex).Take(needed).ToArray();
                            lastIndex += needed;
                            if (lastIndex == lastSampleArray.Length)
                            {
                                lastSampleArray = null;
                                lastIndex = 0;
                            }

                            Marshal.Copy(this_block, 0, IntPtr.Add(output, i * sizeof(float)), needed);
                            return StreamCallbackResult.Continue;
                        }

                        float[] this_block2 = lastSampleArray.Skip(lastIndex).Take(remaining).ToArray();
                        lastIndex = 0;
                        lastSampleArray = null;

                        Marshal.Copy(this_block2, 0, IntPtr.Add(output, i * sizeof(float)), remaining);
                        i += remaining;
                        continue;
                    }

                    if (dataItems.Count != 0)
                    {
                        lastSampleArray = dataItems.Take();
                        lastIndex = 0;
                    }
                }

                if (i < expected)
                {
                    int sizeInBytes = (expected - i) * 4;
                    Marshal.Copy(new byte[sizeInBytes], 0, IntPtr.Add(output, i * sizeof(float)), sizeInBytes);
                }

                return StreamCallbackResult.Continue;
            };

            PortAudioSharp.Stream stream = new PortAudioSharp.Stream(inParams: null, outParams: param, sampleRate: tts.SampleRate,
                framesPerBuffer: 0,
                streamFlags: StreamFlags.ClipOff,
                callback: playCallback,
                userData: IntPtr.Zero
                );

            stream.Start();

            var callback = new OfflineTtsCallback(MyCallback);

            var audio = tts.GenerateWithCallback(Options.Text, speed, sid, callback);
            var ok = audio.SaveToWaveFile(Options.OutputFilename);

            if (ok)
            {
                Console.WriteLine($"Wrote to {Options.OutputFilename} succeeded!");
                textBox1.Text = $"succeeded to write {Options.OutputFilename}";
            }
            else
            {
                Console.WriteLine($"Failed to write {Options.OutputFilename}");
                textBox1.Text = $"Failed to write {Options.OutputFilename}";
            }
            dataItems.CompleteAdding();

            while (!playFinished)
            {
                Thread.Sleep(100); // 100ms
            }
        
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2321057.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C#基于MVC模式实现TCP三次握手,附带简易日志管理模块

C#基于MVC模式实现TCP三次握手 1 Model1.1 ServerModel1.2 ClientModel1.3 配置参数模块1.4 日志管理模块1.4.1 数据结构1.4.1 日志管理工具类1.4.1 日志视图展示1.4.1.1 UcLogManage.cs1.4.1.2 UcLogManage.Designer.cs 2 视图&#xff08;View&#xff09;2.1 ViewServer2.1.…

分布式算法:Paxos Raft 两种共识算法

1. Paxos算法 Paxos算法是 Leslie Lamport&#xff08;莱斯利兰伯特&#xff09;在 1990 年提出的一种分布式系统共识算法。也是第一个被证明完备的共识算法&#xff08;前提是不存在恶意节点&#xff09;。 1.1 简介 Paxos算法是第一个被证明完备的分布式系统共识算法。共识…

什么是数据库监控

数据库监控是一个综合的过程&#xff0c;涉及观察、分析和优化组织内数据库的性能、运行状况和可用性。通过持续跟踪查询执行时间、CPU使用率、内存消耗和存储I/O等指标&#xff0c;数据库监控使管理员能够主动识别和解决潜在问题。这种对数据库操作的实时可见性对于确保应用程…

Java学习总结-泛型

什么是泛型&#xff1f; 定义 类、接口、方法时&#xff0c;同时声明了一个或多个类型变量&#xff08;如&#xff1a;<E>&#xff09;&#xff0c;称为泛型类、泛型接口、泛型方法、他们统称为泛型。public class ArrayList<E>{ }。 有什么作用呢&#xf…

基于深度学习的相位调制算法步骤

1.构建网络结构 2.制作数据集 3.训练网络 4.引入评价指标 5.迭代优化 总结 通过以上步骤&#xff0c;可以实现基于深度学习的相位调制算法&#xff1a; 使用 U-Net 构建神经网络。 生成数据集并训练网络。 使用训练好的网络预测相位分布。 通过相关系数 γ 评估调制效果&…

Go语言的基础类型

一基础数据类型 一、布尔型&#xff08;Bool&#xff09; 定义&#xff1a;表示逻辑真 / 假&#xff0c;仅有两个值&#xff1a;true 和 false内存占用&#xff1a;1 字节使用场景&#xff1a;条件判断、逻辑运算 二、数值型&#xff08;Numeric&#xff09; 1. 整数类型&…

动力保护板测试仪:电池安全的坚实守护者

在新能源技术日新月异的今天&#xff0c;电池作为各类电子设备的心脏&#xff0c;其安全性与可靠性成为了行业内外关注的焦点。而动力保护板&#xff0c;作为电池系统中的重要组成部分&#xff0c;承担着精准调控电池充放电、防止电池过充、过放、短路等危险情况的重任。然而&a…

Lineageos 22.1(Android 15)制定应用强制横屏

一、前言 有时候需要系统的某个应用强制衡平显示&#xff0c;不管他是如何配置的。我们只需要简单的拿到top的Task下面的ActivityRecord&#xff0c;并判断包名来强制实现。 二、调整wms com.android.server.wm.DisplayRotation /*** Given an orientation constant, return…

SpringBoot-3-JWT令牌

目录 引入 引入依赖 拦截器 创建工具类 创建拦截器的包及拦截器 注册拦截器 修改一下登录成功后token的代码 测试 引入 试想一下&#xff0c;以上我们的访问都是直接访问对应的接口。比如说用户登录的时候就访问登录的接口。 那么如果有人他不访问你的登录接口&#…

git使用经验(一)

git使用经验&#xff08;一&#xff09; 我之前已经下载了别人的代码&#xff0c;我想在此基础上进行修改&#xff0c;并移动到自己的私有仓库&#xff0c;方便上传到自己的私有仓库自己进行版本控制 git clone下来别人的代码&#xff0c;删除有关git的隐藏文件 进入到自己的…

文件上传的小点总结

1.文件上传漏洞 服务器端脚本语言对上传文件没有严格的验证和过滤&#xff0c;就可以给攻击者上传恶意脚本文件的可能。 文件上传检测绕过&#xff1a; 简单思路&#xff1a;&#xff08;这里以前端上传图片为例&#xff09; ①开启phpstudy&#xff0c;启动apache即可&…

基于WebRtc,GB28181,Rtsp/Rtmp,SIP,JT1078,H265/WEB融合视频会议接入方案

智能融合视频会议系统方案—多协议、多场景、全兼容的一站式视频协作平台 OvMeet,LiveMeet针对用户​核心痛点实现功能与用户价值 &#xff0c;Web平台实现MCU多协议&#xff0c;H265/H264等不同编码监控&#xff0c;直播&#xff0c;会议&#xff0c;调度资源统一融合在一套界…

基于漂浮式海上风电场系统的浮式风力发电机matlab仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 基于漂浮式海上风电场系统的浮式风力发电机matlab仿真&#xff0c;通过MATLAB数值仿真对浮式风力发电机的性能做模拟与仿真。 2.系统仿真结果 3.核心程序与模型 版本&#x…

深入探索ArkUI中的@LocalBuilder装饰器:构建高效可维护的UI组件

在ArkUI框架中&#xff0c;组件化开发是提升代码复用性和维护性的关键手段。随着项目复杂度的增加&#xff0c;开发者常常面临如何在保持组件封装性的同时&#xff0c;灵活处理组件内部逻辑的问题。传统的Builder装饰器虽然提供了强大的自定义构建能力&#xff0c;但在某些场景…

快速查询手机是否处于联网状态?

手机是否处于联网状态对于我们日常生活中的沟通、工作和娱乐都至关重要。有时候我们需要迅速了解一个手机号码的在网状态&#xff0c;例如是正常使用、停机、不在网等。而要实现这一功能&#xff0c;我们可以利用挖数据平台提供的在线查询工具&#xff0c;通过API接口来查询手机…

使用 .NET Core 的本地 DeepSeek-R1

使用 .NET 在我的 MacBook Pro 上与当地 LLM 聊天的历程。 如今&#xff0c;只需使用浏览器即可轻松使用 ChatGPT 或其他 genAI。作为开发人员&#xff0c;我们可以通过直接集成 OpenAI API 等来做更复杂的事情。如果我们想在自己的机器上运行 LLM&#xff0c;只是为了找人聊天…

SQL Server查询计划操作符(7.3)——查询计划相关操作符(11)

7.3. 查询计划相关操作符 98&#xff09;Table Scan&#xff1a;该操作符从查询计划参数列确定的表中获取所有数据行。如果其参数列中出现WHERE:()谓词&#xff0c;则只返回满足该谓词的数据行。该操作符为逻辑操作符和物理操作符。该操作符具体如图7.3-98节点1所示。 图 7.3-…

xy轴不等比缩放问题——AUTOCAD c#二次开发

在 AutoCAD .net api里&#xff0c;部分实体&#xff0c;像文字、属性、插入块等&#xff0c;是不支持非等比缩放的。 如需对AutoCAD中图形进行xyz方向不等比缩放&#xff0c;则需进行额外的函数封装。 选择图元&#xff0c;指定缩放基准点&#xff0c;scaleX 0.5, scaleY …

【原创首发】开源基于AT32 SIP/VOIP电话

前言 本次为了反馈各位粉丝的关注&#xff0c;特此分享 AT32_VOIP 工程&#xff0c;此功能其实跟我之前发过的《STM32F429的VOIP功能》是一样的&#xff0c;只是用了AT32F437。 其实那个工程是一个比较Demo中的Demo&#xff0c;很多功能和硬件依赖性太大了。后面项目中发现AT…

一篇文章入门Python Flask框架前后端数据库开发实践(pycharm在anaconda环境下)

Python Flask 是一个轻量级的 Web 应用框架&#xff0c;也被称为微框架。它以简洁、灵活和易于上手的特点而受到开发者的喜爱。 核心特点 轻量级&#xff1a;Flask 核心代码简洁&#xff0c;仅包含 Web 开发的基本功能&#xff0c;不强制使用特定的数据库、模板引擎等&#xf…