Fangjun Kuang
Committed by GitHub

Add C# API for KittenTTS (#2477)

@@ -8,6 +8,11 @@ cd ./version-test @@ -8,6 +8,11 @@ cd ./version-test
8 ./run.sh 8 ./run.sh
9 ls -lh 9 ls -lh
10 10
  11 +cd ../kitten-tts
  12 +./run-kitten.sh
  13 +ls -lh
  14 +rm -rf kitten-nano-en-v0_1-fp16
  15 +
11 cd ../vad-non-streaming-asr-paraformer 16 cd ../vad-non-streaming-asr-paraformer
12 ./run-ten-vad.sh 17 ./run-ten-vad.sh
13 rm -fv *.onnx 18 rm -fv *.onnx
@@ -5,7 +5,6 @@ @@ -5,7 +5,6 @@
5 <AllowUnsafeBlocks>true</AllowUnsafeBlocks> 5 <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
6 </PropertyGroup> 6 </PropertyGroup>
7 <ItemGroup> 7 <ItemGroup>
8 - <PackageReference Include="CommandLineParser" Version="2.9.1" />  
9 <PackageReference Include="org.k2fsa.sherpa.onnx" Version="*" /> 8 <PackageReference Include="org.k2fsa.sherpa.onnx" Version="*" />
10 </ItemGroup> 9 </ItemGroup>
11 10
  1 +// Copyright (c) 2025 Xiaomi Corporation
  2 +//
  3 +// This file shows how to use a non-streaming Kitten TTS model
  4 +// for text-to-speech
  5 +// Please refer to
  6 +// https://k2-fsa.github.io/sherpa/onnx/tts/pretrained_models/index.html
  7 +// and
  8 +// https://github.com/k2-fsa/sherpa-onnx/releases/tag/tts-models
  9 +// to download pre-trained models
  10 +using PortAudioSharp;
  11 +using SherpaOnnx;
  12 +using System.Collections.Concurrent;
  13 +using System.Runtime.InteropServices;
  14 +
  15 +class KittenTtsPlayDemo
  16 +{
  17 + static void Main(string[] args)
  18 + {
  19 + var config = new OfflineTtsConfig();
  20 + config.Model.Kitten.Model = "./kitten-nano-en-v0_1-fp16/model.fp16.onnx";
  21 + config.Model.Kitten.Voices = "./kitten-nano-en-v0_1-fp16/voices.bin";
  22 + config.Model.Kitten.Tokens = "./kitten-nano-en-v0_1-fp16/tokens.txt";
  23 + config.Model.Kitten.DataDir = "./kitten-nano-en-v0_1-fp16/espeak-ng-data";
  24 +
  25 + config.Model.NumThreads = 2;
  26 + config.Model.Debug = 1;
  27 + config.Model.Provider = "cpu";
  28 +
  29 + var tts = new OfflineTts(config);
  30 + var speed = 1.0f;
  31 + var text = "Today as always, men fall into two groups: slaves and free men. Whoever " +
  32 + "does not have two-thirds of his day for himself, is a slave, whatever " +
  33 + "he may be: a statesman, a businessman, an official, or a scholar. " +
  34 + "Friends fell out often because life was changing so fast. The easiest " +
  35 + "thing in the world was to lose touch with someone.";
  36 +
  37 + // mapping of sid to voice name
  38 + // 0->expr-voice-2-m, 1->expr-voice-2-f, 2->expr-voice-3-m
  39 + // 3->expr-voice-3-f, 4->expr-voice-4-m, 5->expr-voice-4-f
  40 + // 6->expr-voice-5-m, 7->expr-voice-5-f
  41 + var sid = 0;
  42 +
  43 +
  44 + Console.WriteLine(PortAudio.VersionInfo.versionText);
  45 + PortAudio.Initialize();
  46 + Console.WriteLine($"Number of devices: {PortAudio.DeviceCount}");
  47 +
  48 + for (int i = 0; i != PortAudio.DeviceCount; ++i)
  49 + {
  50 + Console.WriteLine($" Device {i}");
  51 + DeviceInfo deviceInfo = PortAudio.GetDeviceInfo(i);
  52 + Console.WriteLine($" Name: {deviceInfo.name}");
  53 + Console.WriteLine($" Max output channels: {deviceInfo.maxOutputChannels}");
  54 + Console.WriteLine($" Default sample rate: {deviceInfo.defaultSampleRate}");
  55 + }
  56 + int deviceIndex = PortAudio.DefaultOutputDevice;
  57 + if (deviceIndex == PortAudio.NoDevice)
  58 + {
  59 + Console.WriteLine("No default output device found. Please use ../offline-tts instead");
  60 + Environment.Exit(1);
  61 + }
  62 +
  63 + var info = PortAudio.GetDeviceInfo(deviceIndex);
  64 + Console.WriteLine();
  65 + Console.WriteLine($"Use output default device {deviceIndex} ({info.name})");
  66 +
  67 + var param = new StreamParameters();
  68 + param.device = deviceIndex;
  69 + param.channelCount = 1;
  70 + param.sampleFormat = SampleFormat.Float32;
  71 + param.suggestedLatency = info.defaultLowOutputLatency;
  72 + param.hostApiSpecificStreamInfo = IntPtr.Zero;
  73 +
  74 + // https://learn.microsoft.com/en-us/dotnet/standard/collections/thread-safe/blockingcollection-overview
  75 + var dataItems = new BlockingCollection<float[]>();
  76 +
  77 + var MyCallback = (IntPtr samples, int n, float progress) =>
  78 + {
  79 + Console.WriteLine($"Progress {progress*100}%");
  80 +
  81 + float[] data = new float[n];
  82 +
  83 + Marshal.Copy(samples, data, 0, n);
  84 +
  85 + dataItems.Add(data);
  86 +
  87 + // 1 means to keep generating
  88 + // 0 means to stop generating
  89 + return 1;
  90 + };
  91 +
  92 + var playFinished = false;
  93 +
  94 + float[]? lastSampleArray = null;
  95 + int lastIndex = 0; // not played
  96 +
  97 + PortAudioSharp.Stream.Callback playCallback = (IntPtr input, IntPtr output,
  98 + UInt32 frameCount,
  99 + ref StreamCallbackTimeInfo timeInfo,
  100 + StreamCallbackFlags statusFlags,
  101 + IntPtr userData
  102 + ) =>
  103 + {
  104 + if (dataItems.IsCompleted && lastSampleArray == null && lastIndex == 0)
  105 + {
  106 + Console.WriteLine($"Finished playing");
  107 + playFinished = true;
  108 + return StreamCallbackResult.Complete;
  109 + }
  110 +
  111 + int expected = Convert.ToInt32(frameCount);
  112 + int i = 0;
  113 +
  114 + while ((lastSampleArray != null || dataItems.Count != 0) && (i < expected))
  115 + {
  116 + int needed = expected - i;
  117 +
  118 + if (lastSampleArray != null)
  119 + {
  120 + int remaining = lastSampleArray.Length - lastIndex;
  121 + if (remaining >= needed)
  122 + {
  123 + float[] this_block = lastSampleArray.Skip(lastIndex).Take(needed).ToArray();
  124 + lastIndex += needed;
  125 + if (lastIndex == lastSampleArray.Length)
  126 + {
  127 + lastSampleArray = null;
  128 + lastIndex = 0;
  129 + }
  130 +
  131 + Marshal.Copy(this_block, 0, IntPtr.Add(output, i * sizeof(float)), needed);
  132 + return StreamCallbackResult.Continue;
  133 + }
  134 +
  135 + float[] this_block2 = lastSampleArray.Skip(lastIndex).Take(remaining).ToArray();
  136 + lastIndex = 0;
  137 + lastSampleArray = null;
  138 +
  139 + Marshal.Copy(this_block2, 0, IntPtr.Add(output, i * sizeof(float)), remaining);
  140 + i += remaining;
  141 + continue;
  142 + }
  143 +
  144 + if (dataItems.Count != 0)
  145 + {
  146 + lastSampleArray = dataItems.Take();
  147 + lastIndex = 0;
  148 + }
  149 + }
  150 +
  151 + if (i < expected)
  152 + {
  153 + int sizeInBytes = (expected - i) * 4;
  154 + Marshal.Copy(new byte[sizeInBytes], 0, IntPtr.Add(output, i * sizeof(float)), sizeInBytes);
  155 + }
  156 +
  157 + return StreamCallbackResult.Continue;
  158 + };
  159 +
  160 + PortAudioSharp.Stream stream = new PortAudioSharp.Stream(inParams: null, outParams: param, sampleRate: tts.SampleRate,
  161 + framesPerBuffer: 0,
  162 + streamFlags: StreamFlags.ClipOff,
  163 + callback: playCallback,
  164 + userData: IntPtr.Zero
  165 + );
  166 +
  167 + stream.Start();
  168 +
  169 + var callback = new OfflineTtsCallbackProgress(MyCallback);
  170 +
  171 + var audio = tts.GenerateWithCallbackProgress(text, speed, sid, callback);
  172 + var outputFilename = "./generated-kitten-0.wav";
  173 + var ok = audio.SaveToWaveFile(outputFilename);
  174 +
  175 + if (ok)
  176 + {
  177 + Console.WriteLine($"Wrote to {outputFilename} succeeded!");
  178 + }
  179 + else
  180 + {
  181 + Console.WriteLine($"Failed to write {outputFilename}");
  182 + }
  183 + dataItems.CompleteAdding();
  184 +
  185 + while (!playFinished)
  186 + {
  187 + Thread.Sleep(100); // 100ms
  188 + }
  189 + }
  190 +}
  1 +<Project Sdk="Microsoft.NET.Sdk">
  2 +
  3 + <PropertyGroup>
  4 + <OutputType>Exe</OutputType>
  5 + <TargetFramework>net8.0</TargetFramework>
  6 + <RootNamespace>kitten_tts_play</RootNamespace>
  7 + <ImplicitUsings>enable</ImplicitUsings>
  8 + <Nullable>enable</Nullable>
  9 + </PropertyGroup>
  10 +
  11 + <ItemGroup>
  12 + <PackageReference Include="PortAudioSharp2" Version="*" />
  13 + </ItemGroup>
  14 +
  15 + <ItemGroup>
  16 + <ProjectReference Include="..\Common\Common.csproj" />
  17 + </ItemGroup>
  18 +
  19 +</Project>
  1 +#!/usr/bin/env bash
  2 +set -ex
  3 +
  4 +if [ ! -f ./kitten-nano-en-v0_1-fp16/model.fp16.onnx ]; then
  5 + curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/tts-models/kitten-nano-en-v0_1-fp16.tar.bz2
  6 + tar xf kitten-nano-en-v0_1-fp16.tar.bz2
  7 + rm kitten-nano-en-v0_1-fp16.tar.bz2
  8 +fi
  9 +
  10 +
  11 +dotnet run
  1 +// Copyright (c) 2025 Xiaomi Corporation
  2 +//
  3 +// This file shows how to use a non-streaming KittenTTS model
  4 +// for text-to-speech
  5 +// Please refer to
  6 +// https://k2-fsa.github.io/sherpa/onnx/tts/pretrained_models/index.html
  7 +// and
  8 +// https://github.com/k2-fsa/sherpa-onnx/releases/tag/tts-models
  9 +// to download pre-trained models
  10 +using SherpaOnnx;
  11 +using System.Runtime.InteropServices;
  12 +
  13 +class KittenTtsDemo
  14 +{
  15 + static void Main(string[] args)
  16 + {
  17 +
  18 + TestEn();
  19 + }
  20 +
  21 + static void TestEn()
  22 + {
  23 + var config = new OfflineTtsConfig();
  24 + config.Model.Kitten.Model = "./kitten-nano-en-v0_1-fp16/model.fp16.onnx";
  25 + config.Model.Kitten.Voices = "./kitten-nano-en-v0_1-fp16/voices.bin";
  26 + config.Model.Kitten.Tokens = "./kitten-nano-en-v0_1-fp16/tokens.txt";
  27 + config.Model.Kitten.DataDir = "./kitten-nano-en-v0_1-fp16/espeak-ng-data";
  28 +
  29 + config.Model.NumThreads = 2;
  30 + config.Model.Debug = 1;
  31 + config.Model.Provider = "cpu";
  32 +
  33 + var tts = new OfflineTts(config);
  34 + var speed = 1.0f;
  35 + var text = "Today as always, men fall into two groups: slaves and free men. Whoever " +
  36 + "does not have two-thirds of his day for himself, is a slave, whatever " +
  37 + "he may be: a statesman, a businessman, an official, or a scholar. " +
  38 + "Friends fell out often because life was changing so fast. The easiest " +
  39 + "thing in the world was to lose touch with someone.";
  40 +
  41 + // mapping of sid to voice name
  42 + // 0->expr-voice-2-m, 1->expr-voice-2-f, 2->expr-voice-3-m
  43 + // 3->expr-voice-3-f, 4->expr-voice-4-m, 5->expr-voice-4-f
  44 + // 6->expr-voice-5-m, 7->expr-voice-5-f
  45 + var sid = 0;
  46 +
  47 + var MyCallback = (IntPtr samples, int n, float progress) =>
  48 + {
  49 + float[] data = new float[n];
  50 + Marshal.Copy(samples, data, 0, n);
  51 + // You can process samples here, e.g., play them.
  52 + // See ../kitten-tts-playback for how to play them
  53 + Console.WriteLine($"Progress {progress*100}%");
  54 +
  55 + // 1 means to keep generating
  56 + // 0 means to stop generating
  57 + return 1;
  58 + };
  59 +
  60 + var callback = new OfflineTtsCallbackProgress(MyCallback);
  61 +
  62 + var audio = tts.GenerateWithCallbackProgress(text, speed, sid, callback);
  63 +
  64 + var outputFilename = "./generated-kitten-en.wav";
  65 + var ok = audio.SaveToWaveFile(outputFilename);
  66 +
  67 + if (ok)
  68 + {
  69 + Console.WriteLine($"Wrote to {outputFilename} succeeded!");
  70 + }
  71 + else
  72 + {
  73 + Console.WriteLine($"Failed to write {outputFilename}");
  74 + }
  75 + }
  76 +}
  77 +
  1 +<Project Sdk="Microsoft.NET.Sdk">
  2 +
  3 + <PropertyGroup>
  4 + <OutputType>Exe</OutputType>
  5 + <TargetFramework>net8.0</TargetFramework>
  6 + <RootNamespace>kitten_tts</RootNamespace>
  7 + <ImplicitUsings>enable</ImplicitUsings>
  8 + <Nullable>enable</Nullable>
  9 + </PropertyGroup>
  10 +
  11 + <ItemGroup>
  12 + <ProjectReference Include="..\Common\Common.csproj" />
  13 + </ItemGroup>
  14 +
  15 +</Project>
  1 +#!/usr/bin/env bash
  2 +set -ex
  3 +
  4 +if [ ! -f ./kitten-nano-en-v0_1-fp16/model.fp16.onnx ]; then
  5 + curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/tts-models/kitten-nano-en-v0_1-fp16.tar.bz2
  6 + tar xf kitten-nano-en-v0_1-fp16.tar.bz2
  7 + rm kitten-nano-en-v0_1-fp16.tar.bz2
  8 +fi
  9 +
  10 +
  11 +dotnet run
