runtime.LockOSThread/UnlockOSThread added to withEndpointVolume (volume)
and Capturer.run (viz). COM apartments are thread-affine on Windows;
without the lock the Go scheduler can migrate a goroutine mid-call to a
thread that never called CoInitializeEx, causing sporadic failures.
Also check CoInitializeEx HRESULT: S_OK (0) and S_FALSE (1) are success,
anything else is a real error.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- viz/capture.go: waveFormatExtensibleEx now flat struct (not embedded)
Go pads waveFormatEx to 20 bytes (uint32 alignment), but the Windows
C struct is 18 bytes — embedding caused SubFormat to land at wrong
offset, breaking float32 detection (float=false bug)
- server.go: clamp position/length values > 24h to 0
Winamp returns 0xFFFFFFFF for these fields when stopped/no track loaded
- .gitignore: exclude winamp/ dir and runtime .dat files
- config.yaml: added (gitignored) pointing to winamp/winamp.exe
Tested: Winamp 5.9 portable in winamp/, IPC connection verified,
viz loopback correctly detects 48kHz float32 stereo
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
WebSocket (/ws):
- Replaces 2s HTTP polling with persistent WS connection
- Server pushes status updates every 500ms to all clients
- Server pushes FFT spectrum frames at ~30fps
- Bidirectional: client sends commands as JSON ({cmd, delta, level, ...})
- Auto-reconnect on disconnect (3s backoff)
- Status pushed immediately on connect
Visualisation (internal/viz/capture.go):
- WASAPI loopback capture via IAudioClient (same COM approach as volume)
- Captures whatever is playing through the default render device
- 2048-sample Hanning-windowed FFT (pure Go, no deps)
- 64 log-spaced bars, 40Hz-20kHz
- Fast attack / slow decay smoothing per bar
Canvas renderer (app.js):
- requestAnimationFrame loop, DPR-aware resize
- Green->yellow->red HSL gradient by amplitude
- Peak-hold indicators with 1.2%/frame decay
- Graceful: canvas stays dark if no viz data (Winamp paused/stopped)
Architecture:
- internal/server/hub.go: gorilla/websocket hub (register/broadcast/unregister)
- internal/server/server.go: full //go:build windows, REST API kept for debug
- frontend uses WS for all commands, REST only for killist list fetch
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>