react-native-sherpa-onnx 0.3.6 → 0.3.8

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 (228) hide show
  1. package/LICENSE +1 -0
  2. package/README.md +92 -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 +37 -6
  23. package/android/src/main/cpp/jni/model_detect/sherpa-onnx-model-detect.h +9 -1
  24. package/android/src/main/cpp/jni/model_detect/sherpa-onnx-tts-wrapper.cpp +7 -0
  25. package/android/src/main/cpp/jni/model_detect/sherpa-onnx-validate-tts.cpp +18 -2
  26. package/android/src/main/java/com/sherpaonnx/SherpaOnnxArchiveHelper.kt +40 -10
  27. package/android/src/main/java/com/sherpaonnx/SherpaOnnxModule.kt +99 -0
  28. package/android/src/main/java/com/sherpaonnx/SherpaOnnxOnlineSttHelper.kt +4 -1
  29. package/android/src/main/java/com/sherpaonnx/SherpaOnnxTtsHelper.kt +127 -97
  30. package/ios/Resources/model_licenses/asr-models-license-status.csv +409 -0
  31. package/ios/Resources/model_licenses/qnn-asr-models-license-status.csv +695 -0
  32. package/ios/Resources/model_licenses/tts-models-license-status.csv +596 -0
  33. package/ios/SherpaOnnx+OnlineSTT.mm +2 -0
  34. package/ios/SherpaOnnx+PcmLiveStream.mm +2 -29
  35. package/ios/SherpaOnnx+TTS.mm +179 -20
  36. package/ios/SherpaOnnx.mm +54 -0
  37. package/ios/SherpaOnnxAudioConvert.h +10 -0
  38. package/ios/SherpaOnnxAudioConvert.mm +257 -1
  39. package/ios/archive/sherpa-onnx-archive-helper.h +3 -0
  40. package/ios/archive/sherpa-onnx-archive-helper.mm +39 -6
  41. package/ios/model_detect/sherpa-onnx-model-detect-tts.mm +49 -6
  42. package/ios/model_detect/sherpa-onnx-model-detect.h +9 -1
  43. package/ios/model_detect/sherpa-onnx-validate-tts.mm +18 -2
  44. package/ios/online_stt/sherpa-onnx-online-stt-wrapper.h +1 -0
  45. package/ios/online_stt/sherpa-onnx-online-stt-wrapper.mm +4 -0
  46. package/ios/tts/sherpa-onnx-tts-wrapper.h +37 -0
  47. package/ios/tts/sherpa-onnx-tts-wrapper.mm +158 -3
  48. package/lib/module/NativeSherpaOnnx.js.map +1 -1
  49. package/lib/module/audio/index.js +8 -0
  50. package/lib/module/audio/index.js.map +1 -1
  51. package/lib/module/download/ModelDownloadManager.js +10 -929
  52. package/lib/module/download/ModelDownloadManager.js.map +1 -1
  53. package/lib/module/download/activeModelOperations.js +26 -0
  54. package/lib/module/download/activeModelOperations.js.map +1 -0
  55. package/lib/module/download/background-downloader-types.js +2 -0
  56. package/lib/module/download/background-downloader-types.js.map +1 -0
  57. package/lib/module/download/bulkPurge.js +72 -0
  58. package/lib/module/download/bulkPurge.js.map +1 -0
  59. package/lib/module/download/checksumPrompt.js +19 -0
  60. package/lib/module/download/checksumPrompt.js.map +1 -0
  61. package/lib/module/download/constants.js +7 -0
  62. package/lib/module/download/constants.js.map +1 -0
  63. package/lib/module/download/downloadEvents.js +35 -0
  64. package/lib/module/download/downloadEvents.js.map +1 -0
  65. package/lib/module/download/downloadTask.js +438 -0
  66. package/lib/module/download/downloadTask.js.map +1 -0
  67. package/lib/module/download/ensureModel.js +89 -0
  68. package/lib/module/download/ensureModel.js.map +1 -0
  69. package/lib/module/download/index.js +4 -4
  70. package/lib/module/download/index.js.map +1 -1
  71. package/lib/module/download/localModels.js +151 -0
  72. package/lib/module/download/localModels.js.map +1 -0
  73. package/lib/module/download/modelExtraction.js +174 -0
  74. package/lib/module/download/modelExtraction.js.map +1 -0
  75. package/lib/module/download/paths.js +98 -0
  76. package/lib/module/download/paths.js.map +1 -0
  77. package/lib/module/download/postDownloadProcessing.js +206 -0
  78. package/lib/module/download/postDownloadProcessing.js.map +1 -0
  79. package/lib/module/download/protectedModelKeys.js +31 -0
  80. package/lib/module/download/protectedModelKeys.js.map +1 -0
  81. package/lib/module/download/registry.js +268 -0
  82. package/lib/module/download/registry.js.map +1 -0
  83. package/lib/module/download/retry.js +59 -0
  84. package/lib/module/download/retry.js.map +1 -0
  85. package/lib/module/download/types.js +17 -0
  86. package/lib/module/download/types.js.map +1 -0
  87. package/lib/module/download/validation.js +101 -5
  88. package/lib/module/download/validation.js.map +1 -1
  89. package/lib/module/{download → extraction}/extractTarBz2.js +3 -1
  90. package/lib/module/extraction/extractTarBz2.js.map +1 -0
  91. package/lib/module/{download → extraction}/extractTarZst.js +3 -1
  92. package/lib/module/extraction/extractTarZst.js.map +1 -0
  93. package/lib/module/extraction/index.js +3 -4
  94. package/lib/module/extraction/index.js.map +1 -1
  95. package/lib/module/index.js +1 -1
  96. package/lib/module/index.js.map +1 -1
  97. package/lib/module/licenses.js +63 -0
  98. package/lib/module/licenses.js.map +1 -0
  99. package/lib/module/stt/index.js +16 -2
  100. package/lib/module/stt/index.js.map +1 -1
  101. package/lib/module/stt/streaming.js +2 -0
  102. package/lib/module/stt/streaming.js.map +1 -1
  103. package/lib/module/stt/streamingTypes.js.map +1 -1
  104. package/lib/module/stt/types.js.map +1 -1
  105. package/lib/module/tts/index.js +21 -3
  106. package/lib/module/tts/index.js.map +1 -1
  107. package/lib/module/tts/streaming.js +5 -1
  108. package/lib/module/tts/streaming.js.map +1 -1
  109. package/lib/module/tts/types.js +4 -1
  110. package/lib/module/tts/types.js.map +1 -1
  111. package/lib/module/utils.js +16 -1
  112. package/lib/module/utils.js.map +1 -1
  113. package/lib/typescript/src/NativeSherpaOnnx.d.ts +34 -6
  114. package/lib/typescript/src/NativeSherpaOnnx.d.ts.map +1 -1
  115. package/lib/typescript/src/audio/index.d.ts +10 -0
  116. package/lib/typescript/src/audio/index.d.ts.map +1 -1
  117. package/lib/typescript/src/download/ModelDownloadManager.d.ts +11 -108
  118. package/lib/typescript/src/download/ModelDownloadManager.d.ts.map +1 -1
  119. package/lib/typescript/src/download/activeModelOperations.d.ts +6 -0
  120. package/lib/typescript/src/download/activeModelOperations.d.ts.map +1 -0
  121. package/lib/typescript/src/download/background-downloader-types.d.ts +64 -0
  122. package/lib/typescript/src/download/background-downloader-types.d.ts.map +1 -0
  123. package/lib/typescript/src/download/bulkPurge.d.ts +14 -0
  124. package/lib/typescript/src/download/bulkPurge.d.ts.map +1 -0
  125. package/lib/typescript/src/download/checksumPrompt.d.ts +3 -0
  126. package/lib/typescript/src/download/checksumPrompt.d.ts.map +1 -0
  127. package/lib/typescript/src/download/constants.d.ts +5 -0
  128. package/lib/typescript/src/download/constants.d.ts.map +1 -0
  129. package/lib/typescript/src/download/downloadEvents.d.ts +6 -0
  130. package/lib/typescript/src/download/downloadEvents.d.ts.map +1 -0
  131. package/lib/typescript/src/download/downloadTask.d.ts +30 -0
  132. package/lib/typescript/src/download/downloadTask.d.ts.map +1 -0
  133. package/lib/typescript/src/download/ensureModel.d.ts +26 -0
  134. package/lib/typescript/src/download/ensureModel.d.ts.map +1 -0
  135. package/lib/typescript/src/download/index.d.ts +7 -7
  136. package/lib/typescript/src/download/index.d.ts.map +1 -1
  137. package/lib/typescript/src/download/localModels.d.ts +15 -0
  138. package/lib/typescript/src/download/localModels.d.ts.map +1 -0
  139. package/lib/typescript/src/download/modelExtraction.d.ts +36 -0
  140. package/lib/typescript/src/download/modelExtraction.d.ts.map +1 -0
  141. package/lib/typescript/src/download/paths.d.ts +28 -0
  142. package/lib/typescript/src/download/paths.d.ts.map +1 -0
  143. package/lib/typescript/src/download/postDownloadProcessing.d.ts +19 -0
  144. package/lib/typescript/src/download/postDownloadProcessing.d.ts.map +1 -0
  145. package/lib/typescript/src/download/protectedModelKeys.d.ts +6 -0
  146. package/lib/typescript/src/download/protectedModelKeys.d.ts.map +1 -0
  147. package/lib/typescript/src/download/registry.d.ts +14 -0
  148. package/lib/typescript/src/download/registry.d.ts.map +1 -0
  149. package/lib/typescript/src/download/retry.d.ts +15 -0
  150. package/lib/typescript/src/download/retry.d.ts.map +1 -0
  151. package/lib/typescript/src/download/types.d.ts +96 -0
  152. package/lib/typescript/src/download/types.d.ts.map +1 -0
  153. package/lib/typescript/src/download/validation.d.ts +19 -0
  154. package/lib/typescript/src/download/validation.d.ts.map +1 -1
  155. package/lib/typescript/src/extraction/extractTarBz2.d.ts.map +1 -0
  156. package/lib/typescript/src/extraction/extractTarZst.d.ts.map +1 -0
  157. package/lib/typescript/src/index.d.ts +1 -0
  158. package/lib/typescript/src/index.d.ts.map +1 -1
  159. package/lib/typescript/src/licenses.d.ts +10 -0
  160. package/lib/typescript/src/licenses.d.ts.map +1 -0
  161. package/lib/typescript/src/stt/index.d.ts +4 -1
  162. package/lib/typescript/src/stt/index.d.ts.map +1 -1
  163. package/lib/typescript/src/stt/streaming.d.ts.map +1 -1
  164. package/lib/typescript/src/stt/streamingTypes.d.ts +5 -0
  165. package/lib/typescript/src/stt/streamingTypes.d.ts.map +1 -1
  166. package/lib/typescript/src/stt/types.d.ts +3 -1
  167. package/lib/typescript/src/stt/types.d.ts.map +1 -1
  168. package/lib/typescript/src/tts/index.d.ts +4 -2
  169. package/lib/typescript/src/tts/index.d.ts.map +1 -1
  170. package/lib/typescript/src/tts/streaming.d.ts.map +1 -1
  171. package/lib/typescript/src/tts/types.d.ts +12 -6
  172. package/lib/typescript/src/tts/types.d.ts.map +1 -1
  173. package/lib/typescript/src/utils.d.ts +5 -0
  174. package/lib/typescript/src/utils.d.ts.map +1 -1
  175. package/package.json +6 -1
  176. package/scripts/{check-model-csvs.sh → ci/check-model-csvs.sh} +9 -2
  177. package/scripts/ci/collect_all_sherpa_model_streams.sh +101 -0
  178. package/scripts/ci/collect_one_sherpa_release_stream.sh +189 -0
  179. package/scripts/ci/sherpa_asr_model_release_streams.json +21 -0
  180. package/scripts/ci/sherpa_tts_model_release_streams.json +13 -0
  181. package/scripts/ci/update_model_license_csv.sh +765 -0
  182. package/scripts/setup-ios-framework.sh +14 -11
  183. package/scripts/update_commercial_use.js +73 -0
  184. package/src/NativeSherpaOnnx.ts +37 -6
  185. package/src/audio/index.ts +20 -0
  186. package/src/download/ModelDownloadManager.ts +57 -1343
  187. package/src/download/activeModelOperations.ts +38 -0
  188. package/src/download/background-downloader-types.ts +73 -0
  189. package/src/download/bulkPurge.ts +102 -0
  190. package/src/download/checksumPrompt.ts +25 -0
  191. package/src/download/constants.ts +5 -0
  192. package/src/download/downloadEvents.ts +55 -0
  193. package/src/download/downloadTask.ts +565 -0
  194. package/src/download/ensureModel.ts +124 -0
  195. package/src/download/index.ts +21 -4
  196. package/src/download/localModels.ts +234 -0
  197. package/src/download/modelExtraction.ts +244 -0
  198. package/src/download/paths.ts +134 -0
  199. package/src/download/postDownloadProcessing.ts +292 -0
  200. package/src/download/protectedModelKeys.ts +30 -0
  201. package/src/download/registry.ts +405 -0
  202. package/src/download/retry.ts +76 -0
  203. package/src/download/types.ts +120 -0
  204. package/src/download/validation.ts +114 -8
  205. package/src/{download → extraction}/extractTarBz2.ts +3 -1
  206. package/src/{download → extraction}/extractTarZst.ts +3 -1
  207. package/src/extraction/index.ts +3 -7
  208. package/src/index.tsx +1 -0
  209. package/src/licenses.ts +100 -0
  210. package/src/stt/index.ts +20 -2
  211. package/src/stt/streaming.ts +3 -0
  212. package/src/stt/streamingTypes.ts +5 -0
  213. package/src/stt/types.ts +3 -1
  214. package/src/tts/index.ts +33 -2
  215. package/src/tts/streaming.ts +12 -0
  216. package/src/tts/types.ts +15 -5
  217. package/src/utils.ts +22 -1
  218. package/third_party/sherpa-onnx-prebuilt/ANDROID_RELEASE_TAG +1 -1
  219. package/third_party/sherpa-onnx-prebuilt/IOS_RELEASE_TAG +1 -1
  220. package/android/src/main/cpp/jni/tts/sherpa-onnx-tts-zipvoice-jni.cpp +0 -301
  221. package/android/src/main/java/com/sherpaonnx/ZipvoiceTtsWrapper.kt +0 -187
  222. package/lib/module/download/extractTarBz2.js.map +0 -1
  223. package/lib/module/download/extractTarZst.js.map +0 -1
  224. package/lib/typescript/src/download/extractTarBz2.d.ts.map +0 -1
  225. package/lib/typescript/src/download/extractTarZst.d.ts.map +0 -1
  226. package/scripts/check-qnn-support.sh +0 -78
  227. /package/lib/typescript/src/{download → extraction}/extractTarBz2.d.ts +0 -0
  228. /package/lib/typescript/src/{download → extraction}/extractTarZst.d.ts +0 -0
