Skip to content

Commit 965b45d

Browse files
committed
Share single instance for multiple nodes
1 parent 899060e commit 965b45d

File tree

7 files changed

+114
-87
lines changed

7 files changed

+114
-87
lines changed

demo/rnnoise.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ if (navigator.mediaDevices &&
6969
rnnoise.onstatus = data => { vadProb.style.width = data.vadProb * 100 + "%"; };
7070
(function a() {
7171
requestAnimationFrame(() => {
72-
rnnoise.update();
72+
rnnoise.update(true);
7373
a();
7474
});
7575
})();

dist/rnnoise-processor.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/rnnoise-processor.wasm

3 Bytes
Binary file not shown.

dist/rnnoise-runtime.js

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/processor.js

Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,37 @@
1-
registerProcessor("rnnoise", class extends AudioWorkletProcessor {
2-
constructor(options) {
3-
super({
4-
...options,
5-
numberOfInputs: 1,
6-
numberOfOutputs: 1,
7-
outputChannelCount: [1]
8-
});
9-
Object.assign(this, new WebAssembly.Instance(options.processorOptions.module).exports);
10-
this._heapFloat32 = new Float32Array(this.memory.buffer);
11-
this.reset();
12-
this.port.onmessage = () => {
13-
this.port.postMessage({ vadProb: this.getVadProb() });
14-
};
15-
}
1+
{
2+
let instance, heapFloat32;
3+
registerProcessor("rnnoise", class extends AudioWorkletProcessor {
4+
constructor(options) {
5+
super({
6+
...options,
7+
numberOfInputs: 1,
8+
numberOfOutputs: 1,
9+
outputChannelCount: [1]
10+
});
11+
if (!instance)
12+
heapFloat32 = new Float32Array((instance = new WebAssembly.Instance(options.processorOptions.module).exports).memory.buffer);
13+
this.state = instance.newState();
14+
this.alive = true;
15+
this.port.onmessage = ({ data: keepalive }) => {
16+
if (this.alive) {
17+
if (keepalive) {
18+
this.port.postMessage({ vadProb: instance.getVadProb(this.state) });
19+
} else {
20+
this.alive = false;
21+
instance.deleteState(this.state);
22+
}
23+
}
24+
};
25+
}
1626

17-
process(input, output, parameters) {
18-
this._heapFloat32.set(input[0][0], this.getInput() / 4);
19-
const o = output[0][0], ptr4 = this.pipe(o.length) / 4;
20-
if (ptr4)
21-
o.set(this._heapFloat32.subarray(ptr4, ptr4 + o.length));
22-
return true;
23-
}
24-
});
27+
process(input, output, parameters) {
28+
if (this.alive) {
29+
heapFloat32.set(input[0][0], instance.getInput(this.state) / 4);
30+
const o = output[0][0], ptr4 = instance.pipe(this.state, o.length) / 4;
31+
if (ptr4)
32+
o.set(heapFloat32.subarray(ptr4, ptr4 + o.length));
33+
return true;
34+
}
35+
}
36+
});
37+
}

src/runtime.js

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
1-
(function () {
1+
{
22
const base = document.currentScript.src.match(/(.*\/)?/)[0],
33
compilation = (WebAssembly.compileStreaming || (async f => await WebAssembly.compile(await (await f).arrayBuffer())))(fetch(base + "rnnoise-processor.wasm"));
4+
let module, instance, heapFloat32;
45
window.RNNoiseNode = (window.AudioWorkletNode || (window.AudioWorkletNode = window.webkitAudioWorkletNode)) &&
56
class extends AudioWorkletNode {
67
static async register(context) {
7-
if (!context.RNNoiseModule) {
8-
context.RNNoiseModule = await compilation;
9-
await context.audioWorklet.addModule(base + "rnnoise-processor.js");
10-
}
8+
module = await compilation;
9+
await context.audioWorklet.addModule(base + "rnnoise-processor.js");
1110
}
1211

1312
constructor(context) {
@@ -19,7 +18,7 @@
1918
numberOfOutputs: 1,
2019
outputChannelCount: [1],
2120
processorOptions: {
22-
module: context.RNNoiseModule
21+
module: module
2322
}
2423
});
2524
this.port.onmessage = ({ data }) => {
@@ -30,33 +29,38 @@
3029
};
3130
}
3231

33-
update() { this.port.postMessage({}); }
32+
update(keepalive) { this.port.postMessage(keepalive); }
3433
} ||
3534
(window.ScriptProcessorNode || (window.ScriptProcessorNode = window.webkitScriptProcessorNode)) &&
3635
Object.assign(function (context) {
37-
const size = 512, processor = context.createScriptProcessor(size, 1, 1),
38-
instance = context.RNNoiseInstance,
39-
heapFloat32 = new Float32Array(instance.memory.buffer);
40-
instance.reset();
36+
const processor = context.createScriptProcessor(512, 1, 1), state = instance.newState();
37+
let alive = true;
4138
processor.onaudioprocess = ({ inputBuffer, outputBuffer }) => {
42-
heapFloat32.set(inputBuffer.getChannelData(0), instance.getInput() / 4);
43-
const o = outputBuffer.getChannelData(0), ptr4 = instance.pipe(o.length) / 4;
44-
if (ptr4)
45-
o.set(heapFloat32.subarray(ptr4, ptr4 + o.length));
39+
if (alive) {
40+
heapFloat32.set(inputBuffer.getChannelData(0), instance.getInput(state) / 4);
41+
const o = outputBuffer.getChannelData(0), ptr4 = instance.pipe(state, o.length) / 4;
42+
if (ptr4)
43+
o.set(heapFloat32.subarray(ptr4, ptr4 + o.length));
44+
}
4645
};
47-
processor.update = () => {
48-
const e = Object.assign(new Event("status"), {
49-
vadProb: instance.getVadProb()
50-
});
51-
processor.dispatchEvent(e);
52-
if (processor.onstatus)
53-
processor.onstatus(e);
46+
processor.update = keepalive => {
47+
if (alive) {
48+
if (keepalive) {
49+
const e = Object.assign(new Event("status"), { vadProb: instance.getVadProb(state) });
50+
processor.dispatchEvent(e);
51+
if (processor.onstatus)
52+
processor.onstatus(e);
53+
} else {
54+
alive = false;
55+
instance.deleteState(state);
56+
}
57+
}
5458
};
5559
return processor;
5660
}, {
57-
register: async (context) => {
58-
if (!context.RNNoiseInstance)
59-
context.RNNoiseInstance = (await WebAssembly.instantiate(await compilation)).exports;
61+
register: async () => {
62+
if (!instance)
63+
heapFloat32 = new Float32Array((instance = (await WebAssembly.instantiate(await compilation)).exports).memory.buffer);
6064
}
6165
});
62-
})();
66+
}

src/worklet.c

Lines changed: 42 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7,53 +7,63 @@
77
#define MAX_FRAME_SIZE 16384
88

99
static const float scale = -INT16_MIN;
10-
static float buffer[MAX_FRAME_SIZE * 2], vad_prob;
11-
static size_t input, processed, output, buffering, latency;
12-
static DenoiseState *state = NULL;
1310

14-
float *EMSCRIPTEN_KEEPALIVE getInput()
11+
struct State
12+
{
13+
float buffer[MAX_FRAME_SIZE * 2], vad_prob;
14+
size_t input, processed, output, buffering, latency;
15+
DenoiseState *state;
16+
};
17+
18+
struct State *EMSCRIPTEN_KEEPALIVE newState()
19+
{
20+
struct State *s = malloc(sizeof(struct State));
21+
s->vad_prob = s->latency = s->buffering = s->output = s->processed = s->input = 0;
22+
s->state = rnnoise_create(NULL);
23+
return s;
24+
}
25+
26+
void EMSCRIPTEN_KEEPALIVE deleteState(struct State *const state)
27+
{
28+
rnnoise_destroy(state->state);
29+
free(state);
30+
}
31+
32+
float *EMSCRIPTEN_KEEPALIVE getInput(struct State *const state)
1533
{
1634
// Shifts
17-
if (output && input > MAX_FRAME_SIZE)
35+
if (state->output && state->input > MAX_FRAME_SIZE)
1836
{
19-
memmove(buffer, &buffer[output], sizeof(float) * (input -= output));
20-
processed -= output;
21-
output = 0;
37+
memmove(state->buffer, &state->buffer[state->output], sizeof(float) * (state->input -= state->output));
38+
state->processed -= state->output;
39+
state->output = 0;
2240
}
23-
return &buffer[input];
41+
return &state->buffer[state->input];
2442
}
2543

26-
float EMSCRIPTEN_KEEPALIVE getVadProb() { return vad_prob; }
44+
float EMSCRIPTEN_KEEPALIVE getVadProb(const struct State *const state) { return state->vad_prob; }
2745

28-
float *EMSCRIPTEN_KEEPALIVE pipe(size_t length)
46+
float *EMSCRIPTEN_KEEPALIVE pipe(struct State *const state, size_t length)
2947
{
3048
// Increases latency
31-
if (length > buffering)
32-
latency = buffering = (FRAME_SIZE / length + (FRAME_SIZE % length ? 1 : 0)) * length;
49+
if (length > state->buffering)
50+
state->latency = state->buffering = (FRAME_SIZE / length + (FRAME_SIZE % length ? 1 : 0)) * length;
3351
// Scales input
34-
for (size_t end = input + length; input < end; ++input)
35-
buffer[input] *= scale;
52+
for (size_t end = state->input + length; state->input < end; ++state->input)
53+
state->buffer[state->input] *= scale;
3654
// Processes
37-
while (processed + FRAME_SIZE <= input)
55+
while (state->processed + FRAME_SIZE <= state->input)
3856
{
39-
vad_prob = rnnoise_process_frame(state, &buffer[processed], &buffer[processed]);
40-
processed += FRAME_SIZE;
57+
state->vad_prob = rnnoise_process_frame(state->state, &state->buffer[state->processed], &state->buffer[state->processed]);
58+
state->processed += FRAME_SIZE;
4159
}
4260
// Buffers
43-
if (output + latency > processed)
61+
if (state->output + state->latency > state->processed)
4462
return NULL;
45-
latency = length;
46-
size_t o = output;
63+
state->latency = length;
64+
size_t o = state->output;
4765
// Scales output
48-
for (size_t end = output + length; output < end; ++output)
49-
buffer[output] /= scale;
50-
return &buffer[o];
51-
}
52-
53-
void EMSCRIPTEN_KEEPALIVE reset()
54-
{
55-
if (state)
56-
rnnoise_destroy(state);
57-
vad_prob = latency = buffering = output = processed = input = 0;
58-
state = rnnoise_create(NULL);
66+
for (size_t end = state->output + length; state->output < end; ++state->output)
67+
state->buffer[state->output] /= scale;
68+
return &state->buffer[o];
5969
}

0 commit comments

Comments
 (0)