diff --git a/.planning/REQUIREMENTS.md b/.planning/REQUIREMENTS.md index 99cd370..2c5edb1 100644 --- a/.planning/REQUIREMENTS.md +++ b/.planning/REQUIREMENTS.md @@ -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-.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-.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 diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 16f0c97..1fb7975 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -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-.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 | - | diff --git a/.planning/STATE.md b/.planning/STATE.md index 8f2908d..319375c 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -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 diff --git a/.planning/phases/02-audio-synthesis-engine/02-03-SUMMARY.md b/.planning/phases/02-audio-synthesis-engine/02-03-SUMMARY.md new file mode 100644 index 0000000..304f63e --- /dev/null +++ b/.planning/phases/02-audio-synthesis-engine/02-03-SUMMARY.md @@ -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