Skip to content

Add WASAPI loopback capture and save direct connect address#280

Open
mthwJsmith wants to merge 10 commits intosonosaurus:mainfrom
mthwJsmith:feature/wasapi-loopback-and-improvements
Open

Add WASAPI loopback capture and save direct connect address#280
mthwJsmith wants to merge 10 commits intosonosaurus:mainfrom
mthwJsmith:feature/wasapi-loopback-and-improvements

Conversation

@mthwJsmith
Copy link
Copy Markdown

@mthwJsmith mthwJsmith commented Mar 25, 2026

Summary

  • WASAPI Loopback Capture: Render (output) devices are now enumerated as "(Loopback)" input devices in the WASAPI backend. Selecting one captures all audio playing through that device — lossless digital PCM, no virtual audio cables or third-party software needed. Uses AUDCLNT_STREAMFLAGS_LOOPBACK in shared mode.
  • Save Direct Connect Address: The last used direct connect IP:port is persisted in the plugin state and pre-filled on next launch, so users don't have to retype it each session.

Addresses #241 and #53.

Changes

  • deps/juce/.../juce_WASAPI_windows.cpp — Loopback device ID scheme (JUCE_LOOPBACK:: prefix), isLoopbackDevice flag on WASAPIDeviceBase, forced shared mode + loopback stream flags, render devices added as loopback inputs during scan
  • Source/ConnectView.cpp — Pre-fill direct connect field from saved address, save on connect
  • Source/SonobusPluginProcessor.cpp/.hmLastDirectConnectAddress saved/loaded in extraState

Test plan

  • On Windows, open Audio Settings with "Windows Audio" device type — verify output devices appear as "(Loopback)" inputs
  • Select a loopback input, connect to a second instance, confirm audio levels and playback
  • Verify loopback forces shared mode (no exclusive mode option)
  • Set output to << none >> on sender to avoid shared-mode conflict with same device
  • Direct connect to a peer, quit, relaunch — verify the address field is pre-filled
  • Verify no regressions on regular (non-loopback) WASAPI capture devices

Working great on my setup where I want PC2 audio to PC1 and want to avoid using voicemeter as it adds additional latency and is another reliance where things might go wrong (and its not open source).

I send PC2's audio to my monitor speakers (the Dell monitor speakers as its connected by hdmi) and set the hardware volume of that to 0 with the monitor controls. Then sonobus uses the loopback of that dell monitor as the input, and this sends the audio to my PC 1. Works great.

If you have no hardware audio output for whatever reason or cant set the hardware volume of it to 0, you could use https://github.com/VirtualDrivers/Virtual-Audio-Driver as it includes a "fake" speaker output.

A built release is available on the fork: https://github.com/mthwJsmith/sonobus/tree/feature/wasapi-loopback-and-improvements

…n UX improvements

- WASAPI Loopback: Render devices now appear as "(Loopback)" input devices
  in JUCE's WASAPI backend, allowing SonoBus to capture desktop/game audio
  directly without Voicemeeter or virtual audio cables. Lossless PCM capture
  using AUDCLNT_STREAMFLAGS_LOOPBACK in shared mode.

- Per-Process Capture: New ProcessAudioCapture class using Windows 11's
  AUDIOCLIENT_PROCESS_LOOPBACK_PARAMS API to capture audio from a specific
  process (e.g. a game). Includes runtime OS version check and process
  enumeration.

- Save Direct Connect Address: Last used direct connect IP:port is now
  persisted across sessions and pre-filled in the connect dialog.

- Auto-Reconnect Direct Peer: The existing "Reconnect Last" option now
  also reconnects to the last direct peer connection on startup.

Addresses: sonosaurus#241
Addresses: sonosaurus#53
JUCE's Array::sort requires a comparator object with compareElements(),
not a lambda. Use a struct-based comparator instead.
New "Application Audio" option in the Audio Device Type dropdown that
lists running Windows processes. Select a process (e.g. SnowRunner.exe)
to capture its audio directly — no output device, virtual cable, or
loopback needed. Uses Windows 11 per-process loopback API
(AUDIOCLIENT_PROCESS_LOOPBACK_PARAMS).

Only appears on Windows 10 Build 20348+ / Windows 11 where the API
is available. Falls back gracefully on older systems.
Includes direct ethernet setup guide (static IPs, SonoBus config),
three options for sending system audio without real output devices,
and build instructions.
JUCE 8 (sono8good) has breaking API changes that require significant
SonoBus code migration (MidiInput API changes, createPluginFilterOfType
removal, JACK header issues). Staying on sono7good which matches
upstream SonoBus and is fully tested.

Added FORK_README.md with complete setup guide including direct
ethernet configuration and static port recommendation.
JUCE 8 had shared mode loopback issues that couldn't be reliably fixed.
Reverted to JUCE 7 which has working loopback in all modes.

Ported forward all Application Audio improvements:
- WRL RuntimeClass with FtmBase for ActivateAudioInterfaceAsync handler
- IAudioSessionManager2 for enumerating only active audio processes
- GetMixFormat E_NOTIMPL fallback with user-requested sample rate
- Real SDK header (audioclientactivationparams.h) with NTDDI_VERSION
- Fixed isSupported() version check
Root cause: getIndexOfDevice() called device->getName() on a null
pointer. JUCE passes nullptr when no audio device is currently open
(e.g. during device type switching). Added null check.

Also:
- Provide dummy output device name so JUCE's AudioDeviceSelectorComponent
  doesn't crash on empty output device list
- Remove all debug file logging from ProcessAudioCapture and
  ApplicationAudioDevice
- Revert JUCE source files to clean state (no debug logging)
- Remove CoInitializeEx call from getAudioProcesses (conflicts with
  JUCE's STA message thread)
- Add Application Audio device type AFTER initialise so WASAPI is default
… found

- Document connection issue (our build can't connect, installed version can)
- Add incremental test build plan (v0-clean through v3-full) to isolate bug
- Document all bugs found and fixed (null device, empty output list, WRL FtmBase, etc.)
- Remove auto-reconnect direct peer (ephemeral ports)
- Clean up debug logging
WASAPI loopback and save-address features remain. Application Audio
code preserved on feature/application-audio-wip-broken branch.
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.

1 participant