@@ -4,6 +4,7 @@ import android.content.Context
4
4
  import android.util.Log
5
5
  import com.facebook.react.bridge.Arguments
6
6
  import com.facebook.react.bridge.Promise
7
+ import java.util.concurrent.ConcurrentHashMap
7
8
  import java.util.concurrent.ExecutorService
8
9
  import java.util.concurrent.Executors
9
10
  import java.util.concurrent.atomic.AtomicBoolean
@@ -13,11 +14,12 @@ import java.util.concurrent.atomic.AtomicBoolean
13
14
  * This class delegates to C++ native implementation via JNI.
14
15
  */
15
16
  class SherpaOnnxArchiveHelper {
16
- private val cancelRequested = AtomicBoolean(false)
17
-
18
17
  companion object {
19
- /** Single-thread executor so extractions run off the React Native bridge thread and do not block listDownloadedModelsByCategory / RNFS. */
20
- private val extractExecutor: ExecutorService = Executors.newSingleThreadExecutor()
18
+ /** Thread pool for extractions allows up to 2 concurrent extractions while keeping them off the React Native bridge thread. */
19
+ private val extractExecutor: ExecutorService = Executors.newFixedThreadPool(2)
20
+
21
+ /** Per-source-path cancellation flags. Key = absolute source archive path. */
22
+ private val cancelFlags = ConcurrentHashMap<String, AtomicBoolean>()
21
23
 
22
24
  init {
23
25
  try {
@@ -29,15 +31,24 @@ class SherpaOnnxArchiveHelper {
29
31
  }
30
32
 
31
33
  fun cancelExtractTarBz2() {
32
- cancelRequested.set(true)
34
+ // Cancel ALL ongoing extractions (legacy global cancel)
35
+ for (flag in cancelFlags.values) flag.set(true)
33
36
  nativeCancelExtract()
34
37
  }
35
38
 
36
39
  fun cancelExtractTarZst() {
37
- cancelRequested.set(true)
40
+ // Cancel ALL ongoing extractions (legacy global cancel)
41
+ for (flag in cancelFlags.values) flag.set(true)
38
42
  nativeCancelExtract()
39
43
  }
40
44
 
45
+ /** Cancel a specific extraction identified by its source archive path. */
46
+ fun cancelExtractBySourcePath(sourcePath: String) {
47
+ // Only set the per-path flag; do not call nativeCancelExtract() since that is
48
+ // a global cancel that would also interrupt unrelated concurrent extractions.
49
+ cancelFlags[sourcePath]?.set(true)
50
+ }
51
+
41
52
  fun extractTarBz2(
42
53
  sourcePath: String,
43
54
  targetPath: String,
@@ -55,7 +66,9 @@ class SherpaOnnxArchiveHelper {
55
66
  }
56
67
 
57
68
  try {
58
- cancelRequested.set(false)
69
+ // Register per-path cancel flag
70
+ val cancelFlag = AtomicBoolean(false)
71
+ cancelFlags[sourcePath] = cancelFlag
59
72
 
60
73
  // Create a progress callback object that JNI can call
61
74
  val progressCallback = object : Any() {
@@ -65,15 +78,23 @@ class SherpaOnnxArchiveHelper {
65
78
  }
66
79
 
67
80
  // Run extraction on a background thread so the React Native bridge thread is not blocked.
68
- // Otherwise listDownloadedModelsByCategory (RNFS) and other native calls would wait until extraction finishes.
81
+ // The thread pool allows multiple extractions in parallel.
69
82
  extractExecutor.execute {
70
83
  try {
84
+ // Check per-path cancel flag before starting the native extraction.
85
+ if (cancelFlag.get()) {
86
+ resolveOnce(false, "Cancelled")
87
+ return@execute
88
+ }
71
89
  nativeExtractTarBz2(sourcePath, targetPath, force, progressCallback, promise)
72
90
  } catch (e: Exception) {
73
91
  resolveOnce(false, "Archive extraction error: ${e.message}")
92
+ } finally {
93
+ cancelFlags.remove(sourcePath)
74
94
  }
75
95
  }
76
96
  } catch (e: Exception) {
97
+ cancelFlags.remove(sourcePath)
77
98
  resolveOnce(false, "Archive extraction error: ${e.message}")
78
99
  }
79
100
  }
@@ -95,7 +116,9 @@ class SherpaOnnxArchiveHelper {
95
116
  }
96
117
 
97
118
  try {
98
- cancelRequested.set(false)
119
+ val cancelFlag = AtomicBoolean(false)
120
+ cancelFlags[sourcePath] = cancelFlag
121
+
99
122
  val progressCallback = object : Any() {
100
123
  fun invoke(bytesExtracted: Long, totalBytes: Long, percent: Double) {
101
124
  onProgress(bytesExtracted, totalBytes, percent)
@@ -103,12 +126,20 @@ class SherpaOnnxArchiveHelper {
103
126
  }
104
127
  extractExecutor.execute {
105
128
  try {
129
+ // Check per-path cancel flag before starting the native extraction.
130
+ if (cancelFlag.get()) {
131
+ resolveOnce(false, "Cancelled")
132
+ return@execute
133
+ }
106
134
  nativeExtractTarZst(sourcePath, targetPath, force, progressCallback, promise)
107
135
  } catch (e: Exception) {
108
136
  resolveOnce(false, "Archive extraction error: ${e.message}")
137
+ } finally {
138
+ cancelFlags.remove(sourcePath)
109
139
  }
110
140
  }
111
141
  } catch (e: Exception) {
142
+ cancelFlags.remove(sourcePath)
112
143
  resolveOnce(false, "Archive extraction error: ${e.message}")
113
144
  }
114
145
  }
@@ -124,7 +155,6 @@ class SherpaOnnxArchiveHelper {
124
155
  if (BuildConfig.DEBUG) {
125
156
  Log.i("SherpaOnnx", "extractTarZstFromAsset assetPath=$assetPath targetPath=$targetPath")
126
157
  }
127
- cancelRequested.set(false)
128
158
  val progressCallback = object : Any() {
129
159
  fun invoke(bytesExtracted: Long, totalBytes: Long, percent: Double) {
130
160
  onProgress(bytesExtracted, totalBytes, percent)
@@ -8,6 +8,7 @@ import com.facebook.react.bridge.ReadableMap
8
8
  import com.facebook.react.bridge.Arguments
9
9
  import com.facebook.react.module.annotations.ReactModule
10
10
  import com.facebook.react.modules.core.DeviceEventManagerModule
11
+ import com.k2fsa.sherpa.onnx.WaveReader
11
12
 
12
13
  @ReactModule(name = SherpaOnnxModule.NAME)
13
14
  class SherpaOnnxModule(reactContext: ReactApplicationContext) :
@@ -314,6 +315,11 @@ class SherpaOnnxModule(reactContext: ReactApplicationContext) :
314
315
  promise.resolve(null)
315
316
  }
316
317
 
318
+ override fun cancelExtractBySourcePath(sourcePath: String, promise: Promise) {
319
+ archiveHelper.cancelExtractBySourcePath(sourcePath)
320
+ promise.resolve(null)
321
+ }
322
+
317
323
  override fun computeFileSha256(filePath: String, promise: Promise) {
318
324
  archiveHelper.computeFileSha256(filePath, promise)
319
325
  }
@@ -452,6 +458,7 @@ class SherpaOnnxModule(reactContext: ReactApplicationContext) :
452
458
  val provider = if (options.hasKey("provider")) options.getString("provider") else null
453
459
  val ruleFsts = if (options.hasKey("ruleFsts")) options.getString("ruleFsts") else null
454
460
  val ruleFars = if (options.hasKey("ruleFars")) options.getString("ruleFars") else null
461
+ val dither = if (options.hasKey("dither")) options.getDouble("dither") else null
455
462
  val blankPenalty = if (options.hasKey("blankPenalty")) options.getDouble("blankPenalty") else null
456
463
  val debug = if (options.hasKey("debug")) options.getBoolean("debug") else null
457
464
  val rule1MustContainNonSilence = if (options.hasKey("rule1MustContainNonSilence")) options.getBoolean("rule1MustContainNonSilence") else null
@@ -476,6 +483,7 @@ class SherpaOnnxModule(reactContext: ReactApplicationContext) :
476
483
  provider,
477
484
  ruleFsts,
478
485
  ruleFars,
486
+ dither,
479
487
  blankPenalty,
480
488
  debug,
481
489
  rule1MustContainNonSilence,
@@ -712,6 +720,72 @@ class SherpaOnnxModule(reactContext: ReactApplicationContext) :
712
720
  }
713
721
  }
714
722
 
723
+ /**
724
+ * Decode audio to mono float samples (approx. [-1, 1]) and effective sample rate.
725
+ * Same path/URI handling as [convertAudioToFormat]. WAV may use [WaveReader] when no resample is requested.
726
+ */
727
+ override fun decodeAudioFileToFloatSamples(inputPath: String, targetSampleRateHz: Double?, promise: Promise) {
728
+ var tmpFile: java.io.File? = null
729
+ try {
730
+ val targetHz = (targetSampleRateHz ?: 0.0).toInt()
731
+ if (targetHz < 0) {
732
+ promise.reject("DECODE_ERROR", "targetSampleRateHz must be >= 0")
733
+ return
734
+ }
735
+ val (pathToUse, tmp) = resolveInputForConvert(inputPath)
736
+ tmpFile = tmp
737
+
738
+ if (pathToUse.endsWith(".wav", ignoreCase = true)) {
739
+ try {
740
+ val wave = WaveReader.readWave(pathToUse)
741
+ val s = wave.samples
742
+ if (s != null && s.isNotEmpty() && wave.sampleRate > 0 && (targetHz == 0 || targetHz == wave.sampleRate)) {
743
+ val map = Arguments.createMap()
744
+ val arr = Arguments.createArray()
745
+ for (i in s.indices) {
746
+ arr.pushDouble(s[i].toDouble())
747
+ }
748
+ map.putArray("samples", arr)
749
+ map.putInt("sampleRate", wave.sampleRate)
750
+ promise.resolve(map)
751
+ return
752
+ }
753
+ } catch (_: Throwable) {
754
+ // Fall through to FFmpeg/native path (e.g. odd WAV or resample requested).
755
+ }
756
+ }
757
+
758
+ val result = Companion.nativeDecodeAudioFileToFloatSamples(pathToUse, targetHz)
759
+ if (result.size == 1 && result[0] is String) {
760
+ promise.reject("DECODE_ERROR", result[0] as String)
761
+ return
762
+ }
763
+ if (result.size != 2 || result[0] !is FloatArray) {
764
+ promise.reject("DECODE_ERROR", "Unexpected native decode result")
765
+ return
766
+ }
767
+ val floats = result[0] as FloatArray
768
+ val rateObj = result.getOrNull(1) as? Number ?: run {
769
+ promise.reject("DECODE_ERROR", "Unexpected sample rate in native decode result")
770
+ return
771
+ }
772
+ val sr = rateObj.toInt()
773
+ val map = Arguments.createMap()
774
+ val arr = Arguments.createArray()
775
+ for (i in floats.indices) {
776
+ arr.pushDouble(floats[i].toDouble())
777
+ }
778
+ map.putArray("samples", arr)
779
+ map.putInt("sampleRate", sr)
780
+ promise.resolve(map)
781
+ } catch (e: Exception) {
782
+ android.util.Log.e(NAME, "DECODE_EXCEPTION: ${e.message}", e)
783
+ promise.reject("DECODE_EXCEPTION", e.message ?: "Failed to decode audio", e)
784
+ } finally {
785
+ tmpFile?.delete()
786
+ }
787
+ }
788
+
715
789
  // ==================== TTS Methods ====================
716
790
 
717
791
  /**
@@ -1057,6 +1131,25 @@ class SherpaOnnxModule(reactContext: ReactApplicationContext) :
1057
1131
  }
1058
1132
  }
1059
1133
 
1134
+ override fun readAssetFileAsUtf8(assetPath: String, promise: Promise) {
1135
+ // Validate assetPath to prevent path traversal: reject paths containing
1136
+ // "..", starting with "/" or "\", or containing backslashes.
1137
+ if (assetPath.contains("..") ||
1138
+ assetPath.startsWith("/") ||
1139
+ assetPath.startsWith("\\") ||
1140
+ assetPath.contains("\\")) {
1141
+ promise.reject("ASSET_READ_ERROR", "Invalid asset path: $assetPath")
1142
+ return
1143
+ }
1144
+ try {
1145
+ val content = reactApplicationContext.assets.open(assetPath).bufferedReader().use { it.readText() }
1146
+ promise.resolve(content)
1147
+ } catch (e: Exception) {
1148
+ android.util.Log.e(NAME, "Failed to read asset $assetPath: ${e.message}", e)
1149
+ promise.reject("ASSET_READ_ERROR", "Failed to read asset $assetPath: ${e.message}", e)
1150
+ }
1151
+ }
1152
+
1060
1153
  companion object {
1061
1154
  const val NAME = "SherpaOnnx"
1062
1155
 
@@ -1099,5 +1192,11 @@ class SherpaOnnxModule(reactContext: ReactApplicationContext) :
1099
1192
  /** Convert any supported audio file to WAV 16 kHz mono 16-bit PCM. Returns empty string on success, error message otherwise. Requires FFmpeg prebuilts. */
1100
1193
  @JvmStatic
1101
1194
  private external fun nativeConvertAudioToWav16k(inputPath: String, outputPath: String): String
1195
+
1196
+ /**
1197
+ * On success: [FloatArray samples, Integer sampleRate]. On error: [String message].
1198
+ */
1199
+ @JvmStatic
1200
+ private external fun nativeDecodeAudioFileToFloatSamples(inputPath: String, targetSampleRateHz: Int): Array<Any>
1102
1201
  }
1103
1202
  }
@@ -132,6 +132,7 @@ internal class SherpaOnnxOnlineSttHelper(
132
132
  provider: String?,
133
133
  ruleFsts: String?,
134
134
  ruleFars: String?,
135
+ dither: Float?,
135
136
  blankPenalty: Float?,
136
137
  debug: Boolean?,
137
138
  rule1MustContainNonSilence: Boolean?,
@@ -233,7 +234,7 @@ internal class SherpaOnnxOnlineSttHelper(
233
234
  }
234
235
 
235
236
  return OnlineRecognizerConfig(
236
- featConfig = FeatureConfig(sampleRate = 16000, featureDim = 80, dither = 0f),
237
+ featConfig = FeatureConfig(sampleRate = 16000, featureDim = 80, dither = dither ?: 0f),
237
238
  modelConfig = modelConfig,
238
239
  endpointConfig = endpointConfig,
239
240
  enableEndpoint = enableEndpoint,
@@ -260,6 +261,7 @@ internal class SherpaOnnxOnlineSttHelper(
260
261
  provider: String?,
261
262
  ruleFsts: String?,
262
263
  ruleFars: String?,
264
+ dither: Double?,
263
265
  blankPenalty: Double?,
264
266
  debug: Boolean?,
265
267
  rule1MustContainNonSilence: Boolean?,
@@ -286,6 +288,7 @@ internal class SherpaOnnxOnlineSttHelper(
286
288
  provider = provider,
287
289
  ruleFsts = ruleFsts,
288
290
  ruleFars = ruleFars,
291
+ dither = dither?.toFloat(),
289
292
  blankPenalty = blankPenalty?.toFloat(),
290
293
  debug = debug,
291
294
  rule1MustContainNonSilence = rule1MustContainNonSilence,