react-native-sherpa-onnx 0.3.2 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +84 -77
- package/SherpaOnnx.podspec +79 -45
- package/android/build.gradle +8 -2
- package/android/prebuilt-download.gradle +70 -16
- package/android/prebuilt-versions.gradle +14 -6
- package/android/src/main/cpp/CMakeLists.txt +2 -0
- package/android/src/main/cpp/jni/audio/sherpa-onnx-audio-convert-jni.cpp +202 -328
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-detect-jni-common.cpp +22 -0
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-detect-jni-common.h +2 -0
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-model-detect-helper.cpp +96 -142
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-model-detect-helper.h +40 -4
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-model-detect-stt.cpp +774 -316
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-model-detect-tts.cpp +208 -122
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-model-detect.h +92 -0
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-stt-wrapper.cpp +3 -0
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-tts-wrapper.cpp +14 -2
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-validate-stt.cpp +229 -0
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-validate-stt.h +38 -0
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-validate-tts.cpp +144 -0
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-validate-tts.h +38 -0
- package/android/src/main/cpp/jni/module/sherpa-onnx-module-jni.cpp +1 -1
- package/android/src/main/java/com/sherpaonnx/SherpaOnnxModule.kt +157 -11
- package/android/src/main/java/com/sherpaonnx/SherpaOnnxPcmCapture.kt +150 -0
- package/android/src/main/java/com/sherpaonnx/SherpaOnnxSttHelper.kt +75 -24
- package/android/src/main/java/com/sherpaonnx/SherpaOnnxTtsHelper.kt +52 -1
- package/ios/SherpaOnnx+PcmLiveStream.mm +288 -0
- package/ios/SherpaOnnx+STT.mm +2 -0
- package/ios/SherpaOnnx+TTS.mm +17 -0
- package/ios/SherpaOnnx.mm +27 -3
- package/ios/SherpaOnnxAudioConvert.h +28 -0
- package/ios/SherpaOnnxAudioConvert.mm +698 -0
- package/ios/archive/sherpa-onnx-archive-helper.mm +12 -0
- package/ios/model_detect/sherpa-onnx-model-detect-helper.h +37 -3
- package/ios/model_detect/sherpa-onnx-model-detect-helper.mm +80 -45
- package/ios/model_detect/sherpa-onnx-model-detect-stt.mm +629 -267
- package/ios/model_detect/sherpa-onnx-model-detect-tts.mm +148 -56
- package/ios/model_detect/sherpa-onnx-model-detect.h +72 -0
- package/ios/model_detect/sherpa-onnx-validate-stt.h +38 -0
- package/ios/model_detect/sherpa-onnx-validate-stt.mm +229 -0
- package/ios/model_detect/sherpa-onnx-validate-tts.h +38 -0
- package/ios/model_detect/sherpa-onnx-validate-tts.mm +144 -0
- package/ios/stt/sherpa-onnx-stt-wrapper.mm +4 -0
- package/lib/module/NativeSherpaOnnx.js.map +1 -1
- package/lib/module/audio/index.js +55 -1
- package/lib/module/audio/index.js.map +1 -1
- package/lib/module/download/ModelDownloadManager.js +14 -0
- package/lib/module/download/ModelDownloadManager.js.map +1 -1
- package/lib/module/index.js +10 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/stt/streaming.js +6 -3
- package/lib/module/stt/streaming.js.map +1 -1
- package/lib/module/tts/index.js +13 -1
- package/lib/module/tts/index.js.map +1 -1
- package/lib/typescript/src/NativeSherpaOnnx.d.ts +32 -3
- package/lib/typescript/src/NativeSherpaOnnx.d.ts.map +1 -1
- package/lib/typescript/src/audio/index.d.ts +20 -1
- package/lib/typescript/src/audio/index.d.ts.map +1 -1
- package/lib/typescript/src/download/ModelDownloadManager.d.ts +2 -1
- package/lib/typescript/src/download/ModelDownloadManager.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +10 -0
- package/lib/typescript/src/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 +1 -1
- package/lib/typescript/src/stt/streamingTypes.d.ts.map +1 -1
- package/lib/typescript/src/tts/index.d.ts +12 -1
- package/lib/typescript/src/tts/index.d.ts.map +1 -1
- package/package.json +6 -1
- package/scripts/check-model-csvs.sh +72 -0
- package/scripts/setup-ios-framework.sh +272 -191
- package/src/NativeSherpaOnnx.ts +37 -3
- package/src/audio/index.ts +84 -1
- package/src/download/ModelDownloadManager.ts +19 -0
- package/src/index.tsx +15 -0
- package/src/stt/streaming.ts +10 -5
- package/src/stt/streamingTypes.ts +1 -1
- package/src/tts/index.ts +25 -1
- package/third_party/ffmpeg_prebuilt/ANDROID_RELEASE_TAG +1 -1
- package/third_party/libarchive_prebuilt/ANDROID_RELEASE_TAG +1 -1
- package/third_party/libarchive_prebuilt/IOS_RELEASE_TAG +1 -1
- package/third_party/sherpa-onnx-prebuilt/ANDROID_RELEASE_TAG +1 -1
- package/third_party/sherpa-onnx-prebuilt/IOS_RELEASE_TAG +1 -1
- package/ios/scripts/patch-libarchive-includes.sh +0 -61
- package/ios/scripts/setup-ios-libarchive.sh +0 -98
|
@@ -6,8 +6,10 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
#import "sherpa-onnx-archive-helper.h"
|
|
9
|
+
#ifdef HAVE_LIBARCHIVE
|
|
9
10
|
#import <archive.h>
|
|
10
11
|
#import <archive_entry.h>
|
|
12
|
+
#endif
|
|
11
13
|
#import <CommonCrypto/CommonCrypto.h>
|
|
12
14
|
#include <array>
|
|
13
15
|
#include <atomic>
|
|
@@ -17,6 +19,7 @@
|
|
|
17
19
|
static std::atomic_bool g_cancelExtract(false);
|
|
18
20
|
|
|
19
21
|
namespace {
|
|
22
|
+
#ifdef HAVE_LIBARCHIVE
|
|
20
23
|
struct ArchiveReadContext {
|
|
21
24
|
FILE* file = nullptr;
|
|
22
25
|
std::array<unsigned char, 64 * 1024> buffer{};
|
|
@@ -66,6 +69,7 @@ static void DrainRemainingAndClose(ArchiveReadContext* ctx) {
|
|
|
66
69
|
fclose(ctx->file);
|
|
67
70
|
ctx->file = nullptr;
|
|
68
71
|
}
|
|
72
|
+
#endif
|
|
69
73
|
|
|
70
74
|
static NSString* HexStringFromDigest(const unsigned char* digest, size_t size) {
|
|
71
75
|
static const char* kHex = "0123456789abcdef";
|
|
@@ -122,7 +126,11 @@ static NSString* ComputeFileSha256(NSString* filePath, NSError** error) {
|
|
|
122
126
|
|
|
123
127
|
+ (void)cancelExtractTarBz2
|
|
124
128
|
{
|
|
129
|
+
#ifdef HAVE_LIBARCHIVE
|
|
125
130
|
g_cancelExtract.store(true);
|
|
131
|
+
#else
|
|
132
|
+
// feature disabled
|
|
133
|
+
#endif
|
|
126
134
|
}
|
|
127
135
|
|
|
128
136
|
- (NSDictionary *)extractTarBz2:(NSString *)sourcePath
|
|
@@ -130,6 +138,9 @@ static NSString* ComputeFileSha256(NSString* filePath, NSError** error) {
|
|
|
130
138
|
force:(BOOL)force
|
|
131
139
|
progress:(SherpaOnnxArchiveProgressBlock)progress
|
|
132
140
|
{
|
|
141
|
+
#ifndef HAVE_LIBARCHIVE
|
|
142
|
+
return @{ @"success": @NO, @"reason": @"libarchive is disabled in this build. Rebuild without SHERPA_ONNX_DISABLE_LIBARCHIVE=1." };
|
|
143
|
+
#else
|
|
133
144
|
g_cancelExtract.store(false);
|
|
134
145
|
NSFileManager *fileManager = [NSFileManager defaultManager];
|
|
135
146
|
|
|
@@ -285,6 +296,7 @@ static NSString* ComputeFileSha256(NSString* filePath, NSError** error) {
|
|
|
285
296
|
NSString *sha256Hex = HexStringFromDigest(digest, CC_SHA256_DIGEST_LENGTH);
|
|
286
297
|
|
|
287
298
|
return @{ @"success": @YES, @"path": targetPath, @"sha256": sha256Hex ?: @"" };
|
|
299
|
+
#endif
|
|
288
300
|
}
|
|
289
301
|
|
|
290
302
|
- (NSString *)computeFileSha256:(NSString *)filePath
|
|
@@ -21,12 +21,11 @@ std::vector<std::string> ListDirectories(const std::string& path);
|
|
|
21
21
|
std::vector<FileEntry> ListFiles(const std::string& path);
|
|
22
22
|
std::vector<FileEntry> ListFilesRecursive(const std::string& path, int maxDepth);
|
|
23
23
|
std::string ToLower(std::string value);
|
|
24
|
-
std::string ResolveTokenizerDir(const std::string& modelDir);
|
|
25
24
|
|
|
26
|
-
|
|
25
|
+
/** Find file in \p files whose name equals \p fileName (case-insensitive). Uses file tree only, no filesystem. */
|
|
26
|
+
std::string FindFileByName(const std::vector<FileEntry>& files, const std::string& fileName);
|
|
27
27
|
/** Find file whose name equals or ends with suffix (e.g. tokens.txt, tiny-tokens.txt) in a pre-built file list. */
|
|
28
28
|
std::string FindFileEndingWith(const std::vector<FileEntry>& files, const std::string& suffix);
|
|
29
|
-
std::string FindDirectoryByName(const std::string& baseDir, const std::string& dirName, int maxDepth);
|
|
30
29
|
|
|
31
30
|
std::string FindOnnxByToken(
|
|
32
31
|
const std::vector<FileEntry>& files,
|
|
@@ -38,6 +37,13 @@ std::string FindOnnxByAnyToken(
|
|
|
38
37
|
const std::vector<std::string>& tokens,
|
|
39
38
|
const std::optional<bool>& preferInt8
|
|
40
39
|
);
|
|
40
|
+
/** Like FindOnnxByAnyToken but skips any file whose nameLower contains any of \p excludeInName. */
|
|
41
|
+
std::string FindOnnxByAnyTokenExcluding(
|
|
42
|
+
const std::vector<FileEntry>& files,
|
|
43
|
+
const std::vector<std::string>& tokens,
|
|
44
|
+
const std::vector<std::string>& excludeInName,
|
|
45
|
+
const std::optional<bool>& preferInt8
|
|
46
|
+
);
|
|
41
47
|
std::string FindLargestOnnxExcludingTokens(
|
|
42
48
|
const std::vector<FileEntry>& files,
|
|
43
49
|
const std::vector<std::string>& excludeTokens
|
|
@@ -46,6 +52,34 @@ std::string FindLargestOnnxExcludingTokens(
|
|
|
46
52
|
/** Returns true if \p word appears in \p haystack as a standalone token (surrounded by separators: / - _ . space). */
|
|
47
53
|
bool ContainsWord(const std::string& haystack, const std::string& word);
|
|
48
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Find a directory with the given name anywhere under \p rootDir in the file tree.
|
|
57
|
+
* Searches \p files for any path that starts with \p rootDir and contains "/dirName/".
|
|
58
|
+
* Returns the full path to that directory (e.g. rootDir/inner/dirName) or empty if not found.
|
|
59
|
+
* Used e.g. to find espeak-ng-data in modelDir or in modelDir/inner-model-dir/.
|
|
60
|
+
*/
|
|
61
|
+
std::string FindDirectoryUnderRoot(
|
|
62
|
+
const std::vector<FileEntry>& files,
|
|
63
|
+
const std::string& rootDir,
|
|
64
|
+
const std::string& dirName
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
/** Lexicon file with optional language id for multi-lang TTS (e.g. Kokoro). */
|
|
68
|
+
struct LexiconCandidate {
|
|
69
|
+
std::string path; /**< Full path to the lexicon file */
|
|
70
|
+
std::string languageId; /**< From filename: "default" for lexicon.txt, else e.g. "us-en", "zh" from lexicon-us-en.txt, lexicon-zh.txt */
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Find all lexicon files under \p rootDir: exact "lexicon.txt" and any "lexicon-*.txt".
|
|
75
|
+
* Returns a list of LexiconCandidate (path + languageId), ordered: lexicon.txt first (as "default"),
|
|
76
|
+
* then lexicon-*.txt alphabetically by language id. Used for multi-language Kokoro/Kitten TTS.
|
|
77
|
+
*/
|
|
78
|
+
std::vector<LexiconCandidate> FindLexiconCandidates(
|
|
79
|
+
const std::vector<FileEntry>& files,
|
|
80
|
+
const std::string& rootDir
|
|
81
|
+
);
|
|
82
|
+
|
|
49
83
|
} // namespace model_detect
|
|
50
84
|
} // namespace sherpaonnx
|
|
51
85
|
|
|
@@ -30,12 +30,16 @@ bool ContainsToken(const std::string& value, const std::string& token) {
|
|
|
30
30
|
return value.find(token) != std::string::npos;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
static bool IsOnnxOrOrtFile(const FileEntry& entry) {
|
|
34
|
+
return EndsWith(entry.nameLower, ".onnx") || EndsWith(entry.nameLower, ".ort");
|
|
35
|
+
}
|
|
36
|
+
|
|
33
37
|
std::string ChooseLargest(const std::vector<FileEntry>& files,
|
|
34
38
|
const std::vector<std::string>& excludeTokens, bool onlyInt8, bool onlyNonInt8) {
|
|
35
39
|
std::string chosen;
|
|
36
40
|
std::uint64_t bestSize = 0;
|
|
37
41
|
for (const auto& entry : files) {
|
|
38
|
-
if (!
|
|
42
|
+
if (!IsOnnxOrOrtFile(entry)) continue;
|
|
39
43
|
bool hasExcluded = false;
|
|
40
44
|
for (const auto& token : excludeTokens) {
|
|
41
45
|
if (ContainsToken(entry.nameLower, token)) { hasExcluded = true; break; }
|
|
@@ -115,7 +119,7 @@ std::string FindOnnxByToken(const std::vector<FileEntry>& files,
|
|
|
115
119
|
std::string tokenLower = ToLower(token);
|
|
116
120
|
std::vector<FileEntry> matches;
|
|
117
121
|
for (const auto& entry : files) {
|
|
118
|
-
if (!
|
|
122
|
+
if (!IsOnnxOrOrtFile(entry)) continue;
|
|
119
123
|
if (ContainsToken(entry.nameLower, tokenLower)) matches.push_back(entry);
|
|
120
124
|
}
|
|
121
125
|
if (matches.empty()) return "";
|
|
@@ -136,6 +140,37 @@ std::string FindOnnxByAnyToken(const std::vector<FileEntry>& files,
|
|
|
136
140
|
return "";
|
|
137
141
|
}
|
|
138
142
|
|
|
143
|
+
std::string FindOnnxByAnyTokenExcluding(const std::vector<FileEntry>& files,
|
|
144
|
+
const std::vector<std::string>& tokens, const std::vector<std::string>& excludeInName,
|
|
145
|
+
const std::optional<bool>& preferInt8) {
|
|
146
|
+
for (const auto& token : tokens) {
|
|
147
|
+
std::string tokenLower = ToLower(token);
|
|
148
|
+
std::vector<FileEntry> matches;
|
|
149
|
+
for (const auto& entry : files) {
|
|
150
|
+
if (!IsOnnxOrOrtFile(entry)) continue;
|
|
151
|
+
if (!ContainsToken(entry.nameLower, tokenLower)) continue;
|
|
152
|
+
bool excluded = false;
|
|
153
|
+
for (const auto& ex : excludeInName) {
|
|
154
|
+
std::string exLower = ToLower(ex);
|
|
155
|
+
if (ContainsToken(entry.nameLower, exLower)) {
|
|
156
|
+
excluded = true;
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (!excluded) matches.push_back(entry);
|
|
161
|
+
}
|
|
162
|
+
if (matches.empty()) continue;
|
|
163
|
+
std::vector<std::string> emptyTokens;
|
|
164
|
+
bool wantInt8 = preferInt8.has_value() && preferInt8.value();
|
|
165
|
+
bool wantNonInt8 = preferInt8.has_value() && !preferInt8.value();
|
|
166
|
+
std::string chosen = ChooseLargest(matches, emptyTokens, wantInt8, wantNonInt8);
|
|
167
|
+
if (!chosen.empty()) return chosen;
|
|
168
|
+
chosen = ChooseLargest(matches, emptyTokens, false, false);
|
|
169
|
+
if (!chosen.empty()) return chosen;
|
|
170
|
+
}
|
|
171
|
+
return "";
|
|
172
|
+
}
|
|
173
|
+
|
|
139
174
|
std::string FindFileEndingWith(const std::vector<FileEntry>& files, const std::string& suffix) {
|
|
140
175
|
std::string targetSuffix = ToLower(suffix);
|
|
141
176
|
for (const auto& entry : files) {
|
|
@@ -147,9 +182,8 @@ std::string FindFileEndingWith(const std::vector<FileEntry>& files, const std::s
|
|
|
147
182
|
return "";
|
|
148
183
|
}
|
|
149
184
|
|
|
150
|
-
std::string FindFileByName(const std::
|
|
185
|
+
std::string FindFileByName(const std::vector<FileEntry>& files, const std::string& fileName) {
|
|
151
186
|
std::string target = ToLower(fileName);
|
|
152
|
-
auto files = ListFilesRecursive(baseDir, maxDepth);
|
|
153
187
|
for (const auto& entry : files) {
|
|
154
188
|
if (entry.nameLower == target) return entry.path;
|
|
155
189
|
}
|
|
@@ -172,54 +206,55 @@ bool ContainsWord(const std::string& haystack, const std::string& word) {
|
|
|
172
206
|
return false;
|
|
173
207
|
}
|
|
174
208
|
|
|
175
|
-
std::string
|
|
176
|
-
std::
|
|
177
|
-
std::
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
209
|
+
std::string FindDirectoryUnderRoot(
|
|
210
|
+
const std::vector<FileEntry>& files,
|
|
211
|
+
const std::string& rootDir,
|
|
212
|
+
const std::string& dirName
|
|
213
|
+
) {
|
|
214
|
+
if (dirName.empty()) return "";
|
|
215
|
+
const std::string needle = "/" + dirName + "/";
|
|
216
|
+
const size_t dirPathLen = 1 + dirName.size();
|
|
217
|
+
for (const auto& entry : files) {
|
|
218
|
+
if (entry.path.size() < rootDir.size() + needle.size()) continue;
|
|
219
|
+
if (entry.path.compare(0, rootDir.size(), rootDir) != 0) continue;
|
|
220
|
+
size_t pos = entry.path.find(needle, rootDir.size());
|
|
221
|
+
if (pos != std::string::npos) {
|
|
222
|
+
return entry.path.substr(0, pos + dirPathLen);
|
|
188
223
|
}
|
|
189
|
-
toVisit.swap(next);
|
|
190
|
-
depth += 1;
|
|
191
224
|
}
|
|
192
225
|
return "";
|
|
193
226
|
}
|
|
194
227
|
|
|
195
|
-
std::
|
|
196
|
-
std::
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
228
|
+
std::vector<LexiconCandidate> FindLexiconCandidates(
|
|
229
|
+
const std::vector<FileEntry>& files,
|
|
230
|
+
const std::string& rootDir
|
|
231
|
+
) {
|
|
232
|
+
std::vector<LexiconCandidate> candidates;
|
|
233
|
+
const size_t rootLen = rootDir.size();
|
|
234
|
+
for (const auto& entry : files) {
|
|
235
|
+
if (entry.path.size() <= rootLen) continue;
|
|
236
|
+
if (rootLen > 0) {
|
|
237
|
+
if (entry.path.compare(0, rootLen, rootDir) != 0) continue;
|
|
238
|
+
// Enforce path boundary: if rootDir doesn't end with '/', require '/' after it
|
|
239
|
+
if (rootDir.back() != '/' && entry.path[rootLen] != '/') continue;
|
|
240
|
+
}
|
|
241
|
+
const std::string& baseLower = entry.nameLower;
|
|
242
|
+
if (baseLower == "lexicon.txt") {
|
|
243
|
+
candidates.push_back({entry.path, "default"});
|
|
244
|
+
} else if (baseLower.size() > 12 &&
|
|
245
|
+
baseLower.compare(0, 8, "lexicon-") == 0 &&
|
|
246
|
+
baseLower.compare(baseLower.size() - 4, 4, ".txt") == 0) {
|
|
247
|
+
std::string languageId = baseLower.substr(8, baseLower.size() - 12);
|
|
248
|
+
candidates.push_back({entry.path, languageId});
|
|
213
249
|
}
|
|
214
|
-
} catch (const std::exception&) {
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
std::string commonPath = modelDir + "/Qwen3-0.6B";
|
|
218
|
-
if (FileExists(commonPath + "/vocab.json")) {
|
|
219
|
-
return commonPath;
|
|
220
250
|
}
|
|
221
|
-
|
|
222
|
-
|
|
251
|
+
std::sort(candidates.begin(), candidates.end(), [](const LexiconCandidate& a, const LexiconCandidate& b) {
|
|
252
|
+
if (a.languageId == b.languageId) return a.path < b.path;
|
|
253
|
+
if (a.languageId == "default") return true;
|
|
254
|
+
if (b.languageId == "default") return false;
|
|
255
|
+
return a.languageId < b.languageId;
|
|
256
|
+
});
|
|
257
|
+
return candidates;
|
|
223
258
|
}
|
|
224
259
|
|
|
225
260
|
} // namespace model_detect
|