- Created 02-02-SUMMARY.md with plan results and 14 test descriptions - Updated STATE.md: advanced to plan 3 of 3, recorded metrics and decisions - Updated ROADMAP.md: phase 02 showing 2/3 summaries complete - Marked SYNTH-03 complete in REQUIREMENTS.md
3.8 KiB
3.8 KiB
phase, plan, subsystem, tags, dependency_graph, tech_stack, key_files, decisions, metrics
| phase | plan | subsystem | tags | dependency_graph | tech_stack | key_files | decisions | metrics | |||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 02-audio-synthesis-engine | 02 | synth |
|
|
|
|
|
|
Phase 02 Plan 02: OscillatorBank and Stereo Mixer Summary
One-liner: OscillatorBank with 11 EMA-smoothed layers driven by WindowSnapshot, mixed with 1/11 fixed gain and constant-power stereo panning into interleaved int16 PCM bytes.
What Was Built
synth/mixer.go
Provides two core utilities:
PanGains(p float64) (gainL, gainR float64)— constant-power pan law using cos/sin. Maps [-1, 1] pan position to L/R gain pair wheregainL^2 + gainR^2 = 1.0at all positions.StereoFramesToInt16Bytes(frames [][2]float64) []byte— converts stereo float64 frames to interleaved little-endian int16 bytes for go-lame'sWrite()method. Clamps values to [-1.0, 1.0] before conversion to prevent int16 overflow.
synth/bank.go
Provides the core synthesis engine:
NewBank(tau float64) *OscillatorBank— creates 11 layers, one per TrafficClass, each initialized fromClassFreqConfigs.RenderWindow(snap classify.WindowSnapshot) [][2]float64— updates all layer amplitude targets from the snapshot's packet counts, then renders exactlySamplesPerWindow(22050) stereo frames. Each layer contributesGainPerLayer(1/11) of headroom, so even 11 simultaneously maxed layers cannot exceed [-1.0, 1.0].
Tests Added (14 new tests)
mixer_test.go (7 tests):
TestPanGainsCenter— verifies center position yields sqrt(2)/2 for both channelsTestPanGainsFullLeft— gainL=1.0, gainR=0.0 at p=-1.0TestPanGainsFullRight— gainL=0.0, gainR=1.0 at p=1.0TestPanGainsPowerPreserved— gainL^2 + gainR^2 = 1.0 for 9 pan valuesTestStereoFramesToInt16Bytes— 1.0/-1.0 maps to 32767/-32767 LE int16TestStereoFramesToInt16BytesZero— 0.0/0.0 maps to 4 zero bytesTestClampPreventsOverflow— 2.0/-2.0 clamps to 32767/-32767 (no wrap-around)
bank_test.go (7 tests):
TestNewBankHas11Layers— bank has exactly 11 layers, one per AllClasses()TestRenderWindowOutputLength— returns exactly 22050 framesTestRenderWindowSilentWhenNoTraffic— all-zero frames when no class ever seenTestRenderWindowNonZeroWithTraffic— ICMP count=100 produces non-zero outputTestMixerNoClip— 11 classes at max count, 10 windows rendered, no |L| or |R| > 1.0TestStereoPan— ClassDHCP (pan=-0.75) yields rmsL > rmsR (wide-left verified)TestMultipleWindowsEMAConvergence— RMS grows over 5 windows with same snapshot
Deviations from Plan
None — plan executed exactly as written.
Self-Check
Files created:
- synth/mixer.go: EXISTS
- synth/mixer_test.go: EXISTS
- synth/bank.go: EXISTS
- synth/bank_test.go: EXISTS
Commits:
c9794cd: feat(02-02): stereo mixer utilities with constant-power pan law23dcfdb: feat(02-02): OscillatorBank multi-layer rendering from WindowSnapshot