mirror of
https://github.com/k2-fsa/sherpa-onnx.git
synced 2026-11-10 21:11:03 +08:00
Add C# API for FunASR Nano (#3031)
This commit is contained in:
1 parent
95fbb44cd5
commit
c19c471579
13 files changed
+323
-15
No files matched your search
@@ -4,7 +4,19 @@ set -ex
|
||||
|
||||
cd dotnet-examples/
|
||||
|
||||
cd ./version-test
|
||||
cd ./vad-non-streaming-funasr-nano
|
||||
./run-ten-vad.sh
|
||||
rm -fv *.onnx
|
||||
|
||||
./run.sh
|
||||
rm -fv *.onnx
|
||||
|
||||
cd ../non-streaming-funasr-nano-decode-files
|
||||
./run.sh
|
||||
ls -lh
|
||||
rm -rf sherpa-onnx-funasr-*
|
||||
|
||||
cd ../version-test
|
||||
./run.sh
|
||||
ls -lh
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright (c) 2026 Xiaomi Corporation
|
||||
//
|
||||
// This file shows how to use a FunASR Nano model for speech recognition.
|
||||
//
|
||||
// You can find the model doc at
|
||||
// https://k2-fsa.github.io/sherpa/onnx/funasr-nano.html
|
||||
using SherpaOnnx;
|
||||
|
||||
class NonStreamingFunAsrNano
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
// please download model files from
|
||||
// https://github.com/k2-fsa/sherpa-onnx/releases/tag/asr-models
|
||||
var config = new OfflineRecognizerConfig();
|
||||
config.ModelConfig.FunAsrNano.EncoderAdaptor = "./sherpa-onnx-funasr-nano-int8-2025-12-30/encoder_adaptor.int8.onnx";
|
||||
config.ModelConfig.FunAsrNano.LLM = "./sherpa-onnx-funasr-nano-int8-2025-12-30/llm.int8.onnx";
|
||||
config.ModelConfig.FunAsrNano.Embedding = "./sherpa-onnx-funasr-nano-int8-2025-12-30/embedding.int8.onnx";
|
||||
config.ModelConfig.FunAsrNano.Tokenizer = "./sherpa-onnx-funasr-nano-int8-2025-12-30/Qwen3-0.6B";
|
||||
config.ModelConfig.Tokens = "";
|
||||
config.ModelConfig.Debug = 1;
|
||||
var recognizer = new OfflineRecognizer(config);
|
||||
|
||||
var testWaveFilename = "./sherpa-onnx-funasr-nano-int8-2025-12-30/test_wavs/lyrics.wav";
|
||||
var reader = new WaveReader(testWaveFilename);
|
||||
var stream = recognizer.CreateStream();
|
||||
stream.AcceptWaveform(reader.SampleRate, reader.Samples);
|
||||
recognizer.Decode(stream);
|
||||
var text = stream.Result.Text;
|
||||
Console.WriteLine("Text: {0}", text);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<RootNamespace>non_streaming_funasr_nano_decode_files</RootNamespace>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Common\Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -ex
|
||||
|
||||
if [ ! -f ./sherpa-onnx-funasr-nano-int8-2025-12-30/embedding.int8.onnx ]; then
|
||||
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-funasr-nano-int8-2025-12-30.tar.bz2
|
||||
tar xvf sherpa-onnx-funasr-nano-int8-2025-12-30.tar.bz2
|
||||
rm sherpa-onnx-funasr-nano-int8-2025-12-30.tar.bz2
|
||||
fi
|
||||
|
||||
dotnet run
|
||||
@@ -47,6 +47,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "kitten-tts-play", "kitten-t
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "offline-audio-tagging", "offline-audio-tagging\offline-audio-tagging.csproj", "{0EBE2CE5-8940-4472-8A38-6A0E976E678F}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "non-streaming-funasr-nano-decode-files", "non-streaming-funasr-nano-decode-files\non-streaming-funasr-nano-decode-files.csproj", "{32F7534B-117E-4D1D-BAED-A1D1A6C6A62C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "vad-non-streaming-funasr-nano", "vad-non-streaming-funasr-nano\vad-non-streaming-funasr-nano.csproj", "{32C8C12B-D7DB-455E-B35C-945A745520CC}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -141,6 +145,14 @@ Global
|
||||
{0EBE2CE5-8940-4472-8A38-6A0E976E678F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0EBE2CE5-8940-4472-8A38-6A0E976E678F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0EBE2CE5-8940-4472-8A38-6A0E976E678F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{32F7534B-117E-4D1D-BAED-A1D1A6C6A62C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{32F7534B-117E-4D1D-BAED-A1D1A6C6A62C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{32F7534B-117E-4D1D-BAED-A1D1A6C6A62C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{32F7534B-117E-4D1D-BAED-A1D1A6C6A62C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{32C8C12B-D7DB-455E-B35C-945A745520CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{32C8C12B-D7DB-455E-B35C-945A745520CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{32C8C12B-D7DB-455E-B35C-945A745520CC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{32C8C12B-D7DB-455E-B35C-945A745520CC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
// Copyright (c) 2026 Xiaomi Corporation
|
||||
//
|
||||
// This file shows how to use a silero_vad model or ten-vad model
|
||||
// with a non-streaming FunASR Nano for speech recognition.
|
||||
using SherpaOnnx;
|
||||
using System.IO;
|
||||
|
||||
|
||||
class VadNonStreamingFunAsrNano
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
// please download model files from
|
||||
// https://github.com/k2-fsa/sherpa-onnx/releases/tag/asr-models
|
||||
var config = new OfflineRecognizerConfig();
|
||||
config.ModelConfig.FunAsrNano.EncoderAdaptor = "./sherpa-onnx-funasr-nano-int8-2025-12-30/encoder_adaptor.int8.onnx";
|
||||
config.ModelConfig.FunAsrNano.LLM = "./sherpa-onnx-funasr-nano-int8-2025-12-30/llm.int8.onnx";
|
||||
config.ModelConfig.FunAsrNano.Embedding = "./sherpa-onnx-funasr-nano-int8-2025-12-30/embedding.int8.onnx";
|
||||
config.ModelConfig.FunAsrNano.Tokenizer = "./sherpa-onnx-funasr-nano-int8-2025-12-30/Qwen3-0.6B";
|
||||
config.ModelConfig.Tokens = "";
|
||||
config.ModelConfig.Debug = 0;
|
||||
var recognizer = new OfflineRecognizer(config);
|
||||
|
||||
var vadModelConfig = new VadModelConfig();
|
||||
if (File.Exists("./silero_vad.onnx"))
|
||||
{
|
||||
Console.WriteLine("Use silero-vad");
|
||||
vadModelConfig.SileroVad.Model = "./silero_vad.onnx";
|
||||
vadModelConfig.SileroVad.Threshold = 0.3F;
|
||||
vadModelConfig.SileroVad.MinSilenceDuration = 0.5F;
|
||||
vadModelConfig.SileroVad.MinSpeechDuration = 0.25F;
|
||||
vadModelConfig.SileroVad.MaxSpeechDuration = 5.0F;
|
||||
vadModelConfig.SileroVad.WindowSize = 512;
|
||||
}
|
||||
else if (File.Exists("./ten-vad.onnx"))
|
||||
{
|
||||
Console.WriteLine("Use ten-vad");
|
||||
vadModelConfig.TenVad.Model = "./ten-vad.onnx";
|
||||
vadModelConfig.TenVad.Threshold = 0.3F;
|
||||
vadModelConfig.TenVad.MinSilenceDuration = 0.5F;
|
||||
vadModelConfig.TenVad.MinSpeechDuration = 0.25F;
|
||||
vadModelConfig.TenVad.MaxSpeechDuration = 5.0F;
|
||||
vadModelConfig.TenVad.WindowSize = 256;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Please download ./silero_vad.onnx or ./ten-vad.onnx");
|
||||
return;
|
||||
}
|
||||
vadModelConfig.Debug = 0;
|
||||
|
||||
var vad = new VoiceActivityDetector(vadModelConfig, 60);
|
||||
|
||||
var testWaveFilename = "./lei-jun-test.wav";
|
||||
var reader = new WaveReader(testWaveFilename);
|
||||
|
||||
int numSamples = reader.Samples.Length;
|
||||
int windowSize = vadModelConfig.SileroVad.WindowSize;
|
||||
|
||||
if (vadModelConfig.TenVad.Model != "")
|
||||
{
|
||||
windowSize = vadModelConfig.TenVad.WindowSize;
|
||||
}
|
||||
|
||||
int sampleRate = vadModelConfig.SampleRate;
|
||||
int numIter = numSamples / windowSize;
|
||||
|
||||
for (int i = 0; i != numIter; ++i)
|
||||
{
|
||||
int start = i * windowSize;
|
||||
var samples = new float[windowSize];
|
||||
Array.Copy(reader.Samples, start, samples, 0, windowSize);
|
||||
vad.AcceptWaveform(samples);
|
||||
if (vad.IsSpeechDetected())
|
||||
{
|
||||
while (!vad.IsEmpty())
|
||||
{
|
||||
SpeechSegment segment = vad.Front();
|
||||
var startTime = segment.Start / (float)sampleRate;
|
||||
var duration = segment.Samples.Length / (float)sampleRate;
|
||||
|
||||
OfflineStream stream = recognizer.CreateStream();
|
||||
stream.AcceptWaveform(sampleRate, segment.Samples);
|
||||
recognizer.Decode(stream);
|
||||
var text = stream.Result.Text;
|
||||
|
||||
if (!string.IsNullOrEmpty(text))
|
||||
{
|
||||
Console.WriteLine("{0}--{1}: {2}", string.Format("{0:0.00}", startTime),
|
||||
string.Format("{0:0.00}", startTime + duration), text);
|
||||
}
|
||||
|
||||
vad.Pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vad.Flush();
|
||||
|
||||
while (!vad.IsEmpty())
|
||||
{
|
||||
var segment = vad.Front();
|
||||
float startTime = segment.Start / (float)sampleRate;
|
||||
float duration = segment.Samples.Length / (float)sampleRate;
|
||||
|
||||
var stream = recognizer.CreateStream();
|
||||
stream.AcceptWaveform(sampleRate, segment.Samples);
|
||||
recognizer.Decode(stream);
|
||||
var text = stream.Result.Text;
|
||||
|
||||
if (!string.IsNullOrEmpty(text))
|
||||
{
|
||||
Console.WriteLine("{0}--{1}: {2}", string.Format("{0:0.00}", startTime),
|
||||
string.Format("{0:0.00}", startTime + duration), text);
|
||||
}
|
||||
|
||||
vad.Pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -ex
|
||||
|
||||
if [ ! -f ./ten-vad.onnx ]; then
|
||||
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/ten-vad.onnx
|
||||
fi
|
||||
|
||||
if [ ! -f ./lei-jun-test.wav ]; then
|
||||
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/lei-jun-test.wav
|
||||
fi
|
||||
|
||||
if [ ! -f ./sherpa-onnx-funasr-nano-int8-2025-12-30/embedding.int8.onnx ]; then
|
||||
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-funasr-nano-int8-2025-12-30.tar.bz2
|
||||
tar xvf sherpa-onnx-funasr-nano-int8-2025-12-30.tar.bz2
|
||||
rm sherpa-onnx-funasr-nano-int8-2025-12-30.tar.bz2
|
||||
fi
|
||||
|
||||
dotnet run
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -ex
|
||||
|
||||
if [ ! -f ./silero_vad.onnx ]; then
|
||||
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/silero_vad.onnx
|
||||
fi
|
||||
|
||||
if [ ! -f ./lei-jun-test.wav ]; then
|
||||
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/lei-jun-test.wav
|
||||
fi
|
||||
|
||||
if [ ! -f ./sherpa-onnx-funasr-nano-int8-2025-12-30/embedding.int8.onnx ]; then
|
||||
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-funasr-nano-int8-2025-12-30.tar.bz2
|
||||
tar xvf sherpa-onnx-funasr-nano-int8-2025-12-30.tar.bz2
|
||||
rm sherpa-onnx-funasr-nano-int8-2025-12-30.tar.bz2
|
||||
fi
|
||||
|
||||
dotnet run
|
||||
@@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<RootNamespace>vad_non_streaming_funasr_nano</RootNamespace>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Common\Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,48 @@
|
||||
/// Copyright (c) 2025 Xiaomi Corporation (authors: Fangjun Kuang)
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace SherpaOnnx
|
||||
{
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct OfflineFunAsrNanoModelConfig
|
||||
{
|
||||
public OfflineFunAsrNanoModelConfig()
|
||||
{
|
||||
EncoderAdaptor = "";
|
||||
LLM = "";
|
||||
Embedding = "";
|
||||
Tokenizer = "";
|
||||
SystemPrompt = "You are a helpful assistant.";
|
||||
UserPrompt = "语音转写:";
|
||||
MaxNewTokens = 512;
|
||||
Temperature = 1e-6F;
|
||||
TopP = 0.8F;
|
||||
Seed = 42;
|
||||
}
|
||||
|
||||
[MarshalAs(UnmanagedType.LPStr)]
|
||||
public string EncoderAdaptor;
|
||||
|
||||
[MarshalAs(UnmanagedType.LPStr)]
|
||||
public string LLM;
|
||||
|
||||
[MarshalAs(UnmanagedType.LPStr)]
|
||||
public string Embedding;
|
||||
|
||||
[MarshalAs(UnmanagedType.LPStr)]
|
||||
public string Tokenizer;
|
||||
|
||||
[MarshalAs(UnmanagedType.LPStr)]
|
||||
public string SystemPrompt;
|
||||
|
||||
[MarshalAs(UnmanagedType.LPStr)]
|
||||
public string UserPrompt;
|
||||
|
||||
public int MaxNewTokens;
|
||||
public float Temperature;
|
||||
public float TopP;
|
||||
public int Seed;
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,7 @@ namespace SherpaOnnx
|
||||
WenetCtc = new OfflineWenetCtcModelConfig();
|
||||
Omnilingual = new OfflineOmnilingualAsrCtcModelConfig();
|
||||
MedAsr = new OfflineMedAsrCtcModelConfig();
|
||||
FunAsrNano = new OfflineFunAsrNanoModelConfig();
|
||||
}
|
||||
public OfflineTransducerModelConfig Transducer;
|
||||
public OfflineParaformerModelConfig Paraformer;
|
||||
@@ -70,5 +71,6 @@ namespace SherpaOnnx
|
||||
public OfflineWenetCtcModelConfig WenetCtc;
|
||||
public OfflineOmnilingualAsrCtcModelConfig Omnilingual;
|
||||
public OfflineMedAsrCtcModelConfig MedAsr;
|
||||
public OfflineFunAsrNanoModelConfig FunAsrNano;
|
||||
}
|
||||
}
|
||||
+10
-12
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2023 Xiaomi Corporation
|
||||
|
||||
import argparse
|
||||
import glob
|
||||
import os
|
||||
import re
|
||||
from pathlib import Path
|
||||
@@ -28,7 +28,6 @@ def read_proj_file(filename):
|
||||
|
||||
|
||||
def get_dict():
|
||||
version = get_version()
|
||||
return {
|
||||
"version": get_version(),
|
||||
}
|
||||
@@ -55,17 +54,18 @@ def process_linux(s, rid):
|
||||
|
||||
|
||||
def process_macos(s, rid):
|
||||
libs = [
|
||||
"libonnxruntime.1.17.1.dylib",
|
||||
"libsherpa-onnx-c-api.dylib",
|
||||
]
|
||||
prefix = f"{src_dir}/macos-{rid}/"
|
||||
libs = [prefix + lib for lib in libs]
|
||||
libs = "\n ;".join(libs)
|
||||
lib_dir = os.path.join(src_dir, f"macos-{rid}")
|
||||
onnx_libs = glob.glob(os.path.join(lib_dir, "libonnxruntime*.dylib"))
|
||||
if not onnx_libs:
|
||||
raise FileNotFoundError(f"No libonnxruntime*.dylib found in {lib_dir}")
|
||||
|
||||
other_libs = [os.path.join(lib_dir, "libsherpa-onnx-c-api.dylib")]
|
||||
libs = onnx_libs + other_libs
|
||||
libs_str = "\n ;".join(libs)
|
||||
|
||||
d = get_dict()
|
||||
d["dotnet_rid"] = f"osx-{rid}"
|
||||
d["libs"] = libs
|
||||
d["libs"] = libs_str
|
||||
|
||||
environment = jinja2.Environment()
|
||||
template = environment.from_string(s)
|
||||
@@ -80,8 +80,6 @@ def process_windows(s, rid):
|
||||
"sherpa-onnx-c-api.dll",
|
||||
]
|
||||
|
||||
version = get_version()
|
||||
|
||||
prefix = f"{src_dir}/windows-{rid}/"
|
||||
libs = [prefix + lib for lib in libs]
|
||||
libs = "\n ;".join(libs)
|
||||
|
||||
@@ -94,7 +94,7 @@ function osx() {
|
||||
cp -v sherpa_onnx/lib/*.dylib $dst/
|
||||
|
||||
pushd $dst
|
||||
cp -v libonnxruntime.1.17.1.dylib libonnxruntime.dylib
|
||||
cp -v libonnxruntime.*.dylib libonnxruntime.dylib
|
||||
popd
|
||||
|
||||
cd ..
|
||||
@@ -112,7 +112,7 @@ function osx() {
|
||||
cp -v sherpa_onnx/lib/*.dylib $dst/
|
||||
|
||||
pushd $dst
|
||||
cp -v libonnxruntime.1.17.1.dylib libonnxruntime.dylib
|
||||
cp -v libonnxruntime.*.dylib libonnxruntime.dylib
|
||||
popd
|
||||
|
||||
cd ..
|
||||
|
||||
Reference in new issue
Block a user