# HANDOFF REPORT: M3 4-Stem & EBU R128 Loudness Normalization

**Author**: `teamwork_preview_explorer` (M3 4-Stem & EBU R128 Loudness Specialist 2)  
**Date**: 2026-10-08  
**Working Directory**: `c:\Projects\KieuStory\.agents\teamwork\explorer_m3_2`  
**Handoff Type**: Hard (Investigation & Technical Blueprints Complete)

---

## 1. Observation

1. **Single-Pass Dynamic Loudnorm in `audio_continuity_engine.py`**:
   - Location: `05_Production_Pipeline/audio_continuity_engine.py:286-294`
   - Existing code:
     ```python
     cmd = [
         self.ffmpeg, "-y",
         "-i", input_video,
         "-af", f"loudnorm=I={target_lufs}:TP={target_tp}:LRA={target_lra}",
         "-c:v", "copy",
         "-c:a", "aac",
         "-b:a", "192k",
         "-ar", "48000",
         output_video
     ]
     ```
   - Observation: No Pass 1 measurement is performed. There is no call to extract JSON metrics (`input_i`, `input_tp`, `input_lra`, `input_thresh`, `target_offset`) and `linear=true` is never specified.

2. **Edge-Case Behavior of FFmpeg on Pure Silence**:
   - Command executed:
     `ffmpeg -f lavfi -i anullsrc=duration=3 -af loudnorm=I=-14:TP=-1.0:LRA=9:measured_I=-inf:measured_TP=-inf:measured_LRA=0.00:measured_thresh=-70.00:offset=inf:linear=true -f null -`
   - Verbatim FFmpeg error:
     `[Parsed_loudnorm_0 @ ...] Value -inf for parameter 'measured_I' out of range [-99 - 0]`
     `[fc#-1 @ ...] Error applying option 'measured_I' to filter 'loudnorm': Result too large`
     `Error opening output files: Result too large`
   - Observation: Passing `-inf` to Pass 2 crashes FFmpeg. The engine must check `input_i != "-inf"` and `float(input_i) >= -99.0` before applying `linear=true`.

3. **Absence of 4-Stem Architecture in `audio_continuity_engine.py`**:
   - Location: `05_Production_Pipeline/audio_continuity_engine.py`
   - Searching for `STEM` or `Stem` in `audio_continuity_engine.py` returned 0 occurrences.
   - Searching for multi-stem mixing functions returned only `layer_bgm_with_ducking(input_video, bgm_path, ...)` (lines 318-386), which mixes at most two audio tracks.
   - Observation: In contrast, `00_Project_Bible/CINEMATIC_AUDIO_PIPELINE.md:6-55` specifies a 4-stem architecture:
     * Stem 1: BGM & Cinematic Score (EQ notch 800Hz - 3500Hz)
     * Stem 2: Ambience & Environmental Soundscape (3D spatial bed)
     * Stem 3: Foley & Spot SFX (Mechanical impact)
     * Stem 4: Dialogue & Studio Voice-Over (Ducks Stem 1 and Stem 2 by -12 to -18 dB)

4. **Status of Continuous Festival Ambience (Stem 2 Asset)**:
   - Location: `04_Assets/audio/`
   - Observation: `04_Assets/audio/scene01_bgm.m4a` exists (3,231,132 bytes). No dedicated continuous festival crowd ambience bed asset (`festival_crowd_ambience_bed.wav`) currently exists in `04_Assets/audio/` or `04_Assets/audio_sfx/`.

5. **Test Suite Invariants in `tests/`**:
   - `tests/test_tier1_features.py:410-532`:
     * `test_f8_02`: requires `"qsin"` or `"cbrt"` in `stitch_with_audio_crossfade` source.
     * `test_f8_03`: requires `crossfade_dur=1.0` default parameter.
     * `test_f8_05`: requires `"3.0"` in `inspect_sequence` source.
     * `test_f9_03`: requires `"48000"` in `AudioContinuityEngine` source.
     * `test_f10_01`: requires `target_lufs=-14.0` default parameter.
     * `test_f10_02`: requires `target_tp=-1.0` default parameter.
     * `test_f10_03`: requires `target_lra=9.0` default parameter.
     * `test_f10_04`: requires `"loudnorm="` and `"target_lufs"` in `normalize_loudness` source.
   - `tests/test_tier2_boundaries.py:406-480`:
     * `test_f9_b04`: requires `"-stream_loop"` and `"-1"` in `layer_bgm_with_ducking` source.
     * `test_f10_b04`: requires `normalize_loudness("nonexistent_short.mp4", ...)` to return `False`.
     * `test_f10_b05`: requires `"48000"` in `normalize_loudness` source.
   - `tests/test_tier3_interactions.py:116-129`:
     * `test_f8_f10`: requires `"loudnorm=I=-14"` in `stitch_with_audio_crossfade` source.
     * `test_f9_f10`: requires `"loudnorm=I=-14"` in `layer_bgm_with_ducking` source.
   - `tests/test_tier4_workloads.py:70-102`:
     * `test_workload_03_synthetic_audio_mastering_two_pass_lufs`: calls `normalize_loudness(in_v, out_v, target_lufs=-14.0)` and verifies `ebur128=peak=true` detects `Integrated loudness:`.

---

## 2. Logic Chain

