Skip to content

fix(pulseaudio): several correctness and accuracy fixes - #1115

Merged
roderickvd merged 2 commits into
masterfrom
fix/pulseaudio-improvements
Mar 2, 2026
Merged

fix(pulseaudio): several correctness and accuracy fixes#1115
roderickvd merged 2 commits into
masterfrom
fix/pulseaudio-improvements

Conversation

@roderickvd

Copy link
Copy Markdown
Member

A few issues I spotted in the PulseAudio backend after it landed. @colinmarc, would you mind casting an eye over these since you know the code best?

First commit contains hot-path and correctness fixes:

  • U8 silence is 0x80, not 0x00
  • store_latency had a sign bug: casting a negative write_offset - read_offset to u64 wrapped around
  • Record streams were reading sink_usec instead of source_usec
  • Worker thread errors from play_all() were silently dropped
  • Latency poll interval reduced from 100 ms to 5 ms

Second commit improves channel maps, buffer configuration, and timestamps:

  • Channel map was mapping everything except stereo to Mono; now implements standard PA layouts for 1–8 channels, 7.1.2 and 7.1.4 Dolby Atmos for 10 and 12 channels, and sequential Aux positions as a fallback
  • make_buffer_attr split into separate playback and record functions: playback uses a double-buffer (target_length = 2N, minimum_request_length = N) to guarantee N-frame callbacks; record uses fragment_size instead of the playback-only target_length field
  • adjust_latency flag now set when BufferSize::Fixed is requested, so PulseAudio configures the hardware end-to-end to hit the target
  • SystemTime replaced with Instant (monotonic, not affected by NTP); playback timestamps now interpolate latency between polls rather than using a stale value for up to 5 ms

- Fill playback buffer with the correct silence byte per format (0x80 for
  U8, 0x00 for signed/float); use buf.fill() over a manual loop
- Fix sign bug in store_latency: subtract write/read offsets as i64 before
  clamping to zero, avoiding wrapping on negative values at startup
- Use source_usec instead of sink_usec for record-stream latency; sink_usec
  is 0 for a pure record stream, making capture timestamps wrong
- Surface play_all() errors via error_callback instead of silently dropping
  them; share the callback with the worker thread via Arc<Mutex<>>
- Reduce latency poll interval from 100 ms to 5 ms to keep timestamps
  within roughly one buffer period of true hardware latency
- Remove unused return value from store_latency
Channel maps: replace the mono-for-all fallback with correct standard
layouts for 1–8 channels (PA_CHANNEL_MAP_DEFAULT convention), 7.1.2
and 7.1.4 Dolby Atmos layouts for 10 and 12 channels, and sequential
Aux positions for counts with no agreed-upon layout.

Buffer attrs: split into separate playback and record functions.
Playback uses double-buffering (target_length = 2N,
minimum_request_length = N) so the server always requests exactly N
frames per callback. Record now correctly uses fragment_size instead
of the playback-only target_length field, which was silently ignored.

Set the adjust_latency StreamFlag when BufferSize::Fixed is requested,
instructing PulseAudio to configure hardware end-to-end to meet the
latency target.

Timestamps: switch from SystemTime to Instant (monotonic, immune to
NTP adjustments). Add latency interpolation for playback streams
between timing polls — store poll_usec before latency_micros to avoid
a startup race where stale poll_usec=0 would zero out the interpolated
latency.
@roderickvd

Copy link
Copy Markdown
Member Author

I'm on a merge streak and would like to get this in so it gets more testing.
@colinmarc if you see any issues with this, then do not hesitate to open an issue.

@roderickvd
roderickvd merged commit 7185e33 into master Mar 2, 2026
31 checks passed
@roderickvd
roderickvd deleted the fix/pulseaudio-improvements branch March 2, 2026 21:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant