Automate Your Mac’s Audio Input with a Simple Script
How a shell script and the HammerSpoon app can save you from microphone embarrassment
Published on
Oct 12, 2024
Read time
5 min read
Introduction
The rise of remote work has brought with it a new set of video call-related problems. The fact that I am a software engineer—someone expected to know how these things work—only makes the embarrassment worse when issues arise.
Recently, I have found myself contending with a particularly annoying problem. Often, MacOS seems determined to choose the wrong audio input at the worst possible moment.
To be fair to my laptop, it’s not entirely its fault. My audio setup is a a bit of mess, for two main reasons:
- I used to produce music as a hobby and so my home office is filled with audio equipment.
- I like to work from different locations: my home office, my company’s office, and at one of my favourite coffee shops.
This means that I have a lot of audio devices connecting to (or disconnecting from) my laptop, and the default audio input often changes to something I don’t want. Too often, I join a video call and no one can hear me. Of course, I quickly switch to the correct input, but the damage is done.
I had a look around for some software that could help me with this, but I couldn’t find anything. Time to code a solution!
What I Want
To solve this, I want to specify my preferred audio inputs so that, whatever is connected or disconnected, my laptop will always set my preference to the default.
For me, this is:
- Jabra Evolve2 40 SE—if connected, I want to use this.
- Yeti Stereo Microphone—the next best option, though it’s only available in my home office.
- MacBook Pro Microphone—my laptop’s microphone, used as a fallback.
All my other audio devices are intended for output only and should be ignored. The main reason we’re in this mess is that MacOS will happily switch to any audio device that is connected, even if it’s output only!
The Solution
My solution involves:
- a shell script using the SwitchAudioSource library, and
- the HammerSpoon app—a powerful automation tool for MacOS that allows you to write Lua scripts to automate tasks.
Whenever an audio device is connected or disconnected, we will use HammerSpoon to run our shell script, which will set the audio input to our preferred device.
Part 1: The Shell Script
First, let’s install the SwitchAudioSource
library via Homebrew:
brew install switchaudio-osx
Next, we’ll want to grab the location of the SwitchAudioSource
binary. We can do this by running:
which SwitchAudioSource
For me, this returned /opt/homebrew/bin/SwitchAudioSource
.
After that, we’ll write our script. I’ve named mine switch_audio_input.sh
and put it in my ~/scripts
directory, making sure to reference the correct path to SwitchAudioSource
. Here’s the script:
# ~/scripts/switch_audio_input.sh
PREFERRED_DEVICES=("Jabra Evolve2 40 SE" "Yeti Stereo Microphone")
AVAILABLE_INPUTS=$(/opt/homebrew/bin/SwitchAudioSource -a -t input)
if [ -n "$1" ]; then
echo "Adding $1 to available inputs"
AVAILABLE_INPUTS="$AVAILABLE_INPUTS"$'\n'"$1"
fi
echo "Available inputs: $AVAILABLE_INPUTS"
for DEVICE in "${PREFERRED_DEVICES[@]}"; do
if echo "$AVAILABLE_INPUTS" | grep -q "$DEVICE"; then
/opt/homebrew/bin/SwitchAudioSource -t input -s "$DEVICE"
osascript -e "display notification \"Input switched to $DEVICE\" with title \"Audio Input\""
exit 0
fi
done
/opt/homebrew/bin/SwitchAudioSource -t input -s "MacBook Pro Microphone"
osascript -e "display notification \"Input switched to default: MacBook Pro Microphone\" with title \"Audio Input\""
In this script, we set our preferred devices in the PREFERRED_DEVICES
array. Order matters here, as we will try to set the audio input to the first device in the array, then the second, and so on.
You should update this array with your own preferred devices. To find the names of your devices, make sure they are connected, then run the following command in the terminal:
SwitchAudioSource -a -t input
Our script accepts an optional argument, which is the name of any input device that has just been connected. I found this helpful in getting around any delays in SwitchAudioSource detecting the newly connected device. At the end of the script, if no preferred devices are available, we set the default input to the MacBook Pro Microphone. Also, whenver our script runs, it will display a notification with the name of the device it has switched to, handled by the osascript
command.
To run the script, we’ll need to ensure it’s executable:
chmod +x ~/scripts/switch_audio_input.sh # update with your chosen path
We can then test it by running it with no arguments:
bash ~/scripts/switch_audio_input.sh
If everything is working, you should see a notification telling you which audio input has been set!
But we don’t want to have to run this script manually every time we connect a new audio device. Let’s automate that!
Part 2: HammerSpoon
Hammerspoon is an automation tool for MacOS that allows you to write Lua scripts to automate tasks: here, we want to recognise when an audio device is connected or disconnected. There are other options for this kind of automation (such as using a combination of Automator, AppleScript, and launchd
) but, from my research, HammerSpoon was the simplest.
We’ll start by installing HammerSpoon from its official website.
Once installed and unzipped, go into the application’s preferences and enable accessibility permissions. I also recommend enabling the “Launch Hammerspoon at login” option. For debugging purposes, we can open the HammerSpoon console by clicking on the icon in the menu bar and selecting “Open Console”.
I’m going to write my HammerSpoon script in the default ~/.hammerspoon/init.lua
file, but you can create a separate file if your needs are more complex of you’re already using HammerSpoon for something else.
-- ~/.hammerspoon/init.lua
local log = hs.logger.new("audio_watcher", "info")
local function audioDeviceWatcherCallback(event)
-- trigger the script when an input device changes
if event == "dev#" then
-- add a delay to allow the system to fully switch devices
hs.timer.doAfter(2, function()
local inputDevice = hs.audiodevice.defaultInputDevice():name()
local logFile = "~/scripts/audio_input_log.txt"
hs.execute("~/scripts/switch_audio_input.sh "
.. inputDevice
.. " > "
.. logFile
.. " 2>&1 &"
)
log.i("Script executed and notification sent")
end)
end
end
hs.audiodevice.watcher.setCallback(audioDeviceWatcherCallback)
hs.audiodevice.watcher.start()
This script listens for changes in the connected audio devices and runs our shell script when a change is dedicated. We add a short delay of two seconds to allow the system time to fully switch devices before running our script. I’m also logging the output of the script to a file, audio_input_log.txt
, in my ~/scripts
directory, which is useful for debugging. If you run into any issues, make sure to check the HammerSpoon console and the audio_input_log.txt
for any error messages.
Make sure to reload the HammerSpoon configuration by clicking on the icon in the menu bar and selecting “Reload Config”. Now, when you connect or disconnect an audio device, you should see a notification telling you which audio input has been set!
And that’s everything you need. I hope this helps you streamline your audio input setup—and here’s hoping that I’ll never have to deal with microphone embarrassment again!
Related articles
You might also enjoy...
A MacOS Trick to Develop Multiple Projects from a Single Command
How to spawn a terminal and start the development process for each project
3 min read
Installing MongoDB on macOS Catalina
A tricky problem due to Catalina’s read-only root folder
2 min read
How to Automate Merge Requests with Node.js and Jira
A quick guide to speed up your MR or PR workflow with a simple Node.js script
7 min read