Skip to content

Conversation

@kettle11
Copy link
Contributor

OK, this is my last commit for this PR for real this time!

I realized that there's a potential subtle bug that was unaddressed. When a Wasm program resizes the Javascript program's 'view' into the Wasm memory only points to the initial Wasm memory from before the resize.

It's unlikely to be hit often but if the interleaved buffer were resized and reallocated within Rust and given a new memory location outside of the Wasm program's initial range of memory then the AudioWorklet would crash.

This PR adds a check that prevents that.

Additionally while I was looking at this I also tested if other deinterleaving strategies are faster. Deinterleaving within Rust / Wasm (I didn't try SIMD) introduces an additional copy (interleaved -> deinterleaved and then deinterleaved -> JS buffers). That was ~20% to 30% slower. Special casing stereo deinterleaving on the JS side was negligibly faster if at all.

I also did realize that there was a way to tweak the loop to remove the call to subarray, which is good because that call didn't really make the code simpler and created a tiny amount of JS garbage which could trigger periodic garbage collections on the audio thread.

After this I think I'm finally done tweaking the AudioWorklet backend.


for (let i = 0, j = ch; i < frame_size; i++, j += channels_count) {
channel[i] = interleaved[j];
for (let i = 0; i < frame_size; i++) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the risk of interleaved_start + (frame_size * channels_count) being out of bounds? For memory safety would it benefit from an assertion like:

if (interleaved_start + (frame_size * channels_count) > this.wasm_memory.length) {
      console.error("Audio buffer out of bounds!");
      return false; // Stop processing
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The risk should be very low but if it were out of bounds Javascript would effectively write NaNs into the output audio buffer, which is not ideal.

I'll add the check because it's low cost and more plainly fails if somehow such a bug did ever occur.

process(inputs, outputs) {
// Check if memory grew and update view
if (this.wasm_memory.buffer !== this.memory.buffer) {
this.wasm_memory = new Float32Array(this.memory.buffer);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this fail silently? Would this be more prudent?

try {
    if (this.wasm_memory.buffer !== this.memory.buffer) {
        this.wasm_memory = new Float32Array(this.memory.buffer);
    }
} catch (e) {
    console.error("Failed to update Wasm memory view:", e);
    return false;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see a reason this code is much more likely to fail than other parts of code. I also don't think it would fail silently if it were to fail. So I would propose leaving it as is.

for (let ch = 0; ch < channels_count; ch++) {
const channel = channels[ch];
let src = interleaved_start + ch;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe interleaved_idx or read_pos instead of src for clarity?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good. Done!

@roderickvd
Copy link
Member

roderickvd commented Nov 16, 2025

After this I think I'm finally done tweaking the AudioWorklet backend.

😁 no worries, thank you for your commitment!

While at it, would you:

@roderickvd
Copy link
Member

I think we're close to releasing v0.17... would you have time to look at the last bits?

@kettle11
Copy link
Contributor Author

kettle11 commented Dec 4, 2025

Sorry for the lack of response to the last comments, it's been a busy time of year. I'll take a look at this tomorrow!

@kettle11
Copy link
Contributor Author

kettle11 commented Dec 5, 2025

OK, I responded to the code review feedback and renamed "web_audio_worklet" which I admit is a mouthful to just "audioworklet" throughout the codebase.

Previously I felt the need to prepend "Web" to "Audio Worklet" clarify this was a web specific feature but really none of the other backends append things like "Android" or "Mac" so that was probably unnecessary. Switching to just "audioworklet" is simply the name of the Web API being used and is shorter.

I'm also happy to change it back to something else if there's differing opinions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants