- 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>master
| @@ -21,5 +21,15 @@ vendor/ | |||||
| .DS_Store | .DS_Store | ||||
| Thumbs.db | Thumbs.db | ||||
| # Dev config (has local paths — use config.yaml.example as template) | |||||
| config.yaml | |||||
| # Config with secrets | # Config with secrets | ||||
| config.local.yaml | config.local.yaml | ||||
| # Portable Winamp (extracted locally for dev — not committed) | |||||
| winamp/ | |||||
| # Runtime data files | |||||
| killist.dat | |||||
| resume.dat | |||||
| @@ -266,8 +266,13 @@ func (s *Server) statusMsg() ([]byte, error) { | |||||
| msg.State = "stopped" | msg.State = "stopped" | ||||
| } | } | ||||
| msg.Title = s.wa.GetTitle() | msg.Title = s.wa.GetTitle() | ||||
| msg.Position = s.wa.GetPosition() | |||||
| msg.Length = s.wa.GetLength() | |||||
| // Winamp returns 0xFFFFFFFF when stopped/no track — clamp to 0. | |||||
| pos := s.wa.GetPosition() | |||||
| length := s.wa.GetLength() | |||||
| if pos > 86400 { pos = 0 } // > 24h → garbage value | |||||
| if length > 86400 { length = 0 } | |||||
| msg.Position = pos | |||||
| msg.Length = length | |||||
| msg.PlaylistPos = s.wa.GetPlaylistPosition() | msg.PlaylistPos = s.wa.GetPlaylistPosition() | ||||
| msg.PlaylistLength = s.wa.GetPlaylistLength() | msg.PlaylistLength = s.wa.GetPlaylistLength() | ||||
| msg.Version = s.wa.GetVersion() | msg.Version = s.wa.GetVersion() | ||||
| @@ -72,11 +72,21 @@ type waveFormatEx struct { | |||||
| Size uint16 | Size uint16 | ||||
| } | } | ||||
| // waveFormatExtensibleEx is a flat representation of WAVEFORMATEXTENSIBLE. | |||||
| // We cannot embed waveFormatEx because Go pads the struct to 20 bytes | |||||
| // (alignment of largest field = uint32), but the C layout is 18 bytes — | |||||
| // so SubFormat would land at the wrong offset if we used struct embedding. | |||||
| type waveFormatExtensibleEx struct { | type waveFormatExtensibleEx struct { | ||||
| Format waveFormatEx | |||||
| Samples uint16 | |||||
| ChannelMask uint32 | |||||
| SubFormat windows.GUID | |||||
| FormatTag uint16 | |||||
| Channels uint16 | |||||
| SamplesPerSec uint32 | |||||
| AvgBytesPerSec uint32 | |||||
| BlockAlign uint16 | |||||
| BitsPerSample uint16 | |||||
| Size uint16 | |||||
| Samples uint16 // wValidBitsPerSample / wSamplesPerBlock | |||||
| ChannelMask uint32 | |||||
| SubFormat windows.GUID // 16 bytes → total 40 bytes, matches C layout | |||||
| } | } | ||||
| // ── DLL procs ───────────────────────────────────────────────────────────────── | // ── DLL procs ───────────────────────────────────────────────────────────────── | ||||