react-native-sherpa-onnx 0.3.6 → 0.3.7

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.
Files changed (222) hide show
  1. package/LICENSE +1 -0
  2. package/README.md +89 -21
  3. package/SherpaOnnx.podspec +3 -0
  4. package/THIRD_PARTY_LICENSES/README.md +62 -0
  5. package/THIRD_PARTY_LICENSES/ffmpeg.txt +502 -0
  6. package/THIRD_PARTY_LICENSES/libarchive.txt +65 -0
  7. package/THIRD_PARTY_LICENSES/nvidia_omla.txt +181 -0
  8. package/THIRD_PARTY_LICENSES/onnxruntime.txt +21 -0
  9. package/THIRD_PARTY_LICENSES/opus.txt +44 -0
  10. package/THIRD_PARTY_LICENSES/sherpa-onnx.txt +201 -0
  11. package/THIRD_PARTY_LICENSES/shine.txt +482 -0
  12. package/THIRD_PARTY_LICENSES/zstd.txt +30 -0
  13. package/android/build.gradle +7 -3
  14. package/android/prebuilt-download.gradle +344 -152
  15. package/android/prebuilt-versions.gradle +1 -1
  16. package/android/src/main/assets/model_licenses/asr-models-license-status.csv +409 -0
  17. package/android/src/main/assets/model_licenses/qnn-asr-models-license-status.csv +695 -0
  18. package/android/src/main/assets/model_licenses/tts-models-license-status.csv +596 -0
  19. package/android/src/main/cpp/CMakeLists.txt +28 -10
  20. package/android/src/main/cpp/jni/archive/sherpa-onnx-archive-helper.cpp +2 -2
  21. package/android/src/main/cpp/jni/audio/sherpa-onnx-audio-convert-jni.cpp +268 -2
  22. package/android/src/main/cpp/jni/model_detect/sherpa-onnx-model-detect-tts.cpp +6 -2
  23. package/android/src/main/cpp/jni/model_detect/sherpa-onnx-validate-tts.cpp +4 -2
  24. package/android/src/main/java/com/sherpaonnx/SherpaOnnxArchiveHelper.kt +40 -10
  25. package/android/src/main/java/com/sherpaonnx/SherpaOnnxModule.kt +99 -0
  26. package/android/src/main/java/com/sherpaonnx/SherpaOnnxOnlineSttHelper.kt +4 -1
  27. package/android/src/main/java/com/sherpaonnx/SherpaOnnxTtsHelper.kt +112 -97
  28. package/ios/Resources/model_licenses/asr-models-license-status.csv +409 -0
  29. package/ios/Resources/model_licenses/qnn-asr-models-license-status.csv +695 -0
  30. package/ios/Resources/model_licenses/tts-models-license-status.csv +596 -0
  31. package/ios/SherpaOnnx+OnlineSTT.mm +2 -0
  32. package/ios/SherpaOnnx+PcmLiveStream.mm +2 -29
  33. package/ios/SherpaOnnx+TTS.mm +178 -20
  34. package/ios/SherpaOnnx.mm +54 -0
  35. package/ios/SherpaOnnxAudioConvert.h +10 -0
  36. package/ios/SherpaOnnxAudioConvert.mm +257 -1
  37. package/ios/archive/sherpa-onnx-archive-helper.h +3 -0
  38. package/ios/archive/sherpa-onnx-archive-helper.mm +39 -6
  39. package/ios/model_detect/sherpa-onnx-model-detect-tts.mm +13 -2
  40. package/ios/model_detect/sherpa-onnx-validate-tts.mm +4 -2
  41. package/ios/online_stt/sherpa-onnx-online-stt-wrapper.h +1 -0
  42. package/ios/online_stt/sherpa-onnx-online-stt-wrapper.mm +4 -0
  43. package/ios/tts/sherpa-onnx-tts-wrapper.h +37 -0
  44. package/ios/tts/sherpa-onnx-tts-wrapper.mm +149 -3
  45. package/lib/module/NativeSherpaOnnx.js.map +1 -1
  46. package/lib/module/audio/index.js +8 -0
  47. package/lib/module/audio/index.js.map +1 -1
  48. package/lib/module/download/ModelDownloadManager.js +10 -929
  49. package/lib/module/download/ModelDownloadManager.js.map +1 -1
  50. package/lib/module/download/activeModelOperations.js +26 -0
  51. package/lib/module/download/activeModelOperations.js.map +1 -0
  52. package/lib/module/download/background-downloader.d.js +2 -0
  53. package/lib/module/download/background-downloader.d.js.map +1 -0
  54. package/lib/module/download/bulkPurge.js +72 -0
  55. package/lib/module/download/bulkPurge.js.map +1 -0
  56. package/lib/module/download/checksumPrompt.js +19 -0
  57. package/lib/module/download/checksumPrompt.js.map +1 -0
  58. package/lib/module/download/constants.js +7 -0
  59. package/lib/module/download/constants.js.map +1 -0
  60. package/lib/module/download/downloadEvents.js +35 -0
  61. package/lib/module/download/downloadEvents.js.map +1 -0
  62. package/lib/module/download/downloadTask.js +385 -0
  63. package/lib/module/download/downloadTask.js.map +1 -0
  64. package/lib/module/download/ensureModel.js +89 -0
  65. package/lib/module/download/ensureModel.js.map +1 -0
  66. package/lib/module/download/index.js +4 -4
  67. package/lib/module/download/index.js.map +1 -1
  68. package/lib/module/download/localModels.js +151 -0
  69. package/lib/module/download/localModels.js.map +1 -0
  70. package/lib/module/download/modelExtraction.js +174 -0
  71. package/lib/module/download/modelExtraction.js.map +1 -0
  72. package/lib/module/download/paths.js +98 -0
  73. package/lib/module/download/paths.js.map +1 -0
  74. package/lib/module/download/postDownloadProcessing.js +206 -0
  75. package/lib/module/download/postDownloadProcessing.js.map +1 -0
  76. package/lib/module/download/protectedModelKeys.js +31 -0
  77. package/lib/module/download/protectedModelKeys.js.map +1 -0
  78. package/lib/module/download/registry.js +267 -0
  79. package/lib/module/download/registry.js.map +1 -0
  80. package/lib/module/download/retry.js +59 -0
  81. package/lib/module/download/retry.js.map +1 -0
  82. package/lib/module/download/types.js +17 -0
  83. package/lib/module/download/types.js.map +1 -0
  84. package/lib/module/download/validation.js +101 -5
  85. package/lib/module/download/validation.js.map +1 -1
  86. package/lib/module/{download → extraction}/extractTarBz2.js +3 -1
  87. package/lib/module/extraction/extractTarBz2.js.map +1 -0
  88. package/lib/module/{download → extraction}/extractTarZst.js +3 -1
  89. package/lib/module/extraction/extractTarZst.js.map +1 -0
  90. package/lib/module/extraction/index.js +3 -4
  91. package/lib/module/extraction/index.js.map +1 -1
  92. package/lib/module/index.js +1 -1
  93. package/lib/module/index.js.map +1 -1
  94. package/lib/module/licenses.js +63 -0
  95. package/lib/module/licenses.js.map +1 -0
  96. package/lib/module/stt/index.js +16 -2
  97. package/lib/module/stt/index.js.map +1 -1
  98. package/lib/module/stt/streaming.js +2 -0
  99. package/lib/module/stt/streaming.js.map +1 -1
  100. package/lib/module/stt/streamingTypes.js.map +1 -1
  101. package/lib/module/stt/types.js.map +1 -1
  102. package/lib/module/tts/index.js +20 -2
  103. package/lib/module/tts/index.js.map +1 -1
  104. package/lib/module/tts/streaming.js +4 -0
  105. package/lib/module/tts/streaming.js.map +1 -1
  106. package/lib/module/tts/types.js.map +1 -1
  107. package/lib/module/utils.js +16 -1
  108. package/lib/module/utils.js.map +1 -1
  109. package/lib/typescript/src/NativeSherpaOnnx.d.ts +33 -5
  110. package/lib/typescript/src/NativeSherpaOnnx.d.ts.map +1 -1
  111. package/lib/typescript/src/audio/index.d.ts +10 -0
  112. package/lib/typescript/src/audio/index.d.ts.map +1 -1
  113. package/lib/typescript/src/download/ModelDownloadManager.d.ts +10 -108
  114. package/lib/typescript/src/download/ModelDownloadManager.d.ts.map +1 -1
  115. package/lib/typescript/src/download/activeModelOperations.d.ts +6 -0
  116. package/lib/typescript/src/download/activeModelOperations.d.ts.map +1 -0
  117. package/lib/typescript/src/download/bulkPurge.d.ts +14 -0
  118. package/lib/typescript/src/download/bulkPurge.d.ts.map +1 -0
  119. package/lib/typescript/src/download/checksumPrompt.d.ts +3 -0
  120. package/lib/typescript/src/download/checksumPrompt.d.ts.map +1 -0
  121. package/lib/typescript/src/download/constants.d.ts +5 -0
  122. package/lib/typescript/src/download/constants.d.ts.map +1 -0
  123. package/lib/typescript/src/download/downloadEvents.d.ts +6 -0
  124. package/lib/typescript/src/download/downloadEvents.d.ts.map +1 -0
  125. package/lib/typescript/src/download/downloadTask.d.ts +20 -0
  126. package/lib/typescript/src/download/downloadTask.d.ts.map +1 -0
  127. package/lib/typescript/src/download/ensureModel.d.ts +26 -0
  128. package/lib/typescript/src/download/ensureModel.d.ts.map +1 -0
  129. package/lib/typescript/src/download/index.d.ts +7 -7
  130. package/lib/typescript/src/download/index.d.ts.map +1 -1
  131. package/lib/typescript/src/download/localModels.d.ts +15 -0
  132. package/lib/typescript/src/download/localModels.d.ts.map +1 -0
  133. package/lib/typescript/src/download/modelExtraction.d.ts +36 -0
  134. package/lib/typescript/src/download/modelExtraction.d.ts.map +1 -0
  135. package/lib/typescript/src/download/paths.d.ts +28 -0
  136. package/lib/typescript/src/download/paths.d.ts.map +1 -0
  137. package/lib/typescript/src/download/postDownloadProcessing.d.ts +19 -0
  138. package/lib/typescript/src/download/postDownloadProcessing.d.ts.map +1 -0
  139. package/lib/typescript/src/download/protectedModelKeys.d.ts +6 -0
  140. package/lib/typescript/src/download/protectedModelKeys.d.ts.map +1 -0
  141. package/lib/typescript/src/download/registry.d.ts +14 -0
  142. package/lib/typescript/src/download/registry.d.ts.map +1 -0
  143. package/lib/typescript/src/download/retry.d.ts +15 -0
  144. package/lib/typescript/src/download/retry.d.ts.map +1 -0
  145. package/lib/typescript/src/download/types.d.ts +96 -0
  146. package/lib/typescript/src/download/types.d.ts.map +1 -0
  147. package/lib/typescript/src/download/validation.d.ts +19 -0
  148. package/lib/typescript/src/download/validation.d.ts.map +1 -1
  149. package/lib/typescript/src/extraction/extractTarBz2.d.ts.map +1 -0
  150. package/lib/typescript/src/extraction/extractTarZst.d.ts.map +1 -0
  151. package/lib/typescript/src/index.d.ts +1 -0
  152. package/lib/typescript/src/index.d.ts.map +1 -1
  153. package/lib/typescript/src/licenses.d.ts +10 -0
  154. package/lib/typescript/src/licenses.d.ts.map +1 -0
  155. package/lib/typescript/src/stt/index.d.ts +4 -1
  156. package/lib/typescript/src/stt/index.d.ts.map +1 -1
  157. package/lib/typescript/src/stt/streaming.d.ts.map +1 -1
  158. package/lib/typescript/src/stt/streamingTypes.d.ts +5 -0
  159. package/lib/typescript/src/stt/streamingTypes.d.ts.map +1 -1
  160. package/lib/typescript/src/stt/types.d.ts +3 -1
  161. package/lib/typescript/src/stt/types.d.ts.map +1 -1
  162. package/lib/typescript/src/tts/index.d.ts +3 -1
  163. package/lib/typescript/src/tts/index.d.ts.map +1 -1
  164. package/lib/typescript/src/tts/streaming.d.ts.map +1 -1
  165. package/lib/typescript/src/tts/types.d.ts +6 -5
  166. package/lib/typescript/src/tts/types.d.ts.map +1 -1
  167. package/lib/typescript/src/utils.d.ts +5 -0
  168. package/lib/typescript/src/utils.d.ts.map +1 -1
  169. package/package.json +6 -1
  170. package/scripts/{check-model-csvs.sh → ci/check-model-csvs.sh} +9 -2
  171. package/scripts/ci/collect_all_sherpa_model_streams.sh +101 -0
  172. package/scripts/ci/collect_one_sherpa_release_stream.sh +189 -0
  173. package/scripts/ci/sherpa_asr_model_release_streams.json +21 -0
  174. package/scripts/ci/sherpa_tts_model_release_streams.json +13 -0
  175. package/scripts/ci/update_model_license_csv.sh +765 -0
  176. package/scripts/setup-ios-framework.sh +14 -11
  177. package/scripts/update_commercial_use.js +73 -0
  178. package/src/NativeSherpaOnnx.ts +36 -5
  179. package/src/audio/index.ts +20 -0
  180. package/src/download/ModelDownloadManager.ts +55 -1343
  181. package/src/download/activeModelOperations.ts +38 -0
  182. package/src/download/background-downloader.d.ts +43 -0
  183. package/src/download/bulkPurge.ts +102 -0
  184. package/src/download/checksumPrompt.ts +25 -0
  185. package/src/download/constants.ts +5 -0
  186. package/src/download/downloadEvents.ts +55 -0
  187. package/src/download/downloadTask.ts +497 -0
  188. package/src/download/ensureModel.ts +124 -0
  189. package/src/download/index.ts +19 -4
  190. package/src/download/localModels.ts +234 -0
  191. package/src/download/modelExtraction.ts +244 -0
  192. package/src/download/paths.ts +134 -0
  193. package/src/download/postDownloadProcessing.ts +292 -0
  194. package/src/download/protectedModelKeys.ts +30 -0
  195. package/src/download/registry.ts +404 -0
  196. package/src/download/retry.ts +76 -0
  197. package/src/download/types.ts +120 -0
  198. package/src/download/validation.ts +114 -8
  199. package/src/{download → extraction}/extractTarBz2.ts +3 -1
  200. package/src/{download → extraction}/extractTarZst.ts +3 -1
  201. package/src/extraction/index.ts +3 -7
  202. package/src/index.tsx +1 -0
  203. package/src/licenses.ts +100 -0
  204. package/src/stt/index.ts +20 -2
  205. package/src/stt/streaming.ts +3 -0
  206. package/src/stt/streamingTypes.ts +5 -0
  207. package/src/stt/types.ts +3 -1
  208. package/src/tts/index.ts +30 -2
  209. package/src/tts/streaming.ts +10 -0
  210. package/src/tts/types.ts +6 -5
  211. package/src/utils.ts +22 -1
  212. package/third_party/sherpa-onnx-prebuilt/ANDROID_RELEASE_TAG +1 -1
  213. package/third_party/sherpa-onnx-prebuilt/IOS_RELEASE_TAG +1 -1
  214. package/android/src/main/cpp/jni/tts/sherpa-onnx-tts-zipvoice-jni.cpp +0 -301
  215. package/android/src/main/java/com/sherpaonnx/ZipvoiceTtsWrapper.kt +0 -187
  216. package/lib/module/download/extractTarBz2.js.map +0 -1
  217. package/lib/module/download/extractTarZst.js.map +0 -1
  218. package/lib/typescript/src/download/extractTarBz2.d.ts.map +0 -1
  219. package/lib/typescript/src/download/extractTarZst.d.ts.map +0 -1
  220. package/scripts/check-qnn-support.sh +0 -78
  221. /package/lib/typescript/src/{download → extraction}/extractTarBz2.d.ts +0 -0
  222. /package/lib/typescript/src/{download → extraction}/extractTarZst.d.ts +0 -0
