Solution 1:

Here's my take on your interesting challenge:

#!/bin/bash

main() {
    local action=mute
    while getopts :hu option; do 
        case "$option" in 
            h) usage 0 ;;
            u) action=unmute ;;
            ?) usage 1 "invalid option: -$OPTARG" ;;
        esac
    done
    shift $((OPTIND - 1))

    if [[ "$1" ]]; then
        $action "$1"
    else
        usage 1 "specify an application name" 
    fi
}

usage() {
    [[ "$2" ]] && echo "error: $2"
    echo "usage: $0 [-h] [-u] appname"
    echo "where: -u = ummute application (default action is to mute)"
    exit $1
}

mute()   { adjust_muteness "$1" 1; }
unmute() { adjust_muteness "$1" 0; }

adjust_muteness() { 
    local index=$(get_index "$1")
    [[ "$index" ]] && pacmd set-sink-input-mute "$index" $2 >/dev/null 
}

get_index() {
    local pid=$(pidof "$1")
    if [[ -z "$pid" ]]; then
        echo "error: no running processes for: $1" >&2
    else
        pacmd list-sink-inputs | 
        awk -v pid=$pid '
            $1 == "index:" {idx = $2} 
            $1 == "application.process.id" && $3 == "\"" pid "\"" {print idx; exit}
        '
    fi
}

main "$@"

Solution 2:

thanks for the solution! I managed to use the scripts provided here to fix my problem. Since I had to modify them a bit, here I join the improved version.

The reason the original scripts didn't work for me is because some applications can have several instances, i.e. several PID, but maybe only one of them is producing sound, and is thus actually connected to Pulseaudio. Since the script only used the first PID found, it would typically /not/ mute the desired application.

So here is a version where the argument is the application name as registered in PulseAudio. You can find this name by running the pacmd list-sink-inputs command and look for application.name field.

An alternative solution would be to mute/unmute all PIDs which have the same application name.

#!/bin/bash

# Adapter from glenn jackman on http://askubuntu.com/questions/180612/script-to-mute-an-application
# to depend directly on the name of the PulseAudio client
# rather than the application name (several instances of one application could
# run while only one is connected to PulseAudio)

# Possible further improvement: it could be useful to also mute all clients having
# the specified name. Here, only the first one is muted.

#!/bin/bash

main() {
    local action=mute
    while getopts :hu option; do
        case "$option" in
            h) usage 0 ;;
            u) action=unmute ;;
            ?) usage 1 "invalid option: -$OPTARG" ;;
        esac
    done
    shift $((OPTIND - 1))

    if [[ "$1" ]]; then
        $action "$1"
    else
        usage 1 "specify the name of a PulseAudio client"
    fi
}

usage() {
    [[ "$2" ]] && echo "error: $2"
    echo "usage: $0 [-h] [-u] appname"
    echo "where: -u = ummute application (default action is to mute)"
    exit $1
}

mute()   { adjust_muteness "$1" 1; }
unmute() { adjust_muteness "$1" 0; }

adjust_muteness() {
    local index=$(get_index "$1")
    if [[ -z "$index" ]]; then
        echo "error: no PulseAudio sink named $1 was found" >&2
    else
        [[ "$index" ]] && pacmd set-sink-input-mute "$index" $2 >/dev/null
    fi
}

get_index() {
#    local pid=$(pidof "$1")
#    if [[ -z "$pid" ]]; then
#        echo "error: no running processes for: $1" >&2
#    else
        pacmd list-sink-inputs |
        awk -v name=$1 '
            $1 == "index:" {idx = $2}
            $1 == "application.name" && $3 == "\"" name "\"" {print idx; exit}
        '
#    fi
}

main "$@"

Solution 3:

Even though the question is asking for a script, I wanted to leave this here.

I have written a C application that does this on Ubuntu. Even better, it sits on the indicator tray (using libappindicator) and checks what Spotify is playing, on short intervals. If it is playing an ad (checks against a blacklist) it mutes the Spotify. If a new ad is being played, you simply click Mute on the indicator menu and it adds it to the blacklist.

What it does, is looks for an X window, for which XFetchName returns Spotify - Linux Preview. Then it calls XGetWindowProperty to query the _NET_WM_ICON_NAME property of that window, which returns a string in the "Spotify – <Artist> – <Song>" format. When playing ads, it returns something like this:

"Spotify – Spotify – Premium Free Trial Cancel Any Time"

It maintains a Ternary Search Tree of the list of ads, to efficiently check whether the current title is in the list.

It also uses the PulseAudio Asynchronous API to query the sink-inputs and set-mute:

pa_context_get_sink_input_info_list()
pa_context_set_sink_input_mute()

Since it is just simple C code, it is light-weight. Check out the source code and Ubuntu .deb package at: indicator-muteads. It would probably beat a shell script by 2-3 orders of magnitude.