# Next Session: Phone Pipeline AEC (Acoustic Echo Cancellation)

## What
Add echo cancellation to Annie's phone call pipeline. Currently, when Annie speaks via BT HFP, her TTS audio plays through the Pixel's speaker, gets picked up by the Pixel's mic, travels back over BT SCO to Panda, and is heard by the caller as echo of their own voice (and Annie's). Nobody in the chain does AEC — the telecom network normally handles this, but Panda is a raw BT Audio Gateway with no echo cancellation.

## Why
Session 28: User reports hearing own voice echoed during phone calls. Logs prove the echo is massive — 494 echo frames (14.8s) on a single turn even WITHOUT thinking cues. The echo is pre-existing but very noticeable. The `_POST_ECHO_FACTOR=0.3` drain only helps VAD/STT (prevents ghost turns), it does NOT prevent the caller from hearing echo in their earpiece.

## Echo Chain (the problem)
```
USER SPEAKS → Pixel mic → BT SCO → Panda pw-record → [main_queue, bargein_queue]
                                                              ↓
                                         STT → LLM → TTS → pw-play → BT SCO → Pixel speaker
                                                                                     ↓
                                                          Pixel speaker → Pixel mic (acoustic coupling)
                                                                                     ↓
                                                          Echo returns to Panda → user hears themselves
```

The fix: insert AEC between `pw-record` (raw BT input) and the pipeline consumers, using the `pw-play` output as the reference signal.

## Approach: PipeWire Echo-Cancel Filter

PipeWire has a built-in `filter-chain` module that can load `libpipewire-module-echo-cancel` using the WebRTC Audio Processing library. This runs at the PipeWire level — no Python code changes needed for the audio path.

### Option A: PipeWire filter-chain (recommended — zero code changes)
PipeWire's echo-cancel module creates a virtual source that outputs echo-cancelled audio. Our `pw-record` would target this virtual source instead of the raw BT input.

Config file: `~/.config/pipewire/pipewire.conf.d/echo-cancel.conf`
```
context.modules = [
    {   name = libpipewire-module-echo-cancel
        args = {
            capture.props = {
                node.name = "echo_cancel_capture"
                # Capture from BT HFP input (Pixel mic)
                target.object = "bluez_input.FC_41_16_C5_AC_61.0"
            }
            playback.props = {
                node.name = "echo_cancel_playback"
                # Reference signal = what we're playing to BT HFP output (Annie's voice)
                target.object = "bluez_output.FC_41_16_C5_AC_61.1"
            }
            source.props = {
                node.name = "echo_cancel_source"
                node.description = "Echo-Cancelled BT Input"
            }
            sink.props = {
                node.name = "echo_cancel_sink"
                node.description = "Echo-Cancel Reference"
            }
            # AEC library
            library.name = "aec/libspa-aec-webrtc"
            aec.args = {
                webrtc.gain_control = true
                webrtc.extended_filter = true
            }
        }
    }
]
```

Then change `phone_audio.py`:
```python
# Before:
BT_INPUT = f"bluez_input.{_BT_MAC}.0"
# After (use echo-cancelled virtual source):
BT_INPUT = os.getenv("BT_INPUT_NODE", f"bluez_input.{_BT_MAC}.0")
```

Set `BT_INPUT_NODE=echo_cancel_source` in start.sh when AEC is active.

### Option B: Software AEC in Python (fallback)
Use `speexdsp` or `webrtc-audio-processing` Python bindings to process audio frames in `phone_audio.py`. More complex (need to pipe reference signal), but works if PipeWire module isn't available.

### Option C: PulseAudio echo-cancel module (if PipeWire module unavailable)
`pactl load-module module-echo-cancel` — PulseAudio compatibility layer in PipeWire.

## Research Needed
1. **Is `libpipewire-module-echo-cancel` available on Panda?** Check: `find / -name "*echo*cancel*" 2>/dev/null` and `apt list --installed | grep -i webrtc`
2. **Is `libspa-aec-webrtc` (the WebRTC AEC library for PipeWire) installed?** Package: `pipewire-audio` or `libspa-0.2-modules` on Ubuntu.
3. **Does it work with BT HFP SCO?** BT HFP runs at 16kHz mono — verify the AEC module handles this sample rate.
4. **Does the AEC module survive BT reconnects?** When the phone reconnects BT, the bluez nodes change. The AEC config references specific node names.
5. **What about latency?** AEC adds ~10-30ms processing delay. Is this acceptable for the phone loop?

## Key Code Locations
- `services/annie-voice/phone_audio.py:30-32` — `BT_INPUT` / `BT_OUTPUT` constants
- `services/annie-voice/phone_audio.py:160-193` — `start_recording()` — pw-record targets `BT_INPUT`
- `services/annie-voice/phone_audio.py:400-414` — `play_audio()` — pw-play targets `BT_OUTPUT`
- `services/annie-voice/phone_loop.py:70` — `_POST_ECHO_FACTOR = 0.3` — current echo drain (band-aid, not AEC)
- `start.sh:720-741` — phone auto-answer startup with env vars

## Hardware Context
- **Panda:** RTX 5070 Ti, Ubuntu 24.04, PipeWire 1.0.5
- **Pixel 9a:** BT HFP connected, MAC `FC:41:16:C5:AC:61`
- **BT profile:** HFP (Hands-Free), SCO codec (CVSD or mSBC), 16kHz mono
- **Audio format:** 16kHz, 16-bit, mono PCM

## Constraints
- AEC must NOT add >50ms latency (voice calls are latency-sensitive)
- AEC must work at 16kHz mono (BT HFP format)
- AEC must survive BT reconnects (Pixel disconnects/reconnects during day)
- AEC should be a PipeWire config change if possible (no Python audio path changes)
- Fallback: if PipeWire AEC module unavailable, use Python-level AEC

## Evidence from Session 28 Logs
```
Turn 1 (no thinking cue):  echo_drain = 494 frames (14,820ms) — MASSIVE
Turn 2 (with thinking cue): echo_drain = 360 frames (10,800ms)
Turn 3 (with thinking cue): echo_drain = 147 frames (4,410ms)
Barge-in false positives: sentence 2 on both turns 2 and 3 (300ms echo guard too short)
```

## Start Command
```
cat docs/NEXT-SESSION-PHONE-AEC.md
```
Then:
1. Research: check if PipeWire echo-cancel module is available on Panda
2. If yes: create config, test with a call, update `phone_audio.py` BT_INPUT
3. If no: install `libspa-0.2-modules` or fall back to Python AEC
4. Verify: call Annie, speak, confirm no echo

## Verification
1. Call Annie on phone
2. Speak a sentence — should NOT hear your own voice echoed
3. Annie responds — should NOT hear Annie's voice echoed back
4. Barge-in still works (speak during Annie's response)
5. STT quality unchanged (AEC shouldn't degrade mic input)
6. Run existing tests: `cd services/annie-voice && python3 -m pytest tests/ -q`
7. Check latency: `grep LATENCY /tmp/phone-auto.log` — STT and pipeline times should be similar to pre-AEC
