Repository navigation
Conversation
TimeProvider.setDiff computes the median client/server clock offset to drive audio/video synchronization, but sorted the offset buffer with a bare Array.prototype.sort(). With no comparator, sort() coerces elements to strings and orders them lexicographically, so the "median" was taken from a wrongly ordered array (e.g. [2, 10, -5, 100] -> [-5, 10, 100, 2]). The resulting offset is unstable and frequently wrong, corrupting every serverTime() calculation and causing playback drift and "Chunk too old, dropping" stalls. Sort numerically with an explicit comparator. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed PCM samples peak at 2^(bits-1) (e.g. 32767 for 16-bit), so to map them into the Web Audio [-1, 1) float range they must be divided by 2^(bits-1). Dividing by 2^bits halved every sample, playing all audio roughly 6 dB quieter than intended. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
JsonMessage.serialize() wrote the UTF-16 string length into the size field while allocating the buffer from getSize(), which uses the UTF-8 byte length. For any payload containing non-ASCII characters (e.g. a client name like "Küche") the declared size was smaller than the actual encoded bytes, truncating the message on the wire. Use the encoded byte length for the size field. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Prefer the native crypto.randomUUID() for generating the persistent client id, falling back to the existing Math.random implementation when it is unavailable (non-secure contexts) or throws. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The seekbackward/seekforward/seekto handlers computed Math.max/Math.min clamps whose results were never assigned or used. They had no effect; remove them to clarify that the relative offset / absolute position is sent to the server as-is. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The 'change' listener for the system color scheme was added in the component render body, leaking a new listener on every render. Move it into a useEffect that registers once and removes the listener on cleanup. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Drop the multi-kilobyte commented-out example Server JSON (and the unused `server.fromJson(json)` line) that was left in the component for local testing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The dummy re-render counters were updated with setUpdate(update + 1), which reads a value that may be stale under React batching. Switch to the functional form setUpdate(u => u + 1) and drop the now-unused state value binding. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
updateVolume() divided the summed client volume by clients.length without checking for an empty group, producing a NaN slider value when a group had no visible clients. Return a volume of 0 in that case. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
pbtrung
added a commit
to pbtrung/snapweb
that referenced
this pull request
Sep 30, 2026
Ports the fixes from upstream snapcast#165 and snapcast#166 onto the refactored components. Audio: - Sort the clock offsets numerically in TimeProvider.setDiff. A bare sort() ordered them as strings, so the median offset was wrong, which skewed serverTime() and caused drift and dropped chunks - Normalize PCM samples by 2^(bits-1) instead of 2^bits, which played all audio about 6 dB too quiet - Write the UTF-8 byte length into serialized JSON messages instead of the UTF-16 string length, which truncated payloads with non-ASCII characters - Generate the client id with crypto.randomUUID() where available React: - Register the prefers-color-scheme listener once in an effect with cleanup instead of adding a new listener on every render - Use functional updates for the remaining force-update counters - Return 0 instead of NaN for the volume of a group with no clients Cleanup: - Remove no-op clamp expressions in the MediaSession seek handlers - Remove the large commented-out sample server JSON Tests: - Export the message classes and TimeProvider from snapstream.ts and add unit tests for the median, the non-ASCII JSON round trip, Hello and Time messages, and client id generation
pbtrung
added a commit
to pbtrung/snapweb
that referenced
this pull request
Sep 30, 2026
README: - Note the Node.js requirement (20.19+ or 22.12+, from Vite 8) - Add Test and Code style sections covering the unit, coverage and Snapserver integration test commands, Prettier and ESLint - Point out that the prebuilt zip and Debian releases are upstream Snapweb's, since this repository no longer builds Debian packages - Remove the Screenshot and Contributing sections, and the screenshots in docs/images Changelog: - Add the 0.9999 entry: the time sync and audio fixes (including upstream PRs snapcast#165 and snapcast#166), the React and SnapControl fixes, lazy loading and bundle splitting, package updates, tests, Prettier and removed tooling
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A batch of independent, self-contained fixes (one commit each, so they can be
reviewed/cherry-picked individually).
Audio correctness
TimeProvider.setDiffsorted theclock-offset buffer with a bare
Array.sort(), ordering numberslexicographically → wrong median, unstable
serverTime(), playback drift andChunk too old, droppingstalls. Now sorts numerically. (likely Snapweb slightly out of sync with snapdroid clients #39;contributes to Safari not working #49, Change snapweb client to stream in playback, does not work #108)
2^bitsinstead of2^(bits-1), so all audio played ~6 dB too quiet.JsonMessage.serialize()wrote thestring length while the buffer was sized from UTF-8 bytes, truncating payloads
with non-ASCII characters (e.g. "Küche").
Robustness / React
uuidv4()preferscrypto.randomUUID()with a safe fallback.prefers-color-schemelistener moved from render body into auseEffectwithcleanup (was leaking a listener per render).
setState.Cleanup
Verified:
tscclean,vite buildsucceeds.🤖 Generated with Claude Code