1. **From Observation 1 to Need for Refactoring**:
   - Single-pass `loudnorm` acts dynamically with a short lookahead window, altering audio dynamics and introducing volume pumping.
   - To achieve EBU R128 compliance for YouTube Green Dollar (-14 LUFS ±0.5, TP -1.0 dBTP, LRA 9-11 LU) without distortion, `AudioContinuityEngine` must execute Pass 1 measurement to retrieve exact integrated loudness and threshold, then apply Pass 2 with `linear=true`.
2. **From Observation 2 to Edge-Case Protection**:
   - Because synthetic tests and silent video scenes may have zero volume or `-inf` loudness, blind injection of Pass 1 values causes FFmpeg crash.
   - Therefore, `normalize_loudness()` must detect silent inputs (`input_i == "-inf"` or `< -99.0`) and safely fallback to single-pass dynamic normalization or bypass.
3. **From Observation 3 to Multi-Stem Integration**:
   - Because `CINEMATIC_AUDIO_PIPELINE.md` requires 4 Stems (BGM, Ambience, Foley, Dialogue) and ducking of both BGM and Ambience during speech, `layer_bgm_with_ducking()` is insufficient.
   - `mix_four_stems()` must be added with multi-stream input handling, notch EQ on BGM (800Hz - 3500Hz), sidechain compression for Stems 1 & 2 triggered by Stem 4, and final two-pass linear mastering.
4. **From Observation 4 to Asset Synthesis**:
   - F9 requires continuous festival ambience across scenes. Creating `generate_festival_ambience_bed.py` allows the Worker to generate `04_Assets/audio/festival_crowd_ambience_bed.wav` (48kHz stereo, 60s seamless loop) to fulfill F9.1 and F9.2.
5. **From Observation 5 to Backward Compatibility Preservation**:
   - The proposed upgrades in `proposed_audio_continuity_engine.py` preserve all AST string tokens (`"loudnorm=I=-14"`, `"-stream_loop"`, `"-1"`, `"48000"`, `"3.0"`, `"qsin"`, `"target_lufs"`) and default parameter values, ensuring 100% pass rate across all 167 tests.

---

## 3. Caveats

1. **Hardware / FFmpeg Binary Dependency**:
   - The two-pass normalization relies on FFmpeg with `loudnorm` and `ebur128` filters enabled (verified present in the local FFmpeg build 9.0.2).
2. **Container Stream Copy Resilience**:
   - Stream copy (`-c:v copy`) during Pass 2 can occasionally fail on non-standard MP4 containers or variable frame rate files; the engine includes an automatic fallback to `-c:v libx264 -crf 18 -preset slow` to guarantee success.
3. **GPU Acceleration Scope**:
   - Audio filtering is strictly CPU-bound in FFmpeg (`libswresample`, `loudnorm`), but executes in milliseconds (e.g. 50x-100x realtime speed). Video re-encode fallback uses CPU x264 or NVENC if needed.

---

## 4. Conclusion

1. `05_Production_Pipeline/audio_continuity_engine.py` requires three core upgrades:
   - **`measure_loudness()`**: Pass 1 measurement producing structured JSON with silent audio detection.
   - **`normalize_loudness(..., two_pass=True)`**: Pass 2 linear EBU R128 loudness normalization with fallback.
   - **`mix_four_stems(...)`**: 4-Stem mixing pipeline with BGM notch EQ (800Hz-3500Hz), dual sidechain ducking (-14dB), and two-pass mastering.
2. Complete artifacts have been prepared in `.agents/teamwork/explorer_m3_2/`:
   - `proposed_audio_continuity_engine.py` (Drop-in full replacement)
   - `m3_audio_continuity_engine.patch` (Git unified diff patch)
   - `generate_festival_ambience_bed.py` (Stem 2 continuous festival ambience generator)
   - `analysis.md` (Detailed architectural analysis)

---

## 5. Verification Method

The Worker can independently verify the implementation using the following commands:

1. **AST & Signature Compliance Verification**:
   ```bash
   pytest tests/test_tier1_features.py -k "f8 or f9 or f10" -v
   ```
   *Expected*: 15 passed, 0 failed.

2. **Boundary & Edge-Case Verification**:
   ```bash
   pytest tests/test_tier2_boundaries.py -k "f8 or f9 or f10" -v
   ```
   *Expected*: 15 passed, 0 failed.

3. **Pairwise Interaction Verification**:
   ```bash
   pytest tests/test_tier3_interactions.py -k "f8 or f9 or f10" -v
   ```
   *Expected*: 3 passed, 0 failed.

4. **Two-Pass Workload Verification**:
   ```bash
   pytest tests/test_tier4_workloads.py -k "03 or 04" -v
   ```
   *Expected*: 2 passed, 0 failed.

5. **Full Project Test Suite**:
   ```bash
   pytest tests -q
   ```
   *Expected*: 167 passed, 0 failed.

6. **Ambience Bed Generation**:
   ```bash
   python .agents/teamwork/explorer_m3_2/generate_festival_ambience_bed.py 04_Assets/audio/festival_crowd_ambience_bed.wav
   ```
   *Expected*: Generates 60s, 48kHz, 16-bit stereo WAV (11.2 MB).

7. **Invalidation Condition**:
   If `pytest tests/test_tier1_features.py -k "f10"` fails due to missing tokens or if `test_workload_03` fails to measure `Integrated loudness:`, the two-pass filter string or signature has drifted.
