react-native-sherpa-onnx 0.3.9 → 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 (69) hide show
  1. package/README.md +17 -4
  2. package/SherpaOnnx.podspec +1 -0
  3. package/android/prebuilt-download.gradle +67 -27
  4. package/android/prebuilt-versions.gradle +1 -1
  5. package/android/src/main/assets/model_licenses/speech-enhancement-models-license-status.csv +7 -0
  6. package/android/src/main/cpp/CMakeLists.txt +3 -0
  7. package/android/src/main/cpp/jni/model_detect/sherpa-onnx-enhancement-wrapper.cpp +68 -0
  8. package/android/src/main/cpp/jni/model_detect/sherpa-onnx-enhancement-wrapper.h +17 -0
  9. package/android/src/main/cpp/jni/model_detect/sherpa-onnx-model-detect-enhancement.cpp +119 -0
  10. package/android/src/main/cpp/jni/model_detect/sherpa-onnx-model-detect.h +31 -0
  11. package/android/src/main/cpp/jni/model_detect/sherpa-onnx-validate-enhancement.cpp +68 -0
  12. package/android/src/main/cpp/jni/model_detect/sherpa-onnx-validate-enhancement.h +30 -0
  13. package/android/src/main/cpp/jni/module/sherpa-onnx-module-jni.cpp +21 -0
  14. package/android/src/main/java/com/sherpaonnx/SherpaOnnxAssetHelper.kt +6 -0
  15. package/android/src/main/java/com/sherpaonnx/SherpaOnnxEnhancementHelper.kt +377 -0
  16. package/android/src/main/java/com/sherpaonnx/SherpaOnnxModule.kt +106 -0
  17. package/ios/Resources/model_licenses/speech-enhancement-models-license-status.csv +7 -0
  18. package/ios/SherpaOnnx+Assets.mm +5 -0
  19. package/ios/SherpaOnnx+Enhancement.mm +435 -0
  20. package/ios/enhancement/sherpa-onnx-enhancement-wrapper.h +85 -0
  21. package/ios/enhancement/sherpa-onnx-enhancement-wrapper.mm +218 -0
  22. package/ios/model_detect/sherpa-onnx-model-detect-enhancement.mm +92 -0
  23. package/ios/model_detect/sherpa-onnx-model-detect.h +23 -0
  24. package/ios/model_detect/sherpa-onnx-validate-enhancement.h +30 -0
  25. package/ios/model_detect/sherpa-onnx-validate-enhancement.mm +69 -0
  26. package/lib/module/NativeSherpaOnnx.js.map +1 -1
  27. package/lib/module/download/localModels.js +2 -3
  28. package/lib/module/download/localModels.js.map +1 -1
  29. package/lib/module/download/paths.js +2 -1
  30. package/lib/module/download/paths.js.map +1 -1
  31. package/lib/module/enhancement/index.js +63 -48
  32. package/lib/module/enhancement/index.js.map +1 -1
  33. package/lib/module/enhancement/streaming.js +60 -0
  34. package/lib/module/enhancement/streaming.js.map +1 -0
  35. package/lib/module/enhancement/streamingTypes.js +4 -0
  36. package/lib/module/enhancement/streamingTypes.js.map +1 -0
  37. package/lib/module/enhancement/types.js +4 -0
  38. package/lib/module/enhancement/types.js.map +1 -0
  39. package/lib/module/licenses.js +9 -3
  40. package/lib/module/licenses.js.map +1 -1
  41. package/lib/typescript/src/NativeSherpaOnnx.d.ts +45 -0
  42. package/lib/typescript/src/NativeSherpaOnnx.d.ts.map +1 -1
  43. package/lib/typescript/src/download/localModels.d.ts.map +1 -1
  44. package/lib/typescript/src/download/paths.d.ts +2 -1
  45. package/lib/typescript/src/download/paths.d.ts.map +1 -1
  46. package/lib/typescript/src/enhancement/index.d.ts +9 -46
  47. package/lib/typescript/src/enhancement/index.d.ts.map +1 -1
  48. package/lib/typescript/src/enhancement/streaming.d.ts +6 -0
  49. package/lib/typescript/src/enhancement/streaming.d.ts.map +1 -0
  50. package/lib/typescript/src/enhancement/streamingTypes.d.ts +12 -0
  51. package/lib/typescript/src/enhancement/streamingTypes.d.ts.map +1 -0
  52. package/lib/typescript/src/enhancement/types.d.ts +31 -0
  53. package/lib/typescript/src/enhancement/types.d.ts.map +1 -0
  54. package/lib/typescript/src/licenses.d.ts.map +1 -1
  55. package/package.json +1 -1
  56. package/scripts/ci/check-model-csvs.sh +27 -2
  57. package/scripts/ci/collect_all_sherpa_model_streams.sh +3 -1
  58. package/scripts/ci/collect_one_sherpa_release_stream.sh +3 -1
  59. package/scripts/ci/sherpa_speech_enhancement_model_release_streams.json +13 -0
  60. package/scripts/ci/update_model_license_csv.sh +1 -1
  61. package/src/NativeSherpaOnnx.ts +71 -0
  62. package/src/download/localModels.ts +1 -3
  63. package/src/download/paths.ts +2 -1
  64. package/src/enhancement/index.ts +120 -58
  65. package/src/enhancement/streaming.ts +105 -0
  66. package/src/enhancement/streamingTypes.ts +14 -0
  67. package/src/enhancement/types.ts +36 -0
  68. package/src/licenses.ts +13 -2
  69. package/third_party/sherpa-onnx-prebuilt/ANDROID_RELEASE_TAG +1 -1
