Files
omarchy/bin/omarchy-audio-output-sink
T
OmarchybotandClaude Opus 5.5 357d84f17a Follow EasyEffects' links to Bluetooth sinks too
EasyEffects plays into Bluetooth headphones through the same direct port links as into a card, but only alsa_output targets were followed, so with headphones on the keys fell back to the settings file -- which can still name the speakers -- or to easyeffects_sink. Match bluez_output sinks as well, and compare the configured device name literally rather than as a regex.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 07:06:39 +02:00

88 lines
3.2 KiB
Bash
Executable File

#!/bin/bash
# omarchy:summary=Print the sink whose volume and mute a given output really uses
# omarchy:args=[sink-name]
# omarchy:group=audio
# omarchy:examples=omarchy audio output sink | omarchy audio output sink omarchy_speaker_tuning
set -uo pipefail
# A DSP sink -- a speaker tuning filter-chain, or EasyEffects -- can be the
# selected output without being where loudness lives. Changing its volume alters
# the level going *into* the processing: the display moves while the speakers do
# not, and on a chain with a compressor or limiter the tone changes too. Resolve
# through it to the physical sink it feeds.
#
# With no argument this resolves the current default output, so when headphones or
# HDMI are selected it returns those, not the speakers a tuning happens to front.
# Callers that need to describe some *other* output -- an output switcher naming
# the next one in the rotation -- pass that sink explicitly.
sink="${1:-$(pactl get-default-sink 2>/dev/null)}"
if [[ -z $sink || $sink == alsa_output.* ]]; then
printf '%s\n' "$sink"
exit 0
fi
# A DSP sink feeds its physical output through a stream of its own; follow that
# stream down to the sink underneath.
downstream="$(pactl list sink-inputs 2>/dev/null |
awk -v virt="$sink" '
/^Sink Input #/ {target = ""}
/^[[:space:]]*Sink:/ {target = $2}
/node\.name = / {
name = $0
sub(/.*node\.name = "/, "", name)
sub(/"$/, "", name)
if (index(name, virt) == 1 && target != "") {print target; exit}
}
/application\.name = "EasyEffects"/ {
if (virt == "easyeffects_sink" && target != "") {print target; exit}
}')"
if [[ -n $downstream ]]; then
name="$(pactl list sinks short 2>/dev/null |
awk -v id="$downstream" '$1 == id {print $2; exit}')"
if [[ -n $name ]]; then
printf '%s\n' "$name"
exit 0
fi
fi
# EasyEffects 8 does not play into the physical sink through a stream of its
# own: its output node links its ports straight to the sink's playback ports,
# so the sink-input search above finds nothing and the keys would move the
# EasyEffects sink -- whose volume its monitor, the input of the processing,
# does not follow. Follow the port links instead: the first ALSA or Bluetooth
# sink any EasyEffects output port is linked to.
if [[ $sink == "easyeffects_sink" ]]; then
linked="$(pw-link -l 2>/dev/null |
awk '
/^[^ ]/ {src = $0}
/^[[:space:]]*\|->/ {
if (src ~ /^ee_/ && $2 ~ /^(alsa|bluez)_output\..*:playback_/) {
sub(/:playback_.*$/, "", $2); print $2; exit
}
}')"
# Idle, EasyEffects drops those links: fall back to the output device it is
# configured to feed, when that sink exists.
if [[ -z $linked ]]; then
configured="$(sed -n 's/^outputDevice=//p' \
"${XDG_CONFIG_HOME:-$HOME/.config}/easyeffects/db/easyeffectsrc" 2>/dev/null)"
if [[ -n $configured ]] && pactl list sinks short 2>/dev/null | awk '{print $2}' | grep -qxF -- "$configured"; then
linked="$configured"
fi
fi
if [[ -n $linked ]]; then
printf '%s\n' "$linked"
exit 0
fi
fi
# Nothing resolvable downstream -- the DSP sink may simply be idle and unlinked.
# Fall back to the sink itself so callers still have something to act on.
printf '%s\n' "$sink"