Skip to content

Instantly share code, notes, and snippets.

@ttscoff
Created September 11, 2020 10:18
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ttscoff/c5d61971d5251b8795af2415cc1bbb85 to your computer and use it in GitHub Desktop.
Save ttscoff/c5d61971d5251b8795af2415cc1bbb85 to your computer and use it in GitHub Desktop.
Script for macOS to save and restore audio input/output device and volume settings
#!/usr/bin/env ruby
# frozen_string_literal: true
# Brett Terpstra <https://brettterpstra.com>
#
# macOS: Stores all input/output devices and volume settings to a
# text file, restores on demand
#
## Dependencies
# SwitchAudioSource <https://github.com/deweller/switchaudio-osx>
# Install with Homebrew: `brew install switchaudio-osx`
#
## Usage:
# volumetoggle.rb save
# volumetoggle.rb restore
require 'yaml'
TYPE_OUTPUT = 'output'
TYPE_INPUT = 'input'
TYPE_SYSTEM = 'system'
STORE_FILE = File.expand_path('~/.saved_volume')
SWITCH_AUDIO = '/usr/local/bin/SwitchAudioSource'
def get_device(type)
`#{SWITCH_AUDIO} -c -t #{type}`.strip
end
def set_device(type, device)
`#{SWITCH_AUDIO} -t #{type} -s "#{device}"`
end
def set_volume(type, volume, muted = nil)
script = "set volume #{type} volume #{volume}"
if type == TYPE_OUTPUT
script += " #{(muted ? ' with' : ' without')} output muted"
end
`osascript -e "#{script}"`
end
# String helpers
class String
def to_literal
case self
when /\d+/
to_i
when /true/
true
when /false/
false
when /missing value/
nil
end
end
def to_literal!
replace to_literal
end
def to_h
output = {}
split(/, /).each do |item|
parts = item.split(/:/)[0..1]
k = parts[0] =~ /muted/ ? 'muted' : parts[0].sub(/ volume/, '')
k = k.to_sym
v = parts[1].to_literal
output[k] = v
end
output
end
end
case ARGV[0]
when /save/
devices = {}
devices[:input] = get_device(TYPE_INPUT)
devices[:output] = get_device(TYPE_OUTPUT)
devices[:system] = get_device(TYPE_SYSTEM)
settings = `osascript -e 'return (get volume settings)'`.strip.to_h
settings[:devices] = devices
File.open(STORE_FILE, 'w') do |f|
f.puts YAML.dump(settings)
end
warn "Settings written to #{STORE_FILE}"
when /restore/
raise "No store file found at #{STORE_FILE}" unless File.exist?(STORE_FILE)
settings = YAML.load(IO.read(STORE_FILE))
settings[:devices].each { |k, v| set_device(k.to_s, v) }
%i[output input alert].each do |sym|
set_volume(sym.to_s, settings[sym], settings[:muted]) if settings[sym]
end
warn 'Settings restored'
warn YAML.dump settings
else
warn 'Requires argument'
warn "#{File.basename(__FILE__)} save or #{File.basename(__FILE__)} save"
Process.exit 1
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment