fix: two ring-buffer bugs in DelayNode::delayBufferOperation#998
Open
OhByron wants to merge 2 commits intosoftware-mansion:mainfrom
Open
fix: two ring-buffer bugs in DelayNode::delayBufferOperation#998OhByron wants to merge 2 commits intosoftware-mansion:mainfrom
OhByron wants to merge 2 commits intosoftware-mansion:mainfrom
Conversation
framesToEnd was computed as the overflow count (frames past the buffer end) instead of the pre-wrap count (frames remaining before the buffer end). This caused audio data to be written/read at wrong positions on every buffer boundary crossing, producing periodic clicking artifacts and inaudible delay echo on Android. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
The READ path only zeroed the post-wrap chunk (second half of a split operation), leaving the pre-wrap chunk (frames read from operationStartingIndex to buffer end) unconsumed in the delay buffer. On the next full buffer rotation (~1 second for a 1s max-delay buffer) that stale data was re-read as new audio, producing a periodic ~1s metronome tick independent of delayTime. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
mdydek
requested changes
Mar 30, 2026
Contributor
mdydek
left a comment
There was a problem hiding this comment.
one nitpick and I think we're ready to go
Comment on lines
46
to
+47
| int framesToEnd = | ||
| static_cast<int>(operationStartingIndex + framesToProcess - delayBuffer_->getSize()); | ||
| static_cast<int>(delayBuffer_->getSize()) - static_cast<int>(operationStartingIndex); |
Contributor
There was a problem hiding this comment.
Suggested change
| int framesToEnd = | |
| static_cast<int>(operationStartingIndex + framesToProcess - delayBuffer_->getSize()); | |
| static_cast<int>(delayBuffer_->getSize()) - static_cast<int>(operationStartingIndex); | |
| auto framesToEnd = | |
| static_cast<int>(delayBuffer_->getSize() - operationStartingIndex); |
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.
Summary
Two bugs in
DelayNode::delayBufferOperationthat together cause periodic clicking artifacts and inaudible delay echo on Android.Bug 1 — Inverted wraparound index (
framesToEnd)framesToEndwas computed as the overflow count (frames past the buffer end) but used as the size of the pre-wrap chunk (frames remaining before the buffer end):This caused audio to be written/read at wrong positions on every buffer boundary crossing.
Bug 2 — Pre-wrap chunk not zeroed after READ
The READ path only zeroed the second (post-wrap) chunk. The first chunk — frames from
operationStartingIndexto the buffer end — was consumed but never cleared. On the next full buffer rotation (~1 s for a 1 s max-delay buffer) that stale data was re-read as new audio:Symptoms (Android, Nokia XR20, sampleRate 48000)
delayTime.value— fixed by Bug 1Test plan
AudioContext, wireOscillatorNode → DelayNode → GainNode(wet) → destinationwith a separate dry pathdelayTimeto a value <maxDelayTime(e.g. 0.3 s, 0.95 s)delayTimeseconds after stopTested on Android (Nokia XR20, Android 14) via a local dev build with both fixes applied.
🤖 Generated with Claude Code