# 02 — Audio-Topologie (PipeWire) Herzstück: virtueller Misch-Bus **`callmix`** (Null-Sink). Alles, was aufgenommen werden soll, wird per Loopback dorthin geroutet; der Monitor von `callmix` wird als echte Quelle **`callrec_mix`** („CallRecMix“) re-mappt — die wählt die Web-GUI als Aufnahmequelle. ## Gesamtgraph ```mermaid flowchart LR PCIN["PC-Audio (Capture des UAC2-Gadgets)
alsa_input.platform-fc000000.usb"] MIC["Headset-Mikrofon H390
alsa_input.usb-Logi...mono-fallback"] HS["Headset-Hörer H390
alsa_output.usb-Logi...analog-stereo"] GADGET["Gadget-Sink (Board→PC)
alsa_output.platform-fc000000.usb"] GATE["micgate
LADSPA Noise-Gate
(swh gate_1410, Thr −45 dB)"] MIX[("callmix
Null-Sink")] CRMIX["callrec_mix ('CallRecMix')
remap von callmix.monitor"] APP["Aime-App
Aufnahme"] PCIN -- "cr_pc2hs (100%)" --> HS PCIN -- "cr_pc2mix (60% ≈ −13 dB)" --> MIX MIC -- "cr_mic2mix (100%)" --> MIX MIC -- "cr_mic2pc (100%)" --> GATE --> GADGET MIX -.->|"monitor"| CRMIX --> APP ``` Vier Loopbacks, jeder per `media.name`-Tag identifizierbar (robust gegen Index-Verschiebung), Gains setzt `si_vol()` im Setup-Skript `/opt/callrec/aime-usb-audio-setup.sh`: | Loopback | Weg | Gain | Zweck | |---|---|---|---| | `cr_pc2hs` | PC → Headset | 100 % | Nutzer hört PC/Teams | | `cr_pc2mix` | PC → callmix | **60 % (≈ −13 dB)** | Gegenseite in die Aufnahme (clip-sicher) | | `cr_mic2mix` | Mikro → callmix | 100 % | Eigene Stimme in die Aufnahme | | `cr_mic2pc` | Mikro → micgate → Gadget | 100 % | Gegenseite hört den Nutzer | ## Warum Noise-Gate (micgate)? Teams-Gegenseite hörte sich selbst verzögert (Selbst-Echo). Nachgewiesen: **akustisches Übersprechen ins Mikrofon** (nicht elektrisch, nicht Ohrmuschel — Mute-Tests), und Teams-AGC renormalisiert jede Pegelabsenkung. Lösung: LADSPA-Gate im Mikro→PC-Pfad schließt in Sprechpausen → Gegenseite bekommt Stille → kein Echo. AEC wurde getestet und **verworfen** (Knistern, kein Nutzen). ```mermaid flowchart LR M["Mikrofon
HW-Gain fest Stufe 12 (+1,4 dB)"] --> G{"Pegel > −45 dB?"} G -- "ja (Sprache)" --> OPEN["Gate offen → Signal zum PC"] G -- "nein (Pause)" --> CLOSED["Gate zu → Stille zum PC
= kein Selbst-Echo"] ``` **Gotchas (teuer bezahlt):** - Filter-Chain ist ein **Core-Modul**, kein Pulse-Modul → nicht per `pactl load-module`, sondern Drop-in `filter-chain.conf.d/micgate.conf` + eigener Dienst `aime-micgate.service`. - PipeWire liefert einen eigenen preset-aktivierten `filter-chain.service` mit → **doppeltes** micgate → `systemctl --user mask filter-chain.service`. - Mikro-HW-Gain **nur** über `pactl set-source-volume $HSMIC 62%` setzen (= HW-Stufe 12). `amixer` direkt wird von WirePlumber überschrieben; 100 % = +11 dB → Grundrauschen öffnet das Gate wieder. - 2-Mono-Node-Stereo-Graph im Filter-Chain = kein Audio → 1 Mono-Node verwenden. - `pkill -f "pipewire -c filter-chain.conf"` killt die eigene SSH-Session (exit 128) → `setsid` nutzen. ## Merksätze - **PC-Audio = die Quelle `alsa_input.platform-fc000000.usb...`**, NICHT deren `.monitor` (der ist stumm — hat einmal eine Stunde gekostet). Quellen vor Verwendung mit `parecord` + `ffmpeg astats` auf Signal prüfen. - Die GUI filtert alle `*.monitor`-Quellen aus → deshalb der Remap auf `callrec_mix`. - Gain-Staging ist clip-kritisch: Übersteuerung (0 dBFS) hat einmal die Diarization-Embeddings zerstört → 14 „Sprecher“ bei einem 2-Personen-Gespräch (Aufnahme #41, bleibt dauerhaft geclippt). ## Persistenz | Einheit | Aufgabe | |---|---| | `aime-usb-audio.service` (user) | baut die komplette Topologie beim Boot (`aime-usb-audio-setup.sh`); wartet auf micgate, Fallback direkt auf Gadget-Sink | | `aime-micgate.service` (user, enabled) | startet die micgate-Filter-Chain | | `aime-volume-headset.service` (user) | Windows-Volume-Bridge (UAC2 numid=7 → Headset-Sink) | | `aime-headset-keys.service` (root) | H390-Inline-Tasten → Lautstärke | | alte `aime-audio-out` / `aime-volume` | ES8323-Ära, **disabled** | Kaltstart 2× verifiziert: Topologie, Gate, Volume-Bridge und WLAN kommen ohne Eingriff hoch.