Changing Sound Input/Output System Preferences/System Settings

I use this script all the time to change my sound output (and sometimes input).

I just had to update it for Ventura because they completely redid System Preferences (and renamed it System Settings)

Exactly as it stands, I’m sure this isn’t useful to anyone else, because it’s customized for my environment. But it would be easy to adapt. Also, I thought someone wanting to do similar things might find it useful as a reference.

In particular, finding the correct syntax to GUI script some of these things is a bit of a PITA, so thought I might save someone else the trouble.

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions

tell application "System Events"
	set userName to the name of the current user
end tell

set sysVersion to system version of (system info)
set mainVers to text 1 through ((offset of "." in sysVersion) - 1) of sysVersion as number
set newUI to mainVers > 12


if newUI then
	tell application "System Events"
		tell application process "System Settings"
			set soundPane to the first row of outline 1 of scroll area 1 of group 1 of splitter group 1 of group 1 of window 1 whose value of the first static text of UI element 1 is "Sound"
			set selected of soundPane to true
			repeat
				try
					window "Sound"
					exit repeat
				end try
			end repeat
			
			-- Select output tab
			perform action "AXPress" of radio button 1 of tab group 1 of group 2 of scroll area 1 of group 1 of group 2 of splitter group 1 of group 1 of window "Sound"
			set currentOutputName to (the value of the first static text of group 1 of UI element 1) of (the first row of table 1 of scroll area 1 of group 2 of scroll area 1 of group 1 of group 2 of splitter group 1 of group 1 of window "Sound" whose selected is true)
		end tell
	end tell
else
	tell application "System Preferences" to reveal anchor "output" of pane id "com.apple.preference.sound"
	delay 0.3
	tell application "System Events"
		tell process "System Preferences"
			set currentOutput to the first row of table 1 of scroll area 1 of tab group 1 of window "Sound" whose selected is true
			set currentOutputName to the value of text field 1 of currentOutput
		end tell
	end tell
end if


set headSetName to "Mpow HC4"
set ComputerSoundbarName to "LG HDR 4K"
set HomeTheaterReceiverName to "SC-75"
set HeadphoneAmpName to "External Headphones"

if currentOutputName is ComputerSoundbarName then
	set currentOutputName to "Computer Soundbar"
else if currentOutputName is HomeTheaterReceiverName then
	set currentOutputName to "Home Theater Receiver"
else if currentOutputName is HeadphoneAmpName then
	set currentOutputName to "Headphone Amp"
else if currentOutputName is headSetName then
	set currentOutputName to "USB Headset"
end if


set soundOptionsList to {"Phone Headset", "Computer Soundbar", "Headphone Amp", "Home Theater Receiver", "Living Room", "Master Bedroom", "Ellies Room"}

if userName is not "work" then set soundOptionsList to soundOptionsList & "iPhone(s) / Airfoil"

if (currentOutputName is not headSetName) and (currentOutputName is not in soundOptionsList) then copy currentOutputName to the beginning of soundOptionsList

if currentOutputName is "Computer Soundbar" then
	set defaultChoice to "Headphone Amp"
else if currentOutputName is "Headphone Amp" then
	set defaultChoice to "Computer Soundbar"
else
	set defaultChoice to currentOutputName
end if

set soundChoice to (choose from list soundOptionsList with prompt "Select sound output" default items defaultChoice OK button name {"Go"} cancel button name {"Cancel"}) as text
set newSoundInput to false

if soundChoice is "false" then
	return
else if soundChoice is "Phone Headset" then
	set newSoundOutput to headSetName
	set newSoundInput to headSetName
else if soundChoice is "Computer Soundbar" then
	set newSoundOutput to ComputerSoundbarName
else if soundChoice is "Headphone Amp" then
	set newSoundOutput to HeadphoneAmpName
else if soundChoice is "Home Theater Receiver" then
	set newSoundOutput to HomeTheaterReceiverName
else if soundChoice is "iPhone(s) / Airfoil" then
	run script "tell application \"Airfoil.app\" to activate"
