From 1219df3c27404ca0bd2d967a3fa490d604486ec9 Mon Sep 17 00:00:00 2001 From: Jan Svabenik Date: Mon, 25 May 2026 16:12:09 +0200 Subject: [PATCH] fix: struct padding bug in viz loopback, overflow in stopped state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- .gitignore | 10 ++++++++++ internal/server/server.go | 9 +++++++-- internal/viz/capture.go | 18 ++++++++++++++---- 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 270e6a5..622170c 100644 --- a/.gitignore +++ b/.gitignore @@ -21,5 +21,15 @@ vendor/ .DS_Store Thumbs.db +# Dev config (has local paths — use config.yaml.example as template) +config.yaml + # Config with secrets config.local.yaml + +# Portable Winamp (extracted locally for dev — not committed) +winamp/ + +# Runtime data files +killist.dat +resume.dat diff --git a/internal/server/server.go b/internal/server/server.go index e12d372..33b574e 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -266,8 +266,13 @@ func (s *Server) statusMsg() ([]byte, error) { msg.State = "stopped" } 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.PlaylistLength = s.wa.GetPlaylistLength() msg.Version = s.wa.GetVersion() diff --git a/internal/viz/capture.go b/internal/viz/capture.go index a0c9870..98efb61 100644 --- a/internal/viz/capture.go +++ b/internal/viz/capture.go @@ -72,11 +72,21 @@ type waveFormatEx struct { 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 { - 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 ─────────────────────────────────────────────────────────────────