Skip to content

Commit 900b573

Browse files
authored
feat: add pipewire support for volumecontrol.sh (#1316)
# Pull Request ## Description This feature allow to manage volume using native pipewire utilities, if audioserver is running by PipeWire. If volume is managed throug Pulse Audio when the actual audio server is pipewire this leads to audio stream is recreated every time any parameter is changed: - muted - unmuted - volume changes This behaviour leads to some inconvenience: - Any equalizer/filter/etc applied to physical sink is discarded when stream is recreated - Waybar crashes on eq discard for some reason - Virtual sink should be used as workaround Managing volume natively by pipewire utils solves all mentioned problems. ## Type of change - [ ] **Bug fix** (non-breaking change which fixes an issue) - [x] **New feature** (non-breaking change which adds functionality) - [ ] **Breaking change** (fix or feature that would cause existing functionality to not work as expected) - [ ] **Documentation update** (non-breaking change; modified files are limited to the documentations) - [ ] **Technical debt** (a code change that does not fix a bug or add a feature but makes something clearer for devs) - [ ] **Other** (provide details below) ## Checklist - [x] I have read the [CONTRIBUTING](https://github.com/HyDE-Project/HyDE/blob/master/CONTRIBUTING.md) document. - [x] My code follows the code style of this project. - [x] My commit message follows the [commit guidelines](https://github.com/HyDE-Project/HyDE/blob/master/COMMIT_MESSAGE_GUIDELINES.md). - [ ] My change requires a change to the documentation. - [ ] I have updated the documentation accordingly. - [ ] I have added a changelog entry. - [ ] I have added necessary comments/documentation to my code. - [ ] I have added tests to cover my changes. - [x] I have tested my code locally and it works as expected. - [ ] All new and existing tests passed. ## Additional context > [!WARNING] > I have tested it only on my PC and my laptop, which are both AMD-only and have pretty similar audio configs. I will appreciate if someone can test this on different configurations
2 parents 94e7383 + 0cd3484 commit 900b573

File tree

2 files changed

+77
-17
lines changed

2 files changed

+77
-17
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
88

99
### Fixed
1010
- Hyprland: Fix errors when `HYPRLAND_CONFIG` is not set yet
11-
- Fish: Pleas Move you configs to `~/.config/fish/conf.d`
11+
- Fish: Please Move you configs to `~/.config/fish/conf.d`
1212

1313

1414
### Added
@@ -17,7 +17,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
1717
- QT5CT: Added explicit font configuration for QT5 Applications see [#1309](https://github.com/HyDE-Project/HyDE/issues/1309)
1818
- GTK3: Added explicit font configuration for GTK3 Applications see [#1309](https://github.com/HyDE-Project/HyDE/issues/1309)
1919

20+
### Changed
2021

22+
- Audio volume control: use `wpctl` instead of `pamixer` for managing audio volume when PipeWire server is running.
2123

2224
## v25.9.3
2325

Configs/.local/lib/hyde/volumecontrol.sh

Lines changed: 74 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -79,14 +79,26 @@ change_volume() {
7979
[ "${srce}" = "--default-source" ] && mode="--input-volume"
8080
case $device in
8181
"pamixer")
82-
if [ "${isVolumeBoost}" = true ]; then
83-
$use_swayosd && swayosd-client ${mode} "${delta}${step}" --max-volume "${VOLUME_BOOST_LIMIT:-150}" && exit 0
84-
pamixer "$srce" "${allow_boost:-}" --allow-boost --set-limit "${VOLUME_BOOST_LIMIT:-150}" -"${action}" "$step"
82+
if [[ "${use_pipewire}" == true ]]; then
83+
[ "${srce}" = "--default-source" ] && srce="@DEFAULT_AUDIO_SOURCE@"
84+
[ "${srce}" = "" ] && srce="@DEFAULT_AUDIO_SINK@"
85+
if [ "${isVolumeBoost}" = true ]; then
86+
$use_swayosd && swayosd-client ${mode} "${delta}${step}" --max-volume "${VOLUME_BOOST_LIMIT:-150}" && exit 0
87+
else
88+
$use_swayosd && swayosd-client ${mode} "${delta}${step}" && exit 0
89+
fi
90+
wpctl set-volume "${srce}" "${step}%${delta}"
91+
vol=$(wpctl get-volume "${srce}" | awk '{print $2 * 100}')
8592
else
86-
$use_swayosd && swayosd-client ${mode} "${delta}${step}" && exit 0
87-
pamixer "$srce" -"${action}" "$step"
93+
if [ "${isVolumeBoost}" = true ]; then
94+
$use_swayosd && swayosd-client ${mode} "${delta}${step}" --max-volume "${VOLUME_BOOST_LIMIT:-150}" && exit 0
95+
pamixer "$srce" "${allow_boost:-}" --allow-boost --set-limit "${VOLUME_BOOST_LIMIT:-150}" -"${action}" "$step"
96+
else
97+
$use_swayosd && swayosd-client ${mode} "${delta}${step}" && exit 0
98+
pamixer "$srce" -"${action}" "$step"
99+
fi
100+
vol=$(pamixer "$srce" --get-volume)
88101
fi
89-
vol=$(pamixer "$srce" --get-volume)
90102
;;
91103
"playerctl")
92104
playerctl --player="$srce" volume "$(awk -v step="$step" 'BEGIN {print step/100}')${delta}"
@@ -104,7 +116,13 @@ toggle_mute() {
104116
case $device in
105117
"pamixer")
106118
$use_swayosd && swayosd-client "${mode}" mute-toggle && exit 0
107-
pamixer "$srce" -t
119+
if [[ "${use_pipewire}" == true ]]; then
120+
[ "${srce}" = "--default-source" ] && srce="@DEFAULT_AUDIO_SOURCE@"
121+
[ "${srce}" = "" ] && srce="@DEFAULT_AUDIO_SINK@"
122+
wpctl set-mute "${srce}" toggle
123+
else
124+
pamixer "$srce" -t
125+
fi
108126
notify_mute
109127
;;
110128
"playerctl")
@@ -128,22 +146,55 @@ toggle_mute() {
128146

129147
select_output() {
130148
local selection=$1
131-
if [ -n "$selection" ]; then
132-
device=$(pactl list sinks | grep -C2 -F "Description: $selection" | grep Name | cut -d: -f2 | xargs)
133-
if pactl set-default-sink "$device"; then
134-
notify-send -t 2000 -i "${icodir}/unmuted-speaker.svg" -r 8 -u low "Activated: $selection"
149+
if [[ "${use_pipewire}" == true ]]; then
150+
if [ -n "$selection" ]; then
151+
device=$(pw-dump | sel=${selection} jq -r '.[] | select(.info?.props?."media.class" == "Audio/Sink" and .info?.props?."node.description" == env.sel) | .info?.props?."object.id"' | xargs)
152+
if wpctl set-default "$device"; then
153+
notify-send -t 2000 -i "${icodir}/unmuted-speaker.svg" -r 8 -u low "Activated: $selection"
154+
else
155+
notify-send -t 2000 -r 8 -u critical "Error activating $selection"
156+
fi
135157
else
136-
notify-send -t 2000 -r 8 -u critical "Error activating $selection"
158+
pw-dump | jq -r '.[] | select(.info?.props?."media.class" == "Audio/Sink") | .info?.props?."node.description"' | sort
137159
fi
138160
else
139-
pactl list sinks | grep -ie "Description:" | awk -F ': ' '{print $2}' | sort
161+
if [ -n "$selection" ]; then
162+
device=$(pactl list sinks | grep -C2 -F "Description: $selection" | grep Name | cut -d: -f2 | xargs)
163+
if pactl set-default-sink "$device"; then
164+
notify-send -t 2000 -i "${icodir}/unmuted-speaker.svg" -r 8 -u low "Activated: $selection"
165+
else
166+
notify-send -t 2000 -r 8 -u critical "Error activating $selection"
167+
fi
168+
else
169+
pactl list sinks | grep -ie "Description:" | awk -F ': ' '{print $2}' | sort
170+
fi
140171
fi
141172
}
142173

174+
get_default_sink() {
175+
local default_sink
176+
if [[ "${use_pipewire}" == true ]]; then
177+
default_sink=$(pw-dump | jq -r '[.[] | select(.info?.props?."media.class" == "Audio/Sink")] | min_by(.info.props."priority.session" // 9999) | .info.props."node.description"')
178+
else
179+
default_sink=$(pamixer --get-default-sink | awk -F '"' 'END{print $(NF - 1)}')
180+
fi
181+
echo ${default_sink}
182+
}
183+
184+
get_default_source() {
185+
local default_source
186+
if [[ "${use_pipewire}" == true ]]; then
187+
default_source=$(pw-dump | jq -r '[.[] | select(.info?.props?."media.class" == "Audio/Source")] | min_by(.info.props."priority.session" // 9999) | .info.props."node.description"')
188+
else
189+
default_source=$(pamixer --list-sources | awk -F '"' 'END {print $(NF - 1)}')
190+
fi
191+
echo ${default_source}
192+
}
193+
143194
toggle_output() {
144195
local default_sink
145196
local current_index
146-
default_sink=$(pamixer --get-default-sink | awk -F '"' 'END{print $(NF - 1)}')
197+
default_sink=$(get_default_sink)
147198
mapfile -t sink_array < <(select_output)
148199
current_index=$(printf '%s\n' "${sink_array[@]}" | grep -n "$default_sink" | cut -d: -f1)
149200
local next_index=$(((current_index % ${#sink_array[@]}) + 1))
@@ -158,17 +209,24 @@ iconsDir="${iconsDir:-$XDG_DATA_HOME/icons}"
158209
icodir="${iconsDir}/Wallbash-Icon/media"
159210
step=${VOLUME_STEPS:-5}
160211

212+
# Detect pipewire
213+
if pactl info | grep -q "PipeWire"; then
214+
use_pipewire=true
215+
else
216+
use_pipewire=false
217+
fi
218+
161219
while getopts "iop:stq" opt; do
162220
case $opt in
163221
i)
164222
device="pamixer"
165223
srce="--default-source"
166-
nsink=$(pamixer --list-sources | awk -F '"' 'END {print $(NF - 1)}')
224+
nsink=$(get_default_source)
167225
;;
168226
o)
169227
device="pamixer"
170228
srce=""
171-
nsink=$(pamixer --get-default-sink | awk -F '"' 'END{print $(NF - 1)}')
229+
nsink=$(get_default_sink)
172230
;;
173231
p)
174232
device="playerctl"

0 commit comments

Comments
 (0)