@tensamin/audio 0.1.2 → 0.1.3

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.
@@ -63,32 +63,60 @@ var RNNoisePlugin = class {
63
63
  async createNode(context, config) {
64
64
  const { loadRnnoise, RnnoiseWorkletNode } = await import("@sapphi-red/web-noise-suppressor");
65
65
  if (!config?.enabled) {
66
+ console.log("Noise suppression disabled, using passthrough node");
66
67
  const pass = context.createGain();
67
68
  return pass;
68
69
  }
69
70
  if (!config?.wasmUrl || !config?.simdUrl || !config?.workletUrl) {
70
- throw new Error(
71
- "RNNoisePlugin requires 'wasmUrl', 'simdUrl', and 'workletUrl' to be configured. Please download the assets and provide the URLs."
71
+ const error = new Error(
72
+ `RNNoisePlugin requires 'wasmUrl', 'simdUrl', and 'workletUrl' to be configured. Please download the assets from @sapphi-red/web-noise-suppressor and provide the URLs in the config. Current config: wasmUrl=${config?.wasmUrl}, simdUrl=${config?.simdUrl}, workletUrl=${config?.workletUrl}
73
+ To disable noise suppression, set noiseSuppression.enabled to false.`
72
74
  );
75
+ console.error(error.message);
76
+ throw error;
73
77
  }
74
- if (!this.wasmBuffer) {
75
- this.wasmBuffer = await loadRnnoise({
76
- url: config.wasmUrl,
77
- simdUrl: config.simdUrl
78
- });
78
+ try {
79
+ if (!this.wasmBuffer) {
80
+ console.log("Loading RNNoise WASM binary...");
81
+ this.wasmBuffer = await loadRnnoise({
82
+ url: config.wasmUrl,
83
+ simdUrl: config.simdUrl
84
+ });
85
+ console.log("RNNoise WASM loaded successfully");
86
+ }
87
+ } catch (error) {
88
+ const err = new Error(
89
+ `Failed to load RNNoise WASM binary: ${error instanceof Error ? error.message : String(error)}`
90
+ );
91
+ console.error(err);
92
+ throw err;
79
93
  }
80
94
  const workletUrl = config.workletUrl;
81
95
  try {
82
96
  await context.audioWorklet.addModule(workletUrl);
97
+ console.log("RNNoise worklet loaded successfully");
83
98
  } catch (e) {
84
- console.warn("Failed to add RNNoise worklet module:", e);
99
+ const error = new Error(
100
+ `Failed to load RNNoise worklet from ${workletUrl}: ${e instanceof Error ? e.message : String(e)}. Ensure the workletUrl points to a valid RNNoise worklet script.`
101
+ );
102
+ console.error(error.message);
103
+ throw error;
104
+ }
105
+ try {
106
+ const node = new RnnoiseWorkletNode(context, {
107
+ wasmBinary: this.wasmBuffer,
108
+ maxChannels: 1
109
+ // Mono for now
110
+ });
111
+ console.log("RNNoise worklet node created successfully");
112
+ return node;
113
+ } catch (error) {
114
+ const err = new Error(
115
+ `Failed to create RNNoise worklet node: ${error instanceof Error ? error.message : String(error)}`
116
+ );
117
+ console.error(err);
118
+ throw err;
85
119
  }
86
- const node = new RnnoiseWorkletNode(context, {
87
- wasmBinary: this.wasmBuffer,
88
- maxChannels: 1
89
- // Mono for now
90
- });
91
- return node;
92
120
  }
93
121
  };
94
122
 
@@ -136,22 +164,52 @@ registerProcessor('energy-vad-processor', EnergyVadProcessor);
136
164
  var EnergyVADPlugin = class {
137
165
  name = "energy-vad";
138
166
  async createNode(context, config, onDecision) {
167
+ if (!config?.enabled) {
168
+ console.log("VAD disabled, using passthrough node");
169
+ const pass = context.createGain();
170
+ return pass;
171
+ }
139
172
  const blob = new Blob([energyVadWorkletCode], {
140
173
  type: "application/javascript"
141
174
  });
142
175
  const url = URL.createObjectURL(blob);
143
176
  try {
144
177
  await context.audioWorklet.addModule(url);
178
+ console.log("Energy VAD worklet loaded successfully");
145
179
  } catch (e) {
146
- console.warn("Failed to add Energy VAD worklet:", e);
147
- throw e;
148
- } finally {
180
+ const error = new Error(
181
+ `Failed to load Energy VAD worklet: ${e instanceof Error ? e.message : String(e)}`
182
+ );
183
+ console.error(error.message);
149
184
  URL.revokeObjectURL(url);
185
+ throw error;
186
+ }
187
+ URL.revokeObjectURL(url);
188
+ let node;
189
+ try {
190
+ node = new AudioWorkletNode(context, "energy-vad-processor");
191
+ console.log("Energy VAD node created successfully");
192
+ } catch (e) {
193
+ const error = new Error(
194
+ `Failed to create Energy VAD node: ${e instanceof Error ? e.message : String(e)}`
195
+ );
196
+ console.error(error.message);
197
+ throw error;
150
198
  }
151
- const node = new AudioWorkletNode(context, "energy-vad-processor");
152
199
  node.port.onmessage = (event) => {
153
- const { probability } = event.data;
154
- onDecision(probability);
200
+ try {
201
+ const { probability } = event.data;
202
+ if (typeof probability === "number" && !isNaN(probability)) {
203
+ onDecision(probability);
204
+ } else {
205
+ console.warn("Invalid VAD probability received:", event.data);
206
+ }
207
+ } catch (error) {
208
+ console.error("Error in VAD message handler:", error);
209
+ }
210
+ };
211
+ node.port.onmessageerror = (event) => {
212
+ console.error("VAD port message error:", event);
155
213
  };
156
214
  return node;
157
215
  }
@@ -245,42 +303,84 @@ var VADStateMachine = class {
245
303
  async function createAudioPipeline(sourceTrack, config = {}) {
246
304
  const context = getAudioContext();
247
305
  registerPipeline();
306
+ const nsEnabled = config.noiseSuppression?.enabled !== false && Boolean(config.noiseSuppression?.wasmUrl && config.noiseSuppression?.simdUrl && config.noiseSuppression?.workletUrl);
307
+ const vadEnabled = config.vad?.enabled !== false;
248
308
  const fullConfig = {
249
- noiseSuppression: { enabled: true, ...config.noiseSuppression },
250
- vad: { enabled: true, ...config.vad },
309
+ noiseSuppression: {
310
+ enabled: nsEnabled,
311
+ ...config.noiseSuppression
312
+ },
313
+ vad: {
314
+ enabled: vadEnabled,
315
+ ...config.vad
316
+ },
251
317
  output: {
252
318
  speechGain: 1,
253
- silenceGain: 0,
319
+ silenceGain: vadEnabled ? 0 : 1,
320
+ // If no VAD, always output audio
254
321
  gainRampTime: 0.02,
255
322
  ...config.output
256
323
  },
257
324
  livekit: { manageTrackMute: false, ...config.livekit }
258
325
  };
326
+ console.log("Audio pipeline config:", {
327
+ noiseSuppression: fullConfig.noiseSuppression?.enabled,
328
+ vad: fullConfig.vad?.enabled,
329
+ output: fullConfig.output
330
+ });
331
+ if (!sourceTrack || sourceTrack.kind !== "audio") {
332
+ throw new Error("createAudioPipeline requires a valid audio MediaStreamTrack");
333
+ }
334
+ if (sourceTrack.readyState === "ended") {
335
+ throw new Error("Cannot create pipeline from an ended MediaStreamTrack");
336
+ }
259
337
  const sourceStream = new MediaStream([sourceTrack]);
260
338
  const sourceNode = context.createMediaStreamSource(sourceStream);
261
- const nsPlugin = getNoiseSuppressionPlugin(
262
- fullConfig.noiseSuppression?.pluginName
263
- );
264
- const nsNode = await nsPlugin.createNode(
265
- context,
266
- fullConfig.noiseSuppression
267
- );
268
- const vadPlugin = getVADPlugin(fullConfig.vad?.pluginName);
269
- const vadStateMachine = new VADStateMachine(fullConfig.vad);
339
+ let nsNode;
340
+ let vadNode;
270
341
  const emitter = (0, import_mitt.default)();
271
- const vadNode = await vadPlugin.createNode(
272
- context,
273
- fullConfig.vad,
274
- (prob) => {
275
- const timestamp = context.currentTime * 1e3;
276
- const newState = vadStateMachine.processFrame(prob, timestamp);
277
- if (newState.state !== lastVadState.state || Math.abs(newState.probability - lastVadState.probability) > 0.1) {
278
- emitter.emit("vadChange", newState);
279
- lastVadState = newState;
280
- updateGain(newState);
342
+ try {
343
+ const nsPlugin = getNoiseSuppressionPlugin(
344
+ fullConfig.noiseSuppression?.pluginName
345
+ );
346
+ nsNode = await nsPlugin.createNode(
347
+ context,
348
+ fullConfig.noiseSuppression
349
+ );
350
+ } catch (error) {
351
+ const err = error instanceof Error ? error : new Error(String(error));
352
+ console.error("Failed to create noise suppression node:", err);
353
+ emitter.emit("error", err);
354
+ throw err;
355
+ }
356
+ const vadStateMachine = new VADStateMachine(fullConfig.vad);
357
+ try {
358
+ const vadPlugin = getVADPlugin(fullConfig.vad?.pluginName);
359
+ vadNode = await vadPlugin.createNode(
360
+ context,
361
+ fullConfig.vad,
362
+ (prob) => {
363
+ try {
364
+ const timestamp = context.currentTime * 1e3;
365
+ const newState = vadStateMachine.processFrame(prob, timestamp);
366
+ if (newState.state !== lastVadState.state || Math.abs(newState.probability - lastVadState.probability) > 0.1) {
367
+ emitter.emit("vadChange", newState);
368
+ lastVadState = newState;
369
+ updateGain(newState);
370
+ }
371
+ } catch (vadError) {
372
+ const err = vadError instanceof Error ? vadError : new Error(String(vadError));
373
+ console.error("Error in VAD callback:", err);
374
+ emitter.emit("error", err);
375
+ }
281
376
  }
282
- }
283
- );
377
+ );
378
+ } catch (error) {
379
+ const err = error instanceof Error ? error : new Error(String(error));
380
+ console.error("Failed to create VAD node:", err);
381
+ emitter.emit("error", err);
382
+ throw err;
383
+ }
284
384
  let lastVadState = {
285
385
  isSpeaking: false,
286
386
  probability: 0,
@@ -296,34 +396,98 @@ async function createAudioPipeline(sourceTrack, config = {}) {
296
396
  const gainNode = context.createGain();
297
397
  gainNode.gain.value = fullConfig.output?.silenceGain ?? 0;
298
398
  const destination = context.createMediaStreamDestination();
299
- splitter.connect(delayNode);
300
- delayNode.connect(gainNode);
301
- gainNode.connect(destination);
399
+ try {
400
+ splitter.connect(delayNode);
401
+ delayNode.connect(gainNode);
402
+ gainNode.connect(destination);
403
+ } catch (error) {
404
+ const err = error instanceof Error ? error : new Error(String(error));
405
+ console.error("Failed to wire audio pipeline:", err);
406
+ emitter.emit("error", err);
407
+ throw err;
408
+ }
302
409
  function updateGain(state) {
303
- const { speechGain, silenceGain, gainRampTime } = fullConfig.output;
304
- const targetGain = state.isSpeaking ? speechGain ?? 1 : silenceGain ?? 0;
305
- const now = context.currentTime;
306
- gainNode.gain.setTargetAtTime(targetGain, now, gainRampTime ?? 0.02);
410
+ try {
411
+ const { speechGain, silenceGain, gainRampTime } = fullConfig.output;
412
+ const targetGain = state.isSpeaking ? speechGain ?? 1 : silenceGain ?? 0;
413
+ const now = context.currentTime;
414
+ gainNode.gain.setTargetAtTime(targetGain, now, gainRampTime ?? 0.02);
415
+ } catch (error) {
416
+ const err = error instanceof Error ? error : new Error(String(error));
417
+ console.error("Failed to update gain:", err);
418
+ emitter.emit("error", err);
419
+ }
420
+ }
421
+ const audioTracks = destination.stream.getAudioTracks();
422
+ console.log("Destination stream tracks:", {
423
+ count: audioTracks.length,
424
+ tracks: audioTracks.map((t) => ({
425
+ id: t.id,
426
+ label: t.label,
427
+ enabled: t.enabled,
428
+ readyState: t.readyState
429
+ }))
430
+ });
431
+ if (audioTracks.length === 0) {
432
+ const err = new Error(
433
+ "Failed to create processed audio track: destination stream has no audio tracks. This may indicate an issue with the audio graph connection."
434
+ );
435
+ console.error(err);
436
+ emitter.emit("error", err);
437
+ throw err;
438
+ }
439
+ const processedTrack = audioTracks[0];
440
+ if (!processedTrack || processedTrack.readyState === "ended") {
441
+ const err = new Error("Processed audio track is invalid or ended");
442
+ console.error(err);
443
+ emitter.emit("error", err);
444
+ throw err;
307
445
  }
446
+ console.log("Audio pipeline created successfully:", {
447
+ sourceTrack: {
448
+ id: sourceTrack.id,
449
+ label: sourceTrack.label,
450
+ readyState: sourceTrack.readyState
451
+ },
452
+ processedTrack: {
453
+ id: processedTrack.id,
454
+ label: processedTrack.label,
455
+ readyState: processedTrack.readyState
456
+ },
457
+ config: {
458
+ noiseSuppression: fullConfig.noiseSuppression?.enabled,
459
+ vad: fullConfig.vad?.enabled
460
+ }
461
+ });
308
462
  function dispose() {
309
- sourceNode.disconnect();
310
- nsNode.disconnect();
311
- splitter.disconnect();
312
- vadNode.disconnect();
313
- delayNode.disconnect();
314
- gainNode.disconnect();
315
- destination.stream.getTracks().forEach((t) => t.stop());
316
- unregisterPipeline();
463
+ try {
464
+ sourceNode.disconnect();
465
+ nsNode.disconnect();
466
+ splitter.disconnect();
467
+ vadNode.disconnect();
468
+ delayNode.disconnect();
469
+ gainNode.disconnect();
470
+ destination.stream.getTracks().forEach((t) => t.stop());
471
+ unregisterPipeline();
472
+ } catch (error) {
473
+ console.error("Error during pipeline disposal:", error);
474
+ }
317
475
  }
318
476
  return {
319
- processedTrack: destination.stream.getAudioTracks()[0],
477
+ processedTrack,
320
478
  events: emitter,
321
479
  get state() {
322
480
  return lastVadState;
323
481
  },
324
482
  setConfig: (newConfig) => {
325
- if (newConfig.vad) {
326
- vadStateMachine.updateConfig(newConfig.vad);
483
+ try {
484
+ if (newConfig.vad) {
485
+ vadStateMachine.updateConfig(newConfig.vad);
486
+ }
487
+ } catch (error) {
488
+ const err = error instanceof Error ? error : new Error(String(error));
489
+ console.error("Failed to update config:", err);
490
+ emitter.emit("error", err);
327
491
  }
328
492
  },
329
493
  dispose
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  createAudioPipeline
3
- } from "../chunk-QU7E5HBA.mjs";
3
+ } from "../chunk-EXH2PNUE.mjs";
4
4
  import "../chunk-JJASCVEW.mjs";
5
5
  import "../chunk-OZ7KMC4S.mjs";
6
- import "../chunk-FS635GMR.mjs";
7
- import "../chunk-SDTOKWM2.mjs";
8
- import "../chunk-UMU2KIB6.mjs";
6
+ import "../chunk-6P2RDBW5.mjs";
7
+ import "../chunk-XO6B3D4A.mjs";
8
+ import "../chunk-R5JVHKWA.mjs";
9
9
  export {
10
10
  createAudioPipeline
11
11
  };
@@ -66,22 +66,52 @@ registerProcessor('energy-vad-processor', EnergyVadProcessor);
66
66
  var EnergyVADPlugin = class {
67
67
  name = "energy-vad";
68
68
  async createNode(context, config, onDecision) {
69
+ if (!config?.enabled) {
70
+ console.log("VAD disabled, using passthrough node");
71
+ const pass = context.createGain();
72
+ return pass;
73
+ }
69
74
  const blob = new Blob([energyVadWorkletCode], {
70
75
  type: "application/javascript"
71
76
  });
72
77
  const url = URL.createObjectURL(blob);
73
78
  try {
74
79
  await context.audioWorklet.addModule(url);
80
+ console.log("Energy VAD worklet loaded successfully");
75
81
  } catch (e) {
76
- console.warn("Failed to add Energy VAD worklet:", e);
77
- throw e;
78
- } finally {
82
+ const error = new Error(
83
+ `Failed to load Energy VAD worklet: ${e instanceof Error ? e.message : String(e)}`
84
+ );
85
+ console.error(error.message);
79
86
  URL.revokeObjectURL(url);
87
+ throw error;
88
+ }
89
+ URL.revokeObjectURL(url);
90
+ let node;
91
+ try {
92
+ node = new AudioWorkletNode(context, "energy-vad-processor");
93
+ console.log("Energy VAD node created successfully");
94
+ } catch (e) {
95
+ const error = new Error(
96
+ `Failed to create Energy VAD node: ${e instanceof Error ? e.message : String(e)}`
97
+ );
98
+ console.error(error.message);
99
+ throw error;
80
100
  }
81
- const node = new AudioWorkletNode(context, "energy-vad-processor");
82
101
  node.port.onmessage = (event) => {
83
- const { probability } = event.data;
84
- onDecision(probability);
102
+ try {
103
+ const { probability } = event.data;
104
+ if (typeof probability === "number" && !isNaN(probability)) {
105
+ onDecision(probability);
106
+ } else {
107
+ console.warn("Invalid VAD probability received:", event.data);
108
+ }
109
+ } catch (error) {
110
+ console.error("Error in VAD message handler:", error);
111
+ }
112
+ };
113
+ node.port.onmessageerror = (event) => {
114
+ console.error("VAD port message error:", event);
85
115
  };
86
116
  return node;
87
117
  }
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  EnergyVADPlugin
3
- } from "../chunk-UMU2KIB6.mjs";
3
+ } from "../chunk-R5JVHKWA.mjs";
4
4
  export {
5
5
  EnergyVADPlugin
6
6
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tensamin/audio",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/index.mjs",
6
6
  "types": "dist/index.d.ts",
@@ -1,38 +0,0 @@
1
- import {
2
- createAudioPipeline
3
- } from "./chunk-QU7E5HBA.mjs";
4
-
5
- // src/livekit/integration.ts
6
- async function attachProcessingToTrack(track, config = {}) {
7
- const originalTrack = track.mediaStreamTrack;
8
- const pipeline = await createAudioPipeline(originalTrack, config);
9
- await track.replaceTrack(pipeline.processedTrack);
10
- if (config.livekit?.manageTrackMute) {
11
- let isVadMuted = false;
12
- pipeline.events.on("vadChange", async (state) => {
13
- if (state.isSpeaking) {
14
- if (isVadMuted) {
15
- await track.unmute();
16
- isVadMuted = false;
17
- }
18
- } else {
19
- if (!track.isMuted) {
20
- await track.mute();
21
- isVadMuted = true;
22
- }
23
- }
24
- });
25
- }
26
- const originalDispose = pipeline.dispose;
27
- pipeline.dispose = () => {
28
- if (originalTrack.readyState === "live") {
29
- track.replaceTrack(originalTrack).catch(console.error);
30
- }
31
- originalDispose();
32
- };
33
- return pipeline;
34
- }
35
-
36
- export {
37
- attachProcessingToTrack
38
- };
@@ -1,106 +0,0 @@
1
- import {
2
- VADStateMachine
3
- } from "./chunk-JJASCVEW.mjs";
4
- import {
5
- getAudioContext,
6
- registerPipeline,
7
- unregisterPipeline
8
- } from "./chunk-OZ7KMC4S.mjs";
9
- import {
10
- getNoiseSuppressionPlugin,
11
- getVADPlugin
12
- } from "./chunk-FS635GMR.mjs";
13
-
14
- // src/pipeline/audio-pipeline.ts
15
- import mitt from "mitt";
16
- async function createAudioPipeline(sourceTrack, config = {}) {
17
- const context = getAudioContext();
18
- registerPipeline();
19
- const fullConfig = {
20
- noiseSuppression: { enabled: true, ...config.noiseSuppression },
21
- vad: { enabled: true, ...config.vad },
22
- output: {
23
- speechGain: 1,
24
- silenceGain: 0,
25
- gainRampTime: 0.02,
26
- ...config.output
27
- },
28
- livekit: { manageTrackMute: false, ...config.livekit }
29
- };
30
- const sourceStream = new MediaStream([sourceTrack]);
31
- const sourceNode = context.createMediaStreamSource(sourceStream);
32
- const nsPlugin = getNoiseSuppressionPlugin(
33
- fullConfig.noiseSuppression?.pluginName
34
- );
35
- const nsNode = await nsPlugin.createNode(
36
- context,
37
- fullConfig.noiseSuppression
38
- );
39
- const vadPlugin = getVADPlugin(fullConfig.vad?.pluginName);
40
- const vadStateMachine = new VADStateMachine(fullConfig.vad);
41
- const emitter = mitt();
42
- const vadNode = await vadPlugin.createNode(
43
- context,
44
- fullConfig.vad,
45
- (prob) => {
46
- const timestamp = context.currentTime * 1e3;
47
- const newState = vadStateMachine.processFrame(prob, timestamp);
48
- if (newState.state !== lastVadState.state || Math.abs(newState.probability - lastVadState.probability) > 0.1) {
49
- emitter.emit("vadChange", newState);
50
- lastVadState = newState;
51
- updateGain(newState);
52
- }
53
- }
54
- );
55
- let lastVadState = {
56
- isSpeaking: false,
57
- probability: 0,
58
- state: "silent"
59
- };
60
- const splitter = context.createGain();
61
- sourceNode.connect(nsNode);
62
- nsNode.connect(splitter);
63
- splitter.connect(vadNode);
64
- const delayNode = context.createDelay(1);
65
- const preRollSeconds = (fullConfig.vad?.preRollMs ?? 200) / 1e3;
66
- delayNode.delayTime.value = preRollSeconds;
67
- const gainNode = context.createGain();
68
- gainNode.gain.value = fullConfig.output?.silenceGain ?? 0;
69
- const destination = context.createMediaStreamDestination();
70
- splitter.connect(delayNode);
71
- delayNode.connect(gainNode);
72
- gainNode.connect(destination);
73
- function updateGain(state) {
74
- const { speechGain, silenceGain, gainRampTime } = fullConfig.output;
75
- const targetGain = state.isSpeaking ? speechGain ?? 1 : silenceGain ?? 0;
76
- const now = context.currentTime;
77
- gainNode.gain.setTargetAtTime(targetGain, now, gainRampTime ?? 0.02);
78
- }
79
- function dispose() {
80
- sourceNode.disconnect();
81
- nsNode.disconnect();
82
- splitter.disconnect();
83
- vadNode.disconnect();
84
- delayNode.disconnect();
85
- gainNode.disconnect();
86
- destination.stream.getTracks().forEach((t) => t.stop());
87
- unregisterPipeline();
88
- }
89
- return {
90
- processedTrack: destination.stream.getAudioTracks()[0],
91
- events: emitter,
92
- get state() {
93
- return lastVadState;
94
- },
95
- setConfig: (newConfig) => {
96
- if (newConfig.vad) {
97
- vadStateMachine.updateConfig(newConfig.vad);
98
- }
99
- },
100
- dispose
101
- };
102
- }
103
-
104
- export {
105
- createAudioPipeline
106
- };
@@ -1,39 +0,0 @@
1
- // src/noise-suppression/rnnoise-node.ts
2
- var RNNoisePlugin = class {
3
- name = "rnnoise-ns";
4
- wasmBuffer = null;
5
- async createNode(context, config) {
6
- const { loadRnnoise, RnnoiseWorkletNode } = await import("@sapphi-red/web-noise-suppressor");
7
- if (!config?.enabled) {
8
- const pass = context.createGain();
9
- return pass;
10
- }
11
- if (!config?.wasmUrl || !config?.simdUrl || !config?.workletUrl) {
12
- throw new Error(
13
- "RNNoisePlugin requires 'wasmUrl', 'simdUrl', and 'workletUrl' to be configured. Please download the assets and provide the URLs."
14
- );
15
- }
16
- if (!this.wasmBuffer) {
17
- this.wasmBuffer = await loadRnnoise({
18
- url: config.wasmUrl,
19
- simdUrl: config.simdUrl
20
- });
21
- }
22
- const workletUrl = config.workletUrl;
23
- try {
24
- await context.audioWorklet.addModule(workletUrl);
25
- } catch (e) {
26
- console.warn("Failed to add RNNoise worklet module:", e);
27
- }
28
- const node = new RnnoiseWorkletNode(context, {
29
- wasmBinary: this.wasmBuffer,
30
- maxChannels: 1
31
- // Mono for now
32
- });
33
- return node;
34
- }
35
- };
36
-
37
- export {
38
- RNNoisePlugin
39
- };