--- 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