summary refs log tree commit diff
path: root/bin/soundswitch
diff options
context:
space:
mode:
Diffstat (limited to 'bin/soundswitch')
-rwxr-xr-xbin/soundswitch177
1 files changed, 128 insertions, 49 deletions
diff --git a/bin/soundswitch b/bin/soundswitch
index 2436ba4..e2aac39 100755
--- a/bin/soundswitch
+++ b/bin/soundswitch
@@ -1,20 +1,34 @@
 #!/bin/sh
+#
+# DESCRIPTION:
+#  dmenu-based pulseaudio helper
+#
+
 set -e
 [ -n "$DEBUG" ] && set -x
 
+# check requirements
+for tool in jq swq pactl perl pw-pulse; do
+	if ! command -v "$tool" >/dev/null; then
+		printf "%s: %s not installed but required\n" "$(basename "$0")" "$tool" >&2
+		exit 1
+	fi
+done
+
+
+
 DEFAULT_PROFILE="output:analog-stereo+input:analog-stereo"
 HDMI_PROFILE="output:hdmi-stereo"
 export MENU_PROMPT="soundswitch"
 
-global=
-focused=
-profile=
-hdmi=
-reset=
+mode=
 output=
 input=
+mute=
+volume_down=
+volume_up=
 usage() {
-	printf "usage: %s [-gfhr]\n" "$(basename "$0")"
+	printf "usage: %s [-hIOgfpHRmMvV]\n" "$(basename "$0")"
 	printf "\n"
 	printf "  -O \t(manipulate pulseaudio sinks)\n"
 	printf "  -I \t(manipulate pulseaudio sources)\n"
@@ -24,94 +38,147 @@ usage() {
 	printf "  -H \t(switch card to HDMI profile)\n"
 	printf "  -R \t(switch card to default profile)\n"
 	printf "\n"
+	printf "  -m \t(mute default sink)\n"
+	printf "  -M \t(mute default source)\n"
+	printf "  -v \t(reduce sink volume by 5%%)\n"
+	printf "  -V \t(increase sink volume by 5%%)\n"
+	printf "\n"
 	printf "Needs swq(1) to work.\n"
 	exit 2
 }
-while getopts OIfgpHRh name; do
+_args="$*"
+while getopts hIOgfpHRmMvV name; do
 	case ${name} in
-	g) 	global=1 ;;
-	f) 	focused=1 ;;
-	p) 	profile=1 ;;
-	H) 	hdmi=1 ;;
-	R)	reset=1 ;;
+	g) 	mode=global ;;
+	f) 	mode=focused ;;
+	p) 	mode=profile ;;
+	H) 	mode=hdmi ;;
+	R)	mode=reset ;;
 	O)	output=1 ;;
 	I)	input=1 ;;
-	h|?) 	usage ;;
+	# quick actions, to make move to pipewire easier
+	m)	mode=none ; output=1 ; mute=1 ;;
+	M)	mode=none ; input=1 ; mute=1 ;;
+	v)	mode=none ; output=1 ; volume_down=1 ;;
+	V)	mode=none ; output=1 ; volume_up=1 ;;
+	h|?) 	echo "$*" ; usage ;;
 	esac
 done
+shift $((OPTIND-1))
 
-if [ "$#" -lt 1 ]; then
+# ask mode
+if [ -z "$mode" ]; then
 	printf "global\nfocused\ncard profile\nhdmi profile\nreset profile\n" | menu | {
 		read -r line
 		case ${line} in
-		global) 	 exec "$0" -g ;;
-		focused) 	 exec "$0" -f ;;
-		"card profile")  exec "$0" -p ;;
-		"hdmi profile")	 exec "$0" -H ;;
-		"reset profile") exec "$0" -R ;;
-		*)		usage ;;
+		global)          exec "$0" -g "$*" ;;
+		focused)         exec "$0" -f "$*" ;;
+		"card profile")  exec "$0" -p "$*" ;;
+		"hdmi profile")  exec "$0" -H "$*" ;;
+		"reset profile") exec "$0" -R "$*" ;;
+		*)               usage ;;
 		esac
 	}
 	exit $?
 fi
-if [ -n "$global" ] || [ -n "$focused" ]; then
+
+# [ "$mode" = "global" ] && args="-g"
+# [ "$mode" = "focused" ] && args="-f"
+
+if [ -n "$mode" ]; then
+	# ask target?
 	if [ -z "$output" ] && [ -z "$input" ]; then
 		printf "output\ninput\n" | menu | {
 			read -r line
 			case ${line} in
-			output) 	exec "$0" -O "$*" ;;
-			input) 		exec "$0" -I "$*" ;;
-			*)		usage ;;
+			output) exec "$0" -O $_args ;;
+			input)  exec "$0" -I $_args ;;
+			*)      usage ;;
 			esac
 		}
-		exit $?
 	fi
 fi
 
