A Windows audio control utility with a scriptable CLI and an optional GUI. You can:
- Automate common tasks: list devices, set default playback/recording endpoints, adjust volume, mute/unmute, toggle “Listen to this device,” and control Enhancements.
- Learn vendor toggles: “Learn Enhancements” and “Learn FX” observe what Windows/drivers change in the registry for a specific device, then store those rules so audioctl can reproduce the same toggle later.
- Control individual effects (FX): once learned, per-effect toggles (e.g., BassBoost, Loudness) can be enabled/disabled on demand, even when the driver does not provide a direct API.
- Query state safely: fast, read-only commands return current volume, mute, listen, enhancements, and FX state for scripts, hotkeys, and status UIs.
- Use the GUI when convenient: the GUI provides context-aware right-click actions and guided learn workflows, while still using the CLI underneath as the source of truth.
audioctl is designed to be CLI-first for repeatability and automation; the GUI is a helper layer on top.
Quick Start
Command Map (Full Tree)
List Devices
Set Default Endpoints
Adjust Volume / Mute
Listen To This Device
Audio Enhancements
Query Helpers (Read‑Only)
Diagnostic Discovery
Wait For A Device
General Exit Codes
Troubleshooting
GUI (Graphical User Interface)
- Help:
audioctl.exe -h
- No arguments (double‑click or run without args) launches the GUI:
audioctl.exe
--id "{endpoint-id}"(exact endpoint ID)--name "substring"(case-insensitive substring match)--regex(treat--nameas a regex)--flow Render|Capture(optional filter for playback/recording device)--index N(Number-based index to disambiguate between multiple matches)--playback-name "substring"(Render-only name selector; for set-default)--recording-name "substring"(Capture-only name selector; for set-default)
When using the --name, --playback-name, or --recording-name options, commands accept partial matches against device names or descriptions. If the specified text uniquely identifies exactly one playback or recording device, that device will be targeted and an index value is not required.
audioctl
│
├─ list [--all] [--json]
│
├─ set-default
│ ├─ --playback-id/--playback-name [--playback-role console|multimedia|communications|all]
│ └─ --recording-id/--recording-name [--recording-role console|multimedia|communications|all]
│ [--index] [--regex]
│
├─ set-volume
│ └─ (--id | --name) [--flow Render|Capture] (--level 0..100 | --mute | --unmute)
│ [--index] [--regex]
│
├─ listen (Capture only)
│ └─ (--id | --name) (--enable | --disable)
│ [--playback-target-id [<RenderID>]] [--playback-target-name [<RenderName>]]
│ [--index] [--regex]
│
├─ enhancements
│ ├─ Main switch:
│ │ └─ (--id | --name) [--flow] (--enable | --disable | --learn)
│ │ [--index] [--regex] [--prefer-hklm] [--vendor-ini PATH]
│ │
│ └─ FX operations:
│ ├─ --list-fx [--json]
│ ├─ --learn-fx "FX_NAME"
│ ├─ --enable-fx "FX_NAME" | --disable-fx "FX_NAME"
│ └─ --delete-fx "FX_NAME"
│
├─ get-volume [--id|--name] [--flow] [--index] [--regex]
│
├─ get-listen [--id|--name] [--index] [--regex] (Capture)
│
├─ get-enhancements [--id|--name] [--flow] [--index] [--regex]
│
├─ get-device-state [--id|--name] [--flow] [--index] [--regex] [--vendor-ini PATH]
│
├─ diag-sysfx [--id|--name] [--flow] [--index] [--regex]
│
├─ diag-mmdevices [--id|--name] [--flow] [--index] [--regex]
│
├─ discover-enhancements [--id|--name] [--flow] [--index] [--regex]
│ └─ [--output-dir DIR] [--ini-snippet FILE]
│
└─ wait (--id|--name) [--flow] [--timeout] [--index] [--regex]
- Human-readable:
audioctl.exe list
- Include disabled/disconnected:
audioctl.exe list --all
- JSON output:
audioctl.exe list --json
Use audioctl set-default to choose the system’s default playback (Render) and/or recording (Capture) endpoints. Targets must be active (DEVICE_STATE_ACTIVE). On some systems, you may need an elevated console (Run as Administrator).
Roles you can set:
- console - the default device most apps use.
- multimedia - often resolved to the same device as console on most systems.
- communications - used by calling/telephony apps.
- all (applies to all three roles)
Defaults if a role is omitted:
- Playback role defaults to
all. - Recording role defaults to
communications.
About Windows roles (console vs multimedia):
- On most Windows systems, “console” and “multimedia” effectively point to the same default device. If you set either one, you will typically see both flags update in the next
audioctl listoutput. The tool still lets you set them independently, or setallfor completeness and for environments where they’re distinct.
Most users want a device to be the default for everything. Use:
--playback-role allfor playback (Render)--recording-role allfor recording (Capture)
Get IDs first (optional but recommended):
audioctl list --jsonDisambiguation when multiple name matches:
- If your
--playback-nameor--recording-namematches more than one active device, the command will stop and print the matching candidates in GUI order with an index for each. Then rerun with--index Nto choose the one you want. - Example of the prompt:
Multiple playback matches: [Render idx 0] Speakers (USB DAC) id={...} defaults=- [Render idx 1] Speakers (Realtek(R) Audio) id={...} defaults=multimedia,console Use --index to disambiguate.
Notes about JSON output:
- Successful commands print compact, single‑line JSON (no pretty‑printing). For pretty output, pipe to a formatter (e.g.,
| jq .).
audioctl set-default --playback-name "Speakers" --playback-role all --index 0Sample output:
{"set":[{"flow":"Render","role":"all","id":"{RENDER-ENDPOINT-ID}","name":"Speakers (Realtek(R) Audio)"}]}audioctl set-default --playback-id "{RENDER-ENDPOINT-ID}" --playback-role allSample output:
{"set":[{"flow":"Render","role":"all","id":"{RENDER-ENDPOINT-ID}","name":"Speakers (USB DAC)"}]}audioctl set-default --recording-name "USB Microphone" --recording-role all --index 0Sample output:
{"set":[{"flow":"Capture","role":"all","id":"{CAPTURE-ENDPOINT-ID}","name":"USB Microphone"}]}audioctl set-default --recording-id "{CAPTURE-ENDPOINT-ID}" --recording-role allSample output:
{"set":[{"flow":"Capture","role":"all","id":"{CAPTURE-ENDPOINT-ID}","name":"Headset Mic"}]}audioctl set-default \
--playback-id "{RENDER-ENDPOINT-ID}" --playback-role all \
--recording-id "{CAPTURE-ENDPOINT-ID}" --recording-role allSample output:
{"set":[{"flow":"Render","role":"all","id":"{RENDER-ENDPOINT-ID}","name":"Speakers (Realtek(R) Audio)"},{"flow":"Capture","role":"all","id":"{CAPTURE-ENDPOINT-ID}","name":"USB Microphone"}]}Additional notes:
- If list output shows both console and multimedia toggled after setting only one, that is expected on most Windows builds, they commonly map to the same endpoint.
- “device not found (active only)”: verify the device is connected/enabled and visible in
audioctl list. - If multiple name matches occur, rerun with
--index Nusing the indices printed in the disambiguation prompt.
Use audioctl set-volume to change an endpoint’s master volume (0–100%) or toggle its mute state. Targets must be active (DEVICE_STATE_ACTIVE). On name selection, prefer adding --flow to disambiguate between Render (playback) and Capture (recording). If multiple matches remain, add --index N (0‑based, GUI‑order within the flow).
Rules:
- You must specify exactly one of:
--level,--mute, or--unmute. --levelis an integer 0–100 (values outside this range are clamped by the tool).- Either
--idor--nameis required for selection (with optional--regexfor pattern matching).
Command template:
audioctl set-volume
(--id <ID> | --name <NAME>) [--flow <Render|Capture>]
(--level <0..100> | --mute | --unmute)
[--index <N>] [--regex]Disambiguation behavior:
- If name selection matches more than one active device and you don’t pass
--index, the tool returns:Rerun the command withERROR: multiple matches; specify --index--flowand/or--index N. Useaudioctl list(or--json) to see devices in the same GUI order.
Notes about JSON output:
- Successful commands print compact, single‑line JSON. Pipe to a formatter (e.g.,
| jq .) if you want pretty output.
audioctl set-volume --name "Speakers" --flow Render --level 30Sample output:
{"volumeSet":{"id":"{RENDER-ENDPOINT-ID}","name":"Speakers (Realtek(R) Audio)","level":30}}audioctl set-volume --id "{CAPTURE-ENDPOINT-ID}" --level 70Sample output:
{"volumeSet":{"id":"{CAPTURE-ENDPOINT-ID}","name":"USB Microphone","level":70}}If multiple “Speakers” exist, specify which one using --index (0‑based, GUI‑order for Render).
audioctl set-volume --name "Speakers" --flow Render --mute --index 0Sample output:
{"muteSet":{"id":"{RENDER-ENDPOINT-ID}","name":"Speakers (USB DAC)","muted":true}}audioctl set-volume --id "{CAPTURE-ENDPOINT-ID}" --unmuteSample output:
{"muteSet":{"id":"{CAPTURE-ENDPOINT-ID}","name":"USB Microphone","muted":false}}audioctl set-volume --name "^Speakers .* Realtek\\(R\\) Audio$" --regex --flow Render --level 15Sample output:
{"volumeSet":{"id":"{RENDER-ENDPOINT-ID}","name":"Speakers (Realtek(R) Audio)","level":15}}If both Render and Capture have a device matching “USB”, you’ll see:
ERROR: multiple matches; specify --index
Disambiguate by flow and/or index:
audioctl set-volume --name "USB" --flow Capture --unmute --index 0Sample output:
{"muteSet":{"id":"{CAPTURE-ENDPOINT-ID}","name":"USB Microphone","muted":false}}Invalid example (will return an error):
audioctl set-volume --name "Speakers" --flow Render --level 20 --muteError:
ERROR: Cannot specify both --level and --mute/--unmute
Tips:
- Prefer
--idfor exact targeting (no disambiguation needed). - If you get “device not found (active only)”, verify the endpoint is connected/enabled and visible in
audioctl list. - The command controls the endpoint’s master scalar level and mute state (not per‑app volumes).
Use audioctl listen to enable or disable “Listen to this device” on a capture (recording) endpoint. The target must be active (DEVICE_STATE_ACTIVE). This command only operates on Capture devices; Render devices are not eligible.
You must specify exactly one of:
--enable--disable
Optional playback routing:
--playback-target-id "{RENDER-ENDPOINT-ID}"routes the monitored audio to a specific playback (Render) endpoint.--playback-target-id ""routes to the Windows “Default Playback Device.”--playback-target-namemirrors the above using a name match; passing the flag without a value selects “Default Playback Device.”- If the target flag is omitted, the current routing is preserved.
Command template:
audioctl listen
(--id <CAPTURE-ENDPOINT-ID> | --name <NAME>)
(--enable | --disable)
[--playback-target-id [<RenderEndpointID>]]
[--playback-target-name [<RenderEndpointName>]]
[--index <N>] [--regex]Disambiguation behavior:
- If name selection matches more than one active capture device and you don’t pass
--index, the command prints the matching candidates in GUI order (with indices) and exits with:Rerun withERROR: multiple matches; specify --index--index N(0‑based, GUI‑order for the Capture flow). Useaudioctl list(or--json) to see the same order.
Notes about JSON output:
- Success prints compact, single‑line JSON.
- When a retry/verification path is used, the JSON may include a
verifiedByfield ("com"or"registry").
audioctl listen --name "Microphone" --enableSample output:
{"listenSet":{"id":"{CAPTURE-ENDPOINT-ID}","name":"Microphone","enabled":true}}audioctl listen --name "Microphone" --enable --playback-target-idSample output:
{"listenSet":{"id":"{CAPTURE-ENDPOINT-ID}","name":"Microphone","enabled":true}}audioctl listen --name "Microphone" --enable --playback-target-id "{RENDER-ENDPOINT-ID}"Sample output:
{"listenSet":{"id":"{CAPTURE-ENDPOINT-ID}","name":"Microphone","enabled":true}}audioctl listen --id "{CAPTURE-ENDPOINT-ID}" --disableSample output:
{"listenSet":{"id":"{CAPTURE-ENDPOINT-ID}","name":"USB Microphone","enabled":false}}audioctl listen --name "Mic" --regex --enable --index 0Sample output:
{"listenSet":{"id":"{CAPTURE-ENDPOINT-ID}","name":"Studio Mic","enabled":true}}{"listenSet":{"id":"{CAPTURE-ENDPOINT-ID}","name":"Microphone","enabled":true,"verifiedBy":"com"}}or:
{"listenSet":{"id":"{CAPTURE-ENDPOINT-ID}","name":"Microphone","enabled":true,"verifiedBy":"registry"}}Tips:
- This command only targets Capture devices. If you try a Render device, it won’t match.
- Use
audioctl list --jsonto get both Capture (for--id) and Render endpoints (for routing). - If the name matches multiple capture devices, rerun with
--index N.
Use audioctl enhancements to enable or disable “Audio Enhancements” (SysFX) on an endpoint using a vendor‑first method. Many drivers (e.g., Realtek/Waves) expose Enhancements via device‑specific registry DWORDs under MMDevices; this tool writes those DWORDs (when known) and verifies the change.
Key points:
- Control is vendor‑only at runtime: if no vendor toggle is known for the target endpoint, the command fails and asks you to use
--learnfirst. - Windows’
Disable_SysFxis still read for diagnostics, not used to control state here. - HKLM writes require Administrator privileges; use
--prefer-hklmif your driver only honors HKLM.
You must specify exactly one of:
--enable(turn Enhancements ON)--disable(turn Enhancements OFF)--learn(guided manual learning to append a vendor entry to INI)
Command template:
# Toggle using vendor DWORDs (if known)
audioctl enhancements
(--id <ENDPOINT-ID> | --name <NAME>) [--flow <Render|Capture>]
(--enable | --disable)
[--index <N>] [--regex]
[--prefer-hklm]
[--vendor-ini <PATH>]
# Learn a vendor DWORD (you toggle the Windows UI; the tool captures A/B and appends to INI)
audioctl enhancements
(--id <ENDPOINT-ID> | --name <NAME>) [--flow <Render|Capture>]
--learn
[--index <N>] [--regex]
[--vendor-ini <PATH>]Beyond the main switch, you can manage individual effects (per-device) after learning them.
- List learned effects for a device:
audioctl enhancements --id "{ENDPOINT-ID}" --list-fx audioctl enhancements --id "{ENDPOINT-ID}" --list-fx --json
- Learn a specific effect (guided, two-pass A/B):
audioctl enhancements --id "{ENDPOINT-ID}" --flow Render --learn-fx "BassBoost"
- Toggle an effect:
audioctl enhancements --id "{ENDPOINT-ID}" --flow Capture --enable-fx "Loudness" audioctl enhancements --id "{ENDPOINT-ID}" --flow Capture --disable-fx "Loudness"
- Delete a learned effect association for this device:
audioctl enhancements --id "{ENDPOINT-ID}" --delete-fx "BassBoost"
Notes about JSON output:
- Success prints compact, single‑line JSON, e.g.:
{"enhancementsSet":{"id":"{...}","name":"Speakers (Realtek(R) Audio)","enabled":true,"verifiedBy":"vendor:realtek_waves_primary"}} - FX toggles:
{"fxSet":{"id":"{...}","name":"Speakers","fx_name":"BassBoost","enabled":true,"verifiedBy":"vendor-fx:multi:BassBoost"}}
Fast, side‑effect‑free commands perfect for status bars, hotkeys, and scripts.
-
Current volume/mute:
audioctl get-volume [--id <ID> | --name <NAME>] [--flow Render|Capture] [--index <N>] [--regex]
Returns:
{"id":"{...}","name":"...","flow":"Render","volume":78,"muted":false} -
Current “Listen” state (Capture):
audioctl get-listen [--id <ID> | --name <NAME>] [--index <N>] [--regex]
Returns:
{"id":"{...}","name":"...","flow":"Capture","listenEnabled":true} -
Current Enhancements state (vendor‑only):
audioctl get-enhancements [--id <ID> | --name <NAME>] [--flow Render|Capture] [--index <N>] [--regex]
Returns:
{"id":"{...}","name":"...","flow":"Render","enhancementsEnabled":true} -
Aggregated device state (for GUI/scripts):
audioctl get-device-state [--id <ID> | --name <NAME>] [--flow Render|Capture] [--index <N>] [--regex] [--vendor-ini PATH]
Returns:
{ "id":"{...}", "name":"...", "flow":"Render", "volume":78, "muted":false, "listenEnabled":null, "enhancementsEnabled":true, "availableFX":[{"fx_name":"BassBoost","state":true,"source":"ini"}] }
- View live Enhancements status and vendor state:
audioctl diag-sysfx --name "Speakers" --flow Render - Dump all MMDevices values for an endpoint (HKCU/HKLM; FxProperties/Properties):
audioctl diag-mmdevices --id "{RENDER-ENDPOINT-ID}" --flow Render - Interactive discovery with reports (TXT/JSON) and optional INI snippet:
audioctl discover-enhancements --name "Speakers" --flow Render --output-dir "." --ini-snippet ".\vendor_snippets.ini"
Use audioctl wait to block until a device becomes active (appears) or until a timeout expires. Matching is against active endpoints only (DEVICE_STATE_ACTIVE). When a match is found, the command prints a compact, single‑line JSON object and exits with code 0.
- Template:
audioctl wait (--id <ENDPOINT-ID> | --name <NAME>) [--flow <Render|Capture>] [--timeout <seconds>] [--index <N>] [--regex]
- Exit codes:
- 0: found a matching active device
- 3: timeout
- 4: multiple matches (rerun with
--index)
- Tips:
- Prefer
--idwhen you know it. - Add
--flowwhen the name appears in both flows.
- Prefer
0– success1– runtime failure or invalid option combo3– device not found or wait timeout4– multiple matches; provide--index130– Ctrl‑C interrupted
- Set-default fails:
- Try running in an elevated PowerShell / Command Prompt (Run as Administrator).
- comtypes/automation errors in the EXE:
- The build already includes a compatibility shim at the top of
audioctl/compat.py. - Rebuild with the provided PyInstaller command if you change Python or comtypes versions.
- The build already includes a compatibility shim at the top of
- “Listen” seems unchanged:
- Use explicit default routing:
--playback-target-id ""(Default Playback Device). - Check JSON output: look at
enabledandverifiedBy("com"or"registry").
- Use explicit default routing:
- Enhancements don’t toggle:
- First try the toggle directly:
If you get:
audioctl enhancements --id "{ENDPOINT-ID}" --flow Render --enablethen proceed to learn.ERROR: No vendor toggle available for this device. Use --learn to teach a vendor method. - Learn the vendor toggle (manual A/B):
After a successful learn, re-run the toggle:
audioctl enhancements --id "{ENDPOINT-ID}" --flow Render --learnaudioctl enhancements --id "{ENDPOINT-ID}" --flow Render --disable - If learn fails or the result can’t be verified:
- Inspect live state vs vendor with:
audioctl diag-sysfx --id "{ENDPOINT-ID}" --flow Render - Capture ON/OFF snapshots and generate a report/snippet:
Use the suggested INI snippet or share the TXT/JSON for analysis.
audioctl discover-enhancements --id "{ENDPOINT-ID}" --flow Render --output-dir "." --ini-snippet ".\vendor_snippets.ini"
- Inspect live state vs vendor with:
- Permissions and hive preference:
- Some drivers only honor HKLM. Re-run the toggle with:
(Run an elevated console if HKLM writes are required.)
audioctl enhancements --id "{ENDPOINT-ID}" --flow Render --enable --prefer-hklm
- Some drivers only honor HKLM. Re-run the toggle with:
- Confirm selection and INI placement:
- Make sure you’re targeting the correct endpoint/flow (use
--flowand--indexif needed). - Verify
vendor_toggles.iniis at the expected path (exe directory if writable, otherwise%LOCALAPPDATA%\audioctl\vendor_toggles.ini) and that your endpoint GUID is listed underdevices =in the learned section.
- Make sure you’re targeting the correct endpoint/flow (use
- First try the toggle directly:
-
Visual list of playback (Render) and recording (Capture) devices, grouped and sorted.
-
“Show disabled/disconnected” toggle.
- Enable “Print CLI commands” in the top bar.
Every GUI action will print the equivalentaudioctl.execommand to stdout (useful for learning the CLI and scripting).
- Click “Refresh” or press
F5.
- Setting default devices via the GUI may require Administrator privileges; the GUI will warn when recommended.
- Group header rows (e.g., “Playback (Render)”) are not selectable and have no actions; right-click the actual device entries.
This project uses:
- pycaw - https://github.com/AndreMiras/pycaw
- comtypes - https://github.com/enthought/comtypes
Windows GUIDs and PROPERTYKEY constants are Microsoft API identifiers and do not require attribution.
Copyright (c) Andre Miras and contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
...
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
...
Source: https://github.com/AndreMiras/pycaw
Copyright (c) Thomas Heller, Enthought, Inc., and contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
...
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
...
Source: https://github.com/enthought/comtypes
If you distribute a PyInstaller-built executable of this project, PyInstaller is licensed under the GNU General Public License v2 (GPL-2.0) with a special exception that permits using PyInstaller to build and distribute executables, regardless of your program’s license.
Full text: https://github.com/pyinstaller/pyinstaller/blob/develop/COPYING.txt
Project page: https://www.pyinstaller.org/