@@ -422,6 +422,77 @@ export interface Spec extends TurboModule {
422
422
  */
423
423
  unloadTts(instanceId: string): Promise<void>;
424
424
 
425
+ // ==================== Speech Enhancement Methods ====================
426
+
427
+ detectEnhancementModel(
428
+ modelDir: string,
429
+ modelType?: string
430
+ ): Promise<{
431
+ success: boolean;
432
+ error?: string;
433
+ detectedModels: Array<{ type: string; modelDir: string }>;
434
+ modelType?: string;
435
+ }>;
436
+
437
+ initializeEnhancement(
438
+ instanceId: string,
439
+ modelDir: string,
440
+ modelType?: string,
441
+ numThreads?: number,
442
+ provider?: string,
443
+ debug?: boolean
444
+ ): Promise<{
445
+ success: boolean;
446
+ error?: string;
447
+ detectedModels: Array<{ type: string; modelDir: string }>;
448
+ modelType?: string;
449
+ sampleRate?: number;
450
+ }>;
451
+
452
+ enhanceFile(
453
+ instanceId: string,
454
+ inputPath: string,
455
+ outputPath?: string
456
+ ): Promise<{ samples: number[]; sampleRate: number }>;
457
+
458
+ enhanceSamples(
459
+ instanceId: string,
460
+ samples: number[],
461
+ sampleRate: number
462
+ ): Promise<{ samples: number[]; sampleRate: number }>;
463
+
464
+ getEnhancementSampleRate(instanceId: string): Promise<number>;
465
+
466
+ unloadEnhancement(instanceId: string): Promise<void>;
467
+
468
+ initializeOnlineEnhancement(
469
+ instanceId: string,
470
+ modelDir: string,
471
+ modelType?: string,
472
+ numThreads?: number,
473
+ provider?: string,
474
+ debug?: boolean
475
+ ): Promise<{
476
+ success: boolean;
477
+ error?: string;
478
+ sampleRate?: number;
479
+ frameShiftInSamples?: number;
480
+ }>;
481
+
482
+ feedEnhancementSamples(
483
+ instanceId: string,
484
+ samples: number[],
485
+ sampleRate: number
486
+ ): Promise<{ samples: number[]; sampleRate: number }>;
487
+
488
+ flushOnlineEnhancement(
489
+ instanceId: string
490
+ ): Promise<{ samples: number[]; sampleRate: number }>;
491
+
492
+ resetOnlineEnhancement(instanceId: string): Promise<void>;
493
+
494
+ unloadOnlineEnhancement(instanceId: string): Promise<void>;
495
+
425
496
  /**
426
497
  * Save TTS audio samples to a WAV file.
427
498
  * @param samples - Audio samples array
@@ -18,12 +18,11 @@ import {
18
18
  getModelsBaseDir,
19
19
  getModelDir,
20
20
  getManifestPath,
21
- getNativeAssetExtractedModelDir,
22
21
  getReadyMarkerPath,
23
22
  getTarArchivePath,
24
23
  getOnnxPath,
25
24
  } from './paths';
26
- import { resolveActualModelDir, removeDirectoryRecursive } from './validation';
25
+ import { resolveActualModelDir } from './validation';
27
26
  import { emitModelsListUpdated } from './downloadEvents';
28
27
  import { clearMemoryCacheForCategory } from './registry';
29
28
 
@@ -211,7 +210,6 @@ export async function deleteModelByCategory(
211
210
  if (await exists(onnxPath)) {
212
211
  await unlink(onnxPath);
213
212
  }
214
- await removeDirectoryRecursive(getNativeAssetExtractedModelDir(id));
215
213
  const list = await listDownloadedModelsByCategory<ModelMetaBase>(category);
216
214
  emitModelsListUpdated(category, list);
217
215
  }
@@ -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, '');
@@ -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
+ }
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
  }
@@ -1 +1 @@
1
- sherpa-onnx-android-v1.12.34-1
1
+ sherpa-onnx-android-v1.12.34-2