-[ -n "$output" ] && keyword="sink" && what="sink-input"
-[ -n "$input" ] && keyword="source" && what="source-output"
+# [ -n "$output" ] && args="-O $args"
+# [ -n "$input" ] && args="-I $args"
+
+# if ! pactl info >&2 2>/dev/null; then
+# 	# try to work with pipewire
+# 	exec pw-pulse "$0" $_args
+# fi
+
+# process quick actions first
+[ -n "$output" ] && [ -n "$mute" ] && exec pactl set-sink-mute '@DEFAULT_SINK@' toggle
+[ -n "$input" ] && [ -n "$mute" ] && exec pactl set-source-mute '@DEFAULT_SINK@' toggle
+[ -n "$volume_down" ] && exec pactl set-sink-volume '@DEFAULT_SINK@' '-5%'
+[ -n "$volume_up" ] && exec pactl set-sink-volume '@DEFAULT_SINK@' '+5%'
+
+[ -n "$output" ] && keyword="sink"   && what="sink-input"
+[ -n "$input" ]  && keyword="source" && what="source-output"
 
 # returns the sink_id
 selects()
 {
 	pactl list short "${keyword}s" | awk '{print $2}' | menu
 }
+notif()
+{
+	topic=$1
+	mesg=$2
+	urgency=normal
+	[ -n "$3" ] && urgency=critical
+	notify-send -u ${urgency} -a soundswitch "soundswitch: ${topic}" "${mesg}"
+}
 
-if [ -n "${global}" ]
-then
+case ${mode} in
+global)
 	target=$(selects)
-	notify-send -a soundswitch "soundswitch: global" "switch to <i>${target}</i>"
+	if [ -z "${target}" ]; then
+		notif "focused" "no target ${what}" err
+		exit 1
+	fi
+
+	notif "global" "switch to <i>${target}</i>"
 	# set new default
 	pactl "set-default-${keyword}" "${target}"
 	# move all attached to the new target
 	pactl list "${what}s" short | cut -f1 \
 		| xargs -i pactl "move-${what}" "{}" "${target}"
+	;;
 
-elif [ -n "${focused}" ]
-then
+focused)
 	# parse pid of currently focused container
 	# then get the sink-inputs for it and parse the sink-input-id
 	focused_binary=$(swq current con | jq -r '.pid' | xargs ps -o comm=)
 	what_id=$(printf "%s\n" "${focused_binary}" \
-		| xargs pacmd "list-${what}s" \
+		| xargs pactl list "${what}s" \
 			| tr '\n' '\r' \
 			| perl -pe 's/[^\r]*index: ([0-9]+).+?application\.process\.binary = "([^\r]+)"\r.+?(?=index:|$)/\2=\1\r/g' \
 			| tr '\r' '\n' \
 			| grep "^{}" \
 		| cut -d'=' -f2)
-	[ -z "${what_id}" ] && exit 1
+	if [ -z "${what_id}" ]; then
+		notif "focused" "no ${what}" err
+		exit 1
+	fi
 
 	# select where to switch to
 	target=$(selects)
-	[ -z "${what_id}" ] && exit 1
+	if [ -z "${target}" ]; then
+		exit 1
+	fi
 
-	notify-send -a soundswitch "soundswitch: ${focused_binary}" "switch to <i>${target}</i>"
-        pacmd move-sink-input "${what_id}" "${target}"
+	notif "${focused_binary}" "switch to <i>${target}</i>"
+        pactl move-sink-input "${what_id}" "${target}"
+	;;
 
-elif [ -n "${profile}" ]
-then
+profile)
 	target_card=$(pactl list short cards | awk '{print $2}' | menu)
-	target_profile=$(pacmd list-cards \
+	if [ -z "${target_card}" ]; then
+		exit 1
+	fi
+
+	target_profile=$(pactl list cards \
 		| tr '\n' '\r' \
 		| perl -pe 's/[^\r]index: ([0-9]+).+?name: <alsa_card.usb-Burr-Brown_from_TI_USB_Audio_CODEC-00>\r.+profiles:\r(.+)sinks:.*?(?=index:|$)/\2\r/g' \
 		| tr -d '\t' \
@@ -119,21 +186,33 @@ then
 		| tail -n+2 \
 		| menu \
 		| perl -pe 's/^\s*([^\s]+):\s.*$/\1/')
+	if [ -z "${target_profile}" ]; then
+		exit 1
+	fi
 
-	notify-send -a soundswitch "soundswitch: profile" "switch <i>${target_card}</i> to ${target_profile} profile"
-	pactl set-card-profile "${target_card}" ${target_profile}
+	notif "profile" "switch <i>${target_card}</i> to ${target_profile} profile"
+	pactl set-card-profile "${target_card}" "${target_profile}"
+	;;
 
-elif [ -n "${hdmi}" ]
-then
+hdmi)
 	target_card=$(pactl list short cards | awk '{print $2}' | menu)
-	notify-send -a soundswitch "soundswitch: hdmi" "switch <i>${target_card}</i> to HDMI profile"
+	if [ -z "${target_card}" ]; then
+		exit 1
+	fi
+
+	notif "hdmi" "switch <i>${target_card}</i> to HDMI profile"
 	pactl set-card-profile "${target_card}" ${HDMI_PROFILE}
 	exit $?
+	;;
 
-elif [ -n "${reset}" ]
-then
+reset)
 	target_card=$(pactl list short cards | awk '{print $2}' | menu)
-	notify-send -a soundswitch "soundswitch: reset" "switch <i>${target_card}</i> to default profile"
+	if [ -z "${target_card}" ]; then
+		exit 1
+	fi
+
+	notif "reset" "switch <i>${target_card}</i> to default profile"
 	pactl set-card-profile "${target_card}" ${DEFAULT_PROFILE}
+	;;
 
-fi
+esac