A command-line utility to control sound devices on macOS, written in Swift.
This utility controls the sound devices for macOS. You specify the name of the sound device, such as "Built-in Output", and the utility switches the device immediately without any GUI interaction.
This is a command-line utility only and has no graphical user interface.
brew tap graysoncash/soundctl
brew install soundctlswift build -c release
cp .build/release/soundctl ~/.local/bin/Or use just:
just install # copy into ~/.local/bin
just link # or symlink the build output insteadBoth check that ~/.local/bin is on your PATH and offer to fix your shell config if it isn't.
soundctl <subcommand> [options]- current (default): Show current audio device
- list: List all audio devices
- set
<identifier>: Set the audio device - next: Cycle to the next audio device
- mute
[action]: Control mute status - alias
add|list|remove: Manage device aliases - monitor: Watch for device changes and auto-switch the default device
- --type, -t
<type>: Device type (input/output/system/all). Defaults to output. - --format, -f
<format>: Output format (human/cli/json). Defaults to human. (Applies tocurrentandlist)
soundctl current
# or just
soundctlOutput:
Someone's AirPods Max (XX-XX-XX-XX-XX-XX)
soundctl current --format jsonOutput:
{"id":108,"type":"output","name":"Someone's AirPods Max","uid":"XX-XX-XX-XX-XX-XX:output"}soundctl listsoundctl list --type inputsoundctl list --format jsonsoundctl set "MacBook Pro Speakers"soundctl set "XX-XX-XX-XX-XX-XX"soundctl set "93"The set command is smart and auto-detects the type of identifier:
- MAC address format (XX-XX-XX-XX-XX-XX): Matches via UID
- Numeric ID: Matches by device ID
- Anything else: Matches by device name
Priority order: MAC address β numeric ID β name (so a device named "123" can still be matched even if there's an ID 123)
If the identifier doesn't match any active audio device but does match a paired Bluetooth device (by MAC address or name) that isn't currently connected, set connects to it over Bluetooth, waits for it to register as an audio device, and then sets it. Use --bluetooth-timeout <seconds> to change how long to wait for the device to appear after connecting (default: 10).
This requires Bluetooth permission for your terminal. macOS normally prompts on first use; if your terminal can't prompt (e.g., Warp), add it manually under System Settings β Privacy & Security β Bluetooth.
Declare groups of Bluetooth devices that should never be connected at the same time. When set switches to a member of a group, every other connected member is disconnected from Bluetooth β useful when two headsets (say, AirPods Pro and AirPods Max) would otherwise fight over your audio. Rivals are only evicted once the new device is confirmed reachable (registered as an audio device, or its Bluetooth link freshly up), so a set that fails β the device is off, out of range, or the command was a misclick β never interrupts whatever is currently playing.
[exclusive]
groups = [
["AirPods Pro", "AirPods Max"],
]Entries are device names or MAC addresses and are matched against the paired-device list, so they work whether or not the device is currently connected. A device can appear in multiple groups. Enforcement requires the same Bluetooth permission as auto-connect; if access is unavailable, set still switches the device and prints a note that the group wasn't enforced.
Save a short name for a device (by MAC address or name) along with the device type(s) to apply, so you don't have to type the full identifier. Aliases are stored in your config file (~/.config/soundctl/config.toml).
# Save "app" for AirPods Pro, applied to both input and output
soundctl alias add app "AA:BB:CC:DD:EE:FF" -t input,output
# Save "apm" for AirPods Max, output only (default type is output)
soundctl alias add apm "AirPods Max"
soundctl alias list
soundctl alias remove appThen use the alias anywhere an identifier is expected:
soundctl set app # connects (if needed) and sets it as input + outputWhen you set an alias, its saved types are applied. Passing -t overrides them for that invocation (soundctl set app -t output sets output only). Aliases also resolve through Bluetooth auto-connect, so set app will connect paired-but-disconnected AirPods first.
soundctl nextToggle the mute state for the currently selected input (e.g., microphone):
soundctl mute toggle --type input
# or just
soundctl muteMute the input:
soundctl mute on --type inputUnmute the input:
soundctl mute off --type inputThis is useful on a hotkey, e.g., to mute your Teams or Zoom input.
soundctl monitor runs in the foreground and watches for audio devices coming and going, automatically switching the default device for you. Press Ctrl-C to stop.
# Watch output devices (default)
soundctl monitor
# Watch both input and output
soundctl monitor --type input,outputFor each watched type, the behavior is:
- Priority list configured (in the
[monitor]config section): switch to the highest-ranked device that is currently present. Great for "prefer AirPods, fall back to the display, then the built-in speakers." - No list for that type: follow whatever device of that type was just connected.
[monitor]
output = ["AirPods Max", "Studio Display", "MacBook Pro Speakers"]
input = ["AirPods Max", "MacBook Pro Microphone"]Blocklisted devices (see Ignore Devices) are never chosen. Monitor mode always posts a notification when it switches.
Pass --notify to set or next to post a macOS notification when the device changes β handy when the command runs from a hotkey and you want visible confirmation:
soundctl set app --notify
soundctl next --notifyTo make notifications the default for every set/next, enable them in your config:
[behavior]
notify = truesoundctl can generate completion scripts for zsh, bash, and fish. Completions include your saved alias names and the names of your current audio devices.
# Install the zsh completion onto your fpath
just install-completions
# Or generate scripts for all shells into ./completions
just completions
# Or generate a single script by hand
soundctl --generate-completion-script zsh > /path/on/your/fpath/_soundctl- id: Numeric identifier assigned by macOS at runtime. Can change between reboots or reconnections.
- uid: Persistent unique identifier string (the MAC address for Bluetooth devices). This is the reliable identifier for matching devices.
You can optionally create a configuration file at ~/.config/soundctl/config.toml to filter which devices appear in listings and when cycling with the next command, define monitor mode priority lists, and toggle notifications. See config.example.toml for a full example.
[ignoreDevices]
names = ["Virtual Device", "Aggregate Device"]
uids = ["00-00-00-00-00-00"][includeDevices]
names = ["MacBook Pro Speakers"]
uids = ["11-22-33-44-55-66"]Filter Priority: If includeDevices has any entries, only those devices will be shown (allowlist mode). Otherwise, ignoreDevices will be used to exclude devices (blocklist mode). Both filters support:
- names: Array of device name strings. Matches if the device name contains the string or vice versa.
- uids: Array of UID strings (or MAC addresses). Matches if the device UID contains the string.
- macOS 14.0 or later
- Swift 5.9 or later
MIT License - see LICENSE file for details.
Originally inspired by switchaudio-osx by Devon Weller.
Rewritten in Swift for improved macOS integration and maintainability.