Resolve effect_input/effect_output audio filter pairs (#14241)
* Resolve effect_input/effect_output audio filter pairs User filter chains can name their input and output nodes differently, so the existing input-name prefix check cannot find their downstream sink. Match effect_output.<suffix> exactly for effect_input.<suffix>, avoiding similarly named filters while retaining prefix-compatible custom tunings and the existing EasyEffects fallbacks. Generated by GPT-6 in Codex. Reviewed by Claude Opus 5.5 Medium. Co-Authored-By: Claude Opus 5.5 Medium <noreply@anthropic.com> * Prefer an exact filter output over legacy prefix matches An earlier stream for a similarly named filter can satisfy the legacy prefix check and select the wrong device before the exact paired output is read. Keep the first prefix candidate as a fallback for effect_input sinks and finish searching for the exact effect_output pair. Preserve first-match behavior for all other sink names. Generated by GPT-6 in Codex. Reviewed by Claude Opus 5.5 Medium. Co-Authored-By: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --------- Co-authored-by: Omarchy Bot <317366263+omarchybot@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 Medium <noreply@anthropic.com> Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
This commit is contained in:
2 files changed
+67
-2
No files matched your search
@@ -29,16 +29,25 @@ fi
|
||||
# stream down to the sink underneath.
|
||||
downstream="$(pactl list sink-inputs 2>/dev/null |
|
||||
awk -v virt="$sink" '
|
||||
BEGIN {
|
||||
output = virt
|
||||
sub(/^effect_input\./, "effect_output.", output)
|
||||
}
|
||||
/^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}
|
||||
if (target != "" && (name == output || (output == virt && index(name, virt) == 1))) {selected = target; exit}
|
||||
if (target != "" && fallback == "" && index(name, virt) == 1) fallback = target
|
||||
}
|
||||
/application\.name = "EasyEffects"/ {
|
||||
if (virt == "easyeffects_sink" && target != "") {print target; exit}
|
||||
if (virt == "easyeffects_sink" && target != "") {selected = target; exit}
|
||||
}
|
||||
END {
|
||||
if (selected != "") print selected
|
||||
else if (fallback != "") print fallback
|
||||
}')"
|
||||
|
||||
if [[ -n $downstream ]]; then
|
||||
|
||||
@@ -104,3 +104,59 @@ Sink Input #42
|
||||
INPUTS
|
||||
[[ $(resolve) == "$physical" ]] || fail "a filter-chain tuning still resolves through its stream"
|
||||
pass "filter-chain tuning resolves through its stream"
|
||||
|
||||
# The shipped tuning uses an underscore before its output stream suffix.
|
||||
reset_scenario
|
||||
printf '%s\n' omarchy_speaker_tuning >"$test_home/data/default-sink"
|
||||
printf 'Sink Input #42\n Sink: 267\n node.name = "omarchy_speaker_tuning_output"\n' >"$test_home/data/sink-inputs"
|
||||
[[ $(resolve) == "$physical" ]] || fail "the shipped tuning output still resolves through its stream"
|
||||
pass "shipped tuning output resolves through its stream"
|
||||
|
||||
# Keep custom stream suffixes that the existing prefix rule already handles.
|
||||
printf 'Sink Input #42\n Sink: 267\n node.name = "omarchy_speaker_tuning-playback"\n' >"$test_home/data/sink-inputs"
|
||||
[[ $(resolve) == "$physical" ]] || fail "custom legacy tuning suffixes still resolve"
|
||||
pass "custom legacy tuning suffixes still resolve"
|
||||
|
||||
# Reproduce the reported input/output naming pair and downstream sink ID.
|
||||
reset_scenario
|
||||
filter=effect_input.Dolby_Balanced
|
||||
printf '%s\n' "$filter" >"$test_home/data/default-sink"
|
||||
printf '68\t%s\tPipeWire\ts32le 4ch 44100Hz\tRUNNING\n' "$physical" >"$test_home/data/sinks"
|
||||
printf 'Sink Input #42\n Sink: 68\n node.name = "effect_output.Dolby_Balanced"\n' >"$test_home/data/sink-inputs"
|
||||
[[ $(resolve) == "$physical" ]] || fail "the reported effect_input/effect_output pair resolves downstream"
|
||||
pass "reported effect_input/effect_output pair resolves downstream"
|
||||
|
||||
# An explicitly requested filter resolves even when another output is default.
|
||||
printf '%s\n' "$headphones" >"$test_home/data/default-sink"
|
||||
[[ $(resolve "$filter") == "$physical" ]] || fail "an explicit filter resolves independently of the default"
|
||||
pass "explicit filter resolves independently of the default"
|
||||
printf '%s\n' "$filter" >"$test_home/data/default-sink"
|
||||
|
||||
# An effect_input sink can also use a legacy prefix-compatible output name.
|
||||
printf 'Sink Input #42\n Sink: 68\n node.name = "effect_input.Dolby_Balanced.playback"\n' >"$test_home/data/sink-inputs"
|
||||
[[ $(resolve) == "$physical" ]] || fail "effect_input sinks preserve prefix-compatible outputs"
|
||||
pass "effect_input sinks preserve prefix-compatible outputs"
|
||||
|
||||
# A similarly named output must not win just because it appears first.
|
||||
printf '301\t%s\tPipeWire\ts16le 2ch 48000Hz\tRUNNING\n' "$headphones" >>"$test_home/data/sinks"
|
||||
printf 'Sink Input #41\n Sink: 301\n node.name = "effect_output.Dolby_Balanced2"\nSink Input #42\n Sink: 68\n node.name = "effect_output.Dolby_Balanced"\n' >"$test_home/data/sink-inputs"
|
||||
[[ $(resolve) == "$physical" ]] || fail "a filter output matches the complete suffix"
|
||||
pass "filter output matches the complete suffix"
|
||||
|
||||
# The exact paired output wins over an earlier competing legacy prefix.
|
||||
printf 'Sink Input #41\n Sink: 301\n node.name = "effect_input.Dolby_Balanced2.playback"\nSink Input #42\n Sink: 68\n node.name = "effect_output.Dolby_Balanced"\n' >"$test_home/data/sink-inputs"
|
||||
[[ $(resolve) == "$physical" ]] || fail "the exact output wins over a competing legacy prefix"
|
||||
pass "exact output wins over a competing legacy prefix"
|
||||
|
||||
printf 'Sink Input #41\n Sink: 301\n node.name = "effect_input.Dolby_Balanced.playback"\nSink Input #42\n Sink: 68\n node.name = "effect_output.Dolby_Balanced"\n' >"$test_home/data/sink-inputs"
|
||||
[[ $(resolve) == "$physical" ]] || fail "the exact output wins over a valid legacy fallback"
|
||||
pass "exact output wins over a valid legacy fallback"
|
||||
|
||||
printf 'Sink Input #41\n Sink: 301\n node.name = "effect_output.Dolby_Balanced2"\n' >"$test_home/data/sink-inputs"
|
||||
[[ $(resolve) == "$filter" ]] || fail "a filter with no exact output falls back to itself"
|
||||
pass "filter with no exact output falls back to itself"
|
||||
|
||||
# A stale sink ID must not turn into a different output.
|
||||
printf 'Sink Input #42\n Sink: 999\n node.name = "effect_output.Dolby_Balanced"\n' >"$test_home/data/sink-inputs"
|
||||
[[ $(resolve) == "$filter" ]] || fail "a missing downstream sink falls back to the filter"
|
||||
pass "missing downstream sink falls back to the filter"
|
||||
Reference in new issue
Block a user