Skip to content

Implement audio support, fixes #59#60

Open
ThomasWaldmann wants to merge 9 commits into
swellweb:mainfrom
ThomasWaldmann:audio
Open

Implement audio support, fixes #59#60
ThomasWaldmann wants to merge 9 commits into
swellweb:mainfrom
ThomasWaldmann:audio

Conversation

@ThomasWaldmann
Copy link
Copy Markdown
Contributor

@ThomasWaldmann ThomasWaldmann commented May 23, 2026

This PR implements low-latency raw PCM audio streaming between the sender and the receiver, unified capture using ScreenCaptureKit for both Mirror and Extended Display sessions, and session-specific live audio controls.

Summary of Changes

  1. Low-Latency Raw PCM Audio Streaming & Playout:

    • Exposes system audio capture via ScreenCaptureKit and converts the float32 non-interleaved PCM to S16 interleaved stereo PCM at 48000Hz using SBAudioConverter.
    • Bypasses default OS buffer delays on the receiver using a custom SDL audio callback and a 1-second circular ring buffer.
    • Implements a low-latency resync mechanism utilizing a smooth-discard sliding-window that caps backlog at 150ms, preventing drift while avoiding audible clicks or silences.
    • Eliminates receiver CPU busy-spinning by yielding 1ms (SDL_Delay(1)) in the event loop when the network socket is idle (0 bytes read), avoiding thread starvation and ensuring real-time scheduling priority for the SDL audio thread.
    • Restores hardware buffer size to 1024 samples to protect playout against CPU scheduling underflow.
  2. Unified ScreenCaptureKit & Extended Display Mode Audio:

    • Migrates virtual display capture in Extended Display Mode from the legacy CGDisplayStream to modern ScreenCaptureKit (SCStream).
    • Unifies the sender-side pipeline across both Mirror and Extended Display modes, enabling capturesAudio = true for all active streams.
  3. Per-Session Mute & Audio Controls:

    • Introduces independent, session-specific 'Stream Mac audio' checkboxes in the UI (TBDisplaySenderContentView.swift).
    • Dynamically mutes/unmutes audio streams in real-time, decoupling global settings from live stream controls.
  4. Hardening & Performance Refinements:

    • Replaced per-byte iteration with high-performance memcpy and wrap-around handling in the ring buffer write and read paths.
    • Hardened SBAudioConverter input callback with an inputConsumed flag to safeguard against duplicate buffer delivery edge cases.
    • Excluded the sender process's own output audio from being captured to avoid feedback loops.
    • Documented the architecture and resync strategy in docs/audio.md.

- Enable audio capture in ScreenCaptureKit and exclude current process audio.
- Add SBAudioConverter to dynamically convert float32 non-interleaved PCM to S16 interleaved stereo PCM at 48000Hz.
- Implement explicit SDL audio callback and 1-second circular ring buffer on the receiver to bypass OS buffer delays.
- Add smooth-discard sliding-window resync that ceiling caps audio backlog at 80ms without causing silent gaps or clicks.
- Restore hardware buffer size to 1024 samples to protect playout against CPU scheduling underflow.
- Add comprehensive architecture documentation in docs/audio.md.
…apture

- Migrate virtual display capture in Extended Display Mode from legacy video-only CGDisplayStream to modern ScreenCaptureKit (SCStream).
- Expose virtual extended desktop as a standard SCDisplay inside SCShareableContent.
- Enable capturesAudio = true on the virtual display stream to capture and stream system audio in real-time.
- Unify the sender-side capture pipeline for both Mirror and Extended Display sessions.
- Update docs/audio.md to document the unified ScreenCaptureKit pipeline.
- Introduce session-specific 'Stream Mac audio' toggles in TBDisplaySenderContentView.swift.
- Bind per-session toggles to individual session audioEnabled states for instant, live muting.
- Decouple the global audioEnabled setting in TBDisplaySenderManager.swift so it solely defines the default for new sessions.
- Update docs/audio.md to document the per-session audio control and live cutoff logic.
…ove dead code

- Extract ring_read() helper using memcpy with wrap-around handling,
  replacing per-byte iteration in audio_callback.
- Use memcpy with wrap-around for the ring buffer write path in the
  TB_PKT_AUDIO_FRAME handler.
- Remove unused audio_started field from struct app.
- Guard AVAudioConverter input callback with inputConsumed flag to
  prevent duplicate buffer delivery if the converter re-invokes the
  closure (e.g. during sample rate conversion).
- Replace unsafe OpaquePointer cast in dead interleaved branch with
  assertionFailure + early return, since ScreenCaptureKit always
  delivers float32 non-interleaved audio.
@ThomasWaldmann
Copy link
Copy Markdown
Contributor Author

ThomasWaldmann commented May 23, 2026

Gemini FTW!

Works now, please review. Before merge, guess I want to squash some commits.

@ThomasWaldmann ThomasWaldmann changed the title Implement low-latency raw PCM audio streaming, ScreenCaptureKit unified capture, and per-session controls Implement audio support, fixes #59 May 23, 2026
@ThomasWaldmann ThomasWaldmann mentioned this pull request May 24, 2026
- Modify drain_socket to track total bytes read.
- Yield 1ms (SDL_Delay) in event loop when socket is idle to eliminate 100% CPU busy-spinning, preventing real-time thread starvation.
- Raise audio backlog latency ceiling from 80ms to 150ms (28800 bytes) to cushion against ScreenCaptureKit variable delivery chunks and network jitter.
- Update build number.
- Update docs/audio.md to reflect the increased 150ms (28800 bytes) latency safety ceiling.
- Add documentation detailing how receiver busy-spinning CPU usage was eliminated, resolving real-time audio thread starvation.
@ThomasWaldmann ThomasWaldmann marked this pull request as ready for review May 24, 2026 09:44
@swellweb
Copy link
Copy Markdown
Owner

this code it was very important but i had to some breakchanges. Thank's a lot.

@ThomasWaldmann
Copy link
Copy Markdown
Contributor Author

@swellweb Looks like you didn't apply 8089493 yet to your input-dockstation "release" branch?

Only getting 2x 13 FPS there.

@swellweb
Copy link
Copy Markdown
Owner

swellweb commented Jun 3, 2026

Thanks again for this work. The audio relay pieces have been folded into the 3.0 release branch and are part of the new release PR to main. Really appreciate the contribution here.

@ThomasWaldmann
Copy link
Copy Markdown
Contributor Author

@swellweb If you have all you need from here, feel free to close.

@ThomasWaldmann
Copy link
Copy Markdown
Contributor Author

@swellweb Do you have 8089493 ?

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.

2 participants