@@ -3,6 +3,7 @@ import {
3
3
  stat,
4
4
  exists,
5
5
  readDir,
6
+ unlink,
6
7
  type ReadDirResItemT,
7
8
  } from '@dr.pogodin/react-native-fs';
8
9
  import SherpaOnnx from '../NativeSherpaOnnx';
@@ -25,6 +26,37 @@ export class ValidationResult {
25
26
  }
26
27
  }
27
28
 
29
+ /**
30
+ * Delete a directory and all contents. No-op if the path is missing.
31
+ * Best-effort: continues on per-entry errors (permissions, race).
32
+ */
33
+ export async function removeDirectoryRecursive(dirPath: string): Promise<void> {
34
+ if (!(await exists(dirPath))) return;
35
+ let entries: ReadDirResItemT[];
36
+ try {
37
+ entries = await readDir(dirPath);
38
+ } catch {
39
+ return;
40
+ }
41
+ for (const entry of entries) {
42
+ const childPath = `${dirPath}/${entry.name}`.replace(/\/+/g, '/');
43
+ try {
44
+ if (entry.isDirectory()) {
45
+ await removeDirectoryRecursive(childPath);
46
+ } else {
47
+ await unlink(childPath);
48
+ }
49
+ } catch {
50
+ // ignore
51
+ }
52
+ }
53
+ try {
54
+ await unlink(dirPath);
55
+ } catch {
56
+ // ignore
57
+ }
58
+ }
59
+
28
60
  /**
29
61
  * Parse checksum.txt format into a Map of filename -> hash
30
62
  * Expected format:
@@ -148,12 +180,15 @@ export async function validateExtractedFiles(
148
180
 
149
181
  for (const entry of entries) {
150
182
  if (entry.isDirectory()) {
151
- const nested = await collectFilesRecursive(
152
- entry.path,
153
- depth + 1,
154
- maxDepth
155
- );
156
- files.push(...nested);
183
+ const subPath = entry.path;
184
+ if (subPath != null) {
185
+ const nested = await collectFilesRecursive(
186
+ subPath,
187
+ depth + 1,
188
+ maxDepth
189
+ );
190
+ files.push(...nested);
191
+ }
157
192
  } else {
158
193
  files.push(entry);
159
194
  }
@@ -175,13 +210,13 @@ export async function validateExtractedFiles(
175
210
  }
176
211
 
177
212
  let hasModelLikeFiles = actualFiles.some((file) =>
178
- isModelLikeFile(file.name)
213
+ isModelLikeFile(file.name ?? '')
179
214
  );
180
215
 
181
216
  if (!hasModelLikeFiles) {
182
217
  const nestedFiles = await collectFilesRecursive(modelDir);
183
218
  hasModelLikeFiles = nestedFiles.some((file) =>
184
- isModelLikeFile(file.name)
219
+ isModelLikeFile(file.name ?? '')
185
220
  );
186
221
  }
187
222
 
@@ -203,6 +238,77 @@ export async function validateExtractedFiles(
203
238
  }
204
239
  }
205
240
 
241
+ /** True if the file is a native sherpa-onnx model file (e.g. encoder.onnx). Excludes our metadata (.ready, manifest.json). */
242
+ function isNativeModelFileName(name: string): boolean {
243
+ const lower = name.toLowerCase();
244
+ return lower.endsWith('.onnx') || lower.endsWith('.bin');
245
+ }
246
+
247
+ /**
248
+ * Check if a directory contains native model files (.onnx or .bin) at the top level or one level of subdirectories.
249
+ * Used to find the actual model dir; ignores our metadata (manifest.json, .ready).
250
+ */
251
+ async function dirContainsModelFiles(dir: string): Promise<boolean> {
252
+ const entries = await readDir(dir);
253
+ const files = entries.filter((e) => !e.isDirectory());
254
+ if (files.some((f) => isNativeModelFileName(f.name ?? ''))) return true;
255
+ const subdirs = entries.filter((e) => e.isDirectory());
256
+ for (const sub of subdirs) {
257
+ const subPath = sub?.path;
258
+ if (subPath == null) continue;
259
+ const subEntries = await readDir(subPath);
260
+ const subFiles = subEntries.filter((e) => !e.isDirectory());
261
+ if (subFiles.some((f) => isNativeModelFileName(f.name ?? ''))) return true;
262
+ }
263
+ return false;
264
+ }
265
+
266
+ /**
267
+ * Resolve the directory that actually contains model files.
268
+ * After extracting a tarball, model files often end up in a single top-level subdirectory
269
+ * (e.g. installDir/modelId/encoder.onnx). Native APIs expect the path to the folder
270
+ * that directly contains encoder.onnx, decoder.onnx, etc.
271
+ *
272
+ * - If installDir itself contains native model files (.onnx/.bin), returns installDir.
273
+ * - If installDir has exactly one subdirectory that contains native model files, returns that subdirectory path.
274
+ * (Ignores our metadata: .ready, manifest.json.) This can produce paths like
275
+ * .../tts/vits-piper-de_DE-thorsten-medium-int8/vits-piper-de_DE-thorsten-medium-int8 when the
276
+ * archive extracts a top-level folder with the same name as the model id; that is intentional.
277
+ * - Otherwise returns installDir unchanged.
278
+ */
279
+ export async function resolveActualModelDir(
280
+ installDir: string
281
+ ): Promise<string> {
282
+ try {
283
+ const dirExists = await exists(installDir);
284
+ if (!dirExists) return installDir;
285
+
286
+ const entries = await readDir(installDir);
287
+ const topLevelFiles = entries.filter((e) => !e.isDirectory());
288
+ if (topLevelFiles.some((f) => isNativeModelFileName(f.name ?? ''))) {
289
+ return installDir;
290
+ }
291
+
292
+ const subdirs = entries.filter((e) => e.isDirectory());
293
+ const firstSubdir = subdirs[0];
294
+ const singleSubdir = subdirs.length === 1 ? firstSubdir : undefined;
295
+
296
+ if (singleSubdir != null) {
297
+ const candidatePath = singleSubdir.path;
298
+ if (
299
+ candidatePath != null &&
300
+ (await dirContainsModelFiles(candidatePath))
301
+ ) {
302
+ return candidatePath;
303
+ }
304
+ }
305
+
306
+ return installDir;
307
+ } catch {
308
+ return installDir;
309
+ }
310
+ }
311
+
206
312
  /**
207
313
  * Get available disk space (in bytes)
208
314
  * This is a simplified version. For accurate values on Android/iOS, use native modules.
@@ -47,7 +47,9 @@ export async function extractTarBz2(
47
47
  if (signal) {
48
48
  const onAbort = () => {
49
49
  try {
50
- SherpaOnnx.cancelExtractTarBz2();
50
+ // Use per-path cancel so aborting this extraction doesn't affect
51
+ // other extractions that may be running in parallel.
52
+ SherpaOnnx.cancelExtractBySourcePath(sourcePath);
51
53
  } catch {
52
54
  // Ignore cancel errors to avoid crashing on abort.
53
55
  }
@@ -46,7 +46,9 @@ export async function extractTarZst(
46
46
  if (signal) {
47
47
  const onAbort = () => {
48
48
  try {
49
- SherpaOnnx.cancelExtractTarZst();
49
+ // Use per-path cancel so aborting this extraction doesn't affect
50
+ // other extractions that may be running in parallel.
51
+ SherpaOnnx.cancelExtractBySourcePath(sourcePath);
50
52
  } catch {
51
53
  // Ignore cancel errors to avoid crashing on abort.
52
54
  }
@@ -12,8 +12,8 @@
12
12
  import { DeviceEventEmitter, Platform } from 'react-native';
13
13
  import { readDir, stat, exists } from '@dr.pogodin/react-native-fs';
14
14
  import SherpaOnnx from '../NativeSherpaOnnx';
15
- import { extractTarZst } from '../download/extractTarZst';
16
- import { extractTarBz2 } from '../download/extractTarBz2';
15
+ import { extractTarZst } from './extractTarZst';
16
+ import { extractTarBz2 } from './extractTarBz2';
17
17
  import type {
18
18
  BundledArchive,
19
19
  ExtractArchiveOptions,
@@ -228,13 +228,9 @@ async function extractFromAsset(
228
228
  }
229
229
 
230
230
  if (signal) {
231
- const cancel =
232
- archive.format === 'tar.zst'
233
- ? SherpaOnnx.cancelExtractTarZst
234
- : SherpaOnnx.cancelExtractTarBz2;
235
231
  const onAbort = () => {
236
232
  try {
237
- cancel();
233
+ SherpaOnnx.cancelExtractBySourcePath(archive.archivePath);
238
234
  } catch {
239
235
  // ignore
240
236
  }
package/src/index.tsx CHANGED
@@ -18,6 +18,7 @@ export {
18
18
 
19
19
  export { copyFileToContentUri } from './tts';
20
20
 
21
+ export { getModelLicenses, type ModelLicense } from './licenses';
21
22
  // Note: Feature-specific exports are available via subpath imports:
22
23
  // - import { createSTT, createStreamingSTT, ... } from 'react-native-sherpa-onnx/stt'
23
24
  // - import { createTTS, ... } from 'react-native-sherpa-onnx/tts'
@@ -0,0 +1,100 @@
1
+ import SherpaOnnx from './NativeSherpaOnnx';
2
+
3
+ export interface ModelLicense {
4
+ asset_name: string;
5
+ license_type: string;
6
+ commercial_use: 'yes' | 'no' | 'conditional' | 'restricted' | 'unknown';
7
+ confidence: string;
8
+ detection_source: string;
9
+ license_file: string;
10
+ }
11
+
12
+ export async function getModelLicenses(): Promise<ModelLicense[]> {
13
+ const asrPath = 'model_licenses/asr-models-license-status.csv';
14
+ const qnnPath = 'model_licenses/qnn-asr-models-license-status.csv';
15
+ const ttsPath = 'model_licenses/tts-models-license-status.csv';
16
+
17
+ const results = await Promise.allSettled([
18
+ SherpaOnnx.readAssetFileAsUtf8(asrPath),
19
+ SherpaOnnx.readAssetFileAsUtf8(qnnPath),
20
+ SherpaOnnx.readAssetFileAsUtf8(ttsPath),
21
+ ]);
22
+
23
+ const [asrResult, qnnResult, ttsResult] = results;
24
+
25
+ const licenses: ModelLicense[] = [];
26
+
27
+ if (asrResult.status === 'fulfilled') {
28
+ licenses.push(...parseCsv(asrResult.value));
29
+ } else {
30
+ console.warn(
31
+ `[SherpaOnnx] Failed to load ASR model licenses: ${asrResult.reason}`
32
+ );
33
+ }
34
+
35
+ if (qnnResult.status === 'fulfilled') {
36
+ licenses.push(...parseCsv(qnnResult.value));
37
+ } else {
38
+ console.warn(
39
+ `[SherpaOnnx] Failed to load QNN model licenses: ${qnnResult.reason}`
40
+ );
41
+ }
42
+
43
+ if (ttsResult.status === 'fulfilled') {
44
+ licenses.push(...parseCsv(ttsResult.value));
45
+ } else {
46
+ console.warn(
47
+ `[SherpaOnnx] Failed to load TTS model licenses: ${ttsResult.reason}`
48
+ );
49
+ }
50
+
51
+ return licenses;
52
+ }
53
+
54
+ function parseCsv(csvString: string): ModelLicense[] {
55
+ const lines = csvString.split(/\r?\n/);
56
+ if (lines.length === 0) {
57
+ return [];
58
+ }
59
+
60
+ // The first line is the header
61
+ const headerLine = lines[0];
62
+ if (!headerLine) return [];
63
+
64
+ const headers = headerLine.split(',').map((h) => h.trim());
65
+
66
+ const results: ModelLicense[] = [];
67
+
68
+ for (let i = 1; i < lines.length; i++) {
69
+ const line = lines[i];
70
+ if (!line || line.trim() === '') continue;
71
+
72
+ // The CSV has 6 columns: asset_name, license_type, commercial_use,
73
+ // confidence, detection_source, license_file. The last column
74
+ // (license_file) may itself contain commas (e.g. URLs with query strings),
75
+ // so split into at most 6 parts and join any excess back into the last field.
76
+ const COLUMN_COUNT = 6;
77
+ const rawParts = line.split(',');
78
+ const parts =
79
+ rawParts.length <= COLUMN_COUNT
80
+ ? rawParts
81
+ : [
82
+ ...rawParts.slice(0, COLUMN_COUNT - 1),
83
+ rawParts.slice(COLUMN_COUNT - 1).join(','),
84
+ ];
85
+
86
+ const entry: Record<string, string> = {};
87
+ for (let j = 0; j < headers.length; j++) {
88
+ const header = headers[j];
89
+ if (header) {
90
+ entry[header] = (parts[j] ?? '').trim();
91
+ }
92
+ }
93
+
94
+ if (entry['asset_name']) {
95
+ results.push(entry as unknown as ModelLicense);
96
+ }
97
+ }
98
+
99
+ return results;
100
+ }
package/src/stt/index.ts CHANGED
@@ -40,7 +40,7 @@ function normalizeSttResult(raw: {
40
40
  *
41
41
  * @param modelPath - Model path configuration (asset, file, or auto)
42
42
  * @param options - Optional preferInt8 and modelType (default: auto)
43
- * @returns Object with success, detectedModels (array of { type, modelDir }), and modelType (primary detected type)
43
+ * @returns Object with success, detectedModels (array of { type, modelDir }), modelType (primary detected type), optional error when success is false, and optionally isHardwareSpecificUnsupported
44
44
  * @example
45
45
  * ```typescript
46
46
  * const path = { type: 'asset' as const, path: 'models/sherpa-onnx-whisper-tiny-en' };
@@ -55,15 +55,33 @@ export async function detectSttModel(
55
55
  options?: { preferInt8?: boolean; modelType?: STTModelType }
56
56
  ): Promise<{
57
57
  success: boolean;
58
+ /** Native validation/detect failure. */
59
+ error?: string;
58
60
  detectedModels: Array<{ type: string; modelDir: string }>;
