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.
- package/LICENSE +1 -0
- package/README.md +89 -21
- package/SherpaOnnx.podspec +3 -0
- package/THIRD_PARTY_LICENSES/README.md +62 -0
- package/THIRD_PARTY_LICENSES/ffmpeg.txt +502 -0
- package/THIRD_PARTY_LICENSES/libarchive.txt +65 -0
- package/THIRD_PARTY_LICENSES/nvidia_omla.txt +181 -0
- package/THIRD_PARTY_LICENSES/onnxruntime.txt +21 -0
- package/THIRD_PARTY_LICENSES/opus.txt +44 -0
- package/THIRD_PARTY_LICENSES/sherpa-onnx.txt +201 -0
- package/THIRD_PARTY_LICENSES/shine.txt +482 -0
- package/THIRD_PARTY_LICENSES/zstd.txt +30 -0
- package/android/build.gradle +7 -3
- package/android/prebuilt-download.gradle +344 -152
- package/android/prebuilt-versions.gradle +1 -1
- package/android/src/main/assets/model_licenses/asr-models-license-status.csv +409 -0
- package/android/src/main/assets/model_licenses/qnn-asr-models-license-status.csv +695 -0
- package/android/src/main/assets/model_licenses/tts-models-license-status.csv +596 -0
- package/android/src/main/cpp/CMakeLists.txt +28 -10
- package/android/src/main/cpp/jni/archive/sherpa-onnx-archive-helper.cpp +2 -2
- package/android/src/main/cpp/jni/audio/sherpa-onnx-audio-convert-jni.cpp +268 -2
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-model-detect-tts.cpp +6 -2
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-validate-tts.cpp +4 -2
- package/android/src/main/java/com/sherpaonnx/SherpaOnnxArchiveHelper.kt +40 -10
- package/android/src/main/java/com/sherpaonnx/SherpaOnnxModule.kt +99 -0
- package/android/src/main/java/com/sherpaonnx/SherpaOnnxOnlineSttHelper.kt +4 -1
- package/android/src/main/java/com/sherpaonnx/SherpaOnnxTtsHelper.kt +112 -97
- package/ios/Resources/model_licenses/asr-models-license-status.csv +409 -0
- package/ios/Resources/model_licenses/qnn-asr-models-license-status.csv +695 -0
- package/ios/Resources/model_licenses/tts-models-license-status.csv +596 -0
- package/ios/SherpaOnnx+OnlineSTT.mm +2 -0
- package/ios/SherpaOnnx+PcmLiveStream.mm +2 -29
- package/ios/SherpaOnnx+TTS.mm +178 -20
- package/ios/SherpaOnnx.mm +54 -0
- package/ios/SherpaOnnxAudioConvert.h +10 -0
- package/ios/SherpaOnnxAudioConvert.mm +257 -1
- package/ios/archive/sherpa-onnx-archive-helper.h +3 -0
- package/ios/archive/sherpa-onnx-archive-helper.mm +39 -6
- package/ios/model_detect/sherpa-onnx-model-detect-tts.mm +13 -2
- package/ios/model_detect/sherpa-onnx-validate-tts.mm +4 -2
- package/ios/online_stt/sherpa-onnx-online-stt-wrapper.h +1 -0
- package/ios/online_stt/sherpa-onnx-online-stt-wrapper.mm +4 -0
- package/ios/tts/sherpa-onnx-tts-wrapper.h +37 -0
- package/ios/tts/sherpa-onnx-tts-wrapper.mm +149 -3
- package/lib/module/NativeSherpaOnnx.js.map +1 -1
- package/lib/module/audio/index.js +8 -0
- package/lib/module/audio/index.js.map +1 -1
- package/lib/module/download/ModelDownloadManager.js +10 -929
- package/lib/module/download/ModelDownloadManager.js.map +1 -1
- package/lib/module/download/activeModelOperations.js +26 -0
- package/lib/module/download/activeModelOperations.js.map +1 -0
- package/lib/module/download/background-downloader.d.js +2 -0
- package/lib/module/download/background-downloader.d.js.map +1 -0
- package/lib/module/download/bulkPurge.js +72 -0
- package/lib/module/download/bulkPurge.js.map +1 -0
- package/lib/module/download/checksumPrompt.js +19 -0
- package/lib/module/download/checksumPrompt.js.map +1 -0
- package/lib/module/download/constants.js +7 -0
- package/lib/module/download/constants.js.map +1 -0
- package/lib/module/download/downloadEvents.js +35 -0
- package/lib/module/download/downloadEvents.js.map +1 -0
- package/lib/module/download/downloadTask.js +385 -0
- package/lib/module/download/downloadTask.js.map +1 -0
- package/lib/module/download/ensureModel.js +89 -0
- package/lib/module/download/ensureModel.js.map +1 -0
- package/lib/module/download/index.js +4 -4
- package/lib/module/download/index.js.map +1 -1
- package/lib/module/download/localModels.js +151 -0
- package/lib/module/download/localModels.js.map +1 -0
- package/lib/module/download/modelExtraction.js +174 -0
- package/lib/module/download/modelExtraction.js.map +1 -0
- package/lib/module/download/paths.js +98 -0
- package/lib/module/download/paths.js.map +1 -0
- package/lib/module/download/postDownloadProcessing.js +206 -0
- package/lib/module/download/postDownloadProcessing.js.map +1 -0
- package/lib/module/download/protectedModelKeys.js +31 -0
- package/lib/module/download/protectedModelKeys.js.map +1 -0
- package/lib/module/download/registry.js +267 -0
- package/lib/module/download/registry.js.map +1 -0
- package/lib/module/download/retry.js +59 -0
- package/lib/module/download/retry.js.map +1 -0
- package/lib/module/download/types.js +17 -0
- package/lib/module/download/types.js.map +1 -0
- package/lib/module/download/validation.js +101 -5
- package/lib/module/download/validation.js.map +1 -1
- package/lib/module/{download → extraction}/extractTarBz2.js +3 -1
- package/lib/module/extraction/extractTarBz2.js.map +1 -0
- package/lib/module/{download → extraction}/extractTarZst.js +3 -1
- package/lib/module/extraction/extractTarZst.js.map +1 -0
- package/lib/module/extraction/index.js +3 -4
- package/lib/module/extraction/index.js.map +1 -1
- package/lib/module/index.js +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/licenses.js +63 -0
- package/lib/module/licenses.js.map +1 -0
- package/lib/module/stt/index.js +16 -2
- package/lib/module/stt/index.js.map +1 -1
- package/lib/module/stt/streaming.js +2 -0
- package/lib/module/stt/streaming.js.map +1 -1
- package/lib/module/stt/streamingTypes.js.map +1 -1
- package/lib/module/stt/types.js.map +1 -1
- package/lib/module/tts/index.js +20 -2
- package/lib/module/tts/index.js.map +1 -1
- package/lib/module/tts/streaming.js +4 -0
- package/lib/module/tts/streaming.js.map +1 -1
- package/lib/module/tts/types.js.map +1 -1
- package/lib/module/utils.js +16 -1
- package/lib/module/utils.js.map +1 -1
- package/lib/typescript/src/NativeSherpaOnnx.d.ts +33 -5
- package/lib/typescript/src/NativeSherpaOnnx.d.ts.map +1 -1
- package/lib/typescript/src/audio/index.d.ts +10 -0
- package/lib/typescript/src/audio/index.d.ts.map +1 -1
- package/lib/typescript/src/download/ModelDownloadManager.d.ts +10 -108
- package/lib/typescript/src/download/ModelDownloadManager.d.ts.map +1 -1
- package/lib/typescript/src/download/activeModelOperations.d.ts +6 -0
- package/lib/typescript/src/download/activeModelOperations.d.ts.map +1 -0
- package/lib/typescript/src/download/bulkPurge.d.ts +14 -0
- package/lib/typescript/src/download/bulkPurge.d.ts.map +1 -0
- package/lib/typescript/src/download/checksumPrompt.d.ts +3 -0
- package/lib/typescript/src/download/checksumPrompt.d.ts.map +1 -0
- package/lib/typescript/src/download/constants.d.ts +5 -0
- package/lib/typescript/src/download/constants.d.ts.map +1 -0
- package/lib/typescript/src/download/downloadEvents.d.ts +6 -0
- package/lib/typescript/src/download/downloadEvents.d.ts.map +1 -0
- package/lib/typescript/src/download/downloadTask.d.ts +20 -0
- package/lib/typescript/src/download/downloadTask.d.ts.map +1 -0
- package/lib/typescript/src/download/ensureModel.d.ts +26 -0
- package/lib/typescript/src/download/ensureModel.d.ts.map +1 -0
- package/lib/typescript/src/download/index.d.ts +7 -7
- package/lib/typescript/src/download/index.d.ts.map +1 -1
- package/lib/typescript/src/download/localModels.d.ts +15 -0
- package/lib/typescript/src/download/localModels.d.ts.map +1 -0
- package/lib/typescript/src/download/modelExtraction.d.ts +36 -0
- package/lib/typescript/src/download/modelExtraction.d.ts.map +1 -0
- package/lib/typescript/src/download/paths.d.ts +28 -0
- package/lib/typescript/src/download/paths.d.ts.map +1 -0
- package/lib/typescript/src/download/postDownloadProcessing.d.ts +19 -0
- package/lib/typescript/src/download/postDownloadProcessing.d.ts.map +1 -0
- package/lib/typescript/src/download/protectedModelKeys.d.ts +6 -0
- package/lib/typescript/src/download/protectedModelKeys.d.ts.map +1 -0
- package/lib/typescript/src/download/registry.d.ts +14 -0
- package/lib/typescript/src/download/registry.d.ts.map +1 -0
- package/lib/typescript/src/download/retry.d.ts +15 -0
- package/lib/typescript/src/download/retry.d.ts.map +1 -0
- package/lib/typescript/src/download/types.d.ts +96 -0
- package/lib/typescript/src/download/types.d.ts.map +1 -0
- package/lib/typescript/src/download/validation.d.ts +19 -0
- package/lib/typescript/src/download/validation.d.ts.map +1 -1
- package/lib/typescript/src/extraction/extractTarBz2.d.ts.map +1 -0
- package/lib/typescript/src/extraction/extractTarZst.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +1 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/licenses.d.ts +10 -0
- package/lib/typescript/src/licenses.d.ts.map +1 -0
- package/lib/typescript/src/stt/index.d.ts +4 -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/streamingTypes.d.ts +5 -0
- package/lib/typescript/src/stt/streamingTypes.d.ts.map +1 -1
- package/lib/typescript/src/stt/types.d.ts +3 -1
- package/lib/typescript/src/stt/types.d.ts.map +1 -1
- package/lib/typescript/src/tts/index.d.ts +3 -1
- package/lib/typescript/src/tts/index.d.ts.map +1 -1
- package/lib/typescript/src/tts/streaming.d.ts.map +1 -1
- package/lib/typescript/src/tts/types.d.ts +6 -5
- package/lib/typescript/src/tts/types.d.ts.map +1 -1
- package/lib/typescript/src/utils.d.ts +5 -0
- package/lib/typescript/src/utils.d.ts.map +1 -1
- package/package.json +6 -1
- package/scripts/{check-model-csvs.sh → ci/check-model-csvs.sh} +9 -2
- package/scripts/ci/collect_all_sherpa_model_streams.sh +101 -0
- package/scripts/ci/collect_one_sherpa_release_stream.sh +189 -0
- package/scripts/ci/sherpa_asr_model_release_streams.json +21 -0
- package/scripts/ci/sherpa_tts_model_release_streams.json +13 -0
- package/scripts/ci/update_model_license_csv.sh +765 -0
- package/scripts/setup-ios-framework.sh +14 -11
- package/scripts/update_commercial_use.js +73 -0
- package/src/NativeSherpaOnnx.ts +36 -5
- package/src/audio/index.ts +20 -0
- package/src/download/ModelDownloadManager.ts +55 -1343
- package/src/download/activeModelOperations.ts +38 -0
- package/src/download/background-downloader.d.ts +43 -0
- package/src/download/bulkPurge.ts +102 -0
- package/src/download/checksumPrompt.ts +25 -0
- package/src/download/constants.ts +5 -0
- package/src/download/downloadEvents.ts +55 -0
- package/src/download/downloadTask.ts +497 -0
- package/src/download/ensureModel.ts +124 -0
- package/src/download/index.ts +19 -4
- package/src/download/localModels.ts +234 -0
- package/src/download/modelExtraction.ts +244 -0
- package/src/download/paths.ts +134 -0
- package/src/download/postDownloadProcessing.ts +292 -0
- package/src/download/protectedModelKeys.ts +30 -0
- package/src/download/registry.ts +404 -0
- package/src/download/retry.ts +76 -0
- package/src/download/types.ts +120 -0
- package/src/download/validation.ts +114 -8
- package/src/{download → extraction}/extractTarBz2.ts +3 -1
- package/src/{download → extraction}/extractTarZst.ts +3 -1
- package/src/extraction/index.ts +3 -7
- package/src/index.tsx +1 -0
- package/src/licenses.ts +100 -0
- package/src/stt/index.ts +20 -2
- package/src/stt/streaming.ts +3 -0
- package/src/stt/streamingTypes.ts +5 -0
- package/src/stt/types.ts +3 -1
- package/src/tts/index.ts +30 -2
- package/src/tts/streaming.ts +10 -0
- package/src/tts/types.ts +6 -5
- package/src/utils.ts +22 -1
- package/third_party/sherpa-onnx-prebuilt/ANDROID_RELEASE_TAG +1 -1
- package/third_party/sherpa-onnx-prebuilt/IOS_RELEASE_TAG +1 -1
- package/android/src/main/cpp/jni/tts/sherpa-onnx-tts-zipvoice-jni.cpp +0 -301
- package/android/src/main/java/com/sherpaonnx/ZipvoiceTtsWrapper.kt +0 -187
- package/lib/module/download/extractTarBz2.js.map +0 -1
- package/lib/module/download/extractTarZst.js.map +0 -1
- package/lib/typescript/src/download/extractTarBz2.d.ts.map +0 -1
- package/lib/typescript/src/download/extractTarZst.d.ts.map +0 -1
- package/scripts/check-qnn-support.sh +0 -78
- /package/lib/typescript/src/{download → extraction}/extractTarBz2.d.ts +0 -0
- /package/lib/typescript/src/{download → extraction}/extractTarZst.d.ts +0 -0
|
@@ -29,7 +29,7 @@ get_filename_component(PROJECT_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../../.." ABSOL
|
|
|
29
29
|
set(USE_FFMPEG ON)
|
|
30
30
|
if(SHERPA_ONNX_DISABLE_FFMPEG)
|
|
31
31
|
set(USE_FFMPEG OFF)
|
|
32
|
-
message(STATUS "FFmpeg disabled (SHERPA_ONNX_DISABLE_FFMPEG=ON). convertAudioToWav16k/convertAudioToFormat will return an error at runtime.")
|
|
32
|
+
message(STATUS "FFmpeg disabled (SHERPA_ONNX_DISABLE_FFMPEG=ON). convertAudioToWav16k/convertAudioToFormat and decode (non-WAV) will return an error at runtime.")
|
|
33
33
|
endif()
|
|
34
34
|
|
|
35
35
|
if(USE_FFMPEG)
|
|
@@ -39,25 +39,32 @@ set(FFMPEG_PREBUILT_BASE "${PROJECT_ROOT}/../third_party/ffmpeg_prebuilt/android
|
|
|
39
39
|
set(FFMPEG_PREBUILT_LIB "${FFMPEG_PREBUILT_BASE}/${ANDROID_ABI}/lib")
|
|
40
40
|
set(FFMPEG_JNILIBS "${PROJECT_ROOT}/src/main/jniLibs/${ANDROID_ABI}")
|
|
41
41
|
set(FFMPEG_INCLUDE_CPP "${PROJECT_ROOT}/src/main/cpp/include/ffmpeg")
|
|
42
|
-
|
|
42
|
+
# Require a real header — an empty third_party/.../include/ dir must not win over Gradle-populated cpp/include/ffmpeg.
|
|
43
|
+
if(EXISTS "${FFMPEG_PREBUILT_BASE}/include/libavcodec/avcodec.h")
|
|
43
44
|
set(FFMPEG_INCLUDE_DIR "${FFMPEG_PREBUILT_BASE}/include")
|
|
44
45
|
message(STATUS "FFmpeg headers: prebuilts ${FFMPEG_INCLUDE_DIR}")
|
|
45
|
-
elseif(EXISTS "${FFMPEG_INCLUDE_CPP}")
|
|
46
|
+
elseif(EXISTS "${FFMPEG_INCLUDE_CPP}/libavcodec/avcodec.h")
|
|
46
47
|
set(FFMPEG_INCLUDE_DIR "${FFMPEG_INCLUDE_CPP}")
|
|
47
|
-
message(STATUS "FFmpeg headers:
|
|
48
|
+
message(STATUS "FFmpeg headers: module tree ${FFMPEG_INCLUDE_DIR}")
|
|
48
49
|
else()
|
|
49
50
|
message(FATAL_ERROR "FFmpeg headers missing. Either:\n"
|
|
50
51
|
" - Build: cd third_party/ffmpeg_prebuilt && ./build_ffmpeg.sh (creates android/include)\n"
|
|
51
52
|
" - Or use a release that includes include/ (Gradle extracts to ${FFMPEG_INCLUDE_CPP})")
|
|
52
53
|
endif()
|
|
54
|
+
# Prebuilts may be either legacy layout android/<abi>/lib/ (build_ffmpeg.sh) or
|
|
55
|
+
# android/jni/<abi>/ (same as Gradle THIRD_PARTY in docs/PREBUILT_RESOLUTION.md).
|
|
56
|
+
set(FFMPEG_PREBUILT_JNI_ABI "${FFMPEG_PREBUILT_BASE}/jni/${ANDROID_ABI}")
|
|
53
57
|
if(EXISTS "${FFMPEG_PREBUILT_LIB}/libavcodec.so")
|
|
54
58
|
set(FFMPEG_LIB_DIR "${FFMPEG_PREBUILT_LIB}")
|
|
55
59
|
message(STATUS "FFmpeg libs: prebuilts ${FFMPEG_LIB_DIR}")
|
|
60
|
+
elseif(EXISTS "${FFMPEG_PREBUILT_JNI_ABI}/libavcodec.so")
|
|
61
|
+
set(FFMPEG_LIB_DIR "${FFMPEG_PREBUILT_JNI_ABI}")
|
|
62
|
+
message(STATUS "FFmpeg libs: prebuilts (jni/<abi>) ${FFMPEG_LIB_DIR}")
|
|
56
63
|
elseif(EXISTS "${FFMPEG_JNILIBS}/libavcodec.so")
|
|
57
64
|
set(FFMPEG_LIB_DIR "${FFMPEG_JNILIBS}")
|
|
58
65
|
message(STATUS "FFmpeg libs: jniLibs ${FFMPEG_LIB_DIR}")
|
|
59
66
|
else()
|
|
60
|
-
message(FATAL_ERROR "FFmpeg libs missing for ABI ${ANDROID_ABI}. Run
|
|
67
|
+
message(FATAL_ERROR "FFmpeg libs missing for ABI ${ANDROID_ABI}. Run a Gradle Android build so prebuilts populate jniLibs, or add local .so under jniLibs / ffmpeg prebuilts paths.")
|
|
61
68
|
endif()
|
|
62
69
|
endif(USE_FFMPEG)
|
|
63
70
|
|
|
@@ -82,7 +89,6 @@ set(SOURCES
|
|
|
82
89
|
jni/model_detect/sherpa-onnx-stt-wrapper.cpp
|
|
83
90
|
jni/model_detect/sherpa-onnx-tts-wrapper.cpp
|
|
84
91
|
jni/audio/sherpa-onnx-audio-convert-jni.cpp
|
|
85
|
-
jni/tts/sherpa-onnx-tts-zipvoice-jni.cpp
|
|
86
92
|
crypto/sha256.cpp
|
|
87
93
|
)
|
|
88
94
|
|
|
@@ -93,10 +99,19 @@ set(LIBARCHIVE_PREBUILT_LIB "${LIBARCHIVE_PREBUILT_BASE}/${ANDROID_ABI}/lib")
|
|
|
93
99
|
set(LIBARCHIVE_JNILIBS "${PROJECT_ROOT}/src/main/jniLibs/${ANDROID_ABI}")
|
|
94
100
|
set(LIBARCHIVE_INCLUDE_CPP "${PROJECT_ROOT}/src/main/cpp/include/libarchive")
|
|
95
101
|
set(USE_LIBARCHIVE_PREBUILT OFF)
|
|
102
|
+
set(LIBARCHIVE_PREBUILT_JNI_ABI "${LIBARCHIVE_PREBUILT_BASE}/jni/${ANDROID_ABI}")
|
|
96
103
|
if(EXISTS "${LIBARCHIVE_PREBUILT_LIB}/libarchive.so")
|
|
97
104
|
set(USE_LIBARCHIVE_PREBUILT ON)
|
|
98
105
|
set(LIBARCHIVE_LIB_DIR "${LIBARCHIVE_PREBUILT_LIB}")
|
|
99
|
-
if(EXISTS "${LIBARCHIVE_PREBUILT_BASE}/include")
|
|
106
|
+
if(EXISTS "${LIBARCHIVE_PREBUILT_BASE}/include/archive.h")
|
|
107
|
+
set(LIBARCHIVE_INCLUDE_DIR "${LIBARCHIVE_PREBUILT_BASE}/include")
|
|
108
|
+
elseif(EXISTS "${LIBARCHIVE_INCLUDE_CPP}/archive.h")
|
|
109
|
+
set(LIBARCHIVE_INCLUDE_DIR "${LIBARCHIVE_INCLUDE_CPP}")
|
|
110
|
+
endif()
|
|
111
|
+
elseif(EXISTS "${LIBARCHIVE_PREBUILT_JNI_ABI}/libarchive.so")
|
|
112
|
+
set(USE_LIBARCHIVE_PREBUILT ON)
|
|
113
|
+
set(LIBARCHIVE_LIB_DIR "${LIBARCHIVE_PREBUILT_JNI_ABI}")
|
|
114
|
+
if(EXISTS "${LIBARCHIVE_PREBUILT_BASE}/include/archive.h")
|
|
100
115
|
set(LIBARCHIVE_INCLUDE_DIR "${LIBARCHIVE_PREBUILT_BASE}/include")
|
|
101
116
|
elseif(EXISTS "${LIBARCHIVE_INCLUDE_CPP}/archive.h")
|
|
102
117
|
set(LIBARCHIVE_INCLUDE_DIR "${LIBARCHIVE_INCLUDE_CPP}")
|
|
@@ -128,15 +143,19 @@ add_library(sherpaonnx SHARED
|
|
|
128
143
|
# If we used IMPORTED here, AGP would also copy .so from CMake --> duplicate in mergeNativeLibs.
|
|
129
144
|
set(SHERPA_ONNX_PREBUILT_BASE "${PROJECT_ROOT}/../third_party/sherpa-onnx-prebuilt/android")
|
|
130
145
|
set(SHERPA_ONNX_ABI_LIB "${SHERPA_ONNX_PREBUILT_BASE}/${ANDROID_ABI}/lib")
|
|
146
|
+
set(SHERPA_ONNX_JNI_ABI "${SHERPA_ONNX_PREBUILT_BASE}/jni/${ANDROID_ABI}")
|
|
131
147
|
set(SHERPA_C_API_LIB_DIR "")
|
|
132
148
|
if(EXISTS "${SHERPA_ONNX_ABI_LIB}/libsherpa-onnx-c-api.so")
|
|
133
149
|
set(SHERPA_C_API_LIB_DIR "${SHERPA_ONNX_ABI_LIB}")
|
|
134
150
|
message(STATUS "sherpa-onnx C-API (link only): ${SHERPA_C_API_LIB_DIR}")
|
|
151
|
+
elseif(EXISTS "${SHERPA_ONNX_JNI_ABI}/libsherpa-onnx-c-api.so")
|
|
152
|
+
set(SHERPA_C_API_LIB_DIR "${SHERPA_ONNX_JNI_ABI}")
|
|
153
|
+
message(STATUS "sherpa-onnx C-API (link only, third_party jni/<abi>): ${SHERPA_C_API_LIB_DIR}")
|
|
135
154
|
elseif(EXISTS "${PROJECT_ROOT}/src/main/jniLibs/${ANDROID_ABI}/libsherpa-onnx-c-api.so")
|
|
136
155
|
set(SHERPA_C_API_LIB_DIR "${PROJECT_ROOT}/src/main/jniLibs/${ANDROID_ABI}")
|
|
137
156
|
message(STATUS "sherpa-onnx C-API (link only, jniLibs): ${SHERPA_C_API_LIB_DIR}")
|
|
138
157
|
else()
|
|
139
|
-
message(WARNING "sherpa-onnx C-API not found.
|
|
158
|
+
message(WARNING "sherpa-onnx C-API not found. Prebuilt sherpa native libs may be incomplete. "
|
|
140
159
|
"Build prebuilts: cd third_party/sherpa-onnx-prebuilt && ./build_sherpa_onnx.sh")
|
|
141
160
|
endif()
|
|
142
161
|
|
|
@@ -148,7 +167,6 @@ target_include_directories(sherpaonnx PRIVATE
|
|
|
148
167
|
${CMAKE_CURRENT_SOURCE_DIR}/jni/archive
|
|
149
168
|
${CMAKE_CURRENT_SOURCE_DIR}/jni/model_detect
|
|
150
169
|
${CMAKE_CURRENT_SOURCE_DIR}/jni/audio
|
|
151
|
-
${CMAKE_CURRENT_SOURCE_DIR}/jni/tts
|
|
152
170
|
${CMAKE_CURRENT_SOURCE_DIR}/include
|
|
153
171
|
)
|
|
154
172
|
if(USE_FFMPEG)
|
|
@@ -167,7 +185,7 @@ if(USE_LIBARCHIVE)
|
|
|
167
185
|
target_compile_definitions(sherpaonnx PRIVATE HAVE_LIBARCHIVE=1)
|
|
168
186
|
endif()
|
|
169
187
|
|
|
170
|
-
# Link libraries (Kotlin API from AAR handles STT/TTS
|
|
188
|
+
# Link libraries (Kotlin API from AAR handles STT/TTS incl. Zipvoice)
|
|
171
189
|
if(USE_FFMPEG)
|
|
172
190
|
target_link_directories(sherpaonnx PRIVATE ${FFMPEG_LIB_DIR})
|
|
173
191
|
endif()
|
|
@@ -157,7 +157,7 @@ bool ArchiveHelper::ExtractTarBz2(
|
|
|
157
157
|
// If target exists and is a directory, extract into it (merge). Otherwise require empty or force-remove.
|
|
158
158
|
if (std::filesystem::exists(target_path)) {
|
|
159
159
|
if (std::filesystem::is_directory(target_path)) {
|
|
160
|
-
// Merge: extract into existing directory (e.g. multiple archives
|
|
160
|
+
// Merge: extract into existing directory (e.g. multiple archives --> same base path)
|
|
161
161
|
} else if (force) {
|
|
162
162
|
std::error_code ec;
|
|
163
163
|
std::filesystem::remove_all(target_path, ec);
|
|
@@ -459,7 +459,7 @@ bool ArchiveHelper::ExtractFromStream(
|
|
|
459
459
|
|
|
460
460
|
if (std::filesystem::exists(target_path)) {
|
|
461
461
|
if (std::filesystem::is_directory(target_path)) {
|
|
462
|
-
// Merge: extract into existing directory (e.g. multiple archives
|
|
462
|
+
// Merge: extract into existing directory (e.g. multiple archives --> same base path)
|
|
463
463
|
} else if (force) {
|
|
464
464
|
std::error_code ec;
|
|
465
465
|
std::filesystem::remove_all(target_path, ec);
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
#include <jni.h>
|
|
10
10
|
#include <string>
|
|
11
11
|
#include <sys/stat.h>
|
|
12
|
+
#include <vector>
|
|
12
13
|
|
|
13
14
|
#define LOG_TAG "AudioConvertJNI"
|
|
14
15
|
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
|
|
@@ -24,11 +25,14 @@ extern "C" {
|
|
|
24
25
|
#include <libswresample/swresample.h>
|
|
25
26
|
}
|
|
26
27
|
#include <cstdio>
|
|
27
|
-
#include <vector>
|
|
28
28
|
#endif
|
|
29
29
|
|
|
30
30
|
// Forward declaration — convertToFormat handles all formats including WAV (16 kHz mono).
|
|
31
31
|
static std::string convertToFormat(const char* inputPath, const char* outputPath, const char* formatHint, int outputSampleRateHz);
|
|
32
|
+
static std::string decodeAudioFileToFloatMono(const char* inputPath,
|
|
33
|
+
int targetSampleRateHz,
|
|
34
|
+
std::vector<float>* outSamples,
|
|
35
|
+
int* outSampleRate);
|
|
32
36
|
|
|
33
37
|
// Convenience: convert any audio to 16 kHz mono WAV via the main convertToFormat pipeline.
|
|
34
38
|
static std::string convertToWav16kMono(const char* inputPath, const char* outputPath) {
|
|
@@ -614,7 +618,8 @@ static std::string convertToFormat(const char* inputPath, const char* outputPath
|
|
|
614
618
|
av_packet_unref(pkt);
|
|
615
619
|
continue;
|
|
616
620
|
}
|
|
617
|
-
|
|
621
|
+
const uint8_t* const* in_data = frame->extended_data ? frame->extended_data : frame->data;
|
|
622
|
+
int converted = swr_convert(swr, outData, (int)out_nb_samples, in_data, frame->nb_samples);
|
|
618
623
|
if (converted <= 0) {
|
|
619
624
|
av_freep(&outData[0]);
|
|
620
625
|
av_freep(&outData);
|
|
@@ -701,6 +706,204 @@ static std::string convertToFormat(const char* inputPath, const char* outputPath
|
|
|
701
706
|
#endif
|
|
702
707
|
}
|
|
703
708
|
|
|
709
|
+
// Decode any FFmpeg-supported audio to mono float PCM in [-1,1] (clipping not applied) at outSampleRate.
|
|
710
|
+
static std::string decodeAudioFileToFloatMono(const char* inputPath,
|
|
711
|
+
int targetSampleRateHz,
|
|
712
|
+
std::vector<float>* outSamples,
|
|
713
|
+
int* outSampleRate) {
|
|
714
|
+
outSamples->clear();
|
|
715
|
+
*outSampleRate = 0;
|
|
716
|
+
#ifndef HAVE_FFMPEG
|
|
717
|
+
(void)inputPath;
|
|
718
|
+
(void)targetSampleRateHz;
|
|
719
|
+
return std::string("FFmpeg not available. Build prebuilts with third_party/ffmpeg_prebuilt/build_ffmpeg.ps1 or build_ffmpeg.sh.");
|
|
720
|
+
#else
|
|
721
|
+
if (!inputPath) {
|
|
722
|
+
return std::string("inputPath is null");
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
AVFormatContext* inFmt = nullptr;
|
|
726
|
+
if (avformat_open_input(&inFmt, inputPath, nullptr, nullptr) < 0) {
|
|
727
|
+
LOGE("decodeAudioFileToFloatMono: failed to open inputPath=%s", inputPath);
|
|
728
|
+
return std::string("Failed to open input file");
|
|
729
|
+
}
|
|
730
|
+
if (avformat_find_stream_info(inFmt, nullptr) < 0) {
|
|
731
|
+
avformat_close_input(&inFmt);
|
|
732
|
+
return std::string("Failed to find stream info");
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
int audioStreamIndex = -1;
|
|
736
|
+
for (unsigned i = 0; i < inFmt->nb_streams; ++i) {
|
|
737
|
+
if (inFmt->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
|
|
738
|
+
audioStreamIndex = (int)i;
|
|
739
|
+
break;
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
if (audioStreamIndex < 0) {
|
|
743
|
+
avformat_close_input(&inFmt);
|
|
744
|
+
return std::string("No audio stream found in input");
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
AVStream* inStream = inFmt->streams[audioStreamIndex];
|
|
748
|
+
const AVCodec* decoder = avcodec_find_decoder(inStream->codecpar->codec_id);
|
|
749
|
+
if (!decoder) {
|
|
750
|
+
avformat_close_input(&inFmt);
|
|
751
|
+
return std::string("Unsupported input codec");
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
AVCodecContext* decCtx = avcodec_alloc_context3(decoder);
|
|
755
|
+
if (!decCtx) {
|
|
756
|
+
avformat_close_input(&inFmt);
|
|
757
|
+
return std::string("Failed to allocate decoder context");
|
|
758
|
+
}
|
|
759
|
+
if (avcodec_parameters_to_context(decCtx, inStream->codecpar) < 0) {
|
|
760
|
+
avcodec_free_context(&decCtx);
|
|
761
|
+
avformat_close_input(&inFmt);
|
|
762
|
+
return std::string("Failed to copy codec parameters");
|
|
763
|
+
}
|
|
764
|
+
if (avcodec_open2(decCtx, decoder, nullptr) < 0) {
|
|
765
|
+
avcodec_free_context(&decCtx);
|
|
766
|
+
avformat_close_input(&inFmt);
|
|
767
|
+
return std::string("Failed to open decoder");
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
int in_sr = decCtx->sample_rate;
|
|
771
|
+
if (inStream->codecpar->sample_rate > 0) {
|
|
772
|
+
in_sr = inStream->codecpar->sample_rate;
|
|
773
|
+
}
|
|
774
|
+
if (in_sr <= 0) {
|
|
775
|
+
avcodec_free_context(&decCtx);
|
|
776
|
+
avformat_close_input(&inFmt);
|
|
777
|
+
return std::string("Invalid input sample rate");
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
int out_sr = (targetSampleRateHz > 0) ? targetSampleRateHz : in_sr;
|
|
781
|
+
if (out_sr <= 0) {
|
|
782
|
+
avcodec_free_context(&decCtx);
|
|
783
|
+
avformat_close_input(&inFmt);
|
|
784
|
+
return std::string("Invalid output sample rate");
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
AVChannelLayout in_layout{};
|
|
788
|
+
if (inStream->codecpar->ch_layout.nb_channels > 0) {
|
|
789
|
+
if (av_channel_layout_copy(&in_layout, &inStream->codecpar->ch_layout) < 0) {
|
|
790
|
+
avcodec_free_context(&decCtx);
|
|
791
|
+
avformat_close_input(&inFmt);
|
|
792
|
+
return std::string("Failed to copy input channel layout");
|
|
793
|
+
}
|
|
794
|
+
} else {
|
|
795
|
+
if (av_channel_layout_copy(&in_layout, &decCtx->ch_layout) < 0) {
|
|
796
|
+
avcodec_free_context(&decCtx);
|
|
797
|
+
avformat_close_input(&inFmt);
|
|
798
|
+
return std::string("Failed to get decoder channel layout");
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
AVChannelLayout out_layout = AV_CHANNEL_LAYOUT_MONO;
|
|
803
|
+
SwrContext* swr = nullptr;
|
|
804
|
+
if (swr_alloc_set_opts2(&swr,
|
|
805
|
+
&out_layout,
|
|
806
|
+
AV_SAMPLE_FMT_FLT,
|
|
807
|
+
out_sr,
|
|
808
|
+
&in_layout,
|
|
809
|
+
decCtx->sample_fmt,
|
|
810
|
+
in_sr,
|
|
811
|
+
0,
|
|
812
|
+
nullptr) < 0 ||
|
|
813
|
+
!swr) {
|
|
814
|
+
av_channel_layout_uninit(&in_layout);
|
|
815
|
+
avcodec_free_context(&decCtx);
|
|
816
|
+
avformat_close_input(&inFmt);
|
|
817
|
+
return std::string("Failed to initialize resampler");
|
|
818
|
+
}
|
|
819
|
+
if (swr_init(swr) < 0) {
|
|
820
|
+
av_channel_layout_uninit(&in_layout);
|
|
821
|
+
swr_free(&swr);
|
|
822
|
+
avcodec_free_context(&decCtx);
|
|
823
|
+
avformat_close_input(&inFmt);
|
|
824
|
+
return std::string("Failed to initialize resampler (swr_init)");
|
|
825
|
+
}
|
|
826
|
+
av_channel_layout_uninit(&in_layout);
|
|
827
|
+
|
|
828
|
+
AVPacket* pkt = av_packet_alloc();
|
|
829
|
+
AVFrame* frame = av_frame_alloc();
|
|
830
|
+
if (!pkt || !frame) {
|
|
831
|
+
if (pkt) av_packet_free(&pkt);
|
|
832
|
+
if (frame) av_frame_free(&frame);
|
|
833
|
+
swr_free(&swr);
|
|
834
|
+
avcodec_free_context(&decCtx);
|
|
835
|
+
avformat_close_input(&inFmt);
|
|
836
|
+
return std::string("Out of memory");
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
auto appendConverted = [&](uint8_t* buf, int nbFloats) {
|
|
840
|
+
if (!buf || nbFloats <= 0) return;
|
|
841
|
+
const float* f = reinterpret_cast<const float*>(buf);
|
|
842
|
+
outSamples->insert(outSamples->end(), f, f + nbFloats);
|
|
843
|
+
};
|
|
844
|
+
|
|
845
|
+
auto convertOneFrame = [&](AVFrame* fr) {
|
|
846
|
+
const uint8_t* const* in_data = fr->extended_data ? fr->extended_data : fr->data;
|
|
847
|
+
int in_sr2 = inStream->codecpar->sample_rate ? inStream->codecpar->sample_rate : decCtx->sample_rate;
|
|
848
|
+
int64_t max_out =
|
|
849
|
+
av_rescale_rnd(swr_get_delay(swr, in_sr2) + (int64_t)fr->nb_samples, out_sr, in_sr2, AV_ROUND_UP);
|
|
850
|
+
if (max_out < 1) max_out = 1;
|
|
851
|
+
uint8_t* out_buf = nullptr;
|
|
852
|
+
if (av_samples_alloc(&out_buf, nullptr, 1, (int)max_out, AV_SAMPLE_FMT_FLT, 0) < 0) {
|
|
853
|
+
return;
|
|
854
|
+
}
|
|
855
|
+
int converted = swr_convert(swr, &out_buf, (int)max_out, in_data, fr->nb_samples);
|
|
856
|
+
if (converted > 0) {
|
|
857
|
+
appendConverted(out_buf, converted);
|
|
858
|
+
}
|
|
859
|
+
av_freep(&out_buf);
|
|
860
|
+
};
|
|
861
|
+
|
|
862
|
+
while (av_read_frame(inFmt, pkt) >= 0) {
|
|
863
|
+
if (pkt->stream_index == audioStreamIndex) {
|
|
864
|
+
if (avcodec_send_packet(decCtx, pkt) == 0) {
|
|
865
|
+
while (avcodec_receive_frame(decCtx, frame) == 0) {
|
|
866
|
+
convertOneFrame(frame);
|
|
867
|
+
av_frame_unref(frame);
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
av_packet_unref(pkt);
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
if (avcodec_send_packet(decCtx, nullptr) == 0) {
|
|
875
|
+
while (avcodec_receive_frame(decCtx, frame) == 0) {
|
|
876
|
+
convertOneFrame(frame);
|
|
877
|
+
av_frame_unref(frame);
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
{
|
|
882
|
+
int in_sr2 = inStream->codecpar->sample_rate ? inStream->codecpar->sample_rate : decCtx->sample_rate;
|
|
883
|
+
int tailCap = (int)swr_get_delay(swr, in_sr2) + 4096;
|
|
884
|
+
if (tailCap < 16) tailCap = 16;
|
|
885
|
+
uint8_t* tailData = nullptr;
|
|
886
|
+
if (av_samples_alloc(&tailData, nullptr, 1, tailCap, AV_SAMPLE_FMT_FLT, 0) >= 0) {
|
|
887
|
+
int tailConverted = swr_convert(swr, &tailData, tailCap, nullptr, 0);
|
|
888
|
+
if (tailConverted > 0) {
|
|
889
|
+
appendConverted(tailData, tailConverted);
|
|
890
|
+
}
|
|
891
|
+
av_freep(&tailData);
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
av_packet_free(&pkt);
|
|
896
|
+
av_frame_free(&frame);
|
|
897
|
+
swr_free(&swr);
|
|
898
|
+
avcodec_free_context(&decCtx);
|
|
899
|
+
avformat_close_input(&inFmt);
|
|
900
|
+
|
|
901
|
+
*outSampleRate = out_sr;
|
|
902
|
+
LOGI("decodeAudioFileToFloatMono: samples=%zu sampleRate=%d", outSamples->size(), out_sr);
|
|
903
|
+
return std::string("");
|
|
904
|
+
#endif
|
|
905
|
+
}
|
|
906
|
+
|
|
704
907
|
extern "C" {
|
|
705
908
|
|
|
706
909
|
// Called from Kotlin: SherpaOnnxModule.nativeConvertAudioToWav16k(inputPath, outputPath) -> Boolean
|
|
@@ -759,4 +962,67 @@ Java_com_sherpaonnx_SherpaOnnxModule_nativeConvertAudioToFormat(
|
|
|
759
962
|
return env->NewStringUTF(err.c_str());
|
|
760
963
|
}
|
|
761
964
|
|
|
965
|
+
// Returns Object[]: on error [String message]; on success [float[] samples, Integer sampleRate].
|
|
966
|
+
JNIEXPORT jobjectArray JNICALL
|
|
967
|
+
Java_com_sherpaonnx_SherpaOnnxModule_nativeDecodeAudioFileToFloatSamples(JNIEnv* env,
|
|
968
|
+
jobject /* this */,
|
|
969
|
+
jstring inputPath,
|
|
970
|
+
jint targetSampleRateHz) {
|
|
971
|
+
jclass objectClass = env->FindClass("java/lang/Object");
|
|
972
|
+
if (!objectClass) {
|
|
973
|
+
return nullptr;
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
auto makeError = [&](const char* msg) -> jobjectArray {
|
|
977
|
+
jobjectArray ret = env->NewObjectArray(1, objectClass, nullptr);
|
|
978
|
+
if (!ret) return nullptr;
|
|
979
|
+
jstring jmsg = env->NewStringUTF(msg);
|
|
980
|
+
env->SetObjectArrayElement(ret, 0, jmsg);
|
|
981
|
+
env->DeleteLocalRef(jmsg);
|
|
982
|
+
return ret;
|
|
983
|
+
};
|
|
984
|
+
|
|
985
|
+
if (inputPath == nullptr) {
|
|
986
|
+
return makeError("inputPath must be non-null");
|
|
987
|
+
}
|
|
988
|
+
const char* input = env->GetStringUTFChars(inputPath, nullptr);
|
|
989
|
+
if (input == nullptr) {
|
|
990
|
+
return makeError("Failed to get path string");
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
std::vector<float> samples;
|
|
994
|
+
int sampleRate = 0;
|
|
995
|
+
std::string err = decodeAudioFileToFloatMono(input, (int)targetSampleRateHz, &samples, &sampleRate);
|
|
996
|
+
env->ReleaseStringUTFChars(inputPath, input);
|
|
997
|
+
|
|
998
|
+
if (!err.empty()) {
|
|
999
|
+
return makeError(err.c_str());
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
jfloatArray jfloats = env->NewFloatArray((jsize)samples.size());
|
|
1003
|
+
if (!jfloats) {
|
|
1004
|
+
return makeError("Failed to allocate float array");
|
|
1005
|
+
}
|
|
1006
|
+
if (!samples.empty()) {
|
|
1007
|
+
env->SetFloatArrayRegion(jfloats, 0, (jsize)samples.size(), samples.data());
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
jobjectArray ret = env->NewObjectArray(2, objectClass, nullptr);
|
|
1011
|
+
if (!ret) {
|
|
1012
|
+
env->DeleteLocalRef(jfloats);
|
|
1013
|
+
return makeError("Failed to allocate result array");
|
|
1014
|
+
}
|
|
1015
|
+
env->SetObjectArrayElement(ret, 0, jfloats);
|
|
1016
|
+
|
|
1017
|
+
jclass intCls = env->FindClass("java/lang/Integer");
|
|
1018
|
+
jmethodID intCtor = env->GetMethodID(intCls, "<init>", "(I)V");
|
|
1019
|
+
jobject jrate = env->NewObject(intCls, intCtor, sampleRate);
|
|
1020
|
+
env->SetObjectArrayElement(ret, 1, jrate);
|
|
1021
|
+
|
|
1022
|
+
env->DeleteLocalRef(jfloats);
|
|
1023
|
+
env->DeleteLocalRef(jrate);
|
|
1024
|
+
env->DeleteLocalRef(intCls);
|
|
1025
|
+
return ret;
|
|
1026
|
+
}
|
|
1027
|
+
|
|
762
1028
|
} // extern "C"
|
|
@@ -60,8 +60,8 @@ TtsModelKind ParseTtsModelType(const std::string& modelType) {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
/** Returns true if the given kind is supported by the current paths and hints (required files present).
|
|
63
|
-
* data_dir (espeak-ng-data) is required
|
|
64
|
-
* VITS
|
|
63
|
+
* data_dir (espeak-ng-data) is required for Kitten, Kokoro, and Zipvoice (Zipvoice uses MatchaTtsLexicon + espeak).
|
|
64
|
+
* VITS and Matcha use dataDir optionally in this detector; Pocket does not use it. */
|
|
65
65
|
static bool CapabilitySupportsTtsKind(
|
|
66
66
|
TtsModelKind kind,
|
|
67
67
|
bool hasVits,
|
|
@@ -128,6 +128,10 @@ static TtsDetectResult DetectTtsModelFromFiles(
|
|
|
128
128
|
std::string tokensFile = FindFileByName(files, "tokens.txt");
|
|
129
129
|
std::vector<LexiconCandidate> lexiconCandidates = FindLexiconCandidates(files, modelDir);
|
|
130
130
|
std::string dataDirPath = FindDirectoryUnderRoot(files, modelDir, "espeak-ng-data");
|
|
131
|
+
LOGI("DetectTtsModel: modelDir=%s espeak-ng dataDir=%s (empty=%d)",
|
|
132
|
+
modelDir.c_str(),
|
|
133
|
+
dataDirPath.empty() ? "(empty)" : dataDirPath.c_str(),
|
|
134
|
+
(int)dataDirPath.empty());
|
|
131
135
|
std::string voicesFile = FindFileByName(files, "voices.bin");
|
|
132
136
|
|
|
133
137
|
std::string acousticModel = FindOnnxByAnyToken(files, {"acoustic_model", "acoustic-model"}, std::nullopt);
|
|
@@ -55,8 +55,8 @@ static const TtsFieldRequirement kZipvoiceReqs[] = {
|
|
|
55
55
|
{"decoder", &TtsModelPaths::decoder, true},
|
|
56
56
|
{"vocoder", &TtsModelPaths::vocoder, true},
|
|
57
57
|
{"tokens", &TtsModelPaths::tokens, true},
|
|
58
|
-
{"dataDir", &TtsModelPaths::dataDir,
|
|
59
|
-
{"lexicon", &TtsModelPaths::lexicon,
|
|
58
|
+
{"dataDir", &TtsModelPaths::dataDir, true},
|
|
59
|
+
{"lexicon", &TtsModelPaths::lexicon, true},
|
|
60
60
|
};
|
|
61
61
|
|
|
62
62
|
// ============================================================
|
|
@@ -102,6 +102,8 @@ static const char* GetFieldHint(const char* fieldName) {
|
|
|
102
102
|
return "Copy espeak-ng-data into the model directory.";
|
|
103
103
|
if (std::strcmp(fieldName, "tokens") == 0)
|
|
104
104
|
return "Ensure tokens.txt is present in the model directory.";
|
|
105
|
+
if (std::strcmp(fieldName, "lexicon") == 0)
|
|
106
|
+
return "Add lexicon.txt (or lexicon-<lang>.txt) from the official sherpa-onnx Zipvoice/Matcha release; without it the native engine aborts.";
|
|
105
107
|
return nullptr;
|
|
106
108
|
}
|
|
107
109
|
|
|
@@ -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
|
-
/**
|
|
20
|
-
private val extractExecutor: ExecutorService = Executors.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
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)
|