summary refs log tree commit diff
path: root/bin/audm
blob: 33abb3407136d6e077928f23e52d63488dc59438 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#!/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