Repository navigation
Desktop: ask GStreamer for pipewiresink, or Linux plays nothing on Bluetooth - #3
Merged
Merged
Conversation
…uetooth
WebKitGTK has no audio stack of its own. Web Audio leaves through GStreamer,
and WebKit asks `autoaudiosink` to choose the output element. autoaudiosink
prefers `pulsesink`, and pulsesink talking to PipeWire's PulseAudio
compatibility layer is silent on a Bluetooth sink.
Silent in the worst way, too: the stream opens, unmuted, at full volume, on
the right device, the AudioContext reports `running` and `currentTime`
advances. Nothing about it looks wrong from inside the page, and nothing is
logged. Measured on the same page with the same packages, same device:
autoaudiosink -> pulsesink peak 0.0000
pipewiresink ranked MAX peak 0.3349
On a wired output pulsesink happened to work, which is what made this look
intermittent rather than reproducible. Ranking the native pipewiresink top
fixes Bluetooth and costs nothing elsewhere, because the sink then follows
whatever the desktop's default output is.
Set before the webview starts, since GStreamer reads the variable when it
builds its registry, and the web process inherits it from us. An existing
GST_PLUGIN_FEATURE_RANK is appended to rather than replaced, and left alone
entirely if it already mentions pipewiresink.
Browsers never use GStreamer, which is why chopdeck.com plays fine on a
machine where the desktop app is mute. DESKTOP.md now says so, along with the
other thing that wastes an afternoon: WebKit parks the AudioContext in a
non-standard `interrupted` state whenever the window lacks focus.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019U7kCMZc9a7sezDwKKseJy
Deploying chopdeck with
|
| Latest commit: |
1985ba7
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://4500b33b.chopdeck.pages.dev |
| Branch Preview URL: | https://desktop-linux-audio-sink.chopdeck.pages.dev |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The Linux desktop app was completely silent on a machine where chopdeck.com played fine.
Cause
WebKitGTK has no audio stack of its own. Web Audio leaves through GStreamer, and WebKit asks
autoaudiosinkto choose the output element. autoaudiosink preferspulsesink, and pulsesink talking to PipeWire's PulseAudio compatibility layer is silent on a Bluetooth sink.Silent in the worst possible way: the stream opens, unmuted, at 100%, attached to the right device, the AudioContext reports
runningandcurrentTimeadvances. Nothing looks wrong from inside the page and nothing is logged. Measured by recording the sink's own monitor — same page, same packages, same Bluetooth device:autoaudiosink→pulsesinkpipewiresinkranked MAXOn a wired output pulsesink happened to work (peak 0.3119), which is what made this look intermittent rather than reproducible.
Browsers never use GStreamer, which is why the same build plays in Chromium and not in the desktop window — worth remembering before blaming the machine.
Fix
src-tauri/src/lib.rssetsGST_PLUGIN_FEATURE_RANK=pipewiresink:MAXbefore the webview starts. The sink then follows whatever the desktop's default output is, rather than binding to one card.tauri::Builder, because GStreamer reads the variable while building its registry.WebKitWebProcess, which inherits the value from us.GST_PLUGIN_FEATURE_RANKis appended to, not replaced, and left untouched if it already mentions pipewiresink.#[cfg(target_os = "linux")]).Testing
Built the
.deb, then ran the binary withGST_PLUGIN_FEATURE_RANKexplicitly unset in the launch environment, after killing every stale WebKit process:Note that
/proc/<pid>/environof the parent is useless here: it snapshots the environment atexecve()and never reflects a runtimesetenv(). The child web process is the authoritative check.Also ruled out by measurement rather than assumption: the two engines are not the difference. Rendering a synthesised kick through Chop Deck's exact master chain (gain → DynamicsCompressor threshold −3 / knee 6 / ratio 12 → destination) in an
OfflineAudioContextgives identical output in Chromium and WebKitGTK —dry peak 0.72000 rms 0.19554,limiter peak 0.43997 rms 0.15852, matching to five decimal places.Docs
docs/DESKTOP.mdgains the Bluetooth trap with the measurements and how to override it, plus one more time-waster: WebKit parks the AudioContext in a non-standardinterruptedstate whenever the window lacks focus, freezingcurrentTime. An unfocused window is silent by design.🤖 Generated with Claude Code
https://claude.ai/code/session_019U7kCMZc9a7sezDwKKseJy