docs(02-03): complete MP3 encoder and output flag plan

- Create 02-03-SUMMARY.md with TDD flow documentation
- Update STATE.md: advance to last plan, 100% progress, add decisions
- Update ROADMAP.md: phase 02 complete (3/3 plans)
- Mark requirements OUT-01, OUT-02, OUT-03 complete
This commit is contained in:
2026-03-26 12:09:47 +01:00
parent a65dfcd0ba
commit bb9f9a7944
4 changed files with 120 additions and 17 deletions
+6 -6
View File
@@ -31,9 +31,9 @@ Requirements for initial release. Each maps to roadmap phases.
### Output
- [ ] **OUT-01**: User can specify output file path via `-o` flag (defaults to `netsynth-<timestamp>.mp3`)
- [ ] **OUT-02**: Output is encoded as a valid MP3 file
- [ ] **OUT-03**: Empty captures (zero packets) produce a clear error instead of a corrupt file
- [x] **OUT-01**: User can specify output file path via `-o` flag (defaults to `netsynth-<timestamp>.mp3`)
- [x] **OUT-02**: Output is encoded as a valid MP3 file
- [x] **OUT-03**: Empty captures (zero packets) produce a clear error instead of a corrupt file
## v2 Requirements
@@ -79,9 +79,9 @@ Which phases cover which requirements. Updated during roadmap creation.
| SYNTH-01 | Phase 2 | Complete |
| SYNTH-02 | Phase 2 | Complete |
| SYNTH-03 | Phase 2 | Complete |
| OUT-01 | Phase 2 | Pending |
| OUT-02 | Phase 2 | Pending |
| OUT-03 | Phase 2 | Pending |
| OUT-01 | Phase 2 | Complete |
| OUT-02 | Phase 2 | Complete |
| OUT-03 | Phase 2 | Complete |
**Coverage:**
- v1 requirements: 16 total
+4 -4
View File
@@ -13,7 +13,7 @@ NetSynth is built in four phases ordered by technical risk. Phase 1 validates th
Decimal phases appear between their surrounding integers in numeric order.
- [x] **Phase 1: Capture and Classification** - Live packet capture, protocol identification, and CLI scaffolding — no audio yet (completed 2026-03-25)
- [ ] **Phase 2: Audio Synthesis Engine** - Oscillators, EMA amplitude smoothing, mixing, and MP3 encoding against synthetic inputs
- [x] **Phase 2: Audio Synthesis Engine** - Oscillators, EMA amplitude smoothing, mixing, and MP3 encoding against synthetic inputs (completed 2026-03-26)
- [ ] **Phase 3: Pipeline Integration and MVP** - Wire capture into synthesis, Ctrl+C with valid MP3 output, auto-clustering of unknown traffic
- [ ] **Phase 4: Power User Features** - BPF capture filter, offline pcap file input
@@ -47,12 +47,12 @@ Plans:
3. Drone layer amplitude rises and falls with traffic volume over time — sustained traffic sounds louder, quiet periods fade
4. User can specify output path via `-o` flag; it defaults to `netsynth-<timestamp>.mp3` when omitted
5. An empty (zero-packet) input produces a clear error message instead of a corrupt or zero-byte MP3
**Plans:** 2/3 plans executed
**Plans:** 3/3 plans complete
Plans:
- [x] 02-01-PLAN.md — Environment setup (gcc, ffprobe, go-lame), synth config table, oscillator, EMA layer with tests
- [x] 02-02-PLAN.md — Stereo mixer (constant-power panning), OscillatorBank multi-layer rendering with tests
- [ ] 02-03-PLAN.md — MP3 encoder package, zero-packet guard, -o CLI flag, ffprobe integration test
- [x] 02-03-PLAN.md — MP3 encoder package, zero-packet guard, -o CLI flag, ffprobe integration test
### Phase 3: Pipeline Integration and MVP
**Goal**: Live capture flows end-to-end into audio synthesis — the complete v1 MVP: run, capture, Ctrl+C, get an MP3
@@ -82,6 +82,6 @@ Phases execute in numeric order: 1 → 2 → 3 → 4
| Phase | Plans Complete | Status | Completed |
|-------|----------------|--------|-----------|
| 1. Capture and Classification | 4/4 | Complete | 2026-03-25 |
| 2. Audio Synthesis Engine | 2/3 | In Progress| |
| 2. Audio Synthesis Engine | 3/3 | Complete | 2026-03-26 |
| 3. Pipeline Integration and MVP | 0/? | Not started | - |
| 4. Power User Features | 0/? | Not started | - |
+10 -7
View File
@@ -2,14 +2,14 @@
gsd_state_version: 1.0
milestone: v1.0
milestone_name: milestone
status: Ready to execute
stopped_at: "Completed 02-02: OscillatorBank and stereo mixer"
last_updated: "2026-03-26T11:04:17.995Z"
status: Phase complete — ready for verification
stopped_at: "Completed 02-03: MP3 encoder and CLI output flag"
last_updated: "2026-03-26T11:09:30.198Z"
progress:
total_phases: 4
completed_phases: 1
completed_phases: 2
total_plans: 7
completed_plans: 6
completed_plans: 7
---
# Project State
@@ -52,6 +52,7 @@ Plan: 3 of 3
| Phase 01-capture-and-classification P04 | 15min | 2 tasks | 2 files |
| Phase 02 P01 | 15min | 2 tasks | 8 files |
| Phase 02 P02 | 10min | 2 tasks | 4 files |
| Phase 02 P03 | 3min | 2 tasks | 3 files |
## Accumulated Context
@@ -79,6 +80,8 @@ Recent decisions affecting current work:
- [Phase 02]: go-audio/wav excluded — writing PCM bytes directly to LameWriter is simpler (per Claude's Discretion grant in CONTEXT.md)
- [Phase 02]: GainPerLayer applied in bank.go during mixing to ensure 11 max layers sum to 1.0 (D-10)
- [Phase 02]: int16 conversion uses float * 32767 to avoid positive overflow at exactly +1.0
- [Phase 02]: RunSynthesis takes snapshot slice (not channel) enabling zero-packet guard before file creation
- [Phase 02]: EncodeMP3 and RunSynthesis are separate functions for independent testability
### Pending Todos
@@ -92,6 +95,6 @@ None yet.
## Session Continuity
Last session: 2026-03-26T11:04:17.988Z
Stopped at: Completed 02-02: OscillatorBank and stereo mixer
Last session: 2026-03-26T11:09:30.191Z
Stopped at: Completed 02-03: MP3 encoder and CLI output flag
Resume file: None
@@ -0,0 +1,100 @@
---
phase: 02-audio-synthesis-engine
plan: 03
subsystem: encode
tags: [mp3, encoding, go-lame, cli, output-flag, tdd]
dependency_graph:
requires: [02-01, 02-02]
provides: [encode/mp3.go, encode/mp3_test.go, cmd/netsynth/-o-flag]
affects: [cmd/netsynth/main.go]
tech_stack:
added: [github.com/sjzar/go-lame (used directly)]
patterns: [go-lame LameWriter API, zero-packet guard before file creation, TDD red-green workflow]
key_files:
created:
- encode/mp3.go
- encode/mp3_test.go
modified:
- cmd/netsynth/main.go
decisions:
- "EncodeMP3 and RunSynthesis separated into distinct functions to enable independent testing of raw-frame encoding vs full pipeline"
- "RunSynthesis takes []classify.WindowSnapshot slice (not channel) — caller collects from channel enabling zero-packet check before file creation"
- "Zero-packet guard checks totalPackets BEFORE os.Create to ensure no empty file is left on disk"
- "Snapshot accumulator uses _ = collectedSnapshots to suppress unused variable error without importing encode package prematurely"
metrics:
duration: 3min
completed: 2026-03-26
tasks_completed: 2
files_created: 2
files_modified: 1
---
# Phase 2 Plan 3: MP3 Encoder and Output Flag Summary
**One-liner:** MP3 encoder via go-lame (44100 Hz, 128 kbps) with ffprobe-validated tests, zero-packet guard, and -o CLI flag with timestamp default.
## What Was Built
### Task 1: MP3 encoder package (TDD)
**encode/mp3.go** implements two functions:
- `EncodeMP3(outputPath string, frames [][2]float64, sampleRate int) error` — Creates an MP3 file from stereo PCM frames using go-lame's LameWriter. Sets 44100 Hz input/output sample rate, 128 kbps bitrate, stereo (2 channels), quality 5. Calls `InitParams()` after all `Set*` calls (required by go-lame API). Writes all PCM bytes in a single call.
- `RunSynthesis(snapshots []classify.WindowSnapshot, outputPath string) error` — Orchestrates the full pipeline: checks total packet count (zero-packet guard before file creation), creates `OscillatorBank` with tau=1.0, renders each snapshot via `bank.RenderWindow`, concatenates all frames, and calls `EncodeMP3`.
**encode/mp3_test.go** implements three tests, all ffprobe-validated:
- `TestMP3Valid` — 3 synthetic snapshots (ICMP=50+DNS=30, HTTPS=200, SSH=10+HTTP=80) through full pipeline; ffprobe confirms MP3 format, duration > 0
- `TestZeroPacketError` — empty slice and zero-count snapshots return error containing "no packets"; output file does not exist on disk
- `TestEncodeMP3DirectBytes` — 44100 frames of 440 Hz sine wave encoded directly; ffprobe confirms MP3 output
### Task 2: -o output flag and snapshot accumulator stub
**cmd/netsynth/main.go** additions:
- `var outputPath string` package-level var
- `-o`/`--output` Cobra flag with descriptive help text
- Timestamp default resolution: `netsynth-<20060102-150405>.mp3` format when flag omitted
- Snapshot accumulator loop collects `[]classify.WindowSnapshot` alongside existing totals accumulation
- `TODO(phase-3)` comment marking the synthesis integration point
## Test Results
```
ok github.com/netsynth/netsynth/aggregate 0.145s
ok github.com/netsynth/netsynth/capture 0.009s
ok github.com/netsynth/netsynth/classify 0.007s
ok github.com/netsynth/netsynth/cmd/netsynth 0.021s
ok github.com/netsynth/netsynth/encode 0.283s
ok github.com/netsynth/netsynth/synth 0.680s
```
## Deviations from Plan
None — plan executed exactly as written.
## Commits
| Hash | Type | Description |
|------|------|-------------|
| ed3a562 | test | Add failing tests for MP3 encoder and RunSynthesis (RED) |
| f221963 | feat | Implement MP3 encoder package with RunSynthesis orchestrator (GREEN) |
| a65dfcd | feat | Wire -o output flag and snapshot accumulator stub into CLI |
## Phase 2 Completion
This plan completes Phase 2 (audio-synthesis-engine). The full synthesis chain is now validated end-to-end:
```
WindowSnapshot -> OscillatorBank.RenderWindow -> [][2]float64 -> StereoFramesToInt16Bytes -> go-lame LameWriter -> MP3 file
```
Phase 3 will wire `encode.RunSynthesis(collectedSnapshots, outputPath)` into the live capture pipeline.
## Self-Check
Files created/modified:
- encode/mp3.go: EXISTS
- encode/mp3_test.go: EXISTS
- cmd/netsynth/main.go: MODIFIED
Commits verified: ed3a562, f221963, a65dfcd