Repository navigation
Cut the native read-ahead to 24 ms on macOS - #78
Merged
Merged
Conversation
Trigger-to-sound on a MacBook Air (speaker-to-mic loopback, 2026-10-26) drops from a 150-158 ms median to 119-126 ms. 24 ms is the shortest read-ahead the 1024-frame device allows at 44.1 kHz. A few minutes of tesserae at 24 ms / 1024 played clean by ear; 24 ms / 512 crackled the whole time despite clean loopback onsets, so the device buffer stays. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… short Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… the default Only macOS was heard at 24 ms. oto drains it a quarter-buffer at a time; a backend that drains the whole buffer per callback would leave 24 ms almost nothing after each drain. playerBuffer now lives per platform: 24 ms on darwin, 40 ms elsewhere. Where one device buffer outlasts playerBuffer (a low rate, or more frames), the default read-ahead is that buffer plus playerBuffer rather than exactly one buffer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…uffer if longer Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e long comments Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
mparrett
added a commit
that referenced
this pull request
Oct 31, 2026
* Put the macOS native read-ahead back to 40 ms #78 cut it to 24 ms on macOS after tesserae played clean there. xsofy, a heavier lg process, crackled at 24 ms on the same kind of Mac, and UNDERTONE_PLAYER_BUFFER=40ms fixed it (2026-10-31). Every platform now defaults to 40 ms, about 30 ms more trigger latency on macOS (150-158 ms against 119-128 ms) for a stall budget of ~63 ms instead of ~47. A host that wants 24 ms asks for it through Options.Queue. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fold playerBuffer into one constant, and name the lg host's override From review: with both platforms at 40 ms the per-platform files were the same constant twice, and latency.go still said it varied. An lg host opens through the letgo binding with defaults, so it can't reach Options.Queue; UNDERTONE_PLAYER_BUFFER is its route back to 24 ms, and the README and notes now say so. The buffer margin is dated again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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.
On macOS,
playerBuffergoes from 40 ms to 24 ms. The device buffer stays at 1024 frames, and other platforms stay at 40 ms.Evidence, all on a MacBook Air, 2026-10-26:
latency.gorecords this, along with the reason onset timing can't catch underruns, and the stall budget: about 47 ms at 24 ms, against 63 ms at 40 ms.Changes:
playerBufferis set per platform (playerbuffer_darwin.goandplayerbuffer_other.go). Only macOS was heard at 24 ms. There oto drains the device a quarter-buffer at a time; a backend that drains the whole buffer per callback (WASAPI may) would leave 24 ms almost no margin, so other platforms keep 40 ms.Options.Queueis now resolved inoverrides. When one device buffer outlastsplayerBuffer, at a low rate or with more frames, the default stretches to that one buffer, which is continuous in rate and frames. Before, that case was an error.cmd/undertonestill asks for 500 ms for a bed with no one-shots, unchanged. Browsers aren't affected, since they play throughwebaudio.🤖 Generated with Claude Code