else
	set newSoundOutput to soundChoice
end if
try
	if newUI then
		tell application "System Events" to tell application process "System Settings"
			set selected of ((the first row of table 1 of scroll area 1 of group 2 of scroll area 1 of group 1 of group 2 of splitter group 1 of group 1 of window "Sound") whose (value of the first static text of group 1 of UI element 1) is newSoundOutput) to true
			if newSoundInput is not false then
				perform action "AXPress" of radio button 2 of tab group 1 of group 2 of scroll area 1 of group 1 of group 2 of splitter group 1 of group 1 of window "Sound"
				set selected of ((the first row of table 1 of scroll area 1 of group 2 of scroll area 1 of group 1 of group 2 of splitter group 1 of group 1 of window "Sound") whose (value of the first static text of group 1 of UI element 1) is newSoundInput) to true
			end if
		end tell
		
	else
		tell application "System Events" to tell process "System Preferences" to select (row 1 of table 1 of scroll area 1 of tab group 1 of window "Sound" whose value of text field 1 is newSoundOutput)
		if newSoundInput is not false then
			tell application "System Preferences" to reveal anchor "input" of pane id "com.apple.preference.sound"
			tell application "System Events" to tell process "System Preferences" to select (row 1 of table 1 of scroll area 1 of tab group 1 of window "Sound" whose value of text field 1 is newSoundInput)
		end if
		tell application "System Preferences" to quit
	end if
on error
	display dialog "There was a problem selecting \"" & soundChoice & "\" in the Sound Control Panel." buttons {"Cancel"} default button "Cancel"
end try
1 Like

I changed some line in your script so as not to be hardcoded soundOptionList

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions

set {sysVersion, userName} to {system version, short user name} of (system info)
set mainVers to text 1 through ((offset of "." in sysVersion) - 1) of sysVersion as number
set newUI to mainVers > 12

if newUI then
	tell application "System Events"
		if "System Settings" is not in (name of processes) then
			launch application "System Settings"
			repeat until "System Settings" is in (name of processes)
				delay 0.2
			end repeat
		end if
		tell application process "System Settings"
			repeat with i from 10 to 0 by -1 --will try for 5 seconds
				if exists window 1 then exit repeat
				delay 0.2
			end repeat
			if i = 0 then return -- timed-out
			set soundPane to row 1 of outline 1 of scroll area 1 of group 1 of splitter group 1 of group 1 of window 1 whose value of the first static text of UI element 1 is "Sound"
			if not (selected of soundPane) then set selected of soundPane to true
			repeat with i from 25 to 0 by -1 --will try for 5 seconds
				try
					window "Sound"
					exit repeat
				end try
				delay 0.5
			end repeat
			if i = 0 then return -- timed-out
			-- Select output tab
			set myButton to (first radio button of tab group 1 of group 2 of scroll area 1 of group 1 of group 2 of splitter group 1 of group 1 of window "Sound" whose description is "Output")
			if value of myButton = 0 then
				perform action "AXPress" of item 1 of myButton --(radio buttons of tab group 1 of group 2 of scroll area 1 of group 1 of group 2 of splitter group 1 of group 1 of window "Sound" whose description is "Output")
			end if
			--perform action "AXPress" of radio button 1 of tab group 1 of group 2 of scroll area 1 of group 1 of group 2 of splitter group 1 of group 1 of window "Sound"
			set currentOutputName to (value of static text 1 of group 1 of UI element 1) of (first row of table 1 of scroll area 1 of group 2 of scroll area 1 of group 1 of group 2 of splitter group 1 of group 1 of window "Sound" whose selected is true)
			set soundOptionsList to value of static text 1 of group 1 of UI element 1 of rows of table 1 of scroll area 1 of group 2 of scroll area 1 of group 1 of group 2 of splitter group 1 of group 1 of window "Sound"
		end tell
	end tell
