Skip to content

Fix pitch shift on mid-stream format change#180

Merged
maximmaxim345 merged 2 commits intomainfrom
fix/drain-buffer-on-format-switch
Mar 12, 2026
Merged

Fix pitch shift on mid-stream format change#180
maximmaxim345 merged 2 commits intomainfrom
fix/drain-buffer-on-format-switch

Conversation

@maximmaxim345
Copy link
Member

When aiosendspin (as a server) changes sample rate via set_preferred_format, it sends a stream/start for the already-active role. Per the Sendspin spec, this updates the stream configuration without clearing buffers.

The worker immediately called player.set_format(), closing the old sounddevice stream and opening a new one at the new sample rate. Old PCM data still queued at the previous rate would then play through the new stream, causing pitch distortion.

With this PR, the worker now waits for the player to drain its internal queue before switching format. Incoming new-format chunks are buffered locally during the drain and submitted after the switch completes. Control items (stop, clear, volume) continue to be processed during the wait.

Old PCM data still in the player queue would be played through
the new stream at the wrong sample rate, causing pitch shift.
@maximmaxim345 maximmaxim345 added the bugfix Fixes a bug label Mar 10, 2026
@maximmaxim345 maximmaxim345 marked this pull request as ready for review March 10, 2026 12:57
Copilot AI review requested due to automatic review settings March 10, 2026 12:57
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses pitch distortion when the server changes the active stream’s sample rate mid-stream by avoiding a format switch while old-format PCM is still pending in the client’s playback pipeline.

Changes:

  • Add AudioPlayer.is_drained() to detect when the internal playback queue is empty.
  • Update the audio worker to wait for the player to drain before calling set_format().
  • Buffer incoming chunks during the drain and submit them after the format switch, while still processing control messages (stop/clear/volume).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.

File Description
sendspin/audio_connector.py Implements drain-before-switch logic and local buffering of chunks during the drain window.
sendspin/audio.py Adds AudioPlayer.is_drained() to support safe mid-stream format switching.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@maximmaxim345 maximmaxim345 merged commit bb7c0f6 into main Mar 12, 2026
1 check passed
@maximmaxim345 maximmaxim345 deleted the fix/drain-buffer-on-format-switch branch March 12, 2026 14:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugfix Fixes a bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants