A clean-room native Node addon (Rust + napi-rs + the
windows crate) that performs WASAPI
process-tree EXCLUDE loopback on Windows: it captures the full default-endpoint
audio mix except the process tree rooted at a caller-supplied PID.
In GoofCord this is the #46 echo fix (ECHO-01): excluding GoofCord's own
Electron process tree removes the call audio GoofCord itself plays back, so a
remote screenshare viewer hears shared desktop/app audio but not the call
echoed back to them.
This crate is authored solely from the public Microsoft
ApplicationLoopback
MIT sample and the public windows crate / Win32 documentation. No proprietary
or third-party application code or symbol layout was used as a basis. The
Microsoft MIT copyright notice is retained verbatim in NOTICE.
- Hardcodes a 48000 Hz / 2-channel / 32-bit IEEE-float
WAVEFORMATEXTENSIBLE(the renderer/transport contract). The process-loopback device returnsE_NOTIMPLfromGetMixFormat/IsFormatSupported, so the format is not queried — it is hardcoded and the audio engine converts to it in shared mode viaAUDCLNT_STREAMFLAGS_AUTOCONVERTPCM(no Rust-side resampling). - Activates the WASAPI process loopback in
PROCESS_LOOPBACK_MODE_EXCLUDE_TARGET_PROCESS_TREEmode against the supplied exclude-root PID. - Resolves
ActivateAudioInterfaceAsyncdynamically (LoadLibraryW+GetProcAddressonmmdevapi.dll), so the.nodeloads on every Windows build and only activates where the API is present. Any non-S_OKactivation result (or a failedGetProcAddress) is reported as "unsupported" (startresolvesfalse) rather than thrown — the graceful-fallback foundation forECHO-03. - Runs an event-driven capture loop on a dedicated thread and pushes
480-frame (3840-byte) interleaved-stereo f32 chunks to JS over a napi
ThreadsafeFunction(NonBlocking+ bounded queue → drop-oldest backpressure).
// excludeRootPid: the Electron MAIN process PID (process.pid). EXCLUDE_TARGET_PROCESS_TREE
// covers the whole tree, including the separate "Audio Service" utility child.
// onChunk: called with a 3840-byte (480 frames * 2ch * 4 bytes) f32 Buffer per ~10 ms.
function start(excludeRootPid: number, onChunk: (chunk: Buffer) => void): boolean; // false => unsupported
function stop(): void; // idempotent; signals the capture thread and joins with a bounded timeoutThis crate is not compiled by the consumer's build (GoofCord consumes a
prebuilt .node, per its "no new build tooling" constraint). The prebuilt is
produced on a windows-latest CI runner — see
.github/workflows/build.yml:
napi build --release --target x86_64-pc-windows-msvcwhich produces a *.node that CI normalizes to
wasapi-loopback-win32-x64.node (the name must contain BOTH win32 AND
x64) and commits to
prebuilds/windows-x86_64/wasapi-loopback-win32-x64.node. Single target only
(win32-x64); win32-arm64 is a noted fast-follow.
GoofCord depends on this addon via a single optionalDependencies line,
mirroring github:Milkshiift/patchcord:
"optionalDependencies": {
"wasapi-loopback": "github:thomas-quant/wasapi-loopback"
}Bun resolves the github: ref by cloning the repo; the committed
prebuilds/windows-x86_64/wasapi-loopback-win32-x64.node lands at
node_modules/wasapi-loopback/prebuilds/windows-x86_64/… — the exact path
GoofCord's build.ts copies into the packaged app via copyNativeModules().
The "os": ["win32"] / "cpu": ["x64"] fields make Bun skip the install
entirely on Linux/macOS, so the dependency no-ops off-Windows. There is no
postinstall/prepare step — delivery is the committed binary (the
patchcord/venbind convention).