react-native-sherpa-onnx 0.3.7 → 0.3.9
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 +7 -2
- package/SherpaOnnx.podspec +4 -1
- package/android/prebuilt-download.gradle +23 -23
- package/android/src/main/assets/model_licenses/asr-models-license-status.csv +1 -0
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-model-detect-helper.cpp +23 -0
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-model-detect-helper.h +9 -0
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-model-detect-stt.cpp +51 -8
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-model-detect-tts.cpp +31 -4
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-model-detect.h +19 -1
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-stt-wrapper.cpp +5 -0
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-tts-wrapper.cpp +7 -0
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-validate-stt.cpp +11 -0
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-validate-tts.cpp +14 -0
- package/android/src/main/java/com/sherpaonnx/SherpaOnnxArchiveHelper.kt +110 -35
- package/android/src/main/java/com/sherpaonnx/SherpaOnnxExtractionNotificationHelper.kt +102 -0
- package/android/src/main/java/com/sherpaonnx/SherpaOnnxModule.kt +92 -18
- package/android/src/main/java/com/sherpaonnx/SherpaOnnxSttHelper.kt +22 -0
- package/android/src/main/java/com/sherpaonnx/SherpaOnnxTtsHelper.kt +15 -0
- package/ios/Resources/model_licenses/asr-models-license-status.csv +1 -0
- package/ios/SherpaOnnx+STT.mm +13 -1
- package/ios/SherpaOnnx+TTS.mm +1 -0
- package/ios/SherpaOnnx.mm +87 -17
- package/ios/model_detect/sherpa-onnx-model-detect-helper.h +5 -0
- package/ios/model_detect/sherpa-onnx-model-detect-helper.mm +23 -0
- package/ios/model_detect/sherpa-onnx-model-detect-stt.mm +51 -7
- package/ios/model_detect/sherpa-onnx-model-detect-tts.mm +36 -4
- package/ios/model_detect/sherpa-onnx-model-detect.h +19 -1
- package/ios/model_detect/sherpa-onnx-validate-stt.mm +11 -0
- package/ios/model_detect/sherpa-onnx-validate-tts.mm +14 -0
- package/ios/stt/sherpa-onnx-stt-wrapper.h +11 -1
- package/ios/stt/sherpa-onnx-stt-wrapper.mm +30 -2
- package/ios/tts/sherpa-onnx-tts-wrapper.mm +25 -0
- package/lib/module/NativeSherpaOnnx.js.map +1 -1
- package/lib/module/download/ModelDownloadManager.js +1 -1
- package/lib/module/download/ModelDownloadManager.js.map +1 -1
- package/lib/module/download/background-downloader-types.js +2 -0
- package/lib/module/download/background-downloader-types.js.map +1 -0
- package/lib/module/download/downloadTask.js +54 -1
- package/lib/module/download/downloadTask.js.map +1 -1
- package/lib/module/download/index.js +1 -1
- package/lib/module/download/index.js.map +1 -1
- package/lib/module/download/postDownloadProcessing.js +17 -4
- package/lib/module/download/postDownloadProcessing.js.map +1 -1
- package/lib/module/download/registry.js +1 -0
- package/lib/module/download/registry.js.map +1 -1
- package/lib/module/extraction/extractTarBz2.js +2 -2
- package/lib/module/extraction/extractTarBz2.js.map +1 -1
- package/lib/module/extraction/extractTarZst.js +2 -2
- package/lib/module/extraction/extractTarZst.js.map +1 -1
- package/lib/module/extraction/index.js +10 -5
- package/lib/module/extraction/index.js.map +1 -1
- package/lib/module/stt/index.js +4 -2
- package/lib/module/stt/index.js.map +1 -1
- package/lib/module/stt/streaming.js +2 -1
- package/lib/module/stt/streaming.js.map +1 -1
- package/lib/module/stt/types.js +3 -1
- package/lib/module/stt/types.js.map +1 -1
- package/lib/module/tts/index.js +5 -3
- package/lib/module/tts/index.js.map +1 -1
- package/lib/module/tts/streaming.js +4 -2
- package/lib/module/tts/streaming.js.map +1 -1
- package/lib/module/tts/types.js +4 -1
- package/lib/module/tts/types.js.map +1 -1
- package/lib/typescript/src/NativeSherpaOnnx.d.ts +26 -10
- package/lib/typescript/src/NativeSherpaOnnx.d.ts.map +1 -1
- package/lib/typescript/src/download/ModelDownloadManager.d.ts +2 -1
- package/lib/typescript/src/download/ModelDownloadManager.d.ts.map +1 -1
- package/lib/typescript/src/download/background-downloader-types.d.ts +64 -0
- package/lib/typescript/src/download/background-downloader-types.d.ts.map +1 -0
- package/lib/typescript/src/download/downloadTask.d.ts +10 -0
- package/lib/typescript/src/download/downloadTask.d.ts.map +1 -1
- package/lib/typescript/src/download/index.d.ts +2 -2
- package/lib/typescript/src/download/index.d.ts.map +1 -1
- package/lib/typescript/src/download/postDownloadProcessing.d.ts +9 -0
- package/lib/typescript/src/download/postDownloadProcessing.d.ts.map +1 -1
- package/lib/typescript/src/download/registry.d.ts.map +1 -1
- package/lib/typescript/src/extraction/extractTarBz2.d.ts +2 -1
- package/lib/typescript/src/extraction/extractTarBz2.d.ts.map +1 -1
- package/lib/typescript/src/extraction/extractTarZst.d.ts +2 -1
- package/lib/typescript/src/extraction/extractTarZst.d.ts.map +1 -1
- package/lib/typescript/src/extraction/index.d.ts +1 -1
- package/lib/typescript/src/extraction/index.d.ts.map +1 -1
- package/lib/typescript/src/extraction/types.d.ts +12 -0
- package/lib/typescript/src/extraction/types.d.ts.map +1 -1
- package/lib/typescript/src/stt/index.d.ts +1 -1
- package/lib/typescript/src/stt/index.d.ts.map +1 -1
- package/lib/typescript/src/stt/streaming.d.ts.map +1 -1
- package/lib/typescript/src/stt/types.d.ts +16 -1
- package/lib/typescript/src/stt/types.d.ts.map +1 -1
- package/lib/typescript/src/tts/index.d.ts +1 -1
- package/lib/typescript/src/tts/index.d.ts.map +1 -1
- package/lib/typescript/src/tts/streaming.d.ts.map +1 -1
- package/lib/typescript/src/tts/types.d.ts +6 -1
- package/lib/typescript/src/tts/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/scripts/ci/update_model_license_csv.sh +16 -16
- package/src/NativeSherpaOnnx.ts +38 -11
- package/src/download/ModelDownloadManager.ts +2 -0
- package/src/download/background-downloader-types.ts +73 -0
- package/src/download/downloadTask.ts +68 -0
- package/src/download/index.ts +2 -0
- package/src/download/postDownloadProcessing.ts +24 -1
- package/src/download/registry.ts +1 -0
- package/src/extraction/extractTarBz2.ts +7 -2
- package/src/extraction/extractTarZst.ts +7 -2
- package/src/extraction/index.ts +29 -6
- package/src/extraction/types.ts +16 -0
- package/src/stt/index.ts +8 -7
- package/src/stt/streaming.ts +7 -1
- package/src/stt/types.ts +18 -0
- package/src/tts/index.ts +10 -7
- package/src/tts/streaming.ts +8 -3
- package/src/tts/types.ts +9 -0
- package/third_party/sherpa-onnx-prebuilt/ANDROID_RELEASE_TAG +1 -1
- package/third_party/sherpa-onnx-prebuilt/IOS_RELEASE_TAG +1 -1
- package/lib/module/download/background-downloader.d.js +0 -2
- package/lib/module/download/background-downloader.d.js.map +0 -1
- package/src/download/background-downloader.d.ts +0 -43
|
@@ -20,6 +20,7 @@ const char* TtsModelKindToString(TtsModelKind k) {
|
|
|
20
20
|
case TtsModelKind::kKitten: return "kitten";
|
|
21
21
|
case TtsModelKind::kPocket: return "pocket";
|
|
22
22
|
case TtsModelKind::kZipvoice: return "zipvoice";
|
|
23
|
+
case TtsModelKind::kSupertonic: return "supertonic";
|
|
23
24
|
default: return "unknown";
|
|
24
25
|
}
|
|
25
26
|
}
|
|
@@ -78,6 +79,12 @@ jobject TtsDetectResultToJava(JNIEnv* env, const TtsDetectResult& result) {
|
|
|
78
79
|
PutString(env, pathsMap, mapPut, "textConditioner", result.paths.textConditioner);
|
|
79
80
|
PutString(env, pathsMap, mapPut, "vocabJson", result.paths.vocabJson);
|
|
80
81
|
PutString(env, pathsMap, mapPut, "tokenScoresJson", result.paths.tokenScoresJson);
|
|
82
|
+
PutString(env, pathsMap, mapPut, "durationPredictor", result.paths.durationPredictor);
|
|
83
|
+
PutString(env, pathsMap, mapPut, "textEncoder", result.paths.textEncoder);
|
|
84
|
+
PutString(env, pathsMap, mapPut, "vectorEstimator", result.paths.vectorEstimator);
|
|
85
|
+
PutString(env, pathsMap, mapPut, "ttsJson", result.paths.ttsJson);
|
|
86
|
+
PutString(env, pathsMap, mapPut, "unicodeIndexer", result.paths.unicodeIndexer);
|
|
87
|
+
PutString(env, pathsMap, mapPut, "voiceStyle", result.paths.voiceStyle);
|
|
81
88
|
jstring keyPaths = env->NewStringUTF("paths");
|
|
82
89
|
env->CallObjectMethod(map, mapPut, keyPaths, pathsMap);
|
|
83
90
|
env->DeleteLocalRef(keyPaths);
|
|
@@ -52,6 +52,13 @@ static const SttFieldRequirement kFunAsrNanoReqs[] = {
|
|
|
52
52
|
{"funasrTokenizer", &SttModelPaths::funasrTokenizer, true},
|
|
53
53
|
};
|
|
54
54
|
|
|
55
|
+
static const SttFieldRequirement kQwen3AsrReqs[] = {
|
|
56
|
+
{"qwen3ConvFrontend", &SttModelPaths::qwen3ConvFrontend, true},
|
|
57
|
+
{"qwen3Encoder", &SttModelPaths::qwen3Encoder, true},
|
|
58
|
+
{"qwen3Decoder", &SttModelPaths::qwen3Decoder, true},
|
|
59
|
+
{"qwen3Tokenizer", &SttModelPaths::qwen3Tokenizer, true},
|
|
60
|
+
};
|
|
61
|
+
|
|
55
62
|
static const SttFieldRequirement kMoonshineReqs[] = {
|
|
56
63
|
{"moonshinePreprocessor", &SttModelPaths::moonshinePreprocessor, true},
|
|
57
64
|
{"moonshineEncoder", &SttModelPaths::moonshineEncoder, true},
|
|
@@ -120,6 +127,9 @@ static const SttFieldRequirement* GetRequirements(SttModelKind kind, size_t& cou
|
|
|
120
127
|
case SttModelKind::kFunAsrNano:
|
|
121
128
|
count = std::size(kFunAsrNanoReqs);
|
|
122
129
|
return kFunAsrNanoReqs;
|
|
130
|
+
case SttModelKind::kQwen3Asr:
|
|
131
|
+
count = std::size(kQwen3AsrReqs);
|
|
132
|
+
return kQwen3AsrReqs;
|
|
123
133
|
case SttModelKind::kMoonshine:
|
|
124
134
|
count = std::size(kMoonshineReqs);
|
|
125
135
|
return kMoonshineReqs;
|
|
@@ -161,6 +171,7 @@ static const char* SttKindToName(SttModelKind k) {
|
|
|
161
171
|
case SttModelKind::kZipformerCtc: return "Zipformer CTC";
|
|
162
172
|
case SttModelKind::kWhisper: return "Whisper";
|
|
163
173
|
case SttModelKind::kFunAsrNano: return "FunASR Nano";
|
|
174
|
+
case SttModelKind::kQwen3Asr: return "Qwen3 ASR";
|
|
164
175
|
case SttModelKind::kFireRedAsr: return "Fire Red ASR";
|
|
165
176
|
case SttModelKind::kMoonshine: return "Moonshine";
|
|
166
177
|
case SttModelKind::kMoonshineV2: return "Moonshine v2";
|
|
@@ -59,6 +59,16 @@ static const TtsFieldRequirement kZipvoiceReqs[] = {
|
|
|
59
59
|
{"lexicon", &TtsModelPaths::lexicon, true},
|
|
60
60
|
};
|
|
61
61
|
|
|
62
|
+
static const TtsFieldRequirement kSupertonicReqs[] = {
|
|
63
|
+
{"durationPredictor", &TtsModelPaths::durationPredictor, true},
|
|
64
|
+
{"textEncoder", &TtsModelPaths::textEncoder, true},
|
|
65
|
+
{"vectorEstimator", &TtsModelPaths::vectorEstimator, true},
|
|
66
|
+
{"vocoder", &TtsModelPaths::vocoder, true},
|
|
67
|
+
{"ttsJson", &TtsModelPaths::ttsJson, true},
|
|
68
|
+
{"unicodeIndexer", &TtsModelPaths::unicodeIndexer, true},
|
|
69
|
+
{"voiceStyle", &TtsModelPaths::voiceStyle, true},
|
|
70
|
+
};
|
|
71
|
+
|
|
62
72
|
// ============================================================
|
|
63
73
|
|
|
64
74
|
static const TtsFieldRequirement* GetRequirements(TtsModelKind kind, size_t& count) {
|
|
@@ -79,6 +89,9 @@ static const TtsFieldRequirement* GetRequirements(TtsModelKind kind, size_t& cou
|
|
|
79
89
|
case TtsModelKind::kZipvoice:
|
|
80
90
|
count = std::size(kZipvoiceReqs);
|
|
81
91
|
return kZipvoiceReqs;
|
|
92
|
+
case TtsModelKind::kSupertonic:
|
|
93
|
+
count = std::size(kSupertonicReqs);
|
|
94
|
+
return kSupertonicReqs;
|
|
82
95
|
default:
|
|
83
96
|
count = 0;
|
|
84
97
|
return nullptr;
|
|
@@ -93,6 +106,7 @@ static const char* TtsKindToName(TtsModelKind k) {
|
|
|
93
106
|
case TtsModelKind::kKitten: return "Kitten";
|
|
94
107
|
case TtsModelKind::kPocket: return "Pocket";
|
|
95
108
|
case TtsModelKind::kZipvoice: return "Zipvoice";
|
|
109
|
+
case TtsModelKind::kSupertonic: return "Supertonic";
|
|
96
110
|
default: return "Unknown";
|
|
97
111
|
}
|
|
98
112
|
}
|
|
@@ -54,7 +54,8 @@ class SherpaOnnxArchiveHelper {
|
|
|
54
54
|
targetPath: String,
|
|
55
55
|
force: Boolean,
|
|
56
56
|
promise: Promise,
|
|
57
|
-
onProgress: (bytes: Long, totalBytes: Long, percent: Double) -> Unit
|
|
57
|
+
onProgress: (bytes: Long, totalBytes: Long, percent: Double) -> Unit,
|
|
58
|
+
extractionNotification: SherpaOnnxExtractionNotificationHelper? = null,
|
|
58
59
|
) {
|
|
59
60
|
val promiseSettled = AtomicBoolean(false)
|
|
60
61
|
fun resolveOnce(success: Boolean, reason: String? = null) {
|
|
@@ -70,26 +71,28 @@ class SherpaOnnxArchiveHelper {
|
|
|
70
71
|
val cancelFlag = AtomicBoolean(false)
|
|
71
72
|
cancelFlags[sourcePath] = cancelFlag
|
|
72
73
|
|
|
73
|
-
// Create a progress callback object that JNI can call
|
|
74
|
-
val progressCallback = object : Any() {
|
|
75
|
-
fun invoke(bytesExtracted: Long, totalBytes: Long, percent: Double) {
|
|
76
|
-
onProgress(bytesExtracted, totalBytes, percent)
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
74
|
// Run extraction on a background thread so the React Native bridge thread is not blocked.
|
|
81
75
|
// The thread pool allows multiple extractions in parallel.
|
|
82
76
|
extractExecutor.execute {
|
|
77
|
+
val notif = extractionNotification
|
|
83
78
|
try {
|
|
84
79
|
// Check per-path cancel flag before starting the native extraction.
|
|
85
80
|
if (cancelFlag.get()) {
|
|
86
81
|
resolveOnce(false, "Cancelled")
|
|
87
82
|
return@execute
|
|
88
83
|
}
|
|
89
|
-
|
|
84
|
+
notif?.start()
|
|
85
|
+
val wrappedCallback = object : Any() {
|
|
86
|
+
fun invoke(bytesExtracted: Long, totalBytes: Long, percent: Double) {
|
|
87
|
+
onProgress(bytesExtracted, totalBytes, percent)
|
|
88
|
+
notif?.updateProgress(percent)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
nativeExtractTarBz2(sourcePath, targetPath, force, wrappedCallback, promise)
|
|
90
92
|
} catch (e: Exception) {
|
|
91
93
|
resolveOnce(false, "Archive extraction error: ${e.message}")
|
|
92
94
|
} finally {
|
|
95
|
+
notif?.finish()
|
|
93
96
|
cancelFlags.remove(sourcePath)
|
|
94
97
|
}
|
|
95
98
|
}
|
|
@@ -104,7 +107,8 @@ class SherpaOnnxArchiveHelper {
|
|
|
104
107
|
targetPath: String,
|
|
105
108
|
force: Boolean,
|
|
106
109
|
promise: Promise,
|
|
107
|
-
onProgress: (bytes: Long, totalBytes: Long, percent: Double) -> Unit
|
|
110
|
+
onProgress: (bytes: Long, totalBytes: Long, percent: Double) -> Unit,
|
|
111
|
+
extractionNotification: SherpaOnnxExtractionNotificationHelper? = null,
|
|
108
112
|
) {
|
|
109
113
|
val promiseSettled = AtomicBoolean(false)
|
|
110
114
|
fun resolveOnce(success: Boolean, reason: String? = null) {
|
|
@@ -119,22 +123,26 @@ class SherpaOnnxArchiveHelper {
|
|
|
119
123
|
val cancelFlag = AtomicBoolean(false)
|
|
120
124
|
cancelFlags[sourcePath] = cancelFlag
|
|
121
125
|
|
|
122
|
-
val progressCallback = object : Any() {
|
|
123
|
-
fun invoke(bytesExtracted: Long, totalBytes: Long, percent: Double) {
|
|
124
|
-
onProgress(bytesExtracted, totalBytes, percent)
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
126
|
extractExecutor.execute {
|
|
127
|
+
val notif = extractionNotification
|
|
128
128
|
try {
|
|
129
129
|
// Check per-path cancel flag before starting the native extraction.
|
|
130
130
|
if (cancelFlag.get()) {
|
|
131
131
|
resolveOnce(false, "Cancelled")
|
|
132
132
|
return@execute
|
|
133
133
|
}
|
|
134
|
-
|
|
134
|
+
notif?.start()
|
|
135
|
+
val wrappedCallback = object : Any() {
|
|
136
|
+
fun invoke(bytesExtracted: Long, totalBytes: Long, percent: Double) {
|
|
137
|
+
onProgress(bytesExtracted, totalBytes, percent)
|
|
138
|
+
notif?.updateProgress(percent)
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
nativeExtractTarZst(sourcePath, targetPath, force, wrappedCallback, promise)
|
|
135
142
|
} catch (e: Exception) {
|
|
136
143
|
resolveOnce(false, "Archive extraction error: ${e.message}")
|
|
137
144
|
} finally {
|
|
145
|
+
notif?.finish()
|
|
138
146
|
cancelFlags.remove(sourcePath)
|
|
139
147
|
}
|
|
140
148
|
}
|
|
@@ -144,47 +152,106 @@ class SherpaOnnxArchiveHelper {
|
|
|
144
152
|
}
|
|
145
153
|
}
|
|
146
154
|
|
|
155
|
+
/**
|
|
156
|
+
* Which JNI stream entry to use for APK asset extraction.
|
|
157
|
+
*
|
|
158
|
+
* Both paths invoke libarchive’s `ExtractFromStream`, which **auto-detects** compression
|
|
159
|
+
* (`.tar.zst` vs `.tar.bz2`, etc.); `nativeExtractTarBz2FromStream` forwards to the same
|
|
160
|
+
* native implementation as zst. Keeping distinct JNI symbols preserves a clear API and avoids
|
|
161
|
+
* the impression that bz2 assets are mistakenly wired only to a “zst” method.
|
|
162
|
+
*/
|
|
163
|
+
private enum class AssetTarStreamKind {
|
|
164
|
+
ZST,
|
|
165
|
+
BZ2,
|
|
166
|
+
}
|
|
167
|
+
|
|
147
168
|
fun extractTarZstFromAsset(
|
|
148
169
|
context: Context,
|
|
149
170
|
assetPath: String,
|
|
150
171
|
targetPath: String,
|
|
151
172
|
force: Boolean,
|
|
152
173
|
promise: Promise,
|
|
153
|
-
onProgress: (bytes: Long, totalBytes: Long, percent: Double) -> Unit
|
|
174
|
+
onProgress: (bytes: Long, totalBytes: Long, percent: Double) -> Unit,
|
|
175
|
+
extractionNotification: SherpaOnnxExtractionNotificationHelper? = null,
|
|
176
|
+
) {
|
|
177
|
+
extractTarArchiveFromAsset(
|
|
178
|
+
context,
|
|
179
|
+
assetPath,
|
|
180
|
+
targetPath,
|
|
181
|
+
force,
|
|
182
|
+
promise,
|
|
183
|
+
onProgress,
|
|
184
|
+
extractionNotification,
|
|
185
|
+
AssetTarStreamKind.ZST,
|
|
186
|
+
)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
fun extractTarBz2FromAsset(
|
|
190
|
+
context: Context,
|
|
191
|
+
assetPath: String,
|
|
192
|
+
targetPath: String,
|
|
193
|
+
force: Boolean,
|
|
194
|
+
promise: Promise,
|
|
195
|
+
onProgress: (bytes: Long, totalBytes: Long, percent: Double) -> Unit,
|
|
196
|
+
extractionNotification: SherpaOnnxExtractionNotificationHelper? = null,
|
|
197
|
+
) {
|
|
198
|
+
extractTarArchiveFromAsset(
|
|
199
|
+
context,
|
|
200
|
+
assetPath,
|
|
201
|
+
targetPath,
|
|
202
|
+
force,
|
|
203
|
+
promise,
|
|
204
|
+
onProgress,
|
|
205
|
+
extractionNotification,
|
|
206
|
+
AssetTarStreamKind.BZ2,
|
|
207
|
+
)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
private fun extractTarArchiveFromAsset(
|
|
211
|
+
context: Context,
|
|
212
|
+
assetPath: String,
|
|
213
|
+
targetPath: String,
|
|
214
|
+
force: Boolean,
|
|
215
|
+
promise: Promise,
|
|
216
|
+
onProgress: (bytes: Long, totalBytes: Long, percent: Double) -> Unit,
|
|
217
|
+
extractionNotification: SherpaOnnxExtractionNotificationHelper? = null,
|
|
218
|
+
kind: AssetTarStreamKind,
|
|
154
219
|
) {
|
|
155
220
|
if (BuildConfig.DEBUG) {
|
|
156
|
-
Log.i(
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
onProgress(bytesExtracted, totalBytes, percent)
|
|
161
|
-
}
|
|
221
|
+
Log.i(
|
|
222
|
+
"SherpaOnnx",
|
|
223
|
+
"extractTar${if (kind == AssetTarStreamKind.ZST) "Zst" else "Bz2"}FromAsset assetPath=$assetPath targetPath=$targetPath",
|
|
224
|
+
)
|
|
162
225
|
}
|
|
163
226
|
extractExecutor.execute {
|
|
227
|
+
val notif = extractionNotification
|
|
164
228
|
try {
|
|
229
|
+
notif?.start()
|
|
230
|
+
val progressCallback = object : Any() {
|
|
231
|
+
fun invoke(bytesExtracted: Long, totalBytes: Long, percent: Double) {
|
|
232
|
+
onProgress(bytesExtracted, totalBytes, percent)
|
|
233
|
+
notif?.updateProgress(percent)
|
|
234
|
+
}
|
|
235
|
+
}
|
|
165
236
|
context.assets.open(assetPath).use { stream ->
|
|
166
|
-
|
|
237
|
+
when (kind) {
|
|
238
|
+
AssetTarStreamKind.ZST ->
|
|
239
|
+
nativeExtractTarZstFromStream(stream, targetPath, force, progressCallback, promise)
|
|
240
|
+
AssetTarStreamKind.BZ2 ->
|
|
241
|
+
nativeExtractTarBz2FromStream(stream, targetPath, force, progressCallback, promise)
|
|
242
|
+
}
|
|
167
243
|
}
|
|
168
244
|
} catch (e: Exception) {
|
|
169
245
|
val result = Arguments.createMap()
|
|
170
246
|
result.putBoolean("success", false)
|
|
171
247
|
result.putString("reason", e.message ?: "Failed to open asset")
|
|
172
248
|
promise.resolve(result)
|
|
249
|
+
} finally {
|
|
250
|
+
notif?.finish()
|
|
173
251
|
}
|
|
174
252
|
}
|
|
175
253
|
}
|
|
176
254
|
|
|
177
|
-
fun extractTarBz2FromAsset(
|
|
178
|
-
context: Context,
|
|
179
|
-
assetPath: String,
|
|
180
|
-
targetPath: String,
|
|
181
|
-
force: Boolean,
|
|
182
|
-
promise: Promise,
|
|
183
|
-
onProgress: (bytes: Long, totalBytes: Long, percent: Double) -> Unit
|
|
184
|
-
) {
|
|
185
|
-
extractTarZstFromAsset(context, assetPath, targetPath, force, promise, onProgress)
|
|
186
|
-
}
|
|
187
|
-
|
|
188
255
|
fun computeFileSha256(filePath: String, promise: Promise) {
|
|
189
256
|
nativeComputeFileSha256(filePath, promise)
|
|
190
257
|
}
|
|
@@ -214,6 +281,14 @@ class SherpaOnnxArchiveHelper {
|
|
|
214
281
|
promise: Promise
|
|
215
282
|
)
|
|
216
283
|
|
|
284
|
+
private external fun nativeExtractTarBz2FromStream(
|
|
285
|
+
inputStream: java.io.InputStream,
|
|
286
|
+
targetPath: String,
|
|
287
|
+
force: Boolean,
|
|
288
|
+
progressCallback: Any?,
|
|
289
|
+
promise: Promise
|
|
290
|
+
)
|
|
291
|
+
|
|
217
292
|
private external fun nativeCancelExtract()
|
|
218
293
|
|
|
219
294
|
private external fun nativeComputeFileSha256(
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
package com.sherpaonnx
|
|
2
|
+
|
|
3
|
+
import android.app.NotificationChannel
|
|
4
|
+
import android.app.NotificationManager
|
|
5
|
+
import android.content.Context
|
|
6
|
+
import android.os.Build
|
|
7
|
+
import android.util.Log
|
|
8
|
+
import androidx.core.app.NotificationCompat
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Android-only progress notification for archive extraction (mirrors background download visibility).
|
|
12
|
+
* Safe no-ops if posting fails (e.g. POST_NOTIFICATIONS denied).
|
|
13
|
+
*/
|
|
14
|
+
class SherpaOnnxExtractionNotificationHelper private constructor(
|
|
15
|
+
private val context: Context,
|
|
16
|
+
private val notificationId: Int,
|
|
17
|
+
private val title: String,
|
|
18
|
+
private val baseText: String,
|
|
19
|
+
) {
|
|
20
|
+
private val nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
|
21
|
+
@Volatile private var lastBucket: Int = -1
|
|
22
|
+
|
|
23
|
+
companion object {
|
|
24
|
+
private const val TAG = "SherpaOnnxExtractNotif"
|
|
25
|
+
const val CHANNEL_ID = "sherpa_onnx_extraction"
|
|
26
|
+
private val nextNotificationId = java.util.concurrent.atomic.AtomicInteger(9_200_000)
|
|
27
|
+
|
|
28
|
+
private const val DEFAULT_TITLE = "Model extraction"
|
|
29
|
+
private const val DEFAULT_TEXT = "Extracting archive…"
|
|
30
|
+
|
|
31
|
+
fun maybeCreate(
|
|
32
|
+
context: Context,
|
|
33
|
+
showNotificationsEnabled: Boolean?,
|
|
34
|
+
titleOverride: String?,
|
|
35
|
+
textOverride: String?,
|
|
36
|
+
): SherpaOnnxExtractionNotificationHelper? {
|
|
37
|
+
if (showNotificationsEnabled == false) return null
|
|
38
|
+
val title = titleOverride?.trim()?.takeIf { it.isNotEmpty() } ?: DEFAULT_TITLE
|
|
39
|
+
val text = textOverride?.trim()?.takeIf { it.isNotEmpty() } ?: DEFAULT_TEXT
|
|
40
|
+
val id = nextNotificationId.getAndIncrement()
|
|
41
|
+
return SherpaOnnxExtractionNotificationHelper(context.applicationContext, id, title, text)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
fun ensureChannel(ctx: Context) {
|
|
45
|
+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
|
|
46
|
+
val nm = ctx.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
|
47
|
+
val existing = nm.getNotificationChannel(CHANNEL_ID)
|
|
48
|
+
if (existing != null) return
|
|
49
|
+
val ch = NotificationChannel(
|
|
50
|
+
CHANNEL_ID,
|
|
51
|
+
"Model extraction",
|
|
52
|
+
NotificationManager.IMPORTANCE_LOW,
|
|
53
|
+
).apply {
|
|
54
|
+
setShowBadge(false)
|
|
55
|
+
}
|
|
56
|
+
nm.createNotificationChannel(ch)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private fun buildProgress(percentInt: Int): NotificationCompat.Builder {
|
|
61
|
+
val p = percentInt.coerceIn(0, 100)
|
|
62
|
+
val line = "$baseText $p%"
|
|
63
|
+
return NotificationCompat.Builder(context, CHANNEL_ID)
|
|
64
|
+
.setSmallIcon(android.R.drawable.stat_sys_download)
|
|
65
|
+
.setContentTitle(title)
|
|
66
|
+
.setContentText(line)
|
|
67
|
+
.setStyle(NotificationCompat.BigTextStyle().bigText(line))
|
|
68
|
+
.setOnlyAlertOnce(true)
|
|
69
|
+
.setPriority(NotificationCompat.PRIORITY_LOW)
|
|
70
|
+
.setOngoing(true)
|
|
71
|
+
.setProgress(100, p, false)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
fun start() {
|
|
75
|
+
try {
|
|
76
|
+
ensureChannel(context)
|
|
77
|
+
nm.notify(notificationId, buildProgress(0).build())
|
|
78
|
+
} catch (e: Exception) {
|
|
79
|
+
Log.w(TAG, "start: ${e.message}")
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
fun updateProgress(percent: Double) {
|
|
84
|
+
val p = percent.toInt().coerceIn(0, 100)
|
|
85
|
+
val bucket = p / 4
|
|
86
|
+
if (bucket == lastBucket && p != 0 && p != 100) return
|
|
87
|
+
lastBucket = bucket
|
|
88
|
+
try {
|
|
89
|
+
nm.notify(notificationId, buildProgress(p).build())
|
|
90
|
+
} catch (e: Exception) {
|
|
91
|
+
Log.w(TAG, "updateProgress: ${e.message}")
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
fun finish() {
|
|
96
|
+
try {
|
|
97
|
+
nm.cancel(notificationId)
|
|
98
|
+
} catch (e: Exception) {
|
|
99
|
+
Log.w(TAG, "finish: ${e.message}")
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -293,10 +293,30 @@ class SherpaOnnxModule(reactContext: ReactApplicationContext) :
|
|
|
293
293
|
assetHelper.resolveModelPath(config, promise)
|
|
294
294
|
}
|
|
295
295
|
|
|
296
|
-
override fun extractTarBz2(
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
296
|
+
override fun extractTarBz2(
|
|
297
|
+
sourcePath: String,
|
|
298
|
+
targetPath: String,
|
|
299
|
+
force: Boolean,
|
|
300
|
+
showNotificationsEnabled: Boolean?,
|
|
301
|
+
notificationTitle: String?,
|
|
302
|
+
notificationText: String?,
|
|
303
|
+
promise: Promise,
|
|
304
|
+
) {
|
|
305
|
+
val notif = extractionNotificationOrNull(
|
|
306
|
+
showNotificationsEnabled,
|
|
307
|
+
notificationTitle,
|
|
308
|
+
notificationText,
|
|
309
|
+
)
|
|
310
|
+
archiveHelper.extractTarBz2(
|
|
311
|
+
sourcePath,
|
|
312
|
+
targetPath,
|
|
313
|
+
force,
|
|
314
|
+
promise,
|
|
315
|
+
{ bytes, total, percent ->
|
|
316
|
+
emitExtractProgress(sourcePath, bytes, total, percent)
|
|
317
|
+
},
|
|
318
|
+
notif,
|
|
319
|
+
)
|
|
300
320
|
}
|
|
301
321
|
|
|
302
322
|
override fun cancelExtractTarBz2(promise: Promise) {
|
|
@@ -304,10 +324,30 @@ class SherpaOnnxModule(reactContext: ReactApplicationContext) :
|
|
|
304
324
|
promise.resolve(null)
|
|
305
325
|
}
|
|
306
326
|
|
|
307
|
-
override fun extractTarZst(
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
327
|
+
override fun extractTarZst(
|
|
328
|
+
sourcePath: String,
|
|
329
|
+
targetPath: String,
|
|
330
|
+
force: Boolean,
|
|
331
|
+
showNotificationsEnabled: Boolean?,
|
|
332
|
+
notificationTitle: String?,
|
|
333
|
+
notificationText: String?,
|
|
334
|
+
promise: Promise,
|
|
335
|
+
) {
|
|
336
|
+
val notif = extractionNotificationOrNull(
|
|
337
|
+
showNotificationsEnabled,
|
|
338
|
+
notificationTitle,
|
|
339
|
+
notificationText,
|
|
340
|
+
)
|
|
341
|
+
archiveHelper.extractTarZst(
|
|
342
|
+
sourcePath,
|
|
343
|
+
targetPath,
|
|
344
|
+
force,
|
|
345
|
+
promise,
|
|
346
|
+
{ bytes, total, percent ->
|
|
347
|
+
emitExtractTarZstProgress(sourcePath, bytes, total, percent)
|
|
348
|
+
},
|
|
349
|
+
notif,
|
|
350
|
+
)
|
|
311
351
|
}
|
|
312
352
|
|
|
313
353
|
override fun cancelExtractTarZst(promise: Promise) {
|
|
@@ -346,6 +386,20 @@ class SherpaOnnxModule(reactContext: ReactApplicationContext) :
|
|
|
346
386
|
eventEmitter.emit("extractTarZstProgress", payload)
|
|
347
387
|
}
|
|
348
388
|
|
|
389
|
+
/** Null when extraction notifications are disabled (`showNotificationsEnabled == false`). */
|
|
390
|
+
private fun extractionNotificationOrNull(
|
|
391
|
+
showNotificationsEnabled: Boolean?,
|
|
392
|
+
notificationTitle: String?,
|
|
393
|
+
notificationText: String?,
|
|
394
|
+
): SherpaOnnxExtractionNotificationHelper? {
|
|
395
|
+
return SherpaOnnxExtractionNotificationHelper.maybeCreate(
|
|
396
|
+
reactApplicationContext,
|
|
397
|
+
showNotificationsEnabled,
|
|
398
|
+
notificationTitle,
|
|
399
|
+
notificationText,
|
|
400
|
+
)
|
|
401
|
+
}
|
|
402
|
+
|
|
349
403
|
/**
|
|
350
404
|
* Resolve asset path - copy from assets to internal storage if needed
|
|
351
405
|
* Preserves the directory structure from assets (e.g., test_wavs/ stays as test_wavs/)
|
|
@@ -1101,34 +1155,54 @@ class SherpaOnnxModule(reactContext: ReactApplicationContext) :
|
|
|
1101
1155
|
assetPath: String,
|
|
1102
1156
|
targetPath: String,
|
|
1103
1157
|
force: Boolean,
|
|
1104
|
-
|
|
1158
|
+
showNotificationsEnabled: Boolean?,
|
|
1159
|
+
notificationTitle: String?,
|
|
1160
|
+
notificationText: String?,
|
|
1161
|
+
promise: Promise,
|
|
1105
1162
|
) {
|
|
1163
|
+
val notif = extractionNotificationOrNull(
|
|
1164
|
+
showNotificationsEnabled,
|
|
1165
|
+
notificationTitle,
|
|
1166
|
+
notificationText,
|
|
1167
|
+
)
|
|
1106
1168
|
archiveHelper.extractTarZstFromAsset(
|
|
1107
1169
|
reactApplicationContext,
|
|
1108
1170
|
assetPath,
|
|
1109
1171
|
targetPath,
|
|
1110
1172
|
force,
|
|
1111
|
-
promise
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1173
|
+
promise,
|
|
1174
|
+
{ bytes, total, percent ->
|
|
1175
|
+
emitExtractTarZstProgress(assetPath, bytes, total, percent)
|
|
1176
|
+
},
|
|
1177
|
+
notif,
|
|
1178
|
+
)
|
|
1115
1179
|
}
|
|
1116
1180
|
|
|
1117
1181
|
override fun extractTarBz2FromAsset(
|
|
1118
1182
|
assetPath: String,
|
|
1119
1183
|
targetPath: String,
|
|
1120
1184
|
force: Boolean,
|
|
1121
|
-
|
|
1185
|
+
showNotificationsEnabled: Boolean?,
|
|
1186
|
+
notificationTitle: String?,
|
|
1187
|
+
notificationText: String?,
|
|
1188
|
+
promise: Promise,
|
|
1122
1189
|
) {
|
|
1190
|
+
val notif = extractionNotificationOrNull(
|
|
1191
|
+
showNotificationsEnabled,
|
|
1192
|
+
notificationTitle,
|
|
1193
|
+
notificationText,
|
|
1194
|
+
)
|
|
1123
1195
|
archiveHelper.extractTarBz2FromAsset(
|
|
1124
1196
|
reactApplicationContext,
|
|
1125
1197
|
assetPath,
|
|
1126
1198
|
targetPath,
|
|
1127
1199
|
force,
|
|
1128
|
-
promise
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1200
|
+
promise,
|
|
1201
|
+
{ bytes, total, percent ->
|
|
1202
|
+
emitExtractProgress(assetPath, bytes, total, percent)
|
|
1203
|
+
},
|
|
1204
|
+
notif,
|
|
1205
|
+
)
|
|
1132
1206
|
}
|
|
1133
1207
|
|
|
1134
1208
|
override fun readAssetFileAsUtf8(assetPath: String, promise: Promise) {
|
|
@@ -22,6 +22,7 @@ import com.k2fsa.sherpa.onnx.OfflineSenseVoiceModelConfig
|
|
|
22
22
|
import com.k2fsa.sherpa.onnx.OfflineZipformerCtcModelConfig
|
|
23
23
|
import com.k2fsa.sherpa.onnx.OfflineWenetCtcModelConfig
|
|
24
24
|
import com.k2fsa.sherpa.onnx.OfflineFunAsrNanoModelConfig
|
|
25
|
+
import com.k2fsa.sherpa.onnx.OfflineQwen3AsrModelConfig
|
|
25
26
|
import com.k2fsa.sherpa.onnx.OfflineMoonshineModelConfig
|
|
26
27
|
import com.k2fsa.sherpa.onnx.OfflineDolphinModelConfig
|
|
27
28
|
import com.k2fsa.sherpa.onnx.OfflineFireRedAsrModelConfig
|
|
@@ -541,6 +542,10 @@ internal class SherpaOnnxSttHelper(
|
|
|
541
542
|
val hasHotwords = fn.hasKey("hotwords") && fn.getString("hotwords")?.isNotBlank() == true
|
|
542
543
|
parts.add("funasrNano:lang=$lang,hotwords=$hasHotwords")
|
|
543
544
|
}
|
|
545
|
+
modelOptions.getMap("qwen3Asr")?.let { q ->
|
|
546
|
+
val mnt = if (q.hasKey("maxNewTokens")) q.getInt("maxNewTokens") else null
|
|
547
|
+
parts.add("qwen3Asr:maxNewTokens=$mnt")
|
|
548
|
+
}
|
|
544
549
|
return parts.joinToString(";").take(200)
|
|
545
550
|
}
|
|
546
551
|
|
|
@@ -699,6 +704,23 @@ internal class SherpaOnnxSttHelper(
|
|
|
699
704
|
tokens = ""
|
|
700
705
|
)
|
|
701
706
|
}
|
|
707
|
+
"qwen3_asr" -> {
|
|
708
|
+
val q3 = modelOptions?.getMap("qwen3Asr")
|
|
709
|
+
OfflineModelConfig(
|
|
710
|
+
qwen3Asr = OfflineQwen3AsrModelConfig(
|
|
711
|
+
convFrontend = path(paths, "qwen3ConvFrontend"),
|
|
712
|
+
encoder = path(paths, "qwen3Encoder"),
|
|
713
|
+
decoder = path(paths, "qwen3Decoder"),
|
|
714
|
+
tokenizer = path(paths, "qwen3Tokenizer"),
|
|
715
|
+
maxTotalLen = if (q3?.hasKey("maxTotalLen") == true) q3.getInt("maxTotalLen") else 512,
|
|
716
|
+
maxNewTokens = if (q3?.hasKey("maxNewTokens") == true) q3.getInt("maxNewTokens") else 128,
|
|
717
|
+
temperature = if (q3?.hasKey("temperature") == true) q3.getDouble("temperature").toFloat() else 1e-6f,
|
|
718
|
+
topP = if (q3?.hasKey("topP") == true) q3.getDouble("topP").toFloat() else 0.8f,
|
|
719
|
+
seed = if (q3?.hasKey("seed") == true) q3.getInt("seed") else 42
|
|
720
|
+
),
|
|
721
|
+
tokens = ""
|
|
722
|
+
)
|
|
723
|
+
}
|
|
702
724
|
else -> {
|
|
703
725
|
val tokens = path(paths, "tokens")
|
|
704
726
|
when {
|
|
@@ -31,6 +31,7 @@ import com.k2fsa.sherpa.onnx.OfflineTtsMatchaModelConfig
|
|
|
31
31
|
import com.k2fsa.sherpa.onnx.OfflineTtsKokoroModelConfig
|
|
32
32
|
import com.k2fsa.sherpa.onnx.OfflineTtsKittenModelConfig
|
|
33
33
|
import com.k2fsa.sherpa.onnx.OfflineTtsZipVoiceModelConfig
|
|
34
|
+
import com.k2fsa.sherpa.onnx.OfflineTtsSupertonicModelConfig
|
|
34
35
|
import java.io.File
|
|
35
36
|
import java.io.FileInputStream
|
|
36
37
|
import java.io.FileOutputStream
|
|
@@ -1062,6 +1063,20 @@ internal class SherpaOnnxTtsHelper(
|
|
|
1062
1063
|
debug = debug,
|
|
1063
1064
|
provider = prov
|
|
1064
1065
|
)
|
|
1066
|
+
"supertonic" -> OfflineTtsModelConfig(
|
|
1067
|
+
supertonic = OfflineTtsSupertonicModelConfig(
|
|
1068
|
+
durationPredictor = path(paths, "durationPredictor"),
|
|
1069
|
+
textEncoder = path(paths, "textEncoder"),
|
|
1070
|
+
vectorEstimator = path(paths, "vectorEstimator"),
|
|
1071
|
+
vocoder = path(paths, "vocoder"),
|
|
1072
|
+
ttsJson = path(paths, "ttsJson"),
|
|
1073
|
+
unicodeIndexer = path(paths, "unicodeIndexer"),
|
|
1074
|
+
voiceStyle = path(paths, "voiceStyle")
|
|
1075
|
+
),
|
|
1076
|
+
numThreads = numThreads,
|
|
1077
|
+
debug = debug,
|
|
1078
|
+
provider = prov
|
|
1079
|
+
)
|
|
1065
1080
|
else -> {
|
|
1066
1081
|
if (path(paths, "acousticModel").isNotEmpty()) {
|
|
1067
1082
|
OfflineTtsModelConfig(
|
|
@@ -397,6 +397,7 @@ sherpa-onnx-rk3576-streaming-zipformer-en-2023-06-26.tar.bz2,apache-2.0,yes,high
|
|
|
397
397
|
sherpa-onnx-rk3568-streaming-zipformer-en-2023-06-26.tar.bz2,apache-2.0,yes,high,manual,https://huggingface.co/csukuangfj/sherpa-onnx-streaming-zipformer-en-2023-06-26
|
|
398
398
|
sherpa-onnx-rk3566-streaming-zipformer-en-2023-06-26.tar.bz2,apache-2.0,yes,high,manual,https://huggingface.co/csukuangfj/sherpa-onnx-streaming-zipformer-en-2023-06-26
|
|
399
399
|
sherpa-onnx-rk3562-streaming-zipformer-en-2023-06-26.tar.bz2,apache-2.0,yes,high,manual,https://huggingface.co/csukuangfj/sherpa-onnx-streaming-zipformer-en-2023-06-26
|
|
400
|
+
sherpa-onnx-qwen3-asr-0.6B-int8-2026-03-25.tar.bz2,apache-2.0,yes,high,manual,https://huggingface.co/Qwen/Qwen3-ASR-0.6B
|
|
400
401
|
sherpa-onnx-rk3588-streaming-zipformer-small-bilingual-zh-en-2023-02-16.tar.bz2,apache-2.0,yes,high,manual,https://huggingface.co/csukuangfj/k2fsa-zipformer-bilingual-zh-en-t
|
|
401
402
|
sherpa-onnx-rk3588-streaming-zipformer-bilingual-zh-en-2023-02-20.tar.bz2,apache-2.0,yes,high,manual,https://huggingface.co/csukuangfj/k2fsa-zipformer-bilingual-zh-en-t
|
|
402
403
|
sherpa-onnx-rk3576-streaming-zipformer-small-bilingual-zh-en-2023-02-16.tar.bz2,apache-2.0,yes,high,manual,https://huggingface.co/csukuangfj/k2fsa-zipformer-bilingual-zh-en-t
|