Add remote per-player delay configuration#185
Conversation
|
Please update the referenced sendspin-js in the serve HTML too. |
Add SET_STATIC_DELAY handling in TUI and daemon, pass state_supported_commands to SendspinClient, clamp keyboard delay adjustment to 0-5000ms range.
Without this, TUI delay adjustments were local-only and the server never learned about the new static_delay_ms value.
Instead of clearing the audio buffer (which the server won't resend), shift the server timestamp cursor so sync correction gradually speeds up or slows down playback to match the new delay. Plumb the delay delta through the audio work queue for both server-initiated and keyboard-initiated changes.
fa7c7f9 to
439ac0a
Compare
There was a problem hiding this comment.
Pull request overview
Adds end-to-end support for remotely configuring a per-player static playback delay (per spec PR #67), including persistence/migration and audio-timing adjustment so delay changes don’t require clearing the buffer.
Changes:
- Handle incoming
set_static_delayserver commands in both TUI and daemon, advertise support viaclient/state. - Clamp local keyboard delay adjustments to 0–5000ms and propagate delay-change notifications to the audio worker for gradual sync correction.
- Migrate older persisted negative delay values on settings load; adjust audio playback anchoring and update dependency constraints.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| sendspin/tui/keyboard.py | Clamp delay adjustments; notify audio worker of delay deltas; persist delay updates. |
| sendspin/tui/app.py | Advertise SET_STATIC_DELAY support and apply/persist server-issued delay changes in TUI. |
| sendspin/settings.py | Change settings load path to avoid scheduling saves from executor; clamp/migrate persisted delay values. |
| sendspin/daemon/daemon.py | Advertise SET_STATIC_DELAY support and apply/persist server-issued delay changes in daemon mode. |
| sendspin/audio.py | Add cursor adjustment API for delay changes and refine DAC-anchored start estimation. |
| sendspin/audio_connector.py | Plumb delay-change notifications through the sync audio worker to AudioPlayer. |
| README.md | Update documentation/examples for the new delay sign convention and typical values. |
| pyproject.toml | Bump aiosendspin dependency (with [server] extra) and raise minimum av/numpy versions. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
balloob
left a comment
There was a problem hiding this comment.
At +4880ms the TUI starts buffering, increase it even further and audio doesn't play.
In server log:
WARNING:aiosendspin.server.connection.sendspin-cli-mac-1.home:Late binary: skipping 4 chunk(s); late_by_us=1591 ts_us=148084323243 now_us=148079444834 queue=1/8192
DEBUG:aiosendspin.server.connection.sendspin-cli-mac-1.home:Send summary role=player samples=47 send_gap_ms(avg=108.4 min=76.9 max=183.7) ts_gap_ms(avg=108.3 min=96.0 max=192.0) buf_ms(avg=110.3 min=96.3 max=126.2)
DEBUG:aiosendspin.server.connection.sendspin-cli-mac-1.home:Discarding late chunk: late_by=5.3ms, plays_in=-5.3ms
WARNING:aiosendspin.server.connection.sendspin-cli-mac-1.home:Late binary: skipping 1 chunk(s); late_by_us=5291 ts_us=148086435243 now_us=148081560534 queue=1/8192
DEBUG:aiosendspin.server.connection.sendspin-cli-mac-1.home:Discarding late chunk: late_by=4.3ms, plays_in=-4.3ms
DEBUG:aiosendspin.server.connection.sendspin-cli-mac-1.home:Discarding late chunk: late_by=4.1ms, plays_in=-4.1ms
DEBUG:aiosendspin.server.connection.sendspin-cli-mac-1.home:Discarding late chunk: late_by=3.4ms, plays_in=-3.4ms
DEBUG:aiosendspin.server.connection.sendspin-cli-mac-1.home:Send summary role=player samples=49 send_gap_ms(avg=104.0 min=76.8 max=184.3) ts_gap_ms(avg=103.8 min=96.0 max=192.0) buf_ms(avg=111.5 min=97.4 max=127.3)
When a player advertises a large `static_delay_ms`, the server's fixed 250ms send-ahead window wasn't enough. Chunks arrived late from the player's perspective and got dropped at startup. `PushStream` scheduling paths now extend the send-ahead by the largest active player's `static_delay_us` via a new `_max_active_static_delay_us()` helper. Buffer throttling also accounts for the extra lead so the producer isn't throttled prematurely. Resolves the issue raised in Sendspin/sendspin-cli#185
aiosendspin switched to CLOCK_MONOTONIC_RAW on Linux. Use `client.now_us()` instead of `time.monotonic()` / `loop.time()` to keep all components in the same clock domain.
Without this, a delay change during draining would fall through and be miscast as a `_ChunkWorkItem`.
Fixed in Only issue I think thats left is that large changes to the static delay trigger clearing of the buffer, but this was also the case before. Pausing/restarting fixes that. |
Implements per-player delay configuration from [Sendspin spec PR Sendspin#67](Sendspin/spec#67). TUI and daemon handle incoming `set_static_delay` server commands: the client library applies the delay, then the app persists it to settings and updates the UI. Both advertise `supported_commands: ["set_static_delay"]` in `client/state`. Keyboard delay adjustment (`.`,`]`) now clamps to the valid 0-5000ms range and reports the change to the server. Old settings with negative delay values (previous sign convention) are migrated on load. The settings `_load()` path no longer calls `_schedule_save()` from inside an executor, fixing a `RuntimeError: no running event loop`. --------- Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
The DAC-anchored sync fix (Sendspin#226) and remote per-player delay (Sendspin#185) make the old 100-150ms recommendation obsolete. Updated the README to recommend 0ms as the default and mention that compatible servers can configure delay remotely.
Implements per-player delay configuration from Sendspin spec PR #67.
TUI and daemon handle incoming
set_static_delayserver commands: the client library applies the delay, then the app persists it to settings and updates the UI. Both advertisesupported_commands: ["set_static_delay"]inclient/state.Keyboard delay adjustment (
.,]) now clamps to the valid 0-5000ms range and reports the change to the server. Old settings with negative delay values (previous sign convention) are migrated on load. The settings_load()path no longer calls_schedule_save()from inside an executor, fixing aRuntimeError: no running event loop.