Add Kotlin and Java API for Google MedAsr model (#2956)

This commit is contained in:
Fangjun Kuang authored and GitHub committed 2025-12-29 12:53:00 +08:00
1 parent 2f8fb50a24
commit 1f124600f5
16 files changed
+257 -21

No files matched your search

+8
View File
@@ -108,6 +108,14 @@ jobs:
cd ./java-api-examples
./run-version-test.sh
- name: Run java test (MedASR CTC)
shell: bash
run: |
cd ./java-api-examples
./run-non-streaming-decode-file-medasr-ctc.sh
rm -rf sherpa-onnx-medasr-*
- name: Run java test (Omnilingual ASR CTC)
shell: bash
run: |
@@ -0,0 +1,51 @@
// Copyright 2025 Xiaomi Corporation
// This file shows how to use an offline Google MedASR CTC model,
// i.e., non-streaming MedASR CTC model,
// to decode files.
import com.k2fsa.sherpa.onnx.*;
public class NonStreamingDecodeFileMedAsrCtc {
public static void main(String[] args) {
// please refer to
// https://k2-fsa.github.io/sherpa/onnx/medasr/index.html
// to download model files
String model = "./sherpa-onnx-medasr-ctc-en-int8-2025-12-25/model.int8.onnx";
String tokens = "./sherpa-onnx-medasr-ctc-en-int8-2025-12-25/tokens.txt";
String waveFilename = "./sherpa-onnx-medasr-ctc-en-int8-2025-12-25/test_wavs/0.wav";
WaveReader reader = new WaveReader(waveFilename);
OfflineMedAsrCtcModelConfig medasr =
OfflineMedAsrCtcModelConfig.builder().setModel(model).build();
OfflineModelConfig modelConfig =
OfflineModelConfig.builder()
.setMedAsr(medasr)
.setTokens(tokens)
.setNumThreads(1)
.setDebug(true)
.build();
OfflineRecognizerConfig config =
OfflineRecognizerConfig.builder()
.setOfflineModelConfig(modelConfig)
.setDecodingMethod("greedy_search")
.build();
OfflineRecognizer recognizer = new OfflineRecognizer(config);
OfflineStream stream = recognizer.createStream();
stream.acceptWaveform(reader.getSamples(), reader.getSampleRate());
recognizer.decode(stream);
String text = recognizer.getResult(stream).getText();
System.out.printf("filename:%s\nresult:%s\n", waveFilename, text);
stream.release();
recognizer.release();
}
}
@@ -8,7 +8,7 @@ import com.k2fsa.sherpa.onnx.*;
public class NonStreamingDecodeFileOmnilingualAsrCtc {
public static void main(String[] args) {
// please refer to
// https://k2-fsa.github.io/sherpa/onnx/sense-voice/index.html
// https://k2-fsa.github.io/sherpa/onnx/omnilingual-asr/index.html
// to download model files
String model =
"sherpa-onnx-omnilingual-asr-1600-languages-300M-ctc-int8-2025-11-12/model.int8.onnx";
@@ -0,0 +1,38 @@
#!/usr/bin/env bash
set -ex
if [[ ! -f ../build/lib/libsherpa-onnx-jni.dylib && ! -f ../build/lib/libsherpa-onnx-jni.so ]]; then
mkdir -p ../build
pushd ../build
cmake \
-DSHERPA_ONNX_ENABLE_PYTHON=OFF \
-DSHERPA_ONNX_ENABLE_TESTS=OFF \
-DSHERPA_ONNX_ENABLE_CHECK=OFF \
-DBUILD_SHARED_LIBS=ON \
-DSHERPA_ONNX_ENABLE_PORTAUDIO=OFF \
-DSHERPA_ONNX_ENABLE_JNI=ON \
..
make -j4
ls -lh lib
popd
fi
if [ ! -f ../sherpa-onnx/java-api/build/sherpa-onnx.jar ]; then
pushd ../sherpa-onnx/java-api
make
popd
fi
if [ ! -f ./sherpa-onnx-medasr-ctc-en-int8-2025-12-25/tokens.txt ]; then
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-medasr-ctc-en-int8-2025-12-25.tar.bz2
tar xvf sherpa-onnx-medasr-ctc-en-int8-2025-12-25.tar.bz2
rm sherpa-onnx-medasr-ctc-en-int8-2025-12-25.tar.bz2
fi
java \
-Djava.library.path=$PWD/../build/lib \
-cp ../sherpa-onnx/java-api/build/sherpa-onnx.jar \
NonStreamingDecodeFileMedAsrCtc.java
+31 -8
View File
@@ -234,10 +234,10 @@ function testOfflineAsr() {
rm sherpa-onnx-moonshine-tiny-en-int8.tar.bz2
fi
if [ ! -f ./sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17/tokens.txt ]; then
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17.tar.bz2
tar xvf sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17.tar.bz2
rm sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17.tar.bz2
if [ ! -f ./sherpa-onnx-sense-voice-zh-en-ja-ko-yue-int8-2024-07-17/tokens.txt ]; then
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-sense-voice-zh-en-ja-ko-yue-int8-2024-07-17.tar.bz2
tar xvf sherpa-onnx-sense-voice-zh-en-ja-ko-yue-int8-2024-07-17.tar.bz2
rm sherpa-onnx-sense-voice-zh-en-ja-ko-yue-int8-2024-07-17.tar.bz2
fi
if [ ! -f ./sherpa-onnx-whisper-tiny.en/tiny.en-encoder.int8.onnx ]; then
@@ -439,10 +439,10 @@ function testOfflineSpeechDenoiser() {
}
function testOfflineSenseVoiceWithHr() {
if [ ! -f ./sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17/tokens.txt ]; then
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17.tar.bz2
tar xvf sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17.tar.bz2
rm sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17.tar.bz2
if [ ! -f ./sherpa-onnx-sense-voice-zh-en-ja-ko-yue-int8-2024-07-17/tokens.txt ]; then
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-sense-voice-zh-en-ja-ko-yue-int8-2024-07-17.tar.bz2
tar xvf sherpa-onnx-sense-voice-zh-en-ja-ko-yue-int8-2024-07-17.tar.bz2
rm sherpa-onnx-sense-voice-zh-en-ja-ko-yue-int8-2024-07-17.tar.bz2
fi
if [ ! -d dict ]; then
@@ -515,6 +515,28 @@ function testOfflineOmnilingualAsrCtc() {
java -Djava.library.path=../build/lib -jar $out_filename
}
function testOfflineMedAsrCtc() {
if [ ! -f ./sherpa-onnx-medasr-ctc-en-int8-2025-12-25/tokens.txt ]; then
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-medasr-ctc-en-int8-2025-12-25.tar.bz2
tar xvf sherpa-onnx-medasr-ctc-en-int8-2025-12-25.tar.bz2
rm sherpa-onnx-medasr-ctc-en-int8-2025-12-25.tar.bz2
fi
out_filename=test_offline_medasr_ctc.jar
kotlinc-jvm -include-runtime -d $out_filename \
test_offline_medasr_ctc.kt \
FeatureConfig.kt \
QnnConfig.kt \
HomophoneReplacerConfig.kt \
OfflineRecognizer.kt \
OfflineStream.kt \
WaveReader.kt \
faked-asset-manager.kt
ls -lh $out_filename
java -Djava.library.path=../build/lib -jar $out_filename
}
function testOfflineWenetCtc() {
if [ ! -f sherpa-onnx-wenetspeech-yue-u2pp-conformer-ctc-zh-en-cantonese-int8-2025-09-10/model.int8.onnx ]; then
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-wenetspeech-yue-u2pp-conformer-ctc-zh-en-cantonese-int8-2025-09-10.tar.bz2
@@ -539,6 +561,7 @@ function testOfflineWenetCtc() {
testVersion
testOfflineMedAsrCtc
testOfflineOmnilingualAsrCtc
testOfflineWenetCtc
testOfflineNeMoCanary
+1 -1
View File
@@ -15,7 +15,7 @@ fun test(type: Int) {
2 -> "./sherpa-onnx-whisper-tiny.en/test_wavs/0.wav"
5 -> "./sherpa-onnx-zipformer-multi-zh-hans-2023-9-2/test_wavs/1.wav"
6 -> "./sherpa-onnx-nemo-ctc-en-citrinet-512/test_wavs/8k.wav"
15 -> "./sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17/test_wavs/zh.wav"
15 -> "./sherpa-onnx-sense-voice-zh-en-ja-ko-yue-int8-2024-07-17/test_wavs/zh.wav"
21 -> "./sherpa-onnx-moonshine-tiny-en-int8/test_wavs/0.wav"
24 -> "./sherpa-onnx-fire-red-asr-large-zh_en-2025-02-16/test_wavs/0.wav"
25 -> "./sherpa-onnx-dolphin-base-ctc-multi-lang-int8-2025-04-02/test_wavs/0.wav"
@@ -0,0 +1,31 @@
package com.k2fsa.sherpa.onnx
fun main() {
val recognizer = createOfflineRecognizer()
val waveFilename = "./sherpa-onnx-medasr-ctc-en-int8-2025-12-25/test_wavs/0.wav"
val objArray = WaveReader.readWaveFromFile(
filename = waveFilename,
)
val samples: FloatArray = objArray[0] as FloatArray
val sampleRate: Int = objArray[1] as Int
var stream = recognizer.createStream()
stream.acceptWaveform(samples, sampleRate=sampleRate)
recognizer.decode(stream)
var result = recognizer.getResult(stream)
println(result)
stream.release()
recognizer.release()
}
fun createOfflineRecognizer(): OfflineRecognizer {
val config = OfflineRecognizerConfig(
modelConfig = getOfflineModelConfig(type = 45)!!,
)
return OfflineRecognizer(config = config)
}
+6 -6
View File
@@ -287,9 +287,9 @@ how to decode a file with a non-streaming SenseVoice model with homophone replac
You can use the following command to run it:
```bash
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17.tar.bz2
tar xvf sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17.tar.bz2
rm sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17.tar.bz2
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-sense-voice-zh-en-ja-ko-yue-int8-2024-07-17.tar.bz2
tar xvf sherpa-onnx-sense-voice-zh-en-ja-ko-yue-int8-2024-07-17.tar.bz2
rm sherpa-onnx-sense-voice-zh-en-ja-ko-yue-int8-2024-07-17.tar.bz2
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/hr-files/dict.tar.bz2
tar xf dict.tar.bz2
@@ -309,9 +309,9 @@ how to decode a file with a non-streaming SenseVoice model.
You can use the following command to run it:
```bash
wget https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17.tar.bz2
tar xvf sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17.tar.bz2
rm sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17.tar.bz2
wget https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-sense-voice-zh-en-ja-ko-yue-int8-2024-07-17.tar.bz2
tar xvf sherpa-onnx-sense-voice-zh-en-ja-ko-yue-int8-2024-07-17.tar.bz2
rm sherpa-onnx-sense-voice-zh-en-ja-ko-yue-int8-2024-07-17.tar.bz2
node ./test-offline-sense-voice.js
```
@@ -6,11 +6,12 @@ function createOfflineRecognizer() {
let modelConfig = {
senseVoice: {
model:
'./sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17/model.int8.onnx',
'./sherpa-onnx-sense-voice-zh-en-ja-ko-yue-int8-2024-07-17/model.int8.onnx',
language: '',
useInverseTextNormalization: 1,
},
tokens: './sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17/tokens.txt',
tokens:
'./sherpa-onnx-sense-voice-zh-en-ja-ko-yue-int8-2024-07-17/tokens.txt',
};
let config = {
+4 -3
View File
@@ -6,11 +6,12 @@ function createOfflineRecognizer() {
let modelConfig = {
senseVoice: {
model:
'./sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17/model.int8.onnx',
'./sherpa-onnx-sense-voice-zh-en-ja-ko-yue-int8-2024-07-17/model.int8.onnx',
language: '',
useInverseTextNormalization: 1,
},
tokens: './sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17/tokens.txt',
tokens:
'./sherpa-onnx-sense-voice-zh-en-ja-ko-yue-int8-2024-07-17/tokens.txt',
};
let config = {
@@ -24,7 +25,7 @@ const recognizer = createOfflineRecognizer();
const stream = recognizer.createStream();
const waveFilename =
'./sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17/test_wavs/zh.wav';
'./sherpa-onnx-sense-voice-zh-en-ja-ko-yue-int8-2024-07-17/test_wavs/zh.wav';
const wave = sherpa_onnx.readWave(waveFilename);
stream.acceptWaveform(wave.sampleRate, wave.samples);
@@ -758,6 +758,22 @@ def get_models():
ls -lh
popd
""",
),
Model(
model_name="sherpa-onnx-medasr-ctc-en-int8-2025-12-25",
idx=45,
lang="en",
lang2="英语",
short_name="google_medasr_ctc_int8",
cmd="""
pushd $model_name
rm -rfv test_wavs
ls -lh
popd
""",
),
+1
View File
@@ -39,6 +39,7 @@ java_files += OfflineNemoEncDecCtcModelConfig.java
java_files += OfflineZipformerCtcModelConfig.java
java_files += OfflineWenetCtcModelConfig.java
java_files += OfflineOmnilingualAsrCtcModelConfig.java
java_files += OfflineMedAsrCtcModelConfig.java
java_files += OfflineCanaryModelConfig.java
java_files += OfflineSenseVoiceModelConfig.java
java_files += OfflineDolphinModelConfig.java
@@ -0,0 +1,30 @@
package com.k2fsa.sherpa.onnx;
public class OfflineMedAsrCtcModelConfig {
private final String model;
private OfflineMedAsrCtcModelConfig(Builder builder) {
this.model = builder.model;
}
public static Builder builder() {
return new Builder();
}
public String getModel() {
return model;
}
public static class Builder {
private String model = "";
public OfflineMedAsrCtcModelConfig build() {
return new OfflineMedAsrCtcModelConfig(this);
}
public Builder setModel(String model) {
this.model = model;
return this;
}
}
}
@@ -14,6 +14,7 @@ public class OfflineModelConfig {
private final OfflineZipformerCtcModelConfig zipformerCtc;
private final OfflineWenetCtcModelConfig wenetCtc;
private final OfflineOmnilingualAsrCtcModelConfig omnilingual;
private final OfflineMedAsrCtcModelConfig medasr;
private final OfflineCanaryModelConfig canary;
private final String teleSpeech;
private final String tokens;
@@ -36,6 +37,7 @@ public class OfflineModelConfig {
this.canary = builder.canary;
this.wenetCtc = builder.wenetCtc;
this.omnilingual = builder.omnilingual;
this.medasr = builder.medasr;
this.senseVoice = builder.senseVoice;
this.dolphin = builder.dolphin;
this.teleSpeech = builder.teleSpeech;
@@ -92,6 +94,10 @@ public class OfflineModelConfig {
return omnilingual;
}
public OfflineMedAsrCtcModelConfig getMedAsr() {
return medasr;
}
public OfflineCanaryModelConfig getCanary() {
return canary;
}
@@ -140,6 +146,7 @@ public class OfflineModelConfig {
private OfflineZipformerCtcModelConfig zipformerCtc = OfflineZipformerCtcModelConfig.builder().build();
private OfflineWenetCtcModelConfig wenetCtc = OfflineWenetCtcModelConfig.builder().build();
private OfflineOmnilingualAsrCtcModelConfig omnilingual = OfflineOmnilingualAsrCtcModelConfig.builder().build();
private OfflineMedAsrCtcModelConfig medasr = OfflineMedAsrCtcModelConfig.builder().build();
private OfflineCanaryModelConfig canary = OfflineCanaryModelConfig.builder().build();
private String teleSpeech = "";
private String tokens = "";
@@ -189,6 +196,11 @@ public class OfflineModelConfig {
return this;
}
public Builder setMedAsr(OfflineMedAsrCtcModelConfig medasr) {
this.medasr = medasr;
return this;
}
public Builder setCanary(OfflineCanaryModelConfig canary) {
this.canary = canary;
return this;
+9
View File
@@ -258,6 +258,15 @@ static OfflineRecognizerConfig GetOfflineConfig(JNIEnv *env, jobject config,
omnilingual_ctc_config_cls,
omnilingual_ctc_config);
// medasr ctc
fid = env->GetFieldID(model_config_cls, "medasr",
"Lcom/k2fsa/sherpa/onnx/OfflineMedAsrCtcModelConfig;");
jobject medasr_ctc_config = env->GetObjectField(model_config, fid);
jclass medasr_ctc_config_cls = env->GetObjectClass(medasr_ctc_config);
SHERPA_ONNX_JNI_READ_STRING(ans.model_config.medasr.model, model,
medasr_ctc_config_cls, medasr_ctc_config);
// canary
fid = env->GetFieldID(model_config_cls, "canary",
"Lcom/k2fsa/sherpa/onnx/OfflineCanaryModelConfig;");
@@ -46,6 +46,10 @@ data class OfflineOmnilingualAsrCtcModelConfig(
var model: String = "",
)
data class OfflineMedAsrCtcModelConfig(
var model: String = "",
)
data class OfflineWhisperModelConfig(
var encoder: String = "",
var decoder: String = "",
@@ -93,6 +97,7 @@ data class OfflineModelConfig(
var zipformerCtc: OfflineZipformerCtcModelConfig = OfflineZipformerCtcModelConfig(),
var wenetCtc: OfflineWenetCtcModelConfig = OfflineWenetCtcModelConfig(),
var omnilingual: OfflineOmnilingualAsrCtcModelConfig = OfflineOmnilingualAsrCtcModelConfig(),
var medasr: OfflineMedAsrCtcModelConfig = OfflineMedAsrCtcModelConfig(),
var canary: OfflineCanaryModelConfig = OfflineCanaryModelConfig(),
var teleSpeech: String = "",
var numThreads: Int = 1,
@@ -754,6 +759,16 @@ fun getOfflineModelConfig(type: Int): OfflineModelConfig? {
)
}
45 -> {
val modelDir = "sherpa-onnx-medasr-ctc-en-int8-2025-12-25"
return OfflineModelConfig(
medasr = OfflineMedAsrCtcModelConfig(
model = "$modelDir/model.int8.onnx",
),
tokens = "$modelDir/tokens.txt",
)
}
9000 -> {
val modelDir =
"sherpa-onnx-qnn-5-seconds-sense-voice-zh-en-ja-ko-yue-2024-07-17-int8-android-aarch64"