react-native-sherpa-onnx 0.3.8 → 0.4.0

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 (134) hide show
  1. package/README.md +20 -5
  2. package/SherpaOnnx.podspec +5 -1
  3. package/android/prebuilt-download.gradle +89 -49
  4. package/android/prebuilt-versions.gradle +1 -1
  5. package/android/src/main/assets/model_licenses/asr-models-license-status.csv +1 -0
  6. package/android/src/main/assets/model_licenses/speech-enhancement-models-license-status.csv +7 -0
  7. package/android/src/main/cpp/CMakeLists.txt +3 -0
  8. package/android/src/main/cpp/jni/model_detect/sherpa-onnx-enhancement-wrapper.cpp +68 -0
  9. package/android/src/main/cpp/jni/model_detect/sherpa-onnx-enhancement-wrapper.h +17 -0
  10. package/android/src/main/cpp/jni/model_detect/sherpa-onnx-model-detect-enhancement.cpp +119 -0
  11. package/android/src/main/cpp/jni/model_detect/sherpa-onnx-model-detect-helper.cpp +23 -0
  12. package/android/src/main/cpp/jni/model_detect/sherpa-onnx-model-detect-helper.h +9 -0
  13. package/android/src/main/cpp/jni/model_detect/sherpa-onnx-model-detect-stt.cpp +51 -8
  14. package/android/src/main/cpp/jni/model_detect/sherpa-onnx-model-detect.h +41 -0
  15. package/android/src/main/cpp/jni/model_detect/sherpa-onnx-stt-wrapper.cpp +5 -0
  16. package/android/src/main/cpp/jni/model_detect/sherpa-onnx-validate-enhancement.cpp +68 -0
  17. package/android/src/main/cpp/jni/model_detect/sherpa-onnx-validate-enhancement.h +30 -0
  18. package/android/src/main/cpp/jni/model_detect/sherpa-onnx-validate-stt.cpp +11 -0
  19. package/android/src/main/cpp/jni/module/sherpa-onnx-module-jni.cpp +21 -0
  20. package/android/src/main/java/com/sherpaonnx/SherpaOnnxArchiveHelper.kt +110 -35
  21. package/android/src/main/java/com/sherpaonnx/SherpaOnnxAssetHelper.kt +6 -0
  22. package/android/src/main/java/com/sherpaonnx/SherpaOnnxEnhancementHelper.kt +377 -0
  23. package/android/src/main/java/com/sherpaonnx/SherpaOnnxExtractionNotificationHelper.kt +102 -0
  24. package/android/src/main/java/com/sherpaonnx/SherpaOnnxModule.kt +198 -18
  25. package/android/src/main/java/com/sherpaonnx/SherpaOnnxSttHelper.kt +22 -0
  26. package/ios/Resources/model_licenses/asr-models-license-status.csv +1 -0
  27. package/ios/Resources/model_licenses/speech-enhancement-models-license-status.csv +7 -0
  28. package/ios/SherpaOnnx+Assets.mm +5 -0
  29. package/ios/SherpaOnnx+Enhancement.mm +435 -0
  30. package/ios/SherpaOnnx+STT.mm +13 -1
  31. package/ios/SherpaOnnx.mm +87 -17
  32. package/ios/enhancement/sherpa-onnx-enhancement-wrapper.h +85 -0
  33. package/ios/enhancement/sherpa-onnx-enhancement-wrapper.mm +218 -0
  34. package/ios/model_detect/sherpa-onnx-model-detect-enhancement.mm +92 -0
  35. package/ios/model_detect/sherpa-onnx-model-detect-helper.h +5 -0
  36. package/ios/model_detect/sherpa-onnx-model-detect-helper.mm +23 -0
  37. package/ios/model_detect/sherpa-onnx-model-detect-stt.mm +51 -7
  38. package/ios/model_detect/sherpa-onnx-model-detect.h +33 -0
  39. package/ios/model_detect/sherpa-onnx-validate-enhancement.h +30 -0
  40. package/ios/model_detect/sherpa-onnx-validate-enhancement.mm +69 -0
  41. package/ios/model_detect/sherpa-onnx-validate-stt.mm +11 -0
  42. package/ios/stt/sherpa-onnx-stt-wrapper.h +11 -1
  43. package/ios/stt/sherpa-onnx-stt-wrapper.mm +30 -2
  44. package/ios/tts/sherpa-onnx-tts-wrapper.mm +16 -0
  45. package/lib/module/NativeSherpaOnnx.js.map +1 -1
  46. package/lib/module/download/localModels.js +2 -3
  47. package/lib/module/download/localModels.js.map +1 -1
  48. package/lib/module/download/paths.js +2 -1
  49. package/lib/module/download/paths.js.map +1 -1
  50. package/lib/module/download/postDownloadProcessing.js +17 -4
  51. package/lib/module/download/postDownloadProcessing.js.map +1 -1
  52. package/lib/module/enhancement/index.js +63 -48
  53. package/lib/module/enhancement/index.js.map +1 -1
  54. package/lib/module/enhancement/streaming.js +60 -0
  55. package/lib/module/enhancement/streaming.js.map +1 -0
  56. package/lib/module/enhancement/streamingTypes.js +4 -0
  57. package/lib/module/enhancement/streamingTypes.js.map +1 -0
  58. package/lib/module/enhancement/types.js +4 -0
  59. package/lib/module/enhancement/types.js.map +1 -0
  60. package/lib/module/extraction/extractTarBz2.js +2 -2
  61. package/lib/module/extraction/extractTarBz2.js.map +1 -1
  62. package/lib/module/extraction/extractTarZst.js +2 -2
  63. package/lib/module/extraction/extractTarZst.js.map +1 -1
  64. package/lib/module/extraction/index.js +10 -5
  65. package/lib/module/extraction/index.js.map +1 -1
  66. package/lib/module/licenses.js +9 -3
  67. package/lib/module/licenses.js.map +1 -1
  68. package/lib/module/stt/index.js +4 -2
  69. package/lib/module/stt/index.js.map +1 -1
  70. package/lib/module/stt/streaming.js +2 -1
  71. package/lib/module/stt/streaming.js.map +1 -1
  72. package/lib/module/stt/types.js +3 -1
  73. package/lib/module/stt/types.js.map +1 -1
  74. package/lib/module/tts/index.js +4 -2
  75. package/lib/module/tts/index.js.map +1 -1
  76. package/lib/module/tts/streaming.js +3 -1
  77. package/lib/module/tts/streaming.js.map +1 -1
  78. package/lib/typescript/src/NativeSherpaOnnx.d.ts +70 -9
  79. package/lib/typescript/src/NativeSherpaOnnx.d.ts.map +1 -1
  80. package/lib/typescript/src/download/localModels.d.ts.map +1 -1
  81. package/lib/typescript/src/download/paths.d.ts +2 -1
  82. package/lib/typescript/src/download/paths.d.ts.map +1 -1
  83. package/lib/typescript/src/download/postDownloadProcessing.d.ts +9 -0
  84. package/lib/typescript/src/download/postDownloadProcessing.d.ts.map +1 -1
  85. package/lib/typescript/src/enhancement/index.d.ts +9 -46
  86. package/lib/typescript/src/enhancement/index.d.ts.map +1 -1
  87. package/lib/typescript/src/enhancement/streaming.d.ts +6 -0
  88. package/lib/typescript/src/enhancement/streaming.d.ts.map +1 -0
  89. package/lib/typescript/src/enhancement/streamingTypes.d.ts +12 -0
  90. package/lib/typescript/src/enhancement/streamingTypes.d.ts.map +1 -0
  91. package/lib/typescript/src/enhancement/types.d.ts +31 -0
  92. package/lib/typescript/src/enhancement/types.d.ts.map +1 -0
  93. package/lib/typescript/src/extraction/extractTarBz2.d.ts +2 -1
  94. package/lib/typescript/src/extraction/extractTarBz2.d.ts.map +1 -1
  95. package/lib/typescript/src/extraction/extractTarZst.d.ts +2 -1
  96. package/lib/typescript/src/extraction/extractTarZst.d.ts.map +1 -1
  97. package/lib/typescript/src/extraction/index.d.ts +1 -1
  98. package/lib/typescript/src/extraction/index.d.ts.map +1 -1
  99. package/lib/typescript/src/extraction/types.d.ts +12 -0
  100. package/lib/typescript/src/extraction/types.d.ts.map +1 -1
  101. package/lib/typescript/src/licenses.d.ts.map +1 -1
  102. package/lib/typescript/src/stt/index.d.ts +1 -1
  103. package/lib/typescript/src/stt/index.d.ts.map +1 -1
  104. package/lib/typescript/src/stt/streaming.d.ts.map +1 -1
  105. package/lib/typescript/src/stt/types.d.ts +16 -1
  106. package/lib/typescript/src/stt/types.d.ts.map +1 -1
  107. package/lib/typescript/src/tts/index.d.ts.map +1 -1
  108. package/lib/typescript/src/tts/streaming.d.ts.map +1 -1
  109. package/package.json +1 -1
  110. package/scripts/ci/check-model-csvs.sh +27 -2
  111. package/scripts/ci/collect_all_sherpa_model_streams.sh +3 -1
  112. package/scripts/ci/collect_one_sherpa_release_stream.sh +3 -1
  113. package/scripts/ci/sherpa_speech_enhancement_model_release_streams.json +13 -0
  114. package/scripts/ci/update_model_license_csv.sh +17 -17
  115. package/src/NativeSherpaOnnx.ts +108 -10
  116. package/src/download/localModels.ts +1 -3
  117. package/src/download/paths.ts +2 -1
  118. package/src/download/postDownloadProcessing.ts +24 -1
  119. package/src/enhancement/index.ts +120 -58
  120. package/src/enhancement/streaming.ts +105 -0
  121. package/src/enhancement/streamingTypes.ts +14 -0
  122. package/src/enhancement/types.ts +36 -0
  123. package/src/extraction/extractTarBz2.ts +7 -2
  124. package/src/extraction/extractTarZst.ts +7 -2
  125. package/src/extraction/index.ts +29 -6
  126. package/src/extraction/types.ts +16 -0
  127. package/src/licenses.ts +13 -2
  128. package/src/stt/index.ts +8 -7
  129. package/src/stt/streaming.ts +7 -1
  130. package/src/stt/types.ts +18 -0
  131. package/src/tts/index.ts +7 -7
  132. package/src/tts/streaming.ts +6 -3
  133. package/third_party/sherpa-onnx-prebuilt/ANDROID_RELEASE_TAG +1 -1
  134. package/third_party/sherpa-onnx-prebuilt/IOS_RELEASE_TAG +1 -1
