fix(iphone-bt): iPhone-Route-Watcher greift wieder — Gegenseite fehlte in Aufnahme
aime-iphone-route.sh v1 hat seit dem Reboot am 29.07. nie gegriffen (0 Zeilen in journalctl -t aime-iphone-route über den ganzen Boot). Zwei Gründe: 1. Die Portnamen waren hartcodiert auf input_FL/input_FR. HFP/mSBC ist mono, die Ports heißen playback_MONO/capture_MONO — der grep matchte nie. 2. Der Guard "micgate.out schon verlinkt -> überspringen" war einmal pro Anruf. WirePlumber legt die HFP-Nodes nicht atomar an, der Uplink erscheint vor der Gegenseite. War bluez_input in genau diesem Durchlauf noch nicht da, wurde die Gegenseite für den ganzen Anruf nicht mehr nachverlinkt. Ein erst mitten im Anruf gesetztes callrec_mix->Uplink wurde ebenso nie entfernt. Folge: Gegenseite -> callmix fehlte (nicht in der Aufnahme, #80/#81 hatten je nur 1 Sprecher) und der Uplink führte den ungegateten Mix statt micgate.out, also wieder das Echo von vor dem Fix am 13.07. v2 ermittelt die Portnamen über pw-link -i/-o und prüft alle drei Links in jedem Durchlauf einzeln nach. link_all legt genau einen Link pro Ziel-Port an: eine Mono-Quelle speist beide Kanäle, eine Stereo-Quelle auf einem Mono-HFP-Port sendet nur FL, weil beide Links in denselben Port summieren würden (+6 dB Uplink). Gegenprobe am echten Anruf steht noch aus.
This commit is contained in:
+73
-15
@@ -1,24 +1,82 @@
|
||||
#!/bin/bash
|
||||
# Auto-route iPhone HFP calls (the nodes exist only during a call):
|
||||
# Auto-route iPhone HFP calls (the bluez nodes exist only during a live call):
|
||||
# uplink (what far-end hears) <- micgate.out (GATED mic only -> no self-echo)
|
||||
# far-end voice (bluez_input) -> callmix (recorded digitally, clean)
|
||||
# and remove WirePlumber's default callrec_mix->uplink auto-link (that mix carried
|
||||
# the ungated mic which picks up the far-end acoustically = echo).
|
||||
# and remove WirePlumber's default callrec_mix->uplink auto-link (that mix carries
|
||||
# the ungated mic AND the far-end, i.e. a digital echo loop = far-end hears itself).
|
||||
#
|
||||
# v2 (2026-07-30): two fixes after a call recorded near-end only + far-end doubling
|
||||
# 1. every link is verified on EVERY pass, not once per call. The old one-shot
|
||||
# guard ("micgate.out already linked -> skip") meant: if the far-end node
|
||||
# appeared a tick later than the uplink node (WirePlumber does not create the
|
||||
# HFP nodes atomically), far-end->callmix was never made for the whole call,
|
||||
# and a callrec_mix->uplink link added mid-call was never removed.
|
||||
# 2. port names are discovered instead of hardcoded. HFP/mSBC is MONO, so the
|
||||
# ports are playback_MONO/capture_MONO, not input_FL/input_FR. The old
|
||||
# hardcoded FL/FR grep never matched a mono node at all.
|
||||
export XDG_RUNTIME_DIR=/run/user/1000
|
||||
MAC=B4_96_A5_42_BD_D6
|
||||
PL(){ pw-link "$@" 2>/dev/null; }
|
||||
|
||||
# ports_in/ports_out: list "node:port" for nodes whose name matches $1
|
||||
ports_in() { pw-link -i 2>/dev/null | grep -E "^$1"; }
|
||||
ports_out() { pw-link -o 2>/dev/null | grep -E "^$1"; }
|
||||
|
||||
# link_all SRC_PORTS DST_PORTS -- exactly one link per DESTINATION port, cycling
|
||||
# the sources. So a mono source feeds both destination channels, and a stereo
|
||||
# source feeding a mono HFP port sends FL only (linking both would sum them into
|
||||
# the same port = +6 dB on the uplink).
|
||||
link_all() {
|
||||
local -n _src=$1 _dst=$2
|
||||
local mode=$3 i n=${#_src[@]} m=${#_dst[@]}
|
||||
[ "$n" -eq 0 ] || [ "$m" -eq 0 ] && return
|
||||
for ((i=0; i<m; i++)); do
|
||||
pw-link $mode "${_src[i % n]}" "${_dst[i]}" 2>/dev/null
|
||||
done
|
||||
}
|
||||
|
||||
log_once=""
|
||||
while true; do
|
||||
# uplink sink node (has input_FL) present = a call is up
|
||||
UPPORT=$(pw-link -i 2>/dev/null | grep -m1 "bluez_output\.${MAC}.*:input_FL")
|
||||
if [ -n "$UPPORT" ]; then
|
||||
B=${UPPORT%%:*}
|
||||
I=$(pw-link -o 2>/dev/null | grep -m1 "bluez_input\.${MAC}.*:output_FL"); I=${I%%:*}
|
||||
if ! pw-link -l "$B" 2>/dev/null | grep -q micgate.out; then
|
||||
PL -d callrec_mix:capture_FL "$B:input_FL"; PL -d callrec_mix:capture_FR "$B:input_FR"
|
||||
PL micgate.out:output_FL "$B:input_FL"; PL micgate.out:output_FR "$B:input_FR"
|
||||
[ -n "$I" ] && { PL "$I:output_FL" callmix:playback_FL; PL "$I:output_FR" callmix:playback_FR; }
|
||||
logger -t aime-iphone-route "applied iPhone call routing ($B)"
|
||||
mapfile -t UP < <(ports_in "bluez_output\.${MAC}")
|
||||
if [ ${#UP[@]} -gt 0 ]; then
|
||||
B=${UP[0]%%:*}
|
||||
LINKS=$(pw-link -l "$B" 2>/dev/null)
|
||||
CHANGED=""
|
||||
|
||||
# 1) drop WirePlumber's ungated-mix -> uplink link (echo source). Checked every
|
||||
# pass because WP can (re-)add it at any point during the call.
|
||||
if echo "$LINKS" | grep -q 'callrec_mix'; then
|
||||
mapfile -t MIX < <(ports_out 'callrec_mix')
|
||||
link_all MIX UP -d
|
||||
CHANGED="$CHANGED -callrec_mix"
|
||||
fi
|
||||
|
||||
# 2) ensure gated mic -> uplink
|
||||
if ! echo "$LINKS" | grep -q 'micgate\.out'; then
|
||||
mapfile -t GATE < <(ports_out 'micgate\.out')
|
||||
link_all GATE UP ""
|
||||
CHANGED="$CHANGED +micgate"
|
||||
fi
|
||||
|
||||
# 3) ensure far-end -> record mix. Retried every pass: this node often shows up
|
||||
# later than the uplink node.
|
||||
mapfile -t FAR < <(ports_out "bluez_input\.${MAC}")
|
||||
if [ ${#FAR[@]} -gt 0 ]; then
|
||||
I=${FAR[0]%%:*}
|
||||
if ! pw-link -l "$I" 2>/dev/null | grep -q 'callmix'; then
|
||||
mapfile -t REC < <(ports_in 'callmix')
|
||||
link_all FAR REC ""
|
||||
CHANGED="$CHANGED +far-end"
|
||||
fi
|
||||
else
|
||||
CHANGED="$CHANGED (no-far-end-node-yet)"
|
||||
fi
|
||||
|
||||
if [ -n "$CHANGED" ] && [ "$CHANGED" != "$log_once" ]; then
|
||||
logger -t aime-iphone-route "iPhone call $B:$CHANGED"
|
||||
log_once="$CHANGED"
|
||||
fi
|
||||
else
|
||||
log_once=""
|
||||
fi
|
||||
sleep 2
|
||||
sleep 1
|
||||
done
|
||||
|
||||
Reference in New Issue
Block a user