#!/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