@@ -122,7 +122,8 @@ export function getExtractionStatePath(
122
122
  /**
123
123
  * Directory where native `resolveAssetPath` materializes a bundled model folder
124
124
  * (`DocumentDirectoryPath/models/{modelId}` — Android internal `files/models/...`).
125
- * Separate from {@link getModelDir}. Remove on delete so empty dirs do not break detection.
125
+ * Separate from {@link getModelDir}. `deleteModelByCategory` does not remove this tree; it
126
+ * only deletes download-manager installs under `sherpa-onnx/models/`.
126
127
  */
127
128
  export function getNativeAssetExtractedModelDir(modelId: string): string {
128
129
  const safeId = modelId.replace(/[/\\]/g, '');
@@ -43,6 +43,15 @@ export type RunPostDownloadProcessingOptions = {
43
43
  onChecksumIssue?: (issue: ChecksumIssue) => Promise<boolean>;
44
44
  deleteArchiveAfterExtract?: boolean;
45
45
  onProgress?: (progress: DownloadProgress) => void;
46
+ /**
47
+ * **Android:** Native extraction progress notification (default true), aligned with the download-manager flow.
48
+ * **iOS:** No effect.
49
+ */
50
+ showExtractionNotifications?: boolean;
51
+ /** **Android:** Optional notification title (default: SDK/native default title). */
52
+ extractionNotificationTitle?: string;
53
+ /** **Android:** Optional notification body prefix (progress percent is appended natively). */
54
+ extractionNotificationText?: string;
46
55
  /** Called to get current list of downloaded models for emitModelsListUpdated. */
47
56
  getDownloadedList: () => Promise<ModelMetaBase[]>;
48
57
  };
@@ -63,6 +72,9 @@ export async function runPostDownloadProcessing(
63
72
  deleteArchiveAfterExtract,
64
73
  onProgress,
65
74
  getDownloadedList,
75
+ showExtractionNotifications,
76
+ extractionNotificationTitle,
77
+ extractionNotificationText,
66
78
  } = options;
67
79
 
68
80
  registerActivePostProcess(category, id);
@@ -80,6 +92,9 @@ export async function runPostDownloadProcessing(
80
92
  deleteArchiveAfterExtract,
81
93
  onProgress,
82
94
  getDownloadedList,
95
+ showExtractionNotifications,
96
+ extractionNotificationTitle,
97
+ extractionNotificationText,
83
98
  });
84
99
  } finally {
85
100
  unregisterActivePostProcess(category, id);
@@ -102,6 +117,9 @@ async function runPostDownloadProcessingBody(
102
117
  deleteArchiveAfterExtract,
103
118
  onProgress,
104
119
  getDownloadedList,
120
+ showExtractionNotifications,
121
+ extractionNotificationTitle,
122
+ extractionNotificationText,
105
123
  } = options;
106
124
 
107
125
  const isAborted = () => Boolean(signal?.aborted);
@@ -164,7 +182,12 @@ async function runPostDownloadProcessingBody(
164
182
  onProgress?.(progress);
165
183
  emitDownloadProgress(category, id, progress);
166
184
  },
167
- signal
185
+ signal,
186
+ {
187
+ showNotificationsEnabled: showExtractionNotifications !== false,
188
+ notificationTitle: extractionNotificationTitle,
189
+ notificationText: extractionNotificationText,
190
+ }
168
191
  );
