Excessively Adequate

Adjusting a Mac's System Volume on the Command Line

Because I’m approximately 270 years old and listen to music (MP3 files, no less!) on my computer instead of a smart speaker, sometimes I find myself wanting to adjust my Mac’s system volume without needing to, say, get up from the couch. I’m sure there’s plenty of iOS apps that can do that with varying amounts of tracking and in-app-purchases, but because, once again, I’m practically 270 years old, I prefer to SSH into my computer and run a command.

Thanks to AppleScript, the amount of trickery required to make this possible is kept to a minimum: Keeping in mind that the built-in osascript utility executes AppleScript code, running…

osascript -e "set volume output volume 100"

…will turn the volume up to its maximum – the number is interpreted as a percentage. You can also make things louder or quieter by some percentage points…

osascript -e "set volume output volume (output volume of (get volume settings) - 42)"

…or (un)mute the computer:

osascript -e "set volume with output muted"
osascript -e "set volume without output muted"

But, as you can see, these commands are a bit unintuitive and a lot unwieldy. To provide a better user experience in the “lazy couch potato” use case, I’ve written a few lines of bash that provide fewer-keystrokes access to these volume controls:

#!/bin/bash

USAGE="usage: vol [-h | --help | NUMBER_FROM_0_TO_100 | -DECREMENT | +INCREMENT]"

# if the argument isn't one of the expected values, display usage instructions
if [ "$1" == "-h" ] || [ "$1" == "--help" ] || ! [[ "$1" =~ ^$|^[+-]?[0-9]+$ ]]; then
    echo "$USAGE"
    exit 1
fi

# retrieve old volume
OLD_VOLUME="$(osascript -e "output volume of (get volume settings)")"

if [ -z "$1" ]; then
    echo "$OLD_VOLUME"
else
    # default case: just set volume to specified value
    NEW_VOLUME="$1"

    # alternatively: decrement or increment?
    if [[ "$1" == -* ]] || [[ "$1" == +* ]]; then
        NEW_VOLUME=$(($OLD_VOLUME + $1))
    fi

    # clamp to [0, 100]
    if [ "$NEW_VOLUME" -lt 0 ] ; then
        NEW_VOLUME=0
    fi
    if [ "$NEW_VOLUME" -gt 100 ] ; then
        NEW_VOLUME=100
    fi

    # give feedback
    MUTED=""
    if [ "$NEW_VOLUME" -eq 0 ]; then
        MUTED="(muted)"
    fi
    echo "$OLD_VOLUME -> $NEW_VOLUME $MUTED"

    # set
    osascript -e "set volume output volume $NEW_VOLUME"
fi

You can also find this script on GitHub.

Paste these lines of code into a file named vol, place it in a directory that’s on your $PATH and make sure the file’s executable: run chmod u+x vol, for instance.

(A common choice of directory is ~/local/bin – which you can put on your $PATH by adding export PATH="$HOME/local/bin:$PATH to your .bashrc and/or .bash_profile – but I tend to instead maintain small utilities like this as functions in my .bashrc. To each their own.)

Here’s how to use the thusly-created vol command:

$ vol --help
usage: vol [-h | --help | NUMBER_FROM_0_TO_100 | -DECREMENT | +INCREMENT]
$ vol
26
$ vol 0
26 -> 0 (muted)
$ vol 30
0 -> 30
$ vol +15
30 -> 45
$ vol -3
45 -> 42

But wait, there’s more!

Controlling your music player from the command line

The first Apple product I every used was iTunes (on Windows Vista!), and I stuck with it until it was rebranded as Music. I then switched to Swinsian, a native, fast, minimalist, but fully-featured music player. All three applications – iTunes, Music, and Swinsian – support a shared set of AppleScript hooks which the following script makes use of:

# select available player, preferring Swinsian over Music over iTunes
PLAYER="iTunes"
if [ -d "/System/Applications/Music.app" ]; then
    PLAYER="Music"
fi
if [ -d "/Applications/Swinsian.app" ]; then
    PLAYER="Swinsian"
fi

# do the things
if [ -z "$1" ]; then
    osascript -e "tell application \"$PLAYER\" to playpause"
elif [ "$1" = "?" ]; then
    osascript -e "tell application \"$PLAYER\" to get name of current track"
    printf "\033[90mby \033[0m"
    osascript -e "tell application \"$PLAYER\" to get artist of current track"
    printf "\033[90mon \033[0m"
    osascript -e "tell application \"$PLAYER\" to get album of current track"
elif [ "$1" = "prev" ]; then
    osascript -e "tell application \"$PLAYER\" to play previous track"
elif [ "$1" = "next" ]; then
    osascript -e "tell application \"$PLAYER\" to play next track"
else
    osascript -e "tell application \"$PLAYER\" to $1"
fi

As above, paste this code into an executable file – mine’s called it, short for iTunes. Once done, running the it command without any arguments will pause or resume playback, adding ? prints info about the currently-playing track, and prev and next do what you’d expect them to do. Anything else is passed directly to the player, enabling ad-hoc queries. (The first run will pop up a little window asking you to agree to your music player being controlled via AppleScript.)