59
61
  modelType?: string;
62
+ isHardwareSpecificUnsupported?: boolean;
60
63
  }> {
61
64
  const resolvedPath = await resolveModelPath(modelPath);
62
- return SherpaOnnx.detectSttModel(
65
+ const raw = await SherpaOnnx.detectSttModel(
63
66
  resolvedPath,
64
67
  options?.preferInt8,
65
68
  options?.modelType
66
69
  );
70
+ const err =
71
+ typeof (raw as { error?: unknown }).error === 'string'
72
+ ? String((raw as { error: string }).error).trim()
73
+ : '';
74
+ return {
75
+ success: raw.success,
76
+ ...(err.length > 0 ? { error: err } : {}),
77
+ ...(raw.isHardwareSpecificUnsupported === true
78
+ ? { isHardwareSpecificUnsupported: true }
79
+ : {}),
80
+ detectedModels: raw.detectedModels ?? [],
81
+ ...(raw.modelType != null && raw.modelType !== ''
82
+ ? { modelType: raw.modelType }
83
+ : {}),
84
+ };
67
85
  }
68
86
 
69
87
  /**
@@ -82,6 +82,7 @@ function flattenInitOptionsForNative(options: StreamingSttInitOptions): {
82
82
  provider?: string;
83
83
  ruleFsts?: string;
84
84
  ruleFars?: string;
85
+ dither?: number;
85
86
  blankPenalty?: number;
86
87
  debug?: boolean;
87
88
  rule1MustContainNonSilence?: boolean;
@@ -107,6 +108,7 @@ function flattenInitOptionsForNative(options: StreamingSttInitOptions): {
107
108
  provider: options.provider,
108
109
  ruleFsts: options.ruleFsts,
109
110
  ruleFars: options.ruleFars,
111
+ dither: options.dither,
110
112
  blankPenalty: options.blankPenalty,
111
113
  debug: options.debug,
112
114
  rule1MustContainNonSilence: ep?.rule1?.mustContainNonSilence,
@@ -200,6 +202,7 @@ export async function createStreamingSTT(
200
202
  if (flat.provider !== undefined) nativeOptions.provider = flat.provider;
201
203
  if (flat.ruleFsts !== undefined) nativeOptions.ruleFsts = flat.ruleFsts;
202
204
  if (flat.ruleFars !== undefined) nativeOptions.ruleFars = flat.ruleFars;
205
+ if (flat.dither !== undefined) nativeOptions.dither = flat.dither;
203
206
  if (flat.blankPenalty !== undefined)
204
207
  nativeOptions.blankPenalty = flat.blankPenalty;
205
208
  if (flat.debug !== undefined) nativeOptions.debug = flat.debug;
@@ -75,6 +75,11 @@ export interface StreamingSttInitOptions {
75
75
  ruleFsts?: string;
76
76
  /** Path(s) to rule FARs for ITN. */
77
77
  ruleFars?: string;
78
+ /**
79
+ * Feature extraction dither. **Android:** applied natively. **iOS:** ignored (C/CXX API has no
80
+ * `dither` on `FeatureConfig`); library default applies.
81
+ */
82
+ dither?: number;
78
83
  /** Blank penalty. */
79
84
  blankPenalty?: number;
80
85
  /** Enable debug logging. Default: false. */
package/src/stt/types.ts CHANGED
@@ -228,7 +228,9 @@ export interface STTInitializeOptions {
228
228
  ruleFars?: string;
229
229
 
230
230
  /**
231
- * Dither for feature extraction (Kotlin FeatureConfig.dither). Default 0.
231
+ * Dither for feature extraction (Kotlin `FeatureConfig.dither`). Default: no dither.
232
+ * **Android:** applied natively. **iOS:** ignored — the bundled sherpa-onnx C/CXX API does not
233
+ * expose this field; the native default is used.
232
234
  */
233
235
  dither?: number;
234
236
 
package/src/tts/index.ts CHANGED
@@ -86,7 +86,7 @@ function flattenTtsModelOptionsForNative(
86
86
  *
87
87
  * @param modelPath - Model path configuration (asset, file, or auto)
88
88
  * @param options - Optional modelType (default: 'auto')
89
- * @returns Object with success, detectedModels (array of { type, modelDir }), modelType (primary detected type), and optionally lexiconLanguageCandidates (language ids for multi-lang Kokoro/Kitten)
89
+ * @returns Object with success, detectedModels (array of { type, modelDir }), modelType (primary detected type), optional error when success is false, and optionally lexiconLanguageCandidates (language ids for multi-lang Kokoro/Kitten)
90
90
  * @example
91
91
  * ```typescript
92
92
  * const result = await detectTtsModel({ type: 'asset', path: 'models/vits-piper-en' });
@@ -101,13 +101,31 @@ export async function detectTtsModel(
101
101
  options?: { modelType?: TTSModelType }
102
102
  ): Promise<{
103
103
  success: boolean;
104
+ /** Native validation/detect failure (e.g. missing lexicon for Zipvoice). */
105
+ error?: string;
104
106
  detectedModels: Array<{ type: string; modelDir: string }>;
105
107
  modelType?: string;
106
108
  /** Language ids from detected lexicon files ("default" for lexicon.txt, or e.g. "us-en", "zh" from lexicon-us-en.txt, lexicon-zh.txt). Present for Kokoro/Kitten; use for language selection UI. */
107
109
  lexiconLanguageCandidates?: string[];
108
110
  }> {
109
111
  const resolvedPath = await resolveModelPath(modelPath);
110
- return SherpaOnnx.detectTtsModel(resolvedPath, options?.modelType);
112
+ const raw = await SherpaOnnx.detectTtsModel(resolvedPath, options?.modelType);
113
+ const err =
114
+ typeof (raw as { error?: unknown }).error === 'string'
115
+ ? String((raw as { error: string }).error).trim()
116
+ : '';
117
+ return {
118
+ success: raw.success,
119
+ ...(err.length > 0 ? { error: err } : {}),
120
+ detectedModels: raw.detectedModels ?? [],
121
+ ...(raw.modelType != null && raw.modelType !== ''
122
+ ? { modelType: raw.modelType }
123
+ : {}),
124
+ ...(raw.lexiconLanguageCandidates != null &&
125
+ raw.lexiconLanguageCandidates.length > 0
126
+ ? { lexiconLanguageCandidates: raw.lexiconLanguageCandidates }
127
+ : {}),
128
+ };
111
129
  }
112
130
 
113
131
  /**
@@ -124,6 +142,16 @@ function toNativeTtsOptions(
124
142
  if (options.silenceScale !== undefined)
125
143
  out.silenceScale = options.silenceScale;
126
144
  if (options.referenceAudio != null) {
145
+ const sr = options.referenceAudio.sampleRate;
146
+ if (
147
+ typeof __DEV__ !== 'undefined' &&
148
+ __DEV__ &&
149
+ (!Number.isFinite(sr) || sr <= 0)
150
+ ) {
151
+ console.warn(
152
+ '[react-native-sherpa-onnx] TTS referenceAudio.sampleRate must be > 0 for voice cloning (Zipvoice/Pocket).'
153
+ );
154
+ }
127
155
  out.referenceAudio = options.referenceAudio.samples;
128
156
  out.referenceSampleRate = options.referenceAudio.sampleRate;
129
157
  }
@@ -90,6 +90,16 @@ function toNativeTtsOptions(
90
90
  if (options.silenceScale !== undefined)
91
91
  out.silenceScale = options.silenceScale;
92
92
  if (options.referenceAudio != null) {
93
+ const sr = options.referenceAudio.sampleRate;
94
+ if (
95
+ typeof __DEV__ !== 'undefined' &&
96
+ __DEV__ &&
97
+ (!Number.isFinite(sr) || sr <= 0)
98
+ ) {
99
+ console.warn(
100
+ '[react-native-sherpa-onnx] TTS referenceAudio.sampleRate must be > 0 for voice cloning (Zipvoice/Pocket).'
101
+ );
102
+ }
93
103
  out.referenceAudio = options.referenceAudio.samples;
94
104
  out.referenceSampleRate = options.referenceAudio.sampleRate;
95
105
  }
package/src/tts/types.ts CHANGED
@@ -202,15 +202,16 @@ export interface TtsGenerationOptions {
202
202
  silenceScale?: number;
203
203
 
204
204
  /**
205
- * Reference audio for voice cloning (Kotlin GenerationConfig).
206
- * In the Kotlin/RN stack, only Pocket TTS uses this; other model types (vits, matcha, kokoro, kitten) ignore it.
207
- * Mono float samples in [-1, 1] and sample rate in Hz.
205
+ * Reference audio for voice cloning (native GenerationConfig / Zipvoice prompt).
206
+ * **Native (iOS & Android):** Requires non-empty samples and `sampleRate > 0`. Used for **Zipvoice** (cloning) and **Pocket** (Mimi encoder).
207
+ * Other model types (vits, matcha, kokoro, kitten) are **rejected** if reference audio is passed.
208
+ * Mono float samples in [-1, 1].
208
209
  */
209
210
  referenceAudio?: { samples: number[]; sampleRate: number };
210
211
 
211
212
  /**
212
- * Transcript text of the reference audio (Kotlin GenerationConfig.referenceText).
213
- * Required for Pocket TTS when referenceAudio is provided; ignored by other model types.
213
+ * Transcript of the reference utterance for **Zipvoice** voice cloning (prompt text); **required** when cloning with Zipvoice (non-empty after trim).
214
+ * **Pocket:** not read by sherpa-onnx native code; optional, e.g. for app metadata only.
214
215
  */
215
216
  referenceText?: string;
216
217
 
package/src/utils.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { Platform } from 'react-native';
2
2
  import type { ModelPathConfig } from './types';
3
3
  import SherpaOnnx from './NativeSherpaOnnx';
4
+ import { resolveActualModelDir } from './download';
4
5
 
5
6
  /**
6
7
  * Utility functions for model path handling
@@ -73,13 +74,33 @@ export function autoModelPath(path: string): ModelPathConfig {
73
74
  * This handles different path types (asset, file, auto) and returns
74
75
  * a platform-specific absolute path that can be used by native code.
75
76
  *
77
+ * For type 'file', the path is normalized so that when the given path is an
78
+ * install directory (e.g. with .ready and manifest.json and one model subdir),
79
+ * the returned path is the subdirectory that actually contains the .onnx files.
80
+ * This allows apps that build paths as baseDir/modelId to work without change.
81
+ *
76
82
  * @param config - Model path configuration
77
83
  * @returns Promise resolving to absolute path usable by native code
78
84
  */
79
85
  export async function resolveModelPath(
80
86
  config: ModelPathConfig
81
87
  ): Promise<string> {
82
- return SherpaOnnx.resolveModelPath(config);
88
+ const path = await SherpaOnnx.resolveModelPath(config);
89
+ if (config.type === 'file') {
90
+ const resolved = await resolveActualModelDir(path);
91
+ // Diagnostic: log so we can tell if /usr/share/espeak-ng-data is due to our path or sherpa-onnx fallback.
92
+ if (__DEV__) {
93
+ console.log(
94
+ '[SherpaOnnx] resolveModelPath(file): native path=',
95
+ path,
96
+ resolved !== path
97
+ ? `resolvedActualModelDir=> ${resolved}`
98
+ : '(unchanged)'
99
+ );
100
+ }
101
+ return resolved;
102
+ }
103
+ return path;
83
104
  }
84
105
 
85
106
  /**
@@ -1 +1 @@
1
- sherpa-onnx-android-v1.12.28
1
+ sherpa-onnx-android-v1.12.31-1
@@ -1 +1 @@
1
- framework-v1.12.28
1
+ sherpa-onnx-ios-v1.12.31-1