169
192
  }
170
193
 
@@ -1,69 +1,131 @@
1
- /**
2
- * Speech Enhancement feature module
3
- *
4
- * @remarks
5
- * This feature is not yet implemented. This module serves as a placeholder
6
- * for future speech enhancement functionality.
7
- *
8
- * @example
9
- * ```typescript
10
- * // Future usage:
11
- * import { initializeEnhancement, enhanceAudio } from 'react-native-sherpa-onnx/enhancement';
12
- *
13
- * await initializeEnhancement({ modelPath: { type: 'auto', path: 'models/enhancement-model' } });
14
- * const enhancedPath = await enhanceAudio('path/to/noisy-audio.wav');
15
- * ```
16
- */
17
-
1
+ import SherpaOnnx from '../NativeSherpaOnnx';
18
2
  import type { ModelPathConfig } from '../types';
3
+ import { resolveModelPath } from '../utils';
4
+ import type {
5
+ EnhancedAudio,
6
+ EnhancementDetectResult,
7
+ EnhancementEngine,
8
+ EnhancementInitializeOptions,
9
+ } from './types';
19
10
 
20
- /**
21
- * Enhancement initialization options (placeholder)
22
- */
23
- export interface EnhancementInitializeOptions {
24
- modelPath: ModelPathConfig;
25
- // Additional enhancement-specific options will be added here
26
- }
11
+ let enhancementInstanceCounter = 0;
27
12
 
