react-native-nitro-onnx 0.1.1 β 0.1.2
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 +101 -31
- package/android/CMakeLists.txt +5 -3
- package/android/src/main/AndroidManifest.xml +0 -2
- package/android/src/main/cpp/cpp-adapter.cpp +2 -2
- package/android/src/main/java/com/margelo/nitro/onnx/speech/OnnxSpeechPackage.kt +24 -15
- package/cpp/AsrEngine.cpp +150 -58
- package/cpp/AsrEngine.hpp +12 -7
- package/cpp/AudioFileReader.cpp +4 -0
- package/cpp/ModelSingleton.hpp +14 -0
- package/cpp/NitroOnnxSpeech.cpp +8 -9
- package/cpp/NitroOnnxSpeech.hpp +0 -6
- package/cpp/OfflineAsr.cpp +9 -12
- package/cpp/OfflineAsr.hpp +1 -1
- package/cpp/ResourceDir.cpp +5 -5
- package/cpp/ResourceDir.hpp +5 -4
- package/cpp/SpeakerEngine.cpp +38 -28
- package/cpp/SpeakerEngine.hpp +14 -13
- package/cpp/SpeakerManager.cpp +16 -14
- package/cpp/SpeakerManager.hpp +2 -1
- package/cpp/SpeakerRecord.cpp +125 -0
- package/cpp/SpeakerRecord.hpp +42 -0
- package/cpp/StreamingAsr.cpp +13 -10
- package/cpp/StreamingAsr.hpp +1 -1
- package/cpp/Tts.cpp +38 -10
- package/cpp/Tts.hpp +2 -1
- package/cpp/TtsEngine.cpp +15 -7
- package/cpp/TtsEngine.hpp +18 -5
- package/cpp/Vad.cpp +11 -11
- package/cpp/Vad.hpp +1 -1
- package/cpp/VadEngine.cpp +26 -33
- package/cpp/VadEngine.hpp +8 -10
- package/cpp/Version.hpp +7 -0
- package/ios/OnnxSpeechInitializer.mm +18 -6
- package/lib/specs/OnnxSpeech.nitro.d.ts +43 -6
- package/lib/specs/OnnxSpeech.nitro.d.ts.map +1 -1
- package/package.json +4 -3
- package/scripts/generate-version.js +22 -0
- package/src/specs/OnnxSpeech.nitro.ts +25 -8
- package/cpp/ThreadPool.cpp +0 -41
- package/cpp/ThreadPool.hpp +0 -62
package/README.md
CHANGED
|
@@ -9,7 +9,22 @@ A React Native [Nitro Module](https://nitro.margelo.com) that wraps [sherpa-onnx
|
|
|
9
9
|
|
|
10
10
|
All audio I/O uses zero-copy `ArrayBuffer` with **16 kHz mono f32 PCM**.
|
|
11
11
|
|
|
12
|
-
> **Note:**
|
|
12
|
+
> **Note:** Every ASR / TTS model type is wired to the corresponding sherpa-onnx C API config segment. Coverage is complete at the binding layer; per-model quality still depends on the downloaded sherpa-onnx model files.
|
|
13
|
+
|
|
14
|
+
> π’ **Important note about scope & package name**
|
|
15
|
+
>
|
|
16
|
+
> At the present time, this binding is built exclusively for **speechβrelated workloads via sherpaβonnx**:
|
|
17
|
+
> ASR, TTS, VAD and speaker embedding only.
|
|
18
|
+
> It is **NOT a generalβpurpose ONNX Runtime binding** for arbitrary ONNX models (YOLO, LLM etc).
|
|
19
|
+
>
|
|
20
|
+
> The package name `react-native-nitroβonnx` may appear to imply generalβpurpose ONNX support,
|
|
21
|
+
> but that is not the current goal of this repository.
|
|
22
|
+
>
|
|
23
|
+
> If you are an openβsource developer and would like to take over this npm package name
|
|
24
|
+
> to build a truly generalβpurpose Nitro ONNX binding supporting LLM / CV workloads,
|
|
25
|
+
> feel free to open a GitHub issue to contact me for discussion about npm ownership transfer.
|
|
26
|
+
>
|
|
27
|
+
> For now this repo will continue focusing on the speechβonly sherpaβonnx useβcase.
|
|
13
28
|
|
|
14
29
|
## Table of Contents
|
|
15
30
|
|
|
@@ -34,12 +49,14 @@ All audio I/O uses zero-copy `ArrayBuffer` with **16 kHz mono f32 PCM**.
|
|
|
34
49
|
```
|
|
35
50
|
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
36
51
|
β JS / TS β
|
|
37
|
-
β getOnnxSpeech() β createVad() / createTts() /
|
|
52
|
+
β getOnnxSpeech() β createVad() / createTts() / β
|
|
53
|
+
β createOfflineAsr() / createStreamingAsr() β
|
|
38
54
|
ββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββ
|
|
39
55
|
β react-native-nitro-modules (zero-copy ArrayBuffer)
|
|
40
56
|
ββββββββββββββββββββββββ΄βββββββββββββββββββββββββββββββββββββββ
|
|
41
57
|
β C++ β
|
|
42
|
-
β
|
|
58
|
+
β NitroOnnxSpeech β Vad / OfflineAsr / StreamingAsr / Tts / β
|
|
59
|
+
β SpeakerManager β
|
|
43
60
|
β ModelSingleton caches heavy recognizer / TTS instances β
|
|
44
61
|
ββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββ
|
|
45
62
|
β sherpa-onnx C API
|
|
@@ -50,10 +67,11 @@ All audio I/O uses zero-copy `ArrayBuffer` with **16 kHz mono f32 PCM**.
|
|
|
50
67
|
|
|
51
68
|
Key design decisions:
|
|
52
69
|
|
|
53
|
-
- **Singleton preloading:** `OfflineAsrEngine`, `StreamingAsrEngine`, `TtsEngine` and `SpeakerEngine` use `ModelSingleton
|
|
54
|
-
- **Background inference:** Every heavy operation runs on a
|
|
70
|
+
- **Singleton preloading:** `OfflineAsrEngine`, `StreamingAsrEngine`, `TtsEngine` and `SpeakerEngine` use `ModelSingleton`. The cache key covers model dir, type, provider, thread count and other identity-relevant options, so loading the same configuration twice returns the same native instance while a changed option creates a new one.
|
|
71
|
+
- **Background inference:** Every heavy operation runs on a background task pool provided by `react-native-nitro-modules` (`Promise::async`) so the JS thread never blocks. VAD additionally uses a dedicated processor thread for streaming segmentation.
|
|
55
72
|
- **External model download:** The module does not bundle an internal downloader. Download model files in the background with a library such as [`@kesha-antonov/react-native-background-downloader`](https://github.com/kesha-antonov/react-native-background-downloader), then pass the local file paths to `load()` / `initialize()`.
|
|
56
73
|
- **Zero-copy audio:** `ArrayBuffer` is the only audio transport format; samples are expected to be 16 kHz mono little-endian f32 PCM.
|
|
74
|
+
- **Storage:** Bundled assets (e.g. `silero_vad.onnx`) live in the platform resource dir. Registered speakers are written under the app document dir (`Application Support` on iOS, excluded from iCloud backup; `filesDir` on Android) at `<documentDir>/speakers/`.
|
|
57
75
|
|
|
58
76
|
## Supported Models
|
|
59
77
|
|
|
@@ -68,9 +86,9 @@ Key design decisions:
|
|
|
68
86
|
| Conformer | `conformer` | attention-convolution | Streaming accuracy | Yes | Transducer triple |
|
|
69
87
|
| Wenet | `wenet` | U2++ / CTC | Chinese industrial | No | Single `model.onnx` |
|
|
70
88
|
| Telespeech | `telespeech` | telephony ASR | 8 kHz telco audio | No | Single `model.onnx` |
|
|
71
|
-
| Moonshine | `moonshine` | lightweight | Edge devices | No |
|
|
89
|
+
| Moonshine | `moonshine` | lightweight encoder-decoder | Edge devices | No | `preprocessor.onnx` + `encoder.onnx` + decoder pair (see below) |
|
|
72
90
|
| Dolphin | `dolphin` | CTC | English | No | Single `model.onnx` |
|
|
73
|
-
| NeMo | `nemo` | CTC
|
|
91
|
+
| NeMo | `nemo` | CTC | NVIDIA NeMo exported models | No | `model.onnx` + tokens |
|
|
74
92
|
| SenseVoice | `sense_voice` | multilingual | Alibaba SenseVoice | No | Single `model.onnx` |
|
|
75
93
|
|
|
76
94
|
### TTS
|
|
@@ -80,8 +98,8 @@ Key design decisions:
|
|
|
80
98
|
| Kokoro | `kokoro` | internal | High, multi-speaker | Medium | Needs `model.onnx`, `voices.bin`, `tokens.txt`, `lexicon.txt` |
|
|
81
99
|
| VITS | `vits` | internal | High quality | Medium | Needs `model.onnx`, `tokens.txt`, optional lexicon |
|
|
82
100
|
| Matcha | `matcha` | external (e.g. Hifigan) | Fast, natural | Fast | Needs acoustic model + vocoder ONNX |
|
|
83
|
-
| Pocket | `pocket` | internal | Lightweight zero-shot | Very fast |
|
|
84
|
-
| ZipVoice | `zipvoice` | internal |
|
|
101
|
+
| Pocket | `pocket` | internal | Lightweight zero-shot | Very fast | Multi-file (lm / encoder / decoder, see below) |
|
|
102
|
+
| ZipVoice | `zipvoice` | internal | Dedicated encoder/decoder | Fast | Needs `zipvoiceEncoder`, `zipvoiceDecoder`, vocoder, tokens |
|
|
85
103
|
|
|
86
104
|
### VAD
|
|
87
105
|
|
|
@@ -119,7 +137,7 @@ transducer/
|
|
|
119
137
|
tokens.txt
|
|
120
138
|
```
|
|
121
139
|
|
|
122
|
-
### Paraformer /
|
|
140
|
+
### Paraformer / Telespeech / Dolphin / SenseVoice (offline ASR)
|
|
123
141
|
|
|
124
142
|
```
|
|
125
143
|
model/
|
|
@@ -127,12 +145,32 @@ model/
|
|
|
127
145
|
tokens.txt
|
|
128
146
|
```
|
|
129
147
|
|
|
148
|
+
### Wenet (offline ASR)
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
wenet/
|
|
152
|
+
model.onnx
|
|
153
|
+
tokens.txt
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Moonshine (offline ASR)
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
moonshine/
|
|
160
|
+
preprocessor.onnx β model
|
|
161
|
+
encoder.onnx β encoder
|
|
162
|
+
uncached_decoder.onnx β decoder
|
|
163
|
+
cached_decoder.onnx β joiner
|
|
164
|
+
tokens.txt
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Alternatively, pass `merged_decoder.onnx` as `decoder` and leave `joiner` unset.
|
|
168
|
+
|
|
130
169
|
### NeMo (offline ASR)
|
|
131
170
|
|
|
132
171
|
```
|
|
133
172
|
nemo/
|
|
134
173
|
model.onnx
|
|
135
|
-
config.yaml
|
|
136
174
|
tokens.txt
|
|
137
175
|
```
|
|
138
176
|
|
|
@@ -169,8 +207,27 @@ matcha/
|
|
|
169
207
|
|
|
170
208
|
```
|
|
171
209
|
pocket/
|
|
172
|
-
|
|
173
|
-
|
|
210
|
+
lm_main.onnx
|
|
211
|
+
lm_flow.onnx
|
|
212
|
+
encoder.onnx
|
|
213
|
+
decoder.onnx
|
|
214
|
+
text_conditioner.onnx
|
|
215
|
+
vocab.json
|
|
216
|
+
token_scores.json
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Map these to `lmMain`, `lmFlow`, `pocketEncoder`, `pocketDecoder`, `textConditioner`, `vocabJson`, `tokenScoresJson`.
|
|
220
|
+
|
|
221
|
+
### ZipVoice (TTS)
|
|
222
|
+
|
|
223
|
+
```
|
|
224
|
+
zipvoice/
|
|
225
|
+
encoder.onnx β zipvoiceEncoder
|
|
226
|
+
decoder.onnx β zipvoiceDecoder
|
|
227
|
+
vocoder.onnx β vocoder
|
|
228
|
+
tokens.txt
|
|
229
|
+
lexicon.txt
|
|
230
|
+
espeak-ng-data/ (optional)
|
|
174
231
|
```
|
|
175
232
|
|
|
176
233
|
### Silero VAD
|
|
@@ -197,7 +254,7 @@ Build requirements:
|
|
|
197
254
|
- React Native >= 0.78
|
|
198
255
|
- react-native-nitro-modules >= 0.35.8
|
|
199
256
|
- Xcode 15 / Android NDK 26
|
|
200
|
-
- The `prepare-sherpa-onnx.js
|
|
257
|
+
- The `postinstall` script runs `generate-version.js` (keeps `cpp/Version.hpp` in sync with `package.json`) and `prepare-sherpa-onnx.js`, which downloads the sherpa-onnx
|
|
201
258
|
prebuilt tree (host static libraries, Android shared libraries, iOS
|
|
202
259
|
xcframework, and C API headers) into `cpp/sherpa-onnx-prebuilt`.
|
|
203
260
|
|
|
@@ -306,10 +363,11 @@ await tts.saveWav(audio, "/path/to/output.wav");
|
|
|
306
363
|
|
|
307
364
|
## Execution Providers
|
|
308
365
|
|
|
309
|
-
By default, the module
|
|
366
|
+
By default, the module selects a platform-appropriate execution provider:
|
|
310
367
|
|
|
311
|
-
- **Android:** `
|
|
312
|
-
- **
|
|
368
|
+
- **Android (no QNN SDK):** `nnapi` β NNAPI with CPU fallback.
|
|
369
|
+
- **Android (built with `QNN_ROOT`):** `qnn` β Qualcomm HTP via QNN; unsupported operators fall back to CPU.
|
|
370
|
+
- **iOS:** `coreml` β Apple Neural Engine via CoreML; unsupported operators fall back to CPU.
|
|
313
371
|
|
|
314
372
|
To disable NPU acceleration and force CPU-only inference, pass `provider: "cpu"` explicitly:
|
|
315
373
|
|
|
@@ -333,7 +391,7 @@ const soc = speech.getQualcommSoc();
|
|
|
333
391
|
|
|
334
392
|
if (soc) {
|
|
335
393
|
console.log(`Qualcomm SoC: ${soc}`);
|
|
336
|
-
// QNN
|
|
394
|
+
// Use QNN when the app was built with -DQNN_ROOT=...; otherwise NNAPI.
|
|
337
395
|
await asr.load({ type: "whisper", /* ... */ });
|
|
338
396
|
} else {
|
|
339
397
|
await asr.load({ type: "whisper", /* ... */ provider: "cpu" });
|
|
@@ -344,15 +402,15 @@ if (soc) {
|
|
|
344
402
|
|
|
345
403
|
### Building with QNN Support
|
|
346
404
|
|
|
347
|
-
QNN is
|
|
405
|
+
QNN is **opt-in**. Without `QNN_ROOT`, Android builds do not define `SHERPA_ONNX_ENABLE_QNN`, the default provider is `nnapi`, and no QNN runtime libraries are linked.
|
|
348
406
|
|
|
349
|
-
`QNN_ROOT` is
|
|
407
|
+
`QNN_ROOT` is required to enable the QNN execution provider and bundle QNN Binary backend libraries from the Qualcomm AI Runtime (QAIRT) SDK.
|
|
350
408
|
|
|
351
|
-
**Download QAIRT SDK
|
|
409
|
+
**Download QAIRT SDK:**
|
|
352
410
|
|
|
353
411
|
Visit [Qualcomm Software Center](https://softwarecenter.qualcomm.com/api/download/software/sdks/Qualcomm_AI_Runtime_Community/All/2.40.0.251030/v2.40.0.251030.zip) to download the SDK (v2.40.0).
|
|
354
412
|
|
|
355
|
-
**Specify QNN_ROOT
|
|
413
|
+
**Specify QNN_ROOT:**
|
|
356
414
|
|
|
357
415
|
```bash
|
|
358
416
|
# Via environment variable
|
|
@@ -362,7 +420,7 @@ QNN_ROOT=/path/to/qnn/sdk ./gradlew assembleRelease
|
|
|
362
420
|
QNN_ROOT=/path/to/qnn/sdk
|
|
363
421
|
```
|
|
364
422
|
|
|
365
|
-
When `QNN_ROOT` is set, the build
|
|
423
|
+
When `QNN_ROOT` is set, the build defines `SHERPA_ONNX_ENABLE_QNN` (so the default provider becomes `qnn`) and links the QNN core library (`QnnHtp`) plus all available HTP version libraries (`QnnHtpV73Stub`/`HtpV73`, `QnnHtpV75Stub`/`HtpV75`, etc.) from the SDK.
|
|
366
424
|
|
|
367
425
|
> **Note:** QNN support is Android-only. On iOS, CoreML is used by default.
|
|
368
426
|
|
|
@@ -382,27 +440,37 @@ The result is that no speech frames are lost between detection and JS delivery.
|
|
|
382
440
|
|
|
383
441
|
## Voice Cloning
|
|
384
442
|
|
|
443
|
+
Registered speakers are stored under the platform document directory (`Application Support` on iOS β excluded from iCloud backup β and `filesDir` on Android) as `<documentDir>/speakers/<id>.bin`. The record holds the embedding and, for `registerSpeakerFromFile`, the reference audio used by zero-shot TTS.
|
|
444
|
+
|
|
385
445
|
Two voice-cloning paths are exposed:
|
|
386
446
|
|
|
387
|
-
1. **Speaker embedding registration**
|
|
388
|
-
2. **Reference-audio TTS**
|
|
447
|
+
1. **Speaker embedding registration** β compute an embedding from reference audio, store it locally. Embeddings are kept for identity / search; they are not a model speaker index.
|
|
448
|
+
2. **Reference-audio TTS** β models that support prompt-based or zero-shot synthesis (e.g. Pocket) receive stored reference audio during synthesis. Register with `registerSpeakerFromFile` so the reference audio is kept alongside the embedding.
|
|
449
|
+
|
|
450
|
+
`synthesizeWithSpeaker` accepts **two kinds of `speakerId`**:
|
|
451
|
+
|
|
452
|
+
| `speakerId` | Meaning | Typical models |
|
|
453
|
+
|---|---|---|
|
|
454
|
+
| Numeric string, e.g. `"0"` | Model-internal speaker index | Kokoro / VITS multi-speaker |
|
|
455
|
+
| Registered ID, e.g. `"speaker-1"` | Voice-cloning record (needs reference audio) | Pocket (zero-shot) |
|
|
389
456
|
|
|
390
457
|
```typescript
|
|
391
458
|
const speaker = speech.createSpeakerManager();
|
|
392
459
|
await speaker.load({ modelDir: "/path/to/speaker", model: "model.onnx", numThreads: 4 });
|
|
393
460
|
|
|
394
|
-
|
|
395
|
-
const registered = await speaker.
|
|
396
|
-
|
|
397
|
-
// Use the registered speaker with TTS (optional speed override)
|
|
461
|
+
// Zero-shot clone (Pocket): keep the reference audio for synthesis
|
|
462
|
+
const registered = await speaker.registerSpeakerFromFile("speaker-1", "Alice", "/path/to/alice.wav");
|
|
398
463
|
const cloned = await tts.synthesizeWithSpeaker("Hello, I am Alice.", registered.id, 1.1);
|
|
464
|
+
|
|
465
|
+
// Multi-speaker model index (Kokoro / VITS)
|
|
466
|
+
const voice0 = await tts.synthesizeWithSpeaker("Hello there.", "0");
|
|
399
467
|
```
|
|
400
468
|
|
|
401
469
|
## Threading
|
|
402
470
|
|
|
403
|
-
Every native inference task runs on a background thread pool:
|
|
471
|
+
Every native inference task runs on a background thread pool (`Promise::async` from react-native-nitro-modules):
|
|
404
472
|
|
|
405
|
-
- VAD processing
|
|
473
|
+
- VAD processing (plus a dedicated processor thread for streaming segmentation)
|
|
406
474
|
- Offline / streaming ASR decode
|
|
407
475
|
- TTS synthesis
|
|
408
476
|
- Speaker embedding extraction
|
|
@@ -427,6 +495,8 @@ The C++ test suite covers:
|
|
|
427
495
|
|
|
428
496
|
- Audio sample / millisecond conversions
|
|
429
497
|
- Float vector / byte buffer round-trip
|
|
498
|
+
- Speaker record write/read round-trip (embedding + reference audio)
|
|
499
|
+
- `tryParseSpeakerIndex` edge cases (empty, negative, non-numeric, oversized)
|
|
430
500
|
|
|
431
501
|
## License
|
|
432
502
|
|
package/android/CMakeLists.txt
CHANGED
|
@@ -14,8 +14,8 @@ if(BUILD_ONNX_SPEECH_TESTS)
|
|
|
14
14
|
enable_testing()
|
|
15
15
|
add_executable(NitroOnnxSpeechTests
|
|
16
16
|
../cpp/tests/test_core.cpp
|
|
17
|
-
../cpp/ThreadPool.cpp
|
|
18
17
|
../cpp/AudioUtils.cpp
|
|
18
|
+
../cpp/SpeakerRecord.cpp
|
|
19
19
|
)
|
|
20
20
|
target_include_directories(NitroOnnxSpeechTests PRIVATE ../cpp)
|
|
21
21
|
find_package(Threads REQUIRED)
|
|
@@ -163,6 +163,8 @@ if(QNN_ROOT)
|
|
|
163
163
|
endif()
|
|
164
164
|
endforeach()
|
|
165
165
|
else()
|
|
166
|
-
|
|
167
|
-
|
|
166
|
+
# QNN runtime libraries (libQnnHtp.so etc.) are only linked when QNN_ROOT is
|
|
167
|
+
# set. Do not enable the QNN compile-time path without the SDK, otherwise the
|
|
168
|
+
# default provider ("qnn") would fail or silently fall back at runtime.
|
|
169
|
+
message(NOTICE "π§ QNN execution provider disabled (set -DQNN_ROOT=... to enable)")
|
|
168
170
|
endif()
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
2
2
|
<uses-permission android:name="android.permission.INTERNET" />
|
|
3
3
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
|
4
|
-
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
|
5
|
-
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
|
6
4
|
</manifest>
|
|
@@ -17,8 +17,8 @@ Java_com_margelo_nitro_onnx_speech_OnnxSpeechPackage_setResourceDir(JNIEnv* env,
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
extern "C" JNIEXPORT void JNICALL
|
|
20
|
-
|
|
20
|
+
Java_com_margelo_nitro_onnx_speech_OnnxSpeechPackage_setDocumentDir(JNIEnv* env, jclass, jstring dir) {
|
|
21
21
|
const char* utf = env->GetStringUTFChars(dir, nullptr);
|
|
22
|
-
margelo::nitro::onnx::speech::
|
|
22
|
+
margelo::nitro::onnx::speech::setDocumentDir(utf);
|
|
23
23
|
env->ReleaseStringUTFChars(dir, utf);
|
|
24
24
|
}
|
|
@@ -12,6 +12,7 @@ import java.io.FileOutputStream
|
|
|
12
12
|
class OnnxSpeechPackage : ReactPackage {
|
|
13
13
|
companion object {
|
|
14
14
|
private const val TAG = "OnnxSpeechPackage"
|
|
15
|
+
private const val RESOURCE_NAME = "silero_vad.onnx"
|
|
15
16
|
|
|
16
17
|
init {
|
|
17
18
|
NitroOnnxSpeechOnLoad.initializeNative()
|
|
@@ -21,34 +22,42 @@ class OnnxSpeechPackage : ReactPackage {
|
|
|
21
22
|
private external fun setResourceDir(dir: String)
|
|
22
23
|
|
|
23
24
|
@JvmStatic
|
|
24
|
-
private external fun
|
|
25
|
+
private external fun setDocumentDir(dir: String)
|
|
25
26
|
|
|
26
27
|
/**
|
|
27
28
|
* Copies bundled model files from APK assets to internal storage so that
|
|
28
29
|
* the C++ layer can access them via real file paths (fopen-compatible).
|
|
30
|
+
* Re-copies when the app version changes or a file is missing.
|
|
29
31
|
* Called lazily when createNativeModules is first invoked.
|
|
30
32
|
*/
|
|
31
33
|
internal fun ensureResources(context: Context) {
|
|
32
34
|
val filesDir = context.filesDir
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
setCacheDir(filesDir.absolutePath)
|
|
37
|
-
return
|
|
38
|
-
}
|
|
39
|
-
try {
|
|
40
|
-
copyAsset(context, "silero_vad.onnx", filesDir)
|
|
41
|
-
marker.createNewFile()
|
|
42
|
-
setResourceDir(filesDir.absolutePath)
|
|
43
|
-
setCacheDir(filesDir.absolutePath)
|
|
35
|
+
|
|
36
|
+
val version = try {
|
|
37
|
+
context.packageManager.getPackageInfo(context.packageName, 0).versionName ?: "0"
|
|
44
38
|
} catch (e: Exception) {
|
|
45
|
-
|
|
39
|
+
"0"
|
|
40
|
+
}
|
|
41
|
+
val marker = File(filesDir, ".resources_extracted_$version")
|
|
42
|
+
val resourceFile = File(filesDir, RESOURCE_NAME)
|
|
43
|
+
if (!marker.exists() || !resourceFile.exists()) {
|
|
44
|
+
try {
|
|
45
|
+
copyAsset(context, RESOURCE_NAME, filesDir, overwrite = true)
|
|
46
|
+
filesDir.listFiles()
|
|
47
|
+
?.filter { it.name.startsWith(".resources_extracted") }
|
|
48
|
+
?.forEach { it.delete() }
|
|
49
|
+
marker.createNewFile()
|
|
50
|
+
} catch (e: Exception) {
|
|
51
|
+
Log.e(TAG, "Failed to extract bundled resources", e)
|
|
52
|
+
}
|
|
46
53
|
}
|
|
54
|
+
setResourceDir(filesDir.absolutePath)
|
|
55
|
+
setDocumentDir(context.filesDir.absolutePath)
|
|
47
56
|
}
|
|
48
57
|
|
|
49
|
-
private fun copyAsset(context: Context, name: String, destDir: File) {
|
|
58
|
+
private fun copyAsset(context: Context, name: String, destDir: File, overwrite: Boolean = false) {
|
|
50
59
|
val dest = File(destDir, name)
|
|
51
|
-
if (dest.exists()) return
|
|
60
|
+
if (dest.exists() && !overwrite) return
|
|
52
61
|
context.assets.open(name).use { input ->
|
|
53
62
|
FileOutputStream(dest).use { output ->
|
|
54
63
|
input.copyTo(output)
|