126 lines
4.6 KiB
Bash
Executable File
126 lines
4.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Legt den Tonweg für den Stream an und räumt ihn wieder ab.
|
|
#
|
|
# Bluetooth-Ausgaben haben eine eigene, ungenaue Uhr und laufen mit 44,1 kHz.
|
|
# Nimmt OBS deren Monitor auf, wandert der Ton mit der Zeit vom Bild weg —
|
|
# nach fünf Minuten sind es Sekunden. Eine Null-Senke hängt an der Systemuhr
|
|
# und liefert 48 kHz, also genau das, was der Encoder erwartet.
|
|
#
|
|
# ton.sh auf Senke anlegen und zum Standard machen. Der Ton
|
|
# geht nur in den Stream, der Rechner bleibt still.
|
|
# ton.sh auf mithoeren zusätzlich zurück auf dein gewohntes Gerät
|
|
# ton.sh ab Standard zurückstellen, Senke entfernen
|
|
set -euo pipefail
|
|
|
|
SENKE="filmabend"
|
|
MERKER="/tmp/filmabend-vorheriger-ausgang"
|
|
|
|
# wpctl zeigt Beschreibungen, keine Knotennamen — die ID kommt aus pw-dump
|
|
id_von() {
|
|
pw-dump | python3 -c "
|
|
import json, sys
|
|
name = sys.argv[1]
|
|
for o in json.load(sys.stdin):
|
|
if o.get('info', {}).get('props', {}).get('node.name') == name:
|
|
print(o['id']); break
|
|
" "$1"
|
|
}
|
|
|
|
lautstaerke_von() {
|
|
wpctl get-volume "$1" 2>/dev/null | awk '{print $2}'
|
|
}
|
|
|
|
name_des_standards() {
|
|
wpctl inspect @DEFAULT_AUDIO_SINK@ 2>/dev/null |
|
|
sed -n 's/.*node\.name = "\([^"]*\)".*/\1/p' | head -1
|
|
}
|
|
|
|
case "${1:-auf}" in
|
|
auf)
|
|
# Zuerst lesen: WirePlumber macht eine frisch angelegte Senke sofort
|
|
# zum Standard, danach wäre der alte Ausgang nicht mehr zu erfahren
|
|
VORHER="$(name_des_standards)"
|
|
[ -n "$VORHER" ] && [ "$VORHER" != "$SENKE" ] && echo "$VORHER" > "$MERKER"
|
|
ZURUECK="$(cat "$MERKER" 2>/dev/null || true)"
|
|
|
|
if [ -z "$ZURUECK" ]; then
|
|
echo "Kein voriger Ausgang bekannt — bitte einmal 'scripts/ton.sh ab' laufen lassen."
|
|
exit 1
|
|
fi
|
|
|
|
if [ -z "$(id_von "$SENKE")" ]; then
|
|
pw-cli create-node adapter "{
|
|
factory.name=support.null-audio-sink
|
|
node.name=$SENKE node.description=Filmabend
|
|
media.class=Audio/Sink object.linger=true
|
|
audio.rate=48000 audio.position=[FL,FR]
|
|
}" > /dev/null
|
|
sleep 1
|
|
fi
|
|
|
|
# Der Regelfall: der Ton geht nur in den Stream, hier bleibt es still.
|
|
# Am Pegel ändert das nichts — PipeWire lässt die Monitor-Ports vom Regler
|
|
# der Senke unberührt (monitor.channel-volumes ist aus), OBS bekommt also
|
|
# immer den vollen Ton. Genau deshalb macht Stummschalten allein auch nicht
|
|
# leise: das Mithören unten hängt am selben Monitor.
|
|
if [ "${2:-}" != "mithoeren" ]; then
|
|
# Eine Brücke aus einem früheren Lauf spielte sonst weiter auf die
|
|
# Lautsprecher — die Senke allein macht noch nicht still
|
|
pkill -f "node.name=filmabend-mithoeren" 2>/dev/null || true
|
|
wpctl set-default "$(id_von "$SENKE")"
|
|
echo "Ton geht nur in den Stream, $ZURUECK bleibt still."
|
|
exit 0
|
|
fi
|
|
|
|
# Damit du selbst weiter über dein gewohntes Gerät hörst.
|
|
# setsid, sonst nimmt der Prozess das Ende dieser Shell mit und du
|
|
# hörst mitten im Film nichts mehr.
|
|
if ! pgrep -f "node.name=filmabend-mithoeren" > /dev/null; then
|
|
setsid pw-loopback -m '[FL FR]' \
|
|
--capture-props="stream.capture.sink=true target.object=$SENKE node.name=filmabend-mithoeren" \
|
|
--playback-props="target.object=$ZURUECK node.name=filmabend-ausgabe" \
|
|
> /dev/null 2>&1 < /dev/null &
|
|
sleep 1
|
|
fi
|
|
|
|
# Dein Lautstärkeregler fasst ab jetzt die neue Senke an, nicht mehr das
|
|
# Ausgabegerät. Stünde das weiter auf halber Kraft, wäre bei "voll
|
|
# aufgedreht" Schluss bei der Hälfte. Also: Gerät auf Anschlag, und die
|
|
# neue Senke übernimmt den Pegel, den du gewohnt bist.
|
|
ZIEL_ID="$(id_von "$ZURUECK")"
|
|
if [ -n "$ZIEL_ID" ]; then
|
|
ALTER_PEGEL="$(lautstaerke_von "$ZIEL_ID")"
|
|
[ -n "$ALTER_PEGEL" ] && echo "$ALTER_PEGEL" > "$MERKER.pegel"
|
|
wpctl set-volume "$ZIEL_ID" 1.0
|
|
[ -n "$ALTER_PEGEL" ] && wpctl set-volume "$(id_von "$SENKE")" "$ALTER_PEGEL"
|
|
fi
|
|
|
|
# Ab jetzt landet aller Ton in der Senke — und von dort bei dir und im Stream
|
|
wpctl set-default "$(id_von "$SENKE")"
|
|
echo "Ton läuft über die Senke Filmabend, du hörst weiter über $ZURUECK."
|
|
;;
|
|
|
|
ab)
|
|
ZURUECK="$(cat "$MERKER" 2>/dev/null || true)"
|
|
if [ -n "$ZURUECK" ]; then
|
|
ID="$(id_von "$ZURUECK")"
|
|
if [ -n "$ID" ]; then
|
|
wpctl set-default "$ID"
|
|
PEGEL="$(cat "$MERKER.pegel" 2>/dev/null || true)"
|
|
[ -n "$PEGEL" ] && wpctl set-volume "$ID" "$PEGEL"
|
|
fi
|
|
fi
|
|
rm -f "$MERKER.pegel"
|
|
pkill -f "node.name=filmabend-mithoeren" 2>/dev/null || true
|
|
ID="$(id_von "$SENKE")"
|
|
[ -n "$ID" ] && pw-cli destroy "$ID" > /dev/null 2>&1 || true
|
|
rm -f "$MERKER"
|
|
echo "Ton zurück auf ${ZURUECK:-dein Gerät}."
|
|
;;
|
|
|
|
*)
|
|
echo "Aufruf: ton.sh auf [mithoeren] | ab"
|
|
exit 1
|
|
;;
|
|
esac
|