#!/bin/bash set -eu # shellcheck disable=2034 description="audio/sound manager" export FUZL_PROMPT=audm export FUZL_ICON=󰟥 . libfuzl.sh main() { local sink source sink=$(inspect_node '@DEFAULT_SINK@' | awk -F\\t '{print $2}') fuzl_action_add 'change_sink' "󰓃" "$sink" source=$(inspect_node '@DEFAULT_SOURCE@' | awk -F\\t '{print $2}') fuzl_action_add 'change_source' "󰍰" "$source" fuzl_action_add 'change_profile' "󱟲" "Change profile" fuzl_action_add 'mixsource' "󱡫" "Mix source into sink" fuzl_action_menu || exit 1 } # # helpers # inspect_node() { local nid nid=$(wpctl inspect "${1:?}" | awk '/^id/{match($0,/[0-9]+,/); print substr($0,RSTART,RLENGTH-1);}') test -n "$nid" || exit 1 # TODO handle error pw-dump | jq -r --argjson nid "$nid" \ '.[]|select(.id==$nid)|.info.props|[.["object.id"],.["node.description"],.["node.name"]]|join("\t")' } list_nodes() { pw-dump | jq -rc --arg class "${1:?}" \ '.[]|select((.info.props["media.class"]//"")|test($class))|.info.props|[.["object.id"],.["node.description"],.["node.name"]]|join("\t")' } # # actions # audm_change_sink() { local id id=$(list_nodes 'Audio/Sink' | FUZL_PROMPT='sink' fuzl_menu $FUZL_MENU_FILTER --with-nth='{2} [{3}]' --accept-nth=1) test -n "$id" || main fuzl_try -- wpctl set-default "$id" } audm_change_source() { local id id=$(list_nodes 'Audio/Source' | FUZL_PROMPT='source' fuzl_menu $FUZL_MENU_FILTER --with-nth='{2} [{3}]' --accept-nth=1) test -n "$id" || main fuzl_try -- wpctl set-default "$id" } audm_change_profile() { local device did dname profile device=$(pw-dump | jq -rc --arg class "Audio/Device" \ '.[]|select((.info.props["media.class"]//"")|test($class))|.info.props|[.["object.id"],.["device.description"],.["device.nick"]]|join("\t")' | FUZL_PROMPT='device' fuzl_menu $FUZL_MENU_FILTER --with-nth='{2} {3}') test -n "$device" || main did=$(awk -F\\t '{print $1}' <<<"$device") dname=$(awk -F\\t '{print $2}' <<<"$device") profile=$(pw-dump | jq -r --argjson did "$did" \ '.[]|select(.id==$did)|.info.params.EnumProfile[]|[.index,.description]|join("\t")' | FUZL_PROMPT="profile ($dname)" fuzl_menu $FUZL_MENU_FILTER --with-nth=2 --accept-nth=1) test -n "$profile" || audm_change_profile fuzl_try -- wpctl set-profile "$did" "$profile" } audm_mixsource() { sink=$(inspect_node '@DEFAULT_SINK@' | awk -F\\t '{print $1}') sink_json=$(pw-dump -R "$sink" | jq -rc --argjson id "$sink" '.[]|select(.id==$id)') sink_node=$(jq -rc '.info.props["node.name"]' <<<$sink_json) sink_ports=$(jq -r '.info.params.PortConfig[]|select(.direction=="Input")|.format.position[]' <<<$sink_json) source=$(inspect_node '@DEFAULT_SOURCE@' | awk -F\\t '{print $1}') source_json=$(pw-dump -R "$source" | jq -rc --argjson id "$source" '.[]|select(.id==$id)') source_node=$(jq -rc '.info.props["node.name"]' <<<$source_json) source_ports=$(jq -rc '.info.params.PortConfig[]|select(.direction=="Output")|.format.position[]' <<<$source_json) for source_port in $source_ports; do for sink_port in $sink_ports; do ( pw-link --passive "$source_node":capture_"$source_port" "$sink_node":playback_"$sink_port" || pw-link --disconnect "$source_node":capture_"$source_port" "$sink_node":playback_"$sink_port" ) & done done } main