@@ -3,7 +3,7 @@ @@ -3,7 +3,7 @@
3 // This file shows how to use a non-streaming Kokoro TTS model 3 // This file shows how to use a non-streaming Kokoro TTS model
4 // for text-to-speech 4 // for text-to-speech
5 // Please refer to 5 // Please refer to
6 -// https://k2-fsa.github.io/sherpa/onnx/pretrained_models/index.html 6 +// https://k2-fsa.github.io/sherpa/onnx/tts/pretrained_models/index.html
7 // and 7 // and
8 // https://github.com/k2-fsa/sherpa-onnx/releases/tag/tts-models 8 // https://github.com/k2-fsa/sherpa-onnx/releases/tag/tts-models
9 // to download pre-trained models 9 // to download pre-trained models
@@ -12,7 +12,7 @@ using SherpaOnnx; @@ -12,7 +12,7 @@ using SherpaOnnx;
12 using System.Collections.Concurrent; 12 using System.Collections.Concurrent;
13 using System.Runtime.InteropServices; 13 using System.Runtime.InteropServices;
14 14
15 -class OfflineTtsDemo 15 +class KokoroTtsPlayDemo
16 { 16 {
17 static void Main(string[] args) 17 static void Main(string[] args)
18 { 18 {
@@ -3,14 +3,14 @@ @@ -3,14 +3,14 @@
3 // This file shows how to use a non-streaming Kokoro TTS model 3 // This file shows how to use a non-streaming Kokoro TTS model
4 // for text-to-speech 4 // for text-to-speech
5 // Please refer to 5 // Please refer to
6 -// https://k2-fsa.github.io/sherpa/onnx/pretrained_models/index.html 6 +// https://k2-fsa.github.io/sherpa/onnx/tts/pretrained_models/index.html
7 // and 7 // and
8 // https://github.com/k2-fsa/sherpa-onnx/releases/tag/tts-models 8 // https://github.com/k2-fsa/sherpa-onnx/releases/tag/tts-models
9 // to download pre-trained models 9 // to download pre-trained models
10 using SherpaOnnx; 10 using SherpaOnnx;
11 using System.Runtime.InteropServices; 11 using System.Runtime.InteropServices;
12 12
13 -class OfflineTtsDemo 13 +class KokoroTtsDemo
14 { 14 {
15 static void Main(string[] args) 15 static void Main(string[] args)
16 { 16 {
@@ -9,6 +9,7 @@ @@ -9,6 +9,7 @@
9 </PropertyGroup> 9 </PropertyGroup>
10 10
11 <ItemGroup> 11 <ItemGroup>
  12 + <PackageReference Include="CommandLineParser" Version="2.9.1" />
12 <ProjectReference Include="..\Common\Common.csproj" /> 13 <ProjectReference Include="..\Common\Common.csproj" />
13 </ItemGroup> 14 </ItemGroup>
14 15
@@ -9,6 +9,7 @@ @@ -9,6 +9,7 @@
9 </PropertyGroup> 9 </PropertyGroup>
10 10
11 <ItemGroup> 11 <ItemGroup>
  12 + <PackageReference Include="CommandLineParser" Version="2.9.1" />
12 <PackageReference Include="PortAudioSharp2" Version="*" /> 13 <PackageReference Include="PortAudioSharp2" Version="*" />
13 </ItemGroup> 14 </ItemGroup>
14 15
@@ -9,6 +9,7 @@ @@ -9,6 +9,7 @@
9 </PropertyGroup> 9 </PropertyGroup>
10 10
11 <ItemGroup> 11 <ItemGroup>
  12 + <PackageReference Include="CommandLineParser" Version="2.9.1" />
12 <ProjectReference Include="..\Common\Common.csproj" /> 13 <ProjectReference Include="..\Common\Common.csproj" />
13 </ItemGroup> 14 </ItemGroup>
14 15
@@ -9,6 +9,7 @@ @@ -9,6 +9,7 @@
9 </PropertyGroup> 9 </PropertyGroup>
10 10
11 <ItemGroup> 11 <ItemGroup>
  12 + <PackageReference Include="CommandLineParser" Version="2.9.1" />
12 <ProjectReference Include="..\Common\Common.csproj" /> 13 <ProjectReference Include="..\Common\Common.csproj" />
13 </ItemGroup> 14 </ItemGroup>
14 15
@@ -41,6 +41,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "version-test", "version-tes @@ -41,6 +41,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "version-test", "version-tes
41 EndProject 41 EndProject
42 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "non-streaming-canary-decode-files", "non-streaming-canary-decode-files\non-streaming-canary-decode-files.csproj", "{925779DB-4429-4366-87C3-B14DD44AE1D4}" 42 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "non-streaming-canary-decode-files", "non-streaming-canary-decode-files\non-streaming-canary-decode-files.csproj", "{925779DB-4429-4366-87C3-B14DD44AE1D4}"
43 EndProject 43 EndProject
  44 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "kitten-tts", "kitten-tts\kitten-tts.csproj", "{E5AB574B-9E31-45D4-9B75-1C1892241E41}"
  45 +EndProject
  46 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "kitten-tts-play", "kitten-tts-play\kitten-tts-play.csproj", "{D60A8A84-D6D3-4B79-A18A-1817BEBD35B9}"
  47 +EndProject
44 Global 48 Global
45 GlobalSection(SolutionConfigurationPlatforms) = preSolution 49 GlobalSection(SolutionConfigurationPlatforms) = preSolution
46 Debug|Any CPU = Debug|Any CPU 50 Debug|Any CPU = Debug|Any CPU
@@ -123,6 +127,14 @@ Global @@ -123,6 +127,14 @@ Global
123 {925779DB-4429-4366-87C3-B14DD44AE1D4}.Debug|Any CPU.Build.0 = Debug|Any CPU 127 {925779DB-4429-4366-87C3-B14DD44AE1D4}.Debug|Any CPU.Build.0 = Debug|Any CPU
124 {925779DB-4429-4366-87C3-B14DD44AE1D4}.Release|Any CPU.ActiveCfg = Release|Any CPU 128 {925779DB-4429-4366-87C3-B14DD44AE1D4}.Release|Any CPU.ActiveCfg = Release|Any CPU
125 {925779DB-4429-4366-87C3-B14DD44AE1D4}.Release|Any CPU.Build.0 = Release|Any CPU 129 {925779DB-4429-4366-87C3-B14DD44AE1D4}.Release|Any CPU.Build.0 = Release|Any CPU
  130 + {E5AB574B-9E31-45D4-9B75-1C1892241E41}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  131 + {E5AB574B-9E31-45D4-9B75-1C1892241E41}.Debug|Any CPU.Build.0 = Debug|Any CPU
  132 + {E5AB574B-9E31-45D4-9B75-1C1892241E41}.Release|Any CPU.ActiveCfg = Release|Any CPU
  133 + {E5AB574B-9E31-45D4-9B75-1C1892241E41}.Release|Any CPU.Build.0 = Release|Any CPU
  134 + {D60A8A84-D6D3-4B79-A18A-1817BEBD35B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  135 + {D60A8A84-D6D3-4B79-A18A-1817BEBD35B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
  136 + {D60A8A84-D6D3-4B79-A18A-1817BEBD35B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
  137 + {D60A8A84-D6D3-4B79-A18A-1817BEBD35B9}.Release|Any CPU.Build.0 = Release|Any CPU
126 EndGlobalSection 138 EndGlobalSection
127 GlobalSection(SolutionProperties) = preSolution 139 GlobalSection(SolutionProperties) = preSolution
128 HideSolutionNode = FALSE 140 HideSolutionNode = FALSE
@@ -9,6 +9,7 @@ @@ -9,6 +9,7 @@
9 </PropertyGroup> 9 </PropertyGroup>
10 10
11 <ItemGroup> 11 <ItemGroup>
  12 + <PackageReference Include="CommandLineParser" Version="2.9.1" />
12 <PackageReference Include="PortAudioSharp2" Version="*" /> 13 <PackageReference Include="PortAudioSharp2" Version="*" />
13 </ItemGroup> 14 </ItemGroup>
14 15
  1 +/// Copyright (c) 2025 Xiaomi Corporation (authors: Fangjun Kuang)
  2 +
  3 +using System.Runtime.InteropServices;
  4 +
  5 +namespace SherpaOnnx
  6 +{
  7 + [StructLayout(LayoutKind.Sequential)]
  8 + public struct OfflineTtsKittenModelConfig
  9 + {
  10 + public OfflineTtsKittenModelConfig()
  11 + {
  12 + Model = "";
  13 + Voices = "";
  14 + Tokens = "";
  15 + DataDir = "";
  16 +
  17 + LengthScale = 1.0F;
  18 + }
  19 + [MarshalAs(UnmanagedType.LPStr)]
  20 + public string Model;
  21 +
  22 + [MarshalAs(UnmanagedType.LPStr)]
  23 + public string Voices;
  24 +
  25 + [MarshalAs(UnmanagedType.LPStr)]
  26 + public string Tokens;
  27 +
  28 + [MarshalAs(UnmanagedType.LPStr)]
  29 + public string DataDir;
  30 +
  31 + public float LengthScale;
  32 + }
  33 +}
@@ -4,7 +4,6 @@ using System.Runtime.InteropServices; @@ -4,7 +4,6 @@ using System.Runtime.InteropServices;
4 4
5 namespace SherpaOnnx 5 namespace SherpaOnnx
6 { 6 {
7 -  
8 [StructLayout(LayoutKind.Sequential)] 7 [StructLayout(LayoutKind.Sequential)]
9 public struct OfflineTtsModelConfig 8 public struct OfflineTtsModelConfig
10 { 9 {
@@ -13,6 +12,7 @@ namespace SherpaOnnx @@ -13,6 +12,7 @@ namespace SherpaOnnx
13 Vits = new OfflineTtsVitsModelConfig(); 12 Vits = new OfflineTtsVitsModelConfig();
14 Matcha = new OfflineTtsMatchaModelConfig(); 13 Matcha = new OfflineTtsMatchaModelConfig();
15 Kokoro = new OfflineTtsKokoroModelConfig(); 14 Kokoro = new OfflineTtsKokoroModelConfig();
  15 + Kitten = new OfflineTtsKittenModelConfig();
16 NumThreads = 1; 16 NumThreads = 1;
17 Debug = 0; 17 Debug = 0;
18 Provider = "cpu"; 18 Provider = "cpu";
@@ -21,10 +21,12 @@ namespace SherpaOnnx @@ -21,10 +21,12 @@ namespace SherpaOnnx
21 public OfflineTtsVitsModelConfig Vits; 21 public OfflineTtsVitsModelConfig Vits;
22 public int NumThreads; 22 public int NumThreads;
23 public int Debug; 23 public int Debug;
  24 +
24 [MarshalAs(UnmanagedType.LPStr)] 25 [MarshalAs(UnmanagedType.LPStr)]
25 public string Provider; 26 public string Provider;
26 27
27 public OfflineTtsMatchaModelConfig Matcha; 28 public OfflineTtsMatchaModelConfig Matcha;
28 public OfflineTtsKokoroModelConfig Kokoro; 29 public OfflineTtsKokoroModelConfig Kokoro;
  30 + public OfflineTtsKittenModelConfig Kitten;
29 } 31 }
30 } 32 }
@@ -6,7 +6,6 @@ @@ -6,7 +6,6 @@
6 </PropertyGroup> 6 </PropertyGroup>
7 7
8 <ItemGroup> 8 <ItemGroup>
9 - <PackageReference Include="CommandLineParser" Version="2.9.1" />  
10 <PackageReference Include="org.k2fsa.sherpa.onnx" Version="*" /> 9 <PackageReference Include="org.k2fsa.sherpa.onnx" Version="*" />
11 </ItemGroup> 10 </ItemGroup>
12 11