diff options
| author | Robert Günzler <r@gnzler.io> | 2022-08-17 15:40:18 +0200 |
|---|---|---|
| committer | Robert Günzler <r@gnzler.io> | 2022-08-17 15:40:18 +0200 |
| commit | c6a67807ff922e43a1eebc4d28d24399a7841e86 (patch) | |
| tree | 09e224b626d8ce05719d70d06b875fcd8dc6c161 | |
| parent | 0005a90a76954003d975ee9ff82b7e10c176f91d (diff) | |
bin/soundswitch: buy into pipewire more
disable a bunch of stuff, see if we really need it
Diffstat (limited to '')
| -rwxr-xr-x | bin/soundswitch | 379 |
1 files changed, 234 insertions, 145 deletions
diff --git a/bin/soundswitch b/bin/soundswitch index e39a292..78021b6 100755 --- a/bin/soundswitch +++ b/bin/soundswitch @@ -19,11 +19,13 @@ for tool in jq swq pactl perl pw-dump; do done DEFAULT_PROFILE="output:analog-stereo+input:analog-stereo" +# DEFAULT_PROFILE="pro-audio" HDMI_PROFILE="output:hdmi-stereo" _menu() { - export MENU_PROMPT="soundswitch" - menu -l 8 + [ -n "${mode}" ] && prompt="soundswitch (${mode})" || prompt="soundswitch" + export MENU_PROMPT=${MENU_PROMPT:-${prompt}} + menu -l10 --ifne --autoselect } mode= @@ -52,41 +54,42 @@ usage() { exit 2 } _args="$*" -while getopts hIOgfpHRmMvVB name; do +while getopts hIOgfpHRmMvVBj name; do case ${name} in - g) mode=global ;; - f) mode=focused ;; - p) mode=profile output=1 input=1 ;; - H) mode=hdmi output=1 input=1 ;; - R) mode=reset ;; - B) mode=mute output=1 input=1 ;; - O) output=1 ;; - I) input=1 ;; - # 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 - ;; + j) mode=json ;; + g) mode=global ;; + f) mode=focused ;; + p) mode=profile output=1 input=1 ;; + H) mode=hdmi output=1 input=1 ;; + R) mode=reset ;; + B) mode=mute ;; + O) output=1 ;; + I) input=1 ;; + # 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)) @@ -102,15 +105,15 @@ if [ -z "$mode" ]; then printf "$_mode_global\n$_mode_focused\n$_mode_profile_card\n$_mode_profile_hdmi\n$_mode_profile_reset\n$_mode_mute\n" | _menu | { read -r line case ${line} in - $_mode_global) exec "$0" -g "$*" ;; - $_mode_focused) exec "$0" -f "$*" ;; - $_mode_profile_card) exec "$0" -p "$*" ;; - $_mode_profile_hdmi) exec "$0" -H "$*" ;; - $_mode_profile_reset) exec "$0" -R "$*" ;; - $_mode_mute) exec "$0" -B "$*" ;; - *) - usage - ;; + "$_mode_global") exec "$0" -g "$*" ;; + "$_mode_focused") exec "$0" -f "$*" ;; + "$_mode_profile_card") exec "$0" -p "$*" ;; + "$_mode_profile_hdmi") exec "$0" -H "$*" ;; + "$_mode_profile_reset") exec "$0" -R "$*" ;; + "$_mode_mute") exec "$0" -B "$*" ;; + *) + usage + ;; esac } exit $? @@ -124,11 +127,12 @@ if [ -n "$mode" ]; then printf "$_mode_output\n$_mode_input\n" | _menu | { read -r line case ${line} in - $_mode_output) exec "$0" -O $_args ;; - $_mode_input) exec "$0" -I $_args ;; - *) usage ;; + "$_mode_output") exec "$0" -O $_args ;; + "$_mode_input") exec "$0" -I $_args ;; + *) usage ;; esac } + exit $? fi fi @@ -142,8 +146,53 @@ fi [ -n "$input" ] && keyword="source" && what="source-output" # returns the sink_id +default() { + key=$1 + case "$1" in + sink) key="audio.default.sink" ;; + source) key="audio.default.source" ;; + esac + pw-dump | jq -r --arg key "$key" '.[]|select(.type=="PipeWire:Interface:Metadata")|.metadata[]|select(.key==$key)|.value.name' +} + +# __identifier: compacted json containing index and description +# e.g. {"i":60,"d":"Built-in Audio Analog Stereo"} +__identifier_list() { + if [ "$1" = "card" ]; then + pactl --format=json list "${1}s" | jq -c '.[]|{i:.index,d:.properties["api.alsa.card.name"]}' + else + pactl --format=json list "${1}s" | jq -c '.[]|{i:.index,d:.description}' + fi +} +__identifier_index() { + if [ -z "$1" ]; then + jq -r '.i' + else + printf "%s" "$1" | __identifier_index + fi +} +__identifier_desc() { + if [ -z "$1" ]; then + jq -r '.d' + else + printf "%s" "$1" | __identifier_desc + fi +} +__identifier_fmt() { + jq -r '[.i,.d]|@csv' | tr -d '"' | column -s ',' -t +} +__identifier_fmt_parse() { + fmt=$(cat) + i=$(printf "%s" "$fmt" | tr -s ' ' | cut -d' ' -f1) + d=$(printf "%s" "$fmt" | tr -s ' ' | cut -d' ' -f2-) + jq -cn --arg i "$i" --arg d "$d" '{i:$i,d:$d}' +} + selects() { - pactl list short "${1}s" | cut -f2 | _menu + __identifier_list "$1" | + __identifier_fmt | + _menu | + __identifier_fmt_parse } notif() { topic=$1 @@ -153,108 +202,148 @@ notif() { notify-send -i /usr/share/icons/mdi/scalable/cast-audio.svg -u ${urgency} -a soundswitch "soundswitch: ${topic}" "${mesg}" } -case ${mode} in - global) - target=$(selects "${keyword}") - 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}" - ;; +case "${mode}" in +json) + selects "${keyword}" + ;; +global) + target=$(selects "${keyword}") + if [ -z "${target}" ]; then + notif "focused" "no target ${what}" error + exit 1 + fi - 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 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) - if [ -z "${what_id}" ]; then - notif "focused" "no ${what}" err - exit 1 - fi - - # select where to switch to - target=$(selects "${keyword}") - if [ -z "${target}" ]; then - exit 1 - fi - - notif "${focused_binary}" "switch to <i>${target}</i>" - pactl move-sink-input "${what_id}" "${target}" - ;; + notif "global" "switch to <i>$(__identifier_desc "${target}")</i>" + # set new default + pactl "set-default-${keyword}" "$(__identifier_index "${target}")" + # move all attached to the new target + pactl list "${what}s" short | cut -f1 | + xargs -i pactl "move-${what}" "{}" "$(__identifier_index "${target}")" + exit $? + ;; - profile) - target_card=$(selects "card") - if [ -z "${target_card}" ]; then - exit 1 - fi - - # [^\r]#([0-9]+).*?Name: ([^\r]+).*?Profiles:\r(.*?)Active.*?(?=Card #|$) - target_profile=$(pactl list cards \ - | tr '\n' '\r' \ - | perl -pe 's/[^\r]#([0-9]+).*?Name: ([^\r]+).*?Profiles:\r(.*?)Active.*?(?=Card #|$)/\2~\3`/g' \ - | tr -d '\t' \ - | tr '`' '\n' \ - | grep "${target_card}" \ - | cut -d'~' -f2- \ - | tr '\r' '\n' \ - | xargs -L1 \ - | _menu \ - | perl -pe 's/^\s*([^\s]+):\s.*$/\1/') - if [ -z "${target_profile}" ]; then - exit 1 - fi - - notif "profile" "switch <i>${target_card}</i> to ${target_profile} profile" - pactl set-card-profile "${target_card}" "${target_profile}" - ;; +focused) + # parse pid of currently focused container + # then get the sink-inputs for it and parse the sink-input-id + notif "not implemented, do we need it?" error + exit 1 - hdmi) - target_card=$(selects "card") - if [ -z "${target_card}" ]; then - exit 1 - fi + focused_binary=$(swq current con | jq -r '.pid' | xargs ps -o comm=) + what_id=$(printf "%s\n" "${focused_binary}" | + 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) + if [ -z "${what_id}" ]; then + notif "focused" "no ${what}" error + exit 1 + fi - notif "hdmi" "switch <i>${target_card}</i> to HDMI profile" - pactl set-card-profile "${target_card}" ${HDMI_PROFILE} - exit $? - ;; + # select where to switch to + target=$(selects "${keyword}") + if [ -z "${target}" ]; then + exit 1 + fi - reset) - target_card=$(selects "card") - if [ -z "${target_card}" ]; then - exit 1 - fi + notif "${focused_binary}" "switch to <i>${target}</i>" + pactl move-sink-input "${what_id}" "${target}" + exit $? + ;; - notif "reset" "switch <i>${target_card}</i> to default profile" - pactl set-card-profile "${target_card}" ${DEFAULT_PROFILE} - ;; +profile) + notif "not implemented, do we need it?" "" error + exit 1 - mute) - _data=$(pw-dump -N | jq -r '.. | select(.type?=="PipeWire:Interface:Node" and .info.props."media.class"?=="Stream/Output/Audio" and .info.props."stream.is-live"?==true) | @text') - _id=$(printf "${_data}" | jq -r '. | [ .info.props."application.name", .info.props."application.process.id", .id ] | @tsv' \ - | awk '{ system("swaymsg -t get_tree | jq -r \".. | select(.pid?==" $2 ") | .name\" | grep -v Picture-in-Picture | tr -d \"\\n\""); printf " [%s]", $3 }' \ - | _menu \ - | perl -pe 's/^.*\[([0-9]+)\]$/\1/') - if [ -z "${_id}" ]; then - exit 1 - fi - - _oid=$(printf "${_data}" | jq -r --argjson id "${_id}" '.. | select(.id?==$id) | .info.props."object.id"') - _target_state=$(printf "${_data}" | jq -r --argjson id "${_id}" '.. | select(.id?==$id) | .info.params.Props[0] | .mute = (.mute | not) | @text') - pw-cli set-param "${_oid}" Props "${_target_state}" - ;; + target_card=$(selects "card") + if [ -z "${target_card}" ]; then + exit 1 + fi + + # [^\r]#([0-9]+).*?Name: ([^\r]+).*?Profiles:\r(.*?)Active.*?(?=Card #|$) + # target_profile=$(pactl list cards | + # tr '\n' '\r' | + # perl -pe 's/[^\r]#([0-9]+).*?Name: ([^\r]+).*?Profiles:\r(.*?)Active.*?(?=Card #|$)/\2~\3`/g' | + # tr -d '\t' | + # tr '`' '\n' | + # grep "${target_card}" | + # cut -d'~' -f2- | + # tr '\r' '\n' | + # xargs -L1 | + # _menu | + # perl -pe 's/^\s*([^\s]+):\s.*$/\1/') + # if [ -z "${target_profile}" ]; then + # exit 1 + # fi + # + # notif "profile" "switch <i>${target_card}</i> to ${target_profile} profile" + # pactl set-card-profile "${target_card}" "${target_profile}" + # exit $? + ;; + +hdmi) + notif "not implemented, do we need it?" "" error + exit 1 + + target_card=$(selects "card") + if [ -z "${target_card}" ]; then + exit 1 + fi + + target_profile=$(pactl list cards | + tr '\n' '\r' | + perl -pe 's/[^\r]#([0-9]+).*?Name: ([^\r]+).*?Profiles:\r(.*?)Active.*?(?=Card #|$)/\2~\3`/g' | + tr -d '\t' | + tr '`' '\n' | + grep "${target_card}" | + cut -d'~' -f2- | + tr '\r' '\n' | + grep -i "hdmi" | + grep -i -v "input" | + xargs -L1 | + _menu | + perl -pe 's/^\s*([^\s]+):\s.*$/\1/') + if [ -z "${target_profile}" ]; then + target_profile=${HDMI_PROFILE} + fi + + notif "hdmi" "switch <i>${target_card}</i> to HDMI profile\n<i>${target_profile}</i>" + pactl set-card-profile "${target_card}" "${target_profile}" + exit $? + ;; + +reset) + notif "not implemented, do we need it?" "" error + exit 1 + + target_card=$(selects "card") + 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} + exit $? + ;; + +mute) + notif "not implemented, do we need it?" "" error + exit 1 + + _data=$(pw-dump -N | jq -r '.. | select(.type?=="PipeWire:Interface:Node" and .info.props."media.class"?=="Stream/Output/Audio" and .info.props."stream.is-live"?==true) | @text') + _id=$(printf "${_data}" | jq -r '. | [ .info.props."application.name", .info.props."application.process.id", .id ] | @tsv' | + awk '{ system("swaymsg -t get_tree | jq -r \".. | select(.pid?==" $2 ") | .name\" | grep -v Picture-in-Picture | tr -d \"\\n\""); printf " [%s]", $3 }' | + _menu | + perl -pe 's/^.*\[([0-9]+)\]$/\1/') + if [ -z "${_id}" ]; then + exit 1 + fi + + _oid=$(printf "${_data}" | jq -r --argjson id "${_id}" '.. | select(.id?==$id) | .info.props."object.id"') + _target_state=$(printf "${_data}" | jq -r --argjson id "${_id}" '.. | select(.id?==$id) | .info.params.Props[0] | .mute = (.mute | not) | @text') + pw-cli set-param "${_oid}" Props "${_target_state}" + exit $? + ;; esac |