Skip to content

fix(pipeline): dynamic capture sample rates and reactive device format tracking - #50

Merged
Horuse merged 6 commits into
mainfrom
fix/system-app-audio-sample-rate
Sep 16, 2026
Merged

Horuse merged 6 commits into
mainfrom
fix/system-app-audio-sample-rate

Conversation

@Horuse

@Horuse Horuse commented Sep 16, 2026

Copy link
Copy Markdown
Owner

What does this PR do?

  1. Fixes hardcoded 48 kHz in System Audio & App Audio UI:
    Previously, the System Audio and App Audio nodes displayed a static 48.0 kHz · 2 ch · f32 badge and a misleading Resampling: 48 kHz → <pipelineRate> tooltip, even when the OS capture was delivering audio at a different rate (e.g. 96 kHz) and running bit-transparently without resampling. A new capture_device_info Tauri IPC command was added to report the true native capture format across macOS, Windows, and Linux.

  2. Supports dynamic pipeline sample rates for Windows WASAPI App Audio loopback:
    Previously on Windows, VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK was hardcoded to 48 kHz. Now, the virtual capture endpoint initializes directly at the configured Pipeline sample rate, letting Windows Audio Engine deliver process audio at the pipeline rate with zero engine resamplers allocated.

  3. Fixes lack of reactivity to OS audio settings changes in Speaker and Microphone nodes:
    Previously, changing an output or input device's sample rate in external system tools (such as macOS Audio MIDI Setup or Windows Sound Control Panel) did not update the Speaker or Microphone node badges unless the selected device ID changed.

  4. Introduces a universal reactive useDeviceInfo helper:
    Consolidates device/capture inspection into a single composable in $lib/modules/audio/device_info.svelte.ts. It re-queries immediately upon window focus / visibility change and maintains a lightweight background poll (2.5s) so side-by-side configuration updates reflect without manual interaction.

  5. Adds tests for 1:1 bit-transparent audio passthrough:
    Adds unit tests in Rust (audio::pipeline::input::tests and audio::pipeline::output::tests) validating that matching sample rates (e.g. 96 kHz App Audio → 96 kHz Pipeline → 96 kHz Speaker) allocate no resamplers (resampler == None) and preserve audio samples bit-for-bit with exact float bit equality (a.to_bits() == b.to_bits()).

  6. Documents per-platform capture semantics in docs/CONCEPT.md:
    Clarified how sample rate negotiation behaves across macOS (CoreAudio tap follows hardware output device), Windows (App Audio follows pipeline rate, System Audio follows output mix format), and Linux (PipeWire negotiates pipeline rate).


Why is this the right approach?

  • No IPC polling in the audio path: The RT audio path is completely untouched. The backend audio engine already captured and bypassed resamplers when rates matched, but the frontend lacked an IPC method to inspect capture formats.
  • DRY UI logic: Rather than copying focus/visibility event listeners across Speaker, Microphone, System Audio, and App Audio, useDeviceInfo unifies format strings, channel counts, and resampling tooltip derivations in one place.
  • Platform-native contracts respected:
    • macOS: CoreAudio tap follows the default output device's nominal rate; ScreenCaptureKit falls back to 48 kHz.
    • Windows: App audio initializes directly at the pipeline rate; System audio reads the active WASAPI loopback mix format.
    • Linux: PipeWire delivers streams at the negotiated pipeline target sample rate.

Checklist

  • Diff is limited to the change — no unrelated edits
  • bun run check passes (0 errors)
  • cargo check --manifest-path src-tauri/Cargo.toml passes
  • bun run format leaves the tree clean
  • Generated TS types are committed with the Rust change (if any)
  • No new dependency without a reason in the PR description
  • I read the RT audio path section of docs/CONCEPT.md and confirmed this change adds no allocations, locks, or syscalls to cpal / SCK callbacks or DspWorker::run

Platform coverage

  • Developed on: macOS 14
  • Tested on: macOS 14, Fedora 44, Windows 11
    • Tested switching output device sample rates in Audio MIDI Setup between 44.1 kHz, 48 kHz, and 96 kHz; verified that Speaker, System Audio, and App Audio immediately update their sample rate badges and hide the resampling indicator when matching pipelineSampleRate.
    • Verified cargo test --lib audio::pipeline (14 passing tests including bit-transparency tests).
    • Verified bun test (15 passing tests).
  • Per-OS files touched:
    • src-tauri/src/audio/capture/mod.rs (cross-platform capture_device_info dispatcher)
    • src-tauri/src/audio/capture/windows.rs (WASAPI process loopback dynamic rate)
    • src-tauri/src/audio/pipeline/input/windows.rs (target rate passing for Windows inputs)
    • docs/CONCEPT.md (cross-platform capture negotiation documentation)

Related

@Horuse
Horuse merged commit c0b62c6 into main Sep 16, 2026
9 checks passed
@Horuse
Horuse deleted the fix/system-app-audio-sample-rate branch September 16, 2026 23:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[crash] audio stream error:

1 participant