28
- /**
29
- * Enhancement result
30
- */
31
- export interface EnhancementResult {
32
- outputPath: string;
33
- // Additional result fields will be added here
13
+ function normalizeEnhancedAudio(raw: {
14
+ samples?: number[] | Float32Array;
15
+ sampleRate?: number;
16
+ }): EnhancedAudio {
17
+ const samplesArray = Array.isArray(raw.samples)
18
+ ? raw.samples
19
+ : Array.from(raw.samples ?? []);
20
+ return {
21
+ samples: Float32Array.from(samplesArray),
22
+ sampleRate: Number(raw.sampleRate ?? 0),
23
+ };
34
24
  }
35
25
 
36
- /**
37
- * Initialize Speech Enhancement with model directory.
38
- *
39
- * @throws {Error} Not yet implemented
40
- */
41
- export async function initializeEnhancement(
42
- _options: EnhancementInitializeOptions
43
- ): Promise<void> {
44
- throw new Error(
45
- 'Speech Enhancement feature is not yet implemented. This is a placeholder module.'
26
+ export async function detectEnhancementModel(
27
+ modelPath: ModelPathConfig,
28
+ options?: { modelType?: EnhancementInitializeOptions['modelType'] }
29
+ ): Promise<EnhancementDetectResult> {
30
+ const resolvedPath = await resolveModelPath(modelPath);
31
+ const raw = await SherpaOnnx.detectEnhancementModel(
32
+ resolvedPath,
33
+ options?.modelType
46
34
  );
35
+ const err = typeof raw.error === 'string' ? raw.error.trim() : '';
36
+ return {
37
+ success: raw.success,
38
+ ...(err.length > 0 ? { error: err } : {}),
39
+ detectedModels: raw.detectedModels ?? [],
40
+ ...(raw.modelType != null && raw.modelType !== ''
41
+ ? { modelType: raw.modelType }
42
+ : {}),
43
+ };
47
44
  }
48
45
 
49
- /**
50
- * Enhance speech quality in an audio file.
51
- *
52
- * @throws {Error} Not yet implemented
53
- */
54
- export function enhanceAudio(_filePath: string): Promise<EnhancementResult> {
55
- throw new Error(
56
- 'Speech Enhancement feature is not yet implemented. This is a placeholder module.'
46
+ export async function createEnhancement(
47
+ options: EnhancementInitializeOptions
48
+ ): Promise<EnhancementEngine> {
49
+ const instanceId = `enhancement_${++enhancementInstanceCounter}`;
50
+ const resolvedPath = await resolveModelPath(options.modelPath);
51
+ const init = await SherpaOnnx.initializeEnhancement(
52
+ instanceId,
53
+ resolvedPath,
54
+ options.modelType ?? 'auto',
55
+ options.numThreads,
56
+ options.provider,
57
+ options.debug
57
58
  );
58
- }
59
59
 
60
- /**
61
- * Release enhancement resources.
62
- *
63
- * @throws {Error} Not yet implemented
64
- */
65
- export function unloadEnhancement(): Promise<void> {
66
- throw new Error(
67
- 'Speech Enhancement feature is not yet implemented. This is a placeholder module.'
68
- );
60
+ if (!init.success) {
61
+ const nativeError = typeof init.error === 'string' ? init.error.trim() : '';
62
+ throw new Error(
63
+ nativeError.length > 0
64
+ ? `Enhancement initialization failed: ${nativeError}`
65
+ : `Enhancement initialization failed for ${instanceId}`
66
+ );
67
+ }
68
+
69
+ let destroyed = false;
70
+ const guard = () => {
71
+ if (destroyed) {
72
+ throw new Error(
73
+ `Enhancement instance ${instanceId} has been destroyed; cannot call methods on it.`
74
+ );
75
+ }
76
+ };
77
+
78
+ return {
79
+ get instanceId() {
80
+ return instanceId;
81
+ },
82
+ async enhanceFile(
83
+ inputPath: string,
84
+ outputPath?: string
85
+ ): Promise<EnhancedAudio> {
86
+ guard();
87
+ const raw = await SherpaOnnx.enhanceFile(
88
+ instanceId,
89
+ inputPath,
90
+ outputPath
91
+ );
92
+ return normalizeEnhancedAudio(raw);
93
+ },
94
+ async enhanceSamples(
95
+ samples: number[],
96
+ sampleRate: number
97
+ ): Promise<EnhancedAudio> {
98
+ guard();
99
+ const raw = await SherpaOnnx.enhanceSamples(
100
+ instanceId,
101
+ samples,
102
+ sampleRate
103
+ );
104
+ return normalizeEnhancedAudio(raw);
105
+ },
106
+ async getSampleRate(): Promise<number> {
107
+ guard();
108
+ return SherpaOnnx.getEnhancementSampleRate(instanceId);
109
+ },
110
+ async destroy(): Promise<void> {
111
+ if (destroyed) return;
112
+ destroyed = true;
113
+ await SherpaOnnx.unloadEnhancement(instanceId);
114
+ },
115
+ };
69
116
  }
117
+
118
+ export { createStreamingEnhancement } from './streaming';
119
+ export type {
120
+ OnlineEnhancementEngine,
121
+ StreamingEnhancementInitializeOptions,
122
+ } from './streamingTypes';
123
+
124
+ export type {
125
+ EnhancementModelType,
126
+ EnhancedAudio,
127
+ EnhancementInitializeOptions,
128
+ EnhancementDetectResult,
129
+ EnhancementEngine,
130
+ } from './types';
131
+ export { ENHANCEMENT_MODEL_TYPES } from './types';
@@ -0,0 +1,105 @@
1
+ import SherpaOnnx from '../NativeSherpaOnnx';
2
+ import { resolveModelPath } from '../utils';
3
+ import type { EnhancedAudio, EnhancementModelType } from './types';
4
+ import type {
5
+ OnlineEnhancementEngine,
6
+ StreamingEnhancementInitializeOptions,
7
+ } from './streamingTypes';
8
+
9
+ let streamingEnhancementInstanceCounter = 0;
10
+
11
+ function normalizeEnhancedAudio(raw: {
12
+ samples?: number[] | Float32Array;
13
+ sampleRate?: number;
14
+ }): EnhancedAudio {
15
+ const samplesArray = Array.isArray(raw.samples)
16
+ ? raw.samples
17
+ : Array.from(raw.samples ?? []);
18
+ return {
19
+ samples: Float32Array.from(samplesArray),
20
+ sampleRate: Number(raw.sampleRate ?? 0),
21
+ };
22
+ }
23
+
24
+ export async function createStreamingEnhancement(
25
+ options: StreamingEnhancementInitializeOptions
26
+ ): Promise<OnlineEnhancementEngine> {
27
+ const instanceId = `streaming_enhancement_${++streamingEnhancementInstanceCounter}`;
28
+ const resolvedPath = await resolveModelPath(options.modelPath);
29
+ const result = await SherpaOnnx.initializeOnlineEnhancement(
30
+ instanceId,
31
+ resolvedPath,
32
+ options.modelType ?? 'auto',
33
+ options.numThreads,
34
+ options.provider,
35
+ options.debug
36
+ );
37
+
38
+ if (!result.success) {
39
+ const nativeError =
40
+ typeof result.error === 'string' ? result.error.trim() : '';
41
+ throw new Error(
42
+ nativeError.length > 0
43
+ ? `Streaming enhancement initialization failed: ${nativeError}`
44
+ : `Streaming enhancement initialization failed for ${instanceId}`
45
+ );
46
+ }
47
+
48
+ let destroyed = false;
49
+ const guard = () => {
50
+ if (destroyed) {
51
+ throw new Error(
52
+ `Streaming enhancement instance ${instanceId} has been destroyed; cannot call methods on it.`
53
+ );
54
+ }
55
+ };
56
+
57
+ return {
58
+ get instanceId() {
59
+ return instanceId;
60
+ },
61
+
62
+ async feedSamples(
63
+ samples: number[],
64
+ sampleRate: number
65
+ ): Promise<EnhancedAudio> {
66
+ guard();
67
+ const raw = await SherpaOnnx.feedEnhancementSamples(
68
+ instanceId,
69
+ samples,
70
+ sampleRate
71
+ );
72
+ return normalizeEnhancedAudio(raw);
73
+ },
74
+
75
+ async flush(): Promise<EnhancedAudio> {
76
+ guard();
77
+ const raw = await SherpaOnnx.flushOnlineEnhancement(instanceId);
78
+ return normalizeEnhancedAudio(raw);
79
+ },
80
+
81
+ async reset(): Promise<void> {
82
+ guard();
83
+ await SherpaOnnx.resetOnlineEnhancement(instanceId);
84
+ },
85
+
86
+ async getSampleRate(): Promise<number> {
87
+ guard();
88
+ return SherpaOnnx.getEnhancementSampleRate(instanceId);
89
+ },
90
+
91
+ async getFrameShiftInSamples(): Promise<number> {
92
+ guard();
93
+ return Number(result.frameShiftInSamples ?? 0);
94
+ },
95
+
96
+ async destroy(): Promise<void> {
97
+ if (destroyed) return;
98
+ destroyed = true;
99
+ await SherpaOnnx.unloadOnlineEnhancement(instanceId);
100
+ },
101
+ };
102
+ }
103
+
104
+ export type { OnlineEnhancementEngine } from './streamingTypes';
105
+ export type { EnhancementModelType };
@@ -0,0 +1,14 @@
1
+ import type { EnhancedAudio, EnhancementInitializeOptions } from './types';
2
+
3
+ export type StreamingEnhancementInitializeOptions =
4
+ EnhancementInitializeOptions;
5
+
6
+ export interface OnlineEnhancementEngine {
7
+ readonly instanceId: string;
8
+ feedSamples(samples: number[], sampleRate: number): Promise<EnhancedAudio>;
9
+ flush(): Promise<EnhancedAudio>;
10
+ reset(): Promise<void>;
11
+ getSampleRate(): Promise<number>;
12
+ getFrameShiftInSamples(): Promise<number>;
13
+ destroy(): Promise<void>;
14
+ }
@@ -0,0 +1,36 @@
1
+ import type { ModelPathConfig } from '../types';
2
+
3
+ export type EnhancementModelType = 'gtcrn' | 'dpdfnet';
4
+
5
+ export const ENHANCEMENT_MODEL_TYPES: readonly EnhancementModelType[] = [
6
+ 'gtcrn',
7
+ 'dpdfnet',
8
+ ] as const;
9
+
10
+ export type EnhancedAudio = {
11
+ samples: Float32Array;
12
+ sampleRate: number;
13
+ };
14
+
15
+ export interface EnhancementInitializeOptions {
16
+ modelPath: ModelPathConfig;
17
+ modelType?: EnhancementModelType | 'auto';
18
+ numThreads?: number;
19
+ provider?: string;
20
+ debug?: boolean;
21
+ }
22
+
23
+ export type EnhancementDetectResult = {
24
+ success: boolean;
25
+ error?: string;
26
+ detectedModels: Array<{ type: string; modelDir: string }>;
27
+ modelType?: string;
28
+ };
29
+
30
+ export interface EnhancementEngine {
31
+ readonly instanceId: string;
32
+ enhanceFile(inputPath: string, outputPath?: string): Promise<EnhancedAudio>;
33
+ enhanceSamples(samples: number[], sampleRate: number): Promise<EnhancedAudio>;
34
+ getSampleRate(): Promise<number>;
35
+ destroy(): Promise<void>;
36
+ }
@@ -1,5 +1,6 @@
1
1
  import { DeviceEventEmitter } from 'react-native';
2
2
  import SherpaOnnx from '../NativeSherpaOnnx';
3
+ import type { ExtractNotificationArgs } from './types';
3
4
 
4
5
  export type ExtractProgressEvent = {
5
6
  bytes: number;
@@ -19,7 +20,8 @@ export async function extractTarBz2(
19
20
  targetPath: string,
20
21
  force = true,
21
22
  onProgress?: (event: ExtractProgressEvent) => void,
22
- signal?: AbortSignal
23
+ signal?: AbortSignal,
24
+ notification?: ExtractNotificationArgs
23
25
  ): Promise<ExtractResult> {
24
26
  let subscription: { remove: () => void } | null = null;
25
27
  let removeAbortListener: (() => void) | null = null;
@@ -62,7 +64,10 @@ export async function extractTarBz2(
62
64
  const result = await SherpaOnnx.extractTarBz2(
63
65
  sourcePath,
64
66
  targetPath,
65
- force
67
+ force,
68
+ notification?.showNotificationsEnabled,
69
+ notification?.notificationTitle,
70
+ notification?.notificationText
66
71
  );
67
72
  if (!result.success) {
68
73
  const message = result.reason || 'Extraction failed';
@@ -1,5 +1,6 @@
1
1
  import { DeviceEventEmitter } from 'react-native';
2
2
  import SherpaOnnx from '../NativeSherpaOnnx';
3
+ import type { ExtractNotificationArgs } from './types';
3
4
 
4
5
  export type ExtractProgressEvent = {
5
6
  bytes: number;
@@ -19,7 +20,8 @@ export async function extractTarZst(
19
20
  targetPath: string,
20
21
  force = true,
21
22
  onProgress?: (event: ExtractProgressEvent) => void,
22
- signal?: AbortSignal
23
+ signal?: AbortSignal,
24
+ notification?: ExtractNotificationArgs
23
25
  ): Promise<ExtractResult> {
24
26
  let subscription: { remove: () => void } | null = null;
25
27
  let removeAbortListener: (() => void) | null = null;
@@ -61,7 +63,10 @@ export async function extractTarZst(
61
63
  const result = await SherpaOnnx.extractTarZst(
62
64
  sourcePath,
63
65
  targetPath,
64
- force
66
+ force,
67
+ notification?.showNotificationsEnabled,
68
+ notification?.notificationTitle,
69
+ notification?.notificationText
65
70
  );
66
71
  if (!result.success) {
67
72
  const message = result.reason || 'Extraction failed';
@@ -17,6 +17,7 @@ import { extractTarBz2 } from './extractTarBz2';
17
17
  import type {
18
18
  BundledArchive,
19
19
  ExtractArchiveOptions,
20
+ ExtractNotificationArgs,
20
21
  ExtractResult,
21
22
  ExtractProgressEvent,
22
23
  } from './types';
@@ -24,6 +25,7 @@ import type {
24
25
  export type {
25
26
  BundledArchive,
26
27
  ExtractArchiveOptions,
28
+ ExtractNotificationArgs,
27
29
  ExtractResult,
28
30
  ExtractProgressEvent,
29
31
  } from './types';
@@ -160,6 +162,11 @@ export async function extractArchive(
160
162
  const force = options?.force !== false;
161
163
  const onProgress = options?.onProgress;
162
164
  const signal = options?.signal;
165
+ const notification = {
166
+ showNotificationsEnabled: options?.showNotificationsEnabled,
167
+ notificationTitle: options?.notificationTitle,
168
+ notificationText: options?.notificationText,
169
+ };
163
170
 
164
171
  if (signal?.aborted) {
165
172
  const err = new Error('Extraction aborted');
@@ -173,7 +180,14 @@ export async function extractArchive(
173
180
  archive.archivePath.startsWith('asset_packs/'));
174
181
 
175
182
  if (useAssetStream) {
176
- return extractFromAsset(archive, targetPath, force, onProgress, signal);
183
+ return extractFromAsset(
184
+ archive,
185
+ targetPath,
186
+ force,
187
+ onProgress,
188
+ signal,
189
+ notification
190
+ );
177
191
  }
178
192
 
179
193
  if (archive.format === 'tar.zst') {
@@ -182,7 +196,8 @@ export async function extractArchive(
182
196
  targetPath,
183
197
  force,
184
198
  onProgress,
185
- signal
199
+ signal,
200
+ notification
186
201
  );
187
202
  }
188
203
  return extractTarBz2(
@@ -190,7 +205,8 @@ export async function extractArchive(
190
205
  targetPath,
191
206
  force,
192
207
  onProgress,
193
- signal
208
+ signal,
209
+ notification
194
210
  );
195
211
  }
196
212
 
@@ -201,7 +217,8 @@ async function extractFromAsset(
201
217
  targetPath: string,
202
218
  force: boolean,
203
219
  onProgress?: (event: ExtractProgressEvent) => void,
204
- signal?: AbortSignal
220
+ signal?: AbortSignal,
221
+ notification?: ExtractNotificationArgs
205
222
  ): Promise<ExtractResult> {
206
223
  const eventName =
207
224
  archive.format === 'tar.zst'
@@ -245,12 +262,18 @@ async function extractFromAsset(
245
262
  ? await SherpaOnnx.extractTarZstFromAsset(
246
263
  archive.archivePath,
247
264
  targetPath,
248
- force
265
+ force,
266
+ notification?.showNotificationsEnabled,
267
+ notification?.notificationTitle,
268
+ notification?.notificationText
249
269
  )
250
270
  : await SherpaOnnx.extractTarBz2FromAsset(
251
271
  archive.archivePath,
252
272
  targetPath,
253
- force
273
+ force,
274
+ notification?.showNotificationsEnabled,
275
+ notification?.notificationTitle,
276
+ notification?.notificationText
254
277
  );
255
278
 
256
279
  if (!result.success) {
@@ -60,4 +60,20 @@ export type ExtractArchiveOptions = {
60
60
  onProgress?: (event: ExtractProgressEvent) => void;
61
61
  /** AbortSignal to cancel the extraction. */
62
62
  signal?: AbortSignal;
63
+ /**
64
+ * **Android:** When true (default), the native layer posts a system notification with extraction
65
+ * progress. Set to false to disable (e.g. first-run bundled-model prep with in-app UI only).
66
+ * **iOS:** Accepted for API parity; no notification is shown.
67
+ */
68
+ showNotificationsEnabled?: boolean;
69
+ /** **Android:** Notification title. Default: generic “unpacking” title. Ignored on iOS. */
70
+ notificationTitle?: string;
71
+ /** **Android:** Notification body (progress text is appended). Default: generic. Ignored on iOS. */
72
+ notificationText?: string;
63
73
  };
74
+
75
+ /** Subset of `ExtractArchiveOptions` passed through to path- and asset-stream extractors. */
76
+ export type ExtractNotificationArgs = Pick<
77
+ ExtractArchiveOptions,
78
+ 'showNotificationsEnabled' | 'notificationTitle' | 'notificationText'
79
+ >;
package/src/licenses.ts CHANGED
@@ -13,14 +13,17 @@ export async function getModelLicenses(): Promise<ModelLicense[]> {
13
13
  const asrPath = 'model_licenses/asr-models-license-status.csv';
14
14
  const qnnPath = 'model_licenses/qnn-asr-models-license-status.csv';
15
15
  const ttsPath = 'model_licenses/tts-models-license-status.csv';
16
+ const speechEnhancementPath =
17
+ 'model_licenses/speech-enhancement-models-license-status.csv';
16
18
 
17
19
  const results = await Promise.allSettled([
18
20
  SherpaOnnx.readAssetFileAsUtf8(asrPath),
19
21
  SherpaOnnx.readAssetFileAsUtf8(qnnPath),
20
22
  SherpaOnnx.readAssetFileAsUtf8(ttsPath),
23
+ SherpaOnnx.readAssetFileAsUtf8(speechEnhancementPath),
21
24
  ]);
22
25
 
23
- const [asrResult, qnnResult, ttsResult] = results;
26
+ const [asrResult, qnnResult, ttsResult, enhancementResult] = results;
24
27
 
25
28
  const licenses: ModelLicense[] = [];
26
29
 
@@ -48,6 +51,14 @@ export async function getModelLicenses(): Promise<ModelLicense[]> {
48
51
  );
49
52
  }
50
53
 
54
+ if (enhancementResult.status === 'fulfilled') {
55
+ licenses.push(...parseCsv(enhancementResult.value));
56
+ } else {
57
+ console.warn(
58
+ `[SherpaOnnx] Failed to load speech enhancement model licenses: ${enhancementResult.reason}`
59
+ );
60
+ }
61
+
51
62
  return licenses;
52
63
  }
53
64
 
@@ -91,7 +102,7 @@ function parseCsv(csvString: string): ModelLicense[] {
91
102
  }
92
103
  }
93
104
 
94
- if (entry['asset_name']) {
105
+ if (entry.asset_name) {
95
106
  results.push(entry as unknown as ModelLicense);
96
107
  }
97
108
  }
package/src/stt/index.ts CHANGED
@@ -67,10 +67,7 @@ export async function detectSttModel(
67
67
  options?.preferInt8,
68
68
  options?.modelType
69
69
  );
70
- const err =
71
- typeof (raw as { error?: unknown }).error === 'string'
72
- ? String((raw as { error: string }).error).trim()
73
- : '';
70
+ const err = typeof raw.error === 'string' ? raw.error.trim() : '';
74
71
  return {
75
72
  success: raw.success,
76
73
  ...(err.length > 0 ? { error: err } : {}),
@@ -170,10 +167,13 @@ export async function createSTT(
170
167
  );
171
168
 
172
169
  if (!result.success) {
170
+ const nativeError =
171
+ typeof result.error === 'string' ? result.error.trim() : '';
172
+ const detected = JSON.stringify(result.detectedModels ?? []);
173
173
  throw new Error(
174
- `STT initialization failed: ${JSON.stringify(
175
- result.detectedModels ?? []
176
- )}`
174
+ nativeError.length > 0
175
+ ? `STT initialization failed: ${nativeError}`
176
+ : `STT initialization failed: ${detected}`
177
177
  );
178
178
  }
179
179
 
@@ -259,6 +259,7 @@ export type {
259
259
  STTInitializeOptions,
260
260
  STTModelType,
261
261
  SttModelOptions,
262
+ SttQwen3AsrModelOptions,
262
263
  SttRecognitionResult,
263
264
  SttRuntimeConfig,
264
265
  SttEngine,
@@ -231,7 +231,13 @@ export async function createStreamingSTT(
231
231
  );
232
232
 
233
233
  if (!result.success) {
234
- throw new Error(`Streaming STT initialization failed for ${instanceId}`);
234
+ const nativeError =
235
+ typeof result.error === 'string' ? result.error.trim() : '';
236
+ throw new Error(
237
+ nativeError.length > 0
238
+ ? `Streaming STT initialization failed: ${nativeError}`
239
+ : `Streaming STT initialization failed for ${instanceId}`
240
+ );
235
241
  }
236
242
 
237
243
  const enableInputNormalization = options.enableInputNormalization !== false;