react-native-sherpa-onnx 0.3.2 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -15
- package/SherpaOnnx.podspec +13 -5
- package/android/prebuilt-download.gradle +18 -5
- package/android/prebuilt-versions.gradle +8 -4
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-model-detect-helper.cpp +43 -142
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-model-detect-helper.h +12 -4
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-model-detect-stt.cpp +694 -307
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-model-detect-tts.cpp +194 -99
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-model-detect.h +90 -0
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-stt-wrapper.cpp +3 -0
- package/android/src/main/java/com/sherpaonnx/SherpaOnnxModule.kt +70 -0
- package/android/src/main/java/com/sherpaonnx/SherpaOnnxPcmCapture.kt +150 -0
- package/android/src/main/java/com/sherpaonnx/SherpaOnnxSttHelper.kt +39 -19
- package/ios/SherpaOnnx+PcmLiveStream.mm +288 -0
- package/ios/SherpaOnnx+STT.mm +2 -0
- package/ios/SherpaOnnx.mm +1 -1
- package/ios/model_detect/sherpa-onnx-model-detect-helper.h +9 -3
- package/ios/model_detect/sherpa-onnx-model-detect-helper.mm +38 -54
- package/ios/model_detect/sherpa-onnx-model-detect-stt.mm +620 -267
- package/ios/model_detect/sherpa-onnx-model-detect-tts.mm +131 -28
- package/ios/model_detect/sherpa-onnx-model-detect.h +70 -0
- package/ios/stt/sherpa-onnx-stt-wrapper.mm +4 -0
- package/lib/module/NativeSherpaOnnx.js.map +1 -1
- package/lib/module/audio/index.js +52 -0
- package/lib/module/audio/index.js.map +1 -1
- package/lib/module/stt/streaming.js +6 -3
- package/lib/module/stt/streaming.js.map +1 -1
- package/lib/typescript/src/NativeSherpaOnnx.d.ts +16 -2
- package/lib/typescript/src/NativeSherpaOnnx.d.ts.map +1 -1
- package/lib/typescript/src/audio/index.d.ts +17 -0
- package/lib/typescript/src/audio/index.d.ts.map +1 -1
- package/lib/typescript/src/stt/streaming.d.ts.map +1 -1
- package/lib/typescript/src/stt/streamingTypes.d.ts +1 -1
- package/lib/typescript/src/stt/streamingTypes.d.ts.map +1 -1
- package/package.json +6 -1
- package/scripts/check-model-csvs.sh +72 -0
- package/scripts/setup-ios-framework.sh +48 -48
- package/src/NativeSherpaOnnx.ts +18 -2
- package/src/audio/index.ts +81 -0
- package/src/stt/streaming.ts +10 -5
- package/src/stt/streamingTypes.ts +1 -1
- package/third_party/sherpa-onnx-prebuilt/ANDROID_RELEASE_TAG +1 -1
- package/third_party/sherpa-onnx-prebuilt/IOS_RELEASE_TAG +1 -1
package/README.md
CHANGED
|
@@ -14,6 +14,8 @@ React Native SDK for sherpa-onnx – offline and streaming speech processing
|
|
|
14
14
|
[](https://www.android.com/)
|
|
15
15
|
[](https://www.apple.com/ios/)
|
|
16
16
|
|
|
17
|
+
<a href="https://www.buymeacoffee.com/xdcobra" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" width="150" /></a>
|
|
18
|
+
|
|
17
19
|
</div>
|
|
18
20
|
|
|
19
21
|
> **⚠️ SDK 0.3.0 – Breaking changes from 0.2.0**
|
|
@@ -75,14 +77,23 @@ A React Native TurboModule that provides offline and streaming speech processing
|
|
|
75
77
|
|
|
76
78
|
| Model Type | `modelType` Value | Description | Download Links |
|
|
77
79
|
| ------------------------ | ----------------- | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
|
|
78
|
-
| **Zipformer/Transducer** | `'transducer'` |
|
|
79
|
-
| **
|
|
80
|
-
| **
|
|
81
|
-
| **
|
|
82
|
-
| **
|
|
83
|
-
| **
|
|
84
|
-
| **
|
|
85
|
-
| **
|
|
80
|
+
| **Zipformer/Transducer** | `'transducer'` | Encoder–decoder–joiner (e.g. icefall). Good balance of speed and accuracy. Folder name should contain **zipformer** or **transducer** for auto-detection. | [Download](https://k2-fsa.github.io/sherpa/onnx/pretrained_models/offline-transducer/index.html) |
|
|
81
|
+
| **LSTM Transducer** | `'transducer'` | Same layout as Zipformer (encoder–decoder–joiner). LSTM-based streaming ASR; detected as transducer. Folder name may contain **lstm**. | [Download](https://k2-fsa.github.io/sherpa/onnx/pretrained_models/online-transducer/lstm-transducer-models.html) |
|
|
82
|
+
| **Paraformer** | `'paraformer'` | Single-model non-autoregressive ASR; fast and accurate. Detected by `model.onnx`; no folder token required. | [Download](https://k2-fsa.github.io/sherpa/onnx/pretrained_models/offline-paraformer/index.html) |
|
|
83
|
+
| **NeMo CTC** | `'nemo_ctc'` | NeMo CTC; good for English and streaming. Folder name should contain **nemo** or **parakeet**. | [Download](https://k2-fsa.github.io/sherpa/onnx/pretrained_models/offline-ctc/nemo/index.html) |
|
|
84
|
+
| **Whisper** | `'whisper'` | Multilingual, encoder–decoder; strong zero-shot. Detected by encoder+decoder (no joiner); folder token optional. | [Download](https://k2-fsa.github.io/sherpa/onnx/pretrained_models/whisper/index.html) |
|
|
85
|
+
| **WeNet CTC** | `'wenet_ctc'` | CTC from WeNet; compact. Folder name should contain **wenet**. | [Download](https://k2-fsa.github.io/sherpa/onnx/pretrained_models/offline-ctc/wenet/index.html) |
|
|
86
|
+
| **SenseVoice** | `'sense_voice'` | Multilingual with emotion/punctuation. Folder name should contain **sense** or **sensevoice**. | [Download](https://k2-fsa.github.io/sherpa/onnx/pretrained_models/sense-voice/index.html) |
|
|
87
|
+
| **FunASR Nano** | `'funasr_nano'` | Lightweight LLM-based ASR. Folder name should contain **funasr** or **funasr-nano**. | [Download](https://k2-fsa.github.io/sherpa/onnx/pretrained_models/funasr-nano/index.html) |
|
|
88
|
+
| **Moonshine (v1)** | `'moonshine'` | Four-part streaming-capable ASR (preprocess, encode, uncached/cached decode). Folder name should contain **moonshine**. | [Download](https://k2-fsa.github.io/sherpa/onnx/moonshine/index.html) |
|
|
89
|
+
| **Moonshine (v2)** | `'moonshine_v2'` | Two-part Moonshine (encoder + merged decoder); `.onnx` or `.ort`. Folder name should contain **moonshine** (v2 preferred if both layouts present). | [Download](https://k2-fsa.github.io/sherpa/onnx/moonshine/index.html) |
|
|
90
|
+
| **Fire Red ASR** | `'fire_red_asr'` | Fire Red encoder–decoder ASR. Folder name should contain **fire_red** or **fire-red**. | [Download](https://k2-fsa.github.io/sherpa/onnx/FireRedAsr/index.html) |
|
|
91
|
+
| **Dolphin** | `'dolphin'` | Single-model CTC. Folder name should contain **dolphin**. | [Download](https://k2-fsa.github.io/sherpa/onnx/Dolphin/index.html) |
|
|
92
|
+
| **Canary** | `'canary'` | NeMo Canary multilingual. Folder name should contain **canary**. | [Download](https://k2-fsa.github.io/sherpa/onnx/nemo/canary.html) |
|
|
93
|
+
| **Omnilingual** | `'omnilingual'` | Omnilingual CTC. Folder name should contain **omnilingual**. | [Download](https://k2-fsa.github.io/sherpa/onnx/omnilingual-asr/index.html) |
|
|
94
|
+
| **MedASR** | `'medasr'` | Medical ASR CTC. Folder name should contain **medasr**. | [Download](https://github.com/k2-fsa/sherpa-onnx) |
|
|
95
|
+
| **Telespeech CTC** | `'telespeech_ctc'`| Telespeech CTC. Folder name should contain **telespeech**. | [Download](https://k2-fsa.github.io/sherpa/onnx/pretrained_models/telespeech/index.html) |
|
|
96
|
+
| **Tone CTC (t-one)** | `'tone_ctc'` | Lightweight streaming CTC (e.g. t-one). Folder name should contain **t-one**, **t_one**, or **tone** (as word). | [Download](https://k2-fsa.github.io/sherpa/onnx/pretrained_models/online-ctc/index.html) |
|
|
86
97
|
|
|
87
98
|
For **real-time (streaming) recognition** from a microphone or audio stream, use streaming-capable model types: `transducer`, `paraformer`, `zipformer2_ctc`, `nemo_ctc`, or `tone_ctc`. See [Streaming (Online) Speech-to-Text](./docs/stt_streaming.md).
|
|
88
99
|
|
|
@@ -90,12 +101,12 @@ For **real-time (streaming) recognition** from a microphone or audio stream, use
|
|
|
90
101
|
|
|
91
102
|
| Model Type | `modelType` Value | Description | Download Links |
|
|
92
103
|
| ---------------- | ----------------- | ---------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
|
|
93
|
-
| **VITS** | `'vits'` | Fast, high-quality TTS
|
|
94
|
-
| **Matcha** | `'matcha'` | High-quality acoustic model + vocoder.
|
|
95
|
-
| **Kokoro** | `'kokoro'` | Multi-speaker, multi-language.
|
|
96
|
-
| **KittenTTS** | `'kitten'` | Lightweight, multi-speaker.
|
|
97
|
-
| **Zipvoice** | `'zipvoice'` | Voice cloning
|
|
98
|
-
| **Pocket** | `'pocket'` | Flow-matching TTS.
|
|
104
|
+
| **VITS** | `'vits'` | Fast, high-quality TTS (Piper, Coqui, MeloTTS, MMS). Folder name should contain **vits** if used with other voice models. | [Download](https://github.com/k2-fsa/sherpa-onnx/releases/tag/tts-models) |
|
|
105
|
+
| **Matcha** | `'matcha'` | High-quality acoustic model + vocoder. Detected by acoustic_model + vocoder; no folder token required. | [Download](https://k2-fsa.github.io/sherpa/onnx/tts/pretrained_models/matcha.html) |
|
|
106
|
+
| **Kokoro** | `'kokoro'` | Multi-speaker, multi-language. Folder name should contain **kokoro** (not kitten) for auto-detection. | [Download](https://github.com/k2-fsa/sherpa-onnx/releases/tag/tts-models) |
|
|
107
|
+
| **KittenTTS** | `'kitten'` | Lightweight, multi-speaker. Folder name should contain **kitten** (not kokoro) for auto-detection. | [Download](https://github.com/k2-fsa/sherpa-onnx/releases/tag/tts-models) |
|
|
108
|
+
| **Zipvoice** | `'zipvoice'` | Voice cloning (encoder + decoder + vocoder). Detected by file layout; folder token optional. | [Download](https://k2-fsa.github.io/sherpa/onnx/tts/pretrained_models/zipvoice.html) |
|
|
109
|
+
| **Pocket** | `'pocket'` | Flow-matching TTS. Detected by lm_flow, lm_main, text_conditioner, vocab/token_scores; no folder token required. | [Download](https://github.com/k2-fsa/sherpa-onnx/releases/tag/tts-models) |
|
|
99
110
|
|
|
100
111
|
For **streaming TTS** (incremental generation, low latency), use `createStreamingTTS()` with supported model types. See [Streaming Text-to-Speech](./docs/tts_streaming.md).
|
|
101
112
|
|
|
@@ -151,6 +162,7 @@ The XCFramework must include the C++ API (`libsherpa-onnx-cxx-api.a` merged or l
|
|
|
151
162
|
|
|
152
163
|
- [Speech-to-Text (STT)](./docs/stt.md) – Offline transcription (file or samples)
|
|
153
164
|
- [Streaming (Online) Speech-to-Text](./docs/stt_streaming.md) – Real-time recognition, partial results, endpoint detection
|
|
165
|
+
- [PCM Live Stream](./docs/pcm_live_stream.md) – Native microphone capture with resampling for live transcription (use with streaming STT)
|
|
154
166
|
- [Text-to-Speech (TTS)](./docs/tts.md) – Offline and streaming generation
|
|
155
167
|
- [Streaming Text-to-Speech](./docs/tts_streaming.md) – Incremental TTS (createStreamingTTS)
|
|
156
168
|
- [Execution provider support (QNN, NNAPI, XNNPACK, Core ML)](./docs/execution-providers.md) – Checking and using acceleration backends
|
|
@@ -179,7 +191,7 @@ We provide example applications to help you get started with `react-native-sherp
|
|
|
179
191
|
|
|
180
192
|
The example app included in this repository demonstrates audio-to-text transcription, text-to-speech, and streaming features. It includes:
|
|
181
193
|
|
|
182
|
-
- Multiple model type support (Zipformer, Paraformer, NeMo CTC, Whisper, WeNet CTC, SenseVoice, FunASR Nano)
|
|
194
|
+
- Multiple model type support (Zipformer, Paraformer, NeMo CTC, Whisper, WeNet CTC, SenseVoice, FunASR Nano, Moonshine, and more)
|
|
183
195
|
- Model selection and configuration
|
|
184
196
|
- **Offline** audio file transcription
|
|
185
197
|
- **Online (streaming) STT** – live transcription from the microphone with partial results
|
|
@@ -202,6 +214,7 @@ yarn android # or yarn ios
|
|
|
202
214
|
<td><img src="./docs/images/example_stt_2.png" alt="Transcribe cantonese audio" width="240" /></td>
|
|
203
215
|
</tr>
|
|
204
216
|
<tr>
|
|
217
|
+
<td><img src="./docs/images/example_streaming.png" alt="Text to speech generation" width="240" /></td>
|
|
205
218
|
<td><img src="./docs/images/example_tts.png" alt="Text to speech generation" width="240" /></td>
|
|
206
219
|
<td><img src="./docs/images/example_provider.png" alt="Text to speech generation" width="240" /></td>
|
|
207
220
|
</tr>
|
package/SherpaOnnx.podspec
CHANGED
|
@@ -37,6 +37,18 @@ if libarchive_sources.empty?
|
|
|
37
37
|
abort("[SherpaOnnx] Libarchive sources missing. Ensure third_party/libarchive_prebuilt/libarchive-ios-layout exists (run third_party/libarchive_prebuilt/build_libarchive_ios.sh) or ios/scripts/setup-ios-libarchive.sh has run, and that ios/scripts/patch-libarchive-includes.sh succeeds. Check pod install logs for patch script errors.")
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
+
# Run iOS framework setup when podspec is loaded (works for :path pods).
|
|
41
|
+
setup_script = File.join(pod_root, "scripts", "setup-ios-framework.sh")
|
|
42
|
+
if File.exist?(setup_script)
|
|
43
|
+
prev = ENV["SHERPA_ONNX_PROJECT_ROOT"]
|
|
44
|
+
ENV["SHERPA_ONNX_PROJECT_ROOT"] = pod_root
|
|
45
|
+
unless system("bash", setup_script)
|
|
46
|
+
ENV["SHERPA_ONNX_PROJECT_ROOT"] = prev
|
|
47
|
+
abort("[SherpaOnnx] setup-ios-framework.sh failed. Check third_party/sherpa-onnx-prebuilt/IOS_RELEASE_TAG and network. Run manually: bash #{setup_script}")
|
|
48
|
+
end
|
|
49
|
+
ENV["SHERPA_ONNX_PROJECT_ROOT"] = prev
|
|
50
|
+
end
|
|
51
|
+
|
|
40
52
|
Pod::Spec.new do |s|
|
|
41
53
|
s.name = "SherpaOnnx"
|
|
42
54
|
s.version = package["version"]
|
|
@@ -48,14 +60,10 @@ Pod::Spec.new do |s|
|
|
|
48
60
|
s.platforms = { :ios => min_ios_version_supported }
|
|
49
61
|
s.source = { :git => "https://github.com/XDcobra/react-native-sherpa-onnx.git", :tag => "#{s.version}" }
|
|
50
62
|
|
|
51
|
-
# Download sherpa-onnx XCFramework from GitHub Releases before pod install (uses IOS_RELEASE_TAG for pinned version).
|
|
52
|
-
setup_script = File.join(pod_root, "scripts", "setup-ios-framework.sh")
|
|
53
|
-
s.prepare_command = "bash \"#{setup_script}\""
|
|
54
|
-
|
|
55
63
|
s.source_files = ["ios/**/*.{h,m,mm,swift,cpp}", *libarchive_sources]
|
|
56
64
|
s.private_header_files = "ios/**/*.h"
|
|
57
65
|
|
|
58
|
-
s.frameworks = "Foundation", "Accelerate", "CoreML"
|
|
66
|
+
s.frameworks = "Foundation", "Accelerate", "CoreML", "AVFoundation", "AudioToolbox"
|
|
59
67
|
s.vendored_frameworks = "ios/Frameworks/sherpa_onnx.xcframework"
|
|
60
68
|
# Absolute paths so headers are found regardless of PODS_TARGET_SRCROOT (e.g. when building via React Native CLI).
|
|
61
69
|
xcframework_root = File.join(pod_root, "ios", "Frameworks", "sherpa_onnx.xcframework")
|
|
@@ -99,12 +99,20 @@ def readReleaseTag = { File tagFile ->
|
|
|
99
99
|
project.tasks.register("downloadNativeLibsIfNeeded") {
|
|
100
100
|
doLast {
|
|
101
101
|
def downloadDir = file("${project.buildDir}/prebuilt-downloads")
|
|
102
|
+
def currentSherpaVersion = project.ext.sherpaOnnxVersion
|
|
103
|
+
def sherpaVersionFile = new File(downloadDir, "sherpa-onnx-version.txt")
|
|
104
|
+
def storedSherpaVersion = (sherpaVersionFile.exists() ? sherpaVersionFile.text.trim() : null)
|
|
105
|
+
def sherpaNeedsUpdate = !hasAllSherpaLibs() || !hasSherpaHeaders() || storedSherpaVersion == null || storedSherpaVersion != currentSherpaVersion
|
|
102
106
|
|
|
103
|
-
if (hasAllSherpaLibs() && hasSherpaHeaders()) {
|
|
104
|
-
println "[sherpa-onnx] Native libs + headers: (1)
|
|
107
|
+
if (hasAllSherpaLibs() && hasSherpaHeaders() && !sherpaNeedsUpdate) {
|
|
108
|
+
println "[sherpa-onnx] Native libs + headers: (1) already present, version ${currentSherpaVersion}"
|
|
109
|
+
}
|
|
110
|
+
if (sherpaNeedsUpdate && storedSherpaVersion != null && storedSherpaVersion != currentSherpaVersion) {
|
|
111
|
+
println "[sherpa-onnx] Version change detected (${storedSherpaVersion} -> ${currentSherpaVersion}), refreshing libs and headers"
|
|
105
112
|
}
|
|
106
113
|
|
|
107
|
-
|
|
114
|
+
def sherpaUpdatedFromAar = [false]
|
|
115
|
+
if (sherpaNeedsUpdate) {
|
|
108
116
|
try {
|
|
109
117
|
def aarFiles = project.configurations.sherpaOnnxAar.files
|
|
110
118
|
if (!aarFiles.isEmpty()) {
|
|
@@ -127,6 +135,9 @@ project.tasks.register("downloadNativeLibsIfNeeded") {
|
|
|
127
135
|
copy { from fileTree(aarExtractDir) { include 'c-api/**' }; into includeSherpaDir }
|
|
128
136
|
println "Extracted sherpa-onnx C-API headers from Maven AAR"
|
|
129
137
|
}
|
|
138
|
+
downloadDir.mkdirs()
|
|
139
|
+
sherpaVersionFile.text = currentSherpaVersion
|
|
140
|
+
sherpaUpdatedFromAar[0] = true
|
|
130
141
|
println "[sherpa-onnx] Native libs + headers: (2) Maven AAR (${aar.name})"
|
|
131
142
|
}
|
|
132
143
|
} catch (Exception e) {
|
|
@@ -228,7 +239,7 @@ project.tasks.register("downloadNativeLibsIfNeeded") {
|
|
|
228
239
|
if (!repo) {
|
|
229
240
|
def needFfmpeg = !sherpaOnnxDisableFfmpeg && (!hasAllFfmpegLibs() || !hasFfmpegHeaders())
|
|
230
241
|
def needLibarchive = !sherpaOnnxDisableLibarchive && (!hasAllLibarchiveLibs() || !hasLibarchiveHeaders())
|
|
231
|
-
if (needFfmpeg || needLibarchive ||
|
|
242
|
+
if (needFfmpeg || needLibarchive || (sherpaNeedsUpdate && !sherpaUpdatedFromAar[0])) {
|
|
232
243
|
throw new RuntimeException(
|
|
233
244
|
"Native libs/headers still missing and GitHub repo unknown. Set -PprebuiltGitHubRepo=owner/repo or ensure git remote origin is a GitHub URL. " +
|
|
234
245
|
"Alternatively run third_party/ffmpeg_prebuilt/copy_prebuilts_to_sdk.js, third_party/sherpa-onnx-prebuilt/copy_prebuilts_to_sdk.js, third_party/libarchive_prebuilt/copy_prebuilts_to_sdk.js, or use Maven (com.xdcobra.sherpa:ffmpeg / sherpa-onnx / libarchive), or ensure ANDROID_RELEASE_TAG releases exist. " +
|
|
@@ -290,7 +301,7 @@ project.tasks.register("downloadNativeLibsIfNeeded") {
|
|
|
290
301
|
println "Downloaded and extracted libarchive prebuilts (libs + include) from ${tag}"
|
|
291
302
|
}
|
|
292
303
|
|
|
293
|
-
if (
|
|
304
|
+
if (sherpaNeedsUpdate && !sherpaUpdatedFromAar[0]) {
|
|
294
305
|
def tagFile = file("${project.projectDir.parent}/third_party/sherpa-onnx-prebuilt/ANDROID_RELEASE_TAG")
|
|
295
306
|
def tag = readReleaseTag(tagFile)
|
|
296
307
|
if (!tag) throw new RuntimeException("Missing or empty third_party/sherpa-onnx-prebuilt/ANDROID_RELEASE_TAG")
|
|
@@ -319,6 +330,8 @@ project.tasks.register("downloadNativeLibsIfNeeded") {
|
|
|
319
330
|
sherpaOnnxClassesDir.mkdirs()
|
|
320
331
|
copy { from sherpaJavaJar; into sherpaOnnxClassesDir }
|
|
321
332
|
}
|
|
333
|
+
downloadDir.mkdirs()
|
|
334
|
+
sherpaVersionFile.text = currentSherpaVersion
|
|
322
335
|
println "[sherpa-onnx] Native libs + headers: (3) GitHub release (${tag})"
|
|
323
336
|
} else {
|
|
324
337
|
def sherpaClassesJar = file("${project.projectDir.parent}/third_party/sherpa-onnx-prebuilt/android/java/classes.jar")
|
|
@@ -11,18 +11,22 @@ def readVersionFromTagFile(File tagFile, String prefix) {
|
|
|
11
11
|
return null
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
// Module root: parent of android/ (so tag files resolve when SDK is used as dependency, e.g. node_modules/react-native-sherpa-onnx).
|
|
15
|
+
def moduleRoot = project.projectDir.parentFile
|
|
16
|
+
|
|
14
17
|
// sherpa-onnx: 1. SHERPA_ONNX_VERSION env 2. ANDROID_RELEASE_TAG 3. property 4. default
|
|
15
18
|
def sherpaOnnxVersion = System.getenv('SHERPA_ONNX_VERSION')
|
|
16
19
|
if (!sherpaOnnxVersion) {
|
|
17
|
-
def v = readVersionFromTagFile(new File(
|
|
18
|
-
sherpaOnnxVersion = v ?: (project.hasProperty('sherpaOnnxVersion') ? project.sherpaOnnxVersion : '1.12.
|
|
20
|
+
def v = readVersionFromTagFile(new File(moduleRoot, 'third_party/sherpa-onnx-prebuilt/ANDROID_RELEASE_TAG'), 'sherpa-onnx-android-v')
|
|
21
|
+
sherpaOnnxVersion = v ?: (project.hasProperty('sherpaOnnxVersion') ? project.sherpaOnnxVersion : '1.12.28')
|
|
19
22
|
}
|
|
20
23
|
project.ext.sherpaOnnxVersion = sherpaOnnxVersion
|
|
24
|
+
println "[react-native-sherpa-onnx] sherpa-onnx version (extracted/used): ${sherpaOnnxVersion}"
|
|
21
25
|
|
|
22
26
|
// FFmpeg: 1. FFMPEG_VERSION env 2. ANDROID_RELEASE_TAG 3. property 4. default
|
|
23
27
|
def ffmpegVersion = System.getenv('FFMPEG_VERSION')
|
|
24
28
|
if (!ffmpegVersion) {
|
|
25
|
-
def v = readVersionFromTagFile(new File(
|
|
29
|
+
def v = readVersionFromTagFile(new File(moduleRoot, 'third_party/ffmpeg_prebuilt/ANDROID_RELEASE_TAG'), 'ffmpeg-android-v')
|
|
26
30
|
ffmpegVersion = v ?: (project.hasProperty('ffmpegVersion') ? project.ffmpegVersion : '8.0.1')
|
|
27
31
|
}
|
|
28
32
|
project.ext.ffmpegVersion = ffmpegVersion
|
|
@@ -30,7 +34,7 @@ project.ext.ffmpegVersion = ffmpegVersion
|
|
|
30
34
|
// libarchive: 1. LIBARCHIVE_VERSION env 2. ANDROID_RELEASE_TAG 3. property 4. default
|
|
31
35
|
def libarchiveVersion = System.getenv('LIBARCHIVE_VERSION')
|
|
32
36
|
if (!libarchiveVersion) {
|
|
33
|
-
def v = readVersionFromTagFile(new File(
|
|
37
|
+
def v = readVersionFromTagFile(new File(moduleRoot, 'third_party/libarchive_prebuilt/ANDROID_RELEASE_TAG'), 'libarchive-android-v')
|
|
34
38
|
libarchiveVersion = v ?: (project.hasProperty('libarchiveVersion') ? project.libarchiveVersion : '3.8.5')
|
|
35
39
|
}
|
|
36
40
|
project.ext.libarchiveVersion = libarchiveVersion
|
|
@@ -35,8 +35,8 @@ bool ContainsToken(const std::string& value, const std::string& token) {
|
|
|
35
35
|
return value.find(token) != std::string::npos;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
bool
|
|
39
|
-
return EndsWith(entry.nameLower, ".onnx");
|
|
38
|
+
bool IsOnnxOrOrtFile(const FileEntry& entry) {
|
|
39
|
+
return EndsWith(entry.nameLower, ".onnx") || EndsWith(entry.nameLower, ".ort");
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
std::string BaseName(const std::string& path) {
|
|
@@ -55,7 +55,7 @@ std::string ChooseLargest(
|
|
|
55
55
|
std::uint64_t bestSize = 0;
|
|
56
56
|
|
|
57
57
|
for (const auto& entry : files) {
|
|
58
|
-
if (!
|
|
58
|
+
if (!IsOnnxOrOrtFile(entry)) continue;
|
|
59
59
|
|
|
60
60
|
bool hasExcluded = false;
|
|
61
61
|
for (const auto& token : excludeTokens) {
|
|
@@ -212,9 +212,8 @@ std::string ToLower(std::string value) {
|
|
|
212
212
|
return value;
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
-
std::string FindFileByName(const std::
|
|
215
|
+
std::string FindFileByName(const std::vector<FileEntry>& files, const std::string& fileName) {
|
|
216
216
|
std::string target = ToLower(fileName);
|
|
217
|
-
auto files = ListFilesRecursive(baseDir, maxDepth);
|
|
218
217
|
for (const auto& entry : files) {
|
|
219
218
|
if (entry.nameLower == target) {
|
|
220
219
|
return entry.path;
|
|
@@ -223,149 +222,17 @@ std::string FindFileByName(const std::string& baseDir, const std::string& fileNa
|
|
|
223
222
|
return "";
|
|
224
223
|
}
|
|
225
224
|
|
|
226
|
-
std::string FindFileEndingWith(const std::
|
|
225
|
+
std::string FindFileEndingWith(const std::vector<FileEntry>& files, const std::string& suffix) {
|
|
227
226
|
std::string targetSuffix = ToLower(suffix);
|
|
228
|
-
auto files = ListFilesRecursive(baseDir, maxDepth);
|
|
229
|
-
// 1) exact match (e.g. "tokens.txt")
|
|
230
227
|
for (const auto& entry : files) {
|
|
231
|
-
if (entry.nameLower == targetSuffix)
|
|
232
|
-
return entry.path;
|
|
233
|
-
}
|
|
228
|
+
if (entry.nameLower == targetSuffix) return entry.path;
|
|
234
229
|
}
|
|
235
|
-
|
|
236
|
-
// 2) true suffix match (preferred over substring to avoid false positives
|
|
237
|
-
// like "tokens.txt.bak" or "mytokens.txt.tmp").
|
|
238
230
|
for (const auto& entry : files) {
|
|
239
|
-
if (
|
|
231
|
+
if (targetSuffix.size() <= entry.nameLower.size() &&
|
|
232
|
+
std::equal(targetSuffix.rbegin(), targetSuffix.rend(), entry.nameLower.rbegin())) {
|
|
240
233
|
return entry.path;
|
|
241
234
|
}
|
|
242
235
|
}
|
|
243
|
-
|
|
244
|
-
// 3) If we are looking for tokens, fallback to inspecting .txt files' contents.
|
|
245
|
-
// Heuristic: many token files are plain text with lines like "token <index>".
|
|
246
|
-
if (targetSuffix.find("tokens") != std::string::npos) {
|
|
247
|
-
auto IsLikelyTokensFile = [](const std::string& path) -> bool {
|
|
248
|
-
std::ifstream ifs(path);
|
|
249
|
-
if (!ifs.is_open()) return false;
|
|
250
|
-
std::string line;
|
|
251
|
-
int total = 0;
|
|
252
|
-
int matched = 0;
|
|
253
|
-
const int maxLines = 2000;
|
|
254
|
-
|
|
255
|
-
while (total < maxLines && std::getline(ifs, line)) {
|
|
256
|
-
++total;
|
|
257
|
-
if (line.empty()) continue;
|
|
258
|
-
// Trim trailing CR if present
|
|
259
|
-
if (!line.empty() && line.back() == '\r') line.pop_back();
|
|
260
|
-
|
|
261
|
-
// Check if the line ends with an integer index (common token format)
|
|
262
|
-
size_t sp = line.find_last_of(" \t");
|
|
263
|
-
if (sp != std::string::npos && sp + 1 < line.size()) {
|
|
264
|
-
std::string idx = line.substr(sp + 1);
|
|
265
|
-
bool allDigits = !idx.empty();
|
|
266
|
-
for (char c : idx) {
|
|
267
|
-
if (!std::isdigit(static_cast<unsigned char>(c))) { allDigits = false; break; }
|
|
268
|
-
}
|
|
269
|
-
if (allDigits) ++matched;
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
ifs.close();
|
|
274
|
-
if (total < 2) return false;
|
|
275
|
-
// Heuristic: at least half of non-empty lines should match the token pattern
|
|
276
|
-
return matched >= std::max(1, total / 2);
|
|
277
|
-
};
|
|
278
|
-
|
|
279
|
-
for (const auto& entry : files) {
|
|
280
|
-
if (EndsWith(entry.nameLower, ".txt")) {
|
|
281
|
-
if (IsLikelyTokensFile(entry.path)) {
|
|
282
|
-
return entry.path;
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
return "";
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
std::string FindDirectoryByName(const std::string& baseDir, const std::string& dirName, int maxDepth) {
|
|
291
|
-
std::string target = ToLower(dirName);
|
|
292
|
-
std::vector<std::string> toVisit = ListDirectories(baseDir);
|
|
293
|
-
int depth = 0;
|
|
294
|
-
|
|
295
|
-
while (!toVisit.empty() && depth <= maxDepth) {
|
|
296
|
-
std::vector<std::string> next;
|
|
297
|
-
for (const auto& dir : toVisit) {
|
|
298
|
-
std::string name = dir;
|
|
299
|
-
#if __cplusplus >= 201703L && __has_include(<filesystem>)
|
|
300
|
-
try {
|
|
301
|
-
name = fs::path(dir).filename().string();
|
|
302
|
-
} catch (const std::exception&) {
|
|
303
|
-
}
|
|
304
|
-
#elif __has_include(<experimental/filesystem>)
|
|
305
|
-
try {
|
|
306
|
-
name = fs::path(dir).filename().string();
|
|
307
|
-
} catch (const std::exception&) {
|
|
308
|
-
}
|
|
309
|
-
#else
|
|
310
|
-
name = BaseName(dir);
|
|
311
|
-
#endif
|
|
312
|
-
if (ToLower(name) == target) {
|
|
313
|
-
return dir;
|
|
314
|
-
}
|
|
315
|
-
if (depth < maxDepth) {
|
|
316
|
-
auto nested = ListDirectories(dir);
|
|
317
|
-
next.insert(next.end(), nested.begin(), nested.end());
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
toVisit.swap(next);
|
|
321
|
-
depth += 1;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
return "";
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
std::string ResolveTokenizerDir(const std::string& modelDir) {
|
|
328
|
-
std::string vocabInMain = modelDir + "/vocab.json";
|
|
329
|
-
if (FileExists(vocabInMain)) {
|
|
330
|
-
return modelDir;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
std::vector<std::string> toVisit = ListDirectories(modelDir);
|
|
334
|
-
int depth = 0;
|
|
335
|
-
while (!toVisit.empty() && depth <= 2) {
|
|
336
|
-
std::vector<std::string> next;
|
|
337
|
-
for (const auto& dir : toVisit) {
|
|
338
|
-
std::string dirName = dir;
|
|
339
|
-
#if __cplusplus >= 201703L && __has_include(<filesystem>)
|
|
340
|
-
try {
|
|
341
|
-
dirName = fs::path(dir).filename().string();
|
|
342
|
-
} catch (const std::exception&) {
|
|
343
|
-
}
|
|
344
|
-
#elif __has_include(<experimental/filesystem>)
|
|
345
|
-
try {
|
|
346
|
-
dirName = fs::path(dir).filename().string();
|
|
347
|
-
} catch (const std::exception&) {
|
|
348
|
-
}
|
|
349
|
-
#else
|
|
350
|
-
dirName = BaseName(dir);
|
|
351
|
-
#endif
|
|
352
|
-
std::string dirNameLower = ToLower(dirName);
|
|
353
|
-
if (dirNameLower.find("qwen3") != std::string::npos) {
|
|
354
|
-
std::string vocabPath = dir + "/vocab.json";
|
|
355
|
-
if (FileExists(vocabPath)) {
|
|
356
|
-
return dir;
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
if (depth < 2) {
|
|
361
|
-
auto nested = ListDirectories(dir);
|
|
362
|
-
next.insert(next.end(), nested.begin(), nested.end());
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
toVisit.swap(next);
|
|
366
|
-
depth += 1;
|
|
367
|
-
}
|
|
368
|
-
|
|
369
236
|
return "";
|
|
370
237
|
}
|
|
371
238
|
|
|
@@ -377,7 +244,7 @@ std::string FindOnnxByToken(
|
|
|
377
244
|
std::vector<FileEntry> matches;
|
|
378
245
|
std::string tokenLower = ToLower(token);
|
|
379
246
|
for (const auto& entry : files) {
|
|
380
|
-
if (!
|
|
247
|
+
if (!IsOnnxOrOrtFile(entry)) continue;
|
|
381
248
|
if (ContainsToken(entry.nameLower, tokenLower)) {
|
|
382
249
|
matches.push_back(entry);
|
|
383
250
|
}
|
|
@@ -407,6 +274,40 @@ std::string FindOnnxByAnyToken(
|
|
|
407
274
|
return "";
|
|
408
275
|
}
|
|
409
276
|
|
|
277
|
+
std::string FindOnnxByAnyTokenExcluding(
|
|
278
|
+
const std::vector<FileEntry>& files,
|
|
279
|
+
const std::vector<std::string>& tokens,
|
|
280
|
+
const std::vector<std::string>& excludeInName,
|
|
281
|
+
const std::optional<bool>& preferInt8
|
|
282
|
+
) {
|
|
283
|
+
for (const auto& token : tokens) {
|
|
284
|
+
std::string tokenLower = ToLower(token);
|
|
285
|
+
std::vector<FileEntry> matches;
|
|
286
|
+
for (const auto& entry : files) {
|
|
287
|
+
if (!IsOnnxOrOrtFile(entry)) continue;
|
|
288
|
+
if (!ContainsToken(entry.nameLower, tokenLower)) continue;
|
|
289
|
+
bool excluded = false;
|
|
290
|
+
for (const auto& ex : excludeInName) {
|
|
291
|
+
std::string exLower = ToLower(ex);
|
|
292
|
+
if (ContainsToken(entry.nameLower, exLower)) {
|
|
293
|
+
excluded = true;
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
if (!excluded) matches.push_back(entry);
|
|
298
|
+
}
|
|
299
|
+
if (matches.empty()) continue;
|
|
300
|
+
std::vector<std::string> emptyTokens;
|
|
301
|
+
bool wantInt8 = preferInt8.has_value() && preferInt8.value();
|
|
302
|
+
bool wantNonInt8 = preferInt8.has_value() && !preferInt8.value();
|
|
303
|
+
std::string chosen = ChooseLargest(matches, emptyTokens, wantInt8, wantNonInt8);
|
|
304
|
+
if (!chosen.empty()) return chosen;
|
|
305
|
+
chosen = ChooseLargest(matches, emptyTokens, false, false);
|
|
306
|
+
if (!chosen.empty()) return chosen;
|
|
307
|
+
}
|
|
308
|
+
return "";
|
|
309
|
+
}
|
|
310
|
+
|
|
410
311
|
std::string FindLargestOnnx(const std::vector<FileEntry>& files) {
|
|
411
312
|
std::vector<std::string> emptyTokens;
|
|
412
313
|
return ChooseLargest(files, emptyTokens, false, false);
|
|
@@ -22,11 +22,11 @@ std::vector<std::string> ListDirectories(const std::string& path);
|
|
|
22
22
|
std::vector<FileEntry> ListFiles(const std::string& path);
|
|
23
23
|
std::vector<FileEntry> ListFilesRecursive(const std::string& path, int maxDepth = 2);
|
|
24
24
|
std::string ToLower(std::string value);
|
|
25
|
-
std::string ResolveTokenizerDir(const std::string& modelDir);
|
|
26
25
|
|
|
27
|
-
|
|
28
|
-
std::string
|
|
29
|
-
|
|
26
|
+
/** Find file in \p files whose name equals \p fileName (case-insensitive). Uses file tree only, no filesystem. */
|
|
27
|
+
std::string FindFileByName(const std::vector<FileEntry>& files, const std::string& fileName);
|
|
28
|
+
/** Find file in \p files whose name equals or ends with \p suffix (e.g. tokens.txt). Case-insensitive. */
|
|
29
|
+
std::string FindFileEndingWith(const std::vector<FileEntry>& files, const std::string& suffix);
|
|
30
30
|
|
|
31
31
|
std::string FindOnnxByToken(
|
|
32
32
|
const std::vector<FileEntry>& files,
|
|
@@ -40,6 +40,14 @@ std::string FindOnnxByAnyToken(
|
|
|
40
40
|
const std::optional<bool>& preferInt8
|
|
41
41
|
);
|
|
42
42
|
|
|
43
|
+
/** Like FindOnnxByAnyToken but skips any file whose nameLower contains any of \p excludeInName. */
|
|
44
|
+
std::string FindOnnxByAnyTokenExcluding(
|
|
45
|
+
const std::vector<FileEntry>& files,
|
|
46
|
+
const std::vector<std::string>& tokens,
|
|
47
|
+
const std::vector<std::string>& excludeInName,
|
|
48
|
+
const std::optional<bool>& preferInt8
|
|
49
|
+
);
|
|
50
|
+
|
|
43
51
|
std::string FindLargestOnnx(
|
|
44
52
|
const std::vector<FileEntry>& files
|
|
45
53
|
);
|