else
	tell application "System Settings" to reveal anchor "output" of pane id "com.apple.preference.sound"
	delay 0.3
	tell application "System Events"
		tell process "System Preferences"
			set currentOutput to the first row of table 1 of scroll area 1 of tab group 1 of window "Sound" whose selected is true
			set currentOutputName to the value of text field 1 of currentOutput
		end tell
	end tell
end if

set headSetName to "Mpow HC4"
set ComputerSoundbarName to "LG HDR 4K"
set HomeTheaterReceiverName to "SC-75"
set HeadphoneAmpName to "External Headphones"

if currentOutputName is ComputerSoundbarName then
	set currentOutputName to "Computer Soundbar"
else if currentOutputName is HomeTheaterReceiverName then
	set currentOutputName to "Home Theater Receiver"
else if currentOutputName is HeadphoneAmpName then
	set currentOutputName to "Headphone Amp"
else if currentOutputName is headSetName then
	set currentOutputName to "USB Headset"
end if

--set soundOptionsList to {"Phone Headset", "Computer Soundbar", "Headphone Amp", "Home Theater Receiver", "Living Room", "Master Bedroom", "Ellies Room"}

if userName is not "work" then set end of soundOptionsList to "iPhone(s) / Airfoil"

if (currentOutputName is not headSetName) and (currentOutputName is not in soundOptionsList) then copy currentOutputName to the beginning of soundOptionsList

if currentOutputName is "Computer Soundbar" then
	set defaultChoice to "Headphone Amp"
else if currentOutputName is "Headphone Amp" then
	set defaultChoice to "Computer Soundbar"
else
	set defaultChoice to currentOutputName
end if

set soundChoice to (choose from list soundOptionsList with prompt "Select sound output" default items defaultChoice OK button name {"Go"} cancel button name {"Cancel"}) as text
set newSoundInput to false

if soundChoice is "false" then
	return
else if soundChoice is "Phone Headset" then
	set newSoundOutput to headSetName
	set newSoundInput to headSetName
else if soundChoice is "Computer Soundbar" then
	set newSoundOutput to ComputerSoundbarName
else if soundChoice is "Headphone Amp" then
	set newSoundOutput to HeadphoneAmpName
else if soundChoice is "Home Theater Receiver" then
	set newSoundOutput to HomeTheaterReceiverName
else if soundChoice is "iPhone(s) / Airfoil" then
	run script "tell application \"Airfoil.app\" to activate"
else
	set newSoundOutput to soundChoice
end if
try
	if newUI then
		tell application "System Events" to tell application process "System Settings"
			set selected of ((first row of table 1 of scroll area 1 of group 2 of scroll area 1 of group 1 of group 2 of splitter group 1 of group 1 of window "Sound") whose (value of the first static text of group 1 of UI element 1) is newSoundOutput) to true
			if newSoundInput is not false then
				perform action "AXPress" of radio button 2 of tab group 1 of group 2 of scroll area 1 of group 1 of group 2 of splitter group 1 of group 1 of window "Sound"
				set selected of (first row of table 1 of scroll area 1 of group 2 of scroll area 1 of group 1 of group 2 of splitter group 1 of group 1 of window "Sound" whose (value of the first static text of group 1 of UI element 1) is newSoundInput) to true
			end if
		end tell
		
	else
		tell application "System Events" to tell process "System Preferences" to select (row 1 of table 1 of scroll area 1 of tab group 1 of window "Sound" whose value of text field 1 is newSoundOutput)
		if newSoundInput is not false then
			tell application "System Settings" to reveal anchor "input" of pane id "com.apple.preference.sound"
			tell application "System Events" to tell process "System Preferences" to select (row 1 of table 1 of scroll area 1 of tab group 1 of window "Sound" whose value of text field 1 is newSoundInput)
		end if
		tell application "System Settings" to quit
	end if
on error
	display dialog "There was a problem selecting \"" & soundChoice & "\" in the Sound Control Panel." buttons {"Cancel"} default button "Cancel"
end try
1 Like

