Skip to content

Reuse the open audio output stream between streams - #204

Open
CrazyDiam0nd-gh wants to merge 1 commit into
music-assistant:mainfrom
CrazyDiam0nd-gh:fix/reuse-output-stream-between-streams
Open

CrazyDiam0nd-gh wants to merge 1 commit into
music-assistant:mainfrom
CrazyDiam0nd-gh:fix/reuse-output-stream-between-streams

Conversation

@CrazyDiam0nd-gh

@CrazyDiam0nd-gh CrazyDiam0nd-gh commented Oct 8, 2026 •

Copy link
Copy Markdown

Reuse the open audio output stream between streams

Fixes an audible warble (slow/wobbly pitch for 5-10 s) at the start of every playlist on Linux. Addresses #203 for every play after the first; the first play after launch is covered by the library PR linked at the bottom, so this should not close the issue on its own.

Problem

run_playback_thread builds a new SyncedPlayer on every CreatePlayer, i.e. at every StreamStart. A freshly opened output stream is cold: while it settles, the output delay the OS reports overshoots by a few hundred milliseconds (about 550 ms within the first second on my setup) and then relaxes back to its resting value (about 250 ms) over ~5 s. The audio plays gaplessly, but the sync error is measured against that delay, so it swings by the same amount. The corrector reads the swing as drift, speeds playback up by up to 4% (drop_every=25), overshoots, and then slows it down by about 1.5% (insert_every=54..70).

Evidence

  • The library's example player (unpatched sendspin-rs, same server, same Flatpak sandbox) with one persistent output stream: no warble, start errors of 4-47 ms, corrections of 0.2-0.5%.
  • The same program with a single change, dropping and rebuilding the output stream at each stream start: the warble comes back with the Companion's signature (358-391 ms initial error, a 4% correction, then a -30 to -37 ms overshoot).
  • Ruled out: authenticated port 8095 vs direct 8927, the Flatpak sandbox, the sendspin-rs version, PipeWire quantum, network.

Change

On CreatePlayer, keep the existing output stream if all of these hold, otherwise rebuild exactly as before:

  • the PCM format is the same,
  • the freshly resolved output device has the same identity as the one the open stream was built for,
  • the previous stream ended no more than 10 minutes ago (OUTPUT_STREAM_REUSE_MAX_IDLE).

The device is still re-resolved on every StreamStart, so the behavior from #40 (a device that disappeared or reconnected is picked up at the next play) is kept. If the device identity cannot be determined, the stream is rebuilt. The reuse decision is the pure function can_reuse_output_stream, with unit tests.

Testing

  • cargo fmt --check, cargo clippy --all-targets --all-features -- -D warnings, cargo test (132 passed, 4 new).
  • Built and ran on Pop!_OS (ALSA via PulseAudio/PipeWire, USB DAC). Ten stream starts in a row: the first created a stream, the other nine logged Reusing open audio output stream, and none of the later plays warbled.
  • Not tested: macOS, Windows, Bluetooth devices.

Notes

  • The first play after launch (or after more than 10 minutes idle) still opens a cold stream. That part is addressed in the library: fix: hold sync corrections while a cold output stream's delay settles Sendspin/sendspin-rs#103. This app currently pins sendspin-rs at v0.3.5, so it will need a version bump once that is released.
  • With the library fix and this change together, the first play and all later plays are clean on my setup; the strongest speed correction in a run drops from 4% to 0.5%.

A new SyncedPlayer was built for every StreamStart. A freshly opened
output stream is cold: while it settles, the output delay reported by the
OS overshoots by a few hundred milliseconds and then relaxes over several
seconds. The sync corrector reads that as drift, speeds playback up by up
to 4%, overshoots, and then slows it down, which is audible as a warble
for 5-10 seconds at the start of every playlist.

On CreatePlayer, keep the existing output stream if the PCM format is the
same, the freshly resolved output device is the same as the one the open
stream was built for, and the previous stream ended no more than 10
minutes ago. Otherwise rebuild as before. The device is still re-resolved
on every StreamStart, so the behavior from music-assistant#40 (a vanished or reconnected
device is picked up at the next play) is kept; if the device identity
cannot be determined the stream is rebuilt.

The reuse decision is a pure function with unit tests.
@CrazyDiam0nd-gh CrazyDiam0nd-gh changed the title Fixes an audible warble (slow/wobbly pitch for 5-10 s) at the start of every playlist on Linux. Addresses #203 for every play after the first; the first play after launch is covered by the library PR linked at the bottom, so this should not close the issue on its own. Reuse the open audio output stream between streams Oct 8, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant