Skip to content

AudioWorklet based Host for web when Wasm atomics are enabled #958

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: master
Choose a base branch
from

Conversation

kettle11
Copy link
Contributor

@kettle11 kettle11 commented Mar 1, 2025

This pull request adds an optional feature-flagged Host that uses AudioWorklet on web when the atomics feature is enabled. When the web_audio_worklet and wasm-bindgen features are enabled and nightly Rust is compiled with the atomics flag enabled the WebAudioWorklet Host is available.

AudioWorklet should be lower latency, lower overhead, and less prone to jank. This implementations works pretty much the same as a native audio thread, allowing the implementation to be simpler than the existing WebAudio Host implementation.

To used shared memory it's also required (for security reasons) that the server hosting the Wasm to be configured with the correct CORS headers. I've added an example that shows how to build with the correct Rust flags and how to set the Cors headers in a test environment.

So far I've tested this in Chrome, Safari, and Firefox with the beep example and it seems to be working fine.

Edit: I'm not sure why a past merged PR shows up in Github's listed commits, but it seems to have no impact on this PR.

@roderickvd
Copy link
Collaborator

Very nice addition! This is a big one, so I'm gonna first trigger a Copilot review to walk through major points, if any, then do a further code review.

Apologies for the late response - new collaborator here and grooming through the backlog now.

Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This pull request adds AudioWorklet support for web audio in CPAL, providing a lower-latency, lower-overhead alternative to the existing WebAudio host when WebAssembly atomics are enabled. The implementation leverages shared memory between the main thread and audio worklet thread, allowing for more efficient audio processing similar to native platforms.

  • Introduces a new web_audio_worklet feature flag and host implementation
  • Adds JavaScript worklet processor code that interfaces with WebAssembly
  • Provides an example demonstrating the setup requirements and usage

Reviewed Changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/platform/mod.rs Adds conditional compilation for WebAudioWorklet host alongside existing WebAudio host
src/host/web_audio_worklet/mod.rs Core implementation of the AudioWorklet-based host with stream management
src/host/web_audio_worklet/worklet.js JavaScript AudioWorkletProcessor that interfaces with WASM audio processing
src/host/web_audio_worklet/dependent_module.rs Utility for dynamically loading JavaScript modules in AudioWorklet context
src/host/mod.rs Adds conditional module inclusion for web_audio_worklet
examples/web-audio-worklet-beep/* Complete example showing usage with required configuration
Cargo.toml Adds web_audio_worklet feature with necessary web-sys dependencies
Comments suppressed due to low confidence (1)

src/host/web_audio_worklet/worklet.js:1

  • [nitpick] The class name WasmProcessor is generic and could conflict with other WASM processors. Consider using a more specific name like CpalWasmProcessor to match the registered processor name.
registerProcessor("CpalProcessor", class WasmProcessor extends AudioWorkletProcessor {

@kettle11
Copy link
Contributor Author

kettle11 commented Aug 7, 2025

Thanks for following up!

I applied some of Copilot's suggestions and also realized that some files needed to build the example were missing so I fixed that as well. Let me know if anything further is needed.

Copy link
Collaborator

@roderickvd roderickvd left a comment

Choose a reason for hiding this comment

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

This looks very, very nice. Super to see such an exhaustive example too.

asio = ["asio-sys", "num-traits"] # Only available on Windows. See README for setup instructions.
oboe-shared-stdcxx = ["oboe/shared-stdcxx"] # Only available on Android. See README for what it does.
# Only available on web when atomics are enabled. See README for what it does.
web_audio_worklet = [
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please document the feature flag in the README.

@@ -65,6 +76,7 @@ web-sys = { version = "0.3.35", features = [ "AudioContext", "AudioContextOption

[target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies]
wasm-bindgen = { version = "0.2.58", optional = true }
wasm-bindgen-futures = {version = "0.4.33", optional = true}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Style: misses spaces between the curly braces.

rustflags = ["-C", "target-feature=+atomics"]

[unstable]
build-std = ["std", "panic_abort"]
Copy link
Collaborator

Choose a reason for hiding this comment

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

Does it have to abort? Could we leave that choice to the user?

.DS_Store
recorded.wav
rls*.log
/target
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why does it give a diff for the first six lines? (don't bother too much, just wondering)

@@ -0,0 +1,40 @@
[package]
name = "web-audio-worklet-beep"
Copy link
Collaborator

Choose a reason for hiding this comment

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

Feel free to put your name as author.

//
// Copyright (c) 2017-2024 The wasm-bindgen Developers
//
// This file incorporates code from the above source under the terms of those licenses.
Copy link
Collaborator

Choose a reason for hiding this comment

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

We should be explicit that we opt for the Apache license, since that's what's cpal itself is licensed under.

const SUPPORTED_SAMPLE_FORMAT: SampleFormat = SampleFormat::F32;

impl Host {
pub fn new() -> Result<Self, crate::HostUnavailable> {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why have a crate::JostUnavailable error type when it seems infallible?

pub type SupportedInputConfigs = ::std::vec::IntoIter<SupportedStreamConfigRange>;
pub type SupportedOutputConfigs = ::std::vec::IntoIter<SupportedStreamConfigRange>;

const MIN_CHANNELS: u16 = 1;
Copy link
Collaborator

Choose a reason for hiding this comment

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

You can use the cpal::ChannelCount type alias instead of u16.


let audio_context = web_sys::AudioContext::new_with_context_options(&stream_opts).map_err(
|err| -> BuildStreamError {
let description = format!("{:?}", err);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Does this pass clippy? Preferred syntax has become format!("{err:?}").

@@ -662,7 +662,15 @@ mod platform_impl {
SupportedOutputConfigs as WebAudioSupportedOutputConfigs,
};

impl_platform_host!(WebAudio webaudio "WebAudio");
#[cfg(feature = "web_audio_worklet")]
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could you clarify this? Instead of doing impl_platform_host!, importing the relevant webaudio bits yourself when the web_audio_worklet feature is enabled?

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