deeeed commited on
Commit
600c1e3
·
verified ·
1 Parent(s): 8f11de2

Upload wasm/runtime/sherpa-onnx-enhancement.js with huggingface_hub

Browse files
wasm/runtime/sherpa-onnx-enhancement.js ADDED
@@ -0,0 +1,204 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * sherpa-onnx-enhancement.js
3
+ *
4
+ * Speech Enhancement (denoising) functionality for SherpaOnnx
5
+ * Based on upstream sherpa-onnx-speech-enhancement.js
6
+ * Requires sherpa-onnx-core.js to be loaded first
7
+ */
8
+
9
+ (function(global) {
10
+ if (!global.SherpaOnnx) {
11
+ console.error('SherpaOnnx namespace not found. Make sure to load sherpa-onnx-core.js first.');
12
+ return;
13
+ }
14
+
15
+ const SherpaOnnx = global.SherpaOnnx;
16
+
17
+ // --- WASM struct init helpers ---
18
+
19
+ function freeConfig(config, Module) {
20
+ if ('buffer' in config) {
21
+ Module._free(config.buffer);
22
+ }
23
+ if ('config' in config) {
24
+ freeConfig(config.config, Module);
25
+ }
26
+ if ('gtcrn' in config) {
27
+ freeConfig(config.gtcrn, Module);
28
+ }
29
+ Module._free(config.ptr);
30
+ }
31
+
32
+ function initSherpaOnnxOfflineSpeechDenoiserGtcrnModelConfig(config, Module) {
33
+ if (!('model' in config)) {
34
+ config.model = '';
35
+ }
36
+
37
+ const modelLen = Module.lengthBytesUTF8(config.model) + 1;
38
+ const buffer = Module._malloc(modelLen);
39
+ const len = 1 * 4;
40
+ const ptr = Module._malloc(len);
41
+
42
+ Module.stringToUTF8(config.model, buffer, modelLen);
43
+ Module.setValue(ptr, buffer, 'i8*');
44
+
45
+ return { buffer: buffer, ptr: ptr, len: len };
46
+ }
47
+
48
+ function initSherpaOnnxOfflineSpeechDenoiserModelConfig(config, Module) {
49
+ if (!('gtcrn' in config)) {
50
+ config.gtcrn = { model: '' };
51
+ }
52
+
53
+ const gtcrn = initSherpaOnnxOfflineSpeechDenoiserGtcrnModelConfig(config.gtcrn, Module);
54
+
55
+ const len = gtcrn.len + 3 * 4;
56
+ const ptr = Module._malloc(len);
57
+
58
+ let offset = 0;
59
+ Module._CopyHeap(gtcrn.ptr, gtcrn.len, ptr + offset);
60
+ offset += gtcrn.len;
61
+
62
+ Module.setValue(ptr + offset, config.numThreads || 1, 'i32');
63
+ offset += 4;
64
+
65
+ Module.setValue(ptr + offset, config.debug || 0, 'i32');
66
+ offset += 4;
67
+
68
+ const providerLen = Module.lengthBytesUTF8(config.provider || 'cpu') + 1;
69
+ const buffer = Module._malloc(providerLen);
70
+ Module.stringToUTF8(config.provider || 'cpu', buffer, providerLen);
71
+ Module.setValue(ptr + offset, buffer, 'i8*');
72
+ offset += 4;
73
+
74
+ return { buffer: buffer, ptr: ptr, len: len, gtcrn: gtcrn };
75
+ }
76
+
77
+ function initSherpaOnnxOfflineSpeechDenoiserConfig(config, Module) {
78
+ if (!('model' in config)) {
79
+ config.model = {
80
+ gtcrn: { model: '' },
81
+ provider: 'cpu',
82
+ debug: 1,
83
+ numThreads: 1,
84
+ };
85
+ }
86
+
87
+ const modelConfig = initSherpaOnnxOfflineSpeechDenoiserModelConfig(config.model, Module);
88
+ const len = modelConfig.len;
89
+ const ptr = Module._malloc(len);
90
+
91
+ Module._CopyHeap(modelConfig.ptr, modelConfig.len, ptr);
92
+
93
+ return { ptr: ptr, len: len, config: modelConfig };
94
+ }
95
+
96
+ // --- OfflineSpeechDenoiser class ---
97
+
98
+ class OfflineSpeechDenoiser {
99
+ constructor(configObj, Module) {
100
+ const config = initSherpaOnnxOfflineSpeechDenoiserConfig(configObj, Module);
101
+ const handle = Module._SherpaOnnxCreateOfflineSpeechDenoiser(config.ptr);
102
+
103
+ freeConfig(config, Module);
104
+
105
+ if (!handle) {
106
+ throw new Error('Failed to create speech denoiser - null handle');
107
+ }
108
+
109
+ this.handle = handle;
110
+ this.sampleRate = Module._SherpaOnnxOfflineSpeechDenoiserGetSampleRate(this.handle);
111
+ this.Module = Module;
112
+ }
113
+
114
+ free() {
115
+ if (this.handle) {
116
+ this.Module._SherpaOnnxDestroyOfflineSpeechDenoiser(this.handle);
117
+ this.handle = 0;
118
+ }
119
+ }
120
+
121
+ /**
122
+ * @param {Float32Array} samples - Audio samples in the range [-1, 1]
123
+ * @param {number} sampleRate - Sample rate of the input audio
124
+ * @returns {{samples: Float32Array, sampleRate: number}}
125
+ */
126
+ run(samples, sampleRate) {
127
+ const pointer = this.Module._malloc(samples.length * samples.BYTES_PER_ELEMENT);
128
+ this.Module.HEAPF32.set(samples, pointer / samples.BYTES_PER_ELEMENT);
129
+
130
+ const h = this.Module._SherpaOnnxOfflineSpeechDenoiserRun(
131
+ this.handle, pointer, samples.length, sampleRate);
132
+ this.Module._free(pointer);
133
+
134
+ if (!h) {
135
+ throw new Error('Speech denoising failed - null result');
136
+ }
137
+
138
+ const numSamples = this.Module.HEAP32[h / 4 + 1];
139
+ const denoisedSampleRate = this.Module.HEAP32[h / 4 + 2];
140
+
141
+ const samplesPtr = this.Module.HEAP32[h / 4] / 4;
142
+ const denoisedSamples = new Float32Array(numSamples);
143
+ for (let i = 0; i < numSamples; i++) {
144
+ denoisedSamples[i] = this.Module.HEAPF32[samplesPtr + i];
145
+ }
146
+
147
+ this.Module._SherpaOnnxDestroyDenoisedAudio(h);
148
+ return { samples: denoisedSamples, sampleRate: denoisedSampleRate };
149
+ }
150
+ }
151
+
152
+ // --- Namespace API ---
153
+
154
+ SherpaOnnx.SpeechEnhancement = {
155
+ loadModel: async function(modelConfig) {
156
+ const debug = modelConfig.debug || false;
157
+ const modelDir = modelConfig.modelDir || 'enhancement-models';
158
+
159
+ if (debug) console.log(`SpeechEnhancement.loadModel: dir=${modelDir}`);
160
+
161
+ SherpaOnnx.FileSystem.removePath(modelDir, debug);
162
+
163
+ const files = [{
164
+ url: modelConfig.model || 'assets/enhancement/gtcrn.onnx',
165
+ filename: 'gtcrn.onnx'
166
+ }];
167
+
168
+ const result = await SherpaOnnx.FileSystem.prepareModelDirectory(files, modelDir, debug);
169
+ if (!result.success) {
170
+ throw new Error('Failed to load speech enhancement model files');
171
+ }
172
+
173
+ const modelFile = result.files.find(f => f.success && f.original.filename === 'gtcrn.onnx');
174
+ return {
175
+ modelDir: result.modelDir,
176
+ modelPath: modelFile ? modelFile.path : `${result.modelDir}/gtcrn.onnx`,
177
+ };
178
+ },
179
+
180
+ createDenoiser: function(loadedModel, options = {}) {
181
+ const debug = options.debug !== undefined ? options.debug : false;
182
+ const config = {
183
+ model: {
184
+ gtcrn: { model: loadedModel.modelPath },
185
+ numThreads: options.numThreads || 1,
186
+ debug: debug ? 1 : 0,
187
+ provider: options.provider || 'cpu',
188
+ },
189
+ };
190
+
191
+ const denoiser = new OfflineSpeechDenoiser(config, global.Module);
192
+
193
+ if (SherpaOnnx.trackResource) {
194
+ SherpaOnnx.trackResource('denoiser', denoiser);
195
+ }
196
+
197
+ return denoiser;
198
+ },
199
+ };
200
+
201
+ if (typeof module !== 'undefined' && module.exports) {
202
+ module.exports = SherpaOnnx;
203
+ }
204
+ })(typeof window !== 'undefined' ? window : global);