I’ve got options that show up that I never use that I didn’t want cluttering my list, so for me, the hard coded list was best. If for any reason the currently selected output wasn’t on my list, I did have the script just adding it to the list for that run.

I also forgot to mention - while I can send my sound all over the place, 99% of the time when I run this, I’m just switching between my computer speakers and headphones, so I have it automatically switch the default there. I keep this script on a command key via Fastscripts, so to swap between my headphones and speakers, I don’t even need to select off the list. It’s just my shortcut for the script and then immediately hit enter - so it’s almost always just two keystrokes to the change I want to make.

Feel free to edit away - I notice that after your changes, there are still a lot of hardcoded values that are only applicable to my setup in there.

I hadn’t done any work to generalize this for anyone else’s use case - I just wanted to get those wonky lines like:

set selected of ((the first row of table 1 of scroll area 1 of group 2 of scroll area 1 of group 1 of group 2 of splitter group 1 of group 1 of window "Sound") whose (value of the first static text of group 1 of UI element 1) is newSoundInput) to true

out there, so nobody else has to puzzle through that UI mess.

Thank you. It’s just what I was looking for.

I’m glad it’s useful to you.

Unfortunately, it doesn’t work in Sonoma, they’ve changed the UI yet again! It worked without changes for something like a dozen OS releases, then they broke it two versions in a row. I’ve been meaning to update it, I’ll post it when I fix it again.

Revised for Sonoma:

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions

tell application "System Events"
	set userName to the name of the current user
end tell

set sysVersion to system version of (system info)
set mainVers to text 1 through ((offset of "." in sysVersion) - 1) of sysVersion as number

if mainVers > 12 then
	tell application "System Settings" to launch
	tell application "System Events"
		tell application process "System Settings"
			repeat
				try
					window 1
					exit repeat
				end try
			end repeat
			delay 0.1
			set soundPane to the first row of outline 1 of scroll area 1 of group 1 of splitter group 1 of group 1 of window 1 whose value of the first static text of UI element 1 is "Sound"
			set selected of soundPane to true
			repeat
				try
					window "Sound"
					exit repeat
				end try
			end repeat
			
			-- Select output tab
			perform action "AXPress" of radio button 1 of tab group 1 of group 2 of scroll area 1 of group 1 of group 2 of splitter group 1 of group 1 of window "Sound"
			if mainVers > 13 then
				set currentOutputName to (the value of the first static text of group 1 of UI element 1) of (the first row of outline 1 of scroll area 1 of group 2 of scroll area 1 of group 1 of group 2 of splitter group 1 of group 1 of window "Sound" whose selected is true)
			else
				set currentOutputName to (the value of the first static text of group 1 of UI element 1) of (the first row of table 1 of scroll area 1 of group 2 of scroll area 1 of group 1 of group 2 of splitter group 1 of group 1 of window "Sound" whose selected is true)
			end if
		end tell
	end tell
else
	tell application "System Settings" to reveal anchor "output" of pane id "com.apple.preference.sound"
	delay 0.3
	tell application "System Events"
		tell process "System Preferences"
			set currentOutput to the first row of table 1 of scroll area 1 of tab group 1 of window "Sound" whose selected is true
			set currentOutputName to the value of text field 1 of currentOutput
		end tell
	end tell
end if


set headSetName to "Mpow HC4"
set ComputerSoundbarName to "LG HDR 4K"
set HomeTheaterReceiverName to "SC-75"
set HeadphoneAmpName to "External Headphones"

if currentOutputName is ComputerSoundbarName then
	set currentOutputName to "Computer Soundbar"
else if currentOutputName is HomeTheaterReceiverName then
	set currentOutputName to "Home Theater Receiver"
else if currentOutputName is HeadphoneAmpName then
	set currentOutputName to "Headphone Amp"
else if currentOutputName is headSetName then
	set currentOutputName to "USB Headset"
end if


set soundOptionsList to {"Phone Headset", "Computer Soundbar", "Headphone Amp", "Home Theater Receiver", "Living Room", "Master Bedroom", "Ellies Room"}

