react-native-sherpa-onnx 0.3.8 → 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 +4 -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.h +10 -0
- 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-validate-stt.cpp +11 -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/ios/Resources/model_licenses/asr-models-license-status.csv +1 -0
- package/ios/SherpaOnnx+STT.mm +13 -1
- 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.h +10 -0
- package/ios/model_detect/sherpa-onnx-validate-stt.mm +11 -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 +16 -0
- package/lib/module/NativeSherpaOnnx.js.map +1 -1
- package/lib/module/download/postDownloadProcessing.js +17 -4
- package/lib/module/download/postDownloadProcessing.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 +4 -2
- package/lib/module/tts/index.js.map +1 -1
- package/lib/module/tts/streaming.js +3 -1
- package/lib/module/tts/streaming.js.map +1 -1
- package/lib/typescript/src/NativeSherpaOnnx.d.ts +25 -9
- package/lib/typescript/src/NativeSherpaOnnx.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/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.map +1 -1
- package/lib/typescript/src/tts/streaming.d.ts.map +1 -1
- package/package.json +1 -1
- package/scripts/ci/update_model_license_csv.sh +16 -16
- package/src/NativeSherpaOnnx.ts +37 -10
- package/src/download/postDownloadProcessing.ts +24 -1
- 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 +7 -7
- package/src/tts/streaming.ts +6 -3
- package/third_party/sherpa-onnx-prebuilt/ANDROID_RELEASE_TAG +1 -1
- package/third_party/sherpa-onnx-prebuilt/IOS_RELEASE_TAG +1 -1
|
@@ -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 {
|
|
@@ -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
|
package/ios/SherpaOnnx+STT.mm
CHANGED
|
@@ -36,6 +36,7 @@ static NSString *sttModelKindToNSString(sherpaonnx::SttModelKind kind) {
|
|
|
36
36
|
case K::kZipformerCtc: return @"zipformer_ctc";
|
|
37
37
|
case K::kWhisper: return @"whisper";
|
|
38
38
|
case K::kFunAsrNano: return @"funasr_nano";
|
|
39
|
+
case K::kQwen3Asr: return @"qwen3_asr";
|
|
39
40
|
case K::kFireRedAsr: return @"fire_red_asr";
|
|
40
41
|
case K::kMoonshine: return @"moonshine";
|
|
41
42
|
case K::kMoonshineV2: return @"moonshine_v2";
|
|
@@ -164,10 +165,12 @@ static NSDictionary *sttResultToDict(const sherpaonnx::SttRecognitionResult& r)
|
|
|
164
165
|
sherpaonnx::SttSenseVoiceOptions senseVoiceOpts;
|
|
165
166
|
sherpaonnx::SttCanaryOptions canaryOpts;
|
|
166
167
|
sherpaonnx::SttFunAsrNanoOptions funasrNanoOpts;
|
|
168
|
+
sherpaonnx::SttQwen3AsrOptions qwen3AsrOpts;
|
|
167
169
|
const sherpaonnx::SttWhisperOptions *whisperOptsPtr = nullptr;
|
|
168
170
|
const sherpaonnx::SttSenseVoiceOptions *senseVoiceOptsPtr = nullptr;
|
|
169
171
|
const sherpaonnx::SttCanaryOptions *canaryOptsPtr = nullptr;
|
|
170
172
|
const sherpaonnx::SttFunAsrNanoOptions *funasrNanoOptsPtr = nullptr;
|
|
173
|
+
const sherpaonnx::SttQwen3AsrOptions *qwen3AsrOptsPtr = nullptr;
|
|
171
174
|
if (modelOptions != nil && [modelOptions isKindOfClass:[NSDictionary class]]) {
|
|
172
175
|
NSDictionary *w = modelOptions[@"whisper"];
|
|
173
176
|
if ([w isKindOfClass:[NSDictionary class]]) {
|
|
@@ -202,12 +205,21 @@ static NSDictionary *sttResultToDict(const sherpaonnx::SttRecognitionResult& r)
|
|
|
202
205
|
if (fn[@"hotwords"] != nil) funasrNanoOpts.hotwords = std::string([(NSString *)fn[@"hotwords"] UTF8String]);
|
|
203
206
|
funasrNanoOptsPtr = &funasrNanoOpts;
|
|
204
207
|
}
|
|
208
|
+
NSDictionary *q3 = modelOptions[@"qwen3Asr"];
|
|
209
|
+
if ([q3 isKindOfClass:[NSDictionary class]]) {
|
|
210
|
+
if (q3[@"maxTotalLen"] != nil) qwen3AsrOpts.max_total_len = [(NSNumber *)q3[@"maxTotalLen"] intValue];
|
|
211
|
+
if (q3[@"maxNewTokens"] != nil) qwen3AsrOpts.max_new_tokens = [(NSNumber *)q3[@"maxNewTokens"] intValue];
|
|
212
|
+
if (q3[@"temperature"] != nil) qwen3AsrOpts.temperature = [(NSNumber *)q3[@"temperature"] floatValue];
|
|
213
|
+
if (q3[@"topP"] != nil) qwen3AsrOpts.top_p = [(NSNumber *)q3[@"topP"] floatValue];
|
|
214
|
+
if (q3[@"seed"] != nil) qwen3AsrOpts.seed = [(NSNumber *)q3[@"seed"] intValue];
|
|
215
|
+
qwen3AsrOptsPtr = &qwen3AsrOpts;
|
|
216
|
+
}
|
|
205
217
|
}
|
|
206
218
|
|
|
207
219
|
sherpaonnx::SttInitializeResult result = inst->wrapper->initialize(
|
|
208
220
|
modelDirStr, preferInt8Opt, modelTypeOpt, debugVal, hotwordsFileOpt, hotwordsScoreOpt,
|
|
209
221
|
numThreadsOpt, providerOpt, ruleFstsOpt, ruleFarsOpt, ditherOpt,
|
|
210
|
-
whisperOptsPtr, senseVoiceOptsPtr, canaryOptsPtr, funasrNanoOptsPtr);
|
|
222
|
+
whisperOptsPtr, senseVoiceOptsPtr, canaryOptsPtr, funasrNanoOptsPtr, qwen3AsrOptsPtr);
|
|
211
223
|
|
|
212
224
|
if (result.success) {
|
|
213
225
|
RCTLogInfo(@"Sherpa-onnx initialized successfully");
|