Per-app audio routing for Windows, driven by regex rules. Pin a browser to your media DAC, force Discord onto your headset mic, or lock the system default to a specific endpoint. Like Volume Mixer's per-app device picker, but pattern-driven and persistent across reboots, app updates, and driver reinstalls.
- Rule-based routing: each rule is a list of conditions (all must hold) and a list of actions (all run in order, topmost wins per target).
- Four action types: pin an app's render endpoint, pin an app's capture endpoint, set the system default output, set the system default input. Default actions can target the "default" role, the "communications" role, or both.
- Conditions:
Device presentandDevice missing, scoped to render, capture, or any flow. The same regex syntax as device patterns. - Regex pattern matching:
AppPatternis tested against both process name and full executable path;DevicePatternagainst the device's friendly and display names. - Live status: rules dim when off, when shadowed by an earlier rule, or when their conditions are not met. Match counts and resolved devices appear inline as you edit.
- Drag to reorder: rules apply top-down, so reordering changes precedence.
- Auto-reapply: routing reapplies on rule changes, on device add/remove, on default-device changes, and on a 10-second safety tick.
- Tray-friendly: launch hidden, close to tray, single-instance.
- Windows 10 (19041+) or Windows 11 (Win11 22000+ enables the modern audio policy interface).
No external CLIs, no service install, no admin rights.
- Download the latest
.msifor your architecture (x64 or ARM64) from Releases. - Run the installer.
- Launch Earmark from the Start menu.
git clone https://github.com/hoobio/earmark.git
cd earmark
dotnet build src/Earmark.App/Earmark.App.csproj -c Debug -p:Platform=x64
.\src\Earmark.App\bin\x64\Debug\net10.0-windows10.0.26100.0\win-x64\Earmark.App.exeSee CONTRIBUTING.md for the full inner-loop pattern (the app holds open file handles on its own DLLs, so you must kill before rebuilding).
- Click Add rule.
- Pick an action type (e.g. Set output device for app).
- Fill in the regex patterns. Live match counts appear next to the field labels.
- Optionally add conditions, e.g. Device present
Headphones, so the rule only fires when your headset is plugged in. - Drag rules to reorder. Topmost matching rule wins per target.
- Toggle the switch on the card header to disable a rule without deleting it.
Two rules, top to bottom:
| Name | Action | App pattern | Device pattern |
|---|---|---|---|
| Comms | App output | (Teams|Discord|Slack) |
Comms |
| Browsers | App output | (chrome|edge|firefox|brave) |
Media |
A single rule with one condition and one action:
- Condition: Device present, Render,
USB DAC - Action: Set system default output,
USB DAC(default + comms both on)
When the DAC is unplugged, the condition fails, the rule dims, and Windows reverts to its own selection.
| What | Where |
|---|---|
| Rules | %UserProfile%\Documents\Hoobi\Earmark\rules.json |
| Settings | %UserProfile%\Documents\Hoobi\Earmark\settings.json |
| Logs | %LocalAppData%\Earmark\logs\earmark-{yyyyMMdd-HHmmss}.log (one per launch) |
Rules and settings live under Documents/ so OneDrive backs them up across machines.
Earmark uses two distinct Windows audio APIs depending on the rule type:
- Per-app routing uses
IAudioPolicyConfigFactory, an undocumented WinRT interface activated against theWindows.Media.Internal.AudioPolicyConfigruntime class. This is the same mechanism Windows itself uses for the per-app device picker in Volume Mixer. - System default device uses the older
IPolicyConfigVista::SetDefaultEndpointonCPolicyConfigClient.
Both interfaces are wrapped via raw COM interop because modern .NET no longer marshals IInspectable-based WinRT interfaces declaratively. See src/Earmark.Audio/Interop/ for the implementation.
A rule matcher walks rules top-down. For each rule it checks conditions, then iterates actions in order. The first action whose target matches a session (for app actions) or role (for default actions) wins. A rule evaluator runs the same logic for the UI to compute live status (active, idle, shadowed, conditions-not-met) and dim cards.
src/
Earmark.Core/ # Models, rule matcher/evaluator, JSON persistence (no Windows deps in interfaces)
Earmark.Audio/ # COM interop + NAudio session/endpoint services
Earmark.App/ # WinUI 3 UI, hosting, settings, tray, single-instance
tests/
Earmark.Core.Tests/ # xUnit (scaffolding)
# Debug
dotnet build src/Earmark.App/Earmark.App.csproj -c Debug -p:Platform=x64
# Release
dotnet publish src/Earmark.App/Earmark.App.csproj -c Release -p:Platform=x64The csproj declares <Platforms>x64;ARM64</Platforms> only - AnyCPU is not configured, so always pass -p:Platform=x64 (or ARM64).
Bug reports, feature requests, and PRs are all welcome. Start with CONTRIBUTING.md.