if userName is not "work" then set soundOptionsList to soundOptionsList & "iPhone(s) / Airfoil"

if (currentOutputName is not headSetName) and (currentOutputName is not in soundOptionsList) then copy currentOutputName to the beginning of soundOptionsList

if currentOutputName is "Computer Soundbar" then
	set defaultChoice to "Headphone Amp"
else if currentOutputName is "Headphone Amp" then
	set defaultChoice to "Computer Soundbar"
else
	set defaultChoice to currentOutputName
end if

set soundChoice to (choose from list soundOptionsList with prompt "Select sound output" default items defaultChoice OK button name {"Go"} cancel button name {"Cancel"}) as text
set newSoundInput to false

if soundChoice is "false" then
	return
else if soundChoice is "Phone Headset" then
	set newSoundOutput to headSetName
	set newSoundInput to headSetName
else if soundChoice is "Computer Soundbar" then
	set newSoundOutput to ComputerSoundbarName
else if soundChoice is "Headphone Amp" then
	set newSoundOutput to HeadphoneAmpName
else if soundChoice is "Home Theater Receiver" then
	set newSoundOutput to HomeTheaterReceiverName
else if soundChoice is "iPhone(s) / Airfoil" then
	run script "tell application \"Airfoil.app\" to activate"
else
	set newSoundOutput to soundChoice
end if
try
	if mainVers > 12 then
		tell application "System Events" to tell application process "System Settings"
			if mainVers > 13 then
				set selected of ((the first row of outline 1 of scroll area 1 of group 2 of scroll area 1 of group 1 of group 2 of splitter group 1 of group 1 of window "Sound") whose (value of the first static text of group 1 of UI element 1) is newSoundOutput) to true
				if newSoundInput is not false then
					perform action "AXPress" of radio button 2 of tab group 1 of group 2 of scroll area 1 of group 1 of group 2 of splitter group 1 of group 1 of window "Sound"
					set selected of ((the first row of outline 1 of scroll area 1 of group 2 of scroll area 1 of group 1 of group 2 of splitter group 1 of group 1 of window "Sound") whose (value of the first static text of group 1 of UI element 1) is newSoundInput) to true
				end if
			else
				set selected of ((the first row of table 1 of scroll area 1 of group 2 of scroll area 1 of group 1 of group 2 of splitter group 1 of group 1 of window "Sound") whose (value of the first static text of group 1 of UI element 1) is newSoundOutput) to true
				if newSoundInput is not false then
					perform action "AXPress" of radio button 2 of tab group 1 of group 2 of scroll area 1 of group 1 of group 2 of splitter group 1 of group 1 of window "Sound"
					set selected of ((the first row of table 1 of scroll area 1 of group 2 of scroll area 1 of group 1 of group 2 of splitter group 1 of group 1 of window "Sound") whose (value of the first static text of group 1 of UI element 1) is newSoundInput) to true
				end if
			end if
		end tell
	else
		tell application "System Events" to tell process "System Preferences" to select (row 1 of table 1 of scroll area 1 of tab group 1 of window "Sound" whose value of text field 1 is newSoundOutput)
		if newSoundInput is not false then
			tell application "System Settings" to reveal anchor "input" of pane id "com.apple.preference.sound"
			tell application "System Events" to tell process "System Preferences" to select (row 1 of table 1 of scroll area 1 of tab group 1 of window "Sound" whose value of text field 1 is newSoundInput)
		end if
	end if
	-- tell application "System Settings" to quit
on error
	display dialog "There was a problem selecting \"" & soundChoice & "\" in the Sound Control Panel." buttons {"Cancel"} default button "Cancel"
end try

Incidentally, I don't have it closing System Setting after it's done anymore, because for some reason, if I quit System Settings via Applescript on Sonoma, the next time it launches, it has not window and can not do anything until I manually quit it and launch it again? Very strange. At least, that's the behavior I'm experiencing now.