Skip to content

Commit 13b8b84

Browse files
authored
Add C and CXX API for Google MedASR model (#2946)
1 parent acd44bf commit 13b8b84

11 files changed

Lines changed: 243 additions & 0 deletions

File tree

.github/workflows/c-api.yaml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,36 @@ jobs:
7575
otool -L ./install/lib/libsherpa-onnx-c-api.dylib
7676
fi
7777
78+
- name: Test MedASR CTC
79+
shell: bash
80+
run: |
81+
name=medasr-ctc-c-api
82+
gcc -o $name ./c-api-examples/$name.c \
83+
-I ./build/install/include \
84+
-L ./build/install/lib/ \
85+
-l sherpa-onnx-c-api \
86+
-l onnxruntime
87+
88+
ls -lh $name
89+
90+
if [[ ${{ matrix.os }} == ubuntu-latest || ${{ matrix.os }} == ubuntu-22.04-arm ]]; then
91+
ldd ./$name
92+
echo "----"
93+
readelf -d ./$name
94+
fi
95+
96+
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
97+
tar xvf sherpa-onnx-medasr-ctc-en-int8-2025-12-25.tar.bz2
98+
rm sherpa-onnx-medasr-ctc-en-int8-2025-12-25.tar.bz2
99+
100+
export LD_LIBRARY_PATH=$PWD/build/install/lib:$LD_LIBRARY_PATH
101+
export DYLD_LIBRARY_PATH=$PWD/build/install/lib:$DYLD_LIBRARY_PATH
102+
103+
./$name
104+
105+
rm $name
106+
rm -rf sherpa-onnx-medasr-*
107+
78108
- name: Test Omnilingual ASR CTC
79109
shell: bash
80110
run: |

.github/workflows/cxx-api.yaml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,40 @@ jobs:
7878
otool -L ./install/lib/libsherpa-onnx-cxx-api.dylib
7979
fi
8080
81+
- name: Test MedASR CTC
82+
shell: bash
83+
run: |
84+
name=medasr-ctc-cxx-api
85+
g++ -std=c++17 -o $name ./cxx-api-examples/$name.cc \
86+
-I ./build/install/include \
87+
-L ./build/install/lib/ \
88+
-l sherpa-onnx-cxx-api \
89+
-l sherpa-onnx-c-api \
90+
-l onnxruntime
91+
92+
ls -lh $name
93+
94+
if [[ ${{ matrix.os }} == ubuntu-latest || ${{ matrix.os }} == ubuntu-22.04-arm ]]; then
95+
ls -lh ./$name
96+
ldd ./$name
97+
echo "----"
98+
readelf -d ./$name
99+
fi
100+
101+
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
102+
tar xvf sherpa-onnx-medasr-ctc-en-int8-2025-12-25.tar.bz2
103+
rm sherpa-onnx-medasr-ctc-en-int8-2025-12-25.tar.bz2
104+
105+
echo "---"
106+
107+
export LD_LIBRARY_PATH=$PWD/build/install/lib:$LD_LIBRARY_PATH
108+
export DYLD_LIBRARY_PATH=$PWD/build/install/lib:$DYLD_LIBRARY_PATH
109+
110+
./$name
111+
112+
rm -rf sherpa-onnx-medasr-*
113+
rm -v ./$name
114+
81115
- name: Test Omnilingual ASR CTC
82116
shell: bash
83117
run: |

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,3 +165,4 @@ build-riscv64-linux-gnu-spacemit/
165165
spacemit-toolchain*
166166
sherpa-onnx-qnn-*
167167
matcha-icefall-*
168+
sherpa-onnx-medasr-ctc-en-int8-2025-12-25

c-api-examples/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ target_link_libraries(wenet-ctc-c-api sherpa-onnx-c-api)
8686
add_executable(omnilingual-asr-ctc-c-api omnilingual-asr-ctc-c-api.c)
8787
target_link_libraries(omnilingual-asr-ctc-c-api sherpa-onnx-c-api)
8888

89+
add_executable(medasr-ctc-c-api medasr-ctc-c-api.c)
90+
target_link_libraries(medasr-ctc-c-api sherpa-onnx-c-api)
91+
8992
add_executable(streaming-zipformer-c-api streaming-zipformer-c-api.c)
9093
target_link_libraries(streaming-zipformer-c-api sherpa-onnx-c-api)
9194

c-api-examples/medasr-ctc-c-api.c

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// c-api-examples/medasr-ctc-c-api.c
2+
//
3+
// Copyright (c) 2025 Xiaomi Corporation
4+
5+
//
6+
// This file demonstrates how to use MedASR with sherpa-onnx's C API.
7+
// clang-format off
8+
/*
9+
wget https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-medasr-ctc-en-int8-2025-12-25.tar.bz2
10+
tar xvf sherpa-onnx-medasr-ctc-en-int8-2025-12-25.tar.bz2
11+
rm sherpa-onnx-medasr-ctc-en-int8-2025-12-25.tar.bz2
12+
*/
13+
//
14+
// clang-format on
15+
16+
#include <stdio.h>
17+
#include <stdlib.h>
18+
#include <string.h>
19+
20+
#include "sherpa-onnx/c-api/c-api.h"
21+
22+
int32_t main() {
23+
// clang-format off
24+
const char *wav_filename = "./sherpa-onnx-medasr-ctc-en-int8-2025-12-25/test_wavs/0.wav";
25+
const char *model_filename = "./sherpa-onnx-medasr-ctc-en-int8-2025-12-25/model.int8.onnx";
26+
const char *tokens_filename = "./sherpa-onnx-medasr-ctc-en-int8-2025-12-25/tokens.txt";
27+
// clang-format on
28+
29+
const char *provider = "cpu";
30+
31+
const SherpaOnnxWave *wave = SherpaOnnxReadWave(wav_filename);
32+
if (wave == NULL) {
33+
fprintf(stderr, "Failed to read %s\n", wav_filename);
34+
return -1;
35+
}
36+
37+
SherpaOnnxOfflineMedAsrCtcModelConfig medasr;
38+
memset(&medasr, 0, sizeof(medasr));
39+
medasr.model = model_filename;
40+
41+
// Offline model config
42+
SherpaOnnxOfflineModelConfig offline_model_config;
43+
memset(&offline_model_config, 0, sizeof(offline_model_config));
44+
offline_model_config.debug = 1;
45+
offline_model_config.num_threads = 1;
46+
offline_model_config.provider = provider;
47+
offline_model_config.tokens = tokens_filename;
48+
offline_model_config.medasr = medasr;
49+
50+
// Recognizer config
51+
SherpaOnnxOfflineRecognizerConfig recognizer_config;
52+
memset(&recognizer_config, 0, sizeof(recognizer_config));
53+
recognizer_config.decoding_method = "greedy_search";
54+
recognizer_config.model_config = offline_model_config;
55+
56+
const SherpaOnnxOfflineRecognizer *recognizer =
57+
SherpaOnnxCreateOfflineRecognizer(&recognizer_config);
58+
59+
if (recognizer == NULL) {
60+
fprintf(stderr, "Please check your config!\n");
61+
SherpaOnnxFreeWave(wave);
62+
return -1;
63+
}
64+
65+
const SherpaOnnxOfflineStream *stream =
66+
SherpaOnnxCreateOfflineStream(recognizer);
67+
68+
SherpaOnnxAcceptWaveformOffline(stream, wave->sample_rate, wave->samples,
69+
wave->num_samples);
70+
SherpaOnnxDecodeOfflineStream(recognizer, stream);
71+
const SherpaOnnxOfflineRecognizerResult *result =
72+
SherpaOnnxGetOfflineStreamResult(stream);
73+
74+
fprintf(stderr, "Decoded text: %s\n", result->text);
75+
76+
SherpaOnnxDestroyOfflineRecognizerResult(result);
77+
SherpaOnnxDestroyOfflineStream(stream);
78+
SherpaOnnxDestroyOfflineRecognizer(recognizer);
79+
SherpaOnnxFreeWave(wave);
80+
81+
return 0;
82+
}

cxx-api-examples/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ target_link_libraries(wenet-ctc-cxx-api sherpa-onnx-cxx-api)
4242
add_executable(omnilingual-asr-ctc-cxx-api ./omnilingual-asr-ctc-cxx-api.cc)
4343
target_link_libraries(omnilingual-asr-ctc-cxx-api sherpa-onnx-cxx-api)
4444

45+
add_executable(medasr-ctc-cxx-api ./medasr-ctc-cxx-api.cc)
46+
target_link_libraries(medasr-ctc-cxx-api sherpa-onnx-cxx-api)
47+
4548
add_executable(nemo-canary-cxx-api ./nemo-canary-cxx-api.cc)
4649
target_link_libraries(nemo-canary-cxx-api sherpa-onnx-cxx-api)
4750

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// cxx-api-examples/medasr-ctc-cxx-api.cc
2+
// Copyright (c) 2025 Xiaomi Corporation
3+
4+
//
5+
// This file demonstrates how to use MedASR with sherpa-onnx's C++ API.
6+
// clang-format off
7+
/*
8+
wget https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-medasr-ctc-en-int8-2025-12-25.tar.bz2
9+
tar xvf sherpa-onnx-medasr-ctc-en-int8-2025-12-25.tar.bz2
10+
rm sherpa-onnx-medasr-ctc-en-int8-2025-12-25.tar.bz2
11+
*/
12+
//
13+
// clang-format on
14+
15+
#include <chrono> // NOLINT
16+
#include <iostream>
17+
#include <string>
18+
19+
#include "sherpa-onnx/c-api/cxx-api.h"
20+
21+
int32_t main() {
22+
using namespace sherpa_onnx::cxx; // NOLINT
23+
OfflineRecognizerConfig config;
24+
25+
// clang-format off
26+
config.model_config.medasr.model = "./sherpa-onnx-medasr-ctc-en-int8-2025-12-25/model.int8.onnx";
27+
config.model_config.tokens = "./sherpa-onnx-medasr-ctc-en-int8-2025-12-25/tokens.txt";
28+
29+
config.model_config.num_threads = 1;
30+
31+
std::cout << "Loading model\n";
32+
OfflineRecognizer recognizer = OfflineRecognizer::Create(config);
33+
if (!recognizer.Get()) {
34+
std::cerr << "Please check your config\n";
35+
return -1;
36+
}
37+
std::cout << "Loading model done\n";
38+
39+
std::string wave_filename = "./sherpa-onnx-medasr-ctc-en-int8-2025-12-25/test_wavs/0.wav";
40+
// clang-format on
41+
42+
Wave wave = ReadWave(wave_filename);
43+
if (wave.samples.empty()) {
44+
std::cerr << "Failed to read: '" << wave_filename << "'\n";
45+
return -1;
46+
}
47+
48+
std::cout << "Start recognition\n";
49+
const auto begin = std::chrono::steady_clock::now();
50+
51+
OfflineStream stream = recognizer.CreateStream();
52+
stream.AcceptWaveform(wave.sample_rate, wave.samples.data(),
53+
wave.samples.size());
54+
55+
recognizer.Decode(&stream);
56+
57+
OfflineRecognizerResult result = recognizer.GetResult(&stream);
58+
59+
const auto end = std::chrono::steady_clock::now();
60+
const float elapsed_seconds =
61+
std::chrono::duration_cast<std::chrono::milliseconds>(end - begin)
62+
.count() /
63+
1000.;
64+
float duration = wave.samples.size() / static_cast<float>(wave.sample_rate);
65+
float rtf = elapsed_seconds / duration;
66+
67+
std::cout << "text: " << result.text << "\n";
68+
printf("Number of threads: %d\n", config.model_config.num_threads);
69+
printf("Duration: %.3fs\n", duration);
70+
printf("Elapsed seconds: %.3fs\n", elapsed_seconds);
71+
printf("(Real time factor) RTF = %.3f / %.3f = %.3f\n", elapsed_seconds,
72+
duration, rtf);
73+
74+
return 0;
75+
}

sherpa-onnx/c-api/c-api.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,9 @@ static sherpa_onnx::OfflineRecognizerConfig GetOfflineRecognizerConfig(
511511
recognizer_config.model_config.omnilingual.model =
512512
SHERPA_ONNX_OR(config->model_config.omnilingual.model, "");
513513

514+
recognizer_config.model_config.medasr.model =
515+
SHERPA_ONNX_OR(config->model_config.medasr.model, "");
516+
514517
recognizer_config.lm_config.model =
515518
SHERPA_ONNX_OR(config->lm_config.model, "");
516519
recognizer_config.lm_config.scale =

sherpa-onnx/c-api/c-api.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,10 @@ SHERPA_ONNX_API typedef struct SherpaOnnxOfflineOmnilingualAsrCtcModelConfig {
484484
const char *model;
485485
} SherpaOnnxOfflineOmnilingualAsrCtcModelConfig;
486486

487+
SHERPA_ONNX_API typedef struct SherpaOnnxOfflineMedAsrCtcModelConfig {
488+
const char *model;
489+
} SherpaOnnxOfflineMedAsrCtcModelConfig;
490+
487491
SHERPA_ONNX_API typedef struct SherpaOnnxOfflineModelConfig {
488492
SherpaOnnxOfflineTransducerModelConfig transducer;
489493
SherpaOnnxOfflineParaformerModelConfig paraformer;
@@ -511,6 +515,7 @@ SHERPA_ONNX_API typedef struct SherpaOnnxOfflineModelConfig {
511515
SherpaOnnxOfflineCanaryModelConfig canary;
512516
SherpaOnnxOfflineWenetCtcModelConfig wenet_ctc;
513517
SherpaOnnxOfflineOmnilingualAsrCtcModelConfig omnilingual;
518+
SherpaOnnxOfflineMedAsrCtcModelConfig medasr;
514519
} SherpaOnnxOfflineModelConfig;
515520

516521
SHERPA_ONNX_API typedef struct SherpaOnnxOfflineRecognizerConfig {

sherpa-onnx/c-api/cxx-api.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,8 @@ static SherpaOnnxOfflineRecognizerConfig Convert(
272272
c.model_config.omnilingual.model =
273273
config.model_config.omnilingual.model.c_str();
274274

275+
c.model_config.medasr.model = config.model_config.medasr.model.c_str();
276+
275277
c.lm_config.model = config.lm_config.model.c_str();
276278
c.lm_config.scale = config.lm_config.scale;
277279

0 commit comments

Comments
 (0)