#!/usr/bin/env bash set -eu state_muted="${XDG_RUNTIME_DIR}/state_muted" state_muted_headphone="" state_muted_speaker="" state_bt_headphone="" name_bt_headphone="bluez_sink.00_1B_66_02_36_41" cardselector="" card_icon="/usr/share/icons/gnome/48x48/devices/audio-speakers.png" # gnome-icon-theme mute_state_on="[on]" mute_state_off="[off]" get_mute_state() { local name="$1" amixer -M -c "${cardselector}" sget "${name}" |grep -o '\[o[n|f]*\]' |head -n1 } get_volume_percentage() { local name="$1" amixer -M -c "${cardselector}" sget "${name}" |grep -o '\[[0-9]*%*\]' |head -n1 |sed -e 's/\[//;s/\]//' } get_volume_db() { local name="$1" amixer -M -c "${cardselector}" sget "${name}" |grep -Eo '\[[-]?[0-9]+\.[0-9]+dB\]' |head -n1 |sed -e 's/\[//;s/\]//' } is_channel_available() { local name="$1" amixer -M -c "${cardselector}" sget "${name}" > /dev/null 2>&1 } increase_volume() { amixer -M -c "${cardselector}" sset Master 5%+ &>/dev/null local state_master="" state_master=$(get_volume_percentage 'Master') #update pactl bluetooth audio to same level if present if [ "$state_bt_headphone" = "[on]" ]; then pactl set-sink-volume $name_bt_headphone "$state_master%" fi print_volumes } decrease_volume() { amixer -M -c "${cardselector}" sset Master 5%- > /dev/null local state_master="" state_master=$(get_volume_percentage 'Master') #update pactl bluetooth audio to same level if present if [ "$state_bt_headphone" = "[on]" ];then pactl set-sink-volume $name_bt_headphone "$state_master%" fi print_volumes } print_volumes() { local message=() if is_channel_available 'Master'; then message+=("🎚 $(get_volume_percentage 'Master') ($(get_volume_db 'Master'))") fi if is_channel_available 'Headphone'; then message+=("🎧 $(get_volume_percentage 'Headphone') ($(get_volume_db 'Headphone'))") fi if is_channel_available 'Speaker'; then message+=("🔈 $(get_volume_percentage 'Speaker') ($(get_volume_db 'Speaker'))") fi send_notify "${message[*]}" } mute_state_to_icon() { local state="$1" local icon="" if [[ "$state" == "$mute_state_on" ]]; then icon="🔊" fi if [[ "$state" == "$mute_state_off" ]]; then icon="🔇" fi echo "$icon" } print_mute_states() { local message=() if is_channel_available 'Master'; then message+=("🎚 [$(mute_state_to_icon "$(get_mute_state 'Master')")] $(get_volume_percentage 'Master') ($(get_volume_db 'Master'))") fi if is_channel_available 'Headphone'; then message+=("🎧 [$(mute_state_to_icon "$(get_mute_state 'Headphone')")] $(get_volume_percentage 'Headphone') ($(get_volume_db 'Headphone'))") fi if is_channel_available 'Speaker'; then message+=("🔈 [$(mute_state_to_icon "$(get_mute_state 'Speaker')")] $(get_volume_percentage 'Speaker') ($(get_volume_db 'Speaker'))") fi send_notify "${message[*]}" } toggle_volume() { local state_master="" local state_headphone="" local state_speaker="" if is_channel_available 'Master'; then state_master=$(get_mute_state 'Master') fi if is_channel_available 'Headphone'; then state_headphone=$(get_mute_state 'Headphone') fi if is_channel_available 'Speaker'; then state_speaker=$(get_mute_state 'Speaker') fi case "$state_master" in "${mute_state_on}") amixer -M -c "${cardselector}" sset Master mute &>/dev/null state_master="[off]" # if headphones are off already, don't mute, instead save state to tmp file if [ "$state_headphone" = "[off]" ];then echo "headphone [off]" >> "$state_muted" else amixer -M -c "${cardselector}" sset Headphone mute &>/dev/null state_headphone="[off]" fi # if speakers are off already, don't mute, instead save state to tmp file if [ "$state_speaker" = "[off]" ];then echo "speaker [off]" >> "$state_muted" else amixer -M -c "${cardselector}" sset Speaker mute &>/dev/null state_speaker="[off]" fi # if present, also mute bluetooth headphone if [ "$state_bt_headphone" = "[on]" ]; then pactl set-sink-mute $name_bt_headphone 1 fi ;; "${mute_state_off}") amixer -M -c "${cardselector}" sset Master unmute &>/dev/null state_master="[on]" # if headphones are meant to be off, don't unmute them again if [ "$state_muted_headphone" != "[off]" ];then amixer -M -c "${cardselector}" sset Headphone unmute &>/dev/null state_headphone="[on]" fi # if headphones are meant to be off, don't unmute them again if [ "$state_muted_speaker" != "[off]" ];then amixer -M -c "${cardselector}" sset Speaker unmute &>/dev/null state_speaker="[on]" fi # if present, also unmute bluetooth headphone if [ "$state_bt_headphone" = "[on]" ]; then pactl set-sink-mute $name_bt_headphone 0 fi ;; esac print_mute_states } send_notify() { local timeout=1000 notify-send -t $timeout -i "${card_icon}" "Volume" "$1" } if [[ "$HOSTNAME" == "dvzrv" ]]; then cardselector="1" if [ -n "$(pactl list sinks short | grep $name_bt_headphone)" ]; then state_bt_headphone="[on]" fi #TODO: also check pactl for JACK sink fi if [[ "$HOSTNAME" == "hmbx" ]]; then cardselector="1" fi if pgrep jack > /dev/null; then echo "JACK is running" fi case "$1" in "increase" | "up" | "+" ) increase_volume ;; "decrease" | "down" | "-" ) decrease_volume ;; "toggle") if [ -f "$state_muted" ]; then set +e state_muted_speaker="$(cat "$state_muted" | grep speaker | cut -d' ' -f2)" state_muted_headphone="$(cat "$state_muted" | grep headphone | cut -d' ' -f2)" set -e rm "$state_muted" else touch "$state_muted" fi toggle_volume ;; *) ;; esac exit 0